Skip to content

Commit b484cfb

Browse files
committed
During cacheResolutions dont watch failed lookups and dont look at those package.json if one exists per buildInfo
1 parent 7cb4ec1 commit b484cfb

13 files changed

+474
-1823
lines changed

src/compiler/builder.ts

+37
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ namespace ts {
7373
modules: CacheWithRedirects<Path, ModeAwareCache<ResolvedModuleWithFailedLookupLocations>> | undefined;
7474
typeRefs: CacheWithRedirects<Path, ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>> | undefined;
7575
moduleNameToDirectoryMap: CacheWithRedirects<ModeAwareCacheKey, ESMap<Path, ResolvedModuleWithFailedLookupLocations>>;
76+
dirToPackageJsonMap: ESMap<Path, string>;
7677
perDirPackageJsonMap: ESMap<Path, string> | undefined;
7778
packageJsonCache: PackageJsonInfoCache | undefined;
7879
};
@@ -1302,6 +1303,7 @@ namespace ts {
13021303
modules,
13031304
typeRefs,
13041305
moduleNameToDirectoryMap,
1306+
dirToPackageJsonMap,
13051307
perDirPackageJsonMap,
13061308
packageJsonCache: state.program!.getModuleResolutionCache()?.getPackageJsonInfoCache().clone(),
13071309
};
@@ -1963,6 +1965,7 @@ namespace ts {
19631965
const decodedResolvedModules: DecodedResolvedMap = createCacheWithRedirects(compilerOptions);
19641966
const decodedResolvedTypeRefs: DecodedResolvedMap = createCacheWithRedirects(compilerOptions);
19651967
const decodedModuleNameToDirectoryMap: DecodedModuleNameToDirectoryMap = createCacheWithRedirects(compilerOptions);
1968+
let decodedPackageJsonMap: ESMap<Path, string> | undefined;
19661969
let decodedHashes: ESMap<ProgramBuildInfoAbsoluteFileId, string | undefined> | undefined;
19671970

19681971
let resolutions: (Resolution | false)[] | undefined;
@@ -1993,6 +1996,7 @@ namespace ts {
19931996
/*moduleNameToDirectoryMap*/ undefined,
19941997
/*decodedModuleNameToDirectoryMap*/ undefined,
19951998
),
1999+
getPackageJsonPath,
19962000
};
19972001

19982002
function fileExists(fileName: string) {
@@ -2020,6 +2024,39 @@ namespace ts {
20202024
return result;
20212025
}
20222026

2027+
function getPackageJsonPath(dirPath: Path) {
2028+
const fromCache = cacheResolutions?.dirToPackageJsonMap?.get(dirPath);
2029+
if (fromCache) {
2030+
return fileExists(fromCache) ? fromCache : undefined;
2031+
}
2032+
if (!resuableCacheResolutions?.cache.packageJsons) return;
2033+
if (!decodedPackageJsonMap) {
2034+
decodedPackageJsonMap = new Map();
2035+
const filePathDecoder = resuableCacheResolutions.getProgramBuildInfoFilePathDecoder();
2036+
for (const dirIdOrDirAndPackageJson of resuableCacheResolutions.cache.packageJsons) {
2037+
let dirPath: Path, packageJson: string;
2038+
if (isArray(dirIdOrDirAndPackageJson)) {
2039+
dirPath = filePathDecoder.toFilePath(dirIdOrDirAndPackageJson[0]);
2040+
packageJson = filePathDecoder.toFileAbsolutePath(dirIdOrDirAndPackageJson[1]);
2041+
}
2042+
else {
2043+
packageJson = filePathDecoder.toFileAbsolutePath(dirIdOrDirAndPackageJson);
2044+
dirPath = getDirectoryPath(toPath(packageJson, filePathDecoder.currentDirectory, filePathDecoder.getCanonicalFileName));
2045+
}
2046+
moduleNameToDirectorySet(
2047+
decodedPackageJsonMap,
2048+
dirPath,
2049+
packageJson,
2050+
identity,
2051+
fileName => toPath(fileName, filePathDecoder.currentDirectory, filePathDecoder.getCanonicalFileName),
2052+
noop
2053+
);
2054+
}
2055+
}
2056+
const fromDecoded = decodedPackageJsonMap.get(dirPath);
2057+
return fromDecoded && fileExists(fromDecoded) ? fromDecoded : undefined;
2058+
}
2059+
20232060
function getResolvedFromCache<T extends ResolvedModuleWithFailedLookupLocations | ResolvedTypeReferenceDirectiveWithFailedLookupLocations>(
20242061
cache: CacheWithRedirects<Path, ModeAwareCache<T>> | undefined,
20252062
reusableCache: ProgramBuildInfoResolutionCacheWithRedirects | undefined,

src/compiler/parser.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,12 @@ namespace ts {
732732
}
733733
}
734734

735+
/*@internal*/
736+
export interface SourceFilePackageJsonInfo {
737+
packageJsonLocations?: readonly string[];
738+
packageJsonScope?: PackageJsonInfo;
739+
}
740+
735741
export interface CreateSourceFileOptions {
736742
languageVersion: ScriptTarget;
737743
/**
@@ -746,8 +752,10 @@ namespace ts {
746752
* check specified by `isFileProbablyExternalModule` will be used to set the field.
747753
*/
748754
setExternalModuleIndicator?: (file: SourceFile) => void;
749-
/*@internal*/ packageJsonLocations?: readonly string[];
750-
/*@internal*/ packageJsonScope?: PackageJsonInfo;
755+
}
756+
757+
/*@internal*/
758+
export interface CreateSourceFileOptions extends SourceFilePackageJsonInfo {
751759
}
752760

753761
function setExternalModuleIndicator(sourceFile: SourceFile) {

src/compiler/program.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ namespace ts {
859859
* @returns `undefined` if the path has no relevant implied format, `ModuleKind.ESNext` for esm format, and `ModuleKind.CommonJS` for cjs format
860860
*/
861861
export function getImpliedNodeFormatForFile(fileName: Path, packageJsonInfoCache: PackageJsonInfoCache | undefined, host: ModuleResolutionHost, options: CompilerOptions): ResolutionMode {
862-
const result = getImpliedNodeFormatForFileWorker(fileName, packageJsonInfoCache, host, options);
862+
const result = getImpliedNodeFormatForFileWorker(fileName, packageJsonInfoCache, host, options, /*oldBuildInfoProgram*/ undefined);
863863
return typeof result === "object" ? result.impliedNodeFormat : result;
864864
}
865865

@@ -869,6 +869,7 @@ namespace ts {
869869
packageJsonInfoCache: PackageJsonInfoCache | undefined,
870870
host: ModuleResolutionHost,
871871
options: CompilerOptions,
872+
oldBuildInfoProgram: OldBuildInfoProgram | undefined,
872873
) {
873874
switch (getEmitModuleResolutionKind(options)) {
874875
case ModuleResolutionKind.Node16:
@@ -885,9 +886,16 @@ namespace ts {
885886
const packageJsonLocations: string[] = [];
886887
state.failedLookupLocations = packageJsonLocations;
887888
state.affectingLocations = packageJsonLocations;
888-
const packageJsonScope = getPackageScopeForPath(fileName, state);
889+
const fromOld = oldBuildInfoProgram?.getPackageJsonPath(getDirectoryPath(fileName));
890+
const packageJsonScope = fromOld ?
891+
getPackageJsonInfo(getDirectoryPath(fromOld), /*onlyRecordFailures*/ false, state) :
892+
getPackageScopeForPath(fileName, state);
889893
const impliedNodeFormat = packageJsonScope?.packageJsonContent.type === "module" ? ModuleKind.ESNext : ModuleKind.CommonJS;
890-
return { impliedNodeFormat, packageJsonLocations, packageJsonScope };
894+
return {
895+
impliedNodeFormat,
896+
packageJsonLocations: packageJsonLocations.length ? packageJsonLocations : undefined,
897+
packageJsonScope
898+
};
891899
}
892900
}
893901

@@ -1977,7 +1985,7 @@ namespace ts {
19771985
if (!newSourceFile) {
19781986
return StructureIsReused.Not;
19791987
}
1980-
newSourceFile.packageJsonLocations = sourceFileOptions.packageJsonLocations?.length ? sourceFileOptions.packageJsonLocations : undefined;
1988+
newSourceFile.packageJsonLocations = sourceFileOptions.packageJsonLocations;
19811989
newSourceFile.packageJsonScope = sourceFileOptions.packageJsonScope;
19821990

19831991
Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`");
@@ -3048,7 +3056,7 @@ namespace ts {
30483056
redirect.resolvedPath = resolvedPath;
30493057
redirect.originalFileName = originalFileName;
30503058
redirect.redirectInfo = { redirectTarget, unredirected };
3051-
redirect.packageJsonLocations = sourceFileOptions.packageJsonLocations?.length ? sourceFileOptions.packageJsonLocations : undefined;
3059+
redirect.packageJsonLocations = sourceFileOptions.packageJsonLocations;
30523060
redirect.packageJsonScope = sourceFileOptions.packageJsonScope;
30533061
sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
30543062
Object.defineProperties(redirect, {
@@ -3080,7 +3088,7 @@ namespace ts {
30803088
// It's a _little odd_ that we can't set `impliedNodeFormat` until the program step - but it's the first and only time we have a resolution cache
30813089
// and a freshly made source file node on hand at the same time, and we need both to set the field. Persisting the resolution cache all the way
30823090
// to the check and emit steps would be bad - so we much prefer detecting and storing the format information on the source file node upfront.
3083-
const result = getImpliedNodeFormatForFileWorker(toPath(fileName), moduleResolutionCache?.getPackageJsonInfoCache(), host, options);
3091+
const result = getImpliedNodeFormatForFileWorker(toPath(fileName), moduleResolutionCache?.getPackageJsonInfoCache(), host, options, oldBuildInfoProgram);
30843092
const languageVersion = getEmitScriptTarget(options);
30853093
const setExternalModuleIndicator = getSetExternalModuleIndicator(options);
30863094
return typeof result === "object" ?
@@ -3214,7 +3222,7 @@ namespace ts {
32143222
file.path = path;
32153223
file.resolvedPath = toPath(fileName);
32163224
file.originalFileName = originalFileName;
3217-
file.packageJsonLocations = sourceFileOptions.packageJsonLocations?.length ? sourceFileOptions.packageJsonLocations : undefined;
3225+
file.packageJsonLocations = sourceFileOptions.packageJsonLocations;
32183226
file.packageJsonScope = sourceFileOptions.packageJsonScope;
32193227
addFileIncludeReason(file, reason);
32203228

src/compiler/resolutionCache.ts

+31-12
Original file line numberDiff line numberDiff line change
@@ -360,18 +360,7 @@ namespace ts {
360360
/*moduleNameToDirectoryMap*/ undefined,
361361
);
362362
}
363-
const expected = isExternalOrCommonJsModule(newFile) ? newFile.packageJsonLocations?.length ?? 0 : 0;
364-
const existing = impliedFormatPackageJsons.get(newFile.path) ?? emptyArray;
365-
for (let i = existing.length; i < expected; i++) {
366-
createFileWatcherOfAffectingLocation(newFile.packageJsonLocations![i], /*forResolution*/ false);
367-
}
368-
if (existing.length > expected) {
369-
for (let i = expected; i < existing.length; i++) {
370-
fileWatchesOfAffectingLocations.get(existing[i])!.files--;
371-
}
372-
}
373-
if (expected) impliedFormatPackageJsons.set(newFile.path, newFile.packageJsonLocations!);
374-
else impliedFormatPackageJsons.delete(newFile.path);
363+
ensurePackageJsonWatchesForFile(newProgram, newFile);
375364
}
376365
if (needsResolutionUpdate) {
377366
const newProgramAutoTypeRefContainingFile = resolutionHost.toPath(newProgram.getAutomaticTypeDirectiveContainingFile());
@@ -419,6 +408,36 @@ namespace ts {
419408
});
420409
}
421410

411+
function ensurePackageJsonWatchesForFile(newProgram: Program, newFile: SourceFile) {
412+
const options = newProgram.getCompilerOptions();
413+
const existing = impliedFormatPackageJsons.get(newFile.path) ?? emptyArray;
414+
let expected = isExternalOrCommonJsModule(newFile) ? newFile.packageJsonLocations ?? emptyArray : emptyArray;
415+
if (!options.cacheResolutions) {
416+
for (let i = existing.length; i < expected.length; i++) {
417+
createFileWatcherOfAffectingLocation(expected[i], /*forResolution*/ false);
418+
}
419+
if (existing.length > expected.length) {
420+
for (let i = expected.length; i < existing.length; i++) {
421+
fileWatchesOfAffectingLocations.get(existing[i])!.files--;
422+
}
423+
}
424+
}
425+
else {
426+
// Do not watch failed lookups for source file's package.json when caching resolutions
427+
expected = expected.length && newFile.packageJsonScope ? [last(expected)] : expected;
428+
if (expected.length !== 1 || existing.length !== 1 || expected[0] !== existing[0]) {
429+
for (const location of expected) {
430+
createFileWatcherOfAffectingLocation(location, /*forResolution*/ false);
431+
}
432+
for (const location of existing) {
433+
fileWatchesOfAffectingLocations.get(location)!.files--;
434+
}
435+
}
436+
}
437+
if (expected.length) impliedFormatPackageJsons.set(newFile.path, expected);
438+
else impliedFormatPackageJsons.delete(newFile.path);
439+
}
440+
422441
function ensureResolutionsOfFile<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>(
423442
newProgram: Program,
424443
perFileCache: PerFileCache<T>,

src/compiler/types.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -3799,8 +3799,6 @@ namespace ts {
37993799
* CommonJS-output-format by the node module transformer and type checker, regardless of extension or context.
38003800
*/
38013801
impliedNodeFormat?: ResolutionMode;
3802-
/*@internal*/ packageJsonLocations?: readonly string[];
3803-
/*@internal*/ packageJsonScope?: PackageJsonInfo;
38043802

38053803
/* @internal */ scriptKind: ScriptKind;
38063804

@@ -3868,6 +3866,10 @@ namespace ts {
38683866
/* @internal */ endFlowNode?: FlowNode;
38693867
}
38703868

3869+
/*@internal*/
3870+
export interface SourceFile extends SourceFilePackageJsonInfo {
3871+
}
3872+
38713873
/* @internal */
38723874
export interface CommentDirective {
38733875
range: TextRange;
@@ -6629,6 +6631,7 @@ namespace ts {
66296631
mode: ResolutionMode,
66306632
redirectedReference: ResolvedProjectReference | undefined,
66316633
): ResolvedTypeReferenceDirectiveWithFailedLookupLocations | undefined;
6634+
getPackageJsonPath(dirPath: Path): string | undefined;
66326635
}
66336636

66346637
/*@internal*/

0 commit comments

Comments
 (0)