diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index d80283ce3c..1d2f5d2417 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -60,6 +60,10 @@ namespace ts { * Files pending to be emitted */ affectedFilesPendingEmit?: ReadonlyArray | undefined; + /** + * Files pending to be emitted kind. + */ + affectedFilesPendingEmitKind?: ReadonlyMap | undefined; /** * Current index to retrieve pending affected file */ @@ -70,6 +74,11 @@ namespace ts { hasReusableDiagnostic?: true; } + export const enum BuilderFileEmit { + DtsOnly, + Full + } + /** * State to store the changed files, affected files and cache semantic diagnostics */ @@ -127,7 +136,11 @@ namespace ts { /** * Files pending to be emitted */ - affectedFilesPendingEmit: ReadonlyArray | undefined; + affectedFilesPendingEmit: Path[] | undefined; + /** + * Files pending to be emitted kind. + */ + affectedFilesPendingEmitKind: Map | undefined; /** * Current index to retrieve pending affected file */ @@ -139,7 +152,7 @@ namespace ts { /** * Already seen emitted files */ - seenEmittedFiles: Map | undefined; + seenEmittedFiles: Map | undefined; /** * true if program has been emitted */ @@ -186,7 +199,8 @@ namespace ts { copyEntries(changedFilesSet, state.changedFilesSet); } if (!compilerOptions.outFile && !compilerOptions.out && oldState!.affectedFilesPendingEmit) { - state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit; + state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit.slice(); + state.affectedFilesPendingEmitKind = cloneMapOrUndefined(oldState!.affectedFilesPendingEmitKind); state.affectedFilesPendingEmitIndex = oldState!.affectedFilesPendingEmitIndex; } } @@ -233,7 +247,7 @@ namespace ts { if (oldCompilerOptions && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) { // Add all files to affectedFilesPendingEmit since emit changed - addToAffectedFilesPendingEmit(state, newProgram.getSourceFiles().map(f => f.path)); + newProgram.getSourceFiles().forEach(f => addToAffectedFilesPendingEmit(state, f.path, BuilderFileEmit.Full)); Debug.assert(state.seenAffectedFiles === undefined); state.seenAffectedFiles = createMap(); } @@ -295,7 +309,8 @@ namespace ts { newState.semanticDiagnosticsFromOldState = cloneMapOrUndefined(state.semanticDiagnosticsFromOldState); newState.program = state.program; newState.compilerOptions = state.compilerOptions; - newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit; + newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice(); + newState.affectedFilesPendingEmitKind = cloneMapOrUndefined(state.affectedFilesPendingEmitKind); newState.affectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex; newState.seenEmittedFiles = cloneMapOrUndefined(state.seenEmittedFiles); newState.programEmitComplete = state.programEmitComplete; @@ -373,19 +388,24 @@ namespace ts { /** * Returns next file to be emitted from files that retrieved semantic diagnostics but did not emit yet */ - function getNextAffectedFilePendingEmit(state: BuilderProgramState): SourceFile | undefined { + function getNextAffectedFilePendingEmit(state: BuilderProgramState) { const { affectedFilesPendingEmit } = state; if (affectedFilesPendingEmit) { const seenEmittedFiles = state.seenEmittedFiles || (state.seenEmittedFiles = createMap()); for (let i = state.affectedFilesPendingEmitIndex!; i < affectedFilesPendingEmit.length; i++) { const affectedFile = Debug.assertDefined(state.program).getSourceFileByPath(affectedFilesPendingEmit[i]); - if (affectedFile && !seenEmittedFiles.has(affectedFile.path)) { - // emit this file - state.affectedFilesPendingEmitIndex = i; - return affectedFile; + if (affectedFile) { + const seenKind = seenEmittedFiles.get(affectedFile.path); + const emitKind = Debug.assertDefined(Debug.assertDefined(state.affectedFilesPendingEmitKind).get(affectedFile.path)); + if (seenKind === undefined || seenKind < emitKind) { + // emit this file + state.affectedFilesPendingEmitIndex = i; + return { affectedFile, emitKind }; + } } } state.affectedFilesPendingEmit = undefined; + state.affectedFilesPendingEmitKind = undefined; state.affectedFilesPendingEmitIndex = undefined; } return undefined; @@ -442,7 +462,7 @@ namespace ts { ); // If not dts emit, nothing more to do if (getEmitDeclarations(state.compilerOptions)) { - addToAffectedFilesPendingEmit(state, [path]); + addToAffectedFilesPendingEmit(state, path, BuilderFileEmit.DtsOnly); } } } @@ -548,7 +568,13 @@ namespace ts { * This is called after completing operation on the next affected file. * The operations here are postponed to ensure that cancellation during the iteration is handled correctly */ - function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean, isEmitResult?: boolean) { + function doneWithAffectedFile( + state: BuilderProgramState, + affected: SourceFile | Program, + emitKind?: BuilderFileEmit, + isPendingEmit?: boolean, + isBuildInfoEmit?: boolean + ) { if (isBuildInfoEmit) { state.emittedBuildInfo = true; } @@ -558,8 +584,8 @@ namespace ts { } else { state.seenAffectedFiles!.set((affected as SourceFile).path, true); - if (isEmitResult) { - (state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).path, true); + if (emitKind !== undefined) { + (state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).path, emitKind); } if (isPendingEmit) { state.affectedFilesPendingEmitIndex!++; @@ -573,16 +599,23 @@ namespace ts { /** * Returns the result with affected file */ - function toAffectedFileResult(state: BuilderProgramState, result: T, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean): AffectedFileResult { - doneWithAffectedFile(state, affected, isPendingEmit, isBuildInfoEmit); + function toAffectedFileResult(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult { + doneWithAffectedFile(state, affected); return { result, affected }; } /** * Returns the result with affected file */ - function toAffectedFileEmitResult(state: BuilderProgramState, result: EmitResult, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean): AffectedFileResult { - doneWithAffectedFile(state, affected, isPendingEmit, isBuildInfoEmit, /*isEmitResult*/ true); + function toAffectedFileEmitResult( + state: BuilderProgramState, + result: EmitResult, + affected: SourceFile | Program, + emitKind: BuilderFileEmit, + isPendingEmit?: boolean, + isBuildInfoEmit?: boolean + ): AffectedFileResult { + doneWithAffectedFile(state, affected, emitKind, isPendingEmit, isBuildInfoEmit); return { result, affected }; } @@ -849,11 +882,12 @@ namespace ts { */ function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult { let affected = getNextAffectedFile(state, cancellationToken, computeHash); + let emitKind = BuilderFileEmit.Full; let isPendingEmitFile = false; if (!affected) { if (!state.compilerOptions.out && !state.compilerOptions.outFile) { - affected = getNextAffectedFilePendingEmit(state); - if (!affected) { + const pendingAffectedFile = getNextAffectedFilePendingEmit(state); + if (!pendingAffectedFile) { if (state.emittedBuildInfo) { return undefined; } @@ -865,10 +899,12 @@ namespace ts { // Otherwise just affected file affected.emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken), affected, + /*emitKind*/ BuilderFileEmit.Full, /*isPendingEmitFile*/ false, /*isBuildInfoEmit*/ true ); } + ({ affectedFile: affected, emitKind } = pendingAffectedFile); isPendingEmitFile = true; } else { @@ -886,10 +922,17 @@ namespace ts { state, // When whole program is affected, do emit only once (eg when --out or --outFile is specified) // Otherwise just affected file - Debug.assertDefined(state.program).emit(affected === state.program ? undefined : affected as SourceFile, writeFile || maybeBind(host, host.writeFile), cancellationToken, emitOnlyDtsFiles, customTransformers), + Debug.assertDefined(state.program).emit( + affected === state.program ? undefined : affected as SourceFile, + writeFile || maybeBind(host, host.writeFile), + cancellationToken, + emitOnlyDtsFiles || emitKind === BuilderFileEmit.DtsOnly, + customTransformers + ), affected, + emitKind, isPendingEmitFile, - ); + ); } /** @@ -953,7 +996,7 @@ namespace ts { // Add file to affected file pending emit to handle for later emit time if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) { - addToAffectedFilesPendingEmit(state, [(affected as SourceFile).path]); + addToAffectedFilesPendingEmit(state, (affected as SourceFile).path, BuilderFileEmit.Full); } // Get diagnostics for the affected file if its not ignored @@ -1006,8 +1049,14 @@ namespace ts { } } - function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilesPendingEmit: readonly Path[]) { - state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, affectedFilesPendingEmit); + function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilePendingEmit: Path, kind: BuilderFileEmit) { + if (!state.affectedFilesPendingEmit) state.affectedFilesPendingEmit = []; + if (!state.affectedFilesPendingEmitKind) state.affectedFilesPendingEmitKind = createMap(); + + const existingKind = state.affectedFilesPendingEmitKind.get(affectedFilePendingEmit); + state.affectedFilesPendingEmit.push(affectedFilePendingEmit); + state.affectedFilesPendingEmitKind.set(affectedFilePendingEmit, existingKind || kind); + // affectedFilesPendingEmitIndex === undefined // - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files // so start from 0 as array would be affectedFilesPendingEmit diff --git a/tests/baselines/reference/tsbuild/inferredTypeFromTransitiveModule/incremental-declaration-changes/inferred-type-from-transitive-module.js b/tests/baselines/reference/tsbuild/inferredTypeFromTransitiveModule/incremental-declaration-changes/inferred-type-from-transitive-module.js index e27d605560..556f32082f 100644 --- a/tests/baselines/reference/tsbuild/inferredTypeFromTransitiveModule/incremental-declaration-changes/inferred-type-from-transitive-module.js +++ b/tests/baselines/reference/tsbuild/inferredTypeFromTransitiveModule/incremental-declaration-changes/inferred-type-from-transitive-module.js @@ -26,7 +26,6 @@ import { LazyAction } from './bundling'; export declare const lazyBar: LazyAction<() => void, typeof import("./lazyIndex")>; -//// [/src/obj/index.js] file written with same contents //// [/src/obj/lazyIndex.d.ts] file written with same contents //// [/src/obj/lazyIndex.js] file written with same contents //// [/src/obj/tsconfig.tsbuildinfo]