From 72244c5b03ec13a77088c4b2f016708007655ce0 Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 19 Oct 2018 18:00:45 -0700 Subject: [PATCH] Support 'isSourceFileFromExternalLibrary' for source files from '/// '' (#28004) * Support 'isSourceFileFromExternalLibrary' for source files from '/// '' * Calculate `isExternalLibraryImport` at the end * Calculate isExternalLibraryImport with symlink path --- src/compiler/moduleNameResolver.ts | 27 ++++++++++--------- src/compiler/program.ts | 4 +++ src/compiler/resolutionCache.ts | 2 +- src/compiler/types.ts | 2 ++ .../unittests/programMissingFiles.ts | 23 +++++++++++----- .../reference/api/tsserverlibrary.d.ts | 2 ++ tests/baselines/reference/api/typescript.d.ts | 2 ++ tests/baselines/reference/typingsLookup4.js | 8 ------ 8 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index c09c9542055..99b65c8946e 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -298,14 +298,12 @@ namespace ts { let resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; if (resolved) { - if (!options.preserveSymlinks) { - resolved = { ...resolved, fileName: realPath(resolved.fileName, host, traceEnabled) }; - } - + const { fileName, packageId } = resolved; + const resolvedFileName = options.preserveSymlinks ? fileName : realPath(fileName, host, traceEnabled); if (traceEnabled) { - trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved.fileName, primary); + trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFileName, primary); } - resolvedTypeReferenceDirective = { primary, resolvedFileName: resolved.fileName, packageId: resolved.packageId }; + resolvedTypeReferenceDirective = { primary, resolvedFileName, packageId, isExternalLibraryImport: pathContainsNodeModules(fileName) }; } return { resolvedTypeReferenceDirective, failedLookupLocations }; @@ -316,7 +314,7 @@ namespace ts { if (traceEnabled) { trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", ")); } - return forEach(typeRoots, typeRoot => { + return firstDefined(typeRoots, typeRoot => { const candidate = combinePaths(typeRoot, typeReferenceDirectiveName); const candidateDirectory = getDirectoryPath(candidate); const directoryExists = directoryProbablyExists(candidateDirectory, host); @@ -343,15 +341,16 @@ namespace ts { if (traceEnabled) { trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup); } - let result: SearchResult | undefined; + let result: Resolved | undefined; if (!isExternalModuleNameRelative(typeReferenceDirectiveName)) { - result = loadModuleFromNearestNodeModulesDirectory(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, moduleResolutionState, /*cache*/ undefined, /*redirectedReference*/ undefined); + const searchResult = loadModuleFromNearestNodeModulesDirectory(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, moduleResolutionState, /*cache*/ undefined, /*redirectedReference*/ undefined); + result = searchResult && searchResult.value; } else { const { path: candidate } = normalizePathAndParts(combinePaths(initialLocationForSecondaryLookup, typeReferenceDirectiveName)); - result = toSearchResult(nodeLoadModuleByRelativeName(Extensions.DtsOnly, candidate, /*onlyRecordFailures*/ false, moduleResolutionState, /*considerPackageJson*/ true)); + result = nodeLoadModuleByRelativeName(Extensions.DtsOnly, candidate, /*onlyRecordFailures*/ false, moduleResolutionState, /*considerPackageJson*/ true); } - const resolvedFile = resolvedTypeScriptOnly(result && result.value); + const resolvedFile = resolvedTypeScriptOnly(result); if (!resolvedFile && traceEnabled) { trace(host, Diagnostics.Type_reference_directive_0_was_not_resolved, typeReferenceDirectiveName); } @@ -883,7 +882,7 @@ namespace ts { const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => nodeLoadModuleByRelativeName(extensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ true); const resolved = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loader, state); if (resolved) { - return toSearchResult({ resolved, isExternalLibraryImport: stringContains(resolved.path, nodeModulesPathPart) }); + return toSearchResult({ resolved, isExternalLibraryImport: pathContainsNodeModules(resolved.path) }); } if (!isExternalModuleNameRelative(moduleName)) { @@ -960,6 +959,10 @@ namespace ts { /*@internal*/ export const nodeModulesPathPart = "/node_modules/"; + /*@internal*/ + export function pathContainsNodeModules(path: string): boolean { + return stringContains(path, nodeModulesPathPart); + } /** * This will be called on the successfully resolved path from `loadModuleFromFile`. diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7544b081fcd..3c3c1ff5db2 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2345,6 +2345,8 @@ namespace ts { } let saveResolution = true; if (resolvedTypeReferenceDirective) { + if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth++; + if (resolvedTypeReferenceDirective.primary) { // resolved from the primary path processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd); // TODO: GH#18217 @@ -2373,6 +2375,8 @@ namespace ts { processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd); } } + + if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--; } else { fileProcessingDiagnostics.add(createDiagnostic(refFile!, refPos!, refEnd!, Diagnostics.Cannot_find_type_definition_file_for_0, typeReferenceDirective)); // TODO: GH#18217 diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index b462ab96b1a..9a2e62706b3 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -432,7 +432,7 @@ namespace ts { function getDirectoryToWatchFromFailedLookupLocationDirectory(dir: string, dirPath: Path) { // If directory path contains node module, get the most parent node_modules directory for watching - while (stringContains(dirPath, nodeModulesPathPart)) { + while (pathContainsNodeModules(dirPath)) { dir = getDirectoryPath(dir); dirPath = getDirectoryPath(dirPath); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fe31a30a30f..1c2009bc19f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4942,6 +4942,8 @@ namespace ts { // The location of the .d.ts file we located, or undefined if resolution failed resolvedFileName: string | undefined; packageId?: PackageId; + /** True if `resolvedFileName` comes from `node_modules`. */ + isExternalLibraryImport?: boolean; } export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations { diff --git a/src/testRunner/unittests/programMissingFiles.ts b/src/testRunner/unittests/programMissingFiles.ts index eb68e3bd665..55dfa7f5cc2 100644 --- a/src/testRunner/unittests/programMissingFiles.ts +++ b/src/testRunner/unittests/programMissingFiles.ts @@ -113,12 +113,23 @@ namespace ts { const fs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false, { documents: [a, bar, barFooPackage, barFooIndex, fooPackage, fooIndex], cwd: "/" }); const program = createProgram(["/a.ts"], emptyOptions, new fakes.CompilerHost(fs, { newLine: NewLineKind.LineFeed })); - - for (const file of [a, bar, barFooIndex, fooIndex]) { - const isExternalExpected = file !== a; - const isExternalActual = program.isSourceFileFromExternalLibrary(program.getSourceFile(file.file)!); - assert.equal(isExternalActual, isExternalExpected, `Expected ${file.file} isSourceFileFromExternalLibrary to be ${isExternalExpected}, got ${isExternalActual}`); - } + assertIsExternal(program, [a, bar, barFooIndex, fooIndex], f => f !== a); }); + + it('works on `/// `', () => { + const a = new documents.TextDocument("/a.ts", '/// '); + const fooIndex = new documents.TextDocument("/node_modules/foo/index.d.ts", "declare const foo: number;"); + const fs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false, { documents: [a, fooIndex], cwd: "/" }); + const program = createProgram(["/a.ts"], emptyOptions, new fakes.CompilerHost(fs, { newLine: NewLineKind.LineFeed })); + assertIsExternal(program, [a, fooIndex], f => f !== a); + }); + + function assertIsExternal(program: Program, files: ReadonlyArray, isExternalExpected: (file: documents.TextDocument) => boolean): void { + for (const file of files) { + const actual = program.isSourceFileFromExternalLibrary(program.getSourceFile(file.file)!); + const expected = isExternalExpected(file); + assert.equal(actual, expected, `Expected ${file.file} isSourceFileFromExternalLibrary to be ${expected}, got ${actual}`); + } + } }); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index a7e8d3e5690..1680826b31f 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2670,6 +2670,8 @@ declare namespace ts { primary: boolean; resolvedFileName: string | undefined; packageId?: PackageId; + /** True if `resolvedFileName` comes from `node_modules`. */ + isExternalLibraryImport?: boolean; } interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 0c2485d1a25..a7c3f9cd902 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2670,6 +2670,8 @@ declare namespace ts { primary: boolean; resolvedFileName: string | undefined; packageId?: PackageId; + /** True if `resolvedFileName` comes from `node_modules`. */ + isExternalLibraryImport?: boolean; } interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; diff --git a/tests/baselines/reference/typingsLookup4.js b/tests/baselines/reference/typingsLookup4.js index d2424644d16..a464bb59faf 100644 --- a/tests/baselines/reference/typingsLookup4.js +++ b/tests/baselines/reference/typingsLookup4.js @@ -32,14 +32,6 @@ import { m } from "mquery"; j + k + l + m; -//// [lquery.js] -"use strict"; -exports.__esModule = true; -exports.l = 2; -//// [index.js] -"use strict"; -exports.__esModule = true; -exports.m = 3; //// [a.js] "use strict"; exports.__esModule = true;