Skip to content

(fix) patch resolveModuleNameLiterals for ts 5.0 nightly #1799

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 9, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 109 additions & 22 deletions packages/typescript-plugin/src/module-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,52 @@ import { SvelteSnapshotManager } from './svelte-snapshots';
import { createSvelteSys } from './svelte-sys';
import { ensureRealSvelteFilePath, isVirtualSvelteFilePath } from './utils';

// TODO remove when we update to typescript 5.0
declare module 'typescript/lib/tsserverlibrary' {
interface LanguageServiceHost {
/** @deprecated supply resolveModuleNameLiterals instead for resolution that can handle newer resolution modes like nodenext */
resolveModuleNames?(
moduleNames: string[],
containingFile: string,
reusedNames: string[] | undefined,
redirectedReference: ts.ResolvedProjectReference | undefined,
options: ts.CompilerOptions,
containingSourceFile?: ts.SourceFile
): (ts.ResolvedModule | undefined)[];
resolveModuleNameLiterals?(
moduleLiterals: readonly ts.StringLiteralLike[],
containingFile: string,
redirectedReference: ts.ResolvedProjectReference | undefined,
options: ts.CompilerOptions,
containingSourceFile: ts.SourceFile,
reusedNames: readonly ts.StringLiteralLike[] | undefined
): readonly ts.ResolvedModuleWithFailedLookupLocations[];
}
}

/**
* Caches resolved modules.
*/
class ModuleResolutionCache {
constructor(private readonly projectService: ts.server.ProjectService) {}

private cache = new Map<string, ts.ResolvedModule>();
private cache = new Map<string, ts.ResolvedModuleFull>();

/**
* Tries to get a cached module.
*/
get(moduleName: string, containingFile: string): ts.ResolvedModule | undefined {
get(moduleName: string, containingFile: string): ts.ResolvedModuleFull | undefined {
return this.cache.get(this.getKey(moduleName, containingFile));
}

/**
* Caches resolved module, if it is not undefined.
*/
set(moduleName: string, containingFile: string, resolvedModule: ts.ResolvedModule | undefined) {
set(
moduleName: string,
containingFile: string,
resolvedModule: ts.ResolvedModuleFull | undefined
) {
if (!resolvedModule) {
return;
}
Expand Down Expand Up @@ -78,8 +105,13 @@ export function patchModuleLoader(
const svelteSys = createSvelteSys(logger);
const moduleCache = new ModuleResolutionCache(project.projectService);
const origResolveModuleNames = lsHost.resolveModuleNames?.bind(lsHost);
const origResolveModuleNamLiterals = lsHost.resolveModuleNameLiterals?.bind(lsHost);

lsHost.resolveModuleNames = resolveModuleNames;
if (lsHost.resolveModuleNameLiterals) {
lsHost.resolveModuleNameLiterals = resolveModuleNameLiterals;
} else {
lsHost.resolveModuleNames = resolveModuleNames;
}

const origRemoveFile = project.removeFile.bind(project);
project.removeFile = (info, fileExists, detachFromProject) => {
Expand Down Expand Up @@ -119,32 +151,22 @@ export function patchModuleLoader(
return resolved;
}

return resolved.map((moduleName, idx) => {
const fileName = moduleNames[idx];
if (moduleName || !ensureRealSvelteFilePath(fileName).endsWith('.svelte')) {
return moduleName;
return resolved.map((tsResolvedModule, idx) => {
const moduleName = moduleNames[idx];
if (tsResolvedModule || !ensureRealSvelteFilePath(moduleName).endsWith('.svelte')) {
return tsResolvedModule;
}

const cachedModule = moduleCache.get(fileName, containingFile);
if (cachedModule) {
return cachedModule;
}

const resolvedModule = resolveSvelteModuleName(
fileName,
containingFile,
compilerOptions
);
moduleCache.set(fileName, containingFile, resolvedModule);
return resolvedModule;
return resolveSvelteModuleNameFromCache(moduleName, containingFile, compilerOptions)
.resolvedModule;
});
}

function resolveSvelteModuleName(
name: string,
containingFile: string,
compilerOptions: ts.CompilerOptions
): ts.ResolvedModule | undefined {
): ts.ResolvedModuleFull | undefined {
const svelteResolvedModule = typescript.resolveModuleName(
name,
containingFile,
Expand All @@ -168,8 +190,73 @@ export function patchModuleLoader(

const resolvedSvelteModule: ts.ResolvedModuleFull = {
extension: snapshot.isTsFile ? typescript.Extension.Ts : typescript.Extension.Js,
resolvedFileName
resolvedFileName,
isExternalLibraryImport: svelteResolvedModule.isExternalLibraryImport
};
return resolvedSvelteModule;
}

function resolveModuleNameLiterals(
moduleLiterals: readonly ts.StringLiteralLike[],
containingFile: string,
redirectedReference: ts.ResolvedProjectReference | undefined,
options: ts.CompilerOptions,
containingSourceFile: ts.SourceFile,
reusedNames: readonly ts.StringLiteralLike[] | undefined
): readonly ts.ResolvedModuleWithFailedLookupLocations[] {
logger.log('Resolving modules names for ' + containingFile);
// Try resolving all module names with the original method first.
// The ones that are undefined will be re-checked if they are a
// Svelte file and if so, are resolved, too. This way we can defer
// all module resolving logic except for Svelte files to TypeScript.
const resolved =
origResolveModuleNamLiterals?.(
moduleLiterals,
containingFile,
redirectedReference,
options,
containingSourceFile,
reusedNames
) ??
moduleLiterals.map(
(): ts.ResolvedModuleWithFailedLookupLocations => ({
resolvedModule: undefined
})
);

if (!configManager.getConfig().enable) {
return resolved;
}

return resolved.map((tsResolvedModule, idx) => {
const moduleName = moduleLiterals[idx].text;
if (
tsResolvedModule.resolvedModule ||
!ensureRealSvelteFilePath(moduleName).endsWith('.svelte')
) {
return tsResolvedModule;
}

return resolveSvelteModuleNameFromCache(moduleName, containingFile, options);
});
}

function resolveSvelteModuleNameFromCache(
moduleName: string,
containingFile: string,
options: ts.CompilerOptions
) {
const cachedModule = moduleCache.get(moduleName, containingFile);
if (cachedModule) {
return {
resolvedModule: cachedModule
};
}

const resolvedModule = resolveSvelteModuleName(moduleName, containingFile, options);
moduleCache.set(moduleName, containingFile, resolvedModule);
return {
resolvedModule: resolvedModule
};
}
}