Skip to content

Commit bcb1545

Browse files
authored
Allow existing imports in file to supersede package.json filter (#59604)
1 parent 5fd6a6f commit bcb1545

12 files changed

+7233
-25
lines changed

src/compiler/types.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -5461,7 +5461,7 @@ export const enum NodeBuilderFlags {
54615461
AllowEmptyIndexInfoType = 1 << 21,
54625462
// Errors (cont.)
54635463
AllowNodeModulesRelativePaths = 1 << 26,
5464-
5464+
54655465

54665466
IgnoreErrors = AllowThisInObjectLiteral | AllowQualifiedNameInPlaceOfIdentifier | AllowAnonymousIdentifier | AllowEmptyUnionOrIntersection | AllowEmptyTuple | AllowEmptyIndexInfoType | AllowNodeModulesRelativePaths,
54675467

@@ -9855,6 +9855,7 @@ export interface ModulePath {
98559855
export interface ResolvedModuleSpecifierInfo {
98569856
kind: "node_modules" | "paths" | "redirect" | "relative" | "ambient" | undefined;
98579857
modulePaths: readonly ModulePath[] | undefined;
9858+
packageName: string | undefined;
98589859
moduleSpecifiers: readonly string[] | undefined;
98599860
isBlockedByPackageJsonDependencies: boolean | undefined;
98609861
}
@@ -9868,7 +9869,7 @@ export interface ModuleSpecifierOptions {
98689869
export interface ModuleSpecifierCache {
98699870
get(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions): Readonly<ResolvedModuleSpecifierInfo> | undefined;
98709871
set(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, kind: ResolvedModuleSpecifierInfo["kind"], modulePaths: readonly ModulePath[], moduleSpecifiers: readonly string[]): void;
9871-
setBlockedByPackageJsonDependencies(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, isBlockedByPackageJsonDependencies: boolean): void;
9872+
setBlockedByPackageJsonDependencies(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, packageName: string | undefined, isBlockedByPackageJsonDependencies: boolean): void;
98729873
setModulePaths(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, modulePaths: readonly ModulePath[]): void;
98739874
clear(): void;
98749875
count(): number;

src/server/moduleSpecifierCache.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export function createModuleSpecifierCache(host: ModuleSpecifierResolutionCacheH
2828
return cache.get(toFileName);
2929
},
3030
set(fromFileName, toFileName, preferences, options, kind, modulePaths, moduleSpecifiers) {
31-
ensureCache(fromFileName, preferences, options).set(toFileName, createInfo(kind, modulePaths, moduleSpecifiers, /*isBlockedByPackageJsonDependencies*/ false));
31+
ensureCache(fromFileName, preferences, options).set(toFileName, createInfo(kind, modulePaths, moduleSpecifiers, /*packageName*/ undefined, /*isBlockedByPackageJsonDependencies*/ false));
3232

3333
// If any module specifiers were generated based off paths in node_modules,
3434
// a package.json file in that package was read and is an input to the cached.
@@ -58,17 +58,18 @@ export function createModuleSpecifierCache(host: ModuleSpecifierResolutionCacheH
5858
info.modulePaths = modulePaths;
5959
}
6060
else {
61-
cache.set(toFileName, createInfo(/*kind*/ undefined, modulePaths, /*moduleSpecifiers*/ undefined, /*isBlockedByPackageJsonDependencies*/ undefined));
61+
cache.set(toFileName, createInfo(/*kind*/ undefined, modulePaths, /*moduleSpecifiers*/ undefined, /*packageName*/ undefined, /*isBlockedByPackageJsonDependencies*/ undefined));
6262
}
6363
},
64-
setBlockedByPackageJsonDependencies(fromFileName, toFileName, preferences, options, isBlockedByPackageJsonDependencies) {
64+
setBlockedByPackageJsonDependencies(fromFileName, toFileName, preferences, options, packageName, isBlockedByPackageJsonDependencies) {
6565
const cache = ensureCache(fromFileName, preferences, options);
6666
const info = cache.get(toFileName);
6767
if (info) {
6868
info.isBlockedByPackageJsonDependencies = isBlockedByPackageJsonDependencies;
69+
info.packageName = packageName;
6970
}
7071
else {
71-
cache.set(toFileName, createInfo(/*kind*/ undefined, /*modulePaths*/ undefined, /*moduleSpecifiers*/ undefined, isBlockedByPackageJsonDependencies));
72+
cache.set(toFileName, createInfo(/*kind*/ undefined, /*modulePaths*/ undefined, /*moduleSpecifiers*/ undefined, packageName, isBlockedByPackageJsonDependencies));
7273
}
7374
},
7475
clear() {
@@ -103,8 +104,9 @@ export function createModuleSpecifierCache(host: ModuleSpecifierResolutionCacheH
103104
kind: ResolvedModuleSpecifierInfo["kind"] | undefined,
104105
modulePaths: readonly ModulePath[] | undefined,
105106
moduleSpecifiers: readonly string[] | undefined,
107+
packageName: string | undefined,
106108
isBlockedByPackageJsonDependencies: boolean | undefined,
107109
): ResolvedModuleSpecifierInfo {
108-
return { kind, modulePaths, moduleSpecifiers, isBlockedByPackageJsonDependencies };
110+
return { kind, modulePaths, moduleSpecifiers, packageName, isBlockedByPackageJsonDependencies };
109111
}
110112
}

src/services/codefixes/importFixes.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
ExportKind,
3636
ExportMapInfoKey,
3737
factory,
38+
fileContainsPackageImport,
3839
findAncestor,
3940
first,
4041
firstDefined,
@@ -1543,7 +1544,7 @@ function getExportInfos(
15431544
const moduleSpecifierResolutionHost = getModuleSpecifierResolutionHost(isFromPackageJson);
15441545
if (
15451546
toFile && isImportableFile(program, fromFile, toFile, preferences, packageJsonFilter, moduleSpecifierResolutionHost, moduleSpecifierCache) ||
1546-
!toFile && packageJsonFilter.allowsImportingAmbientModule(moduleSymbol, moduleSpecifierResolutionHost)
1547+
(!toFile && packageJsonFilter.allowsImportingAmbientModule(moduleSymbol, moduleSpecifierResolutionHost) || fileContainsPackageImport(fromFile, stripQuotes(moduleSymbol.name)))
15471548
) {
15481549
const checker = program.getTypeChecker();
15491550
originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { symbol: exportedSymbol, moduleSymbol, moduleFileName: toFile?.fileName, exportKind, targetFlags: skipAlias(exportedSymbol, checker).flags, isFromPackageJson });

src/services/completions.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import {
6363
Expression,
6464
ExpressionWithTypeArguments,
6565
factory,
66+
fileContainsPackageImport,
6667
filter,
6768
find,
6869
findAncestor,
@@ -4207,9 +4208,8 @@ function getCompletionData(
42074208
if (JsTyping.nodeCoreModules.has(moduleName) && startsWith(moduleName, "node:") !== shouldUseUriStyleNodeCoreModules(sourceFile, program)) {
42084209
return false;
42094210
}
4210-
return packageJsonFilter
4211-
? packageJsonFilter.allowsImportingAmbientModule(info.moduleSymbol, getModuleSpecifierResolutionHost(info.isFromPackageJson))
4212-
: true;
4211+
return (packageJsonFilter?.allowsImportingAmbientModule(info.moduleSymbol, getModuleSpecifierResolutionHost(info.isFromPackageJson)) ?? true)
4212+
|| fileContainsPackageImport(sourceFile, moduleName);
42134213
}
42144214
return isImportableFile(
42154215
info.isFromPackageJson ? packageJsonAutoImportProvider! : program,

src/services/exportInfoMap.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ export function isImportableFile(
374374
if (from === to) return false;
375375
const cachedResult = moduleSpecifierCache?.get(from.path, to.path, preferences, {});
376376
if (cachedResult?.isBlockedByPackageJsonDependencies !== undefined) {
377-
return !cachedResult.isBlockedByPackageJsonDependencies;
377+
return !cachedResult.isBlockedByPackageJsonDependencies || !!cachedResult.packageName && fileContainsPackageImport(from, cachedResult.packageName);
378378
}
379379

380380
const getCanonicalFileName = hostGetCanonicalFileName(moduleSpecifierResolutionHost);
@@ -394,14 +394,19 @@ export function isImportableFile(
394394
);
395395

396396
if (packageJsonFilter) {
397-
const isAutoImportable = hasImportablePath && packageJsonFilter.allowsImportingSourceFile(to, moduleSpecifierResolutionHost);
398-
moduleSpecifierCache?.setBlockedByPackageJsonDependencies(from.path, to.path, preferences, {}, !isAutoImportable);
399-
return isAutoImportable;
397+
const importInfo = hasImportablePath ? packageJsonFilter.getSourceFileInfo(to, moduleSpecifierResolutionHost) : undefined;
398+
moduleSpecifierCache?.setBlockedByPackageJsonDependencies(from.path, to.path, preferences, {}, importInfo?.packageName, !importInfo?.importable);
399+
return !!importInfo?.importable || !!importInfo?.packageName && fileContainsPackageImport(from, importInfo.packageName);
400400
}
401401

402402
return hasImportablePath;
403403
}
404404

405+
/** @internal */
406+
export function fileContainsPackageImport(sourceFile: SourceFile, packageName: string) {
407+
return sourceFile.imports && sourceFile.imports.some(i => i.text === packageName || i.text.startsWith(packageName + "/"));
408+
}
409+
405410
/**
406411
* Don't include something from a `node_modules` that isn't actually reachable by a global import.
407412
* A relative import to node_modules is usually a bad idea.

src/services/utilities.ts

+12-10
Original file line numberDiff line numberDiff line change
@@ -3740,7 +3740,7 @@ export function createPackageJsonInfo(fileName: string, host: { readFile?(fileNa
37403740
/** @internal */
37413741
export interface PackageJsonImportFilter {
37423742
allowsImportingAmbientModule: (moduleSymbol: Symbol, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost) => boolean;
3743-
allowsImportingSourceFile: (sourceFile: SourceFile, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost) => boolean;
3743+
getSourceFileInfo: (sourceFile: SourceFile, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost) => { importable: boolean; packageName?: string; };
37443744
/**
37453745
* Use for a specific module specifier that has already been resolved.
37463746
* Use `allowsImportingAmbientModule` or `allowsImportingSourceFile` to resolve
@@ -3757,10 +3757,10 @@ export function createPackageJsonImportFilter(fromFile: SourceFile | FutureSourc
37573757

37583758
let usesNodeCoreModules: boolean | undefined;
37593759
let ambientModuleCache: Map<Symbol, boolean> | undefined;
3760-
let sourceFileCache: Map<SourceFile, boolean> | undefined;
3760+
let sourceFileCache: Map<SourceFile, { importable: boolean; packageName?: string; }> | undefined;
37613761
return {
37623762
allowsImportingAmbientModule,
3763-
allowsImportingSourceFile,
3763+
getSourceFileInfo,
37643764
allowsImportingSpecifier,
37653765
};
37663766

@@ -3808,9 +3808,9 @@ export function createPackageJsonImportFilter(fromFile: SourceFile | FutureSourc
38083808
return result;
38093809
}
38103810

3811-
function allowsImportingSourceFile(sourceFile: SourceFile, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost): boolean {
3811+
function getSourceFileInfo(sourceFile: SourceFile, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost): { importable: boolean; packageName?: string; } {
38123812
if (!packageJsons.length) {
3813-
return true;
3813+
return { importable: true, packageName: undefined };
38143814
}
38153815

38163816
if (!sourceFileCache) {
@@ -3823,13 +3823,15 @@ export function createPackageJsonImportFilter(fromFile: SourceFile | FutureSourc
38233823
}
38243824
}
38253825

3826-
const moduleSpecifier = getNodeModulesPackageNameFromFileName(sourceFile.fileName, moduleSpecifierResolutionHost);
3827-
if (!moduleSpecifier) {
3828-
sourceFileCache.set(sourceFile, true);
3829-
return true;
3826+
const packageName = getNodeModulesPackageNameFromFileName(sourceFile.fileName, moduleSpecifierResolutionHost);
3827+
if (!packageName) {
3828+
const result = { importable: true, packageName };
3829+
sourceFileCache.set(sourceFile, result);
3830+
return result;
38303831
}
38313832

3832-
const result = moduleSpecifierIsCoveredByPackageJson(moduleSpecifier);
3833+
const importable = moduleSpecifierIsCoveredByPackageJson(packageName);
3834+
const result = { importable, packageName };
38333835
sourceFileCache.set(sourceFile, result);
38343836
return result;
38353837
}

0 commit comments

Comments
 (0)