8000 Store package.json for the directories in buildinfo · microsoft/TypeScript@e21738c · GitHub
[go: up one dir, main page]

Skip to content

Commit e21738c

Browse files
committed
Store package.json for the directories in buildinfo
1 parent 5c11252 commit e21738c

21 files changed

+2048
-282
lines changed

src/compiler/builder.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
createBuildInfo,
2525
createGetCanonicalFileName,
2626
createPerDirectoryAndNonRelativeNameCache,
27+
createPerNonRelativeNameCache,
2728
createProgram,
2829
CustomTransformers,
2930
Debug,
@@ -58,12 +59,14 @@ import {
5859
getTsBuildInfoEmitOutputFilePath,
5960
handleNoEmitOptions,
6061
HostForComputeHash,
62+
identity,
6163
isArray,
6264
isDeclarationFileName,
6365
isExternalModuleNameRelative,
6466
isJsonSourceFile,
6567
isNumber,
6668
isString,
69+
last,
6770
map,
6871
mapDefined,
6972
maybeBind,
@@ -79,6 +82,7 @@ import {
7982
PackageJsonInfoCache,
8083
Path,
8184
PerDirectoryAndNonRelativeNameCache,
85+
PerNonRelativeNameCache,
8286
Program,
8387
ProjectReference,
8488
ReadBuildProgramHost,
@@ -182,6 +186,7 @@ export interface ReusableBuilderProgramState extends BuilderState {
182186
cacheResolutions?: {
183187
modules: PerDirectoryAndNonRelativeNameCache<ResolvedModuleWithFailedLookupLocations> | undefined;
184188
typeRefs: PerDirectoryAndNonRelativeNameCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations> | undefined;
189+
packageJsons: Map<Path, string> | undefined;
185190
packageJsonCache: PackageJsonInfoCache | undefined;
186191
};
187192
resuableCacheResolutions?: {
@@ -986,13 +991,16 @@ export type ProgramBuildInfoResolutionCacheWithRedirects = ProgramBuildInfoResol
986991
redirects: readonly ProgramBuildInfoResolutionRedirectsCache[];
987992
};
988993
/** @internal */
994+
export type ProgramBuildInfoPackageJson = ProgramBuildInfoAbsoluteFileId | [dirId: ProgramBuildInfoFileId, packageJson: ProgramBuildInfoAbsoluteFileId];
995+
/** @internal */
989996
export interface ProgramBuildInfoCacheResolutions {
990997
resolutions: readonly ProgramBuildInfoResolution[];
991998
names: readonly string[];
992999
hash: readonly ProgramBuildInfoHash[] | undefined;
9931000
resolutionEntries: readonly ProgramBuildInfoResolutionEntry[];
9941001
modules: ProgramBuildInfoResolutionCacheWithRedirects | undefined;
9951002
typeRefs: ProgramBuildInfoResolutionCacheWithRedirects | undefined;
1003+
packageJsons: readonly ProgramBuildInfoPackageJson[] | undefined;
9961004
}
9971005
/** @internal */
9981006
export interface ProgramMultiFileEmitBuildInfo {
@@ -1313,6 +1321,7 @@ function getBuildInfo(state: BuilderProgramState, host: BuilderProgramHost, bund
13131321
if (!resolutions) return;
13141322
Debug.assertIsDefined(names);
13151323
Debug.assertIsDefined(resolutionEntries);
1324+
const packageJsons = toProgramBuildInfoPackageJsons(cacheResolutions?.packageJsons);
13161325
state.resuableCacheResolutions = {
13171326
cache: {
13181327
resolutions,
@@ -1321,6 +1330,7 @@ function getBuildInfo(state: BuilderProgramState, host: BuilderProgramHost, bund
13211330
resolutionEntries,
13221331
modules,
13231332
typeRefs,
1333+
packageJsons,
13241334
},
13251335
getProgramBuildInfoFilePathDecoder: memoize(() => getProgramBuildInfoFilePathDecoder(
13261336
fileNames,
@@ -1332,6 +1342,20 @@ function getBuildInfo(state: BuilderProgramState, host: BuilderProgramHost, bund
13321342
return state.resuableCacheResolutions.cache;
13331343
}
13341344

1345+
function toProgramBuildInfoPackageJsons(cache: Map<Path, string> | undefined): readonly ProgramBuildInfoPackageJson[] | undefined {
1346+
let result: ProgramBuildInfoPackageJson[] | undefined;
1347+
cache?.forEach((packageJson, dirPath) => {
1348+
const packageJsonDirPath = getDirectoryPath(toPath(packageJson, currentDirectory, state.program!.getCanonicalFileName));
1349+
(result ??= []).push(packageJsonDirPath === dirPath ?
1350+
toAbsoluteFileId(packageJson) :
1351+
[
1352+
toFileId(dirPath),
1353+
toAbsoluteFileId(packageJson),
1354+
]);
1355+
});
1356+
return result;
1357+
}
1358+
13351359
function toProgramBuildInfoResolutionCacheWithRedirects<T extends ResolvedModuleWithFailedLookupLocations | ResolvedTypeReferenceDirectiveWithFailedLookupLocations>(
13361360
cache: PerDirectoryAndNonRelativeNameCache<T> | undefined
13371361
): ProgramBuildInfoResolutionCacheWithRedirects | undefined {
@@ -1450,9 +1474,23 @@ function getCacheResolutions(state: BuilderProgramState) {
14501474
if (state.cacheResolutions || !state.compilerOptions.cacheResolutions) return state.cacheResolutions;
14511475
let modules: PerDirectoryAndNonRelativeNameCache<ResolvedModuleWithFailedLookupLocations> | undefined;
14521476
let typeRefs: PerDirectoryAndNonRelativeNameCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations> | undefined;
1477+
let packageJsons: Map<Path, string> | undefined;
1478+
let nonRelativePackageJsons: PerNonRelativeNameCache<string> | undefined;
14531479
for (const f of state.program!.getSourceFiles()) {
14541480
modules = toPerDirectoryAndNonRelativeNameCache(state, modules, getOriginalOrResolvedModuleFileName, f.resolvedModules, f);
14551481
typeRefs = toPerDirectoryAndNonRelativeNameCache(state, typeRefs, getOriginalOrResolvedTypeReferenceFileName, f.resolvedTypeReferenceDirectiveNames, f);
1482+
if (f.packageJsonScope) {
1483+
const dirPath = getDirectoryPath(f.resolvedPath);
1484+
if (!nonRelativePackageJsons?.getWithPath(dirPath)) {
1485+
const result = last(f.packageJsonLocations!);
1486+
(packageJsons ??= new Map()).set(dirPath, result);
1487+
(nonRelativePackageJsons ??= createPerNonRelativeNameCache(
1488+
state.program!.getCurrentDirectory(),
1489+
state.program!.getCanonicalFileName,
1490+
identity,
1491+
)).setWithPath(dirPath, result, ancestorPath => packageJsons!.delete(ancestorPath));
1492+
}
1493+
}
14561494
}
14571495
const automaticTypeDirectiveNames = state.program!.getAutomaticTypeDirectiveNames();
14581496
if (automaticTypeDirectiveNames.length) {
@@ -1462,6 +1500,7 @@ function getCacheResolutions(state: BuilderProgramState) {
14621500
return state.cacheResolutions = {
14631501
modules,
14641502
typeRefs,
1503+
packageJsons,
14651504
packageJsonCache: state.program!.getModuleResolutionCache()?.getPackageJsonInfoCache().clone(),
14661505
};
14671506
}

src/compiler/moduleNameResolver.ts

Lines changed: 85 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,90 @@ export function getOriginalOrResolvedTypeReferenceFileName(result: ResolvedTypeR
10331033
(result.resolvedTypeReferenceDirective.originalPath || result.resolvedTypeReferenceDirective.resolvedFileName);
10341034
}
10351035

1036+
/** @internal */
1037+
export function createPerNonRelativeNameCache<T>(
1038+
currentDirectory: string,
1039+
getCanonicalFileName: (s: string) => string,
1040+
getResolvedFileName: (result: T) => string | undefined,
1041+
): PerNonRelativeNameCache<T> {
1042+
const directoryPathMap = new Map<Path, T>();
1043+
1044+
return { get, set, getWithPath, setWithPath };
1045+
1046+
function get(directory: string): T | undefined {
1047+
return getWithPath(toPath(directory, currentDirectory, getCanonicalFileName));
1048+
}
1049+
1050+
function set(directory: string, result: T): void {
1051+
return setWithPath(toPath(directory, currentDirectory, getCanonicalFileName), result, noop);
1052+
}
1053+
1054+
function getWithPath(directory: Path): T | undefined {
1055+
return directoryPathMap.get(directory);
1056+
}
1057+
1058+
/**
1059+
* At first this function add entry directory -> module resolution result to the table.
1060+
* Then it computes the set of parent folders for 'directory' that should have the same module resolution result
1061+
* and for every parent folder in set it adds entry: parent -> module resolution. .
1062+
* Lets say we first directory name: /a/b/c/d/e and resolution result is: /a/b/bar.ts.
1063+
* Set of parent folders that should have the same result will be:
1064+
* [
1065+
* /a/b/c/d, /a/b/c, /a/b
1066+
* ]
1067+
* this means that request for module resolution from file in any of these folder will be immediately found in cache.
1068+
*/
1069+
function setWithPath(path: Path, result: T, ancestoryWorker: (directory: Path) => void): void {
1070+
// if entry is already in cache do nothing
1071+
if (directoryPathMap.has(path)) return;
1072+
directoryPathMap.set(path, result);
1073+
1074+
const resolvedFileName = getResolvedFileName(result);
1075+
// find common prefix between directory and resolved file name
1076+
// this common prefix should be the shortest path that has the same resolution
1077+
// directory: /a/b/c/d/e
1078+
// resolvedFileName: /a/b/foo.d.ts
1079+
// commonPrefix: /a/b
1080+
// for failed lookups cache the result for every directory up to root
1081+
const commonPrefix = resolvedFileName && getCommonPrefix(path, resolvedFileName);
1082+
let current = path;
1083+
while (current !== commonPrefix) {
1084+
const parent = getDirectoryPath(current);
1085+
if (parent === current) break;
1086+
if (directoryPathMap.has(parent)) {
1087+
ancestoryWorker(parent);
1088+
break;
1089+
}
1090+
directoryPathMap.set(parent, result);
1091+
ancestoryWorker(parent);
1092+
current = parent;
1093+
}
1094+
}
1095+
1096+
function getCommonPrefix(directory: Path, resolution: string) {
1097+
const resolutionDirectory = toPath(getDirectoryPath(resolution), currentDirectory, getCanonicalFileName);
1098+
1099+
// find first position where directory and resolution differs
1100+
let i = 0;
1101+
const limit = Math.min(directory.length, resolutionDirectory.length);
1102+
while (i < limit && directory.charCodeAt(i) === resolutionDirectory.charCodeAt(i)) {
1103+
i++;
1104+
}
1105+
if (i === directory.length && (resolutionDirectory.length === i || resolutionDirectory[i] === directorySeparator)) {
1106+
return directory;
1107+
}
1108+
const rootLength = getRootLength(directory);
1109+
if (i < rootLength) {
1110+
return undefined;
1111+
}
1112+
const sep = directory.lastIndexOf( 10000 directorySeparator, i - 1);
1113+
if (sep === -1) {
1114+
return undefined;
1115+
}
1116+
return directory.substr(0, Math.max(sep, rootLength));
1117+
}
1118+
}
1119+
10361120
function createNonRelativeNameResolutionCache<T>(
10371121
currentDirectory: string,
10381122
getCanonicalFileName: (s: string) => string,
@@ -1079,82 +1163,7 @@ function createNonRelativeNameResolutionCache<T>(
10791163
}
10801164

10811165
function createPerModuleNameCache(): PerNonRelativeNameCache<T> {
1082-
const directoryPathMap = new Map<Path, T>();
1083-
1084-
return { get, set, getWithPath, setWithPath };
1085-
1086-
function get(directory: string): T | undefined {
1087-
return getWithPath(toPath(directory, currentDirectory, getCanonicalFileName));
1088-
}
1089-
1090-
function set(directory: string, result: T): void {
1091-
return setWithPath(toPath(directory, currentDirectory, getCanonicalFileName), result, noop);
1092-
}
1093-
1094-
function getWithPath(directory: Path): T | undefined {
1095-
return directoryPathMap.get(directory);
1096-
}
1097-
1098-
/**
1099-
* At first this function add entry directory -> module resolution result to the table.
1100-
* Then it computes the set of parent folders for 'directory' that should have the same module resolution result
1101-
* and for every parent folder in set it adds entry: parent -> module resolution. .
1102-
* Lets say we first directory name: /a/b/c/d/e and resolution result is: /a/b/bar.ts.
1103-
* Set of parent folders that should have the same result will be:
1104-
* [
1105-
* /a/b/c/d, /a/b/c, /a/b
1106-
* ]
1107-
* this means that request for module resolution from file in any of these folder will be immediately found in cache.
1108-
*/
1109-
function setWithPath(path: Path, result: T, ancestoryWorker: (directory: Path) => void): void {
1110-
// if entry is already in cache do nothing
1111-
if (directoryPathMap.has(path)) return;
1112-
directoryPathMap.set(path, result);
1113-
1114-
const resolvedFileName = getResolvedFileName(result);
1115-
// find common prefix between directory and resolved file name
1116-
// this common prefix should be the shortest path that has the same resolution
1117-
// directory: /a/b/c/d/e
1118-
// resolvedFileName: /a/b/foo.d.ts
1119-
// commonPrefix: /a/b
1120-
// for failed lookups cache the result for every directory up to root
1121-
const commonPrefix = resolvedFileName && getCommonPrefix(path, resolvedFileName);
1122-
let current = path;
1123-
while (current !== commonPrefix) {
1124-
const parent = getDirectoryPath(current);
1125-
if (parent === current) break;
1126-
if (directoryPathMap.has(parent)) {
1127-
ancestoryWorker(parent);
1128-
break;
1129-
}
1130-
directoryPathMap.set(parent, result);
1131-
ancestoryWorker(parent);
1132-
current = parent;
1133-
}
1134-
}
1135-
1136-
function getCommonPrefix(directory: Path, resolution: string) {
1137-
const resolutionDirectory = toPath(getDirectoryPath(resolution), currentDirectory, getCanonicalFileName);
1138-
1139-
// find first position where directory and resolution differs
1140-
let i = 0;
1141-
const limit = Math.min(directory.length, resolutionDirectory.length);
1142-
while (i < limit && directory.charCodeAt(i) === resolutionDirectory.charCodeAt(i)) {
1143-
i++;
1144-
}
1145-
if (i === directory.length && (resolutionDirectory.length === i || resolutionDirectory[i] === directorySeparator)) {
1146-
return directory;
1147-
}
1148-
const rootLength = getRootLength(directory);
1149-
if (i < rootLength) {
1150-
return undefined;
1151-
}
1152-
const sep = directory.lastIndexOf(directorySeparator, i - 1);
1153-
if (sep === -1) {
1154-
return undefined;
1155-
}
1156-
return directory.substr(0, Math.max(sep, rootLength));
1157-
}
1166+
return createPerNonRelativeNameCache(currentDirectory, getCanonicalFileName, getResolvedFileName);
11581167
}
11591168
}
11601169

src/testRunner/unittests/tsc/cacheResolutions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,9 @@ describe("unittests:: tsc:: cacheResolutions::", () => {
323323
{
324324
caption: "Delete package.json",
325325
edit: fs => fs.unlinkSync(`/src/projects/project/package.json`),
326+
discrepancyExplanation: () => [
327+
`Buildinfo is not re-written so it has package.json map from before in incremental and no package.json map in clean build`
328+
]
326329
},
327330
{
328331
caption: "Add package json file with type module",

src/testRunner/unittests/tsc/helpers.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,14 +570,16 @@ type ReadableProgramBuildInfoResolutionCacheWithRedirects = ReadableProgramBuild
570570
redirects: readonly ReadableProgramBuildInfoResolutionRedirectsCache[];
571571
};
572572
type ReadableProgramBuildInfoHash = string | [file: string, hash: string];
573+
type ReadableProgramBuildInfoPackageJson = string | [dir: string, packageJson: string];
573574
type ReadableProgramBuildInfoCacheResolutions = Omit<ts.ProgramBuildInfoCacheResolutions,
574-
"resolutions" | "hash" | "resolutionEntries" | "modules" | "typeRefs"
575+
"resolutions" | "hash" | "resolutionEntries" | "modules" | "typeRefs" | "packageJsons"
575576
> & {
576577
resolutions: readonly ReadableWithOriginal<ReadableProgramBuildInfoResolution, ts.ProgramBuildInfoResolution>[];
577578
hash: readonly ReadableProgramBuildInfoHash[] | undefined;
578579
resolutionEntries: readonly ReadableWithOriginal<ReadableProgramBuildInfoResolutionEntry, ts.ProgramBuildInfoResolutionEntry>[];
579580
modules: ReadableProgramBuildInfoResolutionCacheWithRedirects | undefined;
580581
typeRefs: ReadableProgramBuildInfoResolutionCacheWithRedirects | undefined;
582+
packageJsons: readonly ReadableProgramBuildInfoPackageJson[] | undefined;
581583
};
582584

583585
type ReadableProgramMultiFileEmitBuildInfo = Omit<ts.ProgramMultiFileEmitBuildInfo,
@@ -755,10 +757,17 @@ function generateBuildInfoProgramBaseline(sys: ts.System, buildInfoPath: string,
755757
resolutionEntries: resolutionEntries.withOriginals,
756758
modules: toReadableProgramBuildInfoResolutionCacheWithRedirects(cacheResolutions.modules),
757759
typeRefs: toReadableProgramBuildInfoResolutionCacheWithRedirects(cacheResolutions.typeRefs),
760+
packageJsons: cacheResolutions.packageJsons?.map(toReadableProgramBuildInfoPackageJson),
758761
hash: cacheResolutions.hash?.map(toReadableProgramBuildInfoHash),
759762
};
760763
}
761764

765+
function toReadableProgramBuildInfoPackageJson(entry: ts.ProgramBuildInfoPackageJson): ReadableProgramBuildInfoPackageJson {
766+
return ts.isArray(entry) ?
767+
[toFileName(entry[0]), toFileName(entry[1])] :
768+
toFileName(entry);
769+
}
770+
762771
function toReadableProgramBuildInfoHash(hash: ts.ProgramBuildInfoHash): ReadableProgramBuildInfoHash {
763772
return ts.isArray(hash) ? [toFileName(hash[0]), hash[1]] : toFileName(hash);
764773
}

tests/baselines/reference/tsbuild/cacheResolutions/multi-file-discrepancies.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,11 @@ CleanBuild:
398398
}
399399
]
400400
}
401+
],
402+
"packageJsons": [
403+
"./node_modules/pkg0/package.json",
404+
"./node_modules/pkg2/package.json",
405+
"./node_modules/pkg3/package.json"
401406
]
402407
}
403408
},
@@ -800,6 +805,11 @@ IncrementalBuild:
800805
}
801806
]
802807
}
808+
],
809+
"packageJsons": [
810+
"./node_modules/pkg0/package.json",
811+
"./node_modules/pkg2/package.json",
812+
"./node_modules/pkg3/package.json"
803813
]
804814
}
805815
},

0 commit comments

Comments
 (0)
0