Build script updates

This commit is contained in:
Ron Buckton 2018-07-10 12:00:30 -07:00
parent 1fc1495863
commit 0713e754e7
21 changed files with 2475 additions and 7281 deletions

View File

@ -24,6 +24,10 @@ const baselineAccept = require("./scripts/build/baselineAccept");
const cmdLineOptions = require("./scripts/build/options");
const exec = require("./scripts/build/exec");
const browserify = require("./scripts/build/browserify");
const debounce = require("./scripts/build/debounce");
const prepend = require("./scripts/build/prepend");
const { removeSourceMaps } = require("./scripts/build/sourcemaps");
const { CancelSource, CancelError } = require("./scripts/build/cancellation");
const { libraryTargets, generateLibs } = require("./scripts/build/lib");
const { runConsoleTests, cleanTestDirs, writeTestConfigFile, refBaseline, localBaseline, refRwcBaseline, localRwcBaseline } = require("./scripts/build/tests");
@ -44,17 +48,9 @@ const generateLocalizedDiagnosticMessagesJs = "scripts/generateLocalizedDiagnost
const buildProtocolJs = "scripts/buildProtocol.js";
const produceLKGJs = "scripts/produceLKG.js";
const word2mdJs = "scripts/word2md.js";
gulp.task("scripts", /*help*/ false, () => project.compile(scriptsProject), {
aliases: [
configurePrereleaseJs,
processDiagnosticMessagesJs,
generateLocalizedDiagnosticMessagesJs,
produceLKGJs,
buildProtocolJs,
word2mdJs
]
});
gulp.task("clean-scripts", /*help*/ false, () => project.clean(scriptsProject));
const scriptsTaskAliases = [configurePrereleaseJs, processDiagnosticMessagesJs, generateLocalizedDiagnosticMessagesJs, produceLKGJs, buildProtocolJs, word2mdJs];
gulp.task("scripts", /*help*/ false, () => project.compile(scriptsProject), { aliases: scriptsTaskAliases });
gulp.task("clean:scripts", /*help*/ false, () => project.clean(scriptsProject), { aliases: scriptsTaskAliases.map(alias => `clean:${alias}`)});
// Nightly management tasks
gulp.task(
@ -73,7 +69,7 @@ gulp.task(
const importDefinitelyTypedTestsProject = "scripts/importDefinitelyTypedTests/tsconfig.json";
const importDefinitelyTypedTestsJs = "scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js";
gulp.task(importDefinitelyTypedTestsJs, /*help*/ false, () => project.compile(importDefinitelyTypedTestsProject));
gulp.task("clean:" + importDefinitelyTypedTestsJs, /*help*/ false, () => project.clean(importDefinitelyTypedTestsProject));
gulp.task(`clean:${importDefinitelyTypedTestsJs}`, /*help*/ false, () => project.clean(importDefinitelyTypedTestsProject));
gulp.task(
"importDefinitelyTypedTests",
@ -95,7 +91,7 @@ gulp.task(diagnosticInformationMapTs, /*help*/ false, [processDiagnosticMessages
return exec(host, [processDiagnosticMessagesJs, diagnosticMessagesJson]);
}
});
gulp.task("clean:" + diagnosticInformationMapTs, /*help*/ false, () => del([diagnosticInformationMapTs, diagnosticMessagesGeneratedJson]));
gulp.task(`clean:${diagnosticInformationMapTs}`, /*help*/ false, () => del([diagnosticInformationMapTs, diagnosticMessagesGeneratedJson]));
const builtGeneratedDiagnosticMessagesJson = "built/local/diagnosticMessages.generated.json";
gulp.task(builtGeneratedDiagnosticMessagesJson, /*help*/ false, [diagnosticInformationMapTs], () =>
@ -140,9 +136,10 @@ gulp.task(typescriptServicesProject, /*help*/ false, () => {
// NOTE: flatten services so that we can properly strip @internal
project.flatten(servicesProject, typescriptServicesProject, {
compilerOptions: {
"removeComments": true,
"removeComments": false,
"stripInternal": true,
"outFile": "typescriptServices.js"
"declarationMap": false,
"outFile": "typescriptServices.out.js" // must align with same task in jakefile. We fix this name below.
}
});
});
@ -150,7 +147,16 @@ gulp.task(typescriptServicesProject, /*help*/ false, () => {
const typescriptServicesJs = "built/local/typescriptServices.js";
const typescriptServicesDts = "built/local/typescriptServices.d.ts";
gulp.task(typescriptServicesJs, /*help*/ false, ["lib", "generate-diagnostics", typescriptServicesProject], () =>
project.compile(typescriptServicesProject, { dts: files => files.pipe(convertConstEnums()) }),
project.compile(typescriptServicesProject, {
js: files => files
.pipe(prepend.file(copyright))
.pipe(rename("typescriptServices.js")),
dts: files => files
.pipe(removeSourceMaps())
.pipe(prepend.file(copyright))
.pipe(convertConstEnums())
.pipe(rename("typescriptServices.d.ts"))
}),
{ aliases: [typescriptServicesDts] });
const typescriptJs = "built/local/typescript.js";
@ -179,29 +185,39 @@ gulp.task(typescriptStandaloneDts, /*help*/ false, [typescriptServicesDts], () =
// build all 'typescriptServices'-related outputs
gulp.task("services", /*help*/ false, [typescriptServicesJs, typescriptServicesDts, typescriptJs, typescriptDts, typescriptStandaloneDts]);
const useCompiler = cmdLineOptions.lkg ? "lkg" : "built";
const useCompilerDeps = cmdLineOptions.lkg ? ["lib", "generate-diagnostics"] : [typescriptServicesJs];
const tscProject = "src/tsc/tsconfig.json";
const tscJs = "built/local/tsc.js";
gulp.task(tscJs, /*help*/ false, [typescriptServicesJs], () => project.compile(tscProject, { typescript: "built" }));
gulp.task(tscJs, /*help*/ false, useCompilerDeps, () =>
project.compile(tscProject, {
typescript: useCompiler,
js: files => files.pipe(prepend.file(copyright))
}));
const tscReleaseProject = "src/tsc/tsconfig.release.json";
const tscReleaseJs = "built/local/tsc.release.js";
gulp.task(tscReleaseJs, /*help*/ false, () => project.compile(tscReleaseProject));
gulp.task(tscReleaseJs, /*help*/ false, () =>
project.compile(tscReleaseProject, {
js: files => files.pipe(prepend.file(copyright))
}));
const cancellationTokenProject = "src/cancellationToken/tsconfig.json";
const cancellationTokenJs = "built/local/cancellationToken.js";
gulp.task(cancellationTokenJs, /*help*/ false, [typescriptServicesJs], () => project.compile(cancellationTokenProject, { typescript: "built" }));
gulp.task(cancellationTokenJs, /*help*/ false, useCompilerDeps, () => project.compile(cancellationTokenProject, { typescript: useCompiler }));
const typingsInstallerProject = "src/typingsInstaller/tsconfig.json";
const typingsInstallerJs = "built/local/typingsInstaller.js";
gulp.task(typingsInstallerJs, /*help*/ false, [typescriptServicesJs], () => project.compile(typingsInstallerProject, { typescript: "built" }));
gulp.task(typingsInstallerJs, /*help*/ false, useCompilerDeps, () => project.compile(typingsInstallerProject, { typescript: useCompiler }));
const tsserverProject = "src/tsserver/tsconfig.json";
const tsserverJs = "built/local/tsserver.js";
gulp.task(tsserverJs, /*help*/ false, [typescriptServicesJs], () => project.compile(tsserverProject, { typescript: "built" }));
gulp.task(tsserverJs, /*help*/ false, useCompilerDeps, () => project.compile(tsserverProject, { typescript: useCompiler }));
const watchGuardProject = "src/watchGuard/tsconfig.json";
const watchGuardJs = "built/local/watchGuard.js";
gulp.task(watchGuardJs, /*help*/ false, [typescriptServicesJs], () => project.compile(watchGuardProject, { typescript: "built" }));
gulp.task(watchGuardJs, /*help*/ false, useCompilerDeps, () => project.compile(watchGuardProject, { typescript: useCompiler }));
const typesMapJson = "built/local/typesMap.json";
gulp.task(typesMapJson, /*help*/ false, [], () =>
@ -216,21 +232,28 @@ gulp.task(tsserverlibraryProject, /*help*/ false, () => {
project.flatten("src/tsserver/tsconfig.json", tsserverlibraryProject, {
exclude: ["src/tsserver/server.ts"],
compilerOptions: {
"removeComments": true,
"removeComments": false,
"stripInternal": true,
"outFile": "tsserverlibrary.js"
"declarationMap": false,
"outFile": "tsserverlibrary.out.js" // must align with same task in jakefile. We fix this name below.
}
});
});
const tsserverlibraryJs = "built/local/tsserverlibrary.js";
const tsserverlibraryDts = "built/local/tsserverlibrary.d.ts";
gulp.task(tsserverlibraryJs, /*help*/ false, [typescriptServicesJs, tsserverlibraryProject], () =>
gulp.task(tsserverlibraryJs, /*help*/ false, useCompilerDeps.concat([tsserverlibraryProject]), () =>
project.compile(tsserverlibraryProject, {
js: files => files
.pipe(prepend.file(copyright))
.pipe(rename("tsserverlibrary.js")),
dts: files => files
.pipe(removeSourceMaps())
.pipe(prepend.file(copyright))
.pipe(convertConstEnums())
.pipe(append("\nexport = ts;\nexport as namespace ts;")),
typescript: "built"
.pipe(append("\nexport = ts;\nexport as namespace ts;"))
.pipe(rename("tsserverlibrary.d.ts")),
typescript: useCompiler
}), { aliases: [tsserverlibraryDts] });
gulp.task(
@ -294,29 +317,29 @@ gulp.task(
// Task to build the tests infrastructure using the built compiler
const testRunnerProject = "src/testRunner/tsconfig.json";
const runJs = "built/local/run.js";
gulp.task(runJs, /*help*/ false, [typescriptServicesJs, tsserverlibraryDts], () => project.compile(testRunnerProject, { typescript: "built" }));
gulp.task(runJs, /*help*/ false, useCompilerDeps, () => project.compile(testRunnerProject, { typescript: useCompiler }));
gulp.task(
"tests",
"Builds the test infrastructure using the built compiler",
[runJs]);
[runJs, tsserverlibraryDts]);
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"],
() => runConsoleTests(runJs, "min", /*runInParallel*/ true));
["build-rules", "tests", "services", tsserverlibraryDts],
() => runConsoleTests(runJs, "min", /*runInParallel*/ true, /*watchMode*/ false));
gulp.task(
"runtests",
"Runs the tests using the built run.js file. Optional arguments are: --t[ests]=regex --r[eporter]=[list|spec|json|<more>] --d[ebug]=true --color[s]=false --lint=true.",
["build-rules", "tests"],
() => runConsoleTests(runJs, "mocha-fivemat-progress-reporter", /*runInParallel*/ false));
["build-rules", "tests", "services", tsserverlibraryDts],
() => runConsoleTests(runJs, "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false));
const webTestServerProject = "tests/webTestServer.tsconfig.json";
const webTestServerJs = "tests/webTestServer.js";
gulp.task(webTestServerJs, /*help*/ false, [typescriptServicesJs], () => project.compile(webTestServerProject, { typescript: "built" }));
gulp.task("clean:" + webTestServerJs, /*help*/ false, () => project.clean(webTestServerProject));
gulp.task(webTestServerJs, /*help*/ false, useCompilerDeps, () => project.compile(webTestServerProject, { typescript: useCompiler }));
gulp.task(`clean:${webTestServerJs}`, /*help*/ false, () => project.clean(webTestServerProject));
const bundlePath = path.resolve("built/local/bundle.js");
@ -392,8 +415,8 @@ gulp.task(
// Webhost
const webtscProject = "tests/webhost/webtsc.tsconfig.json";
const webtscJs = "tests/webhost/webtsc.js";
gulp.task(webtscJs, /*help*/ false, [typescriptServicesJs], () => project.compile(webtscProject, { typescript: "built" }));
gulp.task("clean:" + webtscJs, /*help*/ false, () => project.clean(webtscProject));
gulp.task(webtscJs, /*help*/ false, useCompilerDeps, () => project.compile(webtscProject, { typescript: useCompiler }));
gulp.task(`clean:${webtscJs}`, /*help*/ false, () => project.clean(webtscProject));
gulp.task("webhost", "Builds the tsc web host", [webtscJs], () =>
gulp.src("built/local/lib.d.ts")
@ -402,8 +425,8 @@ gulp.task("webhost", "Builds the tsc web host", [webtscJs], () =>
// Perf compiler
const perftscProject = "tests/perftsc.tsconfig.json";
const perftscJs = "built/local/perftsc.js";
gulp.task(perftscJs, /*help*/ false, [typescriptServicesJs], () => project.compile(perftscProject, { typescript: "built" }));
gulp.task("clean:" + perftscJs, /*help*/ false, () => project.clean(perftscProject));
gulp.task(perftscJs, /*help*/ false, useCompilerDeps, () => project.compile(perftscProject, { typescript: useCompiler }));
gulp.task(`clean:${perftscJs}`, /*help*/ false, () => project.clean(perftscProject));
gulp.task(
"perftsc",
@ -423,7 +446,7 @@ gulp.task(loggedIOJs, /*help*/ false, [], (done) => {
const instrumenterProject = "src/instrumenter/tsconfig.json";
const instrumenterJs = "built/local/instrumenter.js";
gulp.task(instrumenterJs, /*help*/ false, () => project.compile(instrumenterProject));
gulp.task("clean:" + instrumenterJs, /*help*/ false, () => project.clean(instrumenterProject));
gulp.task(`clean:${instrumenterJs}`, /*help*/ false, () => project.clean(instrumenterProject));
gulp.task(
"tsc-instrumented",
@ -479,20 +502,37 @@ gulp.task(
gulp.task(
"watch-tsc",
/*help*/ false,
["watch-diagnostics", "watch-lib", typescriptServicesJs],
() => project.watch(tscProject, { typescript: "built" }));
["watch-diagnostics", "watch-lib"].concat(useCompilerDeps),
() => project.watch(tscProject, { typescript: useCompiler }));
const watchServicesPatterns = [
"src/compiler/**/*",
"src/jsTypings/**/*",
"src/services/**/*"
];
gulp.task(
"watch-services",
/*help*/ false,
["watch-diagnostics", "watch-lib", typescriptServicesJs],
() => project.watch(servicesProject, { typescript: "built" }));
["watch-diagnostics", "watch-lib"],
() => gulp.watch(watchServicesPatterns, ["services"]));
const watchLsslPatterns = [
...watchServicesPatterns,
"src/server/**/*",
"src/tsserver/tsconfig.json"
];
gulp.task(
"watch-lssl",
/*help*/ false,
() => gulp.watch(watchLsslPatterns, ["lssl"]));
gulp.task(
"watch-server",
/*help*/ false,
["watch-diagnostics", "watch-lib", typescriptServicesJs],
() => project.watch(tsserverProject, { typescript: "built" }));
["watch-diagnostics", "watch-lib"].concat(useCompilerDeps),
() => project.watch(tsserverProject, { typescript: useCompiler }));
gulp.task(
"watch-local",
@ -500,22 +540,64 @@ gulp.task(
["watch-lib", "watch-tsc", "watch-services", "watch-server"]);
gulp.task(
"watch",
"Watches for changes to the build inputs for built/local/run.js executes runtests-parallel.",
[typescriptServicesJs],
() => project.watch(testRunnerProject, { typescript: "built" }, ["runtests-parallel"]));
"watch-runner",
/*help*/ false,
useCompilerDeps,
() => project.watch(testRunnerProject, { typescript: useCompiler }));
gulp.task("clean-built", /*help*/ false, ["clean:" + diagnosticInformationMapTs], () => del(["built"]));
const watchPatterns = [
runJs,
typescriptDts,
tsserverlibraryDts
];
gulp.task(
"watch",
"Watches for changes to the build inputs for built/local/run.js, then executes runtests-parallel.",
["build-rules", "watch-runner", "watch-services", "watch-lssl"],
() => {
/** @type {CancelSource | undefined} */
let runTestsSource;
const fn = debounce(() => {
runTests().catch(error => {
if (error instanceof CancelError) {
log.warn("Operation was canceled");
}
else {
log.error(error);
}
});
}, /*timeout*/ 100, { max: 500 });
gulp.watch(watchPatterns, () => project.wait().then(fn));
// NOTE: gulp.watch is far too slow when watching tests/cases/**/* as it first enumerates *every* file
const testFilePattern = /(\.ts|[\\/]tsconfig\.json)$/;
fs.watch("tests/cases", { recursive: true }, (_, file) => {
if (testFilePattern.test(file)) project.wait().then(fn);
});
function runTests() {
if (runTestsSource) runTestsSource.cancel();
runTestsSource = new CancelSource();
return cmdLineOptions.tests || cmdLineOptions.failed
? runConsoleTests(runJs, "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ true, runTestsSource.token)
: runConsoleTests(runJs, "min", /*runInParallel*/ true, /*watchMode*/ true, runTestsSource.token);
}
});
gulp.task("clean-built", /*help*/ false, [`clean:${diagnosticInformationMapTs}`], () => del(["built"]));
gulp.task(
"clean",
"Cleans the compiler output, declare files, and tests",
[
"clean:" + importDefinitelyTypedTestsJs,
"clean:" + webtscJs,
"clean:" + perftscJs,
"clean:" + instrumenterJs,
"clean:" + webTestServerJs,
"clean-scripts",
`clean:${importDefinitelyTypedTestsJs}`,
`clean:${webtscJs}`,
`clean:${perftscJs}`,
`clean:${instrumenterJs}`,
`clean:${webTestServerJs}`,
"clean:scripts",
"clean-rules",
"clean-built"
]);

View File

@ -9,6 +9,9 @@ const fold = require("travis-fold");
const ts = require("./lib/typescript");
const del = require("del");
const getDirSize = require("./scripts/build/getDirSize");
const { base64VLQFormatEncode } = require("./scripts/build/sourcemaps");
const needsUpdate = require("./scripts/build/needsUpdate");
const { flatten } = require("./scripts/build/project");
// 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;
@ -64,9 +67,14 @@ Paths.typesMapOutput = "built/local/typesMap.json";
Paths.typescriptFile = "built/local/typescript.js";
Paths.servicesFile = "built/local/typescriptServices.js";
Paths.servicesDefinitionFile = "built/local/typescriptServices.d.ts";
Paths.servicesOutFile = "built/local/typescriptServices.out.js";
Paths.servicesDefinitionOutFile = "built/local/typescriptServices.out.d.ts";
Paths.typescriptDefinitionFile = "built/local/typescript.d.ts";
Paths.typescriptStandaloneDefinitionFile = "built/local/typescript_standalone.d.ts";
Paths.tsserverLibraryFile = "built/local/tsserverlibrary.js";
Paths.tsserverLibraryDefinitionFile = "built/local/tsserverlibrary.d.ts";
Paths.tsserverLibraryOutFile = "built/local/tsserverlibrary.out.js";
Paths.tsserverLibraryDefinitionOutFile = "built/local/tsserverlibrary.out.d.ts";
Paths.baselines = {};
Paths.baselines.local = "tests/baselines/local";
Paths.baselines.localTest262 = "tests/baselines/test262/local";
@ -101,7 +109,9 @@ const ConfigFileFor = {
runjs: "src/testRunner",
lint: "scripts/tslint",
scripts: "scripts",
all: "src"
all: "src",
typescriptServices: "built/local/typescriptServices.tsconfig.json",
tsserverLibrary: "built/local/tsserverlibrary.tsconfig.json",
};
const ExpectedLKGFiles = [
@ -124,13 +134,18 @@ desc("Builds the full compiler and services");
task(TaskNames.local, [
TaskNames.buildFoldStart,
TaskNames.coreBuild,
Paths.servicesDefinitionFile,
Paths.typescriptFile,
Paths.typescriptDefinitionFile,
Paths.typescriptStandaloneDefinitionFile,
Paths.tsserverLibraryDefinitionFile,
TaskNames.localize,
TaskNames.buildFoldEnd
]);
task("default", [TaskNames.local]);
const RunTestsPrereqs = [TaskNames.lib, Paths.servicesDefinitionFile, Paths.tsserverLibraryDefinitionFile];
const RunTestsPrereqs = [TaskNames.lib, Paths.servicesDefinitionFile, Paths.typescriptDefinitionFile, Paths.tsserverLibraryDefinitionFile];
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, RunTestsPrereqs, function () {
tsbuild([ConfigFileFor.runjs], true, () => {
@ -172,6 +187,9 @@ task(TaskNames.lkg, [
TaskNames.release,
TaskNames.local,
Paths.servicesDefinitionFile,
Paths.typescriptFile,
Paths.typescriptDefinitionFile,
Paths.typescriptStandaloneDefinitionFile,
Paths.tsserverLibraryDefinitionFile,
Paths.releaseCompiler,
...libraryTargets
@ -333,64 +351,146 @@ file(Paths.diagnosticInformationMap, [Paths.diagnosticMessagesJson], function ()
});
}, { async: true });
// tsserverlibrary.d.ts
file(Paths.tsserverLibraryDefinitionFile, [TaskNames.coreBuild], function() {
const sources = ["compiler.d.ts", "jsTyping.d.ts", "services.d.ts", "server.d.ts"].map(f => path.join(Paths.builtLocal, f));
let output = "";
for (const f of sources) {
output = output + "\n" + removeConstModifierFromEnumDeclarations(readFileSync(f));
}
output = output + "\nexport = ts;\nexport as namespace ts;";
fs.writeFileSync(Paths.tsserverLibraryDefinitionFile, output, { encoding: "utf-8" });
file(ConfigFileFor.tsserverLibrary, [], function () {
flatten("src/tsserver/tsconfig.json", ConfigFileFor.tsserverLibrary, {
exclude: ["src/tsserver/server.ts"],
compilerOptions: {
"removeComments": false,
"stripInternal": true,
"declarationMap": false,
"outFile": "tsserverlibrary.out.js"
}
})
});
// typescriptservices.d.ts
file(Paths.servicesDefinitionFile, [TaskNames.coreBuild], function() {
// Generate a config file
const files = [];
recur(`src/services/tsconfig.json`);
// tsserverlibrary.js
// tsserverlibrary.d.ts
file(Paths.tsserverLibraryFile, [TaskNames.coreBuild, ConfigFileFor.tsserverLibrary], function() {
tsbuild(ConfigFileFor.tsserverLibrary, false, () => {
if (needsUpdate([Paths.tsserverLibraryOutFile, Paths.tsserverLibraryDefinitionOutFile], [Paths.tsserverLibraryFile, Paths.tsserverLibraryDefinitionFile])) {
const copyright = readFileSync(Paths.copyright);
const config = {
extends: "../../src/tsconfig-base",
let libraryDefinitionContent = readFileSync(Paths.tsserverLibraryDefinitionOutFile);
libraryDefinitionContent = copyright + removeConstModifierFromEnumDeclarations(libraryDefinitionContent);
libraryDefinitionContent += "\nexport = ts;\nexport as namespace ts;";
fs.writeFileSync(Paths.tsserverLibraryDefinitionFile, libraryDefinitionContent, "utf8");
let libraryContent = readFileSync(Paths.tsserverLibraryOutFile);
libraryContent = copyright + libraryContent;
fs.writeFileSync(Paths.tsserverLibraryFile, libraryContent, "utf8");
// adjust source map for tsserverlibrary.js
let libraryMapContent = readFileSync(Paths.tsserverLibraryOutFile + ".map");
const map = JSON.parse(libraryMapContent);
const lineStarts = /**@type {*}*/(ts).computeLineStarts(copyright);
let prependMappings = "";
for (let i = 1; i < lineStarts.length; i++) {
prependMappings += ";";
}
const offset = copyright.length - lineStarts[lineStarts.length - 1];
if (offset > 0) {
prependMappings += base64VLQFormatEncode(offset) + ",";
}
const outputMap = {
version: map.version,
file: map.file,
sources: map.sources,
sourceRoot: map.sourceRoot,
mappings: prependMappings + map.mappings,
names: map.names,
sourcesContent: map.sourcesContent
};
libraryMapContent = JSON.stringify(outputMap);
fs.writeFileSync(Paths.tsserverLibraryFile + ".map", libraryMapContent);
}
complete();
});
}, { async: true });
task(Paths.tsserverLibraryDefinitionFile, [Paths.tsserverLibraryFile]);
file(ConfigFileFor.typescriptServices, [], function () {
flatten("src/services/tsconfig.json", ConfigFileFor.typescriptServices, {
compilerOptions: {
"removeComments": false,
"stripInternal": true,
"outFile": "typescriptServices.js"
},
files
};
"declarationMap": false,
"outFile": "typescriptServices.out.js"
}
});
});
const configFilePath = `built/local/typescriptServices.tsconfig.json`;
fs.writeFileSync(configFilePath, JSON.stringify(config, undefined, 2));
tsbuild(configFilePath, false, () => {
const servicesContent = readFileSync(Paths.servicesDefinitionFile);
const servicesContentWithoutConstEnums = removeConstModifierFromEnumDeclarations(servicesContent);
fs.writeFileSync(Paths.servicesDefinitionFile, servicesContentWithoutConstEnums);
// Also build typescript.js, typescript.js.map, and typescript.d.ts
// typescriptServices.js
// typescriptServices.d.ts
file(Paths.servicesFile, [TaskNames.coreBuild, ConfigFileFor.typescriptServices], function() {
tsbuild(ConfigFileFor.typescriptServices, false, () => {
if (needsUpdate([Paths.servicesOutFile, Paths.servicesDefinitionOutFile], [Paths.servicesFile, Paths.servicesDefinitionFile])) {
const copyright = readFileSync(Paths.copyright);
let servicesDefinitionContent = readFileSync(Paths.servicesDefinitionOutFile);
servicesDefinitionContent = copyright + removeConstModifierFromEnumDeclarations(servicesDefinitionContent);
fs.writeFileSync(Paths.servicesDefinitionFile, servicesDefinitionContent, "utf8");
let servicesContent = readFileSync(Paths.servicesOutFile);
servicesContent = copyright + servicesContent;
fs.writeFileSync(Paths.servicesFile, servicesContent, "utf8");
// adjust source map for typescriptServices.js
let servicesMapContent = readFileSync(Paths.servicesOutFile + ".map");
const map = JSON.parse(servicesMapContent);
const lineStarts = /**@type {*}*/(ts).computeLineStarts(copyright);
let prependMappings = "";
for (let i = 1; i < lineStarts.length; i++) {
prependMappings += ";";
}
const offset = copyright.length - lineStarts[lineStarts.length - 1];
if (offset > 0) {
prependMappings += base64VLQFormatEncode(offset) + ",";
}
const outputMap = {
version: map.version,
file: map.file,
sources: map.sources,
sourceRoot: map.sourceRoot,
mappings: prependMappings + map.mappings,
names: map.names,
sourcesContent: map.sourcesContent
};
servicesMapContent = JSON.stringify(outputMap);
fs.writeFileSync(Paths.servicesFile + ".map", servicesMapContent);
}
complete();
});
}, { async: true });
task(Paths.servicesDefinitionFile, [Paths.servicesFile]);
// typescript.js
// typescript.d.ts
file(Paths.typescriptFile, [Paths.servicesFile], function() {
if (needsUpdate([Paths.servicesFile, Paths.servicesDefinitionFile], [Paths.typescriptFile, Paths.typescriptDefinitionFile])) {
jake.cpR(Paths.servicesFile, Paths.typescriptFile);
if (fs.existsSync(Paths.servicesFile + ".map")) {
jake.cpR(Paths.servicesFile + ".map", Paths.typescriptFile + ".map");
}
fs.writeFileSync(Paths.typescriptDefinitionFile, servicesContentWithoutConstEnums + "\r\nexport = ts", { encoding: "utf-8" });
// And typescript_standalone.d.ts
fs.writeFileSync(Paths.typescriptStandaloneDefinitionFile, servicesContentWithoutConstEnums.replace(/declare (namespace|module) ts(\..+)? \{/g, 'declare module "typescript" {'), { encoding: "utf-8"});
complete();
});
function recur(configPath) {
const cfgFile = readJson(configPath);
if (cfgFile.references) {
for (const ref of cfgFile.references) {
recur(path.join(path.dirname(configPath), ref.path, "tsconfig.json"));
}
}
for (const file of cfgFile.files) {
files.push(path.join(`../../`, path.dirname(configPath), file));
}
const content = readFileSync(Paths.servicesDefinitionFile);
fs.writeFileSync(Paths.typescriptDefinitionFile, content + "\r\nexport = ts;", { encoding: "utf-8" });
}
}, { async: true });
});
task(Paths.typescriptDefinitionFile, [Paths.typescriptFile]);
// typescript_standalone.d.ts
file(Paths.typescriptStandaloneDefinitionFile, [Paths.servicesDefinitionFile], function() {
if (needsUpdate(Paths.servicesDefinitionFile, Paths.typescriptStandaloneDefinitionFile)) {
const content = readFileSync(Paths.servicesDefinitionFile);
fs.writeFileSync(Paths.typescriptStandaloneDefinitionFile, content.replace(/declare (namespace|module) ts(\..+)? \{/g, 'declare module "typescript" {'), { encoding: "utf-8"});
}
});
function getLibraryTargets() {
/** @type {{ libs: string[], paths?: Record<string, string>, sources?: Record<string, string[]> }} */
@ -765,4 +865,4 @@ function getDiffTool() {
*/
function removeConstModifierFromEnumDeclarations(text) {
return text.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, '$1$2enum $3 {$4');
}
}

View File

@ -1,120 +1,34 @@
// @ts-check
const Browserify = require("browserify");
const Vinyl = require("vinyl");
const fs = require("fs");
const path = require("path");
const convertMap = require("convert-source-map");
const applySourceMap = require("vinyl-sourcemaps-apply");
const { Transform, Readable } = require("stream");
const browserify = require("browserify");
const Vinyl = require("./vinyl");
const { Transform } = require("stream");
const { streamFromFile } = require("./utils");
const { replaceContents } = require("./sourcemaps");
module.exports = browserify;
module.exports = browserifyFile;
/**
* @param {import("browserify").Options} [opts]
*/
function browserify(opts) {
function browserifyFile(opts) {
return new Transform({
objectMode: true,
/**
* @param {string | Buffer | File} input
* @param {string | Buffer | Vinyl} input
*/
transform(input, _, cb) {
if (typeof input === "string" || Buffer.isBuffer(input)) return cb(new Error("Only Vinyl files are supported."));
try {
const sourceMap = input.sourceMap;
const cwd = input.cwd || process.cwd();
const base = input.base || cwd;
const output = /**@type {File}*/(new Vinyl({ path: input.path, base: input.base }));
const stream = streamFromFile(input);
const b = new Browserify(Object.assign({}, opts, { debug: !!sourceMap, basedir: input.base }));
b.add(stream, { file: input.path, basedir: input.base });
b.bundle((err, contents) => {
if (err) return cb(err);
output.contents = contents;
if (sourceMap) {
output.sourceMap = typeof sourceMap === "string" ? JSON.parse(sourceMap) : sourceMap;
const sourceRoot = output.sourceMap.sourceRoot;
makeAbsoluteSourceMap(cwd, base, output.sourceMap);
const stringContents = contents.toString("utf8");
const newSourceMapConverter = convertMap.fromSource(stringContents);
if (newSourceMapConverter) {
const newSourceMap = newSourceMapConverter.toObject();
makeAbsoluteSourceMap(cwd, base, newSourceMap);
applySourceMap(output, newSourceMap);
makeRelativeSourceMap(cwd, base, sourceRoot, output.sourceMap);
output.contents = new Buffer(convertMap.removeComments(stringContents), "utf8");
}
}
cb(null, output);
});
browserify(Object.assign({}, opts, { debug: !!input.sourceMap, basedir: input.base }))
.add(streamFromFile(input), { file: input.path, basedir: input.base })
.bundle((err, contents) => {
if (err) return cb(err);
cb(null, replaceContents(input, contents));
});
}
catch (e) {
cb(e);
}
}
});
}
/**
* @param {string | undefined} cwd
* @param {string | undefined} base
* @param {RawSourceMap} sourceMap
*
* @typedef RawSourceMap
* @property {string} version
* @property {string} file
* @property {string} [sourceRoot]
* @property {string[]} sources
* @property {string[]} [sourcesContents]
* @property {string} mappings
* @property {string[]} [names]
*/
function makeAbsoluteSourceMap(cwd = process.cwd(), base = "", sourceMap) {
const sourceRoot = sourceMap.sourceRoot || "";
const resolvedBase = path.resolve(cwd, base);
const resolvedSourceRoot = path.resolve(resolvedBase, sourceRoot);
sourceMap.file = path.resolve(resolvedBase, sourceMap.file).replace(/\\/g, "/");
sourceMap.sources = sourceMap.sources.map(source => path.resolve(resolvedSourceRoot, source).replace(/\\/g, "/"));
sourceMap.sourceRoot = "";
}
/**
* @param {string | undefined} cwd
* @param {string | undefined} base
* @param {string} sourceRoot
* @param {RawSourceMap} sourceMap
*/
function makeRelativeSourceMap(cwd = process.cwd(), base = "", sourceRoot, sourceMap) {
makeAbsoluteSourceMap(cwd, base, sourceMap);
const resolvedBase = path.resolve(cwd, base);
const resolvedSourceRoot = path.resolve(resolvedBase, sourceRoot);
sourceMap.file = path.relative(resolvedBase, sourceMap.file).replace(/\\/g, "/");
sourceMap.sources = sourceMap.sources.map(source => path.relative(resolvedSourceRoot, source).replace(/\\/g, "/"));
sourceMap.sourceRoot = sourceRoot;
}
/**
* @param {File} file
*/
function streamFromFile(file) {
return file.isBuffer() ? streamFromBuffer(file.contents) :
file.isStream() ? file.contents :
fs.createReadStream(file.path, { autoClose: true });
}
/**
* @param {Buffer} buffer
*/
function streamFromBuffer(buffer) {
return new Readable({
read() {
this.push(buffer);
this.push(null);
}
});
}
/**
* @typedef {import("vinyl") & { sourceMap?: any }} File
*/
void 0;
}

View File

@ -0,0 +1,71 @@
// @ts-check
const symSource = Symbol("CancelToken.source");
const symToken = Symbol("CancelSource.token");
const symCancellationRequested = Symbol("CancelSource.cancellationRequested");
const symCancellationCallbacks = Symbol("CancelSource.cancellationCallbacks");
class CancelSource {
constructor() {
this[symCancellationRequested] = false;
this[symCancellationCallbacks] = [];
}
/** @type {CancelToken} */
get token() {
return this[symToken] || (this[symToken] = new CancelToken(this));
}
cancel() {
if (!this[symCancellationRequested]) {
this[symCancellationRequested] = true;
for (const callback of this[symCancellationCallbacks]) {
callback();
}
}
}
}
exports.CancelSource = CancelSource;
class CancelToken {
/**
* @param {CancelSource} source
*/
constructor(source) {
if (source[symToken]) return source[symToken];
this[symSource] = source;
}
/** @type {boolean} */
get cancellationRequested() {
return this[symSource][symCancellationRequested];
}
/**
* @param {() => void} callback
*/
subscribe(callback) {
const source = this[symSource];
if (source[symCancellationRequested]) {
callback();
return;
}
source[symCancellationCallbacks].push(callback);
return {
unsubscribe() {
const index = source[symCancellationCallbacks].indexOf(callback);
if (index !== -1) source[symCancellationCallbacks].splice(index, 1);
}
};
}
}
exports.CancelToken = CancelToken;
class CancelError extends Error {
constructor(message = "Operation was canceled") {
super(message);
this.name = "CancelError";
}
}
exports.CancelError = CancelError;

31
scripts/build/debounce.js Normal file
View File

@ -0,0 +1,31 @@
// @ts-check
module.exports = debounce;
/**
* @param {() => void} cb
* @param {number} timeout
* @param {DebounceOptions} [opts]
*
* @typedef DebounceOptions
* @property {number} [max]
*/
function debounce(cb, timeout, opts = {}) {
if (timeout < 10) timeout = 10;
let max = opts.max || 10;
if (max < timeout) max = timeout;
let minTimer;
let maxTimer;
return trigger;
function trigger() {
if (max > timeout && !maxTimer) maxTimer = setTimeout(done, max);
if (minTimer) clearTimeout(minTimer);
minTimer = setTimeout(done, timeout);
}
function done() {
if (maxTimer) maxTimer = void clearTimeout(maxTimer);
if (minTimer) minTimer = void clearTimeout(minTimer);
cb();
}
}

View File

@ -3,6 +3,7 @@ const cp = require("child_process");
const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util)
const isWin = /^win/.test(process.platform);
const chalk = require("./chalk");
const { CancelToken, CancelError } = require("./cancellation");
module.exports = exec;
@ -10,8 +11,11 @@ module.exports = exec;
* Executes the provided command once with the supplied arguments.
* @param {string} cmd
* @param {string[]} args
* @param {object} [options]
* @param {boolean} [options.ignoreExitCode]
* @param {ExecOptions} [options]
*
* @typedef ExecOptions
* @property {boolean} [ignoreExitCode]
* @property {CancelToken} [cancelToken]
*/
function exec(cmd, args, options = {}) {
return /**@type {Promise<{exitCode: number}>}*/(new Promise((resolve, reject) => {
@ -20,7 +24,13 @@ function exec(cmd, args, options = {}) {
const subshellFlag = isWin ? "/c" : "-c";
const command = isWin ? [possiblyQuote(cmd), ...args] : [`${cmd} ${args.join(" ")}`];
const ex = cp.spawn(isWin ? "cmd" : "/bin/sh", [subshellFlag, ...command], { stdio: "inherit", windowsVerbatimArguments: true });
const subscription = options.cancelToken && options.cancelToken.subscribe(() => {
ex.kill("SIGINT");
ex.kill("SIGTERM");
reject(new CancelError());
});
ex.on("exit", exitCode => {
subscription && subscription.unsubscribe();
if (exitCode === 0 || options.ignoreExitCode) {
resolve({ exitCode });
}
@ -28,7 +38,10 @@ function exec(cmd, args, options = {}) {
reject(new Error(`Process exited with code: ${exitCode}`));
}
});
ex.on("error", reject);
ex.on("error", error => {
subscription && subscription.unsubscribe();
reject(error);
});
}));
}

View File

@ -4,7 +4,7 @@ const os = require("os");
/** @type {CommandLineOptions} */
module.exports = minimist(process.argv.slice(2), {
boolean: ["debug", "inspect", "light", "colors", "lint", "soft", "fix", "failed", "keepFailed"],
boolean: ["debug", "dirty", "inspect", "light", "colors", "lint", "lkg", "soft", "fix", "failed", "keepFailed"],
string: ["browser", "tests", "host", "reporter", "stackTraceLimit", "timeout"],
alias: {
"b": "browser",
@ -33,17 +33,21 @@ module.exports = minimist(process.argv.slice(2), {
fix: process.env.fix || process.env.f,
workers: process.env.workerCount || os.cpus().length,
failed: false,
keepFailed: false
keepFailed: false,
lkg: false,
dirty: false
}
});
/**
* @typedef TypedOptions
* @property {boolean} debug
* @property {boolean} dirty
* @property {boolean} inspect
* @property {boolean} light
* @property {boolean} colors
* @property {boolean} lint
* @property {boolean} lkg
* @property {boolean} soft
* @property {boolean} fix
* @property {string} browser
@ -56,7 +60,7 @@ module.exports = minimist(process.argv.slice(2), {
* @property {string|number} timeout
* @property {boolean} failed
* @property {boolean} keepFailed
*
*
* @typedef {import("minimist").ParsedArgs & TypedOptions} CommandLineOptions
*/
void 0;

66
scripts/build/prepend.js Normal file
View File

@ -0,0 +1,66 @@
// @ts-check
const stream = require("stream");
const Vinyl = require("./vinyl");
const ts = require("../../lib/typescript");
const fs = require("fs");
const { base64VLQFormatEncode } = require("./sourcemaps");
module.exports = exports = prepend;
/**
* @param {string | ((file: Vinyl) => string)} data
*/
function prepend(data) {
return new stream.Transform({
objectMode: true,
/**
* @param {string | Buffer | Vinyl} input
* @param {(error: Error, data?: any) => void} cb
*/
transform(input, _, cb) {
if (typeof input === "string" || Buffer.isBuffer(input)) return cb(new Error("Only Vinyl files are supported."));
if (!input.isBuffer()) return cb(new Error("Streams not supported."));
try {
const output = input.clone();
const prependContent = typeof data === "function" ? data(input) : data;
output.contents = Buffer.concat([Buffer.from(prependContent, "utf8"), input.contents]);
if (input.sourceMap) {
if (typeof input.sourceMap === "string") input.sourceMap = /**@type {import("./sourcemaps").RawSourceMap}*/(JSON.parse(input.sourceMap));
const lineStarts = /**@type {*}*/(ts).computeLineStarts(prependContent);
let prependMappings = "";
for (let i = 1; i < lineStarts.length; i++) {
prependMappings += ";";
}
const offset = prependContent.length - lineStarts[lineStarts.length - 1];
if (offset > 0) {
prependMappings += base64VLQFormatEncode(offset) + ",";
}
output.sourceMap = {
version: input.sourceMap.version,
file: input.sourceMap.file,
sources: input.sourceMap.sources,
sourceRoot: input.sourceMap.sourceRoot,
mappings: prependMappings + input.sourceMap.mappings,
names: input.names,
sourcesContent: input.sourcesContent
};
}
return cb(null, output);
}
catch (e) {
return cb(e);
}
}
})
}
exports.prepend = prepend;
/**
* @param {string | ((file: Vinyl) => string)} file
*/
function prependFile(file) {
const data = typeof file === "string" ? fs.readFileSync(file, "utf8") :
vinyl => fs.readFileSync(file(vinyl), "utf8");
return prepend(data)
}
exports.file = prependFile;

View File

@ -209,6 +209,27 @@ function flatten(projectSpec, flattenedProjectSpec, options = {}) {
}
exports.flatten = flatten;
/**
* Returns a Promise that resolves when all pending build tasks have completed
*/
function wait() {
return new Promise(resolve => {
if (compilationGulp.allDone()) {
resolve();
}
else {
const onDone = () => {
compilationGulp.removeListener("onDone", onDone);
compilationGulp.removeListener("err", onDone);
resolve();
};
compilationGulp.on("stop", onDone);
compilationGulp.on("err", onDone);
}
});
}
exports.wait = wait;
/**
* Resolve a TypeScript specifier into a fully-qualified module specifier and any requisite dependencies.
* @param {string} typescript An unresolved module specifier to a TypeScript version.

143
scripts/build/sourcemaps.js Normal file
View File

@ -0,0 +1,143 @@
// @ts-check
const path = require("path");
const Vinyl = require("./vinyl");
const convertMap = require("convert-source-map");
const applySourceMap = require("vinyl-sourcemaps-apply");
const through2 = require("through2");
/**
* @param {Vinyl} input
* @param {string | Buffer} contents
* @param {string | RawSourceMap} [sourceMap]
*/
function replaceContents(input, contents, sourceMap) {
const output = input.clone();
output.contents = typeof contents === "string" ? Buffer.from(contents, "utf8") : contents;
if (input.sourceMap) {
output.sourceMap = typeof input.sourceMap === "string" ? /**@type {RawSourceMap}*/(JSON.parse(input.sourceMap)) : input.sourceMap;
if (typeof sourceMap === "string") {
sourceMap = /**@type {RawSourceMap}*/(JSON.parse(sourceMap));
}
else if (sourceMap === undefined) {
const stringContents = typeof contents === "string" ? contents : contents.toString("utf8");
const newSourceMapConverter = convertMap.fromSource(stringContents);
if (newSourceMapConverter) {
sourceMap = /**@type {RawSourceMap}*/(newSourceMapConverter.toObject());
output.contents = new Buffer(convertMap.removeMapFileComments(stringContents), "utf8");
}
}
if (sourceMap) {
const cwd = input.cwd || process.cwd();
const base = input.base || cwd;
const sourceRoot = output.sourceMap.sourceRoot;
makeAbsoluteSourceMap(cwd, base, output.sourceMap);
makeAbsoluteSourceMap(cwd, base, sourceMap);
applySourceMap(output, sourceMap);
makeRelativeSourceMap(cwd, base, sourceRoot, output.sourceMap);
}
else {
output.sourceMap = undefined;
}
}
return output;
}
exports.replaceContents = replaceContents;
function removeSourceMaps() {
return through2.obj((/**@type {Vinyl}*/file, _, cb) => {
if (file.sourceMap && file.isBuffer()) {
file.contents = Buffer.from(convertMap.removeMapFileComments(file.contents.toString("utf8")), "utf8");
file.sourceMap = undefined;
}
cb(null, file);
});
}
exports.removeSourceMaps = removeSourceMaps;
/**
* @param {string | undefined} cwd
* @param {string | undefined} base
* @param {RawSourceMap} sourceMap
*
* @typedef RawSourceMap
* @property {string} version
* @property {string} file
* @property {string} [sourceRoot]
* @property {string[]} sources
* @property {string[]} [sourcesContent]
* @property {string} mappings
* @property {string[]} [names]
*/
function makeAbsoluteSourceMap(cwd = process.cwd(), base = "", sourceMap) {
const sourceRoot = sourceMap.sourceRoot || "";
const resolvedBase = path.resolve(cwd, base);
const resolvedSourceRoot = path.resolve(resolvedBase, sourceRoot);
sourceMap.file = path.resolve(resolvedBase, sourceMap.file).replace(/\\/g, "/");
sourceMap.sources = sourceMap.sources.map(source => path.resolve(resolvedSourceRoot, source).replace(/\\/g, "/"));
sourceMap.sourceRoot = "";
}
exports.makeAbsoluteSourceMap = makeAbsoluteSourceMap;
/**
* @param {string | undefined} cwd
* @param {string | undefined} base
* @param {string} sourceRoot
* @param {RawSourceMap} sourceMap
*/
function makeRelativeSourceMap(cwd = process.cwd(), base = "", sourceRoot, sourceMap) {
makeAbsoluteSourceMap(cwd, base, sourceMap);
const resolvedBase = path.resolve(cwd, base);
const resolvedSourceRoot = path.resolve(resolvedBase, sourceRoot);
sourceMap.file = path.relative(resolvedBase, sourceMap.file).replace(/\\/g, "/");
sourceMap.sources = sourceMap.sources.map(source => path.relative(resolvedSourceRoot, source).replace(/\\/g, "/"));
sourceMap.sourceRoot = sourceRoot;
}
exports.makeRelativeSourceMap = makeRelativeSourceMap;
/**
* @param {string} message
* @returns {never}
*/
function fail(message) {
throw new Error(message);
}
/**
* @param {number} value
*/
function base64FormatEncode(value) {
return value < 0 ? fail("Invalid value") :
value < 26 ? 0x41 /*A*/ + value :
value < 52 ? 0x61 /*a*/ + value - 26 :
value < 62 ? 0x30 /*0*/ + value - 52 :
value === 62 ? 0x2B /*+*/ :
value === 63 ? 0x2F /*/*/ :
fail("Invalid value");
}
/**
* @param {number} value
*/
function base64VLQFormatEncode(value) {
if (value < 0) {
value = ((-value) << 1) + 1;
}
else {
value = value << 1;
}
// Encode 5 bits at a time starting from least significant bits
let result = "";
do {
let currentDigit = value & 31; // 11111
value = value >> 5;
if (value > 0) {
// There are still more digits to decode, set the msb (6th bit)
currentDigit = currentDigit | 32;
}
result += String.fromCharCode(base64FormatEncode(currentDigit));
} while (value > 0);
return result;
}
exports.base64VLQFormatEncode = base64VLQFormatEncode;

View File

@ -1,4 +1,5 @@
// @ts-check
const gulp = require("./gulp");
const del = require("del");
const fs = require("fs");
const os = require("os");
@ -6,13 +7,8 @@ const path = require("path");
const mkdirP = require("./mkdirp");
const cmdLineOptions = require("./options");
const exec = require("./exec");
const runSequence = require("run-sequence");
const finished = require("./finished");
const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util)
const nodeModulesPathPrefix = path.resolve("./node_modules/.bin/");
const isWin = /^win/.test(process.platform);
const mocha = path.join(nodeModulesPathPrefix, "mocha") + (isWin ? ".cmd" : "");
const mochaJs = require.resolve("mocha/bin/_mocha");
exports.localBaseline = "tests/baselines/local/";
exports.refBaseline = "tests/baselines/reference/";
@ -24,8 +20,10 @@ exports.localTest262Baseline = "internal/baselines/test262/local";
* @param {string} runJs
* @param {string} defaultReporter
* @param {boolean} runInParallel
* @param {boolean} watchMode
* @param {InstanceType<typeof import("./cancellation").CancelToken>} [cancelToken]
*/
function runConsoleTests(runJs, defaultReporter, runInParallel) {
async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode, cancelToken) {
let testTimeout = cmdLineOptions.timeout;
let tests = cmdLineOptions.tests;
const lintFlag = cmdLineOptions.lint;
@ -36,112 +34,116 @@ function runConsoleTests(runJs, defaultReporter, runInParallel) {
const stackTraceLimit = cmdLineOptions.stackTraceLimit;
const testConfigFile = "test.config";
const failed = cmdLineOptions.failed;
const keepFailed = cmdLineOptions.keepFailed || failed;
return cleanTestDirs()
.then(() => {
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 = cmdLineOptions.workers;
}
if (tests && tests.toLocaleLowerCase() === "rwc") {
testTimeout = 400000;
}
if (tests || runners || light || testTimeout || taskConfigsFolder || keepFailed) {
writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, testTimeout, keepFailed);
}
const colors = cmdLineOptions.colors;
const reporter = cmdLineOptions.reporter || defaultReporter;
/** @type {string} */
let host = "node";
/** @type {string[]} */
let args = [];
// 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) {
args.push("-R", "scripts/failed-tests");
args.push("-O", '"reporter=' + reporter + (keepFailed ? ",keepFailed=true" : "") + '"');
if (tests) {
args.push("-g", `"${tests}"`);
}
if (colors) {
args.push("--colors");
}
else {
args.push("--no-colors");
}
if (inspect) {
args.unshift("--inspect-brk");
}
else if (debug) {
args.unshift("--debug-brk");
}
else {
args.push("-t", "" + testTimeout);
}
args.push(runJs);
host = mocha;
}
else {
// run task to load all tests and partition them between workers
host = "node";
args.push(runJs);
}
setNodeEnvToDevelopment();
if (failed) {
return exec(host, ["scripts/run-failed-tests.js"].concat(args));
}
else {
return exec(host, args);
}
})
.then(({ exitCode }) => {
if (exitCode !== 0) return finish(undefined, exitCode);
if (lintFlag) return finished(runSequence("lint")).then(() => finish(), finish);
return finish();
}, finish);
/**
* @param {any=} error
* @param {number=} errorStatus
*/
function finish(error, errorStatus) {
restoreSavedNodeEnv();
return deleteTestConfig()
.then(deleteTemporaryProjectOutput)
.then(() => {
if (error !== undefined || errorStatus !== undefined) {
process.exit(typeof errorStatus === "number" ? errorStatus : 2);
}
});
const keepFailed = cmdLineOptions.keepFailed;
if (!cmdLineOptions.dirty) {
await cleanTestDirs();
}
function deleteTestConfig() {
return del("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 = cmdLineOptions.workers;
}
if (tests && tests.toLocaleLowerCase() === "rwc") {
testTimeout = 400000;
}
if (tests || runners || light || testTimeout || taskConfigsFolder || keepFailed) {
writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, testTimeout, keepFailed);
}
const colors = cmdLineOptions.colors;
const reporter = cmdLineOptions.reporter || defaultReporter;
/** @type {string[]} */
let args = [];
// 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) {
args.push(failed ? "scripts/run-failed-tests.js" : mochaJs);
args.push("-R", "scripts/failed-tests");
args.push("-O", '"reporter=' + reporter + (keepFailed ? ",keepFailed=true" : "") + '"');
if (tests) {
args.push("-g", `"${tests}"`);
}
if (colors) {
args.push("--colors");
}
else {
args.push("--no-colors");
}
if (inspect) {
args.unshift("--inspect-brk");
}
else if (debug) {
args.unshift("--debug-brk");
}
else {
args.push("-t", "" + testTimeout);
}
args.push(runJs);
}
else {
// run task to load all tests and partition them between workers
args.push(runJs);
}
/** @type {number | undefined} */
let errorStatus;
/** @type {Error | undefined} */
let error;
try {
setNodeEnvToDevelopment();
const { exitCode } = await exec("node", args, { cancelToken });
if (exitCode !== 0) {
errorStatus = exitCode;
error = new Error(`Process exited with status code ${errorStatus}.`);
}
else if (lintFlag) {
await new Promise((resolve, reject) => gulp.start(["lint"], error => error ? reject(error) : resolve()));
}
}
catch (e) {
errorStatus = undefined;
error = e;
}
finally {
restoreSavedNodeEnv();
}
await del("test.config");
await deleteTemporaryProjectOutput();
if (error !== undefined) {
if (watchMode) {
throw error;
}
else {
log.error(error);
process.exit(typeof errorStatus === "number" ? errorStatus : 2);
}
}
}
exports.runConsoleTests = runConsoleTests;
function cleanTestDirs() {
return del([exports.localBaseline, exports.localRwcBaseline,])
return del([exports.localBaseline, exports.localRwcBaseline])
.then(() => mkdirP(exports.localRwcBaseline))
.then(() => mkdirP(exports.localBaseline));
}

27
scripts/build/utils.js Normal file
View File

@ -0,0 +1,27 @@
// @ts-check
const fs = require("fs");
const File = require("./vinyl");
const { Readable } = require("stream");
/**
* @param {File} file
*/
function streamFromFile(file) {
return file.isBuffer() ? streamFromBuffer(file.contents) :
file.isStream() ? file.contents :
fs.createReadStream(file.path, { autoClose: true });
}
exports.streamFromFile = streamFromFile;
/**
* @param {Buffer} buffer
*/
function streamFromBuffer(buffer) {
return new Readable({
read() {
this.push(buffer);
this.push(null);
}
});
}
exports.streamFromBuffer = streamFromBuffer;

60
scripts/build/vinyl.d.ts vendored Normal file
View File

@ -0,0 +1,60 @@
// NOTE: This makes it possible to correctly type vinyl Files under @ts-check.
export = File;
declare class File<T extends File.Contents = File.Contents> {
constructor(options?: File.VinylOptions<T>);
cwd: string;
base: string;
path: string;
readonly history: ReadonlyArray<string>;
contents: T;
relative: string;
dirname: string;
basename: string;
stem: string;
extname: string;
symlink: string | null;
stat: import("fs").Stats | null;
sourceMap?: import("./sourcemaps").RawSourceMap | string;
[custom: string]: any;
isBuffer(): this is T extends Buffer ? File<Buffer> : never;
isStream(): this is T extends NodeJS.ReadableStream ? File<NodeJS.ReadableStream> : never;
isNull(): this is T extends null ? File<null> : never;
isDirectory(): this is T extends null ? File.Directory : never;
isSymbolic(): this is T extends null ? File.Symbolic : never;
clone(opts?: { contents?: boolean, deep?: boolean }): this;
}
namespace File {
export interface VinylOptions<T extends Contents = Contents> {
cwd?: string;
base?: string;
path?: string;
history?: ReadonlyArray<string>;
stat?: import("fs").Stats;
contents?: T;
sourceMap?: import("./sourcemaps").RawSourceMap | string;
[custom: string]: any;
}
export type Contents = Buffer | NodeJS.ReadableStream | null;
export type File = import("./vinyl");
export type NullFile = File<null>;
export type BufferFile = File<Buffer>;
export type StreamFile = File<NodeJS.ReadableStream>;
export interface Directory extends NullFile {
isNull(): true;
isDirectory(): true;
isSymbolic(): this is never;
}
export interface Symbolic extends NullFile {
isNull(): true;
isDirectory(): this is never;
isSymbolic(): true;
}
}

1
scripts/build/vinyl.js Normal file
View File

@ -0,0 +1 @@
module.exports = require("vinyl");

View File

@ -4,9 +4,11 @@ const path = require("path");
const fs = require("fs");
const os = require("os");
const failingHookRegExp = /^(.*) "(before|after) (all|each)" hook$/;
/**
* .failed-tests reporter
*
*
* @typedef {Object} ReporterOptions
* @property {string} [file]
* @property {boolean} [keepFailed]
@ -15,7 +17,7 @@ const os = require("os");
*/
class FailedTestsReporter extends Mocha.reporters.Base {
/**
* @param {Mocha.Runner} runner
* @param {Mocha.Runner} runner
* @param {{ reporterOptions?: ReporterOptions }} [options]
*/
constructor(runner, options) {
@ -49,35 +51,58 @@ class FailedTestsReporter extends Mocha.reporters.Base {
/** @type {Mocha.Test[]} */
this.passes = [];
/** @type {Mocha.Test[]} */
/** @type {(Mocha.Test)[]} */
this.failures = [];
runner.on("pass", test => this.passes.push(test));
runner.on("fail", test => this.failures.push(test));
}
/**
* @param {string} file
* @param {ReadonlyArray<Mocha.Test>} passes
* @param {ReadonlyArray<Mocha.Test>} failures
* @param {boolean} keepFailed
* @param {(err?: NodeJS.ErrnoException) => void} done
* @param {string} file
* @param {ReadonlyArray<Mocha.Test>} passes
* @param {ReadonlyArray<Mocha.Test | Mocha.Hook>} failures
* @param {boolean} keepFailed
* @param {(err?: NodeJS.ErrnoException) => void} done
*/
static writeFailures(file, passes, failures, keepFailed, done) {
const failingTests = new Set(fs.existsSync(file) ? readTests() : undefined);
if (failingTests.size > 0) {
const possiblyPassingSuites = /**@type {Set<string>}*/(new Set());
// Remove tests that are now passing and track suites that are now
// possibly passing.
if (failingTests.size > 0 && !keepFailed) {
for (const test of passes) {
const title = test.fullTitle().trim();
if (title) failingTests.delete(title);
failingTests.delete(test.fullTitle().trim());
possiblyPassingSuites.add(test.parent.fullTitle().trim());
}
}
// Add tests that are now failing. If a hook failed, track its
// containing suite as failing. If the suite for a test or hook was
// possibly passing then it is now definitely failing.
for (const test of failures) {
const title = test.fullTitle().trim();
if (title) failingTests.add(title);
const suiteTitle = test.parent.fullTitle().trim();
if (test.type === "test") {
failingTests.add(test.fullTitle().trim());
}
else {
failingTests.add(suiteTitle);
}
possiblyPassingSuites.delete(suiteTitle);
}
// Remove all definitely passing suites.
for (const suite of possiblyPassingSuites) {
failingTests.delete(suite);
}
if (failingTests.size > 0) {
const failed = Array.from(failingTests).join(os.EOL);
const failed = Array
.from(failingTests)
.sort()
.join(os.EOL);
fs.writeFile(file, failed, "utf8", done);
}
else if (!keepFailed && fs.existsSync(file)) {
@ -96,7 +121,7 @@ class FailedTestsReporter extends Mocha.reporters.Base {
}
/**
* @param {number} failures
* @param {number} failures
* @param {(failures: number) => void} [fn]
*/
done(failures, fn) {

View File

@ -54,16 +54,17 @@ async function copyScriptOutputs() {
await copyWithCopyright("cancellationToken.js");
await copyWithCopyright("tsc.release.js", "tsc.js");
await copyWithCopyright("tsserver.js");
await copyWithCopyright("typescript.js");
await copyWithCopyright("typescriptServices.js");
await copyFromBuiltLocal("tsserverlibrary.js"); // copyright added by build
await copyFromBuiltLocal("typescript.js"); // copyright added by build
await copyFromBuiltLocal("typescriptServices.js"); // copyright added by build
await copyWithCopyright("typingsInstaller.js");
await copyWithCopyright("watchGuard.js");
}
async function copyDeclarationOutputs() {
await copyWithCopyright("tsserverlibrary.d.ts");
await copyWithCopyright("typescript.d.ts");
await copyWithCopyright("typescriptServices.d.ts");
await copyFromBuiltLocal("tsserverlibrary.d.ts"); // copyright added by build
await copyFromBuiltLocal("typescript.d.ts"); // copyright added by build
await copyFromBuiltLocal("typescriptServices.d.ts"); // copyright added by build
}
async function writeGitAttributes() {

View File

@ -69,7 +69,12 @@ const proc = spawn(process.execPath, args, {
proc.on('exit', (code, signal) => {
process.on('exit', () => {
if (grepFile) {
fs.unlinkSync(grepFile);
try {
fs.unlinkSync(grepFile);
}
catch (e) {
if (e.code !== "ENOENT") throw e;
}
}
if (signal) {

View File

@ -1,4 +1,3 @@
/* @internal */
declare namespace ts.server {
export type ActionSet = "action::set";
export type ActionInvalidate = "action::invalidate";

View File

@ -1,5 +1,4 @@
namespace ts.server {
/*@internal*/
export interface ScriptInfoVersion {
svc: number;
text: number;

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,18 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
declare namespace ts {
const versionMajorMinor = "3.0";
/** The version of the TypeScript compiler release */
@ -4570,6 +4585,80 @@ declare namespace ts {
function getAllProjectOutputs(project: ParsedCommandLine): ReadonlyArray<string>;
function formatUpToDateStatus<T>(configFileName: string, status: UpToDateStatus, relName: (fileName: string) => string, formatMessage: (message: DiagnosticMessage, ...args: string[]) => T): T | undefined;
}
declare namespace ts.server {
type ActionSet = "action::set";
type ActionInvalidate = "action::invalidate";
type ActionPackageInstalled = "action::packageInstalled";
type EventTypesRegistry = "event::typesRegistry";
type EventBeginInstallTypes = "event::beginInstallTypes";
type EventEndInstallTypes = "event::endInstallTypes";
type EventInitializationFailed = "event::initializationFailed";
interface SortedReadonlyArray<T> extends ReadonlyArray<T> {
" __sortedArrayBrand": any;
}
interface TypingInstallerResponse {
readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | ActionPackageInstalled | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed;
}
interface TypingInstallerRequestWithProjectName {
readonly projectName: string;
}
interface DiscoverTypings extends TypingInstallerRequestWithProjectName {
readonly fileNames: string[];
readonly projectRootPath: Path;
readonly compilerOptions: CompilerOptions;
readonly typeAcquisition: TypeAcquisition;
readonly unresolvedImports: SortedReadonlyArray<string>;
readonly cachePath?: string;
readonly kind: "discover";
}
interface CloseProject extends TypingInstallerRequestWithProjectName {
readonly kind: "closeProject";
}
interface TypesRegistryRequest {
readonly kind: "typesRegistry";
}
interface InstallPackageRequest extends TypingInstallerRequestWithProjectName {
readonly kind: "installPackage";
readonly fileName: Path;
readonly packageName: string;
readonly projectRootPath: Path;
}
interface PackageInstalledResponse extends ProjectResponse {
readonly kind: ActionPackageInstalled;
readonly success: boolean;
readonly message: string;
}
interface InitializationFailedResponse extends TypingInstallerResponse {
readonly kind: EventInitializationFailed;
readonly message: string;
}
interface ProjectResponse extends TypingInstallerResponse {
readonly projectName: string;
}
interface InvalidateCachedTypings extends ProjectResponse {
readonly kind: ActionInvalidate;
}
interface InstallTypes extends ProjectResponse {
readonly kind: EventBeginInstallTypes | EventEndInstallTypes;
readonly eventId: number;
readonly typingsInstallerVersion: string;
readonly packagesToInstall: ReadonlyArray<string>;
}
interface BeginInstallTypes extends InstallTypes {
readonly kind: EventBeginInstallTypes;
}
interface EndInstallTypes extends InstallTypes {
readonly kind: EventEndInstallTypes;
readonly installSuccess: boolean;
}
interface SetTypings extends ProjectResponse {
readonly typeAcquisition: TypeAcquisition;
readonly compilerOptions: CompilerOptions;
readonly typings: string[];
readonly unresolvedImports: SortedReadonlyArray<string>;
readonly kind: ActionSet;
}
}
declare namespace ts {
interface Node {
getSourceFile(): SourceFile;
@ -5574,5 +5663,5 @@ declare namespace ts {
*/
function transform<T extends Node>(source: T | T[], transformers: TransformerFactory<T>[], compilerOptions?: CompilerOptions): TransformationResult<T>;
}
//# sourceMappingURL=typescriptServices.d.ts.map
export = ts
export = ts;