Skip to content

Commit a5eec24

Browse files
authored
Simplify handling of node:-prefixed modules in auto-imports (#59702)
1 parent f6ec916 commit a5eec24

16 files changed

+941
-189
lines changed

src/compiler/program.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import {
7575
ensureTrailingDirectorySeparator,
7676
equateStringsCaseInsensitive,
7777
equateStringsCaseSensitive,
78+
exclusivelyPrefixedNodeCoreModules,
7879
explainIfFileIsRedirectAndImpliedFormat,
7980
ExportAssignment,
8081
ExportDeclaration,
@@ -323,6 +324,7 @@ import {
323324
TypeChecker,
324325
typeDirectiveIsEqualTo,
325326
TypeReferenceDirectiveResolutionCache,
327+
unprefixedNodeCoreModules,
326328
VariableDeclaration,
327329
VariableStatement,
328330
Version,
@@ -1758,7 +1760,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
17581760
let sourceFileToPackageName = new Map<Path, string>();
17591761
// Key is a file name. Value is the (non-empty, or undefined) list of files that redirect to it.
17601762
let redirectTargetsMap = createMultiMap<Path, string>();
1761-
let usesUriStyleNodeCoreModules = false;
1763+
let usesUriStyleNodeCoreModules: boolean | undefined;
17621764

17631765
/**
17641766
* map with
@@ -3499,7 +3501,14 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
34993501
setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here
35003502
imports = append(imports, moduleNameExpr);
35013503
if (!usesUriStyleNodeCoreModules && currentNodeModulesDepth === 0 && !file.isDeclarationFile) {
3502-
usesUriStyleNodeCoreModules = startsWith(moduleNameExpr.text, "node:");
3504+
if (startsWith(moduleNameExpr.text, "node:") && !exclusivelyPrefixedNodeCoreModules.has(moduleNameExpr.text)) {
3505+
// Presence of `node:` prefix takes precedence over unprefixed node core modules
3506+
usesUriStyleNodeCoreModules = true;
3507+
}
3508+
else if (usesUriStyleNodeCoreModules === undefined && unprefixedNodeCoreModules.has(moduleNameExpr.text)) {
3509+
// Avoid `unprefixedNodeCoreModules.has` for every import
3510+
usesUriStyleNodeCoreModules = false;
3511+
}
35033512
}
35043513
}
35053514
}

src/compiler/types.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -4866,11 +4866,14 @@ export interface Program extends ScriptReferenceHost {
48664866
*/
48674867
redirectTargetsMap: MultiMap<Path, string>;
48684868
/**
4869-
* Whether any (non-external, non-declaration) source files use `node:`-prefixed module specifiers.
4869+
* Whether any (non-external, non-declaration) source files use `node:`-prefixed module specifiers
4870+
* (except for those that are not available without the prefix).
4871+
* `false` indicates that an unprefixed builtin module was seen; `undefined` indicates that no
4872+
* builtin modules (or only modules exclusively available with the prefix) were seen.
48704873
*
48714874
* @internal
48724875
*/
4873-
readonly usesUriStyleNodeCoreModules: boolean;
4876+
readonly usesUriStyleNodeCoreModules: boolean | undefined;
48744877
/**
48754878
* Map from libFileName to actual resolved location of the lib
48764879
* @internal

src/compiler/utilities.ts

+80
Original file line numberDiff line numberDiff line change
@@ -11796,3 +11796,83 @@ export function isSideEffectImport(node: Node): boolean {
1179611796
const ancestor = findAncestor(node, isImportDeclaration);
1179711797
return !!ancestor && !ancestor.importClause;
1179811798
}
11799+
11800+
// require('module').builtinModules.filter(x => !x.startsWith('_'))
11801+
const unprefixedNodeCoreModulesList = [
11802+
"assert",
11803+
"assert/strict",
11804+
"async_hooks",
11805+
"buffer",
11806+
"child_process",
11807+
"cluster",
11808+
"console",
11809+
"constants",
11810+
"crypto",
11811+
"dgram",
11812+
"diagnostics_channel",
11813+
"dns",
11814+
"dns/promises",
11815+
"domain",
11816+
"events",
11817+
"fs",
11818+
"fs/promises",
11819+
"http",
11820+
"http2",
11821+
"https",
11822+
"inspector",
11823+
"inspector/promises",
11824+
"module",
11825+
"net",
11826+
"os",
11827+
"path",
11828+
"path/posix",
11829+
"path/win32",
11830+
"perf_hooks",
11831+
"process",
11832+
"punycode",
11833+
"querystring",
11834+
"readline",
11835+
"readline/promises",
11836+
"repl",
11837+
"stream",
11838+
"stream/consumers",
11839+
"stream/promises",
11840+
"stream/web",
11841+
"string_decoder",
11842+
"sys",
11843+
"test/mock_loader",
11844+
"timers",
11845+
"timers/promises",
11846+
"tls",
11847+
"trace_events",
11848+
"tty",
11849+
"url",
11850+
"util",
11851+
"util/types",
11852+
"v8",
11853+
"vm",
11854+
"wasi",
11855+
"worker_threads",
11856+
"zlib",
11857+
];
11858+
11859+
/** @internal */
11860+
export const unprefixedNodeCoreModules = new Set(unprefixedNodeCoreModulesList);
11861+
11862+
// await fetch('https://nodejs.org/docs/latest/api/all.json').then(r => r.text()).then(t =>
11863+
// new Set(t.match(/(?<=')node:.+?(?=')/g))
11864+
// .difference(new Set(require('module').builtinModules.map(x => `node:${x}`))))
11865+
/** @internal */
11866+
export const exclusivelyPrefixedNodeCoreModules = new Set([
11867+
"node:sea",
11868+
"node:sqlite",
11869+
"node:test",
11870+
"node:test/reporters",
11871+
]);
11872+
11873+
/** @internal */
11874+
export const nodeCoreModules = new Set([
11875+
...unprefixedNodeCoreModulesList,
11876+
...unprefixedNodeCoreModulesList.map(name => `node:${name}`),
11877+
...exclusivelyPrefixedNodeCoreModules,
11878+
]);

src/jsTyping/jsTyping.ts

+1-58
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
hasJSFileExtension,
2020
mapDefined,
2121
MapLike,
22+
nodeCoreModules,
2223
normalizePath,
2324
Path,
2425
readConfigFile,
@@ -61,64 +62,6 @@ export function isTypingUpToDate(cachedTyping: CachedTyping, availableTypingVers
6162
return availableVersion.compareTo(cachedTyping.version) <= 0;
6263
}
6364

64-
const unprefixedNodeCoreModuleList = [
65-
"assert",
66-
"assert/strict",
67-
"async_hooks",
68-
"buffer",
69-
"child_process",
70-
"cluster",
71-
"console",
72-
"constants",
73-
"crypto",
74-
"dgram",
75-
"diagnostics_channel",
76-
"dns",
77-
"dns/promises",
78-
"domain",
79-
"events",
80-
"fs",
81-
"fs/promises",
82-
"http",
83-
"https",
84-
"http2",
85-
"inspector",
86-
"module",
87-
"net",
88-
"os",
89-
"path",
90-
"perf_hooks",
91-
"process",
92-
"punycode",
93-
"querystring",
94-
"readline",
95-
"repl",
96-
"stream",
97-
"stream/promises",
98-
"string_decoder",
99-
"timers",
100-
"timers/promises",
101-
"tls",
102-
"trace_events",
103-
"tty",
104-
"url",
105-
"util",
106-
"util/types",
107-
"v8",
108-
"vm",
109-
"wasi",
110-
"worker_threads",
111-
"zlib",
112-
];
113-
114-
const prefixedNodeCoreModuleList = unprefixedNodeCoreModuleList.map(name => `node:${name}`);
115-
116-
/** @internal */
117-
export const nodeCoreModuleList: readonly string[] = [...unprefixedNodeCoreModuleList, ...prefixedNodeCoreModuleList];
118-
119-
/** @internal */
120-
export const nodeCoreModules = new Set(nodeCoreModuleList);
121-
12265
/** @internal */
12366
export function nonRelativeModuleNameForTypingCache(moduleName: string) {
12467
return nodeCoreModules.has(moduleName) ? "node" : moduleName;

src/services/codefixes/fixCannotFindModule.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import {
1111
InstallPackageAction,
1212
isExternalModuleNameRelative,
1313
isStringLiteral,
14-
JsTyping,
1514
LanguageServiceHost,
15+
nodeCoreModules,
1616
parsePackageName,
1717
SourceFile,
1818
tryCast,
@@ -71,6 +71,6 @@ function tryGetImportedPackageName(sourceFile: SourceFile, pos: number): string
7171

7272
function getTypesPackageNameToInstall(packageName: string, host: LanguageServiceHost, diagCode: number): string | undefined {
7373
return diagCode === errorCodeCannotFindModule
74-
? (JsTyping.nodeCoreModules.has(packageName) ? "@types/node" : undefined)
74+
? (nodeCoreModules.has(packageName) ? "@types/node" : undefined)
7575
: (host.isKnownTypesPackageName?.(packageName) ? getTypesPackageName(packageName) : undefined);
7676
}

src/services/codefixes/importFixes.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import {
3535
ExportKind,
3636
ExportMapInfoKey,
3737
factory,
38-
fileContainsPackageImport,
3938
findAncestor,
4039
first,
4140
firstDefined,
@@ -84,7 +83,7 @@ import {
8483
isExternalModuleReference,
8584
isFullSourceFile,
8685
isIdentifier,
87-
isImportableFile,
86+
isImportable,
8887
isImportDeclaration,
8988
isImportEqualsDeclaration,
9089
isIntrinsicJsxName,
@@ -1542,10 +1541,7 @@ function getExportInfos(
15421541
});
15431542
function addSymbol(moduleSymbol: Symbol, toFile: SourceFile | undefined, exportedSymbol: Symbol, exportKind: ExportKind, program: Program, isFromPackageJson: boolean): void {
15441543
const moduleSpecifierResolutionHost = getModuleSpecifierResolutionHost(isFromPackageJson);
1545-
if (
1546-
toFile && isImportableFile(program, fromFile, toFile, preferences, packageJsonFilter, moduleSpecifierResolutionHost, moduleSpecifierCache) ||
1547-
(!toFile && packageJsonFilter.allowsImportingAmbientModule(moduleSymbol, moduleSpecifierResolutionHost) || fileContainsPackageImport(fromFile, stripQuotes(moduleSymbol.name)))
1548-
) {
1544+
if (isImportable(program, fromFile, toFile, moduleSymbol, preferences, packageJsonFilter, moduleSpecifierResolutionHost, moduleSpecifierCache)) {
15491545
const checker = program.getTypeChecker();
15501546
originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { symbol: exportedSymbol, moduleSymbol, moduleFileName: toFile?.fileName, exportKind, targetFlags: skipAlias(exportedSymbol, checker).flags, isFromPackageJson });
15511547
}

src/services/completions.ts

+6-17
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ import {
6464
Expression,
6565
ExpressionWithTypeArguments,
6666
factory,
67-
fileContainsPackageImport,
6867
filter,
6968
find,
7069
findAncestor,
@@ -175,7 +174,7 @@ import {
175174
isIdentifierPart,
176175
isIdentifierStart,
177176
isIdentifierText,
178-
isImportableFile,
177+
isImportable,
179178
isImportAttributes,
180179
isImportDeclaration,
181180
isImportEqualsDeclaration,
@@ -274,7 +273,6 @@ import {
274273
JSDocTypedefTag,
275274
JSDocTypeExpression,
276275
JSDocTypeTag,
277-
JsTyping,
278276
JsxAttribute,
279277
JsxAttributes,
280278
JsxClosingElement,
@@ -344,7 +342,6 @@ import {
344342
SemanticMeaning,
345343
setEmitFlags,
346344
setSnippetElement,
347-
shouldUseUriStyleNodeCoreModules,
348345
SignatureHelp,
349346
SignatureKind,
350347
singleElementArray,
@@ -4221,19 +4218,11 @@ function getCompletionData(
42214218
);
42224219

42234220
function isImportableExportInfo(info: SymbolExportInfo) {
4224-
const moduleFile = tryCast(info.moduleSymbol.valueDeclaration, isSourceFile);
4225-
if (!moduleFile) {
4226-
const moduleName = stripQuotes(info.moduleSymbol.name);
4227-
if (JsTyping.nodeCoreModules.has(moduleName) && startsWith(moduleName, "node:") !== shouldUseUriStyleNodeCoreModules(sourceFile, program)) {
4228-
return false;
4229-
}
4230-
return (packageJsonFilter?.allowsImportingAmbientModule(info.moduleSymbol, getModuleSpecifierResolutionHost(info.isFromPackageJson)) ?? true)
4231-
|| fileContainsPackageImport(sourceFile, moduleName);
4232-
}
4233-
return isImportableFile(
4221+
return isImportable(
42344222
info.isFromPackageJson ? packageJsonAutoImportProvider! : program,
42354223
sourceFile,
4236-
moduleFile,
4224+
tryCast(info.moduleSymbol.valueDeclaration, isSourceFile),
4225+
info.moduleSymbol,
42374226
preferences,
42384227
packageJsonFilter,
42394228
getModuleSpecifierResolutionHost(info.isFromPackageJson),
@@ -4371,7 +4360,7 @@ function getCompletionData(
43714360
// dprint-ignore
43724361
switch (tokenKind) {
43734362
case SyntaxKind.CommaToken:
4374-
switch (containingNodeKind) {
4363+
switch (containingNodeKind) {
43754364
case SyntaxKind.CallExpression: // func( a, |
43764365
case SyntaxKind.NewExpression: { // new C(a, |
43774366
const expression = (contextToken.parent as CallExpression | NewExpression).expression;
@@ -4454,7 +4443,7 @@ function getCompletionData(
44544443
}
44554444

44564445
case SyntaxKind.TemplateHead:
4457-
return {
4446+
return {
44584447
defaultCommitCharacters: allCommitCharacters,
44594448
isNewIdentifierLocation: containingNodeKind === SyntaxKind.TemplateExpression // `aa ${|
44604449
};

0 commit comments

Comments
 (0)