mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-19 12:45:58 -05:00
Component commits:ca35546663Add test that failsc7b5005acaHandle noEmit on semantic builder's emit as well6a05abdff8Add test for tsbuildinfo text verification8bae5210eeFix noEmit handling for tsbuildinfo emit with SemanticDiagnosticBuilder0fd4a08b75Add test for noEmitOnError with SemanticDiagnosticsBuilder4fa1d78a6cFix tsbuildinfo emit with SemanticDiagnosticsBuilder on noEmitOnErrora3968e7574Update src/compiler/builder.ts Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>174b00a9a6Update src/compiler/builder.ts Co-authored-by: Sheetal Nandi <shkamat@microsoft.com>
This commit is contained in:
@@ -1019,31 +1019,57 @@ namespace ts {
|
||||
* in that order would be used to write the files
|
||||
*/
|
||||
function emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult {
|
||||
let restorePendingEmitOnHandlingNoEmitSuccess = false;
|
||||
let savedAffectedFilesPendingEmit;
|
||||
let savedAffectedFilesPendingEmitKind;
|
||||
let savedAffectedFilesPendingEmitIndex;
|
||||
// Backup and restore affected pendings emit state for non emit Builder if noEmitOnError is enabled and emitBuildInfo could be written in case there are errors
|
||||
// This ensures pending files to emit is updated in tsbuildinfo
|
||||
// Note that when there are no errors, emit proceeds as if everything is emitted as it is callers reponsibility to write the files to disk if at all (because its builder that doesnt track files to emit)
|
||||
if (kind !== BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram &&
|
||||
!targetSourceFile &&
|
||||
!outFile(state.compilerOptions) &&
|
||||
!state.compilerOptions.noEmit &&
|
||||
state.compilerOptions.noEmitOnError) {
|
||||
restorePendingEmitOnHandlingNoEmitSuccess = true;
|
||||
savedAffectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice();
|
||||
savedAffectedFilesPendingEmitKind = state.affectedFilesPendingEmitKind && new Map(state.affectedFilesPendingEmitKind);
|
||||
savedAffectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex;
|
||||
}
|
||||
|
||||
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
|
||||
assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
|
||||
const result = handleNoEmitOptions(builderProgram, targetSourceFile, writeFile, cancellationToken);
|
||||
if (result) return result;
|
||||
if (!targetSourceFile) {
|
||||
// Emit and report any errors we ran into.
|
||||
let sourceMaps: SourceMapEmitResult[] = [];
|
||||
let emitSkipped = false;
|
||||
let diagnostics: Diagnostic[] | undefined;
|
||||
let emittedFiles: string[] = [];
|
||||
}
|
||||
const result = handleNoEmitOptions(builderProgram, targetSourceFile, writeFile, cancellationToken);
|
||||
if (result) return result;
|
||||
|
||||
let affectedEmitResult: AffectedFileResult<EmitResult>;
|
||||
while (affectedEmitResult = emitNextAffectedFile(writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers)) {
|
||||
emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped;
|
||||
diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics);
|
||||
emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles);
|
||||
sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps);
|
||||
}
|
||||
return {
|
||||
emitSkipped,
|
||||
diagnostics: diagnostics || emptyArray,
|
||||
emittedFiles,
|
||||
sourceMaps
|
||||
};
|
||||
if (restorePendingEmitOnHandlingNoEmitSuccess) {
|
||||
state.affectedFilesPendingEmit = savedAffectedFilesPendingEmit;
|
||||
state.affectedFilesPendingEmitKind = savedAffectedFilesPendingEmitKind;
|
||||
state.affectedFilesPendingEmitIndex = savedAffectedFilesPendingEmitIndex;
|
||||
}
|
||||
|
||||
// Emit only affected files if using builder for emit
|
||||
if (!targetSourceFile && kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
|
||||
// Emit and report any errors we ran into.
|
||||
let sourceMaps: SourceMapEmitResult[] = [];
|
||||
let emitSkipped = false;
|
||||
let diagnostics: Diagnostic[] | undefined;
|
||||
let emittedFiles: string[] = [];
|
||||
|
||||
let affectedEmitResult: AffectedFileResult<EmitResult>;
|
||||
while (affectedEmitResult = emitNextAffectedFile(writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers)) {
|
||||
emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped;
|
||||
diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics);
|
||||
emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles);
|
||||
sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps);
|
||||
}
|
||||
return {
|
||||
emitSkipped,
|
||||
diagnostics: diagnostics || emptyArray,
|
||||
emittedFiles,
|
||||
sourceMaps
|
||||
};
|
||||
}
|
||||
return Debug.checkDefined(state.program).emit(targetSourceFile, writeFile || maybeBind(host, host.writeFile), cancellationToken, emitOnlyDtsFiles, customTransformers);
|
||||
}
|
||||
@@ -1069,7 +1095,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Add file to affected file pending emit to handle for later emit time
|
||||
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
|
||||
// Apart for emit builder do this for tsbuildinfo, do this for non emit builder when noEmit is set as tsbuildinfo is written and reused between emitters
|
||||
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram || state.compilerOptions.noEmit || state.compilerOptions.noEmitOnError) {
|
||||
addToAffectedFilesPendingEmit(state, (affected as SourceFile).resolvedPath, BuilderFileEmit.Full);
|
||||
}
|
||||
|
||||
|
||||
@@ -935,6 +935,7 @@ namespace ts {
|
||||
getOptionsDiagnostics,
|
||||
getGlobalDiagnostics,
|
||||
getSemanticDiagnostics,
|
||||
getCachedSemanticDiagnostics,
|
||||
getSuggestionDiagnostics,
|
||||
getDeclarationDiagnostics,
|
||||
getBindAndCheckDiagnostics,
|
||||
@@ -1648,6 +1649,12 @@ namespace ts {
|
||||
return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken);
|
||||
}
|
||||
|
||||
function getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined {
|
||||
return sourceFile
|
||||
? cachedBindAndCheckDiagnosticsForFile.perFile?.get(sourceFile.path)
|
||||
: cachedBindAndCheckDiagnosticsForFile.allDiagnostics;
|
||||
}
|
||||
|
||||
function getBindAndCheckDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
|
||||
return getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -3727,6 +3727,8 @@ namespace ts {
|
||||
/* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker;
|
||||
/* @internal */ dropDiagnosticsProducingTypeChecker(): void;
|
||||
|
||||
/* @internal */ getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined;
|
||||
|
||||
/* @internal */ getClassifiableNames(): Set<__String>;
|
||||
|
||||
getNodeCount(): number;
|
||||
|
||||
@@ -123,4 +123,119 @@ namespace ts.tscWatch {
|
||||
checkProgramActualFiles(watch.getProgram().getProgram(), [mainFile.path, otherFile.path, libFile.path]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unittests:: tsc-watch:: watchAPI:: when watchHost uses createSemanticDiagnosticsBuilderProgram", () => {
|
||||
function getWatch<T extends BuilderProgram>(config: File, optionsToExtend: CompilerOptions | undefined, sys: System, createProgram: CreateProgram<T>) {
|
||||
const watchCompilerHost = createWatchCompilerHost(config.path, optionsToExtend, sys, createProgram);
|
||||
return createWatchProgram(watchCompilerHost);
|
||||
}
|
||||
|
||||
function setup<T extends BuilderProgram>(createProgram: CreateProgram<T>, configText: string) {
|
||||
const config: File = {
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: configText
|
||||
};
|
||||
const mainFile: File = {
|
||||
path: `${projectRoot}/main.ts`,
|
||||
content: "export const x = 10;"
|
||||
};
|
||||
const otherFile: File = {
|
||||
path: `${projectRoot}/other.ts`,
|
||||
content: "export const y = 10;"
|
||||
};
|
||||
const sys = createWatchedSystem([config, mainFile, otherFile, libFile]);
|
||||
const watch = getWatch(config, { noEmit: true }, sys, createProgram);
|
||||
return { sys, watch, mainFile, otherFile, config };
|
||||
}
|
||||
|
||||
function verifyOutputs(sys: System, emitSys: System) {
|
||||
for (const output of [`${projectRoot}/main.js`, `${projectRoot}/main.d.ts`, `${projectRoot}/other.js`, `${projectRoot}/other.d.ts`, `${projectRoot}/tsconfig.tsbuildinfo`]) {
|
||||
assert.strictEqual(sys.readFile(output), emitSys.readFile(output), `Output file text for ${output}`);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyBuilder<T extends BuilderProgram, U extends BuilderProgram>(config: File, sys: System, emitSys: System, createProgram: CreateProgram<T>, createEmitProgram: CreateProgram<U>, optionsToExtend?: CompilerOptions) {
|
||||
const watch = getWatch(config, /*optionsToExtend*/ optionsToExtend, sys, createProgram);
|
||||
const emitWatch = getWatch(config, /*optionsToExtend*/ optionsToExtend, emitSys, createEmitProgram);
|
||||
verifyOutputs(sys, emitSys);
|
||||
watch.close();
|
||||
emitWatch.close();
|
||||
}
|
||||
|
||||
it("verifies that noEmit is handled on createSemanticDiagnosticsBuilderProgram and typechecking happens only on affected files", () => {
|
||||
const { sys, watch, mainFile, otherFile } = setup(createSemanticDiagnosticsBuilderProgram, "{}");
|
||||
checkProgramActualFiles(watch.getProgram().getProgram(), [mainFile.path, otherFile.path, libFile.path]);
|
||||
sys.appendFile(mainFile.path, "\n// SomeComment");
|
||||
sys.runQueuedTimeoutCallbacks();
|
||||
const program = watch.getProgram().getProgram();
|
||||
assert.deepEqual(program.getCachedSemanticDiagnostics(program.getSourceFile(mainFile.path)), []);
|
||||
// Should not retrieve diagnostics for other file thats not changed
|
||||
assert.deepEqual(program.getCachedSemanticDiagnostics(program.getSourceFile(otherFile.path)), /*expected*/ undefined);
|
||||
});
|
||||
|
||||
it("noEmit with composite writes the tsbuildinfo with pending affected files correctly", () => {
|
||||
const configText = JSON.stringify({ compilerOptions: { composite: true } });
|
||||
const { sys, watch, config, mainFile } = setup(createSemanticDiagnosticsBuilderProgram, configText);
|
||||
const { sys: emitSys, watch: emitWatch } = setup(createEmitAndSemanticDiagnosticsBuilderProgram, configText);
|
||||
verifyOutputs(sys, emitSys);
|
||||
|
||||
watch.close();
|
||||
emitWatch.close();
|
||||
|
||||
// Emit on both sys should result in same output
|
||||
verifyBuilder(config, sys, emitSys, createEmitAndSemanticDiagnosticsBuilderProgram, createEmitAndSemanticDiagnosticsBuilderProgram);
|
||||
|
||||
// Change file
|
||||
sys.appendFile(mainFile.path, "\n// SomeComment");
|
||||
emitSys.appendFile(mainFile.path, "\n// SomeComment");
|
||||
|
||||
// Verify noEmit results in same output
|
||||
verifyBuilder(config, sys, emitSys, createSemanticDiagnosticsBuilderProgram, createEmitAndSemanticDiagnosticsBuilderProgram, { noEmit: true });
|
||||
|
||||
// Emit on both sys should result in same output
|
||||
verifyBuilder(config, sys, emitSys, createEmitAndSemanticDiagnosticsBuilderProgram, createEmitAndSemanticDiagnosticsBuilderProgram);
|
||||
|
||||
// Change file
|
||||
sys.appendFile(mainFile.path, "\n// SomeComment");
|
||||
emitSys.appendFile(mainFile.path, "\n// SomeComment");
|
||||
|
||||
// Emit on both the builders should result in same files
|
||||
verifyBuilder(config, sys, emitSys, createSemanticDiagnosticsBuilderProgram, createEmitAndSemanticDiagnosticsBuilderProgram);
|
||||
});
|
||||
|
||||
it("noEmitOnError with composite writes the tsbuildinfo with pending affected files correctly", () => {
|
||||
const config: File = {
|
||||
path: `${projectRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({ compilerOptions: { composite: true } })
|
||||
};
|
||||
const mainFile: File = {
|
||||
path: `${projectRoot}/main.ts`,
|
||||
content: "export const x: string = 10;"
|
||||
};
|
||||
const otherFile: File = {
|
||||
path: `${projectRoot}/other.ts`,
|
||||
content: "export const y = 10;"
|
||||
};
|
||||
const sys = createWatchedSystem([config, mainFile, otherFile, libFile]);
|
||||
const emitSys = createWatchedSystem([config, mainFile, otherFile, libFile]);
|
||||
|
||||
// Verify noEmit results in same output
|
||||
verifyBuilder(config, sys, emitSys, createSemanticDiagnosticsBuilderProgram, createEmitAndSemanticDiagnosticsBuilderProgram, { noEmitOnError: true });
|
||||
|
||||
// Change file
|
||||
sys.appendFile(mainFile.path, "\n// SomeComment");
|
||||
emitSys.appendFile(mainFile.path, "\n// SomeComment");
|
||||
|
||||
// Verify noEmit results in same output
|
||||
verifyBuilder(config, sys, emitSys, createSemanticDiagnosticsBuilderProgram, createEmitAndSemanticDiagnosticsBuilderProgram, { noEmitOnError: true });
|
||||
|
||||
// Fix error
|
||||
const fixed = "export const x = 10;";
|
||||
sys.appendFile(mainFile.path, fixed);
|
||||
emitSys.appendFile(mainFile.path, fixed);
|
||||
|
||||
// Emit on both the builders should result in same files
|
||||
verifyBuilder(config, sys, emitSys, createSemanticDiagnosticsBuilderProgram, createEmitAndSemanticDiagnosticsBuilderProgram, { noEmitOnError: true });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user