Merge branch 'AddDefinitionAndBoundSpan' of https://github.com/armanio123/TypeScript into AddDefinitionAndBoundSpan

This commit is contained in:
Armando Aguirre
2017-10-24 14:02:41 -07:00
313 changed files with 120671 additions and 112683 deletions
+1
View File
@@ -17,6 +17,7 @@ branches:
only:
- master
- release-2.5
- release-2.6
install:
- npm uninstall typescript --no-save
+39 -24
View File
@@ -64,7 +64,7 @@ const cmdLineOptions = minimist(process.argv.slice(2), {
browser: process.env.browser || process.env.b || "IE",
timeout: process.env.timeout || 40000,
tests: process.env.test || process.env.tests || process.env.t,
light: process.env.light || false,
light: process.env.light === undefined || process.env.light !== "false",
reporter: process.env.reporter || process.env.r,
lint: process.env.lint || true,
files: process.env.f || process.env.file || process.env.files || "",
@@ -87,7 +87,7 @@ function possiblyQuote(cmd: string) {
}
let useDebugMode = true;
let host = cmdLineOptions["host"];
let host = cmdLineOptions.host;
// Constants
const compilerDirectory = "src/compiler/";
@@ -180,6 +180,23 @@ const libraryTargets = librarySourceMap.map(function(f) {
return path.join(builtLocalDirectory, f.target);
});
/**
* .lcg file is what localization team uses to know what messages to localize.
* The file is always generated in 'enu\diagnosticMessages.generated.json.lcg'
*/
const generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg");
/**
* The localization target produces the two following transformations:
* 1. 'src\loc\lcl\<locale>\diagnosticMessages.generated.json.lcl' => 'built\local\<locale>\diagnosticMessages.generated.json'
* convert localized resources into a .json file the compiler can understand
* 2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg'
* generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json
*/
const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-CN", "zh-TW"].map(function (f) {
return path.join(builtLocalDirectory, f, "diagnosticMessages.generated.json");
}).concat(generatedLCGFile);
for (const i in libraryTargets) {
const entry = librarySourceMap[i];
const target = libraryTargets[i];
@@ -398,7 +415,6 @@ gulp.task(generateLocalizedDiagnosticMessagesJs, /*help*/ false, [], () => {
});
// Localize diagnostics
const generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg");
gulp.task(generatedLCGFile, [generateLocalizedDiagnosticMessagesJs, diagnosticInfoMapTs], (done) => {
if (fs.existsSync(builtLocalDirectory) && needsUpdate(generatedDiagnosticMessagesJSON, generatedLCGFile)) {
exec(host, [generateLocalizedDiagnosticMessagesJs, lclDirectory, builtLocalDirectory, generatedDiagnosticMessagesJSON], done, done);
@@ -576,8 +592,7 @@ gulp.task("dontUseDebugMode", /*help*/ false, [], (done) => { useDebugMode = fal
gulp.task("VerifyLKG", /*help*/ false, [], () => {
const expectedFiles = [builtLocalCompiler, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, typingsInstallerJs, cancellationTokenJs].concat(libraryTargets);
const missingFiles = expectedFiles.
concat(fs.readdirSync(lclDirectory).map(function (d) { return path.join(builtLocalDirectory, d, "diagnosticMessages.generated.json"); })).
concat(generatedLCGFile).
concat(localizationTargets).
filter(f => !fs.existsSync(f));
if (missingFiles.length > 0) {
throw new Error("Cannot replace the LKG unless all built targets are present in directory " + builtLocalDirectory +
@@ -636,15 +651,15 @@ function restoreSavedNodeEnv() {
}
function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: (e?: any) => void) {
const lintFlag = cmdLineOptions["lint"];
const lintFlag = cmdLineOptions.lint;
cleanTestDirs((err) => {
if (err) { console.error(err); failWithStatus(err, 1); }
let testTimeout = cmdLineOptions["timeout"];
const debug = cmdLineOptions["debug"];
const inspect = cmdLineOptions["inspect"];
const tests = cmdLineOptions["tests"];
const light = cmdLineOptions["light"];
const stackTraceLimit = cmdLineOptions["stackTraceLimit"];
let testTimeout = cmdLineOptions.timeout;
const debug = cmdLineOptions.debug;
const inspect = cmdLineOptions.inspect;
const tests = cmdLineOptions.tests;
const light = cmdLineOptions.light;
const stackTraceLimit = cmdLineOptions.stackTraceLimit;
const testConfigFile = "test.config";
if (fs.existsSync(testConfigFile)) {
fs.unlinkSync(testConfigFile);
@@ -660,7 +675,7 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
} while (fs.existsSync(taskConfigsFolder));
fs.mkdirSync(taskConfigsFolder);
workerCount = cmdLineOptions["workers"];
workerCount = cmdLineOptions.workers;
}
if (tests || light || taskConfigsFolder) {
@@ -671,8 +686,8 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
testTimeout = 400000;
}
const colors = cmdLineOptions["colors"];
const reporter = cmdLineOptions["reporter"] || defaultReporter;
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
@@ -860,7 +875,7 @@ function cleanTestDirs(done: (e?: any) => void) {
// used to pass data from jake command line directly to run.js
function writeTestConfigFile(tests: string, light: boolean, taskConfigsFolder?: string, workerCount?: number, stackTraceLimit?: string) {
const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light, workerCount, stackTraceLimit, taskConfigsFolder, noColor: !cmdLineOptions["colors"] });
const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light, workerCount, stackTraceLimit, taskConfigsFolder, noColor: !cmdLineOptions.colors });
console.log("Running tests with config: " + testConfigContents);
fs.writeFileSync("test.config", testConfigContents);
}
@@ -870,8 +885,8 @@ gulp.task("runtests-browser", "Runs the tests using the built run.js file like '
cleanTestDirs((err) => {
if (err) { console.error(err); done(err); process.exit(1); }
host = "node";
const tests = cmdLineOptions["tests"];
const light = cmdLineOptions["light"];
const tests = cmdLineOptions.tests;
const light = cmdLineOptions.light;
const testConfigFile = "test.config";
if (fs.existsSync(testConfigFile)) {
fs.unlinkSync(testConfigFile);
@@ -881,8 +896,8 @@ gulp.task("runtests-browser", "Runs the tests using the built run.js file like '
}
const args = [nodeServerOutFile];
if (cmdLineOptions["browser"]) {
args.push(cmdLineOptions["browser"]);
if (cmdLineOptions.browser) {
args.push(cmdLineOptions.browser);
}
if (tests) {
args.push(JSON.stringify(tests));
@@ -892,13 +907,13 @@ gulp.task("runtests-browser", "Runs the tests using the built run.js file like '
});
gulp.task("generate-code-coverage", "Generates code coverage data via istanbul", ["tests"], (done) => {
const testTimeout = cmdLineOptions["timeout"];
const testTimeout = cmdLineOptions.timeout;
exec("istanbul", ["cover", "node_modules/mocha/bin/_mocha", "--", "-R", "min", "-t", testTimeout.toString(), run], done, done);
});
function getDiffTool() {
const program = process.env["DIFF"];
const program = process.env.DIFF;
if (!program) {
console.error("Add the 'DIFF' environment variable to the path of the program you want to use.");
process.exit(1);
@@ -1019,7 +1034,7 @@ gulp.task(instrumenterJsPath, /*help*/ false, [servicesFile], () => {
});
gulp.task("tsc-instrumented", "Builds an instrumented tsc.js - run with --test=[testname]", ["local", loggedIOJsPath, instrumenterJsPath, servicesFile], (done) => {
const test = cmdLineOptions["tests"] || "iocapture";
const test = cmdLineOptions.tests || "iocapture";
exec(host, [instrumenterJsPath, "record", test, builtLocalCompiler], done, done);
});
@@ -1088,7 +1103,7 @@ function spawnLintWorker(files: {path: string}[], callback: (failures: number) =
gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: --f[iles]=regex", ["build-rules"], () => {
if (fold.isTravis()) console.log(fold.start("lint"));
const fileMatcher = cmdLineOptions["files"];
const fileMatcher = cmdLineOptions.files;
const files = fileMatcher
? `src/**/${fileMatcher}`
: "Gulpfile.ts 'scripts/generateLocalizedDiagnosticMessages.ts' 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
+20 -4
View File
@@ -154,6 +154,7 @@ var harnessSources = harnessCoreSources.concat([
"symbolWalker.ts",
"languageService.ts",
"publicApi.ts",
"hostNewLineSupport.ts",
].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([
@@ -238,6 +239,23 @@ var libraryTargets = librarySourceMap.map(function (f) {
return path.join(builtLocalDirectory, f.target);
});
/**
* .lcg file is what localization team uses to know what messages to localize.
* The file is always generated in 'enu\diagnosticMessages.generated.json.lcg'
*/
var generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg");
/**
* The localization target produces the two following transformations:
* 1. 'src\loc\lcl\<locale>\diagnosticMessages.generated.json.lcl' => 'built\local\<locale>\diagnosticMessages.generated.json'
* convert localized resources into a .json file the compiler can understand
* 2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg'
* generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json
*/
var localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-CN", "zh-TW"].map(function (f) {
return path.join(builtLocalDirectory, f);
}).concat(path.dirname(generatedLCGFile));
// Prepends the contents of prefixFile to destinationFile
function prependFile(prefixFile, destinationFile) {
if (!fs.existsSync(prefixFile)) {
@@ -443,7 +461,6 @@ compileFile(generateLocalizedDiagnosticMessagesJs,
/*useBuiltCompiler*/ false, { noOutFile: true, types: ["node", "xml2js"] });
// Localize diagnostics
var generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg");
file(generatedLCGFile, [generateLocalizedDiagnosticMessagesJs, diagnosticInfoMapTs, generatedDiagnosticMessagesJSON], function () {
var cmd = host + " " + generateLocalizedDiagnosticMessagesJs + " " + lclDirectory + " " + builtLocalDirectory + " " + generatedDiagnosticMessagesJSON;
console.log(cmd);
@@ -735,8 +752,7 @@ desc("Makes a new LKG out of the built js files");
task("LKG", ["clean", "release", "local"].concat(libraryTargets), function () {
var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, cancellationTokenFile, typingsInstallerFile, buildProtocolDts, watchGuardFile].
concat(libraryTargets).
concat(fs.readdirSync(lclDirectory).map(function (d) { return path.join(builtLocalDirectory, d) })).
concat(path.dirname(generatedLCGFile));
concat(localizationTargets);
var missingFiles = expectedFiles.filter(function (f) {
return !fs.existsSync(f);
});
@@ -855,7 +871,7 @@ function runConsoleTests(defaultReporter, runInParallel) {
var inspect = process.env.inspect || process.env["inspect-brk"] || process.env.i;
var testTimeout = process.env.timeout || defaultTestTimeout;
var tests = process.env.test || process.env.tests || process.env.t;
var light = process.env.light || false;
var light = process.env.light === undefined || process.env.light !== "false";
var stackTraceLimit = process.env.stackTraceLimit;
var testConfigFile = 'test.config';
if (fs.existsSync(testConfigFile)) {
+1 -1
View File
@@ -3,7 +3,7 @@
<!-- SUGGESTIONS: See https://github.com/Microsoft/TypeScript-wiki/blob/master/Writing-Good-Design-Proposals.md -->
<!-- Please try to reproduce the issue with `typescript@next`. It may have already been fixed. -->
**TypeScript Version:** 2.6.0-dev.201xxxxx
**TypeScript Version:** 2.7.0-dev.201xxxxx
**Code**
+115 -77
View File
@@ -1,6 +1,6 @@
{
"name": "typescript",
"version": "2.6.0",
"version": "2.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -42,7 +42,7 @@
"dev": true,
"requires": {
"@types/insert-module-globals": "7.0.0",
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/chai": {
@@ -79,7 +79,7 @@
"dev": true,
"requires": {
"@types/minimatch": "3.0.1",
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/gulp": {
@@ -88,7 +88,7 @@
"integrity": "sha512-3UpA2pkKO40cNPe/8bxMQFWSASR9Jx67JfN9Z2Cf6ogfDMwXgEHm2XjKmuLYEtrp1IHYApOWlYMLYNgtTJgSAw==",
"dev": true,
"requires": {
"@types/node": "8.0.34",
"@types/node": "8.0.46",
"@types/orchestrator": "0.3.0",
"@types/vinyl": "2.0.1"
}
@@ -99,7 +99,7 @@
"integrity": "sha512-F14zRcKn15HC59RXRlHpcxj79WoLjkJBJBPfN0NBZOgkRCfDZYVu8rs0Y/CH4CJGUbbc/nHczD2LmepDS+ARaA==",
"dev": true,
"requires": {
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/gulp-help": {
@@ -109,7 +109,7 @@
"dev": true,
"requires": {
"@types/gulp": "3.8.33",
"@types/node": "8.0.34",
"@types/node": "8.0.46",
"@types/orchestrator": "0.3.0"
}
},
@@ -119,7 +119,7 @@
"integrity": "sha1-bqn7oVsFdr5CTpl31IlCAEZKFR4=",
"dev": true,
"requires": {
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/gulp-sourcemaps": {
@@ -128,7 +128,7 @@
"integrity": "sha512-kJD1byVNx+sdQlaBzZpSGeFH/4l99TXTY4XSGW+aRk27eOnVyk6VknXJpsb1Jk5E4ThKxZ8GYy6ais7MtprK1w==",
"dev": true,
"requires": {
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/insert-module-globals": {
@@ -137,7 +137,7 @@
"integrity": "sha512-zudCJPwluh1VUDB6Gl/OQdRp+fYy3+47huJB/JMQubMS2p+sH18MCVK4WUz3FqaWLB12yh5ELxVR/+tqwlm/qA==",
"dev": true,
"requires": {
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/merge2": {
@@ -146,7 +146,7 @@
"integrity": "sha512-Xy54xPmFQ8oAx0S3ku46i/zXE4dvfxl5M8n4p2M62IwxPau8IpobiRtL4jkrUzX6Kgeyb34BHOh0i70SDjKHeA==",
"dev": true,
"requires": {
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/minimatch": {
@@ -167,7 +167,7 @@
"integrity": "sha512-XA4vNO6GCBz8Smq0hqSRo4yRWMqr4FPQrWjhJt6nKskzly4/p87SfuJMFYGRyYb6jo2WNIQU2FDBsY5r1BibUA==",
"dev": true,
"requires": {
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/mocha": {
@@ -177,9 +177,9 @@
"dev": true
},
"@types/node": {
"version": "8.0.34",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.34.tgz",
"integrity": "sha512-Jnmm57+nHqvJUPwUzt1CLoLzFtF2B2vgG7cWFut+a4nqTp9/L6pL0N+o0Jt3V7AQnCKMsPEqQpLFZYleBCdq3w==",
"version": "8.0.46",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.46.tgz",
"integrity": "sha512-rRkP4kb5JYIfAoRKaDbcdPZBcTNOgzSApyzhPN9e6rhViSJAWQGlSXIX5gc75iR02jikhpzy3usu31wMHllfFw==",
"dev": true
},
"@types/orchestrator": {
@@ -188,7 +188,7 @@
"integrity": "sha1-v4ShaZyTMNT+ic2BJj6PwJ+zKXg=",
"dev": true,
"requires": {
"@types/node": "8.0.34",
"@types/node": "8.0.46",
"@types/q": "0.0.37"
},
"dependencies": {
@@ -213,7 +213,7 @@
"dev": true,
"requires": {
"@types/gulp": "3.8.33",
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/through2": {
@@ -222,7 +222,7 @@
"integrity": "sha1-H/LoihAN+1sUDnu5h5HxGUQA0TE=",
"dev": true,
"requires": {
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/vinyl": {
@@ -231,7 +231,7 @@
"integrity": "sha512-Joudabfn2ZofU2usW04y8OLmN75u7ZQkW0MCT3AnoBf5oUBp5iQ3Pgfz9+y1RdWkzhCPZo9/wBJ7FMWW2JrY0g==",
"dev": true,
"requires": {
"@types/node": "8.0.34"
"@types/node": "8.0.46"
}
},
"@types/xml2js": {
@@ -240,17 +240,7 @@
"integrity": "sha512-3gw0UqFMq7PsfMDwsawD0/L48soXfzOEh0NSAWVO99IZXnhx9LD3nOldHIpGYzZBsrS9NV2vaRFvEdWe+UweXQ==",
"dev": true,
"requires": {
"@types/node": "8.0.34"
}
},
"JSONStream": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
"integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
"dev": true,
"requires": {
"jsonparse": "1.3.1",
"through": "2.3.8"
"@types/node": "8.0.46"
}
},
"abbrev": {
@@ -508,9 +498,9 @@
"integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=",
"dev": true,
"requires": {
"JSONStream": "1.3.1",
"combine-source-map": "0.7.2",
"defined": "1.0.0",
"JSONStream": "1.3.1",
"through2": "2.0.3",
"umd": "3.0.1"
}
@@ -531,16 +521,15 @@
"dev": true
},
"browserify": {
"version": "14.4.0",
"resolved": "https://registry.npmjs.org/browserify/-/browserify-14.4.0.tgz",
"integrity": "sha1-CJo0Y69Y0OSNjNQHCz90ZU1avKk=",
"version": "14.5.0",
"resolved": "https://registry.npmjs.org/browserify/-/browserify-14.5.0.tgz",
"integrity": "sha512-gKfOsNQv/toWz+60nSPfYzuwSEdzvV2WdxrVPUbPD/qui44rAkB3t3muNtmmGYHqrG56FGwX9SUEQmzNLAeS7g==",
"dev": true,
"requires": {
"JSONStream": "1.3.1",
"assert": "1.4.1",
"browser-pack": "6.0.2",
"browser-resolve": "1.11.2",
"browserify-zlib": "0.1.4",
"browserify-zlib": "0.2.0",
"buffer": "5.0.8",
"cached-path-relative": "1.0.1",
"concat-stream": "1.5.2",
@@ -558,9 +547,10 @@
"https-browserify": "1.0.0",
"inherits": "2.0.3",
"insert-module-globals": "7.0.1",
"JSONStream": "1.3.1",
"labeled-stream-splicer": "2.0.0",
"module-deps": "4.1.1",
"os-browserify": "0.1.2",
"os-browserify": "0.3.0",
"parents": "1.0.1",
"path-browserify": "0.0.0",
"process": "0.11.10",
@@ -583,6 +573,29 @@
"util": "0.10.3",
"vm-browserify": "0.0.4",
"xtend": "4.0.1"
},
"dependencies": {
"browserify-zlib": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
"integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
"dev": true,
"requires": {
"pako": "1.0.6"
}
},
"os-browserify": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
"integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
"dev": true
},
"pako": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
"integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==",
"dev": true
}
}
},
"browserify-aes": {
@@ -646,15 +659,6 @@
"parse-asn1": "5.1.0"
}
},
"browserify-zlib": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
"integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
"dev": true,
"requires": {
"pako": "0.2.9"
}
},
"buffer": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.8.tgz",
@@ -2658,10 +2662,10 @@
"integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=",
"dev": true,
"requires": {
"JSONStream": "1.3.1",
"combine-source-map": "0.7.2",
"concat-stream": "1.5.2",
"is-buffer": "1.1.5",
"JSONStream": "1.3.1",
"lexical-scope": "1.2.0",
"process": "0.11.10",
"through2": "2.0.3",
@@ -3016,6 +3020,16 @@
"integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
"dev": true
},
"JSONStream": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
"integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
"dev": true,
"requires": {
"jsonparse": "1.3.1",
"through": "2.3.8"
}
},
"kew": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
@@ -3654,7 +3668,6 @@
"integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=",
"dev": true,
"requires": {
"JSONStream": "1.3.1",
"browser-resolve": "1.11.2",
"cached-path-relative": "1.0.1",
"concat-stream": "1.5.2",
@@ -3662,6 +3675,7 @@
"detective": "4.5.0",
"duplexer2": "0.1.4",
"inherits": "2.0.3",
"JSONStream": "1.3.1",
"parents": "1.0.1",
"readable-stream": "2.3.3",
"resolve": "1.1.7",
@@ -3901,12 +3915,6 @@
"integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=",
"dev": true
},
"os-browserify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz",
"integrity": "sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ=",
"dev": true
},
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
@@ -3919,12 +3927,6 @@
"integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==",
"dev": true
},
"pako": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
"integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
"dev": true
},
"parents": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
@@ -4160,9 +4162,9 @@
"dev": true
},
"q": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz",
"integrity": "sha1-3QG6ydBtMObyGa7LglPunr3DCPE=",
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
"dev": true
},
"querystring": {
@@ -4847,7 +4849,7 @@
"dev": true,
"requires": {
"arrify": "1.0.1",
"chalk": "2.1.0",
"chalk": "2.2.0",
"diff": "3.3.1",
"make-error": "1.3.0",
"minimist": "1.2.0",
@@ -4868,14 +4870,14 @@
}
},
"chalk": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
"integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.0.tgz",
"integrity": "sha512-0BMM/2hG3ZaoPfR6F+h/oWpZtsh3b/s62TjSM6MGCJWEbJDN1acqCXvyhhZsDSVFklpebUoQ5O1kKC7lOzrn9g==",
"dev": true,
"requires": {
"ansi-styles": "3.2.0",
"escape-string-regexp": "1.0.5",
"supports-color": "4.4.0"
"supports-color": "4.5.0"
}
},
"has-flag": {
@@ -4885,9 +4887,9 @@
"dev": true
},
"supports-color": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
"integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
"integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
"dev": true,
"requires": {
"has-flag": "2.0.0"
@@ -4929,13 +4931,14 @@
"dev": true
},
"tslint": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.7.0.tgz",
"integrity": "sha1-wl4NDJL6EgHCvDDoROCOaCtPNVI=",
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.8.0.tgz",
"integrity": "sha1-H0mtWy53x2w69N3K5VKuTjYS6xM=",
"dev": true,
"requires": {
"babel-code-frame": "6.26.0",
"colors": "1.1.2",
"builtin-modules": "1.1.1",
"chalk": "2.2.0",
"commander": "2.11.0",
"diff": "3.3.1",
"glob": "7.1.2",
@@ -4946,6 +4949,32 @@
"tsutils": "2.12.1"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
"dev": true,
"requires": {
"color-convert": "1.9.0"
}
},
"chalk": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.0.tgz",
"integrity": "sha512-0BMM/2hG3ZaoPfR6F+h/oWpZtsh3b/s62TjSM6MGCJWEbJDN1acqCXvyhhZsDSVFklpebUoQ5O1kKC7lOzrn9g==",
"dev": true,
"requires": {
"ansi-styles": "3.2.0",
"escape-string-regexp": "1.0.5",
"supports-color": "4.5.0"
}
},
"has-flag": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
"dev": true
},
"resolve": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
@@ -4960,6 +4989,15 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==",
"dev": true
},
"supports-color": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
"integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
}
}
},
@@ -5000,9 +5038,9 @@
"dev": true
},
"typescript": {
"version": "2.6.0-dev.20171011",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.0-dev.20171011.tgz",
"integrity": "sha512-il66U8zNRbF875Gq6cP3K/CthG7Dp9PRpu5w5mVHaPcolzJhrdLa3K2WavqqJg/7h7sPOZvsU8nYdXO61sHagg==",
"version": "2.7.0-dev.20171020",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.0-dev.20171020.tgz",
"integrity": "sha512-Sy1F2YVw7nj2pcMP2bE6YK5dviaY2WRJb12t27EUiW4wkD8GiaZ0sgNBdTKRcvTNFJ8KXjwnl+Ysi5+J5BlcHw==",
"dev": true
},
"uglify-js": {
+2 -2
View File
@@ -2,7 +2,7 @@
"name": "typescript",
"author": "Microsoft Corp.",
"homepage": "http://typescriptlang.org/",
"version": "2.6.0",
"version": "2.7.0",
"license": "Apache-2.0",
"description": "TypeScript is a language for application scale JavaScript development",
"keywords": [
@@ -83,7 +83,7 @@
},
"scripts": {
"pretest": "jake tests",
"test": "jake runtests-parallel",
"test": "jake runtests-parallel light=false",
"build": "npm run build:compiler && npm run build:tests",
"build:compiler": "jake local",
"build:tests": "jake tests",
+18 -15
View File
@@ -51,22 +51,25 @@ class DeclarationsWalker {
return this.processType((<any>type).typeArguments[0]);
}
else {
for (const decl of s.getDeclarations()) {
const sourceFile = decl.getSourceFile();
if (sourceFile === this.protocolFile || path.basename(sourceFile.fileName) === "lib.d.ts") {
return;
}
if (decl.kind === ts.SyntaxKind.EnumDeclaration && !isStringEnum(decl as ts.EnumDeclaration)) {
this.removedTypes.push(type);
return;
}
else {
// splice declaration in final d.ts file
let text = decl.getFullText();
this.text += `${text}\n`;
// recursively pull all dependencies into result dts file
const declarations = s.getDeclarations();
if (declarations) {
for (const decl of declarations) {
const sourceFile = decl.getSourceFile();
if (sourceFile === this.protocolFile || path.basename(sourceFile.fileName) === "lib.d.ts") {
return;
}
if (decl.kind === ts.SyntaxKind.EnumDeclaration && !isStringEnum(decl as ts.EnumDeclaration)) {
this.removedTypes.push(type);
return;
}
else {
// splice declaration in final d.ts file
let text = decl.getFullText();
this.text += `${text}\n`;
// recursively pull all dependencies into result dts file
this.visitTypeNodes(decl);
this.visitTypeNodes(decl);
}
}
}
}
+43 -5
View File
@@ -27,16 +27,54 @@ function main(): void {
function visitDirectory(name: string) {
const inputFilePath = path.join(inputPath, name, "diagnosticMessages", "diagnosticMessages.generated.json.lcl");
const outputFilePath = path.join(outputPath, name, "diagnosticMessages.generated.json");
fs.readFile(inputFilePath, (err, data) => {
handleError(err);
xml2js.parseString(data.toString(), (err, result) => {
handleError(err);
writeFile(outputFilePath, xmlObjectToString(result));
if (!result || !result.LCX || !result.LCX.$ || !result.LCX.$.TgtCul) {
console.error("Unexpected XML file structure. Expected to find result.LCX.$.TgtCul.");
process.exit(1);
}
const outputDirectoryName = getPreferedLocaleName(result.LCX.$.TgtCul);
if (!outputDirectoryName) {
console.error(`Invalid output locale name for '${result.LCX.$.TgtCul}'.`);
process.exit(1);
}
writeFile(path.join(outputPath, outputDirectoryName, "diagnosticMessages.generated.json"), xmlObjectToString(result));
});
});
}
/**
* A locale name is based on the language tagging conventions of RFC 4646 (Windows Vista
* and later), and is represented by LOCALE_SNAME.
* Generally, the pattern <language>-<REGION> is used. Here, language is a lowercase ISO 639
* language code. The codes from ISO 639-1 are used when available. Otherwise, codes from
* ISO 639-2/T are used. REGION specifies an uppercase ISO 3166-1 country/region identifier.
* For example, the locale name for English (United States) is "en-US" and the locale name
* for Divehi (Maldives) is "dv-MV".
*
* If the locale is a neutral locale (no region), the LOCALE_SNAME value follows the
* pattern <language>. If it is a neutral locale for which the script is significant, the
* pattern is <language>-<Script>.
*
* More at https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx
*
* Most of the languages we support are neutral locales, so we want to use the language name.
* There are three exceptions, zh-CN, zh-TW and pt-BR.
*/
function getPreferedLocaleName(localeName: string) {
switch (localeName) {
case "zh-CN":
case "zh-TW":
case "pt-BR":
return localeName;
default:
return localeName.split("-")[0];
}
}
function handleError(err: null | object) {
if (err) {
console.error(err);
@@ -46,9 +84,9 @@ function main(): void {
function xmlObjectToString(o: any) {
const out: any = {};
for (const item of o["LCX"]["Item"][0]["Item"][0]["Item"]) {
let ItemId = item["$"]["ItemId"];
let Val = item["Str"][0]["Tgt"] ? item["Str"][0]["Tgt"][0]["Val"][0] : item["Str"][0]["Val"][0];
for (const item of o.LCX.Item[0].Item[0].Item) {
let ItemId = item.$.ItemId;
let Val = item.Str[0].Tgt ? item.Str[0].Tgt[0].Val[0] : item.Str[0].Val[0];
if (typeof ItemId !== "string" || typeof Val !== "string") {
console.error("Unexpected XML file structure");
+134 -160
View File
@@ -6,58 +6,38 @@ namespace ts {
emitSkipped: boolean;
}
export interface EmitOutputDetailed extends EmitOutput {
diagnostics: Diagnostic[];
sourceMaps: SourceMapData[];
emittedSourceFiles: SourceFile[];
}
export interface OutputFile {
name: string;
writeByteOrderMark: boolean;
text: string;
}
export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean,
cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput | EmitOutputDetailed {
const outputFiles: OutputFile[] = [];
let emittedSourceFiles: SourceFile[];
const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
if (!isDetailed) {
return { outputFiles, emitSkipped: emitResult.emitSkipped };
}
return {
outputFiles,
emitSkipped: emitResult.emitSkipped,
diagnostics: emitResult.diagnostics,
sourceMaps: emitResult.sourceMaps,
emittedSourceFiles
};
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, _onError: (message: string) => void, sourceFiles: SourceFile[]) {
outputFiles.push({ name: fileName, writeByteOrderMark, text });
if (isDetailed) {
emittedSourceFiles = addRange(emittedSourceFiles, sourceFiles);
}
}
}
}
/* @internal */
namespace ts {
export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean,
cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput {
const outputFiles: OutputFile[] = [];
const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
return { outputFiles, emitSkipped: emitResult.emitSkipped };
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) {
outputFiles.push({ name: fileName, writeByteOrderMark, text });
}
}
export interface Builder {
/**
* Call this to feed new program
*/
/** Called to inform builder about new program */
updateProgram(newProgram: Program): void;
getFilesAffectedBy(program: Program, path: Path): string[];
emitFile(program: Program, path: Path): EmitOutput;
/** Gets the files affected by the file path */
getFilesAffectedBy(program: Program, path: Path): ReadonlyArray<SourceFile>;
/** Emit the changed files and clear the cache of the changed files */
emitChangedFiles(program: Program): EmitOutputDetailed[];
emitChangedFiles(program: Program, writeFileCallback: WriteFileCallback): ReadonlyArray<EmitResult>;
/** When called gets the semantic diagnostics for the program. It also caches the diagnostics and manage them */
getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): Diagnostic[];
getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/** Called to reset the status of the builder */
clear(): void;
@@ -88,20 +68,17 @@ namespace ts {
/**
* Gets the files affected by the script info which has updated shape from the known one
*/
getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile, singleFileResult: string[]): string[];
getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile): ReadonlyArray<SourceFile>;
}
interface FileInfo {
fileName: string;
version: string;
signature: string;
}
export interface BuilderOptions {
getCanonicalFileName: (fileName: string) => string;
getEmitOutput: (program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean) => EmitOutput | EmitOutputDetailed;
computeHash: (data: string) => string;
shouldEmitFile: (sourceFile: SourceFile) => boolean;
}
export function createBuilder(options: BuilderOptions): Builder {
@@ -109,12 +86,13 @@ namespace ts {
const fileInfos = createMap<FileInfo>();
const semanticDiagnosticsPerFile = createMap<ReadonlyArray<Diagnostic>>();
/** The map has key by source file's path that has been changed */
const changedFileNames = createMap<string>();
const changedFilesSet = createMap<true>();
const hasShapeChanged = createMap<true>();
let allFilesExcludingDefaultLibraryFile: ReadonlyArray<SourceFile> | undefined;
let emitHandler: EmitHandler;
return {
updateProgram,
getFilesAffectedBy,
emitFile,
emitChangedFiles,
getSemanticDiagnostics,
clear
@@ -128,6 +106,8 @@ namespace ts {
fileInfos.clear();
semanticDiagnosticsPerFile.clear();
}
hasShapeChanged.clear();
allFilesExcludingDefaultLibraryFile = undefined;
mutateMap(
fileInfos,
arrayToMap(program.getSourceFiles(), sourceFile => sourceFile.path),
@@ -142,31 +122,34 @@ namespace ts {
);
}
function registerChangedFile(path: Path, fileName: string) {
changedFileNames.set(path, fileName);
function registerChangedFile(path: Path) {
changedFilesSet.set(path, true);
// All changed files need to re-evaluate its semantic diagnostics
semanticDiagnosticsPerFile.delete(path);
}
function addNewFileInfo(program: Program, sourceFile: SourceFile): FileInfo {
registerChangedFile(sourceFile.path, sourceFile.fileName);
registerChangedFile(sourceFile.path);
emitHandler.onAddSourceFile(program, sourceFile);
return { fileName: sourceFile.fileName, version: sourceFile.version, signature: undefined };
return { version: sourceFile.version, signature: undefined };
}
function removeExistingFileInfo(existingFileInfo: FileInfo, path: Path) {
registerChangedFile(path, existingFileInfo.fileName);
function removeExistingFileInfo(_existingFileInfo: FileInfo, path: Path) {
// Since we dont need to track removed file as changed file
// We can just remove its diagnostics
changedFilesSet.delete(path);
semanticDiagnosticsPerFile.delete(path);
emitHandler.onRemoveSourceFile(path);
}
function updateExistingFileInfo(program: Program, existingInfo: FileInfo, sourceFile: SourceFile) {
if (existingInfo.version !== sourceFile.version) {
registerChangedFile(sourceFile.path, sourceFile.fileName);
registerChangedFile(sourceFile.path);
existingInfo.version = sourceFile.version;
emitHandler.onUpdateSourceFile(program, sourceFile);
}
else if (emitHandler.onUpdateSourceFileWithSameVersion(program, sourceFile)) {
registerChangedFile(sourceFile.path, sourceFile.fileName);
registerChangedFile(sourceFile.path);
}
}
@@ -182,114 +165,95 @@ namespace ts {
}
}
function getFilesAffectedBy(program: Program, path: Path): string[] {
function getFilesAffectedBy(program: Program, path: Path): ReadonlyArray<SourceFile> {
ensureProgramGraph(program);
const sourceFile = program.getSourceFile(path);
const singleFileResult = sourceFile && options.shouldEmitFile(sourceFile) ? [sourceFile.fileName] : [];
const info = fileInfos.get(path);
if (!info || !updateShapeSignature(program, sourceFile, info)) {
return singleFileResult;
const sourceFile = program.getSourceFileByPath(path);
if (!sourceFile) {
return emptyArray;
}
Debug.assert(!!sourceFile);
return emitHandler.getFilesAffectedByUpdatedShape(program, sourceFile, singleFileResult);
if (!updateShapeSignature(program, sourceFile)) {
return [sourceFile];
}
return emitHandler.getFilesAffectedByUpdatedShape(program, sourceFile);
}
function emitFile(program: Program, path: Path) {
function emitChangedFiles(program: Program, writeFileCallback: WriteFileCallback): ReadonlyArray<EmitResult> {
ensureProgramGraph(program);
if (!fileInfos.has(path)) {
return { outputFiles: [], emitSkipped: true };
const compilerOptions = program.getCompilerOptions();
if (!changedFilesSet.size) {
return emptyArray;
}
return options.getEmitOutput(program, program.getSourceFileByPath(path), /*emitOnlyDtsFiles*/ false, /*isDetailed*/ false);
}
// With --out or --outFile all outputs go into single file, do it only once
if (compilerOptions.outFile || compilerOptions.out) {
Debug.assert(semanticDiagnosticsPerFile.size === 0);
changedFilesSet.clear();
return [program.emit(/*targetSourceFile*/ undefined, writeFileCallback)];
}
function enumerateChangedFilesSet(
program: Program,
onChangedFile: (fileName: string, path: Path) => void,
onAffectedFile: (fileName: string, sourceFile: SourceFile) => void
) {
changedFileNames.forEach((fileName, path) => {
onChangedFile(fileName, path as Path);
const affectedFiles = getFilesAffectedBy(program, path as Path);
for (const file of affectedFiles) {
onAffectedFile(file, program.getSourceFile(file));
}
});
}
function enumerateChangedFilesEmitOutput(
program: Program,
emitOnlyDtsFiles: boolean,
onChangedFile: (fileName: string, path: Path) => void,
onEmitOutput: (emitOutput: EmitOutputDetailed, sourceFile: SourceFile) => void
) {
const seenFiles = createMap<true>();
enumerateChangedFilesSet(program, onChangedFile, (fileName, sourceFile) => {
if (!seenFiles.has(fileName)) {
seenFiles.set(fileName, true);
if (sourceFile) {
// Any affected file shouldnt have the cached diagnostics
semanticDiagnosticsPerFile.delete(sourceFile.path);
let result: EmitResult[] | undefined;
changedFilesSet.forEach((_true, path) => {
// Get the affected Files by this program
const affectedFiles = getFilesAffectedBy(program, path as Path);
affectedFiles.forEach(affectedFile => {
// Affected files shouldnt have cached diagnostics
semanticDiagnosticsPerFile.delete(affectedFile.path);
const emitOutput = options.getEmitOutput(program, sourceFile, emitOnlyDtsFiles, /*isDetailed*/ true) as EmitOutputDetailed;
onEmitOutput(emitOutput, sourceFile);
if (!seenFiles.has(affectedFile.path)) {
seenFiles.set(affectedFile.path, true);
// mark all the emitted source files as seen
if (emitOutput.emittedSourceFiles) {
for (const file of emitOutput.emittedSourceFiles) {
seenFiles.set(file.fileName, true);
}
}
// Emit the affected file
(result || (result = [])).push(program.emit(affectedFile, writeFileCallback));
}
}
});
});
changedFilesSet.clear();
return result || emptyArray;
}
function emitChangedFiles(program: Program): EmitOutputDetailed[] {
function getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
ensureProgramGraph(program);
const result: EmitOutputDetailed[] = [];
enumerateChangedFilesEmitOutput(program, /*emitOnlyDtsFiles*/ false,
/*onChangedFile*/ noop, emitOutput => result.push(emitOutput));
changedFileNames.clear();
return result;
}
Debug.assert(changedFilesSet.size === 0);
function getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): Diagnostic[] {
ensureProgramGraph(program);
// Ensure that changed files have cleared their respective
enumerateChangedFilesSet(program, /*onChangedFile*/ noop, (_affectedFileName, sourceFile) => {
if (sourceFile) {
semanticDiagnosticsPerFile.delete(sourceFile.path);
}
});
const compilerOptions = program.getCompilerOptions();
if (compilerOptions.outFile || compilerOptions.out) {
Debug.assert(semanticDiagnosticsPerFile.size === 0);
// We dont need to cache the diagnostics just return them from program
return program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken);
}
let diagnostics: Diagnostic[];
for (const sourceFile of program.getSourceFiles()) {
const path = sourceFile.path;
const cachedDiagnostics = semanticDiagnosticsPerFile.get(path);
// Report the semantic diagnostics from the cache if we already have those diagnostics present
if (cachedDiagnostics) {
diagnostics = addRange(diagnostics, cachedDiagnostics);
}
else {
// Diagnostics werent cached, get them from program, and cache the result
const cachedDiagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken);
semanticDiagnosticsPerFile.set(path, cachedDiagnostics);
diagnostics = addRange(diagnostics, cachedDiagnostics);
}
diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(program, sourceFile, cancellationToken));
}
return diagnostics || emptyArray;
}
function getSemanticDiagnosticsOfFile(program: Program, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
const path = sourceFile.path;
const cachedDiagnostics = semanticDiagnosticsPerFile.get(path);
// Report the semantic diagnostics from the cache if we already have those diagnostics present
if (cachedDiagnostics) {
return cachedDiagnostics;
}
// Diagnostics werent cached, get them from program, and cache the result
const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken);
semanticDiagnosticsPerFile.set(path, diagnostics);
return diagnostics;
}
function clear() {
isModuleEmit = undefined;
emitHandler = undefined;
fileInfos.clear();
semanticDiagnosticsPerFile.clear();
changedFileNames.clear();
changedFilesSet.clear();
hasShapeChanged.clear();
}
/**
@@ -310,15 +274,26 @@ namespace ts {
/**
* @return {boolean} indicates if the shape signature has changed since last update.
*/
function updateShapeSignature(program: Program, sourceFile: SourceFile, info: FileInfo) {
function updateShapeSignature(program: Program, sourceFile: SourceFile) {
Debug.assert(!!sourceFile);
// If we have cached the result for this file, that means hence forth we should assume file shape is uptodate
if (hasShapeChanged.has(sourceFile.path)) {
return false;
}
hasShapeChanged.set(sourceFile.path, true);
const info = fileInfos.get(sourceFile.path);
Debug.assert(!!info);
const prevSignature = info.signature;
let latestSignature: string;
if (sourceFile.isDeclarationFile) {
latestSignature = options.computeHash(sourceFile.text);
latestSignature = sourceFile.version;
info.signature = latestSignature;
}
else {
const emitOutput = options.getEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true, /*isDetailed*/ false);
const emitOutput = getFileEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true);
if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) {
latestSignature = options.computeHash(emitOutput.outputFiles[0].text);
info.signature = latestSignature;
@@ -386,24 +361,27 @@ namespace ts {
}
/**
* Gets all the emittable files from the program.
* @param firstSourceFile This one will be emitted first. See https://github.com/Microsoft/TypeScript/issues/16888
* Gets all files of the program excluding the default library file
*/
function getAllEmittableFiles(program: Program, firstSourceFile: SourceFile): string[] {
const defaultLibraryFileName = getDefaultLibFileName(program.getCompilerOptions());
const sourceFiles = program.getSourceFiles();
const result: string[] = [];
add(firstSourceFile);
for (const sourceFile of sourceFiles) {
function getAllFilesExcludingDefaultLibraryFile(program: Program, firstSourceFile: SourceFile): ReadonlyArray<SourceFile> {
// Use cached result
if (allFilesExcludingDefaultLibraryFile) {
return allFilesExcludingDefaultLibraryFile;
}
let result: SourceFile[];
addSourceFile(firstSourceFile);
for (const sourceFile of program.getSourceFiles()) {
if (sourceFile !== firstSourceFile) {
add(sourceFile);
addSourceFile(sourceFile);
}
}
return result;
allFilesExcludingDefaultLibraryFile = result || emptyArray;
return allFilesExcludingDefaultLibraryFile;
function add(sourceFile: SourceFile): void {
if (getBaseFileName(sourceFile.fileName) !== defaultLibraryFileName && options.shouldEmitFile(sourceFile)) {
result.push(sourceFile.fileName);
function addSourceFile(sourceFile: SourceFile) {
if (!program.isSourceFileDefaultLibrary(sourceFile)) {
(result || (result = [])).push(sourceFile);
}
}
}
@@ -417,14 +395,14 @@ namespace ts {
getFilesAffectedByUpdatedShape
};
function getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile, singleFileResult: string[]): string[] {
function getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile): ReadonlyArray<SourceFile> {
const options = program.getCompilerOptions();
// If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project,
// so returning the file itself is good enough.
if (options && (options.out || options.outFile)) {
return singleFileResult;
return [sourceFile];
}
return getAllEmittableFiles(program, sourceFile);
return getAllFilesExcludingDefaultLibraryFile(program, sourceFile);
}
}
@@ -481,7 +459,7 @@ namespace ts {
// add files referencing the removedFilePath, as changed files too
const referencedByInfo = fileInfos.get(filePath);
if (referencedByInfo) {
registerChangedFile(filePath as Path, referencedByInfo.fileName);
registerChangedFile(filePath as Path);
}
}
});
@@ -495,37 +473,33 @@ namespace ts {
);
}
function getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile, singleFileResult: string[]): string[] {
function getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile): ReadonlyArray<SourceFile> {
if (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile)) {
return getAllEmittableFiles(program, sourceFile);
return getAllFilesExcludingDefaultLibraryFile(program, sourceFile);
}
const compilerOptions = program.getCompilerOptions();
if (compilerOptions && (compilerOptions.isolatedModules || compilerOptions.out || compilerOptions.outFile)) {
return singleFileResult;
return [sourceFile];
}
// Now we need to if each file in the referencedBy list has a shape change as well.
// Because if so, its own referencedBy files need to be saved as well to make the
// emitting result consistent with files on disk.
const seenFileNamesMap = createMap<string>();
const setSeenFileName = (path: Path, sourceFile: SourceFile) => {
seenFileNamesMap.set(path, sourceFile && options.shouldEmitFile(sourceFile) ? sourceFile.fileName : undefined);
};
const seenFileNamesMap = createMap<SourceFile>();
// Start with the paths this file was referenced by
const path = sourceFile.path;
setSeenFileName(path, sourceFile);
seenFileNamesMap.set(path, sourceFile);
const queue = getReferencedByPaths(path);
while (queue.length > 0) {
const currentPath = queue.pop();
if (!seenFileNamesMap.has(currentPath)) {
const currentSourceFile = program.getSourceFileByPath(currentPath);
if (currentSourceFile && updateShapeSignature(program, currentSourceFile, fileInfos.get(currentPath))) {
seenFileNamesMap.set(currentPath, currentSourceFile);
if (currentSourceFile && updateShapeSignature(program, currentSourceFile)) {
queue.push(...getReferencedByPaths(currentPath));
}
setSeenFileName(currentPath, currentSourceFile);
}
}
+122 -77
View File
@@ -314,6 +314,7 @@ namespace ts {
const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
const globals = createSymbolTable();
let ambientModulesCache: Symbol[] | undefined;
/**
* List of every ambient module with a "*" wildcard.
* Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches.
@@ -559,10 +560,10 @@ namespace ts {
return emitResolver;
}
function error(location: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): void {
function error(location: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void {
const diagnostic = location
? createDiagnosticForNode(location, message, arg0, arg1, arg2)
: createCompilerDiagnostic(message, arg0, arg1, arg2);
? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
: createCompilerDiagnostic(message, arg0, arg1, arg2, arg3);
diagnostics.add(diagnostic);
}
@@ -3398,7 +3399,7 @@ namespace ts {
else if (flags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral &&
type.symbol.valueDeclaration &&
type.symbol.valueDeclaration.kind === SyntaxKind.ClassExpression) {
writeAnonymousType(getDeclaredTypeOfClassOrInterface(type.symbol), flags);
writeAnonymousType(type, flags);
}
else {
// Write the type reference in the format f<A>.g<B>.C<X, Y> where A and B are type arguments
@@ -4416,8 +4417,7 @@ namespace ts {
jsDocType = declarationType;
}
else if (jsDocType !== unknownType && declarationType !== unknownType && !isTypeIdenticalTo(jsDocType, declarationType)) {
const name = getNameOfDeclaration(declaration);
error(name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(name), typeToString(jsDocType), typeToString(declarationType));
errorNextVariableDeclarationMustHaveSameType(symbol.valueDeclaration, jsDocType, declaration, declarationType);
}
}
else if (!jsDocType) {
@@ -5777,7 +5777,7 @@ namespace ts {
for (const propertySymbol of getPropertiesOfType(modifiersType)) {
addMemberForKeyType(getLiteralTypeFromPropertyName(propertySymbol), propertySymbol);
}
if (getIndexInfoOfType(modifiersType, IndexKind.String)) {
if (modifiersType.flags & TypeFlags.Any || getIndexInfoOfType(modifiersType, IndexKind.String)) {
addMemberForKeyType(stringType);
}
}
@@ -5821,7 +5821,7 @@ namespace ts {
prop.syntheticLiteralTypeOrigin = t as StringLiteralType;
members.set(propName, prop);
}
else if (t.flags & TypeFlags.String) {
else if (t.flags & (TypeFlags.Any | TypeFlags.String)) {
stringIndexInfo = createIndexInfo(propType, templateReadonly);
}
}
@@ -6420,11 +6420,11 @@ namespace ts {
* @param typeParameters The requested type parameters.
* @param minTypeArgumentCount The minimum number of required type arguments.
*/
function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScript: boolean) {
function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScriptImplicitAny: boolean) {
const numTypeParameters = length(typeParameters);
if (numTypeParameters) {
const numTypeArguments = length(typeArguments);
if ((isJavaScript || numTypeArguments >= minTypeArgumentCount) && numTypeArguments <= numTypeParameters) {
if ((isJavaScriptImplicitAny || numTypeArguments >= minTypeArgumentCount) && numTypeArguments <= numTypeParameters) {
if (!typeArguments) {
typeArguments = [];
}
@@ -6433,12 +6433,12 @@ namespace ts {
// If a type parameter does not have a default type, or if the default type
// is a forward reference, the empty object type is used.
for (let i = numTypeArguments; i < numTypeParameters; i++) {
typeArguments[i] = getDefaultTypeArgumentType(isJavaScript);
typeArguments[i] = getDefaultTypeArgumentType(isJavaScriptImplicitAny);
}
for (let i = numTypeArguments; i < numTypeParameters; i++) {
const mapper = createTypeMapper(typeParameters, typeArguments);
const defaultType = getDefaultFromTypeParameter(typeParameters[i]);
typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : getDefaultTypeArgumentType(isJavaScript);
typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : getDefaultTypeArgumentType(isJavaScriptImplicitAny);
}
}
}
@@ -6726,6 +6726,16 @@ namespace ts {
isInJavaScriptFile(signature.declaration));
}
function getBaseSignature(signature: Signature) {
const typeParameters = signature.typeParameters;
if (typeParameters) {
const typeEraser = createTypeEraser(typeParameters);
const baseConstraints = map(typeParameters, tp => instantiateType(getBaseConstraintOfType(tp), typeEraser) || emptyObjectType);
return instantiateSignature(signature, createTypeMapper(typeParameters, baseConstraints), /*eraseTypeParameters*/ true);
}
return signature;
}
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
// There are two ways to declare a construct signature, one is by declaring a class constructor
// using the constructor keyword, and the other is declaring a bare construct signature in an
@@ -6874,21 +6884,25 @@ namespace ts {
if (typeParameters) {
const numTypeArguments = length(node.typeArguments);
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
const isJavascript = isInJavaScriptFile(node);
if (!isJavascript && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) {
error(node,
minTypeArgumentCount === typeParameters.length
? Diagnostics.Generic_type_0_requires_1_type_argument_s
: Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments,
typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType),
minTypeArgumentCount,
typeParameters.length);
const isJs = isInJavaScriptFile(node);
const isJsImplicitAny = !compilerOptions.noImplicitAny && isJs;
if (!isJsImplicitAny && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) {
const missingAugmentsTag = isJs && node.parent.kind !== SyntaxKind.JSDocAugmentsTag;
const diag = minTypeArgumentCount === typeParameters.length
? missingAugmentsTag
? Diagnostics.Expected_0_type_arguments_provide_these_with_an_extends_tag
: Diagnostics.Generic_type_0_requires_1_type_argument_s
: missingAugmentsTag
? Diagnostics.Expected_0_1_type_arguments_provide_these_with_an_extends_tag
: Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments;
const typeStr = typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType);
error(node, diag, typeStr, minTypeArgumentCount, typeParameters.length);
return unknownType;
}
// In a type reference, the outer type parameters of the referenced class or interface are automatically
// supplied as type arguments and the type reference only specifies arguments for the local type parameters
// of the class or interface.
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, isJavascript));
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, isJsImplicitAny));
return createTypeReference(<GenericType>type, typeArguments);
}
if (node.typeArguments) {
@@ -7029,13 +7043,11 @@ namespace ts {
function getIntendedTypeFromJSDocTypeReference(node: TypeReferenceNode): Type {
if (isIdentifier(node.typeName)) {
if (node.typeName.escapedText === "Object") {
if (node.typeArguments && node.typeArguments.length === 2) {
if (isJSDocIndexSignature(node)) {
const indexed = getTypeFromTypeNode(node.typeArguments[0]);
const target = getTypeFromTypeNode(node.typeArguments[1]);
const index = createIndexInfo(target, /*isReadonly*/ false);
if (indexed === stringType || indexed === numberType) {
return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, indexed === stringType && index, indexed === numberType && index);
}
return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, indexed === stringType && index, indexed === numberType && index);
}
return anyType;
}
@@ -7971,7 +7983,7 @@ namespace ts {
const spread = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
spread.flags |= propagatedFlags;
spread.flags |= TypeFlags.FreshLiteral;
spread.flags |= TypeFlags.FreshLiteral | TypeFlags.ContainsObjectLiteral;
(spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
spread.symbol = symbol;
return spread;
@@ -8386,7 +8398,7 @@ namespace ts {
}
function isMappableType(type: Type) {
return type.flags & (TypeFlags.TypeParameter | TypeFlags.Object | TypeFlags.Intersection | TypeFlags.IndexedAccess);
return type.flags & (TypeFlags.Any | TypeFlags.TypeParameter | TypeFlags.Object | TypeFlags.Intersection | TypeFlags.IndexedAccess);
}
function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType {
@@ -10666,29 +10678,27 @@ namespace ts {
const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
const members = createSymbolTable();
for (const prop of properties) {
const inferredPropType = inferTargetType(getTypeOfSymbol(prop));
if (!inferredPropType) {
const propType = getTypeOfSymbol(prop);
// If any property contains context sensitive functions that have been skipped, the source type
// is incomplete and we can't infer a meaningful input type.
if (propType.flags & TypeFlags.ContainsAnyFunctionType) {
return undefined;
}
const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName);
inferredProp.checkFlags = readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0;
inferredProp.declarations = prop.declarations;
inferredProp.type = inferredPropType;
inferredProp.type = inferTargetType(propType);
members.set(prop.escapedName, inferredProp);
}
if (indexInfo) {
const inferredIndexType = inferTargetType(indexInfo.type);
if (!inferredIndexType) {
return undefined;
}
indexInfo = createIndexInfo(inferredIndexType, readonlyMask && indexInfo.isReadonly);
indexInfo = createIndexInfo(inferTargetType(indexInfo.type), readonlyMask && indexInfo.isReadonly);
}
return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
function inferTargetType(sourceType: Type): Type {
inference.candidates = undefined;
inferTypes(inferences, sourceType, templateType);
return inference.candidates && getUnionType(inference.candidates, /*subtypeReduction*/ true);
return inference.candidates ? getUnionType(inference.candidates, /*subtypeReduction*/ true) : emptyObjectType;
}
}
@@ -10955,7 +10965,7 @@ namespace ts {
const targetLen = targetSignatures.length;
const len = sourceLen < targetLen ? sourceLen : targetLen;
for (let i = 0; i < len; i++) {
inferFromSignature(getErasedSignature(sourceSignatures[sourceLen - len + i]), getErasedSignature(targetSignatures[targetLen - len + i]));
inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getBaseSignature(targetSignatures[targetLen - len + i]));
}
}
@@ -11377,6 +11387,16 @@ namespace ts {
}
function getTypeWithFacts(type: Type, include: TypeFacts) {
if (type.flags & TypeFlags.IndexedAccess) {
// TODO (weswig): This is a substitute for a lazy negated type to remove the types indicated by the TypeFacts from the (potential) union the IndexedAccess refers to
// - See discussion in https://github.com/Microsoft/TypeScript/pull/19275 for details, and test `strictNullNotNullIndexTypeShouldWork` for current behavior
const baseConstraint = getBaseConstraintOfType(type) || emptyObjectType;
const result = filterType(baseConstraint, t => (getTypeFacts(t) & include) !== 0);
if (result !== baseConstraint) {
return result;
}
return type;
}
return filterType(type, t => (getTypeFacts(t) & include) !== 0);
}
@@ -13043,12 +13063,14 @@ namespace ts {
// at this point the only legal case for parent is ClassLikeDeclaration
const classLikeDeclaration = <ClassLikeDeclaration>container.parent;
if (!getClassExtendsHeritageClauseElement(classLikeDeclaration)) {
error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class);
return unknownType;
}
const classType = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(classLikeDeclaration));
const baseClassType = classType && getBaseTypes(classType)[0];
if (!baseClassType) {
if (!getClassExtendsHeritageClauseElement(classLikeDeclaration)) {
error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class);
}
return unknownType;
}
@@ -13182,7 +13204,7 @@ namespace ts {
}
// Return contextual type of parameter or undefined if no contextual type is available
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type {
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type | undefined {
const func = parameter.parent;
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
const iife = getImmediatelyInvokedFunctionExpression(func);
@@ -13208,7 +13230,12 @@ namespace ts {
if (contextualSignature) {
const funcHasRestParameters = hasRestParameter(func);
const len = func.parameters.length - (funcHasRestParameters ? 1 : 0);
const indexOfParameter = indexOf(func.parameters, parameter);
let indexOfParameter = indexOf(func.parameters, parameter);
if (getThisParameter(func) !== undefined && !contextualSignature.thisParameter) {
Debug.assert(indexOfParameter !== 0); // Otherwise we should not have called `getContextuallyTypedParameterType`.
indexOfParameter -= 1;
}
if (indexOfParameter < len) {
return getTypeAtPosition(contextualSignature, indexOfParameter);
}
@@ -13413,7 +13440,7 @@ namespace ts {
// exists. Otherwise, it is the type of the string index signature in T, if one exists.
function getContextualTypeForObjectLiteralMethod(node: MethodDeclaration): Type {
Debug.assert(isObjectLiteralMethod(node));
if (isInsideWithStatementBody(node)) {
if (node.flags & NodeFlags.InWithStatement) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
}
@@ -13532,7 +13559,7 @@ namespace ts {
* @returns the contextual type of an expression.
*/
function getContextualType(node: Expression): Type | undefined {
if (isInsideWithStatementBody(node)) {
if (node.flags & NodeFlags.InWithStatement) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
}
@@ -15022,7 +15049,7 @@ namespace ts {
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
markPropertyAsReferenced(prop, node);
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
getNodeLinks(node).resolvedSymbol = prop;
@@ -15212,12 +15239,21 @@ namespace ts {
return bestCandidate;
}
function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined) {
function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined, isThisAccess: boolean) {
if (prop &&
noUnusedIdentifiers &&
(prop.flags & SymbolFlags.ClassMember) &&
prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Private)
&& !(nodeForCheckWriteOnly && isWriteOnlyAccess(nodeForCheckWriteOnly))) {
if (isThisAccess) {
// Find any FunctionLikeDeclaration because those create a new 'this' binding. But this should only matter for methods (or getters/setters).
const containingMethod = findAncestor(nodeForCheckWriteOnly, isFunctionLikeDeclaration);
if (containingMethod && containingMethod.symbol === prop) {
return;
}
}
if (getCheckFlags(prop) & CheckFlags.Instantiated) {
getSymbolLinks(prop).target.isReferenced = true;
}
@@ -16847,9 +16883,10 @@ namespace ts {
// in a JS file
// Note:JS inferred classes might come from a variable declaration instead of a function declaration.
// In this case, using getResolvedSymbol directly is required to avoid losing the members from the declaration.
const funcSymbol = node.expression.kind === SyntaxKind.Identifier ?
getResolvedSymbol(node.expression as Identifier) :
checkExpression(node.expression).symbol;
let funcSymbol = checkExpression(node.expression).symbol;
if (!funcSymbol && node.expression.kind === SyntaxKind.Identifier) {
funcSymbol = getResolvedSymbol(node.expression as Identifier);
}
const type = funcSymbol && getJavaScriptClassType(funcSymbol);
if (type) {
return type;
@@ -19163,7 +19200,10 @@ namespace ts {
// There is no resolved symbol cached if the type resolved to a builtin
// via JSDoc type reference resolution (eg, Boolean became boolean), none
// of which are generic when they have no associated symbol
error(node, Diagnostics.Type_0_is_not_generic, typeToString(type));
// (additionally, JSDoc's index signature syntax, Object<string, T> actually uses generic syntax without being generic)
if (!isJSDocIndexSignature(node)) {
error(node, Diagnostics.Type_0_is_not_generic, typeToString(type));
}
return;
}
let typeParameters = symbol.flags & SymbolFlags.TypeAlias && getSymbolLinks(symbol).typeParameters;
@@ -19680,7 +19720,7 @@ namespace ts {
return undefined;
}
return typeAsAwaitable.awaitedTypeOfType = getUnionType(types, /*subtypeReduction*/ true);
return typeAsAwaitable.awaitedTypeOfType = getUnionType(types);
}
const promisedType = getPromisedTypeOfPromise(type);
@@ -20709,7 +20749,7 @@ namespace ts {
const parentType = getTypeForBindingElementParent(parent);
const name = node.propertyName || <Identifier>node.name;
const property = getPropertyOfType(parentType, getTextOfPropertyName(name));
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined); // A destructuring is never a write-only reference.
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
if (parent.initializer && property) {
checkPropertyAccessibility(parent, parent.initializer, parentType, property);
}
@@ -20752,7 +20792,7 @@ namespace ts {
// initializer is consistent with type associated with the node
const declarationType = convertAutoToAny(getWidenedTypeForVariableLikeDeclaration(node));
if (type !== unknownType && declarationType !== unknownType && !isTypeIdenticalTo(type, declarationType)) {
error(node.name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(node.name), typeToString(type), typeToString(declarationType));
errorNextVariableDeclarationMustHaveSameType(symbol.valueDeclaration, type, node, declarationType);
}
if (node.initializer) {
checkTypeAssignableTo(checkExpressionCached(node.initializer), declarationType, node, /*headMessage*/ undefined);
@@ -20776,6 +20816,21 @@ namespace ts {
}
}
function errorNextVariableDeclarationMustHaveSameType(firstDeclaration: Declaration, firstType: Type, nextDeclaration: Declaration, nextType: Type): void {
const firstSourceFile = getSourceFileOfNode(firstDeclaration);
const firstSpan = getErrorSpanForNode(firstSourceFile, getNameOfDeclaration(firstDeclaration) || firstDeclaration);
const firstLocation = getLineAndCharacterOfPosition(firstSourceFile, firstSpan.start);
const firstLocationDescription = firstSourceFile.fileName + " " + firstLocation.line + ":" + firstLocation.character;
const nextDeclarationName = getNameOfDeclaration(nextDeclaration);
error(
nextDeclarationName,
Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_has_type_1_at_2_but_here_has_type_3,
declarationNameToString(nextDeclarationName),
typeToString(firstType),
firstLocationDescription,
typeToString(nextType));
}
function areDeclarationFlagsIdentical(left: Declaration, right: Declaration) {
if ((left.kind === SyntaxKind.Parameter && right.kind === SyntaxKind.VariableDeclaration) ||
(left.kind === SyntaxKind.VariableDeclaration && right.kind === SyntaxKind.Parameter)) {
@@ -23069,21 +23124,8 @@ namespace ts {
// Language service support
function isInsideWithStatementBody(node: Node): boolean {
if (node) {
while (node.parent) {
if (node.parent.kind === SyntaxKind.WithStatement && (<WithStatement>node.parent).statement === node) {
return true;
}
node = node.parent;
}
}
return false;
}
function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] {
if (isInsideWithStatementBody(location)) {
if (location.flags & NodeFlags.InWithStatement) {
// We cannot answer semantic questions within a with block, do not proceed any further
return [];
}
@@ -23383,7 +23425,7 @@ namespace ts {
return isExternalModule(<SourceFile>node) ? getMergedSymbol(node.symbol) : undefined;
}
if (isInsideWithStatementBody(node)) {
if (node.flags & NodeFlags.InWithStatement) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
}
@@ -23491,7 +23533,7 @@ namespace ts {
}
function getTypeOfNode(node: Node): Type {
if (isInsideWithStatementBody(node)) {
if (node.flags & NodeFlags.InWithStatement) {
// We cannot answer semantic questions within a with block, do not proceed any further
return unknownType;
}
@@ -25580,13 +25622,16 @@ namespace ts {
}
function getAmbientModules(): Symbol[] {
const result: Symbol[] = [];
globals.forEach((global, sym) => {
if (ambientModuleSymbolRegex.test(unescapeLeadingUnderscores(sym))) {
result.push(global);
}
});
return result;
if (!ambientModulesCache) {
ambientModulesCache = [];
globals.forEach((global, sym) => {
// No need to `unescapeLeadingUnderscores`, an escaped symbol is never an ambient module.
if (ambientModuleSymbolRegex.test(sym as string)) {
ambientModulesCache.push(global);
}
});
}
return ambientModulesCache;
}
function checkGrammarImportCallExpression(node: ImportCall): boolean {
+14 -14
View File
@@ -1183,8 +1183,8 @@ namespace ts {
}
}
function isDoubleQuotedString(node: Node) {
return node.kind === SyntaxKind.StringLiteral && getSourceTextOfNodeFromSourceFile(sourceFile, node).charCodeAt(0) === CharacterCodes.doubleQuote;
function isDoubleQuotedString(node: Node): boolean {
return isStringLiteral(node) && isStringDoubleQuoted(node, sourceFile);
}
}
@@ -1440,9 +1440,9 @@ namespace ts {
function getFileNames(): ExpandResult {
let filesSpecs: ReadonlyArray<string>;
if (hasProperty(raw, "files") && !isNullOrUndefined(raw["files"])) {
if (isArray(raw["files"])) {
filesSpecs = <ReadonlyArray<string>>raw["files"];
if (hasProperty(raw, "files") && !isNullOrUndefined(raw.files)) {
if (isArray(raw.files)) {
filesSpecs = <ReadonlyArray<string>>raw.files;
if (filesSpecs.length === 0) {
createCompilerDiagnosticOnlyIfJson(Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json");
}
@@ -1453,9 +1453,9 @@ namespace ts {
}
let includeSpecs: ReadonlyArray<string>;
if (hasProperty(raw, "include") && !isNullOrUndefined(raw["include"])) {
if (isArray(raw["include"])) {
includeSpecs = <ReadonlyArray<string>>raw["include"];
if (hasProperty(raw, "include") && !isNullOrUndefined(raw.include)) {
if (isArray(raw.include)) {
includeSpecs = <ReadonlyArray<string>>raw.include;
}
else {
createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "include", "Array");
@@ -1463,16 +1463,16 @@ namespace ts {
}
let excludeSpecs: ReadonlyArray<string>;
if (hasProperty(raw, "exclude") && !isNullOrUndefined(raw["exclude"])) {
if (isArray(raw["exclude"])) {
excludeSpecs = <ReadonlyArray<string>>raw["exclude"];
if (hasProperty(raw, "exclude") && !isNullOrUndefined(raw.exclude)) {
if (isArray(raw.exclude)) {
excludeSpecs = <ReadonlyArray<string>>raw.exclude;
}
else {
createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "exclude", "Array");
}
}
else {
const outDir = raw["compilerOptions"] && raw["compilerOptions"]["outDir"];
const outDir = raw.compilerOptions && raw.compilerOptions.outDir;
if (outDir) {
excludeSpecs = [outDir];
}
@@ -1591,7 +1591,7 @@ namespace ts {
const options = convertCompilerOptionsFromJsonWorker(json.compilerOptions, basePath, errors, configFileName);
// typingOptions has been deprecated and is only supported for backward compatibility purposes.
// It should be removed in future releases - use typeAcquisition instead.
const typeAcquisition = convertTypeAcquisitionFromJsonWorker(json["typeAcquisition"] || json["typingOptions"], basePath, errors, configFileName);
const typeAcquisition = convertTypeAcquisitionFromJsonWorker(json.typeAcquisition || json.typingOptions, basePath, errors, configFileName);
json.compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);
let extendedConfigPath: Path;
@@ -1748,7 +1748,7 @@ namespace ts {
if (!hasProperty(jsonOption, compileOnSaveCommandLineOption.name)) {
return undefined;
}
const result = convertJsonOption(compileOnSaveCommandLineOption, jsonOption["compileOnSave"], basePath, errors);
const result = convertJsonOption(compileOnSaveCommandLineOption, jsonOption.compileOnSave, basePath, errors);
if (typeof result === "boolean" && result) {
return result;
}
+58 -7
View File
@@ -4,7 +4,7 @@
namespace ts {
// WARNING: The script `configureNightly.ts` uses a regexp to parse out these values.
// If changing the text in this section, be sure to test `configureNightly` too.
export const versionMajorMinor = "2.6";
export const versionMajorMinor = "2.7";
/** The version of the TypeScript compiler release */
export const version = `${versionMajorMinor}.0`;
}
@@ -33,8 +33,8 @@ namespace ts {
// Using 'delete' on an object causes V8 to put the object in dictionary mode.
// This disables creation of hidden classes, which are expensive when an object is
// constantly changing shape.
map["__"] = undefined;
delete map["__"];
map.__ = undefined;
delete map.__;
return map;
}
@@ -191,6 +191,18 @@ namespace ts {
}
return undefined;
}
/** Like `forEach`, but suitable for use with numbers and strings (which may be falsy). */
export function firstDefined<T, U>(array: ReadonlyArray<T> | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
for (let i = 0; i < array.length; i++) {
const result = callback(array[i], i);
if (result !== undefined) {
return result;
}
}
return undefined;
}
/**
* Iterates through the parent chain of a node and performs the callback on each parent until the callback
* returns a truthy value, then returns that value.
@@ -261,6 +273,16 @@ namespace ts {
return undefined;
}
export function findLast<T>(array: ReadonlyArray<T>, predicate: (element: T, index: number) => boolean): T | undefined {
for (let i = array.length - 1; i >= 0; i--) {
const value = array[i];
if (predicate(value, i)) {
return value;
}
}
return undefined;
}
/** Works like Array.prototype.findIndex, returning `-1` if no element satisfying the predicate is found. */
export function findIndex<T>(array: ReadonlyArray<T>, predicate: (element: T, index: number) => boolean): number {
for (let i = 0; i < array.length; i++) {
@@ -1147,6 +1169,14 @@ namespace ts {
return result;
}
export function arrayToNumericMap<T>(array: ReadonlyArray<T>, makeKey: (value: T) => number): T[] {
const result: T[] = [];
for (const value of array) {
result[makeKey(value)] = value;
}
return result;
}
/**
* Creates a set from the elements of an array.
*
@@ -1831,7 +1861,7 @@ namespace ts {
return i < 0 ? path : path.substring(i + 1);
}
export function combinePaths(path1: string, path2: string) {
export function combinePaths(path1: string, path2: string): string {
if (!(path1 && path1.length)) return path2;
if (!(path2 && path2.length)) return path1;
if (getRootLength(path2) !== 0) return path2;
@@ -2661,6 +2691,16 @@ namespace ts {
return find<Extension>(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e));
}
// Retrieves any string from the final "." onwards from a base file name.
// Unlike extensionFromPath, which throws an exception on unrecognized extensions.
export function getAnyExtensionFromPath(path: string): string | undefined {
const baseFileName = getBaseFileName(path);
const extensionIndex = baseFileName.lastIndexOf(".");
if (extensionIndex >= 0) {
return baseFileName.substring(extensionIndex);
}
}
export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) {
return sourceFile.checkJsDirective ? sourceFile.checkJsDirective.enabled : compilerOptions.checkJs;
}
@@ -2671,8 +2711,14 @@ namespace ts {
export function assertTypeIsNever(_: never): void { }
export interface FileAndDirectoryExistence {
fileExists: boolean;
directoryExists: boolean;
}
export interface CachedDirectoryStructureHost extends DirectoryStructureHost {
addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): void;
/** Returns the queried result for the file exists and directory exists if at all it was done */
addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): FileAndDirectoryExistence | undefined;
addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind): void;
clearCache(): void;
}
@@ -2842,8 +2888,13 @@ namespace ts {
if (parentResult) {
const baseName = getBaseNameOfFileName(fileOrDirectory);
if (parentResult) {
updateFilesOfFileSystemEntry(parentResult, baseName, host.fileExists(fileOrDirectoryPath));
updateFileSystemEntry(parentResult.directories, baseName, host.directoryExists(fileOrDirectoryPath));
const fsQueryResult: FileAndDirectoryExistence = {
fileExists: host.fileExists(fileOrDirectoryPath),
directoryExists: host.directoryExists(fileOrDirectoryPath)
};
updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
updateFileSystemEntry(parentResult.directories, baseName, fsQueryResult.directoryExists);
return fsQueryResult;
}
}
}
+21 -5
View File
@@ -1316,7 +1316,7 @@
"category": "Error",
"code": 2402
},
"Subsequent variable declarations must have the same type. Variable '{0}' must be of type '{1}', but here has type '{2}'.": {
"Subsequent variable declarations must have the same type. Variable '{0}' has type '{1}' at {2}, but here has type '{3}'.": {
"category": "Error",
"code": 2403
},
@@ -3539,6 +3539,14 @@
"category": "Error",
"code": 8025
},
"Expected {0} type arguments; provide these with an '@extends' tag.": {
"category": "Error",
"code": 8026
},
"Expected {0}-{1} type arguments; provide these with an '@extends' tag.": {
"category": "Error",
"code": 8027
},
"Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": {
"category": "Error",
"code": 9002
@@ -3661,7 +3669,7 @@
"category": "Error",
"code": 90010
},
"Import {0} from {1}.": {
"Import '{0}' from \"{1}\".": {
"category": "Message",
"code": 90013
},
@@ -3669,7 +3677,7 @@
"category": "Message",
"code": 90014
},
"Add {0} to existing import declaration from {1}.": {
"Add '{0}' to existing import declaration from \"{1}\".": {
"category": "Message",
"code": 90015
},
@@ -3758,12 +3766,20 @@
"category": "Message",
"code": 95008
},
"Infer type of '{0}' from usage.": {
"Annotate with type from JSDoc": {
"category": "Message",
"code": 95009
},
"Infer parameter types from usage.": {
"Annotate with types from JSDoc": {
"category": "Message",
"code": 95010
},
"Infer type of '{0}' from usage.": {
"category": "Message",
"code": 95011
},
"Infer parameter types from usage.": {
"category": "Message",
"code": 95012
}
}
+53 -2
View File
@@ -546,6 +546,8 @@ namespace ts {
return emitTypeReference(<TypeReferenceNode>node);
case SyntaxKind.FunctionType:
return emitFunctionType(<FunctionTypeNode>node);
case SyntaxKind.JSDocFunctionType:
return emitJSDocFunctionType(node as JSDocFunctionType);
case SyntaxKind.ConstructorType:
return emitConstructorType(<ConstructorTypeNode>node);
case SyntaxKind.TypeQuery:
@@ -574,6 +576,20 @@ namespace ts {
return emitMappedType(<MappedTypeNode>node);
case SyntaxKind.LiteralType:
return emitLiteralType(<LiteralTypeNode>node);
case SyntaxKind.JSDocAllType:
write("*");
return;
case SyntaxKind.JSDocUnknownType:
write("?");
return;
case SyntaxKind.JSDocNullableType:
return emitJSDocNullableType(node as JSDocNullableType);
case SyntaxKind.JSDocNonNullableType:
return emitJSDocNonNullableType(node as JSDocNonNullableType);
case SyntaxKind.JSDocOptionalType:
return emitJSDocOptionalType(node as JSDocOptionalType);
case SyntaxKind.JSDocVariadicType:
return emitJSDocVariadicType(node as JSDocVariadicType);
// Binding patterns
case SyntaxKind.ObjectBindingPattern:
@@ -914,9 +930,16 @@ namespace ts {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emitIfPresent(node.dotDotDotToken);
emit(node.name);
if (node.name) {
emit(node.name);
}
emitIfPresent(node.questionToken);
emitWithPrefix(": ", node.type);
if (node.parent && node.parent.kind === SyntaxKind.JSDocFunctionType && !node.name) {
emit(node.type);
}
else {
emitWithPrefix(": ", node.type);
}
emitExpressionWithPrefix(" = ", node.initializer);
}
@@ -1035,6 +1058,29 @@ namespace ts {
emit(node.type);
}
function emitJSDocFunctionType(node: JSDocFunctionType) {
write("function");
emitParameters(node, node.parameters);
write(":");
emit(node.type);
}
function emitJSDocNullableType(node: JSDocNullableType) {
write("?");
emit(node.type);
}
function emitJSDocNonNullableType(node: JSDocNonNullableType) {
write("!");
emit(node.type);
}
function emitJSDocOptionalType(node: JSDocOptionalType) {
emit(node.type);
write("=");
}
function emitConstructorType(node: ConstructorTypeNode) {
write("new ");
emitTypeParameters(node, node.typeParameters);
@@ -1060,6 +1106,11 @@ namespace ts {
write("[]");
}
function emitJSDocVariadicType(node: JSDocVariadicType) {
write("...");
emit(node.type);
}
function emitTupleType(node: TupleTypeNode) {
write("[");
emitList(node, node.elementTypes, ListFormat.TupleTypeElements);
+54 -24
View File
@@ -77,16 +77,20 @@ namespace ts {
traceEnabled: boolean;
}
interface PackageJson {
name?: string;
version?: string;
/** Just the fields that we use for module resolution. */
interface PackageJsonPathFields {
typings?: string;
types?: string;
main?: string;
}
interface PackageJson extends PackageJsonPathFields {
name?: string;
version?: string;
}
/** Reads from "main" or "types"/"typings" depending on `extensions`. */
function tryReadPackageJsonFields(readTypes: boolean, jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState): string | undefined {
function tryReadPackageJsonFields(readTypes: boolean, jsonContent: PackageJsonPathFields, baseDirectory: string, state: ModuleResolutionState): string | undefined {
return readTypes ? tryReadFromField("typings") || tryReadFromField("types") : tryReadFromField("main");
function tryReadFromField(fieldName: "typings" | "types" | "main"): string | undefined {
@@ -124,7 +128,11 @@ namespace ts {
}
}
export function getEffectiveTypeRoots(options: CompilerOptions, host: { directoryExists?: (directoryName: string) => boolean, getCurrentDirectory?: () => string }): string[] | undefined {
export interface GetEffectiveTypeRootsHost {
directoryExists?(directoryName: string): boolean;
getCurrentDirectory?(): string;
}
export function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined {
if (options.typeRoots) {
return options.typeRoots;
}
@@ -886,7 +894,7 @@ namespace ts {
return withPackageId(packageId, loadNodeModuleFromDirectoryWorker(extensions, candidate, failedLookupLocations, onlyRecordFailures, state, packageJsonContent));
}
function loadNodeModuleFromDirectoryWorker(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, packageJsonContent: PackageJson | undefined): PathAndExtension | undefined {
function loadNodeModuleFromDirectoryWorker(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, packageJsonContent: PackageJsonPathFields | undefined): PathAndExtension | undefined {
const fromPackageJson = packageJsonContent && loadModuleFromPackageJson(packageJsonContent, extensions, candidate, failedLookupLocations, state);
if (fromPackageJson) {
return fromPackageJson;
@@ -901,7 +909,7 @@ namespace ts {
failedLookupLocations: Push<string>,
onlyRecordFailures: boolean,
{ host, traceEnabled }: ModuleResolutionState,
): { packageJsonContent: PackageJson | undefined, packageId: PackageId | undefined } {
): { found: boolean, packageJsonContent: PackageJsonPathFields | undefined, packageId: PackageId | undefined } {
const directoryExists = !onlyRecordFailures && directoryProbablyExists(nodeModuleDirectory, host);
const packageJsonPath = pathToPackageJson(nodeModuleDirectory);
if (directoryExists && host.fileExists(packageJsonPath)) {
@@ -912,7 +920,7 @@ namespace ts {
const packageId: PackageId = typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string"
? { name: packageJsonContent.name, subModuleName, version: packageJsonContent.version }
: undefined;
return { packageJsonContent, packageId };
return { found: true, packageJsonContent, packageId };
}
else {
if (directoryExists && traceEnabled) {
@@ -920,11 +928,11 @@ namespace ts {
}
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
failedLookupLocations.push(packageJsonPath);
return { packageJsonContent: undefined, packageId: undefined };
return { found: false, packageJsonContent: undefined, packageId: undefined };
}
}
function loadModuleFromPackageJson(jsonContent: PackageJson, extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): PathAndExtension | undefined {
function loadModuleFromPackageJson(jsonContent: PackageJsonPathFields, extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): PathAndExtension | undefined {
const file = tryReadPackageJsonFields(extensions !== Extensions.JavaScript, jsonContent, candidate, state);
if (!file) {
return undefined;
@@ -976,16 +984,29 @@ namespace ts {
}
function loadModuleFromNodeModulesFolder(extensions: Extensions, moduleName: string, nodeModulesFolder: string, nodeModulesFolderExists: boolean, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
const { packageName, rest } = getPackageName(moduleName);
const packageRootPath = combinePaths(nodeModulesFolder, packageName);
const { packageJsonContent, packageId } = getPackageJsonInfo(packageRootPath, rest, failedLookupLocations, !nodeModulesFolderExists, state);
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
// First look for a nested package.json, as in `node_modules/foo/bar/package.json`.
let packageJsonContent: PackageJsonPathFields | undefined;
let packageId: PackageId | undefined;
const packageInfo = getPackageJsonInfo(candidate, "", failedLookupLocations, /*onlyRecordFailures*/ !nodeModulesFolderExists, state);
if (packageInfo.found) {
({ packageJsonContent, packageId } = packageInfo);
}
else {
const { packageName, rest } = getPackageName(moduleName);
if (rest !== "") { // If "rest" is empty, we just did this search above.
const packageRootPath = combinePaths(nodeModulesFolder, packageName);
// Don't use a "types" or "main" from here because we're not loading the root, but a subdirectory -- just here for the packageId.
packageId = getPackageJsonInfo(packageRootPath, rest, failedLookupLocations, !nodeModulesFolderExists, state).packageId;
}
}
const pathAndExtension = loadModuleFromFile(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
loadNodeModuleFromDirectoryWorker(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state, packageJsonContent);
return withPackageId(packageId, pathAndExtension);
}
function getPackageName(moduleName: string): { packageName: string, rest: string } {
/* @internal */
export function getPackageName(moduleName: string): { packageName: string, rest: string } {
let idx = moduleName.indexOf(directorySeparator);
if (moduleName[0] === "@") {
idx = moduleName.indexOf(directorySeparator, idx + 1);
@@ -1043,18 +1064,27 @@ namespace ts {
const mangledScopedPackageSeparator = "__";
/** For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`. */
function mangleScopedPackage(moduleName: string, state: ModuleResolutionState): string {
if (startsWith(moduleName, "@")) {
const replaceSlash = moduleName.replace(ts.directorySeparator, mangledScopedPackageSeparator);
if (replaceSlash !== moduleName) {
const mangled = replaceSlash.slice(1); // Take off the "@"
if (state.traceEnabled) {
trace(state.host, Diagnostics.Scoped_package_detected_looking_in_0, mangled);
}
return mangled;
function mangleScopedPackage(packageName: string, state: ModuleResolutionState): string {
const mangled = getMangledNameForScopedPackage(packageName);
if (state.traceEnabled && mangled !== packageName) {
trace(state.host, Diagnostics.Scoped_package_detected_looking_in_0, mangled);
}
return mangled;
}
/* @internal */
export function getTypesPackageName(packageName: string): string {
return `@types/${getMangledNameForScopedPackage(packageName)}`;
}
function getMangledNameForScopedPackage(packageName: string): string {
if (startsWith(packageName, "@")) {
const replaceSlash = packageName.replace(ts.directorySeparator, mangledScopedPackageSeparator);
if (replaceSlash !== packageName) {
return replaceSlash.slice(1); // Take off the "@"
}
}
return moduleName;
return packageName;
}
/* @internal */
+1 -1
View File
@@ -4724,7 +4724,7 @@ namespace ts {
parseExpected(SyntaxKind.OpenParenToken);
node.expression = allowInAnd(parseExpression);
parseExpected(SyntaxKind.CloseParenToken);
node.statement = parseStatement();
node.statement = doInsideOfContext(NodeFlags.InWithStatement, parseStatement);
return finishNode(node);
}
+8 -2
View File
@@ -1049,6 +1049,10 @@ namespace ts {
// update fileName -> file mapping
for (let i = 0; i < newSourceFiles.length; i++) {
filesByName.set(filePaths[i], newSourceFiles[i]);
// Set the file as found during node modules search if it was found that way in old progra,
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(filePaths[i]))) {
sourceFilesFoundSearchingNodeModules.set(filePaths[i], true);
}
}
files = newSourceFiles;
@@ -1278,8 +1282,10 @@ namespace ts {
const typeChecker = getDiagnosticsProducingTypeChecker();
Debug.assert(!!sourceFile.bindDiagnostics);
// For JavaScript files, we don't want to report semantic errors unless explicitly requested.
const includeBindAndCheckDiagnostics = !isSourceFileJavaScript(sourceFile) || isCheckJsEnabledForFile(sourceFile, options);
// By default, only type-check .ts, .tsx, and 'External' files (external files are added by plugins)
const includeBindAndCheckDiagnostics = sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX ||
sourceFile.scriptKind === ScriptKind.External || isCheckJsEnabledForFile(sourceFile, options);
const bindDiagnostics = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics.getDiagnostics(sourceFile.fileName);
+58 -14
View File
@@ -64,6 +64,7 @@ namespace ts {
interface DirectoryOfFailedLookupWatch {
dir: string;
dirPath: Path;
ignore?: true;
}
export const maxNumberOfFilesToIterateForInvalidation = 256;
@@ -319,6 +320,33 @@ namespace ts {
return endsWith(dirPath, "/node_modules");
}
function isDirectoryAtleastAtLevelFromFSRoot(dirPath: Path, minLevels: number) {
for (let searchIndex = getRootLength(dirPath); minLevels > 0; minLevels--) {
searchIndex = dirPath.indexOf(directorySeparator, searchIndex) + 1;
if (searchIndex === 0) {
// Folder isnt at expected minimun levels
return false;
}
}
return true;
}
function canWatchDirectory(dirPath: Path) {
return isDirectoryAtleastAtLevelFromFSRoot(dirPath,
// When root is "/" do not watch directories like:
// "/", "/user", "/user/username", "/user/username/folderAtRoot"
// When root is "c:/" do not watch directories like:
// "c:/", "c:/folderAtRoot"
dirPath.charCodeAt(0) === CharacterCodes.slash ? 3 : 1);
}
function filterFSRootDirectoriesToWatch(watchPath: DirectoryOfFailedLookupWatch, dirPath: Path): DirectoryOfFailedLookupWatch {
if (!canWatchDirectory(dirPath)) {
watchPath.ignore = true;
}
return watchPath;
}
function getDirectoryToWatchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path): DirectoryOfFailedLookupWatch {
if (isInDirectoryPath(rootPath, failedLookupLocationPath)) {
return { dir: rootDir, dirPath: rootPath };
@@ -335,7 +363,7 @@ namespace ts {
// If the directory is node_modules use it to watch
if (isNodeModulesDirectory(dirPath)) {
return { dir, dirPath };
return filterFSRootDirectoriesToWatch({ dir, dirPath }, getDirectoryPath(dirPath));
}
// Use some ancestor of the root directory
@@ -350,7 +378,7 @@ namespace ts {
}
}
return { dir, dirPath };
return filterFSRootDirectoriesToWatch({ dir, dirPath }, dirPath);
}
function isPathWithDefaultFailedLookupExtension(path: Path) {
@@ -391,13 +419,15 @@ namespace ts {
const refCount = customFailedLookupPaths.get(failedLookupLocationPath) || 0;
customFailedLookupPaths.set(failedLookupLocationPath, refCount + 1);
}
const { dir, dirPath } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
if (dirWatcher) {
dirWatcher.refCount++;
}
else {
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (!ignore) {
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
if (dirWatcher) {
dirWatcher.refCount++;
}
else {
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
}
}
}
}
@@ -422,10 +452,12 @@ namespace ts {
customFailedLookupPaths.set(failedLookupLocationPath, refCount - 1);
}
}
const { dirPath } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
// Do not close the watcher yet since it might be needed by other failed lookup locations.
dirWatcher.refCount--;
const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (!ignore) {
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
// Do not close the watcher yet since it might be needed by other failed lookup locations.
dirWatcher.refCount--;
}
}
}
@@ -577,7 +609,8 @@ namespace ts {
}
// we need to assume the directories exist to ensure that we can get all the type root directories that get included
const typeRoots = getEffectiveTypeRoots(options, { directoryExists: returnTrue, getCurrentDirectory });
// But filter directories that are at root level to say directory doesnt exist, so that we arent watching them
const typeRoots = getEffectiveTypeRoots(options, { directoryExists: directoryExistsForTypeRootWatch, getCurrentDirectory });
if (typeRoots) {
mutateMap(
typeRootsWatches,
@@ -592,5 +625,16 @@ namespace ts {
closeTypeRootsWatch();
}
}
/**
* Use this function to return if directory exists to get type roots to watch
* If we return directory exists then only the paths will be added to type roots
* Hence return true for all directories except root directories which are filtered from watching
*/
function directoryExistsForTypeRootWatch(nodeTypesDirectory: string) {
const dir = getDirectoryPath(getDirectoryPath(nodeTypesDirectory));
const dirPath = resolutionHost.toPath(dir);
return dirPath === rootPath || canWatchDirectory(dirPath);
}
}
}
+1 -1
View File
@@ -351,7 +351,7 @@ namespace ts {
/**
* We assume the first line starts at position 0 and 'position' is non-negative.
*/
export function computeLineAndCharacterOfPosition(lineStarts: ReadonlyArray<number>, position: number) {
export function computeLineAndCharacterOfPosition(lineStarts: ReadonlyArray<number>, position: number): LineAndCharacter {
let lineNumber = binarySearch(lineStarts, position);
if (lineNumber < 0) {
// If the actual position was not found,
+1 -1
View File
@@ -122,7 +122,7 @@ namespace ts {
}
forEach(signature.typeParameters, visitType);
for (const parameter of signature.parameters){
for (const parameter of signature.parameters) {
visitSymbol(parameter);
}
visitType(getRestTypeOfSignature(signature));
+2 -2
View File
@@ -131,7 +131,7 @@ namespace ts {
const _os = require("os");
const _crypto = require("crypto");
const useNonPollingWatchers = process.env["TSC_NONPOLLING_WATCHER"];
const useNonPollingWatchers = process.env.TSC_NONPOLLING_WATCHER;
function createWatchedFileSet() {
const dirWatchers = createMap<DirectoryWatcher>();
@@ -573,7 +573,7 @@ namespace ts {
function recursiveCreateDirectory(directoryPath: string, sys: System) {
const basePath = getDirectoryPath(directoryPath);
const shouldCreateParent = directoryPath !== basePath && !sys.directoryExists(basePath);
const shouldCreateParent = basePath !== "" && directoryPath !== basePath && !sys.directoryExists(basePath);
if (shouldCreateParent) {
recursiveCreateDirectory(basePath, sys);
}
+2 -1
View File
@@ -3676,7 +3676,8 @@ namespace ts {
// Do not do this in the global scope, as any variable we currently generate could conflict with
// variables from outside of the current compilation. In the future, we can revisit this behavior.
if (isExternalModule(currentSourceFile)) {
const tempVar = createTempVariable(recordTaggedTemplateString);
const tempVar = createUniqueName("templateObject");
recordTaggedTemplateString(tempVar);
templateArguments[0] = createLogicalOr(
tempVar,
createAssignment(
+1 -1
View File
@@ -463,7 +463,7 @@ namespace ts {
);
// Mark this node as originally an async function
(generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody;
(generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody | EmitFlags.ReuseTempVariableScope;
return createCall(
getHelperName("__awaiter"),
+4 -1
View File
@@ -826,7 +826,7 @@ namespace ts {
/*needsValue*/ false,
createAssignment
)
: createAssignment(node.name, visitNode(node.initializer, destructuringAndImportCallVisitor, isExpression));
: node.initializer ? createAssignment(node.name, visitNode(node.initializer, destructuringAndImportCallVisitor, isExpression)) : node.name;
}
/**
@@ -1296,6 +1296,9 @@ namespace ts {
let expressions: Expression[];
for (const variable of node.declarations) {
expressions = append(expressions, transformInitializedVariable(variable, /*isExportedDeclaration*/ false));
if (!variable.initializer) {
hoistBindingElement(variable);
}
}
return expressions ? inlineExpressions(expressions) : createOmittedExpression();
+6 -2
View File
@@ -451,6 +451,7 @@ namespace ts {
/* @internal */
PossiblyContainsDynamicImport = 1 << 19,
JSDoc = 1 << 20, // If node was parsed inside jsdoc
/* @internal */ InWithStatement = 1 << 21, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`)
BlockScoped = Let | Const,
@@ -458,7 +459,7 @@ namespace ts {
ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions,
// Parsing context flags
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | InWithStatement,
// Exclude these flags when parsing a Type
TypeExcludesFlags = YieldContext | AwaitContext,
@@ -1055,6 +1056,7 @@ namespace ts {
export interface StringLiteral extends LiteralExpression {
kind: SyntaxKind.StringLiteral;
/* @internal */ textSourceNode?: Identifier | StringLiteral | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms).
/** Note: this is only set when synthesizing a node, not during parsing. */
/* @internal */ singleQuote?: boolean;
}
@@ -3625,6 +3627,7 @@ namespace ts {
export interface JsFileExtensionInfo {
extension: string;
isMixedContent: boolean;
scriptKind?: ScriptKind;
}
export interface DiagnosticMessage {
@@ -3809,9 +3812,10 @@ namespace ts {
}
export interface LineAndCharacter {
/** 0-based. */
line: number;
/*
* This value denotes the character position in line and is different from the 'column' because of tab characters.
* 0-based. This value denotes the character position in line and is different from the 'column' because of tab characters.
*/
character: number;
}
+87 -46
View File
@@ -520,6 +520,17 @@ namespace ts {
}
}
/* @internal */
export function isAnyImportSyntax(node: Node): node is AnyImportSyntax {
switch (node.kind) {
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
return true;
default:
return false;
}
}
// Gets the nearest enclosing block scope container that has the provided node
// as a descendant, that is not the provided node.
export function getEnclosingBlockScopeContainer(node: Node): Node {
@@ -571,14 +582,14 @@ namespace ts {
}
}
export function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): Diagnostic {
export function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
const sourceFile = getSourceFileOfNode(node);
return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2);
return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2, arg3);
}
export function createDiagnosticForNodeInSourceFile(sourceFile: SourceFile, node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): Diagnostic {
export function createDiagnosticForNodeInSourceFile(sourceFile: SourceFile, node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
const span = getErrorSpanForNode(sourceFile, node);
return createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2);
return createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2, arg3);
}
export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain): Diagnostic {
@@ -1349,6 +1360,14 @@ namespace ts {
return node && !!(node.flags & NodeFlags.JSDoc);
}
export function isJSDocIndexSignature(node: TypeReferenceNode | ExpressionWithTypeArguments) {
return isTypeReferenceNode(node) &&
isIdentifier(node.typeName) &&
node.typeName.escapedText === "Object" &&
node.typeArguments && node.typeArguments.length === 2 &&
(node.typeArguments[0].kind === SyntaxKind.StringKeyword || node.typeArguments[0].kind === SyntaxKind.NumberKeyword);
}
/**
* Returns true if the node is a CallExpression to the identifier 'require' with
* exactly one argument (of the form 'require("name")').
@@ -1375,6 +1394,10 @@ namespace ts {
return charCode === CharacterCodes.singleQuote || charCode === CharacterCodes.doubleQuote;
}
export function isStringDoubleQuoted(string: StringLiteral, sourceFile: SourceFile): boolean {
return getSourceTextOfNodeFromSourceFile(sourceFile, string).charCodeAt(0) === CharacterCodes.doubleQuote;
}
/**
* Returns true if the node is a variable declaration whose initializer is a function expression.
* This function does not test if the node is in a JavaScript file or not.
@@ -1515,6 +1538,34 @@ namespace ts {
return getJSDocCommentsAndTags(node);
}
export function getSourceOfAssignment(node: Node): Node {
return isExpressionStatement(node) &&
node.expression && isBinaryExpression(node.expression) &&
node.expression.operatorToken.kind === SyntaxKind.EqualsToken &&
node.expression.right;
}
export function getSingleInitializerOfVariableStatement(node: Node, child?: Node): Node {
return isVariableStatement(node) &&
node.declarationList.declarations.length > 0 &&
(!child || node.declarationList.declarations[0].initializer === child) &&
node.declarationList.declarations[0].initializer;
}
export function getSingleVariableOfVariableStatement(node: Node, child?: Node): Node {
return isVariableStatement(node) &&
node.declarationList.declarations.length > 0 &&
(!child || node.declarationList.declarations[0] === child) &&
node.declarationList.declarations[0];
}
export function getNestedModuleDeclaration(node: Node): Node {
return node.kind === SyntaxKind.ModuleDeclaration &&
(node as ModuleDeclaration).body &&
(node as ModuleDeclaration).body.kind === SyntaxKind.ModuleDeclaration &&
(node as ModuleDeclaration).body;
}
export function getJSDocCommentsAndTags(node: Node): (JSDoc | JSDocTag)[] {
let result: (JSDoc | JSDocTag)[] | undefined;
getJSDocCommentsAndTagsWorker(node);
@@ -1528,35 +1579,15 @@ namespace ts {
// * @returns {number}
// */
// var x = function(name) { return name.length; }
const isInitializerOfVariableDeclarationInStatement =
isVariableLike(parent) &&
parent.initializer === node &&
parent.parent.parent.kind === SyntaxKind.VariableStatement;
const isVariableOfVariableDeclarationStatement = isVariableLike(node) &&
parent.parent.kind === SyntaxKind.VariableStatement;
const variableStatementNode =
isInitializerOfVariableDeclarationInStatement ? parent.parent.parent :
isVariableOfVariableDeclarationStatement ? parent.parent :
undefined;
if (variableStatementNode) {
getJSDocCommentsAndTagsWorker(variableStatementNode);
if (parent && (parent.kind === SyntaxKind.PropertyAssignment || getNestedModuleDeclaration(parent))) {
getJSDocCommentsAndTagsWorker(parent);
}
// Also recognize when the node is the RHS of an assignment expression
const isSourceOfAssignmentExpressionStatement =
parent && parent.parent &&
parent.kind === SyntaxKind.BinaryExpression &&
(parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken &&
parent.parent.kind === SyntaxKind.ExpressionStatement;
if (isSourceOfAssignmentExpressionStatement) {
if (parent && parent.parent &&
(getSingleVariableOfVariableStatement(parent.parent, node) || getSourceOfAssignment(parent.parent))) {
getJSDocCommentsAndTagsWorker(parent.parent);
}
const isModuleDeclaration = node.kind === SyntaxKind.ModuleDeclaration &&
parent && parent.kind === SyntaxKind.ModuleDeclaration;
const isPropertyAssignmentExpression = parent && parent.kind === SyntaxKind.PropertyAssignment;
if (isModuleDeclaration || isPropertyAssignmentExpression) {
getJSDocCommentsAndTagsWorker(parent);
if (parent && parent.parent && parent.parent.parent && getSingleInitializerOfVariableStatement(parent.parent.parent, node)) {
getJSDocCommentsAndTagsWorker(parent.parent.parent);
}
// Pull parameter comments from declaring function as well
@@ -1583,13 +1614,16 @@ namespace ts {
return undefined;
}
const name = node.name.escapedText;
const func = getJSDocHost(node);
if (!isFunctionLike(func)) {
return undefined;
const host = getJSDocHost(node);
const decl = getSourceOfAssignment(host) ||
getSingleInitializerOfVariableStatement(host) ||
getSingleVariableOfVariableStatement(host) ||
getNestedModuleDeclaration(host) ||
host;
if (decl && isFunctionLike(decl)) {
const parameter = find(decl.parameters, p => p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name);
return parameter && parameter.symbol;
}
const parameter = find(func.parameters, p =>
p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name);
return parameter && parameter.symbol;
}
export function getJSDocHost(node: JSDocTag): HasJSDoc {
@@ -2700,11 +2734,11 @@ namespace ts {
* Gets the effective type annotation of a variable, parameter, or property. If the node was
* parsed in a JavaScript file, gets the type annotation from JSDoc.
*/
export function getEffectiveTypeAnnotationNode(node: VariableLikeDeclaration): TypeNode | undefined {
export function getEffectiveTypeAnnotationNode(node: VariableLikeDeclaration, checkJSDoc?: boolean): TypeNode | undefined {
if (node.type) {
return node.type;
}
if (isInJavaScriptFile(node)) {
if (checkJSDoc || isInJavaScriptFile(node)) {
return getJSDocType(node);
}
}
@@ -2713,11 +2747,11 @@ namespace ts {
* Gets the effective return type annotation of a signature. If the node was parsed in a
* JavaScript file, gets the return type annotation from JSDoc.
*/
export function getEffectiveReturnTypeNode(node: SignatureDeclaration): TypeNode | undefined {
export function getEffectiveReturnTypeNode(node: SignatureDeclaration, checkJSDoc?: boolean): TypeNode | undefined {
if (node.type) {
return node.type;
}
if (isInJavaScriptFile(node)) {
if (checkJSDoc || isInJavaScriptFile(node)) {
return getJSDocReturnType(node);
}
}
@@ -2726,11 +2760,11 @@ namespace ts {
* Gets the effective type parameters. If the node was parsed in a
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
*/
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration> {
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters, checkJSDoc?: boolean): ReadonlyArray<TypeParameterDeclaration> {
if (node.typeParameters) {
return node.typeParameters;
}
if (isInJavaScriptFile(node)) {
if (checkJSDoc || isInJavaScriptFile(node)) {
const templateTag = getJSDocTemplateTag(node);
return templateTag && templateTag.typeParameters;
}
@@ -2740,9 +2774,9 @@ namespace ts {
* Gets the effective type annotation of the value parameter of a set accessor. If the node
* was parsed in a JavaScript file, gets the type annotation from JSDoc.
*/
export function getEffectiveSetAccessorTypeAnnotationNode(node: SetAccessorDeclaration): TypeNode {
export function getEffectiveSetAccessorTypeAnnotationNode(node: SetAccessorDeclaration, checkJSDoc?: boolean): TypeNode {
const parameter = getSetAccessorValueParameter(node);
return parameter && getEffectiveTypeAnnotationNode(parameter);
return parameter && getEffectiveTypeAnnotationNode(parameter, checkJSDoc);
}
export function emitNewLineBeforeLeadingComments(lineMap: ReadonlyArray<number>, writer: EmitTextWriter, node: TextRange, leadingComments: ReadonlyArray<CommentRange>) {
@@ -3184,7 +3218,7 @@ namespace ts {
const carriageReturnLineFeed = "\r\n";
const lineFeed = "\n";
export function getNewLineCharacter(options: CompilerOptions | PrinterOptions, system?: System): string {
export function getNewLineCharacter(options: CompilerOptions | PrinterOptions, system?: { newLine: string }): string {
switch (options.newLine) {
case NewLineKind.CarriageReturnLineFeed:
return carriageReturnLineFeed;
@@ -5130,7 +5164,14 @@ namespace ts {
|| kind === SyntaxKind.UndefinedKeyword
|| kind === SyntaxKind.NullKeyword
|| kind === SyntaxKind.NeverKeyword
|| kind === SyntaxKind.ExpressionWithTypeArguments;
|| kind === SyntaxKind.ExpressionWithTypeArguments
|| kind === SyntaxKind.JSDocAllType
|| kind === SyntaxKind.JSDocUnknownType
|| kind === SyntaxKind.JSDocNullableType
|| kind === SyntaxKind.JSDocNonNullableType
|| kind === SyntaxKind.JSDocOptionalType
|| kind === SyntaxKind.JSDocFunctionType
|| kind === SyntaxKind.JSDocVariadicType;
}
/**
+26 -25
View File
@@ -144,13 +144,14 @@ namespace ts {
function compileWatchedProgram(host: DirectoryStructureHost, program: Program, builder: Builder) {
// First get and report any syntactic errors.
let diagnostics = program.getSyntacticDiagnostics().slice();
const diagnostics = program.getSyntacticDiagnostics().slice();
let reportSemanticDiagnostics = false;
// If we didn't have any syntactic errors, then also try getting the global and
// semantic errors.
if (diagnostics.length === 0) {
diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics());
addRange(diagnostics, program.getOptionsDiagnostics());
addRange(diagnostics, program.getGlobalDiagnostics());
if (diagnostics.length === 0) {
reportSemanticDiagnostics = true;
@@ -162,7 +163,7 @@ namespace ts {
let sourceMaps: SourceMapData[];
let emitSkipped: boolean;
const result = builder.emitChangedFiles(program);
const result = builder.emitChangedFiles(program, writeFile);
if (result.length === 0) {
emitSkipped = true;
}
@@ -171,14 +172,13 @@ namespace ts {
if (emitOutput.emitSkipped) {
emitSkipped = true;
}
diagnostics = concatenate(diagnostics, emitOutput.diagnostics);
addRange(diagnostics, emitOutput.diagnostics);
sourceMaps = concatenate(sourceMaps, emitOutput.sourceMaps);
writeOutputFiles(emitOutput.outputFiles);
}
}
if (reportSemanticDiagnostics) {
diagnostics = diagnostics.concat(builder.getSemanticDiagnostics(program));
addRange(diagnostics, builder.getSemanticDiagnostics(program));
}
return handleEmitOutputAndReportErrors(host, program, emittedFiles, emitSkipped,
diagnostics, reportDiagnostic);
@@ -191,31 +191,23 @@ namespace ts {
}
}
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean) {
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
try {
performance.mark("beforeIOWrite");
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
host.writeFile(fileName, data, writeByteOrderMark);
host.writeFile(fileName, text, writeByteOrderMark);
performance.mark("afterIOWrite");
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
if (emittedFiles) {
emittedFiles.push(fileName);
}
}
catch (e) {
return createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, e);
}
}
function writeOutputFiles(outputFiles: OutputFile[]) {
if (outputFiles) {
for (const outputFile of outputFiles) {
const error = writeFile(outputFile.name, outputFile.text, outputFile.writeByteOrderMark);
if (error) {
diagnostics.push(error);
}
if (emittedFiles) {
emittedFiles.push(outputFile.name);
}
if (onError) {
onError(e.message);
}
}
}
@@ -308,7 +300,7 @@ namespace ts {
getCurrentDirectory()
);
// There is no extra check needed since we can just rely on the program to decide emit
const builder = createBuilder({ getCanonicalFileName, getEmitOutput: getFileEmitOutput, computeHash, shouldEmitFile: () => true });
const builder = createBuilder({ getCanonicalFileName, computeHash });
synchronizeProgram();
@@ -605,8 +597,17 @@ namespace ts {
const fileOrDirectoryPath = toPath(fileOrDirectory);
// Since the file existance changed, update the sourceFiles cache
(directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
removeSourceFile(fileOrDirectoryPath);
const result = (directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
// Instead of deleting the file, mark it as changed instead
// Many times node calls add/remove/file when watching directories recursively
const hostSourceFile = sourceFilesCache.get(fileOrDirectoryPath);
if (hostSourceFile && !isString(hostSourceFile) && (result ? result.fileExists : directoryStructureHost.fileExists(fileOrDirectory))) {
hostSourceFile.version++;
}
else {
removeSourceFile(fileOrDirectoryPath);
}
// If the the added or created file or directory is not supported file name, ignore the file
// But when watched directory is added/removed, we need to reload the file list
+1 -3
View File
@@ -1,5 +1,6 @@
/// <reference path="core.ts" />
/* @internal */
namespace ts {
/**
* Updates the existing missing file watches with the new set of missing files after new program is created
@@ -72,10 +73,7 @@ namespace ts {
existingWatchedForWildcards.set(directory, createWildcardDirectoryWatcher(directory, flags));
}
}
}
/* @internal */
namespace ts {
export function addFileWatcher(host: System, file: string, cb: FileWatcherCallback): FileWatcher {
return host.watchFile(file, cb);
}
+5 -5
View File
@@ -48,7 +48,7 @@ class CompilerBaselineRunner extends RunnerBase {
}
public checkTestCodeOutput(fileName: string) {
describe("compiler tests for " + fileName, () => {
describe(`${this.testSuiteName} tests for ${fileName}`, () => {
// Mocha holds onto the closure environment of the describe callback even after the test is done.
// Everything declared here should be cleared out in the "after" callback.
let justName: string;
@@ -80,9 +80,9 @@ class CompilerBaselineRunner extends RunnerBase {
tsConfigFiles.push(this.createHarnessTestFile(testCaseContent.tsConfigFileUnitData, rootDir, ts.combinePaths(rootDir, tsConfigOptions.configFilePath)));
}
else {
const baseUrl = harnessSettings["baseUrl"];
const baseUrl = harnessSettings.baseUrl;
if (baseUrl !== undefined && !ts.isRootedDiskPath(baseUrl)) {
harnessSettings["baseUrl"] = ts.getNormalizedAbsolutePath(baseUrl, rootDir);
harnessSettings.baseUrl = ts.getNormalizedAbsolutePath(baseUrl, rootDir);
}
}
@@ -94,7 +94,7 @@ class CompilerBaselineRunner extends RunnerBase {
toBeCompiled = [];
otherFiles = [];
if (testCaseContent.settings["noImplicitReferences"] || /require\(/.test(lastUnit.content) || /reference\spath/.test(lastUnit.content)) {
if (testCaseContent.settings.noImplicitReferences || /require\(/.test(lastUnit.content) || /reference\spath/.test(lastUnit.content)) {
toBeCompiled.push(this.createHarnessTestFile(lastUnit, rootDir));
units.forEach(unit => {
if (unit.name !== lastUnit.name) {
@@ -114,7 +114,7 @@ class CompilerBaselineRunner extends RunnerBase {
}
const output = Harness.Compiler.compileFiles(
toBeCompiled, otherFiles, harnessSettings, /*options*/ tsConfigOptions, /*currentDirectory*/ harnessSettings["currentDirectory"]);
toBeCompiled, otherFiles, harnessSettings, /*options*/ tsConfigOptions, /*currentDirectory*/ harnessSettings.currentDirectory);
options = output.options;
result = output.result;
+111 -21
View File
@@ -819,10 +819,10 @@ namespace FourSlash {
});
}
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean) {
const completions = this.getCompletionListAtCaret();
if (completions) {
this.assertItemInCompletionList(completions.entries, symbol, text, documentation, kind, spanIndex);
this.assertItemInCompletionList(completions.entries, symbol, text, documentation, kind, spanIndex, hasAction);
}
else {
this.raiseError(`No completions at position '${this.currentCaretPosition}' when looking for '${symbol}'.`);
@@ -989,6 +989,10 @@ namespace FourSlash {
return this.getChecker().getSymbolsInScope(node, ts.SymbolFlags.Value | ts.SymbolFlags.Type | ts.SymbolFlags.Namespace);
}
public setTypesRegistry(map: ts.MapLike<void>): void {
this.languageServiceAdapterHost.typesRegistry = ts.createMapFromTemplate(map);
}
public verifyTypeOfSymbolAtLocation(range: Range, symbol: ts.Symbol, expected: string): void {
const node = this.goToAndGetNode(range);
const checker = this.getChecker();
@@ -1163,7 +1167,7 @@ Actual: ${stringify(fullActual)}`);
}
private getCompletionEntryDetails(entryName: string) {
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName);
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings);
}
private getReferencesAtCaret() {
@@ -2325,6 +2329,29 @@ Actual: ${stringify(fullActual)}`);
this.applyCodeActions(this.getCodeFixActions(fileName, errorCode), index);
}
public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) {
this.goToMarker(markerName);
const actualCompletion = this.getCompletionListAtCaret().entries.find(e => e.name === options.name);
if (!actualCompletion.hasAction) {
this.raiseError(`Completion for ${options.name} does not have an associated action.`);
}
const details = this.getCompletionEntryDetails(options.name);
if (details.codeActions.length !== 1) {
this.raiseError(`Expected one code action, got ${details.codeActions.length}`);
}
if (details.codeActions[0].description !== options.description) {
this.raiseError(`Expected description to be:\n${options.description}\ngot:\n${details.codeActions[0].description}`);
}
this.applyCodeActions(details.codeActions);
this.verifyNewContent(options);
}
public verifyRangeIs(expectedText: string, includeWhiteSpace?: boolean) {
const ranges = this.getRanges();
if (ranges.length !== 1) {
@@ -2396,6 +2423,10 @@ Actual: ${stringify(fullActual)}`);
this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false);
}
this.verifyNewContent(options);
}
private verifyNewContent(options: FourSlashInterface.NewContentOptions) {
if (options.newFileContent) {
assert(!options.newRangeContent);
this.verifyCurrentFileContent(options.newFileContent);
@@ -2786,16 +2817,26 @@ Actual: ${stringify(fullActual)}`);
}
}
public verifyCodeFixAvailable(negative: boolean) {
const codeFix = this.getCodeFixActions(this.activeFile.fileName);
public verifyCodeFixAvailable(negative: boolean, info: FourSlashInterface.VerifyCodeFixAvailableOptions[] | undefined) {
const codeFixes = this.getCodeFixActions(this.activeFile.fileName);
if (negative && codeFix.length) {
this.raiseError(`verifyCodeFixAvailable failed - expected no fixes but found one.`);
if (negative) {
if (codeFixes.length) {
this.raiseError(`verifyCodeFixAvailable failed - expected no fixes but found one.`);
}
return;
}
if (!(negative || codeFix.length)) {
if (!codeFixes.length) {
this.raiseError(`verifyCodeFixAvailable failed - expected code fixes but none found.`);
}
if (info) {
assert.equal(info.length, codeFixes.length);
ts.zipWith(codeFixes, info, (fix, info) => {
assert.equal(fix.description, info.description);
this.assertObjectsEqual(fix.commands, info.commands);
});
}
}
public verifyApplicableRefactorAvailableAtMarker(negative: boolean, markerName: string) {
@@ -2839,6 +2880,14 @@ Actual: ${stringify(fullActual)}`);
}
}
public verifyRefactor({ name, actionName, refactors }: FourSlashInterface.VerifyRefactorOptions) {
const selection = this.getSelection();
const actualRefactors = (this.languageService.getApplicableRefactors(this.activeFile.fileName, selection) || ts.emptyArray)
.filter(r => r.name === name && r.actions.some(a => a.name === actionName));
this.assertObjectsEqual(actualRefactors, refactors);
}
public verifyApplicableRefactorAvailableForRange(negative: boolean) {
const ranges = this.getRanges();
if (!(ranges && ranges.length === 1)) {
@@ -2858,14 +2907,14 @@ Actual: ${stringify(fullActual)}`);
public applyRefactor({ refactorName, actionName, actionDescription, newContent: newContentWithRenameMarker }: FourSlashInterface.ApplyRefactorOptions) {
const range = this.getSelection();
const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range);
const refactor = refactors.find(r => r.name === refactorName);
if (!refactor) {
const refactorsWithName = refactors.filter(r => r.name === refactorName);
if (refactorsWithName.length === 0) {
this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.\nAvailable refactors: ${refactors.map(r => r.name)}`);
}
const action = refactor.actions.find(a => a.name === actionName);
const action = ts.firstDefined(refactorsWithName, refactor => refactor.actions.find(a => a.name === actionName));
if (!action) {
this.raiseError(`The expected action: ${action} is not included in: ${refactor.actions.map(a => a.name)}`);
this.raiseError(`The expected action: ${actionName} is not included in: ${ts.flatMap(refactorsWithName, r => r.actions.map(a => a.name))}`);
}
if (action.description !== actionDescription) {
this.raiseError(`Expected action description to be ${JSON.stringify(actionDescription)}, got: ${JSON.stringify(action.description)}`);
@@ -2969,7 +3018,15 @@ Actual: ${stringify(fullActual)}`);
return text.substring(startPos, endPos);
}
private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
private assertItemInCompletionList(
items: ts.CompletionEntry[],
name: string,
text: string | undefined,
documentation: string | undefined,
kind: string | undefined,
spanIndex: number | undefined,
hasAction: boolean | undefined,
) {
for (const item of items) {
if (item.name === name) {
if (documentation !== undefined || text !== undefined) {
@@ -2992,6 +3049,8 @@ Actual: ${stringify(fullActual)}`);
assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + name));
}
assert.equal(item.hasAction, hasAction);
return;
}
}
@@ -3258,7 +3317,7 @@ ${code}
}
function containTSConfigJson(files: FourSlashFile[]): boolean {
return ts.forEach(files, f => f.fileOptions["Filename"] === "tsconfig.json");
return ts.forEach(files, f => f.fileOptions.Filename === "tsconfig.json");
}
function getNonFileNameOptionInFileList(files: FourSlashFile[]): string {
@@ -3613,6 +3672,10 @@ namespace FourSlashInterface {
public symbolsInScope(range: FourSlash.Range): ts.Symbol[] {
return this.state.symbolsInScope(range);
}
public setTypesRegistry(map: ts.MapLike<void>): void {
this.state.setTypesRegistry(map);
}
}
export class GoTo {
@@ -3705,12 +3768,12 @@ namespace FourSlashInterface {
// Verifies the completion list contains the specified symbol. The
// completion list is brought up if necessary
public completionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
public completionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean) {
if (this.negative) {
this.state.verifyCompletionListDoesNotContain(symbol, text, documentation, kind, spanIndex);
}
else {
this.state.verifyCompletionListContains(symbol, text, documentation, kind, spanIndex);
this.state.verifyCompletionListContains(symbol, text, documentation, kind, spanIndex, hasAction);
}
}
@@ -3788,8 +3851,8 @@ namespace FourSlashInterface {
this.state.verifyCodeFix(options);
}
public codeFixAvailable() {
this.state.verifyCodeFixAvailable(this.negative);
public codeFixAvailable(options?: VerifyCodeFixAvailableOptions[]) {
this.state.verifyCodeFixAvailable(this.negative, options);
}
public applicableRefactorAvailableAtMarker(markerName: string) {
@@ -3800,6 +3863,10 @@ namespace FourSlashInterface {
this.state.verifyApplicableRefactorAvailableForRange(this.negative);
}
public refactor(options: VerifyRefactorOptions) {
this.state.verifyRefactor(options);
}
public refactorAvailable(name: string, actionName?: string) {
this.state.verifyRefactorAvailable(this.negative, name, actionName);
}
@@ -4036,6 +4103,10 @@ namespace FourSlashInterface {
this.state.getAndApplyCodeActions(errorCode, index);
}
public applyCodeActionFromCompletion(markerName: string, options: VerifyCompletionActionOptions): void {
this.state.applyCodeActionFromCompletion(markerName, options);
}
public importFixAtPosition(expectedTextArray: string[], errorCode?: number): void {
this.state.verifyImportFixAtPosition(expectedTextArray, errorCode);
}
@@ -4433,12 +4504,31 @@ namespace FourSlashInterface {
isNewIdentifierLocation?: boolean;
}
export interface VerifyCodeFixOptions {
description: string;
// One of these should be defined.
export interface NewContentOptions {
// Exactly one of these should be defined.
newFileContent?: string;
newRangeContent?: string;
}
export interface VerifyCodeFixOptions extends NewContentOptions {
description: string;
errorCode?: number;
index?: number;
}
export interface VerifyCodeFixAvailableOptions {
description: string;
commands?: ts.CodeActionCommand[];
}
export interface VerifyRefactorOptions {
name: string;
actionName: string;
refactors: ts.ApplicableRefactorInfo[];
}
export interface VerifyCompletionActionOptions extends NewContentOptions {
name: string;
description: string;
}
}
+18 -14
View File
@@ -844,9 +844,7 @@ namespace Harness {
export const es2015DefaultLibFileName = "lib.es2015.d.ts";
// Cache of lib files from "built/local"
const libFileNameSourceFileMap = ts.createMapFromTemplate<ts.SourceFile>({
[defaultLibFileName]: createSourceFileAndAssertInvariants(defaultLibFileName, IO.readFile(libFolder + "lib.es5.d.ts"), /*languageVersion*/ ts.ScriptTarget.Latest)
});
let libFileNameSourceFileMap: ts.Map<ts.SourceFile> | undefined;
// Cache of lib files from "tests/lib/"
const testLibFileNameSourceFileMap = ts.createMap<ts.SourceFile>();
@@ -857,6 +855,12 @@ namespace Harness {
return undefined;
}
if (!libFileNameSourceFileMap) {
libFileNameSourceFileMap = ts.createMapFromTemplate({
[defaultLibFileName]: createSourceFileAndAssertInvariants(defaultLibFileName, IO.readFile(libFolder + "lib.es5.d.ts"), /*languageVersion*/ ts.ScriptTarget.Latest)
});
}
let sourceFile = libFileNameSourceFileMap.get(fileName);
if (!sourceFile) {
libFileNameSourceFileMap.set(fileName, sourceFile = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest));
@@ -912,8 +916,8 @@ namespace Harness {
if (file.content !== undefined) {
const fileName = ts.normalizePath(file.unitName);
const path = ts.toPath(file.unitName, currentDirectory, getCanonicalFileName);
if (file.fileOptions && file.fileOptions["symlink"]) {
const links = file.fileOptions["symlink"].split(",");
if (file.fileOptions && file.fileOptions.symlink) {
const links = file.fileOptions.symlink.split(",");
for (const link of links) {
const linkPath = ts.toPath(link, currentDirectory, getCanonicalFileName);
realPathMap.set(linkPath, fileName);
@@ -1228,7 +1232,7 @@ namespace Harness {
if (options.declaration && result.errors.length === 0 && result.declFilesCode.length > 0) {
ts.forEach(inputFiles, file => addDtsFile(file, declInputFiles));
ts.forEach(otherFiles, file => addDtsFile(file, declOtherFiles));
return { declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory: currentDirectory || harnessSettings["currentDirectory"] };
return { declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory: currentDirectory || harnessSettings.currentDirectory };
}
function addDtsFile(file: TestFile, dtsFiles: TestFile[]) {
@@ -1452,7 +1456,7 @@ namespace Harness {
});
}
export function doTypeAndSymbolBaseline(baselinePath: string, program: ts.Program, allFiles: {unitName: string, content: string}[], opts?: Harness.Baseline.BaselineOptions, multifile?: boolean, skipTypeAndSymbolbaselines?: boolean) {
export function doTypeAndSymbolBaseline(baselinePath: string, program: ts.Program, allFiles: {unitName: string, content: string}[], opts?: Harness.Baseline.BaselineOptions, multifile?: boolean, skipTypeBaselines?: boolean, skipSymbolBaselines?: boolean) {
// The full walker simulates the types that you would get from doing a full
// compile. The pull walker simulates the types you get when you just do
// a type query for a random node (like how the LS would do it). Most of the
@@ -1510,19 +1514,19 @@ namespace Harness {
baselinePath.replace(/\.tsx?/, "") : baselinePath;
if (!multifile) {
const fullBaseLine = generateBaseLine(isSymbolBaseLine, skipTypeAndSymbolbaselines);
const fullBaseLine = generateBaseLine(isSymbolBaseLine, isSymbolBaseLine ? skipSymbolBaselines : skipTypeBaselines);
Harness.Baseline.runBaseline(outputFileName + fullExtension, () => fullBaseLine, opts);
}
else {
Harness.Baseline.runMultifileBaseline(outputFileName, fullExtension, () => {
return iterateBaseLine(isSymbolBaseLine, skipTypeAndSymbolbaselines);
return iterateBaseLine(isSymbolBaseLine, isSymbolBaseLine ? skipSymbolBaselines : skipTypeBaselines);
}, opts);
}
}
function generateBaseLine(isSymbolBaseline: boolean, skipTypeAndSymbolbaselines?: boolean): string {
function generateBaseLine(isSymbolBaseline: boolean, skipBaseline?: boolean): string {
let result = "";
const gen = iterateBaseLine(isSymbolBaseline, skipTypeAndSymbolbaselines);
const gen = iterateBaseLine(isSymbolBaseline, skipBaseline);
for (let {done, value} = gen.next(); !done; { done, value } = gen.next()) {
const [, content] = value;
result += content;
@@ -1532,8 +1536,8 @@ namespace Harness {
/* tslint:enable:no-null-keyword */
}
function *iterateBaseLine(isSymbolBaseline: boolean, skipTypeAndSymbolbaselines?: boolean): IterableIterator<[string, string]> {
if (skipTypeAndSymbolbaselines) {
function *iterateBaseLine(isSymbolBaseline: boolean, skipBaseline?: boolean): IterableIterator<[string, string]> {
if (skipBaseline) {
return;
}
const dupeCase = ts.createMap<number>();
@@ -1672,7 +1676,7 @@ namespace Harness {
}
function fileOutput(file: GeneratedFile, harnessSettings: Harness.TestCaseParser.CompilerSettings): string {
const fileName = harnessSettings["fullEmitPaths"] ? file.fileName : ts.getBaseFileName(file.fileName);
const fileName = harnessSettings.fullEmitPaths ? file.fileName : ts.getBaseFileName(file.fileName);
return "//// [" + fileName + "]\r\n" + getByteOrderMarkText(file) + file.code;
}
+14 -2
View File
@@ -123,6 +123,7 @@ namespace Harness.LanguageService {
}
export class LanguageServiceAdapterHost {
public typesRegistry: ts.Map<void> | undefined;
protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(virtualFileSystemRoot, /*useCaseSensitiveFilenames*/false);
constructor(protected cancellationToken = DefaultHostCancellationToken.Instance,
@@ -182,6 +183,11 @@ namespace Harness.LanguageService {
/// Native adapter
class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost {
isKnownTypesPackageName(name: string): boolean {
return this.typesRegistry && this.typesRegistry.has(name);
}
installPackage = ts.notImplemented;
getCompilationSettings() { return this.settings; }
getCancellationToken() { return this.cancellationToken; }
getDirectories(path: string): string[] {
@@ -206,6 +212,11 @@ namespace Harness.LanguageService {
return script ? script.version.toString() : undefined;
}
directoryExists(dirName: string): boolean {
const fileEntry = this.virtualFileSystem.traversePath(dirName);
return fileEntry && fileEntry.isDirectory();
}
fileExists(fileName: string): boolean {
const script = this.getScriptSnapshot(fileName);
return script !== undefined;
@@ -405,8 +416,8 @@ namespace Harness.LanguageService {
getCompletionsAtPosition(fileName: string, position: number): ts.CompletionInfo {
return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position));
}
getCompletionEntryDetails(fileName: string, position: number, entryName: string): ts.CompletionEntryDetails {
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName));
getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: ts.FormatCodeOptions): ts.CompletionEntryDetails {
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(options)));
}
getCompletionEntrySymbol(): ts.Symbol {
throw new Error("getCompletionEntrySymbol not implemented across the shim layer.");
@@ -496,6 +507,7 @@ namespace Harness.LanguageService {
getCodeFixesAtPosition(): ts.CodeAction[] {
throw new Error("Not supported on the shim.");
}
applyCodeActionCommand = ts.notImplemented;
getCodeFixDiagnostics(): ts.Diagnostic[] {
throw new Error("Not supported on the shim.");
}
+15 -6
View File
@@ -157,11 +157,20 @@ namespace Playback {
return log;
}
const canonicalizeForHarness = ts.createGetCanonicalFileName(/*caseSensitive*/ false); // This is done so tests work on windows _and_ linux
function sanitizeTestFilePath(name: string) {
const path = ts.toPath(ts.normalizeSlashes(name.replace(/[\^<>:"|?*%]/g, "_")).replace(/\.\.\//g, "__dotdot/"), "", canonicalizeForHarness);
if (ts.startsWith(path, "/")) {
return path.substring(1);
}
return path;
}
export function oldStyleLogIntoNewStyleLog(log: IOLog, writeFile: typeof Harness.IO.writeFile, baseTestName: string) {
if (log.filesAppended) {
for (const file of log.filesAppended) {
if (file.contents !== undefined) {
file.contentsPath = ts.combinePaths("appended", Harness.Compiler.sanitizeTestFilePath(file.path));
file.contentsPath = ts.combinePaths("appended", sanitizeTestFilePath(file.path));
writeFile(ts.combinePaths(baseTestName, file.contentsPath), file.contents);
delete file.contents;
}
@@ -170,7 +179,7 @@ namespace Playback {
if (log.filesWritten) {
for (const file of log.filesWritten) {
if (file.contents !== undefined) {
file.contentsPath = ts.combinePaths("written", Harness.Compiler.sanitizeTestFilePath(file.path));
file.contentsPath = ts.combinePaths("written", sanitizeTestFilePath(file.path));
writeFile(ts.combinePaths(baseTestName, file.contentsPath), file.contents);
delete file.contents;
}
@@ -180,7 +189,7 @@ namespace Playback {
for (const file of log.filesRead) {
const { contents } = file.result;
if (contents !== undefined) {
file.result.contentsPath = ts.combinePaths("read", Harness.Compiler.sanitizeTestFilePath(file.path));
file.result.contentsPath = ts.combinePaths("read", sanitizeTestFilePath(file.path));
writeFile(ts.combinePaths(baseTestName, file.result.contentsPath), contents);
const len = contents.length;
if (len >= 2 && contents.charCodeAt(0) === 0xfeff) {
@@ -235,8 +244,8 @@ namespace Playback {
if (recordLog !== undefined) {
let i = 0;
const fn = () => recordLogFileNameBase + i;
while (underlying.fileExists(fn() + ".json")) i++;
underlying.writeFile(ts.combinePaths(fn(), "test.json"), JSON.stringify(oldStyleLogIntoNewStyleLog(recordLog, (path, string) => underlying.writeFile(ts.combinePaths(fn(), path), string), fn()), null, 4)); // tslint:disable-line:no-null-keyword
while (underlying.fileExists(ts.combinePaths(fn(), "test.json"))) i++;
underlying.writeFile(ts.combinePaths(fn(), "test.json"), JSON.stringify(oldStyleLogIntoNewStyleLog(recordLog, (path, string) => underlying.writeFile(path, string), fn()), null, 4)); // tslint:disable-line:no-null-keyword
recordLog = undefined;
}
};
@@ -415,4 +424,4 @@ namespace Playback {
initWrapper(wrapper, underlying);
return wrapper;
}
}
}
+1 -1
View File
@@ -137,7 +137,7 @@ namespace Harness.Parallel.Host {
let closedWorkers = 0;
for (let i = 0; i < workerCount; i++) {
// TODO: Just send the config over the IPC channel or in the command line arguments
const config: TestConfig = { light: Harness.lightMode, listenForWork: true, runUnitTests: runners.length !== 1 };
const config: TestConfig = { light: Harness.lightMode, listenForWork: true, runUnitTests };
const configPath = ts.combinePaths(taskConfigsFolder, `task-config${i}.json`);
Harness.IO.writeFile(configPath, JSON.stringify(config));
const child = fork(__filename, [`--config="${configPath}"`]);
+1 -1
View File
@@ -443,7 +443,7 @@ class ProjectRunner extends RunnerBase {
const name = "Compiling project for " + testCase.scenario + ": testcase " + testCaseFileName;
describe("Projects tests", () => {
describe("projects tests", () => {
describe(name, () => {
function verifyCompilerResults(moduleKind: ts.ModuleKind) {
let compilerResult: BatchCompileProjectTestCaseResult;
+5 -4
View File
@@ -82,7 +82,7 @@ let testConfigContent =
let taskConfigsFolder: string;
let workerCount: number;
let runUnitTests = true;
let runUnitTests: boolean | undefined;
let noColors = false;
interface TestConfig {
@@ -108,9 +108,7 @@ function handleTestConfig() {
if (testConfig.light) {
Harness.lightMode = true;
}
if (testConfig.runUnitTests !== undefined) {
runUnitTests = testConfig.runUnitTests;
}
runUnitTests = testConfig.runUnitTests;
if (testConfig.workerCount) {
workerCount = +testConfig.workerCount;
}
@@ -199,6 +197,9 @@ function handleTestConfig() {
runners.push(new FourSlashRunner(FourSlashTestType.Server));
// runners.push(new GeneratedFourslashRunner());
}
if (runUnitTests === undefined) {
runUnitTests = runners.length !== 1; // Don't run unit tests when running only one runner if unit tests were not explicitly asked for
}
}
function beginTests() {
+7 -6
View File
@@ -26,7 +26,7 @@ namespace RWC {
}
export function runRWCTest(jsonPath: string) {
describe("Testing a RWC project: " + jsonPath, () => {
describe("Testing a rwc project: " + jsonPath, () => {
let inputFiles: Harness.Compiler.TestFile[] = [];
let otherFiles: Harness.Compiler.TestFile[] = [];
let tsconfigFiles: Harness.Compiler.TestFile[] = [];
@@ -39,8 +39,8 @@ namespace RWC {
const baseName = ts.getBaseFileName(jsonPath);
let currentDirectory: string;
let useCustomLibraryFile: boolean;
let skipTypeAndSymbolbaselines = false;
const typeAndSymbolSizeLimit = 10000000;
let skipTypeBaselines = false;
const typeSizeLimit = 10000000;
after(() => {
// Mocha holds onto the closure environment of the describe callback even after the test is done.
// Therefore we have to clean out large objects after the test is done.
@@ -54,7 +54,7 @@ namespace RWC {
// or to use lib.d.ts inside the json object. If the flag is true, use the lib.d.ts inside json file
// otherwise use the lib.d.ts from built/local
useCustomLibraryFile = undefined;
skipTypeAndSymbolbaselines = false;
skipTypeBaselines = false;
});
it("can compile", function(this: Mocha.ITestCallbackContext) {
@@ -64,7 +64,7 @@ namespace RWC {
const ioLog: IOLog = Playback.newStyleLogIntoOldStyleLog(JSON.parse(Harness.IO.readFile(`internal/cases/rwc/${jsonPath}/test.json`)), Harness.IO, `internal/cases/rwc/${baseName}`);
currentDirectory = ioLog.currentDirectory;
useCustomLibraryFile = ioLog.useCustomLibraryFile;
skipTypeAndSymbolbaselines = ioLog.filesRead.reduce((acc, elem) => (elem && elem.result && elem.result.contents) ? acc + elem.result.contents.length : acc, 0) > typeAndSymbolSizeLimit;
skipTypeBaselines = ioLog.filesRead.reduce((acc, elem) => (elem && elem.result && elem.result.contents) ? acc + elem.result.contents.length : acc, 0) > typeSizeLimit;
runWithIOLog(ioLog, () => {
opts = ts.parseCommandLine(ioLog.arguments, fileName => Harness.IO.readFile(fileName));
assert.equal(opts.errors.length, 0);
@@ -224,7 +224,7 @@ namespace RWC {
Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult.program, inputFiles
.concat(otherFiles)
.filter(file => !!compilerResult.program.getSourceFile(file.unitName))
.filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts, /*multifile*/ true, skipTypeAndSymbolbaselines);
.filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts, /*multifile*/ true, skipTypeBaselines, /*skipSymbolbaselines*/ true);
});
// Ideally, a generated declaration file will have no errors. But we allow generated
@@ -266,6 +266,7 @@ class RWCRunner extends RunnerBase {
public initializeTests(): void {
// Read in and evaluate the test list
const testList = this.tests && this.tests.length ? this.tests : this.enumerateTestFiles();
for (let i = 0; i < testList.length; i++) {
this.runTest(testList[i]);
}
+2 -1
View File
@@ -139,6 +139,7 @@
"./unittests/telemetry.ts",
"./unittests/languageService.ts",
"./unittests/programMissingFiles.ts",
"./unittests/publicApi.ts"
"./unittests/publicApi.ts",
"./unittests/hostNewLineSupport.ts"
]
}
+3 -3
View File
@@ -101,8 +101,8 @@ class TypeWriterWalker {
}
count++;
symbolString += ", ";
if ((declaration as any)["__symbolTestOutputCache"]) {
symbolString += (declaration as any)["__symbolTestOutputCache"];
if ((declaration as any).__symbolTestOutputCache) {
symbolString += (declaration as any).__symbolTestOutputCache;
continue;
}
const declSourceFile = declaration.getSourceFile();
@@ -111,7 +111,7 @@ class TypeWriterWalker {
const isLibFile = /lib(.*)\.d\.ts/i.test(fileName);
const declText = `Decl(${ fileName }, ${ isLibFile ? "--" : declLineAndCharacter.line }, ${ isLibFile ? "--" : declLineAndCharacter.character })`;
symbolString += declText;
(declaration as any)["__symbolTestOutputCache"] = declText;
(declaration as any).__symbolTestOutputCache = declText;
}
}
symbolString += ")";
+4 -12
View File
@@ -46,15 +46,14 @@ namespace ts {
function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray<string>) => void {
const builder = createBuilder({
getCanonicalFileName: identity,
getEmitOutput: getFileEmitOutput,
computeHash: identity,
shouldEmitFile: returnTrue,
computeHash: identity
});
return fileNames => {
const program = getProgram();
builder.updateProgram(program);
const changedFiles = builder.emitChangedFiles(program);
assert.deepEqual(changedFileNames(changedFiles), fileNames);
const outputFileNames: string[] = [];
builder.emitChangedFiles(program, fileName => outputFileNames.push(fileName));
assert.deepEqual(outputFileNames, fileNames);
};
}
@@ -63,11 +62,4 @@ namespace ts {
updateProgramText(files, fileName, fileContent);
});
}
function changedFileNames(changedFiles: ReadonlyArray<EmitOutputDetailed>): string[] {
return changedFiles.map(f => {
assert.lengthOf(f.outputFiles, 1);
return f.outputFiles[0].name;
});
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ namespace ts.projectSystem {
describe("CompileOnSave affected list", () => {
function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: FileOrFolder[] }[]) {
const response: server.protocol.CompileOnSaveAffectedFileListSingleProject[] = session.executeCommand(request).response;
const response = session.executeCommand(request).response as server.protocol.CompileOnSaveAffectedFileListSingleProject[];
const actualResult = response.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
expectedFileList = expectedFileList.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
@@ -9,7 +9,7 @@ namespace ts {
}
function assertCompilerOptionsWithJson(json: any, configFileName: string, expectedResult: { compilerOptions: CompilerOptions, errors: Diagnostic[] }) {
const { options: actualCompilerOptions, errors: actualErrors} = convertCompilerOptionsFromJson(json["compilerOptions"], "/apath/", configFileName);
const { options: actualCompilerOptions, errors: actualErrors} = convertCompilerOptionsFromJson(json.compilerOptions, "/apath/", configFileName);
const parsedCompilerOptions = JSON.stringify(actualCompilerOptions);
const expectedCompilerOptions = JSON.stringify(expectedResult.compilerOptions);
@@ -33,7 +33,7 @@ namespace ts {
assert(!!result.endOfFileToken);
const host: ParseConfigHost = new Utils.MockParseConfigHost("/apath/", true, []);
const { options: actualCompilerOptions, errors: actualParseErrors } = parseJsonSourceFileConfigFileContent(result, host, "/apath/", /*existingOptions*/ undefined, configFileName);
expectedResult.compilerOptions["configFilePath"] = configFileName;
expectedResult.compilerOptions.configFilePath = configFileName;
const parsedCompilerOptions = JSON.stringify(actualCompilerOptions);
const expectedCompilerOptions = JSON.stringify(expectedResult.compilerOptions);
@@ -32,7 +32,7 @@ namespace ts {
}
function assertTypeAcquisitionWithJson(json: any, configFileName: string, expectedResult: ExpectedResult) {
const jsonOptions = json["typeAcquisition"] || json["typingOptions"];
const jsonOptions = json.typeAcquisition || json.typingOptions;
const { options: actualTypeAcquisition, errors: actualErrors } = convertTypeAcquisitionFromJson(jsonOptions, "/apath/", configFileName);
verifyAcquisition(actualTypeAcquisition, expectedResult);
verifyErrors(actualErrors, expectedResult);
+5 -5
View File
@@ -195,12 +195,12 @@ namespace ts {
function G<U extends T[]>(t2: U) {
[#|t2.toString();|]
}
}`);
}`, /*includeLib*/ true);
// Confirm that the contextual type of an extracted expression counts as a use.
testExtractFunction("extractFunction16",
`function F<T>() {
const array: T[] = [#|[]|];
}`);
}`, /*includeLib*/ true);
// Class type parameter
testExtractFunction("extractFunction17",
`class C<T1, T2> {
@@ -219,7 +219,7 @@ namespace ts {
testExtractFunction("extractFunction19",
`function F<T, U extends T[], V extends U[]>(v: V) {
[#|v.toString()|];
}`);
}`, /*includeLib*/ true);
testExtractFunction("extractFunction20",
`const _ = class {
@@ -542,7 +542,7 @@ var q = /*b*/ //c
/*m*/; /*n*/ //o`);
});
function testExtractFunction(caption: string, text: string) {
testExtractSymbol(caption, text, "extractFunction", Diagnostics.Extract_function);
function testExtractFunction(caption: string, text: string, includeLib?: boolean) {
testExtractSymbol(caption, text, "extractFunction", Diagnostics.Extract_function, includeLib);
}
}
+16 -5
View File
@@ -97,7 +97,16 @@ namespace ts {
return rulesProvider;
}
export function testExtractSymbol(caption: string, text: string, baselineFolder: string, description: DiagnosticMessage) {
const notImplementedHost: LanguageServiceHost = {
getCompilationSettings: notImplemented,
getScriptFileNames: notImplemented,
getScriptVersion: notImplemented,
getScriptSnapshot: notImplemented,
getDefaultLibFileName: notImplemented,
getCurrentDirectory: notImplemented,
};
export function testExtractSymbol(caption: string, text: string, baselineFolder: string, description: DiagnosticMessage, includeLib?: boolean) {
const t = extractTest(text);
const selectionRange = t.ranges.get("selection");
if (!selectionRange) {
@@ -109,7 +118,7 @@ namespace ts {
function runBaseline(extension: Extension) {
const path = "/a" + extension;
const program = makeProgram({ path, content: t.source });
const program = makeProgram({ path, content: t.source }, includeLib);
if (hasSyntacticDiagnostics(program)) {
// Don't bother generating JS baselines for inputs that aren't valid JS.
@@ -125,6 +134,7 @@ namespace ts {
file: sourceFile,
startPosition: selectionRange.start,
endPosition: selectionRange.end,
host: notImplementedHost,
rulesProvider: getRuleProvider()
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
@@ -144,15 +154,15 @@ namespace ts {
const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation);
data.push(newTextWithRename);
const diagProgram = makeProgram({ path, content: newText });
const diagProgram = makeProgram({ path, content: newText }, includeLib);
assert.isFalse(hasSyntacticDiagnostics(diagProgram));
}
return data.join(newLineCharacter);
});
}
function makeProgram(f: {path: string, content: string }) {
const host = projectSystem.createServerHost([f, projectSystem.libFile]);
function makeProgram(f: {path: string, content: string }, includeLib?: boolean) {
const host = projectSystem.createServerHost(includeLib ? [f, projectSystem.libFile] : [f]); // libFile is expensive to parse repeatedly - only test when required
const projectService = projectSystem.createProjectService(host);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
@@ -188,6 +198,7 @@ namespace ts {
file: sourceFile,
startPosition: selectionRange.start,
endPosition: selectionRange.end,
host: notImplementedHost,
rulesProvider: getRuleProvider()
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
@@ -0,0 +1,67 @@
/// <reference path="..\harness.ts" />
namespace ts {
describe("hostNewLineSupport", () => {
function testLSWithFiles(settings: CompilerOptions, files: Harness.Compiler.TestFile[]) {
function snapFor(path: string): IScriptSnapshot {
if (path === "lib.d.ts") {
return {
dispose() {},
getChangeRange() { return undefined; },
getLength() { return 0; },
getText(_start, _end) {
return "";
}
};
}
const result = forEach(files, f => f.unitName === path ? f : undefined);
if (result) {
return {
dispose() {},
getChangeRange() { return undefined; },
getLength() { return result.content.length; },
getText(start, end) {
return result.content.substring(start, end);
}
};
}
return undefined;
}
const lshost: LanguageServiceHost = {
getCompilationSettings: () => settings,
getScriptFileNames: () => map(files, f => f.unitName),
getScriptVersion: () => "1",
getScriptSnapshot: name => snapFor(name),
getDefaultLibFileName: () => "lib.d.ts",
getCurrentDirectory: () => "",
};
return ts.createLanguageService(lshost);
}
function verifyNewLines(content: string, options: CompilerOptions) {
const ls = testLSWithFiles(options, [{
content,
fileOptions: {},
unitName: "input.ts"
}]);
const result = ls.getEmitOutput("input.ts");
assert(!result.emitSkipped, "emit was skipped");
assert(result.outputFiles.length === 1, "a number of files other than 1 was output");
assert(result.outputFiles[0].name === "input.js", `Expected output file name input.js, but got ${result.outputFiles[0].name}`);
assert(result.outputFiles[0].text.match(options.newLine === NewLineKind.CarriageReturnLineFeed ? /\r\n/ : /[^\r]\n/), "expected to find appropriate newlines");
assert(!result.outputFiles[0].text.match(options.newLine === NewLineKind.CarriageReturnLineFeed ? /[^\r]\n/ : /\r\n/), "expected not to find inappropriate newlines");
}
function verifyBothNewLines(content: string) {
verifyNewLines(content, { newLine: NewLineKind.CarriageReturnLineFeed });
verifyNewLines(content, { newLine: NewLineKind.LineFeed });
}
it("should exist and respect provided compiler options", () => {
verifyBothNewLines(`
function foo() {
return 2 + 2;
}
`);
});
});
}
+4 -1
View File
@@ -392,7 +392,7 @@ export = C;
});
describe("Files with different casing", () => {
const library = createSourceFile("lib.d.ts", "", ScriptTarget.ES5);
let library: SourceFile;
function test(files: Map<string>, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void {
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
if (!useCaseSensitiveFileNames) {
@@ -406,6 +406,9 @@ export = C;
const host: CompilerHost = {
getSourceFile: (fileName: string, languageVersion: ScriptTarget) => {
if (fileName === "lib.d.ts") {
if (!library) {
library = createSourceFile("lib.d.ts", "", ScriptTarget.ES5);
}
return library;
}
const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName)));
+5 -5
View File
@@ -57,7 +57,7 @@ namespace ts.projectSystem {
});
checkNumberOfProjects(projectService, { externalProjects: 1 });
const diags = session.executeCommand(compilerOptionsRequest).response;
const diags = session.executeCommand(compilerOptionsRequest).response as server.protocol.DiagnosticWithLinePosition[];
// only file1 exists - expect error
checkDiagnosticsWithLinePos(diags, ["File '/a/b/applib.ts' not found."]);
}
@@ -65,7 +65,7 @@ namespace ts.projectSystem {
{
// only file2 exists - expect error
checkNumberOfProjects(projectService, { externalProjects: 1 });
const diags = session.executeCommand(compilerOptionsRequest).response;
const diags = session.executeCommand(compilerOptionsRequest).response as server.protocol.DiagnosticWithLinePosition[];
checkDiagnosticsWithLinePos(diags, ["File '/a/b/app.ts' not found."]);
}
@@ -73,7 +73,7 @@ namespace ts.projectSystem {
{
// both files exist - expect no errors
checkNumberOfProjects(projectService, { externalProjects: 1 });
const diags = session.executeCommand(compilerOptionsRequest).response;
const diags = session.executeCommand(compilerOptionsRequest).response as server.protocol.DiagnosticWithLinePosition[];
checkDiagnosticsWithLinePos(diags, []);
}
});
@@ -103,13 +103,13 @@ namespace ts.projectSystem {
seq: 2,
arguments: { projectFileName: project.getProjectName() }
};
let diags = session.executeCommand(compilerOptionsRequest).response;
let diags = session.executeCommand(compilerOptionsRequest).response as server.protocol.DiagnosticWithLinePosition[];
checkDiagnosticsWithLinePos(diags, ["File '/a/b/applib.ts' not found."]);
host.reloadFS([file1, file2, config, libFile]);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
diags = session.executeCommand(compilerOptionsRequest).response;
diags = session.executeCommand(compilerOptionsRequest).response as server.protocol.DiagnosticWithLinePosition[];
checkDiagnosticsWithLinePos(diags, []);
});
+29 -5
View File
@@ -317,7 +317,7 @@ namespace ts.server {
item: false
};
const command = "newhandle";
const result = {
const result: ts.server.HandlerResponse = {
response: respBody,
responseRequired: true
};
@@ -334,7 +334,7 @@ namespace ts.server {
const respBody = {
item: false
};
const resp = {
const resp: ts.server.HandlerResponse = {
response: respBody,
responseRequired: true
};
@@ -374,7 +374,7 @@ namespace ts.server {
};
const command = "test";
session.output(body, command);
session.output(body, command, /*reqSeq*/ 0);
expect(lastSent).to.deep.equal({
seq: 0,
@@ -477,7 +477,7 @@ namespace ts.server {
};
const command = "test";
session.output(body, command);
session.output(body, command, /*reqSeq*/ 0);
expect(session.lastSent).to.deep.equal({
seq: 0,
@@ -544,7 +544,7 @@ namespace ts.server {
handleRequest(msg: protocol.Request) {
let response: protocol.Response;
try {
({ response } = this.executeCommand(msg));
response = this.executeCommand(msg).response as protocol.Response;
}
catch (e) {
this.output(undefined, msg.command, msg.seq, e.toString());
@@ -663,4 +663,28 @@ namespace ts.server {
session.consumeQueue();
});
});
describe("helpers", () => {
it(getLocationInNewDocument.name, () => {
const text = `// blank line\nconst x = 0;`;
const renameLocationInOldText = text.indexOf("0");
const fileName = "/a.ts";
const edits: ts.FileTextChanges = {
fileName,
textChanges: [
{
span: { start: 0, length: 0 },
newText: "const newLocal = 0;\n\n",
},
{
span: { start: renameLocationInOldText, length: 1 },
newText: "newLocal",
},
],
};
const renameLocationInNewText = renameLocationInOldText + edits.textChanges[0].newText.length;
const res = getLocationInNewDocument(text, fileName, renameLocationInNewText, [edits]);
assert.deepEqual(res, { line: 4, offset: 11 });
});
});
}
+191 -13
View File
@@ -80,9 +80,9 @@ namespace ts.tscWatch {
checkOutputDoesNotContain(host, expectedNonAffectedFiles);
}
function checkOutputErrors(host: WatchedSystem, errors?: ReadonlyArray<Diagnostic>, isInitial?: true, skipWaiting?: true) {
function checkOutputErrors(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, isInitial?: true, skipWaiting?: true) {
const outputs = host.getOutput();
const expectedOutputCount = (isInitial ? 0 : 1) + (errors ? errors.length : 0) + (skipWaiting ? 0 : 1);
const expectedOutputCount = (isInitial ? 0 : 1) + errors.length + (skipWaiting ? 0 : 1);
assert.equal(outputs.length, expectedOutputCount, "Outputs = " + outputs.toString());
let index = 0;
if (!isInitial) {
@@ -254,7 +254,7 @@ namespace ts.tscWatch {
checkProgramRootFiles(watch(), [file1.path, file2.path]);
checkWatchedFiles(host, [configFile.path, file1.path, file2.path, libFile.path]);
const configDir = getDirectoryPath(configFile.path);
checkWatchedDirectories(host, projectSystem.getTypeRootsFromLocation(configDir).concat(configDir), /*recursive*/ true);
checkWatchedDirectories(host, [configDir, combinePaths(configDir, projectSystem.nodeModulesAtTypes)], /*recursive*/ true);
});
// TODO: if watching for config file creation
@@ -269,7 +269,7 @@ namespace ts.tscWatch {
const host = createWatchedSystem([commonFile1, libFile, configFile]);
const watch = createWatchModeWithConfigFile(configFile.path, host);
const configDir = getDirectoryPath(configFile.path);
checkWatchedDirectories(host, projectSystem.getTypeRootsFromLocation(configDir).concat(configDir), /*recursive*/ true);
checkWatchedDirectories(host, [configDir, combinePaths(configDir, projectSystem.nodeModulesAtTypes)], /*recursive*/ true);
checkProgramRootFiles(watch(), [commonFile1.path]);
@@ -339,7 +339,7 @@ namespace ts.tscWatch {
host.runQueuedTimeoutCallbacks();
checkProgramRootFiles(watch(), [file1.path]);
checkProgramActualFiles(watch(), [file1.path, libFile.path, commonFile2.path]);
checkOutputErrors(host);
checkOutputErrors(host, emptyArray);
});
it("should reflect change in config file", () => {
@@ -792,7 +792,7 @@ namespace ts.tscWatch {
moduleFile.path = moduleFileOldPath;
host.reloadFS([moduleFile, file1, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputErrors(host);
checkOutputErrors(host, emptyArray);
});
it("rename a module file and rename back should restore the states for configured projects", () => {
@@ -824,7 +824,7 @@ namespace ts.tscWatch {
moduleFile.path = moduleFileOldPath;
host.reloadFS([moduleFile, file1, configFile, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputErrors(host);
checkOutputErrors(host, emptyArray);
});
it("types should load from config file path if config exists", () => {
@@ -867,7 +867,7 @@ namespace ts.tscWatch {
host.reloadFS([file1, moduleFile, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputErrors(host);
checkOutputErrors(host, emptyArray);
});
it("Configure file diagnostics events are generated when the config file has errors", () => {
@@ -942,7 +942,7 @@ namespace ts.tscWatch {
}`;
host.reloadFS([file, configFile, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputErrors(host);
checkOutputErrors(host, emptyArray);
});
it("non-existing directories listed in config file input array should be tolerated without crashing the server", () => {
@@ -1105,6 +1105,64 @@ namespace ts.tscWatch {
const outJs = "/a/out.js";
createWatchForOut(/*out*/ undefined, outJs);
});
function verifyFilesEmittedOnce(useOutFile: boolean) {
const file1: FileOrFolder = {
path: "/a/b/output/AnotherDependency/file1.d.ts",
content: "declare namespace Common.SomeComponent.DynamicMenu { enum Z { Full = 0, Min = 1, Average = 2, } }"
};
const file2: FileOrFolder = {
path: "/a/b/dependencies/file2.d.ts",
content: "declare namespace Dependencies.SomeComponent { export class SomeClass { version: string; } }"
};
const file3: FileOrFolder = {
path: "/a/b/project/src/main.ts",
content: "namespace Main { export function fooBar() {} }"
};
const file4: FileOrFolder = {
path: "/a/b/project/src/main2.ts",
content: "namespace main.file4 { import DynamicMenu = Common.SomeComponent.DynamicMenu; export function foo(a: DynamicMenu.z) { } }"
};
const configFile: FileOrFolder = {
path: "/a/b/project/tsconfig.json",
content: JSON.stringify({
compilerOptions: useOutFile ?
{ outFile: "../output/common.js", target: "es5" } :
{ outDir: "../output", target: "es5" },
files: [file1.path, file2.path, file3.path, file4.path]
})
};
const files = [file1, file2, file3, file4];
const allfiles = files.concat(configFile);
const host = createWatchedSystem(allfiles);
const originalWriteFile = host.writeFile.bind(host);
const mapOfFilesWritten = createMap<number>();
host.writeFile = (p: string, content: string) => {
const count = mapOfFilesWritten.get(p);
mapOfFilesWritten.set(p, count ? count + 1 : 1);
return originalWriteFile(p, content);
};
createWatchModeWithConfigFile(configFile.path, host);
if (useOutFile) {
// Only out file
assert.equal(mapOfFilesWritten.size, 1);
}
else {
// main.js and main2.js
assert.equal(mapOfFilesWritten.size, 2);
}
mapOfFilesWritten.forEach((value, key) => {
assert.equal(value, 1, "Key: " + key);
});
}
it("with --outFile and multiple declaration files in the program", () => {
verifyFilesEmittedOnce(/*useOutFile*/ true);
});
it("without --outFile and multiple declaration files in the program", () => {
verifyFilesEmittedOnce(/*useOutFile*/ false);
});
});
describe("tsc-watch emit for configured projects", () => {
@@ -1687,7 +1745,7 @@ namespace ts.tscWatch {
host.runQueuedTimeoutCallbacks();
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
checkOutputErrors(host);
checkOutputErrors(host, emptyArray);
});
it("should compile correctly when resolved module goes missing and then comes back (module is not part of the root)", () => {
@@ -1733,7 +1791,7 @@ namespace ts.tscWatch {
host.reloadFS(filesWithImported);
host.checkTimeoutQueueLengthAndRun(1);
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
checkOutputErrors(host);
checkOutputErrors(host, emptyArray);
});
it("works when module resolution changes to ambient module", () => {
@@ -1773,7 +1831,7 @@ declare module "fs" {
host.reloadFS(filesWithNodeType);
host.runQueuedTimeoutCallbacks();
checkOutputErrors(host);
checkOutputErrors(host, emptyArray);
});
it("works when included file with ambient module changes", () => {
@@ -1816,7 +1874,127 @@ declare module "fs" {
file.content += fileContentWithFS;
host.reloadFS(files);
host.runQueuedTimeoutCallbacks();
checkOutputErrors(host);
checkOutputErrors(host, emptyArray);
});
it("works when reusing program with files from external library", () => {
interface ExpectedFile { path: string; isExpectedToEmit?: boolean; content?: string; }
const configDir = "/a/b/projects/myProject/src/";
const file1: FileOrFolder = {
path: configDir + "file1.ts",
content: 'import module1 = require("module1");\nmodule1("hello");'
};
const file2: FileOrFolder = {
path: configDir + "file2.ts",
content: 'import module11 = require("module1");\nmodule11("hello");'
};
const module1: FileOrFolder = {
path: "/a/b/projects/myProject/node_modules/module1/index.js",
content: "module.exports = options => { return options.toString(); }"
};
const configFile: FileOrFolder = {
path: configDir + "tsconfig.json",
content: JSON.stringify({
compilerOptions: {
allowJs: true,
rootDir: ".",
outDir: "../dist",
moduleResolution: "node",
maxNodeModuleJsDepth: 1
}
})
};
const outDirFolder = "/a/b/projects/myProject/dist/";
const programFiles = [file1, file2, module1, libFile];
const host = createWatchedSystem(programFiles.concat(configFile), { currentDirectory: "/a/b/projects/myProject/" });
const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramActualFiles(watch(), programFiles.map(f => f.path));
checkOutputErrors(host, emptyArray, /*isInitial*/ true);
const expectedFiles: ExpectedFile[] = [
createExpectedEmittedFile(file1),
createExpectedEmittedFile(file2),
createExpectedToNotEmitFile("index.js"),
createExpectedToNotEmitFile("src/index.js"),
createExpectedToNotEmitFile("src/file1.js"),
createExpectedToNotEmitFile("src/file2.js"),
createExpectedToNotEmitFile("lib.js"),
createExpectedToNotEmitFile("lib.d.ts")
];
verifyExpectedFiles(expectedFiles);
file1.content += "\n;";
expectedFiles[0].content += ";\n"; // Only emit file1 with this change
expectedFiles[1].isExpectedToEmit = false;
host.reloadFS(programFiles.concat(configFile));
host.runQueuedTimeoutCallbacks();
checkProgramActualFiles(watch(), programFiles.map(f => f.path));
checkOutputErrors(host, emptyArray);
verifyExpectedFiles(expectedFiles);
function verifyExpectedFiles(expectedFiles: ExpectedFile[]) {
forEach(expectedFiles, f => {
assert.equal(!!host.fileExists(f.path), f.isExpectedToEmit, "File " + f.path + " is expected to " + (f.isExpectedToEmit ? "emit" : "not emit"));
if (f.isExpectedToEmit) {
assert.equal(host.readFile(f.path), f.content, "Expected contents of " + f.path);
}
});
}
function createExpectedToNotEmitFile(fileName: string): ExpectedFile {
return {
path: outDirFolder + fileName,
isExpectedToEmit: false
};
}
function createExpectedEmittedFile(file: FileOrFolder): ExpectedFile {
return {
path: removeFileExtension(file.path.replace(configDir, outDirFolder)) + Extension.Js,
isExpectedToEmit: true,
content: '"use strict";\nexports.__esModule = true;\n' + file.content.replace("import", "var") + "\n"
};
}
});
});
describe("tsc-watch with when module emit is specified as node", () => {
it("when instead of filechanged recursive directory watcher is invoked", () => {
const configFile: FileOrFolder = {
path: "/a/rootFolder/project/tsconfig.json",
content: JSON.stringify({
"compilerOptions": {
"module": "none",
"allowJs": true,
"outDir": "Static/scripts/"
},
"include": [
"Scripts/**/*"
],
})
};
const outputFolder = "/a/rootFolder/project/Static/scripts/";
const file1: FileOrFolder = {
path: "/a/rootFolder/project/Scripts/TypeScript.ts",
content: "var z = 10;"
};
const file2: FileOrFolder = {
path: "/a/rootFolder/project/Scripts/Javascript.js",
content: "var zz = 10;"
};
const files = [configFile, file1, file2, libFile];
const host = createWatchedSystem(files);
const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
file1.content = "var zz30 = 100;";
host.reloadFS(files, /*invokeDirectoryWatcherInsteadOfFileChanged*/ true);
host.runQueuedTimeoutCallbacks();
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
const outputFile1 = changeExtension((outputFolder + getBaseFileName(file1.path)), ".js");
assert.isTrue(host.fileExists(outputFile1));
assert.equal(host.readFile(outputFile1), file1.content + host.newLine);
});
});
}
File diff suppressed because one or more lines are too long
+11 -9
View File
@@ -4,6 +4,8 @@
namespace ts.projectSystem {
import TI = server.typingsInstaller;
import validatePackageName = JsTyping.validatePackageName;
import PackageNameValidationResult = JsTyping.PackageNameValidationResult;
interface InstallerParams {
globalTypingsCacheLocation?: string;
@@ -266,7 +268,7 @@ namespace ts.projectSystem {
};
const host = createServerHost([file1]);
let enqueueIsCalled = false;
const installer = new (class extends Installer {
const installer: Installer = new (class extends Installer {
constructor() {
super(host, { typesRegistry: createTypesRegistry("jquery") });
}
@@ -983,21 +985,21 @@ namespace ts.projectSystem {
for (let i = 0; i < 8; i++) {
packageName += packageName;
}
assert.equal(TI.validatePackageName(packageName), TI.PackageNameValidationResult.NameTooLong);
assert.equal(validatePackageName(packageName), PackageNameValidationResult.NameTooLong);
});
it("name cannot start with dot", () => {
assert.equal(TI.validatePackageName(".foo"), TI.PackageNameValidationResult.NameStartsWithDot);
assert.equal(validatePackageName(".foo"), PackageNameValidationResult.NameStartsWithDot);
});
it("name cannot start with underscore", () => {
assert.equal(TI.validatePackageName("_foo"), TI.PackageNameValidationResult.NameStartsWithUnderscore);
assert.equal(validatePackageName("_foo"), PackageNameValidationResult.NameStartsWithUnderscore);
});
it("scoped packages not supported", () => {
assert.equal(TI.validatePackageName("@scope/bar"), TI.PackageNameValidationResult.ScopedPackagesNotSupported);
assert.equal(validatePackageName("@scope/bar"), PackageNameValidationResult.ScopedPackagesNotSupported);
});
it("non URI safe characters are not supported", () => {
assert.equal(TI.validatePackageName(" scope "), TI.PackageNameValidationResult.NameContainsNonURISafeCharacters);
assert.equal(TI.validatePackageName("; say Hello from TypeScript! #"), TI.PackageNameValidationResult.NameContainsNonURISafeCharacters);
assert.equal(TI.validatePackageName("a/b/c"), TI.PackageNameValidationResult.NameContainsNonURISafeCharacters);
assert.equal(validatePackageName(" scope "), PackageNameValidationResult.NameContainsNonURISafeCharacters);
assert.equal(validatePackageName("; say Hello from TypeScript! #"), PackageNameValidationResult.NameContainsNonURISafeCharacters);
assert.equal(validatePackageName("a/b/c"), PackageNameValidationResult.NameContainsNonURISafeCharacters);
});
});
@@ -1250,7 +1252,7 @@ namespace ts.projectSystem {
const host = createServerHost([f1, packageFile]);
let beginEvent: server.BeginInstallTypes;
let endEvent: server.EndInstallTypes;
const installer = new (class extends Installer {
const installer: Installer = new (class extends Installer {
constructor() {
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
}
+32 -8
View File
@@ -28,6 +28,7 @@ namespace ts.TestFSWithWatch {
executingFilePath?: string;
currentDirectory?: string;
newLine?: string;
useWindowsStylePaths?: boolean;
}
export function createWatchedSystem(fileOrFolderList: ReadonlyArray<FileOrFolder>, params?: TestServerHostCreationParameters): TestServerHost {
@@ -39,7 +40,8 @@ namespace ts.TestFSWithWatch {
params.executingFilePath || getExecutingFilePathFromLibFile(),
params.currentDirectory || "/",
fileOrFolderList,
params.newLine);
params.newLine,
params.useWindowsStylePaths);
return host;
}
@@ -52,7 +54,8 @@ namespace ts.TestFSWithWatch {
params.executingFilePath || getExecutingFilePathFromLibFile(),
params.currentDirectory || "/",
fileOrFolderList,
params.newLine);
params.newLine,
params.useWindowsStylePaths);
return host;
}
@@ -230,11 +233,14 @@ namespace ts.TestFSWithWatch {
readonly watchedDirectories = createMultiMap<TestDirectoryWatcher>();
readonly watchedDirectoriesRecursive = createMultiMap<TestDirectoryWatcher>();
readonly watchedFiles = createMultiMap<TestFileWatcher>();
private readonly executingFilePath: string;
private readonly currentDirectory: string;
constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: ReadonlyArray<FileOrFolder>, public readonly newLine = "\n") {
constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, executingFilePath: string, currentDirectory: string, fileOrFolderList: ReadonlyArray<FileOrFolder>, public readonly newLine = "\n", public readonly useWindowsStylePath?: boolean) {
this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName);
this.executingFilePath = this.getHostSpecificPath(executingFilePath);
this.currentDirectory = this.getHostSpecificPath(currentDirectory);
this.reloadFS(fileOrFolderList);
}
@@ -250,11 +256,24 @@ namespace ts.TestFSWithWatch {
return this.toPath(this.toNormalizedAbsolutePath(s));
}
reloadFS(fileOrFolderList: ReadonlyArray<FileOrFolder>) {
getHostSpecificPath(s: string) {
if (this.useWindowsStylePath && s.startsWith(directorySeparator)) {
return "c:/" + s.substring(1);
}
return s;
}
reloadFS(fileOrFolderList: ReadonlyArray<FileOrFolder>, invokeDirectoryWatcherInsteadOfFileChanged?: boolean) {
const mapNewLeaves = createMap<true>();
const isNewFs = this.fs.size === 0;
// always inject safelist file in the list of files
for (const fileOrDirectory of fileOrFolderList.concat(this.withSafeList ? safeList : [])) {
fileOrFolderList = fileOrFolderList.concat(this.withSafeList ? safeList : []);
const filesOrFoldersToLoad: ReadonlyArray<FileOrFolder> = !this.useWindowsStylePath ? fileOrFolderList :
fileOrFolderList.map<FileOrFolder>(f => {
const result = clone(f);
result.path = this.getHostSpecificPath(f.path);
return result;
});
for (const fileOrDirectory of filesOrFoldersToLoad) {
const path = this.toFullPath(fileOrDirectory.path);
mapNewLeaves.set(path, true);
// If its a change
@@ -265,7 +284,12 @@ namespace ts.TestFSWithWatch {
// Update file
if (currentEntry.content !== fileOrDirectory.content) {
currentEntry.content = fileOrDirectory.content;
this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed);
if (invokeDirectoryWatcherInsteadOfFileChanged) {
this.invokeDirectoryWatcher(getDirectoryPath(currentEntry.fullPath), currentEntry.fullPath);
}
else {
this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed);
}
}
}
else {
+389 -8
View File
File diff suppressed because it is too large Load Diff
+41 -3
View File
@@ -443,6 +443,8 @@ interface FileReader extends EventTarget, MSBaseReader {
readAsText(blob: Blob, encoding?: string): void;
addEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: FileReader, ev: MSBaseReaderEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: FileReader, ev: MSBaseReaderEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var FileReader: {
@@ -525,6 +527,8 @@ interface IDBDatabase extends EventTarget {
addEventListener(type: "versionchange", listener: (ev: IDBVersionChangeEvent) => any, useCapture?: boolean): void;
addEventListener<K extends keyof IDBDatabaseEventMap>(type: K, listener: (this: IDBDatabase, ev: IDBDatabaseEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof IDBDatabaseEventMap>(type: K, listener: (this: IDBDatabase, ev: IDBDatabaseEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var IDBDatabase: {
@@ -610,6 +614,8 @@ interface IDBOpenDBRequest extends IDBRequest {
onupgradeneeded: (this: IDBOpenDBRequest, ev: IDBVersionChangeEvent) => any;
addEventListener<K extends keyof IDBOpenDBRequestEventMap>(type: K, listener: (this: IDBOpenDBRequest, ev: IDBOpenDBRequestEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof IDBOpenDBRequestEventMap>(type: K, listener: (this: IDBOpenDBRequest, ev: IDBOpenDBRequestEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var IDBOpenDBRequest: {
@@ -632,6 +638,8 @@ interface IDBRequest extends EventTarget {
readonly transaction: IDBTransaction;
addEventListener<K extends keyof IDBRequestEventMap>(type: K, listener: (this: IDBRequest, ev: IDBRequestEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof IDBRequestEventMap>(type: K, listener: (this: IDBRequest, ev: IDBRequestEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var IDBRequest: {
@@ -659,6 +667,8 @@ interface IDBTransaction extends EventTarget {
readonly VERSION_CHANGE: string;
addEventListener<K extends keyof IDBTransactionEventMap>(type: K, listener: (this: IDBTransaction, ev: IDBTransactionEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof IDBTransactionEventMap>(type: K, listener: (this: IDBTransaction, ev: IDBTransactionEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var IDBTransaction: {
@@ -725,6 +735,8 @@ interface MessagePort extends EventTarget {
start(): void;
addEventListener<K extends keyof MessagePortEventMap>(type: K, listener: (this: MessagePort, ev: MessagePortEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof MessagePortEventMap>(type: K, listener: (this: MessagePort, ev: MessagePortEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var MessagePort: {
@@ -754,6 +766,8 @@ interface Notification extends EventTarget {
close(): void;
addEventListener<K extends keyof NotificationEventMap>(type: K, listener: (this: Notification, ev: NotificationEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof NotificationEventMap>(type: K, listener: (this: Notification, ev: NotificationEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var Notification: {
@@ -982,6 +996,8 @@ interface ServiceWorker extends EventTarget, AbstractWorker {
postMessage(message: any, transfer?: any[]): void;
addEventListener<K extends keyof ServiceWorkerEventMap>(type: K, listener: (this: ServiceWorker, ev: ServiceWorkerEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof ServiceWorkerEventMap>(type: K, listener: (this: ServiceWorker, ev: ServiceWorkerEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var ServiceWorker: {
@@ -1007,6 +1023,8 @@ interface ServiceWorkerRegistration extends EventTarget {
update(): Promise<void>;
addEventListener<K extends keyof ServiceWorkerRegistrationEventMap>(type: K, listener: (this: ServiceWorkerRegistration, ev: ServiceWorkerRegistrationEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof ServiceWorkerRegistrationEventMap>(type: K, listener: (this: ServiceWorkerRegistration, ev: ServiceWorkerRegistrationEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var ServiceWorkerRegistration: {
@@ -1073,6 +1091,8 @@ interface WebSocket extends EventTarget {
readonly OPEN: number;
addEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var WebSocket: {
@@ -1094,6 +1114,8 @@ interface Worker extends EventTarget, AbstractWorker {
terminate(): void;
addEventListener<K extends keyof WorkerEventMap>(type: K, listener: (this: Worker, ev: WorkerEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof WorkerEventMap>(type: K, listener: (this: Worker, ev: WorkerEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var Worker: {
@@ -1135,6 +1157,8 @@ interface XMLHttpRequest extends EventTarget, XMLHttpRequestEventTarget {
readonly UNSENT: number;
addEventListener<K extends keyof XMLHttpRequestEventMap>(type: K, listener: (this: XMLHttpRequest, ev: XMLHttpRequestEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof XMLHttpRequestEventMap>(type: K, listener: (this: XMLHttpRequest, ev: XMLHttpRequestEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var XMLHttpRequest: {
@@ -1150,6 +1174,8 @@ declare var XMLHttpRequest: {
interface XMLHttpRequestUpload extends EventTarget, XMLHttpRequestEventTarget {
addEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestUpload, ev: XMLHttpRequestEventTargetEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestUpload, ev: XMLHttpRequestEventTargetEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var XMLHttpRequestUpload: {
@@ -1165,6 +1191,8 @@ interface AbstractWorker {
onerror: (this: AbstractWorker, ev: ErrorEvent) => any;
addEventListener<K extends keyof AbstractWorkerEventMap>(type: K, listener: (this: AbstractWorker, ev: AbstractWorkerEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof AbstractWorkerEventMap>(type: K, listener: (this: AbstractWorker, ev: AbstractWorkerEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
interface Body {
@@ -1203,6 +1231,8 @@ interface MSBaseReader {
readonly LOADING: number;
addEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: MSBaseReader, ev: MSBaseReaderEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: MSBaseReader, ev: MSBaseReaderEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
interface NavigatorBeacon {
@@ -1258,6 +1288,8 @@ interface XMLHttpRequestEventTarget {
ontimeout: (this: XMLHttpRequest, ev: ProgressEvent) => any;
addEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
interface Client {
@@ -1294,6 +1326,8 @@ interface DedicatedWorkerGlobalScope extends WorkerGlobalScope {
postMessage(message: any, transfer?: any[]): void;
addEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(type: K, listener: (this: DedicatedWorkerGlobalScope, ev: DedicatedWorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(type: K, listener: (this: DedicatedWorkerGlobalScope, ev: DedicatedWorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var DedicatedWorkerGlobalScope: {
@@ -1405,6 +1439,8 @@ interface ServiceWorkerGlobalScope extends WorkerGlobalScope {
skipWaiting(): Promise<void>;
addEventListener<K extends keyof ServiceWorkerGlobalScopeEventMap>(type: K, listener: (this: ServiceWorkerGlobalScope, ev: ServiceWorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof ServiceWorkerGlobalScopeEventMap>(type: K, listener: (this: ServiceWorkerGlobalScope, ev: ServiceWorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var ServiceWorkerGlobalScope: {
@@ -1450,6 +1486,8 @@ interface WorkerGlobalScope extends EventTarget, WorkerUtils, WindowConsole, Glo
createImageBitmap(image: ImageBitmap | ImageData | Blob, sx: number, sy: number, sw: number, sh: number, options?: ImageBitmapOptions): Promise<ImageBitmap>;
addEventListener<K extends keyof WorkerGlobalScopeEventMap>(type: K, listener: (this: WorkerGlobalScope, ev: WorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
removeEventListener<K extends keyof WorkerGlobalScopeEventMap>(type: K, listener: (this: WorkerGlobalScope, ev: WorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
declare var WorkerGlobalScope: {
@@ -1642,7 +1680,7 @@ interface EcKeyAlgorithm extends KeyAlgorithm {
typedCurve: string;
}
interface EcKeyImportParams {
interface EcKeyImportParams extends Algorithm {
namedCurve: string;
}
@@ -1819,7 +1857,6 @@ declare function msWriteProfilerMark(profilerMarkName: string): void;
declare function createImageBitmap(image: ImageBitmap | ImageData | Blob, options?: ImageBitmapOptions): Promise<ImageBitmap>;
declare function createImageBitmap(image: ImageBitmap | ImageData | Blob, sx: number, sy: number, sw: number, sh: number, options?: ImageBitmapOptions): Promise<ImageBitmap>;
declare function dispatchEvent(evt: Event): boolean;
declare function removeEventListener(type: string, listener?: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
declare var indexedDB: IDBFactory;
declare var msIndexedDB: IDBFactory;
declare var navigator: WorkerNavigator;
@@ -1838,9 +1875,10 @@ declare function btoa(rawString: string): string;
declare var console: Console;
declare function fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
declare function dispatchEvent(evt: Event): boolean;
declare function removeEventListener(type: string, listener?: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
declare function addEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(type: K, listener: (this: DedicatedWorkerGlobalScope, ev: DedicatedWorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
declare function addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
declare function removeEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(type: K, listener: (this: DedicatedWorkerGlobalScope, ev: DedicatedWorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
declare function removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
type AlgorithmIdentifier = string | Algorithm;
type BodyInit = any;
type IDBKeyPath = string;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -14,7 +14,7 @@ namespace ts.server {
}
/* @internal */
export function extractMessage(message: string) {
export function extractMessage(message: string): string {
// Read the content length
const contentLengthPrefix = "Content-Length: ";
const lines = message.split(/\r?\n/);
@@ -198,7 +198,9 @@ namespace ts.server {
const request = this.processRequest<protocol.CompletionDetailsRequest>(CommandNames.CompletionDetails, args);
const response = this.processResponse<protocol.CompletionDetailsResponse>(request);
Debug.assert(response.body.length === 1, "Unexpected length of completion details response body.");
return response.body[0];
const convertedCodeActions = map(response.body[0].codeActions, codeAction => this.convertCodeActions(codeAction, fileName));
return { ...response.body[0], codeActions: convertedCodeActions };
}
getCompletionEntrySymbol(_fileName: string, _position: number, _entryName: string): Symbol {
@@ -559,6 +561,8 @@ namespace ts.server {
return response.body.map(entry => this.convertCodeActions(entry, file));
}
applyCodeActionCommand = notImplemented;
private createFileLocationOrRangeRequestArgs(positionOrRange: number | TextRange, fileName: string): protocol.FileLocationOrRangeRequestArgs {
return typeof positionOrRange === "number"
? this.createFileLocationRequestArgs(fileName, positionOrRange)
+50 -34
View File
@@ -220,13 +220,28 @@ namespace ts.server {
interface FilePropertyReader<T> {
getFileName(f: T): string;
getScriptKind(f: T): ScriptKind;
getScriptKind(f: T, extraFileExtensions?: JsFileExtensionInfo[]): ScriptKind;
hasMixedContent(f: T, extraFileExtensions: JsFileExtensionInfo[]): boolean;
}
const fileNamePropertyReader: FilePropertyReader<string> = {
getFileName: x => x,
getScriptKind: _ => undefined,
getScriptKind: (fileName, extraFileExtensions) => {
let result: ScriptKind;
if (extraFileExtensions) {
const fileExtension = getAnyExtensionFromPath(fileName);
if (fileExtension) {
some(extraFileExtensions, info => {
if (info.extension === fileExtension) {
result = info.scriptKind;
return true;
}
return false;
});
}
}
return result;
},
hasMixedContent: (fileName, extraFileExtensions) => some(extraFileExtensions, ext => ext.isMixedContent && fileExtensionIs(fileName, ext.extension)),
};
@@ -554,6 +569,11 @@ namespace ts.server {
});
}
/*@internal*/
hasPendingProjectUpdate(project: Project) {
return this.pendingProjectUpdates.has(project.getProjectName());
}
private sendProjectsUpdatedInBackgroundEvent() {
if (!this.eventHandler) {
return;
@@ -795,8 +815,14 @@ namespace ts.server {
);
}
/** Gets the config file existence info for the configured project */
/*@internal*/
getConfigFileExistenceInfo(project: ConfiguredProject) {
return this.configFileExistenceInfoCache.get(project.canonicalConfigFilePath);
}
private onConfigChangedForConfiguredProject(project: ConfiguredProject, eventKind: FileWatcherEventKind) {
const configFileExistenceInfo = this.configFileExistenceInfoCache.get(project.canonicalConfigFilePath);
const configFileExistenceInfo = this.getConfigFileExistenceInfo(project);
if (eventKind === FileWatcherEventKind.Deleted) {
// Update the cached status
// We arent updating or removing the cached config file presence info as that will be taken care of by
@@ -898,18 +924,6 @@ namespace ts.server {
return project;
}
private addToListOfOpenFiles(info: ScriptInfo) {
Debug.assert(!info.isOrphan());
for (const p of info.containingProjects) {
// file is the part of configured project, addref the project
if (p.projectKind === ProjectKind.Configured) {
((<ConfiguredProject>p)).addOpenRef();
}
}
this.openFiles.push(info);
}
/**
* Remove this file from the set of open, non-configured files.
* @param info The file that has been closed or newly configured
@@ -932,10 +946,8 @@ namespace ts.server {
if (info.hasMixedContent) {
info.registerFileUpdate();
}
// Delete the reference to the open configured projects but
// do not remove the project so that we can reuse this project
// Do not remove the project so that we can reuse this project
// if it would need to be re-created with next file open
(<ConfiguredProject>p).deleteOpenRef();
}
else if (p.projectKind === ProjectKind.Inferred && p.isRoot(info)) {
// If this was the open root file of inferred project
@@ -1025,7 +1037,7 @@ namespace ts.server {
}
private setConfigFileExistenceByNewConfiguredProject(project: ConfiguredProject) {
const configFileExistenceInfo = this.configFileExistenceInfoCache.get(project.canonicalConfigFilePath);
const configFileExistenceInfo = this.getConfigFileExistenceInfo(project);
if (configFileExistenceInfo) {
Debug.assert(configFileExistenceInfo.exists);
// close existing watcher
@@ -1054,7 +1066,7 @@ namespace ts.server {
}
private setConfigFileExistenceInfoByClosedConfiguredProject(closedProject: ConfiguredProject) {
const configFileExistenceInfo = this.configFileExistenceInfoCache.get(closedProject.canonicalConfigFilePath);
const configFileExistenceInfo = this.getConfigFileExistenceInfo(closedProject);
Debug.assert(!!configFileExistenceInfo);
if (configFileExistenceInfo.openFilesImpactedByConfigFile.size) {
const configFileName = closedProject.getConfigFilePath();
@@ -1338,10 +1350,10 @@ namespace ts.server {
const projectOptions: ProjectOptions = {
files: parsedCommandLine.fileNames,
compilerOptions: parsedCommandLine.options,
configHasExtendsProperty: parsedCommandLine.raw["extends"] !== undefined,
configHasFilesProperty: parsedCommandLine.raw["files"] !== undefined,
configHasIncludeProperty: parsedCommandLine.raw["include"] !== undefined,
configHasExcludeProperty: parsedCommandLine.raw["exclude"] !== undefined,
configHasExtendsProperty: parsedCommandLine.raw.extends !== undefined,
configHasFilesProperty: parsedCommandLine.raw.files !== undefined,
configHasIncludeProperty: parsedCommandLine.raw.include !== undefined,
configHasExcludeProperty: parsedCommandLine.raw.exclude !== undefined,
wildcardDirectories: createMapFromTemplate(parsedCommandLine.wildcardDirectories),
typeAcquisition: parsedCommandLine.typeAcquisition,
compileOnSave: parsedCommandLine.compileOnSave
@@ -1504,7 +1516,7 @@ namespace ts.server {
scriptInfo = normalizedPath;
}
else {
const scriptKind = propertyReader.getScriptKind(f);
const scriptKind = propertyReader.getScriptKind(f, this.hostConfiguration.extraFileExtensions);
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
scriptInfo = this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(normalizedPath, scriptKind, hasMixedContent, project.directoryStructureHost);
path = scriptInfo.path;
@@ -1943,7 +1955,8 @@ namespace ts.server {
this.assignOrphanScriptInfoToInferredProject(info, projectRootPath);
}
this.addToListOfOpenFiles(info);
Debug.assert(!info.isOrphan());
this.openFiles.push(info);
if (sendConfigFileDiagEvent) {
configFileErrors = project.getAllProjectErrors();
@@ -2043,11 +2056,14 @@ namespace ts.server {
}
}
private closeConfiguredProject(configFile: NormalizedPath): boolean {
private closeConfiguredProjectReferencedFromExternalProject(configFile: NormalizedPath): boolean {
const configuredProject = this.findConfiguredProjectByProjectName(configFile);
if (configuredProject && configuredProject.deleteOpenRef() === 0) {
this.removeProject(configuredProject);
return true;
if (configuredProject) {
configuredProject.deleteExternalProjectReference();
if (!configuredProject.hasOpenRef()) {
this.removeProject(configuredProject);
return true;
}
}
return false;
}
@@ -2058,7 +2074,7 @@ namespace ts.server {
if (configFiles) {
let shouldRefreshInferredProjects = false;
for (const configFile of configFiles) {
if (this.closeConfiguredProject(configFile)) {
if (this.closeConfiguredProjectReferencedFromExternalProject(configFile)) {
shouldRefreshInferredProjects = true;
}
}
@@ -2253,7 +2269,7 @@ namespace ts.server {
const newConfig = tsConfigFiles[iNew];
const oldConfig = oldConfigFiles[iOld];
if (oldConfig < newConfig) {
this.closeConfiguredProject(oldConfig);
this.closeConfiguredProjectReferencedFromExternalProject(oldConfig);
iOld++;
}
else if (oldConfig > newConfig) {
@@ -2268,7 +2284,7 @@ namespace ts.server {
}
for (let i = iOld; i < oldConfigFiles.length; i++) {
// projects for all remaining old config files should be closed
this.closeConfiguredProject(oldConfigFiles[i]);
this.closeConfiguredProjectReferencedFromExternalProject(oldConfigFiles[i]);
}
}
}
@@ -2283,7 +2299,7 @@ namespace ts.server {
}
if (project && !contains(exisingConfigFiles, tsconfigFile)) {
// keep project alive even if no documents are opened - its lifetime is bound to the lifetime of containing external project
project.addOpenRef();
project.addExternalProjectReference();
}
}
}
+63 -28
View File
@@ -242,10 +242,26 @@ namespace ts.server {
this.markAsDirty();
}
isKnownTypesPackageName(name: string): boolean {
return this.typingsCache.isKnownTypesPackageName(name);
}
installPackage(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult> {
return this.typingsCache.installPackage({ ...options, projectRootPath: this.toPath(this.currentDirectory) });
}
private get typingsCache(): TypingsCache {
return this.projectService.typingsCache;
}
// Method of LanguageServiceHost
getCompilationSettings() {
return this.compilerOptions;
}
// Method to support public API
getCompilerOptions() {
return this.getCompilationSettings();
}
getNewLine() {
return this.directoryStructureHost.newLine;
}
@@ -427,31 +443,33 @@ namespace ts.server {
if (!this.builder) {
this.builder = createBuilder({
getCanonicalFileName: this.projectService.toCanonicalFileName,
getEmitOutput: (_program, sourceFile, emitOnlyDts, isDetailed) =>
this.getFileEmitOutput(sourceFile, emitOnlyDts, isDetailed),
computeHash: data =>
this.projectService.host.createHash(data),
shouldEmitFile: sourceFile =>
!this.projectService.getScriptInfoForPath(sourceFile.path).isDynamicOrHasMixedContent()
computeHash: data => this.projectService.host.createHash(data)
});
}
}
private shouldEmitFile(scriptInfo: ScriptInfo) {
return scriptInfo && !scriptInfo.isDynamicOrHasMixedContent();
}
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[] {
if (!this.languageServiceEnabled) {
return [];
}
this.updateGraph();
this.ensureBuilder();
return this.builder.getFilesAffectedBy(this.program, scriptInfo.path);
return mapDefined(this.builder.getFilesAffectedBy(this.program, scriptInfo.path),
sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined);
}
/**
* Returns true if emit was conducted
*/
emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean {
this.ensureBuilder();
const { emitSkipped, outputFiles } = this.builder.emitFile(this.program, scriptInfo.path);
if (!this.languageServiceEnabled || !this.shouldEmitFile(scriptInfo)) {
return false;
}
const { emitSkipped, outputFiles } = this.getLanguageService(/*ensureSynchronized*/ false).getEmitOutput(scriptInfo.fileName);
if (!emitSkipped) {
for (const outputFile of outputFiles) {
const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, this.currentDirectory);
@@ -577,13 +595,6 @@ namespace ts.server {
});
}
private getFileEmitOutput(sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean) {
if (!this.languageServiceEnabled) {
return undefined;
}
return this.getLanguageService(/*ensureSynchronized*/ false).getEmitOutput(sourceFile.fileName, emitOnlyDtsFiles, isDetailed);
}
getExcludedFiles(): ReadonlyArray<NormalizedPath> {
return emptyArray;
}
@@ -892,9 +903,7 @@ namespace ts.server {
}
getScriptInfoForNormalizedPath(fileName: NormalizedPath) {
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(
fileName, /*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.directoryStructureHost
);
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(fileName);
if (scriptInfo && !scriptInfo.isAttached(this)) {
return Errors.ThrowProjectDoesNotContainDocument(fileName, this);
}
@@ -902,7 +911,7 @@ namespace ts.server {
}
getScriptInfo(uncheckedFileName: string) {
return this.getScriptInfoForNormalizedPath(toNormalizedPath(uncheckedFileName));
return this.projectService.getScriptInfo(uncheckedFileName);
}
filesToString(writeProjectFileNames: boolean) {
@@ -1130,8 +1139,8 @@ namespace ts.server {
private plugins: PluginModule[] = [];
/** Used for configured projects which may have multiple open roots */
openRefCount = 0;
/** Ref count to the project when opened from external project */
private externalProjectRefCount = 0;
private projectErrors: Diagnostic[];
@@ -1342,17 +1351,43 @@ namespace ts.server {
super.close();
}
addOpenRef() {
this.openRefCount++;
/* @internal */
addExternalProjectReference() {
this.externalProjectRefCount++;
}
deleteOpenRef() {
this.openRefCount--;
return this.openRefCount;
/* @internal */
deleteExternalProjectReference() {
this.externalProjectRefCount--;
}
/** Returns true if the project is needed by any of the open script info/external project */
/* @internal */
hasOpenRef() {
return !!this.openRefCount;
if (!!this.externalProjectRefCount) {
return true;
}
// Closed project doesnt have any reference
if (this.isClosed()) {
return false;
}
const configFileExistenceInfo = this.projectService.getConfigFileExistenceInfo(this);
if (this.projectService.hasPendingProjectUpdate(this)) {
// If there is pending update for this project,
// we dont know if this project would be needed by any of the open files impacted by this config file
// In that case keep the project alive if there are open files impacted by this project
return !!configFileExistenceInfo.openFilesImpactedByConfigFile.size;
}
// If there is no pending update for this project,
// We know exact set of open files that get impacted by this configured project as the files in the project
// The project is referenced only if open files impacted by this project are present in this project
return forEachEntry(
configFileExistenceInfo.openFilesImpactedByConfigFile,
(_value, infoPath) => this.containsScriptInfo(this.projectService.getScriptInfoForPath(infoPath as Path))
) || false;
}
getEffectiveTypeRoots() {
+33 -1
View File
@@ -97,6 +97,7 @@ namespace ts.server.protocol {
BreakpointStatement = "breakpointStatement",
CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects",
GetCodeFixes = "getCodeFixes",
ApplyCodeActionCommand = "applyCodeActionCommand",
/* @internal */
GetCodeFixesFull = "getCodeFixes-full",
GetSupportedCodeFixes = "getSupportedCodeFixes",
@@ -128,6 +129,8 @@ namespace ts.server.protocol {
* Client-initiated request message
*/
export interface Request extends Message {
type: "request";
/**
* The command to execute
*/
@@ -150,6 +153,8 @@ namespace ts.server.protocol {
* Server-initiated event message
*/
export interface Event extends Message {
type: "event";
/**
* Name of event
*/
@@ -165,6 +170,8 @@ namespace ts.server.protocol {
* Response by server to client request message.
*/
export interface Response extends Message {
type: "response";
/**
* Sequence number of the request message.
*/
@@ -181,7 +188,8 @@ namespace ts.server.protocol {
command: string;
/**
* Contains error message if success === false.
* If success === false, this should always be provided.
* Otherwise, may (or may not) contain a success message.
*/
message?: string;
@@ -523,6 +531,14 @@ namespace ts.server.protocol {
arguments: CodeFixRequestArgs;
}
export interface ApplyCodeActionCommandRequest extends Request {
command: CommandTypes.ApplyCodeActionCommand;
arguments: ApplyCodeActionCommandRequestArgs;
}
// All we need is the `success` and `message` fields of Response.
export interface ApplyCodeActionCommandResponse extends Response {}
export interface FileRangeRequestArgs extends FileRequestArgs {
/**
* The line number for the request (1-based).
@@ -567,6 +583,10 @@ namespace ts.server.protocol {
errorCodes?: number[];
}
export interface ApplyCodeActionCommandRequestArgs extends FileRequestArgs {
command: {};
}
/**
* Response for GetCodeFixes request.
*/
@@ -1553,6 +1573,8 @@ namespace ts.server.protocol {
description: string;
/** Text changes to apply to each file as part of the code action */
changes: FileCodeEdits[];
/** A command is an opaque object that should be passed to `ApplyCodeActionCommandRequestArgs` without modification. */
commands?: {}[];
}
/**
@@ -1670,6 +1692,11 @@ namespace ts.server.protocol {
* this span should be used instead of the default one.
*/
replacementSpan?: TextSpan;
/**
* Indicates whether commiting this completion entry will require additional code actions to be
* made to avoid errors. The CompletionEntryDetails will have these actions.
*/
hasAction?: true;
}
/**
@@ -1702,6 +1729,11 @@ namespace ts.server.protocol {
* JSDoc tags for the symbol.
*/
tags: JSDocTagInfo[];
/**
* The associated code actions for this entry
*/
codeActions?: CodeAction[];
}
export interface CompletionsResponse extends Response {
+55 -9
View File
@@ -250,6 +250,9 @@ namespace ts.server {
private activeRequestCount = 0;
private requestQueue: QueuedOperation[] = [];
private requestMap = createMap<QueuedOperation>(); // Maps operation ID to newest requestQueue entry with that ID
/** We will lazily request the types registry on the first call to `isKnownTypesPackageName` and store it in `typesRegistryCache`. */
private requestedRegistry: boolean;
private typesRegistryCache: Map<void> | undefined;
// This number is essentially arbitrary. Processing more than one typings request
// at a time makes sense, but having too many in the pipe results in a hang
@@ -258,7 +261,7 @@ namespace ts.server {
// buffer, but we have yet to find a way to retrieve that value.
private static readonly maxActiveRequestCount = 10;
private static readonly requestDelayMillis = 100;
private packageInstalledPromise: { resolve(value: ApplyCodeActionCommandResult): void, reject(reason: any): void };
constructor(
private readonly telemetryEnabled: boolean,
@@ -278,6 +281,31 @@ namespace ts.server {
}
}
isKnownTypesPackageName(name: string): boolean {
// We want to avoid looking this up in the registry as that is expensive. So first check that it's actually an NPM package.
const validationResult = JsTyping.validatePackageName(name);
if (validationResult !== JsTyping.PackageNameValidationResult.Ok) {
return false;
}
if (this.requestedRegistry) {
return !!this.typesRegistryCache && this.typesRegistryCache.has(name);
}
this.requestedRegistry = true;
this.send({ kind: "typesRegistry" });
return false;
}
installPackage(options: InstallPackageOptionsWithProjectRootPath): Promise<ApplyCodeActionCommandResult> {
const rq: InstallPackageRequest = { kind: "installPackage", ...options };
this.send(rq);
Debug.assert(this.packageInstalledPromise === undefined);
return new Promise((resolve, reject) => {
this.packageInstalledPromise = { resolve, reject };
});
}
private reportInstallerProcessId() {
if (this.installerPidReported) {
return;
@@ -343,23 +371,27 @@ namespace ts.server {
}
onProjectClosed(p: Project): void {
this.installer.send({ projectName: p.getProjectName(), kind: "closeProject" });
this.send({ projectName: p.getProjectName(), kind: "closeProject" });
}
private send(rq: TypingInstallerRequestUnion): void {
this.installer.send(rq);
}
enqueueInstallTypingsRequest(project: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray<string>): void {
const request = createInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
if (this.logger.hasLevel(LogLevel.verbose)) {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Scheduling throttled operation: ${JSON.stringify(request)}`);
this.logger.info(`Scheduling throttled operation:${stringifyIndented(request)}`);
}
}
const operationId = project.getProjectName();
const operation = () => {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Sending request: ${JSON.stringify(request)}`);
this.logger.info(`Sending request:${stringifyIndented(request)}`);
}
this.installer.send(request);
this.send(request);
};
const queuedRequest: QueuedOperation = { operationId, operation };
@@ -375,12 +407,26 @@ namespace ts.server {
}
}
private handleMessage(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse) {
private handleMessage(response: TypesRegistryResponse | PackageInstalledResponse | SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse) {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Received response: ${JSON.stringify(response)}`);
this.logger.info(`Received response:${stringifyIndented(response)}`);
}
switch (response.kind) {
case EventTypesRegistry:
this.typesRegistryCache = ts.createMapFromTemplate(response.typesRegistry);
break;
case EventPackageInstalled: {
const { success, message } = response;
if (success) {
this.packageInstalledPromise.resolve({ successMessage: message });
}
else {
this.packageInstalledPromise.reject(message);
}
this.packageInstalledPromise = undefined;
break;
}
case EventInitializationFailed:
{
if (!this.eventSender) {
@@ -580,7 +626,7 @@ namespace ts.server {
function createLogger() {
const cmdLineLogFileName = findArgument("--logFile");
const cmdLineVerbosity = getLogLevel(findArgument("--logVerbosity"));
const envLogOptions = parseLoggingEnvironmentString(process.env["TSS_LOG"]);
const envLogOptions = parseLoggingEnvironmentString(process.env.TSS_LOG);
const logFileName = cmdLineLogFileName
? stripQuotes(cmdLineLogFileName)
@@ -780,7 +826,7 @@ namespace ts.server {
try {
const args = [combinePaths(__dirname, "watchGuard.js"), path];
if (logger.hasLevel(LogLevel.verbose)) {
logger.info(`Starting ${process.execPath} with args ${JSON.stringify(args)}`);
logger.info(`Starting ${process.execPath} with args:${stringifyIndented(args)}`);
}
childProcess.execFileSync(process.execPath, args, { stdio: "ignore", env: { "ELECTRON_RUN_AS_NODE": "1" } });
status = true;
+104 -44
View File
@@ -131,7 +131,7 @@ namespace ts.server {
const json = JSON.stringify(msg);
if (verboseLogging) {
logger.info(msg.type + ": " + json);
logger.info(`${msg.type}:${indent(json)}`);
}
const len = byteLength(json, "utf8");
@@ -383,9 +383,9 @@ namespace ts.server {
public logError(err: Error, cmd: string) {
let msg = "Exception on executing command " + cmd;
if (err.message) {
msg += ":\n" + err.message;
msg += ":\n" + indent(err.message);
if ((<StackTraceError>err).stack) {
msg += "\n" + (<StackTraceError>err).stack;
msg += "\n" + indent((<StackTraceError>err).stack);
}
}
this.logger.msg(msg, Msg.Err);
@@ -411,19 +411,27 @@ namespace ts.server {
this.send(ev);
}
public output(info: any, cmdName: string, reqSeq = 0, errorMsg?: string) {
// For backwards-compatibility only.
public output(info: any, cmdName: string, reqSeq?: number, errorMsg?: string): void {
this.doOutput(info, cmdName, reqSeq, /*success*/ !errorMsg, errorMsg);
}
private doOutput(info: {} | undefined, cmdName: string, reqSeq: number, success: boolean, message?: string): void {
const res: protocol.Response = {
seq: 0,
type: "response",
command: cmdName,
request_seq: reqSeq,
success: !errorMsg,
success,
};
if (!errorMsg) {
if (success) {
res.body = info;
}
else {
res.message = errorMsg;
Debug.assert(info === undefined);
}
if (message) {
res.message = message;
}
this.send(res);
}
@@ -1186,11 +1194,12 @@ namespace ts.server {
const completions = project.getLanguageService().getCompletionsAtPosition(file, position);
if (simplifiedResult) {
return mapDefined(completions && completions.entries, entry => {
return mapDefined<CompletionEntry, protocol.CompletionEntry>(completions && completions.entries, entry => {
if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) {
const { name, kind, kindModifiers, sortText, replacementSpan } = entry;
const { name, kind, kindModifiers, sortText, replacementSpan, hasAction } = entry;
const convertedSpan = replacementSpan ? this.toLocationTextSpan(replacementSpan, scriptInfo) : undefined;
return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan };
// Use `hasAction || undefined` to avoid serializing `false`.
return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan, hasAction: hasAction || undefined };
}
}).sort((a, b) => compareStrings(a.name, b.name));
}
@@ -1201,10 +1210,20 @@ namespace ts.server {
private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): ReadonlyArray<protocol.CompletionEntryDetails> {
const { file, project } = this.getFileAndProject(args);
const position = this.getPositionInFile(args, file);
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
const formattingOptions = project.projectService.getFormatCodeOptions(file);
return mapDefined(args.entryNames, entryName =>
project.getLanguageService().getCompletionEntryDetails(file, position, entryName));
return mapDefined(args.entryNames, entryName => {
const details = project.getLanguageService().getCompletionEntryDetails(file, position, entryName, formattingOptions);
if (details) {
const mappedCodeActions = map(details.codeActions, action => this.mapCodeAction(action, scriptInfo));
return { ...details, codeActions: mappedCodeActions };
}
else {
return undefined;
}
});
}
private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): ReadonlyArray<protocol.CompileOnSaveAffectedFileListSingleProject> {
@@ -1304,7 +1323,7 @@ namespace ts.server {
this.changeSeq++;
// make sure no changes happen before this one is finished
if (project.reloadScript(file, tempFileName)) {
this.output(undefined, CommandNames.Reload, reqSeq);
this.doOutput(/*info*/ undefined, CommandNames.Reload, reqSeq, /*success*/ true);
}
}
@@ -1500,16 +1519,18 @@ namespace ts.server {
}
if (simplifiedResult) {
const file = result.renameFilename;
let location: protocol.Location | undefined;
if (file !== undefined && result.renameLocation !== undefined) {
const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(file));
location = renameScriptInfo.positionToLineOffset(result.renameLocation);
const { renameFilename, renameLocation, edits } = result;
let mappedRenameLocation: protocol.Location | undefined;
if (renameFilename !== undefined && renameLocation !== undefined) {
const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(renameFilename));
const snapshot = renameScriptInfo.getSnapshot();
const oldText = snapshot.getText(0, snapshot.getLength());
mappedRenameLocation = getLocationInNewDocument(oldText, renameFilename, renameLocation, edits);
}
return {
renameLocation: location,
renameFilename: file,
edits: result.edits.map(change => this.mapTextChangesToCodeEdits(project, change))
renameLocation: mappedRenameLocation,
renameFilename,
edits: edits.map(change => this.mapTextChangesToCodeEdits(project, change))
};
}
else {
@@ -1539,6 +1560,15 @@ namespace ts.server {
}
}
private applyCodeActionCommand(commandName: string, requestSeq: number, args: protocol.ApplyCodeActionCommandRequestArgs): void {
const { file, project } = this.getFileAndProject(args);
const output = (success: boolean, message: string) => this.doOutput({}, commandName, requestSeq, success, message);
const command = args.command as CodeActionCommand; // They should be sending back the command we sent them.
project.getLanguageService().applyCodeActionCommand(file, command).then(
({ successMessage }) => { output(/*success*/ true, successMessage); },
error => { output(/*success*/ false, error); });
}
private getStartAndEndPosition(args: protocol.FileRangeRequestArgs, scriptInfo: ScriptInfo) {
let startPosition: number = undefined, endPosition: number = undefined;
if (args.startPosition !== undefined) {
@@ -1561,14 +1591,12 @@ namespace ts.server {
return { startPosition, endPosition };
}
private mapCodeAction(codeAction: CodeAction, scriptInfo: ScriptInfo): protocol.CodeAction {
return {
description: codeAction.description,
changes: codeAction.changes.map(change => ({
fileName: change.fileName,
textChanges: change.textChanges.map(textChange => this.convertTextChangeToCodeEdit(textChange, scriptInfo))
}))
};
private mapCodeAction({ description, changes: unmappedChanges, commands }: CodeAction, scriptInfo: ScriptInfo): protocol.CodeAction {
const changes = unmappedChanges.map(change => ({
fileName: change.fileName,
textChanges: change.textChanges.map(textChange => this.convertTextChangeToCodeEdit(textChange, scriptInfo))
}));
return { description, changes, commands };
}
private mapTextChangesToCodeEdits(project: Project, textChanges: FileTextChanges): protocol.FileCodeEdits {
@@ -1654,15 +1682,15 @@ namespace ts.server {
exit() {
}
private notRequired() {
private notRequired(): HandlerResponse {
return { responseRequired: false };
}
private requiredResponse(response: any) {
private requiredResponse(response: {}): HandlerResponse {
return { response, responseRequired: true };
}
private handlers = createMapFromTemplate<(request: protocol.Request) => { response?: any, responseRequired?: boolean }>({
private handlers = createMapFromTemplate<(request: protocol.Request) => HandlerResponse>({
[CommandNames.OpenExternalProject]: (request: protocol.OpenExternalProjectRequest) => {
this.projectService.openExternalProject(request.arguments, /*suppressRefreshOfInferredProjects*/ false);
// TODO: report errors
@@ -1846,7 +1874,7 @@ namespace ts.server {
},
[CommandNames.Configure]: (request: protocol.ConfigureRequest) => {
this.projectService.setHostConfiguration(request.arguments);
this.output(undefined, CommandNames.Configure, request.seq);
this.doOutput(/*info*/ undefined, CommandNames.Configure, request.seq, /*success*/ true);
return this.notRequired();
},
[CommandNames.Reload]: (request: protocol.ReloadRequest) => {
@@ -1913,6 +1941,10 @@ namespace ts.server {
[CommandNames.GetCodeFixesFull]: (request: protocol.CodeFixRequest) => {
return this.requiredResponse(this.getCodeFixes(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.ApplyCodeActionCommand]: (request: protocol.ApplyCodeActionCommandRequest) => {
this.applyCodeActionCommand(request.command, request.seq, request.arguments);
return this.notRequired(); // Response will come asynchronously.
},
[CommandNames.GetSupportedCodeFixes]: () => {
return this.requiredResponse(this.getSupportedCodeFixes());
},
@@ -1927,7 +1959,7 @@ namespace ts.server {
}
});
public addProtocolHandler(command: string, handler: (request: protocol.Request) => { response?: any, responseRequired: boolean }) {
public addProtocolHandler(command: string, handler: (request: protocol.Request) => HandlerResponse) {
if (this.handlers.has(command)) {
throw new Error(`Protocol handler already exists for command "${command}"`);
}
@@ -1956,14 +1988,14 @@ namespace ts.server {
}
}
public executeCommand(request: protocol.Request): { response?: any, responseRequired?: boolean } {
public executeCommand(request: protocol.Request): HandlerResponse {
const handler = this.handlers.get(request.command);
if (handler) {
return this.executeWithRequestId(request.seq, () => handler(request));
}
else {
this.logger.msg(`Unrecognized JSON command: ${JSON.stringify(request)}`, Msg.Err);
this.output(undefined, CommandNames.Unknown, request.seq, `Unrecognized JSON command: ${request.command}`);
this.logger.msg(`Unrecognized JSON command:${stringifyIndented(request)}`, Msg.Err);
this.doOutput(/*info*/ undefined, CommandNames.Unknown, request.seq, /*success*/ false, `Unrecognized JSON command: ${request.command}`);
return { responseRequired: false };
}
}
@@ -1974,7 +2006,7 @@ namespace ts.server {
if (this.logger.hasLevel(LogLevel.requestTime)) {
start = this.hrtime();
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`request: ${message}`);
this.logger.info(`request:${indent(message)}`);
}
}
@@ -1994,25 +2026,53 @@ namespace ts.server {
}
if (response) {
this.output(response, request.command, request.seq);
this.doOutput(response, request.command, request.seq, /*success*/ true);
}
else if (responseRequired) {
this.output(undefined, request.command, request.seq, "No content available.");
this.doOutput(/*info*/ undefined, request.command, request.seq, /*success*/ false, "No content available.");
}
}
catch (err) {
if (err instanceof OperationCanceledException) {
// Handle cancellation exceptions
this.output({ canceled: true }, request.command, request.seq);
this.doOutput({ canceled: true }, request.command, request.seq, /*success*/ true);
return;
}
this.logError(err, message);
this.output(
undefined,
this.doOutput(
/*info*/ undefined,
request ? request.command : CommandNames.Unknown,
request ? request.seq : 0,
/*success*/ false,
"Error processing request. " + (<StackTraceError>err).message + "\n" + (<StackTraceError>err).stack);
}
}
}
export interface HandlerResponse {
response?: {};
responseRequired?: boolean;
}
/* @internal */ // Exported only for tests
export function getLocationInNewDocument(oldText: string, renameFilename: string, renameLocation: number, edits: ReadonlyArray<FileTextChanges>): protocol.Location {
const newText = applyEdits(oldText, renameFilename, edits);
const { line, character } = computeLineAndCharacterOfPosition(computeLineStarts(newText), renameLocation);
return { line: line + 1, offset: character + 1 };
}
function applyEdits(text: string, textFilename: string, edits: ReadonlyArray<FileTextChanges>): string {
for (const { fileName, textChanges } of edits) {
if (fileName !== textFilename) {
continue;
}
for (let i = textChanges.length - 1; i >= 0; i--) {
const { newText, span: { start, length } } = textChanges[i];
text = text.slice(0, start) + newText + text.slice(start + length);
}
}
return text;
}
}
+2
View File
@@ -3,6 +3,8 @@
namespace ts.server {
export const ActionSet: ActionSet = "action::set";
export const ActionInvalidate: ActionInvalidate = "action::invalidate";
export const EventTypesRegistry: EventTypesRegistry = "event::typesRegistry";
export const EventPackageInstalled: EventPackageInstalled = "event::packageInstalled";
export const EventBeginInstallTypes: EventBeginInstallTypes = "event::beginInstallTypes";
export const EventEndInstallTypes: EventEndInstallTypes = "event::endInstallTypes";
export const EventInitializationFailed: EventInitializationFailed = "event::initializationFailed";
+35 -5
View File
@@ -28,12 +28,14 @@ declare namespace ts.server {
" __sortedArrayBrand": any;
}
export interface TypingInstallerRequest {
export interface TypingInstallerRequestWithProjectName {
readonly projectName: string;
readonly kind: "discover" | "closeProject";
}
export interface DiscoverTypings extends TypingInstallerRequest {
/* @internal */
export type TypingInstallerRequestUnion = DiscoverTypings | CloseProject | TypesRegistryRequest | InstallPackageRequest;
export interface DiscoverTypings extends TypingInstallerRequestWithProjectName {
readonly fileNames: string[];
readonly projectRootPath: Path;
readonly compilerOptions: CompilerOptions;
@@ -43,18 +45,46 @@ declare namespace ts.server {
readonly kind: "discover";
}
export interface CloseProject extends TypingInstallerRequest {
export interface CloseProject extends TypingInstallerRequestWithProjectName {
readonly kind: "closeProject";
}
export interface TypesRegistryRequest {
readonly kind: "typesRegistry";
}
export interface InstallPackageRequest {
readonly kind: "installPackage";
readonly fileName: Path;
readonly packageName: string;
readonly projectRootPath: Path;
}
export type ActionSet = "action::set";
export type ActionInvalidate = "action::invalidate";
export type EventTypesRegistry = "event::typesRegistry";
export type EventPackageInstalled = "event::packageInstalled";
export type EventBeginInstallTypes = "event::beginInstallTypes";
export type EventEndInstallTypes = "event::endInstallTypes";
export type EventInitializationFailed = "event::initializationFailed";
export interface TypingInstallerResponse {
readonly kind: ActionSet | ActionInvalidate | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed;
readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | EventPackageInstalled | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed;
}
/* @internal */
export type TypingInstallerResponseUnion = SetTypings | InvalidateCachedTypings | TypesRegistryResponse | PackageInstalledResponse | InstallTypes | InitializationFailedResponse;
/* @internal */
export interface TypesRegistryResponse extends TypingInstallerResponse {
readonly kind: EventTypesRegistry;
readonly typesRegistry: MapLike<void>;
}
/* @internal */
export interface PackageInstalledResponse extends TypingInstallerResponse {
readonly kind: EventPackageInstalled;
readonly success: boolean;
readonly message: string;
}
export interface InitializationFailedResponse extends TypingInstallerResponse {
+17
View File
@@ -1,7 +1,13 @@
/// <reference path="project.ts"/>
namespace ts.server {
export interface InstallPackageOptionsWithProjectRootPath extends InstallPackageOptions {
projectRootPath: Path;
}
export interface ITypingsInstaller {
isKnownTypesPackageName(name: string): boolean;
installPackage(options: InstallPackageOptionsWithProjectRootPath): Promise<ApplyCodeActionCommandResult>;
enqueueInstallTypingsRequest(p: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray<string>): void;
attach(projectService: ProjectService): void;
onProjectClosed(p: Project): void;
@@ -9,6 +15,9 @@ namespace ts.server {
}
export const nullTypingsInstaller: ITypingsInstaller = {
isKnownTypesPackageName: returnFalse,
// Should never be called because we never provide a types registry.
installPackage: notImplemented,
enqueueInstallTypingsRequest: noop,
attach: noop,
onProjectClosed: noop,
@@ -77,6 +86,14 @@ namespace ts.server {
constructor(private readonly installer: ITypingsInstaller) {
}
isKnownTypesPackageName(name: string): boolean {
return this.installer.isKnownTypesPackageName(name);
}
installPackage(options: InstallPackageOptionsWithProjectRootPath): Promise<ApplyCodeActionCommandResult> {
return this.installer.installPackage(options);
}
getTypingsForProject(project: Project, unresolvedImports: SortedReadonlyArray<string>, forceRefresh: boolean): SortedReadonlyArray<string> {
const typeAcquisition = project.getTypeAcquisition();
@@ -53,7 +53,7 @@ namespace ts.server.typingsInstaller {
}
try {
const content = <TypesRegistryFile>JSON.parse(host.readFile(typesRegistryFilePath));
return createMapFromTemplate<void>(content.entries);
return createMapFromTemplate(content.entries);
}
catch (e) {
if (log.isEnabled()) {
@@ -79,7 +79,7 @@ namespace ts.server.typingsInstaller {
private readonly npmPath: string;
readonly typesRegistry: Map<void>;
private delayedInitializationError: InitializationFailedResponse;
private delayedInitializationError: InitializationFailedResponse | undefined;
constructor(globalTypingsCacheLocation: string, typingSafeListLocation: string, typesMapLocation: string, npmLocation: string | undefined, throttleLimit: number, log: Log) {
super(
@@ -127,7 +127,7 @@ namespace ts.server.typingsInstaller {
}
listen() {
process.on("message", (req: DiscoverTypings | CloseProject) => {
process.on("message", (req: TypingInstallerRequestUnion) => {
if (this.delayedInitializationError) {
// report initializationFailed error
this.sendResponse(this.delayedInitializationError);
@@ -139,13 +139,41 @@ namespace ts.server.typingsInstaller {
break;
case "closeProject":
this.closeProject(req);
break;
case "typesRegistry": {
const typesRegistry: { [key: string]: void } = {};
this.typesRegistry.forEach((value, key) => {
typesRegistry[key] = value;
});
const response: TypesRegistryResponse = { kind: EventTypesRegistry, typesRegistry };
this.sendResponse(response);
break;
}
case "installPackage": {
const { fileName, packageName, projectRootPath } = req;
const cwd = getDirectoryOfPackageJson(fileName, this.installTypingHost) || projectRootPath;
if (cwd) {
this.installWorker(-1, [packageName], cwd, success => {
const message = success ? `Package ${packageName} installed.` : `There was an error installing ${packageName}.`;
const response: PackageInstalledResponse = { kind: EventPackageInstalled, success, message };
this.sendResponse(response);
});
}
else {
const response: PackageInstalledResponse = { kind: EventPackageInstalled, success: false, message: "Could not determine a project root path." };
this.sendResponse(response);
}
break;
}
default:
Debug.assertNever(req);
}
});
}
protected sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse) {
protected sendResponse(response: TypingInstallerResponseUnion) {
if (this.log.isEnabled()) {
this.log.writeLine(`Sending response: ${JSON.stringify(response)}`);
this.log.writeLine(`Sending response:\n ${JSON.stringify(response)}`);
}
process.send(response);
if (this.log.isEnabled()) {
@@ -153,11 +181,11 @@ namespace ts.server.typingsInstaller {
}
}
protected installWorker(requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void {
protected installWorker(requestId: number, packageNames: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void {
if (this.log.isEnabled()) {
this.log.writeLine(`#${requestId} with arguments'${JSON.stringify(args)}'.`);
this.log.writeLine(`#${requestId} with arguments'${JSON.stringify(packageNames)}'.`);
}
const command = `${this.npmPath} install --ignore-scripts ${args.join(" ")} --save-dev --user-agent="typesInstaller/${version}"`;
const command = `${this.npmPath} install --ignore-scripts ${packageNames.join(" ")} --save-dev --user-agent="typesInstaller/${version}"`;
const start = Date.now();
const hasError = this.execSyncAndLog(command, { cwd });
if (this.log.isEnabled()) {
@@ -186,6 +214,14 @@ namespace ts.server.typingsInstaller {
}
}
function getDirectoryOfPackageJson(fileName: string, host: InstallTypingHost): string | undefined {
return forEachAncestorDirectory(getDirectoryPath(fileName), directory => {
if (host.fileExists(combinePaths(directory, "package.json"))) {
return directory;
}
});
}
const logFilePath = findArgument(server.Arguments.LogFile);
const globalTypingsCacheLocation = findArgument(server.Arguments.GlobalCacheLocation);
const typingSafeListLocation = findArgument(server.Arguments.TypingSafeListLocation);
@@ -32,50 +32,11 @@ namespace ts.server.typingsInstaller {
}
}
export enum PackageNameValidationResult {
Ok,
ScopedPackagesNotSupported,
EmptyName,
NameTooLong,
NameStartsWithDot,
NameStartsWithUnderscore,
NameContainsNonURISafeCharacters
}
export const MaxPackageNameLength = 214;
/**
* Validates package name using rules defined at https://docs.npmjs.com/files/package.json
*/
export function validatePackageName(packageName: string): PackageNameValidationResult {
if (!packageName) {
return PackageNameValidationResult.EmptyName;
}
if (packageName.length > MaxPackageNameLength) {
return PackageNameValidationResult.NameTooLong;
}
if (packageName.charCodeAt(0) === CharacterCodes.dot) {
return PackageNameValidationResult.NameStartsWithDot;
}
if (packageName.charCodeAt(0) === CharacterCodes._) {
return PackageNameValidationResult.NameStartsWithUnderscore;
}
// check if name is scope package like: starts with @ and has one '/' in the middle
// scoped packages are not currently supported
// TODO: when support will be added we'll need to split and check both scope and package name
if (/^@[^/]+\/[^/]+$/.test(packageName)) {
return PackageNameValidationResult.ScopedPackagesNotSupported;
}
if (encodeURIComponent(packageName) !== packageName) {
return PackageNameValidationResult.NameContainsNonURISafeCharacters;
}
return PackageNameValidationResult.Ok;
}
export type RequestCompletedAction = (success: boolean) => void;
interface PendingRequest {
requestId: number;
args: string[];
packageNames: string[];
cwd: string;
onRequestCompleted: RequestCompletedAction;
}
@@ -255,8 +216,8 @@ namespace ts.server.typingsInstaller {
if (this.missingTypingsSet.get(typing) || this.packageNameToTypingLocation.get(typing)) {
continue;
}
const validationResult = validatePackageName(typing);
if (validationResult === PackageNameValidationResult.Ok) {
const validationResult = JsTyping.validatePackageName(typing);
if (validationResult === JsTyping.PackageNameValidationResult.Ok) {
if (this.typesRegistry.has(typing)) {
result.push(typing);
}
@@ -270,26 +231,7 @@ namespace ts.server.typingsInstaller {
// add typing name to missing set so we won't process it again
this.missingTypingsSet.set(typing, true);
if (this.log.isEnabled()) {
switch (validationResult) {
case PackageNameValidationResult.EmptyName:
this.log.writeLine(`Package name '${typing}' cannot be empty`);
break;
case PackageNameValidationResult.NameTooLong:
this.log.writeLine(`Package name '${typing}' should be less than ${MaxPackageNameLength} characters`);
break;
case PackageNameValidationResult.NameStartsWithDot:
this.log.writeLine(`Package name '${typing}' cannot start with '.'`);
break;
case PackageNameValidationResult.NameStartsWithUnderscore:
this.log.writeLine(`Package name '${typing}' cannot start with '_'`);
break;
case PackageNameValidationResult.ScopedPackagesNotSupported:
this.log.writeLine(`Package '${typing}' is scoped and currently is not supported`);
break;
case PackageNameValidationResult.NameContainsNonURISafeCharacters:
this.log.writeLine(`Package name '${typing}' contains non URI safe characters`);
break;
}
this.log.writeLine(JsTyping.renderPackageNameValidationFailure(validationResult, typing));
}
}
}
@@ -430,8 +372,8 @@ namespace ts.server.typingsInstaller {
};
}
private installTypingsAsync(requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void {
this.pendingRunRequests.unshift({ requestId, args, cwd, onRequestCompleted });
private installTypingsAsync(requestId: number, packageNames: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void {
this.pendingRunRequests.unshift({ requestId, packageNames, cwd, onRequestCompleted });
this.executeWithThrottling();
}
@@ -439,7 +381,7 @@ namespace ts.server.typingsInstaller {
while (this.inFlightRequestCount < this.throttleLimit && this.pendingRunRequests.length) {
this.inFlightRequestCount++;
const request = this.pendingRunRequests.pop();
this.installWorker(request.requestId, request.args, request.cwd, ok => {
this.installWorker(request.requestId, request.packageNames, request.cwd, ok => {
this.inFlightRequestCount--;
request.onRequestCompleted(ok);
this.executeWithThrottling();
@@ -447,7 +389,7 @@ namespace ts.server.typingsInstaller {
}
}
protected abstract installWorker(requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void;
protected abstract installWorker(requestId: number, packageNames: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void;
protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes): void;
}
+11
View File
@@ -318,4 +318,15 @@ namespace ts.server {
deleted(oldItems[oldIndex++]);
}
}
/* @internal */
export function indent(string: string): string {
return "\n " + string;
}
/** Put stringified JSON on the next line, indented. */
/* @internal */
export function stringifyIndented(json: {}): string {
return "\n " + JSON.stringify(json);
}
}
+1 -3
View File
@@ -5,15 +5,13 @@ namespace ts {
getCodeActions(context: CodeFixContext): CodeAction[] | undefined;
}
export interface CodeFixContext {
export interface CodeFixContext extends textChanges.TextChangesContext {
errorCode: number;
sourceFile: SourceFile;
span: TextSpan;
program: Program;
newLineCharacter: string;
host: LanguageServiceHost;
cancellationToken: CancellationToken;
rulesProvider: formatting.RulesProvider;
}
export namespace codefix {
@@ -0,0 +1,34 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [
Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type.code,
],
getCodeActions: context => {
const { sourceFile, span: { start } } = context;
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
if (!isStringLiteral(token)) {
throw Debug.fail(); // These errors should only happen on the module name.
}
const action = tryGetCodeActionForInstallPackageTypes(context.host, token.text);
return action && [action];
},
});
export function tryGetCodeActionForInstallPackageTypes(host: LanguageServiceHost, moduleName: string): CodeAction | undefined {
const { packageName } = getPackageName(moduleName);
if (!host.isKnownTypesPackageName(packageName)) {
// If !registry, registry not available yet, can't do anything.
return undefined;
}
const typesPackageName = getTypesPackageName(packageName);
return {
description: `Install '${typesPackageName}'`,
changes: [],
commands: [{ type: "install package", packageName: typesPackageName }],
};
}
}
+1
View File
@@ -3,6 +3,7 @@
/// <reference path="fixClassIncorrectlyImplementsInterface.ts" />
/// <reference path="fixAddMissingMember.ts" />
/// <reference path="fixSpelling.ts" />
/// <reference path="fixCannotFindModule.ts" />
/// <reference path="fixClassDoesntImplementInheritedAbstractMember.ts" />
/// <reference path="fixClassSuperMustPrecedeThisAccess.ts" />
/// <reference path="fixConstructorForDerivedNeedSuperCall.ts" />
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -521,7 +521,7 @@ namespace ts.codefix {
if (!usageContext.properties) {
usageContext.properties = createUnderscoreEscapedMap<UsageContext>();
}
const propertyUsageContext = {};
const propertyUsageContext = usageContext.properties.get(name) || { };
inferTypeFromContext(parent, checker, propertyUsageContext);
usageContext.properties.set(name, propertyUsageContext);
}
@@ -575,7 +575,7 @@ namespace ts.codefix {
if (usageContext.properties) {
usageContext.properties.forEach((context, name) => {
const symbol = checker.createSymbol(SymbolFlags.Property, name);
symbol.type = getTypeFromUsageContext(context, checker);
symbol.type = getTypeFromUsageContext(context, checker) || checker.getAnyType();
members.set(name, symbol);
});
}
@@ -636,7 +636,7 @@ namespace ts.codefix {
symbol.type = checker.getWidenedType(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[i]));
parameters.push(symbol);
}
const returnType = getTypeFromUsageContext(callContext.returnType, checker);
const returnType = getTypeFromUsageContext(callContext.returnType, checker) || checker.getVoidType();
return checker.createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, callContext.argumentTypes.length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
}
+174 -32
View File
@@ -4,13 +4,31 @@
namespace ts.Completions {
export type Log = (message: string) => void;
interface SymbolOriginInfo {
moduleSymbol: Symbol;
isDefaultExport: boolean;
}
/**
* Map from symbol id -> SymbolOriginInfo.
* Only populated for symbols that come from other modules.
*/
type SymbolOriginInfoMap = SymbolOriginInfo[];
const enum KeywordCompletionFilters {
None,
ClassElementKeywords, // Keywords at class keyword
ConstructorParameterKeywords, // Keywords at constructor parameter
}
export function getCompletionsAtPosition(host: LanguageServiceHost, typeChecker: TypeChecker, log: Log, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number): CompletionInfo | undefined {
export function getCompletionsAtPosition(
host: LanguageServiceHost,
typeChecker: TypeChecker,
log: Log,
compilerOptions: CompilerOptions,
sourceFile: SourceFile,
position: number,
allSourceFiles: ReadonlyArray<SourceFile>,
): CompletionInfo | undefined {
if (isInReferenceComment(sourceFile, position)) {
return PathCompletions.getTripleSlashReferenceCompletion(sourceFile, position, compilerOptions, host);
}
@@ -19,12 +37,12 @@ namespace ts.Completions {
return getStringLiteralCompletionEntries(sourceFile, position, typeChecker, compilerOptions, host, log);
}
const completionData = getCompletionData(typeChecker, log, sourceFile, position);
const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles);
if (!completionData) {
return undefined;
}
const { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, request, keywordFilters } = completionData;
const { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, request, keywordFilters, symbolToOriginInfoMap } = completionData;
if (sourceFile.languageVariant === LanguageVariant.JSX &&
location && location.parent && location.parent.kind === SyntaxKind.JsxClosingElement) {
@@ -56,7 +74,7 @@ namespace ts.Completions {
const entries: CompletionEntry[] = [];
if (isSourceFileJavaScript(sourceFile)) {
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log, allowStringLiteral);
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log, allowStringLiteral, symbolToOriginInfoMap);
getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target, entries);
}
else {
@@ -64,7 +82,7 @@ namespace ts.Completions {
return undefined;
}
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log, allowStringLiteral);
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log, allowStringLiteral, symbolToOriginInfoMap);
}
// TODO add filter for keyword based on type/value/namespace and also location
@@ -134,7 +152,17 @@ namespace ts.Completions {
};
}
function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: Push<CompletionEntry>, location: Node, performCharacterChecks: boolean, typeChecker: TypeChecker, target: ScriptTarget, log: Log, allowStringLiteral: boolean): Map<true> {
function getCompletionEntriesFromSymbols(
symbols: ReadonlyArray<Symbol>,
entries: Push<CompletionEntry>,
location: Node,
performCharacterChecks: boolean,
typeChecker: TypeChecker,
target: ScriptTarget,
log: Log,
allowStringLiteral: boolean,
symbolToOriginInfoMap?: SymbolOriginInfoMap,
): Map<true> {
const start = timestamp();
const uniqueNames = createMap<true>();
if (symbols) {
@@ -143,6 +171,9 @@ namespace ts.Completions {
if (entry) {
const id = entry.name;
if (!uniqueNames.has(id)) {
if (symbolToOriginInfoMap && symbolToOriginInfoMap[getUniqueSymbolId(symbol, typeChecker)]) {
entry.hasAction = true;
}
entries.push(entry);
uniqueNames.set(id, true);
}
@@ -298,53 +329,89 @@ namespace ts.Completions {
}
}
export function getCompletionEntryDetails(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): CompletionEntryDetails {
export function getCompletionEntryDetails(
typeChecker: TypeChecker,
log: (message: string) => void,
compilerOptions: CompilerOptions,
sourceFile: SourceFile,
position: number,
name: string,
allSourceFiles: ReadonlyArray<SourceFile>,
host: LanguageServiceHost,
rulesProvider: formatting.RulesProvider,
): CompletionEntryDetails {
// Compute all the completion symbols again.
const completionData = getCompletionData(typeChecker, log, sourceFile, position);
const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles);
if (completionData) {
const { symbols, location, allowStringLiteral } = completionData;
const { symbols, location, allowStringLiteral, symbolToOriginInfoMap } = completionData;
// Find the symbol with the matching entry name.
// We don't need to perform character checks here because we're only comparing the
// name against 'entryName' (which is known to be good), not building a new
// completion entry.
const symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false, allowStringLiteral) === entryName ? s : undefined);
const symbol = find(symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false, allowStringLiteral) === name);
if (symbol) {
const codeActions = getCompletionEntryCodeActions(symbolToOriginInfoMap, symbol, typeChecker, host, compilerOptions, sourceFile, rulesProvider);
const kindModifiers = SymbolDisplay.getSymbolModifiers(symbol);
const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All);
return {
name: entryName,
kindModifiers: SymbolDisplay.getSymbolModifiers(symbol),
kind: symbolKind,
displayParts,
documentation,
tags
};
return { name, kindModifiers, kind: symbolKind, displayParts, documentation, tags, codeActions };
}
}
// Didn't find a symbol with this name. See if we can find a keyword instead.
const keywordCompletion = forEach(
getKeywordCompletions(KeywordCompletionFilters.None),
c => c.name === entryName
c => c.name === name
);
if (keywordCompletion) {
return {
name: entryName,
name,
kind: ScriptElementKind.keyword,
kindModifiers: ScriptElementKindModifier.none,
displayParts: [displayPart(entryName, SymbolDisplayPartKind.keyword)],
displayParts: [displayPart(name, SymbolDisplayPartKind.keyword)],
documentation: undefined,
tags: undefined
tags: undefined,
codeActions: undefined,
};
}
return undefined;
}
export function getCompletionEntrySymbol(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): Symbol | undefined {
function getCompletionEntryCodeActions(symbolToOriginInfoMap: SymbolOriginInfoMap, symbol: Symbol, checker: TypeChecker, host: LanguageServiceHost, compilerOptions: CompilerOptions, sourceFile: SourceFile, rulesProvider: formatting.RulesProvider): CodeAction[] | undefined {
const symbolOriginInfo = symbolToOriginInfoMap[getUniqueSymbolId(symbol, checker)];
if (!symbolOriginInfo) {
return undefined;
}
const { moduleSymbol, isDefaultExport } = symbolOriginInfo;
return codefix.getCodeActionForImport(moduleSymbol, {
host,
checker,
newLineCharacter: host.getNewLine(),
compilerOptions,
sourceFile,
rulesProvider,
symbolName: symbol.name,
getCanonicalFileName: createGetCanonicalFileName(host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : false),
symbolToken: undefined,
kind: isDefaultExport ? codefix.ImportKind.Default : codefix.ImportKind.Named,
});
}
export function getCompletionEntrySymbol(
typeChecker: TypeChecker,
log: (message: string) => void,
compilerOptions: CompilerOptions,
sourceFile: SourceFile,
position: number,
entryName: string,
allSourceFiles: ReadonlyArray<SourceFile>,
): Symbol | undefined {
// Compute all the completion symbols again.
const completionData = getCompletionData(typeChecker, log, sourceFile, position);
const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles);
if (!completionData) {
return undefined;
}
@@ -353,7 +420,7 @@ namespace ts.Completions {
// We don't need to perform character checks here because we're only comparing the
// name against 'entryName' (which is known to be good), not building a new
// completion entry.
return forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false, allowStringLiteral) === entryName ? s : undefined);
return find(symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false, allowStringLiteral) === entryName);
}
interface CompletionData {
@@ -366,10 +433,17 @@ namespace ts.Completions {
isRightOfDot: boolean;
request?: Request;
keywordFilters: KeywordCompletionFilters;
symbolToOriginInfoMap: SymbolOriginInfoMap;
}
type Request = { kind: "JsDocTagName" } | { kind: "JsDocTag" } | { kind: "JsDocParameterName", tag: JSDocParameterTag };
function getCompletionData(typeChecker: TypeChecker, log: (message: string) => void, sourceFile: SourceFile, position: number): CompletionData | undefined {
function getCompletionData(
typeChecker: TypeChecker,
log: (message: string) => void,
sourceFile: SourceFile,
position: number,
allSourceFiles: ReadonlyArray<SourceFile>,
): CompletionData | undefined {
const isJavaScriptFile = isSourceFileJavaScript(sourceFile);
let request: Request | undefined;
@@ -441,7 +515,18 @@ namespace ts.Completions {
}
if (request) {
return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: false, allowStringLiteral: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, request, keywordFilters: KeywordCompletionFilters.None };
return {
symbols: undefined,
isGlobalCompletion: false,
isMemberCompletion: false,
allowStringLiteral: false,
isNewIdentifierLocation: false,
location: undefined,
isRightOfDot: false,
request,
keywordFilters: KeywordCompletionFilters.None,
symbolToOriginInfoMap: undefined,
};
}
if (!insideJsDocTagTypeExpression) {
@@ -543,6 +628,7 @@ namespace ts.Completions {
let isNewIdentifierLocation: boolean;
let keywordFilters = KeywordCompletionFilters.None;
let symbols: Symbol[] = [];
const symbolToOriginInfoMap: SymbolOriginInfoMap = [];
if (isRightOfDot) {
getTypeScriptMemberSymbols();
@@ -579,7 +665,7 @@ namespace ts.Completions {
log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters };
return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters, symbolToOriginInfoMap };
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
@@ -752,13 +838,16 @@ namespace ts.Completions {
}
const symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | SymbolFlags.Alias;
symbols = filterGlobalCompletion(typeChecker.getSymbolsInScope(scopeNode, symbolMeanings));
symbols = typeChecker.getSymbolsInScope(scopeNode, symbolMeanings);
getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "");
filterGlobalCompletion(symbols);
return true;
}
function filterGlobalCompletion(symbols: Symbol[]) {
return filter(symbols, symbol => {
function filterGlobalCompletion(symbols: Symbol[]): void {
filterMutate(symbols, symbol => {
if (!isSourceFile(location)) {
// export = /**/ here we want to get all meanings, so any symbol is ok
if (isExportAssignment(location.parent)) {
@@ -832,6 +921,59 @@ namespace ts.Completions {
}
}
function getSymbolsFromOtherSourceFileExports(symbols: Symbol[], tokenText: string): void {
const tokenTextLowerCase = tokenText.toLowerCase();
const symbolIdMap = arrayToNumericMap(symbols, s => getUniqueSymbolId(s, typeChecker));
codefix.forEachExternalModule(typeChecker, allSourceFiles, moduleSymbol => {
if (moduleSymbol === sourceFile.symbol) {
return;
}
for (let symbol of typeChecker.getExportsOfModule(moduleSymbol)) {
let { name } = symbol;
const isDefaultExport = name === "default";
if (isDefaultExport) {
const localSymbol = getLocalSymbolForExportDefault(symbol);
if (localSymbol) {
symbol = localSymbol;
name = localSymbol.name;
}
}
const id = getUniqueSymbolId(symbol, typeChecker);
if (!symbolIdMap[id] && stringContainsCharactersInOrder(name.toLowerCase(), tokenTextLowerCase)) {
symbols.push(symbol);
symbolToOriginInfoMap[id] = { moduleSymbol, isDefaultExport };
}
}
});
}
/**
* True if you could remove some characters in `a` to get `b`.
* E.g., true for "abcdef" and "bdf".
* But not true for "abcdef" and "dbf".
*/
function stringContainsCharactersInOrder(str: string, characters: string): boolean {
if (characters.length === 0) {
return true;
}
let characterIndex = 0;
for (let strIndex = 0; strIndex < str.length; strIndex++) {
if (str.charCodeAt(strIndex) === characters.charCodeAt(characterIndex)) {
characterIndex++;
if (characterIndex === characters.length) {
return true;
}
}
}
// Did not find all characters
return false;
}
/**
* Finds the first node that "embraces" the position, so that one may
* accurately aggregate locals from the closest containing scope.
@@ -1627,7 +1769,7 @@ namespace ts.Completions {
// First check of the displayName is not external module; if it is an external module, it is not valid entry
if (symbol.flags & SymbolFlags.Namespace) {
const firstCharCode = name.charCodeAt(0);
if (firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote) {
if (isSingleOrDoubleQuote(firstCharCode)) {
// If the symbol is external module, don't show it in the completion list
// (i.e declare module "http" { const x; } | // <= request completion here, "http" should not be there)
return undefined;
+17 -10
View File
@@ -377,19 +377,26 @@ namespace ts.FindAllReferences.Core {
const result: SymbolAndEntries[] = [];
const state = new State(sourceFiles, /*isForConstructor*/ node.kind === SyntaxKind.ConstructorKeyword, checker, cancellationToken, searchMeaning, options, result);
const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: populateSearchSymbolSet(symbol, node, checker, options.implementations) });
// Try to get the smallest valid scope that we can limit our search to;
// otherwise we'll need to search globally (i.e. include each file).
const scope = getSymbolScope(symbol);
if (scope) {
getReferencesInContainer(scope, scope.getSourceFile(), search, state);
if (node.kind === SyntaxKind.DefaultKeyword) {
addReference(node, symbol, node, state);
searchForImportsOfExport(node, symbol, { exportingModuleSymbol: symbol.parent, exportKind: ExportKind.Default }, state);
}
else {
// Global search
for (const sourceFile of state.sourceFiles) {
state.cancellationToken.throwIfCancellationRequested();
searchForName(sourceFile, search, state);
const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: populateSearchSymbolSet(symbol, node, checker, options.implementations) });
// Try to get the smallest valid scope that we can limit our search to;
// otherwise we'll need to search globally (i.e. include each file).
const scope = getSymbolScope(symbol);
if (scope) {
getReferencesInContainer(scope, scope.getSourceFile(), search, state);
}
else {
// Global search
for (const sourceFile of state.sourceFiles) {
state.cancellationToken.throwIfCancellationRequested();
searchForName(sourceFile, search, state);
}
}
}
+44 -21
View File
@@ -9,16 +9,19 @@ namespace ts.formatting {
}
/**
* Computed indentation for a given position in source file
* @param position - position in file
* @param sourceFile - target source file
* @param options - set of editor options that control indentation
* @param assumeNewLineBeforeCloseBrace - false when getIndentation is called on the text from the real source file.
* true - when we need to assume that position is on the newline. This is usefult for codefixes, i.e.
* @param assumeNewLineBeforeCloseBrace
* `false` when called on text from a real source file.
* `true` when we need to assume `position` is on a newline.
*
* This is useful for codefixes. Consider
* ```
* function f() {
* |}
* when inserting some text after open brace we would like to get the value of indentation as if newline was already there.
* However by default indentation at position | will be 0 so 'assumeNewLineBeforeCloseBrace' allows to override this behavior,
* ```
* with `position` at `|`.
*
* When inserting some text after an open brace, we would like to get indentation as if a newline was already there.
* By default indentation at `position` will be 0 so 'assumeNewLineBeforeCloseBrace' overrides this behavior.
*/
export function getIndentation(position: number, sourceFile: SourceFile, options: EditorSettings, assumeNewLineBeforeCloseBrace = false): number {
if (position > sourceFile.text.length) {
@@ -157,10 +160,11 @@ namespace ts.formatting {
options: EditorSettings): number {
let parent: Node = current.parent;
let parentStart: LineAndCharacter;
let containingListOrParentStart: LineAndCharacter;
// walk upwards and collect indentations for pairs of parent-child nodes
// indentation is not added if parent and child nodes start on the same line or if parent is IfStatement and child starts on the same line with 'else clause'
// Walk up the tree and collect indentation for parent-child node pairs. Indentation is not added if
// * parent and child nodes start on the same line, or
// * parent is an IfStatement and child starts on the same line as an 'else clause'.
while (parent) {
let useActualIndentation = true;
if (ignoreActualIndentationRange) {
@@ -175,9 +179,10 @@ namespace ts.formatting {
return actualIndentation + indentationDelta;
}
}
parentStart = getParentStart(parent, current, sourceFile);
containingListOrParentStart = getContainingListOrParentStart(parent, current, sourceFile);
const parentAndChildShareLine =
parentStart.line === currentStart.line ||
containingListOrParentStart.line === currentStart.line ||
childStartsOnTheSameLineWithElseInIfStatement(parent, current, currentStart.line, sourceFile);
if (useActualIndentation) {
@@ -197,22 +202,30 @@ namespace ts.formatting {
indentationDelta += options.indentSize;
}
// In our AST, a call argument's `parent` is the call-expression, not the argument list.
// We would like to increase indentation based on the relationship between an argument and its argument-list,
// so we spoof the starting position of the (parent) call-expression to match the (non-parent) argument-list.
// But, the spoofed start-value could then cause a problem when comparing the start position of the call-expression
// to *its* parent (in the case of an iife, an expression statement), adding an extra level of indentation.
//
// Instead, when at an argument, we unspoof the starting position of the enclosing call expression
// *after* applying indentation for the argument.
const useTrueStart =
isArgumentAndStartLineOverlapsExpressionBeingCalled(parent, current, currentStart.line, sourceFile);
current = parent;
currentStart = parentStart;
parent = current.parent;
currentStart = useTrueStart ? sourceFile.getLineAndCharacterOfPosition(current.getStart()) : containingListOrParentStart;
}
return indentationDelta + getBaseIndentation(options);
}
function getParentStart(parent: Node, child: Node, sourceFile: SourceFile): LineAndCharacter {
function getContainingListOrParentStart(parent: Node, child: Node, sourceFile: SourceFile): LineAndCharacter {
const containingList = getContainingList(child, sourceFile);
if (containingList) {
return sourceFile.getLineAndCharacterOfPosition(containingList.pos);
}
return sourceFile.getLineAndCharacterOfPosition(parent.getStart(sourceFile));
const startPos = containingList ? containingList.pos : parent.getStart(sourceFile);
return sourceFile.getLineAndCharacterOfPosition(startPos);
}
/*
@@ -291,6 +304,16 @@ namespace ts.formatting {
return sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
}
export function isArgumentAndStartLineOverlapsExpressionBeingCalled(parent: Node, child: Node, childStartLine: number, sourceFile: SourceFileLike): boolean {
if (!(isCallExpression(parent) && contains(parent.arguments, child))) {
return false;
}
const expressionOfCallExpressionEnd = parent.expression.getEnd();
const expressionOfCallExpressionEndLine = getLineAndCharacterOfPosition(sourceFile, expressionOfCallExpressionEnd).line;
return expressionOfCallExpressionEndLine === childStartLine;
}
export function childStartsOnTheSameLineWithElseInIfStatement(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFileLike): boolean {
if (parent.kind === SyntaxKind.IfStatement && (<IfStatement>parent).elseStatement === child) {
const elseKeyword = findChildOfKind(parent, SyntaxKind.ElseKeyword, sourceFile);
+61
View File
@@ -246,4 +246,65 @@ namespace ts.JsTyping {
}
}
export const enum PackageNameValidationResult {
Ok,
ScopedPackagesNotSupported,
EmptyName,
NameTooLong,
NameStartsWithDot,
NameStartsWithUnderscore,
NameContainsNonURISafeCharacters
}
const MaxPackageNameLength = 214;
/**
* Validates package name using rules defined at https://docs.npmjs.com/files/package.json
*/
export function validatePackageName(packageName: string): PackageNameValidationResult {
if (!packageName) {
return PackageNameValidationResult.EmptyName;
}
if (packageName.length > MaxPackageNameLength) {
return PackageNameValidationResult.NameTooLong;
}
if (packageName.charCodeAt(0) === CharacterCodes.dot) {
return PackageNameValidationResult.NameStartsWithDot;
}
if (packageName.charCodeAt(0) === CharacterCodes._) {
return PackageNameValidationResult.NameStartsWithUnderscore;
}
// check if name is scope package like: starts with @ and has one '/' in the middle
// scoped packages are not currently supported
// TODO: when support will be added we'll need to split and check both scope and package name
if (/^@[^/]+\/[^/]+$/.test(packageName)) {
return PackageNameValidationResult.ScopedPackagesNotSupported;
}
if (encodeURIComponent(packageName) !== packageName) {
return PackageNameValidationResult.NameContainsNonURISafeCharacters;
}
return PackageNameValidationResult.Ok;
}
export function renderPackageNameValidationFailure(result: PackageNameValidationResult, typing: string): string {
switch (result) {
case PackageNameValidationResult.EmptyName:
return `Package name '${typing}' cannot be empty`;
case PackageNameValidationResult.NameTooLong:
return `Package name '${typing}' should be less than ${MaxPackageNameLength} characters`;
case PackageNameValidationResult.NameStartsWithDot:
return `Package name '${typing}' cannot start with '.'`;
case PackageNameValidationResult.NameStartsWithUnderscore:
return `Package name '${typing}' cannot start with '_'`;
case PackageNameValidationResult.ScopedPackagesNotSupported:
return `Package '${typing}' is scoped and currently is not supported`;
case PackageNameValidationResult.NameContainsNonURISafeCharacters:
return `Package name '${typing}' contains non URI safe characters`;
case PackageNameValidationResult.Ok:
throw Debug.fail(); // Shouldn't have called this.
default:
Debug.assertNever(result);
}
}
}
+1 -1
View File
@@ -339,7 +339,7 @@ namespace ts.Completions.PathCompletions {
}
}
else if (host.getDirectories) {
let typeRoots: string[];
let typeRoots: ReadonlyArray<string>;
try {
// Wrap in try catch because getEffectiveTypeRoots touches the filesystem
typeRoots = getEffectiveTypeRoots(options, host);
+2 -3
View File
@@ -14,13 +14,12 @@ namespace ts {
getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined;
}
export interface RefactorContext {
export interface RefactorContext extends textChanges.TextChangesContext {
file: SourceFile;
startPosition: number;
endPosition?: number;
program: Program;
newLineCharacter: string;
rulesProvider?: formatting.RulesProvider;
host: LanguageServiceHost;
cancellationToken?: CancellationToken;
}
@@ -0,0 +1,252 @@
/* @internal */
namespace ts.refactor.annotateWithTypeFromJSDoc {
const actionName = "annotate";
const annotateTypeFromJSDoc: Refactor = {
name: "Annotate with type from JSDoc",
description: Diagnostics.Annotate_with_type_from_JSDoc.message,
getEditsForAction,
getAvailableActions
};
type DeclarationWithType =
| FunctionLikeDeclaration
| VariableDeclaration
| ParameterDeclaration
| PropertySignature
| PropertyDeclaration;
registerRefactor(annotateTypeFromJSDoc);
function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
if (isInJavaScriptFile(context.file)) {
return undefined;
}
const node = getTokenAtPosition(context.file, context.startPosition, /*includeJsDocComment*/ false);
if (hasUsableJSDoc(findAncestor(node, isDeclarationWithType))) {
return [{
name: annotateTypeFromJSDoc.name,
description: annotateTypeFromJSDoc.description,
actions: [
{
description: annotateTypeFromJSDoc.description,
name: actionName
}
]
}];
}
}
function hasUsableJSDoc(decl: DeclarationWithType): boolean {
if (!decl) {
return false;
}
if (isFunctionLikeDeclaration(decl)) {
return decl.parameters.some(hasUsableJSDoc) || (!decl.type && !!getJSDocReturnType(decl));
}
return !decl.type && !!getJSDocType(decl);
}
function getEditsForAction(context: RefactorContext, action: string): RefactorEditInfo | undefined {
if (actionName !== action) {
return Debug.fail(`actionName !== action: ${actionName} !== ${action}`);
}
const node = getTokenAtPosition(context.file, context.startPosition, /*includeJsDocComment*/ false);
const decl = findAncestor(node, isDeclarationWithType);
if (!decl || decl.type) {
return undefined;
}
const jsdocType = getJSDocType(decl);
const isFunctionWithJSDoc = isFunctionLikeDeclaration(decl) && (getJSDocReturnType(decl) || decl.parameters.some(p => !!getJSDocType(p)));
if (isFunctionWithJSDoc || jsdocType && decl.kind === SyntaxKind.Parameter) {
return getEditsForFunctionAnnotation(context);
}
else if (jsdocType) {
return getEditsForAnnotation(context);
}
else {
Debug.assert(!!refactor, "No applicable refactor found.");
}
}
function getEditsForAnnotation(context: RefactorContext): RefactorEditInfo | undefined {
const sourceFile = context.file;
const token = getTokenAtPosition(sourceFile, context.startPosition, /*includeJsDocComment*/ false);
const decl = findAncestor(token, isDeclarationWithType);
const jsdocType = getJSDocType(decl);
if (!decl || !jsdocType || decl.type) {
return Debug.fail(`!decl || !jsdocType || decl.type: !${decl} || !${jsdocType} || ${decl.type}`);
}
const changeTracker = textChanges.ChangeTracker.fromContext(context);
const declarationWithType = addType(decl, transformJSDocType(jsdocType) as TypeNode);
suppressLeadingAndTrailingTrivia(declarationWithType);
changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, declarationWithType);
return {
edits: changeTracker.getChanges(),
renameFilename: undefined,
renameLocation: undefined
};
}
function getEditsForFunctionAnnotation(context: RefactorContext): RefactorEditInfo | undefined {
const sourceFile = context.file;
const token = getTokenAtPosition(sourceFile, context.startPosition, /*includeJsDocComment*/ false);
const decl = findAncestor(token, isFunctionLikeDeclaration);
const changeTracker = textChanges.ChangeTracker.fromContext(context);
const functionWithType = addTypesToFunctionLike(decl);
suppressLeadingAndTrailingTrivia(functionWithType);
changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, functionWithType);
return {
edits: changeTracker.getChanges(),
renameFilename: undefined,
renameLocation: undefined
};
}
function isDeclarationWithType(node: Node): node is DeclarationWithType {
return isFunctionLikeDeclaration(node) ||
node.kind === SyntaxKind.VariableDeclaration ||
node.kind === SyntaxKind.Parameter ||
node.kind === SyntaxKind.PropertySignature ||
node.kind === SyntaxKind.PropertyDeclaration;
}
function addTypesToFunctionLike(decl: FunctionLikeDeclaration) {
const typeParameters = getEffectiveTypeParameterDeclarations(decl, /*checkJSDoc*/ true);
const parameters = decl.parameters.map(
p => createParameter(p.decorators, p.modifiers, p.dotDotDotToken, p.name, p.questionToken, transformJSDocType(getEffectiveTypeAnnotationNode(p, /*checkJSDoc*/ true)) as TypeNode, p.initializer));
const returnType = transformJSDocType(getEffectiveReturnTypeNode(decl, /*checkJSDoc*/ true)) as TypeNode;
switch (decl.kind) {
case SyntaxKind.FunctionDeclaration:
return createFunctionDeclaration(decl.decorators, decl.modifiers, decl.asteriskToken, decl.name, typeParameters, parameters, returnType, decl.body);
case SyntaxKind.Constructor:
return createConstructor(decl.decorators, decl.modifiers, parameters, decl.body);
case SyntaxKind.FunctionExpression:
return createFunctionExpression(decl.modifiers, decl.asteriskToken, (decl as FunctionExpression).name, typeParameters, parameters, returnType, decl.body);
case SyntaxKind.ArrowFunction:
return createArrowFunction(decl.modifiers, typeParameters, parameters, returnType, decl.equalsGreaterThanToken, decl.body);
case SyntaxKind.MethodDeclaration:
return createMethod(decl.decorators, decl.modifiers, decl.asteriskToken, decl.name, decl.questionToken, typeParameters, parameters, returnType, decl.body);
case SyntaxKind.GetAccessor:
return createGetAccessor(decl.decorators, decl.modifiers, decl.name, decl.parameters, returnType, decl.body);
case SyntaxKind.SetAccessor:
return createSetAccessor(decl.decorators, decl.modifiers, decl.name, parameters, decl.body);
default:
return Debug.assertNever(decl, `Unexpected SyntaxKind: ${(decl as any).kind}`);
}
}
function addType(decl: DeclarationWithType, jsdocType: TypeNode) {
switch (decl.kind) {
case SyntaxKind.VariableDeclaration:
return createVariableDeclaration(decl.name, jsdocType, decl.initializer);
case SyntaxKind.PropertySignature:
return createPropertySignature(decl.modifiers, decl.name, decl.questionToken, jsdocType, decl.initializer);
case SyntaxKind.PropertyDeclaration:
return createProperty(decl.decorators, decl.modifiers, decl.name, decl.questionToken, jsdocType, decl.initializer);
default:
return Debug.fail(`Unexpected SyntaxKind: ${decl.kind}`);
}
}
function transformJSDocType(node: Node): Node | undefined {
if (node === undefined) {
return undefined;
}
switch (node.kind) {
case SyntaxKind.JSDocAllType:
case SyntaxKind.JSDocUnknownType:
return createTypeReferenceNode("any", emptyArray);
case SyntaxKind.JSDocOptionalType:
return transformJSDocOptionalType(node as JSDocOptionalType);
case SyntaxKind.JSDocNonNullableType:
return transformJSDocType((node as JSDocNonNullableType).type);
case SyntaxKind.JSDocNullableType:
return transformJSDocNullableType(node as JSDocNullableType);
case SyntaxKind.JSDocVariadicType:
return transformJSDocVariadicType(node as JSDocVariadicType);
case SyntaxKind.JSDocFunctionType:
return transformJSDocFunctionType(node as JSDocFunctionType);
case SyntaxKind.Parameter:
return transformJSDocParameter(node as ParameterDeclaration);
case SyntaxKind.TypeReference:
return transformJSDocTypeReference(node as TypeReferenceNode);
default:
const visited = visitEachChild(node, transformJSDocType, /*context*/ undefined) as TypeNode;
setEmitFlags(visited, EmitFlags.SingleLine);
return visited;
}
}
function transformJSDocOptionalType(node: JSDocOptionalType) {
return createUnionTypeNode([visitNode(node.type, transformJSDocType), createTypeReferenceNode("undefined", emptyArray)]);
}
function transformJSDocNullableType(node: JSDocNullableType) {
return createUnionTypeNode([visitNode(node.type, transformJSDocType), createTypeReferenceNode("null", emptyArray)]);
}
function transformJSDocVariadicType(node: JSDocVariadicType) {
return createArrayTypeNode(visitNode(node.type, transformJSDocType));
}
function transformJSDocFunctionType(node: JSDocFunctionType) {
const parameters = node.parameters && node.parameters.map(transformJSDocType);
return createFunctionTypeNode(emptyArray, parameters as ParameterDeclaration[], node.type);
}
function transformJSDocParameter(node: ParameterDeclaration) {
const index = node.parent.parameters.indexOf(node);
const isRest = node.type.kind === SyntaxKind.JSDocVariadicType && index === node.parent.parameters.length - 1;
const name = node.name || (isRest ? "rest" : "arg" + index);
const dotdotdot = isRest ? createToken(SyntaxKind.DotDotDotToken) : node.dotDotDotToken;
return createParameter(node.decorators, node.modifiers, dotdotdot, name, node.questionToken, visitNode(node.type, transformJSDocType), node.initializer);
}
function transformJSDocTypeReference(node: TypeReferenceNode) {
let name = node.typeName;
let args = node.typeArguments;
if (isIdentifier(node.typeName)) {
if (isJSDocIndexSignature(node)) {
return transformJSDocIndexSignature(node);
}
let text = node.typeName.text;
switch (node.typeName.text) {
case "String":
case "Boolean":
case "Object":
case "Number":
text = text.toLowerCase();
break;
case "array":
case "date":
case "promise":
text = text[0].toUpperCase() + text.slice(1);
break;
}
name = createIdentifier(text);
if ((text === "Array" || text === "Promise") && !node.typeArguments) {
args = createNodeArray([createTypeReferenceNode("any", emptyArray)]);
}
else {
args = visitNodes(node.typeArguments, transformJSDocType);
}
}
return createTypeReferenceNode(name, args);
}
function transformJSDocIndexSignature(node: TypeReferenceNode) {
const index = createParameter(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*dotDotDotToken*/ undefined,
node.typeArguments[0].kind === SyntaxKind.NumberKeyword ? "n" : "s",
/*questionToken*/ undefined,
createTypeReferenceNode(node.typeArguments[0].kind === SyntaxKind.NumberKeyword ? "number" : "string", []),
/*initializer*/ undefined);
const indexSignature = createTypeLiteralNode([createIndexSignature(/*decorators*/ undefined, /*modifiers*/ undefined, [index], node.typeArguments[1])]);
setEmitFlags(indexSignature, EmitFlags.SingleLine);
return indexSignature;
}
}
@@ -17,16 +17,16 @@ namespace ts.refactor.convertFunctionToES6Class {
return undefined;
}
const start = context.startPosition;
const node = getTokenAtPosition(context.file, start, /*includeJsDocComment*/ false);
const checker = context.program.getTypeChecker();
let symbol = checker.getSymbolAtLocation(node);
let symbol = getConstructorSymbol(context);
if (!symbol) {
return undefined;
}
if (symbol && isDeclarationOfFunctionOrClassExpression(symbol)) {
if (isDeclarationOfFunctionOrClassExpression(symbol)) {
symbol = (symbol.valueDeclaration as VariableDeclaration).initializer.symbol;
}
if (symbol && (symbol.flags & SymbolFlags.Function) && symbol.members && (symbol.members.size > 0)) {
if ((symbol.flags & SymbolFlags.Function) && symbol.members && (symbol.members.size > 0)) {
return [
{
name: convertFunctionToES6Class.name,
@@ -48,11 +48,8 @@ namespace ts.refactor.convertFunctionToES6Class {
return undefined;
}
const start = context.startPosition;
const sourceFile = context.file;
const checker = context.program.getTypeChecker();
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
const ctorSymbol = checker.getSymbolAtLocation(token);
const { file: sourceFile } = context;
const ctorSymbol = getConstructorSymbol(context);
const newLine = context.rulesProvider.getFormatOptions().newLineCharacter;
const deletedNodes: Node[] = [];
@@ -269,4 +266,10 @@ namespace ts.refactor.convertFunctionToES6Class {
return filter(source.modifiers, modifier => modifier.kind === kind);
}
}
function getConstructorSymbol({ startPosition, file, program }: RefactorContext): Symbol {
const checker = program.getTypeChecker();
const token = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false);
return checker.getSymbolAtLocation(token);
}
}
+14 -25
View File
@@ -470,7 +470,7 @@ namespace ts.refactor.extractSymbol {
* you may be able to extract into a class method *or* local closure *or* namespace function,
* depending on what's in the extracted body.
*/
function collectEnclosingScopes(range: TargetRange): Scope[] | undefined {
function collectEnclosingScopes(range: TargetRange): Scope[] {
let current: Node = isReadonlyArray(range.range) ? first(range.range) : range.range;
if (range.facts & RangeFacts.UsesThis) {
// if range uses this as keyword or as type inside the class then it can only be extracted to a method of the containing class
@@ -483,30 +483,27 @@ namespace ts.refactor.extractSymbol {
}
}
const start = current;
const scopes: Scope[] = [];
while (true) {
current = current.parent;
// A function parameter's initializer is actually in the outer scope, not the function declaration
if (current.kind === SyntaxKind.Parameter) {
// Skip all the way to the outer scope of the function that declared this parameter
current = findAncestor(current, parent => isFunctionLikeDeclaration(parent)).parent;
}
let scopes: Scope[] | undefined = undefined;
while (current) {
// We want to find the nearest parent where we can place an "equivalent" sibling to the node we're extracting out of.
// Walk up to the closest parent of a place where we can logically put a sibling:
// * Function declaration
// * Class declaration or expression
// * Module/namespace or source file
if (current !== start && isScope(current)) {
(scopes = scopes || []).push(current);
if (isScope(current)) {
scopes.push(current);
if (current.kind === SyntaxKind.SourceFile) {
return scopes;
}
}
// A function parameter's initializer is actually in the outer scope, not the function declaration
if (current && current.parent && current.parent.kind === SyntaxKind.Parameter) {
// Skip all the way to the outer scope of the function that declared this parameter
current = findAncestor(current, parent => isFunctionLikeDeclaration(parent)).parent;
}
else {
current = current.parent;
}
}
return scopes;
}
function getFunctionExtractionAtIndex(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number): RefactorEditInfo {
@@ -592,15 +589,7 @@ namespace ts.refactor.extractSymbol {
function getPossibleExtractionsWorker(targetRange: TargetRange, context: RefactorContext): { readonly scopes: Scope[], readonly readsAndWrites: ReadsAndWrites } {
const { file: sourceFile } = context;
if (targetRange === undefined) {
return undefined;
}
const scopes = collectEnclosingScopes(targetRange);
if (scopes === undefined) {
return undefined;
}
const enclosingTextRange = getEnclosingTextRange(targetRange, sourceFile);
const readsAndWrites = collectReadsAndWrites(
targetRange,
@@ -0,0 +1,64 @@
/* @internal */
namespace ts.refactor.installTypesForPackage {
const actionName = "install";
const installTypesForPackage: Refactor = {
name: "Install missing types package",
description: "Install missing types package",
getEditsForAction,
getAvailableActions,
};
registerRefactor(installTypesForPackage);
function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
const options = context.program.getCompilerOptions();
if (options.noImplicitAny || options.strict) {
// Then it will be available via `fixCannotFindModule`.
return undefined;
}
const action = getAction(context);
return action && [
{
name: installTypesForPackage.name,
description: installTypesForPackage.description,
actions: [
{
description: action.description,
name: actionName,
},
],
},
];
}
function getEditsForAction(context: RefactorContext, _actionName: string): RefactorEditInfo | undefined {
Debug.assertEqual(actionName, _actionName);
const action = getAction(context)!; // Should be defined if we said there was an action available.
return {
edits: [],
renameFilename: undefined,
renameLocation: undefined,
commands: action.commands,
};
}
function getAction(context: RefactorContext): CodeAction | undefined {
const { file, startPosition } = context;
const node = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false);
if (isStringLiteral(node) && isModuleIdentifier(node) && getResolvedModule(file, node.text) === undefined) {
return codefix.tryGetCodeActionForInstallPackageTypes(context.host, node.text);
}
}
function isModuleIdentifier(node: StringLiteral): boolean {
switch (node.parent.kind) {
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExternalModuleReference:
return true;
default:
return false;
}
}
}
+2
View File
@@ -1,2 +1,4 @@
/// <reference path="annotateWithTypeFromJSDoc.ts" />
/// <reference path="convertFunctionToEs6Class.ts" />
/// <reference path="extractSymbol.ts" />
/// <reference path="installTypesForPackage.ts" />
+25 -8
View File
@@ -1146,7 +1146,7 @@ namespace ts {
getCancellationToken: () => cancellationToken,
getCanonicalFileName,
useCaseSensitiveFileNames: () => useCaseSensitivefileNames,
getNewLine: () => getNewLineOrDefaultFromHost(host),
getNewLine: () => getNewLineCharacter(newSettings, { newLine: getNewLineOrDefaultFromHost(host) }),
getDefaultLibFileName: (options) => host.getDefaultLibFileName(options),
writeFile: noop,
getCurrentDirectory: () => currentDirectory,
@@ -1324,17 +1324,19 @@ namespace ts {
function getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
synchronizeHostData();
return Completions.getCompletionsAtPosition(host, program.getTypeChecker(), log, program.getCompilerOptions(), getValidSourceFile(fileName), position);
return Completions.getCompletionsAtPosition(host, program.getTypeChecker(), log, program.getCompilerOptions(), getValidSourceFile(fileName), position, program.getSourceFiles());
}
function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
function getCompletionEntryDetails(fileName: string, position: number, entryName: string, formattingOptions?: FormatCodeSettings): CompletionEntryDetails {
synchronizeHostData();
return Completions.getCompletionEntryDetails(program.getTypeChecker(), log, program.getCompilerOptions(), getValidSourceFile(fileName), position, entryName);
const ruleProvider = formattingOptions ? getRuleProvider(formattingOptions) : undefined;
return Completions.getCompletionEntryDetails(
program.getTypeChecker(), log, program.getCompilerOptions(), getValidSourceFile(fileName), position, entryName, program.getSourceFiles(), host, ruleProvider);
}
function getCompletionEntrySymbol(fileName: string, position: number, entryName: string): Symbol {
synchronizeHostData();
return Completions.getCompletionEntrySymbol(program.getTypeChecker(), log, program.getCompilerOptions(), getValidSourceFile(fileName), position, entryName);
return Completions.getCompletionEntrySymbol(program.getTypeChecker(), log, program.getCompilerOptions(), getValidSourceFile(fileName), position, entryName, program.getSourceFiles());
}
function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo {
@@ -1514,12 +1516,12 @@ namespace ts {
return ts.NavigateTo.getNavigateToItems(sourceFiles, program.getTypeChecker(), cancellationToken, searchValue, maxResultCount, excludeDtsFiles);
}
function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean) {
function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean) {
synchronizeHostData();
const sourceFile = getValidSourceFile(fileName);
const customTransformers = host.getCustomTransformers && host.getCustomTransformers();
return getFileEmitOutput(program, sourceFile, emitOnlyDtsFiles, isDetailed, cancellationToken, customTransformers);
return getFileEmitOutput(program, sourceFile, emitOnlyDtsFiles, cancellationToken, customTransformers);
}
// Signature help
@@ -1766,6 +1768,19 @@ namespace ts {
});
}
function applyCodeActionCommand(fileName: Path, action: CodeActionCommand): Promise<ApplyCodeActionCommandResult> {
fileName = toPath(fileName, currentDirectory, getCanonicalFileName);
switch (action.type) {
case "install package":
return host.installPackage
? host.installPackage({ fileName, packageName: action.packageName })
: Promise.reject("Host does not implement `installPackage`");
default:
Debug.fail();
// TODO: Debug.assertNever(action); will only work if there is more than one type.
}
}
function getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion {
return JsDoc.getDocCommentTemplateAtPosition(getNewLineOrDefaultFromHost(host), syntaxTreeCache.getCurrentSourceFile(fileName), position);
}
@@ -1975,8 +1990,9 @@ namespace ts {
endPosition,
program: getProgram(),
newLineCharacter: formatOptions ? formatOptions.newLineCharacter : host.getNewLine(),
host,
rulesProvider: getRuleProvider(formatOptions),
cancellationToken
cancellationToken,
};
}
@@ -2039,6 +2055,7 @@ namespace ts {
isValidBraceCompletionAtPosition,
getSpanOfEnclosingComment,
getCodeFixesAtPosition,
applyCodeActionCommand,
getEmitOutput,
getNonBoundSourceFile,
getSourceFile,
+7 -4
View File
@@ -141,7 +141,7 @@ namespace ts {
getEncodedSemanticClassifications(fileName: string, start: number, length: number): string;
getCompletionsAtPosition(fileName: string, position: number): string;
getCompletionEntryDetails(fileName: string, position: number, entryName: string): string;
getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/): string;
getQuickInfoAtPosition(fileName: string, position: number): string;
@@ -906,10 +906,13 @@ namespace ts {
}
/** Get a string based representation of a completion list entry details */
public getCompletionEntryDetails(fileName: string, position: number, entryName: string) {
public getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/) {
return this.forwardJSONCall(
`getCompletionEntryDetails('${fileName}', ${position}, '${entryName}')`,
() => this.languageService.getCompletionEntryDetails(fileName, position, entryName)
() => {
const localOptions: ts.FormatCodeOptions = JSON.parse(options);
return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions);
}
);
}
@@ -1254,4 +1257,4 @@ namespace TypeScript.Services {
// TODO: it should be moved into a namespace though.
/* @internal */
const toolsVersion = "2.6";
const toolsVersion = ts.versionMajorMinor;
+16 -5
View File
@@ -6,25 +6,25 @@ namespace ts.textChanges {
* It can be changed to side-table later if we decide that current design is too invasive.
*/
function getPos(n: TextRange): number {
const result = (<any>n)["__pos"];
const result = (<any>n).__pos;
Debug.assert(typeof result === "number");
return result;
}
function setPos(n: TextRange, pos: number): void {
Debug.assert(typeof pos === "number");
(<any>n)["__pos"] = pos;
(<any>n).__pos = pos;
}
function getEnd(n: TextRange): number {
const result = (<any>n)["__end"];
const result = (<any>n).__end;
Debug.assert(typeof result === "number");
return result;
}
function setEnd(n: TextRange, end: number): void {
Debug.assert(typeof end === "number");
(<any>n)["__end"] = end;
(<any>n).__end = end;
}
export interface ConfigurableStart {
@@ -186,14 +186,25 @@ namespace ts.textChanges {
return s;
}
export interface TextChangesContext {
newLineCharacter: string;
rulesProvider: formatting.RulesProvider;
}
export class ChangeTracker {
private changes: Change[] = [];
private readonly newLineCharacter: string;
public static fromContext(context: RefactorContext | CodeFixContext) {
public static fromContext(context: TextChangesContext): ChangeTracker {
return new ChangeTracker(context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.rulesProvider);
}
public static with(context: TextChangesContext, cb: (tracker: ChangeTracker) => void): FileTextChanges[] {
const tracker = ChangeTracker.fromContext(context);
cb(tracker);
return tracker.getChanges();
}
constructor(
private readonly newLine: NewLineKind,
private readonly rulesProvider: formatting.RulesProvider,
+33 -4
View File
@@ -145,10 +145,15 @@ namespace ts {
isCancellationRequested(): boolean;
}
export interface InstallPackageOptions {
fileName: Path;
packageName: string;
}
//
// Public interface of the host of a language service instance.
//
export interface LanguageServiceHost {
export interface LanguageServiceHost extends GetEffectiveTypeRootsHost {
getCompilationSettings(): CompilerOptions;
getNewLine?(): string;
getProjectVersion?(): string;
@@ -187,7 +192,6 @@ namespace ts {
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
directoryExists?(directoryName: string): boolean;
/*
* getDirectories is also required for full import and type reference completions. Without it defined, certain
@@ -199,6 +203,9 @@ namespace ts {
* Gets a set of custom transformers to use during emit.
*/
getCustomTransformers?(): CustomTransformers | undefined;
isKnownTypesPackageName?(name: string): boolean;
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
}
//
@@ -230,7 +237,8 @@ namespace ts {
getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications;
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo;
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails;
// "options" is optional only for backwards-compatibility
getCompletionEntryDetails(fileName: string, position: number, entryName: string, options?: FormatCodeOptions | FormatCodeSettings): CompletionEntryDetails;
getCompletionEntrySymbol(fileName: string, position: number, entryName: string): Symbol;
getQuickInfoAtPosition(fileName: string, position: number): QuickInfo;
@@ -276,11 +284,11 @@ namespace ts {
getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan;
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: FormatCodeSettings): CodeAction[];
applyCodeActionCommand(fileName: string, action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[];
getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined;
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput;
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean): EmitOutput | EmitOutputDetailed;
getProgram(): Program;
@@ -295,6 +303,10 @@ namespace ts {
dispose(): void;
}
export interface ApplyCodeActionCommandResult {
successMessage: string;
}
export interface Classifications {
spans: number[];
endOfLineState: EndOfLineState;
@@ -367,6 +379,20 @@ namespace ts {
description: string;
/** Text changes to apply to each file as part of the code action */
changes: FileTextChanges[];
/**
* If the user accepts the code fix, the editor should send the action back in a `applyAction` request.
* This allows the language service to have side effects (e.g. installing dependencies) upon a code fix.
*/
commands?: CodeActionCommand[];
}
// Publicly, this type is just `{}`. Internally it is a union of all the actions we use.
// See `commands?: {}[]` in protocol.ts
export type CodeActionCommand = InstallPackageAction;
export interface InstallPackageAction {
/* @internal */ type: "install package";
/* @internal */ packageName: string;
}
/**
@@ -420,6 +446,7 @@ namespace ts {
edits: FileTextChanges[];
renameFilename: string | undefined;
renameLocation: number | undefined;
commands?: CodeActionCommand[];
}
export interface TextInsertion {
@@ -674,6 +701,7 @@ namespace ts {
* be used in that case
*/
replacementSpan?: TextSpan;
hasAction?: true;
}
export interface CompletionEntryDetails {
@@ -683,6 +711,7 @@ namespace ts {
displayParts: SymbolDisplayPart[];
documentation: SymbolDisplayPart[];
tags: JSDocTagInfo[];
codeActions?: CodeAction[];
}
export interface OutliningSpan {

Some files were not shown because too many files have changed in this diff Show More