mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 21:07:27 -05:00
Handle seenEmittedFiles which was not being set when emit of a file was complete (#33145)
* Add test that fails because file is written multiple times Reported from #33061 * Handle seenEmittedFiles which was not being set when emit of a file was complete. It was issue only when errors are reported before emitting (which puts the files into pendingEmit that needs to check only in seenEmittedFiles) If emit happens before semantic diagnostics query this issue is not repro, because the affected files come into play and those are being set correctly Fixes #31398 * make baselining source map optional * Handle emitDeclarationOnly in --build scenario * Ensure we are using d.ts emit as signature even when --declarationMap is on (map files are emitted before d.ts) * Move module specifiers to verifyTsBuildOutput * implement create Hash to be default hashing plus data so we can verify it easily in baseline * Remove failing baseline * Accept correct baseline name
This commit is contained in:
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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: [
|
||||
|
||||
109
src/testRunner/unittests/tsbuild/emitDeclarationOnly.ts
Normal file
109
src/testRunner/unittests/tsbuild/emitDeclarationOnly.ts
Normal 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
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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: ReadonlyArray<string>;
|
||||
expectedMapFileNames: ReadonlyArray<string>;
|
||||
expectedMapFileNames?: ReadonlyArray<string>;
|
||||
expectedBuildInfoFilesForSectionBaselines?: ReadonlyArray<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 };
|
||||
@@ -268,9 +302,10 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
|
||||
tick: () => void;
|
||||
proj: string;
|
||||
rootNames: ReadonlyArray<string>;
|
||||
expectedMapFileNames: ReadonlyArray<string>;
|
||||
/** map file names to generate baseline of */
|
||||
expectedMapFileNames?: ReadonlyArray<string>;
|
||||
expectedBuildInfoFilesForSectionBaselines?: ReadonlyArray<BuildInfoSectionBaselineFiles>;
|
||||
lastProjectOutputJs: string;
|
||||
lastProjectOutput: string;
|
||||
initialBuild: BuildState;
|
||||
outputFiles?: ReadonlyArray<string>;
|
||||
incrementalDtsChangedBuild?: BuildState;
|
||||
@@ -282,7 +317,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}`, () => {
|
||||
@@ -331,7 +366,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();
|
||||
@@ -343,7 +378,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!;
|
||||
@@ -359,6 +394,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);
|
||||
});
|
||||
@@ -373,7 +414,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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -16,17 +16,17 @@ namespace ts.tscWatch {
|
||||
expectedIncrementalEmit?: ReadonlyArray<File>;
|
||||
expectedIncrementalErrors?: ReadonlyArray<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: ReadonlyArray<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,7 +541,7 @@ namespace ts.tscWatch {
|
||||
}
|
||||
],
|
||||
expectedIncrementalErrors: file2Errors,
|
||||
});
|
||||
}));
|
||||
|
||||
it("verify that state is read correctly", () => {
|
||||
const system = createWatchedSystem([libFile, file1, fileModified, config], { currentDirectory: project });
|
||||
@@ -604,7 +604,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
`;
|
||||
}
|
||||
verifyIncrementalWatchEmit({
|
||||
verifyIncrementalWatchEmit(() => ({
|
||||
files: [libFile, file1, file2, config],
|
||||
expectedInitialEmit: [
|
||||
outFile,
|
||||
@@ -625,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
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user