mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-11 19:27:35 -06:00
Merge pull request #30605 from Microsoft/sourceFileVersionTsc
Ensure that we are setting source file version with --incremental from tsc (without --build or --watch)
This commit is contained in:
commit
307bf39572
@ -230,7 +230,7 @@ namespace ts.BuilderState {
|
||||
|
||||
// Create the reference map, and set the file infos
|
||||
for (const sourceFile of newProgram.getSourceFiles()) {
|
||||
const version = sourceFile.version;
|
||||
const version = Debug.assertDefined(sourceFile.version, "Program intended to be used with Builder should have source files with versions set");
|
||||
const oldInfo = useOldState ? oldState!.fileInfos.get(sourceFile.path) : undefined;
|
||||
if (referencedMap) {
|
||||
const newReferences = getReferencedFiles(newProgram, sourceFile, getCanonicalFileName);
|
||||
|
||||
@ -74,12 +74,7 @@ namespace ts {
|
||||
// TODO(shkamat): update this after reworking ts build API
|
||||
export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost {
|
||||
const existingDirectories = createMap<boolean>();
|
||||
function getCanonicalFileName(fileName: string): string {
|
||||
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
|
||||
// otherwise use toLowerCase as a canonical form.
|
||||
return system.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
}
|
||||
|
||||
const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
|
||||
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile | undefined {
|
||||
let text: string | undefined;
|
||||
try {
|
||||
|
||||
@ -275,7 +275,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: ProgramHost<any>) {
|
||||
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: { createHash?(data: string): string; }) {
|
||||
const originalGetSourceFile = compilerHost.getSourceFile;
|
||||
const computeHash = host.createHash || generateDjb2Hash;
|
||||
compilerHost.getSourceFile = (...args) => {
|
||||
@ -383,6 +383,56 @@ namespace ts {
|
||||
if (!buildInfo.program) return undefined;
|
||||
return createBuildProgramUsingProgramBuildInfo(buildInfo.program);
|
||||
}
|
||||
|
||||
export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost {
|
||||
const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system);
|
||||
host.createHash = maybeBind(system, system.createHash);
|
||||
setGetSourceFileAsHashVersioned(host, system);
|
||||
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName));
|
||||
return host;
|
||||
}
|
||||
|
||||
interface IncrementalProgramOptions<T extends BuilderProgram> {
|
||||
rootNames: ReadonlyArray<string>;
|
||||
options: CompilerOptions;
|
||||
configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>;
|
||||
projectReferences?: ReadonlyArray<ProjectReference>;
|
||||
host?: CompilerHost;
|
||||
createProgram?: CreateProgram<T>;
|
||||
}
|
||||
function createIncrementalProgram<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({
|
||||
rootNames, options, configFileParsingDiagnostics, projectReferences, host, createProgram
|
||||
}: IncrementalProgramOptions<T>): T {
|
||||
host = host || createIncrementalCompilerHost(options);
|
||||
createProgram = createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>;
|
||||
const oldProgram = readBuilderProgram(options, path => host!.readFile(path)) as any as T;
|
||||
return createProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences);
|
||||
}
|
||||
|
||||
export interface IncrementalCompilationOptions {
|
||||
rootNames: ReadonlyArray<string>;
|
||||
options: CompilerOptions;
|
||||
configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>;
|
||||
projectReferences?: ReadonlyArray<ProjectReference>;
|
||||
host?: CompilerHost;
|
||||
reportDiagnostic?: DiagnosticReporter;
|
||||
reportErrorSummary?: ReportEmitErrorSummary;
|
||||
afterProgramEmitAndDiagnostics?(program: EmitAndSemanticDiagnosticsBuilderProgram): void;
|
||||
system?: System;
|
||||
}
|
||||
export function performIncrementalCompilation(input: IncrementalCompilationOptions) {
|
||||
const system = input.system || sys;
|
||||
const host = input.host || (input.host = createIncrementalCompilerHost(input.options, system));
|
||||
const builderProgram = createIncrementalProgram(input);
|
||||
const exitStatus = emitFilesAndReportErrors(
|
||||
builderProgram,
|
||||
input.reportDiagnostic || createDiagnosticReporter(system),
|
||||
s => host.trace && host.trace(s),
|
||||
input.reportErrorSummary || input.options.pretty ? errorCount => system.write(getErrorSummaryText(errorCount, system.newLine)) : undefined
|
||||
);
|
||||
if (input.afterProgramEmitAndDiagnostics) input.afterProgramEmitAndDiagnostics(builderProgram);
|
||||
return exitStatus;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
|
||||
@ -57,20 +57,23 @@ namespace ts.tscWatch {
|
||||
function checkOutputErrors(
|
||||
host: WatchedSystem,
|
||||
logsBeforeWatchDiagnostic: string[] | undefined,
|
||||
preErrorsWatchDiagnostic: Diagnostic,
|
||||
preErrorsWatchDiagnostic: Diagnostic | undefined,
|
||||
logsBeforeErrors: string[] | undefined,
|
||||
errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>,
|
||||
disableConsoleClears?: boolean | undefined,
|
||||
...postErrorsWatchDiagnostics: Diagnostic[]
|
||||
...postErrorsWatchDiagnostics: Diagnostic[] | string[]
|
||||
) {
|
||||
let screenClears = 0;
|
||||
const outputs = host.getOutput();
|
||||
const expectedOutputCount = 1 + errors.length + postErrorsWatchDiagnostics.length +
|
||||
(logsBeforeWatchDiagnostic ? logsBeforeWatchDiagnostic.length : 0) + (logsBeforeErrors ? logsBeforeErrors.length : 0);
|
||||
const expectedOutputCount = (preErrorsWatchDiagnostic ? 1 : 0) +
|
||||
errors.length +
|
||||
postErrorsWatchDiagnostics.length +
|
||||
(logsBeforeWatchDiagnostic ? logsBeforeWatchDiagnostic.length : 0) +
|
||||
(logsBeforeErrors ? logsBeforeErrors.length : 0);
|
||||
assert.equal(outputs.length, expectedOutputCount, JSON.stringify(outputs));
|
||||
let index = 0;
|
||||
forEach(logsBeforeWatchDiagnostic, log => assertLog("logsBeforeWatchDiagnostic", log));
|
||||
assertWatchDiagnostic(preErrorsWatchDiagnostic);
|
||||
if (preErrorsWatchDiagnostic) assertWatchDiagnostic(preErrorsWatchDiagnostic);
|
||||
forEach(logsBeforeErrors, log => assertLog("logBeforeError", log));
|
||||
// Verify errors
|
||||
forEach(errors, assertDiagnostic);
|
||||
@ -98,13 +101,18 @@ namespace ts.tscWatch {
|
||||
index++;
|
||||
}
|
||||
|
||||
function assertWatchDiagnostic(diagnostic: Diagnostic) {
|
||||
const expected = getWatchDiagnosticWithoutDate(diagnostic);
|
||||
if (!disableConsoleClears && contains(screenStartingMessageCodes, diagnostic.code)) {
|
||||
assert.equal(host.screenClears[screenClears], index, `Expected screen clear at this diagnostic: ${expected}`);
|
||||
screenClears++;
|
||||
function assertWatchDiagnostic(diagnostic: Diagnostic | string) {
|
||||
if (isString(diagnostic)) {
|
||||
assert.equal(outputs[index], diagnostic, getOutputAtFailedMessage("Diagnostic", diagnostic));
|
||||
}
|
||||
else {
|
||||
const expected = getWatchDiagnosticWithoutDate(diagnostic);
|
||||
if (!disableConsoleClears && contains(screenStartingMessageCodes, diagnostic.code)) {
|
||||
assert.equal(host.screenClears[screenClears], index, `Expected screen clear at this diagnostic: ${expected}`);
|
||||
screenClears++;
|
||||
}
|
||||
assert.isTrue(endsWith(outputs[index], expected), getOutputAtFailedMessage("Watch diagnostic", expected));
|
||||
}
|
||||
assert.isTrue(endsWith(outputs[index], expected), getOutputAtFailedMessage("Watch diagnostic", expected));
|
||||
index++;
|
||||
}
|
||||
|
||||
@ -159,6 +167,18 @@ namespace ts.tscWatch {
|
||||
assert.equal(host.exitCode, expectedExitCode);
|
||||
}
|
||||
|
||||
export function checkNormalBuildErrors(host: WatchedSystem, errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>, reportErrorSummary?: boolean) {
|
||||
checkOutputErrors(
|
||||
host,
|
||||
/*logsBeforeWatchDiagnostic*/ undefined,
|
||||
/*preErrorsWatchDiagnostic*/ undefined,
|
||||
/*logsBeforeErrors*/ undefined,
|
||||
errors,
|
||||
/*disableConsoleClears*/ undefined,
|
||||
...(reportErrorSummary ? [getErrorSummaryText(errors.length, host.newLine)] : emptyArray)
|
||||
);
|
||||
}
|
||||
|
||||
function isDiagnosticMessageChain(message: DiagnosticMessage | DiagnosticMessageChain): message is DiagnosticMessageChain {
|
||||
return !!(message as DiagnosticMessageChain).messageText;
|
||||
}
|
||||
|
||||
@ -15,9 +15,51 @@ namespace ts.tscWatch {
|
||||
expectedIncrementalEmit?: ReadonlyArray<File>;
|
||||
expectedIncrementalErrors?: ReadonlyArray<string>;
|
||||
}
|
||||
function verifyIncrementalWatchEmit({
|
||||
files, expectedInitialEmit, expectedInitialErrors, modifyFs, expectedIncrementalEmit, expectedIncrementalErrors
|
||||
}: VerifyIncrementalWatchEmitInput) {
|
||||
function verifyIncrementalWatchEmit(input: VerifyIncrementalWatchEmitInput) {
|
||||
it("with tsc --w", () => {
|
||||
verifyIncrementalWatchEmitWorker({
|
||||
input,
|
||||
emitAndReportErrors: createWatchOfConfigFile,
|
||||
verifyErrors: checkOutputErrorsInitial
|
||||
});
|
||||
});
|
||||
it("with tsc", () => {
|
||||
verifyIncrementalWatchEmitWorker({
|
||||
input,
|
||||
emitAndReportErrors: incrementalBuild,
|
||||
verifyErrors: checkNormalBuildErrors
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function incrementalBuild(configFile: string, host: WatchedSystem) {
|
||||
const reportDiagnostic = createDiagnosticReporter(host);
|
||||
const config = parseConfigFileWithSystem(configFile, {}, host, reportDiagnostic);
|
||||
if (config) {
|
||||
performIncrementalCompilation({
|
||||
rootNames: config.fileNames,
|
||||
options: config.options,
|
||||
projectReferences: config.projectReferences,
|
||||
configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config),
|
||||
reportDiagnostic,
|
||||
system: host
|
||||
});
|
||||
}
|
||||
return { close: noop };
|
||||
}
|
||||
|
||||
interface VerifyIncrementalWatchEmitWorkerInput {
|
||||
input: VerifyIncrementalWatchEmitInput;
|
||||
emitAndReportErrors: (configFile: string, host: WatchedSystem) => { close(): void; };
|
||||
verifyErrors: (host: WatchedSystem, errors: ReadonlyArray<string>) => void;
|
||||
}
|
||||
function verifyIncrementalWatchEmitWorker({
|
||||
input: {
|
||||
files, expectedInitialEmit, expectedInitialErrors, modifyFs, expectedIncrementalEmit, expectedIncrementalErrors
|
||||
},
|
||||
emitAndReportErrors,
|
||||
verifyErrors
|
||||
}: VerifyIncrementalWatchEmitWorkerInput) {
|
||||
const host = createWatchedSystem(files, { currentDirectory: project });
|
||||
const originalWriteFile = host.writeFile;
|
||||
const writtenFiles = createMap<string>();
|
||||
@ -26,19 +68,41 @@ namespace ts.tscWatch {
|
||||
writtenFiles.set(path, content);
|
||||
originalWriteFile.call(host, path, content);
|
||||
};
|
||||
verifyWatch(host, writtenFiles, expectedInitialEmit, expectedInitialErrors);
|
||||
verifyBuild({
|
||||
host,
|
||||
writtenFiles,
|
||||
emitAndReportErrors,
|
||||
verifyErrors,
|
||||
expectedEmit: expectedInitialEmit,
|
||||
expectedErrors: expectedInitialErrors
|
||||
});
|
||||
if (modifyFs) {
|
||||
modifyFs(host);
|
||||
verifyWatch(host, writtenFiles, Debug.assertDefined(expectedIncrementalEmit), Debug.assertDefined(expectedIncrementalErrors));
|
||||
verifyBuild({
|
||||
host,
|
||||
writtenFiles,
|
||||
emitAndReportErrors,
|
||||
verifyErrors,
|
||||
expectedEmit: Debug.assertDefined(expectedIncrementalEmit),
|
||||
expectedErrors: Debug.assertDefined(expectedIncrementalErrors)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function verifyWatch(host: WatchedSystem, writtenFiles: Map<string>, expectedEmit: ReadonlyArray<File>, expectedErrors: ReadonlyArray<string>) {
|
||||
interface VerifyBuildWorker {
|
||||
host: WatchedSystem;
|
||||
writtenFiles: Map<string>;
|
||||
emitAndReportErrors: VerifyIncrementalWatchEmitWorkerInput["emitAndReportErrors"];
|
||||
verifyErrors: VerifyIncrementalWatchEmitWorkerInput["verifyErrors"];
|
||||
expectedEmit: ReadonlyArray<File>;
|
||||
expectedErrors: ReadonlyArray<string>;
|
||||
}
|
||||
function verifyBuild({ host, writtenFiles, emitAndReportErrors, verifyErrors, expectedEmit, expectedErrors }: VerifyBuildWorker) {
|
||||
writtenFiles.clear();
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
const result = emitAndReportErrors("tsconfig.json", host);
|
||||
checkFileEmit(writtenFiles, expectedEmit);
|
||||
checkOutputErrorsInitial(host, expectedErrors);
|
||||
watch.close();
|
||||
verifyErrors(host, expectedErrors);
|
||||
result.close();
|
||||
}
|
||||
|
||||
function checkFileEmit(actual: Map<string>, expected: ReadonlyArray<File>) {
|
||||
@ -73,7 +137,7 @@ namespace ts.tscWatch {
|
||||
content: "var y = 20;\n"
|
||||
};
|
||||
|
||||
it("own file emit without errors", () => {
|
||||
describe("own file emit without errors", () => {
|
||||
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
|
||||
verifyIncrementalWatchEmit({
|
||||
files: [libFile, file1, file2, configFile],
|
||||
@ -125,7 +189,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
});
|
||||
|
||||
it("own file emit with errors", () => {
|
||||
describe("own file emit with errors", () => {
|
||||
const fileModified: File = {
|
||||
path: file2.path,
|
||||
content: `const y: string = 20;`
|
||||
@ -208,7 +272,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
});
|
||||
|
||||
it("with --out", () => {
|
||||
describe("with --out", () => {
|
||||
const config: File = {
|
||||
path: configFile.path,
|
||||
content: JSON.stringify({ compilerOptions: { incremental: true, outFile: "out.js" } })
|
||||
@ -279,7 +343,7 @@ namespace ts.tscWatch {
|
||||
content: JSON.stringify({ compilerOptions: { incremental: true, module: "amd" } })
|
||||
};
|
||||
|
||||
it("own file emit without errors", () => {
|
||||
describe("own file emit without errors", () => {
|
||||
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
|
||||
verifyIncrementalWatchEmit({
|
||||
files: [libFile, file1, file2, config],
|
||||
@ -330,7 +394,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
});
|
||||
|
||||
it("own file emit with errors", () => {
|
||||
describe("own file emit with errors", () => {
|
||||
const fileModified: File = {
|
||||
path: file2.path,
|
||||
content: `export const y: string = 20;`
|
||||
@ -412,7 +476,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
});
|
||||
|
||||
it("with --out", () => {
|
||||
describe("with --out", () => {
|
||||
const config: File = {
|
||||
path: configFile.path,
|
||||
content: JSON.stringify({ compilerOptions: { incremental: true, module: "amd", outFile: "out.js" } })
|
||||
|
||||
@ -260,40 +260,16 @@ namespace ts {
|
||||
|
||||
function performIncrementalCompilation(config: ParsedCommandLine) {
|
||||
const { options, fileNames, projectReferences } = config;
|
||||
const host = createCompilerHost(options);
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
|
||||
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, currentDirectory, getCanonicalFileName));
|
||||
enableStatistics(options);
|
||||
const oldProgram = readBuilderProgram(options, path => host.readFile(path));
|
||||
const configFileParsingDiagnostics = getConfigFileParsingDiagnostics(config);
|
||||
const programOptions: CreateProgramOptions = {
|
||||
return sys.exit(ts.performIncrementalCompilation({
|
||||
rootNames: fileNames,
|
||||
options,
|
||||
projectReferences,
|
||||
host,
|
||||
configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config),
|
||||
};
|
||||
const program = createProgram(programOptions);
|
||||
const builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(
|
||||
program,
|
||||
{
|
||||
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
|
||||
createHash: maybeBind(sys, sys.createHash),
|
||||
writeFile: (path, data, writeByteOrderMark) => sys.writeFile(path, data, writeByteOrderMark)
|
||||
},
|
||||
oldProgram,
|
||||
configFileParsingDiagnostics
|
||||
);
|
||||
|
||||
const exitStatus = emitFilesAndReportErrors(
|
||||
builderProgram,
|
||||
projectReferences,
|
||||
reportDiagnostic,
|
||||
s => sys.write(s + sys.newLine),
|
||||
createReportErrorSummary(options)
|
||||
);
|
||||
reportStatistics(program);
|
||||
return sys.exit(exitStatus);
|
||||
reportErrorSummary: createReportErrorSummary(options),
|
||||
afterProgramEmitAndDiagnostics: builderProgram => reportStatistics(builderProgram.getProgram())
|
||||
}));
|
||||
}
|
||||
|
||||
function updateCreateProgram<T extends BuilderProgram>(host: { createProgram: CreateProgram<T>; }) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user