mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into correctlyCacheTaggedTemplates
This commit is contained in:
@@ -59,3 +59,4 @@ internal/
|
||||
.idea
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
.parallelperf.json
|
||||
|
||||
+14
-29
@@ -31,8 +31,6 @@ import merge2 = require("merge2");
|
||||
import * as os from "os";
|
||||
import fold = require("travis-fold");
|
||||
const gulp = helpMaker(originalGulp);
|
||||
const mochaParallel = require("./scripts/mocha-parallel.js");
|
||||
const {runTestsInParallel} = mochaParallel;
|
||||
|
||||
Error.stackTraceLimit = 1000;
|
||||
|
||||
@@ -668,36 +666,18 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
|
||||
}
|
||||
else {
|
||||
// run task to load all tests and partition them between workers
|
||||
const args = [];
|
||||
args.push("-R", "min");
|
||||
if (colors) {
|
||||
args.push("--colors");
|
||||
}
|
||||
else {
|
||||
args.push("--no-colors");
|
||||
}
|
||||
args.push(run);
|
||||
setNodeEnvToDevelopment();
|
||||
runTestsInParallel(taskConfigsFolder, run, { testTimeout, noColors: colors === " --no-colors " }, function(err) {
|
||||
// last worker clean everything and runs linter in case if there were no errors
|
||||
del(taskConfigsFolder).then(() => {
|
||||
if (!err) {
|
||||
lintThenFinish();
|
||||
}
|
||||
else {
|
||||
finish(err);
|
||||
}
|
||||
});
|
||||
exec(host, [run], lintThenFinish, function(e, status) {
|
||||
finish(e, status);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function failWithStatus(err?: any, status?: number) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
if (err || status) {
|
||||
process.exit(typeof status === "number" ? status : 2);
|
||||
}
|
||||
done(err || status);
|
||||
process.exit(status);
|
||||
done();
|
||||
}
|
||||
|
||||
function lintThenFinish() {
|
||||
@@ -711,7 +691,7 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
|
||||
|
||||
function finish(error?: any, errorStatus?: number) {
|
||||
restoreSavedNodeEnv();
|
||||
deleteTemporaryProjectOutput().then(() => {
|
||||
deleteTestConfig().then(deleteTemporaryProjectOutput).then(() => {
|
||||
if (error !== undefined || errorStatus !== undefined) {
|
||||
failWithStatus(error, errorStatus);
|
||||
}
|
||||
@@ -720,6 +700,10 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteTestConfig() {
|
||||
return del("test.config");
|
||||
}
|
||||
}
|
||||
|
||||
gulp.task("runtests-parallel", "Runs all the tests in parallel using the built run.js file. Optional arguments are: --t[ests]=category1|category2|... --d[ebug]=true.", ["build-rules", "tests"], (done) => {
|
||||
@@ -836,7 +820,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 });
|
||||
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);
|
||||
}
|
||||
@@ -1066,10 +1050,11 @@ gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are:
|
||||
const fileMatcher = cmdLineOptions["files"];
|
||||
const files = fileMatcher
|
||||
? `src/**/${fileMatcher}`
|
||||
: "Gulpfile.ts 'scripts/tslint/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
|
||||
const cmd = `node node_modules/tslint/bin/tslint ${files} --format stylish`;
|
||||
: "Gulpfile.ts 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
|
||||
const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
|
||||
console.log("Linting: " + cmd);
|
||||
child_process.execSync(cmd, { stdio: [0, 1, 2] });
|
||||
if (fold.isTravis()) console.log(fold.end("lint"));
|
||||
});
|
||||
|
||||
gulp.task("default", "Runs 'local'", ["local"]);
|
||||
|
||||
+45
-28
@@ -1,11 +1,11 @@
|
||||
// This file contains the build logic for the public repo
|
||||
// @ts-check
|
||||
|
||||
var fs = require("fs");
|
||||
var os = require("os");
|
||||
var path = require("path");
|
||||
var child_process = require("child_process");
|
||||
var fold = require("travis-fold");
|
||||
var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel;
|
||||
var ts = require("./lib/typescript");
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ else if (process.env.PATH !== undefined) {
|
||||
|
||||
function filesFromConfig(configPath) {
|
||||
var configText = fs.readFileSync(configPath).toString();
|
||||
var config = ts.parseConfigFileTextToJson(configPath, configText, /*stripComments*/ true);
|
||||
var config = ts.parseConfigFileTextToJson(configPath, configText);
|
||||
if (config.error) {
|
||||
throw new Error(diagnosticsToString([config.error]));
|
||||
}
|
||||
@@ -104,6 +104,9 @@ var harnessCoreSources = [
|
||||
"loggedIO.ts",
|
||||
"rwcRunner.ts",
|
||||
"test262Runner.ts",
|
||||
"./parallel/shared.ts",
|
||||
"./parallel/host.ts",
|
||||
"./parallel/worker.ts",
|
||||
"runner.ts"
|
||||
].map(function (f) {
|
||||
return path.join(harnessDirectory, f);
|
||||
@@ -143,6 +146,7 @@ var harnessSources = harnessCoreSources.concat([
|
||||
"customTransforms.ts",
|
||||
"programMissingFiles.ts",
|
||||
"symbolWalker.ts",
|
||||
"languageService.ts",
|
||||
].map(function (f) {
|
||||
return path.join(unittestsDirectory, f);
|
||||
})).concat([
|
||||
@@ -595,7 +599,7 @@ file(typesMapOutputPath, function() {
|
||||
var content = fs.readFileSync(path.join(serverDirectory, 'typesMap.json'));
|
||||
// Validate that it's valid JSON
|
||||
try {
|
||||
JSON.parse(content);
|
||||
JSON.parse(content.toString());
|
||||
} catch (e) {
|
||||
console.log("Parse error in typesMap.json: " + e);
|
||||
}
|
||||
@@ -739,7 +743,7 @@ desc("Builds the test infrastructure using the built compiler");
|
||||
task("tests", ["local", run].concat(libraryTargets));
|
||||
|
||||
function exec(cmd, completeHandler, errorHandler) {
|
||||
var ex = jake.createExec([cmd], { windowsVerbatimArguments: true });
|
||||
var ex = jake.createExec([cmd], { windowsVerbatimArguments: true, interactive: true });
|
||||
// Add listeners for output and error
|
||||
ex.addListener("stdout", function (output) {
|
||||
process.stdout.write(output);
|
||||
@@ -765,15 +769,16 @@ function exec(cmd, completeHandler, errorHandler) {
|
||||
ex.run();
|
||||
}
|
||||
|
||||
const del = require("del");
|
||||
function cleanTestDirs() {
|
||||
// Clean the local baselines directory
|
||||
if (fs.existsSync(localBaseline)) {
|
||||
jake.rmRf(localBaseline);
|
||||
del.sync(localBaseline);
|
||||
}
|
||||
|
||||
// Clean the local Rwc baselines directory
|
||||
if (fs.existsSync(localRwcBaseline)) {
|
||||
jake.rmRf(localRwcBaseline);
|
||||
del.sync(localRwcBaseline);
|
||||
}
|
||||
|
||||
jake.mkdirP(localRwcBaseline);
|
||||
@@ -782,13 +787,14 @@ function cleanTestDirs() {
|
||||
}
|
||||
|
||||
// used to pass data from jake command line directly to run.js
|
||||
function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit) {
|
||||
function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit, colors) {
|
||||
var testConfigContents = JSON.stringify({
|
||||
test: tests ? [tests] : undefined,
|
||||
light: light,
|
||||
workerCount: workerCount,
|
||||
taskConfigsFolder: taskConfigsFolder,
|
||||
stackTraceLimit: stackTraceLimit
|
||||
stackTraceLimit: stackTraceLimit,
|
||||
noColor: !colors
|
||||
});
|
||||
fs.writeFileSync('test.config', testConfigContents);
|
||||
}
|
||||
@@ -830,7 +836,7 @@ function runConsoleTests(defaultReporter, runInParallel) {
|
||||
}
|
||||
|
||||
if (tests || light || taskConfigsFolder) {
|
||||
writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit);
|
||||
writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit, colors);
|
||||
}
|
||||
|
||||
if (tests && tests.toLocaleLowerCase() === "rwc") {
|
||||
@@ -893,19 +899,15 @@ function runConsoleTests(defaultReporter, runInParallel) {
|
||||
var savedNodeEnv = process.env.NODE_ENV;
|
||||
process.env.NODE_ENV = "development";
|
||||
var startTime = mark();
|
||||
runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: !colors }, function (err) {
|
||||
exec(host + " " + run, function () {
|
||||
process.env.NODE_ENV = savedNodeEnv;
|
||||
measure(startTime);
|
||||
// last worker clean everything and runs linter in case if there were no errors
|
||||
deleteTemporaryProjectOutput();
|
||||
jake.rmRf(taskConfigsFolder);
|
||||
if (err) {
|
||||
fail(err);
|
||||
}
|
||||
else {
|
||||
runLinter();
|
||||
complete();
|
||||
}
|
||||
runLinter();
|
||||
finish();
|
||||
}, function (e, status) {
|
||||
process.env.NODE_ENV = savedNodeEnv;
|
||||
measure(startTime);
|
||||
finish(status);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -968,8 +970,8 @@ desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is
|
||||
task("runtests-browser", ["browserify", nodeServerOutFile], function () {
|
||||
cleanTestDirs();
|
||||
host = "node";
|
||||
browser = process.env.browser || process.env.b || (os.platform() === "linux" ? "chrome" : "IE");
|
||||
tests = process.env.test || process.env.tests || process.env.t;
|
||||
var browser = process.env.browser || process.env.b || (os.platform() === "linux" ? "chrome" : "IE");
|
||||
var tests = process.env.test || process.env.tests || process.env.t;
|
||||
var light = process.env.light || false;
|
||||
var testConfigFile = 'test.config';
|
||||
if (fs.existsSync(testConfigFile)) {
|
||||
@@ -1041,6 +1043,7 @@ function acceptBaseline(sourceFolder, targetFolder) {
|
||||
if (fs.existsSync(target)) {
|
||||
fs.unlinkSync(target);
|
||||
}
|
||||
jake.mkdirP(path.dirname(target));
|
||||
fs.renameSync(path.join(sourceFolder, filename), target);
|
||||
}
|
||||
}
|
||||
@@ -1118,7 +1121,7 @@ task("update-sublime", ["local", serverFile], function () {
|
||||
jake.cpR(serverFile + ".map", "../TypeScript-Sublime-Plugin/tsserver/");
|
||||
});
|
||||
|
||||
var tslintRuleDir = "scripts/tslint";
|
||||
var tslintRuleDir = "scripts/tslint/rules";
|
||||
var tslintRules = [
|
||||
"booleanTriviaRule",
|
||||
"debugAssertRule",
|
||||
@@ -1134,13 +1137,27 @@ var tslintRulesFiles = tslintRules.map(function (p) {
|
||||
return path.join(tslintRuleDir, p + ".ts");
|
||||
});
|
||||
var tslintRulesOutFiles = tslintRules.map(function (p) {
|
||||
return path.join(builtLocalDirectory, "tslint", p + ".js");
|
||||
return path.join(builtLocalDirectory, "tslint/rules", p + ".js");
|
||||
});
|
||||
var tslintFormattersDir = "scripts/tslint/formatters";
|
||||
var tslintFormatters = [
|
||||
"autolinkableStylishFormatter",
|
||||
];
|
||||
var tslintFormatterFiles = tslintFormatters.map(function (p) {
|
||||
return path.join(tslintFormattersDir, p + ".ts");
|
||||
});
|
||||
var tslintFormattersOutFiles = tslintFormatters.map(function (p) {
|
||||
return path.join(builtLocalDirectory, "tslint/formatters", p + ".js");
|
||||
});
|
||||
desc("Compiles tslint rules to js");
|
||||
task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(["build-rules-end"]));
|
||||
task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(tslintFormattersOutFiles).concat(["build-rules-end"]));
|
||||
tslintRulesFiles.forEach(function (ruleFile, i) {
|
||||
compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false,
|
||||
{ noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint"), lib: "es6" });
|
||||
{ noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint/rules"), lib: "es6" });
|
||||
});
|
||||
tslintFormatterFiles.forEach(function (ruleFile, i) {
|
||||
compileFile(tslintFormattersOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false,
|
||||
{ noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint/formatters"), lib: "es6" });
|
||||
});
|
||||
|
||||
desc("Emit the start of the build-rules fold");
|
||||
@@ -1208,8 +1225,8 @@ task("lint", ["build-rules"], () => {
|
||||
const fileMatcher = process.env.f || process.env.file || process.env.files;
|
||||
const files = fileMatcher
|
||||
? `src/**/${fileMatcher}`
|
||||
: "Gulpfile.ts 'scripts/tslint/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
|
||||
const cmd = `node node_modules/tslint/bin/tslint ${files} --format stylish`;
|
||||
: "Gulpfile.ts 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
|
||||
const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
|
||||
console.log("Linting: " + cmd);
|
||||
jake.exec([cmd], { interactive: true }, () => {
|
||||
if (fold.isTravis()) console.log(fold.end("lint"));
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
|
||||
For the latest stable version:
|
||||
|
||||
```
|
||||
```bash
|
||||
npm install -g typescript
|
||||
```
|
||||
|
||||
For our nightly builds:
|
||||
|
||||
```
|
||||
```bash
|
||||
npm install -g typescript@next
|
||||
```
|
||||
|
||||
@@ -50,19 +50,19 @@ In order to build the TypeScript compiler, ensure that you have [Git](https://gi
|
||||
|
||||
Clone a copy of the repo:
|
||||
|
||||
```
|
||||
```bash
|
||||
git clone https://github.com/Microsoft/TypeScript.git
|
||||
```
|
||||
|
||||
Change to the TypeScript directory:
|
||||
|
||||
```
|
||||
```bash
|
||||
cd TypeScript
|
||||
```
|
||||
|
||||
Install Gulp tools and dev dependencies:
|
||||
|
||||
```
|
||||
```bash
|
||||
npm install -g gulp
|
||||
npm install
|
||||
```
|
||||
@@ -88,7 +88,7 @@ gulp help # List the above commands.
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
```bash
|
||||
node built/local/tsc.js hello.ts
|
||||
```
|
||||
|
||||
|
||||
+3
-1
@@ -31,6 +31,7 @@
|
||||
"devDependencies": {
|
||||
"@types/browserify": "latest",
|
||||
"@types/chai": "latest",
|
||||
"@types/colors": "latest",
|
||||
"@types/convert-source-map": "latest",
|
||||
"@types/del": "latest",
|
||||
"@types/glob": "latest",
|
||||
@@ -48,8 +49,8 @@
|
||||
"@types/q": "latest",
|
||||
"@types/run-sequence": "latest",
|
||||
"@types/through2": "latest",
|
||||
"browserify": "latest",
|
||||
"browser-resolve": "^1.11.2",
|
||||
"browserify": "latest",
|
||||
"chai": "latest",
|
||||
"convert-source-map": "latest",
|
||||
"del": "latest",
|
||||
@@ -75,6 +76,7 @@
|
||||
"travis-fold": "latest",
|
||||
"ts-node": "latest",
|
||||
"tslint": "latest",
|
||||
"colors": "latest",
|
||||
"typescript": "next"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/// <reference path="..\src\harness\external\node.d.ts" />
|
||||
|
||||
/**
|
||||
* You should have ts-node installed globally before executing this, probably!
|
||||
* Otherwise you'll need to compile this script before you start bisecting!
|
||||
*/
|
||||
import cp = require('child_process');
|
||||
import fs = require('fs');
|
||||
|
||||
@@ -42,8 +44,8 @@ jake.on('close', jakeExitCode => {
|
||||
});
|
||||
} else {
|
||||
console.log('Unknown command line arguments.');
|
||||
console.log('Usage (compile errors): git bisect run scripts\bisect.js "foo.ts --module amd" compiles');
|
||||
console.log('Usage (emit check): git bisect run scripts\bisect.js bar.ts emits bar.js "_this = this"');
|
||||
console.log('Usage (compile errors): git bisect run ts-node scripts\bisect-test.ts "../failure.ts --module amd" !compiles');
|
||||
console.log('Usage (emit check): git bisect run ts-node scripts\bisect-test.ts bar.ts emits bar.js "_this = this"');
|
||||
// Aborts the 'git bisect run' process
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
echo off
|
||||
IF NOT EXIST scripts\bisect.cmd GOTO :wrongdir
|
||||
IF "%1" == "" GOTO :usage
|
||||
IF "%1" == "GO" GOTO :run
|
||||
GOTO :copy
|
||||
|
||||
:usage
|
||||
echo Usage: bisect GoodCommit BadCommit test.ts compiles
|
||||
echo Usage: bisect GoodCommit BadCommit test.ts emits test.js "var x = 3"
|
||||
GOTO :eof
|
||||
|
||||
:copy
|
||||
copy scripts\bisect.cmd scripts\bisect-fresh.cmd
|
||||
scripts\bisect-fresh GO %*
|
||||
GOTO :eof
|
||||
|
||||
:run
|
||||
call jake local
|
||||
node built/local/tsc.js scripts/bisect-test.ts --module commonjs
|
||||
git bisect start %2 %3
|
||||
git bisect run node scripts/bisect-test.js %4 %5 %6 %7
|
||||
del scripts\bisect-test.js
|
||||
del scripts\bisect-fresh.cmd
|
||||
GOTO :eof
|
||||
|
||||
:wrongdir
|
||||
@echo Run this file from the repo folder, not the scripts folder
|
||||
GOTO :eof
|
||||
|
||||
:eof
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('mocha').reporters.Base;
|
||||
|
||||
/**
|
||||
* Expose `None`.
|
||||
*/
|
||||
|
||||
exports = module.exports = None;
|
||||
|
||||
/**
|
||||
* Initialize a new `None` test reporter.
|
||||
*
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function None(runner) {
|
||||
Base.call(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
None.prototype.__proto__ = Base.prototype;
|
||||
@@ -1,405 +0,0 @@
|
||||
var tty = require("tty")
|
||||
, readline = require("readline")
|
||||
, fs = require("fs")
|
||||
, path = require("path")
|
||||
, child_process = require("child_process")
|
||||
, os = require("os")
|
||||
, mocha = require("mocha")
|
||||
, Base = mocha.reporters.Base
|
||||
, color = Base.color
|
||||
, cursor = Base.cursor
|
||||
, ms = require("mocha/lib/ms");
|
||||
|
||||
var isatty = tty.isatty(1) && tty.isatty(2);
|
||||
var tapRangePattern = /^(\d+)\.\.(\d+)(?:$|\r\n?|\n)/;
|
||||
var tapTestPattern = /^(not\sok|ok)\s+(\d+)\s+(?:-\s+)?(.*)$/;
|
||||
var tapCommentPattern = /^#(?: (tests|pass|fail) (\d+)$)?/;
|
||||
|
||||
exports.runTestsInParallel = runTestsInParallel;
|
||||
exports.ProgressBars = ProgressBars;
|
||||
|
||||
function runTestsInParallel(taskConfigsFolder, run, options, cb) {
|
||||
if (options === undefined) options = { };
|
||||
|
||||
return discoverTests(run, options, function (error) {
|
||||
if (error) {
|
||||
return cb(error);
|
||||
}
|
||||
|
||||
return runTests(taskConfigsFolder, run, options, cb);
|
||||
});
|
||||
}
|
||||
|
||||
function discoverTests(run, options, cb) {
|
||||
console.log("Discovering tests...");
|
||||
|
||||
var cmd = "mocha -R " + require.resolve("./mocha-none-reporter.js") + " " + run;
|
||||
var p = spawnProcess(cmd);
|
||||
p.on("exit", function (status) {
|
||||
if (status) {
|
||||
cb(new Error("Process exited with code " + status));
|
||||
}
|
||||
else {
|
||||
cb();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function runTests(taskConfigsFolder, run, options, cb) {
|
||||
var configFiles = fs.readdirSync(taskConfigsFolder);
|
||||
var numPartitions = configFiles.length;
|
||||
if (numPartitions <= 0) {
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Running tests on " + numPartitions + " threads...");
|
||||
|
||||
var partitions = Array(numPartitions);
|
||||
var progressBars = new ProgressBars();
|
||||
progressBars.enable();
|
||||
|
||||
var counter = numPartitions;
|
||||
configFiles.forEach(runTestsInPartition);
|
||||
|
||||
function runTestsInPartition(file, index) {
|
||||
var partition = {
|
||||
file: path.join(taskConfigsFolder, file),
|
||||
tests: 0,
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
completed: 0,
|
||||
current: undefined,
|
||||
start: undefined,
|
||||
end: undefined,
|
||||
catastrophicError: "",
|
||||
failures: []
|
||||
};
|
||||
partitions[index] = partition;
|
||||
|
||||
// Set up the progress bar.
|
||||
updateProgress(0);
|
||||
|
||||
// Start the background process.
|
||||
var cmd = "mocha -t " + (options.testTimeout || 20000) + " -R tap --no-colors " + run + " --config='" + partition.file + "'";
|
||||
var p = spawnProcess(cmd);
|
||||
var rl = readline.createInterface({
|
||||
input: p.stdout,
|
||||
terminal: false
|
||||
});
|
||||
|
||||
var rlError = readline.createInterface({
|
||||
input: p.stderr,
|
||||
terminal: false
|
||||
});
|
||||
|
||||
rl.on("line", onmessage);
|
||||
rlError.on("line", onErrorMessage);
|
||||
p.on("exit", onexit)
|
||||
|
||||
function onErrorMessage(line) {
|
||||
partition.catastrophicError += line + os.EOL;
|
||||
}
|
||||
|
||||
function onmessage(line) {
|
||||
if (partition.start === undefined) {
|
||||
partition.start = Date.now();
|
||||
}
|
||||
|
||||
var rangeMatch = tapRangePattern.exec(line);
|
||||
if (rangeMatch) {
|
||||
partition.tests = parseInt(rangeMatch[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
var testMatch = tapTestPattern.exec(line);
|
||||
if (testMatch) {
|
||||
var test = {
|
||||
result: testMatch[1],
|
||||
id: parseInt(testMatch[2]),
|
||||
name: testMatch[3],
|
||||
output: []
|
||||
};
|
||||
|
||||
partition.current = test;
|
||||
partition.completed++;
|
||||
|
||||
if (test.result === "ok") {
|
||||
partition.passed++;
|
||||
}
|
||||
else {
|
||||
partition.failed++;
|
||||
partition.failures.push(test);
|
||||
}
|
||||
|
||||
var progress = partition.completed / partition.tests;
|
||||
if (progress < 1) {
|
||||
updateProgress(progress);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var commentMatch = tapCommentPattern.exec(line);
|
||||
if (commentMatch) {
|
||||
switch (commentMatch[1]) {
|
||||
case "tests":
|
||||
partition.current = undefined;
|
||||
partition.tests = parseInt(commentMatch[2]);
|
||||
break;
|
||||
|
||||
case "pass":
|
||||
partition.passed = parseInt(commentMatch[2]);
|
||||
break;
|
||||
|
||||
case "fail":
|
||||
partition.failed = parseInt(commentMatch[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (partition.current) {
|
||||
partition.current.output.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
function onexit(code) {
|
||||
if (partition.end === undefined) {
|
||||
partition.end = Date.now();
|
||||
}
|
||||
|
||||
partition.duration = partition.end - partition.start;
|
||||
var isPartitionFail = partition.failed || code !== 0;
|
||||
var summaryColor = isPartitionFail ? "fail" : "green";
|
||||
var summarySymbol = isPartitionFail ? Base.symbols.err : Base.symbols.ok;
|
||||
|
||||
var summaryTests = (isPartitionFail ? partition.passed + "/" + partition.tests : partition.passed) + " passing";
|
||||
var summaryDuration = "(" + ms(partition.duration) + ")";
|
||||
var savedUseColors = Base.useColors;
|
||||
Base.useColors = !options.noColors;
|
||||
|
||||
var summary = color(summaryColor, summarySymbol + " " + summaryTests) + " " + color("light", summaryDuration);
|
||||
Base.useColors = savedUseColors;
|
||||
|
||||
updateProgress(1, summary);
|
||||
|
||||
signal();
|
||||
}
|
||||
|
||||
function updateProgress(percentComplete, title) {
|
||||
var progressColor = "pending";
|
||||
if (partition.failed) {
|
||||
progressColor = "fail";
|
||||
}
|
||||
|
||||
progressBars.update(
|
||||
index,
|
||||
percentComplete,
|
||||
progressColor,
|
||||
title
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function signal() {
|
||||
counter--;
|
||||
|
||||
if (counter <= 0) {
|
||||
var reporter = new Base(),
|
||||
stats = reporter.stats,
|
||||
failures = reporter.failures;
|
||||
|
||||
var duration = 0;
|
||||
var catastrophicError = "";
|
||||
for (var i = 0; i < numPartitions; i++) {
|
||||
var partition = partitions[i];
|
||||
stats.passes += partition.passed;
|
||||
stats.failures += partition.failed;
|
||||
stats.tests += partition.tests;
|
||||
duration += partition.duration;
|
||||
if (partition.catastrophicError !== "") {
|
||||
// Partition is written out to a temporary file as a JSON object.
|
||||
// Below is an example of how the partition JSON object looks like
|
||||
// {
|
||||
// "light":false,
|
||||
// "tasks":[
|
||||
// {
|
||||
// "runner":"compiler",
|
||||
// "files":["tests/cases/compiler/es6ImportNamedImportParsingError.ts"]
|
||||
// }
|
||||
// ],
|
||||
// "runUnitTests":false
|
||||
// }
|
||||
var jsonText = fs.readFileSync(partition.file);
|
||||
var configObj = JSON.parse(jsonText);
|
||||
if (configObj.tasks && configObj.tasks[0]) {
|
||||
catastrophicError += "Error from one or more of these files: " + configObj.tasks[0].files + os.EOL;
|
||||
catastrophicError += partition.catastrophicError;
|
||||
catastrophicError += os.EOL;
|
||||
}
|
||||
}
|
||||
for (var j = 0; j < partition.failures.length; j++) {
|
||||
var failure = partition.failures[j];
|
||||
failures.push(makeMochaTest(failure));
|
||||
}
|
||||
}
|
||||
|
||||
stats.duration = duration;
|
||||
progressBars.disable();
|
||||
|
||||
if (options.noColors) {
|
||||
var savedUseColors = Base.useColors;
|
||||
Base.useColors = false;
|
||||
reporter.epilogue();
|
||||
Base.useColors = savedUseColors;
|
||||
}
|
||||
else {
|
||||
reporter.epilogue();
|
||||
}
|
||||
|
||||
if (catastrophicError !== "") {
|
||||
return cb(new Error(catastrophicError));
|
||||
}
|
||||
if (stats.failures) {
|
||||
return cb(new Error("Test failures reported: " + stats.failures));
|
||||
}
|
||||
else {
|
||||
return cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makeMochaTest(test) {
|
||||
return {
|
||||
fullTitle: function() {
|
||||
return test.name;
|
||||
},
|
||||
err: {
|
||||
message: test.output[0],
|
||||
stack: test.output.join(os.EOL)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var nodeModulesPathPrefix = path.resolve("./node_modules/.bin/") + path.delimiter;
|
||||
if (process.env.path !== undefined) {
|
||||
process.env.path = nodeModulesPathPrefix + process.env.path;
|
||||
} else if (process.env.PATH !== undefined) {
|
||||
process.env.PATH = nodeModulesPathPrefix + process.env.PATH;
|
||||
}
|
||||
|
||||
function spawnProcess(cmd, options) {
|
||||
var shell = process.platform === "win32" ? "cmd" : "/bin/sh";
|
||||
var prefix = process.platform === "win32" ? "/c" : "-c";
|
||||
return child_process.spawn(shell, [prefix, cmd], { windowsVerbatimArguments: true });
|
||||
}
|
||||
|
||||
function ProgressBars(options) {
|
||||
if (!options) options = {};
|
||||
var open = options.open || '[';
|
||||
var close = options.close || ']';
|
||||
var complete = options.complete || '▬';
|
||||
var incomplete = options.incomplete || Base.symbols.dot;
|
||||
var maxWidth = Math.floor(Base.window.width * .30) - open.length - close.length - 2;
|
||||
var width = minMax(options.width || maxWidth, 10, maxWidth);
|
||||
this._options = {
|
||||
open: open,
|
||||
complete: complete,
|
||||
incomplete: incomplete,
|
||||
close: close,
|
||||
width: width
|
||||
};
|
||||
|
||||
this._progressBars = [];
|
||||
this._lineCount = 0;
|
||||
this._enabled = false;
|
||||
}
|
||||
ProgressBars.prototype = {
|
||||
enable: function () {
|
||||
if (!this._enabled) {
|
||||
process.stdout.write(os.EOL);
|
||||
this._enabled = true;
|
||||
}
|
||||
},
|
||||
disable: function () {
|
||||
if (this._enabled) {
|
||||
process.stdout.write(os.EOL);
|
||||
this._enabled = false;
|
||||
}
|
||||
},
|
||||
update: function (index, percentComplete, color, title) {
|
||||
percentComplete = minMax(percentComplete, 0, 1);
|
||||
|
||||
var progressBar = this._progressBars[index] || (this._progressBars[index] = { });
|
||||
var width = this._options.width;
|
||||
var n = Math.floor(width * percentComplete);
|
||||
var i = width - n;
|
||||
if (n === progressBar.lastN && title === progressBar.title && color === progressBar.progressColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
progressBar.lastN = n;
|
||||
progressBar.title = title;
|
||||
progressBar.progressColor = color;
|
||||
|
||||
var progress = " ";
|
||||
progress += this._color('progress', this._options.open);
|
||||
progress += this._color(color, fill(this._options.complete, n));
|
||||
progress += this._color('progress', fill(this._options.incomplete, i));
|
||||
progress += this._color('progress', this._options.close);
|
||||
|
||||
if (title) {
|
||||
progress += this._color('progress', ' ' + title);
|
||||
}
|
||||
|
||||
if (progressBar.text !== progress) {
|
||||
progressBar.text = progress;
|
||||
this._render(index);
|
||||
}
|
||||
},
|
||||
_render: function (index) {
|
||||
if (!this._enabled || !isatty) {
|
||||
return;
|
||||
}
|
||||
|
||||
cursor.hide();
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns, -this._lineCount);
|
||||
var lineCount = 0;
|
||||
var numProgressBars = this._progressBars.length;
|
||||
for (var i = 0; i < numProgressBars; i++) {
|
||||
if (i === index) {
|
||||
readline.clearLine(process.stdout, 1);
|
||||
process.stdout.write(this._progressBars[i].text + os.EOL);
|
||||
}
|
||||
else {
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns, +1);
|
||||
}
|
||||
|
||||
lineCount++;
|
||||
}
|
||||
|
||||
this._lineCount = lineCount;
|
||||
cursor.show();
|
||||
},
|
||||
_color: function (type, text) {
|
||||
return type && !this._options.noColors ? color(type, text) : text;
|
||||
}
|
||||
};
|
||||
|
||||
function fill(ch, size) {
|
||||
var s = "";
|
||||
while (s.length < size) {
|
||||
s += ch;
|
||||
}
|
||||
|
||||
return s.length > size ? s.substr(0, size) : s;
|
||||
}
|
||||
|
||||
function minMax(value, min, max) {
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import * as Lint from "tslint";
|
||||
import * as colors from "colors";
|
||||
import { sep } from "path";
|
||||
function groupBy<T>(array: ReadonlyArray<T> | undefined, getGroupId: (elem: T, index: number) => number | string): T[][] {
|
||||
if (!array) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const groupIdToGroup: { [index: string]: T[] } = {};
|
||||
let result: T[][] | undefined; // Compacted array for return value
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
const value = array[index];
|
||||
const key = getGroupId(value, index);
|
||||
if (groupIdToGroup[key]) {
|
||||
groupIdToGroup[key].push(value);
|
||||
}
|
||||
else {
|
||||
const newGroup = [value];
|
||||
groupIdToGroup[key] = newGroup;
|
||||
if (!result) {
|
||||
result = [newGroup];
|
||||
}
|
||||
else {
|
||||
result.push(newGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result || [];
|
||||
}
|
||||
|
||||
function max<T>(array: ReadonlyArray<T> | undefined, selector: (elem: T) => number): number {
|
||||
if (!array) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let max = 0;
|
||||
for (const item of array) {
|
||||
const scalar = selector(item);
|
||||
if (scalar > max) {
|
||||
max = scalar;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
function getLink(failure: Lint.RuleFailure, color: boolean): string {
|
||||
const lineAndCharacter = failure.getStartPosition().getLineAndCharacter();
|
||||
const sev = failure.getRuleSeverity().toUpperCase();
|
||||
let path = failure.getFileName();
|
||||
// Most autolinks only become clickable if they contain a slash in some way; so we make a top level file into a relative path here
|
||||
if (path.indexOf("/") === -1 && path.indexOf("\\") === -1) {
|
||||
path = `.${sep}${path}`;
|
||||
}
|
||||
return `${color ? (sev === "WARNING" ? colors.blue(sev) : colors.red(sev)) : sev}: ${path}:${lineAndCharacter.line + 1}:${lineAndCharacter.character + 1}`;
|
||||
}
|
||||
|
||||
function getLinkMaxSize(failures: Lint.RuleFailure[]): number {
|
||||
return max(failures, f => getLink(f, /*color*/ false).length);
|
||||
}
|
||||
|
||||
function getNameMaxSize(failures: Lint.RuleFailure[]): number {
|
||||
return max(failures, f => f.getRuleName().length);
|
||||
}
|
||||
|
||||
function pad(str: string, visiblelen: number, len: number) {
|
||||
if (visiblelen >= len) return str;
|
||||
const count = len - visiblelen;
|
||||
for (let i = 0; i < count; i++) {
|
||||
str += " ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export class Formatter extends Lint.Formatters.AbstractFormatter {
|
||||
public static metadata: Lint.IFormatterMetadata = {
|
||||
formatterName: "autolinkableStylish",
|
||||
description: "Human-readable formatter which creates stylish messages with autolinkable filepaths.",
|
||||
descriptionDetails: Lint.Utils.dedent`
|
||||
Colorized output grouped by file, with autolinkable filepaths containing line and column information
|
||||
`,
|
||||
sample: Lint.Utils.dedent`
|
||||
src/myFile.ts
|
||||
ERROR: src/myFile.ts:1:14 semicolon Missing semicolon`,
|
||||
consumer: "human"
|
||||
};
|
||||
public format(failures: Lint.RuleFailure[]): string {
|
||||
return groupBy(failures, f => f.getFileName()).map(group => {
|
||||
const currentFile = group[0].getFileName();
|
||||
const linkMaxSize = getLinkMaxSize(group);
|
||||
const nameMaxSize = getNameMaxSize(group);
|
||||
return `
|
||||
${currentFile}
|
||||
${group.map(f => `${pad(getLink(f, /*color*/ true), getLink(f, /*color*/ false).length, linkMaxSize)} ${colors.grey(pad(f.getRuleName(), f.getRuleName().length, nameMaxSize))} ${colors.yellow(f.getFailure())}`).join("\n")}`;
|
||||
}).join("\n");
|
||||
}
|
||||
}
|
||||
+8
-15
@@ -203,9 +203,11 @@ namespace ts {
|
||||
node.symbol = symbol;
|
||||
|
||||
if (!symbol.declarations) {
|
||||
symbol.declarations = [];
|
||||
symbol.declarations = [node];
|
||||
}
|
||||
else {
|
||||
symbol.declarations.push(node);
|
||||
}
|
||||
symbol.declarations.push(node);
|
||||
|
||||
if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) {
|
||||
symbol.exports = createSymbolTable();
|
||||
@@ -282,17 +284,8 @@ namespace ts {
|
||||
const index = indexOf(functionType.parameters, node);
|
||||
return "arg" + index as __String;
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
const parentNode = node.parent && node.parent.parent;
|
||||
let nameFromParentNode: __String;
|
||||
if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) {
|
||||
if ((<VariableStatement>parentNode).declarationList.declarations.length > 0) {
|
||||
const nameIdentifier = (<VariableStatement>parentNode).declarationList.declarations[0].name;
|
||||
if (isIdentifier(nameIdentifier)) {
|
||||
nameFromParentNode = nameIdentifier.escapedText;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nameFromParentNode;
|
||||
const name = getNameOfJSDocTypedef(node as JSDocTypedefTag);
|
||||
return typeof name !== "undefined" ? name.escapedText : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,7 +591,7 @@ namespace ts {
|
||||
// Binding of JsDocComment should be done before the current block scope container changes.
|
||||
// because the scope of JsDocComment should not be affected by whether the current node is a
|
||||
// container or not.
|
||||
if (node.jsDoc) {
|
||||
if (hasJSDocNodes(node)) {
|
||||
if (isInJavaScriptFile(node)) {
|
||||
for (const j of node.jsDoc) {
|
||||
bind(j);
|
||||
@@ -1931,7 +1924,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function bindJSDocTypedefTagIfAny(node: Node) {
|
||||
if (!node.jsDoc) {
|
||||
if (!hasJSDocNodes(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+474
-364
File diff suppressed because it is too large
Load Diff
@@ -1057,7 +1057,7 @@ namespace ts {
|
||||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, extraKeyDiagnosticMessage, keyText));
|
||||
}
|
||||
const value = convertPropertyValueToJson(element.initializer, option);
|
||||
if (typeof keyText !== "undefined" && typeof value !== "undefined") {
|
||||
if (typeof keyText !== "undefined") {
|
||||
result[keyText] = value;
|
||||
// Notify key value set, if user asked for it
|
||||
if (jsonConversionNotifier &&
|
||||
@@ -1104,7 +1104,7 @@ namespace ts {
|
||||
return false;
|
||||
|
||||
case SyntaxKind.NullKeyword:
|
||||
reportInvalidOptionValue(!!option);
|
||||
reportInvalidOptionValue(option && option.name === "extends"); // "extends" is the only option we don't allow null/undefined for
|
||||
return null; // tslint:disable-line:no-null-keyword
|
||||
|
||||
case SyntaxKind.StringLiteral:
|
||||
@@ -1189,6 +1189,7 @@ namespace ts {
|
||||
|
||||
function isCompilerOptionsValue(option: CommandLineOption, value: any): value is CompilerOptionsValue {
|
||||
if (option) {
|
||||
if (isNullOrUndefined(value)) return true; // All options are undefinable/nullable
|
||||
if (option.type === "list") {
|
||||
return isArray(value);
|
||||
}
|
||||
@@ -1379,6 +1380,17 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function isNullOrUndefined(x: any): x is null | undefined {
|
||||
// tslint:disable-next-line:no-null-keyword
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
function directoryOfCombinedPath(fileName: string, basePath: string) {
|
||||
// Use the `identity` function to avoid canonicalizing the path, as it must remain noncanonical
|
||||
// until consistient casing errors are reported
|
||||
return getDirectoryPath(toPath(fileName, basePath, identity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the contents of a config file from json or json source file (tsconfig.json).
|
||||
* @param json The contents of the config file to parse
|
||||
@@ -1419,7 +1431,7 @@ namespace ts {
|
||||
|
||||
function getFileNames(): ExpandResult {
|
||||
let fileNames: ReadonlyArray<string>;
|
||||
if (hasProperty(raw, "files")) {
|
||||
if (hasProperty(raw, "files") && !isNullOrUndefined(raw["files"])) {
|
||||
if (isArray(raw["files"])) {
|
||||
fileNames = <ReadonlyArray<string>>raw["files"];
|
||||
if (fileNames.length === 0) {
|
||||
@@ -1432,7 +1444,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
let includeSpecs: ReadonlyArray<string>;
|
||||
if (hasProperty(raw, "include")) {
|
||||
if (hasProperty(raw, "include") && !isNullOrUndefined(raw["include"])) {
|
||||
if (isArray(raw["include"])) {
|
||||
includeSpecs = <ReadonlyArray<string>>raw["include"];
|
||||
}
|
||||
@@ -1442,7 +1454,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
let excludeSpecs: ReadonlyArray<string>;
|
||||
if (hasProperty(raw, "exclude")) {
|
||||
if (hasProperty(raw, "exclude") && !isNullOrUndefined(raw["exclude"])) {
|
||||
if (isArray(raw["exclude"])) {
|
||||
excludeSpecs = <ReadonlyArray<string>>raw["exclude"];
|
||||
}
|
||||
@@ -1461,7 +1473,7 @@ namespace ts {
|
||||
includeSpecs = ["**/*"];
|
||||
}
|
||||
|
||||
const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors, extraFileExtensions, sourceFile);
|
||||
const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath, options, host, errors, extraFileExtensions, sourceFile);
|
||||
|
||||
if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0) {
|
||||
errors.push(
|
||||
@@ -1552,7 +1564,7 @@ namespace ts {
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: (fileName: string) => string,
|
||||
configFileName: string,
|
||||
configFileName: string | undefined,
|
||||
errors: Push<Diagnostic>
|
||||
): ParsedTsconfig {
|
||||
if (hasProperty(json, "excludes")) {
|
||||
@@ -1571,7 +1583,8 @@ namespace ts {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string"));
|
||||
}
|
||||
else {
|
||||
extendedConfigPath = getExtendsConfigPath(json.extends, host, basePath, getCanonicalFileName, errors, createCompilerDiagnostic);
|
||||
const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath;
|
||||
extendedConfigPath = getExtendsConfigPath(json.extends, host, newBase, getCanonicalFileName, errors, createCompilerDiagnostic);
|
||||
}
|
||||
}
|
||||
return { raw: json, options, typeAcquisition, extendedConfigPath };
|
||||
@@ -1582,7 +1595,7 @@ namespace ts {
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: (fileName: string) => string,
|
||||
configFileName: string,
|
||||
configFileName: string | undefined,
|
||||
errors: Push<Diagnostic>
|
||||
): ParsedTsconfig {
|
||||
const options = getDefaultCompilerOptions(configFileName);
|
||||
@@ -1603,10 +1616,11 @@ namespace ts {
|
||||
onSetValidOptionKeyValueInRoot(key: string, _keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression) {
|
||||
switch (key) {
|
||||
case "extends":
|
||||
const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath;
|
||||
extendedConfigPath = getExtendsConfigPath(
|
||||
<string>value,
|
||||
host,
|
||||
basePath,
|
||||
newBase,
|
||||
getCanonicalFileName,
|
||||
errors,
|
||||
(message, arg0) =>
|
||||
@@ -1803,6 +1817,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function normalizeOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue {
|
||||
if (isNullOrUndefined(value)) return undefined;
|
||||
if (option.type === "list") {
|
||||
const listOption = <CommandLineOptionOfListType>option;
|
||||
if (listOption.element.isFilePath || typeof listOption.element.type !== "string") {
|
||||
@@ -1827,6 +1842,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Push<Diagnostic>) {
|
||||
if (isNullOrUndefined(value)) return undefined;
|
||||
const key = value.toLowerCase();
|
||||
const val = opt.type.get(key);
|
||||
if (val !== undefined) {
|
||||
@@ -1977,7 +1993,7 @@ namespace ts {
|
||||
// remove a literal file.
|
||||
if (fileNames) {
|
||||
for (const fileName of fileNames) {
|
||||
const file = combinePaths(basePath, fileName);
|
||||
const file = getNormalizedAbsolutePath(fileName, basePath);
|
||||
literalFileMap.set(keyMapper(file), file);
|
||||
}
|
||||
}
|
||||
|
||||
+21
-23
@@ -9,6 +9,15 @@ namespace ts {
|
||||
export const version = `${versionMajorMinor}.0`;
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
export function isExternalModuleNameRelative(moduleName: string): boolean {
|
||||
// TypeScript 1.0 spec (April 2014): 11.2.1
|
||||
// An external module name is "relative" if the first term is "." or "..".
|
||||
// Update: We also consider a path like `C:\foo.ts` "relative" because we do not search for it in `node_modules` or treat it as an ambient module.
|
||||
return pathIsRelative(moduleName) || isRootedDiskPath(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
|
||||
@@ -40,7 +49,6 @@ namespace ts {
|
||||
return new MapCtr<T>() as UnderscoreEscapedMap<T>;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createSymbolTable(symbols?: ReadonlyArray<Symbol>): SymbolTable {
|
||||
const result = createMap<Symbol>() as SymbolTable;
|
||||
if (symbols) {
|
||||
@@ -1220,6 +1228,9 @@ namespace ts {
|
||||
/** Does nothing. */
|
||||
export function noop(): void {}
|
||||
|
||||
/** Returns its argument. */
|
||||
export function identity<T>(x: T) { return x; }
|
||||
|
||||
/** Throws an error because a function is not implemented. */
|
||||
export function notImplemented(): never {
|
||||
throw new Error("Not implemented");
|
||||
@@ -1283,7 +1294,7 @@ namespace ts {
|
||||
args[i] = arguments[i];
|
||||
}
|
||||
|
||||
return t => reduceLeft<(t: T) => T, T>(args, (u, f) => f(u), t);
|
||||
return t => reduceLeft(args, (u, f) => f(u), t);
|
||||
}
|
||||
else if (d) {
|
||||
return t => d(c(b(a(t))));
|
||||
@@ -1604,18 +1615,10 @@ namespace ts {
|
||||
return path && !isRootedDiskPath(path) && path.indexOf("://") !== -1;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function pathIsRelative(path: string): boolean {
|
||||
return /^\.\.?($|[\\/])/.test(path);
|
||||
}
|
||||
|
||||
export function isExternalModuleNameRelative(moduleName: string): boolean {
|
||||
// TypeScript 1.0 spec (April 2014): 11.2.1
|
||||
// An external module name is "relative" if the first term is "." or "..".
|
||||
// Update: We also consider a path like `C:\foo.ts` "relative" because we do not search for it in `node_modules` or treat it as an ambient module.
|
||||
return pathIsRelative(moduleName) || isRootedDiskPath(moduleName);
|
||||
}
|
||||
|
||||
/** @deprecated Use `!isExternalModuleNameRelative(moduleName)` instead. */
|
||||
export function moduleHasNonRelativeName(moduleName: string): boolean {
|
||||
return !isExternalModuleNameRelative(moduleName);
|
||||
@@ -1639,7 +1642,6 @@ namespace ts {
|
||||
return moduleResolution;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
|
||||
let seenAsterisk = false;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
@@ -1657,7 +1659,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function isRootedDiskPath(path: string) {
|
||||
return getRootLength(path) !== 0;
|
||||
return path && getRootLength(path) !== 0;
|
||||
}
|
||||
|
||||
export function convertToRelativePath(absoluteOrRelativePath: string, basePath: string, getCanonicalFileName: (path: string) => string): string {
|
||||
@@ -1864,17 +1866,14 @@ namespace ts {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function startsWith(str: string, prefix: string): boolean {
|
||||
return str.lastIndexOf(prefix, 0) === 0;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function removePrefix(str: string, prefix: string): string {
|
||||
return startsWith(str, prefix) ? str.substr(prefix.length) : str;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function endsWith(str: string, suffix: string): boolean {
|
||||
const expectedPos = str.length - suffix.length;
|
||||
return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos;
|
||||
@@ -1888,7 +1887,6 @@ namespace ts {
|
||||
return path.length > extension.length && endsWith(path, extension);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function fileExtensionIsOneOf(path: string, extensions: ReadonlyArray<string>): boolean {
|
||||
for (const extension of extensions) {
|
||||
if (fileExtensionIs(path, extension)) {
|
||||
@@ -1905,7 +1903,6 @@ namespace ts {
|
||||
const reservedCharacterPattern = /[^\w\s\/]/g;
|
||||
const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question];
|
||||
|
||||
/* @internal */
|
||||
export const commonPackageFolders: ReadonlyArray<string> = ["node_modules", "bower_components", "jspm_packages"];
|
||||
|
||||
const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`;
|
||||
@@ -2447,7 +2444,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function fail(message?: string, stackCrawlMark?: Function): void {
|
||||
export function fail(message?: string, stackCrawlMark?: Function): never {
|
||||
debugger;
|
||||
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
|
||||
if ((<any>Error).captureStackTrace) {
|
||||
@@ -2456,6 +2453,10 @@ namespace ts {
|
||||
throw e;
|
||||
}
|
||||
|
||||
export function assertNever(member: never, message?: string, stackCrawlMark?: Function): never {
|
||||
return fail(message || `Illegal value: ${member}`, stackCrawlMark || assertNever);
|
||||
}
|
||||
|
||||
export function getFunctionName(func: Function) {
|
||||
if (typeof func !== "function") {
|
||||
return "";
|
||||
@@ -2523,7 +2524,6 @@ namespace ts {
|
||||
* Return an exact match if possible, or a pattern match, or undefined.
|
||||
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
|
||||
*/
|
||||
/* @internal */
|
||||
export function matchPatternOrExact(patternStrings: ReadonlyArray<string>, candidate: string): string | Pattern | undefined {
|
||||
const patterns: Pattern[] = [];
|
||||
for (const patternString of patternStrings) {
|
||||
@@ -2540,7 +2540,6 @@ namespace ts {
|
||||
return findBestPatternMatch(patterns, _ => _, candidate);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function patternText({prefix, suffix}: Pattern): string {
|
||||
return `${prefix}*${suffix}`;
|
||||
}
|
||||
@@ -2549,14 +2548,12 @@ namespace ts {
|
||||
* Given that candidate matches pattern, returns the text matching the '*'.
|
||||
* E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar"
|
||||
*/
|
||||
/* @internal */
|
||||
export function matchedText(pattern: Pattern, candidate: string): string {
|
||||
Debug.assert(isPatternMatch(pattern, candidate));
|
||||
return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length);
|
||||
}
|
||||
|
||||
/** Return the object corresponding to the best pattern to match `candidate`. */
|
||||
/* @internal */
|
||||
export function findBestPatternMatch<T>(values: ReadonlyArray<T>, getPattern: (value: T) => Pattern, candidate: string): T | undefined {
|
||||
let matchedValue: T | undefined = undefined;
|
||||
// use length of prefix as betterness criteria
|
||||
@@ -2579,7 +2576,6 @@ namespace ts {
|
||||
endsWith(candidate, suffix);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function tryParsePattern(pattern: string): Pattern | undefined {
|
||||
// This should be verified outside of here and a proper error thrown.
|
||||
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
|
||||
@@ -2628,4 +2624,6 @@ namespace ts {
|
||||
export function and<T>(f: (arg: T) => boolean, g: (arg: T) => boolean) {
|
||||
return (arg: T) => f(arg) && g(arg);
|
||||
}
|
||||
|
||||
export function assertTypeIsNever(_: never): void {}
|
||||
}
|
||||
|
||||
@@ -627,11 +627,11 @@
|
||||
"category": "Error",
|
||||
"code": 1200
|
||||
},
|
||||
"Import assignment cannot be used when targeting ECMAScript 2015 modules. Consider using 'import * as ns from \"mod\"', 'import {a} from \"mod\"', 'import d from \"mod\"', or another module format instead.": {
|
||||
"Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from \"mod\"', 'import {a} from \"mod\"', 'import d from \"mod\"', or another module format instead.": {
|
||||
"category": "Error",
|
||||
"code": 1202
|
||||
},
|
||||
"Export assignment cannot be used when targeting ECMAScript 2015 modules. Consider using 'export default' or another module format instead.": {
|
||||
"Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.": {
|
||||
"category": "Error",
|
||||
"code": 1203
|
||||
},
|
||||
@@ -1920,6 +1920,10 @@
|
||||
"category": "Error",
|
||||
"code": 2562
|
||||
},
|
||||
"The containing function or module body is too large for control flow analysis.": {
|
||||
"category": "Error",
|
||||
"code": 2563
|
||||
},
|
||||
"JSX element attributes type '{0}' may not be a union type.": {
|
||||
"category": "Error",
|
||||
"code": 2600
|
||||
@@ -2208,6 +2212,10 @@
|
||||
"category": "Error",
|
||||
"code": 2713
|
||||
},
|
||||
"The expression of an export assignment must be an identifier or qualified name in an ambient context.": {
|
||||
"category": "Error",
|
||||
"code": 2714
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
@@ -2678,7 +2686,7 @@
|
||||
"category": "Message",
|
||||
"code": 6015
|
||||
},
|
||||
"Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": {
|
||||
"Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": {
|
||||
"category": "Message",
|
||||
"code": 6016
|
||||
},
|
||||
@@ -3098,7 +3106,7 @@
|
||||
"category": "Message",
|
||||
"code": 6132
|
||||
},
|
||||
"'{0}' is declared but never used.": {
|
||||
"'{0}' is declared but its value is never read.": {
|
||||
"category": "Error",
|
||||
"code": 6133
|
||||
},
|
||||
@@ -3118,7 +3126,7 @@
|
||||
"category": "Error",
|
||||
"code": 6137
|
||||
},
|
||||
"Property '{0}' is declared but never used.": {
|
||||
"Property '{0}' is declared but its value is never read.": {
|
||||
"category": "Error",
|
||||
"code": 6138
|
||||
},
|
||||
@@ -3138,10 +3146,6 @@
|
||||
"category": "Error",
|
||||
"code": 6142
|
||||
},
|
||||
"Module '{0}' was resolved to '{1}', but '--allowJs' is not set.": {
|
||||
"category": "Error",
|
||||
"code": 6143
|
||||
},
|
||||
"Module '{0}' was resolved as locally declared ambient module in file '{1}'.": {
|
||||
"category": "Message",
|
||||
"code": 6144
|
||||
@@ -3696,7 +3700,7 @@
|
||||
"code": 95003
|
||||
},
|
||||
|
||||
"Extract function into {0}": {
|
||||
"Extract to {0}": {
|
||||
"category": "Message",
|
||||
"code": 95004
|
||||
}
|
||||
|
||||
+86
-98
@@ -406,6 +406,14 @@ namespace ts {
|
||||
setWriter(/*output*/ undefined);
|
||||
}
|
||||
|
||||
// TODO: Should this just be `emit`?
|
||||
// See https://github.com/Microsoft/TypeScript/pull/18284#discussion_r137611034
|
||||
function emitIfPresent(node: Node | undefined) {
|
||||
if (node) {
|
||||
emit(node);
|
||||
}
|
||||
}
|
||||
|
||||
function emit(node: Node) {
|
||||
pipelineEmitWithNotification(EmitHint.Unspecified, node);
|
||||
}
|
||||
@@ -451,6 +459,7 @@ namespace ts {
|
||||
case EmitHint.SourceFile: return pipelineEmitSourceFile(node);
|
||||
case EmitHint.IdentifierName: return pipelineEmitIdentifierName(node);
|
||||
case EmitHint.Expression: return pipelineEmitExpression(node);
|
||||
case EmitHint.MappedTypeParameter: return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration));
|
||||
case EmitHint.Unspecified: return pipelineEmitUnspecified(node);
|
||||
}
|
||||
}
|
||||
@@ -465,6 +474,12 @@ namespace ts {
|
||||
emitIdentifier(<Identifier>node);
|
||||
}
|
||||
|
||||
function emitMappedTypeParameter(node: TypeParameterDeclaration): void {
|
||||
emit(node.name);
|
||||
write(" in ");
|
||||
emit(node.constraint);
|
||||
}
|
||||
|
||||
function pipelineEmitUnspecified(node: Node): void {
|
||||
const kind = node.kind;
|
||||
|
||||
@@ -898,9 +913,9 @@ namespace ts {
|
||||
function emitParameter(node: ParameterDeclaration) {
|
||||
emitDecorators(node, node.decorators);
|
||||
emitModifiers(node, node.modifiers);
|
||||
writeIfPresent(node.dotDotDotToken, "...");
|
||||
emitIfPresent(node.dotDotDotToken);
|
||||
emit(node.name);
|
||||
writeIfPresent(node.questionToken, "?");
|
||||
emitIfPresent(node.questionToken);
|
||||
emitWithPrefix(": ", node.type);
|
||||
emitExpressionWithPrefix(" = ", node.initializer);
|
||||
}
|
||||
@@ -918,7 +933,7 @@ namespace ts {
|
||||
emitDecorators(node, node.decorators);
|
||||
emitModifiers(node, node.modifiers);
|
||||
emit(node.name);
|
||||
writeIfPresent(node.questionToken, "?");
|
||||
emitIfPresent(node.questionToken);
|
||||
emitWithPrefix(": ", node.type);
|
||||
write(";");
|
||||
}
|
||||
@@ -927,7 +942,7 @@ namespace ts {
|
||||
emitDecorators(node, node.decorators);
|
||||
emitModifiers(node, node.modifiers);
|
||||
emit(node.name);
|
||||
writeIfPresent(node.questionToken, "?");
|
||||
emitIfPresent(node.questionToken);
|
||||
emitWithPrefix(": ", node.type);
|
||||
emitExpressionWithPrefix(" = ", node.initializer);
|
||||
write(";");
|
||||
@@ -937,7 +952,7 @@ namespace ts {
|
||||
emitDecorators(node, node.decorators);
|
||||
emitModifiers(node, node.modifiers);
|
||||
emit(node.name);
|
||||
writeIfPresent(node.questionToken, "?");
|
||||
emitIfPresent(node.questionToken);
|
||||
emitTypeParameters(node, node.typeParameters);
|
||||
emitParameters(node, node.parameters);
|
||||
emitWithPrefix(": ", node.type);
|
||||
@@ -947,9 +962,9 @@ namespace ts {
|
||||
function emitMethodDeclaration(node: MethodDeclaration) {
|
||||
emitDecorators(node, node.decorators);
|
||||
emitModifiers(node, node.modifiers);
|
||||
writeIfPresent(node.asteriskToken, "*");
|
||||
emitIfPresent(node.asteriskToken);
|
||||
emit(node.name);
|
||||
writeIfPresent(node.questionToken, "?");
|
||||
emitIfPresent(node.questionToken);
|
||||
emitSignatureAndBody(node, emitSignatureHead);
|
||||
}
|
||||
|
||||
@@ -1035,10 +1050,8 @@ namespace ts {
|
||||
|
||||
function emitTypeLiteral(node: TypeLiteralNode) {
|
||||
write("{");
|
||||
// If the literal is empty, do not add spaces between braces.
|
||||
if (node.members.length > 0) {
|
||||
emitList(node, node.members, getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers);
|
||||
}
|
||||
const flags = getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers;
|
||||
emitList(node, node.members, flags | ListFormat.NoSpaceIfEmpty);
|
||||
write("}");
|
||||
}
|
||||
|
||||
@@ -1094,13 +1107,16 @@ namespace ts {
|
||||
writeLine();
|
||||
increaseIndent();
|
||||
}
|
||||
writeIfPresent(node.readonlyToken, "readonly ");
|
||||
if (node.readonlyToken) {
|
||||
emit(node.readonlyToken);
|
||||
write(" ");
|
||||
}
|
||||
|
||||
write("[");
|
||||
emit(node.typeParameter.name);
|
||||
write(" in ");
|
||||
emit(node.typeParameter.constraint);
|
||||
pipelineEmitWithNotification(EmitHint.MappedTypeParameter, node.typeParameter);
|
||||
write("]");
|
||||
writeIfPresent(node.questionToken, "?");
|
||||
|
||||
emitIfPresent(node.questionToken);
|
||||
write(": ");
|
||||
emit(node.type);
|
||||
write(";");
|
||||
@@ -1148,7 +1164,7 @@ namespace ts {
|
||||
|
||||
function emitBindingElement(node: BindingElement) {
|
||||
emitWithSuffix(node.propertyName, ": ");
|
||||
writeIfPresent(node.dotDotDotToken, "...");
|
||||
emitIfPresent(node.dotDotDotToken);
|
||||
emit(node.name);
|
||||
emitExpressionWithPrefix(" = ", node.initializer);
|
||||
}
|
||||
@@ -1159,33 +1175,22 @@ namespace ts {
|
||||
|
||||
function emitArrayLiteralExpression(node: ArrayLiteralExpression) {
|
||||
const elements = node.elements;
|
||||
if (elements.length === 0) {
|
||||
write("[]");
|
||||
}
|
||||
else {
|
||||
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
|
||||
emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine);
|
||||
}
|
||||
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
|
||||
emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine);
|
||||
}
|
||||
|
||||
function emitObjectLiteralExpression(node: ObjectLiteralExpression) {
|
||||
const properties = node.properties;
|
||||
if (properties.length === 0) {
|
||||
write("{}");
|
||||
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
|
||||
if (indentedFlag) {
|
||||
increaseIndent();
|
||||
}
|
||||
else {
|
||||
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
|
||||
if (indentedFlag) {
|
||||
increaseIndent();
|
||||
}
|
||||
|
||||
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
|
||||
const allowTrailingComma = currentSourceFile.languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
|
||||
emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
|
||||
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
|
||||
const allowTrailingComma = currentSourceFile.languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
|
||||
emitList(node, node.properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
|
||||
|
||||
if (indentedFlag) {
|
||||
decreaseIndent();
|
||||
}
|
||||
if (indentedFlag) {
|
||||
decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1286,7 +1291,8 @@ namespace ts {
|
||||
emitTypeParameters(node, node.typeParameters);
|
||||
emitParametersForArrow(node, node.parameters);
|
||||
emitWithPrefix(": ", node.type);
|
||||
write(" =>");
|
||||
write(" ");
|
||||
emit(node.equalsGreaterThanToken);
|
||||
}
|
||||
|
||||
function emitDeleteExpression(node: DeleteExpression) {
|
||||
@@ -1364,13 +1370,13 @@ namespace ts {
|
||||
|
||||
emitExpression(node.condition);
|
||||
increaseIndentIf(indentBeforeQuestion, " ");
|
||||
write("?");
|
||||
emit(node.questionToken);
|
||||
increaseIndentIf(indentAfterQuestion, " ");
|
||||
emitExpression(node.whenTrue);
|
||||
decreaseIndentIf(indentBeforeQuestion, indentAfterQuestion);
|
||||
|
||||
increaseIndentIf(indentBeforeColon, " ");
|
||||
write(":");
|
||||
emit(node.colonToken);
|
||||
increaseIndentIf(indentAfterColon, " ");
|
||||
emitExpression(node.whenFalse);
|
||||
decreaseIndentIf(indentBeforeColon, indentAfterColon);
|
||||
@@ -1382,7 +1388,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function emitYieldExpression(node: YieldExpression) {
|
||||
write(node.asteriskToken ? "yield*" : "yield");
|
||||
write("yield");
|
||||
emit(node.asteriskToken);
|
||||
emitExpressionWithPrefix(" ", node.expression);
|
||||
}
|
||||
|
||||
@@ -1433,29 +1440,18 @@ namespace ts {
|
||||
//
|
||||
|
||||
function emitBlock(node: Block) {
|
||||
if (isSingleLineEmptyBlock(node)) {
|
||||
writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node);
|
||||
write(" ");
|
||||
writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node);
|
||||
}
|
||||
else {
|
||||
writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node);
|
||||
emitBlockStatements(node);
|
||||
// We have to call emitLeadingComments explicitly here because otherwise leading comments of the close brace token will not be emitted
|
||||
increaseIndent();
|
||||
emitLeadingCommentsOfPosition(node.statements.end);
|
||||
decreaseIndent();
|
||||
writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node);
|
||||
}
|
||||
writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node);
|
||||
emitBlockStatements(node, /*forceSingleLine*/ !node.multiLine && isEmptyBlock(node));
|
||||
// We have to call emitLeadingComments explicitly here because otherwise leading comments of the close brace token will not be emitted
|
||||
increaseIndent();
|
||||
emitLeadingCommentsOfPosition(node.statements.end);
|
||||
decreaseIndent();
|
||||
writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node);
|
||||
}
|
||||
|
||||
function emitBlockStatements(node: BlockLike) {
|
||||
if (getEmitFlags(node) & EmitFlags.SingleLine) {
|
||||
emitList(node, node.statements, ListFormat.SingleLineBlockStatements);
|
||||
}
|
||||
else {
|
||||
emitList(node, node.statements, ListFormat.MultiLineBlockStatements);
|
||||
}
|
||||
function emitBlockStatements(node: BlockLike, forceSingleLine: boolean) {
|
||||
const format = forceSingleLine || getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineBlockStatements : ListFormat.MultiLineBlockStatements;
|
||||
emitList(node, node.statements, format);
|
||||
}
|
||||
|
||||
function emitVariableStatement(node: VariableStatement) {
|
||||
@@ -1662,7 +1658,9 @@ namespace ts {
|
||||
function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) {
|
||||
emitDecorators(node, node.decorators);
|
||||
emitModifiers(node, node.modifiers);
|
||||
write(node.asteriskToken ? "function* " : "function ");
|
||||
write("function");
|
||||
emitIfPresent(node.asteriskToken);
|
||||
write(" ");
|
||||
emitIdentifierName(node.name);
|
||||
emitSignatureAndBody(node, emitSignatureHead);
|
||||
}
|
||||
@@ -1880,16 +1878,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function emitModuleBlock(node: ModuleBlock) {
|
||||
if (isEmptyBlock(node)) {
|
||||
write("{ }");
|
||||
}
|
||||
else {
|
||||
pushNameGenerationScope();
|
||||
write("{");
|
||||
emitBlockStatements(node);
|
||||
write("}");
|
||||
popNameGenerationScope();
|
||||
}
|
||||
pushNameGenerationScope();
|
||||
write("{");
|
||||
emitBlockStatements(node, /*forceSingleLine*/ isEmptyBlock(node));
|
||||
write("}");
|
||||
popNameGenerationScope();
|
||||
}
|
||||
|
||||
function emitCaseBlock(node: CaseBlock) {
|
||||
@@ -2068,9 +2061,7 @@ namespace ts {
|
||||
function emitJsxExpression(node: JsxExpression) {
|
||||
if (node.expression) {
|
||||
write("{");
|
||||
if (node.dotDotDotToken) {
|
||||
write("...");
|
||||
}
|
||||
emitIfPresent(node.dotDotDotToken);
|
||||
emitExpression(node.expression);
|
||||
write("}");
|
||||
}
|
||||
@@ -2128,13 +2119,12 @@ namespace ts {
|
||||
emitTrailingCommentsOfPosition(statements.pos);
|
||||
}
|
||||
|
||||
let format = ListFormat.CaseOrDefaultClauseStatements;
|
||||
if (emitAsSingleStatement) {
|
||||
write(" ");
|
||||
emit(statements[0]);
|
||||
}
|
||||
else {
|
||||
emitList(parentNode, statements, ListFormat.CaseOrDefaultClauseStatements);
|
||||
format &= ~(ListFormat.MultiLine | ListFormat.Indented);
|
||||
}
|
||||
emitList(parentNode, statements, format);
|
||||
}
|
||||
|
||||
function emitHeritageClause(node: HeritageClause) {
|
||||
@@ -2384,7 +2374,7 @@ namespace ts {
|
||||
|
||||
function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray<ParameterDeclaration>) {
|
||||
if (canEmitSimpleArrowHead(parentNode, parameters)) {
|
||||
emit(parameters[0]);
|
||||
emitList(parentNode, parameters, ListFormat.Parameters & ~ListFormat.Parenthesis);
|
||||
}
|
||||
else {
|
||||
emitParameters(parentNode, parameters);
|
||||
@@ -2409,8 +2399,14 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
|
||||
const isEmpty = isUndefined || children.length === 0 || start >= children.length || count === 0;
|
||||
const isEmpty = isUndefined || start >= children.length || count === 0;
|
||||
if (isEmpty && format & ListFormat.OptionalIfEmpty) {
|
||||
if (onBeforeEmitNodeArray) {
|
||||
onBeforeEmitNodeArray(children);
|
||||
}
|
||||
if (onAfterEmitNodeArray) {
|
||||
onAfterEmitNodeArray(children);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2427,7 +2423,7 @@ namespace ts {
|
||||
if (format & ListFormat.MultiLine) {
|
||||
writeLine();
|
||||
}
|
||||
else if (format & ListFormat.SpaceBetweenBraces) {
|
||||
else if (format & ListFormat.SpaceBetweenBraces && !(format & ListFormat.NoSpaceIfEmpty)) {
|
||||
write(" ");
|
||||
}
|
||||
}
|
||||
@@ -2519,7 +2515,7 @@ namespace ts {
|
||||
// 2
|
||||
// /* end of element 2 */
|
||||
// ];
|
||||
if (previousSibling && delimiter && previousSibling.end !== parentNode.end) {
|
||||
if (previousSibling && delimiter && previousSibling.end !== parentNode.end && !(getEmitFlags(previousSibling) & EmitFlags.NoTrailingComments)) {
|
||||
emitLeadingCommentsOfPosition(previousSibling.end);
|
||||
}
|
||||
|
||||
@@ -2568,12 +2564,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function writeIfPresent(node: Node, text: string) {
|
||||
if (node) {
|
||||
write(text);
|
||||
}
|
||||
}
|
||||
|
||||
function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) {
|
||||
return onEmitSourceMapOfToken
|
||||
? onEmitSourceMapOfToken(contextNode, token, pos, writeTokenText)
|
||||
@@ -2584,7 +2574,7 @@ namespace ts {
|
||||
if (onBeforeEmitToken) {
|
||||
onBeforeEmitToken(node);
|
||||
}
|
||||
writeTokenText(node.kind);
|
||||
write(tokenToString(node.kind));
|
||||
if (onAfterEmitToken) {
|
||||
onAfterEmitToken(node);
|
||||
}
|
||||
@@ -2756,11 +2746,6 @@ namespace ts {
|
||||
&& !rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile);
|
||||
}
|
||||
|
||||
function isSingleLineEmptyBlock(block: Block) {
|
||||
return !block.multiLine
|
||||
&& isEmptyBlock(block);
|
||||
}
|
||||
|
||||
function isEmptyBlock(block: BlockLike) {
|
||||
return block.statements.length === 0
|
||||
&& rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile);
|
||||
@@ -3107,6 +3092,9 @@ namespace ts {
|
||||
NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list.
|
||||
NoInterveningComments = 1 << 17, // Do not emit comments between each node
|
||||
|
||||
NoSpaceIfEmpty = 1 << 18, // If the literal is empty, do not add spaces between braces.
|
||||
SingleElement = 1 << 19,
|
||||
|
||||
// Precomputed Formats
|
||||
Modifiers = SingleLine | SpaceBetweenSiblings | NoInterveningComments,
|
||||
HeritageClauses = SingleLine | SpaceBetweenSiblings,
|
||||
@@ -3118,7 +3106,7 @@ namespace ts {
|
||||
IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine,
|
||||
ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings,
|
||||
ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings,
|
||||
ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces,
|
||||
ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces | NoSpaceIfEmpty,
|
||||
ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets,
|
||||
CommaListElements = CommaDelimited | SpaceBetweenSiblings | SingleLine,
|
||||
CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis,
|
||||
|
||||
+63
-13
@@ -281,7 +281,7 @@ namespace ts {
|
||||
|| node.questionToken !== questionToken
|
||||
|| node.type !== type
|
||||
|| node.initializer !== initializer
|
||||
? updateNode(createParameter(decorators, modifiers, dotDotDotToken, name, node.questionToken, type, initializer), node)
|
||||
? updateNode(createParameter(decorators, modifiers, dotDotDotToken, name, questionToken, type, initializer), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
@@ -773,13 +773,13 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createLiteralTypeNode(literal: Expression) {
|
||||
export function createLiteralTypeNode(literal: LiteralTypeNode["literal"]) {
|
||||
const node = createSynthesizedNode(SyntaxKind.LiteralType) as LiteralTypeNode;
|
||||
node.literal = literal;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateLiteralTypeNode(node: LiteralTypeNode, literal: Expression) {
|
||||
export function updateLiteralTypeNode(node: LiteralTypeNode, literal: LiteralTypeNode["literal"]) {
|
||||
return node.literal !== literal
|
||||
? updateNode(createLiteralTypeNode(literal), node)
|
||||
: node;
|
||||
@@ -1016,19 +1016,49 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
/* @deprecated */ export function updateArrowFunction(
|
||||
node: ArrowFunction,
|
||||
modifiers: ReadonlyArray<Modifier> | undefined,
|
||||
typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined,
|
||||
parameters: ReadonlyArray<ParameterDeclaration>,
|
||||
type: TypeNode | undefined,
|
||||
body: ConciseBody): ArrowFunction;
|
||||
export function updateArrowFunction(
|
||||
node: ArrowFunction,
|
||||
modifiers: ReadonlyArray<Modifier> | undefined,
|
||||
typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined,
|
||||
parameters: ReadonlyArray<ParameterDeclaration>,
|
||||
type: TypeNode | undefined,
|
||||
body: ConciseBody) {
|
||||
equalsGreaterThanToken: Token<SyntaxKind.EqualsGreaterThanToken>,
|
||||
body: ConciseBody): ArrowFunction;
|
||||
export function updateArrowFunction(
|
||||
node: ArrowFunction,
|
||||
modifiers: ReadonlyArray<Modifier> | undefined,
|
||||
typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined,
|
||||
parameters: ReadonlyArray<ParameterDeclaration>,
|
||||
type: TypeNode | undefined,
|
||||
equalsGreaterThanTokenOrBody: Token<SyntaxKind.EqualsGreaterThanToken> | ConciseBody,
|
||||
bodyOrUndefined?: ConciseBody,
|
||||
): ArrowFunction {
|
||||
let equalsGreaterThanToken: Token<SyntaxKind.EqualsGreaterThanToken>;
|
||||
let body: ConciseBody;
|
||||
if (bodyOrUndefined === undefined) {
|
||||
equalsGreaterThanToken = node.equalsGreaterThanToken;
|
||||
body = cast(equalsGreaterThanTokenOrBody, isConciseBody);
|
||||
}
|
||||
else {
|
||||
equalsGreaterThanToken = cast(equalsGreaterThanTokenOrBody, (n): n is Token<SyntaxKind.EqualsGreaterThanToken> =>
|
||||
n.kind === SyntaxKind.EqualsGreaterThanToken);
|
||||
body = bodyOrUndefined;
|
||||
}
|
||||
|
||||
return node.modifiers !== modifiers
|
||||
|| node.typeParameters !== typeParameters
|
||||
|| node.parameters !== parameters
|
||||
|| node.type !== type
|
||||
|| node.equalsGreaterThanToken !== equalsGreaterThanToken
|
||||
|| node.body !== body
|
||||
? updateNode(createArrowFunction(modifiers, typeParameters, parameters, type, node.equalsGreaterThanToken, body), node)
|
||||
? updateNode(createArrowFunction(modifiers, typeParameters, parameters, type, equalsGreaterThanToken, body), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
@@ -1135,11 +1165,31 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateConditional(node: ConditionalExpression, condition: Expression, whenTrue: Expression, whenFalse: Expression) {
|
||||
/* @deprecated */ export function updateConditional(
|
||||
node: ConditionalExpression,
|
||||
condition: Expression,
|
||||
whenTrue: Expression,
|
||||
whenFalse: Expression): ConditionalExpression;
|
||||
export function updateConditional(
|
||||
node: ConditionalExpression,
|
||||
condition: Expression,
|
||||
questionToken: Token<SyntaxKind.QuestionToken>,
|
||||
whenTrue: Expression,
|
||||
colonToken: Token<SyntaxKind.ColonToken>,
|
||||
whenFalse: Expression): ConditionalExpression;
|
||||
export function updateConditional(node: ConditionalExpression, condition: Expression, ...args: any[]) {
|
||||
if (args.length === 2) {
|
||||
const [whenTrue, whenFalse] = args;
|
||||
return updateConditional(node, condition, node.questionToken, whenTrue, node.colonToken, whenFalse);
|
||||
}
|
||||
Debug.assert(args.length === 4);
|
||||
const [questionToken, whenTrue, colonToken, whenFalse] = args;
|
||||
return node.condition !== condition
|
||||
|| node.questionToken !== questionToken
|
||||
|| node.whenTrue !== whenTrue
|
||||
|| node.colonToken !== colonToken
|
||||
|| node.whenFalse !== whenFalse
|
||||
? updateNode(createConditional(condition, node.questionToken, whenTrue, node.colonToken, whenFalse), node)
|
||||
? updateNode(createConditional(condition, questionToken, whenTrue, colonToken, whenFalse), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
@@ -3891,11 +3941,10 @@ namespace ts {
|
||||
return recreateOuterExpressions(expression, mutableCall, OuterExpressionKinds.PartiallyEmittedExpressions);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const leftmostExpressionKind = getLeftmostExpression(emittedExpression, /*stopAtCallExpressions*/ false).kind;
|
||||
if (leftmostExpressionKind === SyntaxKind.ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind.FunctionExpression) {
|
||||
return setTextRange(createParen(expression), expression);
|
||||
}
|
||||
|
||||
const leftmostExpressionKind = getLeftmostExpression(emittedExpression, /*stopAtCallExpressions*/ false).kind;
|
||||
if (leftmostExpressionKind === SyntaxKind.ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind.FunctionExpression) {
|
||||
return setTextRange(createParen(expression), expression);
|
||||
}
|
||||
|
||||
return expression;
|
||||
@@ -4113,7 +4162,8 @@ namespace ts {
|
||||
const moduleKind = getEmitModuleKind(compilerOptions);
|
||||
let create = hasExportStarsToExportValues
|
||||
&& moduleKind !== ModuleKind.System
|
||||
&& moduleKind !== ModuleKind.ES2015;
|
||||
&& moduleKind !== ModuleKind.ES2015
|
||||
&& moduleKind !== ModuleKind.ESNext;
|
||||
if (!create) {
|
||||
const helpers = getEmitHelpers(node);
|
||||
if (helpers) {
|
||||
|
||||
@@ -51,13 +51,17 @@ namespace ts {
|
||||
DtsOnly /** Only '.d.ts' */
|
||||
}
|
||||
|
||||
interface PathAndPackageId {
|
||||
readonly fileName: string;
|
||||
readonly packageId: PackageId;
|
||||
}
|
||||
/** Used with `Extensions.DtsOnly` to extract the path from TypeScript results. */
|
||||
function resolvedTypeScriptOnly(resolved: Resolved | undefined): string | undefined {
|
||||
function resolvedTypeScriptOnly(resolved: Resolved | undefined): PathAndPackageId | undefined {
|
||||
if (!resolved) {
|
||||
return undefined;
|
||||
}
|
||||
Debug.assert(extensionIsTypeScript(resolved.extension));
|
||||
return resolved.path;
|
||||
return { fileName: resolved.path, packageId: resolved.packageId };
|
||||
}
|
||||
|
||||
function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
|
||||
@@ -201,18 +205,18 @@ namespace ts {
|
||||
let resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined;
|
||||
if (resolved) {
|
||||
if (!options.preserveSymlinks) {
|
||||
resolved = realPath(resolved, host, traceEnabled);
|
||||
resolved = { ...resolved, fileName: realPath(resolved.fileName, host, traceEnabled) };
|
||||
}
|
||||
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved, primary);
|
||||
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved.fileName, primary);
|
||||
}
|
||||
resolvedTypeReferenceDirective = { primary, resolvedFileName: resolved };
|
||||
resolvedTypeReferenceDirective = { primary, resolvedFileName: resolved.fileName, packageId: resolved.packageId };
|
||||
}
|
||||
|
||||
return { resolvedTypeReferenceDirective, failedLookupLocations };
|
||||
|
||||
function primaryLookup(): string | undefined {
|
||||
function primaryLookup(): PathAndPackageId | undefined {
|
||||
// Check primary library paths
|
||||
if (typeRoots && typeRoots.length) {
|
||||
if (traceEnabled) {
|
||||
@@ -237,8 +241,8 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function secondaryLookup(): string | undefined {
|
||||
let resolvedFile: string;
|
||||
function secondaryLookup(): PathAndPackageId | undefined {
|
||||
let resolvedFile: PathAndPackageId;
|
||||
const initialLocationForSecondaryLookup = containingFile && getDirectoryPath(containingFile);
|
||||
|
||||
if (initialLocationForSecondaryLookup !== undefined) {
|
||||
@@ -675,7 +679,7 @@ namespace ts {
|
||||
if (extension !== undefined) {
|
||||
const path = tryFile(candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state);
|
||||
if (path !== undefined) {
|
||||
return { path, extension, packageId: undefined };
|
||||
return noPackageId({ path, ext: extension });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -875,38 +879,49 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true): Resolved | undefined {
|
||||
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
|
||||
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) {
|
||||
const { packageJsonContent, packageId } = considerPackageJson
|
||||
? getPackageJsonInfo(candidate, "", failedLookupLocations, onlyRecordFailures, state)
|
||||
: { packageJsonContent: undefined, packageId: undefined };
|
||||
return withPackageId(packageId, loadNodeModuleFromDirectoryWorker(extensions, candidate, failedLookupLocations, onlyRecordFailures, state, packageJsonContent));
|
||||
}
|
||||
|
||||
let packageId: PackageId | undefined;
|
||||
|
||||
if (considerPackageJson) {
|
||||
const packageJsonPath = pathToPackageJson(candidate);
|
||||
if (directoryExists && state.host.fileExists(packageJsonPath)) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
|
||||
}
|
||||
const jsonContent = readJson(packageJsonPath, state.host);
|
||||
|
||||
if (typeof jsonContent.name === "string" && typeof jsonContent.version === "string") {
|
||||
packageId = { name: jsonContent.name, version: jsonContent.version };
|
||||
}
|
||||
|
||||
const fromPackageJson = loadModuleFromPackageJson(jsonContent, extensions, candidate, failedLookupLocations, state);
|
||||
if (fromPackageJson) {
|
||||
return withPackageId(packageId, fromPackageJson);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (directoryExists && state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
function loadNodeModuleFromDirectoryWorker(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, packageJsonContent: PackageJson | undefined): PathAndExtension | undefined {
|
||||
const fromPackageJson = packageJsonContent && loadModuleFromPackageJson(packageJsonContent, extensions, candidate, failedLookupLocations, state);
|
||||
if (fromPackageJson) {
|
||||
return fromPackageJson;
|
||||
}
|
||||
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
|
||||
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
|
||||
}
|
||||
|
||||
return withPackageId(packageId, loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state));
|
||||
function getPackageJsonInfo(
|
||||
nodeModuleDirectory: string,
|
||||
subModuleName: string,
|
||||
failedLookupLocations: Push<string>,
|
||||
onlyRecordFailures: boolean,
|
||||
{ host, traceEnabled }: ModuleResolutionState,
|
||||
): { packageJsonContent: PackageJson | undefined, packageId: PackageId | undefined } {
|
||||
const directoryExists = !onlyRecordFailures && directoryProbablyExists(nodeModuleDirectory, host);
|
||||
const packageJsonPath = pathToPackageJson(nodeModuleDirectory);
|
||||
if (directoryExists && host.fileExists(packageJsonPath)) {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
|
||||
}
|
||||
const packageJsonContent = readJson(packageJsonPath, host);
|
||||
const packageId: PackageId = typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string"
|
||||
? { name: packageJsonContent.name, subModuleName, version: packageJsonContent.version }
|
||||
: undefined;
|
||||
return { packageJsonContent, packageId };
|
||||
}
|
||||
else {
|
||||
if (directoryExists && traceEnabled) {
|
||||
trace(host, Diagnostics.File_0_does_not_exist, packageJsonPath);
|
||||
}
|
||||
// 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 };
|
||||
}
|
||||
}
|
||||
|
||||
function loadModuleFromPackageJson(jsonContent: PackageJson, extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): PathAndExtension | undefined {
|
||||
@@ -961,10 +976,21 @@ 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));
|
||||
const pathAndExtension = loadModuleFromFile(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
|
||||
loadNodeModuleFromDirectoryWorker(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state, packageJsonContent);
|
||||
return withPackageId(packageId, pathAndExtension);
|
||||
}
|
||||
|
||||
return loadModuleFromFileNoPackageId(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
|
||||
loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
|
||||
function getPackageName(moduleName: string): { packageName: string, rest: string } {
|
||||
let idx = moduleName.indexOf(directorySeparator);
|
||||
if (moduleName[0] === "@") {
|
||||
idx = moduleName.indexOf(directorySeparator, idx + 1);
|
||||
}
|
||||
return idx === -1 ? { packageName: moduleName, rest: "" } : { packageName: moduleName.slice(0, idx), rest: moduleName.slice(idx + 1) };
|
||||
}
|
||||
|
||||
function loadModuleFromNodeModules(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache): SearchResult<Resolved> {
|
||||
|
||||
+96
-117
@@ -438,8 +438,10 @@ namespace ts {
|
||||
visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression);
|
||||
}
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags) {
|
||||
visitNode(cbNode, tag);
|
||||
if ((node as JSDocTypeLiteral).jsDocPropertyTags) {
|
||||
for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags) {
|
||||
visitNode(cbNode, tag);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case SyntaxKind.PartiallyEmittedExpression:
|
||||
@@ -729,7 +731,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
|
||||
function addJSDocComment<T extends Node>(node: T): T {
|
||||
function addJSDocComment<T extends HasJSDoc>(node: T): T {
|
||||
const comments = getJSDocCommentRanges(node, sourceFile.text);
|
||||
if (comments) {
|
||||
for (const comment of comments) {
|
||||
@@ -768,7 +770,7 @@ namespace ts {
|
||||
const saveParent = parent;
|
||||
parent = n;
|
||||
forEachChild(n, visitNode);
|
||||
if (n.jsDoc) {
|
||||
if (hasJSDocNodes(n)) {
|
||||
for (const jsDoc of n.jsDoc) {
|
||||
jsDoc.parent = n;
|
||||
parent = jsDoc;
|
||||
@@ -940,10 +942,6 @@ namespace ts {
|
||||
return scanner.getStartPos();
|
||||
}
|
||||
|
||||
function getNodeEnd(): number {
|
||||
return scanner.getStartPos();
|
||||
}
|
||||
|
||||
// Use this function to access the current token instead of reading the currentToken
|
||||
// variable. Since function results aren't narrowed in control flow analysis, this ensures
|
||||
// that the type checker doesn't make wrong assumptions about the type of the current
|
||||
@@ -1135,13 +1133,14 @@ namespace ts {
|
||||
new TokenConstructor(kind, pos, pos);
|
||||
}
|
||||
|
||||
function createNodeArray<T extends Node>(elements?: T[], pos?: number): MutableNodeArray<T> {
|
||||
const array = <MutableNodeArray<T>>(elements || []);
|
||||
if (!(pos >= 0)) {
|
||||
pos = getNodePos();
|
||||
}
|
||||
function createNodeArray<T extends Node>(elements: T[], pos: number, end?: number): NodeArray<T> {
|
||||
// Since the element list of a node array is typically created by starting with an empty array and
|
||||
// repeatedly calling push(), the list may not have the optimal memory layout. We invoke slice() for
|
||||
// small arrays (1 to 4 elements) to give the VM a chance to allocate an optimal representation.
|
||||
const length = elements.length;
|
||||
const array = <MutableNodeArray<T>>(length >= 1 && length <= 4 ? elements.slice() : elements);
|
||||
array.pos = pos;
|
||||
array.end = pos;
|
||||
array.end = end === undefined ? scanner.getStartPos() : end;
|
||||
return array;
|
||||
}
|
||||
|
||||
@@ -1208,7 +1207,10 @@ namespace ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
return createMissingNode<Identifier>(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, diagnosticMessage || Diagnostics.Identifier_expected);
|
||||
// Only for end of file because the error gets reported incorrectly on embedded script tags.
|
||||
const reportAtCurrentPosition = token() === SyntaxKind.EndOfFileToken;
|
||||
|
||||
return createMissingNode<Identifier>(SyntaxKind.Identifier, reportAtCurrentPosition, diagnosticMessage || Diagnostics.Identifier_expected);
|
||||
}
|
||||
|
||||
function parseIdentifier(diagnosticMessage?: DiagnosticMessage): Identifier {
|
||||
@@ -1527,12 +1529,13 @@ namespace ts {
|
||||
function parseList<T extends Node>(kind: ParsingContext, parseElement: () => T): NodeArray<T> {
|
||||
const saveParsingContext = parsingContext;
|
||||
parsingContext |= 1 << kind;
|
||||
const result = createNodeArray<T>();
|
||||
const list = [];
|
||||
const listPos = getNodePos();
|
||||
|
||||
while (!isListTerminator(kind)) {
|
||||
if (isListElement(kind, /*inErrorRecovery*/ false)) {
|
||||
const element = parseListElement(kind, parseElement);
|
||||
result.push(element);
|
||||
list.push(element);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -1542,9 +1545,8 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
result.end = getNodeEnd();
|
||||
parsingContext = saveParsingContext;
|
||||
return result;
|
||||
return createNodeArray(list, listPos);
|
||||
}
|
||||
|
||||
function parseListElement<T extends Node>(parsingContext: ParsingContext, parseElement: () => T): T {
|
||||
@@ -1874,13 +1876,14 @@ namespace ts {
|
||||
function parseDelimitedList<T extends Node>(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray<T> {
|
||||
const saveParsingContext = parsingContext;
|
||||
parsingContext |= 1 << kind;
|
||||
const result = createNodeArray<T>();
|
||||
const list = [];
|
||||
const listPos = getNodePos();
|
||||
|
||||
let commaStart = -1; // Meaning the previous token was not a comma
|
||||
while (true) {
|
||||
if (isListElement(kind, /*inErrorRecovery*/ false)) {
|
||||
const startPos = scanner.getStartPos();
|
||||
result.push(parseListElement(kind, parseElement));
|
||||
list.push(parseListElement(kind, parseElement));
|
||||
commaStart = scanner.getTokenPos();
|
||||
|
||||
if (parseOptional(SyntaxKind.CommaToken)) {
|
||||
@@ -1924,6 +1927,8 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
parsingContext = saveParsingContext;
|
||||
const result = createNodeArray(list, listPos);
|
||||
// Recording the trailing comma is deliberately done after the previous
|
||||
// loop, and not just if we see a list terminator. This is because the list
|
||||
// may have ended incorrectly, but it is still important to know if there
|
||||
@@ -1933,14 +1938,11 @@ namespace ts {
|
||||
// Always preserve a trailing comma by marking it on the NodeArray
|
||||
result.hasTrailingComma = true;
|
||||
}
|
||||
|
||||
result.end = getNodeEnd();
|
||||
parsingContext = saveParsingContext;
|
||||
return result;
|
||||
}
|
||||
|
||||
function createMissingList<T extends Node>(): NodeArray<T> {
|
||||
return createNodeArray<T>();
|
||||
return createNodeArray<T>([], getNodePos());
|
||||
}
|
||||
|
||||
function parseBracketedList<T extends Node>(kind: ParsingContext, parseElement: () => T, open: SyntaxKind, close: SyntaxKind): NodeArray<T> {
|
||||
@@ -2015,15 +2017,15 @@ namespace ts {
|
||||
template.head = parseTemplateHead();
|
||||
Debug.assert(template.head.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
|
||||
|
||||
const templateSpans = createNodeArray<TemplateSpan>();
|
||||
const list = [];
|
||||
const listPos = getNodePos();
|
||||
|
||||
do {
|
||||
templateSpans.push(parseTemplateSpan());
|
||||
list.push(parseTemplateSpan());
|
||||
}
|
||||
while (lastOrUndefined(templateSpans).literal.kind === SyntaxKind.TemplateMiddle);
|
||||
while (lastOrUndefined(list).literal.kind === SyntaxKind.TemplateMiddle);
|
||||
|
||||
templateSpans.end = getNodeEnd();
|
||||
template.templateSpans = templateSpans;
|
||||
template.templateSpans = createNodeArray(list, listPos);
|
||||
|
||||
return finishNode(template);
|
||||
}
|
||||
@@ -2158,7 +2160,7 @@ namespace ts {
|
||||
const result = <JSDocFunctionType>createNode(SyntaxKind.JSDocFunctionType);
|
||||
nextToken();
|
||||
fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type | SignatureFlags.JSDoc, result);
|
||||
return finishNode(result);
|
||||
return addJSDocComment(finishNode(result));
|
||||
}
|
||||
const node = <TypeReferenceNode>createNode(SyntaxKind.TypeReference);
|
||||
node.typeName = parseIdentifierName();
|
||||
@@ -2237,10 +2239,11 @@ namespace ts {
|
||||
return token() === SyntaxKind.DotDotDotToken ||
|
||||
isIdentifierOrPattern() ||
|
||||
isModifierKind(token()) ||
|
||||
token() === SyntaxKind.AtToken || isStartOfType();
|
||||
token() === SyntaxKind.AtToken ||
|
||||
isStartOfType(/*inStartOfParameter*/ true);
|
||||
}
|
||||
|
||||
function parseParameter(): ParameterDeclaration {
|
||||
function parseParameter(requireEqualsToken?: boolean): ParameterDeclaration {
|
||||
const node = <ParameterDeclaration>createNode(SyntaxKind.Parameter);
|
||||
if (token() === SyntaxKind.ThisKeyword) {
|
||||
node.name = createIdentifier(/*isIdentifier*/ true);
|
||||
@@ -2269,19 +2272,11 @@ namespace ts {
|
||||
|
||||
node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken);
|
||||
node.type = parseParameterType();
|
||||
node.initializer = parseBindingElementInitializer(/*inParameter*/ true);
|
||||
node.initializer = parseInitializer(/*inParameter*/ true, requireEqualsToken);
|
||||
|
||||
return addJSDocComment(finishNode(node));
|
||||
}
|
||||
|
||||
function parseBindingElementInitializer(inParameter: boolean) {
|
||||
return inParameter ? parseParameterInitializer() : parseNonParameterInitializer();
|
||||
}
|
||||
|
||||
function parseParameterInitializer() {
|
||||
return parseInitializer(/*inParameter*/ true);
|
||||
}
|
||||
|
||||
function fillSignature(
|
||||
returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken,
|
||||
flags: SignatureFlags,
|
||||
@@ -2334,7 +2329,8 @@ namespace ts {
|
||||
setYieldContext(!!(flags & SignatureFlags.Yield));
|
||||
setAwaitContext(!!(flags & SignatureFlags.Await));
|
||||
|
||||
const result = parseDelimitedList(ParsingContext.Parameters, flags & SignatureFlags.JSDoc ? parseJSDocParameter : parseParameter);
|
||||
const result = parseDelimitedList(ParsingContext.Parameters,
|
||||
flags & SignatureFlags.JSDoc ? parseJSDocParameter : () => parseParameter(!!(flags & SignatureFlags.RequireCompleteParameterList)));
|
||||
|
||||
setYieldContext(savedYieldContext);
|
||||
setAwaitContext(savedAwaitContext);
|
||||
@@ -2365,7 +2361,7 @@ namespace ts {
|
||||
parseSemicolon();
|
||||
}
|
||||
|
||||
function parseSignatureMember(kind: SyntaxKind): CallSignatureDeclaration | ConstructSignatureDeclaration {
|
||||
function parseSignatureMember(kind: SyntaxKind.CallSignature | SyntaxKind.ConstructSignature): CallSignatureDeclaration | ConstructSignatureDeclaration {
|
||||
const node = <CallSignatureDeclaration | ConstructSignatureDeclaration>createNode(kind);
|
||||
if (kind === SyntaxKind.ConstructSignature) {
|
||||
parseExpected(SyntaxKind.NewKeyword);
|
||||
@@ -2445,7 +2441,7 @@ namespace ts {
|
||||
node.parameters = parseBracketedList(ParsingContext.Parameters, parseParameter, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
|
||||
node.type = parseTypeAnnotation();
|
||||
parseTypeMemberSemicolon();
|
||||
return finishNode(node);
|
||||
return addJSDocComment(finishNode(node));
|
||||
}
|
||||
|
||||
function parsePropertyOrMethodSignature(fullStart: number, modifiers: NodeArray<Modifier>): PropertySignature | MethodSignature {
|
||||
@@ -2605,7 +2601,7 @@ namespace ts {
|
||||
parseExpected(SyntaxKind.NewKeyword);
|
||||
}
|
||||
fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type, node);
|
||||
return finishNode(node);
|
||||
return addJSDocComment(finishNode(node));
|
||||
}
|
||||
|
||||
function parseKeywordAndNoDot(): TypeNode | undefined {
|
||||
@@ -2621,16 +2617,9 @@ namespace ts {
|
||||
unaryMinusExpression.operator = SyntaxKind.MinusToken;
|
||||
nextToken();
|
||||
}
|
||||
let expression: UnaryExpression;
|
||||
switch (token()) {
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
expression = parseLiteralLikeNode(token()) as LiteralExpression;
|
||||
break;
|
||||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.FalseKeyword:
|
||||
expression = parseTokenNode();
|
||||
}
|
||||
let expression: BooleanLiteral | LiteralExpression | PrefixUnaryExpression = token() === SyntaxKind.TrueKeyword || token() === SyntaxKind.FalseKeyword
|
||||
? parseTokenNode<BooleanLiteral>()
|
||||
: parseLiteralLikeNode(token()) as LiteralExpression;
|
||||
if (negative) {
|
||||
unaryMinusExpression.operand = expression;
|
||||
finishNode(unaryMinusExpression);
|
||||
@@ -2666,6 +2655,7 @@ namespace ts {
|
||||
return parseJSDocNodeWithType(SyntaxKind.JSDocVariadicType);
|
||||
case SyntaxKind.ExclamationToken:
|
||||
return parseJSDocNodeWithType(SyntaxKind.JSDocNonNullableType);
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.TrueKeyword:
|
||||
@@ -2698,7 +2688,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function isStartOfType(): boolean {
|
||||
function isStartOfType(inStartOfParameter?: boolean): boolean {
|
||||
switch (token()) {
|
||||
case SyntaxKind.AnyKeyword:
|
||||
case SyntaxKind.StringKeyword:
|
||||
@@ -2728,11 +2718,11 @@ namespace ts {
|
||||
case SyntaxKind.DotDotDotToken:
|
||||
return true;
|
||||
case SyntaxKind.MinusToken:
|
||||
return lookAhead(nextTokenIsNumericLiteral);
|
||||
return !inStartOfParameter && lookAhead(nextTokenIsNumericLiteral);
|
||||
case SyntaxKind.OpenParenToken:
|
||||
// Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier,
|
||||
// or something that starts a type. We don't want to consider things like '(1)' a type.
|
||||
return lookAhead(isStartOfParenthesizedOrFunctionType);
|
||||
return !inStartOfParameter && lookAhead(isStartOfParenthesizedOrFunctionType);
|
||||
default:
|
||||
return isIdentifier();
|
||||
}
|
||||
@@ -2806,13 +2796,12 @@ namespace ts {
|
||||
parseOptional(operator);
|
||||
let type = parseConstituentType();
|
||||
if (token() === operator) {
|
||||
const types = createNodeArray<TypeNode>([type], type.pos);
|
||||
const types = [type];
|
||||
while (parseOptional(operator)) {
|
||||
types.push(parseConstituentType());
|
||||
}
|
||||
types.end = getNodeEnd();
|
||||
const node = <UnionOrIntersectionTypeNode>createNode(kind, type.pos);
|
||||
node.types = types;
|
||||
node.types = createNodeArray(types, type.pos);
|
||||
type = finishNode(node);
|
||||
}
|
||||
return type;
|
||||
@@ -3018,7 +3007,7 @@ namespace ts {
|
||||
return expr;
|
||||
}
|
||||
|
||||
function parseInitializer(inParameter: boolean): Expression {
|
||||
function parseInitializer(inParameter: boolean, requireEqualsToken?: boolean): Expression {
|
||||
if (token() !== SyntaxKind.EqualsToken) {
|
||||
// It's not uncommon during typing for the user to miss writing the '=' token. Check if
|
||||
// there is no newline after the last token and if we're on an expression. If so, parse
|
||||
@@ -3033,11 +3022,17 @@ namespace ts {
|
||||
// do not try to parse initializer
|
||||
return undefined;
|
||||
}
|
||||
if (inParameter && requireEqualsToken) {
|
||||
// = is required when speculatively parsing arrow function parameters,
|
||||
// so return a fake initializer as a signal that the equals token was missing
|
||||
const result = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics._0_expected, "=") as Identifier;
|
||||
result.escapedText = "= not found" as __String;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Initializer[In, Yield] :
|
||||
// = AssignmentExpression[?In, ?Yield]
|
||||
|
||||
parseExpected(SyntaxKind.EqualsToken);
|
||||
return parseAssignmentExpressionOrHigher();
|
||||
}
|
||||
@@ -3178,8 +3173,7 @@ namespace ts {
|
||||
parameter.name = identifier;
|
||||
finishNode(parameter);
|
||||
|
||||
node.parameters = createNodeArray<ParameterDeclaration>([parameter], parameter.pos);
|
||||
node.parameters.end = parameter.end;
|
||||
node.parameters = createNodeArray<ParameterDeclaration>([parameter], parameter.pos, parameter.end);
|
||||
|
||||
node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, "=>");
|
||||
node.body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier);
|
||||
@@ -3353,8 +3347,7 @@ namespace ts {
|
||||
function tryParseAsyncSimpleArrowFunctionExpression(): ArrowFunction | undefined {
|
||||
// We do a check here so that we won't be doing unnecessarily call to "lookAhead"
|
||||
if (token() === SyntaxKind.AsyncKeyword) {
|
||||
const isUnParenthesizedAsyncArrowFunction = lookAhead(isUnParenthesizedAsyncArrowFunctionWorker);
|
||||
if (isUnParenthesizedAsyncArrowFunction === Tristate.True) {
|
||||
if (lookAhead(isUnParenthesizedAsyncArrowFunctionWorker) === Tristate.True) {
|
||||
const asyncModifier = parseModifiersForArrowFunction();
|
||||
const expr = parseBinaryExpressionOrHigher(/*precedence*/ 0);
|
||||
return parseSimpleArrowFunctionExpression(<Identifier>expr, asyncModifier);
|
||||
@@ -3388,7 +3381,6 @@ namespace ts {
|
||||
const node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction);
|
||||
node.modifiers = parseModifiersForArrowFunction();
|
||||
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
|
||||
|
||||
// Arrow functions are never generators.
|
||||
//
|
||||
// If we're speculatively parsing a signature for a parenthesized arrow function, then
|
||||
@@ -3411,7 +3403,8 @@ namespace ts {
|
||||
// - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation.
|
||||
//
|
||||
// So we need just a bit of lookahead to ensure that it can only be a signature.
|
||||
if (!allowAmbiguity && token() !== SyntaxKind.EqualsGreaterThanToken && token() !== SyntaxKind.OpenBraceToken) {
|
||||
if (!allowAmbiguity && ((token() !== SyntaxKind.EqualsGreaterThanToken && token() !== SyntaxKind.OpenBraceToken) ||
|
||||
find(node.parameters, p => p.initializer && ts.isIdentifier(p.initializer) && p.initializer.escapedText === "= not found"))) {
|
||||
// Returning undefined here will cause our caller to rewind to where we started from.
|
||||
return undefined;
|
||||
}
|
||||
@@ -4029,7 +4022,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseJsxChildren(openingTagName: LeftHandSideExpression): NodeArray<JsxChild> {
|
||||
const result = createNodeArray<JsxChild>();
|
||||
const list = [];
|
||||
const listPos = getNodePos();
|
||||
const saveParsingContext = parsingContext;
|
||||
parsingContext |= 1 << ParsingContext.JsxChildren;
|
||||
|
||||
@@ -4050,15 +4044,13 @@ namespace ts {
|
||||
}
|
||||
const child = parseJsxChild();
|
||||
if (child) {
|
||||
result.push(child);
|
||||
list.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
result.end = scanner.getTokenPos();
|
||||
|
||||
parsingContext = saveParsingContext;
|
||||
|
||||
return result;
|
||||
return createNodeArray(list, listPos);
|
||||
}
|
||||
|
||||
function parseJsxAttributes(): JsxAttributes {
|
||||
@@ -5161,7 +5153,7 @@ namespace ts {
|
||||
const node = <BindingElement>createNode(SyntaxKind.BindingElement);
|
||||
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
|
||||
node.name = parseIdentifierOrPattern();
|
||||
node.initializer = parseBindingElementInitializer(/*inParameter*/ false);
|
||||
node.initializer = parseInitializer(/*inParameter*/ false);
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
@@ -5178,7 +5170,7 @@ namespace ts {
|
||||
node.propertyName = propertyName;
|
||||
node.name = parseIdentifierOrPattern();
|
||||
}
|
||||
node.initializer = parseBindingElementInitializer(/*inParameter*/ false);
|
||||
node.initializer = parseInitializer(/*inParameter*/ false);
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
@@ -5217,7 +5209,7 @@ namespace ts {
|
||||
node.name = parseIdentifierOrPattern();
|
||||
node.type = parseTypeAnnotation();
|
||||
if (!isInOrOfKeyword(token())) {
|
||||
node.initializer = parseInitializer(/*inParameter*/ false);
|
||||
node.initializer = parseNonParameterInitializer();
|
||||
}
|
||||
return finishNode(node);
|
||||
}
|
||||
@@ -5451,27 +5443,19 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseDecorators(): NodeArray<Decorator> {
|
||||
let decorators: NodeArray<Decorator> & Decorator[];
|
||||
let list: Decorator[];
|
||||
const listPos = getNodePos();
|
||||
while (true) {
|
||||
const decoratorStart = getNodePos();
|
||||
if (!parseOptional(SyntaxKind.AtToken)) {
|
||||
break;
|
||||
}
|
||||
|
||||
const decorator = <Decorator>createNode(SyntaxKind.Decorator, decoratorStart);
|
||||
decorator.expression = doInDecoratorContext(parseLeftHandSideExpressionOrHigher);
|
||||
finishNode(decorator);
|
||||
if (!decorators) {
|
||||
decorators = createNodeArray<Decorator>([decorator], decoratorStart);
|
||||
}
|
||||
else {
|
||||
decorators.push(decorator);
|
||||
}
|
||||
(list || (list = [])).push(decorator);
|
||||
}
|
||||
if (decorators) {
|
||||
decorators.end = getNodeEnd();
|
||||
}
|
||||
return decorators;
|
||||
return list && createNodeArray(list, listPos);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5482,7 +5466,8 @@ namespace ts {
|
||||
* In such situations, 'permitInvalidConstAsModifier' should be set to true.
|
||||
*/
|
||||
function parseModifiers(permitInvalidConstAsModifier?: boolean): NodeArray<Modifier> | undefined {
|
||||
let modifiers: MutableNodeArray<Modifier> | undefined;
|
||||
let list: Modifier[];
|
||||
const listPos = getNodePos();
|
||||
while (true) {
|
||||
const modifierStart = scanner.getStartPos();
|
||||
const modifierKind = token();
|
||||
@@ -5501,17 +5486,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
const modifier = finishNode(<Modifier>createNode(modifierKind, modifierStart));
|
||||
if (!modifiers) {
|
||||
modifiers = createNodeArray<Modifier>([modifier], modifierStart);
|
||||
}
|
||||
else {
|
||||
modifiers.push(modifier);
|
||||
}
|
||||
(list || (list = [])).push(modifier);
|
||||
}
|
||||
if (modifiers) {
|
||||
modifiers.end = scanner.getStartPos();
|
||||
}
|
||||
return modifiers;
|
||||
return list && createNodeArray(list, listPos);
|
||||
}
|
||||
|
||||
function parseModifiersForArrowFunction(): NodeArray<Modifier> {
|
||||
@@ -5522,9 +5499,7 @@ namespace ts {
|
||||
nextToken();
|
||||
const modifier = finishNode(<Modifier>createNode(modifierKind, modifierStart));
|
||||
modifiers = createNodeArray<Modifier>([modifier], modifierStart);
|
||||
modifiers.end = scanner.getStartPos();
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
@@ -6182,7 +6157,7 @@ namespace ts {
|
||||
return jsDoc ? { jsDoc, diagnostics } : undefined;
|
||||
}
|
||||
|
||||
export function parseJSDocComment(parent: Node, start: number, length: number): JSDoc {
|
||||
export function parseJSDocComment(parent: HasJSDoc, start: number, length: number): JSDoc {
|
||||
const saveToken = currentToken;
|
||||
const saveParseDiagnosticsLength = parseDiagnostics.length;
|
||||
const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode;
|
||||
@@ -6226,7 +6201,9 @@ namespace ts {
|
||||
Debug.assert(start <= end);
|
||||
Debug.assert(end <= content.length);
|
||||
|
||||
let tags: MutableNodeArray<JSDocTag>;
|
||||
let tags: JSDocTag[];
|
||||
let tagsPos: number;
|
||||
let tagsEnd: number;
|
||||
const comments: string[] = [];
|
||||
let result: JSDoc;
|
||||
|
||||
@@ -6359,7 +6336,7 @@ namespace ts {
|
||||
|
||||
function createJSDocComment(): JSDoc {
|
||||
const result = <JSDoc>createNode(SyntaxKind.JSDocComment, start);
|
||||
result.tags = tags;
|
||||
result.tags = tags && createNodeArray(tags, tagsPos, tagsEnd);
|
||||
result.comment = comments.length ? comments.join("") : undefined;
|
||||
return finishNode(result, end);
|
||||
}
|
||||
@@ -6499,12 +6476,13 @@ namespace ts {
|
||||
tag.comment = comments.join("");
|
||||
|
||||
if (!tags) {
|
||||
tags = createNodeArray([tag], tag.pos);
|
||||
tags = [tag];
|
||||
tagsPos = tag.pos;
|
||||
}
|
||||
else {
|
||||
tags.push(tag);
|
||||
}
|
||||
tags.end = tag.end;
|
||||
tagsEnd = tag.end;
|
||||
}
|
||||
|
||||
function tryParseTypeExpression(): JSDocTypeExpression | undefined {
|
||||
@@ -6671,19 +6649,18 @@ namespace ts {
|
||||
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
|
||||
let child: JSDocTypeTag | JSDocPropertyTag | false;
|
||||
let jsdocTypeLiteral: JSDocTypeLiteral;
|
||||
let alreadyHasTypeTag = false;
|
||||
let childTypeTag: JSDocTypeTag;
|
||||
const start = scanner.getStartPos();
|
||||
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Property))) {
|
||||
if (!jsdocTypeLiteral) {
|
||||
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
|
||||
}
|
||||
if (child.kind === SyntaxKind.JSDocTypeTag) {
|
||||
if (alreadyHasTypeTag) {
|
||||
if (childTypeTag) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
jsdocTypeLiteral.jsDocTypeTag = child;
|
||||
alreadyHasTypeTag = true;
|
||||
childTypeTag = child;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -6697,7 +6674,9 @@ namespace ts {
|
||||
if (typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType) {
|
||||
jsdocTypeLiteral.isArrayType = true;
|
||||
}
|
||||
typedefTag.typeExpression = finishNode(jsdocTypeLiteral);
|
||||
typedefTag.typeExpression = childTypeTag && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
|
||||
childTypeTag.typeExpression :
|
||||
finishNode(jsdocTypeLiteral);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6804,7 +6783,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Type parameter list looks like '@template T,U,V'
|
||||
const typeParameters = createNodeArray<TypeParameterDeclaration>();
|
||||
const typeParameters = [];
|
||||
const typeParametersPos = getNodePos();
|
||||
|
||||
while (true) {
|
||||
const name = parseJSDocIdentifierName();
|
||||
@@ -6832,9 +6812,8 @@ namespace ts {
|
||||
const result = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag, atToken.pos);
|
||||
result.atToken = atToken;
|
||||
result.tagName = tagName;
|
||||
result.typeParameters = typeParameters;
|
||||
result.typeParameters = createNodeArray(typeParameters, typeParametersPos);
|
||||
finishNode(result);
|
||||
typeParameters.end = result.end;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -6997,7 +6976,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
forEachChild(node, visitNode, visitArray);
|
||||
if (node.jsDoc) {
|
||||
if (hasJSDocNodes(node)) {
|
||||
for (const jsDocComment of node.jsDoc) {
|
||||
forEachChild(jsDocComment, visitNode, visitArray);
|
||||
}
|
||||
|
||||
+51
-33
@@ -245,7 +245,7 @@ namespace ts {
|
||||
const redForegroundEscapeSequence = "\u001b[91m";
|
||||
const yellowForegroundEscapeSequence = "\u001b[93m";
|
||||
const blueForegroundEscapeSequence = "\u001b[93m";
|
||||
const gutterStyleSequence = "\u001b[100;30m";
|
||||
const gutterStyleSequence = "\u001b[30;47m";
|
||||
const gutterSeparator = " ";
|
||||
const resetEscapeSequence = "\u001b[0m";
|
||||
const ellipsis = "...";
|
||||
@@ -268,7 +268,7 @@ namespace ts {
|
||||
return s;
|
||||
}
|
||||
|
||||
export function formatDiagnosticsWithColorAndContext(diagnostics: Diagnostic[], host: FormatDiagnosticsHost): string {
|
||||
export function formatDiagnosticsWithColorAndContext(diagnostics: ReadonlyArray<Diagnostic>, host: FormatDiagnosticsHost): string {
|
||||
let output = "";
|
||||
for (const diagnostic of diagnostics) {
|
||||
if (diagnostic.file) {
|
||||
@@ -284,12 +284,12 @@ namespace ts {
|
||||
gutterWidth = Math.max(ellipsis.length, gutterWidth);
|
||||
}
|
||||
|
||||
output += sys.newLine;
|
||||
output += host.getNewLine();
|
||||
for (let i = firstLine; i <= lastLine; i++) {
|
||||
// If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
|
||||
// so we'll skip ahead to the second-to-last line.
|
||||
if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) {
|
||||
output += formatAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + sys.newLine;
|
||||
output += formatAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + host.getNewLine();
|
||||
i = lastLine - 1;
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace ts {
|
||||
|
||||
// Output the gutter and the actual contents of the line.
|
||||
output += formatAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator;
|
||||
output += lineContent + sys.newLine;
|
||||
output += lineContent + host.getNewLine();
|
||||
|
||||
// Output the gutter and the error span for the line using tildes.
|
||||
output += formatAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator;
|
||||
@@ -323,17 +323,17 @@ namespace ts {
|
||||
}
|
||||
output += resetEscapeSequence;
|
||||
|
||||
output += sys.newLine;
|
||||
output += host.getNewLine();
|
||||
}
|
||||
|
||||
output += sys.newLine;
|
||||
output += host.getNewLine();
|
||||
output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `;
|
||||
}
|
||||
|
||||
const categoryColor = getCategoryFormat(diagnostic.category);
|
||||
const category = DiagnosticCategory[diagnostic.category].toLowerCase();
|
||||
output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`;
|
||||
output += sys.newLine;
|
||||
output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()) }`;
|
||||
output += host.getNewLine();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
@@ -438,6 +438,8 @@ namespace ts {
|
||||
host = host || createCompilerHost(options);
|
||||
|
||||
let skipDefaultLib = options.noLib;
|
||||
const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName(options));
|
||||
const defaultLibraryPath = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(getDefaultLibraryFileName());
|
||||
const programDiagnostics = createDiagnosticCollection();
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
const supportedExtensions = getSupportedExtensions(options);
|
||||
@@ -513,12 +515,11 @@ namespace ts {
|
||||
// If '--lib' is not specified, include default library file according to '--target'
|
||||
// otherwise, using options specified in '--lib' instead of '--target' default library file
|
||||
if (!options.lib) {
|
||||
processRootFile(host.getDefaultLibFileName(options), /*isDefaultLib*/ true);
|
||||
processRootFile(getDefaultLibraryFileName(), /*isDefaultLib*/ true);
|
||||
}
|
||||
else {
|
||||
const libDirectory = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(host.getDefaultLibFileName(options));
|
||||
forEach(options.lib, libFileName => {
|
||||
processRootFile(combinePaths(libDirectory, libFileName), /*isDefaultLib*/ true);
|
||||
processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -557,6 +558,7 @@ namespace ts {
|
||||
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
|
||||
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
|
||||
isSourceFileFromExternalLibrary,
|
||||
isSourceFileDefaultLibrary,
|
||||
dropDiagnosticsProducingTypeChecker,
|
||||
getSourceFileFromReference,
|
||||
sourceFileToPackageName,
|
||||
@@ -977,6 +979,18 @@ namespace ts {
|
||||
return sourceFilesFoundSearchingNodeModules.get(file.path);
|
||||
}
|
||||
|
||||
function isSourceFileDefaultLibrary(file: SourceFile): boolean {
|
||||
if (file.hasNoDefaultLib) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (defaultLibraryPath && defaultLibraryPath.length !== 0) {
|
||||
return containsPath(defaultLibraryPath, file.path, currentDirectory, /*ignoreCase*/ !host.useCaseSensitiveFileNames());
|
||||
}
|
||||
|
||||
return compareStrings(file.fileName, getDefaultLibraryFileName(), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
|
||||
}
|
||||
|
||||
function getDiagnosticsProducingTypeChecker() {
|
||||
return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
|
||||
}
|
||||
@@ -1158,9 +1172,7 @@ namespace ts {
|
||||
const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName);
|
||||
|
||||
const diagnostics = bindDiagnostics.concat(checkDiagnostics, fileProcessingDiagnosticsInFile, programDiagnosticsInFile);
|
||||
return isSourceFileJavaScript(sourceFile)
|
||||
? filter(diagnostics, shouldReportDiagnostic)
|
||||
: diagnostics;
|
||||
return filter(diagnostics, shouldReportDiagnostic);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1208,7 +1220,7 @@ namespace ts {
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_can_only_be_used_in_a_ts_file, "?"));
|
||||
return;
|
||||
}
|
||||
// falls through
|
||||
// falls through
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.Constructor:
|
||||
@@ -1290,7 +1302,7 @@ namespace ts {
|
||||
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.type_parameter_declarations_can_only_be_used_in_a_ts_file));
|
||||
return;
|
||||
}
|
||||
// falls through
|
||||
// falls through
|
||||
case SyntaxKind.VariableStatement:
|
||||
// Check modifiers
|
||||
if (nodes === (<ClassDeclaration | FunctionLikeDeclaration | VariableStatement>parent).modifiers) {
|
||||
@@ -1338,8 +1350,8 @@ namespace ts {
|
||||
if (isConstValid) {
|
||||
continue;
|
||||
}
|
||||
// to report error,
|
||||
// falls through
|
||||
// to report error,
|
||||
// falls through
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
@@ -1427,7 +1439,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function processRootFile(fileName: string, isDefaultLib: boolean) {
|
||||
processSourceFile(normalizePath(fileName), isDefaultLib);
|
||||
processSourceFile(normalizePath(fileName), isDefaultLib, /*packageId*/ undefined);
|
||||
}
|
||||
|
||||
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
|
||||
@@ -1553,10 +1565,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getSourceFileFromReferenceWorker(
|
||||
fileName: string,
|
||||
getSourceFile: (fileName: string) => SourceFile | undefined,
|
||||
fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void,
|
||||
refFile?: SourceFile): SourceFile | undefined {
|
||||
fileName: string,
|
||||
getSourceFile: (fileName: string) => SourceFile | undefined,
|
||||
fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void,
|
||||
refFile?: SourceFile): SourceFile | undefined {
|
||||
|
||||
if (hasExtension(fileName)) {
|
||||
if (!options.allowNonTsExtensions && !forEach(supportedExtensions, extension => fileExtensionIs(host.getCanonicalFileName(fileName), extension))) {
|
||||
@@ -1591,9 +1603,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
/** This has side effects through `findSourceFile`. */
|
||||
function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
|
||||
function processSourceFile(fileName: string, isDefaultLib: boolean, packageId: PackageId | undefined, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
|
||||
getSourceFileFromReferenceWorker(fileName,
|
||||
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, /*packageId*/ undefined),
|
||||
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, packageId),
|
||||
(diagnostic, ...args) => {
|
||||
fileProcessingDiagnostics.add(refFile !== undefined && refEnd !== undefined && refPos !== undefined
|
||||
? createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...args)
|
||||
@@ -1675,7 +1687,7 @@ namespace ts {
|
||||
});
|
||||
|
||||
if (packageId) {
|
||||
const packageIdKey = `${packageId.name}@${packageId.version}`;
|
||||
const packageIdKey = `${packageId.name}/${packageId.subModuleName}@${packageId.version}`;
|
||||
const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
|
||||
if (fileFromPackageId) {
|
||||
// Some other SourceFile already exists with this package name and version.
|
||||
@@ -1735,7 +1747,7 @@ namespace ts {
|
||||
function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
|
||||
forEach(file.referencedFiles, ref => {
|
||||
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
|
||||
processSourceFile(referencedFileName, isDefaultLib, file, ref.pos, ref.end);
|
||||
processSourceFile(referencedFileName, isDefaultLib, /*packageId*/ undefined, file, ref.pos, ref.end);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1766,7 +1778,7 @@ namespace ts {
|
||||
if (resolvedTypeReferenceDirective) {
|
||||
if (resolvedTypeReferenceDirective.primary) {
|
||||
// resolved from the primary path
|
||||
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd);
|
||||
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
|
||||
}
|
||||
else {
|
||||
// If we already resolved to this file, it must have been a secondary reference. Check file contents
|
||||
@@ -1789,7 +1801,7 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
// First resolution of this library
|
||||
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd);
|
||||
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1832,7 +1844,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
const isFromNodeModulesSearch = resolution.isExternalLibraryImport;
|
||||
const isJsFileFromNodeModules = isFromNodeModulesSearch && !extensionIsTypeScript(resolution.extension);
|
||||
const isJsFile = !extensionIsTypeScript(resolution.extension);
|
||||
const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile;
|
||||
const resolvedFileName = resolution.resolvedFileName;
|
||||
|
||||
if (isFromNodeModulesSearch) {
|
||||
@@ -1847,7 +1860,12 @@ namespace ts {
|
||||
const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth;
|
||||
// Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
|
||||
// This may still end up being an untyped module -- the file won't be included but imports will be allowed.
|
||||
const shouldAddFile = resolvedFileName && !getResolutionDiagnostic(options, resolution) && !options.noResolve && i < file.imports.length && !elideImport;
|
||||
const shouldAddFile = resolvedFileName
|
||||
&& !getResolutionDiagnostic(options, resolution)
|
||||
&& !options.noResolve
|
||||
&& i < file.imports.length
|
||||
&& !elideImport
|
||||
&& !(isJsFile && !options.allowJs);
|
||||
|
||||
if (elideImport) {
|
||||
modulesWithElidedImports.set(file.path, true);
|
||||
@@ -2222,7 +2240,7 @@ namespace ts {
|
||||
return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set;
|
||||
}
|
||||
function needAllowJs() {
|
||||
return options.allowJs ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_allowJs_is_not_set;
|
||||
return options.allowJs || !options.noImplicitAny ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ namespace ts {
|
||||
Debug.assert(res < lineStarts[line + 1]);
|
||||
}
|
||||
else if (debugText !== undefined) {
|
||||
Debug.assert(res < debugText.length);
|
||||
Debug.assert(res <= debugText.length); // Allow single character overflow for trailing newline
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ namespace ts {
|
||||
const compilerOptions = context.getCompilerOptions();
|
||||
const languageVersion = getEmitScriptTarget(compilerOptions);
|
||||
|
||||
// These variables contain state that changes as we descend into the tree.
|
||||
let currentSourceFile: SourceFile;
|
||||
|
||||
/**
|
||||
* Keeps track of whether expression substitution has been enabled for specific edge cases.
|
||||
* They are persisted between each SourceFile transformation and should not be reset.
|
||||
@@ -51,12 +48,8 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
currentSourceFile = node;
|
||||
|
||||
const visited = visitEachChild(node, visitor, context);
|
||||
addEmitHelpers(visited, context.readEmitHelpers());
|
||||
|
||||
currentSourceFile = undefined;
|
||||
return visited;
|
||||
}
|
||||
|
||||
@@ -197,9 +190,10 @@ namespace ts {
|
||||
/*typeParameters*/ undefined,
|
||||
visitParameterList(node.parameters, visitor, context),
|
||||
/*type*/ undefined,
|
||||
node.equalsGreaterThanToken,
|
||||
getFunctionFlags(node) & FunctionFlags.Async
|
||||
? transformAsyncFunctionBody(node)
|
||||
: visitFunctionBody(node.body, visitor, context)
|
||||
: visitFunctionBody(node.body, visitor, context),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -595,7 +595,8 @@ namespace ts {
|
||||
/*typeParameters*/ undefined,
|
||||
visitParameterList(node.parameters, visitor, context),
|
||||
/*type*/ undefined,
|
||||
transformFunctionBody(node)
|
||||
node.equalsGreaterThanToken,
|
||||
transformFunctionBody(node),
|
||||
);
|
||||
enclosingFunctionFlags = savedEnclosingFunctionFlags;
|
||||
return updated;
|
||||
|
||||
@@ -244,7 +244,6 @@ namespace ts {
|
||||
const previousOnSubstituteNode = context.onSubstituteNode;
|
||||
context.onSubstituteNode = onSubstituteNode;
|
||||
|
||||
let currentSourceFile: SourceFile;
|
||||
let renamedCatchVariables: Map<boolean>;
|
||||
let renamedCatchVariableDeclarations: Identifier[];
|
||||
|
||||
@@ -300,12 +299,9 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
currentSourceFile = node;
|
||||
|
||||
const visited = visitEachChild(node, visitor, context);
|
||||
addEmitHelpers(visited, context.readEmitHelpers());
|
||||
|
||||
currentSourceFile = undefined;
|
||||
return visited;
|
||||
}
|
||||
|
||||
|
||||
@@ -200,6 +200,7 @@ namespace ts {
|
||||
*/
|
||||
function transformUMDModule(node: SourceFile) {
|
||||
const { aliasedModuleNames, unaliasedModuleNames, importAliasNames } = collectAsynchronousDependencies(node, /*includeNonAmdDependencies*/ false);
|
||||
const moduleName = tryGetModuleNameFromFile(node, host, compilerOptions);
|
||||
const umdHeader = createFunctionExpression(
|
||||
/*modifiers*/ undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
@@ -260,6 +261,8 @@ namespace ts {
|
||||
createIdentifier("define"),
|
||||
/*typeArguments*/ undefined,
|
||||
[
|
||||
// Add the module name (if provided).
|
||||
...(moduleName ? [moduleName] : []),
|
||||
createArrayLiteral([
|
||||
createLiteral("require"),
|
||||
createLiteral("exports"),
|
||||
|
||||
@@ -1132,7 +1132,8 @@ namespace ts {
|
||||
*/
|
||||
function createExportExpression(name: Identifier | StringLiteral, value: Expression) {
|
||||
const exportName = isIdentifier(name) ? createLiteral(name) : name;
|
||||
return createCall(exportFunction, /*typeArguments*/ undefined, [exportName, value]);
|
||||
setEmitFlags(value, getEmitFlags(value) | EmitFlags.NoComments);
|
||||
return setCommentRange(createCall(exportFunction, /*typeArguments*/ undefined, [exportName, value]), value);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -208,6 +208,24 @@ namespace ts {
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function sourceElementVisitorWorker(node: Node): VisitResult<Node> {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
case SyntaxKind.ExportAssignment:
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
return visitEllidableStatement(<ImportDeclaration | ImportEqualsDeclaration | ExportAssignment | ExportDeclaration>node);
|
||||
default:
|
||||
return visitorWorker(node);
|
||||
}
|
||||
}
|
||||
|
||||
function visitEllidableStatement(node: ImportDeclaration | ImportEqualsDeclaration | ExportAssignment | ExportDeclaration): VisitResult<Node> {
|
||||
const parsed = getParseTreeNode(node);
|
||||
if (parsed !== node) {
|
||||
// If the node has been transformed by a `before` transformer, perform no ellision on it
|
||||
// As the type information we would attempt to lookup to perform ellision is potentially unavailable for the synthesized nodes
|
||||
return node;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
return visitImportDeclaration(<ImportDeclaration>node);
|
||||
@@ -218,7 +236,7 @@ namespace ts {
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
return visitExportDeclaration(<ExportDeclaration>node);
|
||||
default:
|
||||
return visitorWorker(node);
|
||||
Debug.fail("Unhandled ellided statement");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,7 +521,7 @@ namespace ts {
|
||||
|
||||
function visitSourceFile(node: SourceFile) {
|
||||
const alwaysStrict = (compilerOptions.alwaysStrict === undefined ? compilerOptions.strict : compilerOptions.alwaysStrict) &&
|
||||
!(isExternalModule(node) && moduleKind === ModuleKind.ES2015);
|
||||
!(isExternalModule(node) && moduleKind >= ModuleKind.ES2015);
|
||||
return updateSourceFileNode(
|
||||
node,
|
||||
visitLexicalEnvironment(node.statements, sourceElementVisitor, context, /*start*/ 0, alwaysStrict));
|
||||
@@ -2291,7 +2309,8 @@ namespace ts {
|
||||
/*typeParameters*/ undefined,
|
||||
visitParameterList(node.parameters, visitor, context),
|
||||
/*type*/ undefined,
|
||||
visitFunctionBody(node.body, visitor, context)
|
||||
node.equalsGreaterThanToken,
|
||||
visitFunctionBody(node.body, visitor, context),
|
||||
);
|
||||
return updated;
|
||||
}
|
||||
@@ -2646,6 +2665,7 @@ namespace ts {
|
||||
return isExportOfNamespace(node)
|
||||
|| (isExternalModuleExport(node)
|
||||
&& moduleKind !== ModuleKind.ES2015
|
||||
&& moduleKind !== ModuleKind.ESNext
|
||||
&& moduleKind !== ModuleKind.System);
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -100,7 +100,6 @@ namespace ts {
|
||||
const commandLine = parseCommandLine(args);
|
||||
let configFileName: string; // Configuration file name (if any)
|
||||
let cachedConfigFileText: string; // Cached configuration file text, used for reparsing (if any)
|
||||
let configFileWatcher: FileWatcher; // Configuration file watcher
|
||||
let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal
|
||||
let cachedProgram: Program; // Program cached from last compilation
|
||||
let rootFileNames: string[]; // Root fileNames for compilation
|
||||
@@ -189,7 +188,7 @@ namespace ts {
|
||||
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
if (configFileName) {
|
||||
configFileWatcher = sys.watchFile(configFileName, configFileChanged);
|
||||
sys.watchFile(configFileName, configFileChanged);
|
||||
}
|
||||
if (sys.watchDirectory && configFileName) {
|
||||
const directory = ts.getDirectoryPath(configFileName);
|
||||
|
||||
+120
-65
@@ -516,8 +516,6 @@ namespace ts {
|
||||
parent?: Node; // Parent node (initialized by binding)
|
||||
/* @internal */ original?: Node; // The original node if this is an updated node.
|
||||
/* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms).
|
||||
/* @internal */ jsDoc?: JSDoc[]; // JSDoc that directly precedes this node
|
||||
/* @internal */ jsDocCache?: ReadonlyArray<JSDocTag>; // Cache for getJSDocTags
|
||||
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
|
||||
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
|
||||
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
|
||||
@@ -528,6 +526,44 @@ namespace ts {
|
||||
/* @internal */ contextualMapper?: TypeMapper; // Mapper for contextual type
|
||||
}
|
||||
|
||||
export interface JSDocContainer {
|
||||
/* @internal */ jsDoc?: JSDoc[]; // JSDoc that directly precedes this node
|
||||
/* @internal */ jsDocCache?: ReadonlyArray<JSDocTag>; // Cache for getJSDocTags
|
||||
}
|
||||
|
||||
export type HasJSDoc =
|
||||
| ParameterDeclaration
|
||||
| CallSignatureDeclaration
|
||||
| ConstructSignatureDeclaration
|
||||
| MethodSignature
|
||||
| PropertySignature
|
||||
| ArrowFunction
|
||||
| ParenthesizedExpression
|
||||
| SpreadAssignment
|
||||
| ShorthandPropertyAssignment
|
||||
| PropertyAssignment
|
||||
| FunctionExpression
|
||||
| LabeledStatement
|
||||
| ExpressionStatement
|
||||
| VariableStatement
|
||||
| FunctionDeclaration
|
||||
| ConstructorDeclaration
|
||||
| MethodDeclaration
|
||||
| PropertyDeclaration
|
||||
| AccessorDeclaration
|
||||
| ClassLikeDeclaration
|
||||
| InterfaceDeclaration
|
||||
| TypeAliasDeclaration
|
||||
| EnumMember
|
||||
| EnumDeclaration
|
||||
| ModuleDeclaration
|
||||
| ImportEqualsDeclaration
|
||||
| IndexSignatureDeclaration
|
||||
| FunctionTypeNode
|
||||
| ConstructorTypeNode
|
||||
| JSDocFunctionType
|
||||
| EndOfFileToken;
|
||||
|
||||
/* @internal */
|
||||
export type MutableNodeArray<T extends Node> = NodeArray<T> & T[];
|
||||
|
||||
@@ -546,7 +582,7 @@ namespace ts {
|
||||
export type EqualsToken = Token<SyntaxKind.EqualsToken>;
|
||||
export type AsteriskToken = Token<SyntaxKind.AsteriskToken>;
|
||||
export type EqualsGreaterThanToken = Token<SyntaxKind.EqualsGreaterThanToken>;
|
||||
export type EndOfFileToken = Token<SyntaxKind.EndOfFileToken>;
|
||||
export type EndOfFileToken = Token<SyntaxKind.EndOfFileToken> & JSDocContainer;
|
||||
export type AtToken = Token<SyntaxKind.AtToken>;
|
||||
export type ReadonlyToken = Token<SyntaxKind.ReadonlyKeyword>;
|
||||
export type AwaitKeywordToken = Token<SyntaxKind.AwaitKeyword>;
|
||||
@@ -636,6 +672,7 @@ namespace ts {
|
||||
|
||||
export interface Decorator extends Node {
|
||||
kind: SyntaxKind.Decorator;
|
||||
parent?: NamedDeclaration;
|
||||
expression: LeftHandSideExpression;
|
||||
}
|
||||
|
||||
@@ -650,32 +687,34 @@ namespace ts {
|
||||
expression?: Expression;
|
||||
}
|
||||
|
||||
export interface SignatureDeclaration extends NamedDeclaration {
|
||||
kind: SyntaxKind.CallSignature
|
||||
| SyntaxKind.ConstructSignature
|
||||
| SyntaxKind.MethodSignature
|
||||
| SyntaxKind.IndexSignature
|
||||
| SyntaxKind.FunctionType
|
||||
| SyntaxKind.ConstructorType
|
||||
| SyntaxKind.JSDocFunctionType
|
||||
| SyntaxKind.FunctionDeclaration
|
||||
| SyntaxKind.MethodDeclaration
|
||||
| SyntaxKind.Constructor
|
||||
| SyntaxKind.GetAccessor
|
||||
| SyntaxKind.SetAccessor
|
||||
| SyntaxKind.FunctionExpression
|
||||
| SyntaxKind.ArrowFunction;
|
||||
export interface SignatureDeclarationBase extends NamedDeclaration, JSDocContainer {
|
||||
kind: SignatureDeclaration["kind"];
|
||||
name?: PropertyName;
|
||||
typeParameters?: NodeArray<TypeParameterDeclaration>;
|
||||
parameters: NodeArray<ParameterDeclaration>;
|
||||
type: TypeNode | undefined;
|
||||
}
|
||||
|
||||
export interface CallSignatureDeclaration extends SignatureDeclaration, TypeElement {
|
||||
export type SignatureDeclaration =
|
||||
| CallSignatureDeclaration
|
||||
| ConstructSignatureDeclaration
|
||||
| MethodSignature
|
||||
| IndexSignatureDeclaration
|
||||
| FunctionTypeNode
|
||||
| ConstructorTypeNode
|
||||
| JSDocFunctionType
|
||||
| FunctionDeclaration
|
||||
| MethodDeclaration
|
||||
| ConstructorDeclaration
|
||||
| AccessorDeclaration
|
||||
| FunctionExpression
|
||||
| ArrowFunction;
|
||||
|
||||
export interface CallSignatureDeclaration extends SignatureDeclarationBase, TypeElement {
|
||||
kind: SyntaxKind.CallSignature;
|
||||
}
|
||||
|
||||
export interface ConstructSignatureDeclaration extends SignatureDeclaration, TypeElement {
|
||||
export interface ConstructSignatureDeclaration extends SignatureDeclarationBase, TypeElement {
|
||||
kind: SyntaxKind.ConstructSignature;
|
||||
}
|
||||
|
||||
@@ -695,7 +734,7 @@ namespace ts {
|
||||
declarations: NodeArray<VariableDeclaration>;
|
||||
}
|
||||
|
||||
export interface ParameterDeclaration extends NamedDeclaration {
|
||||
export interface ParameterDeclaration extends NamedDeclaration, JSDocContainer {
|
||||
kind: SyntaxKind.Parameter;
|
||||
parent?: SignatureDeclaration;
|
||||
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
|
||||
@@ -714,7 +753,7 @@ namespace ts {
|
||||
initializer?: Expression; // Optional initializer
|
||||
}
|
||||
|
||||
export interface PropertySignature extends TypeElement {
|
||||
export interface PropertySignature extends TypeElement, JSDocContainer {
|
||||
kind: SyntaxKind.PropertySignature;
|
||||
name: PropertyName; // Declared property name
|
||||
questionToken?: QuestionToken; // Present on optional property
|
||||
@@ -722,7 +761,7 @@ namespace ts {
|
||||
initializer?: Expression; // Optional initializer
|
||||
}
|
||||
|
||||
export interface PropertyDeclaration extends ClassElement {
|
||||
export interface PropertyDeclaration extends ClassElement, JSDocContainer {
|
||||
kind: SyntaxKind.PropertyDeclaration;
|
||||
questionToken?: QuestionToken; // Present for use with reporting a grammar error
|
||||
name: PropertyName;
|
||||
@@ -743,14 +782,16 @@ namespace ts {
|
||||
| AccessorDeclaration
|
||||
;
|
||||
|
||||
export interface PropertyAssignment extends ObjectLiteralElement {
|
||||
export interface PropertyAssignment extends ObjectLiteralElement, JSDocContainer {
|
||||
parent: ObjectLiteralExpression;
|
||||
kind: SyntaxKind.PropertyAssignment;
|
||||
name: PropertyName;
|
||||
questionToken?: QuestionToken;
|
||||
initializer: Expression;
|
||||
}
|
||||
|
||||
export interface ShorthandPropertyAssignment extends ObjectLiteralElement {
|
||||
export interface ShorthandPropertyAssignment extends ObjectLiteralElement, JSDocContainer {
|
||||
parent: ObjectLiteralExpression;
|
||||
kind: SyntaxKind.ShorthandPropertyAssignment;
|
||||
name: Identifier;
|
||||
questionToken?: QuestionToken;
|
||||
@@ -760,7 +801,8 @@ namespace ts {
|
||||
objectAssignmentInitializer?: Expression;
|
||||
}
|
||||
|
||||
export interface SpreadAssignment extends ObjectLiteralElement {
|
||||
export interface SpreadAssignment extends ObjectLiteralElement, JSDocContainer {
|
||||
parent: ObjectLiteralExpression;
|
||||
kind: SyntaxKind.SpreadAssignment;
|
||||
expression: Expression;
|
||||
}
|
||||
@@ -778,7 +820,7 @@ namespace ts {
|
||||
export interface VariableLikeDeclaration extends NamedDeclaration {
|
||||
propertyName?: PropertyName;
|
||||
dotDotDotToken?: DotDotDotToken;
|
||||
name?: DeclarationName; // May be missing for ParameterDeclaration, see comment there
|
||||
name: DeclarationName;
|
||||
questionToken?: QuestionToken;
|
||||
type?: TypeNode;
|
||||
initializer?: Expression;
|
||||
@@ -812,7 +854,7 @@ namespace ts {
|
||||
* - MethodDeclaration
|
||||
* - AccessorDeclaration
|
||||
*/
|
||||
export interface FunctionLikeDeclarationBase extends SignatureDeclaration {
|
||||
export interface FunctionLikeDeclarationBase extends SignatureDeclarationBase {
|
||||
_functionLikeDeclarationBrand: any;
|
||||
|
||||
asteriskToken?: AsteriskToken;
|
||||
@@ -843,7 +885,7 @@ namespace ts {
|
||||
body?: FunctionBody;
|
||||
}
|
||||
|
||||
export interface MethodSignature extends SignatureDeclaration, TypeElement {
|
||||
export interface MethodSignature extends SignatureDeclarationBase, TypeElement {
|
||||
kind: SyntaxKind.MethodSignature;
|
||||
name: PropertyName;
|
||||
}
|
||||
@@ -857,13 +899,13 @@ namespace ts {
|
||||
// Because of this, it may be necessary to determine what sort of MethodDeclaration you have
|
||||
// at later stages of the compiler pipeline. In that case, you can either check the parent kind
|
||||
// of the method, or use helpers like isObjectLiteralMethodDeclaration
|
||||
export interface MethodDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement {
|
||||
export interface MethodDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
|
||||
kind: SyntaxKind.MethodDeclaration;
|
||||
name: PropertyName;
|
||||
body?: FunctionBody;
|
||||
}
|
||||
|
||||
export interface ConstructorDeclaration extends FunctionLikeDeclarationBase, ClassElement {
|
||||
export interface ConstructorDeclaration extends FunctionLikeDeclarationBase, ClassElement, JSDocContainer {
|
||||
kind: SyntaxKind.Constructor;
|
||||
parent?: ClassDeclaration | ClassExpression;
|
||||
body?: FunctionBody;
|
||||
@@ -877,7 +919,7 @@ namespace ts {
|
||||
|
||||
// See the comment on MethodDeclaration for the intuition behind GetAccessorDeclaration being a
|
||||
// ClassElement and an ObjectLiteralElement.
|
||||
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement {
|
||||
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
|
||||
kind: SyntaxKind.GetAccessor;
|
||||
parent?: ClassDeclaration | ClassExpression | ObjectLiteralExpression;
|
||||
name: PropertyName;
|
||||
@@ -886,7 +928,7 @@ namespace ts {
|
||||
|
||||
// See the comment on MethodDeclaration for the intuition behind SetAccessorDeclaration being a
|
||||
// ClassElement and an ObjectLiteralElement.
|
||||
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement {
|
||||
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
|
||||
kind: SyntaxKind.SetAccessor;
|
||||
parent?: ClassDeclaration | ClassExpression | ObjectLiteralExpression;
|
||||
name: PropertyName;
|
||||
@@ -895,7 +937,7 @@ namespace ts {
|
||||
|
||||
export type AccessorDeclaration = GetAccessorDeclaration | SetAccessorDeclaration;
|
||||
|
||||
export interface IndexSignatureDeclaration extends SignatureDeclaration, ClassElement, TypeElement {
|
||||
export interface IndexSignatureDeclaration extends SignatureDeclarationBase, ClassElement, TypeElement {
|
||||
kind: SyntaxKind.IndexSignature;
|
||||
parent?: ClassDeclaration | ClassExpression | InterfaceDeclaration | TypeLiteralNode;
|
||||
}
|
||||
@@ -924,11 +966,11 @@ namespace ts {
|
||||
|
||||
export type FunctionOrConstructorTypeNode = FunctionTypeNode | ConstructorTypeNode;
|
||||
|
||||
export interface FunctionTypeNode extends TypeNode, SignatureDeclaration {
|
||||
export interface FunctionTypeNode extends TypeNode, SignatureDeclarationBase {
|
||||
kind: SyntaxKind.FunctionType;
|
||||
}
|
||||
|
||||
export interface ConstructorTypeNode extends TypeNode, SignatureDeclaration {
|
||||
export interface ConstructorTypeNode extends TypeNode, SignatureDeclarationBase {
|
||||
kind: SyntaxKind.ConstructorType;
|
||||
}
|
||||
|
||||
@@ -942,6 +984,7 @@ namespace ts {
|
||||
|
||||
export interface TypePredicateNode extends TypeNode {
|
||||
kind: SyntaxKind.TypePredicate;
|
||||
parent?: SignatureDeclaration;
|
||||
parameterName: Identifier | ThisTypeNode;
|
||||
type: TypeNode;
|
||||
}
|
||||
@@ -998,7 +1041,6 @@ namespace ts {
|
||||
|
||||
export interface MappedTypeNode extends TypeNode, Declaration {
|
||||
kind: SyntaxKind.MappedType;
|
||||
parent?: TypeAliasDeclaration;
|
||||
readonlyToken?: ReadonlyToken;
|
||||
typeParameter: TypeParameterDeclaration;
|
||||
questionToken?: QuestionToken;
|
||||
@@ -1007,7 +1049,7 @@ namespace ts {
|
||||
|
||||
export interface LiteralTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.LiteralType;
|
||||
literal: Expression;
|
||||
literal: BooleanLiteral | LiteralExpression | PrefixUnaryExpression;
|
||||
}
|
||||
|
||||
export interface StringLiteral extends LiteralExpression {
|
||||
@@ -1351,13 +1393,13 @@ namespace ts {
|
||||
export type FunctionBody = Block;
|
||||
export type ConciseBody = FunctionBody | Expression;
|
||||
|
||||
export interface FunctionExpression extends PrimaryExpression, FunctionLikeDeclarationBase {
|
||||
export interface FunctionExpression extends PrimaryExpression, FunctionLikeDeclarationBase, JSDocContainer {
|
||||
kind: SyntaxKind.FunctionExpression;
|
||||
name?: Identifier;
|
||||
body: FunctionBody; // Required, whereas the member inherited from FunctionDeclaration is optional
|
||||
}
|
||||
|
||||
export interface ArrowFunction extends Expression, FunctionLikeDeclarationBase {
|
||||
export interface ArrowFunction extends Expression, FunctionLikeDeclarationBase, JSDocContainer {
|
||||
kind: SyntaxKind.ArrowFunction;
|
||||
equalsGreaterThanToken: EqualsGreaterThanToken;
|
||||
body: ConciseBody;
|
||||
@@ -1436,7 +1478,7 @@ namespace ts {
|
||||
literal: TemplateMiddle | TemplateTail;
|
||||
}
|
||||
|
||||
export interface ParenthesizedExpression extends PrimaryExpression {
|
||||
export interface ParenthesizedExpression extends PrimaryExpression, JSDocContainer {
|
||||
kind: SyntaxKind.ParenthesizedExpression;
|
||||
expression: Expression;
|
||||
}
|
||||
@@ -1450,6 +1492,7 @@ namespace ts {
|
||||
|
||||
export interface SpreadElement extends Expression {
|
||||
kind: SyntaxKind.SpreadElement;
|
||||
parent?: ArrayLiteralExpression | CallExpression | NewExpression;
|
||||
expression: Expression;
|
||||
}
|
||||
|
||||
@@ -1691,12 +1734,12 @@ namespace ts {
|
||||
/*@internal*/ multiLine?: boolean;
|
||||
}
|
||||
|
||||
export interface VariableStatement extends Statement {
|
||||
export interface VariableStatement extends Statement, JSDocContainer {
|
||||
kind: SyntaxKind.VariableStatement;
|
||||
declarationList: VariableDeclarationList;
|
||||
}
|
||||
|
||||
export interface ExpressionStatement extends Statement {
|
||||
export interface ExpressionStatement extends Statement, JSDocContainer {
|
||||
kind: SyntaxKind.ExpressionStatement;
|
||||
expression: Expression;
|
||||
}
|
||||
@@ -1802,7 +1845,7 @@ namespace ts {
|
||||
|
||||
export type CaseOrDefaultClause = CaseClause | DefaultClause;
|
||||
|
||||
export interface LabeledStatement extends Statement {
|
||||
export interface LabeledStatement extends Statement, JSDocContainer {
|
||||
kind: SyntaxKind.LabeledStatement;
|
||||
label: Identifier;
|
||||
statement: Statement;
|
||||
@@ -1829,7 +1872,7 @@ namespace ts {
|
||||
|
||||
export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag;
|
||||
|
||||
export interface ClassLikeDeclaration extends NamedDeclaration {
|
||||
export interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
|
||||
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
|
||||
name?: Identifier;
|
||||
typeParameters?: NodeArray<TypeParameterDeclaration>;
|
||||
@@ -1837,15 +1880,17 @@ namespace ts {
|
||||
members: NodeArray<ClassElement>;
|
||||
}
|
||||
|
||||
export interface ClassDeclaration extends ClassLikeDeclaration, DeclarationStatement {
|
||||
export interface ClassDeclaration extends ClassLikeDeclarationBase, DeclarationStatement {
|
||||
kind: SyntaxKind.ClassDeclaration;
|
||||
name?: Identifier;
|
||||
}
|
||||
|
||||
export interface ClassExpression extends ClassLikeDeclaration, PrimaryExpression {
|
||||
export interface ClassExpression extends ClassLikeDeclarationBase, PrimaryExpression {
|
||||
kind: SyntaxKind.ClassExpression;
|
||||
}
|
||||
|
||||
export type ClassLikeDeclaration = ClassDeclaration | ClassExpression;
|
||||
|
||||
export interface ClassElement extends NamedDeclaration {
|
||||
_classElementBrand: any;
|
||||
name?: PropertyName;
|
||||
@@ -1857,7 +1902,7 @@ namespace ts {
|
||||
questionToken?: QuestionToken;
|
||||
}
|
||||
|
||||
export interface InterfaceDeclaration extends DeclarationStatement {
|
||||
export interface InterfaceDeclaration extends DeclarationStatement, JSDocContainer {
|
||||
kind: SyntaxKind.InterfaceDeclaration;
|
||||
name: Identifier;
|
||||
typeParameters?: NodeArray<TypeParameterDeclaration>;
|
||||
@@ -1872,14 +1917,14 @@ namespace ts {
|
||||
types: NodeArray<ExpressionWithTypeArguments>;
|
||||
}
|
||||
|
||||
export interface TypeAliasDeclaration extends DeclarationStatement {
|
||||
export interface TypeAliasDeclaration extends DeclarationStatement, JSDocContainer {
|
||||
kind: SyntaxKind.TypeAliasDeclaration;
|
||||
name: Identifier;
|
||||
typeParameters?: NodeArray<TypeParameterDeclaration>;
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
export interface EnumMember extends NamedDeclaration {
|
||||
export interface EnumMember extends NamedDeclaration, JSDocContainer {
|
||||
kind: SyntaxKind.EnumMember;
|
||||
parent?: EnumDeclaration;
|
||||
// This does include ComputedPropertyName, but the parser will give an error
|
||||
@@ -1888,7 +1933,7 @@ namespace ts {
|
||||
initializer?: Expression;
|
||||
}
|
||||
|
||||
export interface EnumDeclaration extends DeclarationStatement {
|
||||
export interface EnumDeclaration extends DeclarationStatement, JSDocContainer {
|
||||
kind: SyntaxKind.EnumDeclaration;
|
||||
name: Identifier;
|
||||
members: NodeArray<EnumMember>;
|
||||
@@ -1898,7 +1943,7 @@ namespace ts {
|
||||
|
||||
export type ModuleBody = NamespaceBody | JSDocNamespaceBody;
|
||||
|
||||
export interface ModuleDeclaration extends DeclarationStatement {
|
||||
export interface ModuleDeclaration extends DeclarationStatement, JSDocContainer {
|
||||
kind: SyntaxKind.ModuleDeclaration;
|
||||
parent?: ModuleBody | SourceFile;
|
||||
name: ModuleName;
|
||||
@@ -1932,7 +1977,7 @@ namespace ts {
|
||||
* - import x = require("mod");
|
||||
* - import x = M.x;
|
||||
*/
|
||||
export interface ImportEqualsDeclaration extends DeclarationStatement {
|
||||
export interface ImportEqualsDeclaration extends DeclarationStatement, JSDocContainer {
|
||||
kind: SyntaxKind.ImportEqualsDeclaration;
|
||||
parent?: SourceFile | ModuleBlock;
|
||||
name: Identifier;
|
||||
@@ -2085,7 +2130,7 @@ namespace ts {
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
export interface JSDocFunctionType extends JSDocType, SignatureDeclaration {
|
||||
export interface JSDocFunctionType extends JSDocType, SignatureDeclarationBase {
|
||||
kind: SyntaxKind.JSDocFunctionType;
|
||||
}
|
||||
|
||||
@@ -2098,6 +2143,7 @@ namespace ts {
|
||||
|
||||
export interface JSDoc extends Node {
|
||||
kind: SyntaxKind.JSDocComment;
|
||||
parent?: HasJSDoc;
|
||||
tags: NodeArray<JSDocTag> | undefined;
|
||||
comment: string | undefined;
|
||||
}
|
||||
@@ -2165,7 +2211,6 @@ namespace ts {
|
||||
export interface JSDocTypeLiteral extends JSDocType {
|
||||
kind: SyntaxKind.JSDocTypeLiteral;
|
||||
jsDocPropertyTags?: ReadonlyArray<JSDocPropertyLikeTag>;
|
||||
jsDocTypeTag?: JSDocTypeTag;
|
||||
/** If true, then this type literal represents an *array* of its type. */
|
||||
isArrayType?: boolean;
|
||||
}
|
||||
@@ -2468,6 +2513,8 @@ namespace ts {
|
||||
/* @internal */ getFileProcessingDiagnostics(): DiagnosticCollection;
|
||||
/* @internal */ getResolvedTypeReferenceDirectives(): Map<ResolvedTypeReferenceDirective>;
|
||||
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
|
||||
/* @internal */ isSourceFileDefaultLibrary(file: SourceFile): boolean;
|
||||
|
||||
// For testing purposes only.
|
||||
/* @internal */ structureIsReused?: StructureIsReused;
|
||||
|
||||
@@ -2658,7 +2705,8 @@ namespace ts {
|
||||
* So for `{ a } | { b }`, this will include both `a` and `b`.
|
||||
* Does not include properties of primitive types.
|
||||
*/
|
||||
/* @internal */ getAllPossiblePropertiesOfType(type: Type): Symbol[];
|
||||
/* @internal */ isArrayLikeType(type: Type): boolean;
|
||||
/* @internal */ getAllPossiblePropertiesOfTypes(type: ReadonlyArray<Type>): Symbol[];
|
||||
/* @internal */ resolveName(name: string, location: Node, meaning: SymbolFlags): Symbol | undefined;
|
||||
/* @internal */ getJsxNamespace(): string;
|
||||
}
|
||||
@@ -3171,6 +3219,7 @@ namespace ts {
|
||||
/* @internal */
|
||||
Nullable = Undefined | Null,
|
||||
Literal = StringLiteral | NumberLiteral | BooleanLiteral,
|
||||
Unit = Literal | Nullable,
|
||||
StringOrNumberLiteral = StringLiteral | NumberLiteral,
|
||||
/* @internal */
|
||||
DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null,
|
||||
@@ -3331,13 +3380,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface MappedType extends ObjectType {
|
||||
export interface MappedType extends AnonymousType {
|
||||
declaration: MappedTypeNode;
|
||||
typeParameter?: TypeParameter;
|
||||
constraintType?: Type;
|
||||
templateType?: Type;
|
||||
modifiersType?: Type;
|
||||
mapper?: TypeMapper; // Instantiation mapper
|
||||
}
|
||||
|
||||
export interface EvolvingArrayType extends ObjectType {
|
||||
@@ -3448,6 +3496,8 @@ namespace ts {
|
||||
/* @internal */
|
||||
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
|
||||
/* @internal */
|
||||
canonicalSignatureCache?: Signature; // Canonical version of signature (deferred)
|
||||
/* @internal */
|
||||
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
|
||||
/* @internal */
|
||||
typePredicate?: TypePredicate;
|
||||
@@ -3469,8 +3519,6 @@ namespace ts {
|
||||
/* @internal */
|
||||
export interface TypeMapper {
|
||||
(t: TypeParameter): Type;
|
||||
mappedTypes?: TypeParameter[]; // Types mapped by this mapper
|
||||
instantiations?: Type[]; // Cache of instantiations created using this type mapper.
|
||||
}
|
||||
|
||||
export const enum InferencePriority {
|
||||
@@ -3584,7 +3632,7 @@ namespace ts {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[];
|
||||
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | null | undefined;
|
||||
|
||||
export interface CompilerOptions {
|
||||
/*@internal*/ all?: boolean;
|
||||
@@ -4010,6 +4058,11 @@ namespace ts {
|
||||
* If accessing a non-index file, this should include its name e.g. "foo/bar".
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Name of a submodule within this package.
|
||||
* May be "".
|
||||
*/
|
||||
subModuleName: string;
|
||||
/** Version of the package, e.g. "1.2.3" */
|
||||
version: string;
|
||||
}
|
||||
@@ -4033,6 +4086,7 @@ namespace ts {
|
||||
primary: boolean;
|
||||
// The location of the .d.ts file we located, or undefined if resolution failed
|
||||
resolvedFileName?: string;
|
||||
packageId?: PackageId;
|
||||
}
|
||||
|
||||
export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
|
||||
@@ -4255,10 +4309,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
export const enum EmitHint {
|
||||
SourceFile, // Emitting a SourceFile
|
||||
Expression, // Emitting an Expression
|
||||
IdentifierName, // Emitting an IdentifierName
|
||||
Unspecified, // Emitting an otherwise unspecified node
|
||||
SourceFile, // Emitting a SourceFile
|
||||
Expression, // Emitting an Expression
|
||||
IdentifierName, // Emitting an IdentifierName
|
||||
MappedTypeParameter, // Emitting a TypeParameterDeclaration inside of a MappedTypeNode
|
||||
Unspecified, // Emitting an otherwise unspecified node
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
+307
-133
@@ -32,7 +32,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
const stringWriter = createSingleLineStringWriter();
|
||||
let stringWriterAcquired = false;
|
||||
|
||||
function createSingleLineStringWriter(): StringSymbolWriter {
|
||||
let str = "";
|
||||
@@ -62,15 +61,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function usingSingleLineStringWriter(action: (writer: StringSymbolWriter) => void): string {
|
||||
const oldString = stringWriter.string();
|
||||
try {
|
||||
Debug.assert(!stringWriterAcquired);
|
||||
stringWriterAcquired = true;
|
||||
action(stringWriter);
|
||||
return stringWriter.string();
|
||||
}
|
||||
finally {
|
||||
stringWriter.clear();
|
||||
stringWriterAcquired = false;
|
||||
stringWriter.writeKeyword(oldString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +104,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
|
||||
return a === b || a && b && a.name === b.name && a.version === b.version;
|
||||
return a === b || a && b && a.name === b.name && a.subModuleName === b.subModuleName && a.version === b.version;
|
||||
}
|
||||
|
||||
export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean {
|
||||
@@ -279,7 +277,7 @@ namespace ts {
|
||||
return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
|
||||
}
|
||||
|
||||
if (includeJsDoc && node.jsDoc && node.jsDoc.length > 0) {
|
||||
if (includeJsDoc && hasJSDocNodes(node)) {
|
||||
return getTokenPosOfNode(node.jsDoc[0]);
|
||||
}
|
||||
|
||||
@@ -502,13 +500,11 @@ namespace ts {
|
||||
case SyntaxKind.ArrowFunction:
|
||||
return true;
|
||||
default:
|
||||
staticAssertNever(node);
|
||||
assertTypeIsNever(node);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function staticAssertNever(_: never): void {}
|
||||
|
||||
// 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 {
|
||||
@@ -1257,57 +1253,60 @@ namespace ts {
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
const parent = node.parent;
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.BindingElement:
|
||||
return (<VariableLikeDeclaration>parent).initializer === node;
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
case SyntaxKind.IfStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.ReturnStatement:
|
||||
case SyntaxKind.WithStatement:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
case SyntaxKind.CaseClause:
|
||||
case SyntaxKind.ThrowStatement:
|
||||
return (<ExpressionStatement>parent).expression === node;
|
||||
case SyntaxKind.ForStatement:
|
||||
const forStatement = <ForStatement>parent;
|
||||
return (forStatement.initializer === node && forStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
|
||||
forStatement.condition === node ||
|
||||
forStatement.incrementor === node;
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
const forInStatement = <ForInStatement | ForOfStatement>parent;
|
||||
return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
|
||||
forInStatement.expression === node;
|
||||
case SyntaxKind.TypeAssertionExpression:
|
||||
case SyntaxKind.AsExpression:
|
||||
return node === (<AssertionExpression>parent).expression;
|
||||
case SyntaxKind.TemplateSpan:
|
||||
return node === (<TemplateSpan>parent).expression;
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
return node === (<ComputedPropertyName>parent).expression;
|
||||
case SyntaxKind.Decorator:
|
||||
case SyntaxKind.JsxExpression:
|
||||
case SyntaxKind.JsxSpreadAttribute:
|
||||
case SyntaxKind.SpreadAssignment:
|
||||
return true;
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
return (<ExpressionWithTypeArguments>parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(parent);
|
||||
default:
|
||||
if (isPartOfExpression(parent)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return isInExpressionContext(node);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isInExpressionContext(node: Node): boolean {
|
||||
const parent = node.parent;
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.BindingElement:
|
||||
return (<VariableLikeDeclaration>parent).initializer === node;
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
case SyntaxKind.IfStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.ReturnStatement:
|
||||
case SyntaxKind.WithStatement:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
case SyntaxKind.CaseClause:
|
||||
case SyntaxKind.ThrowStatement:
|
||||
return (<ExpressionStatement>parent).expression === node;
|
||||
case SyntaxKind.ForStatement:
|
||||
const forStatement = <ForStatement>parent;
|
||||
return (forStatement.initializer === node && forStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
|
||||
forStatement.condition === node ||
|
||||
forStatement.incrementor === node;
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
const forInStatement = <ForInStatement | ForOfStatement>parent;
|
||||
return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
|
||||
forInStatement.expression === node;
|
||||
case SyntaxKind.TypeAssertionExpression:
|
||||
case SyntaxKind.AsExpression:
|
||||
return node === (<AssertionExpression>parent).expression;
|
||||
case SyntaxKind.TemplateSpan:
|
||||
return node === (<TemplateSpan>parent).expression;
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
return node === (<ComputedPropertyName>parent).expression;
|
||||
case SyntaxKind.Decorator:
|
||||
case SyntaxKind.JsxExpression:
|
||||
case SyntaxKind.JsxSpreadAttribute:
|
||||
case SyntaxKind.SpreadAssignment:
|
||||
return true;
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
return (<ExpressionWithTypeArguments>parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(parent);
|
||||
default:
|
||||
return isPartOfExpression(parent);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isExternalModuleImportEqualsDeclaration(node: Node) {
|
||||
@@ -1327,11 +1326,11 @@ namespace ts {
|
||||
return isInJavaScriptFile(file);
|
||||
}
|
||||
|
||||
export function isInJavaScriptFile(node: Node): boolean {
|
||||
export function isInJavaScriptFile(node: Node | undefined): boolean {
|
||||
return node && !!(node.flags & NodeFlags.JavaScriptFile);
|
||||
}
|
||||
|
||||
export function isInJSDoc(node: Node): boolean {
|
||||
export function isInJSDoc(node: Node | undefined): boolean {
|
||||
return node && !!(node.flags & NodeFlags.JSDoc);
|
||||
}
|
||||
|
||||
@@ -1495,15 +1494,6 @@ namespace ts {
|
||||
((node as JSDocFunctionType).parameters[0].name as Identifier).escapedText === "new";
|
||||
}
|
||||
|
||||
export function hasJSDocParameterTags(node: FunctionLikeDeclaration | SignatureDeclaration): boolean {
|
||||
return !!getFirstJSDocTag(node, SyntaxKind.JSDocParameterTag);
|
||||
}
|
||||
|
||||
function getFirstJSDocTag(node: Node, kind: SyntaxKind): JSDocTag | undefined {
|
||||
const tags = getJSDocTags(node);
|
||||
return find(tags, doc => doc.kind === kind);
|
||||
}
|
||||
|
||||
export function getAllJSDocs(node: Node): (JSDoc | JSDocTag)[] {
|
||||
if (isJSDocTypedefTag(node)) {
|
||||
return [node.parent];
|
||||
@@ -1511,16 +1501,7 @@ namespace ts {
|
||||
return getJSDocCommentsAndTags(node);
|
||||
}
|
||||
|
||||
export function getJSDocTags(node: Node): ReadonlyArray<JSDocTag> | undefined {
|
||||
let tags = node.jsDocCache;
|
||||
// If cache is 'null', that means we did the work of searching for JSDoc tags and came up with nothing.
|
||||
if (tags === undefined) {
|
||||
node.jsDocCache = tags = flatMap(getJSDocCommentsAndTags(node), j => isJSDoc(j) ? j.tags : j);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
function getJSDocCommentsAndTags(node: Node): (JSDoc | JSDocTag)[] {
|
||||
export function getJSDocCommentsAndTags(node: Node): (JSDoc | JSDocTag)[] {
|
||||
let result: Array<JSDoc | JSDocTag> | undefined;
|
||||
getJSDocCommentsAndTagsWorker(node);
|
||||
return result || emptyArray;
|
||||
@@ -1569,23 +1550,16 @@ namespace ts {
|
||||
result = addRange(result, getJSDocParameterTags(node as ParameterDeclaration));
|
||||
}
|
||||
|
||||
if (isVariableLike(node) && node.initializer) {
|
||||
if (isVariableLike(node) && node.initializer && hasJSDocNodes(node.initializer)) {
|
||||
result = addRange(result, node.initializer.jsDoc);
|
||||
}
|
||||
|
||||
result = addRange(result, node.jsDoc);
|
||||
if (hasJSDocNodes(node)) {
|
||||
result = addRange(result, node.jsDoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getJSDocParameterTags(param: ParameterDeclaration): JSDocParameterTag[] | undefined {
|
||||
if (param.name && isIdentifier(param.name)) {
|
||||
const name = param.name.escapedText;
|
||||
return getJSDocTags(param.parent).filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && isIdentifier(tag.name) && tag.name.escapedText === name) as JSDocParameterTag[];
|
||||
}
|
||||
// a binding pattern doesn't have a name, so it's not possible to match it a jsdoc parameter, which is identified by name
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Does the opposite of `getJSDocParameterTags`: given a JSDoc parameter, finds the parameter corresponding to it. */
|
||||
export function getParameterSymbolFromJSDoc(node: JSDocParameterTag): Symbol | undefined {
|
||||
if (node.symbol) {
|
||||
@@ -1611,39 +1585,6 @@ namespace ts {
|
||||
return find(typeParameters, p => p.name.escapedText === name);
|
||||
}
|
||||
|
||||
export function getJSDocType(node: Node): TypeNode {
|
||||
let tag: JSDocTypeTag | JSDocParameterTag = getFirstJSDocTag(node, SyntaxKind.JSDocTypeTag) as JSDocTypeTag;
|
||||
if (!tag && node.kind === SyntaxKind.Parameter) {
|
||||
const paramTags = getJSDocParameterTags(node as ParameterDeclaration);
|
||||
if (paramTags) {
|
||||
tag = find(paramTags, tag => !!tag.typeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
return tag && tag.typeExpression && tag.typeExpression.type;
|
||||
}
|
||||
|
||||
export function getJSDocAugmentsTag(node: Node): JSDocAugmentsTag {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocAugmentsTag) as JSDocAugmentsTag;
|
||||
}
|
||||
|
||||
export function getJSDocClassTag(node: Node): JSDocClassTag {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocClassTag) as JSDocClassTag;
|
||||
}
|
||||
|
||||
export function getJSDocReturnTag(node: Node): JSDocReturnTag {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocReturnTag) as JSDocReturnTag;
|
||||
}
|
||||
|
||||
export function getJSDocReturnType(node: Node): TypeNode {
|
||||
const returnTag = getJSDocReturnTag(node);
|
||||
return returnTag && returnTag.typeExpression && returnTag.typeExpression.type;
|
||||
}
|
||||
|
||||
export function getJSDocTemplateTag(node: Node): JSDocTemplateTag {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocTemplateTag) as JSDocTemplateTag;
|
||||
}
|
||||
|
||||
export function hasRestParameter(s: SignatureDeclaration): boolean {
|
||||
return isRestParameter(lastOrUndefined(s.parameters));
|
||||
}
|
||||
@@ -3504,6 +3445,46 @@ namespace ts {
|
||||
export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags {
|
||||
return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags;
|
||||
}
|
||||
|
||||
export function isWriteOnlyAccess(node: Node) {
|
||||
return accessKind(node) === AccessKind.Write;
|
||||
}
|
||||
|
||||
export function isWriteAccess(node: Node) {
|
||||
return accessKind(node) !== AccessKind.Read;
|
||||
}
|
||||
|
||||
const enum AccessKind {
|
||||
/** Only reads from a variable. */
|
||||
Read,
|
||||
/** Only writes to a variable without using the result. E.g.: `x++;`. */
|
||||
Write,
|
||||
/** Writes to a variable and uses the result as an expression. E.g.: `f(x++);`. */
|
||||
ReadWrite
|
||||
}
|
||||
function accessKind(node: Node): AccessKind {
|
||||
const { parent } = node;
|
||||
if (!parent) return AccessKind.Read;
|
||||
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
const { operator } = parent as PrefixUnaryExpression | PostfixUnaryExpression;
|
||||
return operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken ? writeOrReadWrite() : AccessKind.Read;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
const { left, operatorToken } = parent as BinaryExpression;
|
||||
return left === node && isAssignmentOperator(operatorToken.kind) ? writeOrReadWrite() : AccessKind.Read;
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
return (parent as PropertyAccessExpression).name !== node ? AccessKind.Read : accessKind(parent);
|
||||
default:
|
||||
return AccessKind.Read;
|
||||
}
|
||||
|
||||
function writeOrReadWrite(): AccessKind {
|
||||
// If grandparent is not an ExpressionStatement, this is used as an expression in addition to having a side effect.
|
||||
return parent.parent && parent.parent.kind === SyntaxKind.ExpressionStatement ? AccessKind.Write : AccessKind.ReadWrite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
@@ -3960,7 +3941,66 @@ namespace ts {
|
||||
return id;
|
||||
}
|
||||
|
||||
export function getNameOfDeclaration(declaration: Declaration): DeclarationName | undefined {
|
||||
/**
|
||||
* A JSDocTypedef tag has an _optional_ name field - if a name is not directly present, we should
|
||||
* attempt to draw the name from the node the declaration is on (as that declaration is what its' symbol
|
||||
* will be merged with)
|
||||
*/
|
||||
function nameForNamelessJSDocTypedef(declaration: JSDocTypedefTag): Identifier | undefined {
|
||||
const hostNode = declaration.parent.parent;
|
||||
if (!hostNode) {
|
||||
return undefined;
|
||||
}
|
||||
// Covers classes, functions - any named declaration host node
|
||||
if (isDeclaration(hostNode)) {
|
||||
return getDeclarationIdentifier(hostNode);
|
||||
}
|
||||
// Covers remaining cases
|
||||
switch (hostNode.kind) {
|
||||
case SyntaxKind.VariableStatement:
|
||||
if ((hostNode as VariableStatement).declarationList &&
|
||||
(hostNode as VariableStatement).declarationList.declarations[0]) {
|
||||
return getDeclarationIdentifier((hostNode as VariableStatement).declarationList.declarations[0]);
|
||||
}
|
||||
return undefined;
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
const expr = (hostNode as ExpressionStatement).expression;
|
||||
switch (expr.kind) {
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
return (expr as PropertyAccessExpression).name;
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
const arg = (expr as ElementAccessExpression).argumentExpression;
|
||||
if (isIdentifier(arg)) {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
case SyntaxKind.EndOfFileToken:
|
||||
return undefined;
|
||||
case SyntaxKind.ParenthesizedExpression: {
|
||||
return getDeclarationIdentifier(hostNode.expression);
|
||||
}
|
||||
case SyntaxKind.LabeledStatement: {
|
||||
if (isDeclaration(hostNode.statement) || isExpression(hostNode.statement)) {
|
||||
return getDeclarationIdentifier(hostNode.statement);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
default:
|
||||
Debug.assertNever(hostNode, "Found typedef tag attached to node which it should not be!");
|
||||
}
|
||||
}
|
||||
|
||||
function getDeclarationIdentifier(node: Declaration | Expression) {
|
||||
const name = getNameOfDeclaration(node);
|
||||
return isIdentifier(name) ? name : undefined;
|
||||
}
|
||||
|
||||
export function getNameOfJSDocTypedef(declaration: JSDocTypedefTag): Identifier | undefined {
|
||||
return declaration.name || nameForNamelessJSDocTypedef(declaration as JSDocTypedefTag);
|
||||
}
|
||||
|
||||
export function getNameOfDeclaration(declaration: Declaration | Expression): DeclarationName | undefined {
|
||||
if (!declaration) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -3979,10 +4019,121 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else if (declaration.kind === SyntaxKind.JSDocTypedefTag) {
|
||||
return getNameOfJSDocTypedef(declaration as JSDocTypedefTag);
|
||||
}
|
||||
else {
|
||||
return (declaration as NamedDeclaration).name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JSDoc parameter tags for the node if present.
|
||||
*
|
||||
* @remarks Returns any JSDoc param tag that matches the provided
|
||||
* parameter, whether a param tag on a containing function
|
||||
* expression, or a param tag on a variable declaration whose
|
||||
* initializer is the containing function. The tags closest to the
|
||||
* node are returned first, so in the previous example, the param
|
||||
* tag on the containing function expression would be first.
|
||||
*
|
||||
* Does not return tags for binding patterns, because JSDoc matches
|
||||
* parameters by name and binding patterns do not have a name.
|
||||
*/
|
||||
export function getJSDocParameterTags(param: ParameterDeclaration): ReadonlyArray<JSDocParameterTag> | undefined {
|
||||
if (param.name && isIdentifier(param.name)) {
|
||||
const name = param.name.escapedText;
|
||||
return getJSDocTags(param.parent).filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && isIdentifier(tag.name) && tag.name.escapedText === name) as JSDocParameterTag[];
|
||||
}
|
||||
// a binding pattern doesn't have a name, so it's not possible to match it a JSDoc parameter, which is identified by name
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the node has JSDoc parameter tags.
|
||||
*
|
||||
* @remarks Includes parameter tags that are not directly on the node,
|
||||
* for example on a variable declaration whose initializer is a function expression.
|
||||
*/
|
||||
export function hasJSDocParameterTags(node: FunctionLikeDeclaration | SignatureDeclaration): boolean {
|
||||
return !!getFirstJSDocTag(node, SyntaxKind.JSDocParameterTag);
|
||||
}
|
||||
|
||||
/** Gets the JSDoc augments tag for the node if present */
|
||||
export function getJSDocAugmentsTag(node: Node): JSDocAugmentsTag | undefined {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocAugmentsTag) as JSDocAugmentsTag;
|
||||
}
|
||||
|
||||
/** Gets the JSDoc class tag for the node if present */
|
||||
export function getJSDocClassTag(node: Node): JSDocClassTag | undefined {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocClassTag) as JSDocClassTag;
|
||||
}
|
||||
|
||||
/** Gets the JSDoc return tag for the node if present */
|
||||
export function getJSDocReturnTag(node: Node): JSDocReturnTag | undefined {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocReturnTag) as JSDocReturnTag;
|
||||
}
|
||||
|
||||
/** Gets the JSDoc template tag for the node if present */
|
||||
export function getJSDocTemplateTag(node: Node): JSDocTemplateTag | undefined {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocTemplateTag) as JSDocTemplateTag;
|
||||
}
|
||||
|
||||
/** Gets the JSDoc type tag for the node if present */
|
||||
export function getJSDocTypeTag(node: Node): JSDocTypeTag | undefined {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocTypeTag) as JSDocTypeTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type node for the node if provided via JSDoc.
|
||||
*
|
||||
* @remarks The search includes any JSDoc param tag that relates
|
||||
* to the provided parameter, for example a type tag on the
|
||||
* parameter itself, or a param tag on a containing function
|
||||
* expression, or a param tag on a variable declaration whose
|
||||
* initializer is the containing function. The tags closest to the
|
||||
* node are examined first, so in the previous example, the type
|
||||
* tag directly on the node would be returned.
|
||||
*/
|
||||
export function getJSDocType(node: Node): TypeNode | undefined {
|
||||
let tag: JSDocTypeTag | JSDocParameterTag = getFirstJSDocTag(node, SyntaxKind.JSDocTypeTag) as JSDocTypeTag;
|
||||
if (!tag && node.kind === SyntaxKind.Parameter) {
|
||||
const paramTags = getJSDocParameterTags(node as ParameterDeclaration);
|
||||
if (paramTags) {
|
||||
tag = find(paramTags, tag => !!tag.typeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
return tag && tag.typeExpression && tag.typeExpression.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the return type node for the node if provided via JSDoc's return tag.
|
||||
*
|
||||
* @remarks `getJSDocReturnTag` just gets the whole JSDoc tag. This function
|
||||
* gets the type from inside the braces.
|
||||
*/
|
||||
export function getJSDocReturnType(node: Node): TypeNode | undefined {
|
||||
const returnTag = getJSDocReturnTag(node);
|
||||
return returnTag && returnTag.typeExpression && returnTag.typeExpression.type;
|
||||
}
|
||||
|
||||
/** Get all JSDoc tags related to a node, including those on parent nodes. */
|
||||
export function getJSDocTags(node: Node): ReadonlyArray<JSDocTag> | undefined {
|
||||
let tags = (node as JSDocContainer).jsDocCache;
|
||||
// If cache is 'null', that means we did the work of searching for JSDoc tags and came up with nothing.
|
||||
if (tags === undefined) {
|
||||
(node as JSDocContainer).jsDocCache = tags = flatMap(getJSDocCommentsAndTags(node), j => isJSDoc(j) ? j.tags : j);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
/** Get the first JSDoc tag of a specified kind, or undefined if not present. */
|
||||
function getFirstJSDocTag(node: Node, kind: SyntaxKind): JSDocTag | undefined {
|
||||
const tags = getJSDocTags(node);
|
||||
return find(tags, doc => doc.kind === kind);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Simple node tests of the form `node.kind === SyntaxKind.Foo`.
|
||||
@@ -4666,8 +4817,7 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
export function isNodeArray<T extends Node>(array: ReadonlyArray<T>): array is NodeArray<T> {
|
||||
return array.hasOwnProperty("pos")
|
||||
&& array.hasOwnProperty("end");
|
||||
return array.hasOwnProperty("pos") && array.hasOwnProperty("end");
|
||||
}
|
||||
|
||||
// Literals
|
||||
@@ -4768,16 +4918,29 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function isFunctionLikeKind(kind: SyntaxKind): boolean {
|
||||
export function isFunctionLikeDeclaration(node: Node): node is FunctionLikeDeclaration {
|
||||
return node && isFunctionLikeDeclarationKind(node.kind);
|
||||
}
|
||||
|
||||
function isFunctionLikeDeclarationKind(kind: SyntaxKind): boolean {
|
||||
switch (kind) {
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function isFunctionLikeKind(kind: SyntaxKind): boolean {
|
||||
switch (kind) {
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
case SyntaxKind.IndexSignature:
|
||||
@@ -4785,9 +4948,14 @@ namespace ts {
|
||||
case SyntaxKind.JSDocFunctionType:
|
||||
case SyntaxKind.ConstructorType:
|
||||
return true;
|
||||
default:
|
||||
return isFunctionLikeDeclarationKind(kind);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
/* @internal */
|
||||
export function isFunctionOrModuleBlock(node: Node): boolean {
|
||||
return isSourceFile(node) || isModuleBlock(node) || isBlock(node) && isFunctionLike(node.parent);
|
||||
}
|
||||
|
||||
// Classes
|
||||
@@ -5367,4 +5535,10 @@ namespace ts {
|
||||
export function isJSDocTag(node: Node): boolean {
|
||||
return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode;
|
||||
}
|
||||
|
||||
/** True if has jsdoc nodes attached to it. */
|
||||
/* @internal */
|
||||
export function hasJSDocNodes(node: Node): node is HasJSDoc {
|
||||
return !!(node as JSDocContainer).jsDoc && (node as JSDocContainer).jsDoc.length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,6 +488,7 @@ namespace ts {
|
||||
nodesVisitor((<ArrowFunction>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
visitParameterList((<ArrowFunction>node).parameters, visitor, context, nodesVisitor),
|
||||
visitNode((<ArrowFunction>node).type, visitor, isTypeNode),
|
||||
visitNode((<ArrowFunction>node).equalsGreaterThanToken, visitor, isToken),
|
||||
visitFunctionBody((<ArrowFunction>node).body, visitor, context));
|
||||
|
||||
case SyntaxKind.DeleteExpression:
|
||||
@@ -523,7 +524,9 @@ namespace ts {
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
return updateConditional(<ConditionalExpression>node,
|
||||
visitNode((<ConditionalExpression>node).condition, visitor, isExpression),
|
||||
visitNode((<ConditionalExpression>node).questionToken, visitor, isToken),
|
||||
visitNode((<ConditionalExpression>node).whenTrue, visitor, isExpression),
|
||||
visitNode((<ConditionalExpression>node).colonToken, visitor, isToken),
|
||||
visitNode((<ConditionalExpression>node).whenFalse, visitor, isExpression));
|
||||
|
||||
case SyntaxKind.TemplateExpression:
|
||||
|
||||
@@ -11,19 +11,13 @@ const enum CompilerTestType {
|
||||
class CompilerBaselineRunner extends RunnerBase {
|
||||
private basePath = "tests/cases";
|
||||
private testSuiteName: TestRunnerKind;
|
||||
private errors: boolean;
|
||||
private emit: boolean;
|
||||
private decl: boolean;
|
||||
private output: boolean;
|
||||
|
||||
public options: string;
|
||||
|
||||
constructor(public testType: CompilerTestType) {
|
||||
super();
|
||||
this.errors = true;
|
||||
this.emit = true;
|
||||
this.decl = true;
|
||||
this.output = true;
|
||||
if (testType === CompilerTestType.Conformance) {
|
||||
this.testSuiteName = "conformance";
|
||||
}
|
||||
@@ -141,7 +135,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
|
||||
// check errors
|
||||
it("Correct errors for " + fileName, () => {
|
||||
Harness.Compiler.doErrorBaseline(justName, tsConfigFiles.concat(toBeCompiled, otherFiles), result.errors);
|
||||
Harness.Compiler.doErrorBaseline(justName, tsConfigFiles.concat(toBeCompiled, otherFiles), result.errors, !!options.pretty);
|
||||
});
|
||||
|
||||
it (`Correct module resolution tracing for ${fileName}`, () => {
|
||||
@@ -214,26 +208,14 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
|
||||
private parseOptions() {
|
||||
if (this.options && this.options.length > 0) {
|
||||
this.errors = false;
|
||||
this.emit = false;
|
||||
this.decl = false;
|
||||
this.output = false;
|
||||
|
||||
const opts = this.options.split(",");
|
||||
for (let i = 0; i < opts.length; i++) {
|
||||
switch (opts[i]) {
|
||||
case "error":
|
||||
this.errors = true;
|
||||
break;
|
||||
case "emit":
|
||||
this.emit = true;
|
||||
break;
|
||||
case "decl":
|
||||
this.decl = true;
|
||||
break;
|
||||
case "output":
|
||||
this.output = true;
|
||||
break;
|
||||
default:
|
||||
throw new Error("unsupported flag");
|
||||
}
|
||||
|
||||
+123
-83
@@ -22,10 +22,6 @@
|
||||
namespace FourSlash {
|
||||
ts.disableIncrementalParsing = false;
|
||||
|
||||
function normalizeNewLines(s: string) {
|
||||
return s.replace(/\r\n/g, "\n");
|
||||
}
|
||||
|
||||
// Represents a parsed source file with metadata
|
||||
export interface FourSlashFile {
|
||||
// The contents of the file (with markers, etc stripped out)
|
||||
@@ -364,7 +360,7 @@ namespace FourSlash {
|
||||
baseIndentSize: 0,
|
||||
indentSize: 4,
|
||||
tabSize: 4,
|
||||
newLineCharacter: Harness.IO.newLine(),
|
||||
newLineCharacter: "\n",
|
||||
convertTabsToSpaces: true,
|
||||
indentStyle: ts.IndentStyle.Smart,
|
||||
insertSpaceAfterCommaDelimiter: true,
|
||||
@@ -762,7 +758,7 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionsAt(markerName: string, expected: string[]) {
|
||||
public verifyCompletionsAt(markerName: string, expected: string[], options?: FourSlashInterface.CompletionsAtOptions) {
|
||||
this.goToMarker(markerName);
|
||||
|
||||
const actualCompletions = this.getCompletionListAtCaret();
|
||||
@@ -770,6 +766,10 @@ namespace FourSlash {
|
||||
this.raiseError(`No completions at position '${this.currentCaretPosition}'.`);
|
||||
}
|
||||
|
||||
if (options && options.isNewIdentifierLocation !== undefined && actualCompletions.isNewIdentifierLocation !== options.isNewIdentifierLocation) {
|
||||
this.raiseError(`Expected 'isNewIdentifierLocation' to be ${options.isNewIdentifierLocation}, got ${actualCompletions.isNewIdentifierLocation}`);
|
||||
}
|
||||
|
||||
const actual = actualCompletions.entries;
|
||||
|
||||
if (actual.length !== expected.length) {
|
||||
@@ -1599,7 +1599,7 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public printCurrentFileState(makeWhitespaceVisible: boolean, makeCaretVisible: boolean) {
|
||||
public printCurrentFileState(showWhitespace: boolean, makeCaretVisible: boolean) {
|
||||
for (const file of this.testData.files) {
|
||||
const active = (this.activeFile === file);
|
||||
Harness.IO.log(`=== Script (${file.fileName}) ${(active ? "(active, cursor at |)" : "")} ===`);
|
||||
@@ -1607,8 +1607,8 @@ namespace FourSlash {
|
||||
if (active) {
|
||||
content = content.substr(0, this.currentCaretPosition) + (makeCaretVisible ? "|" : "") + content.substr(this.currentCaretPosition);
|
||||
}
|
||||
if (makeWhitespaceVisible) {
|
||||
content = TestState.makeWhitespaceVisible(content);
|
||||
if (showWhitespace) {
|
||||
content = makeWhitespaceVisible(content);
|
||||
}
|
||||
Harness.IO.log(content);
|
||||
}
|
||||
@@ -2124,10 +2124,8 @@ namespace FourSlash {
|
||||
|
||||
public verifyCurrentFileContent(text: string) {
|
||||
const actual = this.getFileContent(this.activeFile.fileName);
|
||||
if (normalizeNewLines(actual) !== normalizeNewLines(text)) {
|
||||
throw new Error("verifyCurrentFileContent\n" +
|
||||
"\tExpected: \"" + TestState.makeWhitespaceVisible(text) + "\"\n" +
|
||||
"\t Actual: \"" + TestState.makeWhitespaceVisible(actual) + "\"");
|
||||
if (actual !== text) {
|
||||
throw new Error(`verifyCurrentFileContent failed:\n${showTextDiff(text, actual)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2301,11 +2299,11 @@ namespace FourSlash {
|
||||
const actualText = this.rangeText(ranges[0]);
|
||||
|
||||
const result = includeWhiteSpace
|
||||
? normalizeNewLines(actualText) === normalizeNewLines(expectedText)
|
||||
? actualText === expectedText
|
||||
: this.removeWhitespace(actualText) === this.removeWhitespace(expectedText);
|
||||
|
||||
if (!result) {
|
||||
this.raiseError(`Actual text doesn't match expected text. Actual:\n'${actualText}'\nExpected:\n'${expectedText}'`);
|
||||
this.raiseError(`Actual range text doesn't match expected text.\n${showTextDiff(expectedText, actualText)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2399,18 +2397,22 @@ namespace FourSlash {
|
||||
const originalContent = scriptInfo.content;
|
||||
for (const codeFix of codeFixes) {
|
||||
this.applyEdits(codeFix.changes[0].fileName, codeFix.changes[0].textChanges, /*isFormattingEdit*/ false);
|
||||
actualTextArray.push(this.normalizeNewlines(this.rangeText(ranges[0])));
|
||||
let text = this.rangeText(ranges[0]);
|
||||
// TODO:GH#18445 (remove this line to see errors in many `importNameCodeFix` tests)
|
||||
text = text.replace(/\r\n/g, "\n");
|
||||
actualTextArray.push(text);
|
||||
scriptInfo.updateContent(originalContent);
|
||||
}
|
||||
const sortedExpectedArray = ts.map(expectedTextArray, str => this.normalizeNewlines(str)).sort();
|
||||
const sortedExpectedArray = expectedTextArray.sort();
|
||||
const sortedActualArray = actualTextArray.sort();
|
||||
if (!ts.arrayIsEqualTo(sortedExpectedArray, sortedActualArray)) {
|
||||
this.raiseError(
|
||||
`Actual text array doesn't match expected text array. \nActual: \n'${sortedActualArray.join("\n\n")}'\n---\nExpected: \n'${sortedExpectedArray.join("\n\n")}'`);
|
||||
}
|
||||
ts.zipWith(sortedExpectedArray, sortedActualArray, (expected, actual, index) => {
|
||||
if (expected !== actual) {
|
||||
this.raiseError(`Import fix at index ${index} doesn't match.\n${showTextDiff(expected, actual)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public verifyDocCommentTemplate(expected?: ts.TextInsertion) {
|
||||
public verifyDocCommentTemplate(expected: ts.TextInsertion | undefined) {
|
||||
const name = "verifyDocCommentTemplate";
|
||||
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
|
||||
@@ -2427,7 +2429,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
if (actual.newText !== expected.newText) {
|
||||
this.raiseError(`${name} failed - expected insertion:\n"${this.clarifyNewlines(expected.newText)}"\nactual insertion:\n"${this.clarifyNewlines(actual.newText)}"`);
|
||||
this.raiseError(`${name} failed for expected insertion.\n${showTextDiff(expected.newText, actual.newText)}`);
|
||||
}
|
||||
|
||||
if (actual.caretOffset !== expected.caretOffset) {
|
||||
@@ -2436,17 +2438,6 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
private clarifyNewlines(str: string) {
|
||||
return str.replace(/\r?\n/g, lineEnding => {
|
||||
const representation = lineEnding === "\r\n" ? "CRLF" : "LF";
|
||||
return "# - " + representation + lineEnding;
|
||||
});
|
||||
}
|
||||
|
||||
private normalizeNewlines(str: string) {
|
||||
return str.replace(/\r?\n/g, "\n");
|
||||
}
|
||||
|
||||
public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) {
|
||||
|
||||
const openBraceMap = ts.createMapFromTemplate<ts.CharacterCodes>({
|
||||
@@ -2580,20 +2571,29 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyNavigationBar(json: any) {
|
||||
const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
|
||||
if (JSON.stringify(items, replacer) !== JSON.stringify(json)) {
|
||||
this.raiseError(`verifyNavigationBar failed - expected: ${stringify(json)}, got: ${stringify(items, replacer)}`);
|
||||
public verifyNavigationBar(json: any, options: { checkSpans?: boolean } | undefined) {
|
||||
this.verifyNavigationTreeOrBar(json, this.languageService.getNavigationBarItems(this.activeFile.fileName), "Bar", options);
|
||||
}
|
||||
|
||||
public verifyNavigationTree(json: any, options: { checkSpans?: boolean } | undefined) {
|
||||
this.verifyNavigationTreeOrBar(json, this.languageService.getNavigationTree(this.activeFile.fileName), "Tree", options);
|
||||
}
|
||||
|
||||
private verifyNavigationTreeOrBar(json: any, tree: any, name: "Tree" | "Bar", options: { checkSpans?: boolean } | undefined) {
|
||||
if (JSON.stringify(tree, replacer) !== JSON.stringify(json)) {
|
||||
this.raiseError(`verifyNavigation${name} failed - expected: ${stringify(json)}, got: ${stringify(tree, replacer)}`);
|
||||
}
|
||||
|
||||
// Make the data easier to read.
|
||||
function replacer(key: string, value: any) {
|
||||
switch (key) {
|
||||
case "spans":
|
||||
// We won't ever check this.
|
||||
return undefined;
|
||||
return options && options.checkSpans ? value : undefined;
|
||||
case "start":
|
||||
case "length":
|
||||
// Never omit the values in a span, even if they are 0.
|
||||
return value;
|
||||
case "childItems":
|
||||
return value.length === 0 ? undefined : value;
|
||||
return !value || value.length === 0 ? undefined : value;
|
||||
default:
|
||||
// Omit falsy values, those are presumed to be the default.
|
||||
return value || undefined;
|
||||
@@ -2601,18 +2601,6 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyNavigationTree(json: any) {
|
||||
const tree = this.languageService.getNavigationTree(this.activeFile.fileName);
|
||||
if (JSON.stringify(tree, replacer) !== JSON.stringify(json)) {
|
||||
this.raiseError(`verifyNavigationTree failed - expected: ${stringify(json)}, got: ${stringify(tree, replacer)}`);
|
||||
}
|
||||
|
||||
function replacer(key: string, value: any) {
|
||||
// Don't check "spans", and omit falsy values.
|
||||
return key === "spans" ? undefined : (value || undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public printNavigationItems(searchValue: string) {
|
||||
const items = this.languageService.getNavigateToItems(searchValue);
|
||||
Harness.IO.log(`NavigationItems list (${items.length} items)`);
|
||||
@@ -2754,11 +2742,11 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
private getSelection() {
|
||||
return ({
|
||||
private getSelection(): ts.TextRange {
|
||||
return {
|
||||
pos: this.currentCaretPosition,
|
||||
end: this.selectionEnd === -1 ? this.currentCaretPosition : this.selectionEnd
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public verifyRefactorAvailable(negative: boolean, name: string, actionName?: string) {
|
||||
@@ -2799,7 +2787,7 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public applyRefactor({ refactorName, actionName, actionDescription }: FourSlashInterface.ApplyRefactorOptions) {
|
||||
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);
|
||||
@@ -2819,6 +2807,35 @@ namespace FourSlash {
|
||||
for (const edit of editInfo.edits) {
|
||||
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
|
||||
}
|
||||
|
||||
const { renamePosition, newContent } = parseNewContent();
|
||||
|
||||
this.verifyCurrentFileContent(newContent);
|
||||
|
||||
if (renamePosition === undefined) {
|
||||
if (editInfo.renameLocation !== undefined) {
|
||||
this.raiseError(`Did not expect a rename location, got ${editInfo.renameLocation}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: test editInfo.renameFilename value
|
||||
assert.isDefined(editInfo.renameFilename);
|
||||
if (renamePosition !== editInfo.renameLocation) {
|
||||
this.raiseError(`Expected rename position of ${renamePosition}, but got ${editInfo.renameLocation}`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseNewContent(): { renamePosition: number | undefined, newContent: string } {
|
||||
const renamePosition = newContentWithRenameMarker.indexOf("/*RENAME*/");
|
||||
if (renamePosition === -1) {
|
||||
return { renamePosition: undefined, newContent: newContentWithRenameMarker };
|
||||
}
|
||||
else {
|
||||
const newContent = newContentWithRenameMarker.slice(0, renamePosition) + newContentWithRenameMarker.slice(renamePosition + "/*RENAME*/".length);
|
||||
return { renamePosition, newContent };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public verifyFileAfterApplyingRefactorAtMarker(
|
||||
@@ -2845,8 +2862,8 @@ namespace FourSlash {
|
||||
}
|
||||
const actualContent = this.getFileContent(this.activeFile.fileName);
|
||||
|
||||
if (this.normalizeNewlines(actualContent) !== this.normalizeNewlines(expectedContent)) {
|
||||
this.raiseError(`verifyFileAfterApplyingRefactors failed: expected:\n${expectedContent}\nactual:\n${actualContent}`);
|
||||
if (actualContent !== expectedContent) {
|
||||
this.raiseError(`verifyFileAfterApplyingRefactors failed:\n${showTextDiff(expectedContent, actualContent)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2981,10 +2998,6 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
private static makeWhitespaceVisible(text: string) {
|
||||
return text.replace(/ /g, "\u00B7").replace(/\r/g, "\u00B6").replace(/\n/g, "\u2193\n").replace(/\t/g, "\u2192\ ");
|
||||
}
|
||||
|
||||
public setCancelled(numberOfCalls: number): void {
|
||||
this.cancellationToken.setCancelled(numberOfCalls);
|
||||
}
|
||||
@@ -3286,12 +3299,7 @@ ${code}
|
||||
let column = 1;
|
||||
|
||||
const flush = (lastSafeCharIndex: number) => {
|
||||
if (lastSafeCharIndex === undefined) {
|
||||
output = output + content.substr(lastNormalCharPosition);
|
||||
}
|
||||
else {
|
||||
output = output + content.substr(lastNormalCharPosition, lastSafeCharIndex - lastNormalCharPosition);
|
||||
}
|
||||
output = output + content.substr(lastNormalCharPosition, lastSafeCharIndex === undefined ? undefined : lastSafeCharIndex - lastNormalCharPosition);
|
||||
};
|
||||
|
||||
if (content.length > 0) {
|
||||
@@ -3478,6 +3486,27 @@ ${code}
|
||||
function toArray<T>(x: T | T[]): T[] {
|
||||
return ts.isArray(x) ? x : [x];
|
||||
}
|
||||
|
||||
function makeWhitespaceVisible(text: string) {
|
||||
return text.replace(/ /g, "\u00B7").replace(/\r/g, "\u00B6").replace(/\n/g, "\u2193\n").replace(/\t/g, "\u2192\ ");
|
||||
}
|
||||
|
||||
function showTextDiff(expected: string, actual: string): string {
|
||||
// Only show whitespace if the difference is whitespace-only.
|
||||
if (differOnlyByWhitespace(expected, actual)) {
|
||||
expected = makeWhitespaceVisible(expected);
|
||||
actual = makeWhitespaceVisible(actual);
|
||||
}
|
||||
return `Expected:\n${expected}\nActual:\n${actual}`;
|
||||
}
|
||||
|
||||
function differOnlyByWhitespace(a: string, b: string) {
|
||||
return stripWhitespace(a) === stripWhitespace(b);
|
||||
}
|
||||
|
||||
function stripWhitespace(s: string): string {
|
||||
return s.replace(/\s/g, "");
|
||||
}
|
||||
}
|
||||
|
||||
namespace FourSlashInterface {
|
||||
@@ -3501,6 +3530,10 @@ namespace FourSlashInterface {
|
||||
return this.state.getRanges();
|
||||
}
|
||||
|
||||
public spans(): ts.TextSpan[] {
|
||||
return this.ranges().map(r => ts.createTextSpan(r.start, r.end - r.start));
|
||||
}
|
||||
|
||||
public rangesByText(): ts.Map<FourSlash.Range[]> {
|
||||
return this.state.rangesByText();
|
||||
}
|
||||
@@ -3705,8 +3738,8 @@ namespace FourSlashInterface {
|
||||
super(state);
|
||||
}
|
||||
|
||||
public completionsAt(markerName: string, completions: string[]) {
|
||||
this.state.verifyCompletionsAt(markerName, completions);
|
||||
public completionsAt(markerName: string, completions: string[], options?: CompletionsAtOptions) {
|
||||
this.state.verifyCompletionsAt(markerName, completions, options);
|
||||
}
|
||||
|
||||
public quickInfoIs(expectedText: string, expectedDocumentation?: string) {
|
||||
@@ -3904,12 +3937,14 @@ namespace FourSlashInterface {
|
||||
this.state.verifyNoMatchingBracePosition(bracePosition);
|
||||
}
|
||||
|
||||
public DocCommentTemplate(expectedText: string, expectedOffset: number, empty?: boolean) {
|
||||
this.state.verifyDocCommentTemplate(empty ? undefined : { newText: expectedText, caretOffset: expectedOffset });
|
||||
public docCommentTemplateAt(marker: string | FourSlash.Marker, expectedOffset: number, expectedText: string) {
|
||||
this.state.goToMarker(marker);
|
||||
this.state.verifyDocCommentTemplate({ newText: expectedText.replace(/\r?\n/g, "\r\n"), caretOffset: expectedOffset });
|
||||
}
|
||||
|
||||
public noDocCommentTemplate() {
|
||||
this.DocCommentTemplate(/*expectedText*/ undefined, /*expectedOffset*/ undefined, /*empty*/ true);
|
||||
public noDocCommentTemplateAt(marker: string | FourSlash.Marker) {
|
||||
this.state.goToMarker(marker);
|
||||
this.state.verifyDocCommentTemplate(/*expected*/ undefined);
|
||||
}
|
||||
|
||||
public rangeAfterCodeFix(expectedText: string, includeWhiteSpace?: boolean, errorCode?: number, index?: number): void {
|
||||
@@ -3932,12 +3967,12 @@ namespace FourSlashInterface {
|
||||
this.state.verifyImportFixAtPosition(expectedTextArray, errorCode);
|
||||
}
|
||||
|
||||
public navigationBar(json: any) {
|
||||
this.state.verifyNavigationBar(json);
|
||||
public navigationBar(json: any, options?: { checkSpans?: boolean }) {
|
||||
this.state.verifyNavigationBar(json, options);
|
||||
}
|
||||
|
||||
public navigationTree(json: any) {
|
||||
this.state.verifyNavigationTree(json);
|
||||
public navigationTree(json: any, options?: { checkSpans?: boolean }) {
|
||||
this.state.verifyNavigationTree(json, options);
|
||||
}
|
||||
|
||||
public navigationItemsListCount(count: number, searchValue: string, matchKind?: string, fileName?: string) {
|
||||
@@ -4108,15 +4143,15 @@ namespace FourSlashInterface {
|
||||
}
|
||||
|
||||
public printCurrentFileState() {
|
||||
this.state.printCurrentFileState(/*makeWhitespaceVisible*/ false, /*makeCaretVisible*/ true);
|
||||
this.state.printCurrentFileState(/*showWhitespace*/ false, /*makeCaretVisible*/ true);
|
||||
}
|
||||
|
||||
public printCurrentFileStateWithWhitespace() {
|
||||
this.state.printCurrentFileState(/*makeWhitespaceVisible*/ true, /*makeCaretVisible*/ true);
|
||||
this.state.printCurrentFileState(/*showWhitespace*/ true, /*makeCaretVisible*/ true);
|
||||
}
|
||||
|
||||
public printCurrentFileStateWithoutCaret() {
|
||||
this.state.printCurrentFileState(/*makeWhitespaceVisible*/ false, /*makeCaretVisible*/ false);
|
||||
this.state.printCurrentFileState(/*showWhitespace*/ false, /*makeCaretVisible*/ false);
|
||||
}
|
||||
|
||||
public printCurrentQuickInfo() {
|
||||
@@ -4313,5 +4348,10 @@ namespace FourSlashInterface {
|
||||
refactorName: string;
|
||||
actionName: string;
|
||||
actionDescription: string;
|
||||
newContent: string;
|
||||
}
|
||||
|
||||
export interface CompletionsAtOptions {
|
||||
isNewIdentifierLocation?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
+166
-43
@@ -500,7 +500,8 @@ namespace Harness {
|
||||
export let IO: IO;
|
||||
|
||||
// harness always uses one kind of new line
|
||||
const harnessNewLine = "\r\n";
|
||||
// But note that `parseTestData` in `fourslash.ts` uses "\n"
|
||||
export const harnessNewLine = "\r\n";
|
||||
|
||||
// Root for file paths that are stored in a virtual file system
|
||||
export const virtualFileSystemRoot = "/";
|
||||
@@ -1284,16 +1285,31 @@ namespace Harness {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function minimalDiagnosticsToString(diagnostics: ReadonlyArray<ts.Diagnostic>) {
|
||||
return ts.formatDiagnostics(diagnostics, { getCanonicalFileName, getCurrentDirectory: () => "", getNewLine: () => Harness.IO.newLine() });
|
||||
export function minimalDiagnosticsToString(diagnostics: ReadonlyArray<ts.Diagnostic>, pretty?: boolean) {
|
||||
const host = { getCanonicalFileName, getCurrentDirectory: () => "", getNewLine: () => Harness.IO.newLine() };
|
||||
return (pretty ? ts.formatDiagnosticsWithColorAndContext : ts.formatDiagnostics)(diagnostics, host);
|
||||
}
|
||||
|
||||
export function getErrorBaseline(inputFiles: ReadonlyArray<TestFile>, diagnostics: ReadonlyArray<ts.Diagnostic>) {
|
||||
export function getErrorBaseline(inputFiles: ReadonlyArray<TestFile>, diagnostics: ReadonlyArray<ts.Diagnostic>, pretty?: boolean) {
|
||||
let outputLines = "";
|
||||
const gen = iterateErrorBaseline(inputFiles, diagnostics, pretty);
|
||||
for (let {done, value} = gen.next(); !done; { done, value } = gen.next()) {
|
||||
const [, content] = value;
|
||||
outputLines += content;
|
||||
}
|
||||
return outputLines;
|
||||
}
|
||||
|
||||
export const diagnosticSummaryMarker = "__diagnosticSummary";
|
||||
export const globalErrorsMarker = "__globalErrors";
|
||||
export function *iterateErrorBaseline(inputFiles: ReadonlyArray<TestFile>, diagnostics: ReadonlyArray<ts.Diagnostic>, pretty?: boolean): IterableIterator<[string, string, number]> {
|
||||
diagnostics = diagnostics.slice().sort(ts.compareDiagnostics);
|
||||
let outputLines = "";
|
||||
// Count up all errors that were found in files other than lib.d.ts so we don't miss any
|
||||
let totalErrorsReportedInNonLibraryFiles = 0;
|
||||
|
||||
let errorsReported = 0;
|
||||
|
||||
let firstLine = true;
|
||||
function newLine() {
|
||||
if (firstLine) {
|
||||
@@ -1312,6 +1328,7 @@ namespace Harness {
|
||||
.filter(s => s.length > 0)
|
||||
.map(s => "!!! " + ts.DiagnosticCategory[error.category].toLowerCase() + " TS" + error.code + ": " + s);
|
||||
errLines.forEach(e => outputLines += (newLine() + e));
|
||||
errorsReported++;
|
||||
|
||||
// do not count errors from lib.d.ts here, they are computed separately as numLibraryDiagnostics
|
||||
// if lib.d.ts is explicitly included in input files and there are some errors in it (i.e. because of duplicate identifiers)
|
||||
@@ -1323,12 +1340,18 @@ namespace Harness {
|
||||
}
|
||||
}
|
||||
|
||||
yield [diagnosticSummaryMarker, minimalDiagnosticsToString(diagnostics, pretty) + Harness.IO.newLine() + Harness.IO.newLine(), diagnostics.length];
|
||||
|
||||
// Report global errors
|
||||
const globalErrors = diagnostics.filter(err => !err.file);
|
||||
globalErrors.forEach(outputErrorText);
|
||||
yield [globalErrorsMarker, outputLines, errorsReported];
|
||||
outputLines = "";
|
||||
errorsReported = 0;
|
||||
|
||||
// 'merge' the lines of each input file with any errors associated with it
|
||||
inputFiles.filter(f => f.content !== undefined).forEach(inputFile => {
|
||||
const dupeCase = ts.createMap<number>();
|
||||
for (const inputFile of inputFiles.filter(f => f.content !== undefined)) {
|
||||
// Filter down to the errors in the file
|
||||
const fileErrors = diagnostics.filter(e => {
|
||||
const errFn = e.file;
|
||||
@@ -1394,7 +1417,10 @@ namespace Harness {
|
||||
|
||||
// Verify we didn't miss any errors in this file
|
||||
assert.equal(markedErrorCount, fileErrors.length, "count of errors in " + inputFile.unitName);
|
||||
});
|
||||
yield [checkDuplicatedFileName(inputFile.unitName, dupeCase), outputLines, errorsReported];
|
||||
outputLines = "";
|
||||
errorsReported = 0;
|
||||
}
|
||||
|
||||
const numLibraryDiagnostics = ts.countWhere(diagnostics, diagnostic => {
|
||||
return diagnostic.file && (isDefaultLibraryFile(diagnostic.file.fileName) || isBuiltFile(diagnostic.file.fileName));
|
||||
@@ -1407,23 +1433,20 @@ namespace Harness {
|
||||
|
||||
// Verify we didn't miss any errors in total
|
||||
assert.equal(totalErrorsReportedInNonLibraryFiles + numLibraryDiagnostics + numTest262HarnessDiagnostics, diagnostics.length, "total number of errors");
|
||||
|
||||
return minimalDiagnosticsToString(diagnostics) +
|
||||
Harness.IO.newLine() + Harness.IO.newLine() + outputLines;
|
||||
}
|
||||
|
||||
export function doErrorBaseline(baselinePath: string, inputFiles: TestFile[], errors: ts.Diagnostic[]) {
|
||||
export function doErrorBaseline(baselinePath: string, inputFiles: TestFile[], errors: ts.Diagnostic[], pretty?: boolean) {
|
||||
Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?$/, ".errors.txt"), (): string => {
|
||||
if (!errors || (errors.length === 0)) {
|
||||
/* tslint:disable:no-null-keyword */
|
||||
return null;
|
||||
/* tslint:enable:no-null-keyword */
|
||||
}
|
||||
return getErrorBaseline(inputFiles, errors);
|
||||
return getErrorBaseline(inputFiles, errors, pretty);
|
||||
});
|
||||
}
|
||||
|
||||
export function doTypeAndSymbolBaseline(baselinePath: string, result: CompilerResult, allFiles: {unitName: string, content: string}[], opts?: Harness.Baseline.BaselineOptions) {
|
||||
export function doTypeAndSymbolBaseline(baselinePath: string, result: CompilerResult, allFiles: {unitName: string, content: string}[], opts?: Harness.Baseline.BaselineOptions, multifile?: boolean) {
|
||||
if (result.errors.length !== 0) {
|
||||
return;
|
||||
}
|
||||
@@ -1484,24 +1507,42 @@ namespace Harness {
|
||||
return;
|
||||
|
||||
function checkBaseLines(isSymbolBaseLine: boolean) {
|
||||
const fullBaseLine = generateBaseLine(fullResults, isSymbolBaseLine);
|
||||
|
||||
const fullExtension = isSymbolBaseLine ? ".symbols" : ".types";
|
||||
|
||||
// When calling this function from rwc-runner, the baselinePath will have no extension.
|
||||
// As rwc test- file is stored in json which ".json" will get stripped off.
|
||||
// When calling this function from compiler-runner, the baselinePath will then has either ".ts" or ".tsx" extension
|
||||
const outputFileName = ts.endsWith(baselinePath, ts.Extension.Ts) || ts.endsWith(baselinePath, ts.Extension.Tsx) ?
|
||||
baselinePath.replace(/\.tsx?/, fullExtension) : baselinePath.concat(fullExtension);
|
||||
Harness.Baseline.runBaseline(outputFileName, () => fullBaseLine, opts);
|
||||
baselinePath.replace(/\.tsx?/, "") : baselinePath;
|
||||
|
||||
if (!multifile) {
|
||||
const fullBaseLine = generateBaseLine(fullResults, isSymbolBaseLine);
|
||||
Harness.Baseline.runBaseline(outputFileName + fullExtension, () => fullBaseLine, opts);
|
||||
}
|
||||
else {
|
||||
Harness.Baseline.runMultifileBaseline(outputFileName, fullExtension, () => {
|
||||
return iterateBaseLine(fullResults, isSymbolBaseLine);
|
||||
}, opts);
|
||||
}
|
||||
}
|
||||
|
||||
function generateBaseLine(typeWriterResults: ts.Map<TypeWriterResult[]>, isSymbolBaseline: boolean): string {
|
||||
const typeLines: string[] = [];
|
||||
const typeMap: { [fileName: string]: { [lineNum: number]: string[]; } } = {};
|
||||
let result = "";
|
||||
const gen = iterateBaseLine(typeWriterResults, isSymbolBaseline);
|
||||
for (let {done, value} = gen.next(); !done; { done, value } = gen.next()) {
|
||||
const [, content] = value;
|
||||
result += content;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
allFiles.forEach(file => {
|
||||
function *iterateBaseLine(typeWriterResults: ts.Map<TypeWriterResult[]>, isSymbolBaseline: boolean): IterableIterator<[string, string]> {
|
||||
let typeLines = "";
|
||||
const typeMap: { [fileName: string]: { [lineNum: number]: string[]; } } = {};
|
||||
const dupeCase = ts.createMap<number>();
|
||||
|
||||
for (const file of allFiles) {
|
||||
const codeLines = file.content.split("\n");
|
||||
const key = file.unitName;
|
||||
typeWriterResults.get(file.unitName).forEach(result => {
|
||||
if (isSymbolBaseline && !result.symbol) {
|
||||
return;
|
||||
@@ -1509,42 +1550,42 @@ namespace Harness {
|
||||
|
||||
const typeOrSymbolString = isSymbolBaseline ? result.symbol : result.type;
|
||||
const formattedLine = result.sourceText.replace(/\r?\n/g, "") + " : " + typeOrSymbolString;
|
||||
if (!typeMap[file.unitName]) {
|
||||
typeMap[file.unitName] = {};
|
||||
if (!typeMap[key]) {
|
||||
typeMap[key] = {};
|
||||
}
|
||||
|
||||
let typeInfo = [formattedLine];
|
||||
const existingTypeInfo = typeMap[file.unitName][result.line];
|
||||
const existingTypeInfo = typeMap[key][result.line];
|
||||
if (existingTypeInfo) {
|
||||
typeInfo = existingTypeInfo.concat(typeInfo);
|
||||
}
|
||||
typeMap[file.unitName][result.line] = typeInfo;
|
||||
typeMap[key][result.line] = typeInfo;
|
||||
});
|
||||
|
||||
typeLines.push("=== " + file.unitName + " ===\r\n");
|
||||
typeLines += "=== " + file.unitName + " ===\r\n";
|
||||
for (let i = 0; i < codeLines.length; i++) {
|
||||
const currentCodeLine = codeLines[i];
|
||||
typeLines.push(currentCodeLine + "\r\n");
|
||||
if (typeMap[file.unitName]) {
|
||||
const typeInfo = typeMap[file.unitName][i];
|
||||
typeLines += currentCodeLine + "\r\n";
|
||||
if (typeMap[key]) {
|
||||
const typeInfo = typeMap[key][i];
|
||||
if (typeInfo) {
|
||||
typeInfo.forEach(ty => {
|
||||
typeLines.push(">" + ty + "\r\n");
|
||||
typeLines += ">" + ty + "\r\n";
|
||||
});
|
||||
if (i + 1 < codeLines.length && (codeLines[i + 1].match(/^\s*[{|}]\s*$/) || codeLines[i + 1].trim() === "")) {
|
||||
}
|
||||
else {
|
||||
typeLines.push("\r\n");
|
||||
typeLines += "\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
typeLines.push("No type information for this code.");
|
||||
typeLines += "No type information for this code.";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return typeLines.join("");
|
||||
yield [checkDuplicatedFileName(file.unitName, dupeCase), typeLines];
|
||||
typeLines = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1640,24 +1681,29 @@ namespace Harness {
|
||||
}
|
||||
|
||||
export function collateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): string {
|
||||
// Collect, test, and sort the fileNames
|
||||
outputFiles.sort((a, b) => ts.compareStrings(cleanName(a.fileName), cleanName(b.fileName)));
|
||||
|
||||
const gen = iterateOutputs(outputFiles);
|
||||
// Emit them
|
||||
let result = "";
|
||||
for (const outputFile of outputFiles) {
|
||||
for (let {done, value} = gen.next(); !done; { done, value } = gen.next()) {
|
||||
// Some extra spacing if this isn't the first file
|
||||
if (result.length) {
|
||||
result += "\r\n\r\n";
|
||||
}
|
||||
|
||||
// FileName header + content
|
||||
result += "/*====== " + outputFile.fileName + " ======*/\r\n";
|
||||
|
||||
result += outputFile.code;
|
||||
const [, content] = value;
|
||||
result += content;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function *iterateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): IterableIterator<[string, string]> {
|
||||
// Collect, test, and sort the fileNames
|
||||
outputFiles.sort((a, b) => ts.compareStrings(cleanName(a.fileName), cleanName(b.fileName)));
|
||||
const dupeCase = ts.createMap<number>();
|
||||
// Yield them
|
||||
for (const outputFile of outputFiles) {
|
||||
yield [checkDuplicatedFileName(outputFile.fileName, dupeCase), "/*====== " + outputFile.fileName + " ======*/\r\n" + outputFile.code];
|
||||
}
|
||||
|
||||
function cleanName(fn: string) {
|
||||
const lastSlash = ts.normalizeSlashes(fn).lastIndexOf("/");
|
||||
@@ -1665,6 +1711,24 @@ namespace Harness {
|
||||
}
|
||||
}
|
||||
|
||||
function checkDuplicatedFileName(resultName: string, dupeCase: ts.Map<number>): string {
|
||||
resultName = sanitizeTestFilePath(resultName);
|
||||
if (dupeCase.has(resultName)) {
|
||||
// A different baseline filename should be manufactured if the names differ only in case, for windows compat
|
||||
const count = 1 + dupeCase.get(resultName);
|
||||
dupeCase.set(resultName, count);
|
||||
resultName = `${resultName}.dupe${count}`;
|
||||
}
|
||||
else {
|
||||
dupeCase.set(resultName, 0);
|
||||
}
|
||||
return resultName;
|
||||
}
|
||||
|
||||
function sanitizeTestFilePath(name: string) {
|
||||
return ts.normalizeSlashes(name.replace(/[\^<>:"|?*%]/g, "_")).replace(/\.\.\//g, "__dotdot/").toLowerCase();
|
||||
}
|
||||
|
||||
// This does not need to exist strictly speaking, but many tests will need to be updated if it's removed
|
||||
export function compileString(_code: string, _unitName: string, _callback: (result: CompilerResult) => void) {
|
||||
// NEWTODO: Re-implement 'compileString'
|
||||
@@ -2002,6 +2066,65 @@ namespace Harness {
|
||||
const comparison = compareToBaseline(actual, relativeFileName, opts);
|
||||
writeComparison(comparison.expected, comparison.actual, relativeFileName, actualFileName);
|
||||
}
|
||||
|
||||
export function runMultifileBaseline(relativeFileBase: string, extension: string, generateContent: () => IterableIterator<[string, string, number]> | IterableIterator<[string, string]>, opts?: BaselineOptions, referencedExtensions?: string[]): void {
|
||||
const gen = generateContent();
|
||||
const writtenFiles = ts.createMap<true>();
|
||||
const canonicalize = ts.createGetCanonicalFileName(/*caseSensitive*/ false); // This is done so tests work on windows _and_ linux
|
||||
/* tslint:disable-next-line:no-null-keyword */
|
||||
const errors: Error[] = [];
|
||||
if (gen !== null) {
|
||||
for (let {done, value} = gen.next(); !done; { done, value } = gen.next()) {
|
||||
const [name, content, count] = value as [string, string, number | undefined];
|
||||
if (count === 0) continue; // Allow error reporter to skip writing files without errors
|
||||
const relativeFileName = relativeFileBase + (ts.startsWith(name, "/") ? "" : "/") + name + extension;
|
||||
const actualFileName = localPath(relativeFileName, opts && opts.Baselinefolder, opts && opts.Subfolder);
|
||||
const comparison = compareToBaseline(content, relativeFileName, opts);
|
||||
try {
|
||||
writeComparison(comparison.expected, comparison.actual, relativeFileName, actualFileName);
|
||||
}
|
||||
catch (e) {
|
||||
errors.push(e);
|
||||
}
|
||||
const path = ts.toPath(relativeFileName, "", canonicalize);
|
||||
writtenFiles.set(path, true);
|
||||
}
|
||||
}
|
||||
|
||||
const referenceDir = referencePath(relativeFileBase, opts && opts.Baselinefolder, opts && opts.Subfolder);
|
||||
let existing = Harness.IO.readDirectory(referenceDir, referencedExtensions || [extension]);
|
||||
if (extension === ".ts" || referencedExtensions && referencedExtensions.indexOf(".ts") > -1 && referencedExtensions.indexOf(".d.ts") === -1) {
|
||||
// special-case and filter .d.ts out of .ts results
|
||||
existing = existing.filter(f => !ts.endsWith(f, ".d.ts"));
|
||||
}
|
||||
const missing: string[] = [];
|
||||
for (const name of existing) {
|
||||
const localCopy = name.substring(referenceDir.length - relativeFileBase.length);
|
||||
const path = ts.toPath(localCopy, "", canonicalize);
|
||||
if (!writtenFiles.has(path)) {
|
||||
missing.push(localCopy);
|
||||
}
|
||||
}
|
||||
if (missing.length) {
|
||||
for (const file of missing) {
|
||||
IO.writeFile(localPath(file + ".delete", opts && opts.Baselinefolder, opts && opts.Subfolder), "");
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length || missing.length) {
|
||||
let errorMsg = "";
|
||||
if (errors.length) {
|
||||
errorMsg += `The baseline for ${relativeFileBase} has changed:${"\n " + errors.map(e => e.message).join("\n ")}`;
|
||||
}
|
||||
if (errors.length && missing.length) {
|
||||
errorMsg += "\n";
|
||||
}
|
||||
if (missing.length) {
|
||||
errorMsg += `Baseline missing files:${"\n " + missing.join("\n ") + "\n"}Written:${"\n " + ts.arrayFrom(writtenFiles.keys()).join("\n ")}`;
|
||||
}
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isDefaultLibraryFile(filePath: string): boolean {
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Harness.LanguageService {
|
||||
}
|
||||
|
||||
public getNewLine(): string {
|
||||
return "\r\n";
|
||||
return harnessNewLine;
|
||||
}
|
||||
|
||||
public getFilenames(): string[] {
|
||||
|
||||
@@ -0,0 +1,472 @@
|
||||
if (typeof describe === "undefined") {
|
||||
(global as any).describe = undefined; // If launched without mocha for parallel mode, we still need a global describe visible to satisfy the parsing of the unit tests
|
||||
(global as any).it = undefined;
|
||||
}
|
||||
namespace Harness.Parallel.Host {
|
||||
|
||||
interface ChildProcessPartial {
|
||||
send(message: ParallelHostMessage, callback?: (error: Error) => void): boolean;
|
||||
on(event: "error", listener: (err: Error) => void): this;
|
||||
on(event: "exit", listener: (code: number, signal: string) => void): this;
|
||||
on(event: "message", listener: (message: ParallelClientMessage) => void): this;
|
||||
}
|
||||
|
||||
interface ProgressBarsOptions {
|
||||
open: string;
|
||||
close: string;
|
||||
complete: string;
|
||||
incomplete: string;
|
||||
width: number;
|
||||
noColors: boolean;
|
||||
}
|
||||
interface ProgressBar {
|
||||
lastN?: number;
|
||||
title?: string;
|
||||
progressColor?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
const perfdataFileName = ".parallelperf.json";
|
||||
function readSavedPerfData(): {[testHash: string]: number} {
|
||||
const perfDataContents = Harness.IO.readFile(perfdataFileName);
|
||||
if (perfDataContents) {
|
||||
return JSON.parse(perfDataContents);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function hashName(runner: TestRunnerKind, test: string) {
|
||||
return `tsrunner-${runner}://${test}`;
|
||||
}
|
||||
|
||||
export function start() {
|
||||
initializeProgressBarsDependencies();
|
||||
console.log("Discovering tests...");
|
||||
const discoverStart = +(new Date());
|
||||
const { statSync }: { statSync(path: string): { size: number }; } = require("fs");
|
||||
const tasks: { runner: TestRunnerKind, file: string, size: number }[] = [];
|
||||
const perfData = readSavedPerfData();
|
||||
let totalCost = 0;
|
||||
let unknownValue: string | undefined;
|
||||
for (const runner of runners) {
|
||||
const files = runner.enumerateTestFiles();
|
||||
for (const file of files) {
|
||||
let size: number;
|
||||
if (!perfData) {
|
||||
size = statSync(file).size;
|
||||
|
||||
}
|
||||
else {
|
||||
const hashedName = hashName(runner.kind(), file);
|
||||
size = perfData[hashedName];
|
||||
if (size === undefined) {
|
||||
size = Number.MAX_SAFE_INTEGER;
|
||||
unknownValue = hashedName;
|
||||
}
|
||||
}
|
||||
tasks.push({ runner: runner.kind(), file, size });
|
||||
totalCost += size;
|
||||
}
|
||||
}
|
||||
tasks.sort((a, b) => a.size - b.size);
|
||||
// 1 fewer batches than threads to account for unittests running on the final thread
|
||||
const batchCount = runners.length === 1 ? workerCount : workerCount - 1;
|
||||
const packfraction = 0.9;
|
||||
const chunkSize = 1000; // ~1KB or 1s for sending batches near the end of a test
|
||||
const batchSize = (totalCost / workerCount) * packfraction; // Keep spare tests for unittest thread in reserve
|
||||
console.log(`Discovered ${tasks.length} test files in ${+(new Date()) - discoverStart}ms.`);
|
||||
console.log(`Starting to run tests using ${workerCount} threads...`);
|
||||
const { fork }: { fork(modulePath: string, args?: string[], options?: {}): ChildProcessPartial; } = require("child_process");
|
||||
|
||||
const totalFiles = tasks.length;
|
||||
let passingFiles = 0;
|
||||
let failingFiles = 0;
|
||||
let errorResults: ErrorInfo[] = [];
|
||||
let totalPassing = 0;
|
||||
const startTime = Date.now();
|
||||
|
||||
const progressBars = new ProgressBars({ noColors });
|
||||
const progressUpdateInterval = 1 / progressBars._options.width;
|
||||
let nextProgress = progressUpdateInterval;
|
||||
|
||||
const newPerfData: {[testHash: string]: number} = {};
|
||||
|
||||
const workers: ChildProcessPartial[] = [];
|
||||
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 ? false : i === workerCount - 1 };
|
||||
const configPath = ts.combinePaths(taskConfigsFolder, `task-config${i}.json`);
|
||||
Harness.IO.writeFile(configPath, JSON.stringify(config));
|
||||
const child = fork(__filename, [`--config="${configPath}"`]);
|
||||
child.on("error", err => {
|
||||
console.error("Unexpected error in child process:");
|
||||
console.error(err);
|
||||
return process.exit(2);
|
||||
});
|
||||
child.on("exit", (code, _signal) => {
|
||||
if (code !== 0) {
|
||||
console.error("Test worker process exited with nonzero exit code!");
|
||||
return process.exit(2);
|
||||
}
|
||||
});
|
||||
child.on("message", (data: ParallelClientMessage) => {
|
||||
switch (data.type) {
|
||||
case "error": {
|
||||
console.error(`Test worker encounted unexpected error and was forced to close:
|
||||
Message: ${data.payload.error}
|
||||
Stack: ${data.payload.stack}`);
|
||||
return process.exit(2);
|
||||
}
|
||||
case "progress":
|
||||
case "result": {
|
||||
totalPassing += data.payload.passing;
|
||||
if (data.payload.errors.length) {
|
||||
errorResults = errorResults.concat(data.payload.errors);
|
||||
failingFiles++;
|
||||
}
|
||||
else {
|
||||
passingFiles++;
|
||||
}
|
||||
newPerfData[hashName(data.payload.runner, data.payload.file)] = data.payload.duration;
|
||||
|
||||
const progress = (failingFiles + passingFiles) / totalFiles;
|
||||
if (progress >= nextProgress) {
|
||||
while (nextProgress < progress) {
|
||||
nextProgress += progressUpdateInterval;
|
||||
}
|
||||
updateProgress(progress, errorResults.length ? `${errorResults.length} failing` : `${totalPassing} passing`, errorResults.length ? "fail" : undefined);
|
||||
}
|
||||
|
||||
if (data.type === "result") {
|
||||
if (tasks.length === 0) {
|
||||
// No more tasks to distribute
|
||||
child.send({ type: "close" });
|
||||
closedWorkers++;
|
||||
if (closedWorkers === workerCount) {
|
||||
outputFinalResult();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Send tasks in blocks if the tasks are small
|
||||
const taskList = [tasks.pop()];
|
||||
while (tasks.length && taskList.reduce((p, c) => p + c.size, 0) < chunkSize) {
|
||||
taskList.push(tasks.pop());
|
||||
}
|
||||
if (taskList.length === 1) {
|
||||
child.send({ type: "test", payload: taskList[0] });
|
||||
}
|
||||
else {
|
||||
child.send({ type: "batch", payload: taskList });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
workers.push(child);
|
||||
}
|
||||
|
||||
// It's only really worth doing an initial batching if there are a ton of files to go through
|
||||
if (totalFiles > 1000) {
|
||||
console.log("Batching initial test lists...");
|
||||
const batches: { runner: TestRunnerKind, file: string, size: number }[][] = new Array(batchCount);
|
||||
const doneBatching = new Array(batchCount);
|
||||
let scheduledTotal = 0;
|
||||
batcher: while (true) {
|
||||
for (let i = 0; i < batchCount; i++) {
|
||||
if (tasks.length === 0) {
|
||||
console.log(`Suboptimal packing detected: no tests remain to be stolen. Reduce packing fraction from ${packfraction} to fix.`);
|
||||
break batcher;
|
||||
}
|
||||
if (doneBatching[i]) {
|
||||
continue;
|
||||
}
|
||||
if (!batches[i]) {
|
||||
batches[i] = [];
|
||||
}
|
||||
const total = batches[i].reduce((p, c) => p + c.size, 0);
|
||||
if (total >= batchSize) {
|
||||
doneBatching[i] = true;
|
||||
continue;
|
||||
}
|
||||
const task = tasks.pop();
|
||||
batches[i].push(task);
|
||||
scheduledTotal += task.size;
|
||||
}
|
||||
for (let j = 0; j < batchCount; j++) {
|
||||
if (!doneBatching[j]) {
|
||||
continue batcher;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
const prefix = `Batched into ${batchCount} groups`;
|
||||
if (unknownValue) {
|
||||
console.log(`${prefix}. Unprofiled tests including ${unknownValue} will be run first.`);
|
||||
}
|
||||
else {
|
||||
console.log(`${prefix} with approximate total ${perfData ? "time" : "file sizes"} of ${perfData ? ms(batchSize) : `${Math.floor(batchSize)} bytes`} in each group. (${(scheduledTotal / totalCost * 100).toFixed(1)}% of total tests batched)`);
|
||||
}
|
||||
for (const worker of workers) {
|
||||
const payload = batches.pop();
|
||||
if (payload) {
|
||||
worker.send({ type: "batch", payload });
|
||||
}
|
||||
else { // Unittest thread - send off just one test
|
||||
worker.send({ type: "test", payload: tasks.pop() });
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < workerCount; i++) {
|
||||
workers[i].send({ type: "test", payload: tasks.pop() });
|
||||
}
|
||||
}
|
||||
|
||||
progressBars.enable();
|
||||
updateProgress(0);
|
||||
let duration: number;
|
||||
|
||||
function completeBar() {
|
||||
const isPartitionFail = failingFiles !== 0;
|
||||
const summaryColor = isPartitionFail ? "fail" : "green";
|
||||
const summarySymbol = isPartitionFail ? Base.symbols.err : Base.symbols.ok;
|
||||
|
||||
const summaryTests = (isPartitionFail ? totalPassing + "/" + (errorResults.length + totalPassing) : totalPassing) + " passing";
|
||||
const summaryDuration = "(" + ms(duration) + ")";
|
||||
const savedUseColors = Base.useColors;
|
||||
Base.useColors = !noColors;
|
||||
|
||||
const summary = color(summaryColor, summarySymbol + " " + summaryTests) + " " + color("light", summaryDuration);
|
||||
Base.useColors = savedUseColors;
|
||||
|
||||
updateProgress(1, summary);
|
||||
}
|
||||
|
||||
function updateProgress(percentComplete: number, title?: string, titleColor?: string) {
|
||||
let progressColor = "pending";
|
||||
if (failingFiles) {
|
||||
progressColor = "fail";
|
||||
}
|
||||
|
||||
progressBars.update(
|
||||
0,
|
||||
percentComplete,
|
||||
progressColor,
|
||||
title,
|
||||
titleColor
|
||||
);
|
||||
}
|
||||
|
||||
function outputFinalResult() {
|
||||
duration = Date.now() - startTime;
|
||||
completeBar();
|
||||
progressBars.disable();
|
||||
|
||||
const reporter = new Base();
|
||||
const stats = reporter.stats;
|
||||
const failures = reporter.failures;
|
||||
stats.passes = totalPassing;
|
||||
stats.failures = errorResults.length;
|
||||
stats.tests = totalPassing + errorResults.length;
|
||||
stats.duration = duration;
|
||||
for (let j = 0; j < errorResults.length; j++) {
|
||||
const failure = errorResults[j];
|
||||
failures.push(makeMochaTest(failure));
|
||||
}
|
||||
if (noColors) {
|
||||
const savedUseColors = Base.useColors;
|
||||
Base.useColors = false;
|
||||
reporter.epilogue();
|
||||
Base.useColors = savedUseColors;
|
||||
}
|
||||
else {
|
||||
reporter.epilogue();
|
||||
}
|
||||
|
||||
Harness.IO.writeFile(perfdataFileName, JSON.stringify(newPerfData, null, 4)); // tslint:disable-line:no-null-keyword
|
||||
|
||||
process.exit(errorResults.length);
|
||||
}
|
||||
|
||||
function makeMochaTest(test: ErrorInfo) {
|
||||
return {
|
||||
fullTitle: () => {
|
||||
return test.name;
|
||||
},
|
||||
err: {
|
||||
message: test.error,
|
||||
stack: test.stack
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe = ts.noop as any; // Disable unit tests
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let Mocha: any;
|
||||
let Base: any;
|
||||
let color: any;
|
||||
let cursor: any;
|
||||
let readline: any;
|
||||
let os: any;
|
||||
let tty: { isatty(x: number): boolean };
|
||||
let isatty: boolean;
|
||||
|
||||
const s = 1000;
|
||||
const m = s * 60;
|
||||
const h = m * 60;
|
||||
const d = h * 24;
|
||||
function ms(ms: number) {
|
||||
let result = "";
|
||||
if (ms >= d) {
|
||||
const count = Math.floor(ms / d);
|
||||
result += count + "d";
|
||||
ms -= count * d;
|
||||
}
|
||||
if (ms >= h) {
|
||||
const count = Math.floor(ms / h);
|
||||
result += count + "h";
|
||||
ms -= count * h;
|
||||
}
|
||||
if (ms >= m) {
|
||||
const count = Math.floor(ms / m);
|
||||
result += count + "m";
|
||||
ms -= count * m;
|
||||
}
|
||||
if (ms >= s) {
|
||||
const count = Math.round(ms / s);
|
||||
result += count + "s";
|
||||
return result;
|
||||
}
|
||||
if (ms > 0) {
|
||||
result += Math.round(ms) + "ms";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function initializeProgressBarsDependencies() {
|
||||
Mocha = require("mocha");
|
||||
Base = Mocha.reporters.Base;
|
||||
color = Base.color;
|
||||
cursor = Base.cursor;
|
||||
readline = require("readline");
|
||||
os = require("os");
|
||||
tty = require("tty");
|
||||
isatty = tty.isatty(1) && tty.isatty(2);
|
||||
}
|
||||
|
||||
class ProgressBars {
|
||||
public readonly _options: Readonly<ProgressBarsOptions>;
|
||||
private _enabled: boolean;
|
||||
private _lineCount: number;
|
||||
private _progressBars: ProgressBar[];
|
||||
constructor(options?: Partial<ProgressBarsOptions>) {
|
||||
if (!options) options = {};
|
||||
const open = options.open || "[";
|
||||
const close = options.close || "]";
|
||||
const complete = options.complete || "▬";
|
||||
const incomplete = options.incomplete || Base.symbols.dot;
|
||||
const maxWidth = Base.window.width - open.length - close.length - 34;
|
||||
const width = minMax(options.width || maxWidth, 10, maxWidth);
|
||||
this._options = {
|
||||
open,
|
||||
complete,
|
||||
incomplete,
|
||||
close,
|
||||
width,
|
||||
noColors: options.noColors || false
|
||||
};
|
||||
|
||||
this._progressBars = [];
|
||||
this._lineCount = 0;
|
||||
this._enabled = false;
|
||||
}
|
||||
enable() {
|
||||
if (!this._enabled) {
|
||||
process.stdout.write(os.EOL);
|
||||
this._enabled = true;
|
||||
}
|
||||
}
|
||||
disable() {
|
||||
if (this._enabled) {
|
||||
process.stdout.write(os.EOL);
|
||||
this._enabled = false;
|
||||
}
|
||||
}
|
||||
update(index: number, percentComplete: number, color: string, title: string, titleColor?: string) {
|
||||
percentComplete = minMax(percentComplete, 0, 1);
|
||||
|
||||
const progressBar = this._progressBars[index] || (this._progressBars[index] = { });
|
||||
const width = this._options.width;
|
||||
const n = Math.floor(width * percentComplete);
|
||||
const i = width - n;
|
||||
if (n === progressBar.lastN && title === progressBar.title && color === progressBar.progressColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
progressBar.lastN = n;
|
||||
progressBar.title = title;
|
||||
progressBar.progressColor = color;
|
||||
|
||||
let progress = " ";
|
||||
progress += this._color("progress", this._options.open);
|
||||
progress += this._color(color, fill(this._options.complete, n));
|
||||
progress += this._color("progress", fill(this._options.incomplete, i));
|
||||
progress += this._color("progress", this._options.close);
|
||||
|
||||
if (title) {
|
||||
progress += this._color(titleColor || "progress", " " + title);
|
||||
}
|
||||
|
||||
if (progressBar.text !== progress) {
|
||||
progressBar.text = progress;
|
||||
this._render(index);
|
||||
}
|
||||
}
|
||||
private _render(index: number) {
|
||||
if (!this._enabled || !isatty) {
|
||||
return;
|
||||
}
|
||||
|
||||
cursor.hide();
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns, -this._lineCount);
|
||||
let lineCount = 0;
|
||||
const numProgressBars = this._progressBars.length;
|
||||
for (let i = 0; i < numProgressBars; i++) {
|
||||
if (i === index) {
|
||||
readline.clearLine(process.stdout, 1);
|
||||
process.stdout.write(this._progressBars[i].text + os.EOL);
|
||||
}
|
||||
else {
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns, +1);
|
||||
}
|
||||
|
||||
lineCount++;
|
||||
}
|
||||
|
||||
this._lineCount = lineCount;
|
||||
cursor.show();
|
||||
}
|
||||
private _color(type: string, text: string) {
|
||||
return type && !this._options.noColors ? color(type, text) : text;
|
||||
}
|
||||
}
|
||||
|
||||
function fill(ch: string, size: number) {
|
||||
let s = "";
|
||||
while (s.length < size) {
|
||||
s += ch;
|
||||
}
|
||||
|
||||
return s.length > size ? s.substr(0, size) : s;
|
||||
}
|
||||
|
||||
function minMax(value: number, min: number, max: number) {
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/// <reference path="./host.ts" />
|
||||
/// <reference path="./worker.ts" />
|
||||
namespace Harness.Parallel {
|
||||
export type ParallelTestMessage = { type: "test", payload: { runner: TestRunnerKind, file: string } } | never;
|
||||
export type ParallelBatchMessage = { type: "batch", payload: ParallelTestMessage["payload"][] } | never;
|
||||
export type ParallelCloseMessage = { type: "close" } | never;
|
||||
export type ParallelHostMessage = ParallelTestMessage | ParallelCloseMessage | ParallelBatchMessage;
|
||||
|
||||
export type ParallelErrorMessage = { type: "error", payload: { error: string, stack: string } } | never;
|
||||
export type ErrorInfo = ParallelErrorMessage["payload"] & { name: string };
|
||||
export type ParallelResultMessage = { type: "result", payload: { passing: number, errors: ErrorInfo[], duration: number, runner: TestRunnerKind, file: string } } | never;
|
||||
export type ParallelBatchProgressMessage = { type: "progress", payload: ParallelResultMessage["payload"] } | never;
|
||||
export type ParallelClientMessage = ParallelErrorMessage | ParallelResultMessage | ParallelBatchProgressMessage;
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
namespace Harness.Parallel.Worker {
|
||||
let errors: ErrorInfo[] = [];
|
||||
let passing = 0;
|
||||
let reportedUnitTests = false;
|
||||
|
||||
type Executor = {name: string, callback: Function, kind: "suite" | "test"} | never;
|
||||
|
||||
function resetShimHarnessAndExecute(runner: RunnerBase) {
|
||||
if (reportedUnitTests) {
|
||||
errors = [];
|
||||
passing = 0;
|
||||
testList.length = 0;
|
||||
}
|
||||
reportedUnitTests = true;
|
||||
const start = +(new Date());
|
||||
runner.initializeTests();
|
||||
testList.forEach(({ name, callback, kind }) => executeCallback(name, callback, kind));
|
||||
return { errors, passing, duration: +(new Date()) - start };
|
||||
}
|
||||
|
||||
|
||||
let beforeEachFunc: Function;
|
||||
const namestack: string[] = [];
|
||||
let testList: Executor[] = [];
|
||||
function shimMochaHarness() {
|
||||
(global as any).before = undefined;
|
||||
(global as any).after = undefined;
|
||||
(global as any).beforeEach = undefined;
|
||||
describe = ((name, callback) => {
|
||||
testList.push({ name, callback, kind: "suite" });
|
||||
}) as Mocha.IContextDefinition;
|
||||
it = ((name, callback) => {
|
||||
if (!testList) {
|
||||
throw new Error("Tests must occur within a describe block");
|
||||
}
|
||||
testList.push({ name, callback, kind: "test" });
|
||||
}) as Mocha.ITestDefinition;
|
||||
}
|
||||
|
||||
function executeSuiteCallback(name: string, callback: Function) {
|
||||
const fakeContext: Mocha.ISuiteCallbackContext = {
|
||||
retries() { return this; },
|
||||
slow() { return this; },
|
||||
timeout() { return this; },
|
||||
};
|
||||
namestack.push(name);
|
||||
let beforeFunc: Function;
|
||||
(before as any) = (cb: Function) => beforeFunc = cb;
|
||||
let afterFunc: Function;
|
||||
(after as any) = (cb: Function) => afterFunc = cb;
|
||||
const savedBeforeEach = beforeEachFunc;
|
||||
(beforeEach as any) = (cb: Function) => beforeEachFunc = cb;
|
||||
const savedTestList = testList;
|
||||
|
||||
testList = [];
|
||||
callback.call(fakeContext);
|
||||
beforeFunc && beforeFunc();
|
||||
beforeFunc = undefined;
|
||||
testList.forEach(({ name, callback, kind }) => executeCallback(name, callback, kind));
|
||||
testList.length = 0;
|
||||
testList = savedTestList;
|
||||
|
||||
afterFunc && afterFunc();
|
||||
afterFunc = undefined;
|
||||
beforeEachFunc = savedBeforeEach;
|
||||
namestack.pop();
|
||||
}
|
||||
|
||||
function executeCallback(name: string, callback: Function, kind: "suite" | "test") {
|
||||
if (kind === "suite") {
|
||||
executeSuiteCallback(name, callback);
|
||||
}
|
||||
else {
|
||||
executeTestCallback(name, callback);
|
||||
}
|
||||
}
|
||||
|
||||
function executeTestCallback(name: string, callback: Function) {
|
||||
const fakeContext: Mocha.ITestCallbackContext = {
|
||||
skip() { return this; },
|
||||
timeout() { return this; },
|
||||
retries() { return this; },
|
||||
slow() { return this; },
|
||||
};
|
||||
name = [...namestack, name].join(" ");
|
||||
if (beforeEachFunc) {
|
||||
try {
|
||||
beforeEachFunc();
|
||||
}
|
||||
catch (error) {
|
||||
errors.push({ error: error.message, stack: error.stack, name });
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (callback.length === 0) {
|
||||
try {
|
||||
// TODO: If we ever start using async test completions, polyfill promise return handling
|
||||
callback.call(fakeContext);
|
||||
}
|
||||
catch (error) {
|
||||
errors.push({ error: error.message, stack: error.stack, name });
|
||||
return;
|
||||
}
|
||||
passing++;
|
||||
}
|
||||
else {
|
||||
// Uses `done` callback
|
||||
let completed = false;
|
||||
try {
|
||||
callback.call(fakeContext, (err: any) => {
|
||||
if (completed) {
|
||||
throw new Error(`done() callback called multiple times; ensure it is only called once.`);
|
||||
}
|
||||
if (err) {
|
||||
errors.push({ error: err.toString(), stack: "", name });
|
||||
}
|
||||
else {
|
||||
passing++;
|
||||
}
|
||||
completed = true;
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
errors.push({ error: error.message, stack: error.stack, name });
|
||||
return;
|
||||
}
|
||||
if (!completed) {
|
||||
errors.push({ error: "Test completes asynchronously, which is unsupported by the parallel harness", stack: "", name });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function start() {
|
||||
let initialized = false;
|
||||
const runners = ts.createMap<RunnerBase>();
|
||||
process.on("message", (data: ParallelHostMessage) => {
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
shimMochaHarness();
|
||||
}
|
||||
switch (data.type) {
|
||||
case "test":
|
||||
const { runner, file } = data.payload;
|
||||
if (!runner) {
|
||||
console.error(data);
|
||||
}
|
||||
const message: ParallelResultMessage = { type: "result", payload: handleTest(runner, file) };
|
||||
process.send(message);
|
||||
break;
|
||||
case "close":
|
||||
process.exit(0);
|
||||
break;
|
||||
case "batch": {
|
||||
const items = data.payload;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const { runner, file } = items[i];
|
||||
if (!runner) {
|
||||
console.error(data);
|
||||
}
|
||||
let message: ParallelBatchProgressMessage | ParallelResultMessage;
|
||||
const payload = handleTest(runner, file);
|
||||
if (i === (items.length - 1)) {
|
||||
message = { type: "result", payload };
|
||||
}
|
||||
else {
|
||||
message = { type: "progress", payload };
|
||||
}
|
||||
process.send(message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
process.on("uncaughtException", error => {
|
||||
const message: ParallelErrorMessage = { type: "error", payload: { error: error.message, stack: error.stack } };
|
||||
try {
|
||||
process.send(message);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
if (!runUnitTests) {
|
||||
// ensure unit tests do not get run
|
||||
describe = ts.noop as any;
|
||||
}
|
||||
else {
|
||||
initialized = true;
|
||||
shimMochaHarness();
|
||||
}
|
||||
|
||||
function handleTest(runner: TestRunnerKind, file: string) {
|
||||
if (!runners.has(runner)) {
|
||||
runners.set(runner, createRunner(runner));
|
||||
}
|
||||
const instance = runners.get(runner);
|
||||
instance.tests = [file];
|
||||
return { ...resetShimHarnessAndExecute(instance), runner, file };
|
||||
}
|
||||
}
|
||||
}
|
||||
+106
-118
@@ -19,6 +19,7 @@
|
||||
/// <reference path="projectsRunner.ts" />
|
||||
/// <reference path="rwcRunner.ts" />
|
||||
/// <reference path="harness.ts" />
|
||||
/// <reference path="./parallel/shared.ts" />
|
||||
|
||||
let runners: RunnerBase[] = [];
|
||||
let iterations = 1;
|
||||
@@ -59,6 +60,7 @@ function createRunner(kind: TestRunnerKind): RunnerBase {
|
||||
case "test262":
|
||||
return new Test262BaselineRunner();
|
||||
}
|
||||
ts.Debug.fail(`Unknown runner kind ${kind}`);
|
||||
}
|
||||
|
||||
if (Harness.IO.tryEnableSourceMapsForHost && /^development$/i.test(Harness.IO.getEnvironmentVariable("NODE_ENV"))) {
|
||||
@@ -81,15 +83,17 @@ let testConfigContent =
|
||||
let taskConfigsFolder: string;
|
||||
let workerCount: number;
|
||||
let runUnitTests = true;
|
||||
let noColors = false;
|
||||
|
||||
interface TestConfig {
|
||||
light?: boolean;
|
||||
taskConfigsFolder?: string;
|
||||
listenForWork?: boolean;
|
||||
workerCount?: number;
|
||||
stackTraceLimit?: number | "full";
|
||||
tasks?: TaskSet[];
|
||||
test?: string[];
|
||||
runUnitTests?: boolean;
|
||||
noColors?: boolean;
|
||||
}
|
||||
|
||||
interface TaskSet {
|
||||
@@ -97,138 +101,122 @@ interface TaskSet {
|
||||
files: string[];
|
||||
}
|
||||
|
||||
if (testConfigContent !== "") {
|
||||
const testConfig = <TestConfig>JSON.parse(testConfigContent);
|
||||
if (testConfig.light) {
|
||||
Harness.lightMode = true;
|
||||
}
|
||||
if (testConfig.taskConfigsFolder) {
|
||||
taskConfigsFolder = testConfig.taskConfigsFolder;
|
||||
}
|
||||
if (testConfig.runUnitTests !== undefined) {
|
||||
runUnitTests = testConfig.runUnitTests;
|
||||
}
|
||||
if (testConfig.workerCount) {
|
||||
workerCount = testConfig.workerCount;
|
||||
}
|
||||
if (testConfig.tasks) {
|
||||
for (const taskSet of testConfig.tasks) {
|
||||
const runner = createRunner(taskSet.runner);
|
||||
for (const file of taskSet.files) {
|
||||
runner.addTest(file);
|
||||
}
|
||||
runners.push(runner);
|
||||
function handleTestConfig() {
|
||||
if (testConfigContent !== "") {
|
||||
const testConfig = <TestConfig>JSON.parse(testConfigContent);
|
||||
if (testConfig.light) {
|
||||
Harness.lightMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (testConfig.stackTraceLimit === "full") {
|
||||
(<any>Error).stackTraceLimit = Infinity;
|
||||
}
|
||||
else if ((+testConfig.stackTraceLimit | 0) > 0) {
|
||||
(<any>Error).stackTraceLimit = testConfig.stackTraceLimit;
|
||||
}
|
||||
|
||||
if (testConfig.test && testConfig.test.length > 0) {
|
||||
for (const option of testConfig.test) {
|
||||
if (!option) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case "compiler":
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions));
|
||||
runners.push(new ProjectRunner());
|
||||
break;
|
||||
case "conformance":
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
break;
|
||||
case "project":
|
||||
runners.push(new ProjectRunner());
|
||||
break;
|
||||
case "fourslash":
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Native));
|
||||
break;
|
||||
case "fourslash-shims":
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Shims));
|
||||
break;
|
||||
case "fourslash-shims-pp":
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.ShimsWithPreprocess));
|
||||
break;
|
||||
case "fourslash-server":
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Server));
|
||||
break;
|
||||
case "fourslash-generated":
|
||||
runners.push(new GeneratedFourslashRunner(FourSlashTestType.Native));
|
||||
break;
|
||||
case "rwc":
|
||||
runners.push(new RWCRunner());
|
||||
break;
|
||||
case "test262":
|
||||
runners.push(new Test262BaselineRunner());
|
||||
break;
|
||||
}
|
||||
if (testConfig.runUnitTests !== undefined) {
|
||||
runUnitTests = testConfig.runUnitTests;
|
||||
}
|
||||
if (testConfig.workerCount) {
|
||||
workerCount = +testConfig.workerCount;
|
||||
}
|
||||
if (testConfig.taskConfigsFolder) {
|
||||
taskConfigsFolder = testConfig.taskConfigsFolder;
|
||||
}
|
||||
if (testConfig.noColors !== undefined) {
|
||||
noColors = testConfig.noColors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (runners.length === 0) {
|
||||
// compiler
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions));
|
||||
if (testConfig.stackTraceLimit === "full") {
|
||||
(<any>Error).stackTraceLimit = Infinity;
|
||||
}
|
||||
else if ((+testConfig.stackTraceLimit | 0) > 0) {
|
||||
(<any>Error).stackTraceLimit = testConfig.stackTraceLimit;
|
||||
}
|
||||
if (testConfig.listenForWork) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: project tests don't work in the browser yet
|
||||
if (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser) {
|
||||
runners.push(new ProjectRunner());
|
||||
}
|
||||
if (testConfig.test && testConfig.test.length > 0) {
|
||||
for (const option of testConfig.test) {
|
||||
if (!option) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// language services
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Native));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Shims));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.ShimsWithPreprocess));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Server));
|
||||
// runners.push(new GeneratedFourslashRunner());
|
||||
}
|
||||
|
||||
if (taskConfigsFolder) {
|
||||
// this instance of mocha should only partition work but not run actual tests
|
||||
runUnitTests = false;
|
||||
const workerConfigs: TestConfig[] = [];
|
||||
for (let i = 0; i < workerCount; i++) {
|
||||
// pass light mode settings to workers
|
||||
workerConfigs.push({ light: Harness.lightMode, tasks: [] });
|
||||
}
|
||||
|
||||
for (const runner of runners) {
|
||||
const files = runner.enumerateTestFiles();
|
||||
const chunkSize = Math.floor(files.length / workerCount) + 1; // add extra 1 to prevent missing tests due to rounding
|
||||
for (let i = 0; i < workerCount; i++) {
|
||||
const startPos = i * chunkSize;
|
||||
const len = Math.min(chunkSize, files.length - startPos);
|
||||
if (len > 0) {
|
||||
workerConfigs[i].tasks.push({
|
||||
runner: runner.kind(),
|
||||
files: files.slice(startPos, startPos + len)
|
||||
});
|
||||
switch (option) {
|
||||
case "compiler":
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions));
|
||||
runners.push(new ProjectRunner());
|
||||
break;
|
||||
case "conformance":
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
break;
|
||||
case "project":
|
||||
runners.push(new ProjectRunner());
|
||||
break;
|
||||
case "fourslash":
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Native));
|
||||
break;
|
||||
case "fourslash-shims":
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Shims));
|
||||
break;
|
||||
case "fourslash-shims-pp":
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.ShimsWithPreprocess));
|
||||
break;
|
||||
case "fourslash-server":
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Server));
|
||||
break;
|
||||
case "fourslash-generated":
|
||||
runners.push(new GeneratedFourslashRunner(FourSlashTestType.Native));
|
||||
break;
|
||||
case "rwc":
|
||||
runners.push(new RWCRunner());
|
||||
break;
|
||||
case "test262":
|
||||
runners.push(new Test262BaselineRunner());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < workerCount; i++) {
|
||||
const config = workerConfigs[i];
|
||||
// use last worker to run unit tests
|
||||
config.runUnitTests = i === workerCount - 1;
|
||||
Harness.IO.writeFile(ts.combinePaths(taskConfigsFolder, `task-config${i}.json`), JSON.stringify(workerConfigs[i]));
|
||||
if (runners.length === 0) {
|
||||
// compiler
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions));
|
||||
|
||||
// TODO: project tests don"t work in the browser yet
|
||||
if (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser) {
|
||||
runners.push(new ProjectRunner());
|
||||
}
|
||||
|
||||
// language services
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Native));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Shims));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.ShimsWithPreprocess));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Server));
|
||||
// runners.push(new GeneratedFourslashRunner());
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
function beginTests() {
|
||||
if (ts.Debug.isDebugging) {
|
||||
ts.Debug.enableDebugInfo();
|
||||
}
|
||||
|
||||
runTests(runners);
|
||||
|
||||
if (!runUnitTests) {
|
||||
// patch `describe` to skip unit tests
|
||||
describe = ts.noop as any;
|
||||
}
|
||||
}
|
||||
if (!runUnitTests) {
|
||||
// patch `describe` to skip unit tests
|
||||
describe = ts.noop as any;
|
||||
|
||||
function startTestEnvironment() {
|
||||
const isWorker = handleTestConfig();
|
||||
if (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser) {
|
||||
if (isWorker) {
|
||||
return Harness.Parallel.Worker.start();
|
||||
}
|
||||
else if (taskConfigsFolder && workerCount && workerCount > 1) {
|
||||
return Harness.Parallel.Host.start();
|
||||
}
|
||||
}
|
||||
beginTests();
|
||||
}
|
||||
|
||||
startTestEnvironment();
|
||||
|
||||
+15
-17
@@ -170,29 +170,29 @@ namespace RWC {
|
||||
|
||||
|
||||
it("has the expected emitted code", () => {
|
||||
Harness.Baseline.runBaseline(`${baseName}.output.js`, () => {
|
||||
return Harness.Compiler.collateOutputs(compilerResult.files);
|
||||
}, baselineOpts);
|
||||
Harness.Baseline.runMultifileBaseline(baseName, "", () => {
|
||||
return Harness.Compiler.iterateOutputs(compilerResult.files);
|
||||
}, baselineOpts, [".js", ".jsx"]);
|
||||
});
|
||||
|
||||
it("has the expected declaration file content", () => {
|
||||
Harness.Baseline.runBaseline(`${baseName}.d.ts`, () => {
|
||||
Harness.Baseline.runMultifileBaseline(baseName, "", () => {
|
||||
if (!compilerResult.declFilesCode.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Harness.Compiler.collateOutputs(compilerResult.declFilesCode);
|
||||
}, baselineOpts);
|
||||
return Harness.Compiler.iterateOutputs(compilerResult.declFilesCode);
|
||||
}, baselineOpts, [".d.ts"]);
|
||||
});
|
||||
|
||||
it("has the expected source maps", () => {
|
||||
Harness.Baseline.runBaseline(`${baseName}.map`, () => {
|
||||
Harness.Baseline.runMultifileBaseline(baseName, "", () => {
|
||||
if (!compilerResult.sourceMaps.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Harness.Compiler.collateOutputs(compilerResult.sourceMaps);
|
||||
}, baselineOpts);
|
||||
return Harness.Compiler.iterateOutputs(compilerResult.sourceMaps);
|
||||
}, baselineOpts, [".map"]);
|
||||
});
|
||||
|
||||
/*it("has correct source map record", () => {
|
||||
@@ -204,14 +204,14 @@ namespace RWC {
|
||||
});*/
|
||||
|
||||
it("has the expected errors", () => {
|
||||
Harness.Baseline.runBaseline(`${baseName}.errors.txt`, () => {
|
||||
Harness.Baseline.runMultifileBaseline(baseName, ".errors.txt", () => {
|
||||
if (compilerResult.errors.length === 0) {
|
||||
return null;
|
||||
}
|
||||
// Do not include the library in the baselines to avoid noise
|
||||
const baselineFiles = tsconfigFiles.concat(inputFiles, otherFiles).filter(f => !Harness.isDefaultLibraryFile(f.unitName));
|
||||
const errors = compilerResult.errors.filter(e => !e.file || !Harness.isDefaultLibraryFile(e.file.fileName));
|
||||
return Harness.Compiler.getErrorBaseline(baselineFiles, errors);
|
||||
return Harness.Compiler.iterateErrorBaseline(baselineFiles, errors);
|
||||
}, baselineOpts);
|
||||
});
|
||||
|
||||
@@ -220,14 +220,14 @@ namespace RWC {
|
||||
Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles
|
||||
.concat(otherFiles)
|
||||
.filter(file => !!compilerResult.program.getSourceFile(file.unitName))
|
||||
.filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts);
|
||||
.filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts, /*multifile*/ true);
|
||||
});
|
||||
|
||||
// Ideally, a generated declaration file will have no errors. But we allow generated
|
||||
// declaration file errors as part of the baseline.
|
||||
it("has the expected errors in generated declaration files", () => {
|
||||
if (compilerOptions.declaration && !compilerResult.errors.length) {
|
||||
Harness.Baseline.runBaseline(`${baseName}.dts.errors.txt`, () => {
|
||||
Harness.Baseline.runMultifileBaseline(baseName, ".dts.errors.txt", () => {
|
||||
if (compilerResult.errors.length === 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -239,9 +239,7 @@ namespace RWC {
|
||||
compilerResult = undefined;
|
||||
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(declContext);
|
||||
|
||||
return Harness.Compiler.minimalDiagnosticsToString(declFileCompilationResult.declResult.errors) +
|
||||
Harness.IO.newLine() + Harness.IO.newLine() +
|
||||
Harness.Compiler.getErrorBaseline(tsconfigFiles.concat(declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors);
|
||||
return Harness.Compiler.iterateErrorBaseline(tsconfigFiles.concat(declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors);
|
||||
}, baselineOpts);
|
||||
}
|
||||
});
|
||||
@@ -263,7 +261,7 @@ class RWCRunner extends RunnerBase {
|
||||
*/
|
||||
public initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
const testList = this.enumerateTestFiles();
|
||||
const testList = this.tests && this.tests.length ? this.tests : this.enumerateTestFiles();
|
||||
for (let i = 0; i < testList.length; i++) {
|
||||
this.runTest(testList[i]);
|
||||
}
|
||||
|
||||
@@ -92,6 +92,9 @@
|
||||
"loggedIO.ts",
|
||||
"rwcRunner.ts",
|
||||
"test262Runner.ts",
|
||||
"./parallel/shared.ts",
|
||||
"./parallel/host.ts",
|
||||
"./parallel/worker.ts",
|
||||
"runner.ts",
|
||||
"../server/protocol.ts",
|
||||
"../server/session.ts",
|
||||
@@ -128,6 +131,7 @@
|
||||
"./unittests/extractMethods.ts",
|
||||
"./unittests/textChanges.ts",
|
||||
"./unittests/telemetry.ts",
|
||||
"./unittests/languageService.ts",
|
||||
"./unittests/programMissingFiles.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ namespace ts.projectSystem {
|
||||
let configFile: FileOrFolder;
|
||||
let changeModuleFile1ShapeRequest1: server.protocol.Request;
|
||||
let changeModuleFile1InternalRequest1: server.protocol.Request;
|
||||
let changeModuleFile1ShapeRequest2: server.protocol.Request;
|
||||
// A compile on save affected file request using file1
|
||||
let moduleFile1FileListRequest: server.protocol.Request;
|
||||
|
||||
@@ -112,16 +111,6 @@ namespace ts.projectSystem {
|
||||
insertString: `var T1: number;`
|
||||
});
|
||||
|
||||
// Change the content of file1 to `export var T: number;export function Foo() { };`
|
||||
changeModuleFile1ShapeRequest2 = makeSessionRequest<server.protocol.ChangeRequestArgs>(CommandNames.Change, {
|
||||
file: moduleFile1.path,
|
||||
line: 1,
|
||||
offset: 1,
|
||||
endLine: 1,
|
||||
endOffset: 1,
|
||||
insertString: `export var T2: number;`
|
||||
});
|
||||
|
||||
moduleFile1FileListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(CommandNames.CompileOnSaveAffectedFileList, { file: moduleFile1.path, projectFileName: configFile.path });
|
||||
});
|
||||
|
||||
|
||||
@@ -78,6 +78,23 @@ namespace ts {
|
||||
},
|
||||
include: ["../supplemental.*"]
|
||||
},
|
||||
"/dev/configs/third.json": {
|
||||
extends: "./second",
|
||||
compilerOptions: {
|
||||
// tslint:disable-next-line:no-null-keyword
|
||||
module: null
|
||||
},
|
||||
include: ["../supplemental.*"]
|
||||
},
|
||||
"/dev/configs/fourth.json": {
|
||||
extends: "./third",
|
||||
compilerOptions: {
|
||||
module: "system"
|
||||
},
|
||||
// tslint:disable-next-line:no-null-keyword
|
||||
include: null,
|
||||
files: ["../main.ts"]
|
||||
},
|
||||
"/dev/extends.json": { extends: 42 },
|
||||
"/dev/extends2.json": { extends: "configs/base" },
|
||||
"/dev/main.ts": "",
|
||||
@@ -106,7 +123,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
describe("Configuration Extension", () => {
|
||||
describe("configurationExtension", () => {
|
||||
forEach<[string, string, Utils.MockParseConfigHost], void>([
|
||||
["under a case insensitive host", caseInsensitiveBasePath, caseInsensitiveHost],
|
||||
["under a case sensitive host", caseSensitiveBasePath, caseSensitiveHost]
|
||||
@@ -206,6 +223,24 @@ namespace ts {
|
||||
category: DiagnosticCategory.Error,
|
||||
messageText: `A path in an 'extends' option must be relative or rooted, but 'configs/base' is not.`
|
||||
}]);
|
||||
|
||||
testSuccess("can overwrite compiler options using extended 'null'", "configs/third.json", {
|
||||
allowJs: true,
|
||||
noImplicitAny: true,
|
||||
strictNullChecks: true,
|
||||
module: undefined // Technically, this is distinct from the key never being set; but within the compiler we don't make the distinction
|
||||
}, [
|
||||
combinePaths(basePath, "supplemental.ts")
|
||||
]);
|
||||
|
||||
testSuccess("can overwrite top-level options using extended 'null'", "configs/fourth.json", {
|
||||
allowJs: true,
|
||||
noImplicitAny: true,
|
||||
strictNullChecks: true,
|
||||
module: ModuleKind.System
|
||||
}, [
|
||||
combinePaths(basePath, "main.ts")
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/// <reference path="..\..\compiler\commandLineParser.ts" />
|
||||
|
||||
namespace ts {
|
||||
type ExpectedResult = { typeAcquisition: TypeAcquisition, errors: Diagnostic[] };
|
||||
interface ExpectedResult { typeAcquisition: TypeAcquisition; errors: Diagnostic[]; }
|
||||
describe("convertTypeAcquisitionFromJson", () => {
|
||||
function assertTypeAcquisition(json: any, configFileName: string, expectedResult: ExpectedResult) {
|
||||
assertTypeAcquisitionWithJson(json, configFileName, expectedResult);
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace ts {
|
||||
testExtractRange(`
|
||||
function f() {
|
||||
while (true) {
|
||||
[#|
|
||||
[#|
|
||||
if (x) {
|
||||
return;
|
||||
} |]
|
||||
@@ -234,7 +234,7 @@ namespace ts {
|
||||
testExtractRange(`
|
||||
function f() {
|
||||
while (true) {
|
||||
[#|
|
||||
[#|
|
||||
[$|if (x) {
|
||||
}
|
||||
return;|]
|
||||
@@ -378,6 +378,40 @@ namespace A {
|
||||
"Cannot extract range containing conditional return statement."
|
||||
]);
|
||||
|
||||
testExtractRangeFailed("extractRangeFailed7",
|
||||
`
|
||||
function test(x: number) {
|
||||
while (x) {
|
||||
x--;
|
||||
[#|break;|]
|
||||
}
|
||||
}
|
||||
`,
|
||||
[
|
||||
"Cannot extract range containing conditional break or continue statements."
|
||||
]);
|
||||
|
||||
testExtractRangeFailed("extractRangeFailed8",
|
||||
`
|
||||
function test(x: number) {
|
||||
switch (x) {
|
||||
case 1:
|
||||
[#|break;|]
|
||||
}
|
||||
}
|
||||
`,
|
||||
[
|
||||
"Cannot extract range containing conditional break or continue statements."
|
||||
]);
|
||||
|
||||
testExtractRangeFailed("extractRangeFailed9",
|
||||
`var x = ([#||]1 + 2);`,
|
||||
[
|
||||
"Statement or expression expected."
|
||||
]);
|
||||
|
||||
testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, ["Select more than a single identifier."]);
|
||||
|
||||
testExtractMethod("extractMethod1",
|
||||
`namespace A {
|
||||
let x = 1;
|
||||
@@ -613,6 +647,132 @@ namespace A {
|
||||
[#|let a1 = { x: 1 };
|
||||
return a1.x + 10;|]
|
||||
}
|
||||
}`);
|
||||
// Write + void return
|
||||
testExtractMethod("extractMethod21",
|
||||
`function foo() {
|
||||
let x = 10;
|
||||
[#|x++;
|
||||
return;|]
|
||||
}`);
|
||||
// Return in finally block
|
||||
testExtractMethod("extractMethod22",
|
||||
`function test() {
|
||||
try {
|
||||
}
|
||||
finally {
|
||||
[#|return 1;|]
|
||||
}
|
||||
}`);
|
||||
// Extraction position - namespace
|
||||
testExtractMethod("extractMethod23",
|
||||
`namespace NS {
|
||||
function M1() { }
|
||||
function M2() {
|
||||
[#|return 1;|]
|
||||
}
|
||||
function M3() { }
|
||||
}`);
|
||||
// Extraction position - function
|
||||
testExtractMethod("extractMethod24",
|
||||
`function Outer() {
|
||||
function M1() { }
|
||||
function M2() {
|
||||
[#|return 1;|]
|
||||
}
|
||||
function M3() { }
|
||||
}`);
|
||||
// Extraction position - file
|
||||
testExtractMethod("extractMethod25",
|
||||
`function M1() { }
|
||||
function M2() {
|
||||
[#|return 1;|]
|
||||
}
|
||||
function M3() { }`);
|
||||
// Extraction position - class without ctor
|
||||
testExtractMethod("extractMethod26",
|
||||
`class C {
|
||||
M1() { }
|
||||
M2() {
|
||||
[#|return 1;|]
|
||||
}
|
||||
M3() { }
|
||||
}`);
|
||||
// Extraction position - class with ctor in middle
|
||||
testExtractMethod("extractMethod27",
|
||||
`class C {
|
||||
M1() { }
|
||||
M2() {
|
||||
[#|return 1;|]
|
||||
}
|
||||
constructor() { }
|
||||
M3() { }
|
||||
}`);
|
||||
// Extraction position - class with ctor at end
|
||||
testExtractMethod("extractMethod28",
|
||||
`class C {
|
||||
M1() { }
|
||||
M2() {
|
||||
[#|return 1;|]
|
||||
}
|
||||
M3() { }
|
||||
constructor() { }
|
||||
}`);
|
||||
// Shorthand property names
|
||||
testExtractMethod("extractMethod29",
|
||||
`interface UnaryExpression {
|
||||
kind: "Unary";
|
||||
operator: string;
|
||||
operand: any;
|
||||
}
|
||||
|
||||
function parseUnaryExpression(operator: string): UnaryExpression {
|
||||
[#|return {
|
||||
kind: "Unary",
|
||||
operator,
|
||||
operand: parsePrimaryExpression(),
|
||||
};|]
|
||||
}
|
||||
|
||||
function parsePrimaryExpression(): any {
|
||||
throw "Not implemented";
|
||||
}`);
|
||||
// Type parameter as declared type
|
||||
testExtractMethod("extractMethod30",
|
||||
`function F<T>() {
|
||||
[#|let t: T;|]
|
||||
}`);
|
||||
// Return in nested function
|
||||
testExtractMethod("extractMethod31",
|
||||
`namespace N {
|
||||
|
||||
export const value = 1;
|
||||
|
||||
() => {
|
||||
var f: () => number;
|
||||
[#|f = function (): number {
|
||||
return value;
|
||||
}|]
|
||||
}
|
||||
}`);
|
||||
// Return in nested class
|
||||
testExtractMethod("extractMethod32",
|
||||
`namespace N {
|
||||
|
||||
export const value = 1;
|
||||
|
||||
() => {
|
||||
[#|var c = class {
|
||||
M() {
|
||||
return value;
|
||||
}
|
||||
}|]
|
||||
}
|
||||
}`);
|
||||
// Selection excludes leading trivia of declaration
|
||||
testExtractMethod("extractMethod33",
|
||||
`function F() {
|
||||
[#|function G() { }|]
|
||||
}`);
|
||||
});
|
||||
|
||||
@@ -649,9 +809,12 @@ namespace A {
|
||||
data.push(`// ==ORIGINAL==`);
|
||||
data.push(sourceFile.text);
|
||||
for (const r of results) {
|
||||
const changes = refactor.extractMethod.getPossibleExtractions(result.targetRange, context, results.indexOf(r))[0].changes;
|
||||
const { renameLocation, edits } = refactor.extractMethod.getExtractionAtIndex(result.targetRange, context, results.indexOf(r));
|
||||
assert.lengthOf(edits, 1);
|
||||
data.push(`// ==SCOPE::${r.scopeDescription}==`);
|
||||
data.push(textChanges.applyChanges(sourceFile.text, changes[0].textChanges));
|
||||
const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges);
|
||||
const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation);
|
||||
data.push(newTextWithRename);
|
||||
}
|
||||
return data.join(newLineCharacter);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
|
||||
namespace ts {
|
||||
describe("languageService", () => {
|
||||
const files: {[index: string]: string} = {
|
||||
"foo.ts": `import Vue from "./vue";
|
||||
import Component from "./vue-class-component";
|
||||
import { vueTemplateHtml } from "./variables";
|
||||
|
||||
@Component({
|
||||
template: vueTemplateHtml,
|
||||
})
|
||||
class Carousel<T> extends Vue {
|
||||
}`,
|
||||
"variables.ts": `export const vueTemplateHtml = \`<div></div>\`;`,
|
||||
"vue.d.ts": `export namespace Vue { export type Config = { template: string }; }`,
|
||||
"vue-class-component.d.ts": `import Vue from "./vue";
|
||||
export function Component(x: Config): any;`
|
||||
};
|
||||
// Regression test for GH #18245 - bug in single line comment writer caused a debug assertion when attempting
|
||||
// to write an alias to a module's default export was referrenced across files and had no default export
|
||||
it("should be able to create a language service which can respond to deinition requests without throwing", () => {
|
||||
const languageService = ts.createLanguageService({
|
||||
getCompilationSettings() {
|
||||
return {};
|
||||
},
|
||||
getScriptFileNames() {
|
||||
return ["foo.ts", "variables.ts", "vue.d.ts", "vue-class-component.d.ts"];
|
||||
},
|
||||
getScriptVersion(_fileName) {
|
||||
return "";
|
||||
},
|
||||
getScriptSnapshot(fileName) {
|
||||
if (fileName === ".ts") {
|
||||
return ts.ScriptSnapshot.fromString("");
|
||||
}
|
||||
return ts.ScriptSnapshot.fromString(files[fileName] || "");
|
||||
},
|
||||
getCurrentDirectory: () => ".",
|
||||
getDefaultLibFileName(options) {
|
||||
return ts.getDefaultLibFilePath(options);
|
||||
},
|
||||
fileExists: noop as any,
|
||||
readFile: noop as any,
|
||||
readDirectory: noop as any,
|
||||
});
|
||||
const definitions = languageService.getDefinitionAtPosition("foo.ts", 160); // 160 is the latter `vueTemplateHtml` position
|
||||
expect(definitions).to.exist;
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -198,33 +198,34 @@ namespace ts {
|
||||
const moduleFile = { name: "/a/b/node_modules/foo.ts" };
|
||||
const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
|
||||
checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [
|
||||
"/a/b/c/d/node_modules/foo/package.json",
|
||||
"/a/b/c/d/node_modules/foo.ts",
|
||||
"/a/b/c/d/node_modules/foo.tsx",
|
||||
"/a/b/c/d/node_modules/foo.d.ts",
|
||||
"/a/b/c/d/node_modules/foo/package.json",
|
||||
|
||||
"/a/b/c/d/node_modules/foo/index.ts",
|
||||
"/a/b/c/d/node_modules/foo/index.tsx",
|
||||
"/a/b/c/d/node_modules/foo/index.d.ts",
|
||||
|
||||
"/a/b/c/d/node_modules/@types/foo.d.ts",
|
||||
"/a/b/c/d/node_modules/@types/foo/package.json",
|
||||
"/a/b/c/d/node_modules/@types/foo.d.ts",
|
||||
|
||||
"/a/b/c/d/node_modules/@types/foo/index.d.ts",
|
||||
|
||||
"/a/b/c/node_modules/foo/package.json",
|
||||
"/a/b/c/node_modules/foo.ts",
|
||||
"/a/b/c/node_modules/foo.tsx",
|
||||
"/a/b/c/node_modules/foo.d.ts",
|
||||
"/a/b/c/node_modules/foo/package.json",
|
||||
|
||||
"/a/b/c/node_modules/foo/index.ts",
|
||||
"/a/b/c/node_modules/foo/index.tsx",
|
||||
"/a/b/c/node_modules/foo/index.d.ts",
|
||||
|
||||
"/a/b/c/node_modules/@types/foo.d.ts",
|
||||
"/a/b/c/node_modules/@types/foo/package.json",
|
||||
"/a/b/c/node_modules/@types/foo.d.ts",
|
||||
|
||||
"/a/b/c/node_modules/@types/foo/index.d.ts",
|
||||
"/a/b/node_modules/foo/package.json",
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -250,52 +251,52 @@ namespace ts {
|
||||
const moduleFile: File = { name: "/a/node_modules/foo/index.d.ts" };
|
||||
const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
|
||||
checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo/package.json",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo.tsx",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo.d.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo/package.json",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.tsx",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.d.ts",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.d.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/package.json",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.d.ts",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/index.d.ts",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/foo/package.json",
|
||||
"/a/node_modules/b/c/node_modules/foo.ts",
|
||||
"/a/node_modules/b/c/node_modules/foo.tsx",
|
||||
"/a/node_modules/b/c/node_modules/foo.d.ts",
|
||||
"/a/node_modules/b/c/node_modules/foo/package.json",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/foo/index.ts",
|
||||
"/a/node_modules/b/c/node_modules/foo/index.tsx",
|
||||
"/a/node_modules/b/c/node_modules/foo/index.d.ts",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/@types/foo.d.ts",
|
||||
"/a/node_modules/b/c/node_modules/@types/foo/package.json",
|
||||
"/a/node_modules/b/c/node_modules/@types/foo.d.ts",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/@types/foo/index.d.ts",
|
||||
|
||||
"/a/node_modules/b/node_modules/foo/package.json",
|
||||
"/a/node_modules/b/node_modules/foo.ts",
|
||||
"/a/node_modules/b/node_modules/foo.tsx",
|
||||
"/a/node_modules/b/node_modules/foo.d.ts",
|
||||
"/a/node_modules/b/node_modules/foo/package.json",
|
||||
|
||||
"/a/node_modules/b/node_modules/foo/index.ts",
|
||||
"/a/node_modules/b/node_modules/foo/index.tsx",
|
||||
"/a/node_modules/b/node_modules/foo/index.d.ts",
|
||||
|
||||
"/a/node_modules/b/node_modules/@types/foo.d.ts",
|
||||
"/a/node_modules/b/node_modules/@types/foo/package.json",
|
||||
"/a/node_modules/b/node_modules/@types/foo.d.ts",
|
||||
|
||||
"/a/node_modules/b/node_modules/@types/foo/index.d.ts",
|
||||
|
||||
"/a/node_modules/foo/package.json",
|
||||
"/a/node_modules/foo.ts",
|
||||
"/a/node_modules/foo.tsx",
|
||||
"/a/node_modules/foo.d.ts",
|
||||
"/a/node_modules/foo/package.json",
|
||||
|
||||
"/a/node_modules/foo/index.ts",
|
||||
"/a/node_modules/foo/index.tsx"
|
||||
@@ -707,21 +708,23 @@ import b = require("./moduleB");
|
||||
"/root/generated/file6/index.d.ts",
|
||||
|
||||
// fallback to standard node behavior
|
||||
"/root/folder1/node_modules/file6/package.json",
|
||||
|
||||
// load from file
|
||||
"/root/folder1/node_modules/file6.ts",
|
||||
"/root/folder1/node_modules/file6.tsx",
|
||||
"/root/folder1/node_modules/file6.d.ts",
|
||||
|
||||
// load from folder
|
||||
"/root/folder1/node_modules/file6/package.json",
|
||||
"/root/folder1/node_modules/file6/index.ts",
|
||||
"/root/folder1/node_modules/file6/index.tsx",
|
||||
"/root/folder1/node_modules/file6/index.d.ts",
|
||||
|
||||
"/root/folder1/node_modules/@types/file6.d.ts",
|
||||
|
||||
"/root/folder1/node_modules/@types/file6/package.json",
|
||||
"/root/folder1/node_modules/@types/file6.d.ts",
|
||||
"/root/folder1/node_modules/@types/file6/index.d.ts",
|
||||
|
||||
"/root/node_modules/file6/package.json",
|
||||
// success on /root/node_modules/file6.ts
|
||||
], /*isExternalLibraryImport*/ true);
|
||||
|
||||
|
||||
@@ -441,20 +441,20 @@ namespace ts {
|
||||
"======== Resolving module 'a' from 'file1.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'NodeJs'.",
|
||||
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
|
||||
"File 'node_modules/a/package.json' does not exist.",
|
||||
"File 'node_modules/a.ts' does not exist.",
|
||||
"File 'node_modules/a.tsx' does not exist.",
|
||||
"File 'node_modules/a.d.ts' does not exist.",
|
||||
"File 'node_modules/a/package.json' does not exist.",
|
||||
"File 'node_modules/a/index.ts' does not exist.",
|
||||
"File 'node_modules/a/index.tsx' does not exist.",
|
||||
"File 'node_modules/a/index.d.ts' does not exist.",
|
||||
"File 'node_modules/@types/a.d.ts' does not exist.",
|
||||
"File 'node_modules/@types/a/package.json' does not exist.",
|
||||
"File 'node_modules/@types/a.d.ts' does not exist.",
|
||||
"File 'node_modules/@types/a/index.d.ts' does not exist.",
|
||||
"Loading module 'a' from 'node_modules' folder, target file type 'JavaScript'.",
|
||||
"File 'node_modules/a/package.json' does not exist.",
|
||||
"File 'node_modules/a.js' does not exist.",
|
||||
"File 'node_modules/a.jsx' does not exist.",
|
||||
"File 'node_modules/a/package.json' does not exist.",
|
||||
"File 'node_modules/a/index.js' does not exist.",
|
||||
"File 'node_modules/a/index.jsx' does not exist.",
|
||||
"======== Module name 'a' was not resolved. ========"
|
||||
@@ -474,10 +474,10 @@ namespace ts {
|
||||
"======== Resolving module 'a' from 'file1.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'NodeJs'.",
|
||||
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
|
||||
"File 'node_modules/a/package.json' does not exist.",
|
||||
"File 'node_modules/a.ts' does not exist.",
|
||||
"File 'node_modules/a.tsx' does not exist.",
|
||||
"File 'node_modules/a.d.ts' does not exist.",
|
||||
"File 'node_modules/a/package.json' does not exist.",
|
||||
"File 'node_modules/a/index.ts' does not exist.",
|
||||
"File 'node_modules/a/index.tsx' does not exist.",
|
||||
"File 'node_modules/a/index.d.ts' exist - use it as a name resolution result.",
|
||||
@@ -510,14 +510,14 @@ namespace ts {
|
||||
"File '/fs.ts' does not exist.",
|
||||
"File '/fs.tsx' does not exist.",
|
||||
"File '/fs.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/a/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/a/b/fs.js' does not exist.",
|
||||
"File '/a/b/fs.jsx' does not exist.",
|
||||
@@ -552,14 +552,14 @@ namespace ts {
|
||||
"File '/fs.ts' does not exist.",
|
||||
"File '/fs.tsx' does not exist.",
|
||||
"File '/fs.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/a/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/a/b/fs.js' does not exist.",
|
||||
"File '/a/b/fs.jsx' does not exist.",
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace ts {
|
||||
|
||||
testBaseline("types", () => {
|
||||
return transformSourceFile(`let a: () => void`, [
|
||||
context => file => visitNode(file, function visitor(node: Node): VisitResult<Node> {
|
||||
context => file => visitNode(file, function visitor(node: Node): VisitResult<Node> {
|
||||
return visitEachChild(node, visitor, context);
|
||||
})
|
||||
]);
|
||||
@@ -91,14 +91,14 @@ namespace ts {
|
||||
class C { foo = 10; static bar = 20 }
|
||||
namespace C { export let x = 10; }
|
||||
`, {
|
||||
transformers: {
|
||||
before: [forceNamespaceRewrite],
|
||||
},
|
||||
compilerOptions: {
|
||||
target: ts.ScriptTarget.ESNext,
|
||||
newLine: NewLineKind.CarriageReturnLineFeed,
|
||||
}
|
||||
}).outputText;
|
||||
transformers: {
|
||||
before: [forceNamespaceRewrite],
|
||||
},
|
||||
compilerOptions: {
|
||||
target: ts.ScriptTarget.ESNext,
|
||||
newLine: NewLineKind.CarriageReturnLineFeed,
|
||||
}
|
||||
}).outputText;
|
||||
});
|
||||
|
||||
testBaseline("synthesizedClassAndNamespaceCombination", () => {
|
||||
@@ -138,6 +138,37 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
testBaseline("transformAwayExportStar", () => {
|
||||
return ts.transpileModule("export * from './helper';", {
|
||||
transformers: {
|
||||
before: [expandExportStar],
|
||||
},
|
||||
compilerOptions: {
|
||||
target: ts.ScriptTarget.ESNext,
|
||||
newLine: NewLineKind.CarriageReturnLineFeed,
|
||||
}
|
||||
}).outputText;
|
||||
|
||||
function expandExportStar(context: ts.TransformationContext) {
|
||||
return (sourceFile: ts.SourceFile): ts.SourceFile => {
|
||||
return visitNode(sourceFile);
|
||||
|
||||
function visitNode<T extends ts.Node>(node: T): T {
|
||||
if (node.kind === ts.SyntaxKind.ExportDeclaration) {
|
||||
const ed = node as ts.Node as ts.ExportDeclaration;
|
||||
const exports = [{ name: "x" }];
|
||||
const exportSpecifiers = exports.map(e => ts.createExportSpecifier(e.name, e.name));
|
||||
const exportClause = ts.createNamedExports(exportSpecifiers);
|
||||
const newEd = ts.updateExportDeclaration(ed, ed.decorators, ed.modifiers, exportClause, ed.moduleSpecifier);
|
||||
|
||||
return newEd as ts.Node as T;
|
||||
}
|
||||
return ts.visitEachChild(node, visitNode, context);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,32 +17,33 @@ namespace ts {
|
||||
let oldTranspileResult: string;
|
||||
let oldTranspileDiagnostics: Diagnostic[];
|
||||
|
||||
transpileOptions = testSettings.options || {};
|
||||
if (!transpileOptions.compilerOptions) {
|
||||
transpileOptions.compilerOptions = {};
|
||||
}
|
||||
|
||||
if (transpileOptions.compilerOptions.newLine === undefined) {
|
||||
// use \r\n as default new line
|
||||
transpileOptions.compilerOptions.newLine = ts.NewLineKind.CarriageReturnLineFeed;
|
||||
}
|
||||
|
||||
transpileOptions.compilerOptions.sourceMap = true;
|
||||
|
||||
if (!transpileOptions.fileName) {
|
||||
transpileOptions.fileName = transpileOptions.compilerOptions.jsx ? "file.tsx" : "file.ts";
|
||||
}
|
||||
|
||||
transpileOptions.reportDiagnostics = true;
|
||||
|
||||
justName = "transpile/" + name.replace(/[^a-z0-9\-. ]/ig, "") + (transpileOptions.compilerOptions.jsx ? Extension.Tsx : Extension.Ts);
|
||||
toBeCompiled = [{
|
||||
unitName: transpileOptions.fileName,
|
||||
content: input
|
||||
}];
|
||||
|
||||
canUseOldTranspile = !transpileOptions.renamedDependencies;
|
||||
|
||||
before(() => {
|
||||
transpileOptions = testSettings.options || {};
|
||||
if (!transpileOptions.compilerOptions) {
|
||||
transpileOptions.compilerOptions = {};
|
||||
}
|
||||
|
||||
if (transpileOptions.compilerOptions.newLine === undefined) {
|
||||
// use \r\n as default new line
|
||||
transpileOptions.compilerOptions.newLine = ts.NewLineKind.CarriageReturnLineFeed;
|
||||
}
|
||||
|
||||
transpileOptions.compilerOptions.sourceMap = true;
|
||||
|
||||
if (!transpileOptions.fileName) {
|
||||
transpileOptions.fileName = transpileOptions.compilerOptions.jsx ? "file.tsx" : "file.ts";
|
||||
}
|
||||
|
||||
transpileOptions.reportDiagnostics = true;
|
||||
|
||||
justName = "transpile/" + name.replace(/[^a-z0-9\-. ]/ig, "") + (transpileOptions.compilerOptions.jsx ? Extension.Tsx : Extension.Ts);
|
||||
toBeCompiled = [{
|
||||
unitName: transpileOptions.fileName,
|
||||
content: input
|
||||
}];
|
||||
|
||||
canUseOldTranspile = !transpileOptions.renamedDependencies;
|
||||
transpileResult = transpileModule(input, transpileOptions);
|
||||
|
||||
if (canUseOldTranspile) {
|
||||
@@ -52,10 +53,6 @@ namespace ts {
|
||||
});
|
||||
|
||||
after(() => {
|
||||
justName = undefined;
|
||||
transpileOptions = undefined;
|
||||
canUseOldTranspile = undefined;
|
||||
toBeCompiled = undefined;
|
||||
transpileResult = undefined;
|
||||
oldTranspileResult = undefined;
|
||||
oldTranspileDiagnostics = undefined;
|
||||
|
||||
@@ -2510,8 +2510,8 @@ namespace ts.projectSystem {
|
||||
"======== Module name 'lib' was not resolved. ========",
|
||||
`Auto discovery for typings is enabled in project '${proj.getProjectName()}'. Running extra resolution pass for module 'lib' using cache location '/a/cache'.`,
|
||||
"File '/a/cache/node_modules/lib.d.ts' does not exist.",
|
||||
"File '/a/cache/node_modules/@types/lib.d.ts' does not exist.",
|
||||
"File '/a/cache/node_modules/@types/lib/package.json' does not exist.",
|
||||
"File '/a/cache/node_modules/@types/lib.d.ts' does not exist.",
|
||||
"File '/a/cache/node_modules/@types/lib/index.d.ts' exist - use it as a name resolution result.",
|
||||
]);
|
||||
checkProjectActualFiles(proj, [file1.path, lib.path]);
|
||||
|
||||
@@ -366,13 +366,11 @@ namespace ts.projectSystem {
|
||||
};
|
||||
|
||||
const host = createServerHost([file1, file2]);
|
||||
let enqueueIsCalled = false;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
enqueueInstallTypingsRequest(project: server.Project, typeAcquisition: TypeAcquisition, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
enqueueIsCalled = true;
|
||||
super.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
|
||||
Vendored
+1
-1
@@ -327,7 +327,7 @@ interface ObjectConstructor {
|
||||
* @param o Object that contains the property.
|
||||
* @param p Name of the property.
|
||||
*/
|
||||
getOwnPropertyDescriptor(o: any, propertyKey: PropertyKey): PropertyDescriptor;
|
||||
getOwnPropertyDescriptor(o: any, propertyKey: PropertyKey): PropertyDescriptor | undefined;
|
||||
|
||||
/**
|
||||
* Adds a property to an object, or modifies attributes of an existing property.
|
||||
|
||||
Vendored
-36
@@ -209,10 +209,6 @@ interface String {
|
||||
[Symbol.iterator](): IterableIterator<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 8-bit integer values. The contents are initialized to 0. If the requested
|
||||
* number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Int8Array {
|
||||
[Symbol.iterator](): IterableIterator<number>;
|
||||
/**
|
||||
@@ -241,10 +237,6 @@ interface Int8ArrayConstructor {
|
||||
from(arrayLike: Iterable<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Int8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 8-bit unsigned integer values. The contents are initialized to 0. If the
|
||||
* requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Uint8Array {
|
||||
[Symbol.iterator](): IterableIterator<number>;
|
||||
/**
|
||||
@@ -273,10 +265,6 @@ interface Uint8ArrayConstructor {
|
||||
from(arrayLike: Iterable<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 8-bit unsigned integer (clamped) values. The contents are initialized to 0.
|
||||
* If the requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Uint8ClampedArray {
|
||||
[Symbol.iterator](): IterableIterator<number>;
|
||||
/**
|
||||
@@ -308,10 +296,6 @@ interface Uint8ClampedArrayConstructor {
|
||||
from(arrayLike: Iterable<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint8ClampedArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 16-bit signed integer values. The contents are initialized to 0. If the
|
||||
* requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Int16Array {
|
||||
[Symbol.iterator](): IterableIterator<number>;
|
||||
/**
|
||||
@@ -342,10 +326,6 @@ interface Int16ArrayConstructor {
|
||||
from(arrayLike: Iterable<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Int16Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 16-bit unsigned integer values. The contents are initialized to 0. If the
|
||||
* requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Uint16Array {
|
||||
[Symbol.iterator](): IterableIterator<number>;
|
||||
/**
|
||||
@@ -374,10 +354,6 @@ interface Uint16ArrayConstructor {
|
||||
from(arrayLike: Iterable<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint16Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 32-bit signed integer values. The contents are initialized to 0. If the
|
||||
* requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Int32Array {
|
||||
[Symbol.iterator](): IterableIterator<number>;
|
||||
/**
|
||||
@@ -406,10 +382,6 @@ interface Int32ArrayConstructor {
|
||||
from(arrayLike: Iterable<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Int32Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 32-bit unsigned integer values. The contents are initialized to 0. If the
|
||||
* requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Uint32Array {
|
||||
[Symbol.iterator](): IterableIterator<number>;
|
||||
/**
|
||||
@@ -438,10 +410,6 @@ interface Uint32ArrayConstructor {
|
||||
from(arrayLike: Iterable<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint32Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 32-bit float values. The contents are initialized to 0. If the requested number
|
||||
* of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Float32Array {
|
||||
[Symbol.iterator](): IterableIterator<number>;
|
||||
/**
|
||||
@@ -470,10 +438,6 @@ interface Float32ArrayConstructor {
|
||||
from(arrayLike: Iterable<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Float32Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 64-bit float values. The contents are initialized to 0. If the requested
|
||||
* number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Float64Array {
|
||||
[Symbol.iterator](): IterableIterator<number>;
|
||||
/**
|
||||
|
||||
Vendored
+1
-1
@@ -4,7 +4,7 @@ declare namespace Reflect {
|
||||
function defineProperty(target: object, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean;
|
||||
function deleteProperty(target: object, propertyKey: PropertyKey): boolean;
|
||||
function get(target: object, propertyKey: PropertyKey, receiver?: any): any;
|
||||
function getOwnPropertyDescriptor(target: object, propertyKey: PropertyKey): PropertyDescriptor;
|
||||
function getOwnPropertyDescriptor(target: object, propertyKey: PropertyKey): PropertyDescriptor | undefined;
|
||||
function getPrototypeOf(target: object): object;
|
||||
function has(target: object, propertyKey: PropertyKey): boolean;
|
||||
function isExtensible(target: object): boolean;
|
||||
|
||||
Vendored
-42
@@ -240,12 +240,6 @@ interface String {
|
||||
split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a raw buffer of binary data, which is used to store data for the
|
||||
* different typed arrays. ArrayBuffers cannot be read from or written to directly,
|
||||
* but can be passed to a typed array or DataView Object to interpret the raw
|
||||
* buffer as needed.
|
||||
*/
|
||||
interface ArrayBuffer {
|
||||
readonly [Symbol.toStringTag]: "ArrayBuffer";
|
||||
}
|
||||
@@ -254,74 +248,38 @@ interface DataView {
|
||||
readonly [Symbol.toStringTag]: "DataView";
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 8-bit integer values. The contents are initialized to 0. If the requested
|
||||
* number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Int8Array {
|
||||
readonly [Symbol.toStringTag]: "Int8Array";
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 8-bit unsigned integer values. The contents are initialized to 0. If the
|
||||
* requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Uint8Array {
|
||||
readonly [Symbol.toStringTag]: "UInt8Array";
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 8-bit unsigned integer (clamped) values. The contents are initialized to 0.
|
||||
* If the requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Uint8ClampedArray {
|
||||
readonly [Symbol.toStringTag]: "Uint8ClampedArray";
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 16-bit signed integer values. The contents are initialized to 0. If the
|
||||
* requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Int16Array {
|
||||
readonly [Symbol.toStringTag]: "Int16Array";
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 16-bit unsigned integer values. The contents are initialized to 0. If the
|
||||
* requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Uint16Array {
|
||||
readonly [Symbol.toStringTag]: "Uint16Array";
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 32-bit signed integer values. The contents are initialized to 0. If the
|
||||
* requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Int32Array {
|
||||
readonly [Symbol.toStringTag]: "Int32Array";
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 32-bit unsigned integer values. The contents are initialized to 0. If the
|
||||
* requested number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Uint32Array {
|
||||
readonly [Symbol.toStringTag]: "Uint32Array";
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 32-bit float values. The contents are initialized to 0. If the requested number
|
||||
* of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Float32Array {
|
||||
readonly [Symbol.toStringTag]: "Float32Array";
|
||||
}
|
||||
|
||||
/**
|
||||
* A typed array of 64-bit float values. The contents are initialized to 0. If the requested
|
||||
* number of bytes could not be allocated an exception is raised.
|
||||
*/
|
||||
interface Float64Array {
|
||||
readonly [Symbol.toStringTag]: "Float64Array";
|
||||
}
|
||||
|
||||
Vendored
+6
@@ -22,4 +22,10 @@ interface ObjectConstructor {
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
entries(o: any): [string, any][];
|
||||
|
||||
/**
|
||||
* Returns an object containing all own property descriptors of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
getOwnPropertyDescriptors<T>(o: T): {[P in keyof T]: TypedPropertyDescriptor<T[P]>} & { [x: string]: PropertyDescriptor };
|
||||
}
|
||||
|
||||
Vendored
+37
-37
@@ -127,7 +127,7 @@ interface ObjectConstructor {
|
||||
* @param o Object that contains the property.
|
||||
* @param p Name of the property.
|
||||
*/
|
||||
getOwnPropertyDescriptor(o: any, p: string): PropertyDescriptor;
|
||||
getOwnPropertyDescriptor(o: any, p: string): PropertyDescriptor | undefined;
|
||||
|
||||
/**
|
||||
* Returns the names of the own properties of an object. The own properties of an object are those that are defined directly
|
||||
@@ -1577,7 +1577,7 @@ interface Int8Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
find(predicate: (value: number, index: number, obj: Int8Array) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
@@ -1588,7 +1588,7 @@ interface Int8Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
|
||||
findIndex(predicate: (value: number, index: number, obj: Int8Array) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -1744,8 +1744,8 @@ interface Int8Array {
|
||||
interface Int8ArrayConstructor {
|
||||
readonly prototype: Int8Array;
|
||||
new(length: number): Int8Array;
|
||||
new(array: ArrayLike<number>): Int8Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset?: number, length?: number): Int8Array;
|
||||
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Int8Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Int8Array;
|
||||
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
@@ -1844,7 +1844,7 @@ interface Uint8Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
find(predicate: (value: number, index: number, obj: Uint8Array) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
@@ -1855,7 +1855,7 @@ interface Uint8Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
|
||||
findIndex(predicate: (value: number, index: number, obj: Uint8Array) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -2012,8 +2012,8 @@ interface Uint8Array {
|
||||
interface Uint8ArrayConstructor {
|
||||
readonly prototype: Uint8Array;
|
||||
new(length: number): Uint8Array;
|
||||
new(array: ArrayLike<number>): Uint8Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint8Array;
|
||||
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint8Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint8Array;
|
||||
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
@@ -2111,7 +2111,7 @@ interface Uint8ClampedArray {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
find(predicate: (value: number, index: number, obj: Uint8ClampedArray) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
@@ -2122,7 +2122,7 @@ interface Uint8ClampedArray {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
|
||||
findIndex(predicate: (value: number, index: number, obj: Uint8ClampedArray) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -2279,8 +2279,8 @@ interface Uint8ClampedArray {
|
||||
interface Uint8ClampedArrayConstructor {
|
||||
readonly prototype: Uint8ClampedArray;
|
||||
new(length: number): Uint8ClampedArray;
|
||||
new(array: ArrayLike<number>): Uint8ClampedArray;
|
||||
new(buffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint8ClampedArray;
|
||||
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint8ClampedArray;
|
||||
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint8ClampedArray;
|
||||
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
@@ -2377,7 +2377,7 @@ interface Int16Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
find(predicate: (value: number, index: number, obj: Int16Array) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
@@ -2388,7 +2388,7 @@ interface Int16Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
|
||||
findIndex(predicate: (value: number, index: number, obj: Int16Array) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -2544,8 +2544,8 @@ interface Int16Array {
|
||||
interface Int16ArrayConstructor {
|
||||
readonly prototype: Int16Array;
|
||||
new(length: number): Int16Array;
|
||||
new(array: ArrayLike<number>): Int16Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset?: number, length?: number): Int16Array;
|
||||
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Int16Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Int16Array;
|
||||
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
@@ -2644,7 +2644,7 @@ interface Uint16Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
find(predicate: (value: number, index: number, obj: Uint16Array) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
@@ -2655,7 +2655,7 @@ interface Uint16Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
|
||||
findIndex(predicate: (value: number, index: number, obj: Uint16Array) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -2812,8 +2812,8 @@ interface Uint16Array {
|
||||
interface Uint16ArrayConstructor {
|
||||
readonly prototype: Uint16Array;
|
||||
new(length: number): Uint16Array;
|
||||
new(array: ArrayLike<number>): Uint16Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint16Array;
|
||||
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint16Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint16Array;
|
||||
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
@@ -2911,7 +2911,7 @@ interface Int32Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
find(predicate: (value: number, index: number, obj: Int32Array) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
@@ -2922,7 +2922,7 @@ interface Int32Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
|
||||
findIndex(predicate: (value: number, index: number, obj: Int32Array) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -3079,8 +3079,8 @@ interface Int32Array {
|
||||
interface Int32ArrayConstructor {
|
||||
readonly prototype: Int32Array;
|
||||
new(length: number): Int32Array;
|
||||
new(array: ArrayLike<number>): Int32Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset?: number, length?: number): Int32Array;
|
||||
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Int32Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Int32Array;
|
||||
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
@@ -3178,7 +3178,7 @@ interface Uint32Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
find(predicate: (value: number, index: number, obj: Uint32Array) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
@@ -3189,7 +3189,7 @@ interface Uint32Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
|
||||
findIndex(predicate: (value: number, index: number, obj: Uint32Array) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -3345,8 +3345,8 @@ interface Uint32Array {
|
||||
interface Uint32ArrayConstructor {
|
||||
readonly prototype: Uint32Array;
|
||||
new(length: number): Uint32Array;
|
||||
new(array: ArrayLike<number>): Uint32Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint32Array;
|
||||
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint32Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint32Array;
|
||||
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
@@ -3444,7 +3444,7 @@ interface Float32Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
find(predicate: (value: number, index: number, obj: Float32Array) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
@@ -3455,7 +3455,7 @@ interface Float32Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
|
||||
findIndex(predicate: (value: number, index: number, obj: Float32Array) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -3612,8 +3612,8 @@ interface Float32Array {
|
||||
interface Float32ArrayConstructor {
|
||||
readonly prototype: Float32Array;
|
||||
new(length: number): Float32Array;
|
||||
new(array: ArrayLike<number>): Float32Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset?: number, length?: number): Float32Array;
|
||||
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Float32Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Float32Array;
|
||||
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
@@ -3712,7 +3712,7 @@ interface Float64Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
find(predicate: (value: number, index: number, obj: Float64Array) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
@@ -3723,7 +3723,7 @@ interface Float64Array {
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
|
||||
findIndex(predicate: (value: number, index: number, obj: Float64Array) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -3880,8 +3880,8 @@ interface Float64Array {
|
||||
interface Float64ArrayConstructor {
|
||||
readonly prototype: Float64Array;
|
||||
new(length: number): Float64Array;
|
||||
new(array: ArrayLike<number>): Float64Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset?: number, length?: number): Float64Array;
|
||||
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Float64Array;
|
||||
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Float64Array;
|
||||
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
|
||||
Vendored
+18
-7
@@ -201,10 +201,18 @@ declare var WScript: {
|
||||
Sleep(intTime: number): void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an Automation SAFEARRAY
|
||||
*/
|
||||
declare class SafeArray<T = any> {
|
||||
private constructor();
|
||||
private SafeArray_typekey: SafeArray<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows enumerating over a COM collection, which may not have indexed item access.
|
||||
*/
|
||||
interface Enumerator<T> {
|
||||
interface Enumerator<T = any> {
|
||||
/**
|
||||
* Returns true if the current item is the last one in the collection, or the collection is empty,
|
||||
* or the current item is undefined.
|
||||
@@ -230,8 +238,9 @@ interface Enumerator<T> {
|
||||
}
|
||||
|
||||
interface EnumeratorConstructor {
|
||||
new <T>(collection: any): Enumerator<T>;
|
||||
new (collection: any): Enumerator<any>;
|
||||
new <T = any>(safearray: SafeArray<T>): Enumerator<T>;
|
||||
new <T = any>(collection: { Item(index: any): T }): Enumerator<T>;
|
||||
new <T = any>(collection: any): Enumerator<T>;
|
||||
}
|
||||
|
||||
declare var Enumerator: EnumeratorConstructor;
|
||||
@@ -239,7 +248,7 @@ declare var Enumerator: EnumeratorConstructor;
|
||||
/**
|
||||
* Enables reading from a COM safe array, which might have an alternate lower bound, or multiple dimensions.
|
||||
*/
|
||||
interface VBArray<T> {
|
||||
interface VBArray<T = any> {
|
||||
/**
|
||||
* Returns the number of dimensions (1-based).
|
||||
*/
|
||||
@@ -271,8 +280,7 @@ interface VBArray<T> {
|
||||
}
|
||||
|
||||
interface VBArrayConstructor {
|
||||
new <T>(safeArray: any): VBArray<T>;
|
||||
new (safeArray: any): VBArray<any>;
|
||||
new <T = any>(safeArray: SafeArray<T>): VBArray<T>;
|
||||
}
|
||||
|
||||
declare var VBArray: VBArrayConstructor;
|
||||
@@ -280,7 +288,10 @@ declare var VBArray: VBArrayConstructor;
|
||||
/**
|
||||
* Automation date (VT_DATE)
|
||||
*/
|
||||
interface VarDate { }
|
||||
declare class VarDate {
|
||||
private constructor();
|
||||
private VarDate_typekey: VarDate;
|
||||
}
|
||||
|
||||
interface DateConstructor {
|
||||
new (vd: VarDate): Date;
|
||||
|
||||
@@ -573,7 +573,7 @@ namespace ts.server {
|
||||
|
||||
getEditsForRefactor(
|
||||
fileName: string,
|
||||
_formatOptions: FormatCodeSettings,
|
||||
formatOptions: FormatCodeSettings,
|
||||
positionOrRange: number | TextRange,
|
||||
refactorName: string,
|
||||
actionName: string): RefactorEditInfo {
|
||||
@@ -581,14 +581,13 @@ namespace ts.server {
|
||||
const args = this.createFileLocationOrRangeRequestArgs(positionOrRange, fileName) as protocol.GetEditsForRefactorRequestArgs;
|
||||
args.refactor = refactorName;
|
||||
args.action = actionName;
|
||||
args.formatOptions = formatOptions;
|
||||
|
||||
const request = this.processRequest<protocol.GetEditsForRefactorRequest>(CommandNames.GetEditsForRefactor, args);
|
||||
const response = this.processResponse<protocol.GetEditsForRefactorResponse>(request);
|
||||
|
||||
if (!response.body) {
|
||||
return {
|
||||
edits: []
|
||||
};
|
||||
return { edits: [], renameFilename: undefined, renameLocation: undefined };
|
||||
}
|
||||
|
||||
const edits: FileTextChanges[] = this.convertCodeEditsToTextChanges(response.body.edits);
|
||||
|
||||
@@ -1190,7 +1190,8 @@ namespace ts.server {
|
||||
/*languageServiceEnabled*/ !sizeLimitExceeded,
|
||||
projectOptions.compileOnSave === undefined ? false : projectOptions.compileOnSave);
|
||||
|
||||
this.addFilesToProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition, configFileErrors);
|
||||
const filesToAdd = projectOptions.files.concat(project.getExternalFiles());
|
||||
this.addFilesToProjectAndUpdateGraph(project, filesToAdd, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition, configFileErrors);
|
||||
|
||||
project.watchConfigFile(project => this.onConfigChangedForConfiguredProject(project));
|
||||
if (!sizeLimitExceeded) {
|
||||
@@ -1210,7 +1211,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: ReadonlyArray<Diagnostic>): void {
|
||||
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: ReadonlyArray<T>, propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: ReadonlyArray<Diagnostic>): void {
|
||||
let errors: Diagnostic[];
|
||||
for (const f of files) {
|
||||
const rootFileName = propertyReader.getFileName(f);
|
||||
@@ -1686,11 +1687,13 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private closeConfiguredProject(configFile: NormalizedPath): void {
|
||||
private closeConfiguredProject(configFile: NormalizedPath): boolean {
|
||||
const configuredProject = this.findConfiguredProjectByProjectName(configFile);
|
||||
if (configuredProject && configuredProject.deleteOpenRef() === 0) {
|
||||
this.removeProject(configuredProject);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
closeExternalProject(uncheckedFileName: string, suppressRefresh = false): void {
|
||||
|
||||
+43
-25
@@ -547,33 +547,32 @@ namespace ts.server {
|
||||
this.cachedUnresolvedImportsPerFile.remove(file);
|
||||
}
|
||||
|
||||
// 1. no changes in structure, no changes in unresolved imports - do nothing
|
||||
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
|
||||
// (can reuse cached imports for files that were not changed)
|
||||
// 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files
|
||||
// (can reuse cached imports for files that were not changed)
|
||||
// 4. compilation settings were changed in the way that might affect module resolution - drop all caches and collect all data from the scratch
|
||||
let unresolvedImports: SortedReadonlyArray<string>;
|
||||
if (hasChanges || changedFiles.length) {
|
||||
const result: string[] = [];
|
||||
for (const sourceFile of this.program.getSourceFiles()) {
|
||||
this.extractUnresolvedImportsFromSourceFile(sourceFile, result);
|
||||
}
|
||||
this.lastCachedUnresolvedImportsList = toDeduplicatedSortedArray(result);
|
||||
}
|
||||
unresolvedImports = this.lastCachedUnresolvedImportsList;
|
||||
|
||||
const cachedTypings = this.projectService.typingsCache.getTypingsForProject(this, unresolvedImports, hasChanges);
|
||||
if (this.setTypings(cachedTypings)) {
|
||||
hasChanges = this.updateGraphWorker() || hasChanges;
|
||||
}
|
||||
|
||||
// update builder only if language service is enabled
|
||||
// otherwise tell it to drop its internal state
|
||||
if (this.languageServiceEnabled) {
|
||||
// 1. no changes in structure, no changes in unresolved imports - do nothing
|
||||
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
|
||||
// (can reuse cached imports for files that were not changed)
|
||||
// 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files
|
||||
// (can reuse cached imports for files that were not changed)
|
||||
// 4. compilation settings were changed in the way that might affect module resolution - drop all caches and collect all data from the scratch
|
||||
if (hasChanges || changedFiles.length) {
|
||||
const result: string[] = [];
|
||||
for (const sourceFile of this.program.getSourceFiles()) {
|
||||
this.extractUnresolvedImportsFromSourceFile(sourceFile, result);
|
||||
}
|
||||
this.lastCachedUnresolvedImportsList = toDeduplicatedSortedArray(result);
|
||||
}
|
||||
|
||||
const cachedTypings = this.projectService.typingsCache.getTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasChanges);
|
||||
if (this.setTypings(cachedTypings)) {
|
||||
hasChanges = this.updateGraphWorker() || hasChanges;
|
||||
}
|
||||
|
||||
this.builder.onProjectUpdateGraph();
|
||||
}
|
||||
else {
|
||||
this.lastCachedUnresolvedImportsList = undefined;
|
||||
this.builder.clear();
|
||||
}
|
||||
|
||||
@@ -747,7 +746,8 @@ namespace ts.server {
|
||||
}
|
||||
// compute and return the difference
|
||||
const lastReportedFileNames = this.lastReportedFileNames;
|
||||
const currentFiles = arrayToSet(this.getFileNames());
|
||||
const externalFiles = this.getExternalFiles().map(f => toNormalizedPath(f));
|
||||
const currentFiles = arrayToSet(this.getFileNames().concat(externalFiles));
|
||||
|
||||
const added: string[] = [];
|
||||
const removed: string[] = [];
|
||||
@@ -770,7 +770,8 @@ namespace ts.server {
|
||||
else {
|
||||
// unknown version - return everything
|
||||
const projectFileNames = this.getFileNames();
|
||||
this.lastReportedFileNames = arrayToSet(projectFileNames);
|
||||
const externalFiles = this.getExternalFiles().map(f => toNormalizedPath(f));
|
||||
this.lastReportedFileNames = arrayToSet(projectFileNames.concat(externalFiles));
|
||||
this.lastReportedVersion = this.projectStructureVersion;
|
||||
return { info, files: projectFileNames, projectErrors: this.getGlobalProjectErrors() };
|
||||
}
|
||||
@@ -999,16 +1000,22 @@ namespace ts.server {
|
||||
if (this.projectService.globalPlugins) {
|
||||
// Enable global plugins with synthetic configuration entries
|
||||
for (const globalPluginName of this.projectService.globalPlugins) {
|
||||
// Skip empty names from odd commandline parses
|
||||
if (!globalPluginName) continue;
|
||||
|
||||
// Skip already-locally-loaded plugins
|
||||
if (options.plugins && options.plugins.some(p => p.name === globalPluginName)) continue;
|
||||
|
||||
// Provide global: true so plugins can detect why they can't find their config
|
||||
this.projectService.logger.info(`Loading global plugin ${globalPluginName}`);
|
||||
this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]) {
|
||||
this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`);
|
||||
|
||||
const log = (message: string) => {
|
||||
this.projectService.logger.info(message);
|
||||
};
|
||||
@@ -1020,7 +1027,7 @@ namespace ts.server {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name} anywhere in paths: ${searchPaths.join(",")}`);
|
||||
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`);
|
||||
}
|
||||
|
||||
private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) {
|
||||
@@ -1039,7 +1046,15 @@ namespace ts.server {
|
||||
};
|
||||
|
||||
const pluginModule = pluginModuleFactory({ typescript: ts });
|
||||
this.languageService = pluginModule.create(info);
|
||||
const newLS = pluginModule.create(info);
|
||||
for (const k of Object.keys(this.languageService)) {
|
||||
if (!(k in newLS)) {
|
||||
this.projectService.logger.info(`Plugin activation warning: Missing proxied method ${k} in created LS. Patching.`);
|
||||
(newLS as any)[k] = (this.languageService as any)[k];
|
||||
}
|
||||
}
|
||||
this.projectService.logger.info(`Plugin validation succeded`);
|
||||
this.languageService = newLS;
|
||||
this.plugins.push(pluginModule);
|
||||
}
|
||||
catch (e) {
|
||||
@@ -1071,6 +1086,9 @@ namespace ts.server {
|
||||
}
|
||||
catch (e) {
|
||||
this.projectService.logger.info(`A plugin threw an exception in getExternalFiles: ${e}`);
|
||||
if (e.stack) {
|
||||
this.projectService.logger.info(e.stack);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -466,7 +466,7 @@ namespace ts.server.protocol {
|
||||
* Represents a single refactoring action - for example, the "Extract Method..." refactor might
|
||||
* offer several actions, each corresponding to a surround class or closure to extract into.
|
||||
*/
|
||||
export type RefactorActionInfo = {
|
||||
export interface RefactorActionInfo {
|
||||
/**
|
||||
* The programmatic name of the refactoring action
|
||||
*/
|
||||
@@ -478,7 +478,7 @@ namespace ts.server.protocol {
|
||||
* so this description should make sense by itself if the parent is inlineable=true
|
||||
*/
|
||||
description: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GetEditsForRefactorRequest extends Request {
|
||||
command: CommandTypes.GetEditsForRefactor;
|
||||
@@ -494,6 +494,7 @@ namespace ts.server.protocol {
|
||||
refactor: string;
|
||||
/* The 'name' property from the refactoring action */
|
||||
action: string;
|
||||
formatOptions?: FormatCodeSettings,
|
||||
};
|
||||
|
||||
|
||||
@@ -501,7 +502,7 @@ namespace ts.server.protocol {
|
||||
body?: RefactorEditInfo;
|
||||
}
|
||||
|
||||
export type RefactorEditInfo = {
|
||||
export interface RefactorEditInfo {
|
||||
edits: FileCodeEdits[];
|
||||
|
||||
/**
|
||||
@@ -510,7 +511,7 @@ namespace ts.server.protocol {
|
||||
*/
|
||||
renameLocation?: Location;
|
||||
renameFilename?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Request for the available codefixes at a specific position.
|
||||
|
||||
+128
-58
@@ -236,25 +236,40 @@ namespace ts.server {
|
||||
return `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}.${d.getMilliseconds()}`;
|
||||
}
|
||||
|
||||
interface QueuedOperation {
|
||||
operationId: string;
|
||||
operation: () => void;
|
||||
}
|
||||
|
||||
class NodeTypingsInstaller implements ITypingsInstaller {
|
||||
private installer: NodeChildProcess;
|
||||
private installerPidReported = false;
|
||||
private socket: NodeSocket;
|
||||
private projectService: ProjectService;
|
||||
private throttledOperations: ThrottledOperations;
|
||||
private eventSender: EventSender;
|
||||
private activeRequestCount = 0;
|
||||
private requestQueue: QueuedOperation[] = [];
|
||||
private requestMap = createMap<QueuedOperation>(); // Maps operation ID to newest requestQueue entry with that ID
|
||||
|
||||
// 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
|
||||
// (see https://github.com/nodejs/node/issues/7657).
|
||||
// It would be preferable to base our limit on the amount of space left in the
|
||||
// buffer, but we have yet to find a way to retrieve that value.
|
||||
private static readonly maxActiveRequestCount = 10;
|
||||
private static readonly requestDelayMillis = 100;
|
||||
|
||||
|
||||
constructor(
|
||||
private readonly telemetryEnabled: boolean,
|
||||
private readonly logger: server.Logger,
|
||||
host: ServerHost,
|
||||
private readonly host: ServerHost,
|
||||
eventPort: number,
|
||||
readonly globalTypingsCacheLocation: string,
|
||||
readonly typingSafeListLocation: string,
|
||||
readonly typesMapLocation: string,
|
||||
private readonly npmLocation: string | undefined,
|
||||
private newLine: string) {
|
||||
this.throttledOperations = new ThrottledOperations(host);
|
||||
if (eventPort) {
|
||||
const s = net.connect({ port: eventPort }, () => {
|
||||
this.socket = s;
|
||||
@@ -338,12 +353,26 @@ namespace ts.server {
|
||||
this.logger.info(`Scheduling throttled operation: ${JSON.stringify(request)}`);
|
||||
}
|
||||
}
|
||||
this.throttledOperations.schedule(project.getProjectName(), /*ms*/ 250, () => {
|
||||
|
||||
const operationId = project.getProjectName();
|
||||
const operation = () => {
|
||||
if (this.logger.hasLevel(LogLevel.verbose)) {
|
||||
this.logger.info(`Sending request: ${JSON.stringify(request)}`);
|
||||
}
|
||||
this.installer.send(request);
|
||||
});
|
||||
};
|
||||
const queuedRequest: QueuedOperation = { operationId, operation };
|
||||
|
||||
if (this.activeRequestCount < NodeTypingsInstaller.maxActiveRequestCount) {
|
||||
this.scheduleRequest(queuedRequest);
|
||||
}
|
||||
else {
|
||||
if (this.logger.hasLevel(LogLevel.verbose)) {
|
||||
this.logger.info(`Deferring request for: ${operationId}`);
|
||||
}
|
||||
this.requestQueue.push(queuedRequest);
|
||||
this.requestMap.set(operationId, queuedRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private handleMessage(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse) {
|
||||
@@ -351,64 +380,106 @@ namespace ts.server {
|
||||
this.logger.info(`Received response: ${JSON.stringify(response)}`);
|
||||
}
|
||||
|
||||
if (response.kind === EventInitializationFailed) {
|
||||
if (!this.eventSender) {
|
||||
return;
|
||||
}
|
||||
const body: protocol.TypesInstallerInitializationFailedEventBody = {
|
||||
message: response.message
|
||||
};
|
||||
const eventName: protocol.TypesInstallerInitializationFailedEventName = "typesInstallerInitializationFailed";
|
||||
this.eventSender.event(body, eventName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.kind === EventBeginInstallTypes) {
|
||||
if (!this.eventSender) {
|
||||
return;
|
||||
}
|
||||
const body: protocol.BeginInstallTypesEventBody = {
|
||||
eventId: response.eventId,
|
||||
packages: response.packagesToInstall,
|
||||
};
|
||||
const eventName: protocol.BeginInstallTypesEventName = "beginInstallTypes";
|
||||
this.eventSender.event(body, eventName);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.kind === EventEndInstallTypes) {
|
||||
if (!this.eventSender) {
|
||||
return;
|
||||
}
|
||||
if (this.telemetryEnabled) {
|
||||
const body: protocol.TypingsInstalledTelemetryEventBody = {
|
||||
telemetryEventName: "typingsInstalled",
|
||||
payload: {
|
||||
installedPackages: response.packagesToInstall.join(","),
|
||||
installSuccess: response.installSuccess,
|
||||
typingsInstallerVersion: response.typingsInstallerVersion
|
||||
}
|
||||
switch (response.kind) {
|
||||
case EventInitializationFailed:
|
||||
{
|
||||
if (!this.eventSender) {
|
||||
break;
|
||||
}
|
||||
const body: protocol.TypesInstallerInitializationFailedEventBody = {
|
||||
message: response.message
|
||||
};
|
||||
const eventName: protocol.TelemetryEventName = "telemetry";
|
||||
const eventName: protocol.TypesInstallerInitializationFailedEventName = "typesInstallerInitializationFailed";
|
||||
this.eventSender.event(body, eventName);
|
||||
break;
|
||||
}
|
||||
case EventBeginInstallTypes:
|
||||
{
|
||||
if (!this.eventSender) {
|
||||
break;
|
||||
}
|
||||
const body: protocol.BeginInstallTypesEventBody = {
|
||||
eventId: response.eventId,
|
||||
packages: response.packagesToInstall,
|
||||
};
|
||||
const eventName: protocol.BeginInstallTypesEventName = "beginInstallTypes";
|
||||
this.eventSender.event(body, eventName);
|
||||
break;
|
||||
}
|
||||
case EventEndInstallTypes:
|
||||
{
|
||||
if (!this.eventSender) {
|
||||
break;
|
||||
}
|
||||
if (this.telemetryEnabled) {
|
||||
const body: protocol.TypingsInstalledTelemetryEventBody = {
|
||||
telemetryEventName: "typingsInstalled",
|
||||
payload: {
|
||||
installedPackages: response.packagesToInstall.join(","),
|
||||
installSuccess: response.installSuccess,
|
||||
typingsInstallerVersion: response.typingsInstallerVersion
|
||||
}
|
||||
};
|
||||
const eventName: protocol.TelemetryEventName = "telemetry";
|
||||
this.eventSender.event(body, eventName);
|
||||
}
|
||||
|
||||
const body: protocol.EndInstallTypesEventBody = {
|
||||
eventId: response.eventId,
|
||||
packages: response.packagesToInstall,
|
||||
success: response.installSuccess,
|
||||
};
|
||||
const eventName: protocol.EndInstallTypesEventName = "endInstallTypes";
|
||||
this.eventSender.event(body, eventName);
|
||||
return;
|
||||
}
|
||||
const body: protocol.EndInstallTypesEventBody = {
|
||||
eventId: response.eventId,
|
||||
packages: response.packagesToInstall,
|
||||
success: response.installSuccess,
|
||||
};
|
||||
const eventName: protocol.EndInstallTypesEventName = "endInstallTypes";
|
||||
this.eventSender.event(body, eventName);
|
||||
break;
|
||||
}
|
||||
case ActionInvalidate:
|
||||
{
|
||||
this.projectService.updateTypingsForProject(response);
|
||||
break;
|
||||
}
|
||||
case ActionSet:
|
||||
{
|
||||
if (this.activeRequestCount > 0) {
|
||||
this.activeRequestCount--;
|
||||
}
|
||||
else {
|
||||
Debug.fail("Received too many responses");
|
||||
}
|
||||
|
||||
this.projectService.updateTypingsForProject(response);
|
||||
if (response.kind === ActionSet && this.socket) {
|
||||
this.sendEvent(0, "setTypings", response);
|
||||
while (this.requestQueue.length > 0) {
|
||||
const queuedRequest = this.requestQueue.shift();
|
||||
if (this.requestMap.get(queuedRequest.operationId) === queuedRequest) {
|
||||
this.requestMap.delete(queuedRequest.operationId);
|
||||
this.scheduleRequest(queuedRequest);
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.logger.hasLevel(LogLevel.verbose)) {
|
||||
this.logger.info(`Skipping defunct request for: ${queuedRequest.operationId}`);
|
||||
}
|
||||
}
|
||||
|
||||
this.projectService.updateTypingsForProject(response);
|
||||
|
||||
if (this.socket) {
|
||||
this.sendEvent(0, "setTypings", response);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assertTypeIsNever(response);
|
||||
}
|
||||
}
|
||||
|
||||
private scheduleRequest(request: QueuedOperation) {
|
||||
if (this.logger.hasLevel(LogLevel.verbose)) {
|
||||
this.logger.info(`Scheduling request for: ${request.operationId}`);
|
||||
}
|
||||
this.activeRequestCount++;
|
||||
this.host.setTimeout(request.operation, NodeTypingsInstaller.requestDelayMillis);
|
||||
}
|
||||
}
|
||||
|
||||
class IOSession extends Session {
|
||||
@@ -528,7 +599,6 @@ namespace ts.server {
|
||||
function createPollingWatchedFileSet(interval = 2500, chunkSize = 30) {
|
||||
const watchedFiles: WatchedFile[] = [];
|
||||
let nextFileToCheck = 0;
|
||||
let watchTimer: any;
|
||||
return { getModifiedTime, poll, startWatchTimer, addFile, removeFile };
|
||||
|
||||
function getModifiedTime(fileName: string): Date {
|
||||
@@ -565,7 +635,7 @@ namespace ts.server {
|
||||
// stat due to inconsistencies of fs.watch
|
||||
// and efficiency of stat on modern filesystems
|
||||
function startWatchTimer() {
|
||||
watchTimer = setInterval(() => {
|
||||
setInterval(() => {
|
||||
let count = 0;
|
||||
let nextToCheck = nextFileToCheck;
|
||||
let firstCheck = -1;
|
||||
|
||||
+11
-11
@@ -1488,7 +1488,7 @@ namespace ts.server {
|
||||
|
||||
const result = project.getLanguageService().getEditsForRefactor(
|
||||
file,
|
||||
this.projectService.getFormatCodeOptions(),
|
||||
args.formatOptions ? convertFormatOptions(args.formatOptions) : this.projectService.getFormatCodeOptions(),
|
||||
position || textRange,
|
||||
args.refactor,
|
||||
args.action
|
||||
@@ -1609,7 +1609,10 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
// No need to analyze lib.d.ts
|
||||
let fileNamesInProject = fileNames.filter(value => value.indexOf("lib.d.ts") < 0);
|
||||
const fileNamesInProject = fileNames.filter(value => value.indexOf("lib.d.ts") < 0);
|
||||
if (fileNamesInProject.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort the file name list to make the recently touched files come first
|
||||
const highPriorityFiles: NormalizedPath[] = [];
|
||||
@@ -1625,7 +1628,7 @@ namespace ts.server {
|
||||
else {
|
||||
const info = this.projectService.getScriptInfo(fileNameInProject);
|
||||
if (!info.isScriptOpen()) {
|
||||
if (fileNameInProject.indexOf(Extension.Dts) > 0) {
|
||||
if (fileExtensionIs(fileNameInProject, Extension.Dts)) {
|
||||
veryLowPriorityFiles.push(fileNameInProject);
|
||||
}
|
||||
else {
|
||||
@@ -1638,14 +1641,11 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
fileNamesInProject = highPriorityFiles.concat(mediumPriorityFiles).concat(lowPriorityFiles).concat(veryLowPriorityFiles);
|
||||
|
||||
if (fileNamesInProject.length > 0) {
|
||||
const checkList = fileNamesInProject.map(fileName => ({ fileName, project }));
|
||||
// Project level error analysis runs on background files too, therefore
|
||||
// doesn't require the file to be opened
|
||||
this.updateErrorCheck(next, checkList, delay, /*requireOpen*/ false);
|
||||
}
|
||||
const sortedFiles = [...highPriorityFiles, ...mediumPriorityFiles, ...lowPriorityFiles, ...veryLowPriorityFiles];
|
||||
const checkList = sortedFiles.map(fileName => ({ fileName, project }));
|
||||
// Project level error analysis runs on background files too, therefore
|
||||
// doesn't require the file to be opened
|
||||
this.updateErrorCheck(next, checkList, delay, /*requireOpen*/ false);
|
||||
}
|
||||
|
||||
getCanonicalFileName(fileName: string) {
|
||||
|
||||
@@ -73,12 +73,12 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
|
||||
export type RequestCompletedAction = (success: boolean) => void;
|
||||
type PendingRequest = {
|
||||
interface PendingRequest {
|
||||
requestId: number;
|
||||
args: string[];
|
||||
cwd: string;
|
||||
onRequestCompleted: RequestCompletedAction;
|
||||
};
|
||||
}
|
||||
|
||||
export abstract class TypingsInstaller {
|
||||
private readonly packageNameToTypingLocation: Map<string> = createMap<string>();
|
||||
|
||||
@@ -179,6 +179,12 @@ namespace ts.server {
|
||||
constructor(private readonly host: ServerHost) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait `number` milliseconds and then invoke `cb`. If, while waiting, schedule
|
||||
* is called again with the same `operationId`, cancel this operation in favor
|
||||
* of the new one. (Note that the amount of time the canceled operation had been
|
||||
* waiting does not affect the amount of time that the new operation waits.)
|
||||
*/
|
||||
public schedule(operationId: string, delay: number, cb: () => void) {
|
||||
const pendingTimeout = this.pendingTimeouts.get(operationId);
|
||||
if (pendingTimeout) {
|
||||
|
||||
@@ -699,7 +699,8 @@ namespace ts {
|
||||
// specially.
|
||||
const docCommentAndDiagnostics = parseIsolatedJSDocComment(sourceFile.text, start, width);
|
||||
if (docCommentAndDiagnostics && docCommentAndDiagnostics.jsDoc) {
|
||||
docCommentAndDiagnostics.jsDoc.parent = token;
|
||||
// TODO: This should be predicated on `token["kind"]` being compatible with `HasJSDoc["kind"]`
|
||||
docCommentAndDiagnostics.jsDoc.parent = token as HasJSDoc;
|
||||
classifyJSDocComment(docCommentAndDiagnostics.jsDoc);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace ts.codefix {
|
||||
const replacement = createIndexedAccessTypeNode(
|
||||
createTypeReferenceNode(qualifiedName.left, /*typeArguments*/ undefined),
|
||||
createLiteralTypeNode(createLiteral(rightText)));
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
changeTracker.replaceNode(sourceFile, qualifiedName, replacement);
|
||||
|
||||
return [{
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace ts.codefix {
|
||||
createPropertyAccess(createIdentifier(className), tokenName),
|
||||
createIdentifier("undefined")));
|
||||
|
||||
const staticInitializationChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const staticInitializationChangeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
staticInitializationChangeTracker.insertNodeAfter(
|
||||
classDeclarationSourceFile,
|
||||
classDeclaration,
|
||||
@@ -111,7 +111,7 @@ namespace ts.codefix {
|
||||
createPropertyAccess(createThis(), tokenName),
|
||||
createIdentifier("undefined")));
|
||||
|
||||
const propertyInitializationChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const propertyInitializationChangeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
propertyInitializationChangeTracker.insertNodeAt(
|
||||
classDeclarationSourceFile,
|
||||
classConstructor.body.getEnd() - 1,
|
||||
@@ -153,7 +153,7 @@ namespace ts.codefix {
|
||||
/*questionToken*/ undefined,
|
||||
typeNode,
|
||||
/*initializer*/ undefined);
|
||||
const propertyChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const propertyChangeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
propertyChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, property, { suffix: context.newLineCharacter });
|
||||
|
||||
(actions || (actions = [])).push({
|
||||
@@ -178,7 +178,7 @@ namespace ts.codefix {
|
||||
[indexingParameter],
|
||||
typeNode);
|
||||
|
||||
const indexSignatureChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const indexSignatureChangeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
indexSignatureChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, indexSignature, { suffix: context.newLineCharacter });
|
||||
|
||||
actions.push({
|
||||
@@ -195,7 +195,7 @@ namespace ts.codefix {
|
||||
const callExpression = <CallExpression>token.parent.parent;
|
||||
const methodDeclaration = createMethodFromCallExpression(callExpression, tokenName, includeTypeScriptSyntax, makeStatic);
|
||||
|
||||
const methodDeclarationChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const methodDeclarationChangeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
methodDeclarationChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, methodDeclaration, { suffix: context.newLineCharacter });
|
||||
return {
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(makeStatic ?
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
}
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
changeTracker.insertNodeAfter(sourceFile, getOpenBrace(<ConstructorDeclaration>constructor, sourceFile), superCall, { suffix: context.newLineCharacter });
|
||||
changeTracker.deleteNode(sourceFile, superCall);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
const superCall = createStatement(createCall(createSuper(), /*typeArguments*/ undefined, /*argumentsArray*/ emptyArray));
|
||||
changeTracker.insertNodeAfter(sourceFile, getOpenBrace(<ConstructorDeclaration>token.parent, sourceFile), superCall, { suffix: context.newLineCharacter });
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
changeTracker.replaceNode(sourceFile, extendsToken, createToken(SyntaxKind.ImplementsKeyword));
|
||||
|
||||
// We replace existing keywords with commas.
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace ts.codefix {
|
||||
if (token.kind !== SyntaxKind.Identifier) {
|
||||
return undefined;
|
||||
}
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
changeTracker.replaceNode(sourceFile, token, createPropertyAccess(createThis(), <Identifier>token));
|
||||
|
||||
return [{
|
||||
|
||||
@@ -8,11 +8,32 @@ namespace ts.codefix {
|
||||
function getActionsForJSDocTypes(context: CodeFixContext): CodeAction[] | undefined {
|
||||
const sourceFile = context.sourceFile;
|
||||
const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false);
|
||||
const decl = ts.findAncestor(node, n => n.kind === SyntaxKind.VariableDeclaration);
|
||||
|
||||
// NOTE: Some locations are not handled yet:
|
||||
// MappedTypeNode.typeParameters and SignatureDeclaration.typeParameters, as well as CallExpression.typeArguments
|
||||
const decl = ts.findAncestor(node,
|
||||
n =>
|
||||
n.kind === SyntaxKind.AsExpression ||
|
||||
n.kind === SyntaxKind.CallSignature ||
|
||||
n.kind === SyntaxKind.ConstructSignature ||
|
||||
n.kind === SyntaxKind.FunctionDeclaration ||
|
||||
n.kind === SyntaxKind.GetAccessor ||
|
||||
n.kind === SyntaxKind.IndexSignature ||
|
||||
n.kind === SyntaxKind.MappedType ||
|
||||
n.kind === SyntaxKind.MethodDeclaration ||
|
||||
n.kind === SyntaxKind.MethodSignature ||
|
||||
n.kind === SyntaxKind.Parameter ||
|
||||
n.kind === SyntaxKind.PropertyDeclaration ||
|
||||
n.kind === SyntaxKind.PropertySignature ||
|
||||
n.kind === SyntaxKind.SetAccessor ||
|
||||
n.kind === SyntaxKind.TypeAliasDeclaration ||
|
||||
n.kind === SyntaxKind.TypeAssertionExpression ||
|
||||
n.kind === SyntaxKind.VariableDeclaration);
|
||||
if (!decl) return;
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
const jsdocType = (decl as VariableDeclaration).type;
|
||||
if (!jsdocType) return;
|
||||
const original = getTextOfNode(jsdocType);
|
||||
const type = checker.getTypeFromTypeNode(jsdocType);
|
||||
const actions = [createAction(jsdocType, sourceFile.fileName, original, checker.typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTruncation))];
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [
|
||||
Diagnostics._0_is_declared_but_never_used.code,
|
||||
Diagnostics.Property_0_is_declared_but_never_used.code
|
||||
Diagnostics._0_is_declared_but_its_value_is_never_read.code,
|
||||
Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code
|
||||
],
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
const sourceFile = context.sourceFile;
|
||||
@@ -175,23 +175,23 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function deleteNode(n: Node) {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).deleteNode(sourceFile, n));
|
||||
return makeChange(textChanges.ChangeTracker.fromContext(context).deleteNode(sourceFile, n));
|
||||
}
|
||||
|
||||
function deleteRange(range: TextRange) {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).deleteRange(sourceFile, range));
|
||||
return makeChange(textChanges.ChangeTracker.fromContext(context).deleteRange(sourceFile, range));
|
||||
}
|
||||
|
||||
function deleteNodeInList(n: Node) {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).deleteNodeInList(sourceFile, n));
|
||||
return makeChange(textChanges.ChangeTracker.fromContext(context).deleteNodeInList(sourceFile, n));
|
||||
}
|
||||
|
||||
function deleteNodeRange(start: Node, end: Node) {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).deleteNodeRange(sourceFile, start, end));
|
||||
return makeChange(textChanges.ChangeTracker.fromContext(context).deleteNodeRange(sourceFile, start, end));
|
||||
}
|
||||
|
||||
function replaceNode(n: Node, newNode: Node) {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).replaceNode(sourceFile, n, newNode));
|
||||
return makeChange(textChanges.ChangeTracker.fromContext(context).replaceNode(sourceFile, n, newNode));
|
||||
}
|
||||
|
||||
function makeChange(changeTracker: textChanges.ChangeTracker): CodeAction {
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace ts.codefix {
|
||||
export function newNodesToChanges(newNodes: Node[], insertAfter: Node, context: CodeFixContext) {
|
||||
const sourceFile = context.sourceFile;
|
||||
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
|
||||
for (const newNode of newNodes) {
|
||||
changeTracker.insertNodeAfter(sourceFile, insertAfter, newNode, { suffix: context.newLineCharacter });
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace ts.codefix {
|
||||
const symbolIdActionMap = new ImportCodeActionMap();
|
||||
|
||||
// this is a module id -> module import declaration map
|
||||
const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = [];
|
||||
const cachedImportDeclarations: AnyImportSyntax[][] = [];
|
||||
let lastImportDeclaration: Node;
|
||||
|
||||
const currentTokenMeaning = getMeaningFromLocation(token);
|
||||
@@ -199,28 +199,20 @@ namespace ts.codefix {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const existingDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[] = [];
|
||||
for (const importModuleSpecifier of sourceFile.imports) {
|
||||
const importSymbol = checker.getSymbolAtLocation(importModuleSpecifier);
|
||||
if (importSymbol === moduleSymbol) {
|
||||
existingDeclarations.push(getImportDeclaration(importModuleSpecifier));
|
||||
}
|
||||
}
|
||||
const existingDeclarations = mapDefined(sourceFile.imports, importModuleSpecifier =>
|
||||
checker.getSymbolAtLocation(importModuleSpecifier) === moduleSymbol ? getImportDeclaration(importModuleSpecifier) : undefined);
|
||||
cachedImportDeclarations[moduleSymbolId] = existingDeclarations;
|
||||
return existingDeclarations;
|
||||
|
||||
function getImportDeclaration(moduleSpecifier: LiteralExpression) {
|
||||
let node: Node = moduleSpecifier;
|
||||
while (node) {
|
||||
if (node.kind === SyntaxKind.ImportDeclaration) {
|
||||
return <ImportDeclaration>node;
|
||||
}
|
||||
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
|
||||
return <ImportEqualsDeclaration>node;
|
||||
}
|
||||
node = node.parent;
|
||||
function getImportDeclaration({ parent }: LiteralExpression): AnyImportSyntax {
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
return parent as ImportDeclaration;
|
||||
case SyntaxKind.ExternalModuleReference:
|
||||
return (parent as ExternalModuleReference).parent;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,7 +684,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function createChangeTracker() {
|
||||
return textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
return textChanges.ChangeTracker.fromContext(context);
|
||||
}
|
||||
|
||||
function createCodeAction(
|
||||
|
||||
+46
-22
@@ -24,7 +24,7 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, request, keywordFilters } = completionData;
|
||||
const { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, request, keywordFilters } = completionData;
|
||||
|
||||
if (sourceFile.languageVariant === LanguageVariant.JSX &&
|
||||
location && location.parent && location.parent.kind === SyntaxKind.JsxClosingElement) {
|
||||
@@ -56,7 +56,7 @@ namespace ts.Completions {
|
||||
const entries: CompletionEntry[] = [];
|
||||
|
||||
if (isSourceFileJavaScript(sourceFile)) {
|
||||
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log);
|
||||
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log, allowStringLiteral);
|
||||
getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target, entries);
|
||||
}
|
||||
else {
|
||||
@@ -64,7 +64,7 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log);
|
||||
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log, allowStringLiteral);
|
||||
}
|
||||
|
||||
// TODO add filter for keyword based on type/value/namespace and also location
|
||||
@@ -97,7 +97,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
uniqueNames.set(realName, true);
|
||||
const displayName = getCompletionEntryDisplayName(realName, target, /*performCharacterChecks*/ true);
|
||||
const displayName = getCompletionEntryDisplayName(realName, target, /*performCharacterChecks*/ true, /*allowStringLiteral*/ false);
|
||||
if (displayName) {
|
||||
entries.push({
|
||||
name: displayName,
|
||||
@@ -109,11 +109,11 @@ namespace ts.Completions {
|
||||
});
|
||||
}
|
||||
|
||||
function createCompletionEntry(symbol: Symbol, location: Node, performCharacterChecks: boolean, typeChecker: TypeChecker, target: ScriptTarget): CompletionEntry {
|
||||
function createCompletionEntry(symbol: Symbol, location: Node, performCharacterChecks: boolean, typeChecker: TypeChecker, target: ScriptTarget, allowStringLiteral: boolean): CompletionEntry {
|
||||
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
|
||||
// We would like to only show things that can be added after a dot, so for instance numeric properties can
|
||||
// not be accessed with a dot (a.1 <- invalid)
|
||||
const displayName = getCompletionEntryDisplayNameForSymbol(symbol, target, performCharacterChecks);
|
||||
const displayName = getCompletionEntryDisplayNameForSymbol(symbol, target, performCharacterChecks, allowStringLiteral);
|
||||
if (!displayName) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -134,12 +134,12 @@ namespace ts.Completions {
|
||||
};
|
||||
}
|
||||
|
||||
function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: Push<CompletionEntry>, location: Node, performCharacterChecks: boolean, typeChecker: TypeChecker, target: ScriptTarget, log: Log): Map<true> {
|
||||
function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: Push<CompletionEntry>, location: Node, performCharacterChecks: boolean, typeChecker: TypeChecker, target: ScriptTarget, log: Log, allowStringLiteral: boolean): Map<true> {
|
||||
const start = timestamp();
|
||||
const uniqueNames = createMap<true>();
|
||||
if (symbols) {
|
||||
for (const symbol of symbols) {
|
||||
const entry = createCompletionEntry(symbol, location, performCharacterChecks, typeChecker, target);
|
||||
const entry = createCompletionEntry(symbol, location, performCharacterChecks, typeChecker, target, allowStringLiteral);
|
||||
if (entry) {
|
||||
const id = entry.name;
|
||||
if (!uniqueNames.has(id)) {
|
||||
@@ -224,7 +224,7 @@ namespace ts.Completions {
|
||||
const type = typeChecker.getContextualType((<ObjectLiteralExpression>element.parent));
|
||||
const entries: CompletionEntry[] = [];
|
||||
if (type) {
|
||||
getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, element, /*performCharacterChecks*/ false, typeChecker, target, log);
|
||||
getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, element, /*performCharacterChecks*/ false, typeChecker, target, log, /*allowStringLiteral*/ true);
|
||||
if (entries.length) {
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
|
||||
}
|
||||
@@ -253,7 +253,7 @@ namespace ts.Completions {
|
||||
const type = typeChecker.getTypeAtLocation(node.expression);
|
||||
const entries: CompletionEntry[] = [];
|
||||
if (type) {
|
||||
getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, node, /*performCharacterChecks*/ false, typeChecker, target, log);
|
||||
getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, node, /*performCharacterChecks*/ false, typeChecker, target, log, /*allowStringLiteral*/ true);
|
||||
if (entries.length) {
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
|
||||
}
|
||||
@@ -284,7 +284,7 @@ namespace ts.Completions {
|
||||
addStringLiteralCompletionsFromType(t, result, typeChecker, uniques);
|
||||
}
|
||||
}
|
||||
else if (type.flags & TypeFlags.StringLiteral) {
|
||||
else if (type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral)) {
|
||||
const name = (<StringLiteralType>type).value;
|
||||
if (!uniques.has(name)) {
|
||||
uniques.set(name, true);
|
||||
@@ -302,13 +302,13 @@ namespace ts.Completions {
|
||||
// Compute all the completion symbols again.
|
||||
const completionData = getCompletionData(typeChecker, log, sourceFile, position);
|
||||
if (completionData) {
|
||||
const { symbols, location } = completionData;
|
||||
const { symbols, location, allowStringLiteral } = 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) === entryName ? s : undefined);
|
||||
const symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false, allowStringLiteral) === entryName ? s : undefined);
|
||||
|
||||
if (symbol) {
|
||||
const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All);
|
||||
@@ -345,17 +345,22 @@ namespace ts.Completions {
|
||||
export function getCompletionEntrySymbol(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): Symbol | undefined {
|
||||
// Compute all the completion symbols again.
|
||||
const completionData = getCompletionData(typeChecker, log, sourceFile, position);
|
||||
if (!completionData) {
|
||||
return undefined;
|
||||
}
|
||||
const { symbols, allowStringLiteral } = 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.
|
||||
return completionData && forEach(completionData.symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false) === entryName ? s : undefined);
|
||||
return forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false, allowStringLiteral) === entryName ? s : undefined);
|
||||
}
|
||||
|
||||
interface CompletionData {
|
||||
symbols: Symbol[];
|
||||
isGlobalCompletion: boolean;
|
||||
isMemberCompletion: boolean;
|
||||
allowStringLiteral: boolean;
|
||||
isNewIdentifierLocation: boolean;
|
||||
location: Node;
|
||||
isRightOfDot: boolean;
|
||||
@@ -436,7 +441,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
if (request) {
|
||||
return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: 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 };
|
||||
}
|
||||
|
||||
if (!insideJsDocTagTypeExpression) {
|
||||
@@ -534,6 +539,7 @@ namespace ts.Completions {
|
||||
const semanticStart = timestamp();
|
||||
let isGlobalCompletion = false;
|
||||
let isMemberCompletion: boolean;
|
||||
let allowStringLiteral = false;
|
||||
let isNewIdentifierLocation: boolean;
|
||||
let keywordFilters = KeywordCompletionFilters.None;
|
||||
let symbols: Symbol[] = [];
|
||||
@@ -573,7 +579,7 @@ namespace ts.Completions {
|
||||
|
||||
log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
|
||||
|
||||
return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters };
|
||||
return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters };
|
||||
|
||||
type JSDocTagWithTypeExpression = JSDocAugmentsTag | JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
|
||||
|
||||
@@ -961,6 +967,7 @@ namespace ts.Completions {
|
||||
function tryGetObjectLikeCompletionSymbols(objectLikeContainer: ObjectLiteralExpression | ObjectBindingPattern): boolean {
|
||||
// We're looking up possible property names from contextual/inferred/declared type.
|
||||
isMemberCompletion = true;
|
||||
allowStringLiteral = true;
|
||||
|
||||
let typeMembers: Symbol[];
|
||||
let existingMembers: ReadonlyArray<Declaration>;
|
||||
@@ -971,7 +978,7 @@ namespace ts.Completions {
|
||||
isNewIdentifierLocation = true;
|
||||
const typeForObject = typeChecker.getContextualType(<ObjectLiteralExpression>objectLikeContainer);
|
||||
if (!typeForObject) return false;
|
||||
typeMembers = typeChecker.getAllPossiblePropertiesOfType(typeForObject);
|
||||
typeMembers = getPropertiesForCompletion(typeForObject, typeChecker);
|
||||
existingMembers = (<ObjectLiteralExpression>objectLikeContainer).properties;
|
||||
}
|
||||
else {
|
||||
@@ -1609,7 +1616,7 @@ namespace ts.Completions {
|
||||
*
|
||||
* @return undefined if the name is of external module
|
||||
*/
|
||||
function getCompletionEntryDisplayNameForSymbol(symbol: Symbol, target: ScriptTarget, performCharacterChecks: boolean): string | undefined {
|
||||
function getCompletionEntryDisplayNameForSymbol(symbol: Symbol, target: ScriptTarget, performCharacterChecks: boolean, allowStringLiteral: boolean): string | undefined {
|
||||
const name = symbol.name;
|
||||
if (!name) return undefined;
|
||||
|
||||
@@ -1623,20 +1630,21 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
|
||||
return getCompletionEntryDisplayName(name, target, performCharacterChecks);
|
||||
return getCompletionEntryDisplayName(name, target, performCharacterChecks, allowStringLiteral);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a displayName from a given for completion list, performing any necessary quotes stripping
|
||||
* and checking whether the name is valid identifier name.
|
||||
*/
|
||||
function getCompletionEntryDisplayName(name: string, target: ScriptTarget, performCharacterChecks: boolean): string {
|
||||
function getCompletionEntryDisplayName(name: string, target: ScriptTarget, performCharacterChecks: boolean, allowStringLiteral: boolean): string {
|
||||
// If the user entered name for the symbol was quoted, removing the quotes is not enough, as the name could be an
|
||||
// invalid identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name.
|
||||
// e.g "b a" is valid quoted name but when we strip off the quotes, it is invalid.
|
||||
// We, thus, need to check if whatever was inside the quotes is actually a valid identifier name.
|
||||
if (performCharacterChecks && !isIdentifierText(name, target)) {
|
||||
return undefined;
|
||||
// TODO: GH#18169
|
||||
return allowStringLiteral ? JSON.stringify(name) : undefined;
|
||||
}
|
||||
|
||||
return name;
|
||||
@@ -1731,7 +1739,7 @@ namespace ts.Completions {
|
||||
|
||||
/** Get the corresponding JSDocTag node if the position is in a jsDoc comment */
|
||||
function getJsDocTagAtPosition(node: Node, position: number): JSDocTag | undefined {
|
||||
const { jsDoc } = getJsDocHavingNode(node);
|
||||
const { jsDoc } = getJsDocHavingNode(node) as JSDocContainer;
|
||||
if (!jsDoc) return undefined;
|
||||
|
||||
for (const { pos, end, tags } of jsDoc) {
|
||||
@@ -1758,4 +1766,20 @@ namespace ts.Completions {
|
||||
return node.parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all properties on a type, but if that type is a union of several types,
|
||||
* tries to only include those types which declare properties, not methods.
|
||||
* This ensures that we don't try providing completions for all the methods on e.g. Array.
|
||||
*/
|
||||
function getPropertiesForCompletion(type: Type, checker: TypeChecker): Symbol[] {
|
||||
if (!(type.flags & TypeFlags.Union)) {
|
||||
return checker.getPropertiesOfType(type);
|
||||
}
|
||||
|
||||
const { types } = type as UnionType;
|
||||
const filteredTypes = types.filter(memberType => !(memberType.flags & TypeFlags.Primitive || checker.isArrayLikeType(memberType)));
|
||||
// If there are no property-only types, just provide completions for every type as usual.
|
||||
return checker.getAllPossiblePropertiesOfTypes(filteredTypes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,8 +175,10 @@ namespace ts.FindAllReferences {
|
||||
return {
|
||||
fileName: node.getSourceFile().fileName,
|
||||
textSpan: getTextSpan(node),
|
||||
isWriteAccess: isWriteAccess(node),
|
||||
isDefinition: isAnyDeclarationName(node) || isLiteralComputedPropertyDeclarationName(node),
|
||||
isWriteAccess: isWriteAccessForReference(node),
|
||||
isDefinition: node.kind === SyntaxKind.DefaultKeyword
|
||||
|| isAnyDeclarationName(node)
|
||||
|| isLiteralComputedPropertyDeclarationName(node),
|
||||
isInString
|
||||
};
|
||||
}
|
||||
@@ -222,7 +224,7 @@ namespace ts.FindAllReferences {
|
||||
|
||||
const { node, isInString } = entry;
|
||||
const fileName = entry.node.getSourceFile().fileName;
|
||||
const writeAccess = isWriteAccess(node);
|
||||
const writeAccess = isWriteAccessForReference(node);
|
||||
const span: HighlightSpan = {
|
||||
textSpan: getTextSpan(node),
|
||||
kind: writeAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference,
|
||||
@@ -242,21 +244,8 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
/** A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment */
|
||||
function isWriteAccess(node: Node): boolean {
|
||||
if (isAnyDeclarationName(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { parent } = node;
|
||||
switch (parent && parent.kind) {
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
return true;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
return (<BinaryExpression>parent).left === node && isAssignmentOperator((<BinaryExpression>parent).operatorToken.kind);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
function isWriteAccessForReference(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.DefaultKeyword || isAnyDeclarationName(node) || isWriteAccess(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -743,7 +732,7 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
function isValidReferencePosition(node: Node, searchSymbolName: string): boolean {
|
||||
// Compare the length so we filter out strict superstrings of the symbol we are looking for
|
||||
switch (node && node.kind) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
return (node as Identifier).text.length === searchSymbolName.length;
|
||||
|
||||
@@ -754,6 +743,9 @@ namespace ts.FindAllReferences.Core {
|
||||
case SyntaxKind.NumericLiteral:
|
||||
return isLiteralNameOfPropertyDeclarationOrIndexAccess(node as NumericLiteral) && (node as NumericLiteral).text.length === searchSymbolName.length;
|
||||
|
||||
case SyntaxKind.DefaultKeyword:
|
||||
return "default".length === searchSymbolName.length;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1435,22 +1427,27 @@ namespace ts.FindAllReferences.Core {
|
||||
const bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol, checker);
|
||||
if (bindingElementPropertySymbol) {
|
||||
result.push(bindingElementPropertySymbol);
|
||||
addRootSymbols(bindingElementPropertySymbol);
|
||||
}
|
||||
|
||||
// If this is a union property, add all the symbols from all its source symbols in all unioned types.
|
||||
// If the symbol is an instantiation from a another symbol (e.g. widened symbol) , add the root the list
|
||||
for (const rootSymbol of checker.getRootSymbols(symbol)) {
|
||||
if (rootSymbol !== symbol) {
|
||||
result.push(rootSymbol);
|
||||
}
|
||||
|
||||
// Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions
|
||||
if (!implementations && rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
|
||||
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), checker);
|
||||
}
|
||||
}
|
||||
addRootSymbols(symbol);
|
||||
|
||||
return result;
|
||||
|
||||
function addRootSymbols(sym: Symbol): void {
|
||||
// If this is a union property, add all the symbols from all its source symbols in all unioned types.
|
||||
// If the symbol is an instantiation from a another symbol (e.g. widened symbol) , add the root the list
|
||||
for (const rootSymbol of checker.getRootSymbols(sym)) {
|
||||
if (rootSymbol !== sym) {
|
||||
result.push(rootSymbol);
|
||||
}
|
||||
|
||||
// Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions
|
||||
if (!implementations && rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
|
||||
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), checker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1542,34 +1539,39 @@ namespace ts.FindAllReferences.Core {
|
||||
// then include the binding element in the related symbols
|
||||
// let { a } : { a };
|
||||
const bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName(referenceSymbol, state.checker);
|
||||
if (bindingElementPropertySymbol && search.includes(bindingElementPropertySymbol)) {
|
||||
return bindingElementPropertySymbol;
|
||||
if (bindingElementPropertySymbol) {
|
||||
const fromBindingElement = findRootSymbol(bindingElementPropertySymbol);
|
||||
if (fromBindingElement) return fromBindingElement;
|
||||
}
|
||||
|
||||
// Unwrap symbols to get to the root (e.g. transient symbols as a result of widening)
|
||||
// Or a union property, use its underlying unioned symbols
|
||||
return forEach(state.checker.getRootSymbols(referenceSymbol), rootSymbol => {
|
||||
// if it is in the list, then we are done
|
||||
if (search.includes(rootSymbol)) {
|
||||
return rootSymbol;
|
||||
}
|
||||
return findRootSymbol(referenceSymbol);
|
||||
|
||||
// Finally, try all properties with the same name in any type the containing type extended or implemented, and
|
||||
// see if any is in the list. If we were passed a parent symbol, only include types that are subtypes of the
|
||||
// parent symbol
|
||||
if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
|
||||
// Parents will only be defined if implementations is true
|
||||
if (search.parents && !some(search.parents, parent => explicitlyInheritsFrom(rootSymbol.parent, parent, state.inheritsFromCache, state.checker))) {
|
||||
return undefined;
|
||||
function findRootSymbol(sym: Symbol): Symbol | undefined {
|
||||
// Unwrap symbols to get to the root (e.g. transient symbols as a result of widening)
|
||||
// Or a union property, use its underlying unioned symbols
|
||||
return forEach(state.checker.getRootSymbols(sym), rootSymbol => {
|
||||
// if it is in the list, then we are done
|
||||
if (search.includes(rootSymbol)) {
|
||||
return rootSymbol;
|
||||
}
|
||||
|
||||
const result: Symbol[] = [];
|
||||
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), state.checker);
|
||||
return find(result, search.includes);
|
||||
}
|
||||
// Finally, try all properties with the same name in any type the containing type extended or implemented, and
|
||||
// see if any is in the list. If we were passed a parent symbol, only include types that are subtypes of the
|
||||
// parent symbol
|
||||
if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
|
||||
// Parents will only be defined if implementations is true
|
||||
if (search.parents && !some(search.parents, parent => explicitlyInheritsFrom(rootSymbol.parent, parent, state.inheritsFromCache, state.checker))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
const result: Symbol[] = [];
|
||||
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), state.checker);
|
||||
return find(result, search.includes);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getNameFromObjectLiteralElement(node: ObjectLiteralElement): string {
|
||||
|
||||
@@ -339,17 +339,17 @@ namespace ts.formatting {
|
||||
/* @internal */
|
||||
export function formatNodeGivenIndentation(node: Node, sourceFileLike: SourceFileLike, languageVariant: LanguageVariant, initialIndentation: number, delta: number, rulesProvider: RulesProvider): TextChange[] {
|
||||
const range = { pos: 0, end: sourceFileLike.text.length };
|
||||
return formatSpanWorker(
|
||||
return getFormattingScanner(sourceFileLike.text, languageVariant, range.pos, range.end, scanner => formatSpanWorker(
|
||||
range,
|
||||
node,
|
||||
initialIndentation,
|
||||
delta,
|
||||
getFormattingScanner(sourceFileLike.text, languageVariant, range.pos, range.end),
|
||||
scanner,
|
||||
rulesProvider.getFormatOptions(),
|
||||
rulesProvider,
|
||||
FormattingRequestKind.FormatSelection,
|
||||
_ => false, // assume that node does not have any errors
|
||||
sourceFileLike);
|
||||
sourceFileLike));
|
||||
}
|
||||
|
||||
function formatNodeLines(node: Node, sourceFile: SourceFile, options: FormatCodeSettings, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] {
|
||||
@@ -372,17 +372,17 @@ namespace ts.formatting {
|
||||
requestKind: FormattingRequestKind): TextChange[] {
|
||||
// find the smallest node that fully wraps the range and compute the initial indentation for the node
|
||||
const enclosingNode = findEnclosingNode(originalRange, sourceFile);
|
||||
return formatSpanWorker(
|
||||
return getFormattingScanner(sourceFile.text, sourceFile.languageVariant, getScanStartPosition(enclosingNode, originalRange, sourceFile), originalRange.end, scanner => formatSpanWorker(
|
||||
originalRange,
|
||||
enclosingNode,
|
||||
SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, options),
|
||||
getOwnOrInheritedDelta(enclosingNode, options, sourceFile),
|
||||
getFormattingScanner(sourceFile.text, sourceFile.languageVariant, getScanStartPosition(enclosingNode, originalRange, sourceFile), originalRange.end),
|
||||
scanner,
|
||||
options,
|
||||
rulesProvider,
|
||||
requestKind,
|
||||
prepareRangeContainsErrorFunction(sourceFile.parseDiagnostics, originalRange),
|
||||
sourceFile);
|
||||
sourceFile));
|
||||
}
|
||||
|
||||
function formatSpanWorker(originalRange: TextRange,
|
||||
@@ -427,8 +427,6 @@ namespace ts.formatting {
|
||||
}
|
||||
}
|
||||
|
||||
formattingScanner.close();
|
||||
|
||||
return edits;
|
||||
|
||||
// local functions
|
||||
@@ -728,6 +726,7 @@ namespace ts.formatting {
|
||||
parent: Node,
|
||||
parentStartLine: number,
|
||||
parentDynamicIndentation: DynamicIndentation): void {
|
||||
Debug.assert(isNodeArray(nodes));
|
||||
|
||||
const listStartToken = getOpenTokenForList(parent, nodes);
|
||||
const listEndToken = getCloseTokenForOpenToken(listStartToken);
|
||||
|
||||
@@ -6,11 +6,6 @@ namespace ts.formatting {
|
||||
const standardScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, LanguageVariant.Standard);
|
||||
const jsxScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, LanguageVariant.JSX);
|
||||
|
||||
/**
|
||||
* Scanner that is currently used for formatting
|
||||
*/
|
||||
let scanner: Scanner;
|
||||
|
||||
export interface FormattingScanner {
|
||||
advance(): void;
|
||||
isOnToken(): boolean;
|
||||
@@ -18,7 +13,6 @@ namespace ts.formatting {
|
||||
getCurrentLeadingTrivia(): TextRangeWithKind[];
|
||||
lastTrailingTriviaWasNewLine(): boolean;
|
||||
skipToEndOf(node: Node): void;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
const enum ScanAction {
|
||||
@@ -30,9 +24,8 @@ namespace ts.formatting {
|
||||
RescanJsxText,
|
||||
}
|
||||
|
||||
export function getFormattingScanner(text: string, languageVariant: LanguageVariant, startPos: number, endPos: number): FormattingScanner {
|
||||
Debug.assert(scanner === undefined, "Scanner should be undefined");
|
||||
scanner = languageVariant === LanguageVariant.JSX ? jsxScanner : standardScanner;
|
||||
export function getFormattingScanner<T>(text: string, languageVariant: LanguageVariant, startPos: number, endPos: number, cb: (scanner: FormattingScanner) => T): T {
|
||||
const scanner = languageVariant === LanguageVariant.JSX ? jsxScanner : standardScanner;
|
||||
|
||||
scanner.setText(text);
|
||||
scanner.setTextPos(startPos);
|
||||
@@ -45,21 +38,19 @@ namespace ts.formatting {
|
||||
let lastScanAction: ScanAction | undefined;
|
||||
let lastTokenInfo: TokenInfo | undefined;
|
||||
|
||||
return {
|
||||
const res = cb({
|
||||
advance,
|
||||
readTokenInfo,
|
||||
isOnToken,
|
||||
getCurrentLeadingTrivia: () => leadingTrivia,
|
||||
lastTrailingTriviaWasNewLine: () => wasNewLine,
|
||||
skipToEndOf,
|
||||
close: () => {
|
||||
Debug.assert(scanner !== undefined);
|
||||
});
|
||||
|
||||
lastTokenInfo = undefined;
|
||||
scanner.setText(undefined);
|
||||
scanner = undefined;
|
||||
}
|
||||
};
|
||||
lastTokenInfo = undefined;
|
||||
scanner.setText(undefined);
|
||||
|
||||
return res;
|
||||
|
||||
function advance(): void {
|
||||
Debug.assert(scanner !== undefined, "Scanner should be present");
|
||||
@@ -134,7 +125,8 @@ namespace ts.formatting {
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
case SyntaxKind.JsxClosingElement:
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
return node.kind === SyntaxKind.Identifier;
|
||||
// May parse an identifier like `module-layout`; that will be scanned as a keyword at first, but we should parse the whole thing to get an identifier.
|
||||
return isKeyword(node.kind) || node.kind === SyntaxKind.Identifier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +210,7 @@ namespace ts.formatting {
|
||||
currentToken = scanner.reScanTemplateToken();
|
||||
lastScanAction = ScanAction.RescanTemplateToken;
|
||||
}
|
||||
else if (expectedScanAction === ScanAction.RescanJsxIdentifier && currentToken === SyntaxKind.Identifier) {
|
||||
else if (expectedScanAction === ScanAction.RescanJsxIdentifier) {
|
||||
currentToken = scanner.scanJsxIdentifier();
|
||||
lastScanAction = ScanAction.RescanJsxIdentifier;
|
||||
}
|
||||
|
||||
@@ -3,16 +3,12 @@
|
||||
/* @internal */
|
||||
namespace ts.formatting {
|
||||
export class Rule {
|
||||
// Used for debugging to identify each rule based on the property name it's assigned to.
|
||||
public debugName?: string;
|
||||
constructor(
|
||||
public Descriptor: RuleDescriptor,
|
||||
public Operation: RuleOperation,
|
||||
public Flag: RuleFlags = RuleFlags.None) {
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return "[desc=" + this.Descriptor + "," +
|
||||
"operation=" + this.Operation + "," +
|
||||
"flag=" + this.Flag + "]";
|
||||
readonly Descriptor: RuleDescriptor,
|
||||
readonly Operation: RuleOperation,
|
||||
readonly Flag: RuleFlags = RuleFlags.None) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,6 @@
|
||||
/* @internal */
|
||||
namespace ts.formatting {
|
||||
export class Rules {
|
||||
public getRuleName(rule: Rule) {
|
||||
const o: ts.MapLike<any> = <any>this;
|
||||
for (const name in o) {
|
||||
if (o[name] === rule) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
throw new Error("Unknown rule");
|
||||
}
|
||||
|
||||
[name: string]: any;
|
||||
|
||||
public IgnoreBeforeComment: Rule;
|
||||
public IgnoreAfterLineComment: Rule;
|
||||
|
||||
@@ -569,6 +557,16 @@ namespace ts.formatting {
|
||||
this.SpaceAfterSemicolon,
|
||||
this.SpaceBetweenStatements, this.SpaceAfterTryFinally
|
||||
];
|
||||
|
||||
if (Debug.isDebugging) {
|
||||
const o: ts.MapLike<any> = <any>this;
|
||||
for (const name in o) {
|
||||
const rule = o[name];
|
||||
if (rule instanceof Rule) {
|
||||
rule.debugName = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@@ -757,9 +755,8 @@ namespace ts.formatting {
|
||||
return true;
|
||||
case SyntaxKind.Block: {
|
||||
const blockParent = context.currentTokenParent.parent;
|
||||
if (blockParent.kind !== SyntaxKind.ArrowFunction &&
|
||||
blockParent.kind !== SyntaxKind.FunctionExpression
|
||||
) {
|
||||
// In a codefix scenario, we can't rely on parents being set. So just always return true.
|
||||
if (!blockParent || blockParent.kind !== SyntaxKind.ArrowFunction && blockParent.kind !== SyntaxKind.FunctionExpression) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,18 +9,10 @@ namespace ts.formatting {
|
||||
|
||||
constructor() {
|
||||
this.globalRules = new Rules();
|
||||
const activeRules = this.globalRules.HighPriorityCommonRules.slice(0).concat(this.globalRules.UserConfigurableRules).concat(this.globalRules.LowPriorityCommonRules);
|
||||
const activeRules = this.globalRules.HighPriorityCommonRules.concat(this.globalRules.UserConfigurableRules).concat(this.globalRules.LowPriorityCommonRules);
|
||||
this.rulesMap = RulesMap.create(activeRules);
|
||||
}
|
||||
|
||||
public getRuleName(rule: Rule): string {
|
||||
return this.globalRules.getRuleName(rule);
|
||||
}
|
||||
|
||||
public getRuleByName(name: string): Rule {
|
||||
return this.globalRules[name];
|
||||
}
|
||||
|
||||
public getRulesMap() {
|
||||
return this.rulesMap;
|
||||
}
|
||||
|
||||
@@ -505,6 +505,8 @@ namespace ts.formatting {
|
||||
case SyntaxKind.NamedImports:
|
||||
case SyntaxKind.ExportSpecifier:
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -180,7 +180,6 @@ namespace ts.FindAllReferences {
|
||||
* But re-exports will be placed in 'singleReferences' since they cannot be locally referenced.
|
||||
*/
|
||||
function getSearchesFromDirectImports(directImports: Importer[], exportSymbol: Symbol, exportKind: ExportKind, checker: TypeChecker, isForRename: boolean): Pick<ImportsResult, "importSearches" | "singleReferences"> {
|
||||
const exportName = exportSymbol.escapedName;
|
||||
const importSearches: Array<[Identifier, Symbol]> = [];
|
||||
const singleReferences: Identifier[] = [];
|
||||
function addSearch(location: Identifier, symbol: Symbol): void {
|
||||
@@ -218,12 +217,11 @@ namespace ts.FindAllReferences {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decl.importClause) {
|
||||
const { importClause } = decl;
|
||||
if (!importClause) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { importClause } = decl;
|
||||
|
||||
const { namedBindings } = importClause;
|
||||
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
|
||||
handleNamespaceImportLike(namedBindings.name);
|
||||
@@ -245,7 +243,6 @@ namespace ts.FindAllReferences {
|
||||
|
||||
// 'default' might be accessed as a named import `{ default as foo }`.
|
||||
if (!isForRename && exportKind === ExportKind.Default) {
|
||||
Debug.assert(exportName === "default");
|
||||
searchForNamedImport(namedBindings as NamedImports | undefined);
|
||||
}
|
||||
}
|
||||
@@ -258,36 +255,43 @@ namespace ts.FindAllReferences {
|
||||
*/
|
||||
function handleNamespaceImportLike(importName: Identifier): void {
|
||||
// Don't rename an import that already has a different name than the export.
|
||||
if (exportKind === ExportKind.ExportEquals && (!isForRename || importName.escapedText === exportName)) {
|
||||
if (exportKind === ExportKind.ExportEquals && (!isForRename || isNameMatch(importName.escapedText))) {
|
||||
addSearch(importName, checker.getSymbolAtLocation(importName));
|
||||
}
|
||||
}
|
||||
|
||||
function searchForNamedImport(namedBindings: NamedImportsOrExports | undefined): void {
|
||||
if (namedBindings) {
|
||||
for (const element of namedBindings.elements) {
|
||||
const { name, propertyName } = element;
|
||||
if ((propertyName || name).escapedText !== exportName) {
|
||||
continue;
|
||||
}
|
||||
if (!namedBindings) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (propertyName) {
|
||||
// This is `import { foo as bar } from "./a"` or `export { foo as bar } from "./a"`. `foo` isn't a local in the file, so just add it as a single reference.
|
||||
singleReferences.push(propertyName);
|
||||
if (!isForRename) { // If renaming `foo`, don't touch `bar`, just `foo`.
|
||||
// Search locally for `bar`.
|
||||
addSearch(name, checker.getSymbolAtLocation(name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const localSymbol = element.kind === SyntaxKind.ExportSpecifier && element.propertyName
|
||||
? checker.getExportSpecifierLocalTargetSymbol(element) // For re-exporting under a different name, we want to get the re-exported symbol.
|
||||
: checker.getSymbolAtLocation(name);
|
||||
addSearch(name, localSymbol);
|
||||
for (const element of namedBindings.elements) {
|
||||
const { name, propertyName } = element;
|
||||
if (!isNameMatch((propertyName || name).escapedText)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propertyName) {
|
||||
// This is `import { foo as bar } from "./a"` or `export { foo as bar } from "./a"`. `foo` isn't a local in the file, so just add it as a single reference.
|
||||
singleReferences.push(propertyName);
|
||||
if (!isForRename) { // If renaming `foo`, don't touch `bar`, just `foo`.
|
||||
// Search locally for `bar`.
|
||||
addSearch(name, checker.getSymbolAtLocation(name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const localSymbol = element.kind === SyntaxKind.ExportSpecifier && element.propertyName
|
||||
? checker.getExportSpecifierLocalTargetSymbol(element) // For re-exporting under a different name, we want to get the re-exported symbol.
|
||||
: checker.getSymbolAtLocation(name);
|
||||
addSearch(name, localSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isNameMatch(name: __String): boolean {
|
||||
// Use name of "default" even in `export =` case because we may have allowSyntheticDefaultImports
|
||||
return name === exportSymbol.escapedName || exportKind !== ExportKind.Named && name === "default";
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns 'true' is the namespace 'name' is re-exported from this module, and 'false' if it is only used locally. */
|
||||
@@ -413,7 +417,7 @@ namespace ts.FindAllReferences {
|
||||
case SyntaxKind.ExternalModuleReference:
|
||||
return (decl as ExternalModuleReference).parent;
|
||||
default:
|
||||
Debug.fail(`Unexpected module specifier parent: ${decl.kind}`);
|
||||
Debug.fail("Unexpected module specifier parent: " + decl.kind);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,11 +472,11 @@ namespace ts.FindAllReferences {
|
||||
return exportInfo(symbol, getExportKindForDeclaration(exportNode));
|
||||
}
|
||||
}
|
||||
// If we are in `export = a;`, `parent` is the export assignment.
|
||||
// If we are in `export = a;` or `export default a;`, `parent` is the export assignment.
|
||||
else if (isExportAssignment(parent)) {
|
||||
return getExportAssignmentExport(parent);
|
||||
}
|
||||
// If we are in `export = class A {};` at `A`, `parent.parent` is the export assignment.
|
||||
// If we are in `export = class A {};` (or `export = class A {};`) at `A`, `parent.parent` is the export assignment.
|
||||
else if (isExportAssignment(parent.parent)) {
|
||||
return getExportAssignmentExport(parent.parent);
|
||||
}
|
||||
@@ -489,7 +493,8 @@ namespace ts.FindAllReferences {
|
||||
// Get the symbol for the `export =` node; its parent is the module it's the export of.
|
||||
const exportingModuleSymbol = ex.symbol.parent;
|
||||
Debug.assert(!!exportingModuleSymbol);
|
||||
return { kind: ImportExport.Export, symbol, exportInfo: { exportingModuleSymbol, exportKind: ExportKind.ExportEquals } };
|
||||
const exportKind = ex.isExportEquals ? ExportKind.ExportEquals : ExportKind.Default;
|
||||
return { kind: ImportExport.Export, symbol, exportInfo: { exportingModuleSymbol, exportKind } };
|
||||
}
|
||||
|
||||
function getSpecialPropertyExport(node: ts.BinaryExpression, useLhsSymbol: boolean): ExportedSymbol | undefined {
|
||||
@@ -525,7 +530,11 @@ namespace ts.FindAllReferences {
|
||||
importedSymbol = getExportEqualsLocalSymbol(importedSymbol, checker);
|
||||
}
|
||||
|
||||
if (symbolName(importedSymbol) === symbol.escapedName) { // If this is a rename import, do not continue searching.
|
||||
// If the import has a different name than the export, do not continue searching.
|
||||
// If `importedName` is undefined, do continue searching as the export is anonymous.
|
||||
// (All imports returned from this function will be ignored anyway if we are in rename and this is a not a named export.)
|
||||
const importedName = symbolName(importedSymbol);
|
||||
if (importedName === undefined || importedName === "default" || importedName === symbol.escapedName) {
|
||||
return { kind: ImportExport.Import, symbol: importedSymbol, ...isImport };
|
||||
}
|
||||
}
|
||||
|
||||
+62
-49
@@ -173,38 +173,15 @@ namespace ts.JsDoc {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// TODO: add support for:
|
||||
// - enums/enum members
|
||||
// - interfaces
|
||||
// - property declarations
|
||||
// - potentially property assignments
|
||||
let commentOwner: Node;
|
||||
findOwner: for (commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) {
|
||||
switch (commentOwner.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.VariableStatement:
|
||||
break findOwner;
|
||||
case SyntaxKind.SourceFile:
|
||||
return undefined;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
// If in walking up the tree, we hit a a nested namespace declaration,
|
||||
// then we must be somewhere within a dotted namespace name; however we don't
|
||||
// want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
|
||||
if (commentOwner.parent.kind === SyntaxKind.ModuleDeclaration) {
|
||||
return undefined;
|
||||
}
|
||||
break findOwner;
|
||||
}
|
||||
const commentOwnerInfo = getCommentOwnerInfo(tokenAtPos);
|
||||
if (!commentOwnerInfo) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!commentOwner || commentOwner.getStart() < position) {
|
||||
const { commentOwner, parameters } = commentOwnerInfo;
|
||||
if (commentOwner.getStart() < position) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parameters = getParametersForJsDocOwningNode(commentOwner);
|
||||
const posLineAndChar = sourceFile.getLineAndCharacterOfPosition(position);
|
||||
const lineStart = sourceFile.getLineStarts()[posLineAndChar.line];
|
||||
|
||||
@@ -213,16 +190,18 @@ namespace ts.JsDoc {
|
||||
const isJavaScriptFile = hasJavaScriptFileExtension(sourceFile.fileName);
|
||||
|
||||
let docParams = "";
|
||||
for (let i = 0; i < parameters.length; i++) {
|
||||
const currentName = parameters[i].name;
|
||||
const paramName = currentName.kind === SyntaxKind.Identifier ?
|
||||
(<Identifier>currentName).escapedText :
|
||||
"param" + i;
|
||||
if (isJavaScriptFile) {
|
||||
docParams += `${indentationStr} * @param {any} ${paramName}${newLine}`;
|
||||
}
|
||||
else {
|
||||
docParams += `${indentationStr} * @param ${paramName}${newLine}`;
|
||||
if (parameters) {
|
||||
for (let i = 0; i < parameters.length; i++) {
|
||||
const currentName = parameters[i].name;
|
||||
const paramName = currentName.kind === SyntaxKind.Identifier ?
|
||||
(<Identifier>currentName).escapedText :
|
||||
"param" + i;
|
||||
if (isJavaScriptFile) {
|
||||
docParams += `${indentationStr} * @param {any} ${paramName}${newLine}`;
|
||||
}
|
||||
else {
|
||||
docParams += `${indentationStr} * @param ${paramName}${newLine}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,21 +223,55 @@ namespace ts.JsDoc {
|
||||
return { newText: result, caretOffset: preamble.length };
|
||||
}
|
||||
|
||||
function getParametersForJsDocOwningNode(commentOwner: Node): ReadonlyArray<ParameterDeclaration> {
|
||||
if (isFunctionLike(commentOwner)) {
|
||||
return commentOwner.parameters;
|
||||
}
|
||||
interface CommentOwnerInfo {
|
||||
readonly commentOwner: Node;
|
||||
readonly parameters?: ReadonlyArray<ParameterDeclaration>;
|
||||
}
|
||||
function getCommentOwnerInfo(tokenAtPos: Node): CommentOwnerInfo | undefined {
|
||||
// TODO: add support for:
|
||||
// - enums/enum members
|
||||
// - interfaces
|
||||
// - property declarations
|
||||
// - potentially property assignments
|
||||
for (let commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) {
|
||||
switch (commentOwner.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration;
|
||||
return { commentOwner, parameters };
|
||||
|
||||
if (commentOwner.kind === SyntaxKind.VariableStatement) {
|
||||
const varStatement = <VariableStatement>commentOwner;
|
||||
const varDeclarations = varStatement.declarationList.declarations;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
return { commentOwner };
|
||||
|
||||
if (varDeclarations.length === 1 && varDeclarations[0].initializer) {
|
||||
return getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer);
|
||||
case SyntaxKind.VariableStatement: {
|
||||
const varStatement = <VariableStatement>commentOwner;
|
||||
const varDeclarations = varStatement.declarationList.declarations;
|
||||
const parameters = varDeclarations.length === 1 && varDeclarations[0].initializer
|
||||
? getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer)
|
||||
: undefined;
|
||||
return { commentOwner, parameters };
|
||||
}
|
||||
|
||||
case SyntaxKind.SourceFile:
|
||||
return undefined;
|
||||
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
// If in walking up the tree, we hit a a nested namespace declaration,
|
||||
// then we must be somewhere within a dotted namespace name; however we don't
|
||||
// want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
|
||||
return commentOwner.parent.kind === SyntaxKind.ModuleDeclaration ? undefined : { commentOwner };
|
||||
|
||||
case SyntaxKind.BinaryExpression: {
|
||||
const be = commentOwner as BinaryExpression;
|
||||
if (getSpecialPropertyAssignmentKind(be) === ts.SpecialPropertyAssignmentKind.None) {
|
||||
return undefined;
|
||||
}
|
||||
const parameters = isFunctionLike(be.right) ? be.right.parameters : emptyArray;
|
||||
return { commentOwner, parameters };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+149
-145
@@ -1,6 +1,12 @@
|
||||
/* @internal */
|
||||
namespace ts.NavigateTo {
|
||||
type RawNavigateToItem = { name: string; fileName: string; matchKind: PatternMatchKind; isCaseSensitive: boolean; declaration: Declaration };
|
||||
interface RawNavigateToItem {
|
||||
name: string;
|
||||
fileName: string;
|
||||
matchKind: PatternMatchKind;
|
||||
isCaseSensitive: boolean;
|
||||
declaration: Declaration;
|
||||
}
|
||||
|
||||
export function getNavigateToItems(sourceFiles: ReadonlyArray<SourceFile>, checker: TypeChecker, cancellationToken: CancellationToken, searchValue: string, maxResultCount: number, excludeDtsFiles: boolean): NavigateToItem[] {
|
||||
const patternMatcher = createPatternMatcher(searchValue);
|
||||
@@ -15,185 +21,183 @@ namespace ts.NavigateTo {
|
||||
}
|
||||
|
||||
forEachEntry(sourceFile.getNamedDeclarations(), (declarations, name) => {
|
||||
if (declarations) {
|
||||
// First do a quick check to see if the name of the declaration matches the
|
||||
// last portion of the (possibly) dotted name they're searching for.
|
||||
let matches = patternMatcher.getMatchesForLastSegmentOfPattern(name);
|
||||
|
||||
if (!matches) {
|
||||
return; // continue to next named declarations
|
||||
}
|
||||
|
||||
for (const declaration of declarations) {
|
||||
// It was a match! If the pattern has dots in it, then also see if the
|
||||
// declaration container matches as well.
|
||||
if (patternMatcher.patternContainsDots) {
|
||||
const containers = getContainers(declaration);
|
||||
if (!containers) {
|
||||
return true; // Break out of named declarations and go to the next source file.
|
||||
}
|
||||
|
||||
matches = patternMatcher.getMatches(containers, name);
|
||||
|
||||
if (!matches) {
|
||||
return; // continue to next named declarations
|
||||
}
|
||||
}
|
||||
|
||||
const fileName = sourceFile.fileName;
|
||||
const matchKind = bestMatchKind(matches);
|
||||
rawItems.push({ name, fileName, matchKind, isCaseSensitive: allMatchesAreCaseSensitive(matches), declaration });
|
||||
}
|
||||
}
|
||||
getItemsFromNamedDeclaration(patternMatcher, name, declarations, checker, sourceFile.fileName, rawItems);
|
||||
});
|
||||
}
|
||||
|
||||
// Remove imports when the imported declaration is already in the list and has the same name.
|
||||
rawItems = filter(rawItems, item => {
|
||||
const decl = item.declaration;
|
||||
if (decl.kind === SyntaxKind.ImportClause || decl.kind === SyntaxKind.ImportSpecifier || decl.kind === SyntaxKind.ImportEqualsDeclaration) {
|
||||
const importer = checker.getSymbolAtLocation((decl as NamedDeclaration).name);
|
||||
const imported = checker.getAliasedSymbol(importer);
|
||||
return importer.escapedName !== imported.escapedName;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
rawItems.sort(compareNavigateToItems);
|
||||
if (maxResultCount !== undefined) {
|
||||
rawItems = rawItems.slice(0, maxResultCount);
|
||||
}
|
||||
return rawItems.map(createNavigateToItem);
|
||||
}
|
||||
|
||||
const items = map(rawItems, createNavigateToItem);
|
||||
function getItemsFromNamedDeclaration(patternMatcher: PatternMatcher, name: string, declarations: ReadonlyArray<Declaration>, checker: TypeChecker, fileName: string, rawItems: Push<RawNavigateToItem>): void {
|
||||
// First do a quick check to see if the name of the declaration matches the
|
||||
// last portion of the (possibly) dotted name they're searching for.
|
||||
const matches = patternMatcher.getMatchesForLastSegmentOfPattern(name);
|
||||
|
||||
return items;
|
||||
if (!matches) {
|
||||
return; // continue to next named declarations
|
||||
}
|
||||
|
||||
function allMatchesAreCaseSensitive(matches: PatternMatch[]): boolean {
|
||||
Debug.assert(matches.length > 0);
|
||||
for (const declaration of declarations) {
|
||||
if (!shouldKeepItem(declaration, checker)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is a case sensitive match, only if all the submatches were case sensitive.
|
||||
for (const match of matches) {
|
||||
if (!match.isCaseSensitive) {
|
||||
// It was a match! If the pattern has dots in it, then also see if the
|
||||
// declaration container matches as well.
|
||||
let containerMatches = matches;
|
||||
if (patternMatcher.patternContainsDots) {
|
||||
containerMatches = patternMatcher.getMatches(getContainers(declaration), name);
|
||||
if (!containerMatches) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const matchKind = bestMatchKind(containerMatches);
|
||||
const isCaseSensitive = allMatchesAreCaseSensitive(containerMatches);
|
||||
rawItems.push({ name, fileName, matchKind, isCaseSensitive, declaration });
|
||||
}
|
||||
}
|
||||
|
||||
function shouldKeepItem(declaration: Declaration, checker: ts.TypeChecker): boolean {
|
||||
switch (declaration.kind) {
|
||||
case SyntaxKind.ImportClause:
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
const importer = checker.getSymbolAtLocation((declaration as ImportClause | ImportSpecifier | ImportEqualsDeclaration).name);
|
||||
const imported = checker.getAliasedSymbol(importer);
|
||||
return importer.escapedName !== imported.escapedName;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function allMatchesAreCaseSensitive(matches: ReadonlyArray<PatternMatch>): boolean {
|
||||
Debug.assert(matches.length > 0);
|
||||
|
||||
// This is a case sensitive match, only if all the submatches were case sensitive.
|
||||
for (const match of matches) {
|
||||
if (!match.isCaseSensitive) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]): boolean {
|
||||
if (declaration) {
|
||||
const name = getNameOfDeclaration(declaration);
|
||||
if (name) {
|
||||
const text = getTextOfIdentifierOrLiteral(name as (Identifier | LiteralExpression));
|
||||
if (text !== undefined) {
|
||||
containers.unshift(text);
|
||||
}
|
||||
else if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
else {
|
||||
// Don't know how to add this.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only added the names of computed properties if they're simple dotted expressions, like:
|
||||
//
|
||||
// [X.Y.Z]() { }
|
||||
function tryAddComputedPropertyName(expression: Expression, containers: string[], includeLastPortion: boolean): boolean {
|
||||
const text = getTextOfIdentifierOrLiteral(expression as LiteralExpression);
|
||||
if (text !== undefined) {
|
||||
if (includeLastPortion) {
|
||||
containers.unshift(text);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]) {
|
||||
if (declaration) {
|
||||
const name = getNameOfDeclaration(declaration);
|
||||
if (name) {
|
||||
const text = getTextOfIdentifierOrLiteral(name as (Identifier | LiteralExpression));
|
||||
if (text !== undefined) {
|
||||
containers.unshift(text);
|
||||
}
|
||||
else if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
else {
|
||||
// Don't know how to add this.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (expression.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
const propertyAccess = <PropertyAccessExpression>expression;
|
||||
if (includeLastPortion) {
|
||||
containers.unshift(propertyAccess.name.text);
|
||||
}
|
||||
|
||||
return true;
|
||||
return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
|
||||
// Only added the names of computed properties if they're simple dotted expressions, like:
|
||||
//
|
||||
// [X.Y.Z]() { }
|
||||
function tryAddComputedPropertyName(expression: Expression, containers: string[], includeLastPortion: boolean): boolean {
|
||||
const text = getTextOfIdentifierOrLiteral(expression as LiteralExpression);
|
||||
if (text !== undefined) {
|
||||
if (includeLastPortion) {
|
||||
containers.unshift(text);
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function getContainers(declaration: Declaration): string[] {
|
||||
const containers: string[] = [];
|
||||
|
||||
// First, if we started with a computed property name, then add all but the last
|
||||
// portion into the container array.
|
||||
const name = getNameOfDeclaration(declaration);
|
||||
if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
if (!tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ false)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (expression.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
const propertyAccess = <PropertyAccessExpression>expression;
|
||||
if (includeLastPortion) {
|
||||
containers.unshift(propertyAccess.name.text);
|
||||
}
|
||||
|
||||
return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getContainers(declaration: Declaration) {
|
||||
const containers: string[] = [];
|
||||
// Now, walk up our containers, adding all their names to the container array.
|
||||
declaration = getContainerNode(declaration);
|
||||
|
||||
// First, if we started with a computed property name, then add all but the last
|
||||
// portion into the container array.
|
||||
const name = getNameOfDeclaration(declaration);
|
||||
if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
if (!tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ false)) {
|
||||
return undefined;
|
||||
}
|
||||
while (declaration) {
|
||||
if (!tryAddSingleDeclarationName(declaration, containers)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Now, walk up our containers, adding all their names to the container array.
|
||||
declaration = getContainerNode(declaration);
|
||||
}
|
||||
|
||||
while (declaration) {
|
||||
if (!tryAddSingleDeclarationName(declaration, containers)) {
|
||||
return undefined;
|
||||
}
|
||||
return containers;
|
||||
}
|
||||
|
||||
declaration = getContainerNode(declaration);
|
||||
function bestMatchKind(matches: ReadonlyArray<PatternMatch>): PatternMatchKind {
|
||||
Debug.assert(matches.length > 0);
|
||||
let bestMatchKind = PatternMatchKind.camelCase;
|
||||
|
||||
for (const match of matches) {
|
||||
const kind = match.kind;
|
||||
if (kind < bestMatchKind) {
|
||||
bestMatchKind = kind;
|
||||
}
|
||||
|
||||
return containers;
|
||||
}
|
||||
|
||||
function bestMatchKind(matches: PatternMatch[]) {
|
||||
Debug.assert(matches.length > 0);
|
||||
let bestMatchKind = PatternMatchKind.camelCase;
|
||||
return bestMatchKind;
|
||||
}
|
||||
|
||||
for (const match of matches) {
|
||||
const kind = match.kind;
|
||||
if (kind < bestMatchKind) {
|
||||
bestMatchKind = kind;
|
||||
}
|
||||
}
|
||||
function compareNavigateToItems(i1: RawNavigateToItem, i2: RawNavigateToItem): number {
|
||||
// TODO(cyrusn): get the gamut of comparisons that VS already uses here.
|
||||
// Right now we just sort by kind first, and then by name of the item.
|
||||
// We first sort case insensitively. So "Aaa" will come before "bar".
|
||||
// Then we sort case sensitively, so "aaa" will come before "Aaa".
|
||||
return i1.matchKind - i2.matchKind ||
|
||||
ts.compareStringsCaseInsensitive(i1.name, i2.name) ||
|
||||
ts.compareStrings(i1.name, i2.name);
|
||||
}
|
||||
|
||||
return bestMatchKind;
|
||||
}
|
||||
|
||||
function compareNavigateToItems(i1: RawNavigateToItem, i2: RawNavigateToItem) {
|
||||
// TODO(cyrusn): get the gamut of comparisons that VS already uses here.
|
||||
// Right now we just sort by kind first, and then by name of the item.
|
||||
// We first sort case insensitively. So "Aaa" will come before "bar".
|
||||
// Then we sort case sensitively, so "aaa" will come before "Aaa".
|
||||
return i1.matchKind - i2.matchKind ||
|
||||
ts.compareStringsCaseInsensitive(i1.name, i2.name) ||
|
||||
ts.compareStrings(i1.name, i2.name);
|
||||
}
|
||||
|
||||
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
|
||||
const declaration = rawItem.declaration;
|
||||
const container = <Declaration>getContainerNode(declaration);
|
||||
const containerName = container && getNameOfDeclaration(container);
|
||||
return {
|
||||
name: rawItem.name,
|
||||
kind: getNodeKind(declaration),
|
||||
kindModifiers: getNodeModifiers(declaration),
|
||||
matchKind: PatternMatchKind[rawItem.matchKind],
|
||||
isCaseSensitive: rawItem.isCaseSensitive,
|
||||
fileName: rawItem.fileName,
|
||||
textSpan: createTextSpanFromNode(declaration),
|
||||
// TODO(jfreeman): What should be the containerName when the container has a computed name?
|
||||
containerName: containerName ? (<Identifier>containerName).text : "",
|
||||
containerKind: containerName ? getNodeKind(container) : ScriptElementKind.unknown
|
||||
};
|
||||
}
|
||||
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
|
||||
const declaration = rawItem.declaration;
|
||||
const container = <Declaration>getContainerNode(declaration);
|
||||
const containerName = container && getNameOfDeclaration(container);
|
||||
return {
|
||||
name: rawItem.name,
|
||||
kind: getNodeKind(declaration),
|
||||
kindModifiers: getNodeModifiers(declaration),
|
||||
matchKind: PatternMatchKind[rawItem.matchKind],
|
||||
isCaseSensitive: rawItem.isCaseSensitive,
|
||||
fileName: rawItem.fileName,
|
||||
textSpan: createTextSpanFromNode(declaration),
|
||||
// TODO(jfreeman): What should be the containerName when the container has a computed name?
|
||||
containerName: containerName ? (<Identifier>containerName).text : "",
|
||||
containerKind: containerName ? getNodeKind(container) : ScriptElementKind.unknown
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,17 +209,24 @@ namespace ts.NavigationBar {
|
||||
|
||||
case SyntaxKind.BindingElement:
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
const decl = <VariableDeclaration>node;
|
||||
const name = decl.name;
|
||||
const { name, initializer } = <VariableDeclaration | BindingElement>node;
|
||||
if (isBindingPattern(name)) {
|
||||
addChildrenRecursively(name);
|
||||
}
|
||||
else if (decl.initializer && isFunctionOrClassExpression(decl.initializer)) {
|
||||
// For `const x = function() {}`, just use the function node, not the const.
|
||||
addChildrenRecursively(decl.initializer);
|
||||
else if (initializer && isFunctionOrClassExpression(initializer)) {
|
||||
if (initializer.name) {
|
||||
// Don't add a node for the VariableDeclaration, just for the initializer.
|
||||
addChildrenRecursively(initializer);
|
||||
}
|
||||
else {
|
||||
// Add a node for the VariableDeclaration, but not for the initializer.
|
||||
startNode(node);
|
||||
forEachChild(initializer, addChildrenRecursively);
|
||||
endNode();
|
||||
}
|
||||
}
|
||||
else {
|
||||
addNodeWithRecursiveChild(decl, decl.initializer);
|
||||
addNodeWithRecursiveChild(node, initializer);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -263,13 +270,15 @@ namespace ts.NavigationBar {
|
||||
break;
|
||||
|
||||
default:
|
||||
forEach(node.jsDoc, jsDoc => {
|
||||
forEach(jsDoc.tags, tag => {
|
||||
if (tag.kind === SyntaxKind.JSDocTypedefTag) {
|
||||
addLeafNode(tag);
|
||||
}
|
||||
if (hasJSDocNodes(node)) {
|
||||
forEach(node.jsDoc, jsDoc => {
|
||||
forEach(jsDoc.tags, tag => {
|
||||
if (tag.kind === SyntaxKind.JSDocTypedefTag) {
|
||||
addLeafNode(tag);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
forEachChild(node, addChildrenRecursively);
|
||||
}
|
||||
@@ -642,7 +651,14 @@ namespace ts.NavigationBar {
|
||||
}
|
||||
}
|
||||
|
||||
function isFunctionOrClassExpression(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction || node.kind === SyntaxKind.ClassExpression;
|
||||
function isFunctionOrClassExpression(node: Node): node is ArrowFunction | FunctionExpression | ClassExpression {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ClassExpression:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
namespace ts.OutliningElementsCollector {
|
||||
const collapseText = "...";
|
||||
const maxDepth = 20;
|
||||
const defaultLabel = "#region";
|
||||
const regionMatch = new RegExp("^\\s*//\\s*#(end)?region(?:\\s+(.*))?$");
|
||||
|
||||
export function collectElements(sourceFile: SourceFile, cancellationToken: CancellationToken): OutliningSpan[] {
|
||||
const elements: OutliningSpan[] = [];
|
||||
let depth = 0;
|
||||
const regions: OutliningSpan[] = [];
|
||||
|
||||
walk(sourceFile);
|
||||
return elements;
|
||||
gatherRegions();
|
||||
return elements.sort((span1, span2) => span1.textSpan.start - span2.textSpan.start);
|
||||
|
||||
/** If useFullStart is true, then the collapsing span includes leading whitespace, including linebreaks. */
|
||||
function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean, useFullStart: boolean) {
|
||||
@@ -89,6 +93,39 @@ namespace ts.OutliningElementsCollector {
|
||||
return isFunctionBlock(node) && node.parent.kind !== SyntaxKind.ArrowFunction;
|
||||
}
|
||||
|
||||
function gatherRegions(): void {
|
||||
const lineStarts = sourceFile.getLineStarts();
|
||||
|
||||
for (let i = 0; i < lineStarts.length; i++) {
|
||||
const currentLineStart = lineStarts[i];
|
||||
const lineEnd = lineStarts[i + 1] - 1 || sourceFile.getEnd();
|
||||
const comment = sourceFile.text.substring(currentLineStart, lineEnd);
|
||||
const result = comment.match(regionMatch);
|
||||
|
||||
if (result && !isInComment(sourceFile, currentLineStart)) {
|
||||
if (!result[1]) {
|
||||
const start = sourceFile.getFullText().indexOf("//", currentLineStart);
|
||||
const textSpan = createTextSpanFromBounds(start, lineEnd);
|
||||
const region: OutliningSpan = {
|
||||
textSpan,
|
||||
hintSpan: textSpan,
|
||||
bannerText: result[2] || defaultLabel,
|
||||
autoCollapse: false
|
||||
};
|
||||
regions.push(region);
|
||||
}
|
||||
else {
|
||||
const region = regions.pop();
|
||||
if (region) {
|
||||
region.textSpan.length = lineEnd - region.textSpan.start;
|
||||
region.hintSpan.length = lineEnd - region.textSpan.start;
|
||||
elements.push(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function walk(n: Node): void {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
if (depth > maxDepth) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user