Skip to content

Commit 5c55ce1

Browse files
authored
Expand auto-import API to work on non-existent files and symbols (#58093)
1 parent 7a4cbfa commit 5c55ce1

File tree

8 files changed

+478
-89
lines changed

8 files changed

+478
-89
lines changed

src/compiler/moduleSpecifiers.ts

+33-11
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
flatten,
3333
forEach,
3434
forEachAncestorDirectory,
35+
FutureSourceFile,
3536
getBaseFileName,
3637
GetCanonicalFileName,
3738
getConditions,
@@ -65,6 +66,7 @@ import {
6566
isDeclarationFileName,
6667
isExternalModuleAugmentation,
6768
isExternalModuleNameRelative,
69+
isFullSourceFile,
6870
isMissingPackageJsonInfo,
6971
isModuleBlock,
7072
isModuleDeclaration,
@@ -141,7 +143,7 @@ export interface ModuleSpecifierPreferences {
141143
export function getModuleSpecifierPreferences(
142144
{ importModuleSpecifierPreference, importModuleSpecifierEnding }: UserPreferences,
143145
compilerOptions: CompilerOptions,
144-
importingSourceFile: SourceFile,
146+
importingSourceFile: Pick<SourceFile, "fileName" | "impliedNodeFormat">,
145147
oldImportSpecifier?: string,
146148
): ModuleSpecifierPreferences {
147149
const filePreferredEnding = getPreferredEnding();
@@ -197,7 +199,7 @@ export function getModuleSpecifierPreferences(
197199
importModuleSpecifierEnding,
198200
resolutionMode ?? importingSourceFile.impliedNodeFormat,
199201
compilerOptions,
200-
importingSourceFile,
202+
isFullSourceFile(importingSourceFile) ? importingSourceFile : undefined,
201203
);
202204
}
203205
}
@@ -230,7 +232,7 @@ export function updateModuleSpecifier(
230232
/** @internal */
231233
export function getModuleSpecifier(
232234
compilerOptions: CompilerOptions,
233-
importingSourceFile: SourceFile,
235+
importingSourceFile: SourceFile | FutureSourceFile,
234236
importingSourceFileName: string,
235237
toFileName: string,
236238
host: ModuleSpecifierResolutionHost,
@@ -242,7 +244,7 @@ export function getModuleSpecifier(
242244
/** @internal */
243245
export function getNodeModulesPackageName(
244246
compilerOptions: CompilerOptions,
245-
importingSourceFile: SourceFile,
247+
importingSourceFile: SourceFile | FutureSourceFile,
246248
nodeModulesFileName: string,
247249
host: ModuleSpecifierResolutionHost,
248250
preferences: UserPreferences,
@@ -255,7 +257,7 @@ export function getNodeModulesPackageName(
255257

256258
function getModuleSpecifierWorker(
257259
compilerOptions: CompilerOptions,
258-
importingSourceFile: SourceFile,
260+
importingSourceFile: SourceFile | FutureSourceFile,
259261
importingSourceFileName: string,
260262
toFileName: string,
261263
host: ModuleSpecifierResolutionHost,
@@ -272,7 +274,7 @@ function getModuleSpecifierWorker(
272274
/** @internal */
273275
export function tryGetModuleSpecifiersFromCache(
274276
moduleSymbol: Symbol,
275-
importingSourceFile: SourceFile,
277+
importingSourceFile: SourceFile | FutureSourceFile,
276278
host: ModuleSpecifierResolutionHost,
277279
userPreferences: UserPreferences,
278280
options: ModuleSpecifierOptions = {},
@@ -288,7 +290,7 @@ export function tryGetModuleSpecifiersFromCache(
288290

289291
function tryGetModuleSpecifiersFromCacheWorker(
290292
moduleSymbol: Symbol,
291-
importingSourceFile: SourceFile,
293+
importingSourceFile: SourceFile | FutureSourceFile,
292294
host: ModuleSpecifierResolutionHost,
293295
userPreferences: UserPreferences,
294296
options: ModuleSpecifierOptions = {},
@@ -334,7 +336,7 @@ export function getModuleSpecifiersWithCacheInfo(
334336
moduleSymbol: Symbol,
335337
checker: TypeChecker,
336338
compilerOptions: CompilerOptions,
337-
importingSourceFile: SourceFile,
339+
importingSourceFile: SourceFile | FutureSourceFile,
338340
host: ModuleSpecifierResolutionHost,
339341
userPreferences: UserPreferences,
340342
options: ModuleSpecifierOptions = {},
@@ -370,18 +372,38 @@ export function getModuleSpecifiersWithCacheInfo(
370372
return { moduleSpecifiers: result, computedWithoutCache };
371373
}
372374

375+
/** @internal */
376+
export function getLocalModuleSpecifierBetweenFileNames(
377+
importingFile: Pick<SourceFile, "fileName" | "impliedNodeFormat">,
378+
targetFileName: string,
379+
compilerOptions: CompilerOptions,
380+
host: ModuleSpecifierResolutionHost,
381+
options: ModuleSpecifierOptions = {},
382+
): string {
383+
const info = getInfo(importingFile.fileName, host);
384+
const importMode = options.overrideImportMode ?? importingFile.impliedNodeFormat;
385+
return getLocalModuleSpecifier(
386+
targetFileName,
387+
info,
388+
compilerOptions,
389+
host,
390+
importMode,
391+
getModuleSpecifierPreferences({}, compilerOptions, importingFile),
392+
);
393+
}
394+
373395
function computeModuleSpecifiers(
374396
modulePaths: readonly ModulePath[],
375397
compilerOptions: CompilerOptions,
376-
importingSourceFile: SourceFile,
398+
importingSourceFile: SourceFile | FutureSourceFile,
377399
host: ModuleSpecifierResolutionHost,
378400
userPreferences: UserPreferences,
379401
options: ModuleSpecifierOptions = {},
380402
forAutoImport: boolean,
381403
): readonly string[] {
382404
const info = getInfo(importingSourceFile.fileName, host);
383405
const preferences = getModuleSpecifierPreferences(userPreferences, compilerOptions, importingSourceFile);
384-
const existingSpecifier = forEach(modulePaths, modulePath =>
406+
const existingSpecifier = isFullSourceFile(importingSourceFile) && forEach(modulePaths, modulePath =>
385407
forEach(
386408
host.getFileIncludeReasons().get(toPath(modulePath.path, host.getCurrentDirectory(), info.getCanonicalFileName)),
387409
reason => {
@@ -1014,7 +1036,7 @@ function tryGetModuleNameFromRootDirs(rootDirs: readonly string[], moduleFileNam
10141036
return processEnding(shortest, allowedEndings, compilerOptions);
10151037
}
10161038

1017-
function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCanonicalFileName, canonicalSourceDirectory }: Info, importingSourceFile: SourceFile, host: ModuleSpecifierResolutionHost, options: CompilerOptions, userPreferences: UserPreferences, packageNameOnly?: boolean, overrideMode?: ResolutionMode): string | undefined {
1039+
function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCanonicalFileName, canonicalSourceDirectory }: Info, importingSourceFile: SourceFile | FutureSourceFile, host: ModuleSpecifierResolutionHost, options: CompilerOptions, userPreferences: UserPreferences, packageNameOnly?: boolean, overrideMode?: ResolutionMode): string | undefined {
10181040
if (!host.fileExists || !host.readFile) {
10191041
return undefined;
10201042
}

src/compiler/types.ts

+12
Original file line numberDiff line numberDiff line change
@@ -4217,6 +4217,18 @@ export interface SourceFileLike {
42174217
getPositionOfLineAndCharacter?(line: number, character: number, allowEdits?: true): number;
42184218
}
42194219

4220+
/** @internal */
4221+
export interface FutureSourceFile {
4222+
readonly path: Path;
4223+
readonly fileName: string;
4224+
readonly impliedNodeFormat?: ResolutionMode;
4225+
readonly packageJsonScope?: PackageJsonInfo;
4226+
readonly externalModuleIndicator?: true | undefined;
4227+
readonly commonJsModuleIndicator?: true | undefined;
4228+
readonly statements: readonly never[];
4229+
readonly imports: readonly never[];
4230+
}
4231+
42204232
/** @internal */
42214233
export interface RedirectInfo {
42224234
/** Source file this redirects to. */

src/compiler/utilities.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
AmpersandAmpersandEqualsToken,
1212
AnyImportOrBareOrAccessedRequire,
1313
AnyImportOrReExport,
14+
AnyImportOrRequireStatement,
1415
AnyImportSyntax,
1516
AnyValidImportOrReExport,
1617
append,
@@ -1985,6 +1986,11 @@ export function isAnyImportOrBareOrAccessedRequire(node: Node): node is AnyImpor
19851986
return isAnyImportSyntax(node) || isVariableDeclarationInitializedToBareOrAccessedRequire(node);
19861987
}
19871988

1989+
/** @internal */
1990+
export function isAnyImportOrRequireStatement(node: Node): node is AnyImportOrRequireStatement {
1991+
return isAnyImportSyntax(node) || isRequireVariableStatement(node);
1992+
}
1993+
19881994
/** @internal */
19891995
export function isLateVisibilityPaintedStatement(node: Node): node is LateVisibilityPaintedStatement {
19901996
switch (node.kind) {
@@ -3461,6 +3467,11 @@ export function isInternalModuleImportEqualsDeclaration(node: Node): node is Imp
34613467
return node.kind === SyntaxKind.ImportEqualsDeclaration && (node as ImportEqualsDeclaration).moduleReference.kind !== SyntaxKind.ExternalModuleReference;
34623468
}
34633469

3470+
/** @internal */
3471+
export function isFullSourceFile(sourceFile: object): sourceFile is SourceFile {
3472+
return (sourceFile as Partial<SourceFile>)?.kind === SyntaxKind.SourceFile;
3473+
}
3474+
34643475
/** @internal */
34653476
export function isSourceFileJS(file: SourceFile): boolean {
34663477
return isInJSFile(file);
@@ -9576,7 +9587,7 @@ export function usesExtensionsOnImports({ imports }: SourceFile, hasExtension: (
95769587
}
95779588

95789589
/** @internal */
9579-
export function getModuleSpecifierEndingPreference(preference: UserPreferences["importModuleSpecifierEnding"], resolutionMode: ResolutionMode, compilerOptions: CompilerOptions, sourceFile: SourceFile): ModuleSpecifierEnding {
9590+
export function getModuleSpecifierEndingPreference(preference: UserPreferences["importModuleSpecifierEnding"], resolutionMode: ResolutionMode, compilerOptions: CompilerOptions, sourceFile?: SourceFile): ModuleSpecifierEnding {
95809591
const moduleResolution = getEmitModuleResolutionKind(compilerOptions);
95819592
const moduleResolutionIsNodeNext = ModuleResolutionKind.Node16 <= moduleResolution && moduleResolution <= ModuleResolutionKind.NodeNext;
95829593
if (preference === "js" || resolutionMode === ModuleKind.ESNext && moduleResolutionIsNodeNext) {
@@ -9603,22 +9614,22 @@ export function getModuleSpecifierEndingPreference(preference: UserPreferences["
96039614
// accurately, and more importantly, literally nobody wants `Index` and its existence is a mystery.
96049615
if (!shouldAllowImportingTsExtension(compilerOptions)) {
96059616
// If .ts imports are not valid, we only need to see one .js import to go with that.
9606-
return usesExtensionsOnImports(sourceFile) ? ModuleSpecifierEnding.JsExtension : ModuleSpecifierEnding.Minimal;
9617+
return sourceFile && usesExtensionsOnImports(sourceFile) ? ModuleSpecifierEnding.JsExtension : ModuleSpecifierEnding.Minimal;
96079618
}
96089619

96099620
return inferPreference();
96109621

96119622
function inferPreference() {
96129623
let usesJsExtensions = false;
9613-
const specifiers = sourceFile.imports.length ? sourceFile.imports :
9614-
isSourceFileJS(sourceFile) ? getRequiresAtTopOfFile(sourceFile).map(r => r.arguments[0]) :
9624+
const specifiers = sourceFile?.imports.length ? sourceFile.imports :
9625+
sourceFile && isSourceFileJS(sourceFile) ? getRequiresAtTopOfFile(sourceFile).map(r => r.arguments[0]) :
96159626
emptyArray;
96169627
for (const specifier of specifiers) {
96179628
if (pathIsRelative(specifier.text)) {
96189629
if (
96199630
moduleResolutionIsNodeNext &&
96209631
resolutionMode === ModuleKind.CommonJS &&
9621-
getModeForUsageLocation(sourceFile, specifier, compilerOptions) === ModuleKind.ESNext
9632+
getModeForUsageLocation(sourceFile!, specifier, compilerOptions) === ModuleKind.ESNext
96229633
) {
96239634
// We're trying to decide a preference for a CommonJS module specifier, but looking at an ESM import.
96249635
continue;

0 commit comments

Comments
 (0)