Add writeFile and customTransformers to build and buildReferences (#43984)

This commit is contained in:
Sheetal Nandi
2021-05-07 12:13:17 -07:00
committed by GitHub
parent 44238717f1
commit e5395efe49
6 changed files with 352 additions and 11 deletions

View File

@@ -131,9 +131,9 @@ namespace ts {
}
export interface SolutionBuilder<T extends BuilderProgram> {
build(project?: string, cancellationToken?: CancellationToken): ExitStatus;
build(project?: string, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, getCustomTransformers?: (project: string) => CustomTransformers): ExitStatus;
clean(project?: string): ExitStatus;
buildReferences(project: string, cancellationToken?: CancellationToken): ExitStatus;
buildReferences(project: string, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, getCustomTransformers?: (project: string) => CustomTransformers): ExitStatus;
cleanReferences(project?: string): ExitStatus;
getNextInvalidatedProject(cancellationToken?: CancellationToken): InvalidatedProject<T> | undefined;
@@ -1125,7 +1125,7 @@ namespace ts {
break;
case BuildStep.BuildInvalidatedProjectOfBundle:
Debug.checkDefined(invalidatedProjectOfBundle).done(cancellationToken);
Debug.checkDefined(invalidatedProjectOfBundle).done(cancellationToken, writeFile, customTransformers);
step = BuildStep.Done;
break;
@@ -1649,7 +1649,7 @@ namespace ts {
}
}
function build(state: SolutionBuilderState, project?: string, cancellationToken?: CancellationToken, onlyReferences?: boolean): ExitStatus {
function build(state: SolutionBuilderState, project?: string, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, getCustomTransformers?: (project: string) => CustomTransformers, onlyReferences?: boolean): ExitStatus {
const buildOrder = getBuildOrderFor(state, project, onlyReferences);
if (!buildOrder) return ExitStatus.InvalidProject_OutputsSkipped;
@@ -1661,7 +1661,7 @@ namespace ts {
const invalidatedProject = getNextInvalidatedProject(state, buildOrder, reportQueue);
if (!invalidatedProject) break;
reportQueue = false;
invalidatedProject.done(cancellationToken);
invalidatedProject.done(cancellationToken, writeFile, getCustomTransformers?.(invalidatedProject.project));
if (!state.diagnostics.has(invalidatedProject.projectPath)) successfulProjects++;
}
@@ -1894,9 +1894,9 @@ namespace ts {
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: boolean, hostOrHostWithWatch: SolutionBuilderHost<T> | SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], options: BuildOptions, baseWatchOptions?: WatchOptions): SolutionBuilder<T> {
const state = createSolutionBuilderState(watch, hostOrHostWithWatch, rootNames, options, baseWatchOptions);
return {
build: (project, cancellationToken) => build(state, project, cancellationToken),
build: (project, cancellationToken, writeFile, getCustomTransformers) => build(state, project, cancellationToken, writeFile, getCustomTransformers),
clean: project => clean(state, project),
buildReferences: (project, cancellationToken) => build(state, project, cancellationToken, /*onlyReferences*/ true),
buildReferences: (project, cancellationToken, writeFile, getCustomTransformers) => build(state, project, cancellationToken, writeFile, getCustomTransformers, /*onlyReferences*/ true),
cleanReferences: project => clean(state, project, /*onlyReferences*/ true),
getNextInvalidatedProject: cancellationToken => {
setupInitialBuild(state, cancellationToken);

View File

@@ -131,6 +131,7 @@
"unittests/tsbuild/noEmitOnError.ts",
"unittests/tsbuild/outFile.ts",
"unittests/tsbuild/outputPaths.ts",
"unittests/tsbuild/publicApi.ts",
"unittests/tsbuild/referencesWithRootDirInParent.ts",
"unittests/tsbuild/resolveJsonModule.ts",
"unittests/tsbuild/sample.ts",

View File

@@ -0,0 +1,124 @@
namespace ts {
describe("unittests:: tsbuild:: Public API with custom transformers when passed to build", () => {
let sys: TscCompileSystem;
before(() => {
const initialFs = getFsWithTime(loadProjectFromFiles({
"/src/tsconfig.json": JSON.stringify({
references: [
{ path: "./shared/tsconfig.json" },
{ path: "./webpack/tsconfig.json" }
],
files: []
}),
"/src/shared/tsconfig.json": JSON.stringify({
compilerOptions: { composite: true },
}),
"/src/shared/index.ts": `export function f1() { }
export class c { }
export enum e { }
// leading
export function f2() { } // trailing`,
"/src/webpack/tsconfig.json": JSON.stringify({
compilerOptions: {
composite: true,
},
references: [{ path: "../shared/tsconfig.json" }]
}),
"/src/webpack/index.ts": `export function f2() { }
export class c2 { }
export enum e2 { }
// leading
export function f22() { } // trailing`,
})).fs.makeReadonly();
const inputFs = initialFs.shadow();
inputFs.makeReadonly();
const fs = inputFs.shadow();
// Create system
sys = new fakes.System(fs, { executingFilePath: "/lib/tsc" }) as TscCompileSystem;
fakes.patchHostForBuildInfoReadWrite(sys);
const commandLineArgs = ["--b", "/src/tsconfig.json"];
sys.write(`${sys.getExecutingFilePath()} ${commandLineArgs.join(" ")}\n`);
sys.exit = exitCode => sys.exitCode = exitCode;
const writtenFiles = sys.writtenFiles = new Set();
const originalWriteFile = sys.writeFile;
sys.writeFile = (fileName, content, writeByteOrderMark) => {
const path = toPathWithSystem(sys, fileName);
assert.isFalse(writtenFiles.has(path));
writtenFiles.add(path);
return originalWriteFile.call(sys, fileName, content, writeByteOrderMark);
};
const { cb, getPrograms } = commandLineCallbacks(sys, /*originalReadCall*/ undefined, originalWriteFile);
const buildHost = createSolutionBuilderHost(
sys,
/*createProgram*/ undefined,
createDiagnosticReporter(sys, /*pretty*/ true),
createBuilderStatusReporter(sys, /*pretty*/ true),
errorCount => sys.write(getErrorSummaryText(errorCount, sys.newLine))
);
buildHost.afterProgramEmitAndDiagnostics = cb;
buildHost.afterEmitBundle = cb;
const builder = createSolutionBuilder(buildHost, [commandLineArgs[1]], { verbose: true });
const exitStatus = builder.build(/*project*/ undefined, /*cancellationToken*/ undefined, /*writeFile*/ undefined, getCustomTransformers);
sys.exit(exitStatus);
sys.write(`exitCode:: ExitStatus.${ExitStatus[sys.exitCode as ExitStatus]}\n`);
const baseline: string[] = [];
tscWatch.baselinePrograms(baseline, getPrograms, emptyArray, /*baselineDependencies*/ false);
sys.write(baseline.join("\n"));
fs.makeReadonly();
sys.baseLine = () => {
const baseFsPatch = inputFs.diff(/*base*/ undefined, { baseIsNotShadowRoot: true });
const patch = fs.diff(inputFs, { includeChangedFileWithSameContent: true });
return {
file: `tsbuild/$publicAPI/${BuildKind.Initial}/${"build with custom transformers".split(" ").join("-")}.js`,
text: `Input::
${baseFsPatch ? vfs.formatPatch(baseFsPatch) : ""}
Output::
${sys.output.join("")}
${patch ? vfs.formatPatch(patch) : ""}`
};
};
function getCustomTransformers(project: string): CustomTransformers {
const before: TransformerFactory<SourceFile> = context => {
return file => visitEachChild(file, visit, context);
function visit(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
return visitFunction(<FunctionDeclaration>node);
default:
return visitEachChild(node, visit, context);
}
}
function visitFunction(node: FunctionDeclaration) {
addSyntheticLeadingComment(node, SyntaxKind.MultiLineCommentTrivia, `@before${project}`, /*hasTrailingNewLine*/ true);
return node;
}
};
const after: TransformerFactory<SourceFile> = context => {
return file => visitEachChild(file, visit, context);
function visit(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.VariableStatement:
return visitVariableStatement(<VariableStatement>node);
default:
return visitEachChild(node, visit, context);
}
}
function visitVariableStatement(node: VariableStatement) {
addSyntheticLeadingComment(node, SyntaxKind.SingleLineCommentTrivia, `@after${project}`);
return node;
}
};
return { before: [before], after: [after] };
}
});
after(() => {
sys = undefined!;
});
verifyTscBaseline(() => sys);
});
}

View File

@@ -5219,9 +5219,9 @@ declare namespace ts {
interface SolutionBuilderWithWatchHost<T extends BuilderProgram> extends SolutionBuilderHostBase<T>, WatchHost {
}
interface SolutionBuilder<T extends BuilderProgram> {
build(project?: string, cancellationToken?: CancellationToken): ExitStatus;
build(project?: string, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, getCustomTransformers?: (project: string) => CustomTransformers): ExitStatus;
clean(project?: string): ExitStatus;
buildReferences(project: string, cancellationToken?: CancellationToken): ExitStatus;
buildReferences(project: string, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, getCustomTransformers?: (project: string) => CustomTransformers): ExitStatus;
cleanReferences(project?: string): ExitStatus;
getNextInvalidatedProject(cancellationToken?: CancellationToken): InvalidatedProject<T> | undefined;
}

View File

@@ -5219,9 +5219,9 @@ declare namespace ts {
interface SolutionBuilderWithWatchHost<T extends BuilderProgram> extends SolutionBuilderHostBase<T>, WatchHost {
}
interface SolutionBuilder<T extends BuilderProgram> {
build(project?: string, cancellationToken?: CancellationToken): ExitStatus;
build(project?: string, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, getCustomTransformers?: (project: string) => CustomTransformers): ExitStatus;
clean(project?: string): ExitStatus;
buildReferences(project: string, cancellationToken?: CancellationToken): ExitStatus;
buildReferences(project: string, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, getCustomTransformers?: (project: string) => CustomTransformers): ExitStatus;
cleanReferences(project?: string): ExitStatus;
getNextInvalidatedProject(cancellationToken?: CancellationToken): InvalidatedProject<T> | undefined;
}

View File

@@ -0,0 +1,216 @@
Input::
//// [/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
interface ReadonlyArray<T> {}
declare const console: { log(msg: any): void; };
//// [/src/shared/index.ts]
export function f1() { }
export class c { }
export enum e { }
// leading
export function f2() { } // trailing
//// [/src/shared/tsconfig.json]
{"compilerOptions":{"composite":true}}
//// [/src/tsconfig.json]
{"references":[{"path":"./shared/tsconfig.json"},{"path":"./webpack/tsconfig.json"}],"files":[]}
//// [/src/webpack/index.ts]
export function f2() { }
export class c2 { }
export enum e2 { }
// leading
export function f22() { } // trailing
//// [/src/webpack/tsconfig.json]
{"compilerOptions":{"composite":true},"references":[{"path":"../shared/tsconfig.json"}]}
Output::
/lib/tsc --b /src/tsconfig.json
[12:00:00 AM] Projects in this build:
* src/shared/tsconfig.json
* src/webpack/tsconfig.json
* src/tsconfig.json
[12:00:00 AM] Project 'src/shared/tsconfig.json' is out of date because output file 'src/shared/index.js' does not exist
[12:00:00 AM] Building project '/src/shared/tsconfig.json'...
[12:00:00 AM] Project 'src/webpack/tsconfig.json' is out of date because output file 'src/webpack/index.js' does not exist
[12:00:00 AM] Building project '/src/webpack/tsconfig.json'...
exitCode:: ExitStatus.Success
Program root files: ["/src/shared/index.ts"]
Program options: {"composite":true,"configFilePath":"/src/shared/tsconfig.json"}
Program structureReused: Not
Program files::
/lib/lib.d.ts
/src/shared/index.ts
Semantic diagnostics in builder refreshed for::
/lib/lib.d.ts
/src/shared/index.ts
Program root files: ["/src/webpack/index.ts"]
Program options: {"composite":true,"configFilePath":"/src/webpack/tsconfig.json"}
Program structureReused: Not
Program files::
/lib/lib.d.ts
/src/webpack/index.ts
Semantic diagnostics in builder refreshed for::
/lib/lib.d.ts
/src/webpack/index.ts
//// [/src/shared/index.d.ts]
export declare function f1(): void;
export declare class c {
}
export declare enum e {
}
export declare function f2(): void;
//// [/src/shared/index.js]
"use strict";
exports.__esModule = true;
exports.f2 = exports.e = exports.c = exports.f1 = void 0;
/*@before/src/shared/tsconfig.json*/
function f1() { }
exports.f1 = f1;
//@after/src/shared/tsconfig.json
var c = /** @class */ (function () {
function c() {
}
return c;
}());
exports.c = c;
//@after/src/shared/tsconfig.json
var e;
(function (e) {
})(e = exports.e || (exports.e = {}));
// leading
/*@before/src/shared/tsconfig.json*/
function f2() { } // trailing
exports.f2 = f2;
//// [/src/shared/tsconfig.tsbuildinfo]
{"program":{"fileNames":["../../lib/lib.d.ts","./index.ts"],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},"8649344783-export function f1() { }\nexport class c { }\nexport enum e { }\n// leading\nexport function f2() { } // trailing"],"options":{"composite":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,2]},"version":"FakeTSVersion"}
//// [/src/shared/tsconfig.tsbuildinfo.readable.baseline.txt]
{
"program": {
"fileNames": [
"../../lib/lib.d.ts",
"./index.ts"
],
"fileInfos": {
"../../lib/lib.d.ts": {
"version": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
"signature": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
"affectsGlobalScope": true
},
"./index.ts": {
"version": "8649344783-export function f1() { }\nexport class c { }\nexport enum e { }\n// leading\nexport function f2() { } // trailing",
"signature": "8649344783-export function f1() { }\nexport class c { }\nexport enum e { }\n// leading\nexport function f2() { } // trailing"
}
},
"options": {
"composite": true
},
"referencedMap": {},
"exportedModulesMap": {},
"semanticDiagnosticsPerFile": [
"../../lib/lib.d.ts",
"./index.ts"
]
},
"version": "FakeTSVersion",
"size": 814
}
//// [/src/webpack/index.d.ts]
export declare function f2(): void;
export declare class c2 {
}
export declare enum e2 {
}
export declare function f22(): void;
//// [/src/webpack/index.js]
"use strict";
exports.__esModule = true;
exports.f22 = exports.e2 = exports.c2 = exports.f2 = void 0;
/*@before/src/webpack/tsconfig.json*/
function f2() { }
exports.f2 = f2;
//@after/src/webpack/tsconfig.json
var c2 = /** @class */ (function () {
function c2() {
}
return c2;
}());
exports.c2 = c2;
//@after/src/webpack/tsconfig.json
var e2;
(function (e2) {
})(e2 = exports.e2 || (exports.e2 = {}));
// leading
/*@before/src/webpack/tsconfig.json*/
function f22() { } // trailing
exports.f22 = f22;
//// [/src/webpack/tsconfig.tsbuildinfo]
{"program":{"fileNames":["../../lib/lib.d.ts","./index.ts"],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},"20140662566-export function f2() { }\nexport class c2 { }\nexport enum e2 { }\n// leading\nexport function f22() { } // trailing"],"options":{"composite":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,2]},"version":"FakeTSVersion"}
//// [/src/webpack/tsconfig.tsbuildinfo.readable.baseline.txt]
{
"program": {
"fileNames": [
"../../lib/lib.d.ts",
"./index.ts"
],
"fileInfos": {
"../../lib/lib.d.ts": {
"version": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
"signature": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
"affectsGlobalScope": true
},
"./index.ts": {
"version": "20140662566-export function f2() { }\nexport class c2 { }\nexport enum e2 { }\n// leading\nexport function f22() { } // trailing",
"signature": "20140662566-export function f2() { }\nexport class c2 { }\nexport enum e2 { }\n// leading\nexport function f22() { } // trailing"
}
},
"options": {
"composite": true
},
"referencedMap": {},
"exportedModulesMap": {},
"semanticDiagnosticsPerFile": [
"../../lib/lib.d.ts",
"./index.ts"
]
},
"version": "FakeTSVersion",
"size": 818
}