diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9cf1c9d8f3f..f3c59a61717 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,7 +7,7 @@ // ${cwd}: the current working directory of the spawned process { "version": "0.1.0", - "command": "jake", + "command": "gulp", "isShellCommand": true, "showOutput": "silent", "tasks": [ @@ -18,25 +18,6 @@ "problemMatcher": [ "$tsc" ] - }, - { - "taskName": "lint-server", - "args": [], - "problemMatcher": { - "owner": "typescript", - "fileLocation": ["relative", "${workspaceRoot}"], - "pattern": { - "regexp": "^(warning|error)\\s+([^(]+)\\s+\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(.*)$", - "severity": 1, - "file": 2, - "location": 3, - "message": 4 - }, - "watchedTaskBeginsRegExp": "^\\*\\*\\*Lint failure\\*\\*\\*$", - "watchedTaskEndsRegExp": "^\\*\\*\\* Total \\d+ failures\\.$" - }, - "showOutput": "always", - "isWatching": true } ] } \ No newline at end of file diff --git a/Gulpfile.ts b/Gulpfile.ts new file mode 100644 index 00000000000..26c32362a4d --- /dev/null +++ b/Gulpfile.ts @@ -0,0 +1,1083 @@ +import * as cp from "child_process"; +import * as path from "path"; +import * as fs from "fs"; +import * as originalGulp from "gulp"; +import * as helpMaker from "gulp-help"; +import * as runSequence from "run-sequence"; +import * as concat from "gulp-concat"; +declare global { + // This is silly. We include Q because orchestrator (a part of gulp) depends on it, but its not included. + // `del` further depends on `Promise` (and is also not included), so we just, patch the global scope's Promise to Q's + type Promise = Q.Promise; +} +import * as del from "del"; +import * as mkdirP from "mkdirP"; +import * as minimist from "minimist"; +import * as os from "os"; +import * as Linter from "tslint"; +const gulp = helpMaker(originalGulp); +import {runTestsInParallel} from "./scripts/mocha-parallel"; + +const cmdLineOptions = minimist(process.argv.slice(2), { + boolean: ["debug", "light", "colors", "lint", "soft"], + string: ["browser", "tests", "host", "reporter", ], + alias: { + d: "debug", + t: "tests", + test: "tests", + r: "reporter", + color: "colors", + f: "files", + file: "files" + }, + default: { + soft: false, + colors: process.env.colors || process.env.color || true, + debug: process.env.debug || process.env.d, + host: process.env.TYPESCRIPT_HOST || process.env.host || "node", + browser: process.env.browser || process.env.b || "IE", + tests: process.env.test || process.env.tests || process.env.t, + light: process.env.light || false, + port: process.env.port || process.env.p || "8888", + reporter: process.env.reporter || process.env.r, + lint: process.env.lint || true, + files: process.env.f || process.env.file || process.env.files || "", + } +}); + +function disableAtTypes() { + if (fs.existsSync("node_modules/@types")) { + fs.renameSync("node_modules/@types", "node_modules/@types_off"); + } +} + +function enableAtTypes() { + if (fs.existsSync("node_modules/@types_off")) { + fs.renameSync("node_modules/@types_off", "node_modules/@types"); + } +} + +disableAtTypes(); +process.on("exit", enableAtTypes); +process.on("uncaughtException", enableAtTypes); + +function exec(cmd: string, args: string[], complete: () => void = (() => {}), error: (e: any, status: number) => void = (() => {})) { + console.log(`${cmd} ${args.join(" ")}`); + const ex = cp.spawn(cmd, args); + ex.stdout.pipe(process.stdout); + ex.stderr.pipe(process.stderr); + ex.on("exit", (code) => code === 0 ? complete() : error(/*e*/ undefined, code)); + ex.on("error", error); +} + + +let useDebugMode = true; +let host = cmdLineOptions["host"]; + +// Constants +const compilerDirectory = "src/compiler/"; +const servicesDirectory = "src/services/"; +const serverDirectory = "src/server/"; +const harnessDirectory = "src/harness/"; +const libraryDirectory = "src/lib/"; +const scriptsDirectory = "scripts/"; +const unittestsDirectory = "tests/cases/unittests/"; +const docDirectory = "doc/"; + +const builtDirectory = "built/"; +const builtLocalDirectory = "built/local/"; +const LKGDirectory = "lib/"; + +const copyright = "CopyrightNotice.txt"; + +const compilerFilename = "tsc.js"; +const LKGCompiler = path.join(LKGDirectory, compilerFilename); +const builtLocalCompiler = path.join(builtLocalDirectory, compilerFilename); + +// add node_modules to path so we don"t need global modules, prefer the modules by adding them first +const nodeModulesPathPrefix = path.resolve("./node_modules/.bin/"); +const isWin = /^win/.test(process.platform); +const mocha = path.join(nodeModulesPathPrefix, "mocha") + (isWin ? ".cmd" : ""); +const browserify = path.join(nodeModulesPathPrefix, "browserify") + (isWin ? ".cmd" : ""); + +const compilerSources = [ + "core.ts", + "sys.ts", + "types.ts", + "scanner.ts", + "parser.ts", + "utilities.ts", + "binder.ts", + "checker.ts", + "sourcemap.ts", + "declarationEmitter.ts", + "emitter.ts", + "program.ts", + "commandLineParser.ts", + "tsc.ts", + "diagnosticInformationMap.generated.ts" +].map(function (f) { + return path.join(compilerDirectory, f); +}); + +const servicesSources = [ + "core.ts", + "sys.ts", + "types.ts", + "scanner.ts", + "parser.ts", + "utilities.ts", + "binder.ts", + "checker.ts", + "sourcemap.ts", + "declarationEmitter.ts", + "emitter.ts", + "program.ts", + "commandLineParser.ts", + "diagnosticInformationMap.generated.ts" +].map(function (f) { + return path.join(compilerDirectory, f); +}).concat([ + "breakpoints.ts", + "navigateTo.ts", + "navigationBar.ts", + "outliningElementsCollector.ts", + "patternMatcher.ts", + "services.ts", + "shims.ts", + "signatureHelp.ts", + "utilities.ts", + "formatting/formatting.ts", + "formatting/formattingContext.ts", + "formatting/formattingRequestKind.ts", + "formatting/formattingScanner.ts", + "formatting/references.ts", + "formatting/rule.ts", + "formatting/ruleAction.ts", + "formatting/ruleDescriptor.ts", + "formatting/ruleFlag.ts", + "formatting/ruleOperation.ts", + "formatting/ruleOperationContext.ts", + "formatting/rules.ts", + "formatting/rulesMap.ts", + "formatting/rulesProvider.ts", + "formatting/smartIndenter.ts", + "formatting/tokenRange.ts" +].map(function (f) { + return path.join(servicesDirectory, f); +})); + +const serverCoreSources = [ + "node.d.ts", + "editorServices.ts", + "protocol.d.ts", + "session.ts", + "server.ts" +].map(function (f) { + return path.join(serverDirectory, f); +}); + +const serverSources = serverCoreSources.concat(servicesSources); + +const languageServiceLibrarySources = [ + "editorServices.ts", + "protocol.d.ts", + "session.ts" +].map(function (f) { + return path.join(serverDirectory, f); +}).concat(servicesSources); + +const harnessCoreSources = [ + "harness.ts", + "sourceMapRecorder.ts", + "harnessLanguageService.ts", + "fourslash.ts", + "runnerbase.ts", + "compilerRunner.ts", + "typeWriter.ts", + "fourslashRunner.ts", + "projectsRunner.ts", + "loggedIO.ts", + "rwcRunner.ts", + "test262Runner.ts", + "runner.ts" +].map(function (f) { + return path.join(harnessDirectory, f); +}); + +const harnessSources = harnessCoreSources.concat([ + "incrementalParser.ts", + "jsDocParsing.ts", + "services/colorization.ts", + "services/documentRegistry.ts", + "services/preProcessFile.ts", + "services/patternMatcher.ts", + "session.ts", + "versionCache.ts", + "convertToBase64.ts", + "transpile.ts", + "reuseProgramStructure.ts", + "cachingInServerLSHost.ts", + "moduleResolution.ts", + "tsconfigParsing.ts", + "commandLineParsing.ts", + "convertCompilerOptionsFromJson.ts", + "convertTypingOptionsFromJson.ts", + "tsserverProjectSystem.ts" +].map(function (f) { + return path.join(unittestsDirectory, f); +})).concat([ + "protocol.d.ts", + "session.ts", + "client.ts", + "editorServices.ts" +].map(function (f) { + return path.join(serverDirectory, f); +})); + +const es2015LibrarySources = [ + "es2015.core.d.ts", + "es2015.collection.d.ts", + "es2015.generator.d.ts", + "es2015.iterable.d.ts", + "es2015.promise.d.ts", + "es2015.proxy.d.ts", + "es2015.reflect.d.ts", + "es2015.symbol.d.ts", + "es2015.symbol.wellknown.d.ts" +]; + +const es2015LibrarySourceMap = es2015LibrarySources.map(function(source) { + return { target: "lib." + source, sources: ["header.d.ts", source] }; +}); + +const es2016LibrarySource = [ "es2016.array.include.d.ts" ]; + +const es2016LibrarySourceMap = es2016LibrarySource.map(function (source) { + return { target: "lib." + source, sources: ["header.d.ts", source] }; +}); + +const es2017LibrarySource = [ + "es2017.object.d.ts", + "es2017.sharedmemory.d.ts" +]; + +const es2017LibrarySourceMap = es2017LibrarySource.map(function (source) { + return { target: "lib." + source, sources: ["header.d.ts", source] }; +}); + +const hostsLibrarySources = ["dom.generated.d.ts", "webworker.importscripts.d.ts", "scripthost.d.ts"]; + +const librarySourceMap = [ + // Host library + { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] }, + { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] }, + { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] }, + { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] }, + + // JavaScript library + { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] }, + { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] }, + { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] }, + { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] }, + + // JavaScript + all host library + { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) }, + { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") } +].concat(es2015LibrarySourceMap, es2016LibrarySourceMap, es2017LibrarySourceMap); + +const libraryTargets = librarySourceMap.map(function (f) { + return path.join(builtLocalDirectory, f.target); +}); + +for (const i in libraryTargets) { + const entry = librarySourceMap[i]; + const target = libraryTargets[i]; + const sources = [copyright].concat(entry.sources.map(function (s) { + return path.join(libraryDirectory, s); + })); + gulp.task(target, false, [], function() { + return gulp.src(sources).pipe(concat(target, {newLine: ""})).pipe(gulp.dest(".")); + }); +} + +const configureNightlyJs = path.join(scriptsDirectory, "configureNightly.js"); +const configureNightlyTs = path.join(scriptsDirectory, "configureNightly.ts"); +const packageJson = "package.json"; +const programTs = path.join(compilerDirectory, "program.ts"); + + +// Prepends the contents of prefixFile to destinationFile +function prependFile(prefixFile, destinationFile) { + if (!fs.existsSync(prefixFile)) { + throw new Error(prefixFile + " does not exist!"); + } + if (!fs.existsSync(destinationFile)) { + throw new Error(destinationFile + " failed to be created!"); + } + const temp = "temptemp"; + fs.writeFileSync(temp, fs.readFileSync(prefixFile)); + fs.appendFileSync(temp, fs.readFileSync(destinationFile)); + fs.renameSync(temp, destinationFile); +} + +function needsUpdate(source: string | string[], dest: string | string[]): boolean { + if (typeof source === "string" && typeof dest === "string") { + if (fs.existsSync(dest)) { + const {mtime: outTime} = fs.statSync(dest); + const {mtime: inTime} = fs.statSync(source); + if (+inTime <= +outTime) { + return false; + } + } + } + else if (typeof source === "string" && typeof dest !== "string") { + const {mtime: inTime} = fs.statSync(source); + for (const filepath of dest) { + if (fs.existsSync(filepath)) { + const {mtime: outTime} = fs.statSync(filepath); + if (+inTime > +outTime) { + return true; + } + } + else { + return true; + } + } + return false; + } + else if (typeof source !== "string" && typeof dest === "string") { + if (fs.existsSync(dest)) { + const {mtime: outTime} = fs.statSync(dest); + for (const filepath of source) { + if (fs.existsSync(filepath)) { + const {mtime: inTime} = fs.statSync(filepath); + if (+inTime > +outTime) { + return true; + } + } + else { + return true; + } + } + return false; + } + } + else if (typeof source !== "string" && typeof dest !== "string") { + for (let i = 0; i < source.length; i++) { + if (!dest[i]) { + continue; + } + if (fs.existsSync(dest[i])) { + const {mtime: outTime} = fs.statSync(dest[i]); + const {mtime: inTime} = fs.statSync(source[i]); + if (+inTime > +outTime) { + return true; + } + } + else { + return true; + } + } + return false; + } + return true; +} + +interface CompileFileOptions { + noOutFile?: boolean; // true to compile without using --out + generateDeclarations?: boolean; // true to compile using --declaration + outDir?: string; // value for "--outDir" command line option + keepComments?: boolean; // false to compile using --removeComments + preserveConstEnums?: boolean; // true if compiler should keep const enums in code + noResolve?: boolean; // true if compiler should not include non-rooted files in compilation + stripInternal?: boolean; // true if compiler should remove declarations marked as @internal + noMapRoot?: boolean; // true if compiler omit mapRoot option +} + +/* Compiles a file from a list of sources + * @param outFile: the target file name + * @param sources: an array of the names of the source files + * @param prereqs: prerequisite tasks to compiling the file + * @param prefixes: a list of files to prepend to the target file + * @param useBuiltCompiler: true to use the built compiler, false to use the LKG + * @param {Object} opts - property bag containing auxiliary options + * @param callback: a function to execute after the compilation process ends + */ +function compileFile(outFile: string, sources: string[], prereqs?: string[], prefixes?: string[], useBuiltCompiler?: boolean, opts: CompileFileOptions = {}, onComplete?: (done: () => void) => void) { + const compilerPath = useBuiltCompiler ? builtLocalCompiler : LKGCompiler; + gulp.task(outFile, false, useBuiltCompiler ? [builtLocalCompiler].concat(prereqs) : prereqs, (done) => { + const options = [compilerPath, "--noImplicitAny", "--noEmitOnError", "--pretty"]; + // Keep comments when specifically requested + // or when in debug mode. + if (!(opts.keepComments || useDebugMode)) { + options.push("--removeComments"); + } + + if (opts.generateDeclarations) { + options.push("--declaration"); + } + + if (opts.preserveConstEnums || useDebugMode) { + options.push("--preserveConstEnums"); + } + + if (opts.outDir) { + options.push("--outDir", opts.outDir); + } + + if (!opts.noOutFile) { + // If the out file exists, check if any inputs are more recently modified, otherwise - assume no change and don't compile + if (!needsUpdate(sources, outFile)) { + return complete(); + } + options.push("--out", outFile); + } + else { + if (!needsUpdate(sources, sources.map(s => { + if (s.lastIndexOf(".d.ts", 0) === 0) { + return undefined; + } + if (opts.outDir) { + return path.join(opts.outDir, path.parse(s).name + ".js") + } + else { + const parsed = path.parse(s); + parsed.ext = ".js"; + return path.format(parsed); + } + }))) { + return complete(); + } + options.push("--module", "commonjs"); + } + + if (opts.noResolve) { + options.push("--noResolve"); + } + + if (useDebugMode) { + options.push("--inlineSourceMap", "--inlineSources"); + } + else { + options.push("--newLine", "LF"); + } + + if (opts.stripInternal) { + options.push("--stripInternal"); + } + + exec(host, options.concat(sources), () => { + if (!useDebugMode && prefixes && fs.existsSync(outFile)) { + for (const i in prefixes) { + prependFile(prefixes[i], outFile); + } + } + + return complete(); + }, (e, status) => { + if (fs.existsSync(outFile)) { + fs.unlinkSync(outFile); + } + done(`Compilation of ${outFile} unsuccessful. Status code ${status}. Error: ${e}`); + }); + + function complete() { + if (onComplete) { + onComplete(done); + } + else { + done(); + } + } + }); +} + + +compileFile(/*outfile*/configureNightlyJs, + /*sources*/ [configureNightlyTs], + /*prereqs*/ [], + /*prefixes*/ [], + /*useBuiltCompiler*/ false, + { noOutFile: false, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false }); + + +// Nightly management tasks +gulp.task("configure-nightly", "Runs scripts/configureNightly.ts to prepare a build for nightly publishing", [configureNightlyJs], (done) => { + exec(host, [configureNightlyJs, packageJson, programTs], done, done); +}); +gulp.task("publish-nightly", "Runs `npm publish --tag next` to create a new nightly build on npm", ["LKG"], () => { + return runSequence("clean", "useDebugMode", "runtests", (done) => { + exec("npm", ["publish", "--tag", "next"], done, done); + }); +}); + +const scriptsTsdJson = path.join(scriptsDirectory, "tsd.json"); +gulp.task("tsd-scripts", `Runs \`tsd --config ${scriptsTsdJson}\` install`, [], (done) => { + exec("tsd", ["--config", scriptsTsdJson, "install"], done, done); +}); + + +const importDefinitelyTypedTestsDirectory = path.join(scriptsDirectory, "importDefinitelyTypedTests"); +const importDefinitelyTypedTestsJs = path.join(importDefinitelyTypedTestsDirectory, "importDefinitelyTypedTests.js"); +const importDefinitelyTypedTestsTs = path.join(importDefinitelyTypedTestsDirectory, "importDefinitelyTypedTests.ts"); +compileFile(importDefinitelyTypedTestsJs, [importDefinitelyTypedTestsTs]); + +gulp.task("importDefinitelyTypedTests", "Runs scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.ts to copy DT's tests to the TS-internal RWC tests", [importDefinitelyTypedTestsJs], (done) => { + exec(host, [importDefinitelyTypedTestsJs, "./", "../DefinitelyTyped"], done, done); +}); + +gulp.task("lib", "Builds the library targets", libraryTargets); + + +// Generate diagnostics +const processDiagnosticMessagesJs = path.join(scriptsDirectory, "processDiagnosticMessages.js"); +const processDiagnosticMessagesTs = path.join(scriptsDirectory, "processDiagnosticMessages.ts"); +const diagnosticMessagesJson = path.join(compilerDirectory, "diagnosticMessages.json"); +const diagnosticInfoMapTs = path.join(compilerDirectory, "diagnosticInformationMap.generated.ts"); +const generatedDiagnosticMessagesJSON = path.join(compilerDirectory, "diagnosticMessages.generated.json"); +const builtGeneratedDiagnosticMessagesJSON = path.join(builtLocalDirectory, "diagnosticMessages.generated.json"); + +// processDiagnosticMessages script +compileFile(processDiagnosticMessagesJs, + [processDiagnosticMessagesTs], + [], + [], + /*useBuiltCompiler*/ false); + +// The generated diagnostics map; built for the compiler and for the "generate-diagnostics" task +gulp.task(diagnosticInfoMapTs, [processDiagnosticMessagesJs], (done) => { + if (needsUpdate(diagnosticMessagesJson, [generatedDiagnosticMessagesJSON, diagnosticInfoMapTs])) { + exec(host, [processDiagnosticMessagesJs, diagnosticMessagesJson], done, done); + } + else { + done(); + } +}); + +gulp.task(builtGeneratedDiagnosticMessagesJSON, [diagnosticInfoMapTs], (done) => { + if (fs.existsSync(builtLocalDirectory) && needsUpdate(generatedDiagnosticMessagesJSON, builtGeneratedDiagnosticMessagesJSON)) { + fs.writeFileSync(builtGeneratedDiagnosticMessagesJSON, fs.readFileSync(generatedDiagnosticMessagesJSON)); + } + done(); +}); + +gulp.task("generate-diagnostics", "Generates a diagnostic file in TypeScript based on an input JSON file", [diagnosticInfoMapTs]); + +compileFile(builtLocalCompiler, compilerSources, ["lib", "generate-diagnostics"], [copyright], /*useBuiltCompiler:*/ false); + +const servicesFile = path.join(builtLocalDirectory, "typescriptServices.js"); +const standaloneDefinitionsFile = path.join(builtLocalDirectory, "typescriptServices.d.ts"); +const nodePackageFile = path.join(builtLocalDirectory, "typescript.js"); +const nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts"); +const nodeStandaloneDefinitionsFile = path.join(builtLocalDirectory, "typescript_standalone.d.ts"); + +function copyFile(source: string, dest: string, cb: (err: any) => void) { + mkdirP(path.dirname(dest), (err) => { + if (err) cb(err); + fs.readFile(source, (err, data) => { + if (err) cb(err); + fs.writeFile(dest, data, cb); + }); + }); +} + +function fail(err: any) { + console.error(err); + throw new Error(err); +} + +compileFile(servicesFile, servicesSources, [], + /*prefixes*/ [copyright], + /*useBuiltCompiler*/ true, + { noOutFile: false, generateDeclarations: true, preserveConstEnums: true, keepComments: true, noResolve: false, stripInternal: true }, + /*callback*/ (done) => { + copyFile(servicesFile, nodePackageFile, (err) => { + if (err) fail(err); + prependFile(copyright, standaloneDefinitionsFile); + + // Stanalone/web definition file using global 'ts' namespace + copyFile(standaloneDefinitionsFile, nodeDefinitionsFile, (err) => { + if (err) fail(err); + let definitionFileContents = fs.readFileSync(nodeDefinitionsFile).toString(); + definitionFileContents = definitionFileContents.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4"); + 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 + const nodeDefinitionsFileContents = definitionFileContents + "\r\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. + const nodeStandaloneDefinitionsFileContents = definitionFileContents.replace(/declare (namespace|module) ts/g, 'declare module "typescript"'); + fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents); + done(); + }); + }); + }); + +const serverFile = path.join(builtLocalDirectory, "tsserver.js"); +compileFile(serverFile, serverSources, [], /*prefixes*/ [copyright], /*useBuiltCompiler*/ true); + +const tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js"); +const tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts"); +compileFile( + tsserverLibraryFile, + languageServiceLibrarySources, + [], + /*prefixes*/ [copyright], + /*useBuiltCompiler*/ true, + { noOutFile: false, generateDeclarations: true }); + +gulp.task("lssl", "Builds language service server library", [tsserverLibraryFile]); +gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON]); +gulp.task("tsc", "Builds only the compiler", [builtLocalCompiler]); + + +// Generate Markdown spec +const word2mdJs = path.join(scriptsDirectory, "word2md.js"); +const word2mdTs = path.join(scriptsDirectory, "word2md.ts"); +const specWord = path.join(docDirectory, "TypeScript Language Specification.docx"); +const specMd = path.join(docDirectory, "spec.md"); + +compileFile(word2mdJs, + [word2mdTs], + [], + [], + /*useBuiltCompiler*/ false); + +gulp.task(specMd, false, [word2mdJs], (done) => { + const specWordFullPath = path.resolve(specWord); + const specMDFullPath = path.resolve(specMd); + const cmd = "cscript //nologo " + word2mdJs + " \"" + specWordFullPath + "\" " + "\"" + specMDFullPath + "\""; + console.log(cmd); + cp.exec(cmd, function () { + done(); + }); +}); + +gulp.task("generate-spec", "Generates a Markdown version of the Language Specification", [specMd]); + +gulp.task("clean", "Cleans the compiler output, declare files, and tests", [], () => { + return del([builtDirectory]); +}); + +gulp.task("useDebugMode", false, [], (done) => { useDebugMode = false; done(); }); +gulp.task("dontUseDebugMode", false, [], (done) => { useDebugMode = true; done(); }); + +gulp.task("VerifyLKG", false, [], () => { + const expectedFiles = [builtLocalCompiler, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets); + const missingFiles = expectedFiles.filter(function (f) { + return !fs.existsSync(f); + }); + if (missingFiles.length > 0) { + throw 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 + return gulp.src(expectedFiles).pipe(gulp.dest(LKGDirectory)); +}); + +gulp.task("LKG", "Makes a new LKG out of the built js files", [], () => { + return runSequence("dontUseDebugMode", "lib", "local", "lssl", "VerifyLKG"); +}); + + +// Task to build the tests infrastructure using the built compiler +const run = path.join(builtLocalDirectory, "run.js"); +compileFile(run, harnessSources, [], [], /*useBuiltCompiler:*/ true); + +const internalTests = "internal/"; + +const localBaseline = "tests/baselines/local/"; +const refBaseline = "tests/baselines/reference/"; + +const localRwcBaseline = path.join(internalTests, "baselines/rwc/local"); +const refRwcBaseline = path.join(internalTests, "baselines/rwc/reference"); + +const localTest262Baseline = path.join(internalTests, "baselines/test262/local"); +const refTest262Baseline = path.join(internalTests, "baselines/test262/reference"); + + +gulp.task("tests", "Builds the test infrastructure using the built compiler", ["local", run]); +gulp.task("tests-debug", "Builds the test sources and automation in debug mode", () => { + return runSequence("useDebugMode", "tests"); +}); + +function deleteTemporaryProjectOutput() { + return del(path.join(localBaseline, "projectOutput/")); +} + +let savedNodeEnv: string; +function setNodeEnvToDevelopment() { + savedNodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = "development"; +} + +function restoreSavedNodeEnv() { + process.env.NODE_ENV = savedNodeEnv; +} + +let testTimeout = 20000; +function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: (e?: any) => void) { + const lintFlag = cmdLineOptions["lint"]; + cleanTestDirs((err) => { + if (err) { console.error(err); failWithStatus(err, 1); } + const debug = cmdLineOptions["debug"]; + const tests = cmdLineOptions["tests"]; + const light = cmdLineOptions["light"]; + const testConfigFile = "test.config"; + if (fs.existsSync(testConfigFile)) { + fs.unlinkSync(testConfigFile); + } + let workerCount, taskConfigsFolder; + if (runInParallel) { + // generate name to store task configuration files + const prefix = os.tmpdir() + "/ts-tests"; + let i = 1; + do { + taskConfigsFolder = prefix + i; + i++; + } while (fs.existsSync(taskConfigsFolder)); + fs.mkdirSync(taskConfigsFolder); + + workerCount = process.env.workerCount || os.cpus().length; + } + + if (tests || light || taskConfigsFolder) { + writeTestConfigFile(tests, light, taskConfigsFolder, workerCount); + } + + if (tests && tests.toLocaleLowerCase() === "rwc") { + testTimeout = 100000; + } + + const colors = cmdLineOptions["colors"]; + const reporter = cmdLineOptions["reporter"] || defaultReporter; + + // 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) { + const args = []; + if (debug) { + args.push("--debug-brk"); + } + args.push("-R", reporter); + if (tests) { + args.push("-g", `"${tests}"`); + } + if (colors) { + args.push("--colors"); + } + else { + args.push("--no-colors"); + } + args.push("-t", testTimeout); + args.push(run); + setNodeEnvToDevelopment(); + exec(mocha, args, lintThenFinish, function(e, status) { + finish(e, status); + }); + + } + else { + // run task to load all tests and partition them between workers + const args = []; + args.push("-R", "min"); + if (colors) { + args.push("--colors"); + } + else { + args.push("--no-colors"); + } + args.push(run); + setNodeEnvToDevelopment(); + runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) { + // last worker clean everything and runs linter in case if there were no errors + del(taskConfigsFolder).then(() => { + if (!err) { + lintThenFinish(); + } + else { + finish(err); + } + }); + + }); + } + }); + + function failWithStatus(err?: any, status?: number) { + if (err) { + console.log(err); + } + done(err || status); + process.exit(status); + } + + function lintThenFinish() { + if (lintFlag) { + runSequence("lint", finish); + } + else { + finish(); + } + } + + function finish(error?: any, errorStatus?: number) { + restoreSavedNodeEnv(); + deleteTemporaryProjectOutput().then(() => { + if (error !== undefined || errorStatus !== undefined) { + failWithStatus(error, errorStatus); + } + else { + done(); + } + }); + } +} + +gulp.task("runtests-parallel", "Runs all the tests in parallel using the built run.js file. Optional arguments are: --t[ests]=category1|category2|... --d[ebug]=true.", ["build-rules", "tests"], (done) => { + runConsoleTests("min", /*runInParallel*/ true, done); +}); +gulp.task("runtests", + "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.", + ["build-rules", "tests"], + (done) => { + runConsoleTests("mocha-fivemat-progress-reporter", /*runInParallel*/ false, done); +}); + +const nodeServerOutFile = "tests/webTestServer.js"; +const nodeServerInFile = "tests/webTestServer.ts"; +compileFile(nodeServerOutFile, [nodeServerInFile], [], [], /*useBuiltCompiler:*/ true, { noOutFile: true }); + +gulp.task("browserify", "Runs browserify on run.js to produce a file suitable for running tests in the browser", ["tests", run, nodeServerOutFile], (done) => { + // TODO (weswig): Use browserify JS api with gulp streams and correctly manage sourcemaps + exec(browserify, [run, "-d", "-o", "built/local/bundle.js"], done, done); +}); + + +function cleanTestDirs(done: (e?: any) => void) { + // Clean the local baselines & Rwc baselines directories + del([ + localBaseline, + localRwcBaseline, + ]).then(() => { + mkdirP(localRwcBaseline, (err) => { + if (err) done(err); + mkdirP(localTest262Baseline, () => { + if (err) done(err); + mkdirP(localBaseline, (err) => done(err)); + }); + }); + }); +} + +// used to pass data from jake command line directly to run.js +function writeTestConfigFile(tests: string, light: boolean, taskConfigsFolder?: string, workerCount?: number) { + const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light: light, workerCount: workerCount, taskConfigsFolder: taskConfigsFolder }); + console.log("Running tests with config: " + testConfigContents); + fs.writeFileSync("test.config", testConfigContents); +} + + +gulp.task("runtests-browser", "Runs the tests using the built run.js file like 'gulp runtests'. Syntax is gulp runtests-browser. Additional optional parameters --tests=[regex], --port=, --browser=[chrome|IE]", ["browserify"], (done) => { + cleanTestDirs((err) => { + if (err) { console.error(err); done(err); process.exit(1); } + host = "node"; + let tests = cmdLineOptions["tests"]; + const light = cmdLineOptions["light"]; + const testConfigFile = "test.config"; + if (fs.existsSync(testConfigFile)) { + fs.unlinkSync(testConfigFile); + } + if (tests || light) { + writeTestConfigFile(tests, light); + } + + const args = [nodeServerOutFile]; + if (cmdLineOptions["port"]) { + args.push(cmdLineOptions["port"]); + } + if (cmdLineOptions["browser"]) { + args.push(cmdLineOptions["browser"]); + } + if (tests) { + args.push(JSON.stringify(tests)); + } + exec(host, args, done, done); + }); +}); + +gulp.task("generate-code-coverage", "Generates code coverage data via instanbul", ["tests"], (done) => { + exec("istanbul", ["cover", "node_modules/mocha/bin/_mocha", "--", "-R", "min", "-t", testTimeout.toString(), run], done, done); +}); + + +function getDiffTool() { + const program = process.env["DIFF"]; + if (!program) { + console.error("Add the 'DIFF' environment constiable to the path of the program you want to use."); + process.exit(1); + } + return program; +} + +gulp.task("diff", "Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment constiable", (done) => { + exec(getDiffTool(), [refBaseline, localBaseline], done, done); +}); +gulp.task("diff-rwc", "Diffs the RWC baselines using the diff tool specified by the 'DIFF' environment constiable", (done) => { + exec(getDiffTool(), [refRwcBaseline, localRwcBaseline], done, done); +}); + + +gulp.task("baseline-accept", "Makes the most recent test results the new baseline, overwriting the old baseline", (done) => { + const softAccept = cmdLineOptions["soft"]; + if (!softAccept) { + del(refBaseline).then(() => { + fs.renameSync(localBaseline, refBaseline); + done(); + }, done); + } + else { + gulp.src(localBaseline) + .pipe(gulp.dest(refBaseline)) + .on("end", () => { + del(path.join(refBaseline, "local")).then(() => done(), done); + }); + } +}); +gulp.task("baseline-accept-rwc", "Makes the most recent rwc test results the new baseline, overwriting the old baseline", () => { + return del(refRwcBaseline).then(() => { + fs.renameSync(localRwcBaseline, refRwcBaseline); + }); +}); +gulp.task("baseline-accept-test262", "Makes the most recent test262 test results the new baseline, overwriting the old baseline", () => { + return del(refTest262Baseline).then(() => { + fs.renameSync(localTest262Baseline, refTest262Baseline); + }); +}); + + +// Webhost +const webhostPath = "tests/webhost/webtsc.ts"; +const webhostJsPath = "tests/webhost/webtsc.js"; +compileFile(webhostJsPath, [webhostPath], [], [], /*useBuiltCompiler*/true); + +gulp.task("webhost", "Builds the tsc web host", [webhostJsPath], () => { + return gulp.src(path.join(builtLocalDirectory, "lib.d.ts")).pipe(gulp.dest("tests/webhost/")); +}); + + +// Perf compiler +const perftscPath = "tests/perftsc.ts"; +const perftscJsPath = "built/local/perftsc.js"; +compileFile(perftscJsPath, [perftscPath], [], [], /*useBuiltCompiler*/ true); + +gulp.task("perftsc", "Builds augmented version of the compiler for perf tests", [perftscJsPath]); + + +// Instrumented compiler +const loggedIOpath = path.join(harnessDirectory, "loggedIO.ts"); +const loggedIOJsPath = path.join(builtLocalDirectory, "loggedIO.js"); +gulp.task(loggedIOJsPath, false, [], (done) => { + const temp = path.join(builtLocalDirectory, "temp"); + mkdirP(temp, (err) => { + if (err) { console.error(err); done(err); process.exit(1); }; + exec(host, [LKGCompiler, "--outdir", temp, loggedIOpath], () => { + fs.renameSync(path.join(temp, "/harness/loggedIO.js"), loggedIOJsPath); + del(temp).then(() => done(), done); + }, done); + }); +}); + +const instrumenterPath = path.join(harnessDirectory, "instrumenter.ts"); +const instrumenterJsPath = path.join(builtLocalDirectory, "instrumenter.js"); +compileFile(instrumenterJsPath, [instrumenterPath], [], [], /*useBuiltCompiler*/ true); + +gulp.task("tsc-instrumented", "Builds an instrumented tsc.js", [loggedIOJsPath, instrumenterJsPath, builtLocalCompiler], (done) => { + exec(host, [instrumenterJsPath, "record", "iocapture", builtLocalDirectory, compilerFilename], done, done); +}); + +gulp.task("update-sublime", "Updates the sublime plugin's tsserver", ["local", serverFile], () => { + return gulp.src([serverFile, serverFile + ".map"]).pipe(gulp.dest("../TypeScript-Sublime-Plugin/tsserver/")); +}); + + +const tslintRuleDir = "scripts/tslint"; +const tslintRules = [ + "nextLineRule", + "preferConstRule", + "booleanTriviaRule", + "typeOperatorSpacingRule", + "noInOperatorRule", + "noIncrementDecrementRule" +]; +const tslintRulesFiles = tslintRules.map(function(p) { + return path.join(tslintRuleDir, p + ".ts"); +}); +const tslintRulesOutFiles = tslintRules.map(function(p, i) { + const pathname = path.join(builtLocalDirectory, "tslint", p + ".js"); + compileFile(pathname, [tslintRulesFiles[i]], [], [], /*useBuiltCompiler*/ false, + { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint")}); + return pathname; +}); + +gulp.task("build-rules", "Compiles tslint rules to js", tslintRulesOutFiles); + + +function getLinterOptions() { + return { + configuration: require("./tslint.json"), + formatter: "prose", + formattersDirectory: undefined, + rulesDirectory: "built/local/tslint" + }; +} + +function lintFileContents(options, path, contents) { + const ll = new Linter(path, contents, options); + console.log("Linting '" + path + "'."); + return ll.lint(); +} + +function lintFile(options, path) { + const contents = fs.readFileSync(path, "utf8"); + return lintFileContents(options, path, contents); +} + +const lintTargets = ["Gulpfile.ts"] + .concat(compilerSources) + .concat(harnessSources) + // Other harness sources + .concat(["instrumenter.ts"].map(function(f) { return path.join(harnessDirectory, f); })) + .concat(serverCoreSources) + .concat(tslintRulesFiles) + .concat(servicesSources); + + +gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: --f[iles]=regex", ["build-rules"], () => { + const lintOptions = getLinterOptions(); + let failed = 0; + const fileMatcher = RegExp(cmdLineOptions["files"]); + const done = {}; + for (const i in lintTargets) { + const target = lintTargets[i]; + if (!done[target] && fileMatcher.test(target)) { + const result = lintFile(lintOptions, target); + if (result.failureCount > 0) { + console.log(result.output); + failed += result.failureCount; + } + done[target] = true; + } + } + if (failed > 0) { + console.error("Linter errors."); + process.exit(1); + } +}); + + +gulp.task("default", "Runs 'local'", ["local"]); diff --git a/Jakefile.js b/Jakefile.js deleted file mode 100644 index 85ae5f60361..00000000000 --- a/Jakefile.js +++ /dev/null @@ -1,1076 +0,0 @@ -// This file contains the build logic for the public repo - -var fs = require("fs"); -var os = require("os"); -var path = require("path"); -var child_process = require("child_process"); -var Linter = require("tslint"); -var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel; - -// Variables -var compilerDirectory = "src/compiler/"; -var servicesDirectory = "src/services/"; -var serverDirectory = "src/server/"; -var harnessDirectory = "src/harness/"; -var libraryDirectory = "src/lib/"; -var scriptsDirectory = "scripts/"; -var unittestsDirectory = "tests/cases/unittests/"; -var docDirectory = "doc/"; - -var builtDirectory = "built/"; -var builtLocalDirectory = "built/local/"; -var LKGDirectory = "lib/"; - -var copyright = "CopyrightNotice.txt"; -var thirdParty = "ThirdPartyNoticeText.txt"; - -// 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; -} - -var compilerSources = [ - "core.ts", - "sys.ts", - "types.ts", - "scanner.ts", - "parser.ts", - "utilities.ts", - "binder.ts", - "checker.ts", - "sourcemap.ts", - "declarationEmitter.ts", - "emitter.ts", - "program.ts", - "commandLineParser.ts", - "tsc.ts", - "diagnosticInformationMap.generated.ts" -].map(function (f) { - return path.join(compilerDirectory, f); -}); - -var servicesSources = [ - "core.ts", - "sys.ts", - "types.ts", - "scanner.ts", - "parser.ts", - "utilities.ts", - "binder.ts", - "checker.ts", - "sourcemap.ts", - "declarationEmitter.ts", - "emitter.ts", - "program.ts", - "commandLineParser.ts", - "diagnosticInformationMap.generated.ts" -].map(function (f) { - return path.join(compilerDirectory, f); -}).concat([ - "breakpoints.ts", - "navigateTo.ts", - "navigationBar.ts", - "outliningElementsCollector.ts", - "patternMatcher.ts", - "services.ts", - "shims.ts", - "signatureHelp.ts", - "utilities.ts", - "formatting/formatting.ts", - "formatting/formattingContext.ts", - "formatting/formattingRequestKind.ts", - "formatting/formattingScanner.ts", - "formatting/references.ts", - "formatting/rule.ts", - "formatting/ruleAction.ts", - "formatting/ruleDescriptor.ts", - "formatting/ruleFlag.ts", - "formatting/ruleOperation.ts", - "formatting/ruleOperationContext.ts", - "formatting/rules.ts", - "formatting/rulesMap.ts", - "formatting/rulesProvider.ts", - "formatting/smartIndenter.ts", - "formatting/tokenRange.ts" -].map(function (f) { - return path.join(servicesDirectory, f); -})); - -var serverCoreSources = [ - "node.d.ts", - "editorServices.ts", - "protocol.d.ts", - "session.ts", - "server.ts" -].map(function (f) { - return path.join(serverDirectory, f); -}); - -var serverSources = serverCoreSources.concat(servicesSources); - -var languageServiceLibrarySources = [ - "editorServices.ts", - "protocol.d.ts", - "session.ts" -].map(function (f) { - return path.join(serverDirectory, f); -}).concat(servicesSources); - -var harnessCoreSources = [ - "harness.ts", - "sourceMapRecorder.ts", - "harnessLanguageService.ts", - "fourslash.ts", - "runnerbase.ts", - "compilerRunner.ts", - "typeWriter.ts", - "fourslashRunner.ts", - "projectsRunner.ts", - "loggedIO.ts", - "rwcRunner.ts", - "test262Runner.ts", - "runner.ts" -].map(function (f) { - return path.join(harnessDirectory, f); -}); - -var harnessSources = harnessCoreSources.concat([ - "incrementalParser.ts", - "jsDocParsing.ts", - "services/colorization.ts", - "services/documentRegistry.ts", - "services/preProcessFile.ts", - "services/patternMatcher.ts", - "session.ts", - "versionCache.ts", - "convertToBase64.ts", - "transpile.ts", - "reuseProgramStructure.ts", - "cachingInServerLSHost.ts", - "moduleResolution.ts", - "tsconfigParsing.ts", - "commandLineParsing.ts", - "convertCompilerOptionsFromJson.ts", - "convertTypingOptionsFromJson.ts", - "tsserverProjectSystem.ts" -].map(function (f) { - return path.join(unittestsDirectory, f); -})).concat([ - "protocol.d.ts", - "session.ts", - "client.ts", - "editorServices.ts" -].map(function (f) { - return path.join(serverDirectory, f); -})); - -var es2015LibrarySources = [ - "es2015.core.d.ts", - "es2015.collection.d.ts", - "es2015.generator.d.ts", - "es2015.iterable.d.ts", - "es2015.promise.d.ts", - "es2015.proxy.d.ts", - "es2015.reflect.d.ts", - "es2015.symbol.d.ts", - "es2015.symbol.wellknown.d.ts" -]; - -var es2015LibrarySourceMap = es2015LibrarySources.map(function(source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -var es2016LibrarySource = [ "es2016.array.include.d.ts" ]; - -var es2016LibrarySourceMap = es2016LibrarySource.map(function (source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -var es2017LibrarySource = [ - "es2017.object.d.ts", - "es2017.sharedmemory.d.ts" -]; - -var es2017LibrarySourceMap = es2017LibrarySource.map(function (source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -var hostsLibrarySources = ["dom.generated.d.ts", "webworker.importscripts.d.ts", "scripthost.d.ts"]; - -var librarySourceMap = [ - // Host library - { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] }, - { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] }, - { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] }, - { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] }, - - // JavaScript library - { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] }, - { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] }, - { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] }, - { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] }, - - // JavaScript + all host library - { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) }, - { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") } -].concat(es2015LibrarySourceMap, es2016LibrarySourceMap, es2017LibrarySourceMap); - -var libraryTargets = librarySourceMap.map(function (f) { - return path.join(builtLocalDirectory, f.target); -}); - -// Prepends the contents of prefixFile to destinationFile -function prependFile(prefixFile, destinationFile) { - if (!fs.existsSync(prefixFile)) { - fail(prefixFile + " does not exist!"); - } - 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); -} - -// concatenate a list of sourceFiles to a destinationFile -function concatenateFiles(destinationFile, sourceFiles) { - var temp = "temptemp"; - // Copy the first file to temp - if (!fs.existsSync(sourceFiles[0])) { - fail(sourceFiles[0] + " does not exist!"); - } - jake.cpR(sourceFiles[0], temp, {silent: true}); - // append all files in sequence - for (var i = 1; i < sourceFiles.length; i++) { - if (!fs.existsSync(sourceFiles[i])) { - fail(sourceFiles[i] + " does not exist!"); - } - fs.appendFileSync(temp, fs.readFileSync(sourceFiles[i])); - } - // 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 outFile: the target file name - * @param sources: an array of the names of the source files - * @param prereqs: prerequisite tasks to compiling the file - * @param prefixes: a list of files to prepend to the target file - * @param useBuiltCompiler: true to use the built compiler, false to use the LKG - * @parap {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.noMapRoot: true if compiler omit mapRoot option - * @param callback: a function to execute after the compilation process ends - */ -function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts, callback) { - file(outFile, prereqs, function() { - var compilerPath = useBuiltCompiler ? builtLocalCompiler : LKGCompiler; - var options = "--noImplicitAny --noEmitOnError --pretty"; - opts = opts || {}; - // Keep comments when specifically requested - // or when in debug mode. - if (!(opts.keepComments || useDebugMode)) { - options += " --removeComments"; - } - - if (opts.generateDeclarations) { - options += " --declaration"; - } - - if (opts.preserveConstEnums || useDebugMode) { - options += " --preserveConstEnums"; - } - - if (opts.outDir) { - options += " --outDir " + opts.outDir; - } - - if (!opts.noOutFile) { - options += " --out " + outFile; - } - else { - options += " --module commonjs"; - } - - if(opts.noResolve) { - options += " --noResolve"; - } - - if (useDebugMode) { - options += " --inlineSourceMap --inlineSources"; - } else { - options += " --newLine LF"; - } - - if (opts.stripInternal) { - options += " --stripInternal"; - } - - var cmd = host + " " + compilerPath + " " + options + " "; - cmd = cmd + sources.join(" "); - console.log(cmd + "\n"); - - 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(); - } - - complete(); - }); - ex.addListener("error", function() { - fs.unlinkSync(outFile); - fail("Compilation of " + outFile + " unsuccessful"); - }); - ex.run(); - }, {async: true}); -} - -// Prerequisite task for built directory and library typings -directory(builtLocalDirectory); - -for (var i in libraryTargets) { - (function (i) { - var entry = librarySourceMap[i]; - var target = libraryTargets[i]; - var sources = [copyright].concat(entry.sources.map(function (s) { - return path.join(libraryDirectory, s); - })); - file(target, [builtLocalDirectory].concat(sources), function() { - concatenateFiles(target, sources); - }); - })(i); -} - -// 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 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, - [processDiagnosticMessagesTs], - [processDiagnosticMessagesTs], - [], - /*useBuiltCompiler*/ false); - -// 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 configureNightlyJs = path.join(scriptsDirectory, "configureNightly.js"); -var configureNightlyTs = path.join(scriptsDirectory, "configureNightly.ts"); -var packageJson = "package.json"; -var programTs = path.join(compilerDirectory, "program.ts"); - -file(configureNightlyTs); - -compileFile(/*outfile*/configureNightlyJs, - /*sources*/ [configureNightlyTs], - /*prereqs*/ [configureNightlyTs], - /*prefixes*/ [], - /*useBuiltCompiler*/ false, - { noOutFile: false, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false }); - -task("setDebugMode", function() { - useDebugMode = true; -}); - -task("configure-nightly", [configureNightlyJs], function() { - var cmd = host + " " + configureNightlyJs + " " + packageJson + " " + programTs; - 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"], function () { - var cmd = "npm publish --tag next"; - console.log(cmd); - exec(cmd); -}); - -var scriptsTsdJson = path.join(scriptsDirectory, "tsd.json"); -file(scriptsTsdJson); - -task("tsd-scripts", [scriptsTsdJson], function () { - var cmd = "tsd --config " + scriptsTsdJson + " install"; - console.log(cmd); - exec(cmd); -}, { async: true }); - -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, - { 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 = definitionFileContents.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, '$1$2enum $3 {$4'); - 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 + "\r\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); - }); - - -var serverFile = path.join(builtLocalDirectory, "tsserver.js"); -compileFile(serverFile, serverSources,[builtLocalDirectory, copyright].concat(serverSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true); - -var tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js"); -var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts"); -compileFile( - tsserverLibraryFile, - languageServiceLibrarySources, - [builtLocalDirectory, copyright].concat(languageServiceLibrarySources), - /*prefixes*/ [copyright], - /*useBuiltCompiler*/ true, - { noOutFile: false, generateDeclarations: true }); - -// Local target to build the language service server library -desc("Builds language service server library"); -task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile]); - -// Local target to build the compiler and services -desc("Builds the full compiler and services"); -task("local", ["generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, builtGeneratedDiagnosticMessagesJSON]); - -// 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); - -// 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", "lssl"].concat(libraryTargets), function() { - var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets); - var missingFiles = expectedFiles.filter(function (f) { - return !fs.existsSync(f); - }); - if (missingFiles.length > 0) { - fail("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); - for (i in expectedFiles) { - jake.cpR(expectedFiles[i], LKGDirectory); - } - //var resourceDirectories = fs.readdirSync(builtLocalResourcesDirectory).map(function(p) { return path.join(builtLocalResourcesDirectory, p); }); - //resourceDirectories.map(function(d) { - // jake.cpR(d, LKGResourcesDirectory); - //}); -}); - -// Test directory -directory(builtLocalDirectory); - -// Task to build the tests infrastructure using the built compiler -var run = path.join(builtLocalDirectory, "run.js"); -compileFile(run, harnessSources, [builtLocalDirectory, tscFile].concat(libraryTargets).concat(harnessSources), [], /*useBuiltCompiler:*/ true); - -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], {windowsVerbatimArguments: 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(); -} - -function cleanTestDirs() { - // Clean the local baselines directory - if (fs.existsSync(localBaseline)) { - jake.rmRf(localBaseline); - } - - // Clean the local Rwc baselines directory - if (fs.existsSync(localRwcBaseline)) { - jake.rmRf(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, light, taskConfigsFolder, workerCount) { - var testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light: light, workerCount: workerCount, taskConfigsFolder: taskConfigsFolder }); - fs.writeFileSync('test.config', testConfigContents); -} - -function deleteTemporaryProjectOutput() { - if (fs.existsSync(path.join(localBaseline, "projectOutput/"))) { - jake.rmRf(path.join(localBaseline, "projectOutput/")); - } -} - -function runConsoleTests(defaultReporter, runInParallel) { - cleanTestDirs(); - var debug = process.env.debug || process.env.d; - 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); - } - var workerCount, taskConfigsFolder; - if (runInParallel) { - // generate name to store task configuration files - var prefix = os.tmpdir() + "/ts-tests"; - var i = 1; - do { - taskConfigsFolder = prefix + i; - i++; - } while (fs.existsSync(taskConfigsFolder)); - fs.mkdirSync(taskConfigsFolder); - - workerCount = process.env.workerCount || os.cpus().length; - } - - if (tests || light || taskConfigsFolder) { - writeTestConfigFile(tests, light, taskConfigsFolder, workerCount); - } - - if (tests && tests.toLocaleLowerCase() === "rwc") { - testTimeout = 100000; - } - - colors = process.env.colors || process.env.color; - colors = colors ? ' --no-colors ' : ' --colors '; - reporter = process.env.reporter || process.env.r || defaultReporter; - var lintFlag = process.env.lint !== 'false'; - - // 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) { - tests = tests ? ' -g "' + tests + '"' : ''; - var cmd = "mocha" + (debug ? " --debug-brk" : "") + " -R " + reporter + tests + colors + ' -t ' + testTimeout + ' ' + run; - console.log(cmd); - - var savedNodeEnv = process.env.NODE_ENV; - process.env.NODE_ENV = "development"; - exec(cmd, function () { - process.env.NODE_ENV = savedNodeEnv; - runLinter(); - finish(); - }, function(e, status) { - process.env.NODE_ENV = savedNodeEnv; - finish(status); - }); - - } - else { - var savedNodeEnv = process.env.NODE_ENV; - process.env.NODE_ENV = "development"; - runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) { - process.env.NODE_ENV = savedNodeEnv; - - // last worker clean everything and runs linter in case if there were no errors - deleteTemporaryProjectOutput(); - jake.rmRf(taskConfigsFolder); - if (err) { - fail(err); - } - else { - runLinter(); - complete(); - } - }); - } - - function failWithStatus(status) { - fail("Process exited with code " + status); - } - - function finish(errorStatus) { - deleteTemporaryProjectOutput(); - if (errorStatus !== undefined) { - failWithStatus(errorStatus); - } - else { - complete(); - } - } - function runLinter() { - if (!lintFlag) { - return; - } - var lint = jake.Task['lint']; - lint.addListener('complete', function () { - complete(); - }); - lint.invoke(); - } -} - -var testTimeout = 20000; -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."); -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 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 }); - -desc("Runs browserify on run.js to produce a file suitable for running tests in the browser"); -task("browserify", ["tests", builtLocalDirectory, nodeServerOutFile], function() { - var cmd = 'browserify built/local/run.js -d -o built/local/bundle.js'; - 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], port=, browser=[chrome|IE]"); -task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFile], function() { - cleanTestDirs(); - host = "node"; - port = process.env.port || process.env.p || '8888'; - browser = process.env.browser || process.env.b || "IE"; - 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 || light) { - writeTestConfigFile(tests, light); - } - - tests = tests ? tests : ''; - var cmd = host + " tests/webTestServer.js " + port + " " + browser + " " + JSON.stringify(tests); - console.log(cmd); - exec(cmd); -}, {async: true}); - -function getDiffTool() { - var program = process.env['DIFF']; - if (!program) { - fail("Add the 'DIFF' environment variable to the path of the program you want to use."); - } - return program; -} - -// 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(hardOrSoft) { - if (!hardOrSoft || hardOrSoft === "hard") { - jake.rmRf(refBaseline); - fs.renameSync(localBaseline, refBaseline); - } - else if (hardOrSoft === "soft") { - var files = jake.readdirR(localBaseline); - for (var i in files) { - jake.cpR(files[i], refBaseline); - } - jake.rmRf(path.join(refBaseline, "local")); - } -}); - -desc("Makes the most recent rwc test results the new baseline, overwriting the old baseline"); -task("baseline-accept-rwc", function() { - jake.rmRf(refRwcBaseline); - fs.renameSync(localRwcBaseline, refRwcBaseline); -}); - -desc("Makes the most recent test262 test results the new baseline, overwriting the old baseline"); -task("baseline-accept-test262", function() { - jake.rmRf(refTest262Baseline); - fs.renameSync(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 = "--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); - -desc("Builds an instrumented tsc.js"); -task('tsc-instrumented', [loggedIOJsPath, instrumenterJsPath, tscFile], function() { - var cmd = host + ' ' + instrumenterJsPath + ' record iocapture ' + 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"; -var tslintRules = [ - "nextLineRule", - "preferConstRule", - "booleanTriviaRule", - "typeOperatorSpacingRule", - "noInOperatorRule", - "noIncrementDecrementRule" -]; -var tslintRulesFiles = tslintRules.map(function(p) { - return path.join(tslintRuleDir, p + ".ts"); -}); -var tslintRulesOutFiles = tslintRules.map(function(p) { - return path.join(builtLocalDirectory, "tslint", p + ".js"); -}); -desc("Compiles tslint rules to js"); -task("build-rules", tslintRulesOutFiles); -tslintRulesFiles.forEach(function(ruleFile, i) { - compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false, - { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint")}); -}); - -function getLinterOptions() { - return { - configuration: require("./tslint.json"), - formatter: "prose", - formattersDirectory: undefined, - rulesDirectory: "built/local/tslint" - }; -} - -function lintFileContents(options, path, contents) { - var ll = new Linter(path, contents, options); - console.log("Linting '" + path + "'."); - return ll.lint(); -} - -function lintFile(options, path) { - var contents = fs.readFileSync(path, "utf8"); - return lintFileContents(options, path, contents); -} - -function lintFileAsync(options, path, cb) { - fs.readFile(path, "utf8", function(err, contents) { - if (err) { - return cb(err); - } - var result = lintFileContents(options, path, contents); - cb(undefined, result); - }); -} - -var lintTargets = compilerSources - .concat(harnessSources) - // Other harness sources - .concat(["instrumenter.ts"].map(function(f) { return path.join(harnessDirectory, f) })) - .concat(serverCoreSources) - .concat(tslintRulesFiles) - .concat(servicesSources); - - -desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex"); -task("lint", ["build-rules"], function() { - var lintOptions = getLinterOptions(); - var failed = 0; - var fileMatcher = RegExp(process.env.f || process.env.file || process.env.files || ""); - var done = {}; - for (var i in lintTargets) { - var target = lintTargets[i]; - if (!done[target] && fileMatcher.test(target)) { - var result = lintFile(lintOptions, target); - if (result.failureCount > 0) { - console.log(result.output); - failed += result.failureCount; - } - done[target] = true; - } - } - if (failed > 0) { - fail('Linter errors.', failed); - } -}); - -/** - * This is required because file watches on Windows get fires _twice_ - * when a file changes on some node/windows version configuations - * (node v4 and win 10, for example). By not running a lint for a file - * which already has a pending lint, we avoid duplicating our work. - * (And avoid printing duplicate results!) - */ -var lintSemaphores = {}; - -function lintWatchFile(filename) { - fs.watch(filename, {persistent: true}, function(event) { - if (event !== "change") { - return; - } - - if (!lintSemaphores[filename]) { - lintSemaphores[filename] = true; - lintFileAsync(getLinterOptions(), filename, function(err, result) { - delete lintSemaphores[filename]; - if (err) { - console.log(err); - return; - } - if (result.failureCount > 0) { - console.log("***Lint failure***"); - for (var i = 0; i < result.failures.length; i++) { - var failure = result.failures[i]; - var start = failure.startPosition.lineAndCharacter; - var end = failure.endPosition.lineAndCharacter; - console.log("warning " + filename + " (" + (start.line + 1) + "," + (start.character + 1) + "," + (end.line + 1) + "," + (end.character + 1) + "): " + failure.failure); - } - console.log("*** Total " + result.failureCount + " failures."); - } - }); - } - }); -} - -desc("Watches files for changes to rerun a lint pass"); -task("lint-server", ["build-rules"], function() { - console.log("Watching ./src for changes to linted files"); - for (var i = 0; i < lintTargets.length; i++) { - lintWatchFile(lintTargets[i]); - } -}); diff --git a/README.md b/README.md index 13e1f3e4786..fca2890bc77 100644 --- a/README.md +++ b/README.md @@ -56,29 +56,29 @@ Change to the TypeScript directory: cd TypeScript ``` -Install Jake tools and dev dependencies: +Install Gulp tools and dev dependencies: ``` -npm install -g jake +npm install -g gulp npm install ``` Use one of the following to build and test: ``` -jake local # Build the compiler into built/local -jake clean # Delete the built compiler -jake LKG # Replace the last known good with the built one. +gulp local # Build the compiler into built/local +gulp clean # Delete the built compiler +gulp LKG # Replace the last known good with the built one. # Bootstrapping step to be executed when the built compiler reaches a stable state. -jake tests # Build the test infrastructure using the built compiler. -jake runtests # Run tests using the built compiler and test infrastructure. +gulp tests # Build the test infrastructure using the built compiler. +gulp runtests # Run tests using the built compiler and test infrastructure. # You can override the host or specify a test for this command. # Use host= or tests=. -jake runtests-browser # Runs the tests using the built run.js file. Syntax is jake runtests. Optional +gulp runtests-browser # Runs the tests using the built run.js file. Syntax is gulp runtests. Optional parameters 'host=', 'tests=[regex], reporter=[list|spec|json|]'. -jake baseline-accept # This replaces the baseline test results with the results obtained from jake runtests. -jake lint # Runs tslint on the TypeScript source. -jake -T # List the above commands. +gulp baseline-accept # This replaces the baseline test results with the results obtained from gulp runtests. +gulp lint # Runs tslint on the TypeScript source. +gulp help # List the above commands. ``` diff --git a/package.json b/package.json index bb476070286..2e5987eb3fb 100644 --- a/package.json +++ b/package.json @@ -29,26 +29,45 @@ "node": ">=0.8.0" }, "devDependencies": { - "jake": "latest", - "mocha": "latest", - "chai": "latest", + "@types/Q": "latest", + "@types/del": "latest", + "@types/glob": "latest", + "@types/gulp": "latest", + "@types/gulp-concat": "latest", + "@types/gulp-help": "latest", + "@types/minimatch": "latest", + "@types/minimist": "latest", + "@types/mkdirp": "^0.3.22-alpha", + "@types/node": "latest", + "@types/q": "0.0.21-alpha", + "@types/run-sequence": "latest", "browserify": "latest", + "chai": "latest", + "del": "latest", + "gulp": "latest", + "gulp-concat": "latest", + "gulp-help": "latest", "istanbul": "latest", + "minimist": "latest", + "mkdirp": "latest", + "mocha": "latest", "mocha-fivemat-progress-reporter": "latest", + "run-sequence": "latest", + "ts-node": "latest", + "tsd": "latest", "tslint": "next", - "typescript": "next", - "tsd": "latest" + "typescript": "next" }, "scripts": { - "pretest": "jake tests", - "test": "jake runtests", + "pretest": "gulp tests", + "test": "gulp runtests", "build": "npm run build:compiler && npm run build:tests", - "build:compiler": "jake local", - "build:tests": "jake tests", + "build:compiler": "gulp local", + "build:tests": "gulp tests", "start": "node lib/tsc", - "clean": "jake clean", - "jake": "jake", - "lint": "jake lint", + "clean": "gulp clean", + "gulp": "gulp", + "lint": "gulp lint", "setup-hooks": "node scripts/link-hooks.js" }, "browser": {