diff --git a/Jakefile.js b/Jakefile.js index 93f6dc528d5..a6483355c73 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -2,253 +2,262 @@ // @ts-check /// -const fs = require("fs"); -const os = require("os"); -const path = require("path"); -const child_process = require("child_process"); -const removeInternal = require("remove-internal"); -const fold = require("travis-fold"); -const ts = require("./lib/typescript"); -const del = require("del"); +var fs = require("fs"); +var os = require("os"); +var path = require("path"); +var child_process = require("child_process"); +var fold = require("travis-fold"); +var ts = require("./lib/typescript"); const getDirSize = require("./scripts/build/getDirSize"); -const host = process.env.TYPESCRIPT_HOST || process.env.host || "node"; +// Variables +var compilerDirectory = "src/compiler/"; +var serverDirectory = "src/server/"; +var harnessDirectory = "src/harness/"; +var libraryDirectory = "src/lib/"; +var scriptsDirectory = "scripts/"; +var unittestsDirectory = "src/harness/unittests/"; +var docDirectory = "doc/"; +var lclDirectory = "src/loc/lcl"; -const locales = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-br", "ru", "tr", "zh-cn", "zh-tw"]; +var builtDirectory = "built/"; +var builtLocalDirectory = "built/local/"; +var LKGDirectory = "lib/"; -const defaultTestTimeout = 40000; +var copyright = "CopyrightNotice.txt"; +var thirdParty = "ThirdPartyNoticeText.txt"; -let useDebugMode = true; +var defaultTestTimeout = 40000; -const TaskNames = { - local: "local", - runtests: "runtests", - runtestsParallel: "runtests-parallel", - buildRules: "build-rules", - clean: "clean", - lib: "lib", - buildFoldStart: "build-fold-start", - buildFoldEnd: "build-fold-end", - generateDiagnostics: "generate-diagnostics", - coreBuild: "core-build", - lkg: "LKG", - release: "release", - lssl: "lssl", - lint: "lint" -}; +// add node_modules to path so we don't need global modules, prefer the modules by adding them first +var nodeModulesPathPrefix = path.resolve("./node_modules/.bin/") + path.delimiter; +if (process.env.path !== undefined) { + process.env.path = nodeModulesPathPrefix + process.env.path; +} +else if (process.env.PATH !== undefined) { + process.env.PATH = nodeModulesPathPrefix + process.env.PATH; +} -const Paths = { - lkg: "lib", - lkgCompiler: "lib/tsc.js", +/** + * @param diagnostics {ts.Diagnostic[]} + * @param [pretty] {boolean} + */ +function diagnosticsToString(diagnostics, pretty) { + const host = { + getCurrentDirectory() { return process.cwd(); }, + getCanonicalFileName(fileName) { return fileName; }, + getNewLine() { return os.EOL; } + }; + return pretty ? ts.formatDiagnosticsWithColorAndContext(diagnostics, host) : + ts.formatDiagnostics(diagnostics, host); +} - built: "built", - builtLocal: "built/local", - builtLocalCompiler: "built/local/tsc.js", - builtLocalRun: "built/local/run.js", - locLcg: "built/local/enu/diagnosticMessages.generated.json.lcg", - typesMapOutput: "built/local/typesMap.json", - servicesFile: "built/local/typescriptServices.js", - servicesDefinitionFile: "built/local/typescriptServices.d.ts", - tsserverLibraryFile: "built/local/tsserverlibrary.js", - tsserverLibraryDefinitionFile: "built/local/tsserverlibrary.d.ts", +/** @param diagnostics {ts.Diagnostic[]} */ +function reportDiagnostics(diagnostics) { + console.log(diagnosticsToString(diagnostics, process.stdout.isTTY)); +} - baselines: { - local: "tests/baselines/local", - localTest262: "tests/baselines/test262/local", - localRwc: "tests/baselines/rwc/local", - reference: "tests/baselines/reference", - referenceTest262: "tests/baselines/test262/reference", - referenceRwc: "tests/baselines/rwc/reference" - }, - copyright: "CopyrightNotice.txt", - thirdParty: "ThirdPartyNoticeText.txt", - library: "src/lib", +/** @param jsonPath {string} */ +function readJson(jsonPath) { + const jsonText = fs.readFileSync(jsonPath, "utf8"); + const result = ts.parseConfigFileTextToJson(jsonPath, jsonText); + if (result.error) { + reportDiagnostics([result.error]); + throw new Error("An error occurred during parse."); + } + return result.config; +} - processDiagnosticMessagesJs: "scripts/processDiagnosticMessages.js", - diagnosticInformationMap: "src/parser/diagnosticInformationMap.generated.ts", - diagnosticMessagesJson: "src/parser/diagnosticMessages.json", +/** @param configPath {string} */ +function filesFromConfig(configPath) { + const config = readJson(configPath); + const configFileContent = ts.parseJsonConfigFileContent(config, ts.sys, path.dirname(configPath)); + if (configFileContent.errors && configFileContent.errors.length) { + reportDiagnostics(configFileContent.errors); + throw new Error("An error occurred during parse."); + } + return configFileContent.fileNames; +} - srcServer: "src/server", +function toNs(diff) { + return diff[0] * 1e9 + diff[1]; +} -}; +function mark() { + if (!fold.isTravis()) return; + var stamp = process.hrtime(); + var id = Math.floor(Math.random() * 0xFFFFFFFF).toString(16); + console.log("travis_time:start:" + id + "\r"); + return { + stamp: stamp, + id: id + }; +} -const ConfigFileFor = { - tsc: "src/tsc", - tsserver: "src/tsserver", - runjs: "src/testRunner", - lint: "scripts/tslint", - all: "src" -}; +function measure(marker) { + if (!fold.isTravis()) return; + var diff = process.hrtime(marker.stamp); + var total = [marker.stamp[0] + diff[0], marker.stamp[1] + diff[1]]; + console.log("travis_time:end:" + marker.id + ":start=" + toNs(marker.stamp) + ",finish=" + toNs(total) + ",duration=" + toNs(diff) + "\r"); +} -const ExpectedLKGFiles = [ - "tsc.js", - "tsserver.js", - "typescriptServices.js", - "typescriptServices.d.ts", - "typescript.js", - "typescript.d.ts", - "tsserverlibrary.js", - "tsserverlibrary.d.js", - "cancellationToken.js", - "typingsInstaller.js", - "protocol.d.ts", - "watchGuard.js" -]; +function removeConstModifierFromEnumDeclarations(text) { + return text.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, '$1$2enum $3 {$4'); +} + +var compilerSources = filesFromConfig("./src/compiler/tsconfig.json"); +var servicesSources = filesFromConfig("./src/services/tsconfig.json"); +var cancellationTokenSources = filesFromConfig(path.join(serverDirectory, "cancellationToken/tsconfig.json")); +var typingsInstallerSources = filesFromConfig(path.join(serverDirectory, "typingsInstaller/tsconfig.json")); +var watchGuardSources = filesFromConfig(path.join(serverDirectory, "watchGuard/tsconfig.json")); +var serverSources = filesFromConfig(path.join(serverDirectory, "tsconfig.json")); +var languageServiceLibrarySources = filesFromConfig(path.join(serverDirectory, "tsconfig.library.json")); +var harnessSources = filesFromConfig("./src/harness/tsconfig.json"); + +var typesMapOutputPath = path.join(builtLocalDirectory, 'typesMap.json'); /** @type {{ libs: string[], paths?: Record, sources?: Record }} */ -const libraries = readJson("./src/lib/libs.json"); +var libraries = readJson("./src/lib/libs.json"); -directory(Paths.builtLocal); +/** + * .lcg file is what localization team uses to know what messages to localize. + * The file is always generated in 'enu\diagnosticMessages.generated.json.lcg' + */ +var generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg"); -// Local target to build the compiler and services -desc("Builds the full compiler and services"); -task(TaskNames.local, [ - TaskNames.buildFoldStart, - TaskNames.generateDiagnostics, - TaskNames.lib, - TaskNames.coreBuild, - // buildProtocolDts, - // builtGeneratedDiagnosticMessagesJSON, - TaskNames.lssl, - // "localize", - TaskNames.buildFoldEnd -]); +/** + * The localization target produces the two following transformations: + * 1. 'src\loc\lcl\\diagnosticMessages.generated.json.lcl' => 'built\local\\diagnosticMessages.generated.json' + * convert localized resources into a .json file the compiler can understand + * 2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg' + * generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json + */ +var localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-br", "ru", "tr", "zh-cn", "zh-tw"].map(function (f) { + return path.join(builtLocalDirectory, f); +}).concat(path.dirname(generatedLCGFile)); -desc("Runs all the tests in parallel using the built run.js file. Optional arguments are: t[ests]=category1|category2|... d[ebug]=true."); -task(TaskNames.runtestsParallel, [TaskNames.lib], function () { - tsbuild([ConfigFileFor.runjs, ConfigFileFor.lint]); - runConsoleTests("min", /*parallel*/ true); -}, { async: true }); - -desc("Runs all the tests in parallel using the built run.js file. Optional arguments are: t[ests]=category1|category2|... d[ebug]=true."); -task(TaskNames.runtests, [TaskNames.lib], function () { - tsbuild([ConfigFileFor.runjs, ConfigFileFor.lint]); - runConsoleTests('mocha-fivemat-progress-reporter', /*runInParallel*/ false);; -}, { async: true }); - -const libraryTargets = getLibraryTargets(); -desc("Builds the library targets"); -task(TaskNames.lib, libraryTargets); - -desc("Builds language service server library"); -task("lssl", [Paths.tsserverLibraryFile, Paths.tsserverLibraryDefinitionFile, Paths.typesMapOutput]); - -// Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory -desc("Makes a new LKG out of the built js files"); -task(TaskNames.lkg, [ - TaskNames.clean, - TaskNames.release, - TaskNames.local, - ...libraryTargets -], () => { - const sizeBefore = getDirSize(Paths.lkg); - const localizationTargets = locales.map(f => path.join(Paths.builtLocal, f)).concat(path.dirname(Paths.locLcg)); - - const copyrightContent = fs.readFileSync(Paths.copyright, { encoding: 'utf-8' }); - - const expectedFiles = [...libraryTargets, ...ExpectedLKGFiles, ...localizationTargets]; - const missingFiles = expectedFiles.filter(f => !fs.existsSync(f)); - if (missingFiles.length > 0) { - fail(new Error("Cannot replace the LKG unless all built targets are present in directory " + Paths.builtLocal + - ". The following files are missing:\n" + missingFiles.join("\n"))); +// Prepends the contents of prefixFile to destinationFile +function prependFile(prefixFile, destinationFile) { + if (!fs.existsSync(prefixFile)) { + fail(prefixFile + " does not exist!"); } - // Copy all the targets into the LKG directory - jake.mkdirP(Paths.lkg); - expectedFiles.forEach(f => { - let content = fs.readFileSync(f, { encoding: 'utf-8' }); + if (!fs.existsSync(destinationFile)) { + fail(destinationFile + " failed to be created!"); + } + var temp = "temptemp"; + jake.cpR(prefixFile, temp, { silent: true }); + fs.appendFileSync(temp, fs.readFileSync(destinationFile)); + fs.renameSync(temp, destinationFile); +} - // If this is a .d.ts file, run remove-internal on it - if (f.endsWith(".d.ts")) { - content = removeInternal.elide(content).result; +// concatenate a list of sourceFiles to a destinationFile +function concatenateFiles(destinationFile, sourceFiles) { + var temp = "temptemp"; + // append all files in sequence + var text = ""; + for (var i = 0; i < sourceFiles.length; i++) { + if (!fs.existsSync(sourceFiles[i])) { + fail(sourceFiles[i] + " does not exist!"); + } + if (i > 0) { text += "\n\n"; } + text += fs.readFileSync(sourceFiles[i]).toString().replace(/\r?\n/g, "\n"); + } + fs.writeFileSync(temp, text); + // Move the file to the final destination + fs.renameSync(temp, destinationFile); +} + +var useDebugMode = true; +var host = process.env.TYPESCRIPT_HOST || process.env.host || "node"; +var compilerFilename = "tsc.js"; +var LKGCompiler = path.join(LKGDirectory, compilerFilename); +var builtLocalCompiler = path.join(builtLocalDirectory, compilerFilename); + +/** + * Compiles a file from a list of sources + * @param {string} outFile the target file name + * @param {string[]} sources an array of the names of the source files + * @param {string[]} prereqs prerequisite tasks to compiling the file + * @param {string[]} prefixes a list of files to prepend to the target file + * @param {boolean} useBuiltCompiler true to use the built compiler, false to use the LKG + * @param {object} [opts] property bag containing auxiliary options + * @param {boolean} [opts.noOutFile] true to compile without using --out + * @param {boolean} [opts.generateDeclarations] true to compile using --declaration + * @param {string} [opts.outDir] value for '--outDir' command line option + * @param {boolean} [opts.keepComments] false to compile using --removeComments + * @param {boolean} [opts.preserveConstEnums] true if compiler should keep const enums in code + * @param {boolean} [opts.noResolve] true if compiler should not include non-rooted files in compilation + * @param {boolean} [opts.stripInternal] true if compiler should remove declarations marked as internal + * @param {boolean} [opts.inlineSourceMap] true if compiler should inline sourceMap + * @param {string[]} [opts.types] array of types to include in compilation + * @param {string} [opts.lib] explicit libs to include. + * @param {function(): void} [callback] a function to execute after the compilation process ends + */ +function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts, callback) { + file(outFile, prereqs, function() { + var startCompileTime = mark(); + opts = opts || {}; + var compilerPath = useBuiltCompiler ? builtLocalCompiler : LKGCompiler; + var options = "--noImplicitAny --noImplicitThis --alwaysStrict --noEmitOnError"; + if (opts.types) { + options += " --types " + opts.types.join(","); + } + options += " --pretty"; + // Keep comments when specifically requested + // or when in debug mode. + if (!(opts.keepComments || useDebugMode)) { + options += " --removeComments"; } - if (f.endsWith(".d.ts") || f.endsWith(".js")) { - // Prepend the copyright header to it - content = copyrightContent + content; + if (opts.generateDeclarations) { + options += " --declaration"; } - fs.writeFile(path.join(Paths.lkg, path.basename(f)), content, { encoding: 'utf-8' }, (err) => { - if (err) throw err; - }); - }); + if (opts.preserveConstEnums || useDebugMode) { + options += " --preserveConstEnums"; + } - const sizeAfter = getDirSize(Paths.lkg); - if (sizeAfter > (sizeBefore * 1.10)) { - throw new Error("The lib folder increased by 10% or more. This likely indicates a bug."); - } -}); + if (opts.outDir) { + options += " --outDir " + opts.outDir; + } -desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex"); -task(TaskNames.lint, [TaskNames.buildRules], () => { - if (fold.isTravis()) console.log(fold.start("lint")); - function lint(project, cb) { - const fix = process.env.fix || process.env.f; - const cmd = `node node_modules/tslint/bin/tslint --project ${project} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish${fix ? " --fix" : ""}`; - console.log("Linting: " + cmd); - jake.exec([cmd], cb, /** @type {jake.ExecOptions} */({ interactive: true, windowsVerbatimArguments: true })); - } - lint("scripts/tslint/tsconfig.json", () => lint("src/tsconfig-base.json", () => { - if (fold.isTravis()) console.log(fold.end("lint")); - complete(); - })); -}); + if (!opts.noOutFile) { + options += " --out " + outFile; + } + else { + options += " --module commonjs"; + } -desc("Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment variable"); -task('diff', function () { - var cmd = `"${getDiffTool()} ${Paths.baselines.reference} ${Paths.baselines.local}`; - console.log(cmd); - exec(cmd); -}, { async: true }); + if (opts.noResolve) { + options += " --noResolve"; + } -desc("Diffs the RWC baselines using the diff tool specified by the 'DIFF' environment variable"); -task('diff-rwc', function () { - var cmd = `"${getDiffTool()} ${Paths.baselines.referenceRwc} ${Paths.baselines.localRwc}`; - console.log(cmd); - exec(cmd); -}, { async: true }); + if (useDebugMode) { + if (opts.inlineSourceMap) { + options += " --inlineSourceMap --inlineSources"; + } + else { + options += " --sourcemap"; + } + } + options += " --newLine LF"; -desc("Sets the release mode flag"); -task("release", function () { - useDebugMode = false; -}); + if (opts.stripInternal) { + options += " --stripInternal"; + } + options += " --target es5"; + if (opts.lib) { + options += " --lib " + opts.lib; + } + else { + options += " --lib es5"; + } + options += " --noUnusedLocals --noUnusedParameters --strictNullChecks"; -desc("Clears the release mode flag"); -task("setDebugMode", function () { - useDebugMode = true; -}); + var cmd = host + " " + compilerPath + " " + options + " "; + cmd = cmd + sources.join(" "); + console.log(cmd + "\n"); -desc("Emit the start of the build fold"); -task(TaskNames.buildFoldStart, [], function () { - if (fold.isTravis()) console.log(fold.start("build")); -}); - -desc("Emit the end of the build fold"); -task(TaskNames.buildFoldEnd, [], function () { - if (fold.isTravis()) console.log(fold.end("build")); -}); - -desc("Compiles tslint rules to js"); -task(TaskNames.buildRules, [], function () { - tsbuild(ConfigFileFor.lint); -}, { async: true }); - -desc("Cleans the compiler output, declare files, and tests"); -task(TaskNames.clean, function () { - jake.rmRf(Paths.built); -}); - -desc("Generates a diagnostic file in TypeScript based on an input JSON file"); -task(TaskNames.generateDiagnostics, [Paths.diagnosticInformationMap]); - -task(TaskNames.coreBuild, function () { - tsbuild(ConfigFileFor.all); -}); - -file(Paths.diagnosticMessagesJson); -file(Paths.diagnosticInformationMap, [Paths.diagnosticMessagesJson], function (complete) { - tsbuild("scripts/processDiagnosticMessages.tsconfig.json", /*lkg*/ true, function () { - const cmd = `${host} scripts/processDiagnosticMessages.js ${Paths.diagnosticMessagesJson}`; - console.log(cmd); var ex = jake.createExec([cmd]); // Add listeners for output and error ex.addListener("stdout", function (output) { @@ -258,44 +267,492 @@ file(Paths.diagnosticInformationMap, [Paths.diagnosticMessagesJson], function (c process.stderr.write(error); }); ex.addListener("cmdEnd", function () { + if (!useDebugMode && prefixes && fs.existsSync(outFile)) { + for (var i in prefixes) { + prependFile(prefixes[i], outFile); + } + } + + if (callback) { + callback(); + } + + measure(startCompileTime); complete(); }); + ex.addListener("error", function () { + fs.unlinkSync(outFile); + fail("Compilation of " + outFile + " unsuccessful"); + measure(startCompileTime); + }); ex.run(); + }, { async: true }); +} + +// Prerequisite task for built directory and library typings +directory(builtLocalDirectory); + +var libraryTargets = libraries.libs.map(function (lib) { + var relativeSources = ["header.d.ts"].concat(libraries.sources && libraries.sources[lib] || [lib + ".d.ts"]); + var relativeTarget = libraries.paths && libraries.paths[lib] || ("lib." + lib + ".d.ts"); + var sources = [copyright].concat(relativeSources.map(s => path.join(libraryDirectory, s))); + var target = path.join(builtLocalDirectory, relativeTarget); + file(target, [builtLocalDirectory].concat(sources), function () { + concatenateFiles(target, sources); }); + return target; +}); + +// Lib target to build the library files +desc("Builds the library targets"); +task("lib", libraryTargets); + + +// Generate diagnostics +var processDiagnosticMessagesJs = path.join(scriptsDirectory, "processDiagnosticMessages.js"); +var processDiagnosticMessagesTs = path.join(scriptsDirectory, "processDiagnosticMessages.ts"); +var processDiagnosticMessagesSources = filesFromConfig("./scripts/processDiagnosticMessages.tsconfig.json"); + +var diagnosticMessagesJson = path.join(compilerDirectory, "diagnosticMessages.json"); +var diagnosticInfoMapTs = path.join(compilerDirectory, "diagnosticInformationMap.generated.ts"); +var generatedDiagnosticMessagesJSON = path.join(compilerDirectory, "diagnosticMessages.generated.json"); +var builtGeneratedDiagnosticMessagesJSON = path.join(builtLocalDirectory, "diagnosticMessages.generated.json"); + +file(processDiagnosticMessagesTs); + +// processDiagnosticMessages script +compileFile(processDiagnosticMessagesJs, + processDiagnosticMessagesSources, + processDiagnosticMessagesSources, + [], + /*useBuiltCompiler*/ false); + +// Localize diagnostics script +var generateLocalizedDiagnosticMessagesJs = path.join(scriptsDirectory, "generateLocalizedDiagnosticMessages.js"); +var generateLocalizedDiagnosticMessagesTs = path.join(scriptsDirectory, "generateLocalizedDiagnosticMessages.ts"); + +file(generateLocalizedDiagnosticMessagesTs); + +compileFile(generateLocalizedDiagnosticMessagesJs, + [generateLocalizedDiagnosticMessagesTs], + [generateLocalizedDiagnosticMessagesTs], + [], + /*useBuiltCompiler*/ false, { noOutFile: true, types: ["node", "xml2js"] }); + +// Localize diagnostics +file(generatedLCGFile, [generateLocalizedDiagnosticMessagesJs, diagnosticInfoMapTs, generatedDiagnosticMessagesJSON], function () { + var cmd = host + " " + generateLocalizedDiagnosticMessagesJs + " " + lclDirectory + " " + builtLocalDirectory + " " + generatedDiagnosticMessagesJSON; + console.log(cmd); + var ex = jake.createExec([cmd]); + // Add listeners for output and error + ex.addListener("stdout", function (output) { + process.stdout.write(output); + }); + ex.addListener("stderr", function (error) { + process.stderr.write(error); + }); + ex.addListener("cmdEnd", function () { + complete(); + }); + ex.run(); }, { async: true }); -file(Paths.typesMapOutput, /** @type {*} */(function () { - var content = fs.readFileSync(path.join(Paths.srcServer, 'typesMap.json')); +task("localize", [generatedLCGFile]); + +var buildProtocolTs = path.join(scriptsDirectory, "buildProtocol.ts"); +var buildProtocolJs = path.join(scriptsDirectory, "buildProtocol.js"); +var buildProtocolDts = path.join(builtLocalDirectory, "protocol.d.ts"); +var typescriptServicesDts = path.join(builtLocalDirectory, "typescriptServices.d.ts"); + +file(buildProtocolTs); + +compileFile(buildProtocolJs, + [buildProtocolTs], + [buildProtocolTs], + [], + /*useBuiltCompiler*/ false, + { noOutFile: true, lib: "es6" }); + +file(buildProtocolDts, [buildProtocolTs, buildProtocolJs, typescriptServicesDts], function() { + + var protocolTs = path.join(serverDirectory, "protocol.ts"); + + var cmd = host + " " + buildProtocolJs + " "+ protocolTs + " " + typescriptServicesDts + " " + buildProtocolDts; + console.log(cmd); + var ex = jake.createExec([cmd]); + // Add listeners for output and error + ex.addListener("stdout", function (output) { + process.stdout.write(output); + }); + ex.addListener("stderr", function (error) { + process.stderr.write(error); + }); + ex.addListener("cmdEnd", function () { + complete(); + }); + ex.run(); +}, { async: true }); + +// The generated diagnostics map; built for the compiler and for the 'generate-diagnostics' task +file(diagnosticInfoMapTs, [processDiagnosticMessagesJs, diagnosticMessagesJson], function () { + var cmd = host + " " + processDiagnosticMessagesJs + " " + diagnosticMessagesJson; + console.log(cmd); + var ex = jake.createExec([cmd]); + // Add listeners for output and error + ex.addListener("stdout", function (output) { + process.stdout.write(output); + }); + ex.addListener("stderr", function (error) { + process.stderr.write(error); + }); + ex.addListener("cmdEnd", function () { + complete(); + }); + ex.run(); +}, { async: true }); + +file(builtGeneratedDiagnosticMessagesJSON, [generatedDiagnosticMessagesJSON], function () { + if (fs.existsSync(builtLocalDirectory)) { + jake.cpR(generatedDiagnosticMessagesJSON, builtGeneratedDiagnosticMessagesJSON); + } +}); + +desc("Generates a diagnostic file in TypeScript based on an input JSON file"); +task("generate-diagnostics", [diagnosticInfoMapTs]); + +// Publish nightly +var configurePrereleaseJs = path.join(scriptsDirectory, "configurePrerelease.js"); +var configurePrereleaseTs = path.join(scriptsDirectory, "configurePrerelease.ts"); +var packageJson = "package.json"; +var versionFile = path.join(compilerDirectory, "core.ts"); + +file(configurePrereleaseTs); + +compileFile(/*outfile*/configurePrereleaseJs, + /*sources*/[configurePrereleaseTs], + /*prereqs*/[configurePrereleaseTs], + /*prefixes*/[], + /*useBuiltCompiler*/ false, + { noOutFile: true, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false }); + +task("setDebugMode", function () { + useDebugMode = true; +}); + +task("configure-nightly", [configurePrereleaseJs], function () { + var cmd = host + " " + configurePrereleaseJs + " dev " + packageJson + " " + versionFile; + console.log(cmd); + exec(cmd); +}, { async: true }); + +desc("Configure, build, test, and publish the nightly release."); +task("publish-nightly", ["configure-nightly", "LKG", "clean", "setDebugMode", "runtests-parallel"], function () { + var cmd = "npm publish --tag next"; + console.log(cmd); + exec(cmd); +}); + +task("configure-insiders", [configurePrereleaseJs], function () { + var cmd = host + " " + configurePrereleaseJs + " insiders " + packageJson + " " + versionFile; + console.log(cmd); + exec(cmd); +}, { async: true }); + +desc("Configure, build, test, and publish the insiders release."); +task("publish-insiders", ["configure-insiders", "LKG", "clean", "setDebugMode", "runtests-parallel"], function () { + var cmd = "npm publish --tag insiders"; + console.log(cmd); + exec(cmd); +}); + +var importDefinitelyTypedTestsDirectory = path.join(scriptsDirectory, "importDefinitelyTypedTests"); +var importDefinitelyTypedTestsJs = path.join(importDefinitelyTypedTestsDirectory, "importDefinitelyTypedTests.js"); +var importDefinitelyTypedTestsTs = path.join(importDefinitelyTypedTestsDirectory, "importDefinitelyTypedTests.ts"); + +file(importDefinitelyTypedTestsTs); +file(importDefinitelyTypedTestsJs, ["tsd-scripts", importDefinitelyTypedTestsTs], function () { + var cmd = host + " " + LKGCompiler + " -p " + importDefinitelyTypedTestsDirectory; + console.log(cmd); + exec(cmd); +}, { async: true }); + +task("importDefinitelyTypedTests", [importDefinitelyTypedTestsJs], function () { + var cmd = host + " " + importDefinitelyTypedTestsJs + " ./ ../DefinitelyTyped"; + console.log(cmd); + exec(cmd); +}, { async: true }); + +// Local target to build the compiler and services +var tscFile = path.join(builtLocalDirectory, compilerFilename); +compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false); + +var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js"); +var standaloneDefinitionsFile = path.join(builtLocalDirectory, "typescriptServices.d.ts"); +var nodePackageFile = path.join(builtLocalDirectory, "typescript.js"); +var nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts"); +var nodeStandaloneDefinitionsFile = path.join(builtLocalDirectory, "typescript_standalone.d.ts"); + +compileFile(servicesFile, servicesSources, [builtLocalDirectory, copyright].concat(servicesSources), + /*prefixes*/[copyright], + /*useBuiltCompiler*/ true, + /*opts*/ { + noOutFile: false, + generateDeclarations: true, + preserveConstEnums: true, + keepComments: true, + noResolve: false, + stripInternal: true + }, + /*callback*/ function () { + jake.cpR(servicesFile, nodePackageFile, { silent: true }); + + prependFile(copyright, standaloneDefinitionsFile); + + // Stanalone/web definition file using global 'ts' namespace + jake.cpR(standaloneDefinitionsFile, nodeDefinitionsFile, { silent: true }); + var definitionFileContents = fs.readFileSync(nodeDefinitionsFile).toString(); + definitionFileContents = removeConstModifierFromEnumDeclarations(definitionFileContents); + fs.writeFileSync(standaloneDefinitionsFile, definitionFileContents); + + // Official node package definition file, pointed to by 'typings' in package.json + // Created by appending 'export = ts;' at the end of the standalone file to turn it into an external module + var nodeDefinitionsFileContents = definitionFileContents + "\nexport = ts;"; + fs.writeFileSync(nodeDefinitionsFile, nodeDefinitionsFileContents); + + // Node package definition file to be distributed without the package. Created by replacing + // 'ts' namespace with '"typescript"' as a module. + var nodeStandaloneDefinitionsFileContents = definitionFileContents.replace(/declare (namespace|module) ts/g, 'declare module "typescript"'); + fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents); + }); + +file(typescriptServicesDts, [servicesFile]); + +var cancellationTokenFile = path.join(builtLocalDirectory, "cancellationToken.js"); +compileFile(cancellationTokenFile, cancellationTokenSources, [builtLocalDirectory].concat(cancellationTokenSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], outDir: builtLocalDirectory, noOutFile: true, lib: "es6" }); + +var typingsInstallerFile = path.join(builtLocalDirectory, "typingsInstaller.js"); +compileFile(typingsInstallerFile, typingsInstallerSources, [builtLocalDirectory].concat(typingsInstallerSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], outDir: builtLocalDirectory, noOutFile: false, lib: "es6" }); + +var watchGuardFile = path.join(builtLocalDirectory, "watchGuard.js"); +compileFile(watchGuardFile, watchGuardSources, [builtLocalDirectory].concat(watchGuardSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], outDir: builtLocalDirectory, noOutFile: false, lib: "es6" }); + +var serverFile = path.join(builtLocalDirectory, "tsserver.js"); +compileFile(serverFile, serverSources, [builtLocalDirectory, copyright, cancellationTokenFile, typingsInstallerFile, watchGuardFile].concat(serverSources).concat(servicesSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], preserveConstEnums: true, lib: "es6" }); +var tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js"); +var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts"); +file(typesMapOutputPath, /** @type {*} */(function() { + var content = fs.readFileSync(path.join(serverDirectory, 'typesMap.json')); // Validate that it's valid JSON try { JSON.parse(content.toString()); } catch (e) { console.log("Parse error in typesMap.json: " + e); } - fs.writeFileSync(Paths.typesMapOutput, content); + fs.writeFileSync(typesMapOutputPath, content); })); +compileFile( + tsserverLibraryFile, + languageServiceLibrarySources, + [builtLocalDirectory, copyright, builtLocalCompiler].concat(languageServiceLibrarySources).concat(libraryTargets), + /*prefixes*/[copyright], + /*useBuiltCompiler*/ true, + { noOutFile: false, generateDeclarations: true, stripInternal: true, preserveConstEnums: true, keepComments: true }, + /*callback*/ function () { + prependFile(copyright, tsserverLibraryDefinitionFile); -file(Paths.tsserverLibraryDefinitionFile, [TaskNames.coreBuild, Paths.copyright, ...libraryTargets], function () { - const content = fs.readFileSync(Paths.tsserverLibraryDefinitionFile, { encoding: 'utf-8' }); - const newContent = - removeConstModifierFromEnumDeclarations(content) + - `\nexport = ts` + - `\nexport as namespace ts;`; + // Appending exports at the end of the server library + var tsserverLibraryDefinitionFileContents = + fs.readFileSync(tsserverLibraryDefinitionFile).toString() + + "\nexport = ts;" + + "\nexport as namespace ts;"; + tsserverLibraryDefinitionFileContents = removeConstModifierFromEnumDeclarations(tsserverLibraryDefinitionFileContents); - fs.writeFileSync(Paths.tsserverLibraryDefinitionFile, newContent, { encoding: 'utf-8' }); + fs.writeFileSync(tsserverLibraryDefinitionFile, tsserverLibraryDefinitionFileContents); + }); + +// Local target to build the language service server library +desc("Builds language service server library"); +task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile, typesMapOutputPath]); + +desc("Emit the start of the build fold"); +task("build-fold-start", [], function () { + if (fold.isTravis()) console.log(fold.start("build")); }); -function getLibraryTargets() { - return libraries.libs.map(function (lib) { - var relativeSources = ["header.d.ts"].concat(libraries.sources && libraries.sources[lib] || [lib + ".d.ts"]); - var relativeTarget = libraries.paths && libraries.paths[lib] || ("lib." + lib + ".d.ts"); - var sources = [Paths.copyright].concat(relativeSources.map(s => path.join(Paths.library, s))); - var target = path.join(Paths.builtLocal, relativeTarget); - file(target, [Paths.builtLocal].concat(sources), function () { - concatenateFiles(target, sources); - }); - return target; +desc("Emit the end of the build fold"); +task("build-fold-end", [], function () { + if (fold.isTravis()) console.log(fold.end("build")); +}); + +// Local target to build the compiler and services +desc("Builds the full compiler and services"); +task("local", ["build-fold-start", "generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, buildProtocolDts, builtGeneratedDiagnosticMessagesJSON, "lssl", "localize", "build-fold-end"]); + +// Local target to build only tsc.js +desc("Builds only the compiler"); +task("tsc", ["generate-diagnostics", "lib", tscFile]); + +// Local target to build the compiler and services +desc("Sets release mode flag"); +task("release", function () { + useDebugMode = false; +}); + +// Set the default task to "local" +task("default", ["local"]); + +// Cleans the built directory +desc("Cleans the compiler output, declare files, and tests"); +task("clean", function () { + jake.rmRf(builtDirectory); +}); + +// Generate Markdown spec +var word2mdJs = path.join(scriptsDirectory, "word2md.js"); +var word2mdTs = path.join(scriptsDirectory, "word2md.ts"); +var specWord = path.join(docDirectory, "TypeScript Language Specification.docx"); +var specMd = path.join(docDirectory, "spec.md"); + +file(word2mdTs); + +// word2md script +compileFile(word2mdJs, + [word2mdTs], + [word2mdTs], + [], + /*useBuiltCompiler*/ false, + { + lib: "scripthost,es5" }); + +// The generated spec.md; built for the 'generate-spec' task +file(specMd, [word2mdJs, specWord], function () { + var specWordFullPath = path.resolve(specWord); + var specMDFullPath = path.resolve(specMd); + var cmd = "cscript //nologo " + word2mdJs + ' "' + specWordFullPath + '" ' + '"' + specMDFullPath + '"'; + console.log(cmd); + child_process.exec(cmd, function () { + complete(); + }); +}, { async: true }); + + +desc("Generates a Markdown version of the Language Specification"); +task("generate-spec", [specMd]); + + +// Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory +desc("Makes a new LKG out of the built js files"); +task("LKG", ["clean", "release", "local"].concat(libraryTargets), () => { + const sizeBefore = getDirSize(LKGDirectory); + var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, cancellationTokenFile, typingsInstallerFile, buildProtocolDts, watchGuardFile]. + concat(libraryTargets). + concat(localizationTargets); + var missingFiles = expectedFiles.filter(f => !fs.existsSync(f)); + if (missingFiles.length > 0) { + fail(new Error("Cannot replace the LKG unless all built targets are present in directory " + builtLocalDirectory + + ". The following files are missing:\n" + missingFiles.join("\n"))); + } + // Copy all the targets into the LKG directory + jake.mkdirP(LKGDirectory); + expectedFiles.forEach(f => jake.cpR(f, LKGDirectory)); + + const sizeAfter = getDirSize(LKGDirectory); + if (sizeAfter > (sizeBefore * 1.10)) { + throw new Error("The lib folder increased by 10% or more. This likely indicates a bug."); + } +}); + +// Test directory +directory(builtLocalDirectory); + +// Task to build the tests infrastructure using the built compiler +var run = path.join(builtLocalDirectory, "run.js"); +compileFile( + /*outFile*/ run, + /*source*/ harnessSources, + /*prereqs*/[builtLocalDirectory, tscFile, tsserverLibraryFile].concat(libraryTargets).concat(servicesSources).concat(harnessSources), + /*prefixes*/[], + /*useBuiltCompiler:*/ true, + /*opts*/ { types: ["node", "mocha", "chai"], lib: "es6" }); + +var internalTests = "internal/"; + +var localBaseline = "tests/baselines/local/"; +var refBaseline = "tests/baselines/reference/"; + +var localRwcBaseline = path.join(internalTests, "baselines/rwc/local"); +var refRwcBaseline = path.join(internalTests, "baselines/rwc/reference"); + +var localTest262Baseline = path.join(internalTests, "baselines/test262/local"); +var refTest262Baseline = path.join(internalTests, "baselines/test262/reference"); + +desc("Builds the test infrastructure using the built compiler"); +task("tests", ["local", run].concat(libraryTargets)); + +function exec(cmd, completeHandler, errorHandler) { + var ex = jake.createExec([cmd], /** @type {jake.ExecOptions} */({ windowsVerbatimArguments: true, interactive: true })); + // Add listeners for output and error + ex.addListener("stdout", function (output) { + process.stdout.write(output); + }); + ex.addListener("stderr", function (error) { + process.stderr.write(error); + }); + ex.addListener("cmdEnd", function () { + if (completeHandler) { + completeHandler(); + } + complete(); + }); + ex.addListener("error", function (e, status) { + if (errorHandler) { + errorHandler(e, status); + } + else { + fail("Process exited with code " + status); + } + }); + + ex.run(); +} + +const del = require("del"); +function cleanTestDirs() { + // Clean the local baselines directory + if (fs.existsSync(localBaseline)) { + del.sync(localBaseline); + } + + // Clean the local Rwc baselines directory + if (fs.existsSync(localRwcBaseline)) { + del.sync(localRwcBaseline); + } + + jake.mkdirP(localRwcBaseline); + jake.mkdirP(localTest262Baseline); + jake.mkdirP(localBaseline); +} + +// used to pass data from jake command line directly to run.js +function writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, colors, testTimeout) { + var testConfigContents = JSON.stringify({ + runners: runners ? runners.split(",") : undefined, + test: tests ? [tests] : undefined, + light: light, + workerCount: workerCount, + taskConfigsFolder: taskConfigsFolder, + stackTraceLimit: stackTraceLimit, + noColor: !colors, + timeout: testTimeout + }); + fs.writeFileSync('test.config', testConfigContents); +} + +function deleteTemporaryProjectOutput() { + if (fs.existsSync(path.join(localBaseline, "projectOutput/"))) { + jake.rmRf(path.join(localBaseline, "projectOutput/")); + } } function runConsoleTests(defaultReporter, runInParallel) { @@ -346,7 +803,7 @@ function runConsoleTests(defaultReporter, runInParallel) { // timeout normally isn't necessary but Travis-CI has been timing out on compiler baselines occasionally // default timeout is 2sec which really should be enough, but maybe we just need a small amount longer if (!runInParallel) { - var startTime = Travis.mark(); + var startTime = mark(); var args = []; args.push("-R", reporter); if (tests) { @@ -370,7 +827,7 @@ function runConsoleTests(defaultReporter, runInParallel) { else { args.push("-t", testTimeout); } - args.push(Paths.builtLocalRun); + args.push(run); var cmd = "mocha " + args.join(" "); console.log(cmd); @@ -379,12 +836,12 @@ function runConsoleTests(defaultReporter, runInParallel) { process.env.NODE_ENV = "development"; exec(cmd, function () { process.env.NODE_ENV = savedNodeEnv; - Travis.measure(startTime); + measure(startTime); runLinter(); finish(); }, function (e, status) { process.env.NODE_ENV = savedNodeEnv; - Travis.measure(startTime); + measure(startTime); finish(status); }); @@ -392,15 +849,15 @@ function runConsoleTests(defaultReporter, runInParallel) { else { var savedNodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = "development"; - var startTime = Travis.mark(); + var startTime = mark(); exec(host + " " + run, function () { process.env.NODE_ENV = savedNodeEnv; - Travis.measure(startTime); + measure(startTime); runLinter(); finish(); }, function (e, status) { process.env.NODE_ENV = savedNodeEnv; - Travis.measure(startTime); + measure(startTime); finish(status); }); } @@ -428,178 +885,59 @@ function runConsoleTests(defaultReporter, runInParallel) { }); lint.invoke(); } +} - // used to pass data from jake command line directly to run.js - function writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, colors, testTimeout) { - var testConfigContents = JSON.stringify({ - runners: runners ? runners.split(",") : undefined, - test: tests ? [tests] : undefined, - light: light, - workerCount: workerCount, - taskConfigsFolder: taskConfigsFolder, - stackTraceLimit: stackTraceLimit, - noColor: !colors, - timeout: testTimeout - }); - fs.writeFileSync('test.config', testConfigContents); +desc("Runs all the tests in parallel using the built run.js file. Optional arguments are: t[ests]=category1|category2|... d[ebug]=true."); +task("runtests-parallel", ["build-rules", "tests", builtLocalDirectory], function () { + runConsoleTests('min', /*runInParallel*/ true); +}, { async: true }); + +desc("Runs the tests using the built run.js file. Optional arguments are: t[ests]=regex r[eporter]=[list|spec|json|] d[ebug]=true color[s]=false lint=true bail=false dirty=false."); +task("runtests", ["build-rules", "tests", builtLocalDirectory], function() { + runConsoleTests('mocha-fivemat-progress-reporter', /*runInParallel*/ false); +}, { async: true }); + +desc("Generates code coverage data via instanbul"); +task("generate-code-coverage", ["tests", builtLocalDirectory], function () { + var testTimeout = process.env.timeout || defaultTestTimeout; + var cmd = 'istanbul cover node_modules/mocha/bin/_mocha -- -R min -t ' + testTimeout + ' ' + run; + console.log(cmd); + exec(cmd); +}, { async: true }); + +// Browser tests +var nodeServerOutFile = "tests/webTestServer.js"; +var nodeServerInFile = "tests/webTestServer.ts"; +compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile], [], /*useBuiltCompiler:*/ true, { noOutFile: true, lib: "es6" }); + +desc("Runs browserify on run.js to produce a file suitable for running tests in the browser"); +task("browserify", [], function() { + // Shell out to `gulp`, since we do the work to handle sourcemaps correctly w/o inline maps there + var cmd = 'gulp browserify --silent'; + exec(cmd); +}, { async: true }); + +desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is jake runtests-browser. Additional optional parameters tests=[regex], browser=[chrome|IE]"); +task("runtests-browser", ["browserify", nodeServerOutFile], function () { + cleanTestDirs(); + host = "node"; + var browser = process.env.browser || process.env.b || (os.platform() === "win32" ? "edge" : "chrome"); + var runners = process.env.runners || process.env.runner || process.env.ru; + var tests = process.env.test || process.env.tests || process.env.t; + var light = process.env.light || false; + var testConfigFile = 'test.config'; + if (fs.existsSync(testConfigFile)) { + fs.unlinkSync(testConfigFile); + } + if (tests || runners || light) { + writeTestConfigFile(tests, runners, light); } - function deleteTemporaryProjectOutput() { - if (fs.existsSync(path.join(Paths.baselines.local, "projectOutput/"))) { - jake.rmRf(path.join(Paths.baselines.local, "projectOutput/")); - } - } -} - -function cleanTestDirs() { - // Clean the local baselines directory - if (fs.existsSync(Paths.baselines.local)) { - del.sync(Paths.baselines.local); - } - - // Clean the local Rwc baselines directory - if (fs.existsSync(Paths.baselines.localRwc)) { - del.sync(Paths.baselines.localRwc); - } - - jake.mkdirP(Paths.baselines.local); - jake.mkdirP(Paths.baselines.localRwc); - jake.mkdirP(Paths.baselines.localTest262); -} - -function tsbuild(tsconfigPath, useLkg = true, done = undefined) { - const startCompileTime = Travis.mark(); - - const compilerPath = useLkg ? Paths.lkgCompiler : Paths.builtLocalCompiler; - const cmd = `${host} ${compilerPath} -b -v ${Array.isArray(tsconfigPath) ? tsconfigPath.join(" ") : tsconfigPath}`; - - var ex = jake.createExec([cmd]); - // Add listeners for output and error - ex.addListener("stdout", function (output) { - process.stdout.write(output); - }); - ex.addListener("stderr", function (error) { - process.stderr.write(error); - }); - ex.addListener("cmdEnd", function () { - /* - if (!useDebugMode && prefixes && fs.existsSync(outFile)) { - for (var i in prefixes) { - prependFile(prefixes[i], outFile); - } - } - - if (callback) { - callback(); - } - */ - - Travis.measure(startCompileTime); - done && done(); - }); - ex.addListener("error", function () { - fail(`Compilation of ${tsconfigPath} unsuccessful`); - Travis.measure(startCompileTime); - }); - ex.run(); -} - -const Travis = { - mark() { - if (!fold.isTravis()) return; - var stamp = process.hrtime(); - var id = Math.floor(Math.random() * 0xFFFFFFFF).toString(16); - console.log("travis_time:start:" + id + "\r"); - return { - stamp: stamp, - id: id - }; - }, - measure(marker) { - if (!fold.isTravis()) return; - var diff = process.hrtime(marker.stamp); - var total = [marker.stamp[0] + diff[0], marker.stamp[1] + diff[1]]; - console.log("travis_time:end:" + marker.id + ":start=" + toNs(marker.stamp) + ",finish=" + toNs(total) + ",duration=" + toNs(diff) + "\r"); - } -}; - -function toNs(diff) { - return diff[0] * 1e9 + diff[1]; -} - -function exec(cmd, completeHandler, errorHandler) { - var ex = jake.createExec([cmd], /** @type {jake.ExecOptions} */({ windowsVerbatimArguments: true, interactive: true })); - // Add listeners for output and error - ex.addListener("stdout", function (output) { - process.stdout.write(output); - }); - ex.addListener("stderr", function (error) { - process.stderr.write(error); - }); - ex.addListener("cmdEnd", function () { - if (completeHandler) { - completeHandler(); - } - complete(); - }); - ex.addListener("error", function (e, status) { - if (errorHandler) { - errorHandler(e, status); - } - else { - fail("Process exited with code " + status); - } - }); - - ex.run(); -} - -/** @param jsonPath {string} */ -function readJson(jsonPath) { - const jsonText = fs.readFileSync(jsonPath, "utf8"); - const result = ts.parseConfigFileTextToJson(jsonPath, jsonText); - if (result.error) { - reportDiagnostics([result.error]); - throw new Error("An error occurred during parse."); - } - return result.config; -} - -/** @param diagnostics {ts.Diagnostic[]} */ -function reportDiagnostics(diagnostics) { - console.log(diagnosticsToString(diagnostics, process.stdout.isTTY)); -} - -/** - * @param diagnostics {ts.Diagnostic[]} - * @param [pretty] {boolean} - */ -function diagnosticsToString(diagnostics, pretty) { - const host = { - getCurrentDirectory() { return process.cwd(); }, - getCanonicalFileName(fileName) { return fileName; }, - getNewLine() { return os.EOL; } - }; - return pretty ? ts.formatDiagnosticsWithColorAndContext(diagnostics, host) : - ts.formatDiagnostics(diagnostics, host); -} - -// concatenate a list of sourceFiles to a destinationFile -function concatenateFiles(destinationFile, sourceFiles) { - var temp = "temptemp"; - // append all files in sequence - var text = ""; - for (var i = 0; i < sourceFiles.length; i++) { - if (!fs.existsSync(sourceFiles[i])) { - fail(sourceFiles[i] + " does not exist!"); - } - if (i > 0) { text += "\n\n"; } - text += fs.readFileSync(sourceFiles[i]).toString().replace(/\r?\n/g, "\n"); - } - fs.writeFileSync(temp, text); - // Move the file to the final destination - fs.renameSync(temp, destinationFile); -} + tests = tests ? tests : ''; + var cmd = host + " tests/webTestServer.js " + browser + " " + JSON.stringify(tests); + console.log(cmd); + exec(cmd); +}, { async: true }); function getDiffTool() { var program = process.env['DIFF']; @@ -609,6 +947,183 @@ function getDiffTool() { return program; } -function removeConstModifierFromEnumDeclarations(text) { - return text.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, '$1$2enum $3 {$4'); +// Baseline Diff +desc("Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment variable"); +task('diff', function () { + var cmd = '"' + getDiffTool() + '" ' + refBaseline + ' ' + localBaseline; + console.log(cmd); + exec(cmd); +}, { async: true }); + +desc("Diffs the RWC baselines using the diff tool specified by the 'DIFF' environment variable"); +task('diff-rwc', function () { + var cmd = '"' + getDiffTool() + '" ' + refRwcBaseline + ' ' + localRwcBaseline; + console.log(cmd); + exec(cmd); +}, { async: true }); + +desc("Builds the test sources and automation in debug mode"); +task("tests-debug", ["setDebugMode", "tests"]); + + +// Makes the test results the new baseline +desc("Makes the most recent test results the new baseline, overwriting the old baseline"); +task("baseline-accept", function () { + acceptBaseline(localBaseline, refBaseline); +}); + +function acceptBaseline(sourceFolder, targetFolder) { + console.log('Accept baselines from ' + sourceFolder + ' to ' + targetFolder); + var deleteEnding = '.delete'; + + acceptBaselineFolder(sourceFolder, targetFolder); + + function acceptBaselineFolder(sourceFolder, targetFolder) { + var files = fs.readdirSync(sourceFolder); + + for (var i in files) { + var filename = files[i]; + var fullLocalPath = path.join(sourceFolder, filename); + var stat = fs.statSync(fullLocalPath); + if (stat.isFile()) { + if (filename.substr(filename.length - deleteEnding.length) === deleteEnding) { + filename = filename.substr(0, filename.length - deleteEnding.length); + fs.unlinkSync(path.join(targetFolder, filename)); + } + else { + var target = path.join(targetFolder, filename); + if (fs.existsSync(target)) { + fs.unlinkSync(target); + } + jake.mkdirP(path.dirname(target)); + fs.renameSync(path.join(sourceFolder, filename), target); + } + } + else if (stat.isDirectory()) { + acceptBaselineFolder(fullLocalPath, path.join(targetFolder, filename)); + } + } + } } + +desc("Makes the most recent rwc test results the new baseline, overwriting the old baseline"); +task("baseline-accept-rwc", function () { + acceptBaseline(localRwcBaseline, refRwcBaseline); +}); + +desc("Makes the most recent test262 test results the new baseline, overwriting the old baseline"); +task("baseline-accept-test262", function () { + acceptBaseline(localTest262Baseline, refTest262Baseline); +}); + + +// Webhost +var webhostPath = "tests/webhost/webtsc.ts"; +var webhostJsPath = "tests/webhost/webtsc.js"; +compileFile(webhostJsPath, [webhostPath], [tscFile, webhostPath].concat(libraryTargets), [], /*useBuiltCompiler*/true); + +desc("Builds the tsc web host"); +task("webhost", [webhostJsPath], function () { + jake.cpR(path.join(builtLocalDirectory, "lib.d.ts"), "tests/webhost/", { silent: true }); +}); + +// Perf compiler +var perftscPath = "tests/perftsc.ts"; +var perftscJsPath = "built/local/perftsc.js"; +compileFile(perftscJsPath, [perftscPath], [tscFile, perftscPath, "tests/perfsys.ts"].concat(libraryTargets), [], /*useBuiltCompiler*/ true); +desc("Builds augmented version of the compiler for perf tests"); +task("perftsc", [perftscJsPath]); + +// Instrumented compiler +var loggedIOpath = harnessDirectory + 'loggedIO.ts'; +var loggedIOJsPath = builtLocalDirectory + 'loggedIO.js'; +file(loggedIOJsPath, [builtLocalDirectory, loggedIOpath], function () { + var temp = builtLocalDirectory + 'temp'; + jake.mkdirP(temp); + var options = "--target es5 --lib es6 --types --outdir " + temp + ' ' + loggedIOpath; + var cmd = host + " " + LKGDirectory + compilerFilename + " " + options + " "; + console.log(cmd + "\n"); + var ex = jake.createExec([cmd]); + ex.addListener("cmdEnd", function () { + fs.renameSync(temp + '/harness/loggedIO.js', loggedIOJsPath); + jake.rmRf(temp); + complete(); + }); + ex.run(); +}, { async: true }); + +var instrumenterPath = harnessDirectory + 'instrumenter.ts'; +var instrumenterJsPath = builtLocalDirectory + 'instrumenter.js'; +compileFile(instrumenterJsPath, [instrumenterPath], [tscFile, instrumenterPath].concat(libraryTargets), [], /*useBuiltCompiler*/ true, { lib: "es6", types: ["node"], noOutFile: true, outDir: builtLocalDirectory }); + +desc("Builds an instrumented tsc.js - run with test=[testname]"); +task('tsc-instrumented', [loggedIOJsPath, instrumenterJsPath, tscFile], function () { + var test = process.env.test || process.env.tests || process.env.t || "iocapture"; + var cmd = host + ' ' + instrumenterJsPath + " record " + test + " " + builtLocalDirectory + compilerFilename; + console.log(cmd); + var ex = jake.createExec([cmd]); + ex.addListener("cmdEnd", function () { + complete(); + }); + ex.run(); +}, { async: true }); + +desc("Updates the sublime plugin's tsserver"); +task("update-sublime", ["local", serverFile], function () { + jake.cpR(serverFile, "../TypeScript-Sublime-Plugin/tsserver/"); + jake.cpR(serverFile + ".map", "../TypeScript-Sublime-Plugin/tsserver/"); +}); + +var tslintRuleDir = "scripts/tslint/rules"; +var tslintRules = fs.readdirSync(tslintRuleDir); +var tslintRulesFiles = tslintRules.map(function (p) { + return path.join(tslintRuleDir, p); +}); +var tslintRulesOutFiles = tslintRules.map(function (p) { + return path.join(builtLocalDirectory, "tslint/rules", p.replace(".ts", ".js")); +}); +var tslintFormattersDir = "scripts/tslint/formatters"; +var tslintFormatters = [ + "autolinkableStylishFormatter", +]; +var tslintFormatterFiles = tslintFormatters.map(function (p) { + return path.join(tslintFormattersDir, p + ".ts"); +}); +var tslintFormattersOutFiles = tslintFormatters.map(function (p) { + return path.join(builtLocalDirectory, "tslint/formatters", p + ".js"); +}); +desc("Compiles tslint rules to js"); +task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(tslintFormattersOutFiles).concat(["build-rules-end"])); +tslintRulesFiles.forEach(function (ruleFile, i) { + compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false, + { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint/rules"), lib: "es6" }); +}); +tslintFormatterFiles.forEach(function (ruleFile, i) { + compileFile(tslintFormattersOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false, + { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint/formatters"), lib: "es6" }); +}); + +desc("Emit the start of the build-rules fold"); +task("build-rules-start", [], function () { + if (fold.isTravis()) console.log(fold.start("build-rules")); +}); + +desc("Emit the end of the build-rules fold"); +task("build-rules-end", [], function () { + if (fold.isTravis()) console.log(fold.end("build-rules")); +}); + +desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex"); +task("lint", ["build-rules"], () => { + if (fold.isTravis()) console.log(fold.start("lint")); + function lint(project, cb) { + const fix = process.env.fix || process.env.f; + const cmd = `node node_modules/tslint/bin/tslint --project ${project} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish${fix ? " --fix" : ""}`; + console.log("Linting: " + cmd); + jake.exec([cmd], cb, /** @type {jake.ExecOptions} */({ interactive: true, windowsVerbatimArguments: true })); + } + lint("scripts/tslint/tsconfig.json", () => lint("src/tsconfig-base.json", () => { + if (fold.isTravis()) console.log(fold.end("lint")); + complete(); + })); +});