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