Skip to content

Commit 6995a90

Browse files
authored
Merge pull request #33358 from microsoft/dtsOnlyEmit
Instead of emitting js as well as d.ts files when only d.ts emit could change, emit only d.ts files since program.emit has that option
2 parents fb9b3fe + c0f0d9f commit 6995a90

File tree

72 files changed

+3273
-17791
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+3273
-17791
lines changed

src/compiler/builder.ts

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ namespace ts {
6060
* Files pending to be emitted
6161
*/
6262
affectedFilesPendingEmit?: readonly Path[] | undefined;
63+
/**
64+
* Files pending to be emitted kind.
65+
*/
66+
affectedFilesPendingEmitKind?: ReadonlyMap<BuilderFileEmit> | undefined;
6367
/**
6468
* Current index to retrieve pending affected file
6569
*/
@@ -70,6 +74,11 @@ namespace ts {
7074
hasReusableDiagnostic?: true;
7175
}
7276

77+
export const enum BuilderFileEmit {
78+
DtsOnly,
79+
Full
80+
}
81+
7382
/**
7483
* State to store the changed files, affected files and cache semantic diagnostics
7584
*/
@@ -127,7 +136,11 @@ namespace ts {
127136
/**
128137
* Files pending to be emitted
129138
*/
130-
affectedFilesPendingEmit: readonly Path[] | undefined;
139+
affectedFilesPendingEmit: Path[] | undefined;
140+
/**
141+
* Files pending to be emitted kind.
142+
*/
143+
affectedFilesPendingEmitKind: Map<BuilderFileEmit> | undefined;
131144
/**
132145
* Current index to retrieve pending affected file
133146
*/
@@ -139,7 +152,7 @@ namespace ts {
139152
/**
140153
* Already seen emitted files
141154
*/
142-
seenEmittedFiles: Map<true> | undefined;
155+
seenEmittedFiles: Map<BuilderFileEmit> | undefined;
143156
/**
144157
* true if program has been emitted
145158
*/
@@ -186,7 +199,8 @@ namespace ts {
186199
copyEntries(changedFilesSet, state.changedFilesSet);
187200
}
188201
if (!compilerOptions.outFile && !compilerOptions.out && oldState!.affectedFilesPendingEmit) {
189-
state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit;
202+
state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit.slice();
203+
state.affectedFilesPendingEmitKind = cloneMapOrUndefined(oldState!.affectedFilesPendingEmitKind);
190204
state.affectedFilesPendingEmitIndex = oldState!.affectedFilesPendingEmitIndex;
191205
}
192206
}
@@ -233,7 +247,7 @@ namespace ts {
233247

234248
if (oldCompilerOptions && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) {
235249
// Add all files to affectedFilesPendingEmit since emit changed
236-
addToAffectedFilesPendingEmit(state, newProgram.getSourceFiles().map(f => f.path));
250+
newProgram.getSourceFiles().forEach(f => addToAffectedFilesPendingEmit(state, f.path, BuilderFileEmit.Full));
237251
Debug.assert(state.seenAffectedFiles === undefined);
238252
state.seenAffectedFiles = createMap<true>();
239253
}
@@ -295,7 +309,8 @@ namespace ts {
295309
newState.semanticDiagnosticsFromOldState = cloneMapOrUndefined(state.semanticDiagnosticsFromOldState);
296310
newState.program = state.program;
297311
newState.compilerOptions = state.compilerOptions;
298-
newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit;
312+
newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice();
313+
newState.affectedFilesPendingEmitKind = cloneMapOrUndefined(state.affectedFilesPendingEmitKind);
299314
newState.affectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex;
300315
newState.seenEmittedFiles = cloneMapOrUndefined(state.seenEmittedFiles);
301316
newState.programEmitComplete = state.programEmitComplete;
@@ -373,19 +388,24 @@ namespace ts {
373388
/**
374389
* Returns next file to be emitted from files that retrieved semantic diagnostics but did not emit yet
375390
*/
376-
function getNextAffectedFilePendingEmit(state: BuilderProgramState): SourceFile | undefined {
391+
function getNextAffectedFilePendingEmit(state: BuilderProgramState) {
377392
const { affectedFilesPendingEmit } = state;
378393
if (affectedFilesPendingEmit) {
379394
const seenEmittedFiles = state.seenEmittedFiles || (state.seenEmittedFiles = createMap());
380395
for (let i = state.affectedFilesPendingEmitIndex!; i < affectedFilesPendingEmit.length; i++) {
381396
const affectedFile = Debug.assertDefined(state.program).getSourceFileByPath(affectedFilesPendingEmit[i]);
382-
if (affectedFile && !seenEmittedFiles.has(affectedFile.path)) {
383-
// emit this file
384-
state.affectedFilesPendingEmitIndex = i;
385-
return affectedFile;
397+
if (affectedFile) {
398+
const seenKind = seenEmittedFiles.get(affectedFile.path);
399+
const emitKind = Debug.assertDefined(Debug.assertDefined(state.affectedFilesPendingEmitKind).get(affectedFile.path));
400+
if (seenKind === undefined || seenKind < emitKind) {
401+
// emit this file
402+
state.affectedFilesPendingEmitIndex = i;
403+
return { affectedFile, emitKind };
404+
}
386405
}
387406
}
388407
state.affectedFilesPendingEmit = undefined;
408+
state.affectedFilesPendingEmitKind = undefined;
389409
state.affectedFilesPendingEmitIndex = undefined;
390410
}
391411
return undefined;
@@ -442,7 +462,7 @@ namespace ts {
442462
);
443463
// If not dts emit, nothing more to do
444464
if (getEmitDeclarations(state.compilerOptions)) {
445-
addToAffectedFilesPendingEmit(state, [path]);
465+
addToAffectedFilesPendingEmit(state, path, BuilderFileEmit.DtsOnly);
446466
}
447467
}
448468
}
@@ -548,7 +568,13 @@ namespace ts {
548568
* This is called after completing operation on the next affected file.
549569
* The operations here are postponed to ensure that cancellation during the iteration is handled correctly
550570
*/
551-
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean, isEmitResult?: boolean) {
571+
function doneWithAffectedFile(
572+
state: BuilderProgramState,
573+
affected: SourceFile | Program,
574+
emitKind?: BuilderFileEmit,
575+
isPendingEmit?: boolean,
576+
isBuildInfoEmit?: boolean
577+
) {
552578
if (isBuildInfoEmit) {
553579
state.emittedBuildInfo = true;
554580
}
@@ -558,8 +584,8 @@ namespace ts {
558584
}
559585
else {
560586
state.seenAffectedFiles!.set((affected as SourceFile).path, true);
561-
if (isEmitResult) {
562-
(state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).path, true);
587+
if (emitKind !== undefined) {
588+
(state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).path, emitKind);
563589
}
564590
if (isPendingEmit) {
565591
state.affectedFilesPendingEmitIndex!++;
@@ -573,16 +599,23 @@ namespace ts {
573599
/**
574600
* Returns the result with affected file
575601
*/
576-
function toAffectedFileResult<T>(state: BuilderProgramState, result: T, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean): AffectedFileResult<T> {
577-
doneWithAffectedFile(state, affected, isPendingEmit, isBuildInfoEmit);
602+
function toAffectedFileResult<T>(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult<T> {
603+
doneWithAffectedFile(state, affected);
578604
return { result, affected };
579605
}
580606

581607
/**
582608
* Returns the result with affected file
583609
*/
584-
function toAffectedFileEmitResult(state: BuilderProgramState, result: EmitResult, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean): AffectedFileResult<EmitResult> {
585-
doneWithAffectedFile(state, affected, isPendingEmit, isBuildInfoEmit, /*isEmitResult*/ true);
610+
function toAffectedFileEmitResult(
611+
state: BuilderProgramState,
612+
result: EmitResult,
613+
affected: SourceFile | Program,
614+
emitKind: BuilderFileEmit,
615+
isPendingEmit?: boolean,
616+
isBuildInfoEmit?: boolean
617+
): AffectedFileResult<EmitResult> {
618+
doneWithAffectedFile(state, affected, emitKind, isPendingEmit, isBuildInfoEmit);
586619
return { result, affected };
587620
}
588621

@@ -849,11 +882,12 @@ namespace ts {
849882
*/
850883
function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult> {
851884
let affected = getNextAffectedFile(state, cancellationToken, computeHash);
885+
let emitKind = BuilderFileEmit.Full;
852886
let isPendingEmitFile = false;
853887
if (!affected) {
854888
if (!state.compilerOptions.out && !state.compilerOptions.outFile) {
855-
affected = getNextAffectedFilePendingEmit(state);
856-
if (!affected) {
889+
const pendingAffectedFile = getNextAffectedFilePendingEmit(state);
890+
if (!pendingAffectedFile) {
857891
if (state.emittedBuildInfo) {
858892
return undefined;
859893
}
@@ -865,10 +899,12 @@ namespace ts {
865899
// Otherwise just affected file
866900
affected.emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken),
867901
affected,
902+
/*emitKind*/ BuilderFileEmit.Full,
868903
/*isPendingEmitFile*/ false,
869904
/*isBuildInfoEmit*/ true
870905
);
871906
}
907+
({ affectedFile: affected, emitKind } = pendingAffectedFile);
872908
isPendingEmitFile = true;
873909
}
874910
else {
@@ -886,10 +922,17 @@ namespace ts {
886922
state,
887923
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
888924
// Otherwise just affected file
889-
Debug.assertDefined(state.program).emit(affected === state.program ? undefined : affected as SourceFile, writeFile || maybeBind(host, host.writeFile), cancellationToken, emitOnlyDtsFiles, customTransformers),
925+
Debug.assertDefined(state.program).emit(
926+
affected === state.program ? undefined : affected as SourceFile,
927+
writeFile || maybeBind(host, host.writeFile),
928+
cancellationToken,
929+
emitOnlyDtsFiles || emitKind === BuilderFileEmit.DtsOnly,
930+
customTransformers
931+
),
890932
affected,
933+
emitKind,
891934
isPendingEmitFile,
892-
);
935+
);
893936
}
894937

895938
/**
@@ -953,7 +996,7 @@ namespace ts {
953996

954997
// Add file to affected file pending emit to handle for later emit time
955998
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
956-
addToAffectedFilesPendingEmit(state, [(affected as SourceFile).path]);
999+
addToAffectedFilesPendingEmit(state, (affected as SourceFile).path, BuilderFileEmit.Full);
9571000
}
9581001

9591002
// Get diagnostics for the affected file if its not ignored
@@ -1006,8 +1049,14 @@ namespace ts {
10061049
}
10071050
}
10081051

1009-
function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilesPendingEmit: readonly Path[]) {
1010-
state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, affectedFilesPendingEmit);
1052+
function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilePendingEmit: Path, kind: BuilderFileEmit) {
1053+
if (!state.affectedFilesPendingEmit) state.affectedFilesPendingEmit = [];
1054+
if (!state.affectedFilesPendingEmitKind) state.affectedFilesPendingEmitKind = createMap();
1055+
1056+
const existingKind = state.affectedFilesPendingEmitKind.get(affectedFilePendingEmit);
1057+
state.affectedFilesPendingEmit.push(affectedFilePendingEmit);
1058+
state.affectedFilesPendingEmitKind.set(affectedFilePendingEmit, existingKind || kind);
1059+
10111060
// affectedFilesPendingEmitIndex === undefined
10121061
// - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files
10131062
// so start from 0 as array would be affectedFilesPendingEmit

src/compiler/builderState.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ namespace ts {
1515
/*@internal*/
1616
namespace ts {
1717
export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean,
18-
cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput {
18+
cancellationToken?: CancellationToken, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitOutput {
1919
const outputFiles: OutputFile[] = [];
20-
const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
20+
const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, forceDtsEmit);
2121
return { outputFiles, emitSkipped: emitResult.emitSkipped, exportedModulesFromDeclarationEmit: emitResult.exportedModulesFromDeclarationEmit };
2222

2323
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) {
@@ -344,7 +344,14 @@ namespace ts.BuilderState {
344344
}
345345
}
346346
else {
347-
const emitOutput = getFileEmitOutput(programOfThisState, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken);
347+
const emitOutput = getFileEmitOutput(
348+
programOfThisState,
349+
sourceFile,
350+
/*emitOnlyDtsFiles*/ true,
351+
cancellationToken,
352+
/*customTransformers*/ undefined,
353+
/*forceDtsEmit*/ true
354+
);
348355
const firstDts = emitOutput.outputFiles &&
349356
programOfThisState.getCompilerOptions().declarationMap ?
350357
emitOutput.outputFiles.length > 1 ? emitOutput.outputFiles[1] : undefined :

src/compiler/emitter.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace ts {
2020
export function forEachEmittedFile<T>(
2121
host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) => T,
2222
sourceFilesOrTargetSourceFile?: readonly SourceFile[] | SourceFile,
23-
emitOnlyDtsFiles = false,
23+
forceDtsEmit = false,
2424
onlyBuildInfo?: boolean,
2525
includeBuildInfo?: boolean) {
2626
const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile);
@@ -29,7 +29,7 @@ namespace ts {
2929
const prepends = host.getPrependNodes();
3030
if (sourceFiles.length || prepends.length) {
3131
const bundle = createBundle(sourceFiles, prepends);
32-
const result = action(getOutputPathsFor(bundle, host, emitOnlyDtsFiles), bundle);
32+
const result = action(getOutputPathsFor(bundle, host, forceDtsEmit), bundle);
3333
if (result) {
3434
return result;
3535
}
@@ -38,7 +38,7 @@ namespace ts {
3838
else {
3939
if (!onlyBuildInfo) {
4040
for (const sourceFile of sourceFiles) {
41-
const result = action(getOutputPathsFor(sourceFile, host, emitOnlyDtsFiles), sourceFile);
41+
const result = action(getOutputPathsFor(sourceFile, host, forceDtsEmit), sourceFile);
4242
if (result) {
4343
return result;
4444
}
@@ -227,7 +227,7 @@ namespace ts {
227227

228228
/*@internal*/
229229
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
230-
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnlyDtsFiles?: boolean, onlyBuildInfo?: boolean): EmitResult {
230+
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnlyDtsFiles?: boolean, onlyBuildInfo?: boolean, forceDtsEmit?: boolean): EmitResult {
231231
const compilerOptions = host.getCompilerOptions();
232232
const sourceMapDataList: SourceMapEmitResult[] | undefined = (compilerOptions.sourceMap || compilerOptions.inlineSourceMap || getAreDeclarationMapsEnabled(compilerOptions)) ? [] : undefined;
233233
const emittedFilesList: string[] | undefined = compilerOptions.listEmittedFiles ? [] : undefined;
@@ -241,7 +241,14 @@ namespace ts {
241241

242242
// Emit each output file
243243
enter();
244-
forEachEmittedFile(host, emitSourceFileOrBundle, getSourceFilesToEmit(host, targetSourceFile), emitOnlyDtsFiles, onlyBuildInfo, !targetSourceFile);
244+
forEachEmittedFile(
245+
host,
246+
emitSourceFileOrBundle,
247+
getSourceFilesToEmit(host, targetSourceFile),
248+
forceDtsEmit,
249+
onlyBuildInfo,
250+
!targetSourceFile
251+
);
245252
exit();
246253

247254

@@ -400,7 +407,7 @@ namespace ts {
400407
});
401408
const declBlocked = (!!declarationTransform.diagnostics && !!declarationTransform.diagnostics.length) || !!host.isEmitBlocked(declarationFilePath) || !!compilerOptions.noEmit;
402409
emitSkipped = emitSkipped || declBlocked;
403-
if (!declBlocked || emitOnlyDtsFiles) {
410+
if (!declBlocked || forceDtsEmit) {
404411
Debug.assert(declarationTransform.transformed.length === 1, "Should only see one output from the decl transform");
405412
printSourceFileOrBundle(
406413
declarationFilePath,
@@ -415,7 +422,7 @@ namespace ts {
415422
// Explicitly do not passthru either `inline` option
416423
}
417424
);
418-
if (emitOnlyDtsFiles && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) {
425+
if (forceDtsEmit && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) {
419426
const sourceFile = declarationTransform.transformed[0];
420427
exportedModulesFromDeclarationEmit = sourceFile.exportedModulesFromDeclarationEmit;
421428
}

0 commit comments

Comments
 (0)