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