Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/compiler/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,9 @@ function hasSameKeys(
function createBuilderProgramState(
newProgram: Program,
oldState: Readonly<ReusableBuilderProgramState> | undefined,
disableUseFileVersionAsSignature?: boolean,
): BuilderProgramState {
const state = BuilderState.create(newProgram, oldState, /*disableUseFileVersionAsSignature*/ false) as BuilderProgramState;
const state = BuilderState.create(newProgram, oldState, !!disableUseFileVersionAsSignature) as BuilderProgramState;
state.program = newProgram;
const compilerOptions = newProgram.getCompilerOptions();
state.compilerOptions = compilerOptions;
Expand Down Expand Up @@ -1682,7 +1683,7 @@ export function createBuilderProgram(
return oldProgram;
}

const state = createBuilderProgramState(newProgram, oldState);
const state = createBuilderProgramState(newProgram, oldState, host.disableUseFileVersionAsSignature);
newProgram.getBuildInfo = () => getBuildInfo(toBuilderProgramStateWithDefinedProgram(state));

// To ensure that we arent storing any references to old program or new program without state
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/builderPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ export interface BuilderProgramHost {
* @internal
*/
storeSignatureInfo?: boolean;
/**
* When true, the builder always computes .d.ts shape signatures for source files instead
* of falling back to the file version (text hash) on the initial program. This makes
* change detection precise from the first edit onward — at the cost of computing
* declaration signatures during the initial build — and avoids invalidating a file's
* entire dependent closure on the first edit after a fresh build.
*/
disableUseFileVersionAsSignature?: boolean;
}

/** @internal */
Expand Down
17 changes: 15 additions & 2 deletions src/compiler/builderState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export enum SignatureInfo {
ComputedDts,
StoredSignatureAtEmit,
UsedVersion,
KeptExistingSignature,
}
/** @internal */
export interface BuilderState {
Expand Down Expand Up @@ -450,8 +451,20 @@ export namespace BuilderState {
}
// Default is to use file version as signature
if (latestSignature === undefined) {
latestSignature = sourceFile.version;
if (host.storeSignatureInfo) (state.signatureInfo ??= new Map()).set(sourceFile.resolvedPath, SignatureInfo.UsedVersion);
// Do not overwrite a previously computed .d.ts signature with the file version:
// replacing it would make the next comparison against a freshly computed
// signature a guaranteed mismatch, falsely invalidating the file's entire
// dependent closure even for edits that do not change the file's shape.
// Keep the stored signature instead; use the version only when no computed
// signature exists.
if (!sourceFile.isDeclarationFile && !state.useFileVersionAsSignature && prevSignature !== undefined && prevSignature !== sourceFile.version) {
latestSignature = prevSignature;
if (host.storeSignatureInfo) (state.signatureInfo ??= new Map()).set(sourceFile.resolvedPath, SignatureInfo.KeptExistingSignature);
}
else {
latestSignature = sourceFile.version;
if (host.storeSignatureInfo) (state.signatureInfo ??= new Map()).set(sourceFile.resolvedPath, SignatureInfo.UsedVersion);
}
Comment on lines +454 to +467
}
(state.oldSignatures ||= new Map()).set(sourceFile.resolvedPath, prevSignature || false);
(state.hasCalledUpdateShapeSignature ||= new Set()).add(sourceFile.resolvedPath);
Expand Down
4 changes: 4 additions & 0 deletions src/testRunner/unittests/helpers/baseline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ function baselineProgram(baseline: string[], [program, builderProgram]: CommandL
ts.Debug.assert(info?.version === info?.signature || !info?.signature);
baseline.push(path + " (used version)");
break;
case ts.SignatureInfo.KeptExistingSignature:
ts.Debug.assert(info?.signature && info.signature !== info.version);
baseline.push(path + " (kept existing computed signature)");
break;
default:
ts.Debug.assertNever(signatureInfo);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
0:: incremental-declaration-changes
*** Needs explanation
Incremental signature is neither dts signature nor file version for File:: ../index.ts
Incremental:: {
"original": {
"version": "6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n",
"signature": "-13696684486-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<(param: string) => void, typeof import(\"./lazyIndex\")>;\n"
},
"version": "6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n",
"signature": "-13696684486-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<(param: string) => void, typeof import(\"./lazyIndex\")>;\n"
}
Clean:: {
"original": {
"version": "6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n",
"signature": "-4053129224-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<() => void, typeof import(\"./lazyIndex\")>;\n"
},
"version": "6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n",
"signature": "-4053129224-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<() => void, typeof import(\"./lazyIndex\")>;\n"
}
Dts Signature:: "-4053129224-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<() => void, typeof import(\"./lazyIndex\")>;\n"
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
0:: incremental-declaration-changes
*** Needs explanation
Incremental signature is neither dts signature nor file version for File:: ../index.ts
Incremental:: {
"original": {
"version": "6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n",
"signature": "-13696684486-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<(param: string) => void, typeof import(\"./lazyIndex\")>;\n"
},
"version": "6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n",
"signature": "-13696684486-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<(param: string) => void, typeof import(\"./lazyIndex\")>;\n"
}
Clean:: {
"original": {
"version": "6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n",
"signature": "-4053129224-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<() => void, typeof import(\"./lazyIndex\")>;\n"
},
"version": "6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n",
"signature": "-4053129224-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<() => void, typeof import(\"./lazyIndex\")>;\n"
}
Dts Signature:: "-4053129224-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<() => void, typeof import(\"./lazyIndex\")>;\n"
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ export declare const lazyBar: LazyAction<() => void, typeof import("./lazyIndex"


//// [/home/src/workspaces/project/obj/tsconfig.tsbuildinfo]
{"fileNames":["../../../tslibs/ts/lib/lib.es6.d.ts","../bar.ts","../bundling.ts","../global.d.ts","../lazyindex.ts","../index.ts"],"fileIdsList":[[3,5],[2]],"fileInfos":[{"version":"-25093698414-interface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"10075719182-interface RawAction {\n (...args: any[]): Promise<any> | void;\n}\ninterface ActionFactory {\n <T extends RawAction>(target: T): T;\n}\ndeclare function foo<U extends any[] = any[]>(): ActionFactory;\nexport default foo()(function foobar(): void {\n});\n","signature":"-1866892563-declare const _default: () => void;\nexport default _default;\n"},{"version":"-5105594088-export class LazyModule<TModule> {\n constructor(private importCallback: () => Promise<TModule>) {}\n}\n\nexport class LazyAction<\n TAction extends (...args: any[]) => any,\n TModule\n> {\n constructor(_lazyModule: LazyModule<TModule>, _getter: (module: TModule) => TAction) {\n }\n}\n","signature":"-23343356903-export declare class LazyModule<TModule> {\n private importCallback;\n constructor(importCallback: () => Promise<TModule>);\n}\nexport declare class LazyAction<TAction extends (...args: any[]) => any, TModule> {\n constructor(_lazyModule: LazyModule<TModule>, _getter: (module: TModule) => TAction);\n}\n"},{"version":"-20910599262-interface PromiseConstructor {\n new <T>(): Promise<T>;\n}\ndeclare var Promise: PromiseConstructor;\ninterface Promise<T> {\n}\n","affectsGlobalScope":true},"-6956449754-export { default as bar } from './bar';\n",{"version":"6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n","signature":"-4053129224-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<() => void, typeof import(\"./lazyIndex\")>;\n"}],"root":[[2,6]],"options":{"declaration":true,"outDir":"./","target":2},"referencedMap":[[6,1],[5,2]],"semanticDiagnosticsPerFile":[[6,[{"start":97,"length":21,"messageText":"Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'es2022', 'esnext', 'commonjs', 'amd', 'system', 'umd', 'node16', 'node18', 'node20', or 'nodenext'.","category":1,"code":1323}]]],"version":"FakeTSVersion"}
{"fileNames":["../../../tslibs/ts/lib/lib.es6.d.ts","../bar.ts","../bundling.ts","../global.d.ts","../lazyindex.ts","../index.ts"],"fileIdsList":[[3,5],[2]],"fileInfos":[{"version":"-25093698414-interface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"10075719182-interface RawAction {\n (...args: any[]): Promise<any> | void;\n}\ninterface ActionFactory {\n <T extends RawAction>(target: T): T;\n}\ndeclare function foo<U extends any[] = any[]>(): ActionFactory;\nexport default foo()(function foobar(): void {\n});\n","signature":"-1866892563-declare const _default: () => void;\nexport default _default;\n"},{"version":"-5105594088-export class LazyModule<TModule> {\n constructor(private importCallback: () => Promise<TModule>) {}\n}\n\nexport class LazyAction<\n TAction extends (...args: any[]) => any,\n TModule\n> {\n constructor(_lazyModule: LazyModule<TModule>, _getter: (module: TModule) => TAction) {\n }\n}\n","signature":"-23343356903-export declare class LazyModule<TModule> {\n private importCallback;\n constructor(importCallback: () => Promise<TModule>);\n}\nexport declare class LazyAction<TAction extends (...args: any[]) => any, TModule> {\n constructor(_lazyModule: LazyModule<TModule>, _getter: (module: TModule) => TAction);\n}\n"},{"version":"-20910599262-interface PromiseConstructor {\n new <T>(): Promise<T>;\n}\ndeclare var Promise: PromiseConstructor;\ninterface Promise<T> {\n}\n","affectsGlobalScope":true},"-6956449754-export { default as bar } from './bar';\n",{"version":"6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n","signature":"-13696684486-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<(param: string) => void, typeof import(\"./lazyIndex\")>;\n"}],"root":[[2,6]],"options":{"declaration":true,"outDir":"./","target":2},"referencedMap":[[6,1],[5,2]],"semanticDiagnosticsPerFile":[[6,[{"start":97,"length":21,"messageText":"Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'es2022', 'esnext', 'commonjs', 'amd', 'system', 'umd', 'node16', 'node18', 'node20', or 'nodenext'.","category":1,"code":1323}]]],"version":"FakeTSVersion"}

//// [/home/src/workspaces/project/obj/tsconfig.tsbuildinfo.readable.baseline.txt]
{
Expand Down Expand Up @@ -379,10 +379,10 @@ export declare const lazyBar: LazyAction<() => void, typeof import("./lazyIndex"
"../index.ts": {
"original": {
"version": "6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n",
"signature": "-4053129224-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<() => void, typeof import(\"./lazyIndex\")>;\n"
"signature": "-13696684486-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<(param: string) => void, typeof import(\"./lazyIndex\")>;\n"
},
"version": "6186344161-import { LazyAction, LazyModule } from './bundling';\nconst lazyModule = new LazyModule(() =>\n import('./lazyIndex')\n);\nexport const lazyBar = new LazyAction(lazyModule, m => m.bar);\n",
"signature": "-4053129224-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<() => void, typeof import(\"./lazyIndex\")>;\n"
"signature": "-13696684486-import { LazyAction } from './bundling';\nexport declare const lazyBar: LazyAction<(param: string) => void, typeof import(\"./lazyIndex\")>;\n"
}
},
"root": [
Expand Down Expand Up @@ -429,7 +429,7 @@ export declare const lazyBar: LazyAction<() => void, typeof import("./lazyIndex"
]
],
"version": "FakeTSVersion",
"size": 2682
"size": 2696
}


Expand Down
Loading