• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// @ts-check
2const path = require("path");
3const fs = require("fs");
4const log = require("fancy-log");
5const newer = require("gulp-newer");
6const sourcemaps = require("gulp-sourcemaps");
7const del = require("del");
8const rename = require("gulp-rename");
9const concat = require("gulp-concat");
10const merge2 = require("merge2");
11const mkdirp = require("mkdirp");
12const { src, dest, task, parallel, series, watch } = require("gulp");
13const { append, transform } = require("gulp-insert");
14const { prependFile } = require("./scripts/build/prepend");
15const { exec, readJson, needsUpdate, getDiffTool, getDirSize, rm } = require("./scripts/build/utils");
16const { runConsoleTests, refBaseline, localBaseline, refRwcBaseline, localRwcBaseline } = require("./scripts/build/tests");
17const { buildProject, cleanProject, watchProject } = require("./scripts/build/projects");
18const cmdLineOptions = require("./scripts/build/options");
19
20const copyright = "CopyrightNotice.txt";
21const cleanTasks = [];
22
23const buildScripts = () => buildProject("scripts");
24task("scripts", buildScripts);
25task("scripts").description = "Builds files in the 'scripts' folder.";
26
27const cleanScripts = () => cleanProject("scripts");
28cleanTasks.push(cleanScripts);
29
30const libraries = readJson("./src/lib/libs.json");
31const libs = libraries.libs.map(lib => {
32    const relativeSources = ["header.d.ts"].concat(libraries.sources && libraries.sources[lib] || [lib + ".d.ts"]);
33    const relativeTarget = libraries.paths && libraries.paths[lib] || ("lib." + lib + ".d.ts");
34    const sources = relativeSources.map(s => path.posix.join("src/lib", s));
35    const target = `built/local/${relativeTarget}`;
36    return { target, relativeTarget, sources };
37});
38
39const generateLibs = () => {
40    return merge2(libs.map(({ sources, target, relativeTarget }) =>
41        src([copyright, ...sources])
42            .pipe(newer(target))
43            .pipe(concat(relativeTarget, { newLine: "\n\n" }))
44            .pipe(dest("built/local"))));
45};
46task("lib", generateLibs);
47task("lib").description = "Builds the library targets";
48
49const cleanLib = () => del(libs.map(lib => lib.target));
50cleanTasks.push(cleanLib);
51
52const watchLib = () => watch(["src/lib/**/*"], generateLibs);
53
54const diagnosticInformationMapTs = "src/compiler/diagnosticInformationMap.generated.ts";
55const diagnosticMessagesJson = "src/compiler/diagnosticMessages.json";
56const diagnosticMessagesGeneratedJson = "src/compiler/diagnosticMessages.generated.json";
57const generateDiagnostics = async () => {
58    if (needsUpdate(diagnosticMessagesJson, [diagnosticMessagesGeneratedJson, diagnosticInformationMapTs])) {
59        await exec(process.execPath, ["scripts/processDiagnosticMessages.js", diagnosticMessagesJson]);
60    }
61};
62task("generate-diagnostics", series(buildScripts, generateDiagnostics));
63task("generate-diagnostics").description = "Generates a diagnostic file in TypeScript based on an input JSON file";
64
65const cleanDiagnostics = () => del([diagnosticInformationMapTs, diagnosticMessagesGeneratedJson]);
66cleanTasks.push(cleanDiagnostics);
67
68const watchDiagnostics = () => watch(["src/compiler/diagnosticMessages.json"], task("generate-diagnostics"));
69
70// Localize diagnostics
71/**
72 * .lcg file is what localization team uses to know what messages to localize.
73 * The file is always generated in 'enu/diagnosticMessages.generated.json.lcg'
74 */
75const generatedLCGFile = "built/local/enu/diagnosticMessages.generated.json.lcg";
76
77/**
78 * The localization target produces the two following transformations:
79 *    1. 'src\loc\lcl\<locale>\diagnosticMessages.generated.json.lcl' => 'built\local\<locale>\diagnosticMessages.generated.json'
80 *       convert localized resources into a .json file the compiler can understand
81 *    2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg'
82 *       generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json
83 */
84const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-br", "ru", "tr", "zh-cn", "zh-tw"]
85    .map(f => f.toLowerCase())
86    .map(f => `built/local/${f}/diagnosticMessages.generated.json`)
87    .concat(generatedLCGFile);
88
89const localize = async () => {
90    if (needsUpdate(diagnosticMessagesGeneratedJson, generatedLCGFile)) {
91        return exec(process.execPath, ["scripts/generateLocalizedDiagnosticMessages.js", "src/loc/lcl", "built/local", diagnosticMessagesGeneratedJson], { ignoreExitCode: true });
92    }
93};
94
95const buildShims = () => buildProject("src/shims");
96const cleanShims = () => cleanProject("src/shims");
97cleanTasks.push(cleanShims);
98
99const buildDebugTools = () => buildProject("src/debug");
100const cleanDebugTools = () => cleanProject("src/debug");
101cleanTasks.push(cleanDebugTools);
102
103const buildShimsAndTools = parallel(buildShims, buildDebugTools);
104
105// Pre-build steps when targeting the LKG compiler
106const lkgPreBuild = parallel(generateLibs, series(buildScripts, generateDiagnostics, buildShimsAndTools));
107
108const buildTsc = () => buildProject("src/tsc");
109task("tsc", series(lkgPreBuild, buildTsc));
110task("tsc").description = "Builds the command-line compiler";
111
112const cleanTsc = () => cleanProject("src/tsc");
113cleanTasks.push(cleanTsc);
114task("clean-tsc", cleanTsc);
115task("clean-tsc").description = "Cleans outputs for the command-line compiler";
116
117const watchTsc = () => watchProject("src/tsc");
118task("watch-tsc", series(lkgPreBuild, parallel(watchLib, watchDiagnostics, watchTsc)));
119task("watch-tsc").description = "Watch for changes and rebuild the command-line compiler only.";
120
121// Pre-build steps when targeting the built/local compiler.
122const localPreBuild = parallel(generateLibs, series(buildScripts, generateDiagnostics, buildShimsAndTools, buildTsc));
123
124// Pre-build steps to use based on supplied options.
125const preBuild = cmdLineOptions.lkg ? lkgPreBuild : localPreBuild;
126
127const buildServices = (() => {
128    // build typescriptServices.out.js
129    const buildTypescriptServicesOut = () => buildProject("src/typescriptServices/tsconfig.json", cmdLineOptions);
130
131    // create typescriptServices.js
132    const createTypescriptServicesJs = () => src("built/local/typescriptServices.out.js")
133        .pipe(newer("built/local/typescriptServices.js"))
134        .pipe(sourcemaps.init({ loadMaps: true }))
135        .pipe(prependFile(copyright))
136        .pipe(rename("typescriptServices.js"))
137        .pipe(sourcemaps.write(".", { includeContent: false, destPath: "built/local" }))
138        .pipe(dest("built/local"));
139
140    // create typescriptServices.d.ts
141    const createTypescriptServicesDts = () => src("built/local/typescriptServices.out.d.ts")
142        .pipe(newer("built/local/typescriptServices.d.ts"))
143        .pipe(prependFile(copyright))
144        .pipe(transform(content => content.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4")))
145        .pipe(rename("typescriptServices.d.ts"))
146        .pipe(dest("built/local"));
147
148    // create typescript.js
149    const createTypescriptJs = () => src("built/local/typescriptServices.js")
150        .pipe(newer("built/local/typescript.js"))
151        .pipe(sourcemaps.init({ loadMaps: true }))
152        .pipe(rename("typescript.js"))
153        .pipe(sourcemaps.write(".", { includeContent: false, destPath: "built/local" }))
154        .pipe(dest("built/local"));
155
156    // create typescript.d.ts
157    const createTypescriptDts = () => src("built/local/typescriptServices.d.ts")
158        .pipe(newer("built/local/typescript.d.ts"))
159        .pipe(append("\nexport = ts;"))
160        .pipe(rename("typescript.d.ts"))
161        .pipe(dest("built/local"));
162
163    // create typescript_standalone.d.ts
164    const createTypescriptStandaloneDts = () => src("built/local/typescriptServices.d.ts")
165        .pipe(newer("built/local/typescript_standalone.d.ts"))
166        .pipe(transform(content => content.replace(/declare (namespace|module) ts/g, 'declare module "typescript"')))
167        .pipe(rename("typescript_standalone.d.ts"))
168        .pipe(dest("built/local"));
169
170    return series(
171        buildTypescriptServicesOut,
172        createTypescriptServicesJs,
173        createTypescriptServicesDts,
174        createTypescriptJs,
175        createTypescriptDts,
176        createTypescriptStandaloneDts,
177    );
178})();
179task("services", series(preBuild, buildServices));
180task("services").description = "Builds the language service";
181task("services").flags = {
182    "   --built": "Compile using the built version of the compiler."
183};
184
185const cleanServices = async () => {
186    if (fs.existsSync("built/local/typescriptServices.tsconfig.json")) {
187        await cleanProject("built/local/typescriptServices.tsconfig.json");
188    }
189    await del([
190        "built/local/typescriptServices.out.js",
191        "built/local/typescriptServices.out.d.ts",
192        "built/local/typescriptServices.out.tsbuildinfo",
193        "built/local/typescriptServices.js",
194        "built/local/typescript.js",
195        "built/local/typescript.d.ts",
196        "built/local/typescript_standalone.d.ts"
197    ]);
198};
199cleanTasks.push(cleanServices);
200task("clean-services", cleanServices);
201task("clean-services").description = "Cleans outputs for the language service";
202
203const watchServices = () => watch([
204    "src/compiler/tsconfig.json",
205    "src/compiler/**/*.ts",
206    "src/jsTyping/tsconfig.json",
207    "src/jsTyping/**/*.ts",
208    "src/services/tsconfig.json",
209    "src/services/**/*.ts",
210], series(preBuild, buildServices));
211task("watch-services", series(preBuild, parallel(watchLib, watchDiagnostics, watchServices)));
212task("watch-services").description = "Watches for changes and rebuild language service only";
213task("watch-services").flags = {
214    "   --built": "Compile using the built version of the compiler."
215};
216
217const buildServer = () => buildProject("src/tsserver", cmdLineOptions);
218task("tsserver", series(preBuild, buildServer));
219task("tsserver").description = "Builds the language server";
220task("tsserver").flags = {
221    "   --built": "Compile using the built version of the compiler."
222};
223
224const cleanServer = () => cleanProject("src/tsserver");
225cleanTasks.push(cleanServer);
226task("clean-tsserver", cleanServer);
227task("clean-tsserver").description = "Cleans outputs for the language server";
228
229const watchServer = () => watchProject("src/tsserver", cmdLineOptions);
230task("watch-tsserver", series(preBuild, parallel(watchLib, watchDiagnostics, watchServer)));
231task("watch-tsserver").description = "Watch for changes and rebuild the language server only";
232task("watch-tsserver").flags = {
233    "   --built": "Compile using the built version of the compiler."
234};
235
236task("min", series(preBuild, parallel(buildTsc, buildServer)));
237task("min").description = "Builds only tsc and tsserver";
238task("min").flags = {
239    "   --built": "Compile using the built version of the compiler."
240};
241
242task("clean-min", series(cleanTsc, cleanServer));
243task("clean-min").description = "Cleans outputs for tsc and tsserver";
244
245task("watch-min", series(preBuild, parallel(watchLib, watchDiagnostics, watchTsc, watchServer)));
246task("watch-min").description = "Watches for changes to a tsc and tsserver only";
247task("watch-min").flags = {
248    "   --built": "Compile using the built version of the compiler."
249};
250
251const buildLssl = (() => {
252    // build tsserverlibrary.out.js
253    const buildServerLibraryOut = () => buildProject("src/tsserverlibrary/tsconfig.json", cmdLineOptions);
254
255    // create tsserverlibrary.js
256    const createServerLibraryJs = () => src("built/local/tsserverlibrary.out.js")
257        .pipe(newer("built/local/tsserverlibrary.js"))
258        .pipe(sourcemaps.init({ loadMaps: true }))
259        .pipe(prependFile(copyright))
260        .pipe(rename("tsserverlibrary.js"))
261        .pipe(sourcemaps.write(".", { includeContent: false, destPath: "built/local" }))
262        .pipe(dest("built/local"));
263
264    // create tsserverlibrary.d.ts
265    const createServerLibraryDts = () => src("built/local/tsserverlibrary.out.d.ts")
266        .pipe(newer("built/local/tsserverlibrary.d.ts"))
267        .pipe(prependFile(copyright))
268        .pipe(transform(content => content.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4")))
269        .pipe(append("\nexport = ts;\nexport as namespace ts;"))
270        .pipe(rename("tsserverlibrary.d.ts"))
271        .pipe(dest("built/local"));
272
273    return series(
274        buildServerLibraryOut,
275        createServerLibraryJs,
276        createServerLibraryDts,
277    );
278})();
279task("lssl", series(preBuild, buildLssl));
280task("lssl").description = "Builds language service server library";
281task("lssl").flags = {
282    "   --built": "Compile using the built version of the compiler."
283};
284
285const cleanLssl = async () => {
286    if (fs.existsSync("built/local/tsserverlibrary.tsconfig.json")) {
287        await cleanProject("built/local/tsserverlibrary.tsconfig.json");
288    }
289    await del([
290        "built/local/tsserverlibrary.out.js",
291        "built/local/tsserverlibrary.out.d.ts",
292        "built/local/tsserverlibrary.out.tsbuildinfo",
293        "built/local/tsserverlibrary.js",
294        "built/local/tsserverlibrary.d.ts",
295    ]);
296};
297cleanTasks.push(cleanLssl);
298task("clean-lssl", cleanLssl);
299task("clean-lssl").description = "Clean outputs for the language service server library";
300
301const watchLssl = () => watch([
302    "src/compiler/tsconfig.json",
303    "src/compiler/**/*.ts",
304    "src/jsTyping/tsconfig.json",
305    "src/jsTyping/**/*.ts",
306    "src/services/tsconfig.json",
307    "src/services/**/*.ts",
308    "src/server/tsconfig.json",
309    "src/server/**/*.ts",
310    "src/webServer/tsconfig.json",
311    "src/webServer/**/*.ts",
312    "src/tsserver/tsconfig.json",
313    "src/tsserver/**/*.ts",
314], buildLssl);
315task("watch-lssl", series(preBuild, parallel(watchLib, watchDiagnostics, watchLssl)));
316task("watch-lssl").description = "Watch for changes and rebuild tsserverlibrary only";
317task("watch-lssl").flags = {
318    "   --built": "Compile using the built version of the compiler."
319};
320
321const ohTestCasesGeneration = async () => {
322    if (!fs.existsSync(path.join(__dirname, "tests/cases/fourslash/oh/")) || !fs.existsSync(path.join(__dirname, "tests/cases/compiler-oh/"))) {
323        await exec(process.execPath, ["scripts/ohTestCasesGenerationScript.js", diagnosticMessagesJson]);
324    }
325}
326
327const buildTests = () => buildProject("src/testRunner");
328task("tests", series(preBuild, ohTestCasesGeneration, parallel(buildLssl, buildTests)));
329task("tests").description = "Builds the test infrastructure";
330task("tests").flags = {
331    "   --built": "Compile using the built version of the compiler."
332};
333
334const cleanTests = () => cleanProject("src/testRunner");
335cleanTasks.push(cleanTests);
336task("clean-tests", cleanTests);
337task("clean-tests").description = "Cleans the outputs for the test infrastructure";
338
339const watchTests = () => watchProject("src/testRunner", cmdLineOptions);
340
341const buildEslintRules = () => buildProject("scripts/eslint");
342task("build-eslint-rules", buildEslintRules);
343task("build-eslint-rules").description = "Compiles eslint rules to js";
344
345const cleanEslintRules = () => cleanProject("scripts/eslint");
346cleanTasks.push(cleanEslintRules);
347task("clean-eslint-rules", cleanEslintRules);
348task("clean-eslint-rules").description = "Cleans the outputs for the eslint rules";
349
350const runEslintRulesTests = () => runConsoleTests("scripts/eslint/built/tests", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false);
351task("run-eslint-rules-tests", series(buildEslintRules, runEslintRulesTests));
352task("run-eslint-rules-tests").description = "Runs the eslint rule tests";
353
354/** @type { (folder: string) => { (): Promise<any>; displayName?: string } } */
355const eslint = (folder) => async () => {
356
357    const formatter = cmdLineOptions.ci ? "stylish" : "autolinkable-stylish";
358    const args = [
359        "node_modules/eslint/bin/eslint",
360        "--cache",
361        "--cache-location", `${folder}/.eslintcache`,
362        "--format", formatter,
363        "--rulesdir", "scripts/eslint/built/rules",
364        "--ext", ".ts",
365    ];
366
367    if (cmdLineOptions.fix) {
368        args.push("--fix");
369    }
370
371    args.push(folder);
372
373    log(`Linting: ${args.join(" ")}`);
374    return exec(process.execPath, args);
375};
376
377const lintScripts = eslint("scripts");
378lintScripts.displayName = "lint-scripts";
379task("lint-scripts", series([buildEslintRules, lintScripts]));
380task("lint-scripts").description = "Runs eslint on the scripts sources.";
381
382const lintCompiler = eslint("src");
383lintCompiler.displayName = "lint-compiler";
384task("lint-compiler", series([buildEslintRules, lintCompiler]));
385task("lint-compiler").description = "Runs eslint on the compiler sources.";
386task("lint-compiler").flags = {
387    "   --ci": "Runs eslint additional rules",
388};
389
390const lint = series([buildEslintRules, lintScripts, lintCompiler]);
391lint.displayName = "lint";
392task("lint", series([buildEslintRules, lint]));
393task("lint").description = "Runs eslint on the compiler and scripts sources.";
394task("lint").flags = {
395    "   --ci": "Runs eslint additional rules",
396};
397
398const buildCancellationToken = () => buildProject("src/cancellationToken");
399const cleanCancellationToken = () => cleanProject("src/cancellationToken");
400cleanTasks.push(cleanCancellationToken);
401
402const buildTypingsInstaller = () => buildProject("src/typingsInstaller");
403const cleanTypingsInstaller = () => cleanProject("src/typingsInstaller");
404cleanTasks.push(cleanTypingsInstaller);
405
406const buildWatchGuard = () => buildProject("src/watchGuard");
407const cleanWatchGuard = () => cleanProject("src/watchGuard");
408cleanTasks.push(cleanWatchGuard);
409
410const generateTypesMap = () => src("src/server/typesMap.json")
411    .pipe(newer("built/local/typesMap.json"))
412    .pipe(transform(contents => (JSON.parse(contents), contents))) // validates typesMap.json is valid JSON
413    .pipe(dest("built/local"));
414task("generate-types-map", generateTypesMap);
415
416const cleanTypesMap = () => del("built/local/typesMap.json");
417cleanTasks.push(cleanTypesMap);
418
419// Drop a copy of diagnosticMessages.generated.json into the built/local folder. This allows
420// it to be synced to the Azure DevOps repo, so that it can get picked up by the build
421// pipeline that generates the localization artifacts that are then fed into the translation process.
422const builtLocalDiagnosticMessagesGeneratedJson = "built/local/diagnosticMessages.generated.json";
423const copyBuiltLocalDiagnosticMessages = () => src(diagnosticMessagesGeneratedJson)
424    .pipe(newer(builtLocalDiagnosticMessagesGeneratedJson))
425    .pipe(dest("built/local"));
426
427const cleanBuiltLocalDiagnosticMessages = () => del(builtLocalDiagnosticMessagesGeneratedJson);
428cleanTasks.push(cleanBuiltLocalDiagnosticMessages);
429
430const buildOtherOutputs = parallel(buildCancellationToken, buildTypingsInstaller, buildWatchGuard, generateTypesMap, copyBuiltLocalDiagnosticMessages);
431task("other-outputs", series(preBuild, buildOtherOutputs));
432task("other-outputs").description = "Builds miscelaneous scripts and documents distributed with the LKG";
433
434task("local", series(preBuild, parallel(localize, buildTsc, buildServer, buildServices, buildLssl, buildOtherOutputs)));
435task("local").description = "Builds the full compiler and services";
436task("local").flags = {
437    "   --built": "Compile using the built version of the compiler."
438};
439
440task("watch-local", series(preBuild, parallel(watchLib, watchDiagnostics, watchTsc, watchServices, watchServer, watchLssl)));
441task("watch-local").description = "Watches for changes to projects in src/ (but does not execute tests).";
442task("watch-local").flags = {
443    "   --built": "Compile using the built version of the compiler."
444};
445
446const generateCodeCoverage = () => exec("istanbul", ["cover", "node_modules/mocha/bin/_mocha", "--", "-R", "min", "-t", "" + cmdLineOptions.testTimeout, "built/local/run.js"]);
447task("generate-code-coverage", series(preBuild, buildTests, generateCodeCoverage));
448task("generate-code-coverage").description = "Generates code coverage data via istanbul";
449
450const preTest = parallel(buildTsc, buildTests, buildServices, buildLssl);
451preTest.displayName = "preTest";
452
453const postTest = (done) => cmdLineOptions.lint ? lint(done) : done();
454
455const runTests = () => runConsoleTests("built/local/run.js", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false);
456task("runtests", series(preBuild, ohTestCasesGeneration, preTest, runTests, postTest));
457task("runtests").description = "Runs the tests using the built run.js file.";
458task("runtests").flags = {
459    "-t --tests=<regex>": "Pattern for tests to run.",
460    "   --failed": "Runs tests listed in '.failed-tests'.",
461    "-r --reporter=<reporter>": "The mocha reporter to use.",
462    "-d --debug": "Runs tests in debug mode (NodeJS 6 and earlier)",
463    "-i --inspect": "Runs tests in inspector mode (NodeJS 8 and later)",
464    "   --keepFailed": "Keep tests in .failed-tests even if they pass",
465    "   --light": "Run tests in light mode (fewer verifications, but tests run faster)",
466    "   --dirty": "Run tests without first cleaning test output directories",
467    "   --stackTraceLimit=<limit>": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
468    "   --no-color": "Disables color",
469    "   --no-lint": "Disables lint",
470    "   --timeout=<ms>": "Overrides the default test timeout.",
471    "   --built": "Compile using the built version of the compiler.",
472    "   --shards": "Total number of shards running tests (default: 1)",
473    "   --shardId": "1-based ID of this shard (default: 1)",
474};
475
476const runTestsParallel = () => runConsoleTests("built/local/run.js", "min", /*runInParallel*/ cmdLineOptions.workers > 1, /*watchMode*/ false);
477task("runtests-parallel", series(preBuild, ohTestCasesGeneration, preTest, runTestsParallel, postTest));
478task("runtests-parallel").description = "Runs all the tests in parallel using the built run.js file.";
479task("runtests-parallel").flags = {
480    "   --no-lint": "disables lint.",
481    "   --light": "Run tests in light mode (fewer verifications, but tests run faster).",
482    "   --keepFailed": "Keep tests in .failed-tests even if they pass.",
483    "   --dirty": "Run tests without first cleaning test output directories.",
484    "   --stackTraceLimit=<limit>": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
485    "   --workers=<number>": "The number of parallel workers to use.",
486    "   --timeout=<ms>": "Overrides the default test timeout.",
487    "   --built": "Compile using the built version of the compiler.",
488    "   --shards": "Total number of shards running tests (default: 1)",
489    "   --shardId": "1-based ID of this shard (default: 1)",
490};
491
492
493task("test-browser-integration", () => exec(process.execPath, ["scripts/browserIntegrationTest.js"]));
494task("test-browser-integration").description = "Runs scripts/browserIntegrationTest.ts which tests that typescript.js loads in a browser";
495
496
497task("diff", () => exec(getDiffTool(), [refBaseline, localBaseline], { ignoreExitCode: true, waitForExit: false }));
498task("diff").description = "Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment variable";
499
500task("diff-rwc", () => exec(getDiffTool(), [refRwcBaseline, localRwcBaseline], { ignoreExitCode: true, waitForExit: false }));
501task("diff-rwc").description = "Diffs the RWC baselines using the diff tool specified by the 'DIFF' environment variable";
502
503/**
504 * @param {string} localBaseline Path to the local copy of the baselines
505 * @param {string} refBaseline Path to the reference copy of the baselines
506 */
507const baselineAccept = (localBaseline, refBaseline) => merge2(
508    src([`${localBaseline}/**`, `!${localBaseline}/**/*.delete`], { base: localBaseline })
509        .pipe(dest(refBaseline)),
510    src([`${localBaseline}/**/*.delete`], { base: localBaseline, read: false })
511        .pipe(rm())
512        .pipe(rename({ extname: "" }))
513        .pipe(rm(refBaseline)));
514task("baseline-accept", () => baselineAccept(localBaseline, refBaseline));
515task("baseline-accept").description = "Makes the most recent test results the new baseline, overwriting the old baseline";
516
517task("baseline-accept-rwc", () => baselineAccept(localRwcBaseline, refRwcBaseline));
518task("baseline-accept-rwc").description = "Makes the most recent rwc test results the new baseline, overwriting the old baseline";
519
520const buildLoggedIO = async () => {
521    mkdirp.sync("built/local/temp");
522    await exec(process.execPath, ["lib/tsc", "--types", "--target", "es5", "--lib", "es5", "--outdir", "built/local/temp", "src/harness/loggedIO.ts"]);
523    fs.renameSync("built/local/temp/harness/loggedIO.js", "built/local/loggedIO.js");
524    await del("built/local/temp");
525};
526
527const cleanLoggedIO = () => del("built/local/temp/loggedIO.js");
528cleanTasks.push(cleanLoggedIO);
529
530const buildInstrumenter = () => buildProject("src/instrumenter");
531const cleanInstrumenter = () => cleanProject("src/instrumenter");
532cleanTasks.push(cleanInstrumenter);
533
534const tscInstrumented = () => exec(process.execPath, ["built/local/instrumenter.js", "record", cmdLineOptions.tests || "iocapture", "built/local"]);
535task("tsc-instrumented", series(lkgPreBuild, parallel(localize, buildTsc, buildServer, buildServices, buildLssl, buildLoggedIO, buildInstrumenter), tscInstrumented));
536task("tsc-instrumented").description = "Builds an instrumented tsc.js";
537task("tsc-instrumented").flags = {
538    "-t --tests=<testname>": "The test to run."
539};
540
541// TODO(rbuckton): Determine if we still need this task. Depending on a relative
542//                 path here seems like a bad idea.
543const updateSublime = () => src(["built/local/tsserver.js", "built/local/tsserver.js.map"])
544    .pipe(dest("../TypeScript-Sublime-Plugin/tsserver/"));
545task("update-sublime", updateSublime);
546task("update-sublime").description = "Updates the sublime plugin's tsserver";
547
548const buildImportDefinitelyTypedTests = () => buildProject("scripts/importDefinitelyTypedTests");
549const cleanImportDefinitelyTypedTests = () => cleanProject("scripts/importDefinitelyTypedTests");
550cleanTasks.push(cleanImportDefinitelyTypedTests);
551
552// TODO(rbuckton): Should the path to DefinitelyTyped be configurable via an environment variable?
553const importDefinitelyTypedTests = () => exec(process.execPath, ["scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js", "./", "../DefinitelyTyped"]);
554task("importDefinitelyTypedTests", series(buildImportDefinitelyTypedTests, importDefinitelyTypedTests));
555task("importDefinitelyTypedTests").description = "Runs the importDefinitelyTypedTests script to copy DT's tests to the TS-internal RWC tests";
556
557const buildReleaseTsc = () => buildProject("src/tsc/tsconfig.release.json");
558const cleanReleaseTsc = () => cleanProject("src/tsc/tsconfig.release.json");
559cleanTasks.push(cleanReleaseTsc);
560
561const cleanBuilt = () => del("built");
562
563const produceLKG = async () => {
564    const expectedFiles = [
565        "built/local/tsc.release.js",
566        "built/local/typescriptServices.js",
567        "built/local/typescriptServices.d.ts",
568        "built/local/tsserver.js",
569        "built/local/typescript.js",
570        "built/local/typescript.d.ts",
571        "built/local/tsserverlibrary.js",
572        "built/local/tsserverlibrary.d.ts",
573        "built/local/typingsInstaller.js",
574        "built/local/cancellationToken.js"
575    ].concat(libs.map(lib => lib.target));
576    const missingFiles = expectedFiles
577        .concat(localizationTargets)
578        .filter(f => !fs.existsSync(f));
579    if (missingFiles.length > 0) {
580        throw new Error("Cannot replace the LKG unless all built targets are present in directory 'built/local/'. The following files are missing:\n" + missingFiles.join("\n"));
581    }
582    const sizeBefore = getDirSize("lib");
583    await exec(process.execPath, ["scripts/produceLKG.js"]);
584    const sizeAfter = getDirSize("lib");
585    if (sizeAfter > (sizeBefore * 1.10)) {
586        throw new Error("The lib folder increased by 10% or more. This likely indicates a bug.");
587    }
588};
589
590task("LKG", series(lkgPreBuild, parallel(localize, buildTsc, buildServer, buildServices, buildLssl, buildOtherOutputs, buildReleaseTsc), produceLKG));
591task("LKG").description = "Makes a new LKG out of the built js files";
592task("LKG").flags = {
593    "   --built": "Compile using the built version of the compiler.",
594};
595task("lkg", series("LKG"));
596
597const generateSpec = () => exec("cscript", ["//nologo", "scripts/word2md.js", path.resolve("doc/TypeScript Language Specification - ARCHIVED.docx"), path.resolve("doc/spec-ARCHIVED.md")]);
598task("generate-spec", series(buildScripts, generateSpec));
599task("generate-spec").description = "Generates a Markdown version of the Language Specification";
600
601task("clean", series(parallel(cleanTasks), cleanBuilt));
602task("clean").description = "Cleans build outputs";
603
604const configureNightly = () => exec(process.execPath, ["scripts/configurePrerelease.js", "dev", "package.json", "src/compiler/corePublic.ts"]);
605task("configure-nightly", series(buildScripts, configureNightly));
606task("configure-nightly").description = "Runs scripts/configurePrerelease.ts to prepare a build for nightly publishing";
607
608const configureInsiders = () => exec(process.execPath, ["scripts/configurePrerelease.js", "insiders", "package.json", "src/compiler/corePublic.ts"]);
609task("configure-insiders", series(buildScripts, configureInsiders));
610task("configure-insiders").description = "Runs scripts/configurePrerelease.ts to prepare a build for insiders publishing";
611
612const configureExperimental = () => exec(process.execPath, ["scripts/configurePrerelease.js", "experimental", "package.json", "src/compiler/corePublic.ts"]);
613task("configure-experimental", series(buildScripts, configureExperimental));
614task("configure-experimental").description = "Runs scripts/configurePrerelease.ts to prepare a build for experimental publishing";
615
616const createLanguageServicesBuild = () => exec(process.execPath, ["scripts/createLanguageServicesBuild.js"]);
617task("create-language-services-build", series(buildScripts, createLanguageServicesBuild));
618task("create-language-services-build").description = "Runs scripts/createLanguageServicesBuild.ts to prepare a build which only has the require('typescript') JS.";
619
620const publishNightly = () => exec("npm", ["publish", "--tag", "next"]);
621task("publish-nightly", series(task("clean"), task("LKG"), task("clean"), task("runtests-parallel"), publishNightly));
622task("publish-nightly").description = "Runs `npm publish --tag next` to create a new nightly build on npm";
623
624// TODO(rbuckton): The problem with watching in this way is that a change in compiler/ will result
625// in cascading changes in other projects that may take differing amounts of times to complete. As
626// a result, the watch may accidentally trigger early, so we have to set a significant delay. An
627// alternative approach would be to leverage a builder API, or to have 'tsc -b' have an option to
628// write some kind of trigger file that indicates build completion that we could listen for instead.
629const watchRuntests = () => watch(["built/local/*.js", "tests/cases/**/*.ts", "tests/cases/**/tsconfig.json"], { delay: 5000 }, async () => {
630    if (cmdLineOptions.tests || cmdLineOptions.failed) {
631        await runConsoleTests("built/local/run.js", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ true);
632    }
633    else {
634        await runConsoleTests("built/local/run.js", "min", /*runInParallel*/ true, /*watchMode*/ true);
635    }
636});
637task("watch", series(preBuild, preTest, parallel(watchLib, watchDiagnostics, watchServices, watchLssl, watchTests, watchRuntests)));
638task("watch").description = "Watches for changes and rebuilds and runs tests in parallel.";
639task("watch").flags = {
640    "-t --tests=<regex>": "Pattern for tests to run. Forces tests to be run in a single worker.",
641    "   --failed": "Runs tests listed in '.failed-tests'. Forces tests to be run in a single worker.",
642    "-r --reporter=<reporter>": "The mocha reporter to use.",
643    "   --keepFailed": "Keep tests in .failed-tests even if they pass",
644    "   --light": "Run tests in light mode (fewer verifications, but tests run faster)",
645    "   --dirty": "Run tests without first cleaning test output directories",
646    "   --stackTraceLimit=<limit>": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
647    "   --no-color": "Disables color",
648    "   --no-lint": "Disables lint",
649    "   --timeout=<ms>": "Overrides the default test timeout.",
650    "   --workers=<number>": "The number of parallel workers to use.",
651    "   --built": "Compile using the built version of the compiler.",
652};
653
654task("default", series("local"));
655task("default").description = "Runs 'local'";
656
657task("help", () => exec("gulp", ["--tasks", "--depth", "1", "--sort-tasks"], { hidePrompt: true }));
658task("help").description = "Prints the top-level tasks.";
659