mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Handle dts emit time errors for incremental scenarios and buildInfo (#56295)
This commit is contained in:
+138
-87
@@ -133,6 +133,8 @@ export interface ReusableBuilderProgramState extends BuilderState {
|
||||
* Cache of bind and check diagnostics for files with their Path being the key
|
||||
*/
|
||||
semanticDiagnosticsPerFile?: Map<Path, readonly ReusableDiagnostic[] | readonly Diagnostic[]> | undefined;
|
||||
/** Cache of dts emit diagnostics for files with their Path being the key */
|
||||
emitDiagnosticsPerFile?: Map<Path, readonly ReusableDiagnostic[] | readonly Diagnostic[]> | undefined;
|
||||
/**
|
||||
* The map has key by source file's path that has been changed
|
||||
*/
|
||||
@@ -202,6 +204,8 @@ export interface BuilderProgramState extends BuilderState, ReusableBuilderProgra
|
||||
* Cache of bind and check diagnostics for files with their Path being the key
|
||||
*/
|
||||
semanticDiagnosticsPerFile: Map<Path, readonly Diagnostic[]> | undefined;
|
||||
/** Cache of dts emit diagnostics for files with their Path being the key */
|
||||
emitDiagnosticsPerFile?: Map<Path, readonly Diagnostic[]> | undefined;
|
||||
/**
|
||||
* The map has key by source file's path that has been changed
|
||||
*/
|
||||
@@ -261,6 +265,8 @@ export type SavedBuildProgramEmitState =
|
||||
| "outSignature"
|
||||
| "latestChangedDtsFile"
|
||||
| "hasChangedEmitSignature"
|
||||
| "buildInfoEmitPending"
|
||||
| "emitDiagnosticsPerFile"
|
||||
>
|
||||
& { changedFilesSet: BuilderProgramState["changedFilesSet"] | undefined; };
|
||||
|
||||
@@ -376,25 +382,33 @@ function createBuilderProgramState(newProgram: Program, oldState: Readonly<Reusa
|
||||
// Register file as changed file and do not copy semantic diagnostics, since all changed files need to be re-evaluated
|
||||
addFileToChangeSet(state, sourceFilePath);
|
||||
}
|
||||
else if (canCopySemanticDiagnostics) {
|
||||
else {
|
||||
const sourceFile = newProgram.getSourceFileByPath(sourceFilePath)!;
|
||||
|
||||
if (sourceFile.isDeclarationFile && !copyDeclarationFileDiagnostics) return;
|
||||
if (sourceFile.hasNoDefaultLib && !copyLibFileDiagnostics) return;
|
||||
|
||||
// Unchanged file copy diagnostics
|
||||
const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
|
||||
if (diagnostics) {
|
||||
state.semanticDiagnosticsPerFile!.set(
|
||||
const emitDiagnostics = oldState!.emitDiagnosticsPerFile?.get(sourceFilePath);
|
||||
if (emitDiagnostics) {
|
||||
(state.emitDiagnosticsPerFile ??= new Map()).set(
|
||||
sourceFilePath,
|
||||
oldState!.hasReusableDiagnostic ?
|
||||
convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], newProgram) :
|
||||
repopulateDiagnostics(diagnostics as readonly Diagnostic[], newProgram),
|
||||
convertToDiagnostics(emitDiagnostics as readonly ReusableDiagnostic[], newProgram) :
|
||||
repopulateDiagnostics(emitDiagnostics as readonly Diagnostic[], newProgram),
|
||||
);
|
||||
if (!state.semanticDiagnosticsFromOldState) {
|
||||
state.semanticDiagnosticsFromOldState = new Set();
|
||||
}
|
||||
|
||||
if (canCopySemanticDiagnostics) {
|
||||
if (sourceFile.isDeclarationFile && !copyDeclarationFileDiagnostics) return;
|
||||
if (sourceFile.hasNoDefaultLib && !copyLibFileDiagnostics) return;
|
||||
|
||||
// Unchanged file copy diagnostics
|
||||
const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
|
||||
if (diagnostics) {
|
||||
state.semanticDiagnosticsPerFile!.set(
|
||||
sourceFilePath,
|
||||
oldState!.hasReusableDiagnostic ?
|
||||
convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], newProgram) :
|
||||
repopulateDiagnostics(diagnostics as readonly Diagnostic[], newProgram),
|
||||
);
|
||||
(state.semanticDiagnosticsFromOldState ??= new Set()).add(sourceFilePath);
|
||||
}
|
||||
state.semanticDiagnosticsFromOldState.add(sourceFilePath);
|
||||
}
|
||||
}
|
||||
if (canCopyEmitSignatures) {
|
||||
@@ -570,6 +584,8 @@ function backupBuilderProgramEmitState(state: Readonly<BuilderProgramState>): Sa
|
||||
latestChangedDtsFile: state.latestChangedDtsFile,
|
||||
hasChangedEmitSignature: state.hasChangedEmitSignature,
|
||||
changedFilesSet: outFilePath ? new Set(state.changedFilesSet) : undefined,
|
||||
buildInfoEmitPending: state.buildInfoEmitPending,
|
||||
emitDiagnosticsPerFile: state.emitDiagnosticsPerFile && new Map(state.emitDiagnosticsPerFile),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -581,6 +597,8 @@ function restoreBuilderProgramEmitState(state: BuilderProgramState, savedEmitSta
|
||||
state.outSignature = savedEmitState.outSignature;
|
||||
state.latestChangedDtsFile = savedEmitState.latestChangedDtsFile;
|
||||
state.hasChangedEmitSignature = savedEmitState.hasChangedEmitSignature;
|
||||
state.buildInfoEmitPending = savedEmitState.buildInfoEmitPending;
|
||||
state.emitDiagnosticsPerFile = savedEmitState.emitDiagnosticsPerFile;
|
||||
if (savedEmitState.changedFilesSet) state.changedFilesSet = savedEmitState.changedFilesSet;
|
||||
}
|
||||
|
||||
@@ -692,6 +710,19 @@ function getNextAffectedFilePendingEmit(state: BuilderProgramState, emitOnlyDtsF
|
||||
});
|
||||
}
|
||||
|
||||
function getNextPendingEmitDiagnosticsFile(state: BuilderProgramState) {
|
||||
if (!state.emitDiagnosticsPerFile?.size) return undefined;
|
||||
return forEachEntry(state.emitDiagnosticsPerFile, (diagnostics, path) => {
|
||||
const affectedFile = state.program!.getSourceFileByPath(path);
|
||||
if (!affectedFile || !sourceFileMayBeEmitted(affectedFile, state.program!)) {
|
||||
state.emitDiagnosticsPerFile!.delete(path);
|
||||
return undefined;
|
||||
}
|
||||
const seenKind = state.seenEmittedFiles?.get(affectedFile.resolvedPath) || BuilderFileEmit.None;
|
||||
if (!(seenKind & BuilderFileEmit.AllDts)) return { affectedFile, diagnostics, seenKind };
|
||||
});
|
||||
}
|
||||
|
||||
function removeDiagnosticsOfLibraryFiles(state: BuilderProgramState) {
|
||||
if (!state.cleanedDiagnosticsOfLibFiles) {
|
||||
state.cleanedDiagnosticsOfLibFiles = true;
|
||||
@@ -997,6 +1028,7 @@ export interface ProgramMultiFileEmitBuildInfo {
|
||||
referencedMap: ProgramBuildInfoReferencedMap | undefined;
|
||||
exportedModulesMap: ProgramBuildInfoReferencedMap | undefined;
|
||||
semanticDiagnosticsPerFile: ProgramBuildInfoDiagnostic[] | undefined;
|
||||
emitDiagnosticsPerFile: ProgramBuildInfoDiagnostic[] | undefined;
|
||||
affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
|
||||
changeFileSet: readonly ProgramBuildInfoFileId[] | undefined;
|
||||
emitSignatures: readonly ProgramBuildInfoEmitSignature[] | undefined;
|
||||
@@ -1140,21 +1172,7 @@ function getBuildInfo(state: BuilderProgramState, bundle: BundleBuildInfo | unde
|
||||
});
|
||||
}
|
||||
|
||||
let semanticDiagnosticsPerFile: ProgramBuildInfoDiagnostic[] | undefined;
|
||||
if (state.semanticDiagnosticsPerFile) {
|
||||
for (const key of arrayFrom(state.semanticDiagnosticsPerFile.keys()).sort(compareStringsCaseSensitive)) {
|
||||
const value = state.semanticDiagnosticsPerFile.get(key)!;
|
||||
(semanticDiagnosticsPerFile ||= []).push(
|
||||
value.length ?
|
||||
[
|
||||
toFileId(key),
|
||||
convertToReusableDiagnostics(value, relativeToBuildInfo),
|
||||
] :
|
||||
toFileId(key),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const semanticDiagnosticsPerFile = convertToProgramBuildInfoDiagnostics(state.semanticDiagnosticsPerFile);
|
||||
let affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
|
||||
if (state.affectedFilesPendingEmit?.size) {
|
||||
const fullEmitForOptions = getBuilderFileEmit(state.compilerOptions);
|
||||
@@ -1181,7 +1199,7 @@ function getBuildInfo(state: BuilderProgramState, bundle: BundleBuildInfo | unde
|
||||
(changeFileSet ||= []).push(toFileId(path));
|
||||
}
|
||||
}
|
||||
|
||||
const emitDiagnosticsPerFile = convertToProgramBuildInfoDiagnostics(state.emitDiagnosticsPerFile);
|
||||
const program: ProgramMultiFileEmitBuildInfo = {
|
||||
fileNames,
|
||||
fileInfos,
|
||||
@@ -1191,6 +1209,7 @@ function getBuildInfo(state: BuilderProgramState, bundle: BundleBuildInfo | unde
|
||||
referencedMap,
|
||||
exportedModulesMap,
|
||||
semanticDiagnosticsPerFile,
|
||||
emitDiagnosticsPerFile,
|
||||
affectedFilesPendingEmit,
|
||||
changeFileSet,
|
||||
emitSignatures,
|
||||
@@ -1257,80 +1276,97 @@ function getBuildInfo(state: BuilderProgramState, bundle: BundleBuildInfo | unde
|
||||
(result ||= {})[name] = convertToReusableCompilerOptionValue(
|
||||
optionInfo,
|
||||
options[name] as CompilerOptionsValue,
|
||||
relativeToBuildInfoEnsuringAbsolutePath,
|
||||
);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function convertToReusableCompilerOptionValue(option: CommandLineOption | undefined, value: CompilerOptionsValue, relativeToBuildInfo: (path: string) => string) {
|
||||
if (option) {
|
||||
Debug.assert(option.type !== "listOrElement");
|
||||
if (option.type === "list") {
|
||||
const values = value as readonly string[];
|
||||
if (option.element.isFilePath && values.length) {
|
||||
return values.map(relativeToBuildInfo);
|
||||
function convertToReusableCompilerOptionValue(option: CommandLineOption | undefined, value: CompilerOptionsValue) {
|
||||
if (option) {
|
||||
Debug.assert(option.type !== "listOrElement");
|
||||
if (option.type === "list") {
|
||||
const values = value as readonly string[];
|
||||
if (option.element.isFilePath && values.length) {
|
||||
return values.map(relativeToBuildInfoEnsuringAbsolutePath);
|
||||
}
|
||||
}
|
||||
else if (option.isFilePath) {
|
||||
return relativeToBuildInfoEnsuringAbsolutePath(value as string);
|
||||
}
|
||||
}
|
||||
else if (option.isFilePath) {
|
||||
return relativeToBuildInfo(value as string);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function convertToReusableDiagnostics(diagnostics: readonly Diagnostic[], relativeToBuildInfo: (path: string) => string): readonly ReusableDiagnostic[] {
|
||||
Debug.assert(!!diagnostics.length);
|
||||
return diagnostics.map(diagnostic => {
|
||||
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic, relativeToBuildInfo);
|
||||
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
|
||||
result.reportDeprecated = diagnostic.reportsDeprecated;
|
||||
result.source = diagnostic.source;
|
||||
result.skippedOn = diagnostic.skippedOn;
|
||||
const { relatedInformation } = diagnostic;
|
||||
result.relatedInformation = relatedInformation ?
|
||||
relatedInformation.length ?
|
||||
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r, relativeToBuildInfo)) :
|
||||
[] :
|
||||
undefined;
|
||||
function convertToProgramBuildInfoDiagnostics(diagnostics: Map<Path, readonly Diagnostic[]> | undefined) {
|
||||
let result: ProgramBuildInfoDiagnostic[] | undefined;
|
||||
if (diagnostics) {
|
||||
for (const key of arrayFrom(diagnostics.keys()).sort(compareStringsCaseSensitive)) {
|
||||
const value = diagnostics.get(key)!;
|
||||
(result ||= []).push(
|
||||
value.length ?
|
||||
[
|
||||
toFileId(key),
|
||||
convertToReusableDiagnostics(value),
|
||||
] :
|
||||
toFileId(key),
|
||||
);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation, relativeToBuildInfo: (path: string) => string): ReusableDiagnosticRelatedInformation {
|
||||
const { file } = diagnostic;
|
||||
return {
|
||||
...diagnostic,
|
||||
file: file ? relativeToBuildInfo(file.resolvedPath) : undefined,
|
||||
messageText: isString(diagnostic.messageText) ? diagnostic.messageText : convertToReusableDiagnosticMessageChain(diagnostic.messageText),
|
||||
};
|
||||
}
|
||||
function convertToReusableDiagnostics(diagnostics: readonly Diagnostic[]): readonly ReusableDiagnostic[] {
|
||||
Debug.assert(!!diagnostics.length);
|
||||
return diagnostics.map(diagnostic => {
|
||||
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic);
|
||||
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
|
||||
result.reportDeprecated = diagnostic.reportsDeprecated;
|
||||
result.source = diagnostic.source;
|
||||
result.skippedOn = diagnostic.skippedOn;
|
||||
const { relatedInformation } = diagnostic;
|
||||
result.relatedInformation = relatedInformation ?
|
||||
relatedInformation.length ?
|
||||
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r)) :
|
||||
[] :
|
||||
undefined;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function convertToReusableDiagnosticMessageChain(chain: DiagnosticMessageChain): ReusableDiagnosticMessageChain {
|
||||
if (chain.repopulateInfo) {
|
||||
function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation): ReusableDiagnosticRelatedInformation {
|
||||
const { file } = diagnostic;
|
||||
return {
|
||||
info: chain.repopulateInfo(),
|
||||
next: convertToReusableDiagnosticMessageChainArray(chain.next),
|
||||
...diagnostic,
|
||||
file: file ? relativeToBuildInfo(file.resolvedPath) : undefined,
|
||||
messageText: isString(diagnostic.messageText) ? diagnostic.messageText : convertToReusableDiagnosticMessageChain(diagnostic.messageText),
|
||||
};
|
||||
}
|
||||
const next = convertToReusableDiagnosticMessageChainArray(chain.next);
|
||||
return next === chain.next ? chain : { ...chain, next };
|
||||
}
|
||||
|
||||
function convertToReusableDiagnosticMessageChainArray(array: DiagnosticMessageChain[] | undefined): ReusableDiagnosticMessageChain[] | undefined {
|
||||
if (!array) return array;
|
||||
return forEach(array, (chain, index) => {
|
||||
const reusable = convertToReusableDiagnosticMessageChain(chain);
|
||||
if (chain === reusable) return undefined;
|
||||
const result: ReusableDiagnosticMessageChain[] = index > 0 ? array.slice(0, index - 1) : [];
|
||||
result.push(reusable);
|
||||
for (let i = index + 1; i < array.length; i++) {
|
||||
result.push(convertToReusableDiagnosticMessageChain(array[i]));
|
||||
function convertToReusableDiagnosticMessageChain(chain: DiagnosticMessageChain): ReusableDiagnosticMessageChain {
|
||||
if (chain.repopulateInfo) {
|
||||
return {
|
||||
info: chain.repopulateInfo(),
|
||||
next: convertToReusableDiagnosticMessageChainArray(chain.next),
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}) || array;
|
||||
const next = convertToReusableDiagnosticMessageChainArray(chain.next);
|
||||
return next === chain.next ? chain : { ...chain, next };
|
||||
}
|
||||
|
||||
function convertToReusableDiagnosticMessageChainArray(array: DiagnosticMessageChain[] | undefined): ReusableDiagnosticMessageChain[] | undefined {
|
||||
if (!array) return array;
|
||||
return forEach(array, (chain, index) => {
|
||||
const reusable = convertToReusableDiagnosticMessageChain(chain);
|
||||
if (chain === reusable) return undefined;
|
||||
const result: ReusableDiagnosticMessageChain[] = index > 0 ? array.slice(0, index - 1) : [];
|
||||
result.push(reusable);
|
||||
for (let i = index + 1; i < array.length; i++) {
|
||||
result.push(convertToReusableDiagnosticMessageChain(array[i]));
|
||||
}
|
||||
return result;
|
||||
}) || array;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -1497,6 +1533,14 @@ export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, hos
|
||||
if (!outFile(state.compilerOptions)) {
|
||||
const pendingAffectedFile = getNextAffectedFilePendingEmit(state, emitOnlyDtsFiles);
|
||||
if (!pendingAffectedFile) {
|
||||
const pendingForDiagnostics = getNextPendingEmitDiagnosticsFile(state);
|
||||
if (pendingForDiagnostics) {
|
||||
(state.seenEmittedFiles ??= new Map()).set(pendingForDiagnostics.affectedFile.resolvedPath, pendingForDiagnostics.seenKind | BuilderFileEmit.AllDts);
|
||||
return {
|
||||
result: { emitSkipped: true, diagnostics: pendingForDiagnostics.diagnostics },
|
||||
affected: pendingForDiagnostics.affectedFile,
|
||||
};
|
||||
}
|
||||
// Emit buildinfo if pending
|
||||
if (!state.buildInfoEmitPending) return undefined;
|
||||
const affected = state.program!;
|
||||
@@ -1550,6 +1594,7 @@ export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, hos
|
||||
const pendingKind = getPendingEmitKind(existingPending, emitKind | existing);
|
||||
if (pendingKind) (state.affectedFilesPendingEmit ??= new Map()).set(affectedSourceFile.resolvedPath, pendingKind);
|
||||
else state.affectedFilesPendingEmit?.delete(affectedSourceFile.resolvedPath);
|
||||
if (result.diagnostics.length) (state.emitDiagnosticsPerFile ??= new Map()).set(affectedSourceFile.resolvedPath, result.diagnostics);
|
||||
}
|
||||
else {
|
||||
// In program clear our changed files since any emit handles all changes
|
||||
@@ -1765,6 +1810,7 @@ export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, hos
|
||||
function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilePendingEmit: Path, kind: BuilderFileEmit) {
|
||||
const existingKind = state.affectedFilesPendingEmit?.get(affectedFilePendingEmit) || BuilderFileEmit.None;
|
||||
(state.affectedFilesPendingEmit ??= new Map()).set(affectedFilePendingEmit, existingKind | kind);
|
||||
state.emitDiagnosticsPerFile?.delete(affectedFilePendingEmit);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -1840,7 +1886,8 @@ export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo,
|
||||
compilerOptions: program.options ? convertToOptionsWithAbsolutePaths(program.options, toAbsolutePath) : {},
|
||||
referencedMap: toManyToManyPathMap(program.referencedMap),
|
||||
exportedModulesMap: toManyToManyPathMap(program.exportedModulesMap),
|
||||
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => toFilePath(isNumber(value) ? value : value[0]), value => isNumber(value) ? emptyArray : value[1]),
|
||||
semanticDiagnosticsPerFile: toPerFileDiagnostics(program.semanticDiagnosticsPerFile),
|
||||
emitDiagnosticsPerFile: toPerFileDiagnostics(program.emitDiagnosticsPerFile),
|
||||
hasReusableDiagnostic: true,
|
||||
affectedFilesPendingEmit: program.affectedFilesPendingEmit && arrayToMap(program.affectedFilesPendingEmit, value => toFilePath(isNumber(value) ? value : value[0]), value => toBuilderFileEmit(value, fullEmitForOptions!)),
|
||||
changedFilesSet: new Set(map(program.changeFileSet, toFilePath)),
|
||||
@@ -1900,6 +1947,10 @@ export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo,
|
||||
referenceMap.forEach(([fileId, fileIdListId]) => map.set(toFilePath(fileId), toFilePathsSet(fileIdListId)));
|
||||
return map;
|
||||
}
|
||||
|
||||
function toPerFileDiagnostics(diagnostics: readonly ProgramBuildInfoDiagnostic[] | undefined): Map<Path, readonly ReusableDiagnostic[]> | undefined {
|
||||
return diagnostics && arrayToMap(diagnostics, value => toFilePath(isNumber(value) ? value : value[0]), value => isNumber(value) ? emptyArray : value[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
||||
+40
-48
@@ -800,28 +800,6 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
|
||||
emitBuildInfo(bundleBuildInfo, buildInfoPath);
|
||||
tracing?.pop();
|
||||
|
||||
if (!emitSkipped && emittedFilesList) {
|
||||
if (!emitOnly) {
|
||||
if (jsFilePath) {
|
||||
emittedFilesList.push(jsFilePath);
|
||||
}
|
||||
if (sourceMapFilePath) {
|
||||
emittedFilesList.push(sourceMapFilePath);
|
||||
}
|
||||
if (buildInfoPath) {
|
||||
emittedFilesList.push(buildInfoPath);
|
||||
}
|
||||
}
|
||||
if (emitOnly !== EmitOnly.Js) {
|
||||
if (declarationFilePath) {
|
||||
emittedFilesList.push(declarationFilePath);
|
||||
}
|
||||
if (declarationMapPath) {
|
||||
emittedFilesList.push(declarationMapPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function relativeToBuildInfo(path: string) {
|
||||
return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory!, path, host.getCanonicalFileName));
|
||||
}
|
||||
@@ -837,6 +815,7 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
|
||||
const buildInfo = host.getBuildInfo(bundle) || createBuildInfo(/*program*/ undefined, bundle);
|
||||
// Pass buildinfo as additional data to avoid having to reparse
|
||||
writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText(buildInfo), /*writeByteOrderMark*/ false, /*sourceFiles*/ undefined, { buildInfo });
|
||||
emittedFilesList?.push(buildInfoPath);
|
||||
}
|
||||
|
||||
function emitJsFileOrBundle(
|
||||
@@ -888,6 +867,13 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
|
||||
// Clean up emit nodes on parse tree
|
||||
transform.dispose();
|
||||
if (bundleBuildInfo) bundleBuildInfo.js = printer.bundleFileInfo;
|
||||
|
||||
if (emittedFilesList) {
|
||||
emittedFilesList.push(jsFilePath);
|
||||
if (sourceMapFilePath) {
|
||||
emittedFilesList.push(sourceMapFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emitDeclarationFileOrBundle(
|
||||
@@ -917,35 +903,35 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
|
||||
}
|
||||
}
|
||||
|
||||
const printerOptions: PrinterOptions = {
|
||||
removeComments: compilerOptions.removeComments,
|
||||
newLine: compilerOptions.newLine,
|
||||
noEmitHelpers: true,
|
||||
module: compilerOptions.module,
|
||||
target: compilerOptions.target,
|
||||
sourceMap: !forceDtsEmit && compilerOptions.declarationMap,
|
||||
inlineSourceMap: compilerOptions.inlineSourceMap,
|
||||
extendedDiagnostics: compilerOptions.extendedDiagnostics,
|
||||
onlyPrintJsDocStyle: true,
|
||||
omitBraceSourceMapPositions: true,
|
||||
writeBundleFileInfo: !!bundleBuildInfo,
|
||||
recordInternalSection: !!bundleBuildInfo,
|
||||
relativeToBuildInfo,
|
||||
};
|
||||
|
||||
const declarationPrinter = createPrinter(printerOptions, {
|
||||
// resolver hooks
|
||||
hasGlobalName: resolver.hasGlobalName,
|
||||
|
||||
// transform hooks
|
||||
onEmitNode: declarationTransform.emitNodeWithNotification,
|
||||
isEmitNotificationEnabled: declarationTransform.isEmitNotificationEnabled,
|
||||
substituteNode: declarationTransform.substituteNode,
|
||||
});
|
||||
const declBlocked = (!!declarationTransform.diagnostics && !!declarationTransform.diagnostics.length) || !!host.isEmitBlocked(declarationFilePath) || !!compilerOptions.noEmit;
|
||||
emitSkipped = emitSkipped || declBlocked;
|
||||
if (!declBlocked || forceDtsEmit) {
|
||||
Debug.assert(declarationTransform.transformed.length === 1, "Should only see one output from the decl transform");
|
||||
const printerOptions: PrinterOptions = {
|
||||
removeComments: compilerOptions.removeComments,
|
||||
newLine: compilerOptions.newLine,
|
||||
noEmitHelpers: true,
|
||||
module: compilerOptions.module,
|
||||
target: compilerOptions.target,
|
||||
sourceMap: !forceDtsEmit && compilerOptions.declarationMap,
|
||||
inlineSourceMap: compilerOptions.inlineSourceMap,
|
||||
extendedDiagnostics: compilerOptions.extendedDiagnostics,
|
||||
onlyPrintJsDocStyle: true,
|
||||
omitBraceSourceMapPositions: true,
|
||||
writeBundleFileInfo: !!bundleBuildInfo,
|
||||
recordInternalSection: !!bundleBuildInfo,
|
||||
relativeToBuildInfo,
|
||||
};
|
||||
|
||||
const declarationPrinter = createPrinter(printerOptions, {
|
||||
// resolver hooks
|
||||
hasGlobalName: resolver.hasGlobalName,
|
||||
|
||||
// transform hooks
|
||||
onEmitNode: declarationTransform.emitNodeWithNotification,
|
||||
isEmitNotificationEnabled: declarationTransform.isEmitNotificationEnabled,
|
||||
substituteNode: declarationTransform.substituteNode,
|
||||
});
|
||||
printSourceFileOrBundle(
|
||||
declarationFilePath,
|
||||
declarationMapPath,
|
||||
@@ -959,9 +945,15 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
|
||||
// Explicitly do not passthru either `inline` option
|
||||
},
|
||||
);
|
||||
if (emittedFilesList) {
|
||||
emittedFilesList.push(declarationFilePath);
|
||||
if (declarationMapPath) {
|
||||
emittedFilesList.push(declarationMapPath);
|
||||
}
|
||||
}
|
||||
if (bundleBuildInfo) bundleBuildInfo.dts = declarationPrinter.bundleFileInfo;
|
||||
}
|
||||
declarationTransform.dispose();
|
||||
if (bundleBuildInfo) bundleBuildInfo.dts = declarationPrinter.bundleFileInfo;
|
||||
}
|
||||
|
||||
function collectLinkedAliases(node: Node) {
|
||||
|
||||
@@ -1809,7 +1809,8 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
|
||||
if (
|
||||
(buildInfo.program as ProgramMultiFileEmitBuildInfo).changeFileSet?.length ||
|
||||
(!project.options.noEmit ?
|
||||
(buildInfo.program as ProgramMultiFileEmitBuildInfo).affectedFilesPendingEmit?.length :
|
||||
(buildInfo.program as ProgramMultiFileEmitBuildInfo).affectedFilesPendingEmit?.length ||
|
||||
(buildInfo.program as ProgramMultiFileEmitBuildInfo).emitDiagnosticsPerFile?.length :
|
||||
some((buildInfo.program as ProgramMultiFileEmitBuildInfo).semanticDiagnosticsPerFile, isArray))
|
||||
) {
|
||||
return {
|
||||
|
||||
@@ -180,13 +180,14 @@ export type ReadableProgramBuildInfoFileInfo<T> = Omit<ts.BuilderState.FileInfo,
|
||||
export type ReadableProgramBuildInfoRoot =
|
||||
| [original: ts.ProgramBuildInfoFileId, readable: string]
|
||||
| [original: ts.ProgramBuildInfoRootStartEnd, readable: readonly string[]];
|
||||
export type ReadableProgramMultiFileEmitBuildInfo = Omit<ts.ProgramMultiFileEmitBuildInfo, "fileIdsList" | "fileInfos" | "root" | "referencedMap" | "exportedModulesMap" | "semanticDiagnosticsPerFile" | "affectedFilesPendingEmit" | "changeFileSet" | "emitSignatures"> & {
|
||||
export type ReadableProgramMultiFileEmitBuildInfo = Omit<ts.ProgramMultiFileEmitBuildInfo, "fileIdsList" | "fileInfos" | "root" | "referencedMap" | "exportedModulesMap" | "semanticDiagnosticsPerFile" | "emitDiagnosticsPerFile" | "affectedFilesPendingEmit" | "changeFileSet" | "emitSignatures"> & {
|
||||
fileNamesList: readonly (readonly string[])[] | undefined;
|
||||
fileInfos: ts.MapLike<ReadableProgramBuildInfoFileInfo<ts.ProgramMultiFileEmitBuildInfoFileInfo>>;
|
||||
root: readonly ReadableProgramBuildInfoRoot[];
|
||||
referencedMap: ts.MapLike<string[]> | undefined;
|
||||
exportedModulesMap: ts.MapLike<string[]> | undefined;
|
||||
semanticDiagnosticsPerFile: readonly ReadableProgramBuildInfoDiagnostic[] | undefined;
|
||||
emitDiagnosticsPerFile: readonly ReadableProgramBuildInfoDiagnostic[] | undefined;
|
||||
affectedFilesPendingEmit: readonly ReadableProgramBuilderInfoFilePendingEmit[] | undefined;
|
||||
changeFileSet: readonly string[] | undefined;
|
||||
emitSignatures: readonly ReadableProgramBuildInfoEmitSignature[] | undefined;
|
||||
@@ -240,11 +241,8 @@ function generateBuildInfoProgramBaseline(sys: ts.System, buildInfoPath: string,
|
||||
options: buildInfo.program.options,
|
||||
referencedMap: toMapOfReferencedSet(buildInfo.program.referencedMap),
|
||||
exportedModulesMap: toMapOfReferencedSet(buildInfo.program.exportedModulesMap),
|
||||
semanticDiagnosticsPerFile: buildInfo.program.semanticDiagnosticsPerFile?.map(d =>
|
||||
ts.isNumber(d) ?
|
||||
toFileName(d) :
|
||||
[toFileName(d[0]), d[1]]
|
||||
),
|
||||
semanticDiagnosticsPerFile: toReadableProgramBuildInfoDiagnosticsPerFile(buildInfo.program.semanticDiagnosticsPerFile),
|
||||
emitDiagnosticsPerFile: toReadableProgramBuildInfoDiagnosticsPerFile(buildInfo.program.emitDiagnosticsPerFile),
|
||||
affectedFilesPendingEmit: buildInfo.program.affectedFilesPendingEmit?.map(value => toReadableProgramBuilderInfoFilePendingEmit(value, fullEmitForOptions!)),
|
||||
changeFileSet: buildInfo.program.changeFileSet?.map(toFileName),
|
||||
emitSignatures: buildInfo.program.emitSignatures?.map(s =>
|
||||
@@ -334,6 +332,14 @@ function generateBuildInfoProgramBaseline(sys: ts.System, buildInfoPath: string,
|
||||
result = result ? `${result} | ${flag}` : flag;
|
||||
}
|
||||
}
|
||||
|
||||
function toReadableProgramBuildInfoDiagnosticsPerFile(diagnostics: ts.ProgramBuildInfoDiagnostic[] | undefined): readonly ReadableProgramBuildInfoDiagnostic[] | undefined {
|
||||
return diagnostics?.map(d =>
|
||||
ts.isNumber(d) ?
|
||||
toFileName(d) :
|
||||
[toFileName(d[0]), d[1]]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function toPathWithSystem(sys: ts.System, fileName: string): ts.Path {
|
||||
|
||||
@@ -364,6 +364,26 @@ function verifyTscEditDiscrepancies({
|
||||
expectedIndex++;
|
||||
});
|
||||
}
|
||||
if (incrementalReadableBuildInfo?.program?.emitDiagnosticsPerFile) {
|
||||
incrementalReadableBuildInfo.program.emitDiagnosticsPerFile.forEach(([actualFileOrArray]) => {
|
||||
const actualFile = ts.isString(actualFileOrArray) ? actualFileOrArray : actualFileOrArray[0];
|
||||
if (
|
||||
!ts.find(
|
||||
(cleanReadableBuildInfo!.program! as ReadableProgramMultiFileEmitBuildInfo).emitDiagnosticsPerFile,
|
||||
([expectedFileOrArray]) => actualFile === (ts.isString(expectedFileOrArray) ? expectedFileOrArray : expectedFileOrArray[0]),
|
||||
) && !ts.find(
|
||||
(cleanReadableBuildInfo!.program! as ReadableProgramMultiFileEmitBuildInfo).affectedFilesPendingEmit,
|
||||
([expectedFileOrArray]) => actualFile === (ts.isString(expectedFileOrArray) ? expectedFileOrArray : expectedFileOrArray[0]),
|
||||
)
|
||||
) {
|
||||
addBaseline(
|
||||
`Incremental build contains ${actualFile} file has errors, clean build does not have errors or does not mark is as pending emit: ${outputFile}::`,
|
||||
`Incremental buildInfoText:: ${incrementalBuildText}`,
|
||||
`Clean buildInfoText:: ${cleanBuildText}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,6 +473,7 @@ function getBuildInfoForIncrementalCorrectnessCheck(text: string | undefined): {
|
||||
options: { ...readableBuildInfo.program.options, noEmit: undefined },
|
||||
exportedModulesMap: undefined,
|
||||
affectedFilesPendingEmit: undefined,
|
||||
emitDiagnosticsPerFile: undefined,
|
||||
latestChangedDtsFile: readableBuildInfo.program.latestChangedDtsFile ? "FakeFileName" : undefined,
|
||||
},
|
||||
size: undefined, // Size doesnt need to be equal
|
||||
|
||||
@@ -8,6 +8,10 @@ import {
|
||||
jsonToReadableText,
|
||||
} from "../helpers";
|
||||
import {
|
||||
libContent,
|
||||
} from "../helpers/contents";
|
||||
import {
|
||||
noChangeOnlyRuns,
|
||||
verifyTsc,
|
||||
} from "../helpers/tsc";
|
||||
import {
|
||||
@@ -132,3 +136,43 @@ export function fn4() {
|
||||
commandLineArgs: ["--b", "/src/packages/pkg2/tsconfig.json", "--verbose"],
|
||||
});
|
||||
});
|
||||
|
||||
verifyTsc({
|
||||
scenario: "declarationEmit",
|
||||
subScenario: "reports dts generation errors with incremental",
|
||||
commandLineArgs: ["-b", `/src/project`, "--explainFiles", "--listEmittedFiles", "--v"],
|
||||
fs: () =>
|
||||
loadProjectFromFiles({
|
||||
"/src/project/tsconfig.json": jsonToReadableText({
|
||||
compilerOptions: {
|
||||
module: "NodeNext",
|
||||
moduleResolution: "NodeNext",
|
||||
incremental: true,
|
||||
declaration: true,
|
||||
skipLibCheck: true,
|
||||
skipDefaultLibCheck: true,
|
||||
},
|
||||
}),
|
||||
"/src/project/index.ts": dedent`
|
||||
import ky from 'ky';
|
||||
export const api = ky.extend({});
|
||||
`,
|
||||
"/src/project/package.json": jsonToReadableText({
|
||||
type: "module",
|
||||
}),
|
||||
"/src/project/node_modules/ky/distribution/index.d.ts": dedent`
|
||||
type KyInstance = {
|
||||
extend(options: Record<string,unknown>): KyInstance;
|
||||
}
|
||||
declare const ky: KyInstance;
|
||||
export default ky;
|
||||
`,
|
||||
"/src/project/node_modules/ky/package.json": jsonToReadableText({
|
||||
name: "ky",
|
||||
type: "module",
|
||||
main: "./distribution/index.js",
|
||||
}),
|
||||
"/lib/lib.esnext.full.d.ts": libContent,
|
||||
}),
|
||||
edits: noChangeOnlyRuns,
|
||||
});
|
||||
|
||||
@@ -618,14 +618,17 @@ console.log(a);`,
|
||||
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;`,
|
||||
),
|
||||
edits: [
|
||||
noChangeRun,
|
||||
{
|
||||
caption: "modify public to protected",
|
||||
edit: fs => replaceText(fs, "/src/project/MessageablePerson.ts", "public", "protected"),
|
||||
},
|
||||
noChangeRun,
|
||||
{
|
||||
caption: "modify protected to public",
|
||||
edit: fs => replaceText(fs, "/src/project/MessageablePerson.ts", "protected", "public"),
|
||||
},
|
||||
noChangeRun,
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -959,4 +962,49 @@ console.log(a);`,
|
||||
edit: fs => appendText(fs, "/src/project/src/bug.js", `export const something = 1;`),
|
||||
}],
|
||||
});
|
||||
|
||||
verifyTsc({
|
||||
scenario: "incremental",
|
||||
subScenario: "reports dts generation errors",
|
||||
commandLineArgs: ["-p", `/src/project`, "--explainFiles", "--listEmittedFiles"],
|
||||
fs: () =>
|
||||
loadProjectFromFiles({
|
||||
"/src/project/tsconfig.json": jsonToReadableText({
|
||||
compilerOptions: {
|
||||
module: "NodeNext",
|
||||
moduleResolution: "NodeNext",
|
||||
composite: true,
|
||||
skipLibCheck: true,
|
||||
skipDefaultLibCheck: true,
|
||||
},
|
||||
}),
|
||||
"/src/project/index.ts": Utils.dedent`
|
||||
import ky from 'ky';
|
||||
export const api = ky.extend({});
|
||||
`,
|
||||
"/src/project/package.json": jsonToReadableText({
|
||||
type: "module",
|
||||
}),
|
||||
"/src/project/node_modules/ky/distribution/index.d.ts": Utils.dedent`
|
||||
type KyInstance = {
|
||||
extend(options: Record<string,unknown>): KyInstance;
|
||||
}
|
||||
declare const ky: KyInstance;
|
||||
export default ky;
|
||||
`,
|
||||
"/src/project/node_modules/ky/package.json": jsonToReadableText({
|
||||
name: "ky",
|
||||
type: "module",
|
||||
main: "./distribution/index.js",
|
||||
}),
|
||||
"/lib/lib.esnext.full.d.ts": libContent,
|
||||
}),
|
||||
edits: [
|
||||
noChangeRun,
|
||||
{
|
||||
...noChangeRun,
|
||||
commandLineArgs: ["-b", `/src/project`, "--explainFiles", "--listEmittedFiles", "-v"],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user