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