Merge branch 'master' of https://github.com/Microsoft/TypeScript into feature/eslint

This commit is contained in:
Alexander
2019-08-31 22:12:41 +03:00
55 changed files with 1783 additions and 807 deletions

View File

@@ -137,7 +137,7 @@ namespace ts {
*/
emittedBuildInfo?: boolean;
/**
* Already seen affected files
* Already seen emitted files
*/
seenEmittedFiles: Map<true> | undefined;
/**
@@ -329,7 +329,6 @@ namespace ts {
handleDtsMayChangeOfAffectedFile(state, affectedFile, cancellationToken, computeHash);
return affectedFile;
}
seenAffectedFiles.set(affectedFile.path, true);
affectedFilesIndex++;
}
@@ -549,7 +548,7 @@ 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) {
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean, isEmitResult?: boolean) {
if (isBuildInfoEmit) {
state.emittedBuildInfo = true;
}
@@ -559,6 +558,9 @@ 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 (isPendingEmit) {
state.affectedFilesPendingEmitIndex!++;
}
@@ -576,6 +578,14 @@ namespace ts {
return { result, affected };
}
/**
* Returns the result with affected file
*/
function toAffectedFileEmitResult(state: BuilderProgramState, result: EmitResult, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean): AffectedFileResult<EmitResult> {
doneWithAffectedFile(state, affected, isPendingEmit, isBuildInfoEmit, /*isEmitResult*/ true);
return { result, affected };
}
/**
* Gets the semantic diagnostics either from cache if present, or otherwise from program and caches it
* Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files/changed file set
@@ -849,7 +859,7 @@ namespace ts {
}
const affected = Debug.assertDefined(state.program);
return toAffectedFileResult(
return toAffectedFileEmitResult(
state,
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
// Otherwise just affected file
@@ -872,14 +882,14 @@ namespace ts {
}
}
return toAffectedFileResult(
return toAffectedFileEmitResult(
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),
affected,
isPendingEmitFile
);
isPendingEmitFile,
);
}
/**
@@ -1036,7 +1046,7 @@ namespace ts {
compilerOptions: convertFromReusableCompilerOptions(program.options, toAbsolutePath),
referencedMap: getMapOfReferencedSet(program.referencedMap, toPath),
exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap, toPath),
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => isString(value) ? value : value[0], value => isString(value) ? emptyArray : value[1]),
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => toPath(isString(value) ? value : value[0]), value => isString(value) ? emptyArray : value[1]),
hasReusableDiagnostic: true
};
return {

View File

@@ -345,8 +345,13 @@ namespace ts.BuilderState {
}
else {
const emitOutput = getFileEmitOutput(programOfThisState, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken);
if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) {
latestSignature = computeHash(emitOutput.outputFiles[0].text);
const firstDts = emitOutput.outputFiles &&
programOfThisState.getCompilerOptions().declarationMap ?
emitOutput.outputFiles.length > 1 ? emitOutput.outputFiles[1] : undefined :
emitOutput.outputFiles.length > 0 ? emitOutput.outputFiles[0] : undefined;
if (firstDts) {
Debug.assert(fileExtensionIs(firstDts.name, Extension.Dts), "File extension for signature expected to be dts", () => `Found: ${getAnyExtensionFromPath(firstDts.name)} for ${firstDts.name}:: All output files: ${JSON.stringify(emitOutput.outputFiles.map(f => f.name))}`);
latestSignature = computeHash(firstDts.text);
if (exportedModulesMapCache && latestSignature !== prevSignature) {
updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache);
}

View File

@@ -155,6 +155,7 @@ namespace ts {
}
function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) {
if (configFile.options.emitDeclarationOnly) return undefined;
const isJsonFile = fileExtensionIs(inputFileName, Extension.Json);
const outputFileName = changeExtension(
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir),
@@ -187,7 +188,7 @@ namespace ts {
const js = getOutputJSFileName(inputFileName, configFile, ignoreCase);
addOutput(js);
if (fileExtensionIs(inputFileName, Extension.Json)) continue;
if (configFile.options.sourceMap) {
if (js && configFile.options.sourceMap) {
addOutput(`${js}.map`);
}
if (getEmitDeclarations(configFile.options) && hasTSFileExtension(inputFileName)) {
@@ -214,6 +215,10 @@ namespace ts {
if (fileExtensionIs(inputFileName, Extension.Dts)) continue;
const jsFilePath = getOutputJSFileName(inputFileName, configFile, ignoreCase);
if (jsFilePath) return jsFilePath;
if (fileExtensionIs(inputFileName, Extension.Json)) continue;
if (getEmitDeclarations(configFile.options) && hasTSFileExtension(inputFileName)) {
return getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
}
}
const buildInfoPath = getOutputPathForBuildInfo(configFile.options);
if (buildInfoPath) return buildInfoPath;

View File

@@ -545,6 +545,10 @@ ${indentText}${text}`;
super.writeFile(fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark);
}
createHash(data: string) {
return `${ts.generateDjb2Hash(data)}-${data}`;
}
now() {
return new Date(this.sys.vfs.time());
}
@@ -571,6 +575,15 @@ Actual: ${JSON.stringify(actual, /*replacer*/ undefined, " ")}
Expected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}`);
}
assertErrors(...expectedDiagnostics: ExpectedErrorDiagnostic[]) {
const actual = this.diagnostics.filter(d => d.kind === DiagnosticKind.Error).map(diagnosticToText);
const expected = expectedDiagnostics.map(expectedDiagnosticToText);
assert.deepEqual(actual, expected, `Diagnostics arrays did not match:
Actual: ${JSON.stringify(actual, /*replacer*/ undefined, " ")}
Expected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}
Actual All:: ${JSON.stringify(this.diagnostics.slice().map(diagnosticToText), /*replacer*/ undefined, " ")}`);
}
printDiagnostics(header = "== Diagnostics ==") {
const out = ts.createDiagnosticReporter(ts.sys);
ts.sys.write(header + "\r\n");

View File

@@ -540,10 +540,14 @@ namespace ts.formatting {
return true;
case SyntaxKind.VariableDeclaration:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.BinaryExpression:
if (!settings.indentMultiLineObjectLiteralBeginningOnBlankLine && sourceFile && childKind === SyntaxKind.ObjectLiteralExpression) { // TODO: GH#18217
return rangeIsOnOneLine(sourceFile, child!);
}
return true;
if (parent.kind !== SyntaxKind.BinaryExpression) {
return true;
}
break;
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ForInStatement:

View File

@@ -94,6 +94,7 @@
"unittests/tsbuild/amdModulesWithOut.ts",
"unittests/tsbuild/containerOnlyReferenced.ts",
"unittests/tsbuild/demo.ts",
"unittests/tsbuild/emitDeclarationOnly.ts",
"unittests/tsbuild/emptyFiles.ts",
"unittests/tsbuild/graphOrdering.ts",
"unittests/tsbuild/inferredTypeFromTransitiveModule.ts",

View File

@@ -77,7 +77,7 @@ namespace ts {
[outputFiles[project.lib][ext.buildinfo], outputFiles[project.lib][ext.js], outputFiles[project.lib][ext.dts]],
[outputFiles[project.app][ext.buildinfo], outputFiles[project.app][ext.js], outputFiles[project.app][ext.dts]]
],
lastProjectOutputJs: outputFiles[project.app][ext.js],
lastProjectOutput: outputFiles[project.app][ext.js],
initialBuild: {
modifyFs
},
@@ -231,7 +231,7 @@ ${internal} export enum internalEnum { a, b, c }`);
[libOutputFile[ext.buildinfo], libOutputFile[ext.js], libOutputFile[ext.dts]],
[outputFiles[project.app][ext.buildinfo], outputFiles[project.app][ext.js], outputFiles[project.app][ext.dts]]
],
lastProjectOutputJs: outputFiles[project.app][ext.js],
lastProjectOutput: outputFiles[project.app][ext.js],
initialBuild: {
modifyFs,
expectedDiagnostics: [

View File

@@ -0,0 +1,109 @@
namespace ts {
describe("unittests:: tsbuild:: on project with emitDeclarationOnly set to true", () => {
let projFs: vfs.FileSystem;
const { time, tick } = getTime();
before(() => {
projFs = loadProjectFromDisk("tests/projects/emitDeclarationOnly", time);
});
after(() => {
projFs = undefined!;
});
function verifyEmitDeclarationOnly(disableMap?: true) {
verifyTsbuildOutput({
scenario: `only dts output in circular import project with emitDeclarationOnly${disableMap ? "" : " and declarationMap"}`,
projFs: () => projFs,
time,
tick,
proj: "emitDeclarationOnly",
rootNames: ["/src"],
lastProjectOutput: `/src/lib/index.d.ts`,
outputFiles: [
"/src/lib/a.d.ts",
"/src/lib/b.d.ts",
"/src/lib/c.d.ts",
"/src/lib/index.d.ts",
"/src/tsconfig.tsbuildinfo",
...(disableMap ? emptyArray : [
"/src/lib/a.d.ts.map",
"/src/lib/b.d.ts.map",
"/src/lib/c.d.ts.map",
"/src/lib/index.d.ts.map"
])
],
initialBuild: {
modifyFs: disableMap ?
(fs => replaceText(fs, "/src/tsconfig.json", `"declarationMap": true,`, "")) :
noop,
expectedDiagnostics: [
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/lib/a.d.ts"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
]
},
incrementalDtsChangedBuild: {
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "b: B;", "b: B; foo: any;"),
expectedDiagnostics: [
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tsconfig.json", "src/lib/a.d.ts", "src/src/a.ts"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
]
},
baselineOnly: true,
verifyDiagnostics: true
});
}
verifyEmitDeclarationOnly();
verifyEmitDeclarationOnly(/*disableMap*/ true);
verifyTsbuildOutput({
scenario: `only dts output in non circular imports project with emitDeclarationOnly`,
projFs: () => projFs,
time,
tick,
proj: "emitDeclarationOnly",
rootNames: ["/src"],
lastProjectOutput: `/src/lib/a.d.ts`,
outputFiles: [
"/src/lib/a.d.ts",
"/src/lib/b.d.ts",
"/src/lib/c.d.ts",
"/src/tsconfig.tsbuildinfo",
"/src/lib/a.d.ts.map",
"/src/lib/b.d.ts.map",
"/src/lib/c.d.ts.map",
],
initialBuild: {
modifyFs: fs => {
fs.rimrafSync("/src/src/index.ts");
replaceText(fs, "/src/src/a.ts", `import { B } from "./b";`, `export class B { prop = "hello"; }`);
},
expectedDiagnostics: [
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/lib/a.d.ts"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
]
},
incrementalDtsChangedBuild: {
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "b: B;", "b: B; foo: any;"),
expectedDiagnostics: [
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tsconfig.json", "src/lib/a.d.ts", "src/src/a.ts"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
]
},
incrementalDtsUnchangedBuild: {
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "export interface A {", `class C { }
export interface A {`),
expectedDiagnostics: [
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tsconfig.json", "src/lib/a.d.ts", "src/src/a.ts"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"],
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, "/src/tsconfig.json"]
]
},
baselineOnly: true,
verifyDiagnostics: true
});
});
}

View File

@@ -102,7 +102,22 @@ namespace ts {
interface ReadonlyArray<T> {}
declare const console: { log(msg: any): void; };`;
export function loadProjectFromDisk(root: string, time?: vfs.FileSystemOptions["time"]): vfs.FileSystem {
export const symbolLibContent = `
interface SymbolConstructor {
readonly species: symbol;
readonly toStringTag: symbol;
}
declare var Symbol: SymbolConstructor;
interface Symbol {
readonly [Symbol.toStringTag]: string;
}
`;
export function loadProjectFromDisk(
root: string,
time?: vfs.FileSystemOptions["time"],
libContentToAppend?: string
): vfs.FileSystem {
const resolver = vfs.createResolver(Harness.IO);
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
files: {
@@ -112,12 +127,31 @@ declare const console: { log(msg: any): void; };`;
meta: { defaultLibLocation: "/lib" },
time
});
fs.mkdirSync("/lib");
fs.writeFileSync("/lib/lib.d.ts", libContent);
fs.makeReadonly();
addLibAndMakeReadonly(fs, libContentToAppend);
return fs;
}
export function loadProjectFromFiles(
files: vfs.FileSet,
time?: vfs.FileSystemOptions["time"],
libContentToAppend?: string
): vfs.FileSystem {
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
files,
cwd: "/",
meta: { defaultLibLocation: "/lib" },
time
});
addLibAndMakeReadonly(fs, libContentToAppend);
return fs;
}
function addLibAndMakeReadonly(fs: vfs.FileSystem, libContentToAppend?: string) {
fs.mkdirSync("/lib");
fs.writeFileSync("/lib/lib.d.ts", libContentToAppend ? `${libContent}${libContentToAppend}` : libContent);
fs.makeReadonly();
}
export function verifyOutputsPresent(fs: vfs.FileSystem, outputs: readonly string[]) {
for (const output of outputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
@@ -199,7 +233,7 @@ declare const console: { log(msg: any): void; };`;
fs: vfs.FileSystem;
tick: () => void;
rootNames: readonly string[];
expectedMapFileNames: readonly string[];
expectedMapFileNames?: readonly string[];
expectedBuildInfoFilesForSectionBaselines?: readonly BuildInfoSectionBaselineFiles[];
modifyFs: (fs: vfs.FileSystem) => void;
}
@@ -221,7 +255,7 @@ declare const console: { log(msg: any): void; };`;
return originalReadFile.call(host, path);
};
builder.build();
generateSourceMapBaselineFiles(fs, expectedMapFileNames);
if (expectedMapFileNames) generateSourceMapBaselineFiles(fs, expectedMapFileNames);
generateBuildInfoSectionBaselineFiles(fs, expectedBuildInfoFilesForSectionBaselines || emptyArray);
fs.makeReadonly();
return { fs, actualReadFileMap, host, builder };
@@ -267,9 +301,10 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
tick: () => void;
proj: string;
rootNames: readonly string[];
expectedMapFileNames: readonly string[];
/** map file names to generate baseline of */
expectedMapFileNames?: readonly string[];
expectedBuildInfoFilesForSectionBaselines?: readonly BuildInfoSectionBaselineFiles[];
lastProjectOutputJs: string;
lastProjectOutput: string;
initialBuild: BuildState;
outputFiles?: readonly string[];
incrementalDtsChangedBuild?: BuildState;
@@ -281,7 +316,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
export function verifyTsbuildOutput({
scenario, projFs, time, tick, proj, rootNames, outputFiles, baselineOnly, verifyDiagnostics,
expectedMapFileNames, expectedBuildInfoFilesForSectionBaselines, lastProjectOutputJs,
expectedMapFileNames, expectedBuildInfoFilesForSectionBaselines, lastProjectOutput,
initialBuild, incrementalDtsChangedBuild, incrementalDtsUnchangedBuild, incrementalHeaderChangedBuild
}: VerifyTsBuildInput) {
describe(`tsc --b ${proj}:: ${scenario}`, () => {
@@ -330,7 +365,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
let beforeBuildTime: number;
let afterBuildTime: number;
before(() => {
beforeBuildTime = fs.statSync(lastProjectOutputJs).mtimeMs;
beforeBuildTime = fs.statSync(lastProjectOutput).mtimeMs;
tick();
newFs = fs.shadow();
tick();
@@ -342,7 +377,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
expectedBuildInfoFilesForSectionBaselines,
modifyFs: incrementalModifyFs,
}));
afterBuildTime = newFs.statSync(lastProjectOutputJs).mtimeMs;
afterBuildTime = newFs.statSync(lastProjectOutput).mtimeMs;
});
after(() => {
newFs = undefined!;
@@ -358,6 +393,12 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
host.assertDiagnosticMessages(...(incrementalExpectedDiagnostics || emptyArray));
});
}
else {
// Build should pass without errors if not verifying diagnostics
it(`verify no errors`, () => {
host.assertErrors(/*empty*/);
});
}
it(`Generates files matching the baseline`, () => {
generateBaseline(newFs, proj, scenario, subScenario, fs);
});
@@ -372,7 +413,6 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
fs: newFs.shadow(),
tick,
rootNames,
expectedMapFileNames: emptyArray,
modifyFs: fs => {
// Delete output files
for (const outputFile of expectedOutputFiles) {

View File

@@ -16,8 +16,7 @@ namespace ts {
tick,
proj: "inferredTypeFromTransitiveModule",
rootNames: ["/src"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: `/src/obj/index.js`,
lastProjectOutput: `/src/obj/index.js`,
outputFiles: [
"/src/obj/bar.js", "/src/obj/bar.d.ts",
"/src/obj/bundling.js", "/src/obj/bundling.d.ts",

View File

@@ -16,8 +16,7 @@ namespace ts {
tick,
proj: "lateBoundSymbol",
rootNames: ["/src/tsconfig.json"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: "/src/src/main.js",
lastProjectOutput: "/src/src/main.js",
outputFiles: [
"/src/src/hkt.js",
"/src/src/main.js",

View File

@@ -1,8 +1,10 @@
namespace ts {
// https://github.com/microsoft/TypeScript/issues/31696
it("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers to referenced projects resolve correctly", () => {
const baseFs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false, {
files: {
describe("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers to referenced projects resolve correctly", () => {
let projFs: vfs.FileSystem;
const { time, tick } = getTime();
before(() => {
projFs = loadProjectFromFiles({
"/src/common/nominal.ts": utils.dedent`
export declare type Nominal<T, Name extends string> = T & {
[Symbol.species]: Name;
@@ -71,7 +73,6 @@ namespace ts {
"skipLibCheck": true,
"rootDir": "./",
"outDir": "lib",
"lib": ["dom", "es2015", "es2015.symbol.wellknown"]
}
}`,
"/tsconfig.json": utils.dedent`{
@@ -83,16 +84,23 @@ namespace ts {
],
"include": []
}`
},
cwd: "/"
}, time, symbolLibContent);
});
after(() => {
projFs = undefined!;
});
verifyTsbuildOutput({
scenario: `synthesized module specifiers resolve correctly`,
projFs: () => projFs,
time,
tick,
proj: "moduleSpecifiers",
rootNames: ["/"],
lastProjectOutput: `/src/lib/index.d.ts`,
initialBuild: {
modifyFs: noop,
},
baselineOnly: true
});
const fs = baseFs.makeReadonly().shadow();
const sys = new fakes.System(fs, { executingFilePath: "/", newLine: "\n" });
const host = new fakes.SolutionBuilderHost(sys);
const builder = createSolutionBuilder(host, ["/tsconfig.json"], { dry: false, force: false, verbose: false });
builder.build();
// Prior to fixing GH31696 the import in `/lib/src/sub-project-2/index.d.ts` was `import("../../lib/src/common/nonterminal")`, which was invalid.
Harness.Baseline.runBaseline("tsbuild/moduleSpecifiers/initial-build/resolves-correctly.js", vfs.formatPatch(fs.diff(baseFs)));
});
}
}

View File

@@ -288,7 +288,7 @@ namespace ts {
rootNames: ["/src/third"],
expectedMapFileNames,
expectedBuildInfoFilesForSectionBaselines: expectedBuildInfoFilesForSectionBaselines || expectedTsbuildInfoFileNames,
lastProjectOutputJs: outputFiles[project.third][ext.js],
lastProjectOutput: outputFiles[project.third][ext.js],
initialBuild: {
modifyFs,
expectedDiagnostics: initialExpectedDiagnostics,

View File

@@ -617,7 +617,7 @@ export class cNew {}`);
"/src/core/index.d.ts.map",
"/src/logic/index.js.map"
],
lastProjectOutputJs: "/src/tests/index.js",
lastProjectOutput: "/src/tests/index.js",
initialBuild,
incrementalDtsChangedBuild: {
modifyFs: fs => appendText(fs, "/src/core/index.ts", `
@@ -727,7 +727,7 @@ class someClass { }`),
"/src/core/index.d.ts.map",
"/src/logic/index.js.map"
],
lastProjectOutputJs: "/src/tests/index.js",
lastProjectOutput: "/src/tests/index.js",
initialBuild,
incrementalDtsChangedBuild: {
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"declaration": true,`, `"declaration": true,
@@ -795,7 +795,7 @@ class someClass { }`),
"/src/core/index.d.ts.map",
"/src/logic/index.js.map"
],
lastProjectOutputJs: "/src/tests/index.js",
lastProjectOutput: "/src/tests/index.js",
initialBuild: {
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"composite": true,`, `"composite": true,
"tsBuildInfoFile": "ownFile.tsbuildinfo",`),
@@ -851,8 +851,7 @@ class someClass { }`),
tick,
proj: "sample1",
rootNames: ["/src/core"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: "/src/core/index.js",
lastProjectOutput: "/src/core/index.js",
initialBuild: {
modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{
"compilerOptions": {
@@ -892,8 +891,7 @@ class someClass { }`),
tick,
proj: "sample1",
rootNames: ["/src/core"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: "/src/core/index.js",
lastProjectOutput: "/src/core/index.js",
initialBuild: {
modifyFs: fs => {
fs.writeFileSync("/lib/lib.esnext.full.d.ts", `/// <reference no-default-lib="true"/>
@@ -942,8 +940,7 @@ class someClass { }`),
tick,
proj: "sample1",
rootNames: ["/src/core"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: "/src/core/index.js",
lastProjectOutput: "/src/core/index.js",
initialBuild: {
modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{
"compilerOptions": {
@@ -981,8 +978,7 @@ class someClass { }`),
tick,
proj: "sample1",
rootNames: ["/src/tests"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: "/src/tests/index.js",
lastProjectOutput: "/src/tests/index.js",
initialBuild: {
modifyFs: fs => fs.writeFileSync("/src/tests/tsconfig.json", `{
"references": [

View File

@@ -16,17 +16,17 @@ namespace ts.tscWatch {
expectedIncrementalEmit?: readonly File[];
expectedIncrementalErrors?: readonly string[];
}
function verifyIncrementalWatchEmit(input: VerifyIncrementalWatchEmitInput) {
function verifyIncrementalWatchEmit(input: () => VerifyIncrementalWatchEmitInput) {
it("with tsc --w", () => {
verifyIncrementalWatchEmitWorker({
input,
input: input(),
emitAndReportErrors: createWatchOfConfigFile,
verifyErrors: checkOutputErrorsInitial
});
});
it("with tsc", () => {
verifyIncrementalWatchEmitWorker({
input,
input: input(),
emitAndReportErrors: incrementalBuild,
verifyErrors: checkNormalBuildErrors
});
@@ -122,7 +122,7 @@ namespace ts.tscWatch {
function checkFileEmit(actual: Map<string>, expected: readonly File[]) {
assert.equal(actual.size, expected.length, `Actual: ${JSON.stringify(arrayFrom(actual.entries()), /*replacer*/ undefined, " ")}\nExpected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}`);
expected.forEach(file => {
for (const file of expected) {
let expectedContent = file.content;
let actualContent = actual.get(file.path);
if (isBuildInfoFile(file.path)) {
@@ -130,7 +130,7 @@ namespace ts.tscWatch {
expectedContent = sanitizeBuildInfo(expectedContent);
}
assert.equal(actualContent, expectedContent, `Emit for ${file.path}`);
});
}
}
const libFileInfo: BuilderState.FileInfo = {
@@ -170,7 +170,7 @@ namespace ts.tscWatch {
describe("own file emit without errors", () => {
function verify(optionsToExtend?: CompilerOptions, expectedBuildinfoOptions?: CompilerOptions) {
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, file2, configFile],
optionsToExtend,
expectedInitialEmit: [
@@ -226,7 +226,7 @@ namespace ts.tscWatch {
}
],
expectedIncrementalErrors: emptyArray,
});
}));
}
verify();
describe("with commandline parameters that are not relative", () => {
@@ -259,7 +259,7 @@ namespace ts.tscWatch {
"file2.ts(1,7): error TS2322: Type '20' is not assignable to type 'string'.\n"
];
const modifiedFile1Content = file1.content.replace("x", "z");
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, fileModified, configFile],
expectedInitialEmit: [
file1Js,
@@ -320,7 +320,7 @@ namespace ts.tscWatch {
}
],
expectedIncrementalErrors: file2Errors,
});
}));
});
describe("with --out", () => {
@@ -332,7 +332,7 @@ namespace ts.tscWatch {
path: `${project}/out.js`,
content: "var x = 10;\nvar y = 20;\n"
};
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, file2, config],
expectedInitialEmit: [
outFile,
@@ -353,7 +353,7 @@ namespace ts.tscWatch {
}
],
expectedInitialErrors: emptyArray
});
}));
});
});
@@ -397,7 +397,7 @@ namespace ts.tscWatch {
describe("own file emit without errors", () => {
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, file2, config],
expectedInitialEmit: [
file1Js,
@@ -451,7 +451,7 @@ namespace ts.tscWatch {
}
],
expectedIncrementalErrors: emptyArray,
});
}));
});
describe("own file emit with errors", () => {
@@ -479,7 +479,7 @@ namespace ts.tscWatch {
"file2.ts(1,14): error TS2322: Type '20' is not assignable to type 'string'.\n"
];
const modifiedFile1Content = file1.content.replace("x = 10", "z = 10");
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, fileModified, config],
expectedInitialEmit: [
file1Js,
@@ -541,6 +541,49 @@ namespace ts.tscWatch {
}
],
expectedIncrementalErrors: file2Errors,
}));
it("verify that state is read correctly", () => {
const system = createWatchedSystem([libFile, file1, fileModified, config], { currentDirectory: project });
incrementalBuild("tsconfig.json", system);
const command = parseConfigFileWithSystem("tsconfig.json", {}, system, noop)!;
const builderProgram = createIncrementalProgram({
rootNames: command.fileNames,
options: command.options,
projectReferences: command.projectReferences,
configFileParsingDiagnostics: getConfigFileParsingDiagnostics(command),
host: createIncrementalCompilerHost(command.options, system)
});
const state = builderProgram.getState();
assert.equal(state.changedFilesSet!.size, 0, "changes");
assert.equal(state.fileInfos.size, 3, "FileInfo size");
assert.deepEqual(state.fileInfos.get(libFile.path), libFileInfo);
assert.deepEqual(state.fileInfos.get(file1.path), getFileInfo(file1.content));
assert.deepEqual(state.fileInfos.get(file2.path), file2FileInfo);
assert.deepEqual(state.compilerOptions, {
incremental: true,
module: ModuleKind.AMD,
configFilePath: config.path
});
assert.equal(state.referencedMap!.size, 0);
assert.equal(state.exportedModulesMap!.size, 0);
assert.equal(state.semanticDiagnosticsPerFile!.size, 3);
assert.deepEqual(state.semanticDiagnosticsPerFile!.get(libFile.path), emptyArray);
assert.deepEqual(state.semanticDiagnosticsPerFile!.get(file1.path), emptyArray);
const { file: _, relatedInformation: __, ...rest } = file2ReuasableError[1][0];
assert.deepEqual(state.semanticDiagnosticsPerFile!.get(file2.path), [{
...rest,
file: state.program!.getSourceFileByPath(file2.path as Path)!,
relatedInformation: undefined,
reportsUnnecessary: undefined,
source: undefined
}]);
});
});
@@ -561,7 +604,7 @@ namespace ts.tscWatch {
});
`;
}
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, file2, config],
expectedInitialEmit: [
outFile,
@@ -582,7 +625,140 @@ namespace ts.tscWatch {
}
],
expectedInitialErrors: emptyArray
});
}));
});
});
describe("incremental with circular references", () => {
function getFileInfo(content: string): BuilderState.FileInfo {
const signature = Harness.mockHash(content);
return { version: signature, signature };
}
const config: File = {
path: configFile.path,
content: JSON.stringify({
compilerOptions: {
incremental: true,
target: "es5",
module: "commonjs",
declaration: true,
emitDeclarationOnly: true
}
})
};
const aTs: File = {
path: `${project}/a.ts`,
content: `import { B } from "./b";
export interface A {
b: B;
}
`
};
const bTs: File = {
path: `${project}/b.ts`,
content: `import { C } from "./c";
export interface B {
b: C;
}
`
};
const cTs: File = {
path: `${project}/c.ts`,
content: `import { A } from "./a";
export interface C {
a: A;
}
`
};
const indexTs: File = {
path: `${project}/index.ts`,
content: `export { A } from "./a";
export { B } from "./b";
export { C } from "./c";
`
};
verifyIncrementalWatchEmit(() => {
const referencedMap: MapLike<string[]> = {
"./a.ts": ["./b.ts"],
"./b.ts": ["./c.ts"],
"./c.ts": ["./a.ts"],
"./index.ts": ["./a.ts", "./b.ts", "./c.ts"],
};
const initialProgram: ProgramBuildInfo = {
fileInfos: {
[libFilePath]: libFileInfo,
"./c.ts": getFileInfo(cTs.content),
"./b.ts": getFileInfo(bTs.content),
"./a.ts": getFileInfo(aTs.content),
"./index.ts": getFileInfo(indexTs.content)
},
options: {
incremental: true,
target: ScriptTarget.ES5,
module: ModuleKind.CommonJS,
declaration: true,
emitDeclarationOnly: true,
configFilePath: "./tsconfig.json"
},
referencedMap,
exportedModulesMap: referencedMap,
semanticDiagnosticsPerFile: [
libFilePath,
"./a.ts",
"./b.ts",
"./c.ts",
"./index.ts",
]
};
const { fileInfos, ...rest } = initialProgram;
const expectedADts: File = { path: `${project}/a.d.ts`, content: aTs.content };
const expectedBDts: File = { path: `${project}/b.d.ts`, content: bTs.content };
const expectedCDts: File = { path: `${project}/c.d.ts`, content: cTs.content };
const expectedIndexDts: File = { path: `${project}/index.d.ts`, content: indexTs.content };
const modifiedATsContent = aTs.content.replace("b: B;", `b: B;
foo: any;`);
return {
files: [libFile, aTs, bTs, cTs, indexTs, config],
expectedInitialEmit: [
expectedADts,
expectedBDts,
expectedCDts,
expectedIndexDts,
{
path: `${project}/tsconfig.tsbuildinfo`,
content: getBuildInfoText({
program: initialProgram,
version
})
}
],
expectedInitialErrors: emptyArray,
modifyFs: host => host.writeFile(aTs.path, modifiedATsContent),
expectedIncrementalEmit: [
{ path: expectedADts.path, content: modifiedATsContent },
expectedBDts,
expectedCDts,
expectedIndexDts,
{
path: `${project}/tsconfig.tsbuildinfo`,
content: getBuildInfoText({
program: {
fileInfos: {
[libFilePath]: libFileInfo,
"./c.ts": getFileInfo(cTs.content),
"./b.ts": getFileInfo(bTs.content),
"./a.ts": getFileInfo(modifiedATsContent),
"./index.ts": getFileInfo(indexTs.content)
},
...rest
},
version
})
}
],
expectedIncrementalErrors: emptyArray
};
});
});
});