From 2c30bd8e9ee8506e35fef7486cf2fdb805763913 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 29 Jun 2022 13:22:14 -0700 Subject: [PATCH 1/3] Test showing wrong resolution is returned because of incorrect mode calculation Test for #48229 --- .../unittests/tscWatch/moduleResolution.ts | 70 ++++ ...esolutions-from-file-are-partially-used.js | 348 ++++++++++++++++++ 2 files changed, 418 insertions(+) create mode 100644 tests/baselines/reference/tscWatch/moduleResolution/module-resolutions-from-file-are-partially-used.js diff --git a/src/testRunner/unittests/tscWatch/moduleResolution.ts b/src/testRunner/unittests/tscWatch/moduleResolution.ts index 5e596d9c9d5c8..faf3e8e709767 100644 --- a/src/testRunner/unittests/tscWatch/moduleResolution.ts +++ b/src/testRunner/unittests/tscWatch/moduleResolution.ts @@ -72,5 +72,75 @@ namespace ts.tscWatch { }, ] }); + + verifyTscWatch({ + scenario: "moduleResolution", + subScenario: "module resolutions from file are partially used", + sys: () => createWatchedSystem([ + { + path: `${projectRoot}/tsconfig.json`, + content: JSON.stringify({ + compilerOptions: { moduleResolution: "node16" }, + }) + }, + { + path: `${projectRoot}/index.ts`, + content: Utils.dedent` + import type { ImportInterface } from "pkg" assert { "resolution-mode": "import" }; + import type { RequireInterface } from "pkg1" assert { "resolution-mode": "require" }; + import {x} from "./a"; + ` + }, + { + path: `${projectRoot}/a.ts`, + content: Utils.dedent` + export const x = 10; + ` + }, + { + path: `${projectRoot}/node_modules/pkg/package.json`, + content: JSON.stringify({ + name: "pkg", + version: "0.0.1", + exports: { + import: "./import.js", + require: "./require.js" + } + }) + }, + { + path: `${projectRoot}/node_modules/pkg/import.d.ts`, + content: `export interface ImportInterface {}` + }, + { + path: `${projectRoot}/node_modules/pkg/require.d.ts`, + content: `export interface RequireInterface {}` + }, + { + path: `${projectRoot}/node_modules/pkg1/package.json`, + content: JSON.stringify({ + name: "pkg1", + version: "0.0.1", + exports: { + import: "./import.js", + require: "./require.js" + } + }) + }, + { + path: `${projectRoot}/node_modules/pkg1/import.d.ts`, + content: `export interface ImportInterface {}` + }, + libFile + ], { currentDirectory: projectRoot }), + commandLineArgs: ["-w", "--traceResolution"], + changes: [ + { + caption: "modify aFile by adding import", + change: sys => sys.appendFile(`${projectRoot}/a.ts`, `import type { ImportInterface } from "pkg" assert { "resolution-mode": "import" }`), + timeouts: sys => sys.runQueuedTimeoutCallbacks(), + } + ] + }); }); } \ No newline at end of file diff --git a/tests/baselines/reference/tscWatch/moduleResolution/module-resolutions-from-file-are-partially-used.js b/tests/baselines/reference/tscWatch/moduleResolution/module-resolutions-from-file-are-partially-used.js new file mode 100644 index 0000000000000..51cb4993096d4 --- /dev/null +++ b/tests/baselines/reference/tscWatch/moduleResolution/module-resolutions-from-file-are-partially-used.js @@ -0,0 +1,348 @@ +Input:: +//// [/user/username/projects/myproject/tsconfig.json] +{"compilerOptions":{"moduleResolution":"node16"}} + +//// [/user/username/projects/myproject/index.ts] +import type { ImportInterface } from "pkg" assert { "resolution-mode": "import" }; +import type { RequireInterface } from "pkg1" assert { "resolution-mode": "require" }; +import {x} from "./a"; + + +//// [/user/username/projects/myproject/a.ts] +export const x = 10; + + +//// [/user/username/projects/myproject/node_modules/pkg/package.json] +{"name":"pkg","version":"0.0.1","exports":{"import":"./import.js","require":"./require.js"}} + +//// [/user/username/projects/myproject/node_modules/pkg/import.d.ts] +export interface ImportInterface {} + +//// [/user/username/projects/myproject/node_modules/pkg/require.d.ts] +export interface RequireInterface {} + +//// [/user/username/projects/myproject/node_modules/pkg1/package.json] +{"name":"pkg1","version":"0.0.1","exports":{"import":"./import.js","require":"./require.js"}} + +//// [/user/username/projects/myproject/node_modules/pkg1/import.d.ts] +export interface ImportInterface {} + +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } + + +/a/lib/tsc.js -w --traceResolution +Output:: +>> Screen clear +[12:00:39 AM] Starting compilation in watch mode... + +File '/user/username/projects/myproject/package.json' does not exist. +File '/user/username/projects/package.json' does not exist. +File '/user/username/package.json' does not exist. +File '/user/package.json' does not exist. +File '/package.json' does not exist. +File '/user/username/projects/myproject/package.json' does not exist. +File '/user/username/projects/package.json' does not exist. +File '/user/username/package.json' does not exist. +File '/user/package.json' does not exist. +File '/package.json' does not exist. +File '/user/username/projects/myproject/package.json' does not exist. +File '/user/username/projects/package.json' does not exist. +File '/user/username/package.json' does not exist. +File '/user/package.json' does not exist. +File '/package.json' does not exist. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +======== Resolving module 'pkg' from '/user/username/projects/myproject/index.ts'. ======== +Explicitly specified module resolution kind: 'Node16'. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +Loading module 'pkg' from 'node_modules' folder, target file type 'TypeScript'. +Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg/package.json'. +'package.json' does not have a 'typesVersions' field. +File name '/user/username/projects/myproject/node_modules/pkg/import.js' has a '.js' extension - stripping it. +File '/user/username/projects/myproject/node_modules/pkg/import.ts' does not exist. +File '/user/username/projects/myproject/node_modules/pkg/import.tsx' does not exist. +File '/user/username/projects/myproject/node_modules/pkg/import.d.ts' exist - use it as a name resolution result. +Resolving real path for '/user/username/projects/myproject/node_modules/pkg/import.d.ts', result '/user/username/projects/myproject/node_modules/pkg/import.d.ts'. +======== Module name 'pkg' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1'. ======== +======== Resolving module 'pkg1' from '/user/username/projects/myproject/index.ts'. ======== +Explicitly specified module resolution kind: 'Node16'. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +Loading module 'pkg1' from 'node_modules' folder, target file type 'TypeScript'. +Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg1/package.json'. +'package.json' does not have a 'typesVersions' field. +File name '/user/username/projects/myproject/node_modules/pkg1/require.js' has a '.js' extension - stripping it. +File '/user/username/projects/myproject/node_modules/pkg1/require.ts' does not exist. +File '/user/username/projects/myproject/node_modules/pkg1/require.tsx' does not exist. +File '/user/username/projects/myproject/node_modules/pkg1/require.d.ts' does not exist. +Directory '/user/username/projects/myproject/node_modules/@types' does not exist, skipping all lookups in it. +Directory '/user/username/projects/node_modules' does not exist, skipping all lookups in it. +Directory '/user/username/node_modules' does not exist, skipping all lookups in it. +Directory '/user/node_modules' does not exist, skipping all lookups in it. +Directory '/node_modules' does not exist, skipping all lookups in it. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +Loading module 'pkg1' from 'node_modules' folder, target file type 'JavaScript'. +File '/user/username/projects/myproject/node_modules/pkg1/package.json' exists according to earlier cached lookups. +File name '/user/username/projects/myproject/node_modules/pkg1/require.js' has a '.js' extension - stripping it. +File '/user/username/projects/myproject/node_modules/pkg1/require.js' does not exist. +File '/user/username/projects/myproject/node_modules/pkg1/require.jsx' does not exist. +Directory '/user/username/projects/node_modules' does not exist, skipping all lookups in it. +Directory '/user/username/node_modules' does not exist, skipping all lookups in it. +Directory '/user/node_modules' does not exist, skipping all lookups in it. +Directory '/node_modules' does not exist, skipping all lookups in it. +======== Module name 'pkg1' was not resolved. ======== +======== Resolving module './a' from '/user/username/projects/myproject/index.ts'. ======== +Explicitly specified module resolution kind: 'Node16'. +Loading module as file / folder, candidate module location '/user/username/projects/myproject/a', target file type 'TypeScript'. +File '/user/username/projects/myproject/a.ts' exist - use it as a name resolution result. +======== Module name './a' was successfully resolved to '/user/username/projects/myproject/a.ts'. ======== +Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg/package.json'. +'package.json' does not have a 'typesVersions' field. +File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups. +File '/a/lib/package.json' does not exist. +File '/a/package.json' does not exist. +File '/package.json' does not exist. +File '/a/lib/package.json' does not exist. +File '/a/package.json' does not exist. +File '/package.json' does not exist according to earlier cached lookups. +index.ts:2:39 - error TS2307: Cannot find module 'pkg1' or its corresponding type declarations. + +2 import type { RequireInterface } from "pkg1" assert { "resolution-mode": "require" }; +   ~~~~~~ + +[12:00:44 AM] Found 1 error. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/a.ts","/user/username/projects/myproject/index.ts"] +Program options: {"moduleResolution":3,"watch":true,"traceResolution":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +Program structureReused: Not +Program files:: +/a/lib/lib.d.ts +/user/username/projects/myproject/a.ts +/user/username/projects/myproject/node_modules/pkg/import.d.ts +/user/username/projects/myproject/index.ts + +Semantic diagnostics in builder refreshed for:: +/a/lib/lib.d.ts +/user/username/projects/myproject/a.ts +/user/username/projects/myproject/node_modules/pkg/import.d.ts +/user/username/projects/myproject/index.ts + +Shape signatures in builder refreshed for:: +/a/lib/lib.d.ts (used version) +/user/username/projects/myproject/a.ts (used version) +/user/username/projects/myproject/node_modules/pkg/import.d.ts (used version) +/user/username/projects/myproject/index.ts (used version) + +WatchedFiles:: +/user/username/projects/myproject/tsconfig.json: + {"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250} +/user/username/projects/myproject/a.ts: + {"fileName":"/user/username/projects/myproject/a.ts","pollingInterval":250} +/user/username/projects/myproject/index.ts: + {"fileName":"/user/username/projects/myproject/index.ts","pollingInterval":250} +/user/username/projects/myproject/node_modules/pkg/import.d.ts: + {"fileName":"/user/username/projects/myproject/node_modules/pkg/import.d.ts","pollingInterval":250} +/a/lib/lib.d.ts: + {"fileName":"/a/lib/lib.d.ts","pollingInterval":250} +/user/username/projects/myproject/node_modules/pkg/package.json: + {"fileName":"/user/username/projects/myproject/node_modules/pkg/package.json","pollingInterval":250} +/user/username/projects/myproject/node_modules/pkg1/package.json: + {"fileName":"/user/username/projects/myproject/node_modules/pkg1/package.json","pollingInterval":250} +/user/username/projects/myproject/node_modules/@types: + {"fileName":"/user/username/projects/myproject/node_modules/@types","pollingInterval":500} + +FsWatches:: + +FsWatchesRecursive:: +/user/username/projects/myproject/node_modules: + {"directoryName":"/user/username/projects/myproject/node_modules"} +/user/username/projects/myproject: + {"directoryName":"/user/username/projects/myproject"} + +exitCode:: ExitStatus.undefined + +//// [/user/username/projects/myproject/a.js] +"use strict"; +exports.__esModule = true; +exports.x = void 0; +exports.x = 10; + + +//// [/user/username/projects/myproject/index.js] +"use strict"; +exports.__esModule = true; + + + +Change:: modify aFile by adding import + +Input:: +//// [/user/username/projects/myproject/a.ts] +export const x = 10; +import type { ImportInterface } from "pkg" assert { "resolution-mode": "import" } + + +Output:: +>> Screen clear +[12:00:47 AM] File change detected. Starting incremental compilation... + +File '/a/lib/package.json' does not exist. +File '/a/package.json' does not exist. +File '/package.json' does not exist. +File '/user/username/projects/myproject/package.json' does not exist. +File '/user/username/projects/package.json' does not exist. +File '/user/username/package.json' does not exist. +File '/user/package.json' does not exist. +File '/package.json' does not exist. +File '/user/username/projects/myproject/package.json' does not exist. +File '/user/username/projects/package.json' does not exist. +File '/user/username/package.json' does not exist. +File '/user/package.json' does not exist. +File '/package.json' does not exist. +Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg/package.json'. +'package.json' does not have a 'typesVersions' field. +File '/user/username/projects/myproject/package.json' does not exist. +File '/user/username/projects/package.json' does not exist. +File '/user/username/package.json' does not exist. +File '/user/package.json' does not exist. +File '/package.json' does not exist. +File '/user/username/projects/myproject/package.json' does not exist. +File '/user/username/projects/package.json' does not exist. +File '/user/username/package.json' does not exist. +File '/user/package.json' does not exist. +File '/package.json' does not exist. +======== Resolving module 'pkg' from '/user/username/projects/myproject/a.ts'. ======== +Explicitly specified module resolution kind: 'Node16'. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +Loading module 'pkg' from 'node_modules' folder, target file type 'TypeScript'. +Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg/package.json'. +'package.json' does not have a 'typesVersions' field. +File name '/user/username/projects/myproject/node_modules/pkg/import.js' has a '.js' extension - stripping it. +File '/user/username/projects/myproject/node_modules/pkg/import.ts' does not exist. +File '/user/username/projects/myproject/node_modules/pkg/import.tsx' does not exist. +File '/user/username/projects/myproject/node_modules/pkg/import.d.ts' exist - use it as a name resolution result. +Resolving real path for '/user/username/projects/myproject/node_modules/pkg/import.d.ts', result '/user/username/projects/myproject/node_modules/pkg/import.d.ts'. +======== Module name 'pkg' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1'. ======== +Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg/package.json'. +'package.json' does not have a 'typesVersions' field. +File '/user/username/projects/myproject/package.json' does not exist. +File '/user/username/projects/package.json' does not exist. +File '/user/username/package.json' does not exist. +File '/user/package.json' does not exist. +File '/package.json' does not exist. +Reusing resolution of module 'pkg' from '/user/username/projects/myproject/index.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1'. +Reusing resolution of module './a' from '/user/username/projects/myproject/index.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/a.ts'. +======== Resolving module 'pkg1' from '/user/username/projects/myproject/index.ts'. ======== +Explicitly specified module resolution kind: 'Node16'. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +Loading module 'pkg1' from 'node_modules' folder, target file type 'TypeScript'. +Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg1/package.json'. +'package.json' does not have a 'typesVersions' field. +File name '/user/username/projects/myproject/node_modules/pkg1/import.js' has a '.js' extension - stripping it. +File '/user/username/projects/myproject/node_modules/pkg1/import.ts' does not exist. +File '/user/username/projects/myproject/node_modules/pkg1/import.tsx' does not exist. +File '/user/username/projects/myproject/node_modules/pkg1/import.d.ts' exist - use it as a name resolution result. +Resolving real path for '/user/username/projects/myproject/node_modules/pkg1/import.d.ts', result '/user/username/projects/myproject/node_modules/pkg1/import.d.ts'. +======== Module name 'pkg1' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg1/import.d.ts' with Package ID 'pkg1/import.d.ts@0.0.1'. ======== +Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg1/package.json'. +'package.json' does not have a 'typesVersions' field. +File '/user/username/projects/myproject/node_modules/pkg1/package.json' exists according to earlier cached lookups. +File '/a/lib/package.json' does not exist. +File '/a/package.json' does not exist. +File '/package.json' does not exist. +index.ts:2:15 - error TS2305: Module '"pkg1"' has no exported member 'RequireInterface'. + +2 import type { RequireInterface } from "pkg1" assert { "resolution-mode": "require" }; +   ~~~~~~~~~~~~~~~~ + +[12:00:54 AM] Found 1 error. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/a.ts","/user/username/projects/myproject/index.ts"] +Program options: {"moduleResolution":3,"watch":true,"traceResolution":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +Program structureReused: SafeModules +Program files:: +/a/lib/lib.d.ts +/user/username/projects/myproject/node_modules/pkg/import.d.ts +/user/username/projects/myproject/a.ts +/user/username/projects/myproject/node_modules/pkg1/import.d.ts +/user/username/projects/myproject/index.ts + +Semantic diagnostics in builder refreshed for:: +/user/username/projects/myproject/a.ts +/user/username/projects/myproject/node_modules/pkg1/import.d.ts +/user/username/projects/myproject/index.ts + +Shape signatures in builder refreshed for:: +/user/username/projects/myproject/a.ts (computed .d.ts) +/user/username/projects/myproject/index.ts (computed .d.ts) +/user/username/projects/myproject/node_modules/pkg1/import.d.ts (used version) + +WatchedFiles:: +/user/username/projects/myproject/tsconfig.json: + {"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250} +/user/username/projects/myproject/a.ts: + {"fileName":"/user/username/projects/myproject/a.ts","pollingInterval":250} +/user/username/projects/myproject/index.ts: + {"fileName":"/user/username/projects/myproject/index.ts","pollingInterval":250} +/user/username/projects/myproject/node_modules/pkg/import.d.ts: + {"fileName":"/user/username/projects/myproject/node_modules/pkg/import.d.ts","pollingInterval":250} +/a/lib/lib.d.ts: + {"fileName":"/a/lib/lib.d.ts","pollingInterval":250} +/user/username/projects/myproject/node_modules/pkg/package.json: + {"fileName":"/user/username/projects/myproject/node_modules/pkg/package.json","pollingInterval":250} +/user/username/projects/myproject/node_modules/pkg1/package.json: + {"fileName":"/user/username/projects/myproject/node_modules/pkg1/package.json","pollingInterval":250} +/user/username/projects/myproject/node_modules/@types: + {"fileName":"/user/username/projects/myproject/node_modules/@types","pollingInterval":500} +/user/username/projects/myproject/node_modules/pkg1/import.d.ts: + {"fileName":"/user/username/projects/myproject/node_modules/pkg1/import.d.ts","pollingInterval":250} + +FsWatches:: + +FsWatchesRecursive:: +/user/username/projects/myproject/node_modules: + {"directoryName":"/user/username/projects/myproject/node_modules"} +/user/username/projects/myproject: + {"directoryName":"/user/username/projects/myproject"} + +exitCode:: ExitStatus.undefined + +//// [/user/username/projects/myproject/a.js] file written with same contents +//// [/user/username/projects/myproject/index.js] file written with same contents From deaa5a42ebfcbce11ef04875f6b07b19def45e14 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 29 Jun 2022 14:54:27 -0700 Subject: [PATCH 2/3] Pass in information for the module name resolution when resolutions from file are partially used Fixes #48229 --- src/compiler/moduleNameResolver.ts | 7 ++- src/compiler/program.ts | 58 ++++++++++++------- src/compiler/resolutionCache.ts | 41 ++++++++----- src/compiler/types.ts | 7 ++- src/compiler/watchPublic.ts | 4 +- src/server/project.ts | 4 +- src/services/types.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 15 +++-- tests/baselines/reference/api/typescript.d.ts | 13 ++++- ...esolutions-from-file-are-partially-used.js | 29 +--------- 10 files changed, 105 insertions(+), 75 deletions(-) diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index be1a09548fa6a..8ea1953cafb34 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -737,8 +737,9 @@ namespace ts { /* @internal */ export function createModeAwareCache(): ModeAwareCache { - const underlying = new Map(); - const memoizedReverseKeys = new Map(); + const underlying = new Map(); + type ModeAwareCacheKey = string & { __modeAwareCacheKey: any; }; + const memoizedReverseKeys = new Map(); const cache: ModeAwareCache = { get(specifier, mode) { @@ -768,7 +769,7 @@ namespace ts { return cache; function getUnderlyingCacheKey(specifier: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined) { - const result = mode === undefined ? specifier : `${mode}|${specifier}`; + const result = (mode === undefined ? specifier : `${mode}|${specifier}`) as ModeAwareCacheKey; memoizedReverseKeys.set(result, [specifier, mode]); return result; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index d241a9add2624..1fc874d439da4 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1060,24 +1060,39 @@ namespace ts { let moduleResolutionCache: ModuleResolutionCache | undefined; let typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined; - let actualResolveModuleNamesWorker: (moduleNames: string[], containingFile: SourceFile, containingFileName: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[]; + let actualResolveModuleNamesWorker: ( + moduleNames: string[], + containingFile: SourceFile, + containingFileName: string, + redirectedReference: ResolvedProjectReference | undefined, + partialResolutionInfo: PartialResolutionInfo | undefined, + ) => ResolvedModuleFull[]; const hasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse; if (host.resolveModuleNames) { - actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, reusedNames, redirectedReference) => host.resolveModuleNames!(Debug.checkEachDefined(moduleNames), containingFileName, reusedNames, redirectedReference, options, containingFile).map(resolved => { - // An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName. - if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) { - return resolved as ResolvedModuleFull; - } - const withExtension = clone(resolved) as ResolvedModuleFull; - withExtension.extension = extensionFromPath(resolved.resolvedFileName); - return withExtension; - }); + actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, redirectedReference, partialResolutionInfo) => + host.resolveModuleNames!( + Debug.checkEachDefined(moduleNames), + containingFileName, + partialResolutionInfo?.reusedNames?.map(({ name }) => name), + redirectedReference, + options, + containingFile, + partialResolutionInfo + ).map(resolved => { + // An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName. + if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) { + return resolved as ResolvedModuleFull; + } + const withExtension = clone(resolved) as ResolvedModuleFull; + withExtension.extension = extensionFromPath(resolved.resolvedFileName); + return withExtension; + }); moduleResolutionCache = host.getModuleResolutionCache?.(); } else { moduleResolutionCache = createModuleResolutionCache(currentDirectory, getCanonicalFileName, options); const loader = (moduleName: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFileName, options, host, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule!; // TODO: GH#18217 - actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, _reusedNames, redirectedReference) => loadWithModeAwareCache(Debug.checkEachDefined(moduleNames), containingFile, containingFileName, redirectedReference, loader); + actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, redirectedReference) => loadWithModeAwareCache(Debug.checkEachDefined(moduleNames), containingFile, containingFileName, redirectedReference, loader); } let actualResolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined) => (ResolvedTypeReferenceDirective | undefined)[]; @@ -1383,13 +1398,13 @@ namespace ts { } } - function resolveModuleNamesWorker(moduleNames: string[], containingFile: SourceFile, reusedNames: string[] | undefined): readonly ResolvedModuleFull[] { + function resolveModuleNamesWorker(moduleNames: string[], containingFile: SourceFile, partialResolutionInfo: PartialResolutionInfo | undefined): readonly ResolvedModuleFull[] { if (!moduleNames.length) return emptyArray; const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory); const redirectedReference = getRedirectReferenceForResolution(containingFile); tracing?.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName }); performance.mark("beforeResolveModule"); - const result = actualResolveModuleNamesWorker(moduleNames, containingFile, containingFileName, reusedNames, redirectedReference); + const result = actualResolveModuleNamesWorker(moduleNames, containingFile, containingFileName, redirectedReference, partialResolutionInfo); performance.mark("afterResolveModule"); performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule"); tracing?.pop(); @@ -1496,7 +1511,7 @@ namespace ts { if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) { // If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules, // the best we can do is fallback to the default logic. - return resolveModuleNamesWorker(moduleNames, file, /*reusedNames*/ undefined); + return resolveModuleNamesWorker(moduleNames, file, /*partialResolutionInfo*/ undefined); } const oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName); @@ -1525,6 +1540,7 @@ namespace ts { /** An ordered list of module names for which we cannot recover the resolution. */ let unknownModuleNames: string[] | undefined; + let unknownModuleNamesIndex: number[] | undefined; /** * The indexing of elements in this list matches that of `moduleNames`. * @@ -1535,7 +1551,7 @@ namespace ts { * * ResolvedModuleFull instance: can be reused. */ let result: ResolvedModuleFull[] | undefined; - let reusedNames: string[] | undefined; + let reusedNames: { name: string; mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined; }[] | undefined; /** A transient placeholder used to mark predicted resolution in the result list. */ const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = {} as any; @@ -1543,7 +1559,8 @@ namespace ts { const moduleName = moduleNames[i]; // If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions if (file === oldSourceFile && !hasInvalidatedResolution(oldSourceFile.path)) { - const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName, getModeForResolutionAtIndex(oldSourceFile, i)); + const mode = getModeForResolutionAtIndex(oldSourceFile, i); + const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName, mode); if (oldResolvedModule) { if (isTraceEnabled(options, host)) { trace(host, @@ -1556,8 +1573,8 @@ namespace ts { oldResolvedModule.packageId && packageIdToString(oldResolvedModule.packageId) ); } - (result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule; - (reusedNames || (reusedNames = [])).push(moduleName); + (result ??= new Array(moduleNames.length))[i] = oldResolvedModule; + (reusedNames ??= []).push({ name: moduleName, mode }); continue; } } @@ -1581,12 +1598,13 @@ namespace ts { } else { // Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result. - (unknownModuleNames || (unknownModuleNames = [])).push(moduleName); + (unknownModuleNames ??= []).push(moduleName); + (unknownModuleNamesIndex ??= []).push(i); } } const resolutions = unknownModuleNames && unknownModuleNames.length - ? resolveModuleNamesWorker(unknownModuleNames, file, reusedNames) + ? resolveModuleNamesWorker(unknownModuleNames, file, { reusedNames, namesIndex: unknownModuleNamesIndex! }) : emptyArray; // Combine results of resolutions and predicted results diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index efc0b4c95a11c..b3d1ad915d500 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -5,7 +5,13 @@ namespace ts { startRecordingFilesWithChangedResolutions(): void; finishRecordingFilesWithChangedResolutions(): Path[] | undefined; - resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[]; + resolveModuleNames( + moduleNames: string[], + containingFile: string, + redirectedReference: ResolvedProjectReference | undefined, + containingSourceFile: SourceFile | undefined, + partialResolutionInfo: PartialResolutionInfo | undefined + ): (ResolvedModuleFull | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): CachedResolvedModuleWithFailedLookupLocations | undefined; resolveTypeReferenceDirectives(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"]): (ResolvedTypeReferenceDirective | undefined)[]; @@ -384,7 +390,7 @@ namespace ts { loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext | undefined) => T; getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName; shouldRetryResolution: (t: T) => boolean; - reusedNames?: readonly string[]; + partialResolutionInfo?: PartialResolutionInfo; logChanges?: boolean; containingSourceFile?: SourceFile; containingSourceFileMode?: SourceFile["impliedNodeFormat"]; @@ -393,7 +399,7 @@ namespace ts { names, containingFile, redirectedReference, cache, perDirectoryCacheWithRedirects, loader, getResolutionWithResolvedFileName, - shouldRetryResolution, reusedNames, logChanges, containingSourceFile, containingSourceFileMode + shouldRetryResolution, partialResolutionInfo, logChanges, containingSourceFile, containingSourceFileMode }: ResolveNamesWithLocalCacheInput): (R | undefined)[] { const path = resolutionHost.toPath(containingFile); const resolutionsInFile = cache.get(path) || cache.set(path, createModeAwareCache()).get(path)!; @@ -425,7 +431,7 @@ namespace ts { // Type references instead supply a `containingSourceFileMode` and a non-string entry which contains // a default file mode override if applicable. const mode = !isString(entry) ? getModeForFileReference(entry, containingSourceFileMode) : - containingSourceFile ? getModeForResolutionAtIndex(containingSourceFile, i) : undefined; + containingSourceFile ? getModeForResolutionAtIndex(containingSourceFile, partialResolutionInfo?.namesIndex[i] ?? i) : undefined; i++; let resolution = resolutionsInFile.get(name, mode); // Resolution is valid if it is present and not invalidated @@ -509,13 +515,16 @@ namespace ts { resolvedModules.push(getResolutionWithResolvedFileName(resolution)); } - // Stop watching and remove the unused name - resolutionsInFile.forEach((resolution, name, mode) => { - if (!seenNamesInFile.has(name, mode) && !contains(reusedNames, name)) { - stopWatchFailedLookupLocationOfResolution(resolution, path, getResolutionWithResolvedFileName); - resolutionsInFile.delete(name, mode); - } - }); + partialResolutionInfo?.reusedNames?.forEach(({ name, mode }) => seenNamesInFile.set(name, mode, true)); + if (resolutionsInFile.size() !== seenNamesInFile.size()) { + // Stop watching and remove the unused name + resolutionsInFile.forEach((resolution, name, mode) => { + if (!seenNamesInFile.has(name, mode)) { + stopWatchFailedLookupLocationOfResolution(resolution, path, getResolutionWithResolvedFileName); + resolutionsInFile.delete(name, mode); + } + }); + } return resolvedModules; @@ -552,7 +561,13 @@ namespace ts { }); } - function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[] { + function resolveModuleNames( + moduleNames: string[], + containingFile: string, + redirectedReference?: ResolvedProjectReference, + containingSourceFile?: SourceFile, + partialResolutionInfo?: PartialResolutionInfo + ): (ResolvedModuleFull | undefined)[] { return resolveNamesWithLocalCache({ names: moduleNames, containingFile, @@ -562,7 +577,7 @@ namespace ts { loader: resolveModuleName, getResolutionWithResolvedFileName: getResolvedModule, shouldRetryResolution: resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension), - reusedNames, + partialResolutionInfo, logChanges: logChangesWhenResolvingModule, containingSourceFile, }); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1364aff8d5e4c..4d9dc2f1bb7cb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6936,6 +6936,11 @@ namespace ts { /* @internal */ export type HasChangedAutomaticTypeDirectiveNames = () => boolean; + export interface PartialResolutionInfo { + reusedNames: { name: string; mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined; }[] | undefined; + namesIndex: readonly number[]; + } + export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; @@ -6956,7 +6961,7 @@ namespace ts { * If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just * 'throw new Error("NotImplemented")' */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; /** * Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it */ diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index afc4245537c89..f04713105e782 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -112,7 +112,7 @@ namespace ts { getEnvironmentVariable?(name: string): string | undefined; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; } @@ -363,7 +363,7 @@ namespace ts { // Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names compilerHost.resolveModuleNames = host.resolveModuleNames ? ((...args) => host.resolveModuleNames!(...args)) : - ((moduleNames, containingFile, reusedNames, redirectedReference, _options, sourceFile) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, sourceFile)); + ((moduleNames, containingFile, _reusedNames, redirectedReference, _options, sourceFile, partialResolutionInfo) => resolutionCache.resolveModuleNames(moduleNames, containingFile, redirectedReference, sourceFile, partialResolutionInfo)); compilerHost.resolveTypeReferenceDirectives = host.resolveTypeReferenceDirectives ? ((...args) => host.resolveTypeReferenceDirectives!(...args)) : ((typeDirectiveNames, containingFile, redirectedReference, _options, containingFileMode) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference, containingFileMode)); diff --git a/src/server/project.ts b/src/server/project.ts index 3ee83ea4c8cd0..a34c218e347e1 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -505,8 +505,8 @@ namespace ts.server { return !this.isWatchedMissingFile(path) && this.directoryStructureHost.fileExists(file); } - resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[] { - return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, containingSourceFile); + resolveModuleNames(moduleNames: string[], containingFile: string, _reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModuleFull | undefined)[] { + return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, redirectedReference, containingSourceFile, partialResolutionInfo); } getModuleResolutionCache(): ModuleResolutionCache | undefined { diff --git a/src/services/types.ts b/src/services/types.ts index 27c7616bf34b2..c080e05b72c2c 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -284,7 +284,7 @@ namespace ts { * * If this is implemented, `getResolvedModuleWithFailedLookupLocationsFromCache` should be too. */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; /* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index fb15ec4f0113b..5315220029f02 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3275,6 +3275,13 @@ declare namespace ts { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; readonly failedLookupLocations: string[]; } + export interface PartialResolutionInfo { + reusedNames: { + name: string; + mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + }[] | undefined; + namesIndex: readonly number[]; + } export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; @@ -3287,7 +3294,7 @@ declare namespace ts { useCaseSensitiveFileNames(): boolean; getNewLine(): string; readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[]; - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; /** * Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it */ @@ -5402,7 +5409,7 @@ declare namespace ts { /** If provided is used to get the environment variable */ getEnvironmentVariable?(name: string): string | undefined; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; } @@ -5798,7 +5805,7 @@ declare namespace ts { readFile(path: string, encoding?: string): string | undefined; fileExists(path: string): boolean; getTypeRootsVersion?(): number; - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; getDirectories?(directoryName: string): string[]; @@ -10079,7 +10086,7 @@ declare namespace ts.server { readFile(fileName: string): string | undefined; writeFile(fileName: string, content: string): void; fileExists(file: string): boolean; - resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[]; + resolveModuleNames(moduleNames: string[], containingFile: string, _reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModuleFull | undefined)[]; getModuleResolutionCache(): ModuleResolutionCache | undefined; getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; resolveTypeReferenceDirectives(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index ed87f7ef8913b..462c746c38abe 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3275,6 +3275,13 @@ declare namespace ts { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; readonly failedLookupLocations: string[]; } + export interface PartialResolutionInfo { + reusedNames: { + name: string; + mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + }[] | undefined; + namesIndex: readonly number[]; + } export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; @@ -3287,7 +3294,7 @@ declare namespace ts { useCaseSensitiveFileNames(): boolean; getNewLine(): string; readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[]; - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; /** * Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it */ @@ -5402,7 +5409,7 @@ declare namespace ts { /** If provided is used to get the environment variable */ getEnvironmentVariable?(name: string): string | undefined; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; } @@ -5798,7 +5805,7 @@ declare namespace ts { readFile(path: string, encoding?: string): string | undefined; fileExists(path: string): boolean; getTypeRootsVersion?(): number; - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; getDirectories?(directoryName: string): string[]; diff --git a/tests/baselines/reference/tscWatch/moduleResolution/module-resolutions-from-file-are-partially-used.js b/tests/baselines/reference/tscWatch/moduleResolution/module-resolutions-from-file-are-partially-used.js index 51cb4993096d4..efdcecadda95d 100644 --- a/tests/baselines/reference/tscWatch/moduleResolution/module-resolutions-from-file-are-partially-used.js +++ b/tests/baselines/reference/tscWatch/moduleResolution/module-resolutions-from-file-are-partially-used.js @@ -263,32 +263,14 @@ File '/user/package.json' does not exist. File '/package.json' does not exist. Reusing resolution of module 'pkg' from '/user/username/projects/myproject/index.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1'. Reusing resolution of module './a' from '/user/username/projects/myproject/index.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/a.ts'. -======== Resolving module 'pkg1' from '/user/username/projects/myproject/index.ts'. ======== -Explicitly specified module resolution kind: 'Node16'. -File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. -File '/user/username/projects/package.json' does not exist according to earlier cached lookups. -File '/user/username/package.json' does not exist according to earlier cached lookups. -File '/user/package.json' does not exist according to earlier cached lookups. -File '/package.json' does not exist according to earlier cached lookups. -Loading module 'pkg1' from 'node_modules' folder, target file type 'TypeScript'. -Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg1/package.json'. -'package.json' does not have a 'typesVersions' field. -File name '/user/username/projects/myproject/node_modules/pkg1/import.js' has a '.js' extension - stripping it. -File '/user/username/projects/myproject/node_modules/pkg1/import.ts' does not exist. -File '/user/username/projects/myproject/node_modules/pkg1/import.tsx' does not exist. -File '/user/username/projects/myproject/node_modules/pkg1/import.d.ts' exist - use it as a name resolution result. -Resolving real path for '/user/username/projects/myproject/node_modules/pkg1/import.d.ts', result '/user/username/projects/myproject/node_modules/pkg1/import.d.ts'. -======== Module name 'pkg1' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg1/import.d.ts' with Package ID 'pkg1/import.d.ts@0.0.1'. ======== -Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg1/package.json'. -'package.json' does not have a 'typesVersions' field. -File '/user/username/projects/myproject/node_modules/pkg1/package.json' exists according to earlier cached lookups. +Reusing resolution of module 'pkg1' from '/user/username/projects/myproject/index.ts' of old program, it was not resolved. File '/a/lib/package.json' does not exist. File '/a/package.json' does not exist. File '/package.json' does not exist. -index.ts:2:15 - error TS2305: Module '"pkg1"' has no exported member 'RequireInterface'. +index.ts:2:39 - error TS2307: Cannot find module 'pkg1' or its corresponding type declarations. 2 import type { RequireInterface } from "pkg1" assert { "resolution-mode": "require" }; -   ~~~~~~~~~~~~~~~~ +   ~~~~~~ [12:00:54 AM] Found 1 error. Watching for file changes. @@ -301,18 +283,15 @@ Program files:: /a/lib/lib.d.ts /user/username/projects/myproject/node_modules/pkg/import.d.ts /user/username/projects/myproject/a.ts -/user/username/projects/myproject/node_modules/pkg1/import.d.ts /user/username/projects/myproject/index.ts Semantic diagnostics in builder refreshed for:: /user/username/projects/myproject/a.ts -/user/username/projects/myproject/node_modules/pkg1/import.d.ts /user/username/projects/myproject/index.ts Shape signatures in builder refreshed for:: /user/username/projects/myproject/a.ts (computed .d.ts) /user/username/projects/myproject/index.ts (computed .d.ts) -/user/username/projects/myproject/node_modules/pkg1/import.d.ts (used version) WatchedFiles:: /user/username/projects/myproject/tsconfig.json: @@ -331,8 +310,6 @@ WatchedFiles:: {"fileName":"/user/username/projects/myproject/node_modules/pkg1/package.json","pollingInterval":250} /user/username/projects/myproject/node_modules/@types: {"fileName":"/user/username/projects/myproject/node_modules/@types","pollingInterval":500} -/user/username/projects/myproject/node_modules/pkg1/import.d.ts: - {"fileName":"/user/username/projects/myproject/node_modules/pkg1/import.d.ts","pollingInterval":250} FsWatches:: From 326d4380e40ac9f3d44ff76730f0a6dba17fe4ec Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 31 Oct 2022 11:53:16 -0700 Subject: [PATCH 3/3] Make the resolution info complete --- src/compiler/moduleNameResolver.ts | 18 ++-- src/compiler/program.ts | 89 +++++++++---------- src/compiler/resolutionCache.ts | 34 ++++--- src/compiler/tsbuildPublic.ts | 4 +- src/compiler/types.ts | 8 +- src/compiler/utilities.ts | 9 +- src/compiler/utilitiesPublic.ts | 4 +- src/compiler/watchPublic.ts | 4 +- src/server/project.ts | 4 +- src/services/types.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 19 ++-- tests/baselines/reference/api/typescript.d.ts | 17 ++-- 12 files changed, 110 insertions(+), 102 deletions(-) diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index a4f79ce9945ac..100e399004da7 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -773,15 +773,23 @@ namespace ts { } /* @internal */ - export function zipToModeAwareCache(file: SourceFile, keys: readonly string[] | readonly FileReference[], values: readonly V[]): ModeAwareCache { + export function getResolutionName(entry: FileReference | StringLiteralLike) { + // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. + return isStringLiteralLike(entry) ? entry.text : entry.fileName.toLowerCase(); + } + + /* @internal */ + export function getResolutionMode(entry: FileReference | StringLiteralLike, file: SourceFile) { + return isStringLiteralLike(entry) ? getModeForUsageLocation(file, entry) : entry.resolutionMode || file.impliedNodeFormat; + } + + /* @internal */ + export function zipToModeAwareCache(file: SourceFile, keys: readonly StringLiteralLike[] | readonly FileReference[], values: readonly V[]): ModeAwareCache { Debug.assert(keys.length === values.length); const map = createModeAwareCache(); for (let i = 0; i < keys.length; ++i) { const entry = keys[i]; - // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. - const name = !isString(entry) ? entry.fileName.toLowerCase() : entry; - const mode = !isString(entry) ? entry.resolutionMode || file.impliedNodeFormat : getModeForResolutionAtIndex(file, i); - map.set(name, mode, values[i]); + map.set(getResolutionName(entry), getResolutionMode(entry, file), values[i]); } return map; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7fe3f4225899b..c84b37c169469 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -604,17 +604,20 @@ namespace ts { } /* @internal */ - export function loadWithModeAwareCache(names: string[], containingFile: SourceFile, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined, partialResolutionInfo: PartialResolutionInfo | undefined, loader: (name: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] { + export function loadWithModeAwareCache(names: readonly StringLiteralLike[] | readonly string[], containingFile: SourceFile, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined, resolutionInfo: ModuleResolutionInfo | undefined, loader: (name: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] { if (names.length === 0) { return []; } const resolutions: T[] = []; const cache = new Map(); let i = 0; - for (const name of names) { + for (const entry of resolutionInfo ? resolutionInfo.names : names) { let result: T; - const mode = getModeForResolutionAtIndex(containingFile, partialResolutionInfo?.namesIndex[i] ?? i); + const mode = !isString(entry) ? + getModeForUsageLocation(containingFile, entry) : + getModeForResolutionAtIndex(containingFile, i); i++; + const name = isString(entry) ? entry : entry.text; const cacheKey = mode !== undefined ? `${mode}|${name}` : name; if (cache.has(cacheKey)) { result = cache.get(cacheKey)!; @@ -1080,23 +1083,23 @@ namespace ts { let moduleResolutionCache: ModuleResolutionCache | undefined; let typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined; let actualResolveModuleNamesWorker: ( - moduleNames: string[], + moduleNames: readonly StringLiteralLike[], containingFile: SourceFile, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined, - partialResolutionInfo: PartialResolutionInfo | undefined, + resolutionInfo: ModuleResolutionInfo | undefined, ) => (ResolvedModuleFull | undefined)[]; const hasInvalidatedResolutions = host.hasInvalidatedResolutions || returnFalse; if (host.resolveModuleNames) { - actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, redirectedReference, partialResolutionInfo) => + actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo) => host.resolveModuleNames!( - Debug.checkEachDefined(moduleNames), + moduleNames.map(literal => literal.text), containingFileName, - partialResolutionInfo?.reusedNames?.map(({ name }) => name), + resolutionInfo?.reusedNames?.map(literal => literal.text), redirectedReference, options, containingFile, - partialResolutionInfo + resolutionInfo, ).map(resolved => { // An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName. if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) { @@ -1112,8 +1115,8 @@ namespace ts { moduleResolutionCache = createModuleResolutionCache(currentDirectory, getCanonicalFileName, options); const loader = (moduleName: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFileName, options, host, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule; - actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, redirectedReference, partialResolutionInfo) => - loadWithModeAwareCache(Debug.checkEachDefined(moduleNames), containingFile, containingFileName, redirectedReference, partialResolutionInfo, loader); + actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo) => + loadWithModeAwareCache(moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo, loader); } let actualResolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined) => (ResolvedTypeReferenceDirective | undefined)[]; @@ -1396,18 +1399,15 @@ namespace ts { } } - function pullDiagnosticsFromCache(names: string[] | readonly FileReference[], containingFile: SourceFile) { + function pullDiagnosticsFromCache(names: readonly StringLiteralLike[] | readonly FileReference[], containingFile: SourceFile) { if (!moduleResolutionCache) return; const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory); - const containingFileMode = !isString(containingFile) ? containingFile.impliedNodeFormat : undefined; const containingDir = getDirectoryPath(containingFileName); const redirectedReference = getRedirectReferenceForResolution(containingFile); - let i = 0; for (const n of names) { // mimics logic done in the resolution cache, should be resilient to upgrading it to use `FileReference`s for non-type-reference modal lookups to make it rely on the index in the list less - const mode = typeof n === "string" ? getModeForResolutionAtIndex(containingFile, i) : getModeForFileReference(n, containingFileMode); - const name = typeof n === "string" ? n : n.fileName; - i++; + const mode = getResolutionMode(n, containingFile); + const name = getResolutionName(n); // only nonrelative names hit the cache, and, at least as of right now, only nonrelative names can issue diagnostics // (Since diagnostics are only issued via import or export map lookup) // This may totally change if/when the issue of output paths not mapping to input files is fixed in a broader context @@ -1419,13 +1419,13 @@ namespace ts { } } - function resolveModuleNamesWorker(moduleNames: string[], containingFile: SourceFile, partialResolutionInfo: PartialResolutionInfo | undefined): readonly (ResolvedModuleFull | undefined)[] { + function resolveModuleNamesWorker(moduleNames: readonly StringLiteralLike[], containingFile: SourceFile, resolutionInfo: ModuleResolutionInfo | undefined): readonly (ResolvedModuleFull | undefined)[] { if (!moduleNames.length) return emptyArray; const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory); const redirectedReference = getRedirectReferenceForResolution(containingFile); tracing?.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName }); performance.mark("beforeResolveModule"); - const result = actualResolveModuleNamesWorker(moduleNames, containingFile, containingFileName, redirectedReference, partialResolutionInfo); + const result = actualResolveModuleNamesWorker(moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo); performance.mark("afterResolveModule"); performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule"); tracing?.pop(); @@ -1528,11 +1528,11 @@ namespace ts { return classifiableNames; } - function resolveModuleNamesReusingOldState(moduleNames: string[], file: SourceFile): readonly (ResolvedModuleFull | undefined)[] { + function resolveModuleNamesReusingOldState(moduleNames: readonly StringLiteralLike[], file: SourceFile): readonly (ResolvedModuleFull | undefined)[] { if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) { // If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules, // the best we can do is fallback to the default logic. - return resolveModuleNamesWorker(moduleNames, file, /*partialResolutionInfo*/ undefined); + return resolveModuleNamesWorker(moduleNames, file, /*resolutionInfo*/ undefined); } const oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName); @@ -1546,10 +1546,8 @@ namespace ts { // Since we assume the filesystem does not change during program creation, // it is safe to reuse resolutions from the earlier call. const result: (ResolvedModuleFull | undefined)[] = []; - let i = 0; for (const moduleName of moduleNames) { - const resolvedModule = file.resolvedModules.get(moduleName, getModeForResolutionAtIndex(file, i)); - i++; + const resolvedModule = file.resolvedModules.get(moduleName.text, getModeForUsageLocation(file, moduleName)); result.push(resolvedModule); } return result; @@ -1560,8 +1558,7 @@ namespace ts { // With this information, we can infer some module resolutions without performing resolution. /** An ordered list of module names for which we cannot recover the resolution. */ - let unknownModuleNames: string[] | undefined; - let unknownModuleNamesIndex: number[] | undefined; + let unknownModuleNames: StringLiteralLike[] | undefined; /** * The indexing of elements in this list matches that of `moduleNames`. * @@ -1572,7 +1569,7 @@ namespace ts { * * ResolvedModuleFull instance: can be reused. */ let result: (ResolvedModuleFull | undefined)[] | undefined; - let reusedNames: { name: string; mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined; }[] | undefined; + let reusedNames: StringLiteralLike[] | undefined; /** A transient placeholder used to mark predicted resolution in the result list. */ const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = {} as any; @@ -1580,22 +1577,22 @@ namespace ts { const moduleName = moduleNames[i]; // If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions if (file === oldSourceFile && !hasInvalidatedResolutions(oldSourceFile.path)) { - const mode = getModeForResolutionAtIndex(oldSourceFile, i); - const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName, mode); + const mode = getModeForUsageLocation(file, moduleName); + const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName.text, mode); if (oldResolvedModule) { if (isTraceEnabled(options, host)) { trace(host, oldResolvedModule.packageId ? Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2_with_Package_ID_3 : Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2, - moduleName, + moduleName.text, getNormalizedAbsolutePath(file.originalFileName, currentDirectory), oldResolvedModule.resolvedFileName, oldResolvedModule.packageId && packageIdToString(oldResolvedModule.packageId) ); } (result ??= new Array(moduleNames.length))[i] = oldResolvedModule; - (reusedNames ??= []).push({ name: moduleName, mode }); + (reusedNames ??= []).push(moduleName); continue; } } @@ -1604,14 +1601,14 @@ namespace ts { // - resolved to an ambient module in the old program whose declaration is in an unmodified file // (so the same module declaration will land in the new program) let resolvesToAmbientModuleInNonModifiedFile = false; - if (contains(file.ambientModuleNames, moduleName)) { + if (contains(file.ambientModuleNames, moduleName.text)) { resolvesToAmbientModuleInNonModifiedFile = true; if (isTraceEnabled(options, host)) { - trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory)); + trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName.text, getNormalizedAbsolutePath(file.originalFileName, currentDirectory)); } } else { - resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, i); + resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName); } if (resolvesToAmbientModuleInNonModifiedFile) { @@ -1620,12 +1617,11 @@ namespace ts { else { // Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result. (unknownModuleNames ??= []).push(moduleName); - (unknownModuleNamesIndex ??= []).push(i); } } const resolutions = unknownModuleNames && unknownModuleNames.length - ? resolveModuleNamesWorker(unknownModuleNames, file, { reusedNames, namesIndex: unknownModuleNamesIndex! }) + ? resolveModuleNamesWorker(unknownModuleNames, file, { names: unknownModuleNames, reusedNames }) : emptyArray; // Combine results of resolutions and predicted results @@ -1655,9 +1651,8 @@ namespace ts { // If we change our policy of rechecking failed lookups on each program create, // we should adjust the value returned here. - function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, index: number): boolean { - if (index >= length(oldSourceFile?.imports) + length(oldSourceFile?.moduleAugmentations)) return false; // mode index out of bounds, don't reuse resolution - const resolutionToFile = getResolvedModule(oldSourceFile, moduleName, oldSourceFile && getModeForResolutionAtIndex(oldSourceFile, index)); + function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: StringLiteralLike): boolean { + const resolutionToFile = getResolvedModule(oldSourceFile, moduleName.text, getModeForUsageLocation(file, moduleName)); const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.resolvedFileName); if (resolutionToFile && resolvedFile) { // In the old program, we resolved to an ambient module that was in the same @@ -1668,14 +1663,14 @@ namespace ts { } // at least one of declarations should come from non-modified source file - const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName); + const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName.text); if (!unmodifiedFile) { return false; } if (isTraceEnabled(options, host)) { - trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, unmodifiedFile); + trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName.text, unmodifiedFile); } return true; } @@ -1875,7 +1870,7 @@ namespace ts { const moduleNames = getModuleNames(newSourceFile); const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFile); // ensure that module resolution results are still correct - const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, oldSourceFile, moduleResolutionIsEqualTo); + const resolutionsChanged = hasChangesInResolutions(moduleNames, newSourceFile, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo); if (resolutionsChanged) { structureIsReused = StructureIsReused.SafeModules; newSourceFile.resolvedModules = zipToModeAwareCache(newSourceFile, moduleNames, resolutions); @@ -1886,7 +1881,7 @@ namespace ts { const typesReferenceDirectives = newSourceFile.typeReferenceDirectives; const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFile); // ensure that types resolutions are still correct - const typeReferenceResolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, oldSourceFile, typeDirectiveIsEqualTo); + const typeReferenceResolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, newSourceFile, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo); if (typeReferenceResolutionsChanged) { structureIsReused = StructureIsReused.SafeModules; newSourceFile.resolvedTypeReferenceDirectiveNames = zipToModeAwareCache(newSourceFile, typesReferenceDirectives, typeReferenceResolutions); @@ -3288,7 +3283,7 @@ namespace ts { const optionsForFile = (useSourceOfProjectReferenceRedirect ? getRedirectReferenceForResolution(file)?.commandLine.options : undefined) || options; for (let index = 0; index < moduleNames.length; index++) { const resolution = resolutions[index]; - setResolvedModule(file, moduleNames[index], resolution, getModeForResolutionAtIndex(file, index)); + setResolvedModule(file, moduleNames[index].text, resolution, getModeForUsageLocation(file, moduleNames[index])); if (!resolution) { continue; @@ -4374,11 +4369,11 @@ namespace ts { } } - function getModuleNames({ imports, moduleAugmentations }: SourceFile): string[] { - const res = imports.map(i => i.text); + function getModuleNames({ imports, moduleAugmentations }: SourceFile): StringLiteralLike[] { + const res = imports.map(i => i); for (const aug of moduleAugmentations) { if (aug.kind === SyntaxKind.StringLiteral) { - res.push(aug.text); + res.push(aug); } // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`. } diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 7326d4c11ea04..6953ebc845c86 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -8,9 +8,10 @@ namespace ts { resolveModuleNames( moduleNames: string[], containingFile: string, + reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, containingSourceFile: SourceFile | undefined, - partialResolutionInfo: PartialResolutionInfo | undefined + resolutionInfo: ModuleResolutionInfo | undefined ): (ResolvedModuleFull | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): CachedResolvedModuleWithFailedLookupLocations | undefined; resolveTypeReferenceDirectives(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"]): (ResolvedTypeReferenceDirective | undefined)[]; @@ -414,7 +415,8 @@ namespace ts { loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext | undefined) => T; getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName; shouldRetryResolution: (t: T) => boolean; - partialResolutionInfo?: PartialResolutionInfo; + reusedNames?: readonly string[]; + resolutionInfo?: ModuleResolutionInfo; logChanges?: boolean; containingSourceFile?: SourceFile; containingSourceFileMode?: SourceFile["impliedNodeFormat"]; @@ -423,7 +425,7 @@ namespace ts { names, containingFile, redirectedReference, cache, perDirectoryCacheWithRedirects, loader, getResolutionWithResolvedFileName, - shouldRetryResolution, partialResolutionInfo, logChanges, containingSourceFile, containingSourceFileMode + shouldRetryResolution, reusedNames, resolutionInfo, logChanges, containingSourceFile, containingSourceFileMode }: ResolveNamesWithLocalCacheInput): (R | undefined)[] { const path = resolutionHost.toPath(containingFile); const resolutionsInFile = cache.get(path) || cache.set(path, createModeAwareCache()).get(path)!; @@ -447,15 +449,20 @@ namespace ts { const seenNamesInFile = createModeAwareCache(); let i = 0; - for (const entry of names) { - const name = isString(entry) ? entry : entry.fileName.toLowerCase(); + for (const entry of containingSourceFile && resolutionInfo ? resolutionInfo.names : names) { + const name = !isString(entry) ? getResolutionName(entry) : entry; // Imports supply a `containingSourceFile` but no `containingSourceFileMode` - it would be redundant // they require calculating the mode for a given import from it's position in the resolution table, since a given // import's syntax may override the file's default mode. // Type references instead supply a `containingSourceFileMode` and a non-string entry which contains // a default file mode override if applicable. - const mode = !isString(entry) ? getModeForFileReference(entry, containingSourceFileMode) : - containingSourceFile ? getModeForResolutionAtIndex(containingSourceFile, partialResolutionInfo?.namesIndex[i] ?? i) : undefined; + const mode = !isString(entry) ? + isStringLiteralLike(entry) ? + getModeForUsageLocation(containingSourceFile!, entry) : + getModeForFileReference(entry, containingSourceFileMode) : + containingSourceFile ? + getModeForResolutionAtIndex(containingSourceFile, i) : + undefined; i++; let resolution = resolutionsInFile.get(name, mode); // Resolution is valid if it is present and not invalidated @@ -539,11 +546,14 @@ namespace ts { resolvedModules.push(getResolutionWithResolvedFileName(resolution)); } - partialResolutionInfo?.reusedNames?.forEach(({ name, mode }) => seenNamesInFile.set(name, mode, true)); + if (containingSourceFile && resolutionInfo) { + resolutionInfo.reusedNames?.forEach(literal => seenNamesInFile.set(literal.text, getModeForUsageLocation(containingSourceFile, literal), true)); + reusedNames = undefined; + } if (resolutionsInFile.size() !== seenNamesInFile.size()) { // Stop watching and remove the unused name resolutionsInFile.forEach((resolution, name, mode) => { - if (!seenNamesInFile.has(name, mode)) { + if (!seenNamesInFile.has(name, mode) && !contains(reusedNames, name)) { stopWatchFailedLookupLocationOfResolution(resolution, path, getResolutionWithResolvedFileName); resolutionsInFile.delete(name, mode); } @@ -588,9 +598,10 @@ namespace ts { function resolveModuleNames( moduleNames: string[], containingFile: string, + reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile, - partialResolutionInfo?: PartialResolutionInfo + resolutionInfo?: ModuleResolutionInfo ): (ResolvedModuleFull | undefined)[] { return resolveNamesWithLocalCache({ names: moduleNames, @@ -601,7 +612,8 @@ namespace ts { loader: resolveModuleName, getResolutionWithResolvedFileName: getResolvedModule, shouldRetryResolution: resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension), - partialResolutionInfo, + reusedNames, + resolutionInfo, logChanges: logChangesWhenResolvingModule, containingSourceFile, }); diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 2f881f4bcc7b2..13ce360e8740b 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -302,8 +302,8 @@ namespace ts { if (!compilerHost.resolveModuleNames) { const loader = (moduleName: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, state.projectCompilerOptions, compilerHost, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule; - compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, _options, containingSourceFile, partialResolutionInfo) => - loadWithModeAwareCache(Debug.checkEachDefined(moduleNames), Debug.checkDefined(containingSourceFile), containingFile, redirectedReference, partialResolutionInfo, loader); + compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, _options, containingSourceFile, resolutionInfo) => + loadWithModeAwareCache(Debug.checkEachDefined(moduleNames), Debug.checkDefined(containingSourceFile), containingFile, redirectedReference, resolutionInfo, loader); compilerHost.getModuleResolutionCache = () => moduleResolutionCache; } if (!compilerHost.resolveTypeReferenceDirectives) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index df7dab4aca8cc..f4de08a606fc6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7185,9 +7185,9 @@ namespace ts { /* @internal */ export type HasChangedAutomaticTypeDirectiveNames = () => boolean; - export interface PartialResolutionInfo { - reusedNames: { name: string; mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined; }[] | undefined; - namesIndex: readonly number[]; + export interface ModuleResolutionInfo { + names: readonly StringLiteralLike[]; + reusedNames: readonly StringLiteralLike[] | undefined; } export interface CompilerHost extends ModuleResolutionHost { @@ -7210,7 +7210,7 @@ namespace ts { * If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just * 'throw new Error("NotImplemented")' */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; /** * Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0571a9c92df0d..bed7e1fafd806 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -211,19 +211,18 @@ namespace ts { } export function hasChangesInResolutions( - names: readonly string[] | readonly FileReference[], + names: readonly StringLiteralLike[] | readonly FileReference[], + newSourceFile: SourceFile, newResolutions: readonly T[], oldResolutions: ModeAwareCache | undefined, - oldSourceFile: SourceFile | undefined, comparer: (oldResolution: T, newResolution: T) => boolean): boolean { Debug.assert(names.length === newResolutions.length); for (let i = 0; i < names.length; i++) { const newResolution = newResolutions[i]; const entry = names[i]; - // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. - const name = !isString(entry) ? entry.fileName.toLowerCase() : entry; - const mode = !isString(entry) ? getModeForFileReference(entry, oldSourceFile?.impliedNodeFormat) : oldSourceFile && getModeForResolutionAtIndex(oldSourceFile, i); + const name = getResolutionName(entry); + const mode = getResolutionMode(entry, newSourceFile); const oldResolution = oldResolutions && oldResolutions.get(name, mode); const changed = oldResolution diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index c6e083e9d7e1a..e29a548a9f022 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -2072,8 +2072,8 @@ namespace ts { return indentation === MAX_SMI_X86 ? undefined : indentation; } - export function isStringLiteralLike(node: Node): node is StringLiteralLike { - return node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; + export function isStringLiteralLike(node: Node | FileReference): node is StringLiteralLike { + return (node as Node).kind === SyntaxKind.StringLiteral || (node as Node).kind === SyntaxKind.NoSubstitutionTemplateLiteral; } export function isJSDocLinkLike(node: Node): node is JSDocLink | JSDocLinkCode | JSDocLinkPlain { diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index 0b9aa73d3d9e9..546afb5b457ef 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -109,7 +109,7 @@ namespace ts { getEnvironmentVariable?(name: string): string | undefined; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; /** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */ @@ -365,7 +365,7 @@ namespace ts { // Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names compilerHost.resolveModuleNames = host.resolveModuleNames ? ((...args) => host.resolveModuleNames!(...args)) : - ((moduleNames, containingFile, _reusedNames, redirectedReference, _options, sourceFile, partialResolutionInfo) => resolutionCache.resolveModuleNames(moduleNames, containingFile, redirectedReference, sourceFile, partialResolutionInfo)); + ((moduleNames, containingFile, reusedNames, redirectedReference, _options, sourceFile, resolutionInfo) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, sourceFile, resolutionInfo)); compilerHost.resolveTypeReferenceDirectives = host.resolveTypeReferenceDirectives ? ((...args) => host.resolveTypeReferenceDirectives!(...args)) : ((typeDirectiveNames, containingFile, redirectedReference, _options, containingFileMode) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference, containingFileMode)); diff --git a/src/server/project.ts b/src/server/project.ts index 955b68a29b88c..1c339d4202f53 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -506,8 +506,8 @@ namespace ts.server { return !this.isWatchedMissingFile(path) && this.directoryStructureHost.fileExists(file); } - resolveModuleNames(moduleNames: string[], containingFile: string, _reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModuleFull | undefined)[] { - return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, redirectedReference, containingSourceFile, partialResolutionInfo); + resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModuleFull | undefined)[] { + return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, containingSourceFile, resolutionInfo); } getModuleResolutionCache(): ModuleResolutionCache | undefined { diff --git a/src/services/types.ts b/src/services/types.ts index d78ce10e9fa82..fdd692b5b2233 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -284,7 +284,7 @@ namespace ts { * * If this is implemented, `getResolvedModuleWithFailedLookupLocationsFromCache` should be too. */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; /* @internal */ hasInvalidatedResolutions?: HasInvalidatedResolutions; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 54faa0a779f5f..60128336e0eab 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3298,12 +3298,9 @@ declare namespace ts { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; readonly failedLookupLocations: string[]; } - export interface PartialResolutionInfo { - reusedNames: { - name: string; - mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined; - }[] | undefined; - namesIndex: readonly number[]; + export interface ModuleResolutionInfo { + names: readonly StringLiteralLike[]; + reusedNames: readonly StringLiteralLike[] | undefined; } export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; @@ -3317,7 +3314,7 @@ declare namespace ts { useCaseSensitiveFileNames(): boolean; getNewLine(): string; readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[]; - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; /** * Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it */ @@ -4571,7 +4568,7 @@ declare namespace ts { /** True if has initializer node attached to it. */ function hasOnlyExpressionInitializer(node: Node): node is HasExpressionInitializer; function isObjectLiteralElement(node: Node): node is ObjectLiteralElement; - function isStringLiteralLike(node: Node): node is StringLiteralLike; + function isStringLiteralLike(node: Node | FileReference): node is StringLiteralLike; function isJSDocLinkLike(node: Node): node is JSDocLink | JSDocLinkCode | JSDocLinkPlain; function hasRestParameter(s: SignatureDeclaration | JSDocSignature): boolean; function isRestParameter(node: ParameterDeclaration | JSDocParameterTag): boolean; @@ -5448,7 +5445,7 @@ declare namespace ts { /** If provided is used to get the environment variable */ getEnvironmentVariable?(name: string): string | undefined; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; /** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */ @@ -5850,7 +5847,7 @@ declare namespace ts { readFile(path: string, encoding?: string): string | undefined; fileExists(path: string): boolean; getTypeRootsVersion?(): number; - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; getDirectories?(directoryName: string): string[]; @@ -10153,7 +10150,7 @@ declare namespace ts.server { readFile(fileName: string): string | undefined; writeFile(fileName: string, content: string): void; fileExists(file: string): boolean; - resolveModuleNames(moduleNames: string[], containingFile: string, _reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModuleFull | undefined)[]; + resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModuleFull | undefined)[]; getModuleResolutionCache(): ModuleResolutionCache | undefined; getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; resolveTypeReferenceDirectives(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 6672dc7f92dee..f7a056b5993ab 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3298,12 +3298,9 @@ declare namespace ts { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; readonly failedLookupLocations: string[]; } - export interface PartialResolutionInfo { - reusedNames: { - name: string; - mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined; - }[] | undefined; - namesIndex: readonly number[]; + export interface ModuleResolutionInfo { + names: readonly StringLiteralLike[]; + reusedNames: readonly StringLiteralLike[] | undefined; } export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; @@ -3317,7 +3314,7 @@ declare namespace ts { useCaseSensitiveFileNames(): boolean; getNewLine(): string; readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[]; - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; /** * Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it */ @@ -4571,7 +4568,7 @@ declare namespace ts { /** True if has initializer node attached to it. */ function hasOnlyExpressionInitializer(node: Node): node is HasExpressionInitializer; function isObjectLiteralElement(node: Node): node is ObjectLiteralElement; - function isStringLiteralLike(node: Node): node is StringLiteralLike; + function isStringLiteralLike(node: Node | FileReference): node is StringLiteralLike; function isJSDocLinkLike(node: Node): node is JSDocLink | JSDocLinkCode | JSDocLinkPlain; function hasRestParameter(s: SignatureDeclaration | JSDocSignature): boolean; function isRestParameter(node: ParameterDeclaration | JSDocParameterTag): boolean; @@ -5448,7 +5445,7 @@ declare namespace ts { /** If provided is used to get the environment variable */ getEnvironmentVariable?(name: string): string | undefined; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; /** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */ @@ -5850,7 +5847,7 @@ declare namespace ts { readFile(path: string, encoding?: string): string | undefined; fileExists(path: string): boolean; getTypeRootsVersion?(): number; - resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, partialResolutionInfo?: PartialResolutionInfo): (ResolvedModule | undefined)[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; getDirectories?(directoryName: string): string[];