From 2408ba4fc2b7e00c973db50ee4d463be8a5e1f34 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Fri, 24 Nov 2023 15:20:46 +0000 Subject: [PATCH] Improve virtual TS host and resolution --- src/__tests__/tsHarness/resolutionUtils.ts | 21 +++++++++- src/__tests__/tsHarness/typeCheckerHost.ts | 10 ++--- src/__tests__/tsHarness/virtualHost.ts | 46 +++++++++++++++------- 3 files changed, 56 insertions(+), 21 deletions(-) diff --git a/src/__tests__/tsHarness/resolutionUtils.ts b/src/__tests__/tsHarness/resolutionUtils.ts index 65bce91c..2d597d36 100644 --- a/src/__tests__/tsHarness/resolutionUtils.ts +++ b/src/__tests__/tsHarness/resolutionUtils.ts @@ -2,7 +2,6 @@ import { CompilerHost, StringLiteralLike, StringLiteral, - ScriptTarget, Identifier, SourceFile, Statement, @@ -15,6 +14,8 @@ import { Mutable, Node, ResolutionMode, + JSDocParsingMode, + CreateSourceFileOptions, walkUpParenthesizedExpressions, isImportEqualsDeclaration, toPath, @@ -41,8 +42,12 @@ import { isAmbientModule, getTextOfIdentifierOrLiteral, hasSyntacticModifier, + getEmitScriptTarget, + getSetExternalModuleIndicator, } from '@0no-co/typescript.js'; +import { compilerOptions } from './virtualHost'; + export function getModuleNames({ imports, moduleAugmentations }: SourceFile): StringLiteralLike[] { const res = [...imports]; for (const aug of moduleAugmentations) @@ -50,8 +55,20 @@ export function getModuleNames({ imports, moduleAugmentations }: SourceFile): St return res; } +function getCreateSourceFileOptions(): CreateSourceFileOptions { + const languageVersion = getEmitScriptTarget(compilerOptions); + const setExternalModuleIndicator = getSetExternalModuleIndicator(compilerOptions); + const jsDocParsingMode = 0 satisfies JSDocParsingMode.ParseAll; + return { + impliedNodeFormat: 99 satisfies ModuleKind.ESNext, + languageVersion, + setExternalModuleIndicator, + jsDocParsingMode, + }; +} + export function findSourceFile(fileName: string, host: CompilerHost): SourceFile | undefined { - const file = host.getSourceFile(fileName, 99 satisfies ScriptTarget.ESNext); + const file = host.getSourceFile(fileName, getCreateSourceFileOptions()); if (file) { const path = toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName); file.fileName = file.originalFileName = fileName; diff --git a/src/__tests__/tsHarness/typeCheckerHost.ts b/src/__tests__/tsHarness/typeCheckerHost.ts index 27a33161..8e19d4fa 100644 --- a/src/__tests__/tsHarness/typeCheckerHost.ts +++ b/src/__tests__/tsHarness/typeCheckerHost.ts @@ -96,16 +96,16 @@ export function createTypeHost(rootFileNames: readonly string[], host: CompilerH } } - for (const rootFileName of rootFileNames) { - const rootFile = getSourceFile(rootFileName); - if (rootFile) rootFiles.push(rootFile); - } - if (!compilerOptions.noLib) { const libFile = getSourceFile(host.getDefaultLibFileName(compilerOptions)); if (libFile) rootFiles.push(libFile); } + for (const rootFileName of rootFileNames) { + const rootFile = getSourceFile(rootFileName); + if (rootFile) rootFiles.push(rootFile); + } + const files = [...importedFiles, ...rootFiles]; const typeHost: TypeHost = { diff --git a/src/__tests__/tsHarness/virtualHost.ts b/src/__tests__/tsHarness/virtualHost.ts index ed4f44d2..74341486 100644 --- a/src/__tests__/tsHarness/virtualHost.ts +++ b/src/__tests__/tsHarness/virtualHost.ts @@ -4,6 +4,7 @@ import { ResolvedModule, CompilerHost, ScriptTarget, + CreateSourceFileOptions, SourceFile, JsxEmit, createModuleResolutionCache, @@ -32,6 +33,8 @@ export const compilerOptions: CompilerOptions = { strict: false, noEmit: true, noLib: false, + disableReferencedProjectLoad: true, + disableSourceOfProjectReferenceRedirect: true, disableSizeLimit: true, disableSolutionSearching: true, }; @@ -56,10 +59,14 @@ class File { } } - toSourceFile(target: ScriptTarget) { + toSourceFile(languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions) { + const target = + typeof languageVersionOrOptions === 'object' + ? languageVersionOrOptions.languageVersion + : languageVersionOrOptions; return ( this.cache[target] || - (this.cache[target] = createSourceFile(this.name, this.toString(), target)) + (this.cache[target] = createSourceFile(this.name, this.toString(), languageVersionOrOptions)) ); } @@ -132,14 +139,18 @@ export function createVirtualHost(files: Files) { } function split(filename: string): string[] { - return filename.split(path.sep).slice(1); + return filename !== path.sep ? filename.split(path.sep).slice(1) : []; } function lookup(filename: string): File | Directory | undefined { const parts = split(normalize(filename)); let directory = root; - for (let i = 0; i < parts.length - 1; i++) directory = directory.dir(parts[i]); - return directory.get(parts[parts.length - 1]); + if (parts.length) { + for (let i = 0; i < parts.length - 1; i++) directory = directory.dir(parts[i]); + return directory.get(parts[parts.length - 1]); + } else { + return directory; + } } for (const key in files) { @@ -155,7 +166,7 @@ export function createVirtualHost(files: Files) { getCanonicalFileName: normalize, getDefaultLibFileName() { // TODO: When another lib with references is selected, the resolution mode doesn't adapt - return '/node_modules/@0no-co/typescript.js/lib/lib.es5.d.ts'; + return normalize('/node_modules/@0no-co/typescript.js/lib/lib.es5.d.ts'); }, getCurrentDirectory() { return path.sep; @@ -163,12 +174,21 @@ export function createVirtualHost(files: Files) { getNewLine() { return '\n'; }, + getModuleResolutionCache() { + return cache; + }, useCaseSensitiveFileNames() { return true; }, + useSourceOfProjectReferenceRedirect() { + return false; + }, fileExists(filename: string) { return lookup(filename) instanceof File; }, + directoryExists(directoryName: string) { + return lookup(directoryName) instanceof Directory; + }, writeFile(filename: string, content: Uint8Array | string) { const name = normalize(filename); @@ -196,10 +216,13 @@ export function createVirtualHost(files: Files) { } }, - getSourceFile(filename: string, target: ScriptTarget) { + getSourceFile( + filename: string, + languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions + ) { const entry = lookup(filename); if (entry instanceof File) { - return entry.toSourceFile(target); + return entry.toSourceFile(languageVersionOrOptions); } }, @@ -211,10 +234,5 @@ export function createVirtualHost(files: Files) { } return resolvedModules; }, - - resolveRootModule(moduleName: string) { - return resolveModuleName(moduleName, '/', compilerOptions, this)?.resolvedModule - ?.resolvedFileName; - }, - }; + } satisfies CompilerHost; }