Skip to content

Commit 307bf39

Browse files
authored
Merge pull request #30605 from Microsoft/sourceFileVersionTsc
Ensure that we are setting source file version with --incremental from tsc (without --build or --watch)
2 parents 72dc16a + 7457e5d commit 307bf39

File tree

6 files changed

+168
-63
lines changed

6 files changed

+168
-63
lines changed

src/compiler/builderState.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ namespace ts.BuilderState {
230230

231231
// Create the reference map, and set the file infos
232232
for (const sourceFile of newProgram.getSourceFiles()) {
233-
const version = sourceFile.version;
233+
const version = Debug.assertDefined(sourceFile.version, "Program intended to be used with Builder should have source files with versions set");
234234
const oldInfo = useOldState ? oldState!.fileInfos.get(sourceFile.path) : undefined;
235235
if (referencedMap) {
236236
const newReferences = getReferencedFiles(newProgram, sourceFile, getCanonicalFileName);

src/compiler/program.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,7 @@ namespace ts {
7474
// TODO(shkamat): update this after reworking ts build API
7575
export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost {
7676
const existingDirectories = createMap<boolean>();
77-
function getCanonicalFileName(fileName: string): string {
78-
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
79-
// otherwise use toLowerCase as a canonical form.
80-
return system.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
81-
}
82-
77+
const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
8378
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile | undefined {
8479
let text: string | undefined;
8580
try {

src/compiler/watch.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ namespace ts {
275275
}
276276
}
277277

278-
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: ProgramHost<any>) {
278+
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: { createHash?(data: string): string; }) {
279279
const originalGetSourceFile = compilerHost.getSourceFile;
280280
const computeHash = host.createHash || generateDjb2Hash;
281281
compilerHost.getSourceFile = (...args) => {
@@ -383,6 +383,56 @@ namespace ts {
383383
if (!buildInfo.program) return undefined;
384384
return createBuildProgramUsingProgramBuildInfo(buildInfo.program);
385385
}
386+
387+
export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost {
388+
const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system);
389+
host.createHash = maybeBind(system, system.createHash);
390+
setGetSourceFileAsHashVersioned(host, system);
391+
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName));
392+
return host;
393+
}
394+
395+
interface IncrementalProgramOptions<T extends BuilderProgram> {
396+
rootNames: ReadonlyArray<string>;
397+
options: CompilerOptions;
398+
configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>;
399+
projectReferences?: ReadonlyArray<ProjectReference>;
400+
host?: CompilerHost;
401+
createProgram?: CreateProgram<T>;
402+
}
403+
function createIncrementalProgram<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({
404+
rootNames, options, configFileParsingDiagnostics, projectReferences, host, createProgram
405+
}: IncrementalProgramOptions<T>): T {
406+
host = host || createIncrementalCompilerHost(options);
407+
createProgram = createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>;
408+
const oldProgram = readBuilderProgram(options, path => host!.readFile(path)) as any as T;
409+
return createProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences);
410+
}
411+
412+
export interface IncrementalCompilationOptions {
413+
rootNames: ReadonlyArray<string>;
414+
options: CompilerOptions;
415+
configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>;
416+
projectReferences?: ReadonlyArray<ProjectReference>;
417+
host?: CompilerHost;
418+
reportDiagnostic?: DiagnosticReporter;
419+
reportErrorSummary?: ReportEmitErrorSummary;
420+
afterProgramEmitAndDiagnostics?(program: EmitAndSemanticDiagnosticsBuilderProgram): void;
421+
system?: System;
422+
}
423+
export function performIncrementalCompilation(input: IncrementalCompilationOptions) {
424+
const system = input.system || sys;
425+
const host = input.host || (input.host = createIncrementalCompilerHost(input.options, system));
426+
const builderProgram = createIncrementalProgram(input);
427+
const exitStatus = emitFilesAndReportErrors(
428+
builderProgram,
429+
input.reportDiagnostic || createDiagnosticReporter(system),
430+
s => host.trace && host.trace(s),
431+
input.reportErrorSummary || input.options.pretty ? errorCount => system.write(getErrorSummaryText(errorCount, system.newLine)) : undefined
432+
);
433+
if (input.afterProgramEmitAndDiagnostics) input.afterProgramEmitAndDiagnostics(builderProgram);
434+
return exitStatus;
435+
}
386436
}
387437

388438
namespace ts {

src/testRunner/unittests/tscWatch/helpers.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,23 @@ namespace ts.tscWatch {
5757
function checkOutputErrors(
5858
host: WatchedSystem,
5959
logsBeforeWatchDiagnostic: string[] | undefined,
60-
preErrorsWatchDiagnostic: Diagnostic,
60+
preErrorsWatchDiagnostic: Diagnostic | undefined,
6161
logsBeforeErrors: string[] | undefined,
6262
errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>,
6363
disableConsoleClears?: boolean | undefined,
64-
...postErrorsWatchDiagnostics: Diagnostic[]
64+
...postErrorsWatchDiagnostics: Diagnostic[] | string[]
6565
) {
6666
let screenClears = 0;
6767
const outputs = host.getOutput();
68-
const expectedOutputCount = 1 + errors.length + postErrorsWatchDiagnostics.length +
69-
(logsBeforeWatchDiagnostic ? logsBeforeWatchDiagnostic.length : 0) + (logsBeforeErrors ? logsBeforeErrors.length : 0);
68+
const expectedOutputCount = (preErrorsWatchDiagnostic ? 1 : 0) +
69+
errors.length +
70+
postErrorsWatchDiagnostics.length +
71+
(logsBeforeWatchDiagnostic ? logsBeforeWatchDiagnostic.length : 0) +
72+
(logsBeforeErrors ? logsBeforeErrors.length : 0);
7073
assert.equal(outputs.length, expectedOutputCount, JSON.stringify(outputs));
7174
let index = 0;
7275
forEach(logsBeforeWatchDiagnostic, log => assertLog("logsBeforeWatchDiagnostic", log));
73-
assertWatchDiagnostic(preErrorsWatchDiagnostic);
76+
if (preErrorsWatchDiagnostic) assertWatchDiagnostic(preErrorsWatchDiagnostic);
7477
forEach(logsBeforeErrors, log => assertLog("logBeforeError", log));
7578
// Verify errors
7679
forEach(errors, assertDiagnostic);
@@ -98,13 +101,18 @@ namespace ts.tscWatch {
98101
index++;
99102
}
100103

101-
function assertWatchDiagnostic(diagnostic: Diagnostic) {
102-
const expected = getWatchDiagnosticWithoutDate(diagnostic);
103-
if (!disableConsoleClears && contains(screenStartingMessageCodes, diagnostic.code)) {
104-
assert.equal(host.screenClears[screenClears], index, `Expected screen clear at this diagnostic: ${expected}`);
105-
screenClears++;
104+
function assertWatchDiagnostic(diagnostic: Diagnostic | string) {
105+
if (isString(diagnostic)) {
106+
assert.equal(outputs[index], diagnostic, getOutputAtFailedMessage("Diagnostic", diagnostic));
107+
}
108+
else {
109+
const expected = getWatchDiagnosticWithoutDate(diagnostic);
110+
if (!disableConsoleClears && contains(screenStartingMessageCodes, diagnostic.code)) {
111+
assert.equal(host.screenClears[screenClears], index, `Expected screen clear at this diagnostic: ${expected}`);
112+
screenClears++;
113+
}
114+
assert.isTrue(endsWith(outputs[index], expected), getOutputAtFailedMessage("Watch diagnostic", expected));
106115
}
107-
assert.isTrue(endsWith(outputs[index], expected), getOutputAtFailedMessage("Watch diagnostic", expected));
108116
index++;
109117
}
110118

@@ -159,6 +167,18 @@ namespace ts.tscWatch {
159167
assert.equal(host.exitCode, expectedExitCode);
160168
}
161169

170+
export function checkNormalBuildErrors(host: WatchedSystem, errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>, reportErrorSummary?: boolean) {
171+
checkOutputErrors(
172+
host,
173+
/*logsBeforeWatchDiagnostic*/ undefined,
174+
/*preErrorsWatchDiagnostic*/ undefined,
175+
/*logsBeforeErrors*/ undefined,
176+
errors,
177+
/*disableConsoleClears*/ undefined,
178+
...(reportErrorSummary ? [getErrorSummaryText(errors.length, host.newLine)] : emptyArray)
179+
);
180+
}
181+
162182
function isDiagnosticMessageChain(message: DiagnosticMessage | DiagnosticMessageChain): message is DiagnosticMessageChain {
163183
return !!(message as DiagnosticMessageChain).messageText;
164184
}

src/testRunner/unittests/tscWatch/incremental.ts

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,51 @@ namespace ts.tscWatch {
1515
expectedIncrementalEmit?: ReadonlyArray<File>;
1616
expectedIncrementalErrors?: ReadonlyArray<string>;
1717
}
18-
function verifyIncrementalWatchEmit({
19-
files, expectedInitialEmit, expectedInitialErrors, modifyFs, expectedIncrementalEmit, expectedIncrementalErrors
20-
}: VerifyIncrementalWatchEmitInput) {
18+
function verifyIncrementalWatchEmit(input: VerifyIncrementalWatchEmitInput) {
19+
it("with tsc --w", () => {
20+
verifyIncrementalWatchEmitWorker({
21+
input,
22+
emitAndReportErrors: createWatchOfConfigFile,
23+
verifyErrors: checkOutputErrorsInitial
24+
});
25+
});
26+
it("with tsc", () => {
27+
verifyIncrementalWatchEmitWorker({
28+
input,
29+
emitAndReportErrors: incrementalBuild,
30+
verifyErrors: checkNormalBuildErrors
31+
});
32+
});
33+
}
34+
35+
function incrementalBuild(configFile: string, host: WatchedSystem) {
36+
const reportDiagnostic = createDiagnosticReporter(host);
37+
const config = parseConfigFileWithSystem(configFile, {}, host, reportDiagnostic);
38+
if (config) {
39+
performIncrementalCompilation({
40+
rootNames: config.fileNames,
41+
options: config.options,
42+
projectReferences: config.projectReferences,
43+
configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config),
44+
reportDiagnostic,
45+
system: host
46+
});
47+
}
48+
return { close: noop };
49+
}
50+
51+
interface VerifyIncrementalWatchEmitWorkerInput {
52+
input: VerifyIncrementalWatchEmitInput;
53+
emitAndReportErrors: (configFile: string, host: WatchedSystem) => { close(): void; };
54+
verifyErrors: (host: WatchedSystem, errors: ReadonlyArray<string>) => void;
55+
}
56+
function verifyIncrementalWatchEmitWorker({
57+
input: {
58+
files, expectedInitialEmit, expectedInitialErrors, modifyFs, expectedIncrementalEmit, expectedIncrementalErrors
59+
},
60+
emitAndReportErrors,
61+
verifyErrors
62+
}: VerifyIncrementalWatchEmitWorkerInput) {
2163
const host = createWatchedSystem(files, { currentDirectory: project });
2264
const originalWriteFile = host.writeFile;
2365
const writtenFiles = createMap<string>();
@@ -26,19 +68,41 @@ namespace ts.tscWatch {
2668
writtenFiles.set(path, content);
2769
originalWriteFile.call(host, path, content);
2870
};
29-
verifyWatch(host, writtenFiles, expectedInitialEmit, expectedInitialErrors);
71+
verifyBuild({
72+
host,
73+
writtenFiles,
74+
emitAndReportErrors,
75+
verifyErrors,
76+
expectedEmit: expectedInitialEmit,
77+
expectedErrors: expectedInitialErrors
78+
});
3079
if (modifyFs) {
3180
modifyFs(host);
32-
verifyWatch(host, writtenFiles, Debug.assertDefined(expectedIncrementalEmit), Debug.assertDefined(expectedIncrementalErrors));
81+
verifyBuild({
82+
host,
83+
writtenFiles,
84+
emitAndReportErrors,
85+
verifyErrors,
86+
expectedEmit: Debug.assertDefined(expectedIncrementalEmit),
87+
expectedErrors: Debug.assertDefined(expectedIncrementalErrors)
88+
});
3389
}
3490
}
3591

36-
function verifyWatch(host: WatchedSystem, writtenFiles: Map<string>, expectedEmit: ReadonlyArray<File>, expectedErrors: ReadonlyArray<string>) {
92+
interface VerifyBuildWorker {
93+
host: WatchedSystem;
94+
writtenFiles: Map<string>;
95+
emitAndReportErrors: VerifyIncrementalWatchEmitWorkerInput["emitAndReportErrors"];
96+
verifyErrors: VerifyIncrementalWatchEmitWorkerInput["verifyErrors"];
97+
expectedEmit: ReadonlyArray<File>;
98+
expectedErrors: ReadonlyArray<string>;
99+
}
100+
function verifyBuild({ host, writtenFiles, emitAndReportErrors, verifyErrors, expectedEmit, expectedErrors }: VerifyBuildWorker) {
37101
writtenFiles.clear();
38-
const watch = createWatchOfConfigFile("tsconfig.json", host);
102+
const result = emitAndReportErrors("tsconfig.json", host);
39103
checkFileEmit(writtenFiles, expectedEmit);
40-
checkOutputErrorsInitial(host, expectedErrors);
41-
watch.close();
104+
verifyErrors(host, expectedErrors);
105+
result.close();
42106
}
43107

44108
function checkFileEmit(actual: Map<string>, expected: ReadonlyArray<File>) {
@@ -73,7 +137,7 @@ namespace ts.tscWatch {
73137
content: "var y = 20;\n"
74138
};
75139

76-
it("own file emit without errors", () => {
140+
describe("own file emit without errors", () => {
77141
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
78142
verifyIncrementalWatchEmit({
79143
files: [libFile, file1, file2, configFile],
@@ -125,7 +189,7 @@ namespace ts.tscWatch {
125189
});
126190
});
127191

128-
it("own file emit with errors", () => {
192+
describe("own file emit with errors", () => {
129193
const fileModified: File = {
130194
path: file2.path,
131195
content: `const y: string = 20;`
@@ -208,7 +272,7 @@ namespace ts.tscWatch {
208272
});
209273
});
210274

211-
it("with --out", () => {
275+
describe("with --out", () => {
212276
const config: File = {
213277
path: configFile.path,
214278
content: JSON.stringify({ compilerOptions: { incremental: true, outFile: "out.js" } })
@@ -279,7 +343,7 @@ namespace ts.tscWatch {
279343
content: JSON.stringify({ compilerOptions: { incremental: true, module: "amd" } })
280344
};
281345

282-
it("own file emit without errors", () => {
346+
describe("own file emit without errors", () => {
283347
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
284348
verifyIncrementalWatchEmit({
285349
files: [libFile, file1, file2, config],
@@ -330,7 +394,7 @@ namespace ts.tscWatch {
330394
});
331395
});
332396

333-
it("own file emit with errors", () => {
397+
describe("own file emit with errors", () => {
334398
const fileModified: File = {
335399
path: file2.path,
336400
content: `export const y: string = 20;`
@@ -412,7 +476,7 @@ namespace ts.tscWatch {
412476
});
413477
});
414478

415-
it("with --out", () => {
479+
describe("with --out", () => {
416480
const config: File = {
417481
path: configFile.path,
418482
content: JSON.stringify({ compilerOptions: { incremental: true, module: "amd", outFile: "out.js" } })

src/tsc/tsc.ts

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -260,40 +260,16 @@ namespace ts {
260260

261261
function performIncrementalCompilation(config: ParsedCommandLine) {
262262
const { options, fileNames, projectReferences } = config;
263-
const host = createCompilerHost(options);
264-
const currentDirectory = host.getCurrentDirectory();
265-
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
266-
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, currentDirectory, getCanonicalFileName));
267263
enableStatistics(options);
268-
const oldProgram = readBuilderProgram(options, path => host.readFile(path));
269-
const configFileParsingDiagnostics = getConfigFileParsingDiagnostics(config);
270-
const programOptions: CreateProgramOptions = {
264+
return sys.exit(ts.performIncrementalCompilation({
271265
rootNames: fileNames,
272266
options,
273-
projectReferences,
274-
host,
275267
configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config),
276-
};
277-
const program = createProgram(programOptions);
278-
const builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(
279-
program,
280-
{
281-
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
282-
createHash: maybeBind(sys, sys.createHash),
283-
writeFile: (path, data, writeByteOrderMark) => sys.writeFile(path, data, writeByteOrderMark)
284-
},
285-
oldProgram,
286-
configFileParsingDiagnostics
287-
);
288-
289-
const exitStatus = emitFilesAndReportErrors(
290-
builderProgram,
268+
projectReferences,
291269
reportDiagnostic,
292-
s => sys.write(s + sys.newLine),
293-
createReportErrorSummary(options)
294-
);
295-
reportStatistics(program);
296-
return sys.exit(exitStatus);
270+
reportErrorSummary: createReportErrorSummary(options),
271+
afterProgramEmitAndDiagnostics: builderProgram => reportStatistics(builderProgram.getProgram())
272+
}));
297273
}
298274

299275
function updateCreateProgram<T extends BuilderProgram>(host: { createProgram: CreateProgram<T>; }) {

0 commit comments

Comments
 (0)