Merge branch 'master' into add-codefix-cannot-find-name-in-for-loop

This commit is contained in:
Florian Regensburger
2019-07-02 00:41:27 +02:00
1356 changed files with 59797 additions and 13031 deletions
+48
View File
@@ -0,0 +1,48 @@
node_modules
.node_modules
built/*
test-args.txt
~*.docx
\#*\#
.\#*
src/harness/*.js
src/compiler/diagnosticInformationMap.generated.ts
src/compiler/diagnosticMessages.generated.json
src/parser/diagnosticInformationMap.generated.ts
src/parser/diagnosticMessages.generated.json
rwc-report.html
*.swp
build.json
*.actual
*.config
scripts/debug.bat
scripts/run.bat
scripts/word2md.js
scripts/buildProtocol.js
scripts/ior.js
scripts/authors.js
scripts/configurePrerelease.js
scripts/open-user-pr.js
scripts/open-cherry-pick-pr.js
scripts/processDiagnosticMessages.d.ts
scripts/processDiagnosticMessages.js
scripts/produceLKG.js
scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js
scripts/generateLocalizedDiagnosticMessages.js
scripts/*.js.map
scripts/typings/
coverage/
internal/
**/.DS_Store
.settings
**/.vs
.idea
yarn.lock
yarn-error.log
.parallelperf.*
.failed-tests
TEST-results.xml
package-lock.json
tests
.vscode
.git
+2
View File
@@ -34,6 +34,7 @@ tests/webTestServer.js.map
tests/webhost/*.d.ts
tests/webhost/webtsc.js
tests/cases/**/*.js
!tests/cases/docker/*.js/
tests/cases/**/*.js.map
*.config
scripts/debug.bat
@@ -44,6 +45,7 @@ scripts/ior.js
scripts/authors.js
scripts/configurePrerelease.js
scripts/open-user-pr.js
scripts/open-cherry-pick-pr.js
scripts/processDiagnosticMessages.d.ts
scripts/processDiagnosticMessages.js
scripts/produceLKG.js
+2
View File
@@ -29,3 +29,5 @@ package-lock.json
yarn.lock
CONTRIBUTING.md
TEST-results.xml
.dockerignore
Dockerfile
+1
View File
@@ -0,0 +1 @@
--install.no-lockfile true
+7
View File
@@ -0,0 +1,7 @@
# We use this dockerfile to build a packed tarfile which we import in our `docker` tests
FROM node:current
COPY . /typescript
WORKDIR /typescript
RUN npm install
RUN npm i -g gulp-cli
RUN gulp configure-insiders && gulp LKG && gulp clean && npm pack .
+5
View File
@@ -415,6 +415,8 @@ task("runtests").flags = {
" --no-lint": "Disables lint",
" --timeout=<ms>": "Overrides the default test timeout.",
" --built": "Compile using the built version of the compiler.",
" --shards": "Total number of shards running tests (default: 1)",
" --shardId": "1-based ID of this shard (default: 1)",
}
const runTestsParallel = () => runConsoleTests("built/local/run.js", "min", /*runInParallel*/ true, /*watchMode*/ false);
@@ -429,6 +431,9 @@ task("runtests-parallel").flags = {
" --workers=<number>": "The number of parallel workers to use.",
" --timeout=<ms>": "Overrides the default test timeout.",
" --built": "Compile using the built version of the compiler.",
" --skipPercent=<number>": "Skip expensive tests with <percent> chance to miss an edit. Default 5%.",
" --shards": "Total number of shards running tests (default: 1)",
" --shardId": "1-based ID of this shard (default: 1)",
};
task("diff", () => exec(getDiffTool(), [refBaseline, localBaseline], { ignoreExitCode: true }));
+4 -2
View File
@@ -1,11 +1,13 @@
# TypeScript
[![Join the chat at https://gitter.im/Microsoft/TypeScript](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Microsoft/TypeScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://travis-ci.org/microsoft/TypeScript.svg?branch=master)](https://travis-ci.org/Microsoft/TypeScript)
[![VSTS Build Status](https://dev.azure.com/typescript/TypeScript/_apis/build/status/Typescript/node10)](https://dev.azure.com/typescript/TypeScript/_build/latest?definitionId=4&view=logs)
[![npm version](https://badge.fury.io/js/typescript.svg)](https://www.npmjs.com/package/typescript)
[![Downloads](https://img.shields.io/npm/dm/typescript.svg)](https://www.npmjs.com/package/typescript)
# TypeScript
[![Join the chat at https://gitter.im/Microsoft/TypeScript](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Microsoft/TypeScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[TypeScript](https://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types to JavaScript that support tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](https://www.typescriptlang.org/play/), and stay up to date via [our blog](https://blogs.msdn.microsoft.com/typescript) and [Twitter account](https://twitter.com/typescript).
+7 -2
View File
@@ -2,7 +2,7 @@
"name": "typescript",
"author": "Microsoft Corp.",
"homepage": "https://www.typescriptlang.org/",
"version": "3.5.0",
"version": "3.6.0",
"license": "Apache-2.0",
"description": "TypeScript is a language for application scale JavaScript development",
"keywords": [
@@ -48,11 +48,13 @@
"@types/mocha": "latest",
"@types/ms": "latest",
"@types/node": "8.5.5",
"@types/node-fetch": "^2.3.4",
"@types/q": "latest",
"@types/source-map-support": "latest",
"@types/through2": "latest",
"@types/travis-fold": "latest",
"@types/xml2js": "^0.4.0",
"azure-devops-node-api": "^8.0.0",
"browser-resolve": "^1.11.2",
"browserify": "latest",
"chai": "latest",
@@ -74,11 +76,13 @@
"mocha": "latest",
"mocha-fivemat-progress-reporter": "latest",
"ms": "latest",
"node-fetch": "^2.6.0",
"plugin-error": "latest",
"pretty-hrtime": "^1.0.3",
"prex": "^0.4.3",
"q": "latest",
"remove-internal": "^2.9.2",
"simple-git": "^1.113.0",
"source-map-support": "latest",
"through2": "latest",
"travis-fold": "latest",
@@ -99,7 +103,8 @@
"gulp": "gulp",
"jake": "gulp",
"lint": "gulp lint",
"setup-hooks": "node scripts/link-hooks.js"
"setup-hooks": "node scripts/link-hooks.js",
"update-costly-tests": "node scripts/costly-tests.js"
},
"browser": {
"fs": false,
+3 -2
View File
@@ -5,7 +5,7 @@ const os = require("os");
/** @type {CommandLineOptions} */
module.exports = minimist(process.argv.slice(2), {
boolean: ["debug", "dirty", "inspect", "light", "colors", "lint", "lkg", "soft", "fix", "failed", "keepFailed", "force", "built"],
string: ["browser", "tests", "host", "reporter", "stackTraceLimit", "timeout"],
string: ["browser", "tests", "host", "reporter", "stackTraceLimit", "timeout", "shards", "shardId"],
alias: {
"b": "browser",
"d": "debug", "debug-brk": "debug",
@@ -14,6 +14,7 @@ module.exports = minimist(process.argv.slice(2), {
"ru": "runners", "runner": "runners",
"r": "reporter",
"c": "colors", "color": "colors",
"skip-percent": "skipPercent",
"w": "workers",
"f": "fix"
},
@@ -69,4 +70,4 @@ if (module.exports.built) {
*
* @typedef {import("minimist").ParsedArgs & TypedOptions} CommandLineOptions
*/
void 0;
void 0;
+13 -4
View File
@@ -31,10 +31,13 @@ async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode,
const inspect = cmdLineOptions.inspect;
const runners = cmdLineOptions.runners;
const light = cmdLineOptions.light;
const skipPercent = process.env.CI === "true" ? 0 : cmdLineOptions.skipPercent;
const stackTraceLimit = cmdLineOptions.stackTraceLimit;
const testConfigFile = "test.config";
const failed = cmdLineOptions.failed;
const keepFailed = cmdLineOptions.keepFailed;
const shards = +cmdLineOptions.shards || undefined;
const shardId = +cmdLineOptions.shardId || undefined;
if (!cmdLineOptions.dirty) {
await cleanTestDirs();
cancelToken.throwIfCancellationRequested();
@@ -62,8 +65,8 @@ async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode,
testTimeout = 400000;
}
if (tests || runners || light || testTimeout || taskConfigsFolder || keepFailed) {
writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, testTimeout, keepFailed);
if (tests || runners || light || testTimeout || taskConfigsFolder || keepFailed || skipPercent !== undefined || shards || shardId) {
writeTestConfigFile(tests, runners, light, skipPercent, taskConfigsFolder, workerCount, stackTraceLimit, testTimeout, keepFailed, shards, shardId);
}
const colors = cmdLineOptions.colors;
@@ -158,23 +161,29 @@ exports.cleanTestDirs = cleanTestDirs;
* @param {string} tests
* @param {string} runners
* @param {boolean} light
* @param {string} skipPercent
* @param {string} [taskConfigsFolder]
* @param {string | number} [workerCount]
* @param {string} [stackTraceLimit]
* @param {string | number} [timeout]
* @param {boolean} [keepFailed]
* @param {number | undefined} [shards]
* @param {number | undefined} [shardId]
*/
function writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, timeout, keepFailed) {
function writeTestConfigFile(tests, runners, light, skipPercent, taskConfigsFolder, workerCount, stackTraceLimit, timeout, keepFailed, shards, shardId) {
const testConfigContents = JSON.stringify({
test: tests ? [tests] : undefined,
runners: runners ? runners.split(",") : undefined,
light,
skipPercent,
workerCount,
stackTraceLimit,
taskConfigsFolder,
noColor: !cmdLineOptions.colors,
timeout,
keepFailed
keepFailed,
shards,
shardId
});
log.info("Running tests with config: " + testConfigContents);
fs.writeFileSync("test.config", testConfigContents);
+103
View File
@@ -0,0 +1,103 @@
// @ts-check
const fs = require("fs");
const git = require('simple-git/promise')('.')
const readline = require('readline')
/** @typedef {{ [s: string]: number}} Histogram */
async function main() {
/** @type {Histogram} */
const edits = Object.create(null)
/** @type {Histogram} */
const perf = JSON.parse(fs.readFileSync('.parallelperf.json', 'utf8'))
await collectCommits(git, "release-2.3", "master", /*author*/ undefined, files => fillMap(files, edits))
const totalTime = Object.values(perf).reduce((n,m) => n + m, 0)
const untouched = Object.values(perf).length - Object.values(edits).length
const totalEdits = Object.values(edits).reduce((n,m) => n + m, 0) + untouched + Object.values(edits).length
let i = 0
/** @type {{ name: string, time: number, edits: number, cost: number }[]} */
let data = []
for (const k in perf) {
const otherk = k.replace(/tsrunner-[a-z-]+?:\/\//, '')
const percentTime = perf[k] / totalTime
const percentHits = (1 + (edits[otherk] || 0)) / totalEdits
const cost = 5 + Math.log(percentTime / percentHits)
data.push({ name: otherk, time: perf[k], edits: 1 + (edits[otherk] || 0), cost})
if (edits[otherk])
i++
}
const output = {
totalTime,
totalEdits,
data: data.sort((x,y) => y.cost - x.cost).map(x => ({ ...x, cost: x.cost.toFixed(2) }))
}
fs.writeFileSync('tests/.test-cost.json', JSON.stringify(output), 'utf8')
}
main().catch(e => {
console.log(e);
process.exit(1);
})
/**
* @param {string[]} files
* @param {Histogram} histogram
*/
function fillMap(files, histogram) {
// keep edits to test cases (but not /users), and not file moves
const tests = files.filter(f => f.startsWith('tests/cases/') && !f.startsWith('tests/cases/user') && !/=>/.test(f))
for (const test of tests) {
histogram[test] = (histogram[test] || 0) + 1
}
}
/**
* @param {string} s
*/
function isSquashMergeMessage(s) {
return /\(#[0-9]+\)$/.test(s)
}
/**
* @param {string} s
*/
function isMergeCommit(s) {
return /Merge pull request #[0-9]+/.test(s)
}
/**
* @param {string} s
*/
function parseFiles(s) {
const lines = s.split('\n')
// Note that slice(2) only works for merge commits, which have an empty newline after the title
return lines.slice(2, lines.length - 2).map(line => line.split("|")[0].trim())
}
/**
* @param {import('simple-git/promise').SimpleGit} git
* @param {string} from
* @param {string} to
* @param {string | undefined} author - only include commits from this author
* @param {(files: string[]) => void} update
*/
async function collectCommits(git, from, to, author, update) {
let i = 0
for (const commit of (await git.log({ from, to })).all) {
i++
if ((!author || commit.author_name === author) && isMergeCommit(commit.message) || isSquashMergeMessage(commit.message)) {
readline.clearLine(process.stdout, /*left*/ -1)
readline.cursorTo(process.stdout, 0)
process.stdout.write(i + ": " + commit.date)
const files = parseFiles(await git.show([commit.hash, "--stat=1000,960,40", "--pretty=oneline"]))
update(files)
}
}
}
+106
View File
@@ -0,0 +1,106 @@
/// <reference lib="esnext.asynciterable" />
// Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally
/// <reference types="node" />
import Octokit = require("@octokit/rest");
const {runSequence} = require("./run-sequence");
import fs = require("fs");
import path = require("path");
const userName = process.env.GH_USERNAME;
const reviewers = process.env.REQUESTING_USER ? [process.env.REQUESTING_USER] : ["weswigham", "RyanCavanaugh"];
const branchName = `pick/${process.env.SOURCE_ISSUE}/${process.env.TARGET_BRANCH}`;
const remoteUrl = `https://${process.argv[2]}@github.com/${userName}/TypeScript.git`;
async function main() {
if (!process.env.TARGET_BRANCH) {
throw new Error("Target branch not specified");
}
if (!process.env.SOURCE_ISSUE) {
throw new Error("Source issue not specified");
}
const currentSha = runSequence([
["git", ["rev-parse", "HEAD"]]
]);
const currentAuthor = runSequence([
["git", ["log", "-1", `--pretty="%aN <%aE>"`]]
]);
runSequence([
["git", ["fetch", "origin", "master"]]
]);
let logText = runSequence([
["git", ["log", `origin/master..${currentSha.trim()}`, `--pretty="%h %s%n%b"`, "--reverse"]]
]);
logText = `Cherry-pick PR #${process.env.SOURCE_ISSUE} into ${process.env.TARGET_BRANCH}
Component commits:
${logText.trim()}`
const logpath = path.join(__dirname, "../", "logmessage.txt");
const mergebase = runSequence([["git", ["merge-base", "origin/master", currentSha]]]).trim();
runSequence([
["git", ["checkout", "-b", "temp-branch"]],
["git", ["reset", mergebase, "--soft"]]
]);
fs.writeFileSync(logpath, logText);
runSequence([
["git", ["commit", "-F", logpath, `--author="${currentAuthor.trim()}"`]]
]);
fs.unlinkSync(logpath);
const squashSha = runSequence([
["git", ["rev-parse", "HEAD"]]
]);
runSequence([
["git", ["checkout", process.env.TARGET_BRANCH]], // checkout the target branch
["git", ["checkout", "-b", branchName]], // create a new branch
["git", ["cherry-pick", squashSha.trim()]], //
["git", ["remote", "add", "fork", remoteUrl]], // Add the remote fork
["git", ["push", "--set-upstream", "fork", branchName, "-f"]] // push the branch
]);
const gh = new Octokit();
gh.authenticate({
type: "token",
token: process.argv[2]
});
const r = await gh.pulls.create({
owner: "Microsoft",
repo: "TypeScript",
maintainer_can_modify: true,
title: `🤖 Cherry-pick PR #${process.env.SOURCE_ISSUE} into ${process.env.TARGET_BRANCH}`,
head: `${userName}:${branchName}`,
base: process.env.TARGET_BRANCH,
body:
`This cherry-pick was triggerd by a request on https://github.com/Microsoft/TypeScript/pull/${process.env.SOURCE_ISSUE}
Please review the diff and merge if no changes are unexpected.
You can view the cherry-pick log [here](https://typescript.visualstudio.com/TypeScript/_build/index?buildId=${process.env.BUILD_BUILDID}&_a=summary).
cc ${reviewers.map(r => "@" + r).join(" ")}`,
});
const num = r.data.number;
console.log(`Pull request ${num} created.`);
await gh.issues.createComment({
number: +process.env.SOURCE_ISSUE,
owner: "Microsoft",
repo: "TypeScript",
body: `Hey @${process.env.REQUESTING_USER}, I've opened #${num} for you.`
});
}
main().catch(async e => {
console.error(e);
process.exitCode = 1;
if (process.env.SOURCE_ISSUE) {
const gh = new Octokit();
gh.authenticate({
type: "token",
token: process.argv[2]
});
await gh.issues.createComment({
number: +process.env.SOURCE_ISSUE,
owner: "Microsoft",
repo: "TypeScript",
body: `Hey @${process.env.REQUESTING_USER}, I couldn't open a PR with the cherry-pick. ([You can check the log here](https://typescript.visualstudio.com/TypeScript/_build/index?buildId=${process.env.BUILD_BUILDID}&_a=summary)). You may need to squash and pick this PR into ${process.env.TARGET_BRANCH} manually.`
});
}
});
+8 -7
View File
@@ -1,4 +1,5 @@
/// <reference lib="esnext.asynciterable" />
/// <reference lib="es2015.promise" />
// Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally
import Octokit = require("@octokit/rest");
import {runSequence} from "./run-sequence";
@@ -9,7 +10,7 @@ function padNum(number: number) {
}
const userName = process.env.GH_USERNAME;
const reviewers = process.env.requesting_user ? [process.env.requesting_user] : ["weswigham", "sandersn", "RyanCavanaugh"];
const reviewers = process.env.REQUESTING_USER ? [process.env.REQUESTING_USER] : ["weswigham", "sandersn", "RyanCavanaugh"];
const now = new Date();
const branchName = `user-update-${process.env.TARGET_FORK}-${now.getFullYear()}${padNum(now.getMonth())}${padNum(now.getDay())}${process.env.TARGET_BRANCH ? "-" + process.env.TARGET_BRANCH : ""}`;
const remoteUrl = `https://${process.argv[2]}@github.com/${userName}/TypeScript.git`;
@@ -29,31 +30,31 @@ gh.authenticate({
token: process.argv[2]
});
gh.pulls.create({
owner: process.env.TARGET_FORK,
owner: process.env.TARGET_FORK!,
repo: "TypeScript",
maintainer_can_modify: true,
title: `🤖 User test baselines have changed` + (process.env.TARGET_BRANCH ? ` for ${process.env.TARGET_BRANCH}` : ""),
head: `${userName}:${branchName}`,
base: process.env.TARGET_BRANCH || "master",
body:
`${process.env.source_issue ? `This test run was triggerd by a request on https://github.com/Microsoft/TypeScript/pull/${process.env.source_issue} `+"\n" : ""}Please review the diff and merge if no changes are unexpected.
`${process.env.SOURCE_ISSUE ? `This test run was triggerd by a request on https://github.com/Microsoft/TypeScript/pull/${process.env.SOURCE_ISSUE} `+"\n" : ""}Please review the diff and merge if no changes are unexpected.
You can view the build log [here](https://typescript.visualstudio.com/TypeScript/_build/index?buildId=${process.env.BUILD_BUILDID}&_a=summary).
cc ${reviewers.map(r => "@" + r).join(" ")}`,
}).then(async r => {
const num = r.data.number;
console.log(`Pull request ${num} created.`);
if (!process.env.source_issue) {
if (!process.env.SOURCE_ISSUE) {
await gh.pulls.createReviewRequest({
owner: process.env.TARGET_FORK,
owner: process.env.TARGET_FORK!,
repo: "TypeScript",
number: num,
pull_number: num,
reviewers,
});
}
else {
await gh.issues.createComment({
number: +process.env.source_issue,
number: +process.env.SOURCE_ISSUE,
owner: "Microsoft",
repo: "TypeScript",
body: `The user suite test run you requested has finished and _failed_. I've opened a [PR with the baseline diff from master](${r.data.html_url}).`
+64
View File
@@ -0,0 +1,64 @@
// @ts-check
/// <reference lib="esnext.asynciterable" />
// Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally
const Octokit = require("@octokit/rest");
const ado = require("azure-devops-node-api");
const { default: fetch } = require("node-fetch");
async function main() {
if (!process.env.SOURCE_ISSUE) {
throw new Error("No source issue specified");
}
if (!process.env.BUILD_BUILDID) {
throw new Error("No build ID specified");
}
// The pipelines API does _not_ make getting the direct URL to a specific file _within_ an artifact trivial
const cli = new ado.WebApi("https://typescript.visualstudio.com/defaultcollection", ado.getHandlerFromToken("")); // Empty token, anon auth
const build = await cli.getBuildApi();
const artifact = await build.getArtifact("typescript", +process.env.BUILD_BUILDID, "tgz");
const updatedUrl = new URL(artifact.resource.url);
updatedUrl.search = `artifactName=tgz&fileId=${artifact.resource.data}&fileName=manifest`;
const resp = await (await fetch(`${updatedUrl}`)).json();
const file = resp.items[0];
const tgzUrl = new URL(artifact.resource.url);
tgzUrl.search = `artifactName=tgz&fileId=${file.blob.id}&fileName=${file.path}`;
const link = "" + tgzUrl;
const gh = new Octokit();
gh.authenticate({
type: "token",
token: process.argv[2]
});
await gh.issues.createComment({
number: +process.env.SOURCE_ISSUE,
owner: "Microsoft",
repo: "TypeScript",
body: `Hey @${process.env.REQUESTING_USER}, I've packed this into [an installable tgz](${link}). You can install it for testing by referencing it in your \`package.json\` like so:
\`\`\`
{
"devDependencies": {
"typescript": "${link}"
}
}
\`\`\`
and then running \`npm install\`.
`
});
}
main().catch(async e => {
console.error(e);
process.exitCode = 1;
if (process.env.SOURCE_ISSUE) {
const gh = new Octokit();
gh.authenticate({
type: "token",
token: process.argv[2]
});
await gh.issues.createComment({
number: +process.env.SOURCE_ISSUE,
owner: "Microsoft",
repo: "TypeScript",
body: `Hey @${process.env.REQUESTING_USER}, something went wrong when looking for the build artifact. ([You can check the log here](https://typescript.visualstudio.com/TypeScript/_build/index?buildId=${process.env.BUILD_BUILDID}&_a=summary)).`
});
}
});
+2 -1
View File
@@ -5,12 +5,13 @@ const cp = require("child_process");
* @param {[string, string[]][]} tasks
* @param {cp.SpawnSyncOptions} opts
*/
function runSequence(tasks, opts = { timeout: 100000, shell: true, stdio: "inherit" }) {
function runSequence(tasks, opts = { timeout: 100000, shell: true }) {
let lastResult;
for (const task of tasks) {
console.log(`${task[0]} ${task[1].join(" ")}`);
const result = cp.spawnSync(task[0], task[1], opts);
if (result.status !== 0) throw new Error(`${task[0]} ${task[1].join(" ")} failed: ${result.stderr && result.stderr.toString()}`);
console.log(result.stdout && result.stdout.toString());
lastResult = result;
}
return lastResult && lastResult.stdout && lastResult.stdout.toString();
+59 -54
View File
@@ -1,87 +1,92 @@
// @ts-check
/// <reference lib="esnext.asynciterable" />
const Octokit = require("@octokit/rest");
const {runSequence} = require("./run-sequence");
const { runSequence } = require("./run-sequence");
// The first is used by bot-based kickoffs, the second by automatic triggers
const triggeredPR = process.env.SOURCE_ISSUE || process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER;
/**
* This program should be invoked as `node ./scripts/update-experimental-branches <GithubAccessToken> <Branch1> [Branch2] [...]`
* This program should be invoked as `node ./scripts/update-experimental-branches <GithubAccessToken>`
* TODO: the following is racey - if two experiment-enlisted PRs trigger simultaneously and witness one another in an unupdated state, they'll both produce
* a new experimental branch, but each will be missing a change from the other. There's no _great_ way to fix this beyond setting the maximum concurrency
* of this task to 1 (so only one job is allowed to update experiments at a time).
*/
async function main() {
const branchesRaw = process.argv[3];
const branches = process.argv.slice(3);
if (!branches.length) {
throw new Error(`No experimental branches, aborting...`);
}
console.log(`Performing experimental branch updating and merging for branches ${branchesRaw}`);
const gh = new Octokit();
gh.authenticate({
type: "token",
token: process.argv[2]
const gh = new Octokit({
auth: process.argv[2]
});
// Fetch all relevant refs
runSequence([
["git", ["fetch", "origin", "master:master", ...branches.map(b => `${b}:${b}`)]]
])
const prnums = (await gh.issues.listForRepo({
labels: "typescript@experimental",
sort: "created",
state: "open",
owner: "Microsoft",
repo: "TypeScript",
})).data.filter(i => !!i.pull_request).map(i => i.number);
if (triggeredPR && !prnums.some(n => n === +triggeredPR)) {
return; // Only have work to do for enlisted PRs
}
console.log(`Performing experimental branch updating and merging for pull requests ${prnums.join(", ")}`);
const userName = process.env.GH_USERNAME;
const remoteUrl = `https://${process.argv[2]}@github.com/${userName}/TypeScript.git`;
// Forcibly cleanup workspace
runSequence([
["git", ["clean", "-fdx"]],
["git", ["checkout", "."]],
["git", ["fetch", "-fu", "origin", "master:master"]],
["git", ["checkout", "master"]],
["git", ["remote", "add", "fork", remoteUrl]], // Add the remote fork
]);
// Update branches
for (const branch of branches) {
// Checkout, then get the merge base
const mergeBase = runSequence([
["git", ["checkout", branch]],
["git", ["merge-base", branch, "master"]],
]);
// Simulate the merge and abort if there are conflicts
const mergeTree = runSequence([
["git", ["merge-tree", mergeBase, branch, "master"]]
]);
if (mergeTree.indexOf(`===${"="}===`)) { // 7 equals is the center of the merge conflict marker
const res = await gh.pulls.list({owner: "Microsoft", repo: "TypeScript", base: branch});
if (res && res.data && res.data[0]) {
const pr = res.data[0];
await gh.issues.createComment({
owner: "Microsoft",
repo: "TypeScript",
number: pr.number,
body: `This PR is configured as an experiment, and currently has merge conflicts with master - please rebase onto master and fix the conflicts.`
});
for (const numRaw of prnums) {
const num = +numRaw;
if (num) {
// PR number rather than branch name - lookup info
const inputPR = await gh.pulls.get({ owner: "Microsoft", repo: "TypeScript", pull_number: num });
// GH calculates the rebaseable-ness of a PR into its target, so we can just use that here
if (!inputPR.data.rebaseable) {
if (+triggeredPR === num) {
await gh.issues.createComment({
owner: "Microsoft",
repo: "TypeScript",
issue_number: num,
body: `This PR is configured as an experiment, and currently has rebase conflicts with master - please rebase onto master and fix the conflicts.`
});
}
throw new Error(`Rebase conflict detected in PR ${num} with master`); // A PR is currently in conflict, give up
}
throw new Error(`Merge conflict detected on branch ${branch} with master`);
runSequence([
["git", ["fetch", "origin", `pull/${num}/head:${num}`]],
["git", ["checkout", `${num}`]],
["git", ["rebase", "master"]],
["git", ["push", "-f", "-u", "fork", `${num}`]], // Keep a rebased copy of this branch in our fork
]);
}
else {
throw new Error(`Invalid PR number: ${numRaw}`);
}
// Merge is good - apply a rebase and (force) push
runSequence([
["git", ["rebase", "master"]],
["git", ["push", "-f", "-u", "origin", branch]],
]);
}
// Return to `master` and make a new `experimental` branch
runSequence([
["git", ["checkout", "master"]],
["git", ["branch", "-D", "experimental"]],
["git", ["checkout", "-b", "experimental"]],
]);
// Merge each branch into `experimental` (which, if there is a conflict, we now know is from inter-experiment conflict)
for (const branch of branches) {
for (const branch of prnums) {
// Find the merge base
const mergeBase = runSequence([
["git", ["merge-base", branch, "experimental"]],
]);
// Simulate the merge and abort if there are conflicts
const mergeTree = runSequence([
["git", ["merge-tree", mergeBase, branch, "experimental"]]
["git", ["merge-tree", mergeBase.trim(), branch, "experimental"]]
]);
if (mergeTree.indexOf(`===${"="}===`)) { // 7 equals is the center of the merge conflict marker
throw new Error(`Merge conflict detected on branch ${branch} with other experiment`);
if (mergeTree.indexOf(`===${"="}===`) >= 0) { // 7 equals is the center of the merge conflict marker
throw new Error(`Merge conflict detected involving PR ${branch} with other experiment`);
}
// Merge (always producing a merge commit)
runSequence([
@@ -90,7 +95,7 @@ async function main() {
}
// Every branch merged OK, force push the replacement `experimental` branch
runSequence([
["git", ["push", "-f", "-u", "origin", "experimental"]],
["git", ["push", "-f", "-u", "fork", "experimental"]],
]);
}
+36 -28
View File
@@ -274,6 +274,9 @@ namespace ts {
if (isStringOrNumericLiteralLike(nameExpression)) {
return escapeLeadingUnderscores(nameExpression.text);
}
if (isSignedNumericLiteral(nameExpression)) {
return tokenToString(nameExpression.operator) + nameExpression.operand.text as __String;
}
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name));
@@ -2515,7 +2518,7 @@ namespace ts {
break;
default:
Debug.fail(Debug.showSyntaxKind(thisContainer));
Debug.failBadSyntaxKind(thisContainer);
}
}
@@ -2581,7 +2584,7 @@ namespace ts {
// Fix up parent pointers since we're going to use these nodes before we bind into them
node.left.parent = node;
node.right.parent = node;
if (isIdentifier(lhs.expression) && container === file && isNameOfExportsOrModuleExportsAliasDeclaration(file, lhs.expression)) {
if (isIdentifier(lhs.expression) && container === file && isExportsOrModuleExportsOrAlias(file, lhs.expression)) {
// This can be an alias for the 'exports' or 'module.exports' names, e.g.
// var util = module.exports;
// util.property = function ...
@@ -2975,21 +2978,27 @@ namespace ts {
}
export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean {
return isExportsIdentifier(node) ||
isModuleExportsPropertyAccessExpression(node) ||
isIdentifier(node) && isNameOfExportsOrModuleExportsAliasDeclaration(sourceFile, node);
}
function isNameOfExportsOrModuleExportsAliasDeclaration(sourceFile: SourceFile, node: Identifier): boolean {
const symbol = lookupSymbolForNameWorker(sourceFile, node.escapedText);
return !!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) &&
!!symbol.valueDeclaration.initializer && isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, symbol.valueDeclaration.initializer);
}
function isExportsOrModuleExportsOrAliasOrAssignment(sourceFile: SourceFile, node: Expression): boolean {
return isExportsOrModuleExportsOrAlias(sourceFile, node) ||
(isAssignmentExpression(node, /*excludeCompoundAssignment*/ true) && (
isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, node.left) || isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, node.right)));
let i = 0;
const q = [node];
while (q.length && i < 100) {
i++;
node = q.shift()!;
if (isExportsIdentifier(node) || isModuleExportsPropertyAccessExpression(node)) {
return true;
}
else if (isIdentifier(node)) {
const symbol = lookupSymbolForNameWorker(sourceFile, node.escapedText);
if (!!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && !!symbol.valueDeclaration.initializer) {
const init = symbol.valueDeclaration.initializer;
q.push(init);
if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) {
q.push(init.left);
q.push(init.right);
}
}
}
}
return false;
}
function lookupSymbolForNameWorker(container: Node, name: __String): Symbol | undefined {
@@ -3223,8 +3232,7 @@ namespace ts {
// A ClassDeclaration is ES6 syntax.
transformFlags = subtreeFlags | TransformFlags.AssertES2015;
// A class with a parameter property assignment, property initializer, computed property name, or decorator is
// TypeScript syntax.
// A class with a parameter property assignment or decorator is TypeScript syntax.
// An exported declaration may be TypeScript syntax, but is handled by the visitor
// for a namespace declaration.
if ((subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax)
@@ -3241,8 +3249,7 @@ namespace ts {
// A ClassExpression is ES6 syntax.
let transformFlags = subtreeFlags | TransformFlags.AssertES2015;
// A class with a parameter property assignment, property initializer, or decorator is
// TypeScript syntax.
// A class with a parameter property assignment or decorator is TypeScript syntax.
if (subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax
|| node.typeParameters) {
transformFlags |= TransformFlags.AssertTypeScript;
@@ -3332,7 +3339,6 @@ namespace ts {
|| hasModifier(node, ModifierFlags.TypeScriptModifier)
|| node.typeParameters
|| node.type
|| (node.name && isComputedPropertyName(node.name)) // While computed method names aren't typescript, the TS transform must visit them to emit property declarations correctly
|| !node.body) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -3363,7 +3369,6 @@ namespace ts {
if (node.decorators
|| hasModifier(node, ModifierFlags.TypeScriptModifier)
|| node.type
|| (node.name && isComputedPropertyName(node.name)) // While computed accessor names aren't typescript, the TS transform must visit them to emit property declarations correctly
|| !node.body) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -3378,12 +3383,15 @@ namespace ts {
}
function computePropertyDeclaration(node: PropertyDeclaration, subtreeFlags: TransformFlags) {
// A PropertyDeclaration is TypeScript syntax.
let transformFlags = subtreeFlags | TransformFlags.AssertTypeScript;
let transformFlags = subtreeFlags | TransformFlags.ContainsClassFields;
// If the PropertyDeclaration has an initializer or a computed name, we need to inform its ancestor
// so that it handle the transformation.
if (node.initializer || isComputedPropertyName(node.name)) {
// Decorators, TypeScript-specific modifiers, and type annotations are TypeScript syntax.
if (some(node.decorators) || hasModifier(node, ModifierFlags.TypeScriptModifier) || node.type) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// Hoisted variables related to class properties should live within the TypeScript class wrapper.
if (isComputedPropertyName(node.name) || (hasStaticModifier(node) && node.initializer)) {
transformFlags |= TransformFlags.ContainsTypeScriptClassSyntax;
}
+139 -70
View File
@@ -10,18 +10,13 @@ namespace ts {
export interface ReusableDiagnosticRelatedInformation {
category: DiagnosticCategory;
code: number;
file: Path | undefined;
file: string | undefined;
start: number | undefined;
length: number | undefined;
messageText: string | ReusableDiagnosticMessageChain;
}
export interface ReusableDiagnosticMessageChain {
messageText: string;
category: DiagnosticCategory;
code: number;
next?: ReusableDiagnosticMessageChain;
}
export type ReusableDiagnosticMessageChain = DiagnosticMessageChain;
export interface ReusableBuilderProgramState extends ReusableBuilderState {
/**
@@ -227,7 +222,7 @@ namespace ts {
// Unchanged file copy diagnostics
const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
if (diagnostics) {
state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as ReadonlyArray<ReusableDiagnostic>, newProgram) : diagnostics as ReadonlyArray<Diagnostic>);
state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as ReadonlyArray<ReusableDiagnostic>, newProgram, getCanonicalFileName) : diagnostics as ReadonlyArray<Diagnostic>);
if (!state.semanticDiagnosticsFromOldState) {
state.semanticDiagnosticsFromOldState = createMap<true>();
}
@@ -246,37 +241,32 @@ namespace ts {
return state;
}
function convertToDiagnostics(diagnostics: ReadonlyArray<ReusableDiagnostic>, newProgram: Program): ReadonlyArray<Diagnostic> {
function convertToDiagnostics(diagnostics: ReadonlyArray<ReusableDiagnostic>, newProgram: Program, getCanonicalFileName: GetCanonicalFileName): ReadonlyArray<Diagnostic> {
if (!diagnostics.length) return emptyArray;
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getOutputPathForBuildInfo(newProgram.getCompilerOptions())!, newProgram.getCurrentDirectory()));
return diagnostics.map(diagnostic => {
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram);
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPath);
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
result.source = diagnostic.source;
const { relatedInformation } = diagnostic;
result.relatedInformation = relatedInformation ?
relatedInformation.length ?
relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram)) :
relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram, toPath)) :
emptyArray :
undefined;
return result;
});
function toPath(path: string) {
return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
}
}
function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program): DiagnosticRelatedInformation {
const { file, messageText } = diagnostic;
function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program, toPath: (path: string) => Path): DiagnosticRelatedInformation {
const { file } = diagnostic;
return {
...diagnostic,
file: file && newProgram.getSourceFileByPath(file),
messageText: messageText === undefined || isString(messageText) ?
messageText :
convertToDiagnosticMessageChain(messageText, newProgram)
};
}
function convertToDiagnosticMessageChain(diagnostic: ReusableDiagnosticMessageChain, newProgram: Program): DiagnosticMessageChain {
return {
...diagnostic,
next: diagnostic.next && convertToDiagnosticMessageChain(diagnostic.next, newProgram)
file: file ? newProgram.getSourceFileByPath(toPath(file)) : undefined
};
}
@@ -620,19 +610,23 @@ namespace ts {
/**
* Gets the program information to be emitted in buildInfo so that we can use it to create new program
*/
function getProgramBuildInfo(state: Readonly<ReusableBuilderProgramState>): ProgramBuildInfo | undefined {
function getProgramBuildInfo(state: Readonly<ReusableBuilderProgramState>, getCanonicalFileName: GetCanonicalFileName): ProgramBuildInfo | undefined {
if (state.compilerOptions.outFile || state.compilerOptions.out) return undefined;
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getOutputPathForBuildInfo(state.compilerOptions)!, Debug.assertDefined(state.program).getCurrentDirectory()));
const fileInfos: MapLike<BuilderState.FileInfo> = {};
state.fileInfos.forEach((value, key) => {
const signature = state.currentAffectedFilesSignatures && state.currentAffectedFilesSignatures.get(key);
fileInfos[key] = signature === undefined ? value : { version: value.version, signature };
fileInfos[relativeToBuildInfo(key)] = signature === undefined ? value : { version: value.version, signature };
});
const result: ProgramBuildInfo = { fileInfos, options: state.compilerOptions };
const result: ProgramBuildInfo = {
fileInfos,
options: convertToReusableCompilerOptions(state.compilerOptions, relativeToBuildInfo)
};
if (state.referencedMap) {
const referencedMap: MapLike<string[]> = {};
state.referencedMap.forEach((value, key) => {
referencedMap[key] = arrayFrom(value.keys());
referencedMap[relativeToBuildInfo(key)] = arrayFrom(value.keys(), relativeToBuildInfo);
});
result.referencedMap = referencedMap;
}
@@ -642,9 +636,9 @@ namespace ts {
state.exportedModulesMap.forEach((value, key) => {
const newValue = state.currentAffectedFilesExportedModulesMap && state.currentAffectedFilesExportedModulesMap.get(key);
// Not in temporary cache, use existing value
if (newValue === undefined) exportedModulesMap[key] = arrayFrom(value.keys());
if (newValue === undefined) exportedModulesMap[relativeToBuildInfo(key)] = arrayFrom(value.keys(), relativeToBuildInfo);
// Value in cache and has updated value map, use that
else if (newValue) exportedModulesMap[key] = arrayFrom(newValue.keys());
else if (newValue) exportedModulesMap[relativeToBuildInfo(key)] = arrayFrom(newValue.keys(), relativeToBuildInfo);
});
result.exportedModulesMap = exportedModulesMap;
}
@@ -655,50 +649,78 @@ namespace ts {
state.semanticDiagnosticsPerFile.forEach((value, key) => semanticDiagnosticsPerFile.push(
value.length ?
[
key,
relativeToBuildInfo(key),
state.hasReusableDiagnostic ?
value as ReadonlyArray<ReusableDiagnostic> :
convertToReusableDiagnostics(value as ReadonlyArray<Diagnostic>)
convertToReusableDiagnostics(value as ReadonlyArray<Diagnostic>, relativeToBuildInfo)
] :
key
relativeToBuildInfo(key)
));
result.semanticDiagnosticsPerFile = semanticDiagnosticsPerFile;
}
return result;
function relativeToBuildInfo(path: string) {
return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory, path, getCanonicalFileName));
}
}
function convertToReusableDiagnostics(diagnostics: ReadonlyArray<Diagnostic>): ReadonlyArray<ReusableDiagnostic> {
function convertToReusableCompilerOptions(options: CompilerOptions, relativeToBuildInfo: (path: string) => string) {
const result: CompilerOptions = {};
const optionsNameMap = getOptionNameMap().optionNameMap;
for (const name in options) {
if (hasProperty(options, name)) {
result[name] = convertToReusableCompilerOptionValue(
optionsNameMap.get(name.toLowerCase()),
options[name] as CompilerOptionsValue,
relativeToBuildInfo
);
}
}
if (result.configFilePath) {
result.configFilePath = relativeToBuildInfo(result.configFilePath);
}
return result;
}
function convertToReusableCompilerOptionValue(option: CommandLineOption | undefined, value: CompilerOptionsValue, relativeToBuildInfo: (path: string) => string) {
if (option) {
if (option.type === "list") {
const values = value as ReadonlyArray<string | number>;
if (option.element.isFilePath && values.length) {
return values.map(relativeToBuildInfo);
}
}
else if (option.isFilePath) {
return relativeToBuildInfo(value as string);
}
}
return value;
}
function convertToReusableDiagnostics(diagnostics: ReadonlyArray<Diagnostic>, relativeToBuildInfo: (path: string) => string): ReadonlyArray<ReusableDiagnostic> {
Debug.assert(!!diagnostics.length);
return diagnostics.map(diagnostic => {
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic);
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic, relativeToBuildInfo);
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
result.source = diagnostic.source;
const { relatedInformation } = diagnostic;
result.relatedInformation = relatedInformation ?
relatedInformation.length ?
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r)) :
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r, relativeToBuildInfo)) :
emptyArray :
undefined;
return result;
});
}
function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation): ReusableDiagnosticRelatedInformation {
const { file, messageText } = diagnostic;
function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation, relativeToBuildInfo: (path: string) => string): ReusableDiagnosticRelatedInformation {
const { file } = diagnostic;
return {
...diagnostic,
file: file && file.path,
messageText: messageText === undefined || isString(messageText) ?
messageText :
convertToReusableDiagnosticMessageChain(messageText)
};
}
function convertToReusableDiagnosticMessageChain(diagnostic: DiagnosticMessageChain): ReusableDiagnosticMessageChain {
return {
...diagnostic,
next: diagnostic.next && convertToReusableDiagnosticMessageChain(diagnostic.next)
file: file ? relativeToBuildInfo(file.path) : undefined
};
}
@@ -767,7 +789,7 @@ namespace ts {
const computeHash = host.createHash || generateDjb2Hash;
let state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState);
let backupState: BuilderProgramState | undefined;
newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state);
newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state, getCanonicalFileName);
// To ensure that we arent storing any references to old program or new program without state
newProgram = undefined!; // TODO: GH#18217
@@ -796,6 +818,7 @@ namespace ts {
(result as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
}
else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
(result as EmitAndSemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
(result as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile;
}
else {
@@ -913,6 +936,11 @@ namespace ts {
);
}
// Add file to affected file pending emit to handle for later emit time
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
addToAffectedFilesPendingEmit(state, [(affected as SourceFile).path]);
}
// Get diagnostics for the affected file if its not ignored
if (ignoreSourceFile && ignoreSourceFile(affected as SourceFile)) {
// Get next affected file
@@ -951,18 +979,8 @@ namespace ts {
// When semantic builder asks for diagnostics of the whole program,
// ensure that all the affected files are handled
let affected: SourceFile | Program | undefined;
let affectedFilesPendingEmit: Path[] | undefined;
while (affected = getNextAffectedFile(state, cancellationToken, computeHash)) {
if (affected !== state.program && kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
(affectedFilesPendingEmit || (affectedFilesPendingEmit = [])).push((affected as SourceFile).path);
}
doneWithAffectedFile(state, affected);
}
// In case of emit builder, cache the files to be emitted
if (affectedFilesPendingEmit) {
addToAffectedFilesPendingEmit(state, affectedFilesPendingEmit);
// tslint:disable-next-line no-empty
while (getSemanticDiagnosticsOfNextAffectedFile(cancellationToken)) {
}
let diagnostics: Diagnostic[] | undefined;
@@ -984,26 +1002,35 @@ namespace ts {
}
}
function getMapOfReferencedSet(mapLike: MapLike<ReadonlyArray<string>> | undefined): ReadonlyMap<BuilderState.ReferencedSet> | undefined {
function getMapOfReferencedSet(mapLike: MapLike<ReadonlyArray<string>> | undefined, toPath: (path: string) => Path): ReadonlyMap<BuilderState.ReferencedSet> | undefined {
if (!mapLike) return undefined;
const map = createMap<BuilderState.ReferencedSet>();
// Copies keys/values from template. Note that for..in will not throw if
// template is undefined, and instead will just exit the loop.
for (const key in mapLike) {
if (hasProperty(mapLike, key)) {
map.set(key, arrayToSet(mapLike[key]));
map.set(toPath(key), arrayToSet(mapLike[key], toPath));
}
}
return map;
}
export function createBuildProgramUsingProgramBuildInfo(program: ProgramBuildInfo): EmitAndSemanticDiagnosticsBuilderProgram & SemanticDiagnosticsBuilderProgram {
const fileInfos = createMapFromTemplate(program.fileInfos);
export function createBuildProgramUsingProgramBuildInfo(program: ProgramBuildInfo, buildInfoPath: string, host: ReadBuildProgramHost): EmitAndSemanticDiagnosticsBuilderProgram {
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
const fileInfos = createMap<BuilderState.FileInfo>();
for (const key in program.fileInfos) {
if (hasProperty(program.fileInfos, key)) {
fileInfos.set(toPath(key), program.fileInfos[key]);
}
}
const state: ReusableBuilderProgramState = {
fileInfos,
compilerOptions: program.options,
referencedMap: getMapOfReferencedSet(program.referencedMap),
exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap),
compilerOptions: convertFromReusableCompilerOptions(program.options, toAbsolutePath),
referencedMap: getMapOfReferencedSet(program.referencedMap, toPath),
exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap, toPath),
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => isString(value) ? value : value[0], value => isString(value) ? emptyArray : value[1]),
hasReusableDiagnostic: true
};
@@ -1029,6 +1056,48 @@ namespace ts {
emitNextAffectedFile: notImplemented,
getSemanticDiagnosticsOfNextAffectedFile: notImplemented,
};
function toPath(path: string) {
return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
}
function toAbsolutePath(path: string) {
return getNormalizedAbsolutePath(path, buildInfoDirectory);
}
}
function convertFromReusableCompilerOptions(options: CompilerOptions, toAbsolutePath: (path: string) => string) {
const result: CompilerOptions = {};
const optionsNameMap = getOptionNameMap().optionNameMap;
for (const name in options) {
if (hasProperty(options, name)) {
result[name] = convertFromReusableCompilerOptionValue(
optionsNameMap.get(name.toLowerCase()),
options[name] as CompilerOptionsValue,
toAbsolutePath
);
}
}
if (result.configFilePath) {
result.configFilePath = toAbsolutePath(result.configFilePath);
}
return result;
}
function convertFromReusableCompilerOptionValue(option: CommandLineOption | undefined, value: CompilerOptionsValue, toAbsolutePath: (path: string) => string) {
if (option) {
if (option.type === "list") {
const values = value as ReadonlyArray<string | number>;
if (option.element.isFilePath && values.length) {
return values.map(toAbsolutePath);
}
}
else if (option.isFilePath) {
return toAbsolutePath(value as string);
}
}
return value;
}
export function createRedirectedBuilderProgram(state: { program: Program | undefined; compilerOptions: CompilerOptions; }, configFileParsingDiagnostics: ReadonlyArray<Diagnostic>): BuilderProgram {
@@ -1181,7 +1250,7 @@ namespace ts {
* The builder that can handle the changes in program and iterate through changed file to emit the files
* The semantic diagnostics are cached per file and managed by clearing for the changed/affected files
*/
export interface EmitAndSemanticDiagnosticsBuilderProgram extends BuilderProgram {
export interface EmitAndSemanticDiagnosticsBuilderProgram extends SemanticDiagnosticsBuilderProgram {
/**
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
+762 -369
View File
File diff suppressed because it is too large Load Diff
+14 -12
View File
@@ -147,6 +147,12 @@ namespace ts {
category: Diagnostics.Basic_Options,
description: Diagnostics.Enable_incremental_compilation,
},
{
name: "locale",
type: "string",
category: Diagnostics.Advanced_Options,
description: Diagnostics.The_locale_used_when_displaying_messages_to_the_user_e_g_en_us
},
];
/* @internal */
@@ -239,6 +245,7 @@ namespace ts {
esnext: ModuleKind.ESNext
}),
affectsModuleResolution: true,
affectsEmit: true,
paramType: Diagnostics.KIND,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
@@ -584,6 +591,7 @@ namespace ts {
name: "esModuleInterop",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsEmit: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.Enables_emit_interoperability_between_CommonJS_and_ES_Modules_via_creation_of_namespace_objects_for_all_imports_Implies_allowSyntheticDefaultImports
@@ -698,12 +706,6 @@ namespace ts {
category: Diagnostics.Advanced_Options,
description: Diagnostics.Emit_a_UTF_8_Byte_Order_Mark_BOM_in_the_beginning_of_output_files
},
{
name: "locale",
type: "string",
category: Diagnostics.Advanced_Options,
description: Diagnostics.The_locale_used_when_displaying_messages_to_the_user_e_g_en_us
},
{
name: "newLine",
type: createMapFromTemplate({
@@ -969,7 +971,8 @@ namespace ts {
return typeAcquisition;
}
function getOptionNameMap(): OptionNameMap {
/* @internal */
export function getOptionNameMap(): OptionNameMap {
return optionNameMapCache || (optionNameMapCache = createOptionNameMap(optionDeclarations));
}
@@ -1022,8 +1025,7 @@ namespace ts {
}
}
/* @internal */
export interface OptionsBase {
interface OptionsBase {
[option: string]: CompilerOptionsValue | undefined;
}
@@ -1172,7 +1174,7 @@ namespace ts {
export interface ParsedBuildCommand {
buildOptions: BuildOptions;
projects: string[];
errors: ReadonlyArray<Diagnostic>;
errors: Diagnostic[];
}
/*@internal*/
@@ -2361,7 +2363,7 @@ namespace ts {
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, Extension.Json)) {
extendedConfigPath = `${extendedConfigPath}.json`;
if (!host.fileExists(extendedConfigPath)) {
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig));
return undefined;
}
}
@@ -2372,7 +2374,7 @@ namespace ts {
if (resolved.resolvedModule) {
return resolved.resolvedModule.resolvedFileName;
}
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig));
return undefined;
}
+1 -84
View File
@@ -1,7 +1,7 @@
namespace ts {
// WARNING: The script `configureNightly.ts` uses a regexp to parse out these values.
// If changing the text in this section, be sure to test `configureNightly` too.
export const versionMajorMinor = "3.5";
export const versionMajorMinor = "3.6";
/** The version of the TypeScript compiler release */
export const version = `${versionMajorMinor}.0-dev`;
}
@@ -1686,89 +1686,6 @@ namespace ts {
export type AnyFunction = (...args: never[]) => void;
export type AnyConstructor = new (...args: unknown[]) => unknown;
export namespace Debug {
export let currentAssertionLevel = AssertionLevel.None;
export let isDebugging = false;
export function shouldAssert(level: AssertionLevel): boolean {
return currentAssertionLevel >= level;
}
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): void {
if (!expression) {
if (verboseDebugInfo) {
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
}
fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert);
}
}
export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string): void {
if (a !== b) {
const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
fail(`Expected ${a} === ${b}. ${message}`);
}
}
export function assertLessThan(a: number, b: number, msg?: string): void {
if (a >= b) {
fail(`Expected ${a} < ${b}. ${msg || ""}`);
}
}
export function assertLessThanOrEqual(a: number, b: number): void {
if (a > b) {
fail(`Expected ${a} <= ${b}`);
}
}
export function assertGreaterThanOrEqual(a: number, b: number): void {
if (a < b) {
fail(`Expected ${a} >= ${b}`);
}
}
export function fail(message?: string, stackCrawlMark?: AnyFunction): never {
debugger;
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
if ((<any>Error).captureStackTrace) {
(<any>Error).captureStackTrace(e, stackCrawlMark || fail);
}
throw e;
}
export function assertDefined<T>(value: T | null | undefined, message?: string): T {
if (value === undefined || value === null) return fail(message);
return value;
}
export function assertEachDefined<T, A extends ReadonlyArray<T>>(value: A, message?: string): A {
for (const v of value) {
assertDefined(v, message);
}
return value;
}
export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never {
const detail = typeof member === "object" && "kind" in member && "pos" in member ? "SyntaxKind: " + showSyntaxKind(member as Node) : JSON.stringify(member);
return fail(`${message} ${detail}`, stackCrawlMark || assertNever);
}
export function getFunctionName(func: AnyFunction) {
if (typeof func !== "function") {
return "";
}
else if (func.hasOwnProperty("name")) {
return (<any>func).name;
}
else {
const text = Function.prototype.toString.call(func);
const match = /^function\s+([\w\$]+)\s*\(/.exec(text);
return match ? match[1] : "";
}
}
}
export function equateValues<T>(a: T, b: T) {
return a === b;
}
+261
View File
@@ -0,0 +1,261 @@
/* @internal */
namespace ts {
export namespace Debug {
export let currentAssertionLevel = AssertionLevel.None;
export let isDebugging = false;
export function shouldAssert(level: AssertionLevel): boolean {
return currentAssertionLevel >= level;
}
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): void {
if (!expression) {
if (verboseDebugInfo) {
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
}
fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert);
}
}
export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string): void {
if (a !== b) {
const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
fail(`Expected ${a} === ${b}. ${message}`);
}
}
export function assertLessThan(a: number, b: number, msg?: string): void {
if (a >= b) {
fail(`Expected ${a} < ${b}. ${msg || ""}`);
}
}
export function assertLessThanOrEqual(a: number, b: number): void {
if (a > b) {
fail(`Expected ${a} <= ${b}`);
}
}
export function assertGreaterThanOrEqual(a: number, b: number): void {
if (a < b) {
fail(`Expected ${a} >= ${b}`);
}
}
export function fail(message?: string, stackCrawlMark?: AnyFunction): never {
debugger;
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
if ((<any>Error).captureStackTrace) {
(<any>Error).captureStackTrace(e, stackCrawlMark || fail);
}
throw e;
}
export function assertDefined<T>(value: T | null | undefined, message?: string): T {
if (value === undefined || value === null) return fail(message);
return value;
}
export function assertEachDefined<T, A extends ReadonlyArray<T>>(value: A, message?: string): A {
for (const v of value) {
assertDefined(v, message);
}
return value;
}
export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never {
const detail = typeof member === "object" && "kind" in member && "pos" in member && formatSyntaxKind ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member);
return fail(`${message} ${detail}`, stackCrawlMark || assertNever);
}
export function getFunctionName(func: AnyFunction) {
if (typeof func !== "function") {
return "";
}
else if (func.hasOwnProperty("name")) {
return (<any>func).name;
}
else {
const text = Function.prototype.toString.call(func);
const match = /^function\s+([\w\$]+)\s*\(/.exec(text);
return match ? match[1] : "";
}
}
export function formatSymbol(symbol: Symbol): string {
return `{ name: ${unescapeLeadingUnderscores(symbol.escapedName)}; flags: ${formatSymbolFlags(symbol.flags)}; declarations: ${map(symbol.declarations, node => formatSyntaxKind(node.kind))} }`;
}
/**
* Formats an enum value as a string for debugging and debug assertions.
*/
export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) {
const members = getEnumMembers(enumObject);
if (value === 0) {
return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0";
}
if (isFlags) {
let result = "";
let remainingFlags = value;
for (let i = members.length - 1; i >= 0 && remainingFlags !== 0; i--) {
const [enumValue, enumName] = members[i];
if (enumValue !== 0 && (remainingFlags & enumValue) === enumValue) {
remainingFlags &= ~enumValue;
result = `${enumName}${result ? "|" : ""}${result}`;
}
}
if (remainingFlags === 0) {
return result;
}
}
else {
for (const [enumValue, enumName] of members) {
if (enumValue === value) {
return enumName;
}
}
}
return value.toString();
}
function getEnumMembers(enumObject: any) {
const result: [number, string][] = [];
for (const name in enumObject) {
const value = enumObject[name];
if (typeof value === "number") {
result.push([value, name]);
}
}
return stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0]));
}
export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
return formatEnum(kind, (<any>ts).SyntaxKind, /*isFlags*/ false);
}
export function formatNodeFlags(flags: NodeFlags | undefined): string {
return formatEnum(flags, (<any>ts).NodeFlags, /*isFlags*/ true);
}
export function formatModifierFlags(flags: ModifierFlags | undefined): string {
return formatEnum(flags, (<any>ts).ModifierFlags, /*isFlags*/ true);
}
export function formatTransformFlags(flags: TransformFlags | undefined): string {
return formatEnum(flags, (<any>ts).TransformFlags, /*isFlags*/ true);
}
export function formatEmitFlags(flags: EmitFlags | undefined): string {
return formatEnum(flags, (<any>ts).EmitFlags, /*isFlags*/ true);
}
export function formatSymbolFlags(flags: SymbolFlags | undefined): string {
return formatEnum(flags, (<any>ts).SymbolFlags, /*isFlags*/ true);
}
export function formatTypeFlags(flags: TypeFlags | undefined): string {
return formatEnum(flags, (<any>ts).TypeFlags, /*isFlags*/ true);
}
export function formatObjectFlags(flags: ObjectFlags | undefined): string {
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true);
}
export function failBadSyntaxKind(node: Node, message?: string): never {
return fail(
`${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`,
failBadSyntaxKind);
}
export const assertEachNode = shouldAssert(AssertionLevel.Normal)
? (nodes: Node[], test: (node: Node) => boolean, message?: string): void => assert(
test === undefined || every(nodes, test),
message || "Unexpected node.",
() => `Node array did not pass test '${getFunctionName(test)}'.`,
assertEachNode)
: noop;
export const assertNode = shouldAssert(AssertionLevel.Normal)
? (node: Node | undefined, test: ((node: Node | undefined) => boolean) | undefined, message?: string): void => assert(
test === undefined || test(node),
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node!.kind)} did not pass test '${getFunctionName(test!)}'.`,
assertNode)
: noop;
export const assertOptionalNode = shouldAssert(AssertionLevel.Normal)
? (node: Node, test: (node: Node) => boolean, message?: string): void => assert(
test === undefined || node === undefined || test(node),
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`,
assertOptionalNode)
: noop;
export const assertOptionalToken = shouldAssert(AssertionLevel.Normal)
? (node: Node, kind: SyntaxKind, message?: string): void => assert(
kind === undefined || node === undefined || node.kind === kind,
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} was not a '${formatSyntaxKind(kind)}' token.`,
assertOptionalToken)
: noop;
export const assertMissingNode = shouldAssert(AssertionLevel.Normal)
? (node: Node, message?: string): void => assert(
node === undefined,
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} was unexpected'.`,
assertMissingNode)
: noop;
let isDebugInfoEnabled = false;
/**
* Injects debug information into frequently used types.
*/
export function enableDebugInfo() {
if (isDebugInfoEnabled) return;
// Add additional properties in debug mode to assist with debugging.
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
__debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
});
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
__debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
__debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } },
__debugTypeToString: { value(this: Type) { return this.checker.typeToString(this); } },
});
const nodeConstructors = [
objectAllocator.getNodeConstructor(),
objectAllocator.getIdentifierConstructor(),
objectAllocator.getTokenConstructor(),
objectAllocator.getSourceFileConstructor()
];
for (const ctor of nodeConstructors) {
if (!ctor.prototype.hasOwnProperty("__debugKind")) {
Object.defineProperties(ctor.prototype, {
__debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } },
__debugNodeFlags: { get(this: Node) { return formatNodeFlags(this.flags); } },
__debugModifierFlags: { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } },
__debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
__debugIsParseTreeNode: { get(this: Node) { return isParseTreeNode(this); } },
__debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
__debugGetText: {
value(this: Node, includeTrivia?: boolean) {
if (nodeIsSynthesized(this)) return "";
const parseNode = getParseTreeNode(this);
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
}
}
});
}
}
isDebugInfoEnabled = true;
}
}
}
+70 -10
View File
@@ -655,7 +655,7 @@
"category": "Error",
"code": 1207
},
"Cannot compile namespaces when the '--isolatedModules' flag is provided.": {
"All files must be modules when the '--isolatedModules' flag is provided.": {
"category": "Error",
"code": 1208
},
@@ -1236,7 +1236,7 @@
"category": "Error",
"code": 2348
},
"Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.": {
"This expression is not callable.": {
"category": "Error",
"code": 2349
},
@@ -1244,7 +1244,7 @@
"category": "Error",
"code": 2350
},
"Cannot use 'new' with an expression whose type lacks a call or construct signature.": {
"This expression is not constructable.": {
"category": "Error",
"code": 2351
},
@@ -2621,6 +2621,38 @@
"category": "Error",
"code": 2754
},
"No constituent of type '{0}' is callable.": {
"category": "Error",
"code": 2755
},
"Not all constituents of type '{0}' are callable.": {
"category": "Error",
"code": 2756
},
"Type '{0}' has no call signatures.": {
"category": "Error",
"code": 2757
},
"Each member of the union type '{0}' has signatures, but none of those signatures are compatible with each other.": {
"category": "Error",
"code": 2758
},
"No constituent of type '{0}' is constructable.": {
"category": "Error",
"code": 2759
},
"Not all constituents of type '{0}' are constructable.": {
"category": "Error",
"code": 2760
},
"Type '{0}' has no construct signatures.": {
"category": "Error",
"code": 2761
},
"Each member of the union type '{0}' has construct signatures, but none of those signatures are compatible with each other.": {
"category": "Error",
"code": 2762
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
@@ -2963,6 +2995,10 @@
"category": "Error",
"code": 4104
},
"Private or protected member '{0}' cannot be accessed on a type parameter.": {
"category": "Error",
"code": 4105
},
"The current host does not support the '{0}' option.": {
"category": "Error",
@@ -3811,10 +3847,6 @@
"category": "Error",
"code": 6189
},
"Found 'package.json' at '{0}'. Package ID is '{1}'.": {
"category": "Message",
"code": 6190
},
"Whether to keep outdated console output in watch mode instead of clearing the screen.": {
"category": "Message",
"code": 6191
@@ -3923,6 +3955,18 @@
"category": "Message",
"code": 6217
},
"======== Module name '{0}' was successfully resolved to '{1}' with Package ID '{2}'. ========": {
"category": "Message",
"code": 6218
},
"======== Type reference directive '{0}' was successfully resolved to '{1}' with Package ID '{2}', primary: {3}. ========": {
"category": "Message",
"code": 6219
},
"'package.json' had a falsy '{0}' field.": {
"category": "Message",
"code": 6220
},
"Projects to reference": {
"category": "Message",
@@ -3948,7 +3992,7 @@
"category": "Error",
"code": 6306
},
"File '{0}' is not in project file list. Projects must list all files or use an 'include' pattern.": {
"File '{0}' is not listed within the file list of project '{1}'. Projects must list all files or use an 'include' pattern.": {
"category": "Error",
"code": 6307
},
@@ -4284,6 +4328,14 @@
"category": "Error",
"code": 7052
},
"Element implicitly has an 'any' type because expression of type '{0}' can't be used to index type '{1}'.": {
"category": "Error",
"code": 7053
},
"No index signature with a parameter of type '{0}' was found on type '{1}'.": {
"category": "Error",
"code": 7054
},
"You cannot rename this element.": {
"category": "Error",
"code": 8000
@@ -4974,14 +5026,18 @@
"category": "Message",
"code": 95079
},
"Add 'const' to unresolved variable": {
"Infer 'this' type of '{0}' from usage": {
"category": "Message",
"code": 95080
},
"Add 'const' to all unresolved variables": {
"Add 'const' to unresolved variable": {
"category": "Message",
"code": 95081
},
"Add 'const' to all unresolved variables": {
"category": "Message",
"code": 95082
},
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
"category": "Error",
"code": 18004
@@ -4993,5 +5049,9 @@
"Classes may not have a field named 'constructor'.": {
"category": "Error",
"code": 18006
},
"JSX expressions may not use the comma operator. Did you mean to write an array?": {
"category": "Error",
"code": 18007
}
}
+57 -17
View File
@@ -249,14 +249,16 @@ namespace ts {
};
function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) {
let buildInfoDirectory: string | undefined;
if (buildInfoPath && sourceFileOrBundle && isBundle(sourceFileOrBundle)) {
buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
bundleBuildInfo = {
commonSourceDirectory: host.getCommonSourceDirectory(),
sourceFiles: sourceFileOrBundle.sourceFiles.map(file => file.fileName)
commonSourceDirectory: relativeToBuildInfo(host.getCommonSourceDirectory()),
sourceFiles: sourceFileOrBundle.sourceFiles.map(file => relativeToBuildInfo(getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory())))
};
}
emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath);
emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath);
emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath, relativeToBuildInfo);
emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath, relativeToBuildInfo);
emitBuildInfo(bundleBuildInfo, buildInfoPath);
if (!emitSkipped && emittedFilesList) {
@@ -278,13 +280,16 @@ namespace ts {
emittedFilesList.push(declarationMapPath);
}
}
function relativeToBuildInfo(path: string) {
return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory!, path, host.getCanonicalFileName));
}
}
function emitBuildInfo(bundle: BundleBuildInfo | undefined, buildInfoPath: string | undefined) {
// Write build information if applicable
if (!buildInfoPath || targetSourceFile || emitSkipped) return;
const program = host.getProgramBuildInfo();
if (!bundle && !program) return;
if (host.isEmitBlocked(buildInfoPath) || compilerOptions.noEmit) {
emitSkipped = true;
return;
@@ -292,7 +297,11 @@ namespace ts {
writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText({ bundle, program, version }), /*writeByteOrderMark*/ false);
}
function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle | undefined, jsFilePath: string | undefined, sourceMapFilePath: string | undefined) {
function emitJsFileOrBundle(
sourceFileOrBundle: SourceFile | Bundle | undefined,
jsFilePath: string | undefined,
sourceMapFilePath: string | undefined,
relativeToBuildInfo: (path: string) => string) {
if (!sourceFileOrBundle || emitOnlyDtsFiles || !jsFilePath) {
return;
}
@@ -315,7 +324,8 @@ namespace ts {
inlineSourceMap: compilerOptions.inlineSourceMap,
inlineSources: compilerOptions.inlineSources,
extendedDiagnostics: compilerOptions.extendedDiagnostics,
writeBundleFileInfo: !!bundleBuildInfo
writeBundleFileInfo: !!bundleBuildInfo,
relativeToBuildInfo
};
// Create a printer to print the nodes
@@ -336,7 +346,11 @@ namespace ts {
if (bundleBuildInfo) bundleBuildInfo.js = printer.bundleFileInfo;
}
function emitDeclarationFileOrBundle(sourceFileOrBundle: SourceFile | Bundle | undefined, declarationFilePath: string | undefined, declarationMapPath: string | undefined) {
function emitDeclarationFileOrBundle(
sourceFileOrBundle: SourceFile | Bundle | undefined,
declarationFilePath: string | undefined,
declarationMapPath: string | undefined,
relativeToBuildInfo: (path: string) => string) {
if (!sourceFileOrBundle || !(declarationFilePath && !isInJSFile(sourceFileOrBundle))) {
return;
}
@@ -367,7 +381,8 @@ namespace ts {
extendedDiagnostics: compilerOptions.extendedDiagnostics,
onlyPrintJsDocStyle: true,
writeBundleFileInfo: !!bundleBuildInfo,
recordInternalSection: !!bundleBuildInfo
recordInternalSection: !!bundleBuildInfo,
relativeToBuildInfo
};
const declarationPrinter = createPrinter(printerOptions, {
@@ -614,10 +629,14 @@ namespace ts {
getNewLine(): string;
}
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo): ReadonlyArray<SourceFile> {
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: EmitUsingBuildInfoHost): ReadonlyArray<SourceFile> {
const sourceFiles = bundle.sourceFiles.map(fileName => {
const sourceFile = createNode(SyntaxKind.SourceFile, 0, 0) as SourceFile;
sourceFile.fileName = fileName;
sourceFile.fileName = getRelativePathFromDirectory(
host.getCurrentDirectory(),
getNormalizedAbsolutePath(fileName, buildInfoDirectory),
!host.useCaseSensitiveFileNames()
);
sourceFile.text = "";
sourceFile.statements = createNodeArray();
return sourceFile;
@@ -638,7 +657,12 @@ namespace ts {
}
/*@internal*/
export function emitUsingBuildInfo(config: ParsedCommandLine, host: EmitUsingBuildInfoHost, getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined): EmitUsingBuildInfoResult {
export function emitUsingBuildInfo(
config: ParsedCommandLine,
host: EmitUsingBuildInfoHost,
getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined,
customTransformers?: CustomTransformers
): EmitUsingBuildInfoResult {
const { buildInfoPath, jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath } = getOutputPathsForBundle(config.options, /*forceDtsPaths*/ false);
const buildInfoText = host.readFile(Debug.assertDefined(buildInfoPath));
if (!buildInfoText) return buildInfoPath!;
@@ -656,6 +680,7 @@ namespace ts {
const buildInfo = getBuildInfo(buildInfoText);
if (!buildInfo.bundle || !buildInfo.bundle.js || (declarationText && !buildInfo.bundle.dts)) return buildInfoPath!;
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath!, host.getCurrentDirectory()));
const ownPrependInput = createInputFiles(
jsFileText,
declarationText!,
@@ -671,11 +696,11 @@ namespace ts {
);
const outputFiles: OutputFile[] = [];
const prependNodes = createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f));
const sourceFilesForJsEmit = createSourceFilesFromBundleBuildInfo(buildInfo.bundle);
const sourceFilesForJsEmit = createSourceFilesFromBundleBuildInfo(buildInfo.bundle, buildInfoDirectory, host);
const emitHost: EmitHost = {
getPrependNodes: memoize(() => [...prependNodes, ownPrependInput]),
getCanonicalFileName: host.getCanonicalFileName,
getCommonSourceDirectory: () => buildInfo.bundle!.commonSourceDirectory,
getCommonSourceDirectory: () => getNormalizedAbsolutePath(buildInfo.bundle!.commonSourceDirectory, buildInfoDirectory),
getCompilerOptions: () => config.options,
getCurrentDirectory: () => host.getCurrentDirectory(),
getNewLine: () => host.getNewLine(),
@@ -721,9 +746,15 @@ namespace ts {
fileExists: f => host.fileExists(f),
directoryExists: host.directoryExists && (f => host.directoryExists!(f)),
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
getProgramBuildInfo: returnUndefined
getProgramBuildInfo: returnUndefined,
getSourceFileFromReference: returnUndefined,
};
emitFiles(notImplementedResolver, emitHost, /*targetSourceFile*/ undefined, getTransformers(config.options), /*emitOnlyDtsFiles*/ false);
emitFiles(
notImplementedResolver,
emitHost,
/*targetSourceFile*/ undefined,
getTransformers(config.options, customTransformers)
);
return outputFiles;
}
@@ -765,6 +796,7 @@ namespace ts {
let write = writeBase;
let isOwnFileEmit: boolean;
const bundleFileInfo = printerOptions.writeBundleFileInfo ? { sections: [] } as BundleFileInfo : undefined;
const relativeToBuildInfo = bundleFileInfo ? Debug.assertDefined(printerOptions.relativeToBuildInfo) : undefined;
const recordInternalSection = printerOptions.recordInternalSection;
let sourceFileTextPos = 0;
let sourceFileTextKind: BundleFileTextLikeKind = BundleFileSectionKind.Text;
@@ -933,7 +965,13 @@ namespace ts {
if (prepend.oldFileOfCurrentEmit) bundleFileInfo.sections.push(...newSections);
else {
newSections.forEach(section => Debug.assert(isBundleFileTextLike(section)));
bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prepend, data: (prepend as UnparsedSource).fileName, texts: newSections as BundleFileTextLike[] });
bundleFileInfo.sections.push({
pos,
end: writer.getTextPos(),
kind: BundleFileSectionKind.Prepend,
data: relativeToBuildInfo!((prepend as UnparsedSource).fileName),
texts: newSections as BundleFileTextLike[]
});
}
}
}
@@ -4527,6 +4565,8 @@ namespace ts {
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return generateNameForMethodOrAccessor(<MethodDeclaration | AccessorDeclaration>node);
case SyntaxKind.ComputedPropertyName:
return makeTempVariableName(TempFlags.Auto, /*reserveInNestedScopes*/ true);
default:
return makeTempVariableName(TempFlags.Auto);
}
+60 -9
View File
@@ -1544,8 +1544,8 @@ namespace ts {
export function createIf(expression: Expression, thenStatement: Statement, elseStatement?: Statement) {
const node = <IfStatement>createSynthesizedNode(SyntaxKind.IfStatement);
node.expression = expression;
node.thenStatement = thenStatement;
node.elseStatement = elseStatement;
node.thenStatement = asEmbeddedStatement(thenStatement);
node.elseStatement = asEmbeddedStatement(elseStatement);
return node;
}
@@ -1559,7 +1559,7 @@ namespace ts {
export function createDo(statement: Statement, expression: Expression) {
const node = <DoStatement>createSynthesizedNode(SyntaxKind.DoStatement);
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
node.expression = expression;
return node;
}
@@ -1574,7 +1574,7 @@ namespace ts {
export function createWhile(expression: Expression, statement: Statement) {
const node = <WhileStatement>createSynthesizedNode(SyntaxKind.WhileStatement);
node.expression = expression;
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -1590,7 +1590,7 @@ namespace ts {
node.initializer = initializer;
node.condition = condition;
node.incrementor = incrementor;
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -1607,7 +1607,7 @@ namespace ts {
const node = <ForInStatement>createSynthesizedNode(SyntaxKind.ForInStatement);
node.initializer = initializer;
node.expression = expression;
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -1624,7 +1624,7 @@ namespace ts {
node.awaitModifier = awaitModifier;
node.initializer = initializer;
node.expression = expression;
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -1676,7 +1676,7 @@ namespace ts {
export function createWith(expression: Expression, statement: Statement) {
const node = <WithStatement>createSynthesizedNode(SyntaxKind.WithStatement);
node.expression = expression;
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -1704,7 +1704,7 @@ namespace ts {
export function createLabel(label: string | Identifier, statement: Statement) {
const node = <LabeledStatement>createSynthesizedNode(SyntaxKind.LabeledStatement);
node.label = asName(label);
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -2204,6 +2204,13 @@ namespace ts {
return tag;
}
/** @internal */
export function createJSDocThisTag(typeExpression?: JSDocTypeExpression): JSDocThisTag {
const tag = createJSDocTag<JSDocThisTag>(SyntaxKind.JSDocThisTag, "this");
tag.typeExpression = typeExpression;
return tag;
}
/* @internal */
export function createJSDocParamTag(name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, comment?: string): JSDocParameterTag {
const tag = createJSDocTag<JSDocParameterTag>(SyntaxKind.JSDocParameterTag, "param");
@@ -2657,6 +2664,7 @@ namespace ts {
valuesHelper,
readHelper,
spreadHelper,
spreadArraysHelper,
restHelper,
decorateHelper,
metadataHelper,
@@ -3072,6 +3080,12 @@ namespace ts {
return typeof value === "number" ? createToken(value) : value;
}
function asEmbeddedStatement<T extends Node>(statement: T): T | EmptyStatement;
function asEmbeddedStatement<T extends Node>(statement: T | undefined): T | EmptyStatement | undefined;
function asEmbeddedStatement<T extends Node>(statement: T | undefined): T | EmptyStatement | undefined {
return statement && isNotEmittedStatement(statement) ? setTextRange(setOriginalNode(createEmptyStatement(), statement), statement) : statement;
}
/**
* Clears any EmitNode entries from parse-tree nodes.
* @param sourceFile A source file.
@@ -3118,6 +3132,18 @@ namespace ts {
return node.emitNode;
}
/**
* Sets `EmitFlags.NoComments` on a node and removes any leading and trailing synthetic comments.
* @internal
*/
export function removeAllComments<T extends Node>(node: T): T {
const emitNode = getOrCreateEmitNode(node);
emitNode.flags |= EmitFlags.NoComments;
emitNode.leadingComments = undefined;
emitNode.trailingComments = undefined;
return node;
}
export function setTextRange<T extends TextRange>(range: T, location: TextRange | undefined): T {
if (location) {
range.pos = location.pos;
@@ -3693,6 +3719,31 @@ namespace ts {
);
}
export const spreadArraysHelper: UnscopedEmitHelper = {
name: "typescript:spreadArrays",
scoped: false,
text: `
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};`
};
export function createSpreadArraysHelper(context: TransformationContext, argumentList: ReadonlyArray<Expression>, location?: TextRange) {
context.requestEmitHelper(spreadArraysHelper);
return setTextRange(
createCall(
getHelperName("__spreadArrays"),
/*typeArguments*/ undefined,
argumentList
),
location
);
}
// Utilities
export function createForOfBindingStatement(node: ForInitializer, boundValue: Expression): Statement {
+76 -88
View File
@@ -16,12 +16,23 @@ namespace ts {
push(value: T): void;
}
function withPackageId(packageId: PackageId | undefined, r: PathAndExtension | undefined): Resolved | undefined {
function withPackageId(packageInfo: PackageJsonInfo | undefined, r: PathAndExtension | undefined): Resolved | undefined {
let packageId: PackageId | undefined;
if (r && packageInfo) {
const packageJsonContent = packageInfo.packageJsonContent as PackageJson;
if (typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string") {
packageId = {
name: packageJsonContent.name,
subModuleName: r.path.slice(packageInfo.packageDirectory.length + directorySeparator.length),
version: packageJsonContent.version
};
}
}
return r && { path: r.path, extension: r.ext, packageId };
}
function noPackageId(r: PathAndExtension | undefined): Resolved | undefined {
return withPackageId(/*packageId*/ undefined, r);
return withPackageId(/*packageInfo*/ undefined, r);
}
function removeIgnoredPackageId(r: Resolved | undefined): PathAndExtension | undefined {
@@ -130,7 +141,15 @@ namespace ts {
function readPackageJsonPathField<K extends "typings" | "types" | "main" | "tsconfig">(jsonContent: PackageJson, fieldName: K, baseDirectory: string, state: ModuleResolutionState): PackageJson[K] | undefined {
const fileName = readPackageJsonField(jsonContent, fieldName, "string", state);
if (fileName === undefined) return;
if (fileName === undefined) {
return;
}
if (!fileName) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_had_a_falsy_0_field, fieldName);
}
return;
}
const path = normalizePath(combinePaths(baseDirectory, fileName));
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, fileName, path);
@@ -307,7 +326,12 @@ namespace ts {
const { fileName, packageId } = resolved;
const resolvedFileName = options.preserveSymlinks ? fileName : realPath(fileName, host, traceEnabled);
if (traceEnabled) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFileName, primary);
if (packageId) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_with_Package_ID_2_primary_Colon_3, typeReferenceDirectiveName, resolvedFileName, packageIdToString(packageId), primary);
}
else {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFileName, primary);
}
}
resolvedTypeReferenceDirective = { primary, resolvedFileName, packageId, isExternalLibraryImport: pathContainsNodeModules(fileName) };
}
@@ -663,7 +687,12 @@ namespace ts {
if (traceEnabled) {
if (result.resolvedModule) {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
if (result.resolvedModule.packageId) {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1_with_Package_ID_2, moduleName, result.resolvedModule.resolvedFileName, packageIdToString(result.resolvedModule.packageId));
}
else {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
}
}
else {
trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName);
@@ -968,10 +997,9 @@ namespace ts {
}
const resolvedFromFile = loadModuleFromFile(extensions, candidate, onlyRecordFailures, state);
if (resolvedFromFile) {
const nm = considerPackageJson ? parseNodeModuleFromPath(resolvedFromFile) : undefined;
const packageInfo = nm && getPackageJsonInfo(nm.packageDirectory, nm.subModuleName, /*onlyRecordFailures*/ false, state);
const packageId = packageInfo && packageInfo.packageId;
return withPackageId(packageId, resolvedFromFile);
const packageDirectory = considerPackageJson ? parseNodeModuleFromPath(resolvedFromFile) : undefined;
const packageInfo = packageDirectory ? getPackageJsonInfo(packageDirectory, /*onlyRecordFailures*/ false, state) : undefined;
return withPackageId(packageInfo, resolvedFromFile);
}
}
if (!onlyRecordFailures) {
@@ -998,13 +1026,12 @@ namespace ts {
* (Not neeeded for `loadModuleFromNodeModules` as that looks up the `package.json` as part of resolution.)
*
* packageDirectory is the directory of the package itself.
* subModuleName is the path within the package.
* For `blah/node_modules/foo/index.d.ts` this is { packageDirectory: "foo", subModuleName: "index.d.ts" }. (Part before "/node_modules/" is ignored.)
* For `/node_modules/foo/bar.d.ts` this is { packageDirectory: "foo", subModuleName": "bar/index.d.ts" }.
* For `/node_modules/@types/foo/bar/index.d.ts` this is { packageDirectory: "@types/foo", subModuleName: "bar/index.d.ts" }.
* For `/node_modules/foo/bar/index.d.ts` this is { packageDirectory: "foo", subModuleName": "bar/index.d.ts" }.
* For `blah/node_modules/foo/index.d.ts` this is packageDirectory: "foo"
* For `/node_modules/foo/bar.d.ts` this is packageDirectory: "foo"
* For `/node_modules/@types/foo/bar/index.d.ts` this is packageDirectory: "@types/foo"
* For `/node_modules/foo/bar/index.d.ts` this is packageDirectory: "foo"
*/
function parseNodeModuleFromPath(resolved: PathAndExtension): { packageDirectory: string, subModuleName: string } | undefined {
function parseNodeModuleFromPath(resolved: PathAndExtension): string | undefined {
const path = normalizePath(resolved.path);
const idx = path.lastIndexOf(nodeModulesPathPart);
if (idx === -1) {
@@ -1016,9 +1043,7 @@ namespace ts {
if (path.charCodeAt(indexAfterNodeModules) === CharacterCodes.at) {
indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName);
}
const packageDirectory = path.slice(0, indexAfterPackageName);
const subModuleName = removeExtension(path.slice(indexAfterPackageName + 1), resolved.ext) + Extension.Dts;
return { packageDirectory, subModuleName };
return path.slice(0, indexAfterPackageName);
}
function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number): number {
@@ -1026,19 +1051,6 @@ namespace ts {
return nextSeparatorIndex === -1 ? prevSeparatorIndex : nextSeparatorIndex;
}
function addExtensionAndIndex(path: string): string {
if (path === "") {
return "index.d.ts";
}
if (endsWith(path, ".d.ts")) {
return path;
}
if (path === "index" || endsWith(path, "/index")) {
return path + ".d.ts";
}
return path + "/index.d.ts";
}
function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
return noPackageId(loadModuleFromFile(extensions, candidate, onlyRecordFailures, state));
}
@@ -1119,61 +1131,29 @@ namespace ts {
}
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) {
const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, "", onlyRecordFailures, state) : undefined;
const packageId = packageInfo && packageInfo.packageId;
const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, onlyRecordFailures, state) : undefined;
const packageJsonContent = packageInfo && packageInfo.packageJsonContent;
const versionPaths = packageInfo && packageInfo.versionPaths;
return withPackageId(packageId, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths));
return withPackageId(packageInfo, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths));
}
interface PackageJsonInfo {
packageJsonContent: PackageJsonPathFields | undefined;
packageId: PackageId | undefined;
packageDirectory: string;
packageJsonContent: PackageJsonPathFields;
versionPaths: VersionPaths | undefined;
}
function getPackageJsonInfo(packageDirectory: string, subModuleName: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
function getPackageJsonInfo(packageDirectory: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
const { host, traceEnabled } = state;
const directoryExists = !onlyRecordFailures && directoryProbablyExists(packageDirectory, host);
const packageJsonPath = combinePaths(packageDirectory, "package.json");
if (directoryExists && host.fileExists(packageJsonPath)) {
const packageJsonContent = readJson(packageJsonPath, host) as PackageJson;
if (subModuleName === "") { // looking up the root - need to handle types/typings/main redirects for subModuleName
const path = readPackageJsonTypesFields(packageJsonContent, packageDirectory, state);
if (typeof path === "string") {
subModuleName = addExtensionAndIndex(path.substring(packageDirectory.length + 1));
}
else {
const jsPath = readPackageJsonMainField(packageJsonContent, packageDirectory, state);
if (typeof jsPath === "string" && jsPath.length > packageDirectory.length) {
const potentialSubModule = jsPath.substring(packageDirectory.length + 1);
subModuleName = (forEach(supportedJSExtensions, extension =>
tryRemoveExtension(potentialSubModule, extension)) || potentialSubModule) + Extension.Dts;
}
else {
subModuleName = "index.d.ts";
}
}
}
if (!endsWith(subModuleName, Extension.Dts)) {
subModuleName = addExtensionAndIndex(subModuleName);
}
const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state);
const packageId: PackageId | undefined = typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string"
? { name: packageJsonContent.name, subModuleName, version: packageJsonContent.version }
: undefined;
if (traceEnabled) {
if (packageId) {
trace(host, Diagnostics.Found_package_json_at_0_Package_ID_is_1, packageJsonPath, packageIdToString(packageId));
}
else {
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
return { packageJsonContent, packageId, versionPaths };
const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state);
return { packageDirectory, packageJsonContent, versionPaths };
}
else {
if (directoryExists && traceEnabled) {
@@ -1328,27 +1308,36 @@ namespace ts {
const candidate = normalizePath(combinePaths(nodeModulesDirectory, moduleName));
// First look for a nested package.json, as in `node_modules/foo/bar/package.json`.
let packageJsonContent: PackageJsonPathFields | undefined;
let packageId: PackageId | undefined;
let versionPaths: VersionPaths | undefined;
const packageInfo = getPackageJsonInfo(candidate, "", !nodeModulesDirectoryExists, state);
let packageInfo = getPackageJsonInfo(candidate, !nodeModulesDirectoryExists, state);
if (packageInfo) {
({ packageJsonContent, packageId, versionPaths } = packageInfo);
const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state);
if (fromFile) {
return noPackageId(fromFile);
}
const fromDirectory = loadNodeModuleFromDirectoryWorker(extensions, candidate, !nodeModulesDirectoryExists, state, packageJsonContent, versionPaths);
return withPackageId(packageId, fromDirectory);
const fromDirectory = loadNodeModuleFromDirectoryWorker(
extensions,
candidate,
!nodeModulesDirectoryExists,
state,
packageInfo.packageJsonContent,
packageInfo.versionPaths
);
return withPackageId(packageInfo, fromDirectory);
}
const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => {
const pathAndExtension =
loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) ||
loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths);
return withPackageId(packageId, pathAndExtension);
loadNodeModuleFromDirectoryWorker(
extensions,
candidate,
onlyRecordFailures,
state,
packageInfo && packageInfo.packageJsonContent,
packageInfo && packageInfo.versionPaths
);
return withPackageId(packageInfo, pathAndExtension);
};
const { packageName, rest } = parsePackageName(moduleName);
@@ -1356,14 +1345,13 @@ namespace ts {
const packageDirectory = combinePaths(nodeModulesDirectory, packageName);
// Don't use a "types" or "main" from here because we're not loading the root, but a subdirectory -- just here for the packageId and path mappings.
const packageInfo = getPackageJsonInfo(packageDirectory, rest, !nodeModulesDirectoryExists, state);
if (packageInfo) ({ packageId, versionPaths } = packageInfo);
if (versionPaths) {
packageInfo = getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists, state);
if (packageInfo && packageInfo.versionPaths) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, versionPaths.version, version, rest);
trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, packageInfo.versionPaths.version, version, rest);
}
const packageDirectoryExists = nodeModulesDirectoryExists && directoryProbablyExists(packageDirectory, state.host);
const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, versionPaths.paths, loader, !packageDirectoryExists, state);
const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, packageInfo.versionPaths.paths, loader, !packageDirectoryExists, state);
if (fromPaths) {
return fromPaths.value;
}
@@ -1499,8 +1487,8 @@ namespace ts {
}
/**
* LSHost may load a module from a global cache of typings.
* This is the minumum code needed to expose that functionality; the rest is in LSHost.
* A host may load a module from a global cache of typings.
* This is the minumum code needed to expose that functionality; the rest is in the host.
*/
/* @internal */
export function loadModuleFromGlobalCache(moduleName: string, projectName: string | undefined, compilerOptions: CompilerOptions, host: ModuleResolutionHost, globalCache: string): ResolvedModuleWithFailedLookupLocations {
+3 -3
View File
@@ -175,9 +175,9 @@ namespace ts.moduleSpecifiers {
function discoverProbableSymlinks(files: ReadonlyArray<SourceFile>, getCanonicalFileName: GetCanonicalFileName, cwd: string): ReadonlyMap<string> {
const result = createMap<string>();
const symlinks = mapDefined(files, sf =>
sf.resolvedModules && firstDefinedIterator(sf.resolvedModules.values(), res =>
res && res.originalPath && res.resolvedFileName !== res.originalPath ? [res.resolvedFileName, res.originalPath] : undefined));
const symlinks = flatten<readonly [string, string]>(mapDefined(files, sf =>
sf.resolvedModules && compact(arrayFrom(mapIterator(sf.resolvedModules.values(), res =>
res && res.originalPath && res.resolvedFileName !== res.originalPath ? [res.resolvedFileName, res.originalPath] as const : undefined)))));
for (const [resolvedPath, originalPath] of symlinks) {
const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedPath, originalPath, cwd, getCanonicalFileName);
result.set(commonOriginal, commonResolved);
+26 -5
View File
@@ -4430,14 +4430,18 @@ namespace ts {
if (token() !== SyntaxKind.CloseBraceToken) {
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
node.expression = parseAssignmentExpressionOrHigher();
// Only an AssignmentExpression is valid here per the JSX spec,
// but we can unambiguously parse a comma sequence and provide
// a better error message in grammar checking.
node.expression = parseExpression();
}
if (inExpressionContext) {
parseExpected(SyntaxKind.CloseBraceToken);
}
else {
parseExpected(SyntaxKind.CloseBraceToken, /*message*/ undefined, /*shouldAdvance*/ false);
scanJsxText();
if (parseExpected(SyntaxKind.CloseBraceToken, /*message*/ undefined, /*shouldAdvance*/ false)) {
scanJsxText();
}
}
return finishNode(node);
@@ -6427,6 +6431,7 @@ namespace ts {
BeginningOfLine,
SawAsterisk,
SavingComments,
SavingBackticks, // NOTE: Only used when parsing tag comments
}
const enum PropertyLikeParse {
@@ -6687,18 +6692,23 @@ namespace ts {
case SyntaxKind.NewLineTrivia:
if (state >= JSDocState.SawAsterisk) {
state = JSDocState.BeginningOfLine;
// don't use pushComment here because we want to keep the margin unchanged
comments.push(scanner.getTokenText());
}
indent = 0;
break;
case SyntaxKind.AtToken:
if (state === JSDocState.SavingBackticks) {
comments.push(scanner.getTokenText());
break;
}
scanner.setTextPos(scanner.getTextPos() - 1);
// falls through
case SyntaxKind.EndOfFileToken:
// Done
break loop;
case SyntaxKind.WhitespaceTrivia:
if (state === JSDocState.SavingComments) {
if (state === JSDocState.SavingComments || state === JSDocState.SavingBackticks) {
pushComment(scanner.getTokenText());
}
else {
@@ -6720,6 +6730,15 @@ namespace ts {
}
pushComment(scanner.getTokenText());
break;
case SyntaxKind.BacktickToken:
if (state === JSDocState.SavingBackticks) {
state = JSDocState.SavingComments;
}
else {
state = JSDocState.SavingBackticks;
}
pushComment(scanner.getTokenText());
break;
case SyntaxKind.AsteriskToken:
if (state === JSDocState.BeginningOfLine) {
// leading asterisks start recording on the *next* (non-whitespace) token
@@ -6730,7 +6749,9 @@ namespace ts {
// record the * as a comment
// falls through
default:
state = JSDocState.SavingComments; // leading identifiers start recording as well
if (state !== JSDocState.SavingBackticks) {
state = JSDocState.SavingComments; // leading identifiers start recording as well
}
pushComment(scanner.getTokenText());
break;
}
+11 -7
View File
@@ -1395,7 +1395,7 @@ namespace ts {
const filePath = newSourceFile.path;
addFileToFilesByName(newSourceFile, filePath, newSourceFile.resolvedPath);
// Set the file as found during node modules search if it was found that way in old progra,
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(filePath)!)) {
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(newSourceFile.resolvedPath)!)) {
sourceFilesFoundSearchingNodeModules.set(filePath, true);
}
}
@@ -1442,7 +1442,8 @@ namespace ts {
},
...(host.directoryExists ? { directoryExists: f => host.directoryExists!(f) } : {}),
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
getProgramBuildInfo: () => program.getProgramBuildInfo && program.getProgramBuildInfo()
getProgramBuildInfo: () => program.getProgramBuildInfo && program.getProgramBuildInfo(),
getSourceFileFromReference: (file, ref) => program.getSourceFileFromReference(file, ref),
};
}
@@ -2127,7 +2128,7 @@ namespace ts {
}
/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
function getSourceFileFromReference(referencingFile: SourceFile, ref: FileReference): SourceFile | undefined {
function getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined {
return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), fileName => filesByName.get(toPath(fileName)) || undefined);
}
@@ -2232,7 +2233,10 @@ namespace ts {
if (isRedirect) {
inputName = getProjectReferenceRedirect(fileName) || fileName;
}
if (getNormalizedAbsolutePath(checkedName, currentDirectory) !== getNormalizedAbsolutePath(inputName, currentDirectory)) {
// Check if it differs only in drive letters its ok to ignore that error:
const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory);
const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(inputName, currentDirectory);
if (checkedAbsolutePath !== inputAbsolutePath) {
reportFileNamesDifferOnlyInCasingError(inputName, checkedName, refFile, refPos, refEnd);
}
}
@@ -2770,7 +2774,7 @@ namespace ts {
// Ignore file that is not emitted
if (!sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect)) continue;
if (rootPaths.indexOf(file.path) === -1) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern, file.fileName));
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, file.fileName, options.configFilePath || ""));
}
}
}
@@ -2855,10 +2859,10 @@ namespace ts {
createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target");
}
const firstNonExternalModuleSourceFile = find(files, f => !isExternalModule(f) && !f.isDeclarationFile && f.scriptKind !== ScriptKind.JSON);
const firstNonExternalModuleSourceFile = find(files, f => !isExternalModule(f) && !isSourceFileJS(f) && !f.isDeclarationFile && f.scriptKind !== ScriptKind.JSON);
if (firstNonExternalModuleSourceFile) {
const span = getErrorSpanForNode(firstNonExternalModuleSourceFile, firstNonExternalModuleSourceFile);
programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_namespaces_when_the_isolatedModules_flag_is_provided));
programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length, Diagnostics.All_files_must_be_modules_when_the_isolatedModules_flag_is_provided));
}
}
else if (firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget.ES2015 && options.module === ModuleKind.None) {
+48 -36
View File
@@ -51,9 +51,11 @@ namespace ts {
getCachedDirectoryStructureHost(): CachedDirectoryStructureHost | undefined;
projectName?: string;
getGlobalCache?(): string | undefined;
globalCacheResolutionModuleName?(externalModuleName: string): string;
writeLog(s: string): void;
maxNumberOfFilesToIterateForInvalidation?: number;
getCurrentProgram(): Program | undefined;
fileIsOpen(filePath: Path): boolean;
}
interface DirectoryWatchesOfFailedLookup {
@@ -75,6 +77,41 @@ namespace ts {
return some(ignoredPaths, searchPath => stringContains(path, searchPath));
}
/**
* Filter out paths like
* "/", "/user", "/user/username", "/user/username/folderAtRoot",
* "c:/", "c:/users", "c:/users/username", "c:/users/username/folderAtRoot", "c:/folderAtRoot"
* @param dirPath
*/
export function canWatchDirectory(dirPath: Path) {
const rootLength = getRootLength(dirPath);
if (dirPath.length === rootLength) {
// Ignore "/", "c:/"
return false;
}
const nextDirectorySeparator = dirPath.indexOf(directorySeparator, rootLength);
if (nextDirectorySeparator === -1) {
// ignore "/user", "c:/users" or "c:/folderAtRoot"
return false;
}
if (dirPath.charCodeAt(0) !== CharacterCodes.slash &&
dirPath.substr(rootLength, nextDirectorySeparator).search(/users/i) === -1) {
// Paths like c:/folderAtRoot/subFolder are allowed
return true;
}
for (let searchIndex = nextDirectorySeparator + 1, searchLevels = 2; searchLevels > 0; searchLevels--) {
searchIndex = dirPath.indexOf(directorySeparator, searchIndex) + 1;
if (searchIndex === 0) {
// Folder isnt at expected minimun levels
return false;
}
}
return true;
}
export const maxNumberOfFilesToIterateForInvalidation = 256;
type GetResolutionWithResolvedFileName<T extends ResolutionWithFailedLookupLocations = ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName = ResolutionWithResolvedFileName> =
@@ -234,7 +271,12 @@ namespace ts {
if (globalCache !== undefined && !isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTS(primaryResult.resolvedModule.extension))) {
// create different collection of failed lookup locations for second pass
// if it will fail and we've already found something during the first pass - we don't want to pollute its results
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, resolutionHost.projectName, compilerOptions, host, globalCache);
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(
Debug.assertDefined(resolutionHost.globalCacheResolutionModuleName)(moduleName),
resolutionHost.projectName,
compilerOptions,
host,
globalCache);
if (resolvedModule) {
return { resolvedModule, failedLookupLocations: addRange(primaryResult.failedLookupLocations as string[], failedLookupLocations) };
}
@@ -372,41 +414,6 @@ namespace ts {
return endsWith(dirPath, "/node_modules/@types");
}
/**
* Filter out paths like
* "/", "/user", "/user/username", "/user/username/folderAtRoot",
* "c:/", "c:/users", "c:/users/username", "c:/users/username/folderAtRoot", "c:/folderAtRoot"
* @param dirPath
*/
function canWatchDirectory(dirPath: Path) {
const rootLength = getRootLength(dirPath);
if (dirPath.length === rootLength) {
// Ignore "/", "c:/"
return false;
}
const nextDirectorySeparator = dirPath.indexOf(directorySeparator, rootLength);
if (nextDirectorySeparator === -1) {
// ignore "/user", "c:/users" or "c:/folderAtRoot"
return false;
}
if (dirPath.charCodeAt(0) !== CharacterCodes.slash &&
dirPath.substr(rootLength, nextDirectorySeparator).search(/users/i) === -1) {
// Paths like c:/folderAtRoot/subFolder are allowed
return true;
}
for (let searchIndex = nextDirectorySeparator + 1, searchLevels = 2; searchLevels > 0; searchLevels--) {
searchIndex = dirPath.indexOf(directorySeparator, searchIndex) + 1;
if (searchIndex === 0) {
// Folder isnt at expected minimun levels
return false;
}
}
return true;
}
function getDirectoryToWatchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path): DirectoryOfFailedLookupWatch | undefined {
if (isInDirectoryPath(rootPath, failedLookupLocationPath)) {
// Ensure failed look up is normalized path
@@ -698,6 +705,11 @@ namespace ts {
// If something to do with folder/file starting with "." in node_modules folder, skip it
if (isPathIgnored(fileOrDirectoryPath)) return false;
// prevent saving an open file from over-eagerly triggering invalidation
if (resolutionHost.fileIsOpen(fileOrDirectoryPath)) {
return false;
}
// Some file or directory in the watching directory is created
// Return early if it does not have any of the watching extension or not the custom failed lookup path
const dirOfFileOrDirectory = getDirectoryPath(fileOrDirectoryPath);
+1 -1
View File
@@ -330,7 +330,7 @@ namespace ts {
}
/*@internal*/
export const ignoredPaths = ["/node_modules/.", "/.git"];
export const ignoredPaths = ["/node_modules/.", "/.git", "/.#"];
/*@internal*/
export interface RecursiveDirectoryWatcherHost {
+1
View File
@@ -44,6 +44,7 @@ namespace ts {
addRange(transformers, customTransformers && map(customTransformers.before, wrapScriptTransformerFactory));
transformers.push(transformTypeScript);
transformers.push(transformClassFields);
if (jsx === JsxEmit.React) {
transformers.push(transformJsx);
+511
View File
@@ -0,0 +1,511 @@
/*@internal*/
namespace ts {
const enum ClassPropertySubstitutionFlags {
/**
* Enables substitutions for class expressions with static fields
* which have initializers that reference the class name.
*/
ClassAliases = 1 << 0,
}
/**
* Transforms ECMAScript Class Syntax.
* TypeScript parameter property syntax is transformed in the TypeScript transformer.
* For now, this transforms public field declarations using TypeScript class semantics
* (where the declarations get elided and initializers are transformed as assignments in the constructor).
* Eventually, this transform will change to the ECMAScript semantics (with Object.defineProperty).
*/
export function transformClassFields(context: TransformationContext) {
const {
hoistVariableDeclaration,
endLexicalEnvironment,
resumeLexicalEnvironment
} = context;
const resolver = context.getEmitResolver();
const previousOnSubstituteNode = context.onSubstituteNode;
context.onSubstituteNode = onSubstituteNode;
let enabledSubstitutions: ClassPropertySubstitutionFlags;
let classAliases: Identifier[];
/**
* Tracks what computed name expressions originating from elided names must be inlined
* at the next execution site, in document order
*/
let pendingExpressions: Expression[] | undefined;
/**
* Tracks what computed name expression statements and static property initializers must be
* emitted at the next execution site, in document order (for decorated classes).
*/
let pendingStatements: Statement[] | undefined;
return chainBundle(transformSourceFile);
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
return visited;
}
function visitor(node: Node): VisitResult<Node> {
if (!(node.transformFlags & TransformFlags.ContainsClassFields)) return node;
switch (node.kind) {
case SyntaxKind.ClassExpression:
return visitClassExpression(node as ClassExpression);
case SyntaxKind.ClassDeclaration:
return visitClassDeclaration(node as ClassDeclaration);
case SyntaxKind.VariableStatement:
return visitVariableStatement(node as VariableStatement);
}
return visitEachChild(node, visitor, context);
}
/**
* Visits the members of a class that has fields.
*
* @param node The node to visit.
*/
function classElementVisitor(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.Constructor:
// Constructors for classes using class fields are transformed in
// `visitClassDeclaration` or `visitClassExpression`.
return undefined;
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.MethodDeclaration:
// Visit the name of the member (if it's a computed property name).
return visitEachChild(node, classElementVisitor, context);
case SyntaxKind.PropertyDeclaration:
return visitPropertyDeclaration(node as PropertyDeclaration);
case SyntaxKind.ComputedPropertyName:
return visitComputedPropertyName(node as ComputedPropertyName);
case SyntaxKind.SemicolonClassElement:
return node;
default:
return visitor(node);
}
}
function visitVariableStatement(node: VariableStatement) {
const savedPendingStatements = pendingStatements;
pendingStatements = [];
const visitedNode = visitEachChild(node, visitor, context);
const statement = some(pendingStatements) ?
[visitedNode, ...pendingStatements] :
visitedNode;
pendingStatements = savedPendingStatements;
return statement;
}
function visitComputedPropertyName(name: ComputedPropertyName) {
let node = visitEachChild(name, visitor, context);
if (some(pendingExpressions)) {
const expressions = pendingExpressions;
expressions.push(name.expression);
pendingExpressions = [];
node = updateComputedPropertyName(
node,
inlineExpressions(expressions)
);
}
return node;
}
function visitPropertyDeclaration(node: PropertyDeclaration) {
Debug.assert(!some(node.decorators));
// Create a temporary variable to store a computed property name (if necessary).
// If it's not inlineable, then we emit an expression after the class which assigns
// the property name to the temporary variable.
const expr = getPropertyNameExpressionIfNeeded(node.name, !!node.initializer);
if (expr && !isSimpleInlineableExpression(expr)) {
(pendingExpressions || (pendingExpressions = [])).push(expr);
}
return undefined;
}
function visitClassDeclaration(node: ClassDeclaration) {
if (!forEach(node.members, isPropertyDeclaration)) {
return visitEachChild(node, visitor, context);
}
const savedPendingExpressions = pendingExpressions;
pendingExpressions = undefined!;
const extendsClauseElement = getEffectiveBaseTypeNode(node);
const isDerivedClass = !!(extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword);
const statements: Statement[] = [
updateClassDeclaration(
node,
/*decorators*/ undefined,
node.modifiers,
node.name,
/*typeParameters*/ undefined,
visitNodes(node.heritageClauses, visitor, isHeritageClause),
transformClassMembers(node, isDerivedClass)
)
];
// Write any pending expressions from elided or moved computed property names
if (some(pendingExpressions)) {
statements.push(createExpressionStatement(inlineExpressions(pendingExpressions)));
}
pendingExpressions = savedPendingExpressions;
// Emit static property assignment. Because classDeclaration is lexically evaluated,
// it is safe to emit static property assignment after classDeclaration
// From ES6 specification:
// HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using
// a lexical declaration such as a LexicalDeclaration or a ClassDeclaration.
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
if (some(staticProperties)) {
addInitializedPropertyStatements(statements, staticProperties, getInternalName(node));
}
return statements;
}
function visitClassExpression(node: ClassExpression): Expression {
if (!forEach(node.members, isPropertyDeclaration)) {
return visitEachChild(node, visitor, context);
}
const savedPendingExpressions = pendingExpressions;
pendingExpressions = undefined;
// If this class expression is a transformation of a decorated class declaration,
// then we want to output the pendingExpressions as statements, not as inlined
// expressions with the class statement.
//
// In this case, we use pendingStatements to produce the same output as the
// class declaration transformation. The VariableStatement visitor will insert
// these statements after the class expression variable statement.
const isDecoratedClassDeclaration = isClassDeclaration(getOriginalNode(node));
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
const extendsClauseElement = getEffectiveBaseTypeNode(node);
const isDerivedClass = !!(extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword);
const classExpression = updateClassExpression(
node,
node.modifiers,
node.name,
/*typeParameters*/ undefined,
visitNodes(node.heritageClauses, visitor, isHeritageClause),
transformClassMembers(node, isDerivedClass)
);
if (some(staticProperties) || some(pendingExpressions)) {
if (isDecoratedClassDeclaration) {
Debug.assertDefined(pendingStatements, "Decorated classes transformed by TypeScript are expected to be within a variable declaration.");
// Write any pending expressions from elided or moved computed property names
if (pendingStatements && pendingExpressions && some(pendingExpressions)) {
pendingStatements.push(createExpressionStatement(inlineExpressions(pendingExpressions)));
}
pendingExpressions = savedPendingExpressions;
if (pendingStatements && some(staticProperties)) {
addInitializedPropertyStatements(pendingStatements, staticProperties, getInternalName(node));
}
return classExpression;
}
else {
const expressions: Expression[] = [];
const isClassWithConstructorReference = resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference;
const temp = createTempVariable(hoistVariableDeclaration, !!isClassWithConstructorReference);
if (isClassWithConstructorReference) {
// record an alias as the class name is not in scope for statics.
enableSubstitutionForClassAliases();
const alias = getSynthesizedClone(temp);
alias.autoGenerateFlags &= ~GeneratedIdentifierFlags.ReservedInNestedScopes;
classAliases[getOriginalNodeId(node)] = alias;
}
// To preserve the behavior of the old emitter, we explicitly indent
// the body of a class with static initializers.
setEmitFlags(classExpression, EmitFlags.Indented | getEmitFlags(classExpression));
expressions.push(startOnNewLine(createAssignment(temp, classExpression)));
// Add any pending expressions leftover from elided or relocated computed property names
addRange(expressions, map(pendingExpressions, startOnNewLine));
addRange(expressions, generateInitializedPropertyExpressions(staticProperties, temp));
expressions.push(startOnNewLine(temp));
pendingExpressions = savedPendingExpressions;
return inlineExpressions(expressions);
}
}
pendingExpressions = savedPendingExpressions;
return classExpression;
}
function transformClassMembers(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
const members: ClassElement[] = [];
const constructor = transformConstructor(node, isDerivedClass);
if (constructor) {
members.push(constructor);
}
addRange(members, visitNodes(node.members, classElementVisitor, isClassElement));
return setTextRange(createNodeArray(members), /*location*/ node.members);
}
function transformConstructor(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
const constructor = visitNode(getFirstConstructorWithBody(node), visitor, isConstructorDeclaration);
const containsPropertyInitializer = forEach(node.members, isInitializedProperty);
if (!containsPropertyInitializer) {
return constructor;
}
const parameters = visitParameterList(constructor ? constructor.parameters : undefined, visitor, context);
const body = transformConstructorBody(node, constructor, isDerivedClass);
if (!body) {
return undefined;
}
return startOnNewLine(
setOriginalNode(
setTextRange(
createConstructor(
/*decorators*/ undefined,
/*modifiers*/ undefined,
parameters,
body
),
constructor || node
),
constructor
)
);
}
function transformConstructorBody(node: ClassDeclaration | ClassExpression, constructor: ConstructorDeclaration | undefined, isDerivedClass: boolean) {
const properties = getInitializedProperties(node, /*isStatic*/ false);
// Only generate synthetic constructor when there are property initializers to move.
if (!constructor && !some(properties)) {
return visitFunctionBody(/*node*/ undefined, visitor, context);
}
resumeLexicalEnvironment();
let indexOfFirstStatement = 0;
let statements: Statement[] = [];
if (!constructor && isDerivedClass) {
// Add a synthetic `super` call:
//
// super(...arguments);
//
statements.push(
createExpressionStatement(
createCall(
createSuper(),
/*typeArguments*/ undefined,
[createSpread(createIdentifier("arguments"))]
)
)
);
}
if (constructor) {
indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements, visitor);
}
// Add the property initializers. Transforms this:
//
// public x = 1;
//
// Into this:
//
// constructor() {
// this.x = 1;
// }
//
if (constructor && constructor.body) {
let parameterPropertyDeclarationCount = 0;
for (let i = indexOfFirstStatement; i < constructor.body.statements.length; i++) {
if (isParameterPropertyDeclaration(getOriginalNode(constructor.body.statements[i]))) {
parameterPropertyDeclarationCount++;
}
else {
break;
}
}
if (parameterPropertyDeclarationCount > 0) {
addRange(statements, visitNodes(constructor.body.statements, visitor, isStatement, indexOfFirstStatement, parameterPropertyDeclarationCount));
indexOfFirstStatement += parameterPropertyDeclarationCount;
}
}
addInitializedPropertyStatements(statements, properties, createThis());
// Add existing statements, skipping the initial super call.
if (constructor) {
addRange(statements, visitNodes(constructor.body!.statements, visitor, isStatement, indexOfFirstStatement));
}
statements = mergeLexicalEnvironment(statements, endLexicalEnvironment());
return setTextRange(
createBlock(
setTextRange(
createNodeArray(statements),
/*location*/ constructor ? constructor.body!.statements : node.members
),
/*multiLine*/ true
),
/*location*/ constructor ? constructor.body : undefined
);
}
/**
* Generates assignment statements for property initializers.
*
* @param properties An array of property declarations to transform.
* @param receiver The receiver on which each property should be assigned.
*/
function addInitializedPropertyStatements(statements: Statement[], properties: ReadonlyArray<PropertyDeclaration>, receiver: LeftHandSideExpression) {
for (const property of properties) {
const statement = createExpressionStatement(transformInitializedProperty(property, receiver));
setSourceMapRange(statement, moveRangePastModifiers(property));
setCommentRange(statement, property);
setOriginalNode(statement, property);
statements.push(statement);
}
}
/**
* Generates assignment expressions for property initializers.
*
* @param properties An array of property declarations to transform.
* @param receiver The receiver on which each property should be assigned.
*/
function generateInitializedPropertyExpressions(properties: ReadonlyArray<PropertyDeclaration>, receiver: LeftHandSideExpression) {
const expressions: Expression[] = [];
for (const property of properties) {
const expression = transformInitializedProperty(property, receiver);
startOnNewLine(expression);
setSourceMapRange(expression, moveRangePastModifiers(property));
setCommentRange(expression, property);
setOriginalNode(expression, property);
expressions.push(expression);
}
return expressions;
}
/**
* Transforms a property initializer into an assignment statement.
*
* @param property The property declaration.
* @param receiver The object receiving the property assignment.
*/
function transformInitializedProperty(property: PropertyDeclaration, receiver: LeftHandSideExpression) {
// We generate a name here in order to reuse the value cached by the relocated computed name expression (which uses the same generated name)
const propertyName = isComputedPropertyName(property.name) && !isSimpleInlineableExpression(property.name.expression)
? updateComputedPropertyName(property.name, getGeneratedNameForNode(property.name))
: property.name;
const initializer = visitNode(property.initializer!, visitor, isExpression);
const memberAccess = createMemberAccessForPropertyName(receiver, propertyName, /*location*/ propertyName);
return createAssignment(memberAccess, initializer);
}
function enableSubstitutionForClassAliases() {
if ((enabledSubstitutions & ClassPropertySubstitutionFlags.ClassAliases) === 0) {
enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases;
// We need to enable substitutions for identifiers. This allows us to
// substitute class names inside of a class declaration.
context.enableSubstitution(SyntaxKind.Identifier);
// Keep track of class aliases.
classAliases = [];
}
}
/**
* Hooks node substitutions.
*
* @param hint The context for the emitter.
* @param node The node to substitute.
*/
function onSubstituteNode(hint: EmitHint, node: Node) {
node = previousOnSubstituteNode(hint, node);
if (hint === EmitHint.Expression) {
return substituteExpression(node as Expression);
}
return node;
}
function substituteExpression(node: Expression) {
switch (node.kind) {
case SyntaxKind.Identifier:
return substituteExpressionIdentifier(node as Identifier);
}
return node;
}
function substituteExpressionIdentifier(node: Identifier): Expression {
return trySubstituteClassAlias(node) || node;
}
function trySubstituteClassAlias(node: Identifier): Expression | undefined {
if (enabledSubstitutions & ClassPropertySubstitutionFlags.ClassAliases) {
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ConstructorReferenceInClass) {
// Due to the emit for class decorators, any reference to the class from inside of the class body
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
// behavior of class names in ES6.
// Also, when emitting statics for class expressions, we must substitute a class alias for
// constructor references in static property initializers.
const declaration = resolver.getReferencedValueDeclaration(node);
if (declaration) {
const classAlias = classAliases[declaration.id!]; // TODO: GH#18217
if (classAlias) {
const clone = getSynthesizedClone(classAlias);
setSourceMapRange(clone, node);
setCommentRange(clone, node);
return clone;
}
}
}
}
return undefined;
}
/**
* If the name is a computed property, this function transforms it, then either returns an expression which caches the
* value of the result or the expression itself if the value is either unused or safe to inline into multiple locations
* @param shouldHoist Does the expression need to be reused? (ie, for an initializer or a decorator)
*/
function getPropertyNameExpressionIfNeeded(name: PropertyName, shouldHoist: boolean): Expression | undefined {
if (isComputedPropertyName(name)) {
const expression = visitNode(name.expression, visitor, isExpression);
const innerExpression = skipPartiallyEmittedExpressions(expression);
const inlinable = isSimpleInlineableExpression(innerExpression);
const alreadyTransformed = isAssignmentExpression(innerExpression) && isGeneratedIdentifier(innerExpression.left);
if (!alreadyTransformed && !inlinable && shouldHoist) {
const generatedName = getGeneratedNameForNode(name);
hoistVariableDeclaration(generatedName);
return createAssignment(generatedName, expression);
}
return (inlinable || isIdentifier(innerExpression)) ? undefined : expression;
}
}
}
}
+76 -33
View File
@@ -190,6 +190,10 @@ namespace ts {
}
}
function createEmptyExports() {
return createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined);
}
function transformRoot(node: Bundle): Bundle;
function transformRoot(node: SourceFile): SourceFile;
function transformRoot(node: SourceFile | Bundle): SourceFile | Bundle;
@@ -277,7 +281,7 @@ namespace ts {
refs.forEach(referenceVisitor);
const emittedImports = filter(combinedStatements, isAnyImportSyntax);
if (isExternalModule(node) && (!resultHasExternalModuleIndicator || (needsScopeFixMarker && !resultHasScopeMarker))) {
combinedStatements = setTextRange(createNodeArray([...combinedStatements, createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined)]), combinedStatements);
combinedStatements = setTextRange(createNodeArray([...combinedStatements, createEmptyExports()]), combinedStatements);
}
const updated = updateSourceFileNode(node, combinedStatements, /*isDeclarationFile*/ true, references, getFileReferencesForUsedTypeReferences(), node.hasNoDefaultLib, getLibReferences());
updated.exportedModulesFromDeclarationEmit = exportedModulesFromDeclarationEmit;
@@ -348,7 +352,7 @@ namespace ts {
function collectReferences(sourceFile: SourceFile | UnparsedSource, ret: Map<SourceFile>) {
if (noResolve || (!isUnparsedSource(sourceFile) && isSourceFileJS(sourceFile))) return ret;
forEach(sourceFile.referencedFiles, f => {
const elem = tryResolveScriptReference(host, sourceFile, f);
const elem = host.getSourceFileFromReference(sourceFile, f);
if (elem) {
ret.set("" + getOriginalNodeId(elem), elem);
}
@@ -670,7 +674,7 @@ namespace ts {
}
const priorNeedsDeclare = needsDeclare;
needsDeclare = i.parent && isSourceFile(i.parent) && !(isExternalModule(i.parent) && isBundledEmit);
const result = transformTopLevelDeclaration(i, /*privateDeclaration*/ true);
const result = transformTopLevelDeclaration(i);
needsDeclare = priorNeedsDeclare;
lateStatementReplacementMap.set("" + getOriginalNodeId(i), result);
}
@@ -685,12 +689,12 @@ namespace ts {
if (lateStatementReplacementMap.has(key)) {
const result = lateStatementReplacementMap.get(key);
lateStatementReplacementMap.delete(key);
if (result && isSourceFile(statement.parent)) {
if (result) {
if (isArray(result) ? some(result, needsScopeMarker) : needsScopeMarker(result)) {
// Top-level declarations in .d.ts files are always considered exported even without a modifier unless there's an export assignment or specifier
needsScopeFixMarker = true;
}
if (isArray(result) ? some(result, isExternalModuleIndicator) : isExternalModuleIndicator(result)) {
if (isSourceFile(statement.parent) && (isArray(result) ? some(result, isExternalModuleIndicator) : isExternalModuleIndicator(result))) {
resultHasExternalModuleIndicator = true;
}
}
@@ -939,8 +943,8 @@ namespace ts {
case SyntaxKind.ExportDeclaration: {
if (isSourceFile(input.parent)) {
resultHasExternalModuleIndicator = true;
resultHasScopeMarker = true;
}
resultHasScopeMarker = true;
// Always visible if the parent node isn't dropped for being not visible
// Rewrite external module names if necessary
return updateExportDeclaration(input, /*decorators*/ undefined, input.modifiers, input.exportClause, rewriteModuleSpecifier(input, input.moduleSpecifier));
@@ -949,8 +953,8 @@ namespace ts {
// Always visible if the parent node isn't dropped for being not visible
if (isSourceFile(input.parent)) {
resultHasExternalModuleIndicator = true;
resultHasScopeMarker = true;
}
resultHasScopeMarker = true;
if (input.expression.kind === SyntaxKind.Identifier) {
return input;
}
@@ -973,7 +977,19 @@ namespace ts {
return input;
}
function transformTopLevelDeclaration(input: LateVisibilityPaintedStatement, isPrivate?: boolean) {
function stripExportModifiers(statement: Statement): Statement {
if (isImportEqualsDeclaration(statement) || hasModifier(statement, ModifierFlags.Default)) {
// `export import` statements should remain as-is, as imports are _not_ implicitly exported in an ambient namespace
// Likewise, `export default` classes and the like and just be `default`, so we preserve their `export` modifiers, too
return statement;
}
const clone = getMutableClone(statement);
const modifiers = createModifiersFromModifierFlags(getModifierFlags(statement) & (ModifierFlags.All ^ ModifierFlags.Export));
clone.modifiers = modifiers.length ? createNodeArray(modifiers) : undefined;
return clone;
}
function transformTopLevelDeclaration(input: LateVisibilityPaintedStatement) {
if (shouldStripInternal(input)) return;
switch (input.kind) {
case SyntaxKind.ImportEqualsDeclaration: {
@@ -1006,7 +1022,7 @@ namespace ts {
return cleanup(updateTypeAliasDeclaration(
input,
/*decorators*/ undefined,
ensureModifiers(input, isPrivate),
ensureModifiers(input),
input.name,
visitNodes(input.typeParameters, visitDeclarationSubtree, isTypeParameterDeclaration),
visitNode(input.type, visitDeclarationSubtree, isTypeNode)
@@ -1015,7 +1031,7 @@ namespace ts {
return cleanup(updateInterfaceDeclaration(
input,
/*decorators*/ undefined,
ensureModifiers(input, isPrivate),
ensureModifiers(input),
input.name,
ensureTypeParams(input, input.typeParameters),
transformHeritageClauses(input.heritageClauses),
@@ -1027,7 +1043,7 @@ namespace ts {
const clean = cleanup(updateFunctionDeclaration(
input,
/*decorators*/ undefined,
ensureModifiers(input, isPrivate),
ensureModifiers(input),
/*asteriskToken*/ undefined,
input.name,
ensureTypeParams(input, input.typeParameters),
@@ -1036,19 +1052,25 @@ namespace ts {
/*body*/ undefined
));
if (clean && resolver.isExpandoFunctionDeclaration(input)) {
const declarations = mapDefined(resolver.getPropertiesOfContainerFunction(input), p => {
const props = resolver.getPropertiesOfContainerFunction(input);
const fakespace = createModuleDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, clean.name || createIdentifier("_default"), createModuleBlock([]), NodeFlags.Namespace);
fakespace.flags ^= NodeFlags.Synthesized; // unset synthesized so it is usable as an enclosing declaration
fakespace.parent = enclosingDeclaration as SourceFile | NamespaceDeclaration;
fakespace.locals = createSymbolTable(props);
fakespace.symbol = props[0].parent!;
const declarations = mapDefined(props, p => {
if (!isPropertyAccessExpression(p.valueDeclaration)) {
return undefined;
}
getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(p.valueDeclaration);
const type = resolver.createTypeOfDeclaration(p.valueDeclaration, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker);
const type = resolver.createTypeOfDeclaration(p.valueDeclaration, fakespace, declarationEmitNodeBuilderFlags, symbolTracker);
getSymbolAccessibilityDiagnostic = oldDiag;
const varDecl = createVariableDeclaration(unescapeLeadingUnderscores(p.escapedName), type, /*initializer*/ undefined);
return createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([varDecl]));
});
const namespaceDecl = createModuleDeclaration(/*decorators*/ undefined, ensureModifiers(input, isPrivate), input.name!, createModuleBlock(declarations), NodeFlags.Namespace);
const namespaceDecl = createModuleDeclaration(/*decorators*/ undefined, ensureModifiers(input), input.name!, createModuleBlock(declarations), NodeFlags.Namespace);
if (!hasModifier(clean, ModifierFlags.ExportDefault)) {
if (!hasModifier(clean, ModifierFlags.Default)) {
return [clean, namespaceDecl];
}
@@ -1080,7 +1102,9 @@ namespace ts {
namespaceDecl.name
);
resultHasExternalModuleIndicator = true;
if (isSourceFile(input.parent)) {
resultHasExternalModuleIndicator = true;
}
resultHasScopeMarker = true;
return [cleanDeclaration, namespaceDeclaration, exportDefaultDeclaration];
@@ -1093,10 +1117,32 @@ namespace ts {
needsDeclare = false;
const inner = input.body;
if (inner && inner.kind === SyntaxKind.ModuleBlock) {
const oldNeedsScopeFix = needsScopeFixMarker;
const oldHasScopeFix = resultHasScopeMarker;
resultHasScopeMarker = false;
needsScopeFixMarker = false;
const statements = visitNodes(inner.statements, visitDeclarationStatements);
const body = updateModuleBlock(inner, transformAndReplaceLatePaintedStatements(statements));
let lateStatements = transformAndReplaceLatePaintedStatements(statements);
if (input.flags & NodeFlags.Ambient) {
needsScopeFixMarker = false; // If it was `declare`'d everything is implicitly exported already, ignore late printed "privates"
}
// With the final list of statements, there are 3 possibilities:
// 1. There's an export assignment or export declaration in the namespace - do nothing
// 2. Everything is exported and there are no export assignments or export declarations - strip all export modifiers
// 3. Some things are exported, some are not, and there's no marker - add an empty marker
if (!isGlobalScopeAugmentation(input) && !hasScopeMarker(lateStatements) && !resultHasScopeMarker) {
if (needsScopeFixMarker) {
lateStatements = createNodeArray([...lateStatements, createEmptyExports()]);
}
else {
lateStatements = visitNodes(lateStatements, stripExportModifiers);
}
}
const body = updateModuleBlock(inner, lateStatements);
needsDeclare = previousNeedsDeclare;
const mods = ensureModifiers(input, isPrivate);
needsScopeFixMarker = oldNeedsScopeFix;
resultHasScopeMarker = oldHasScopeFix;
const mods = ensureModifiers(input);
return cleanup(updateModuleDeclaration(
input,
/*decorators*/ undefined,
@@ -1107,7 +1153,7 @@ namespace ts {
}
else {
needsDeclare = previousNeedsDeclare;
const mods = ensureModifiers(input, isPrivate);
const mods = ensureModifiers(input);
needsDeclare = false;
visitNode(inner, visitDeclarationStatements);
// eagerly transform nested namespaces (the nesting doesn't need any elision or painting done)
@@ -1124,7 +1170,7 @@ namespace ts {
}
}
case SyntaxKind.ClassDeclaration: {
const modifiers = createNodeArray(ensureModifiers(input, isPrivate));
const modifiers = createNodeArray(ensureModifiers(input));
const typeParameters = ensureTypeParams(input, input.typeParameters);
const ctor = getFirstConstructorWithBody(input);
let parameterProperties: ReadonlyArray<PropertyDeclaration> | undefined;
@@ -1218,10 +1264,10 @@ namespace ts {
}
}
case SyntaxKind.VariableStatement: {
return cleanup(transformVariableStatement(input, isPrivate));
return cleanup(transformVariableStatement(input));
}
case SyntaxKind.EnumDeclaration: {
return cleanup(updateEnumDeclaration(input, /*decorators*/ undefined, createNodeArray(ensureModifiers(input, isPrivate)), input.name, createNodeArray(mapDefined(input.members, m => {
return cleanup(updateEnumDeclaration(input, /*decorators*/ undefined, createNodeArray(ensureModifiers(input)), input.name, createNodeArray(mapDefined(input.members, m => {
if (shouldStripInternal(m)) return;
// Rewrite enum values to their constants, if available
const constValue = resolver.getConstantValue(m);
@@ -1249,11 +1295,11 @@ namespace ts {
}
}
function transformVariableStatement(input: VariableStatement, privateDeclaration?: boolean) {
function transformVariableStatement(input: VariableStatement) {
if (!forEach(input.declarationList.declarations, getBindingNameVisible)) return;
const nodes = visitNodes(input.declarationList.declarations, visitDeclarationSubtree);
if (!length(nodes)) return;
return updateVariableStatement(input, createNodeArray(ensureModifiers(input, privateDeclaration)), updateVariableDeclarationList(input.declarationList, nodes));
return updateVariableStatement(input, createNodeArray(ensureModifiers(input)), updateVariableDeclarationList(input.declarationList, nodes));
}
function recreateBindingPattern(d: BindingPattern): VariableDeclaration[] {
@@ -1300,28 +1346,25 @@ namespace ts {
return isExportAssignment(node) || isExportDeclaration(node);
}
function hasScopeMarker(node: Node) {
if (isModuleBlock(node)) {
return some(node.statements, isScopeMarker);
}
return false;
function hasScopeMarker(statements: ReadonlyArray<Statement>) {
return some(statements, isScopeMarker);
}
function ensureModifiers(node: Node, privateDeclaration?: boolean): ReadonlyArray<Modifier> | undefined {
function ensureModifiers(node: Node): ReadonlyArray<Modifier> | undefined {
const currentFlags = getModifierFlags(node);
const newFlags = ensureModifierFlags(node, privateDeclaration);
const newFlags = ensureModifierFlags(node);
if (currentFlags === newFlags) {
return node.modifiers;
}
return createModifiersFromModifierFlags(newFlags);
}
function ensureModifierFlags(node: Node, privateDeclaration?: boolean): ModifierFlags {
function ensureModifierFlags(node: Node): ModifierFlags {
let mask = ModifierFlags.All ^ (ModifierFlags.Public | ModifierFlags.Async); // No async modifiers in declaration files
let additions = (needsDeclare && !isAlwaysType(node)) ? ModifierFlags.Ambient : ModifierFlags.None;
const parentIsFile = node.parent.kind === SyntaxKind.SourceFile;
if (!parentIsFile || (isBundledEmit && parentIsFile && isExternalModule(node.parent as SourceFile))) {
mask ^= ((privateDeclaration || (isBundledEmit && parentIsFile) || hasScopeMarker(node.parent) ? 0 : ModifierFlags.Export) | ModifierFlags.Ambient);
mask ^= ModifierFlags.Ambient;
additions = ModifierFlags.None;
}
return maskModifierFlags(node, mask, additions);
+52 -30
View File
@@ -145,7 +145,7 @@ namespace ts {
loopOutParameters: LoopOutParameter[];
}
type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined) => Statement;
type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined, ancestorFacts: HierarchyFacts) => Statement;
// Facts we track as we traverse the tree
const enum HierarchyFacts {
@@ -163,11 +163,12 @@ namespace ts {
ExportedVariableStatement = 1 << 5, // Enclosed in an exported variable statement in the current scope
TopLevel = 1 << 6, // Enclosing block-scoped container is a top-level container
Block = 1 << 7, // Enclosing block-scoped container is a Block
IterationStatement = 1 << 8, // Enclosed in an IterationStatement
IterationStatement = 1 << 8, // Immediately enclosed in an IterationStatement
IterationStatementBlock = 1 << 9, // Enclosing Block is enclosed in an IterationStatement
ForStatement = 1 << 10, // Enclosing block-scoped container is a ForStatement
ForInOrForOfStatement = 1 << 11, // Enclosing block-scoped container is a ForInStatement or ForOfStatement
ConstructorWithCapturedSuper = 1 << 12, // Enclosed in a constructor that captures 'this' for use with 'super'
IterationContainer = 1 << 10, // Enclosed in an outer IterationStatement
ForStatement = 1 << 11, // Enclosing block-scoped container is a ForStatement
ForInOrForOfStatement = 1 << 12, // Enclosing block-scoped container is a ForInStatement or ForOfStatement
ConstructorWithCapturedSuper = 1 << 13, // Enclosed in a constructor that captures 'this' for use with 'super'
// NOTE: do not add more ancestor flags without also updating AncestorFactsMask below.
// NOTE: when adding a new ancestor flag, be sure to update the subtree flags below.
@@ -184,11 +185,11 @@ namespace ts {
// A source file is a top-level block scope.
SourceFileIncludes = TopLevel,
SourceFileExcludes = BlockScopeExcludes & ~TopLevel,
SourceFileExcludes = BlockScopeExcludes & ~TopLevel | IterationContainer,
// Functions, methods, and accessors are both new lexical scopes and new block scopes.
FunctionIncludes = Function | TopLevel,
FunctionExcludes = BlockScopeExcludes & ~TopLevel | ArrowFunction | AsyncFunctionBody | CapturesThis | NonStaticClassElement | ConstructorWithCapturedSuper,
FunctionExcludes = BlockScopeExcludes & ~TopLevel | ArrowFunction | AsyncFunctionBody | CapturesThis | NonStaticClassElement | ConstructorWithCapturedSuper | IterationContainer,
AsyncFunctionBodyIncludes = FunctionIncludes | AsyncFunctionBody,
AsyncFunctionBodyExcludes = FunctionExcludes & ~NonStaticClassElement,
@@ -205,16 +206,16 @@ namespace ts {
// 'do' and 'while' statements are not block scopes. We track that the subtree is contained
// within an IterationStatement to indicate whether the embedded statement is an
// IterationStatementBlock.
DoOrWhileStatementIncludes = IterationStatement,
DoOrWhileStatementIncludes = IterationStatement | IterationContainer,
DoOrWhileStatementExcludes = None,
// 'for' statements are new block scopes and have special handling for 'let' declarations.
ForStatementIncludes = IterationStatement | ForStatement,
ForStatementIncludes = IterationStatement | ForStatement | IterationContainer,
ForStatementExcludes = BlockScopeExcludes & ~ForStatement,
// 'for-in' and 'for-of' statements are new block scopes and have special handling for
// 'let' declarations.
ForInOrForOfStatementIncludes = IterationStatement | ForInOrForOfStatement,
ForInOrForOfStatementIncludes = IterationStatement | ForInOrForOfStatement | IterationContainer,
ForInOrForOfStatementExcludes = BlockScopeExcludes & ~ForInOrForOfStatement,
// Blocks (other than function bodies) are new block scopes.
@@ -228,8 +229,8 @@ namespace ts {
// Subtree facts
//
NewTarget = 1 << 13, // Contains a 'new.target' meta-property
CapturedLexicalThis = 1 << 14, // Contains a lexical `this` reference captured by an arrow function.
NewTarget = 1 << 14, // Contains a 'new.target' meta-property
CapturedLexicalThis = 1 << 15, // Contains a lexical `this` reference captured by an arrow function.
//
// Subtree masks
@@ -1566,7 +1567,7 @@ namespace ts {
break;
default:
Debug.failBadSyntaxKind(node);
Debug.failBadSyntaxKind(member, currentSourceFile && currentSourceFile.fileName);
break;
}
}
@@ -2227,7 +2228,7 @@ namespace ts {
function visitIterationStatementWithFacts(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts, node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter) {
const ancestorFacts = enterSubtree(excludeFacts, includeFacts);
const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, convert);
const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, ancestorFacts, convert);
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
return updated;
}
@@ -2434,7 +2435,7 @@ namespace ts {
return restoreEnclosingLabel(forStatement, outermostLabeledStatement, convertedLoopState && resetLabel);
}
function convertForOfStatementForIterable(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[]): Statement {
function convertForOfStatementForIterable(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[], ancestorFacts: HierarchyFacts): Statement {
const expression = visitNode(node.expression, visitor, isExpression);
const iterator = isIdentifier(expression) ? getGeneratedNameForNode(expression) : createTempVariable(/*recordTempVariable*/ undefined);
const result = isIdentifier(expression) ? getGeneratedNameForNode(iterator) : createTempVariable(/*recordTempVariable*/ undefined);
@@ -2447,13 +2448,18 @@ namespace ts {
hoistVariableDeclaration(errorRecord);
hoistVariableDeclaration(returnMethod);
// if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration
const initializer = ancestorFacts & HierarchyFacts.IterationContainer
? inlineExpressions([createAssignment(errorRecord, createVoidZero()), values])
: values;
const forStatement = setEmitFlags(
setTextRange(
createFor(
/*initializer*/ setEmitFlags(
setTextRange(
createVariableDeclarationList([
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, values), node.expression),
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, initializer), node.expression),
createVariableDeclaration(result, /*type*/ undefined, next)
]),
node.expression
@@ -2665,7 +2671,7 @@ namespace ts {
}
}
function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter): VisitResult<Statement> {
function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, ancestorFacts: HierarchyFacts, convert?: LoopConverter): VisitResult<Statement> {
if (!shouldConvertIterationStatement(node)) {
let saveAllowedNonLabeledJumps: Jump | undefined;
if (convertedLoopState) {
@@ -2676,7 +2682,7 @@ namespace ts {
}
const result = convert
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined)
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined, ancestorFacts)
: restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement, convertedLoopState && resetLabel);
if (convertedLoopState) {
@@ -2708,7 +2714,7 @@ namespace ts {
let loop: Statement;
if (bodyFunction) {
if (convert) {
loop = convert(node, outermostLabeledStatement, bodyFunction.part);
loop = convert(node, outermostLabeledStatement, bodyFunction.part, ancestorFacts);
}
else {
const clone = convertIterationStatementCore(node, initializerFunction, createBlock(bodyFunction.part, /*multiLine*/ true));
@@ -3813,8 +3819,11 @@ namespace ts {
// [source]
// [a, ...b, c]
//
// [output (downlevelIteration)]
// __spread([a], b, [c])
//
// [output]
// [a].concat(b, [c])
// __spreadArrays([a], b, [c])
// Map spans of spread expressions into their expressions and spans of other
// expressions into an array literal.
@@ -3828,10 +3837,7 @@ namespace ts {
if (compilerOptions.downlevelIteration) {
if (segments.length === 1) {
const firstSegment = segments[0];
if (isCallExpression(firstSegment)
&& isIdentifier(firstSegment.expression)
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
&& firstSegment.expression.escapedText === "___spread") {
if (isCallToHelper(firstSegment, "___spread" as __String)) {
return segments[0];
}
}
@@ -3840,17 +3846,33 @@ namespace ts {
}
else {
if (segments.length === 1) {
const firstElement = elements[0];
return needsUniqueCopy && isSpreadElement(firstElement) && firstElement.expression.kind !== SyntaxKind.ArrayLiteralExpression
? createArraySlice(segments[0])
: segments[0];
const firstSegment = segments[0];
if (!needsUniqueCopy
|| isPackedArrayLiteral(firstSegment)
|| isCallToHelper(firstSegment, "___spreadArrays" as __String)) {
return segments[0];
}
}
// Rewrite using the pattern <segment0>.concat(<segment1>, <segment2>, ...)
return createArrayConcat(segments.shift()!, segments);
return createSpreadArraysHelper(context, segments);
}
}
function isPackedElement(node: Expression) {
return !isOmittedExpression(node);
}
function isPackedArrayLiteral(node: Expression) {
return isArrayLiteralExpression(node) && every(node.elements, isPackedElement);
}
function isCallToHelper(firstSegment: Expression, helperName: __String) {
return isCallExpression(firstSegment)
&& isIdentifier(firstSegment.expression)
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
&& firstSegment.expression.escapedText === helperName;
}
function partitionSpread(node: Expression) {
return isSpreadElement(node)
? visitSpanOfSpreads
+24
View File
@@ -77,6 +77,8 @@ namespace ts {
return visitObjectLiteralExpression(node as ObjectLiteralExpression);
case SyntaxKind.BinaryExpression:
return visitBinaryExpression(node as BinaryExpression, noDestructuringValue);
case SyntaxKind.CatchClause:
return visitCatchClause(node as CatchClause);
case SyntaxKind.VariableDeclaration:
return visitVariableDeclaration(node as VariableDeclaration);
case SyntaxKind.ForOfStatement:
@@ -272,6 +274,28 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
function visitCatchClause(node: CatchClause) {
if (node.variableDeclaration &&
isBindingPattern(node.variableDeclaration.name) &&
node.variableDeclaration.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
const name = getGeneratedNameForNode(node.variableDeclaration.name);
const updatedDecl = updateVariableDeclaration(node.variableDeclaration, node.variableDeclaration.name, /*type*/ undefined, name);
const visitedBindings = flattenDestructuringBinding(updatedDecl, visitor, context, FlattenLevel.ObjectRest);
let block = visitNode(node.block, visitor, isBlock);
if (some(visitedBindings)) {
block = updateBlock(block, [
createVariableStatement(/*modifiers*/ undefined, visitedBindings),
...block.statements,
]);
}
return updateCatchClause(
node,
updateVariableDeclaration(node.variableDeclaration, name, /*type*/ undefined, /*initializer*/ undefined),
block);
}
return visitEachChild(node, visitor, context);
}
/**
* Visits a VariableDeclaration node with a binding pattern.
*
+146 -446
View File
@@ -64,6 +64,7 @@ namespace ts {
let currentLexicalScope: SourceFile | Block | ModuleBlock | CaseBlock;
let currentNameScope: ClassDeclaration | undefined;
let currentScopeFirstDeclarationsOfName: UnderscoreEscapedMap<Node> | undefined;
let currentClassHasParameterProperties: boolean | undefined;
/**
* Keeps track of whether expression substitution has been enabled for specific edge cases.
@@ -83,12 +84,6 @@ namespace ts {
*/
let applicableSubstitutions: TypeScriptSubstitutionFlags;
/**
* Tracks what computed name expressions originating from elided names must be inlined
* at the next execution site, in document order
*/
let pendingExpressions: Expression[] | undefined;
return transformSourceFileOrBundle;
function transformSourceFileOrBundle(node: SourceFile | Bundle) {
@@ -136,6 +131,7 @@ namespace ts {
const savedCurrentScope = currentLexicalScope;
const savedCurrentNameScope = currentNameScope;
const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName;
const savedCurrentClassHasParameterProperties = currentClassHasParameterProperties;
// Handle state changes before visiting a node.
onBeforeVisitNode(node);
@@ -149,6 +145,7 @@ namespace ts {
currentLexicalScope = savedCurrentScope;
currentNameScope = savedCurrentNameScope;
currentClassHasParameterProperties = savedCurrentClassHasParameterProperties;
return visited;
}
@@ -315,12 +312,12 @@ namespace ts {
function classElementVisitorWorker(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.Constructor:
// TypeScript constructors are transformed in `visitClassDeclaration`.
// We elide them here as `visitorWorker` checks transform flags, which could
// erronously include an ES6 constructor without TypeScript syntax.
return undefined;
return visitConstructor(node as ConstructorDeclaration);
case SyntaxKind.PropertyDeclaration:
// Property declarations are not TypeScript syntax, but they must be visited
// for the decorator transformation.
return visitPropertyDeclaration(node as PropertyDeclaration);
case SyntaxKind.IndexSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
@@ -437,7 +434,6 @@ namespace ts {
// - decorators
// - optional `implements` heritage clause
// - parameter property assignments in the constructor
// - property declarations
// - index signatures
// - method overload signatures
return visitClassDeclaration(<ClassDeclaration>node);
@@ -449,7 +445,6 @@ namespace ts {
// - decorators
// - optional `implements` heritage clause
// - parameter property assignments in the constructor
// - property declarations
// - index signatures
// - method overload signatures
return visitClassExpression(<ClassExpression>node);
@@ -612,9 +607,6 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
const savedPendingExpressions = pendingExpressions;
pendingExpressions = undefined;
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
const facts = getClassFacts(node, staticProperties);
@@ -624,25 +616,11 @@ namespace ts {
const name = node.name || (facts & ClassFacts.NeedsName ? getGeneratedNameForNode(node) : undefined);
const classStatement = facts & ClassFacts.HasConstructorDecorators
? createClassDeclarationHeadWithDecorators(node, name, facts)
? createClassDeclarationHeadWithDecorators(node, name)
: createClassDeclarationHeadWithoutDecorators(node, name, facts);
let statements: Statement[] = [classStatement];
// Write any pending expressions from elided or moved computed property names
if (some(pendingExpressions)) {
statements.push(createExpressionStatement(inlineExpressions(pendingExpressions!)));
}
pendingExpressions = savedPendingExpressions;
// Emit static property assignment. Because classDeclaration is lexically evaluated,
// it is safe to emit static property assignment after classDeclaration
// From ES6 specification:
// HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using
// a lexical declaration such as a LexicalDeclaration or a ClassDeclaration.
if (facts & ClassFacts.HasStaticInitializedProperties) {
addInitializedPropertyStatements(statements, staticProperties, facts & ClassFacts.UseImmediatelyInvokedFunctionExpression ? getInternalName(node) : getLocalName(node));
}
// Write any decorators of the node.
addClassElementDecorationStatements(statements, node, /*isStatic*/ false);
@@ -745,7 +723,7 @@ namespace ts {
name,
/*typeParameters*/ undefined,
visitNodes(node.heritageClauses, visitor, isHeritageClause),
transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0)
transformClassMembers(node)
);
// To better align with the old emitter, we should not emit a trailing source map
@@ -755,6 +733,7 @@ namespace ts {
emitFlags |= EmitFlags.NoTrailingSourceMap;
}
aggregateTransformFlags(classDeclaration);
setTextRange(classDeclaration, node);
setOriginalNode(classDeclaration, node);
setEmitFlags(classDeclaration, emitFlags);
@@ -765,7 +744,7 @@ namespace ts {
* Transforms a decorated class declaration and appends the resulting statements. If
* the class requires an alias to avoid issues with double-binding, the alias is returned.
*/
function createClassDeclarationHeadWithDecorators(node: ClassDeclaration, name: Identifier | undefined, facts: ClassFacts) {
function createClassDeclarationHeadWithDecorators(node: ClassDeclaration, name: Identifier | undefined) {
// When we emit an ES6 class that has a class decorator, we must tailor the
// emit to certain specific cases.
//
@@ -860,8 +839,9 @@ namespace ts {
// ${members}
// }
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
const members = transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0);
const members = transformClassMembers(node);
const classExpression = createClassExpression(/*modifiers*/ undefined, name, /*typeParameters*/ undefined, heritageClauses, members);
aggregateTransformFlags(classExpression);
setOriginalNode(classExpression, node);
setTextRange(classExpression, location);
@@ -888,49 +868,18 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
const savedPendingExpressions = pendingExpressions;
pendingExpressions = undefined;
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
const members = transformClassMembers(node, some(heritageClauses, c => c.token === SyntaxKind.ExtendsKeyword));
const classExpression = createClassExpression(
/*modifiers*/ undefined,
node.name,
/*typeParameters*/ undefined,
heritageClauses,
members
visitNodes(node.heritageClauses, visitor, isHeritageClause),
transformClassMembers(node)
);
aggregateTransformFlags(classExpression);
setOriginalNode(classExpression, node);
setTextRange(classExpression, node);
if (some(staticProperties) || some(pendingExpressions)) {
const expressions: Expression[] = [];
const isClassWithConstructorReference = resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference;
const temp = createTempVariable(hoistVariableDeclaration, !!isClassWithConstructorReference);
if (isClassWithConstructorReference) {
// record an alias as the class name is not in scope for statics.
enableSubstitutionForClassAliases();
const alias = getSynthesizedClone(temp);
alias.autoGenerateFlags &= ~GeneratedIdentifierFlags.ReservedInNestedScopes;
classAliases[getOriginalNodeId(node)] = alias;
}
// To preserve the behavior of the old emitter, we explicitly indent
// the body of a class with static initializers.
setEmitFlags(classExpression, EmitFlags.Indented | getEmitFlags(classExpression));
expressions.push(startOnNewLine(createAssignment(temp, classExpression)));
// Add any pending expressions leftover from elided or relocated computed property names
addRange(expressions, map(pendingExpressions, startOnNewLine));
pendingExpressions = savedPendingExpressions;
addRange(expressions, generateInitializedPropertyExpressions(staticProperties, temp));
expressions.push(startOnNewLine(temp));
return inlineExpressions(expressions);
}
pendingExpressions = savedPendingExpressions;
return classExpression;
}
@@ -938,344 +887,31 @@ namespace ts {
* Transforms the members of a class.
*
* @param node The current class.
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
*/
function transformClassMembers(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
function transformClassMembers(node: ClassDeclaration | ClassExpression) {
const members: ClassElement[] = [];
const constructor = transformConstructor(node, isDerivedClass);
if (constructor) {
members.push(constructor);
const constructor = getFirstConstructorWithBody(node);
const parametersWithPropertyAssignments = constructor &&
filter(constructor.parameters, isParameterPropertyDeclaration);
if (parametersWithPropertyAssignments) {
for (const parameter of parametersWithPropertyAssignments) {
if (isIdentifier(parameter.name)) {
members.push(aggregateTransformFlags(createProperty(
/*decorators*/ undefined,
/*modifiers*/ undefined,
parameter.name,
/*questionOrExclamationToken*/ undefined,
/*type*/ undefined,
/*initializer*/ undefined)));
}
}
}
addRange(members, visitNodes(node.members, classElementVisitor, isClassElement));
return setTextRange(createNodeArray(members), /*location*/ node.members);
}
/**
* Transforms (or creates) a constructor for a class.
*
* @param node The current class.
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
*/
function transformConstructor(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
// Check if we have property assignment inside class declaration.
// If there is a property assignment, we need to emit constructor whether users define it or not
// If there is no property assignment, we can omit constructor if users do not define it
const constructor = getFirstConstructorWithBody(node);
const hasInstancePropertyWithInitializer = forEach(node.members, isInstanceInitializedProperty);
const hasParameterPropertyAssignments = constructor &&
constructor.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax &&
forEach(constructor.parameters, isParameterWithPropertyAssignment);
// If the class does not contain nodes that require a synthesized constructor,
// accept the current constructor if it exists.
if (!hasInstancePropertyWithInitializer && !hasParameterPropertyAssignments) {
return visitEachChild(constructor, visitor, context);
}
const parameters = transformConstructorParameters(constructor);
const body = transformConstructorBody(node, constructor, isDerivedClass);
// constructor(${parameters}) {
// ${body}
// }
return startOnNewLine(
setOriginalNode(
setTextRange(
createConstructor(
/*decorators*/ undefined,
/*modifiers*/ undefined,
parameters,
body
),
constructor || node
),
constructor
)
);
}
/**
* Transforms (or creates) the parameters for the constructor of a class with
* parameter property assignments or instance property initializers.
*
* @param constructor The constructor declaration.
*/
function transformConstructorParameters(constructor: ConstructorDeclaration | undefined) {
// The ES2015 spec specifies in 14.5.14. Runtime Semantics: ClassDefinitionEvaluation:
// If constructor is empty, then
// If ClassHeritag_eopt is present and protoParent is not null, then
// Let constructor be the result of parsing the source text
// constructor(...args) { super (...args);}
// using the syntactic grammar with the goal symbol MethodDefinition[~Yield].
// Else,
// Let constructor be the result of parsing the source text
// constructor( ){ }
// using the syntactic grammar with the goal symbol MethodDefinition[~Yield].
//
// While we could emit the '...args' rest parameter, certain later tools in the pipeline might
// downlevel the '...args' portion less efficiently by naively copying the contents of 'arguments' to an array.
// Instead, we'll avoid using a rest parameter and spread into the super call as
// 'super(...arguments)' instead of 'super(...args)', as you can see in "transformConstructorBody".
return visitParameterList(constructor && constructor.parameters, visitor, context)
|| <ParameterDeclaration[]>[];
}
/**
* Transforms (or creates) a constructor body for a class with parameter property
* assignments or instance property initializers.
*
* @param node The current class.
* @param constructor The current class constructor.
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
*/
function transformConstructorBody(node: ClassExpression | ClassDeclaration, constructor: ConstructorDeclaration | undefined, isDerivedClass: boolean) {
let statements: Statement[] = [];
let indexOfFirstStatement = 0;
resumeLexicalEnvironment();
if (constructor) {
indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements);
// Add parameters with property assignments. Transforms this:
//
// constructor (public x, public y) {
// }
//
// Into this:
//
// constructor (x, y) {
// this.x = x;
// this.y = y;
// }
//
const propertyAssignments = getParametersWithPropertyAssignments(constructor);
addRange(statements, map(propertyAssignments, transformParameterWithPropertyAssignment));
}
else if (isDerivedClass) {
// Add a synthetic `super` call:
//
// super(...arguments);
//
statements.push(
createExpressionStatement(
createCall(
createSuper(),
/*typeArguments*/ undefined,
[createSpread(createIdentifier("arguments"))]
)
)
);
}
// Add the property initializers. Transforms this:
//
// public x = 1;
//
// Into this:
//
// constructor() {
// this.x = 1;
// }
//
const properties = getInitializedProperties(node, /*isStatic*/ false);
addInitializedPropertyStatements(statements, properties, createThis());
if (constructor) {
// The class already had a constructor, so we should add the existing statements, skipping the initial super call.
addRange(statements, visitNodes(constructor.body!.statements, visitor, isStatement, indexOfFirstStatement));
}
// End the lexical environment.
statements = mergeLexicalEnvironment(statements, endLexicalEnvironment());
return setTextRange(
createBlock(
setTextRange(
createNodeArray(statements),
/*location*/ constructor ? constructor.body!.statements : node.members
),
/*multiLine*/ true
),
/*location*/ constructor ? constructor.body : undefined
);
}
/**
* Adds super call and preceding prologue directives into the list of statements.
*
* @param ctor The constructor node.
* @returns index of the statement that follows super call
*/
function addPrologueDirectivesAndInitialSuperCall(ctor: ConstructorDeclaration, result: Statement[]): number {
if (ctor.body) {
const statements = ctor.body.statements;
// add prologue directives to the list (if any)
const index = addPrologue(result, statements, /*ensureUseStrict*/ false, visitor);
if (index === statements.length) {
// list contains nothing but prologue directives (or empty) - exit
return index;
}
const statement = statements[index];
if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((<ExpressionStatement>statement).expression)) {
result.push(visitNode(statement, visitor, isStatement));
return index + 1;
}
return index;
}
return 0;
}
/**
* Gets all parameters of a constructor that should be transformed into property assignments.
*
* @param node The constructor node.
*/
function getParametersWithPropertyAssignments(node: ConstructorDeclaration): ReadonlyArray<ParameterDeclaration> {
return filter(node.parameters, isParameterWithPropertyAssignment);
}
/**
* Determines whether a parameter should be transformed into a property assignment.
*
* @param parameter The parameter node.
*/
function isParameterWithPropertyAssignment(parameter: ParameterDeclaration) {
return hasModifier(parameter, ModifierFlags.ParameterPropertyModifier)
&& isIdentifier(parameter.name);
}
/**
* Transforms a parameter into a property assignment statement.
*
* @param node The parameter declaration.
*/
function transformParameterWithPropertyAssignment(node: ParameterDeclaration) {
Debug.assert(isIdentifier(node.name));
const name = node.name as Identifier;
const propertyName = getMutableClone(name);
setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoSourceMap);
const localName = getMutableClone(name);
setEmitFlags(localName, EmitFlags.NoComments);
return startOnNewLine(
setEmitFlags(
setTextRange(
createExpressionStatement(
createAssignment(
setTextRange(
createPropertyAccess(
createThis(),
propertyName
),
node.name
),
localName
)
),
moveRangePos(node, -1)
),
EmitFlags.NoComments
)
);
}
/**
* Gets all property declarations with initializers on either the static or instance side of a class.
*
* @param node The class node.
* @param isStatic A value indicating whether to get properties from the static or instance side of the class.
*/
function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray<PropertyDeclaration> {
return filter(node.members, isStatic ? isStaticInitializedProperty : isInstanceInitializedProperty);
}
/**
* Gets a value indicating whether a class element is a static property declaration with an initializer.
*
* @param member The class element node.
*/
function isStaticInitializedProperty(member: ClassElement): member is PropertyDeclaration {
return isInitializedProperty(member, /*isStatic*/ true);
}
/**
* Gets a value indicating whether a class element is an instance property declaration with an initializer.
*
* @param member The class element node.
*/
function isInstanceInitializedProperty(member: ClassElement): member is PropertyDeclaration {
return isInitializedProperty(member, /*isStatic*/ false);
}
/**
* Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer.
*
* @param member The class element node.
* @param isStatic A value indicating whether the member should be a static or instance member.
*/
function isInitializedProperty(member: ClassElement, isStatic: boolean) {
return member.kind === SyntaxKind.PropertyDeclaration
&& isStatic === hasModifier(member, ModifierFlags.Static)
&& (<PropertyDeclaration>member).initializer !== undefined;
}
/**
* Generates assignment statements for property initializers.
*
* @param properties An array of property declarations to transform.
* @param receiver The receiver on which each property should be assigned.
*/
function addInitializedPropertyStatements(statements: Statement[], properties: ReadonlyArray<PropertyDeclaration>, receiver: LeftHandSideExpression) {
for (const property of properties) {
const statement = createExpressionStatement(transformInitializedProperty(property, receiver));
setSourceMapRange(statement, moveRangePastModifiers(property));
setCommentRange(statement, property);
setOriginalNode(statement, property);
statements.push(statement);
}
}
/**
* Generates assignment expressions for property initializers.
*
* @param properties An array of property declarations to transform.
* @param receiver The receiver on which each property should be assigned.
*/
function generateInitializedPropertyExpressions(properties: ReadonlyArray<PropertyDeclaration>, receiver: LeftHandSideExpression) {
const expressions: Expression[] = [];
for (const property of properties) {
const expression = transformInitializedProperty(property, receiver);
startOnNewLine(expression);
setSourceMapRange(expression, moveRangePastModifiers(property));
setCommentRange(expression, property);
setOriginalNode(expression, property);
expressions.push(expression);
}
return expressions;
}
/**
* Transforms a property initializer into an assignment statement.
*
* @param property The property declaration.
* @param receiver The object receiving the property assignment.
*/
function transformInitializedProperty(property: PropertyDeclaration, receiver: LeftHandSideExpression) {
// We generate a name here in order to reuse the value cached by the relocated computed name expression (which uses the same generated name)
const propertyName = isComputedPropertyName(property.name) && !isSimpleInlineableExpression(property.name.expression)
? updateComputedPropertyName(property.name, getGeneratedNameForNode(property.name))
: property.name;
const initializer = visitNode(property.initializer!, visitor, isExpression);
const memberAccess = createMemberAccessForPropertyName(receiver, propertyName, /*location*/ propertyName);
return createAssignment(memberAccess, initializer);
}
/**
* Gets either the static or instance members of a class that are decorated, or have
@@ -2144,16 +1780,6 @@ namespace ts {
: createIdentifier("BigInt");
}
/**
* A simple inlinable expression is an expression which can be copied into multiple locations
* without risk of repeating any sideeffects and whose value could not possibly change between
* any such locations
*/
function isSimpleInlineableExpression(expression: Expression) {
return !isIdentifier(expression) && isSimpleCopiableExpression(expression) ||
isWellKnownSymbolSyntactically(expression);
}
/**
* Gets an expression that represents a property name. For a computed property, a
* name is generated for the node.
@@ -2175,26 +1801,6 @@ namespace ts {
}
}
/**
* If the name is a computed property, this function transforms it, then either returns an expression which caches the
* value of the result or the expression itself if the value is either unused or safe to inline into multiple locations
* @param shouldHoist Does the expression need to be reused? (ie, for an initializer or a decorator)
* @param omitSimple Should expressions with no observable side-effects be elided? (ie, the expression is not hoisted for a decorator or initializer and is a literal)
*/
function getPropertyNameExpressionIfNeeded(name: PropertyName, shouldHoist: boolean, omitSimple: boolean): Expression | undefined {
if (isComputedPropertyName(name)) {
const expression = visitNode(name.expression, visitor, isExpression);
const innerExpression = skipPartiallyEmittedExpressions(expression);
const inlinable = isSimpleInlineableExpression(innerExpression);
if (!inlinable && shouldHoist) {
const generatedName = getGeneratedNameForNode(name);
hoistVariableDeclaration(generatedName);
return createAssignment(generatedName, expression);
}
return (omitSimple && (inlinable || isIdentifier(innerExpression))) ? undefined : expression;
}
}
/**
* Visits the property name of a class element, for use when emitting property
* initializers. For a computed property on a node with decorators, a temporary
@@ -2204,18 +1810,20 @@ namespace ts {
*/
function visitPropertyNameOfClassElement(member: ClassElement): PropertyName {
const name = member.name!;
let expr = getPropertyNameExpressionIfNeeded(name, some(member.decorators), /*omitSimple*/ false);
if (expr) { // expr only exists if `name` is a computed property name
// Inline any pending expressions from previous elided or relocated computed property name expressions in order to preserve execution order
if (some(pendingExpressions)) {
expr = inlineExpressions([...pendingExpressions, expr]);
pendingExpressions.length = 0;
// Computed property names need to be transformed into a hoisted variable when they are used more than once.
// The names are used more than once when:
// - the property is non-static and its initializer is moved to the constructor (when there are parameter property assignments).
// - the property has a decorator.
if (isComputedPropertyName(name) && ((!hasStaticModifier(member) && currentClassHasParameterProperties) || some(member.decorators))) {
const expression = visitNode(name.expression, visitor, isExpression);
const innerExpression = skipPartiallyEmittedExpressions(expression);
if (!isSimpleInlineableExpression(innerExpression)) {
const generatedName = getGeneratedNameForNode(name);
hoistVariableDeclaration(generatedName);
return updateComputedPropertyName(name, createAssignment(generatedName, expression));
}
return updateComputedPropertyName(name as ComputedPropertyName, expr);
}
else {
return name;
}
return visitNode(name, visitor, isPropertyName);
}
/**
@@ -2257,16 +1865,27 @@ namespace ts {
*
* @param node The declaration node.
*/
function shouldEmitFunctionLikeDeclaration(node: FunctionLikeDeclaration) {
function shouldEmitFunctionLikeDeclaration<T extends FunctionLikeDeclaration>(node: T): node is T & { body: NonNullable<T["body"]> } {
return !nodeIsMissing(node.body);
}
function visitPropertyDeclaration(node: PropertyDeclaration): undefined {
const expr = getPropertyNameExpressionIfNeeded(node.name, some(node.decorators) || !!node.initializer, /*omitSimple*/ true);
if (expr && !isSimpleInlineableExpression(expr)) {
(pendingExpressions || (pendingExpressions = [])).push(expr);
function visitPropertyDeclaration(node: PropertyDeclaration) {
const updated = updateProperty(
node,
/*decorators*/ undefined,
visitNodes(node.modifiers, visitor, isModifier),
visitPropertyNameOfClassElement(node),
/*questionOrExclamationToken*/ undefined,
/*type*/ undefined,
visitNode(node.initializer, visitor)
);
if (updated !== node) {
// While we emit the source map for the node after skipping decorators and modifiers,
// we need to emit the comments for the original range.
setCommentRange(updated, node);
setSourceMapRange(updated, moveRangePastDecorators(node));
}
return undefined;
return updated;
}
function visitConstructor(node: ConstructorDeclaration) {
@@ -2276,10 +1895,90 @@ namespace ts {
return updateConstructor(
node,
visitNodes(node.decorators, visitor, isDecorator),
visitNodes(node.modifiers, visitor, isModifier),
/*decorators*/ undefined,
/*modifiers*/ undefined,
visitParameterList(node.parameters, visitor, context),
visitFunctionBody(node.body, visitor, context)
transformConstructorBody(node.body, node)
);
}
function transformConstructorBody(body: Block, constructor: ConstructorDeclaration) {
const parametersWithPropertyAssignments = constructor &&
filter(constructor.parameters, isParameterPropertyDeclaration);
if (!some(parametersWithPropertyAssignments)) {
return visitFunctionBody(body, visitor, context);
}
let statements: Statement[] = [];
let indexOfFirstStatement = 0;
resumeLexicalEnvironment();
indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements, visitor);
// Add parameters with property assignments. Transforms this:
//
// constructor (public x, public y) {
// }
//
// Into this:
//
// constructor (x, y) {
// this.x = x;
// this.y = y;
// }
//
addRange(statements, map(parametersWithPropertyAssignments, transformParameterWithPropertyAssignment));
// Add the existing statements, skipping the initial super call.
addRange(statements, visitNodes(body.statements, visitor, isStatement, indexOfFirstStatement));
// End the lexical environment.
statements = mergeLexicalEnvironment(statements, endLexicalEnvironment());
const block = createBlock(setTextRange(createNodeArray(statements), body.statements), /*multiLine*/ true);
setTextRange(block, /*location*/ body);
setOriginalNode(block, body);
return block;
}
/**
* Transforms a parameter into a property assignment statement.
*
* @param node The parameter declaration.
*/
function transformParameterWithPropertyAssignment(node: ParameterPropertyDeclaration) {
const name = node.name;
if (!isIdentifier(name)) {
return undefined;
}
const propertyName = getMutableClone(name);
setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoSourceMap);
const localName = getMutableClone(name);
setEmitFlags(localName, EmitFlags.NoComments);
return startOnNewLine(
removeAllComments(
setTextRange(
setOriginalNode(
createExpressionStatement(
createAssignment(
setTextRange(
createPropertyAccess(
createThis(),
propertyName
),
node.name
),
localName
)
),
node
),
moveRangePos(node, -1)
)
)
);
}
@@ -2418,6 +2117,7 @@ namespace ts {
if (parameterIsThisKeyword(node)) {
return undefined;
}
const updated = updateParameter(
node,
/*decorators*/ undefined,
+80
View File
@@ -240,6 +240,47 @@ namespace ts {
isIdentifier(expression);
}
/**
* A simple inlinable expression is an expression which can be copied into multiple locations
* without risk of repeating any sideeffects and whose value could not possibly change between
* any such locations
*/
export function isSimpleInlineableExpression(expression: Expression) {
return !isIdentifier(expression) && isSimpleCopiableExpression(expression) ||
isWellKnownSymbolSyntactically(expression);
}
/**
* Adds super call and preceding prologue directives into the list of statements.
*
* @param ctor The constructor node.
* @param result The list of statements.
* @param visitor The visitor to apply to each node added to the result array.
* @returns index of the statement that follows super call
*/
export function addPrologueDirectivesAndInitialSuperCall(ctor: ConstructorDeclaration, result: Statement[], visitor: Visitor): number {
if (ctor.body) {
const statements = ctor.body.statements;
// add prologue directives to the list (if any)
const index = addPrologue(result, statements, /*ensureUseStrict*/ false, visitor);
if (index === statements.length) {
// list contains nothing but prologue directives (or empty) - exit
return index;
}
const statement = statements[index];
if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((<ExpressionStatement>statement).expression)) {
result.push(visitNode(statement, visitor, isStatement));
return index + 1;
}
return index;
}
return 0;
}
/**
* @param input Template string input strings
* @param args Names which need to be made file-level unique
@@ -255,4 +296,43 @@ namespace ts {
return result;
};
}
/**
* Gets all property declarations with initializers on either the static or instance side of a class.
*
* @param node The class node.
* @param isStatic A value indicating whether to get properties from the static or instance side of the class.
*/
export function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray<PropertyDeclaration> {
return filter(node.members, isStatic ? isStaticInitializedProperty : isInstanceInitializedProperty);
}
/**
* Gets a value indicating whether a class element is a static property declaration with an initializer.
*
* @param member The class element node.
*/
export function isStaticInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } {
return isInitializedProperty(member) && hasStaticModifier(member);
}
/**
* Gets a value indicating whether a class element is an instance property declaration with an initializer.
*
* @param member The class element node.
*/
export function isInstanceInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } {
return isInitializedProperty(member) && !hasStaticModifier(member);
}
/**
* Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer.
*
* @param member The class element node.
* @param isStatic A value indicating whether the member should be a static or instance member.
*/
export function isInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } {
return member.kind === SyntaxKind.PropertyDeclaration
&& (<PropertyDeclaration>member).initializer !== undefined;
}
}
+1727 -1232
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -8,6 +8,7 @@
"files": [
"core.ts",
"debug.ts",
"performance.ts",
"semver.ts",
@@ -29,6 +30,7 @@
"transformers/utilities.ts",
"transformers/destructuring.ts",
"transformers/ts.ts",
"transformers/classFields.ts",
"transformers/es2017.ts",
"transformers/es2018.ts",
"transformers/es2019.ts",
+45 -24
View File
@@ -2546,6 +2546,8 @@ namespace ts {
Shared = 1 << 10, // Referenced as antecedent more than once
PreFinally = 1 << 11, // Injected edge that links pre-finally label and pre-try flow
AfterFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph
/** @internal */
Cached = 1 << 13, // Indicates that at least one cross-call cache entry exists for this node, even if not a loop participant
Label = BranchLabel | LoopLabel,
Condition = TrueCondition | FalseCondition
}
@@ -2973,7 +2975,7 @@ namespace ts {
// For testing purposes only.
/* @internal */ structureIsReused?: StructureIsReused;
/* @internal */ getSourceFileFromReference(referencingFile: SourceFile, ref: FileReference): SourceFile | undefined;
/* @internal */ getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined;
/* @internal */ getLibFileFromReference(ref: FileReference): SourceFile | undefined;
/** Given a source file, get the name of the package it was imported from. */
@@ -3066,6 +3068,9 @@ namespace ts {
// Diagnostics were produced and outputs were generated in spite of them.
DiagnosticsPresent_OutputsGenerated = 2,
// When build skipped because passed in project is invalid
InvalidProject_OutputsSkipped = 3,
}
export interface EmitResult {
@@ -3152,6 +3157,7 @@ namespace ts {
*/
getExportSymbolOfSymbol(symbol: Symbol): Symbol;
getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol | undefined;
getTypeOfAssignmentPattern(pattern: AssignmentPattern): Type;
getTypeAtLocation(node: Node): Type;
getTypeFromTypeNode(node: TypeNode): Type;
@@ -3749,6 +3755,8 @@ namespace ts {
extendedContainers?: Symbol[]; // Containers (other than the parent) which this symbol is aliased in
extendedContainersByFile?: Map<Symbol[]>; // Containers (other than the parent) which this symbol is aliased in
variances?: VarianceFlags[]; // Alias symbol type argument variance cache
deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type
deferralParent?: Type; // Source union/intersection of a deferred type
}
/* @internal */
@@ -3775,6 +3783,7 @@ namespace ts {
ReverseMapped = 1 << 13, // Property of reverse-inferred homomorphic mapped type
OptionalParameter = 1 << 14, // Optional parameter
RestParameter = 1 << 15, // Rest parameter
DeferredType = 1 << 16, // Calculation of the type of this symbol is deferred due to processing costs, should be fetched with `getTypeOfSymbolWithDeferredType`
Synthetic = SyntheticProperty | SyntheticMethod,
Discriminant = HasNonUniformType | HasLiteralType,
Partial = ReadPartial | WritePartial
@@ -3964,6 +3973,8 @@ namespace ts {
StructuredOrInstantiable = StructuredType | Instantiable,
/* @internal */
ObjectFlagsType = Nullable | Never | Object | Union | Intersection,
/* @internal */
Simplifiable = IndexedAccess | Conditional,
// 'Narrowable' types are types where narrowing actually narrows.
// This *should* be every type other than null, undefined, void, and never
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
@@ -3972,7 +3983,7 @@ namespace ts {
NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | StructuredOrInstantiable,
// The following flags are aggregated during union and intersection type construction
/* @internal */
IncludesMask = Any | Unknown | Primitive | Never | Object | Union,
IncludesMask = Any | Unknown | Primitive | Never | Object | Union | NonPrimitive,
// The following flags are used for different purposes during union and intersection type construction
/* @internal */
IncludesStructuredOrInstantiable = TypeParameter,
@@ -4005,6 +4016,8 @@ namespace ts {
restrictiveInstantiation?: Type; // Instantiation with type parameters mapped to unconstrained form
/* @internal */
immediateBaseConstraint?: Type; // Immediate base constraint cache
/* @internal */
widened?: Type; // Cached widened form of the type
}
/* @internal */
@@ -4076,19 +4089,20 @@ namespace ts {
MarkerType = 1 << 13, // Marker type used for variance probing
JSLiteral = 1 << 14, // Object type declared in JS - disables errors on read/write of nonexisting members
FreshLiteral = 1 << 15, // Fresh object literal
ArrayLiteral = 1 << 16, // Originates in an array literal
/* @internal */
PrimitiveUnion = 1 << 16, // Union of only primitive types
PrimitiveUnion = 1 << 17, // Union of only primitive types
/* @internal */
ContainsWideningType = 1 << 17, // Type is or contains undefined or null widening type
ContainsWideningType = 1 << 18, // Type is or contains undefined or null widening type
/* @internal */
ContainsObjectLiteral = 1 << 18, // Type is or contains object literal type
ContainsObjectOrArrayLiteral = 1 << 19, // Type is or contains object literal type
/* @internal */
NonInferrableType = 1 << 19, // Type is or contains anyFunctionType or silentNeverType
NonInferrableType = 1 << 20, // Type is or contains anyFunctionType or silentNeverType
ClassOrInterface = Class | Interface,
/* @internal */
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
RequiresWidening = ContainsWideningType | ContainsObjectOrArrayLiteral,
/* @internal */
PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | NonInferrableType
PropagatingFlags = ContainsWideningType | ContainsObjectOrArrayLiteral | NonInferrableType
}
/* @internal */
@@ -4141,6 +4155,8 @@ namespace ts {
export interface TypeReference extends ObjectType {
target: GenericType; // Type reference target
typeArguments?: ReadonlyArray<Type>; // Type reference type arguments (undefined if none)
/* @internal */
literalType?: TypeReference; // Clone of type with ObjectFlags.ArrayLiteral set
}
/* @internal */
@@ -4336,8 +4352,8 @@ namespace ts {
root: ConditionalRoot;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
resolvedTrueType: Type;
resolvedFalseType: Type;
/* @internal */
resolvedInferredTrueType?: Type; // The `trueType` instantiated with the `combinedMapper`, if present
/* @internal */
@@ -4422,15 +4438,16 @@ namespace ts {
export type TypeMapper = (t: TypeParameter) => Type;
export const enum InferencePriority {
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
MappedTypeConstraint = 1 << 2, // Reverse inference for mapped type
ReturnType = 1 << 3, // Inference made from return type of generic function
LiteralKeyof = 1 << 4, // Inference made from a string literal to a keyof T
NoConstraints = 1 << 5, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 6, // Always use strict rules for contravariant inferences
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
PartialHomomorphicMappedType = 1 << 2, // Partial reverse inference for homomorphic mapped type
MappedTypeConstraint = 1 << 3, // Reverse inference for mapped type
ReturnType = 1 << 4, // Inference made from return type of generic function
LiteralKeyof = 1 << 5, // Inference made from a string literal to a keyof T
NoConstraints = 1 << 6, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 7, // Always use strict rules for contravariant inferences
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
}
/* @internal */
@@ -5183,6 +5200,7 @@ namespace ts {
ContainsYield = 1 << 17,
ContainsHoistedDeclarationOrCompletion = 1 << 18,
ContainsDynamicImport = 1 << 19,
ContainsClassFields = 1 << 20,
// Please leave this as 1 << 29.
// It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system.
@@ -5325,12 +5343,13 @@ namespace ts {
Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations)
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations)
Await = 1 << 11, // __await (used by ES2017 async generator transformation)
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation)
MakeTemplateObject = 1 << 16, // __makeTemplateObject (used for constructing template string array objects)
SpreadArrays = 1 << 11, // __spreadArrays (used by ES2015 array spread and argument list spread transformations)
Await = 1 << 12, // __await (used by ES2017 async generator transformation)
AsyncGenerator = 1 << 13, // __asyncGenerator (used by ES2017 async generator transformation)
AsyncDelegator = 1 << 14, // __asyncDelegator (used by ES2017 async generator yield* transformation)
AsyncValues = 1 << 15, // __asyncValues (used by ES2017 for..await..of transformation)
ExportStar = 1 << 16, // __exportStar (used by CommonJS/AMD/UMD module transformation)
MakeTemplateObject = 1 << 17, // __makeTemplateObject (used for constructing template string array objects)
FirstEmitHelper = Extends,
LastEmitHelper = MakeTemplateObject,
@@ -5380,6 +5399,7 @@ namespace ts {
writeFile: WriteFileCallback;
getProgramBuildInfo(): ProgramBuildInfo | undefined;
getSourceFileFromReference: Program["getSourceFileFromReference"];
}
export interface TransformationContext {
@@ -5710,6 +5730,7 @@ namespace ts {
/*@internal*/ writeBundleFileInfo?: boolean;
/*@internal*/ recordInternalSection?: boolean;
/*@internal*/ stripInternal?: boolean;
/*@internal*/ relativeToBuildInfo?: (path: string) => string;
}
/* @internal */
+27 -109
View File
@@ -2073,7 +2073,7 @@ namespace ts {
}
export function importFromModuleSpecifier(node: StringLiteralLike): AnyValidImportOrReExport {
return tryGetImportFromModuleSpecifier(node) || Debug.fail(Debug.showSyntaxKind(node.parent));
return tryGetImportFromModuleSpecifier(node) || Debug.failBadSyntaxKind(node.parent);
}
export function tryGetImportFromModuleSpecifier(node: StringLiteralLike): AnyValidImportOrReExport | undefined {
@@ -2201,13 +2201,13 @@ namespace ts {
let result: (JSDoc | JSDocTag)[] | undefined;
// Pull parameter comments from declaring function as well
if (isVariableLike(hostNode) && hasInitializer(hostNode) && hasJSDocNodes(hostNode.initializer!)) {
result = addRange(result, (hostNode.initializer as HasJSDoc).jsDoc!);
result = append(result, last((hostNode.initializer as HasJSDoc).jsDoc!));
}
let node: Node | undefined = hostNode;
while (node && node.parent) {
if (hasJSDocNodes(node)) {
result = addRange(result, node.jsDoc!);
result = append(result, last(node.jsDoc!));
}
if (node.kind === SyntaxKind.Parameter) {
@@ -2611,13 +2611,6 @@ namespace ts {
return undefined;
}
export function tryResolveScriptReference(host: ScriptReferenceHost, sourceFile: SourceFile | UnparsedSource, reference: FileReference) {
if (!host.getCompilerOptions().noResolve) {
const referenceFileName = isRootedDiskPath(reference.fileName) ? reference.fileName : combinePaths(getDirectoryPath(sourceFile.fileName), reference.fileName);
return host.getSourceFile(referenceFileName);
}
}
export function getAncestor(node: Node | undefined, kind: SyntaxKind): Node | undefined {
while (node) {
if (node.kind === kind) {
@@ -2712,11 +2705,19 @@ namespace ts {
return isStringLiteralLike(node) || isNumericLiteral(node);
}
export function isSignedNumericLiteral(node: Node): node is PrefixUnaryExpression & { operand: NumericLiteral } {
return isPrefixUnaryExpression(node) && (node.operator === SyntaxKind.PlusToken || node.operator === SyntaxKind.MinusToken) && isNumericLiteral(node.operand);
}
/**
* A declaration has a dynamic name if both of the following are true:
* 1. The declaration has a computed property name
* 2. The computed name is *not* expressed as Symbol.<name>, where name
* is a property of the Symbol constructor that denotes a built in
* A declaration has a dynamic name if all of the following are true:
* 1. The declaration has a computed property name.
* 2. The computed name is *not* expressed as a StringLiteral.
* 3. The computed name is *not* expressed as a NumericLiteral.
* 4. The computed name is *not* expressed as a PlusToken or MinusToken
* immediately followed by a NumericLiteral.
* 5. The computed name is *not* expressed as `Symbol.<name>`, where `<name>`
* is a property of the Symbol constructor that denotes a built-in
* Symbol.
*/
export function hasDynamicName(declaration: Declaration): declaration is DynamicNamedDeclaration {
@@ -2727,6 +2728,7 @@ namespace ts {
export function isDynamicName(name: DeclarationName): boolean {
return name.kind === SyntaxKind.ComputedPropertyName &&
!isStringOrNumericLiteralLike(name.expression) &&
!isSignedNumericLiteral(name.expression) &&
!isWellKnownSymbolSyntactically(name.expression);
}
@@ -4186,78 +4188,6 @@ namespace ts {
return getNewLine ? getNewLine() : sys ? sys.newLine : carriageReturnLineFeed;
}
/**
* Formats an enum value as a string for debugging and debug assertions.
*/
function formatEnum(value = 0, enumObject: any, isFlags?: boolean) {
const members = getEnumMembers(enumObject);
if (value === 0) {
return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0";
}
if (isFlags) {
let result = "";
let remainingFlags = value;
for (let i = members.length - 1; i >= 0 && remainingFlags !== 0; i--) {
const [enumValue, enumName] = members[i];
if (enumValue !== 0 && (remainingFlags & enumValue) === enumValue) {
remainingFlags &= ~enumValue;
result = `${enumName}${result ? ", " : ""}${result}`;
}
}
if (remainingFlags === 0) {
return result;
}
}
else {
for (const [enumValue, enumName] of members) {
if (enumValue === value) {
return enumName;
}
}
}
return value.toString();
}
function getEnumMembers(enumObject: any) {
const result: [number, string][] = [];
for (const name in enumObject) {
const value = enumObject[name];
if (typeof value === "number") {
result.push([value, name]);
}
}
return stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0]));
}
export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
return formatEnum(kind, (<any>ts).SyntaxKind, /*isFlags*/ false);
}
export function formatModifierFlags(flags: ModifierFlags | undefined): string {
return formatEnum(flags, (<any>ts).ModifierFlags, /*isFlags*/ true);
}
export function formatTransformFlags(flags: TransformFlags | undefined): string {
return formatEnum(flags, (<any>ts).TransformFlags, /*isFlags*/ true);
}
export function formatEmitFlags(flags: EmitFlags | undefined): string {
return formatEnum(flags, (<any>ts).EmitFlags, /*isFlags*/ true);
}
export function formatSymbolFlags(flags: SymbolFlags | undefined): string {
return formatEnum(flags, (<any>ts).SymbolFlags, /*isFlags*/ true);
}
export function formatTypeFlags(flags: TypeFlags | undefined): string {
return formatEnum(flags, (<any>ts).TypeFlags, /*isFlags*/ true);
}
export function formatObjectFlags(flags: ObjectFlags | undefined): string {
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true);
}
/**
* Creates a new TextRange from the provided pos and end.
*
@@ -5259,6 +5189,9 @@ namespace ts {
return node.parent.left.name;
}
}
else if (isVariableDeclaration(node.parent) && isIdentifier(node.parent.name)) {
return node.parent.name;
}
}
/**
@@ -7646,6 +7579,14 @@ namespace ts {
return root + pathComponents.slice(1).join(directorySeparator);
}
export function getNormalizedAbsolutePathWithoutRoot(fileName: string, currentDirectory: string | undefined) {
return getPathWithoutRoot(getNormalizedPathComponents(fileName, currentDirectory));
}
function getPathWithoutRoot(pathComponents: ReadonlyArray<string>) {
if (pathComponents.length === 0) return "";
return pathComponents.slice(1).join(directorySeparator);
}
}
/* @internal */
@@ -8420,29 +8361,6 @@ namespace ts {
return pathext ? path.slice(0, path.length - pathext.length) + (startsWith(ext, ".") ? ext : "." + ext) : path;
}
export namespace Debug {
export function showSymbol(symbol: Symbol): string {
const symbolFlags = (ts as any).SymbolFlags;
return `{ flags: ${symbolFlags ? showFlags(symbol.flags, symbolFlags) : symbol.flags}; declarations: ${map(symbol.declarations, showSyntaxKind)} }`;
}
function showFlags(flags: number, flagsEnum: { [flag: number]: string }): string {
const out: string[] = [];
for (let pow = 0; pow <= 30; pow++) {
const n = 1 << pow;
if (flags & n) {
out.push(flagsEnum[n]);
}
}
return out.join("|");
}
export function showSyntaxKind(node: Node): string {
const syntaxKind = (ts as any).SyntaxKind;
return syntaxKind ? syntaxKind[node.kind] : node.kind.toString();
}
}
export function tryParsePattern(pattern: string): Pattern | undefined {
// This should be verified outside of here and a proper error thrown.
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
-96
View File
@@ -1558,100 +1558,4 @@ namespace ts {
function aggregateTransformFlagsForChildNodes(transformFlags: TransformFlags, nodes: NodeArray<Node>): TransformFlags {
return transformFlags | aggregateTransformFlagsForNodeArray(nodes);
}
export namespace Debug {
let isDebugInfoEnabled = false;
export function failBadSyntaxKind(node: Node, message?: string): never {
return fail(
`${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`,
failBadSyntaxKind);
}
export const assertEachNode = shouldAssert(AssertionLevel.Normal)
? (nodes: Node[], test: (node: Node) => boolean, message?: string): void => assert(
test === undefined || every(nodes, test),
message || "Unexpected node.",
() => `Node array did not pass test '${getFunctionName(test)}'.`,
assertEachNode)
: noop;
export const assertNode = shouldAssert(AssertionLevel.Normal)
? (node: Node | undefined, test: ((node: Node | undefined) => boolean) | undefined, message?: string): void => assert(
test === undefined || test(node),
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node!.kind)} did not pass test '${getFunctionName(test!)}'.`,
assertNode)
: noop;
export const assertOptionalNode = shouldAssert(AssertionLevel.Normal)
? (node: Node, test: (node: Node) => boolean, message?: string): void => assert(
test === undefined || node === undefined || test(node),
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`,
assertOptionalNode)
: noop;
export const assertOptionalToken = shouldAssert(AssertionLevel.Normal)
? (node: Node, kind: SyntaxKind, message?: string): void => assert(
kind === undefined || node === undefined || node.kind === kind,
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} was not a '${formatSyntaxKind(kind)}' token.`,
assertOptionalToken)
: noop;
export const assertMissingNode = shouldAssert(AssertionLevel.Normal)
? (node: Node, message?: string): void => assert(
node === undefined,
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} was unexpected'.`,
assertMissingNode)
: noop;
/**
* Injects debug information into frequently used types.
*/
export function enableDebugInfo() {
if (isDebugInfoEnabled) return;
// Add additional properties in debug mode to assist with debugging.
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
__debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
});
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
__debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
__debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } },
__debugTypeToString: { value(this: Type) { return this.checker.typeToString(this); } },
});
const nodeConstructors = [
objectAllocator.getNodeConstructor(),
objectAllocator.getIdentifierConstructor(),
objectAllocator.getTokenConstructor(),
objectAllocator.getSourceFileConstructor()
];
for (const ctor of nodeConstructors) {
if (!ctor.prototype.hasOwnProperty("__debugKind")) {
Object.defineProperties(ctor.prototype, {
__debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } },
__debugModifierFlags: { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } },
__debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
__debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
__debugGetText: {
value(this: Node, includeTrivia?: boolean) {
if (nodeIsSynthesized(this)) return "";
const parseNode = getParseTreeNode(this);
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
}
}
});
}
}
isDebugInfoEnabled = true;
}
}
}
+96 -55
View File
@@ -88,8 +88,6 @@ namespace ts {
return result;
}
export type ReportEmitErrorSummary = (errorCount: number) => void;
export function getErrorCountForSummary(diagnostics: ReadonlyArray<Diagnostic>) {
return countWhere(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error);
}
@@ -113,12 +111,12 @@ namespace ts {
getCurrentDirectory(): string;
getCompilerOptions(): CompilerOptions;
getSourceFiles(): ReadonlyArray<SourceFile>;
getSyntacticDiagnostics(): ReadonlyArray<Diagnostic>;
getOptionsDiagnostics(): ReadonlyArray<Diagnostic>;
getGlobalDiagnostics(): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(): ReadonlyArray<Diagnostic>;
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getOptionsDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback): EmitResult;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
}
export function listFiles(program: ProgramToEmitFilesAndReportErrors, writeFileName: (s: string) => void) {
@@ -132,25 +130,35 @@ namespace ts {
/**
* Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options
*/
export function emitFilesAndReportErrors(program: ProgramToEmitFilesAndReportErrors, reportDiagnostic: DiagnosticReporter, writeFileName?: (s: string) => void, reportSummary?: ReportEmitErrorSummary, writeFile?: WriteFileCallback) {
export function emitFilesAndReportErrors(
program: ProgramToEmitFilesAndReportErrors,
reportDiagnostic: DiagnosticReporter,
writeFileName?: (s: string) => void,
reportSummary?: ReportEmitErrorSummary,
writeFile?: WriteFileCallback,
cancellationToken?: CancellationToken,
emitOnlyDtsFiles?: boolean,
customTransformers?: CustomTransformers
) {
// First get and report any syntactic errors.
const diagnostics = program.getConfigFileParsingDiagnostics().slice();
const configFileParsingDiagnosticsLength = diagnostics.length;
addRange(diagnostics, program.getSyntacticDiagnostics());
addRange(diagnostics, program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
// If we didn't have any syntactic errors, then also try getting the global and
// semantic errors.
if (diagnostics.length === configFileParsingDiagnosticsLength) {
addRange(diagnostics, program.getOptionsDiagnostics());
addRange(diagnostics, program.getGlobalDiagnostics());
addRange(diagnostics, program.getOptionsDiagnostics(cancellationToken));
addRange(diagnostics, program.getGlobalDiagnostics(cancellationToken));
if (diagnostics.length === configFileParsingDiagnosticsLength) {
addRange(diagnostics, program.getSemanticDiagnostics());
addRange(diagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
}
}
// Emit and report any errors we ran into.
const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(/*targetSourceFile*/ undefined, writeFile);
const emitResult = program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
const { emittedFiles, diagnostics: emitDiagnostics } = emitResult;
addRange(diagnostics, emitDiagnostics);
sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic);
@@ -167,7 +175,34 @@ namespace ts {
reportSummary(getErrorCountForSummary(diagnostics));
}
if (emitSkipped && diagnostics.length > 0) {
return {
emitResult,
diagnostics,
};
}
export function emitFilesAndReportErrorsAndGetExitStatus(
program: ProgramToEmitFilesAndReportErrors,
reportDiagnostic: DiagnosticReporter,
writeFileName?: (s: string) => void,
reportSummary?: ReportEmitErrorSummary,
writeFile?: WriteFileCallback,
cancellationToken?: CancellationToken,
emitOnlyDtsFiles?: boolean,
customTransformers?: CustomTransformers
) {
const { emitResult, diagnostics } = emitFilesAndReportErrors(
program,
reportDiagnostic,
writeFileName,
reportSummary,
writeFile,
cancellationToken,
emitOnlyDtsFiles,
customTransformers
);
if (emitResult.emitSkipped && diagnostics.length > 0) {
// If the emitter didn't emit anything, then pass that value along.
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
@@ -179,7 +214,7 @@ namespace ts {
return ExitStatus.Success;
}
const noopFileWatcher: FileWatcher = { close: noop };
export const noopFileWatcher: FileWatcher = { close: noop };
export function createWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter): WatchHost {
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
@@ -375,43 +410,6 @@ namespace ts {
return host;
}
export function readBuilderProgram(compilerOptions: CompilerOptions, readFile: (path: string) => string | undefined) {
if (compilerOptions.out || compilerOptions.outFile) return undefined;
const buildInfoPath = getOutputPathForBuildInfo(compilerOptions);
if (!buildInfoPath) return undefined;
const content = readFile(buildInfoPath);
if (!content) return undefined;
const buildInfo = getBuildInfo(content);
if (buildInfo.version !== version) return undefined;
if (!buildInfo.program) return undefined;
return createBuildProgramUsingProgramBuildInfo(buildInfo.program);
}
export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost {
const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system);
host.createHash = maybeBind(system, system.createHash);
setGetSourceFileAsHashVersioned(host, system);
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName));
return host;
}
interface IncrementalProgramOptions<T extends BuilderProgram> {
rootNames: ReadonlyArray<string>;
options: CompilerOptions;
configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>;
projectReferences?: ReadonlyArray<ProjectReference>;
host?: CompilerHost;
createProgram?: CreateProgram<T>;
}
function createIncrementalProgram<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({
rootNames, options, configFileParsingDiagnostics, projectReferences, host, createProgram
}: IncrementalProgramOptions<T>): T {
host = host || createIncrementalCompilerHost(options);
createProgram = createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>;
const oldProgram = readBuilderProgram(options, path => host!.readFile(path)) as any as T;
return createProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences);
}
export interface IncrementalCompilationOptions {
rootNames: ReadonlyArray<string>;
options: CompilerOptions;
@@ -427,7 +425,7 @@ namespace ts {
const system = input.system || sys;
const host = input.host || (input.host = createIncrementalCompilerHost(input.options, system));
const builderProgram = createIncrementalProgram(input);
const exitStatus = emitFilesAndReportErrors(
const exitStatus = emitFilesAndReportErrorsAndGetExitStatus(
builderProgram,
input.reportDiagnostic || createDiagnosticReporter(system),
s => host.trace && host.trace(s),
@@ -439,6 +437,49 @@ namespace ts {
}
namespace ts {
export interface ReadBuildProgramHost {
useCaseSensitiveFileNames(): boolean;
getCurrentDirectory(): string;
readFile(fileName: string): string | undefined;
}
export function readBuilderProgram(compilerOptions: CompilerOptions, host: ReadBuildProgramHost) {
if (compilerOptions.out || compilerOptions.outFile) return undefined;
const buildInfoPath = getOutputPathForBuildInfo(compilerOptions);
if (!buildInfoPath) return undefined;
const content = host.readFile(buildInfoPath);
if (!content) return undefined;
const buildInfo = getBuildInfo(content);
if (buildInfo.version !== version) return undefined;
if (!buildInfo.program) return undefined;
return createBuildProgramUsingProgramBuildInfo(buildInfo.program, buildInfoPath, host);
}
export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost {
const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system);
host.createHash = maybeBind(system, system.createHash);
setGetSourceFileAsHashVersioned(host, system);
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName));
return host;
}
export interface IncrementalProgramOptions<T extends BuilderProgram> {
rootNames: ReadonlyArray<string>;
options: CompilerOptions;
configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>;
projectReferences?: ReadonlyArray<ProjectReference>;
host?: CompilerHost;
createProgram?: CreateProgram<T>;
}
export function createIncrementalProgram<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({
rootNames, options, configFileParsingDiagnostics, projectReferences, host, createProgram
}: IncrementalProgramOptions<T>): T {
host = host || createIncrementalCompilerHost(options);
createProgram = createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>;
const oldProgram = readBuilderProgram(options, host) as any as T;
return createProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences);
}
export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference> | undefined) => T;
@@ -567,7 +608,6 @@ namespace ts {
/*@internal*/
getCurrentProgram(): T;
/** Closes the watch */
/*@internal*/
close(): void;
}
@@ -691,6 +731,7 @@ namespace ts {
hasChangedAutomaticTypeDirectiveNames = true;
scheduleProgramUpdate();
};
compilerHost.fileIsOpen = returnFalse;
compilerHost.maxNumberOfFilesToIterateForInvalidation = host.maxNumberOfFilesToIterateForInvalidation;
compilerHost.getCurrentProgram = getCurrentProgram;
compilerHost.writeLog = writeLog;
@@ -710,7 +751,7 @@ namespace ts {
((typeDirectiveNames, containingFile, redirectedReference) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference));
const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
builderProgram = readBuilderProgram(compilerOptions, path => compilerHost.readFile(path)) as any as T;
builderProgram = readBuilderProgram(compilerOptions, compilerHost) as any as T;
synchronizeProgram();
// Update the wild card directory watch
+13 -2
View File
@@ -395,8 +395,15 @@ namespace ts.server {
const locations: RenameLocation[] = [];
for (const entry of body.locs) {
const fileName = entry.file;
for (const { start, end, ...prefixSuffixText } of entry.locs) {
locations.push({ textSpan: this.decodeSpan({ start, end }, fileName), fileName, ...prefixSuffixText });
for (const { start, end, contextStart, contextEnd, ...prefixSuffixText } of entry.locs) {
locations.push({
textSpan: this.decodeSpan({ start, end }, fileName),
fileName,
...(contextStart !== undefined ?
{ contextSpan: this.decodeSpan({ start: contextStart, end: contextEnd! }, fileName) } :
undefined),
...prefixSuffixText
});
}
}
@@ -424,6 +431,10 @@ namespace ts.server {
return renameInfo;
}
getSmartSelectionRange() {
return notImplemented();
}
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[] {
if (!this.lastRenameEntry ||
this.lastRenameEntry.inputs.fileName !== fileName ||
+34 -25
View File
@@ -388,10 +388,42 @@ namespace fakes {
return ts.compareStringsCaseSensitive(ts.isString(a) ? a : a[0], ts.isString(b) ? b : b[0]);
}
export function sanitizeBuildInfoProgram(buildInfo: ts.BuildInfo) {
if (buildInfo.program) {
// reference Map
if (buildInfo.program.referencedMap) {
const referencedMap: ts.MapLike<string[]> = {};
for (const path of ts.getOwnKeys(buildInfo.program.referencedMap).sort()) {
referencedMap[path] = buildInfo.program.referencedMap[path].sort();
}
buildInfo.program.referencedMap = referencedMap;
}
// exportedModulesMap
if (buildInfo.program.exportedModulesMap) {
const exportedModulesMap: ts.MapLike<string[]> = {};
for (const path of ts.getOwnKeys(buildInfo.program.exportedModulesMap).sort()) {
exportedModulesMap[path] = buildInfo.program.exportedModulesMap[path].sort();
}
buildInfo.program.exportedModulesMap = exportedModulesMap;
}
// semanticDiagnosticsPerFile
if (buildInfo.program.semanticDiagnosticsPerFile) {
buildInfo.program.semanticDiagnosticsPerFile.sort(compareProgramBuildInfoDiagnostic);
}
}
}
export const version = "FakeTSVersion";
export class SolutionBuilderHost extends CompilerHost implements ts.SolutionBuilderHost<ts.BuilderProgram> {
createProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram;
createProgram: ts.CreateProgram<ts.BuilderProgram>;
constructor(sys: System | vfs.FileSystem, options?: ts.CompilerOptions, setParentNodes?: boolean, createProgram?: ts.CreateProgram<ts.BuilderProgram>) {
super(sys, options, setParentNodes);
this.createProgram = createProgram || ts.createEmitAndSemanticDiagnosticsBuilderProgram;
}
readFile(path: string) {
const value = super.readFile(path);
@@ -405,30 +437,7 @@ namespace fakes {
public writeFile(fileName: string, content: string, writeByteOrderMark: boolean) {
if (!ts.isBuildInfoFile(fileName)) return super.writeFile(fileName, content, writeByteOrderMark);
const buildInfo = ts.getBuildInfo(content);
if (buildInfo.program) {
// reference Map
if (buildInfo.program.referencedMap) {
const referencedMap: ts.MapLike<string[]> = {};
for (const path of ts.getOwnKeys(buildInfo.program.referencedMap).sort()) {
referencedMap[path] = buildInfo.program.referencedMap[path].sort();
}
buildInfo.program.referencedMap = referencedMap;
}
// exportedModulesMap
if (buildInfo.program.exportedModulesMap) {
const exportedModulesMap: ts.MapLike<string[]> = {};
for (const path of ts.getOwnKeys(buildInfo.program.exportedModulesMap).sort()) {
exportedModulesMap[path] = buildInfo.program.exportedModulesMap[path].sort();
}
buildInfo.program.exportedModulesMap = exportedModulesMap;
}
// semanticDiagnosticsPerFile
if (buildInfo.program.semanticDiagnosticsPerFile) {
buildInfo.program.semanticDiagnosticsPerFile.sort(compareProgramBuildInfoDiagnostic);
}
}
sanitizeBuildInfoProgram(buildInfo);
buildInfo.version = version;
super.writeFile(fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark);
}
+215 -56
View File
@@ -42,6 +42,7 @@ namespace FourSlash {
* is a range with `text in range` "selected".
*/
ranges: Range[];
rangesByText?: ts.MultiMap<Range>;
}
export interface Marker {
@@ -582,6 +583,21 @@ namespace FourSlash {
});
}
public verifyErrorExistsAtRange(range: Range, code: number, expectedMessage?: string) {
const span = ts.createTextSpanFromRange(range);
const hasMatchingError = ts.some(
this.getDiagnostics(range.fileName),
({ code, messageText, start, length }) =>
code === code &&
(!expectedMessage || expectedMessage === messageText) &&
ts.isNumber(start) && ts.isNumber(length) &&
ts.textSpansEqual(span, { start, length }));
if (!hasMatchingError) {
this.raiseError(`No error with code ${code} found at provided range.`);
}
}
public verifyNumberOfErrorsInCurrentFile(expected: number) {
const errors = this.getDiagnostics(this.activeFile.fileName);
const actual = errors.length;
@@ -798,8 +814,8 @@ namespace FourSlash {
}
private verifyCompletionEntry(actual: ts.CompletionEntry, expected: FourSlashInterface.ExpectedCompletionEntry) {
const { insertText, replacementSpan, hasAction, isRecommended, kind, kindModifiers, text, documentation, tags, source, sourceDisplay } = typeof expected === "string"
? { insertText: undefined, replacementSpan: undefined, hasAction: undefined, isRecommended: undefined, kind: undefined, kindModifiers: undefined, text: undefined, documentation: undefined, tags: undefined, source: undefined, sourceDisplay: undefined }
const { insertText, replacementSpan, hasAction, isRecommended, kind, kindModifiers, text, documentation, tags, source, sourceDisplay, sortText } = typeof expected === "string"
? { insertText: undefined, replacementSpan: undefined, hasAction: undefined, isRecommended: undefined, kind: undefined, kindModifiers: undefined, text: undefined, documentation: undefined, tags: undefined, source: undefined, sourceDisplay: undefined, sortText: undefined }
: expected;
if (actual.insertText !== insertText) {
@@ -825,6 +841,7 @@ namespace FourSlash {
assert.equal(actual.hasAction, hasAction);
assert.equal(actual.isRecommended, isRecommended);
assert.equal(actual.source, source);
assert.equal(actual.sortText, sortText || ts.Completions.SortText.LocationPriority, this.messageAtLastKnownMarker(`Actual entry: ${JSON.stringify(actual)}`));
if (text !== undefined) {
const actualDetails = this.getCompletionEntryDetails(actual.name, actual.source)!;
@@ -954,12 +971,15 @@ namespace FourSlash {
const fullExpected = ts.map<FourSlashInterface.ReferenceGroup, ReferenceGroupJson>(parts, ({ definition, ranges }) => ({
definition: typeof definition === "string" ? definition : { ...definition, range: ts.createTextSpanFromRange(definition.range) },
references: ranges.map<ts.ReferenceEntry>(r => {
const { isWriteAccess = false, isDefinition = false, isInString } = (r.marker && r.marker.data || {}) as { isWriteAccess?: boolean, isDefinition?: boolean, isInString?: true };
const { isWriteAccess = false, isDefinition = false, isInString, contextRangeIndex } = (r.marker && r.marker.data || {}) as { isWriteAccess?: boolean, isDefinition?: boolean, isInString?: true, contextRangeIndex?: number };
return {
fileName: r.fileName,
textSpan: ts.createTextSpanFromRange(r),
isWriteAccess,
isDefinition,
...(contextRangeIndex !== undefined ?
{ contextSpan: ts.createTextSpanFromRange(this.getRanges()[contextRangeIndex]) } :
undefined),
...(isInString ? { isInString: true } : undefined),
};
}),
@@ -996,8 +1016,8 @@ namespace FourSlash {
assert.deepEqual<ReadonlyArray<ts.ReferenceEntry> | undefined>(refs, expected);
}
public verifySingleReferenceGroup(definition: FourSlashInterface.ReferenceGroupDefinition, ranges?: Range[]) {
ranges = ranges || this.getRanges();
public verifySingleReferenceGroup(definition: FourSlashInterface.ReferenceGroupDefinition, ranges?: Range[] | string) {
ranges = ts.isString(ranges) ? this.rangesByText().get(ranges)! : ranges || this.getRanges();
this.verifyReferenceGroups(ranges, [{ definition, ranges }]);
}
@@ -1010,7 +1030,7 @@ Actual: ${stringify(fullActual)}`);
};
if ((actual === undefined) !== (expected === undefined)) {
fail(`Expected ${expected}, got ${actual}`);
fail(`Expected ${stringify(expected)}, got ${stringify(actual)}`);
}
for (const key in actual) {
@@ -1020,7 +1040,7 @@ Actual: ${stringify(fullActual)}`);
recur(ak, ek, path ? path + "." + key : key);
}
else if (ak !== ek) {
fail(`Expected '${key}' to be '${ek}', got '${ak}'`);
fail(`Expected '${key}' to be '${stringify(ek)}', got '${stringify(ak)}'`);
}
}
}
@@ -1172,7 +1192,9 @@ Actual: ${stringify(fullActual)}`);
public verifyRenameLocations(startRanges: ArrayOrSingle<Range>, options: FourSlashInterface.RenameLocationsOptions) {
const { findInStrings = false, findInComments = false, ranges = this.getRanges(), providePrefixAndSuffixTextForRename = true } = ts.isArray(options) ? { findInStrings: false, findInComments: false, ranges: options, providePrefixAndSuffixTextForRename: true } : options;
for (const startRange of toArray(startRanges)) {
const _startRanges = toArray(startRanges);
assert(_startRanges.length);
for (const startRange of _startRanges) {
this.goToRangeStart(startRange);
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition);
@@ -1188,7 +1210,15 @@ Actual: ${stringify(fullActual)}`);
locations && ts.sort(locations, (r1, r2) => ts.compareStringsCaseSensitive(r1.fileName, r2.fileName) || r1.textSpan.start - r2.textSpan.start);
assert.deepEqual(sort(references), sort(ranges.map((rangeOrOptions): ts.RenameLocation => {
const { range, ...prefixSuffixText } = "range" in rangeOrOptions ? rangeOrOptions : { range: rangeOrOptions };
return { fileName: range.fileName, textSpan: ts.createTextSpanFromRange(range), ...prefixSuffixText };
const { contextRangeIndex } = (range.marker && range.marker.data || {}) as { contextRangeIndex?: number; };
return {
fileName: range.fileName,
textSpan: ts.createTextSpanFromRange(range),
...(contextRangeIndex !== undefined ?
{ contextSpan: ts.createTextSpanFromRange(this.getRanges()[contextRangeIndex]) } :
undefined),
...prefixSuffixText
};
})));
}
}
@@ -1207,7 +1237,7 @@ Actual: ${stringify(fullActual)}`);
}
}
public verifySignatureHelpPresence(expectPresent: boolean, triggerReason: ts.SignatureHelpTriggerReason | undefined, markers: ReadonlyArray<string>) {
public verifySignatureHelpPresence(expectPresent: boolean, triggerReason: ts.SignatureHelpTriggerReason | undefined, markers: ReadonlyArray<string | Marker>) {
if (markers.length) {
for (const marker of markers) {
this.goToMarker(marker);
@@ -1417,12 +1447,7 @@ Actual: ${stringify(fullActual)}`);
}
public baselineCurrentFileBreakpointLocations() {
let baselineFile = this.testData.globalOptions[MetadataOptionNames.baselineFile];
if (!baselineFile) {
baselineFile = this.activeFile.fileName.replace(this.basePath + "/breakpointValidation", "bpSpan");
baselineFile = baselineFile.replace(ts.Extension.Ts, ".baseline");
}
const baselineFile = this.getBaselineFileName().replace("breakpointValidation", "bpSpan");
Harness.Baseline.runBaseline(baselineFile, this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos)!));
}
@@ -1497,8 +1522,7 @@ Actual: ${stringify(fullActual)}`);
}
public baselineQuickInfo() {
const baselineFile = this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
const baselineFile = this.getBaselineFileName();
Harness.Baseline.runBaseline(
baselineFile,
stringify(
@@ -1508,6 +1532,39 @@ Actual: ${stringify(fullActual)}`);
}))));
}
public baselineSmartSelection() {
const n = "\n";
const baselineFile = this.getBaselineFileName();
const markers = this.getMarkers();
const fileContent = this.activeFile.content;
const text = markers.map(marker => {
const baselineContent = [fileContent.slice(0, marker.position) + "/**/" + fileContent.slice(marker.position) + n];
let selectionRange: ts.SelectionRange | undefined = this.languageService.getSmartSelectionRange(this.activeFile.fileName, marker.position);
while (selectionRange) {
const { textSpan } = selectionRange;
let masked = Array.from(fileContent).map((char, index) => {
const charCode = char.charCodeAt(0);
if (index >= textSpan.start && index < ts.textSpanEnd(textSpan)) {
return char === " " ? "•" : ts.isLineBreak(charCode) ? `${n}` : char;
}
return ts.isLineBreak(charCode) ? char : " ";
}).join("");
masked = masked.replace(/^\s*$\r?\n?/gm, ""); // Remove blank lines
const isRealCharacter = (char: string) => char !== "•" && char !== "↲" && !ts.isWhiteSpaceLike(char.charCodeAt(0));
const leadingWidth = Array.from(masked).findIndex(isRealCharacter);
const trailingWidth = ts.findLastIndex(Array.from(masked), isRealCharacter);
masked = masked.slice(0, leadingWidth)
+ masked.slice(leadingWidth, trailingWidth).replace(/•/g, " ").replace(/↲/g, "")
+ masked.slice(trailingWidth);
baselineContent.push(masked);
selectionRange = selectionRange.parent;
}
return baselineContent.join(fileContent.includes("\n") ? n + n : n);
}).join(n.repeat(2) + "=".repeat(80) + n.repeat(2));
Harness.Baseline.runBaseline(baselineFile, text);
}
public printBreakpointLocation(pos: number) {
Harness.IO.log("\n**Pos: " + pos + " " + this.spanInfoToString(this.getBreakpointStatementLocation(pos)!, " "));
}
@@ -1562,6 +1619,11 @@ Actual: ${stringify(fullActual)}`);
Harness.IO.log(stringify(help.items[help.selectedItemIndex]));
}
private getBaselineFileName() {
return this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
}
private getSignatureHelp({ triggerReason }: FourSlashInterface.VerifySignatureHelpOptions): ts.SignatureHelpItems | undefined {
return this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition, {
triggerReason
@@ -1811,6 +1873,7 @@ Actual: ${stringify(fullActual)}`);
range.end = updatePosition(range.end, editStart, editEnd, newText);
}
}
this.testData.rangesByText = undefined;
}
private removeWhitespace(text: string): string {
@@ -1993,7 +2056,9 @@ Actual: ${stringify(fullActual)}`);
}
public rangesByText(): ts.Map<Range[]> {
if (this.testData.rangesByText) return this.testData.rangesByText;
const result = ts.createMultiMap<Range>();
this.testData.rangesByText = result;
for (const range of this.getRanges()) {
const text = this.rangeText(range);
result.add(text, range);
@@ -2205,6 +2270,20 @@ Actual: ${stringify(fullActual)}`);
});
}
public verifyOutliningHintSpans(spans: Range[]) {
const actual = this.languageService.getOutliningSpans(this.activeFile.fileName);
if (actual.length !== spans.length) {
this.raiseError(`verifyOutliningHintSpans failed - expected total spans to be ${spans.length}, but was ${actual.length}`);
}
ts.zipWith(spans, actual, (expectedSpan, actualSpan, i) => {
if (expectedSpan.pos !== actualSpan.hintSpan.start || expectedSpan.end !== ts.textSpanEnd(actualSpan.hintSpan)) {
return this.raiseError(`verifyOutliningSpans failed - span ${(i + 1)} expected: (${expectedSpan.pos},${expectedSpan.end}), actual: (${actualSpan.hintSpan.start},${ts.textSpanEnd(actualSpan.hintSpan)})`);
}
});
}
public verifyTodoComments(descriptors: string[], spans: Range[]) {
const actual = this.languageService.getTodoComments(this.activeFile.fileName,
descriptors.map(d => { return { text: d, priority: 0 }; }));
@@ -2667,8 +2746,9 @@ Actual: ${stringify(fullActual)}`);
return this.languageService.getDocumentHighlights(this.activeFile.fileName, this.currentCaretPosition, filesToSearch);
}
public verifyRangesAreOccurrences(isWriteAccess?: boolean) {
const ranges = this.getRanges();
public verifyRangesAreOccurrences(isWriteAccess?: boolean, ranges?: Range[]) {
ranges = ranges || this.getRanges();
assert(ranges.length);
for (const r of ranges) {
this.goToRangeStart(r);
this.verifyOccurrencesAtPositionListCount(ranges.length);
@@ -2678,8 +2758,13 @@ Actual: ${stringify(fullActual)}`);
}
}
public verifyRangesWithSameTextAreRenameLocations() {
this.rangesByText().forEach(ranges => this.verifyRangesAreRenameLocations(ranges));
public verifyRangesWithSameTextAreRenameLocations(...texts: string[]) {
if (texts.length) {
texts.forEach(text => this.verifyRangesAreRenameLocations(this.rangesByText().get(text)!));
}
else {
this.rangesByText().forEach(ranges => this.verifyRangesAreRenameLocations(ranges));
}
}
public verifyRangesWithSameTextAreDocumentHighlights() {
@@ -2694,6 +2779,7 @@ Actual: ${stringify(fullActual)}`);
public verifyRangesAreDocumentHighlights(ranges: Range[] | undefined, options: FourSlashInterface.VerifyDocumentHighlightsOptions | undefined) {
ranges = ranges || this.getRanges();
assert(ranges.length);
const fileNames = options && options.filesToSearch || unique(ranges, range => range.fileName);
for (const range of ranges) {
this.goToRangeStart(range);
@@ -2863,7 +2949,9 @@ Actual: ${stringify(fullActual)}`);
}
public noMoveToNewFile() {
for (const range of this.getRanges()) {
const ranges = this.getRanges();
assert(ranges.length);
for (const range of ranges) {
for (const refactor of this.getApplicableRefactors(range, { allowTextChangesInNewFiles: true })) {
if (refactor.name === "Move to a new file") {
ts.Debug.fail("Did not expect to get 'move to a new file' refactor");
@@ -3735,15 +3823,15 @@ namespace FourSlashInterface {
assert(ranges.length !== 0, "Array of ranges is expected to be non-empty");
}
public noSignatureHelp(...markers: string[]): void {
public noSignatureHelp(...markers: (string | FourSlash.Marker)[]): void {
this.state.verifySignatureHelpPresence(/*expectPresent*/ false, /*triggerReason*/ undefined, markers);
}
public noSignatureHelpForTriggerReason(reason: ts.SignatureHelpTriggerReason, ...markers: string[]): void {
public noSignatureHelpForTriggerReason(reason: ts.SignatureHelpTriggerReason, ...markers: (string | FourSlash.Marker)[]): void {
this.state.verifySignatureHelpPresence(/*expectPresent*/ false, reason, markers);
}
public signatureHelpPresentForTriggerReason(reason: ts.SignatureHelpTriggerReason, ...markers: string[]): void {
public signatureHelpPresentForTriggerReason(reason: ts.SignatureHelpTriggerReason, ...markers: (string | FourSlash.Marker)[]): void {
this.state.verifySignatureHelpPresence(/*expectPresent*/ true, reason, markers);
}
@@ -3924,7 +4012,7 @@ namespace FourSlashInterface {
this.state.verifyGetReferencesForServerTest(expected);
}
public singleReferenceGroup(definition: ReferenceGroupDefinition, ranges?: FourSlash.Range[]) {
public singleReferenceGroup(definition: ReferenceGroupDefinition, ranges?: FourSlash.Range[] | string) {
this.state.verifySingleReferenceGroup(definition, ranges);
}
@@ -3936,6 +4024,10 @@ namespace FourSlashInterface {
this.state.verifyNoErrors();
}
public errorExistsAtRange(range: FourSlash.Range, code: number, message?: string) {
this.state.verifyErrorExistsAtRange(range, code, message);
}
public numberOfErrorsInCurrentFile(expected: number) {
this.state.verifyNumberOfErrorsInCurrentFile(expected);
}
@@ -3960,6 +4052,10 @@ namespace FourSlashInterface {
this.state.baselineQuickInfo();
}
public baselineSmartSelection() {
this.state.baselineSmartSelection();
}
public nameOrDottedNameSpanTextIs(text: string) {
this.state.verifyCurrentNameOrDottedNameSpanText(text);
}
@@ -3968,6 +4064,10 @@ namespace FourSlashInterface {
this.state.verifyOutliningSpans(spans, kind);
}
public outliningHintSpansInCurrentFile(spans: FourSlash.Range[]) {
this.state.verifyOutliningHintSpans(spans);
}
public todoCommentsInCurrentFile(descriptors: string[]) {
this.state.verifyTodoComments(descriptors, this.state.getRanges());
}
@@ -4038,12 +4138,12 @@ namespace FourSlashInterface {
this.state.verifyOccurrencesAtPositionListCount(expectedCount);
}
public rangesAreOccurrences(isWriteAccess?: boolean) {
this.state.verifyRangesAreOccurrences(isWriteAccess);
public rangesAreOccurrences(isWriteAccess?: boolean, ranges?: FourSlash.Range[]) {
this.state.verifyRangesAreOccurrences(isWriteAccess, ranges);
}
public rangesWithSameTextAreRenameLocations() {
this.state.verifyRangesWithSameTextAreRenameLocations();
public rangesWithSameTextAreRenameLocations(...texts: string[]) {
this.state.verifyRangesWithSameTextAreRenameLocations(...texts);
}
public rangesAreRenameLocations(options?: FourSlash.Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: FourSlash.Range[] }) {
@@ -4398,18 +4498,63 @@ namespace FourSlashInterface {
}
}
export namespace Completion {
const functionEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "function", kindModifiers: "declare" });
const varEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "var", kindModifiers: "declare" });
const moduleEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "module", kindModifiers: "declare" });
const keywordEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "keyword" });
const methodEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "method", kindModifiers: "declare" });
const propertyEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "property", kindModifiers: "declare" });
const interfaceEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "interface", kindModifiers: "declare" });
const typeEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "type", kindModifiers: "declare" });
export import SortText = ts.Completions.SortText;
const functionEntry = (name: string): ExpectedCompletionEntryObject => ({
name,
kind: "function",
kindModifiers: "declare",
sortText: SortText.GlobalsOrKeywords
});
const varEntry = (name: string): ExpectedCompletionEntryObject => ({
name,
kind: "var",
kindModifiers: "declare",
sortText: SortText.GlobalsOrKeywords
});
const moduleEntry = (name: string): ExpectedCompletionEntryObject => ({
name,
kind: "module",
kindModifiers: "declare",
sortText: SortText.GlobalsOrKeywords
});
const keywordEntry = (name: string): ExpectedCompletionEntryObject => ({
name,
kind: "keyword",
sortText: SortText.GlobalsOrKeywords
});
const methodEntry = (name: string): ExpectedCompletionEntryObject => ({
name,
kind: "method",
kindModifiers: "declare",
sortText: SortText.LocationPriority
});
const propertyEntry = (name: string): ExpectedCompletionEntryObject => ({
name,
kind: "property",
kindModifiers: "declare",
sortText: SortText.LocationPriority
});
const interfaceEntry = (name: string): ExpectedCompletionEntryObject => ({
name,
kind: "interface",
kindModifiers: "declare",
sortText: SortText.GlobalsOrKeywords
});
const typeEntry = (name: string): ExpectedCompletionEntryObject => ({
name,
kind: "type",
kindModifiers: "declare",
sortText: SortText.GlobalsOrKeywords
});
const res: ExpectedCompletionEntryObject[] = [];
for (let i = ts.SyntaxKind.FirstKeyword; i <= ts.SyntaxKind.LastKeyword; i++) {
res.push({ name: ts.Debug.assertDefined(ts.tokenToString(i)), kind: "keyword" });
res.push({
name: ts.Debug.assertDefined(ts.tokenToString(i)),
kind: "keyword",
sortText: SortText.GlobalsOrKeywords
});
}
export const keywordsWithUndefined: ReadonlyArray<ExpectedCompletionEntryObject> = res;
export const keywords: ReadonlyArray<ExpectedCompletionEntryObject> = keywordsWithUndefined.filter(k => k.name !== "undefined");
@@ -4516,11 +4661,15 @@ namespace FourSlashInterface {
moduleEntry("Intl"),
];
export const globalThisEntry: ExpectedCompletionEntry = {
name: "globalThis",
kind: "module",
sortText: SortText.GlobalsOrKeywords
};
export const globalTypes = globalTypesPlus([]);
export function globalTypesPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
return [
{ name: "globalThis", kind: "module" },
globalThisEntry,
...globalTypeDecls,
...plus,
...typeKeywords,
@@ -4569,7 +4718,11 @@ namespace FourSlashInterface {
export const classElementInJsKeywords = getInJsKeywords(classElementKeywords);
export const constructorParameterKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntryObject => ({ name, kind: "keyword" }));
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntryObject => ({
name,
kind: "keyword",
sortText: SortText.GlobalsOrKeywords
}));
export const functionMembers: ReadonlyArray<ExpectedCompletionEntryObject> = [
methodEntry("apply"),
@@ -4798,13 +4951,18 @@ namespace FourSlashInterface {
"await",
].map(keywordEntry);
export const undefinedVarEntry: ExpectedCompletionEntry = {
name: "undefined",
kind: "var",
sortText: SortText.GlobalsOrKeywords
};
// TODO: many of these are inappropriate to always provide
export const globalsInsideFunction = (plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> => [
{ name: "arguments", kind: "local var" },
...plus,
{ name: "globalThis", kind: "module" },
globalThisEntry,
...globalsVars,
{ name: "undefined", kind: "var" },
undefinedVarEntry,
...globalKeywordsInsideFunction,
];
@@ -4813,10 +4971,10 @@ namespace FourSlashInterface {
// TODO: many of these are inappropriate to always provide
export const globalsInJsInsideFunction = (plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> => [
{ name: "arguments", kind: "local var" },
{ name: "globalThis", kind: "module" },
globalThisEntry,
...globalsVars,
...plus,
{ name: "undefined", kind: "var" },
undefinedVarEntry,
...globalInJsKeywordsInsideFunction,
];
@@ -4954,34 +5112,34 @@ namespace FourSlashInterface {
})();
export const globals: ReadonlyArray<ExpectedCompletionEntryObject> = [
{ name: "globalThis", kind: "module" },
globalThisEntry,
...globalsVars,
{ name: "undefined", kind: "var" },
undefinedVarEntry,
...globalKeywords
];
export const globalsInJs: ReadonlyArray<ExpectedCompletionEntryObject> = [
{ name: "globalThis", kind: "module" },
globalThisEntry,
...globalsVars,
{ name: "undefined", kind: "var" },
undefinedVarEntry,
...globalInJsKeywords
];
export function globalsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
return [
{ name: "globalThis", kind: "module" },
globalThisEntry,
...globalsVars,
...plus,
{ name: "undefined", kind: "var" },
undefinedVarEntry,
...globalKeywords];
}
export function globalsInJsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
return [
{ name: "globalThis", kind: "module" },
globalThisEntry,
...globalsVars,
...plus,
{ name: "undefined", kind: "var" },
undefinedVarEntry,
...globalInJsKeywords];
}
}
@@ -5014,6 +5172,7 @@ namespace FourSlashInterface {
readonly documentation?: string;
readonly sourceDisplay?: string;
readonly tags?: ReadonlyArray<ts.JSDocTagInfo>;
readonly sortText?: ts.Completions.SortText;
}
export interface VerifyCompletionsOptions {
@@ -5028,7 +5187,7 @@ namespace FourSlashInterface {
}
export interface VerifySignatureHelpOptions {
readonly marker?: ArrayOrSingle<string>;
readonly marker?: ArrayOrSingle<string | FourSlash.Marker>;
/** @default 1 */
readonly overloadsCount?: number;
/** @default undefined */
+8 -1
View File
@@ -501,7 +501,7 @@ namespace Harness {
}
function enumerateTestFiles(runner: RunnerBase) {
return runner.enumerateTestFiles();
return runner.getTestFiles();
}
function listFiles(path: string, spec: RegExp, options: { recursive?: boolean } = {}) {
@@ -1311,6 +1311,13 @@ namespace Harness {
if (jsCode.length && jsCode.charCodeAt(jsCode.length - 1) !== ts.CharacterCodes.lineFeed) {
jsCode += "\r\n";
}
if (!result.diagnostics.length && !ts.endsWith(file.file, ts.Extension.Json)) {
const fileParseResult = ts.createSourceFile(file.file, file.text, options.target || ts.ScriptTarget.ES3, /*parentNodes*/ false, ts.endsWith(file.file, "x") ? ts.ScriptKind.JSX : ts.ScriptKind.JS);
if (ts.length(fileParseResult.parseDiagnostics)) {
jsCode += getErrorBaseline([file.asTestFile()], fileParseResult.parseDiagnostics);
return;
}
}
jsCode += fileOutput(file, harnessSettings);
});
+3
View File
@@ -472,6 +472,9 @@ namespace Harness.LanguageService {
getRenameInfo(fileName: string, position: number, options?: ts.RenameInfoOptions): ts.RenameInfo {
return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position, options));
}
getSmartSelectionRange(fileName: string, position: number): ts.SelectionRange {
return unwrapJSONCallResult(this.shim.getSmartSelectionRange(fileName, position));
}
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): ts.RenameLocation[] {
return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename));
}
+12 -1
View File
@@ -1,7 +1,10 @@
type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" | "test262" | "user" | "dt";
type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" | "test262" | "user" | "dt" | "docker";
type CompilerTestKind = "conformance" | "compiler";
type FourslashTestKind = "fourslash" | "fourslash-shims" | "fourslash-shims-pp" | "fourslash-server";
let shards = 1;
let shardId = 1;
abstract class RunnerBase {
// contains the tests to run
public tests: (string | Harness.FileBasedTest)[] = [];
@@ -19,6 +22,14 @@ abstract class RunnerBase {
abstract enumerateTestFiles(): (string | Harness.FileBasedTest)[];
getTestFiles(): ReturnType<this["enumerateTestFiles"]> {
const all = this.enumerateTestFiles();
if (shards === 1) {
return all as ReturnType<this["enumerateTestFiles"]>;
}
return all.filter((_val, idx) => idx % shards === (shardId - 1)) as ReturnType<this["enumerateTestFiles"]>;
}
/** The working directory where tests are found. Needed for batch testing where the input path will differ from the output path inside baselines */
public workingDirectory = "";
+5 -1
View File
@@ -70,6 +70,10 @@ namespace ts.JsTyping {
export const nodeCoreModules = arrayToSet(nodeCoreModuleList);
export function nonRelativeModuleNameForTypingCache(moduleName: string) {
return nodeCoreModules.has(moduleName) ? "node" : moduleName;
}
/**
* A map of loose file names to library names that we are confident require typings
*/
@@ -150,7 +154,7 @@ namespace ts.JsTyping {
// add typings for unresolved imports
if (unresolvedImports) {
const module = deduplicate<string>(
unresolvedImports.map(moduleId => nodeCoreModules.has(moduleId) ? "node" : moduleId),
unresolvedImports.map(nonRelativeModuleNameForTypingCache),
equateStringsCaseSensitive,
compareStringsCaseSensitive);
addInferredTypings(module, "Inferred typings from unresolved imports");
+2020 -1805
View File
File diff suppressed because it is too large Load Diff
+94 -30
View File
@@ -2,6 +2,10 @@
/// DOM Iterable APIs
/////////////////////////////
interface AudioParam {
setValueCurveAtTime(values: Iterable<number>, startTime: number, duration: number): AudioParam;
}
interface AudioParamMap extends ReadonlyMap<string, AudioParam> {
}
@@ -9,6 +13,11 @@ interface AudioTrackList {
[Symbol.iterator](): IterableIterator<AudioTrack>;
}
interface BaseAudioContext {
createIIRFilter(feedforward: Iterable<number>, feedback: Iterable<number>): IIRFilterNode;
createPeriodicWave(real: Iterable<number>, imag: Iterable<number>, constraints?: PeriodicWaveConstraints): PeriodicWave;
}
interface CSSRuleList {
[Symbol.iterator](): IterableIterator<CSSRule>;
}
@@ -17,6 +26,14 @@ interface CSSStyleDeclaration {
[Symbol.iterator](): IterableIterator<string>;
}
interface Cache {
addAll(requests: Iterable<RequestInfo>): Promise<void>;
}
interface CanvasPathDrawingStyles {
setLineDash(segments: Iterable<number>): void;
}
interface ClientRectList {
[Symbol.iterator](): IterableIterator<ClientRect>;
}
@@ -46,16 +63,16 @@ interface FileList {
interface FormData {
[Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;
/**
* Returns an array of key, value pairs for every entry in the list.
/**
* Returns an array of key, value pairs for every entry in the list.
*/
entries(): IterableIterator<[string, FormDataEntryValue]>;
/**
* Returns a list of keys in the list.
/**
* Returns a list of keys in the list.
*/
keys(): IterableIterator<string>;
/**
* Returns a list of values in the list.
/**
* Returns a list of values in the list.
*/
values(): IterableIterator<FormDataEntryValue>;
}
@@ -82,20 +99,29 @@ interface HTMLSelectElement {
interface Headers {
[Symbol.iterator](): IterableIterator<[string, string]>;
/**
* Returns an iterator allowing to go through all key/value pairs contained in this object.
/**
* Returns an iterator allowing to go through all key/value pairs contained in this object.
*/
entries(): IterableIterator<[string, string]>;
/**
* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object.
/**
* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object.
*/
keys(): IterableIterator<string>;
/**
* Returns an iterator allowing to go through all values of the key/value pairs contained in this object.
/**
* Returns an iterator allowing to go through all values of the key/value pairs contained in this object.
*/
values(): IterableIterator<string>;
}
interface IDBObjectStore {
/**
* Creates a new index in store with the given name, keyPath and options and returns a new IDBIndex. If the keyPath and options define constraints that cannot be satisfied with the data already in store the upgrade transaction will abort with a "ConstraintError" DOMException.
*
* Throws an "InvalidStateError" DOMException if not called within an upgrade transaction.
*/
createIndex(name: string, keyPath: string | Iterable<string>, options?: IDBIndexParameters): IDBIndex;
}
interface MediaKeyStatusMap {
[Symbol.iterator](): IterableIterator<[BufferSource, MediaKeyStatus]>;
entries(): IterableIterator<[BufferSource, MediaKeyStatus]>;
@@ -115,34 +141,38 @@ interface NamedNodeMap {
[Symbol.iterator](): IterableIterator<Attr>;
}
interface Navigator {
requestMediaKeySystemAccess(keySystem: string, supportedConfigurations: Iterable<MediaKeySystemConfiguration>): Promise<MediaKeySystemAccess>;
}
interface NodeList {
[Symbol.iterator](): IterableIterator<Node>;
/**
* Returns an array of key, value pairs for every entry in the list.
/**
* Returns an array of key, value pairs for every entry in the list.
*/
entries(): IterableIterator<[number, Node]>;
/**
* Returns an list of keys in the list.
/**
* Returns an list of keys in the list.
*/
keys(): IterableIterator<number>;
/**
* Returns an list of values in the list.
/**
* Returns an list of values in the list.
*/
values(): IterableIterator<Node>;
}
interface NodeListOf<TNode extends Node> {
[Symbol.iterator](): IterableIterator<TNode>;
/**
* Returns an array of key, value pairs for every entry in the list.
/**
* Returns an array of key, value pairs for every entry in the list.
*/
entries(): IterableIterator<[number, TNode]>;
/**
* Returns an list of keys in the list.
/**
* Returns an list of keys in the list.
*/
keys(): IterableIterator<number>;
/**
* Returns an list of values in the list.
/**
* Returns an list of values in the list.
*/
values(): IterableIterator<TNode>;
}
@@ -155,6 +185,10 @@ interface PluginArray {
[Symbol.iterator](): IterableIterator<Plugin>;
}
interface RTCRtpTransceiver {
setCodecPreferences(codecs: Iterable<RTCRtpCodecCapability>): void;
}
interface RTCStatsReport extends ReadonlyMap<string, any> {
}
@@ -208,20 +242,50 @@ interface TouchList {
interface URLSearchParams {
[Symbol.iterator](): IterableIterator<[string, string]>;
/**
* Returns an array of key, value pairs for every entry in the search params.
/**
* Returns an array of key, value pairs for every entry in the search params.
*/
entries(): IterableIterator<[string, string]>;
/**
* Returns a list of keys in the search params.
/**
* Returns a list of keys in the search params.
*/
keys(): IterableIterator<string>;
/**
* Returns a list of values in the search params.
/**
* Returns a list of values in the search params.
*/
values(): IterableIterator<string>;
}
interface VRDisplay {
requestPresent(layers: Iterable<VRLayer>): Promise<void>;
}
interface VideoTrackList {
[Symbol.iterator](): IterableIterator<VideoTrack>;
}
interface WEBGL_draw_buffers {
drawBuffersWEBGL(buffers: Iterable<GLenum>): void;
}
interface WebAuthentication {
makeCredential(accountInformation: Account, cryptoParameters: Iterable<ScopedCredentialParameters>, attestationChallenge: Int8Array | Int16Array | Int32Array | Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array | DataView | ArrayBuffer | null, options?: ScopedCredentialOptions): Promise<ScopedCredentialInfo>;
}
interface WebGLRenderingContextBase {
uniform1fv(location: WebGLUniformLocation | null, v: Iterable<GLfloat>): void;
uniform2fv(location: WebGLUniformLocation | null, v: Iterable<GLfloat>): void;
uniform3fv(location: WebGLUniformLocation | null, v: Iterable<GLfloat>): void;
uniform4fv(location: WebGLUniformLocation | null, v: Iterable<GLfloat>): void;
uniform1iv(location: WebGLUniformLocation | null, v: Iterable<GLint>): void;
uniform2iv(location: WebGLUniformLocation | null, v: Iterable<GLint>): void;
uniform3iv(location: WebGLUniformLocation | null, v: Iterable<GLint>): void;
uniform4iv(location: WebGLUniformLocation | null, v: Iterable<GLint>): void;
uniformMatrix2fv(location: WebGLUniformLocation | null, transpose: GLboolean, value: Iterable<GLfloat>): void;
uniformMatrix3fv(location: WebGLUniformLocation | null, transpose: GLboolean, value: Iterable<GLfloat>): void;
uniformMatrix4fv(location: WebGLUniformLocation | null, transpose: GLboolean, value: Iterable<GLfloat>): void;
vertexAttrib1fv(index: GLuint, values: Iterable<GLfloat>): void;
vertexAttrib2fv(index: GLuint, values: Iterable<GLfloat>): void;
vertexAttrib3fv(index: GLuint, values: Iterable<GLfloat>): void;
vertexAttrib4fv(index: GLuint, values: Iterable<GLfloat>): void;
}
+2 -2
View File
@@ -373,8 +373,8 @@ interface RegExp {
}
interface RegExpConstructor {
new (pattern: RegExp, flags?: string): RegExp;
(pattern: RegExp, flags?: string): RegExp;
new (pattern: RegExp | string, flags?: string): RegExp;
(pattern: RegExp | string, flags?: string): RegExp;
}
interface String {
+2 -2
View File
@@ -5,9 +5,9 @@ interface String {
/** Removes the leading white space and line terminator characters from a string. */
trimStart(): string;
/** Removes the trailing white space and line terminator characters from a string. */
/** Removes the leading white space and line terminator characters from a string. */
trimLeft(): string;
/** Removes the leading white space and line terminator characters from a string. */
/** Removes the trailing white space and line terminator characters from a string. */
trimRight(): string;
}
+2 -4
View File
@@ -513,7 +513,7 @@ interface Boolean {
interface BooleanConstructor {
new(value?: any): Boolean;
<T>(value?: T): value is Exclude<T, false | null | undefined | '' | 0>;
<T>(value?: T): boolean;
readonly prototype: Boolean;
}
@@ -1446,9 +1446,7 @@ type Extract<T, U> = T extends U ? T : never;
/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = {
[P in Exclude<keyof T, K>]: T[P]
};
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
/**
* Exclude null and undefined from T
+397 -320
View File
File diff suppressed because it is too large Load Diff
@@ -2881,7 +2881,7 @@
<Str Cat="Text">
<Val><![CDATA[Default export of the module has or is using private name '{0}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[모듈의 기본 내보내기에서 전용 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[모듈의 기본 내보내기에서 프라이빗 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -3667,7 +3667,7 @@
<Str Cat="Text">
<Val><![CDATA[Exported type alias '{0}' has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 형식 별칭 '{0}'은(는) '{1}' 전용 이름을 포함하거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 형식 별칭 '{0}'은(는) '{1}' 프라이빗 이름을 포함하거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -3685,7 +3685,7 @@
<Str Cat="Text">
<Val><![CDATA[Exported variable '{0}' has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 변수 '{0}'이(가) 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 변수 '{0}'이(가) 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -3694,7 +3694,7 @@
<Str Cat="Text">
<Val><![CDATA[Exported variable '{0}' has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 변수 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 변수 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -4369,7 +4369,7 @@
<Str Cat="Text">
<Val><![CDATA[Implements clause of exported class '{0}' has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스 '{0}'의 Implements 절이 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스 '{0}'의 Implements 절이 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -4399,7 +4399,7 @@
<Str Cat="Text">
<Val><![CDATA[Import declaration '{0}' is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[가져오기 선언 '{0}'이(가) 전용 이름 '{1}'을(를) 사용하고 있습니다.]]></Val>
<Val><![CDATA[가져오기 선언 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 사용하고 있습니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -5242,7 +5242,7 @@
<Str Cat="Text">
<Val><![CDATA[Method '{0}' of exported interface has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스의 '{0}' 메서드가 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스의 '{0}' 메서드가 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -5251,7 +5251,7 @@
<Str Cat="Text">
<Val><![CDATA[Method '{0}' of exported interface has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스의 '{0}' 메서드가 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스의 '{0}' 메서드가 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -5935,7 +5935,7 @@
<Str Cat="Text">
<Val><![CDATA[Overload signatures must all be public, private or protected.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[오버로드 시그니처는 모두 공용, 전용 또는 보호된 상태여야 합니다.]]></Val>
<Val><![CDATA[오버로드 시그니처는 모두 퍼블릭, 프라이빗 또는 보호된 상태여야 합니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -5971,7 +5971,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of call signature from exported interface has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 호출 시그니처의 '{0}' 매개 변수가 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 호출 시그니처의 '{0}' 매개 변수가 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -5980,7 +5980,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of call signature from exported interface has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 호출 시그니처의 '{0}' 매개 변수가 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 호출 시그니처의 '{0}' 매개 변수가 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -5998,7 +5998,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of constructor from exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 생성자의 '{0}' 매개 변수가 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 생성자의 '{0}' 매개 변수가 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6007,7 +6007,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of constructor from exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 생성자의 '{0}' 매개 변수가 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 생성자의 '{0}' 매개 변수가 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6016,7 +6016,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of constructor signature from exported interface has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 생성자 시그니처의 '{0}' 매개 변수가 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 생성자 시그니처의 '{0}' 매개 변수가 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6025,7 +6025,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of constructor signature from exported interface has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 생성자 시그니처의 '{0}' 매개 변수가 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 생성자 시그니처의 '{0}' 매개 변수가 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6043,7 +6043,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of exported function has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 함수의 '{0}' 매개 변수가 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 함수의 '{0}' 매개 변수가 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6052,7 +6052,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of exported function has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 함수의 '{0}' 매개 변수가 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 함수의 '{0}' 매개 변수가 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6061,7 +6061,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of index signature from exported interface has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 인덱스 시그니처의 '{0}' 매개 변수가 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 인덱스 시그니처의 '{0}' 매개 변수가 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6070,7 +6070,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of index signature from exported interface has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 인덱스 시그니처의 '{0}' 매개 변수가 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 인덱스 시그니처의 '{0}' 매개 변수가 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6079,7 +6079,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of method from exported interface has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 메서드의 '{0}' 매개 변수가 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 메서드의 '{0}' 매개 변수가 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6088,7 +6088,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of method from exported interface has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 메서드의 '{0}' 매개 변수가 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 메서드의 '{0}' 매개 변수가 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6106,7 +6106,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of public method from exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 메서드의 '{0}' 매개 변수가 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 메서드의 '{0}' 매개 변수가 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6115,7 +6115,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of public method from exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 메서드의 '{0}' 매개 변수가 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 메서드의 '{0}' 매개 변수가 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6133,7 +6133,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of public static method from exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드의 '{0}' 매개 변수가 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드의 '{0}' 매개 변수가 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6142,7 +6142,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter '{0}' of public static method from exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드의 '{0}' 매개 변수가 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드의 '{0}' 매개 변수가 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6169,7 +6169,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter type of public setter '{0}' from exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 setter '{0}'의 매개 변수 형식이 전용 모듈 '{2}'의 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 setter '{0}'의 매개 변수 형식이 프라이빗 모듈 '{2}'의 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6178,7 +6178,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter type of public setter '{0}' from exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 setter '{0}'의 매개 변수 형식이 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 setter '{0}'의 매개 변수 형식이 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6187,7 +6187,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter type of public static setter '{0}' from exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 setter '{0}'의 매개 변수 형식이 전용 모듈 '{2}'의 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 setter '{0}'의 매개 변수 형식이 프라이빗 모듈 '{2}'의 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6196,7 +6196,7 @@
<Str Cat="Text">
<Val><![CDATA[Parameter type of public static setter '{0}' from exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 setter '{0}'의 매개 변수 형식이 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 setter '{0}'의 매개 변수 형식이 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6577,7 +6577,7 @@
<Str Cat="Text">
<Val><![CDATA[Property '{0}' of exported class expression may not be private or protected.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스 식의 속성 '{0}'이(가) 비공개가 아니거나 보호되지 않을 수 있습니다.]]></Val>
<Val><![CDATA[내보낸 클래스 식의 속성 '{0}'이(가) 프라이빗이 아니거나 보호되지 않을 수 있습니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6586,7 +6586,7 @@
<Str Cat="Text">
<Val><![CDATA[Property '{0}' of exported interface has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스의 '{0}' 속성이 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스의 '{0}' 속성이 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6595,7 +6595,7 @@
<Str Cat="Text">
<Val><![CDATA[Property '{0}' of exported interface has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스의 '{0}' 속성이 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스의 '{0}' 속성이 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6676,7 +6676,7 @@
<Str Cat="Text">
<Val><![CDATA[Public method '{0}' of exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스의 공용 메서드 '{0}'이(가) 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스의 공용 메서드 '{0}'이(가) 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6685,7 +6685,7 @@
<Str Cat="Text">
<Val><![CDATA[Public method '{0}' of exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스의 공용 메서드의 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스의 공용 메서드의 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6703,7 +6703,7 @@
<Str Cat="Text">
<Val><![CDATA[Public property '{0}' of exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스의 공용 속성 '{0}'이(가) 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스의 공용 속성 '{0}'이(가) 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6712,7 +6712,7 @@
<Str Cat="Text">
<Val><![CDATA[Public property '{0}' of exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스의 공용 속성 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스의 공용 속성 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6730,7 +6730,7 @@
<Str Cat="Text">
<Val><![CDATA[Public static method '{0}' of exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드 '{0}'이(가) 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드 '{0}'이(가) 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6739,7 +6739,7 @@
<Str Cat="Text">
<Val><![CDATA[Public static method '{0}' of exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스의 공용 정적 메서드 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스의 공용 정적 메서드 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6757,7 +6757,7 @@
<Str Cat="Text">
<Val><![CDATA[Public static property '{0}' of exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스의 공용 정적 속성 '{0}'이(가) 전용 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스의 공용 정적 속성 '{0}'이(가) 프라이빗 모듈 '{2}'의 '{1}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -6766,7 +6766,7 @@
<Str Cat="Text">
<Val><![CDATA[Public static property '{0}' of exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스의 공용 정적 속성 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스의 공용 정적 속성 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7090,7 +7090,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of call signature from exported interface has or is using name '{0}' from private module '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 호출 시그니처의 반환 형식이 전용 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 호출 시그니처의 반환 형식이 프라이빗 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7099,7 +7099,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of call signature from exported interface has or is using private name '{0}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 호출 시그니처의 반환 형식이 전용 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 호출 시그니처의 반환 형식이 프라이빗 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7108,7 +7108,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of constructor signature from exported interface has or is using name '{0}' from private module '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 생성자 시그니처의 반환 형식이 전용 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 생성자 시그니처의 반환 형식이 프라이빗 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7117,7 +7117,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of constructor signature from exported interface has or is using private name '{0}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 생성자 시그니처의 반환 형식이 전용 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 생성자 시그니처의 반환 형식이 프라이빗 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7147,7 +7147,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of exported function has or is using name '{0}' from private module '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 함수의 반환 형식이 전용 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 함수의 반환 형식이 프라이빗 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7156,7 +7156,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of exported function has or is using private name '{0}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 함수의 반환 형식이 전용 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 함수의 반환 형식이 프라이빗 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7165,7 +7165,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of index signature from exported interface has or is using name '{0}' from private module '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 인덱스 시그니처의 반환 형식이 전용 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 인덱스 시그니처의 반환 형식이 프라이빗 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7174,7 +7174,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of index signature from exported interface has or is using private name '{0}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 인덱스 시그니처의 반환 형식이 전용 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 인덱스 시그니처의 반환 형식이 프라이빗 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7183,7 +7183,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of method from exported interface has or is using name '{0}' from private module '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 메서드의 반환 형식이 전용 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 메서드의 반환 형식이 프라이빗 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7192,7 +7192,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of method from exported interface has or is using private name '{0}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 메서드의 반환 형식이 전용 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 메서드의 반환 형식이 프라이빗 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7210,7 +7210,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of public getter '{0}' from exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 getter '{0}'의 반환 형식이 전용 모듈 '{2}'의 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 getter '{0}'의 반환 형식이 프라이빗 모듈 '{2}'의 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7219,7 +7219,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of public getter '{0}' from exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 getter '{0}'의 반환 형식이 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 getter '{0}'의 반환 형식이 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7237,7 +7237,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of public method from exported class has or is using name '{0}' from private module '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 메서드의 반환 형식이 전용 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 메서드의 반환 형식이 프라이빗 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7246,7 +7246,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of public method from exported class has or is using private name '{0}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 메서드의 반환 형식이 전용 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 메서드의 반환 형식이 프라이빗 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7264,7 +7264,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of public static getter '{0}' from exported class has or is using name '{1}' from private module '{2}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 getter '{0}'의 반환 형식이 전용 모듈 '{2}'의 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 getter '{0}'의 반환 형식이 프라이빗 모듈 '{2}'의 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7273,7 +7273,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of public static getter '{0}' from exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 getter '{0}'의 반환 형식이 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 getter '{0}'의 반환 형식이 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7291,7 +7291,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of public static method from exported class has or is using name '{0}' from private module '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드의 반환 형식이 전용 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드의 반환 형식이 프라이빗 모듈 '{1}'의 '{0}' 이름을 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -7300,7 +7300,7 @@
<Str Cat="Text">
<Val><![CDATA[Return type of public static method from exported class has or is using private name '{0}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드의 반환 형식이 전용 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드의 반환 형식이 프라이빗 이름 '{0}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -8692,7 +8692,7 @@
<Str Cat="Text">
<Val><![CDATA[Type parameter '{0}' of call signature from exported interface has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 호출 시그니처의 형식 매개 변수 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 호출 시그니처의 형식 매개 변수 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -8701,7 +8701,7 @@
<Str Cat="Text">
<Val><![CDATA[Type parameter '{0}' of constructor signature from exported interface has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 생성자 시그니처의 형식 매개 변수 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 생성자 시그니처의 형식 매개 변수 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -8710,7 +8710,7 @@
<Str Cat="Text">
<Val><![CDATA[Type parameter '{0}' of exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스의 형식 매개 변수 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스의 형식 매개 변수 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -8719,7 +8719,7 @@
<Str Cat="Text">
<Val><![CDATA[Type parameter '{0}' of exported function has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 함수의 형식 매개 변수 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 함수의 형식 매개 변수 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -8728,7 +8728,7 @@
<Str Cat="Text">
<Val><![CDATA[Type parameter '{0}' of exported interface has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스의 형식 매개 변수 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스의 형식 매개 변수 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -8737,7 +8737,7 @@
<Str Cat="Text">
<Val><![CDATA[Type parameter '{0}' of exported type alias has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 형식 별칭의 형식 매개 변수 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 형식 별칭의 형식 매개 변수 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -8746,7 +8746,7 @@
<Str Cat="Text">
<Val><![CDATA[Type parameter '{0}' of method from exported interface has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스에 있는 메서드의 형식 매개 변수 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스에 있는 메서드의 형식 매개 변수 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -8755,7 +8755,7 @@
<Str Cat="Text">
<Val><![CDATA[Type parameter '{0}' of public method from exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 메서드의 형식 매개 변수 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 메서드의 형식 매개 변수 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -8764,7 +8764,7 @@
<Str Cat="Text">
<Val><![CDATA[Type parameter '{0}' of public static method from exported class has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드의 형식 매개 변수 '{0}'이(가) 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스에 있는 공용 정적 메서드의 형식 매개 변수 '{0}'이(가) 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -8839,7 +8839,7 @@
<Str Cat="Text">
<Val><![CDATA[Types have separate declarations of a private property '{0}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[형식에 별도의 전용 속성 '{0}' 선언이 있습니다.]]></Val>
<Val><![CDATA[형식에 별도의 프라이빗 속성 '{0}' 선언이 있습니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -9676,7 +9676,7 @@
<Str Cat="Text">
<Val><![CDATA['extends' clause of exported class '{0}' has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 클래스 '{0}'의 Extends 절이 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 클래스 '{0}'의 Extends 절이 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -9685,7 +9685,7 @@
<Str Cat="Text">
<Val><![CDATA['extends' clause of exported interface '{0}' has or is using private name '{1}'.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[내보낸 인터페이스 '{0}'의 Extends 절이 전용 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
<Val><![CDATA[내보낸 인터페이스 '{0}'의 Extends 절이 프라이빗 이름 '{1}'을(를) 가지고 있거나 사용 중입니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
+28 -9
View File
@@ -1003,6 +1003,12 @@ namespace ts.server {
fileOrDirectory => {
const fileOrDirectoryPath = this.toPath(fileOrDirectory);
project.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
// don't trigger callback on open, existing files
if (project.fileIsOpen(fileOrDirectoryPath)) {
return;
}
if (isPathIgnored(fileOrDirectoryPath)) return;
const configFilename = project.getConfigFilePath();
@@ -1327,7 +1333,11 @@ namespace ts.server {
const watches: WatchType[] = [];
if (configFileExistenceInfo.configFileWatcherForRootOfInferredProject) {
watches.push(WatchType.ConfigFileForInferredRoot);
watches.push(
configFileExistenceInfo.configFileWatcherForRootOfInferredProject === noopFileWatcher ?
WatchType.NoopConfigFileForInferredRoot :
WatchType.ConfigFileForInferredRoot
);
}
if (this.configuredProjects.has(canonicalConfigFilePath)) {
watches.push(WatchType.ConfigFile);
@@ -1343,13 +1353,16 @@ namespace ts.server {
canonicalConfigFilePath: string,
configFileExistenceInfo: ConfigFileExistenceInfo
) {
configFileExistenceInfo.configFileWatcherForRootOfInferredProject = this.watchFactory.watchFile(
this.host,
configFileName,
(_filename, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind),
PollingInterval.High,
WatchType.ConfigFileForInferredRoot
);
configFileExistenceInfo.configFileWatcherForRootOfInferredProject =
canWatchDirectory(getDirectoryPath(canonicalConfigFilePath) as Path) ?
this.watchFactory.watchFile(
this.host,
configFileName,
(_filename, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind),
PollingInterval.High,
WatchType.ConfigFileForInferredRoot
) :
noopFileWatcher;
this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.UpdatedCallback);
}
@@ -2221,7 +2234,13 @@ namespace ts.server {
getDocumentPositionMapper(project: Project, generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined {
// Since declaration info and map file watches arent updating project's directory structure host (which can cache file structure) use host
const declarationInfo = this.getOrCreateScriptInfoNotOpenedByClient(generatedFileName, project.currentDirectory, this.host);
if (!declarationInfo) return undefined;
if (!declarationInfo) {
if (sourceFileName) {
// Project contains source file and it generates the generated file name
project.addGeneratedFileWatch(generatedFileName, sourceFileName);
}
return undefined;
}
// Try to get from cache
declarationInfo.getSnapshot(); // Ensure synchronized
+116 -8
View File
@@ -109,12 +109,22 @@ namespace ts.server {
return value instanceof ScriptInfo;
}
interface GeneratedFileWatcher {
generatedFilePath: Path;
watcher: FileWatcher;
}
type GeneratedFileWatcherMap = GeneratedFileWatcher | Map<GeneratedFileWatcher>;
function isGeneratedFileWatcher(watch: GeneratedFileWatcherMap): watch is GeneratedFileWatcher {
return (watch as GeneratedFileWatcher).generatedFilePath !== undefined;
}
export abstract class Project implements LanguageServiceHost, ModuleResolutionHost {
private rootFiles: ScriptInfo[] = [];
private rootFilesMap: Map<ProjectRoot> = createMap<ProjectRoot>();
private program: Program | undefined;
private externalFiles: SortedReadonlyArray<string> | undefined;
private missingFilesMap: Map<FileWatcher> | undefined;
private generatedFilesMap: GeneratedFileWatcherMap | undefined;
private plugins: PluginModuleWithName[] = [];
/*@internal*/
@@ -198,13 +208,13 @@ namespace ts.server {
return hasOneOrMoreJsAndNoTsFiles(this);
}
public static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void): {} | undefined {
public static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void, logErrors?: (message: string) => void): {} | undefined {
const resolvedPath = normalizeSlashes(host.resolvePath(combinePaths(initialDir, "node_modules")));
log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`);
const result = host.require!(resolvedPath, moduleName); // TODO: GH#18217
if (result.error) {
const err = result.error.stack || result.error.message || JSON.stringify(result.error);
log(`Failed to load module '${moduleName}': ${err}`);
(logErrors || log)(`Failed to load module '${moduleName}' from ${resolvedPath}: ${err}`);
return undefined;
}
return result.module;
@@ -457,6 +467,14 @@ namespace ts.server {
return this.getTypeAcquisition().enable ? this.projectService.typingsInstaller.globalTypingsCacheLocation : undefined;
}
/*@internal*/
globalCacheResolutionModuleName = JsTyping.nonRelativeModuleNameForTypingCache;
/*@internal*/
fileIsOpen(filePath: Path) {
return this.projectService.openFiles.has(filePath);
}
/*@internal*/
writeLog(s: string) {
this.projectService.logger.info(s);
@@ -560,6 +578,7 @@ namespace ts.server {
this.lastFileExceededProgramSize = lastFileExceededProgramSize;
this.builderState = undefined;
this.resolutionCache.closeTypeRootsWatch();
this.clearGeneratedFileWatch();
this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ false);
}
@@ -641,6 +660,7 @@ namespace ts.server {
clearMap(this.missingFilesMap, closeFileWatcher);
this.missingFilesMap = undefined!;
}
this.clearGeneratedFileWatch();
// signal language service to release source files acquired from document registry
this.languageService.dispose();
@@ -934,6 +954,39 @@ namespace ts.server {
missingFilePath => this.addMissingFileWatcher(missingFilePath)
);
if (this.generatedFilesMap) {
const outPath = this.compilerOptions.outFile && this.compilerOptions.out;
if (isGeneratedFileWatcher(this.generatedFilesMap)) {
// --out
if (!outPath || !this.isValidGeneratedFileWatcher(
removeFileExtension(outPath) + Extension.Dts,
this.generatedFilesMap,
)) {
this.clearGeneratedFileWatch();
}
}
else {
// MultiFile
if (outPath) {
this.clearGeneratedFileWatch();
}
else {
this.generatedFilesMap.forEach((watcher, source) => {
const sourceFile = this.program!.getSourceFileByPath(source as Path);
if (!sourceFile ||
sourceFile.resolvedPath !== source ||
!this.isValidGeneratedFileWatcher(
getDeclarationEmitOutputFilePathWorker(sourceFile.fileName, this.compilerOptions, this.currentDirectory, this.program!.getCommonSourceDirectory(), this.getCanonicalFileName),
watcher
)) {
closeFileWatcherOf(watcher);
(this.generatedFilesMap as Map<GeneratedFileWatcher>).delete(source);
}
});
}
}
}
// Watch the type locations that would be added to program as part of automatic type resolutions
if (this.languageServiceEnabled) {
this.resolutionCache.updateTypeRootsWatch();
@@ -944,7 +997,7 @@ namespace ts.server {
this.externalFiles = this.getExternalFiles();
enumerateInsertsAndDeletes<string, string>(this.externalFiles, oldExternalFiles, getStringComparer(!this.useCaseSensitiveFileNames()),
// Ensure a ScriptInfo is created for new external files. This is performed indirectly
// by the LSHost for files in the program when the program is retrieved above but
// by the host for files in the program when the program is retrieved above but
// the program doesn't contain external files so this must be done explicitly.
inserted => {
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(inserted, this.currentDirectory, this.directoryStructureHost)!;
@@ -998,6 +1051,61 @@ namespace ts.server {
return !!this.missingFilesMap && this.missingFilesMap.has(path);
}
/* @internal */
addGeneratedFileWatch(generatedFile: string, sourceFile: string) {
if (this.compilerOptions.outFile || this.compilerOptions.out) {
// Single watcher
if (!this.generatedFilesMap) {
this.generatedFilesMap = this.createGeneratedFileWatcher(generatedFile);
}
}
else {
// Map
const path = this.toPath(sourceFile);
if (this.generatedFilesMap) {
if (isGeneratedFileWatcher(this.generatedFilesMap)) {
Debug.fail(`${this.projectName} Expected to not have --out watcher for generated file with options: ${JSON.stringify(this.compilerOptions)}`);
return;
}
if (this.generatedFilesMap.has(path)) return;
}
else {
this.generatedFilesMap = createMap();
}
this.generatedFilesMap.set(path, this.createGeneratedFileWatcher(generatedFile));
}
}
private createGeneratedFileWatcher(generatedFile: string): GeneratedFileWatcher {
return {
generatedFilePath: this.toPath(generatedFile),
watcher: this.projectService.watchFactory.watchFile(
this.projectService.host,
generatedFile,
() => this.projectService.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(this),
PollingInterval.High,
WatchType.MissingGeneratedFile,
this
)
};
}
private isValidGeneratedFileWatcher(generateFile: string, watcher: GeneratedFileWatcher) {
return this.toPath(generateFile) === watcher.generatedFilePath;
}
private clearGeneratedFileWatch() {
if (this.generatedFilesMap) {
if (isGeneratedFileWatcher(this.generatedFilesMap)) {
closeFileWatcherOf(this.generatedFilesMap);
}
else {
clearMap(this.generatedFilesMap, closeFileWatcherOf);
}
this.generatedFilesMap = undefined;
}
}
getScriptInfoForNormalizedPath(fileName: NormalizedPath): ScriptInfo | undefined {
const scriptInfo = this.projectService.getScriptInfoForPath(this.toPath(fileName));
if (scriptInfo && !scriptInfo.isAttached(this)) {
@@ -1142,12 +1250,11 @@ namespace ts.server {
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[], pluginConfigOverrides: Map<any> | undefined) {
this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`);
const log = (message: string) => {
this.projectService.logger.info(message);
};
const log = (message: string) => this.projectService.logger.info(message);
let errorLogs: string[] | undefined;
const logError = (message: string) => { (errorLogs || (errorLogs = [])).push(message); };
const resolvedModule = firstDefined(searchPaths, searchPath =>
<PluginModuleFactory | undefined>Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log));
<PluginModuleFactory | undefined>Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log, logError));
if (resolvedModule) {
const configurationOverride = pluginConfigOverrides && pluginConfigOverrides.get(pluginConfigEntry.name);
if (configurationOverride) {
@@ -1160,6 +1267,7 @@ namespace ts.server {
this.enableProxy(resolvedModule, pluginConfigEntry);
}
else {
forEach(errorLogs, log);
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`);
}
}
+64 -9
View File
@@ -94,6 +94,8 @@ namespace ts.server.protocol {
ApplyChangedToOpenFiles = "applyChangedToOpenFiles",
UpdateOpen = "updateOpen",
/* @internal */
EncodedSyntacticClassificationsFull = "encodedSyntacticClassifications-full",
/* @internal */
EncodedSemanticClassificationsFull = "encodedSemanticClassifications-full",
/* @internal */
Cleanup = "cleanup",
@@ -130,7 +132,10 @@ namespace ts.server.protocol {
GetEditsForFileRename = "getEditsForFileRename",
/* @internal */
GetEditsForFileRenameFull = "getEditsForFileRename-full",
ConfigurePlugin = "configurePlugin"
ConfigurePlugin = "configurePlugin",
SelectionRange = "selectionRange",
/* @internal */
SelectionRangeFull = "selectionRange-full",
// NOTE: If updating this, be sure to also update `allCommandNames` in `harness/unittests/session.ts`.
}
@@ -761,6 +766,29 @@ namespace ts.server.protocol {
body?: string[];
}
/**
* A request to get encoded Syntactic classifications for a span in the file
*/
/** @internal */
export interface EncodedSyntacticClassificationsRequest extends FileRequest {
arguments: EncodedSyntacticClassificationsRequestArgs;
}
/**
* Arguments for EncodedSyntacticClassificationsRequest request.
*/
/** @internal */
export interface EncodedSyntacticClassificationsRequestArgs extends FileRequestArgs {
/**
* Start position of the span.
*/
start: number;
/**
* Length of the span.
*/
length: number;
}
/**
* A request to get encoded semantic classifications for a span in the file
*/
@@ -772,6 +800,7 @@ namespace ts.server.protocol {
/**
* Arguments for EncodedSemanticClassificationsRequest request.
*/
/** @internal */
export interface EncodedSemanticClassificationsRequestArgs extends FileRequestArgs {
/**
* Start position of the span.
@@ -869,8 +898,16 @@ namespace ts.server.protocol {
file: string;
}
export interface TextSpanWithContext extends TextSpan {
contextStart?: Location;
contextEnd?: Location;
}
export interface FileSpanWithContext extends FileSpan, TextSpanWithContext {
}
export interface DefinitionInfoAndBoundSpan {
definitions: ReadonlyArray<FileSpan>;
definitions: ReadonlyArray<FileSpanWithContext>;
textSpan: TextSpan;
}
@@ -878,7 +915,7 @@ namespace ts.server.protocol {
* Definition response message. Gives text range for definition.
*/
export interface DefinitionResponse extends Response {
body?: FileSpan[];
body?: FileSpanWithContext[];
}
export interface DefinitionInfoAndBoundSpanReponse extends Response {
@@ -889,14 +926,14 @@ namespace ts.server.protocol {
* Definition response message. Gives text range for definition.
*/
export interface TypeDefinitionResponse extends Response {
body?: FileSpan[];
body?: FileSpanWithContext[];
}
/**
* Implementation response message. Gives text range for implementations.
*/
export interface ImplementationResponse extends Response {
body?: FileSpan[];
body?: FileSpanWithContext[];
}
/**
@@ -939,7 +976,7 @@ namespace ts.server.protocol {
}
/** @deprecated */
export interface OccurrencesResponseItem extends FileSpan {
export interface OccurrencesResponseItem extends FileSpanWithContext {
/**
* True if the occurrence is a write location, false otherwise.
*/
@@ -969,7 +1006,7 @@ namespace ts.server.protocol {
/**
* Span augmented with extra information that denotes the kind of the highlighting to be used for span.
*/
export interface HighlightSpan extends TextSpan {
export interface HighlightSpan extends TextSpanWithContext {
kind: HighlightSpanKind;
}
@@ -1004,7 +1041,7 @@ namespace ts.server.protocol {
command: CommandTypes.References;
}
export interface ReferencesResponseItem extends FileSpan {
export interface ReferencesResponseItem extends FileSpanWithContext {
/** Text of line containing the reference. Including this
* with the response avoids latency of editor loading files
* to show text of reference line (the server already has
@@ -1147,7 +1184,7 @@ namespace ts.server.protocol {
locs: RenameTextSpan[];
}
export interface RenameTextSpan extends TextSpan {
export interface RenameTextSpan extends TextSpanWithContext {
readonly prefixText?: string;
readonly suffixText?: string;
}
@@ -1395,6 +1432,24 @@ namespace ts.server.protocol {
export interface ConfigurePluginResponse extends Response {
}
export interface SelectionRangeRequest extends FileRequest {
command: CommandTypes.SelectionRange;
arguments: SelectionRangeRequestArgs;
}
export interface SelectionRangeRequestArgs extends FileRequestArgs {
locations: Location[];
}
export interface SelectionRangeResponse extends Response {
body?: SelectionRange[];
}
export interface SelectionRange {
textSpan: TextSpan;
parent?: SelectionRange;
}
/**
* Information found in an "open" request.
*/
+174 -127
View File
@@ -354,13 +354,17 @@ namespace ts.server {
defaultProject,
initialLocation,
({ project, location }, getMappedLocation) => {
for (const outputReferencedSymbol of project.getLanguageService().findReferences(location.fileName, location.pos) || emptyArray) {
for (const outputReferencedSymbol of project.getLanguageService().findReferences(location.fileName, location.pos) || emptyArray) {
const mappedDefinitionFile = getMappedLocation(project, documentSpanLocation(outputReferencedSymbol.definition));
const definition: ReferencedSymbolDefinitionInfo = mappedDefinitionFile === undefined ? outputReferencedSymbol.definition : {
...outputReferencedSymbol.definition,
textSpan: createTextSpan(mappedDefinitionFile.pos, outputReferencedSymbol.definition.textSpan.length),
fileName: mappedDefinitionFile.fileName,
};
const definition: ReferencedSymbolDefinitionInfo = mappedDefinitionFile === undefined ?
outputReferencedSymbol.definition :
{
...outputReferencedSymbol.definition,
textSpan: createTextSpan(mappedDefinitionFile.pos, outputReferencedSymbol.definition.textSpan.length),
fileName: mappedDefinitionFile.fileName,
contextSpan: getMappedContextSpan(outputReferencedSymbol.definition, project)
};
let symbolToAddTo = find(outputs, o => documentSpansEqual(o.definition, definition));
if (!symbolToAddTo) {
symbolToAddTo = { definition, references: [] };
@@ -481,9 +485,39 @@ namespace ts.server {
return { fileName, pos: textSpan.start };
}
function getMappedLocation(location: DocumentPosition, projectService: ProjectService, project: Project): DocumentPosition | undefined {
function getMappedLocation(location: DocumentPosition, project: Project): DocumentPosition | undefined {
const mapsTo = project.getSourceMapper().tryGetSourcePosition(location);
return mapsTo && projectService.fileExists(toNormalizedPath(mapsTo.fileName)) ? mapsTo : undefined;
return mapsTo && project.projectService.fileExists(toNormalizedPath(mapsTo.fileName)) ? mapsTo : undefined;
}
function getMappedDocumentSpan(documentSpan: DocumentSpan, project: Project): DocumentSpan | undefined {
const newPosition = getMappedLocation(documentSpanLocation(documentSpan), project);
if (!newPosition) return undefined;
return {
fileName: newPosition.fileName,
textSpan: {
start: newPosition.pos,
length: documentSpan.textSpan.length
},
originalFileName: documentSpan.fileName,
originalTextSpan: documentSpan.textSpan,
contextSpan: getMappedContextSpan(documentSpan, project),
originalContextSpan: documentSpan.contextSpan
};
}
function getMappedContextSpan(documentSpan: DocumentSpan, project: Project): TextSpan | undefined {
const contextSpanStart = documentSpan.contextSpan && getMappedLocation(
{ fileName: documentSpan.fileName, pos: documentSpan.contextSpan.start },
project
);
const contextSpanEnd = documentSpan.contextSpan && getMappedLocation(
{ fileName: documentSpan.fileName, pos: documentSpan.contextSpan.start + documentSpan.contextSpan.length },
project
);
return contextSpanStart && contextSpanEnd ?
{ start: contextSpanStart.pos, length: contextSpanEnd.pos - contextSpanStart.pos } :
undefined;
}
export interface SessionOptions {
@@ -847,6 +881,11 @@ namespace ts.server {
}
}
private getEncodedSyntacticClassifications(args: protocol.EncodedSyntacticClassificationsRequestArgs) {
const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args);
return languageService.getEncodedSyntacticClassifications(file, args);
}
private getEncodedSemanticClassifications(args: protocol.EncodedSemanticClassificationsRequestArgs) {
const { file, project } = this.getFileAndProject(args);
return project.getLanguageService().getEncodedSemanticClassifications(file, args);
@@ -937,7 +976,7 @@ namespace ts.server {
: diagnostics.map(d => formatDiag(file, project, d));
}
private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.FileSpan> | ReadonlyArray<DefinitionInfo> {
private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.FileSpanWithContext> | ReadonlyArray<DefinitionInfo> {
const { file, project } = this.getFileAndProject(args);
const position = this.getPositionInFile(args, file);
const definitions = this.mapDefinitionInfoLocations(project.getLanguageService().getDefinitionAtPosition(file, position) || emptyArray, project);
@@ -946,19 +985,13 @@ namespace ts.server {
private mapDefinitionInfoLocations(definitions: ReadonlyArray<DefinitionInfo>, project: Project): ReadonlyArray<DefinitionInfo> {
return definitions.map((info): DefinitionInfo => {
const newLoc = getMappedLocation(documentSpanLocation(info), this.projectService, project);
return !newLoc ? info : {
const newDocumentSpan = getMappedDocumentSpan(info, project);
return !newDocumentSpan ? info : {
...newDocumentSpan,
containerKind: info.containerKind,
containerName: info.containerName,
fileName: newLoc.fileName,
kind: info.kind,
name: info.name,
textSpan: {
start: newLoc.pos,
length: info.textSpan.length
},
originalFileName: info.fileName,
originalTextSpan: info.textSpan,
};
});
}
@@ -983,7 +1016,7 @@ namespace ts.server {
if (simplifiedResult) {
return {
definitions: this.mapDefinitionInfo(definitions, project),
textSpan: this.toLocationTextSpan(textSpan, scriptInfo)
textSpan: toProcolTextSpan(textSpan, scriptInfo)
};
}
@@ -998,8 +1031,8 @@ namespace ts.server {
return project.getLanguageService().getEmitOutput(file);
}
private mapDefinitionInfo(definitions: ReadonlyArray<DefinitionInfo>, project: Project): ReadonlyArray<protocol.FileSpan> {
return definitions.map(def => this.toFileSpan(def.fileName, def.textSpan, project));
private mapDefinitionInfo(definitions: ReadonlyArray<DefinitionInfo>, project: Project): ReadonlyArray<protocol.FileSpanWithContext> {
return definitions.map(def => this.toFileSpanWithContext(def.fileName, def.textSpan, def.contextSpan, project));
}
/*
@@ -1017,7 +1050,9 @@ namespace ts.server {
fileName: def.originalFileName,
textSpan: def.originalTextSpan,
targetFileName: def.fileName,
targetTextSpan: def.textSpan
targetTextSpan: def.textSpan,
contextSpan: def.originalContextSpan,
targetContextSpan: def.contextSpan
};
}
return def;
@@ -1035,7 +1070,15 @@ namespace ts.server {
};
}
private getTypeDefinition(args: protocol.FileLocationRequestArgs): ReadonlyArray<protocol.FileSpan> {
private toFileSpanWithContext(fileName: string, textSpan: TextSpan, contextSpan: TextSpan | undefined, project: Project): protocol.FileSpanWithContext {
const fileSpan = this.toFileSpan(fileName, textSpan, project);
const context = contextSpan && this.toFileSpan(fileName, contextSpan, project);
return context ?
{ ...fileSpan, contextStart: context.start, contextEnd: context.end } :
fileSpan;
}
private getTypeDefinition(args: protocol.FileLocationRequestArgs): ReadonlyArray<protocol.FileSpanWithContext> {
const { file, project } = this.getFileAndProject(args);
const position = this.getPositionInFile(args, file);
@@ -1045,58 +1088,40 @@ namespace ts.server {
private mapImplementationLocations(implementations: ReadonlyArray<ImplementationLocation>, project: Project): ReadonlyArray<ImplementationLocation> {
return implementations.map((info): ImplementationLocation => {
const newLoc = getMappedLocation(documentSpanLocation(info), this.projectService, project);
return !newLoc ? info : {
fileName: newLoc.fileName,
const newDocumentSpan = getMappedDocumentSpan(info, project);
return !newDocumentSpan ? info : {
...newDocumentSpan,
kind: info.kind,
displayParts: info.displayParts,
textSpan: {
start: newLoc.pos,
length: info.textSpan.length
},
originalFileName: info.fileName,
originalTextSpan: info.textSpan,
};
});
}
private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.FileSpan> | ReadonlyArray<ImplementationLocation> {
private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.FileSpanWithContext> | ReadonlyArray<ImplementationLocation> {
const { file, project } = this.getFileAndProject(args);
const position = this.getPositionInFile(args, file);
const implementations = this.mapImplementationLocations(project.getLanguageService().getImplementationAtPosition(file, position) || emptyArray, project);
if (simplifiedResult) {
return implementations.map(({ fileName, textSpan }) => this.toFileSpan(fileName, textSpan, project));
}
return implementations.map(Session.mapToOriginalLocation);
return simplifiedResult ?
implementations.map(({ fileName, textSpan, contextSpan }) => this.toFileSpanWithContext(fileName, textSpan, contextSpan, project)) :
implementations.map(Session.mapToOriginalLocation);
}
private getOccurrences(args: protocol.FileLocationRequestArgs): ReadonlyArray<protocol.OccurrencesResponseItem> {
const { file, project } = this.getFileAndProject(args);
const position = this.getPositionInFile(args, file);
const occurrences = project.getLanguageService().getOccurrencesAtPosition(file, position);
if (!occurrences) {
return emptyArray;
}
return occurrences.map(occurrence => {
const { fileName, isWriteAccess, textSpan, isInString } = occurrence;
const scriptInfo = project.getScriptInfo(fileName)!;
const result: protocol.OccurrencesResponseItem = {
start: scriptInfo.positionToLineOffset(textSpan.start),
end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan)),
file: fileName,
isWriteAccess,
};
// no need to serialize the property if it is not true
if (isInString) {
result.isInString = isInString;
}
return result;
});
return occurrences ?
occurrences.map<protocol.OccurrencesResponseItem>(occurrence => {
const { fileName, isWriteAccess, textSpan, isInString, contextSpan } = occurrence;
const scriptInfo = project.getScriptInfo(fileName)!;
return {
...toProtocolTextSpanWithContext(textSpan, contextSpan, scriptInfo),
file: fileName,
isWriteAccess,
...(isInString ? { isInString } : undefined)
};
}) :
emptyArray;
}
private getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): ReadonlyArray<protocol.Diagnostic> | ReadonlyArray<protocol.DiagnosticWithLinePosition> {
@@ -1139,33 +1164,19 @@ namespace ts.server {
const position = this.getPositionInFile(args, file);
const documentHighlights = project.getLanguageService().getDocumentHighlights(file, position, args.filesToSearch);
if (!documentHighlights) {
return emptyArray;
}
if (simplifiedResult) {
return documentHighlights.map(convertToDocumentHighlightsItem);
}
else {
return documentHighlights;
}
function convertToDocumentHighlightsItem(documentHighlights: DocumentHighlights): protocol.DocumentHighlightsItem {
const { fileName, highlightSpans } = documentHighlights;
if (!documentHighlights) return emptyArray;
if (!simplifiedResult) return documentHighlights;
return documentHighlights.map<protocol.DocumentHighlightsItem>(({ fileName, highlightSpans }) => {
const scriptInfo = project.getScriptInfo(fileName)!;
return {
file: fileName,
highlightSpans: highlightSpans.map(convertHighlightSpan)
highlightSpans: highlightSpans.map(({ textSpan, kind, contextSpan }) => ({
...toProtocolTextSpanWithContext(textSpan, contextSpan, scriptInfo),
kind
}))
};
function convertHighlightSpan(highlightSpan: HighlightSpan): protocol.HighlightSpan {
const { textSpan, kind } = highlightSpan;
const start = scriptInfo.positionToLineOffset(textSpan.start);
const end = scriptInfo.positionToLineOffset(textSpanEnd(textSpan));
return { start, end, kind };
}
}
});
}
private setCompilerOptionsForInferredProjects(args: protocol.SetCompilerOptionsForInferredProjectsArgs): void {
@@ -1258,7 +1269,7 @@ namespace ts.server {
if (info.canRename) {
const { canRename, fileToRename, displayName, fullDisplayName, kind, kindModifiers, triggerSpan } = info;
return identity<protocol.RenameInfoSuccess>(
{ canRename, fileToRename, displayName, fullDisplayName, kind, kindModifiers, triggerSpan: this.toLocationTextSpan(triggerSpan, scriptInfo) });
{ canRename, fileToRename, displayName, fullDisplayName, kind, kindModifiers, triggerSpan: toProcolTextSpan(triggerSpan, scriptInfo) });
}
else {
return info;
@@ -1267,11 +1278,11 @@ namespace ts.server {
private toSpanGroups(locations: ReadonlyArray<RenameLocation>): ReadonlyArray<protocol.SpanGroup> {
const map = createMap<protocol.SpanGroup>();
for (const { fileName, textSpan, originalTextSpan: _, originalFileName: _1, ...prefixSuffixText } of locations) {
for (const { fileName, textSpan, contextSpan, originalContextSpan: _2, originalTextSpan: _, originalFileName: _1, ...prefixSuffixText } of locations) {
let group = map.get(fileName);
if (!group) map.set(fileName, group = { file: fileName, locs: [] });
const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(fileName));
group.locs.push({ ...this.toLocationTextSpan(textSpan, scriptInfo), ...prefixSuffixText });
group.locs.push({ ...toProtocolTextSpanWithContext(textSpan, contextSpan, scriptInfo), ...prefixSuffixText });
}
return arrayFrom(map.values());
}
@@ -1286,30 +1297,31 @@ namespace ts.server {
{ fileName: args.file, pos: position },
);
if (simplifiedResult) {
const defaultProject = this.getDefaultProject(args);
const scriptInfo = defaultProject.getScriptInfoForNormalizedPath(file)!;
const nameInfo = defaultProject.getLanguageService().getQuickInfoAtPosition(file, position);
const symbolDisplayString = nameInfo ? displayPartsToString(nameInfo.displayParts) : "";
const nameSpan = nameInfo && nameInfo.textSpan;
const symbolStartOffset = nameSpan ? scriptInfo.positionToLineOffset(nameSpan.start).offset : 0;
const symbolName = nameSpan ? scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)) : "";
const refs: ReadonlyArray<protocol.ReferencesResponseItem> = flatMap(references, referencedSymbol =>
referencedSymbol.references.map(({ fileName, textSpan, isWriteAccess, isDefinition }): protocol.ReferencesResponseItem => {
const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(fileName));
const start = scriptInfo.positionToLineOffset(textSpan.start);
const lineSpan = scriptInfo.lineToTextSpan(start.line - 1);
const lineText = scriptInfo.getSnapshot().getText(lineSpan.start, textSpanEnd(lineSpan)).replace(/\r|\n/g, "");
return { ...toFileSpan(fileName, textSpan, scriptInfo), lineText, isWriteAccess, isDefinition };
}));
const result: protocol.ReferencesResponseBody = { refs, symbolName, symbolStartOffset, symbolDisplayString };
return result;
}
else {
return references;
}
}
if (!simplifiedResult) return references;
const defaultProject = this.getDefaultProject(args);
const scriptInfo = defaultProject.getScriptInfoForNormalizedPath(file)!;
const nameInfo = defaultProject.getLanguageService().getQuickInfoAtPosition(file, position);
const symbolDisplayString = nameInfo ? displayPartsToString(nameInfo.displayParts) : "";
const nameSpan = nameInfo && nameInfo.textSpan;
const symbolStartOffset = nameSpan ? scriptInfo.positionToLineOffset(nameSpan.start).offset : 0;
const symbolName = nameSpan ? scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)) : "";
const refs: ReadonlyArray<protocol.ReferencesResponseItem> = flatMap(references, referencedSymbol =>
referencedSymbol.references.map(({ fileName, textSpan, contextSpan, isWriteAccess, isDefinition }): protocol.ReferencesResponseItem => {
const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(fileName));
const span = toProtocolTextSpanWithContext(textSpan, contextSpan, scriptInfo);
const lineSpan = scriptInfo.lineToTextSpan(span.start.line - 1);
const lineText = scriptInfo.getSnapshot().getText(lineSpan.start, textSpanEnd(lineSpan)).replace(/\r|\n/g, "");
return {
file: fileName,
...span,
lineText,
isWriteAccess,
isDefinition
};
}));
return { refs, symbolName, symbolStartOffset, symbolDisplayString };
}
/**
* @param fileName is the name of the file to be opened
* @param fileContent is a version of the file content that is known to be more up to date than the one on disk
@@ -1318,11 +1330,11 @@ namespace ts.server {
this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath);
}
private getPosition(args: protocol.FileLocationRequestArgs, scriptInfo: ScriptInfo): number {
private getPosition(args: protocol.Location & { position?: number }, scriptInfo: ScriptInfo): number {
return args.position !== undefined ? args.position : scriptInfo.lineOffsetToPosition(args.line, args.offset);
}
private getPositionInFile(args: protocol.FileLocationRequestArgs, file: NormalizedPath): number {
private getPositionInFile(args: protocol.Location & { position?: number }, file: NormalizedPath): number {
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!;
return this.getPosition(args, scriptInfo);
}
@@ -1357,8 +1369,8 @@ namespace ts.server {
if (simplifiedResult) {
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!;
return spans.map(s => ({
textSpan: this.toLocationTextSpan(s.textSpan, scriptInfo),
hintSpan: this.toLocationTextSpan(s.hintSpan, scriptInfo),
textSpan: toProcolTextSpan(s.textSpan, scriptInfo),
hintSpan: toProcolTextSpan(s.hintSpan, scriptInfo),
bannerText: s.bannerText,
autoCollapse: s.autoCollapse,
kind: s.kind
@@ -1547,7 +1559,7 @@ namespace ts.server {
const entries = mapDefined<CompletionEntry, protocol.CompletionEntry>(completions.entries, entry => {
if (completions.isMemberCompletion || startsWith(entry.name.toLowerCase(), prefix.toLowerCase())) {
const { name, kind, kindModifiers, sortText, insertText, replacementSpan, hasAction, source, isRecommended } = entry;
const convertedSpan = replacementSpan ? this.toLocationTextSpan(replacementSpan, scriptInfo) : undefined;
const convertedSpan = replacementSpan ? toProcolTextSpan(replacementSpan, scriptInfo) : undefined;
// Use `hasAction || undefined` to avoid serializing `false`.
return { name, kind, kindModifiers, sortText, insertText, replacementSpan: convertedSpan, hasAction: hasAction || undefined, source, isRecommended };
}
@@ -1710,7 +1722,7 @@ namespace ts.server {
text: item.text,
kind: item.kind,
kindModifiers: item.kindModifiers,
spans: item.spans.map(span => this.toLocationTextSpan(span, scriptInfo)),
spans: item.spans.map(span => toProcolTextSpan(span, scriptInfo)),
childItems: this.mapLocationNavigationBarItems(item.childItems, scriptInfo),
indent: item.indent
}));
@@ -1731,19 +1743,12 @@ namespace ts.server {
text: tree.text,
kind: tree.kind,
kindModifiers: tree.kindModifiers,
spans: tree.spans.map(span => this.toLocationTextSpan(span, scriptInfo)),
nameSpan: tree.nameSpan && this.toLocationTextSpan(tree.nameSpan, scriptInfo),
spans: tree.spans.map(span => toProcolTextSpan(span, scriptInfo)),
nameSpan: tree.nameSpan && toProcolTextSpan(tree.nameSpan, scriptInfo),
childItems: map(tree.childItems, item => this.toLocationNavigationTree(item, scriptInfo))
};
}
private toLocationTextSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan {
return {
start: scriptInfo.positionToLineOffset(span.start),
end: scriptInfo.positionToLineOffset(textSpanEnd(span))
};
}
private getNavigationTree(args: protocol.FileRequestArgs, simplifiedResult: boolean): protocol.NavigationTree | NavigationTree | undefined {
const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args);
const tree = languageService.getNavigationTree(file);
@@ -2001,7 +2006,7 @@ namespace ts.server {
return !spans
? undefined
: simplifiedResult
? spans.map(span => this.toLocationTextSpan(span, scriptInfo))
? spans.map(span => toProcolTextSpan(span, scriptInfo))
: spans;
}
@@ -2059,6 +2064,28 @@ namespace ts.server {
this.projectService.configurePlugin(args);
}
private getSmartSelectionRange(args: protocol.SelectionRangeRequestArgs, simplifiedResult: boolean) {
const { locations } = args;
const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args);
const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(file));
return map(locations, location => {
const pos = this.getPosition(location, scriptInfo);
const selectionRange = languageService.getSmartSelectionRange(file, pos);
return simplifiedResult ? this.mapSelectionRange(selectionRange, scriptInfo) : selectionRange;
});
}
private mapSelectionRange(selectionRange: SelectionRange, scriptInfo: ScriptInfo): protocol.SelectionRange {
const result: protocol.SelectionRange = {
textSpan: toProcolTextSpan(selectionRange.textSpan, scriptInfo),
};
if (selectionRange.parent) {
result.parent = this.mapSelectionRange(selectionRange.parent, scriptInfo);
}
return result;
}
getCanonicalFileName(fileName: string) {
const name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
return normalizePath(name);
@@ -2277,6 +2304,9 @@ namespace ts.server {
[CommandNames.CompilerOptionsDiagnosticsFull]: (request: protocol.CompilerOptionsDiagnosticsRequest) => {
return this.requiredResponse(this.getCompilerOptionsDiagnostics(request.arguments));
},
[CommandNames.EncodedSyntacticClassificationsFull]: (request: protocol.EncodedSyntacticClassificationsRequest) => {
return this.requiredResponse(this.getEncodedSyntacticClassifications(request.arguments));
},
[CommandNames.EncodedSemanticClassificationsFull]: (request: protocol.EncodedSemanticClassificationsRequest) => {
return this.requiredResponse(this.getEncodedSemanticClassifications(request.arguments));
},
@@ -2414,7 +2444,13 @@ namespace ts.server {
this.configurePlugin(request.arguments);
this.doOutput(/*info*/ undefined, CommandNames.ConfigurePlugin, request.seq, /*success*/ true);
return this.notRequired();
}
},
[CommandNames.SelectionRange]: (request: protocol.SelectionRangeRequest) => {
return this.requiredResponse(this.getSmartSelectionRange(request.arguments, /*simplifiedResult*/ true));
},
[CommandNames.SelectionRangeFull]: (request: protocol.SelectionRangeRequest) => {
return this.requiredResponse(this.getSmartSelectionRange(request.arguments, /*simplifiedResult*/ false));
},
});
public addProtocolHandler(command: string, handler: (request: protocol.Request) => HandlerResponse) {
@@ -2530,8 +2566,19 @@ namespace ts.server {
readonly project: Project;
}
function toFileSpan(fileName: string, textSpan: TextSpan, scriptInfo: ScriptInfo): protocol.FileSpan {
return { file: fileName, start: scriptInfo.positionToLineOffset(textSpan.start), end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan)) };
function toProcolTextSpan(textSpan: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan {
return {
start: scriptInfo.positionToLineOffset(textSpan.start),
end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan))
};
}
function toProtocolTextSpanWithContext(span: TextSpan, contextSpan: TextSpan | undefined, scriptInfo: ScriptInfo): protocol.TextSpanWithContext {
const textSpan = toProcolTextSpan(span, scriptInfo);
const contextTextSpan = contextSpan && toProcolTextSpan(contextSpan, scriptInfo);
return contextTextSpan ?
{ ...textSpan, contextStart: contextTextSpan.start, contextEnd: contextTextSpan.end } :
textSpan;
}
function convertTextChangeToCodeEdit(change: TextChange, scriptInfo: ScriptInfoOrConfig): protocol.CodeEdit {
+2
View File
@@ -226,5 +226,7 @@ namespace ts {
ConfigFileForInferredRoot = "Config file for the inferred project root",
NodeModulesForClosedScriptInfo = "node_modules for closed script infos in them",
MissingSourceMapFile = "Missing source map file",
NoopConfigFileForInferredRoot = "Noop Config file for the inferred project root",
MissingGeneratedFile = "Missing generated file"
}
}
@@ -15,11 +15,11 @@ namespace ts.codefix {
function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number) {
const token = getTokenAtPosition(sourceFile, pos);
if (!isIdentifier(token)) {
return Debug.fail("add-name-to-nameless-parameter operates on identifiers, but got a " + formatSyntaxKind(token.kind));
return Debug.fail("add-name-to-nameless-parameter operates on identifiers, but got a " + Debug.formatSyntaxKind(token.kind));
}
const param = token.parent;
if (!isParameter(param)) {
return Debug.fail("Tried to add a parameter name to a non-parameter: " + formatSyntaxKind(token.kind));
return Debug.fail("Tried to add a parameter name to a non-parameter: " + Debug.formatSyntaxKind(token.kind));
}
const i = param.parent.parameters.indexOf(param);
Debug.assert(!param.type, "Tried to add a parameter name to a parameter that already had one.");
@@ -31,16 +31,16 @@ namespace ts.codefix {
registerCodeFix({
errorCodes: [
Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures.code,
Diagnostics.Cannot_use_new_with_an_expression_whose_type_lacks_a_call_or_construct_signature.code,
Diagnostics.This_expression_is_not_callable.code,
Diagnostics.This_expression_is_not_constructable.code,
],
getCodeActions: getActionsForUsageOfInvalidImport
});
function getActionsForUsageOfInvalidImport(context: CodeFixContext): CodeFixAction[] | undefined {
const sourceFile = context.sourceFile;
const targetKind = Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures.code === context.errorCode ? SyntaxKind.CallExpression : SyntaxKind.NewExpression;
const node = findAncestor(getTokenAtPosition(sourceFile, context.span.start), a => a.kind === targetKind && a.getStart() === context.span.start && a.getEnd() === (context.span.start + context.span.length)) as CallExpression | NewExpression;
const targetKind = Diagnostics.This_expression_is_not_callable.code === context.errorCode ? SyntaxKind.CallExpression : SyntaxKind.NewExpression;
const node = findAncestor(getTokenAtPosition(sourceFile, context.span.start), a => a.kind === targetKind) as CallExpression | NewExpression;
if (!node) {
return [];
}
+100 -10
View File
@@ -42,6 +42,9 @@ namespace ts.codefix {
// Property declarations
Diagnostics.Member_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code,
// Function expressions and declarations
Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation.code,
];
registerCodeFix({
errorCodes,
@@ -73,6 +76,8 @@ namespace ts.codefix {
case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code:
case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage.code:
return Diagnostics.Infer_parameter_types_from_usage;
case Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation.code:
return Diagnostics.Infer_this_type_of_0_from_usage;
default:
return Diagnostics.Infer_type_of_0_from_usage;
}
@@ -176,6 +181,14 @@ namespace ts.codefix {
}
return undefined;
// Function 'this'
case Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation.code:
if (textChanges.isThisTypeAnnotatable(containingFunction) && markSeen(containingFunction)) {
annotateThis(changes, sourceFile, containingFunction, program, host, cancellationToken);
return containingFunction;
}
return undefined;
default:
return Debug.fail(String(errorCode));
}
@@ -191,7 +204,9 @@ namespace ts.codefix {
if (!isIdentifier(parameterDeclaration.name)) {
return;
}
const parameterInferences = inferTypeForParametersFromUsage(containingFunction, sourceFile, program, cancellationToken) ||
const references = inferFunctionReferencesFromUsage(containingFunction, sourceFile, program, cancellationToken);
const parameterInferences = InferFromReference.inferTypeForParametersFromReferences(references, containingFunction, program, cancellationToken) ||
containingFunction.parameters.map<ParameterInference>(p => ({
declaration: p,
type: isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, program, cancellationToken) : program.getTypeChecker().getAnyType()
@@ -213,6 +228,36 @@ namespace ts.codefix {
}
}
function annotateThis(changes: textChanges.ChangeTracker, sourceFile: SourceFile, containingFunction: textChanges.ThisTypeAnnotatable, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken) {
const references = inferFunctionReferencesFromUsage(containingFunction, sourceFile, program, cancellationToken);
if (!references) {
return;
}
const thisInference = InferFromReference.inferTypeForThisFromReferences(references, program, cancellationToken);
if (!thisInference) {
return;
}
const typeNode = getTypeNodeIfAccessible(thisInference, containingFunction, program, host);
if (!typeNode) {
return;
}
if (isInJSFile(containingFunction)) {
annotateJSDocThis(changes, sourceFile, containingFunction, typeNode);
}
else {
changes.tryInsertThisTypeAnnotation(sourceFile, containingFunction, typeNode);
}
}
function annotateJSDocThis(changes: textChanges.ChangeTracker, sourceFile: SourceFile, containingFunction: FunctionLike, typeNode: TypeNode) {
addJSDocTags(changes, sourceFile, containingFunction, [
createJSDocThisTag(createJSDocTypeExpression(typeNode)),
]);
}
function annotateSetAccessor(changes: textChanges.ChangeTracker, sourceFile: SourceFile, setAccessorDeclaration: SetAccessorDeclaration, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken): void {
const param = firstOrUndefined(setAccessorDeclaration.parameters);
if (param && isIdentifier(setAccessorDeclaration.name) && isIdentifier(param.name)) {
@@ -317,7 +362,7 @@ namespace ts.codefix {
return InferFromReference.unifyFromContext(types, checker);
}
function inferTypeForParametersFromUsage(containingFunction: FunctionLike, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
function inferFunctionReferencesFromUsage(containingFunction: FunctionLike, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): ReadonlyArray<Identifier> | undefined {
let searchToken;
switch (containingFunction.kind) {
case SyntaxKind.Constructor:
@@ -335,9 +380,12 @@ namespace ts.codefix {
searchToken = containingFunction.name;
break;
}
if (searchToken) {
return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken, program, cancellationToken), containingFunction, program, cancellationToken);
if (!searchToken) {
return undefined;
}
return getReferences(searchToken, program, cancellationToken);
}
interface ParameterInference {
@@ -364,6 +412,7 @@ namespace ts.codefix {
constructContexts?: CallContext[];
numberIndexContext?: UsageContext;
stringIndexContext?: UsageContext;
candidateThisTypes?: Type[];
}
export function inferTypesFromReferences(references: ReadonlyArray<Identifier>, checker: TypeChecker, cancellationToken: CancellationToken): Type[] {
@@ -375,15 +424,12 @@ namespace ts.codefix {
return inferFromContext(usageContext, checker);
}
export function inferTypeForParametersFromReferences(references: ReadonlyArray<Identifier>, declaration: FunctionLike, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
const checker = program.getTypeChecker();
if (references.length === 0) {
return undefined;
}
if (!declaration.parameters) {
export function inferTypeForParametersFromReferences(references: ReadonlyArray<Identifier> | undefined, declaration: FunctionLike, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
if (references === undefined || references.length === 0 || !declaration.parameters) {
return undefined;
}
const checker = program.getTypeChecker();
const usageContext: UsageContext = {};
for (const reference of references) {
cancellationToken.throwIfCancellationRequested();
@@ -421,6 +467,22 @@ namespace ts.codefix {
});
}
export function inferTypeForThisFromReferences(references: ReadonlyArray<Identifier>, program: Program, cancellationToken: CancellationToken) {
if (references.length === 0) {
return undefined;
}
const checker = program.getTypeChecker();
const usageContext: UsageContext = {};
for (const reference of references) {
cancellationToken.throwIfCancellationRequested();
inferTypeFromContext(reference, checker, usageContext);
}
return unifyFromContext(usageContext.candidateThisTypes || emptyArray, checker);
}
function inferTypeFromContext(node: Expression, checker: TypeChecker, usageContext: UsageContext): void {
while (isRightSideOfQualifiedNameOrPropertyAccess(node)) {
node = <Expression>node.parent;
@@ -455,6 +517,13 @@ namespace ts.codefix {
case SyntaxKind.ElementAccessExpression:
inferTypeFromPropertyElementExpressionContext(<ElementAccessExpression>node.parent, node, checker, usageContext);
break;
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
inferTypeFromPropertyAssignment(<PropertyAssignment | ShorthandPropertyAssignment>node.parent, checker, usageContext);
break;
case SyntaxKind.PropertyDeclaration:
inferTypeFromPropertyDeclaration(<PropertyDeclaration>node.parent, checker, usageContext);
break;
case SyntaxKind.VariableDeclaration: {
const { name, initializer } = node.parent as VariableDeclaration;
if (node === name) {
@@ -647,6 +716,21 @@ namespace ts.codefix {
}
}
function inferTypeFromPropertyAssignment(assignment: PropertyAssignment | ShorthandPropertyAssignment, checker: TypeChecker, usageContext: UsageContext) {
const objectLiteral = isShorthandPropertyAssignment(assignment) ?
assignment.parent :
assignment.parent.parent;
const nodeWithRealType = isVariableDeclaration(objectLiteral.parent) ?
objectLiteral.parent :
objectLiteral;
addCandidateThisType(usageContext, checker.getTypeAtLocation(nodeWithRealType));
}
function inferTypeFromPropertyDeclaration(declaration: PropertyDeclaration, checker: TypeChecker, usageContext: UsageContext) {
addCandidateThisType(usageContext, checker.getTypeAtLocation(declaration.parent));
}
interface Priority {
high: (t: Type) => boolean;
low: (t: Type) => boolean;
@@ -841,6 +925,12 @@ namespace ts.codefix {
}
}
function addCandidateThisType(context: UsageContext, type: Type | undefined) {
if (type && !(type.flags & TypeFlags.Any) && !(type.flags & TypeFlags.Never)) {
(context.candidateThisTypes || (context.candidateThisTypes = [])).push(type);
}
}
function hasCallContext(usageContext: UsageContext | undefined): boolean {
return !!usageContext && !!usageContext.callContexts;
}
+107 -24
View File
@@ -1,5 +1,12 @@
/* @internal */
namespace ts.Completions {
export enum SortText {
LocationPriority = "0",
SuggestedClassMembers = "1",
GlobalsOrKeywords = "2",
AutoImportSuggestions = "3",
JavascriptIdentifiers = "4"
}
export type Log = (message: string) => void;
const enum SymbolOriginInfoKind { ThisType, SymbolMemberNoExport, SymbolMemberExport, Export }
@@ -22,6 +29,8 @@ namespace ts.Completions {
*/
type SymbolOriginInfoMap = (SymbolOriginInfo | undefined)[];
type SymbolSortTextMap = (SortText | undefined)[];
const enum KeywordCompletionFilters {
None, // No keywords
All, // Every possible keyword (TODO: This is never appropriate)
@@ -40,7 +49,9 @@ namespace ts.Completions {
const compilerOptions = program.getCompilerOptions();
const contextToken = findPrecedingToken(position, sourceFile);
if (triggerCharacter && !isValidTrigger(sourceFile, triggerCharacter, contextToken, position)) return undefined;
if (triggerCharacter && !isInString(sourceFile, position, contextToken) && !isValidTrigger(sourceFile, triggerCharacter, contextToken, position)) {
return undefined;
}
const stringCompletions = StringCompletions.getStringLiteralCompletions(sourceFile, position, contextToken, typeChecker, compilerOptions, host, log, preferences);
if (stringCompletions) {
@@ -78,7 +89,21 @@ namespace ts.Completions {
}
function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, preferences: UserPreferences): CompletionInfo | undefined {
const { symbols, completionKind, isInSnippetScope, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, literals, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer, insideJsDocTagTypeExpression } = completionData;
const {
symbols,
completionKind,
isInSnippetScope,
isNewIdentifierLocation,
location,
propertyAccessToConvert,
keywordFilters,
literals,
symbolToOriginInfoMap,
recommendedCompletion,
isJsxInitializer,
insideJsDocTagTypeExpression,
symbolToSortTextMap,
} = completionData;
if (location && location.parent && isJsxClosingElement(location.parent)) {
// In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag,
@@ -93,7 +118,7 @@ namespace ts.Completions {
name: tagName.getFullText(sourceFile) + (hasClosingAngleBracket ? "" : ">"),
kind: ScriptElementKind.classElement,
kindModifiers: undefined,
sortText: "0",
sortText: SortText.LocationPriority,
};
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: false, entries: [entry] };
}
@@ -101,7 +126,22 @@ namespace ts.Completions {
const entries: CompletionEntry[] = [];
if (isUncheckedFile(sourceFile, compilerOptions)) {
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target!, log, completionKind, preferences, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap);
const uniqueNames = getCompletionEntriesFromSymbols(
symbols,
entries,
location,
sourceFile,
typeChecker,
compilerOptions.target!,
log,
completionKind,
preferences,
propertyAccessToConvert,
isJsxInitializer,
recommendedCompletion,
symbolToOriginInfoMap,
symbolToSortTextMap
);
getJSCompletionEntries(sourceFile, location!.pos, uniqueNames, compilerOptions.target!, entries); // TODO: GH#18217
}
else {
@@ -109,7 +149,22 @@ namespace ts.Completions {
return undefined;
}
getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target!, log, completionKind, preferences, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap);
getCompletionEntriesFromSymbols(
symbols,
entries,
location,
sourceFile,
typeChecker,
compilerOptions.target!,
log,
completionKind,
preferences,
propertyAccessToConvert,
isJsxInitializer,
recommendedCompletion,
symbolToOriginInfoMap,
symbolToSortTextMap
);
}
if (keywordFilters !== KeywordCompletionFilters.None) {
@@ -160,7 +215,7 @@ namespace ts.Completions {
name: realName,
kind: ScriptElementKind.warning,
kindModifiers: "",
sortText: "1"
sortText: SortText.JavascriptIdentifiers
});
}
});
@@ -169,28 +224,23 @@ namespace ts.Completions {
const completionNameForLiteral = (literal: string | number | PseudoBigInt) =>
typeof literal === "object" ? pseudoBigIntToString(literal) + "n" : JSON.stringify(literal);
function createCompletionEntryForLiteral(literal: string | number | PseudoBigInt): CompletionEntry {
return { name: completionNameForLiteral(literal), kind: ScriptElementKind.string, kindModifiers: ScriptElementKindModifier.none, sortText: "0" };
return { name: completionNameForLiteral(literal), kind: ScriptElementKind.string, kindModifiers: ScriptElementKindModifier.none, sortText: SortText.LocationPriority };
}
function createCompletionEntry(
symbol: Symbol,
sortText: SortText,
location: Node | undefined,
sourceFile: SourceFile,
typeChecker: TypeChecker,
target: ScriptTarget,
kind: CompletionKind,
name: string,
needsConvertPropertyAccess: boolean,
origin: SymbolOriginInfo | undefined,
recommendedCompletion: Symbol | undefined,
propertyAccessToConvert: PropertyAccessExpression | undefined,
isJsxInitializer: IsJsxInitializer | undefined,
preferences: UserPreferences,
): CompletionEntry | undefined {
const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind);
if (!info) {
return undefined;
}
const { name, needsConvertPropertyAccess } = info;
let insertText: string | undefined;
let replacementSpan: TextSpan | undefined;
if (origin && origin.kind === SymbolOriginInfoKind.ThisType) {
@@ -230,7 +280,7 @@ namespace ts.Completions {
name,
kind: SymbolDisplay.getSymbolKind(typeChecker, symbol, location!), // TODO: GH#18217
kindModifiers: SymbolDisplay.getSymbolModifiers(symbol),
sortText: "0",
sortText,
source: getSourceFromOrigin(origin),
hasAction: trueOrUndefined(!!origin && originIsExport(origin)),
isRecommended: trueOrUndefined(isRecommendedCompletionMatch(symbol, recommendedCompletion, typeChecker)),
@@ -266,6 +316,7 @@ namespace ts.Completions {
isJsxInitializer?: IsJsxInitializer,
recommendedCompletion?: Symbol,
symbolToOriginInfoMap?: SymbolOriginInfoMap,
symbolToSortTextMap?: SymbolSortTextMap,
): Map<true> {
const start = timestamp();
// Tracks unique names.
@@ -275,13 +326,30 @@ namespace ts.Completions {
const uniques = createMap<true>();
for (const symbol of symbols) {
const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined;
const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, isJsxInitializer, preferences);
if (!entry) {
const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind);
if (!info) {
continue;
}
const { name, needsConvertPropertyAccess } = info;
if (uniques.has(name)) {
continue;
}
const { name } = entry;
if (uniques.has(name)) {
const entry = createCompletionEntry(
symbol,
symbolToSortTextMap && symbolToSortTextMap[getSymbolId(symbol)] || SortText.LocationPriority,
location,
sourceFile,
typeChecker,
name,
needsConvertPropertyAccess,
origin,
recommendedCompletion,
propertyAccessToConvert,
isJsxInitializer,
preferences
);
if (!entry) {
continue;
}
@@ -321,7 +389,7 @@ namespace ts.Completions {
name,
kindModifiers: ScriptElementKindModifier.none,
kind: ScriptElementKind.label,
sortText: "0"
sortText: SortText.LocationPriority
});
}
}
@@ -358,7 +426,7 @@ namespace ts.Completions {
// We don't need to perform character checks here because we're only comparing the
// name against 'entryName' (which is known to be good), not building a new
// completion entry.
return firstDefined<Symbol, SymbolCompletion>(symbols, (symbol): SymbolCompletion | undefined => { // TODO: Shouldn't need return type annotation (GH#12632)
return firstDefined(symbols, (symbol): SymbolCompletion | undefined => {
const origin = symbolToOriginInfoMap[getSymbolId(symbol)];
const info = getCompletionEntryDisplayNameForSymbol(symbol, compilerOptions.target!, origin, completionKind);
return info && info.name === entryId.name && getSourceFromOrigin(origin) === entryId.source
@@ -512,6 +580,7 @@ namespace ts.Completions {
readonly previousToken: Node | undefined;
readonly isJsxInitializer: IsJsxInitializer;
readonly insideJsDocTagTypeExpression: boolean;
readonly symbolToSortTextMap: SymbolSortTextMap;
}
type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag };
@@ -804,6 +873,7 @@ namespace ts.Completions {
let keywordFilters = KeywordCompletionFilters.None;
let symbols: Symbol[] = [];
const symbolToOriginInfoMap: SymbolOriginInfoMap = [];
const symbolToSortTextMap: SymbolSortTextMap = [];
if (isRightOfDot) {
getTypeScriptMemberSymbols();
@@ -853,7 +923,8 @@ namespace ts.Completions {
recommendedCompletion,
previousToken,
isJsxInitializer,
insideJsDocTagTypeExpression
insideJsDocTagTypeExpression,
symbolToSortTextMap
};
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
@@ -965,6 +1036,9 @@ namespace ts.Completions {
symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)] =
!moduleSymbol || !isExternalModuleSymbol(moduleSymbol) ? { kind: SymbolOriginInfoKind.SymbolMemberNoExport } : { kind: SymbolOriginInfoKind.SymbolMemberExport, moduleSymbol, isDefaultExport: false };
}
else if (preferences.includeCompletionsWithInsertText) {
symbols.push(symbol);
}
}
else {
symbols.push(symbol);
@@ -1054,6 +1128,12 @@ namespace ts.Completions {
const symbolMeanings = (isTypeOnly ? SymbolFlags.None : SymbolFlags.Value) | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias;
symbols = Debug.assertEachDefined(typeChecker.getSymbolsInScope(scopeNode, symbolMeanings), "getSymbolsInScope() should all be defined");
for (const symbol of symbols) {
if (!typeChecker.isArgumentsSymbol(symbol) &&
!some(symbol.declarations, d => d.getSourceFile() === sourceFile)) {
symbolToSortTextMap[getSymbolId(symbol)] = SortText.GlobalsOrKeywords;
}
}
// Need to insert 'this.' before properties of `this` type, so only do that if `includeInsertTextCompletions`
if (preferences.includeCompletionsWithInsertText && scopeNode.kind !== SyntaxKind.SourceFile) {
@@ -1062,6 +1142,7 @@ namespace ts.Completions {
for (const symbol of getPropertiesForCompletion(thisType, typeChecker)) {
symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.ThisType };
symbols.push(symbol);
symbolToSortTextMap[getSymbolId(symbol)] = SortText.SuggestedClassMembers;
}
}
}
@@ -1195,6 +1276,7 @@ namespace ts.Completions {
// So in `declare namespace foo {} declare module "foo" { export = foo; }`, there will just be the global completion for `foo`.
some(resolvedModuleSymbol.declarations, d => !!d.getSourceFile().externalModuleIndicator)) {
symbols.push(resolvedModuleSymbol);
symbolToSortTextMap[getSymbolId(resolvedModuleSymbol)] = SortText.AutoImportSuggestions;
symbolToOriginInfoMap[getSymbolId(resolvedModuleSymbol)] = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport: false };
}
@@ -1220,6 +1302,7 @@ namespace ts.Completions {
const origin: SymbolOriginInfoExport = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport };
if (detailsEntryId || stringContainsCharactersInOrder(getSymbolName(symbol, origin, target).toLowerCase(), tokenTextLowerCase)) {
symbols.push(symbol);
symbolToSortTextMap[getSymbolId(symbol)] = SortText.AutoImportSuggestions;
symbolToOriginInfoMap[getSymbolId(symbol)] = origin;
}
}
@@ -1941,7 +2024,7 @@ namespace ts.Completions {
name: tokenToString(i)!,
kind: ScriptElementKind.keyword,
kindModifiers: ScriptElementKindModifier.none,
sortText: "0"
sortText: SortText.GlobalsOrKeywords
});
}
return res;
+197 -24
View File
@@ -16,9 +16,15 @@ namespace ts.FindAllReferences {
export const enum EntryKind { Span, Node, StringLiteral, SearchedLocalFoundProperty, SearchedPropertyFoundLocal }
export type NodeEntryKind = EntryKind.Node | EntryKind.StringLiteral | EntryKind.SearchedLocalFoundProperty | EntryKind.SearchedPropertyFoundLocal;
export type Entry = NodeEntry | SpanEntry;
export interface ContextWithStartAndEndNode {
start: Node;
end: Node;
}
export type ContextNode = Node | ContextWithStartAndEndNode;
export interface NodeEntry {
readonly kind: NodeEntryKind;
readonly node: Node;
readonly context?: ContextNode;
}
export interface SpanEntry {
readonly kind: EntryKind.Span;
@@ -26,7 +32,143 @@ namespace ts.FindAllReferences {
readonly textSpan: TextSpan;
}
export function nodeEntry(node: Node, kind: NodeEntryKind = EntryKind.Node): NodeEntry {
return { kind, node: (node as NamedDeclaration).name || node };
return {
kind,
node: (node as NamedDeclaration).name || node,
context: getContextNodeForNodeEntry(node)
};
}
export function isContextWithStartAndEndNode(node: ContextNode): node is ContextWithStartAndEndNode {
return node && (node as Node).kind === undefined;
}
function getContextNodeForNodeEntry(node: Node): ContextNode | undefined {
if (isDeclaration(node)) {
return getContextNode(node);
}
if (!node.parent) return undefined;
if (!isDeclaration(node.parent) && !isExportAssignment(node.parent)) {
// Special property assignment in javascript
if (isInJSFile(node)) {
const binaryExpression = isBinaryExpression(node.parent) ?
node.parent :
isPropertyAccessExpression(node.parent) &&
isBinaryExpression(node.parent.parent) &&
node.parent.parent.left === node.parent ?
node.parent.parent :
undefined;
if (binaryExpression && getAssignmentDeclarationKind(binaryExpression) !== AssignmentDeclarationKind.None) {
return getContextNode(binaryExpression);
}
}
// Jsx Tags
if (isJsxOpeningElement(node.parent) || isJsxClosingElement(node.parent)) {
return node.parent.parent;
}
else if (isJsxSelfClosingElement(node.parent) ||
isLabeledStatement(node.parent) ||
isBreakOrContinueStatement(node.parent)) {
return node.parent;
}
else if (isStringLiteralLike(node)) {
const validImport = tryGetImportFromModuleSpecifier(node);
if (validImport) {
const declOrStatement = findAncestor(validImport, node =>
isDeclaration(node) ||
isStatement(node) ||
isJSDocTag(node)
)! as NamedDeclaration | Statement | JSDocTag;
return isDeclaration(declOrStatement) ?
getContextNode(declOrStatement) :
declOrStatement;
}
}
// Handle computed property name
const propertyName = findAncestor(node, isComputedPropertyName);
return propertyName ?
getContextNode(propertyName.parent) :
undefined;
}
if (node.parent.name === node || // node is name of declaration, use parent
isConstructorDeclaration(node.parent) ||
isExportAssignment(node.parent) ||
// Property name of the import export specifier or binding pattern, use parent
((isImportOrExportSpecifier(node.parent) || isBindingElement(node.parent))
&& node.parent.propertyName === node) ||
// Is default export
(node.kind === SyntaxKind.DefaultKeyword && hasModifier(node.parent, ModifierFlags.ExportDefault))) {
return getContextNode(node.parent);
}
return undefined;
}
export function getContextNode(node: NamedDeclaration | BinaryExpression | ForInOrOfStatement | undefined): ContextNode | undefined {
if (!node) return undefined;
switch (node.kind) {
case SyntaxKind.VariableDeclaration:
return !isVariableDeclarationList(node.parent) || node.parent.declarations.length !== 1 ?
node :
isVariableStatement(node.parent.parent) ?
node.parent.parent :
isForInOrOfStatement(node.parent.parent) ?
getContextNode(node.parent.parent) :
node.parent;
case SyntaxKind.BindingElement:
return getContextNode(node.parent.parent as NamedDeclaration);
case SyntaxKind.ImportSpecifier:
return node.parent.parent.parent;
case SyntaxKind.ExportSpecifier:
case SyntaxKind.NamespaceImport:
return node.parent.parent;
case SyntaxKind.ImportClause:
return node.parent;
case SyntaxKind.BinaryExpression:
return isExpressionStatement(node.parent) ?
node.parent :
node;
case SyntaxKind.ForOfStatement:
case SyntaxKind.ForInStatement:
return {
start: (node as ForInOrOfStatement).initializer,
end: (node as ForInOrOfStatement).expression
};
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
return isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent) ?
getContextNode(
findAncestor(node.parent, node =>
isBinaryExpression(node) || isForInOrOfStatement(node)
) as BinaryExpression | ForInOrOfStatement
) :
node;
default:
return node;
}
}
export function toContextSpan(textSpan: TextSpan, sourceFile: SourceFile, context?: ContextNode): { contextSpan: TextSpan } | undefined {
if (!context) return undefined;
const contextSpan = isContextWithStartAndEndNode(context) ?
getTextSpan(context.start, sourceFile, context.end) :
getTextSpan(context, sourceFile);
return contextSpan.start !== textSpan.start || contextSpan.length !== textSpan.length ?
{ contextSpan } :
undefined;
}
export interface Options {
@@ -123,7 +265,16 @@ namespace ts.FindAllReferences {
const { symbol } = def;
const { displayParts, kind } = getDefinitionKindAndDisplayParts(symbol, checker, originalNode);
const name = displayParts.map(p => p.text).join("");
return { node: symbol.declarations ? getNameOfDeclaration(first(symbol.declarations)) || first(symbol.declarations) : originalNode, name, kind, displayParts };
const declaration = symbol.declarations ? first(symbol.declarations) : undefined;
return {
node: declaration ?
getNameOfDeclaration(declaration) || declaration :
originalNode,
name,
kind,
displayParts,
context: getContextNode(declaration)
};
}
case DefinitionKind.Label: {
const { node } = def;
@@ -150,9 +301,19 @@ namespace ts.FindAllReferences {
}
})();
const { node, name, kind, displayParts } = info;
const { node, name, kind, displayParts, context } = info;
const sourceFile = node.getSourceFile();
return { containerKind: ScriptElementKind.unknown, containerName: "", fileName: sourceFile.fileName, kind, name, textSpan: getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile), displayParts };
const textSpan = getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile);
return {
containerKind: ScriptElementKind.unknown,
containerName: "",
fileName: sourceFile.fileName,
kind,
name,
textSpan,
displayParts,
...toContextSpan(textSpan, sourceFile, context)
};
}
function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker, node: Node): { displayParts: SymbolDisplayPart[], kind: ScriptElementKind } {
@@ -168,14 +329,13 @@ namespace ts.FindAllReferences {
}
export function toReferenceEntry(entry: Entry): ReferenceEntry {
const { textSpan, fileName } = entryToDocumentSpan(entry);
const documentSpan = entryToDocumentSpan(entry);
if (entry.kind === EntryKind.Span) {
return { textSpan, fileName, isWriteAccess: false, isDefinition: false };
return { ...documentSpan, isWriteAccess: false, isDefinition: false };
}
const { kind, node } = entry;
return {
textSpan,
fileName,
...documentSpan,
isWriteAccess: isWriteAccessForReference(node),
isDefinition: node.kind === SyntaxKind.DefaultKeyword
|| !!getDeclarationFromName(node)
@@ -190,7 +350,12 @@ namespace ts.FindAllReferences {
}
else {
const sourceFile = entry.node.getSourceFile();
return { textSpan: getTextSpan(entry.node, sourceFile), fileName: sourceFile.fileName };
const textSpan = getTextSpan(entry.node, sourceFile);
return {
textSpan,
fileName: sourceFile.fileName,
...toContextSpan(textSpan, sourceFile, entry.context)
};
}
}
@@ -223,14 +388,16 @@ namespace ts.FindAllReferences {
}
function toImplementationLocation(entry: Entry, checker: TypeChecker): ImplementationLocation {
const documentSpan = entryToDocumentSpan(entry);
if (entry.kind !== EntryKind.Span) {
const { node } = entry;
const sourceFile = node.getSourceFile();
return { textSpan: getTextSpan(node, sourceFile), fileName: sourceFile.fileName, ...implementationKindDisplayParts(node, checker) };
return {
...documentSpan,
...implementationKindDisplayParts(node, checker)
};
}
else {
const { textSpan, fileName } = entry;
return { textSpan, fileName, kind: ScriptElementKind.unknown, displayParts: [] };
return { ...documentSpan, kind: ScriptElementKind.unknown, displayParts: [] };
}
}
@@ -257,26 +424,32 @@ namespace ts.FindAllReferences {
}
export function toHighlightSpan(entry: Entry): { fileName: string, span: HighlightSpan } {
const documentSpan = entryToDocumentSpan(entry);
if (entry.kind === EntryKind.Span) {
const { fileName, textSpan } = entry;
return { fileName, span: { textSpan, kind: HighlightSpanKind.reference } };
return {
fileName: documentSpan.fileName,
span: {
textSpan: documentSpan.textSpan,
kind: HighlightSpanKind.reference
}
};
}
const { node, kind } = entry;
const sourceFile = node.getSourceFile();
const writeAccess = isWriteAccessForReference(node);
const writeAccess = isWriteAccessForReference(entry.node);
const span: HighlightSpan = {
textSpan: getTextSpan(node, sourceFile),
textSpan: documentSpan.textSpan,
kind: writeAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference,
isInString: kind === EntryKind.StringLiteral ? true : undefined,
isInString: entry.kind === EntryKind.StringLiteral ? true : undefined,
...documentSpan.contextSpan && { contextSpan: documentSpan.contextSpan }
};
return { fileName: sourceFile.fileName, span };
return { fileName: documentSpan.fileName, span };
}
function getTextSpan(node: Node, sourceFile: SourceFile): TextSpan {
function getTextSpan(node: Node, sourceFile: SourceFile, endNode?: Node): TextSpan {
let start = node.getStart(sourceFile);
let end = node.getEnd();
let end = (endNode || node).getEnd();
if (node.kind === SyntaxKind.StringLiteral) {
Debug.assert(endNode === undefined);
start += 1;
end -= 1;
}
@@ -635,7 +808,7 @@ namespace ts.FindAllReferences.Core {
// Ignore UMD module and global merge
if (symbol.flags & SymbolFlags.Transient) return undefined;
// Assertions for GH#21814. We should be handling SourceFile symbols in `getReferencedSymbolsForModule` instead of getting here.
Debug.fail(`Unexpected symbol at ${Debug.showSyntaxKind(node)}: ${Debug.showSymbol(symbol)}`);
Debug.fail(`Unexpected symbol at ${Debug.formatSyntaxKind(node.kind)}: ${Debug.formatSymbol(symbol)}`);
}
return isTypeLiteralNode(decl.parent) && isUnionTypeNode(decl.parent.parent)
? checker.getPropertyOfType(checker.getTypeFromTypeNode(decl.parent.parent), symbol.name)
+33
View File
@@ -0,0 +1,33 @@
# How does TypeScript formatting work?
To format code you need to have a formatting context and a `SourceFile`. The formatting context contains
all user settings like tab size, newline character, etc.
The end result of formatting is represented by TextChange objects which hold the new string content, and
the text to replace it with.
```ts
export interface TextChange {
span: TextSpan; // start, length
newText: string;
}
```
## Internals
Most of the exposed APIs internally are `format*` and they all set up and configure `formatSpan` which could be considered the root call for formatting. Span in this case refers to the range of
the sourcefile which should be formatted.
The formatSpan then uses a scanner (either with or without JSX support) which starts at the highest
node the covers the span of text and recurses down through the node's children.
As it recurses, `processNode` is called on the children setting the indentation is decided and passed
through into each of that node's children.
The meat of formatting decisions is made via `processPair`, the pair here being the current node and the previous node. `processPair` which mutates the formatting context to represent the current place in the scanner and requests a set of rules which can be applied to the items via `createRulesMap`.
There are a lot of rules, which you can find in [rules.ts](./rules.ts) each one has a left and right reference to nodes or token ranges and note of what action should be applied by the formatter.
### Where is this used?
The formatter is used mainly from any language service operation that inserts or modifies code. The formatter is not exported publicly, and so all usage can only come through the language server.
+13
View File
@@ -229,6 +229,7 @@ namespace ts.formatting {
rule("NoSpaceBeforeNonNullAssertionOperator", anyToken, SyntaxKind.ExclamationToken, [isNonJsxSameLineTokenContext, isNonNullAssertionContext], RuleAction.Delete),
rule("NoSpaceAfterNewKeywordOnConstructorSignature", SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isConstructorSignatureContext], RuleAction.Delete),
rule("SpaceLessThanAndNonJSXTypeAnnotation", SyntaxKind.LessThanToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext], RuleAction.Space),
];
// These rules are applied after high priority
@@ -349,6 +350,18 @@ namespace ts.formatting {
];
}
/**
* A rule takes a two tokens (left/right) and a particular context
* for which you're meant to look at them. You then declare what should the
* whitespace annotation be between these tokens via the action param.
*
* @param debugName Name to print
* @param left The left side of the comparison
* @param right The right side of the comparison
* @param context A set of filters to narrow down the space in which this formatter rule applies
* @param action a declaration of the expected whitespace
* @param flags whether the rule deletes a line or not, defaults to no-op
*/
function rule(
debugName: string,
left: SyntaxKind | ReadonlyArray<SyntaxKind> | TokenRange,
+9 -3
View File
@@ -108,7 +108,7 @@ namespace ts.GoToDefinition {
export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { fileName: string, file: SourceFile } | undefined {
const referencePath = findReferenceInPosition(sourceFile.referencedFiles, position);
if (referencePath) {
const file = tryResolveScriptReference(program, sourceFile, referencePath);
const file = program.getSourceFileFromReference(sourceFile, referencePath);
return file && { fileName: referencePath.fileName, file };
}
@@ -273,13 +273,19 @@ namespace ts.GoToDefinition {
function createDefinitionInfoFromName(declaration: Declaration, symbolKind: ScriptElementKind, symbolName: string, containerName: string): DefinitionInfo {
const name = getNameOfDeclaration(declaration) || declaration;
const sourceFile = name.getSourceFile();
const textSpan = createTextSpanFromNode(name, sourceFile);
return {
fileName: sourceFile.fileName,
textSpan: createTextSpanFromNode(name, sourceFile),
textSpan,
kind: symbolKind,
name: symbolName,
containerKind: undefined!, // TODO: GH#18217
containerName
containerName,
...FindAllReferences.toContextSpan(
textSpan,
sourceFile,
FindAllReferences.getContextNode(declaration)
)
};
}
+2 -2
View File
@@ -133,7 +133,7 @@ namespace ts.FindAllReferences {
break;
default:
Debug.assertNever(direct, `Unexpected import kind: ${Debug.showSyntaxKind(direct)}`);
Debug.failBadSyntaxKind(direct, "Unexpected import kind.");
}
}
}
@@ -515,7 +515,7 @@ namespace ts.FindAllReferences {
const sym = useLhsSymbol ? checker.getSymbolAtLocation(cast(node.left, isPropertyAccessExpression).name) : symbol;
// Better detection for GH#20803
if (sym && !(checker.getMergedSymbol(sym.parent!).flags & SymbolFlags.Module)) {
Debug.fail(`Special property assignment kind does not have a module as its parent. Assignment is ${Debug.showSymbol(sym)}, parent is ${Debug.showSymbol(sym.parent!)}`);
Debug.fail(`Special property assignment kind does not have a module as its parent. Assignment is ${Debug.formatSymbol(sym)}, parent is ${Debug.formatSymbol(sym.parent!)}`);
}
return sym && exportInfo(sym, kind);
}
+25 -1
View File
@@ -89,7 +89,7 @@ namespace ts.OrganizeImports {
const usedImports: ImportDeclaration[] = [];
for (const importDecl of oldImports) {
const {importClause} = importDecl;
const { importClause, moduleSpecifier } = importDecl;
if (!importClause) {
// Imports without import clauses are assumed to be included for their side effects and are not removed.
@@ -125,6 +125,23 @@ namespace ts.OrganizeImports {
if (name || namedBindings) {
usedImports.push(updateImportDeclarationAndClause(importDecl, name, namedBindings));
}
// If a module is imported to be augmented, its used
else if (hasModuleDeclarationMatchingSpecifier(sourceFile, moduleSpecifier)) {
// If were in a declaration file, its safe to remove the import clause from it
if (sourceFile.isDeclarationFile) {
usedImports.push(createImportDeclaration(
importDecl.decorators,
importDecl.modifiers,
/*importClause*/ undefined,
moduleSpecifier));
}
// If were not in a declaration file, we cant remove the import clause even though
// the imported symbols are unused, because removing them makes it look like the import
// declaration has side effects, which will cause it to be preserved in the JS emit.
else {
usedImports.push(importDecl);
}
}
}
return usedImports;
@@ -135,6 +152,13 @@ namespace ts.OrganizeImports {
}
}
function hasModuleDeclarationMatchingSpecifier(sourceFile: SourceFile, moduleSpecifier: Expression) {
const moduleSpecifierText = isStringLiteral(moduleSpecifier) && moduleSpecifier.text;
return isString(moduleSpecifierText) && some(sourceFile.moduleAugmentations, moduleName =>
isStringLiteral(moduleName)
&& moduleName.text === moduleSpecifierText);
}
function getExternalModuleName(specifier: Expression) {
return specifier !== undefined && isStringLiteralLike(specifier)
? specifier.text
+1 -1
View File
@@ -237,7 +237,7 @@ namespace ts.OutliningElementsCollector {
? findChildOfKind(node, SyntaxKind.OpenParenToken, sourceFile)
: findChildOfKind(body, SyntaxKind.OpenBraceToken, sourceFile);
const closeToken = findChildOfKind(body, SyntaxKind.CloseBraceToken, sourceFile);
return openToken && closeToken && spanBetweenTokens(openToken, closeToken, node.parent, sourceFile, /*autoCollapse*/ node.parent.kind !== SyntaxKind.ArrowFunction);
return openToken && closeToken && spanBetweenTokens(openToken, closeToken, node, sourceFile, /*autoCollapse*/ node.kind !== SyntaxKind.ArrowFunction);
}
function spanBetweenTokens(openToken: Node, closeToken: Node, hintSpanNode: Node, sourceFile: SourceFile, autoCollapse = false, useFullStart = true): OutliningSpan {
+39 -16
View File
@@ -173,7 +173,7 @@ namespace ts {
const textPos = scanner.getTextPos();
if (textPos <= end) {
if (token === SyntaxKind.Identifier) {
Debug.fail(`Did not expect ${Debug.showSyntaxKind(parent)} to have an Identifier in its trivia`);
Debug.fail(`Did not expect ${Debug.formatSyntaxKind(parent.kind)} to have an Identifier in its trivia`);
}
nodes.push(createNode(token, pos, textPos, parent));
}
@@ -1467,33 +1467,41 @@ namespace ts {
}
const typeChecker = program.getTypeChecker();
const symbol = getSymbolAtLocationForQuickInfo(node, typeChecker);
const nodeForQuickInfo = getNodeForQuickInfo(node);
const symbol = getSymbolAtLocationForQuickInfo(nodeForQuickInfo, typeChecker);
if (!symbol || typeChecker.isUnknownSymbol(symbol)) {
const type = shouldGetType(sourceFile, node, position) ? typeChecker.getTypeAtLocation(node) : undefined;
const type = shouldGetType(sourceFile, nodeForQuickInfo, position) ? typeChecker.getTypeAtLocation(nodeForQuickInfo) : undefined;
return type && {
kind: ScriptElementKind.unknown,
kindModifiers: ScriptElementKindModifier.none,
textSpan: createTextSpanFromNode(node, sourceFile),
displayParts: typeChecker.runWithCancellationToken(cancellationToken, typeChecker => typeToDisplayParts(typeChecker, type, getContainerNode(node))),
textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile),
displayParts: typeChecker.runWithCancellationToken(cancellationToken, typeChecker => typeToDisplayParts(typeChecker, type, getContainerNode(nodeForQuickInfo))),
documentation: type.symbol ? type.symbol.getDocumentationComment(typeChecker) : undefined,
tags: type.symbol ? type.symbol.getJsDocTags() : undefined
};
}
const { symbolKind, displayParts, documentation, tags } = typeChecker.runWithCancellationToken(cancellationToken, typeChecker =>
SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, getContainerNode(node), node)
SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, getContainerNode(nodeForQuickInfo), nodeForQuickInfo)
);
return {
kind: symbolKind,
kindModifiers: SymbolDisplay.getSymbolModifiers(symbol),
textSpan: createTextSpanFromNode(node, sourceFile),
textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile),
displayParts,
documentation,
tags,
};
}
function getNodeForQuickInfo(node: Node): Node {
if (isNewExpression(node.parent) && node.pos === node.parent.pos) {
return node.parent.expression;
}
return node;
}
function shouldGetType(sourceFile: SourceFile, node: Node, position: number): boolean {
switch (node.kind) {
case SyntaxKind.Identifier:
@@ -1536,13 +1544,17 @@ namespace ts {
/// References and Occurrences
function getOccurrencesAtPosition(fileName: string, position: number): ReadonlyArray<ReferenceEntry> | undefined {
return flatMap(getDocumentHighlights(fileName, position, [fileName]), entry => entry.highlightSpans.map<ReferenceEntry>(highlightSpan => ({
fileName: entry.fileName,
textSpan: highlightSpan.textSpan,
isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
isDefinition: false,
isInString: highlightSpan.isInString,
})));
return flatMap(
getDocumentHighlights(fileName, position, [fileName]),
entry => entry.highlightSpans.map<ReferenceEntry>(highlightSpan => ({
fileName: entry.fileName,
textSpan: highlightSpan.textSpan,
isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
isDefinition: false,
...highlightSpan.isInString && { isInString: true },
...highlightSpan.contextSpan && { contextSpan: highlightSpan.contextSpan }
}))
);
}
function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray<string>): DocumentHighlights[] | undefined {
@@ -1560,8 +1572,14 @@ namespace ts {
const node = getTouchingPropertyName(sourceFile, position);
if (isIdentifier(node) && (isJsxOpeningElement(node.parent) || isJsxClosingElement(node.parent)) && isIntrinsicJsxName(node.escapedText)) {
const { openingElement, closingElement } = node.parent.parent;
return [openingElement, closingElement].map((node): RenameLocation =>
({ fileName: sourceFile.fileName, textSpan: createTextSpanFromNode(node.tagName, sourceFile) }));
return [openingElement, closingElement].map((node): RenameLocation => {
const textSpan = createTextSpanFromNode(node.tagName, sourceFile);
return {
fileName: sourceFile.fileName,
textSpan,
...FindAllReferences.toContextSpan(textSpan, sourceFile, node.parent)
};
});
}
else {
return getReferencesWorker(node, position, { findInStrings, findInComments, providePrefixAndSuffixTextForRename, isForRename: true },
@@ -2072,6 +2090,10 @@ namespace ts {
};
}
function getSmartSelectionRange(fileName: string, position: number): SelectionRange {
return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName));
}
function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions): ApplicableRefactorInfo[] {
synchronizeHostData();
const file = getValidSourceFile(fileName);
@@ -2119,6 +2141,7 @@ namespace ts {
getBreakpointStatementAtPosition,
getNavigateToItems,
getRenameInfo,
getSmartSelectionRange,
findRenameLocations,
getNavigationBarItems,
getNavigationTree,
+8
View File
@@ -165,6 +165,7 @@ namespace ts {
* { canRename: boolean, localizedErrorMessage: string, displayName: string, fullDisplayName: string, kind: string, kindModifiers: string, triggerSpan: { start; length } }
*/
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): string;
getSmartSelectionRange(fileName: string, position: number): string;
/**
* Returns a JSON-encoded value of the type:
@@ -838,6 +839,13 @@ namespace ts {
);
}
public getSmartSelectionRange(fileName: string, position: number): string {
return this.forwardJSONCall(
`getSmartSelectionRange('${fileName}', ${position})`,
() => this.languageService.getSmartSelectionRange(fileName, position)
);
}
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string {
return this.forwardJSONCall(
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments}, ${providePrefixAndSuffixTextForRename})`,
+16 -6
View File
@@ -133,11 +133,21 @@ namespace ts.SignatureHelp {
}
function containsPrecedingToken(startingToken: Node, sourceFile: SourceFile, container: Node) {
const precedingToken = Debug.assertDefined(
findPrecedingToken(startingToken.getFullStart(), sourceFile, startingToken.parent, /*excludeJsdoc*/ true)
);
return rangeContainsRange(container, precedingToken);
const pos = startingToken.getFullStart();
// Theres a possibility that `startingToken.parent` contains only `startingToken` and
// missing nodes, none of which are valid to be returned by `findPrecedingToken`. In that
// case, the preceding token we want is actually higher up the tree—almost definitely the
// next parent, but theoretically the situation with missing nodes might be happening on
// multiple nested levels.
let currentParent: Node | undefined = startingToken.parent;
while (currentParent) {
const precedingToken = findPrecedingToken(pos, sourceFile, currentParent, /*excludeJsdoc*/ true);
if (precedingToken) {
return rangeContainsRange(container, precedingToken);
}
currentParent = currentParent.parent;
}
return Debug.fail("Could not find preceding token");
}
export interface ArgumentInfoForCompletions {
@@ -455,7 +465,7 @@ namespace ts.SignatureHelp {
for (let n = node; !isSourceFile(n) && (isManuallyInvoked || !isBlock(n)); n = n.parent) {
// If the node is not a subspan of its parent, this is a big problem.
// There have been crashes that might be caused by this violation.
Debug.assert(rangeContainsRange(n.parent, n), "Not a subspan", () => `Child: ${Debug.showSyntaxKind(n)}, parent: ${Debug.showSyntaxKind(n.parent)}`);
Debug.assert(rangeContainsRange(n.parent, n), "Not a subspan", () => `Child: ${Debug.formatSyntaxKind(n.kind)}, parent: ${Debug.formatSyntaxKind(n.parent.kind)}`);
const argumentInfo = getImmediatelyContainingArgumentOrContextualParameterInfo(n, position, sourceFile, checker);
if (argumentInfo) {
return argumentInfo;
+270
View File
@@ -0,0 +1,270 @@
/* @internal */
namespace ts.SmartSelectionRange {
export function getSmartSelectionRange(pos: number, sourceFile: SourceFile): SelectionRange {
let selectionRange: SelectionRange = {
textSpan: createTextSpanFromBounds(sourceFile.getFullStart(), sourceFile.getEnd())
};
let parentNode: Node = sourceFile;
outer: while (true) {
const children = getSelectionChildren(parentNode);
if (!children.length) break;
for (let i = 0; i < children.length; i++) {
const prevNode: Node | undefined = children[i - 1];
const node: Node = children[i];
const nextNode: Node | undefined = children[i + 1];
if (node.getStart(sourceFile) > pos) {
break outer;
}
if (positionShouldSnapToNode(pos, node, nextNode)) {
// 1. Blocks are effectively redundant with SyntaxLists.
// 2. TemplateSpans, along with the SyntaxLists containing them, are a somewhat unintuitive grouping
// of things that should be considered independently.
// 3. A VariableStatements children are just a VaraiableDeclarationList and a semicolon.
// 4. A lone VariableDeclaration in a VaraibleDeclaration feels redundant with the VariableStatement.
//
// Dive in without pushing a selection range.
if (isBlock(node)
|| isTemplateSpan(node) || isTemplateHead(node)
|| prevNode && isTemplateHead(prevNode)
|| isVariableDeclarationList(node) && isVariableStatement(parentNode)
|| isSyntaxList(node) && isVariableDeclarationList(parentNode)
|| isVariableDeclaration(node) && isSyntaxList(parentNode) && children.length === 1) {
parentNode = node;
break;
}
// Synthesize a stop for '${ ... }' since '${' and '}' actually belong to siblings.
if (isTemplateSpan(parentNode) && nextNode && isTemplateMiddleOrTemplateTail(nextNode)) {
const start = node.getFullStart() - "${".length;
const end = nextNode.getStart() + "}".length;
pushSelectionRange(start, end);
}
// Blocks with braces, brackets, parens, or JSX tags on separate lines should be
// selected from open to close, including whitespace but not including the braces/etc. themselves.
const isBetweenMultiLineBookends = isSyntaxList(node)
&& isListOpener(prevNode)
&& isListCloser(nextNode)
&& !positionsAreOnSameLine(prevNode.getStart(), nextNode.getStart(), sourceFile);
const jsDocCommentStart = hasJSDocNodes(node) && node.jsDoc![0].getStart();
const start = isBetweenMultiLineBookends ? prevNode.getEnd() : node.getStart();
const end = isBetweenMultiLineBookends ? nextNode.getStart() : node.getEnd();
if (isNumber(jsDocCommentStart)) {
pushSelectionRange(jsDocCommentStart, end);
}
pushSelectionRange(start, end);
// String literals should have a stop both inside and outside their quotes.
if (isStringLiteral(node) || isTemplateLiteral(node)) {
pushSelectionRange(start + 1, end - 1);
}
parentNode = node;
break;
}
}
}
return selectionRange;
function pushSelectionRange(start: number, end: number): void {
// Skip empty ranges
if (start !== end) {
const textSpan = createTextSpanFromBounds(start, end);
if (!selectionRange || (
// Skip ranges that are identical to the parent
!textSpansEqual(textSpan, selectionRange.textSpan) &&
// Skip ranges that dont contain the original position
textSpanIntersectsWithPosition(textSpan, pos)
)) {
selectionRange = { textSpan, ...selectionRange && { parent: selectionRange } };
}
}
}
}
/**
* Like `ts.positionBelongsToNode`, except positions immediately after nodes
* count too, unless that position belongs to the next node. In effect, makes
* selections able to snap to preceding tokens when the cursor is on the tail
* end of them with only whitespace ahead.
* @param pos The position to check.
* @param node The candidate node to snap to.
* @param nextNode The next sibling node in the tree.
* @param sourceFile The source file containing the nodes.
*/
function positionShouldSnapToNode(pos: number, node: Node, nextNode: Node | undefined) {
// Cant use 'ts.positionBelongsToNode()' here because it cleverly accounts
// for missing nodes, which cant really be considered when deciding what
// to select.
Debug.assert(node.pos <= pos);
if (pos < node.end) {
return true;
}
const nodeEnd = node.getEnd();
const nextNodeStart = nextNode && nextNode.getStart();
if (nodeEnd === pos) {
return pos !== nextNodeStart;
}
return false;
}
const isImport = or(isImportDeclaration, isImportEqualsDeclaration);
/**
* Gets the children of a node to be considered for selection ranging,
* transforming them into an artificial tree according to their intuitive
* grouping where no grouping actually exists in the parse tree. For example,
* top-level imports are grouped into their own SyntaxList so they can be
* selected all together, even though in the AST theyre just siblings of each
* other as well as of other top-level statements and declarations.
*/
function getSelectionChildren(node: Node): ReadonlyArray<Node> {
// Group top-level imports
if (isSourceFile(node)) {
return groupChildren(node.getChildAt(0).getChildren(), isImport);
}
// Mapped types _look_ like ObjectTypes with a single member,
// but in fact dont contain a SyntaxList or a node containing
// the “key/value” pair like ObjectTypes do, but it seems intuitive
// that the selection would snap to those points. The philosophy
// of choosing a selection range is not so much about what the
// syntax currently _is_ as what the syntax might easily become
// if the user is making a selection; e.g., we synthesize a selection
// around the “key/value” pair not because theres a node there, but
// because it allows the mapped type to become an object type with a
// few keystrokes.
if (isMappedTypeNode(node)) {
const [openBraceToken, ...children] = node.getChildren();
const closeBraceToken = Debug.assertDefined(children.pop());
Debug.assertEqual(openBraceToken.kind, SyntaxKind.OpenBraceToken);
Debug.assertEqual(closeBraceToken.kind, SyntaxKind.CloseBraceToken);
// Group `-/+readonly` and `-/+?`
const groupedWithPlusMinusTokens = groupChildren(children, child =>
child === node.readonlyToken || child.kind === SyntaxKind.ReadonlyKeyword ||
child === node.questionToken || child.kind === SyntaxKind.QuestionToken);
// Group type parameter with surrounding brackets
const groupedWithBrackets = groupChildren(groupedWithPlusMinusTokens, ({ kind }) =>
kind === SyntaxKind.OpenBracketToken ||
kind === SyntaxKind.TypeParameter ||
kind === SyntaxKind.CloseBracketToken
);
return [
openBraceToken,
// Pivot on `:`
createSyntaxList(splitChildren(groupedWithBrackets, ({ kind }) => kind === SyntaxKind.ColonToken)),
closeBraceToken,
];
}
// Group modifiers and property name, then pivot on `:`.
if (isPropertySignature(node)) {
const children = groupChildren(node.getChildren(), child =>
child === node.name || contains(node.modifiers, child));
return splitChildren(children, ({ kind }) => kind === SyntaxKind.ColonToken);
}
// Group the parameter name with its `...`, then that group with its `?`, then pivot on `=`.
if (isParameter(node)) {
const groupedDotDotDotAndName = groupChildren(node.getChildren(), child =>
child === node.dotDotDotToken || child === node.name);
const groupedWithQuestionToken = groupChildren(groupedDotDotDotAndName, child =>
child === groupedDotDotDotAndName[0] || child === node.questionToken);
return splitChildren(groupedWithQuestionToken, ({ kind }) => kind === SyntaxKind.EqualsToken);
}
// Pivot on '='
if (isBindingElement(node)) {
return splitChildren(node.getChildren(), ({ kind }) => kind === SyntaxKind.EqualsToken);
}
return node.getChildren();
}
/**
* Groups sibling nodes together into their own SyntaxList if they
* a) are adjacent, AND b) match a predicate function.
*/
function groupChildren(children: Node[], groupOn: (child: Node) => boolean): Node[] {
const result: Node[] = [];
let group: Node[] | undefined;
for (const child of children) {
if (groupOn(child)) {
group = group || [];
group.push(child);
}
else {
if (group) {
result.push(createSyntaxList(group));
group = undefined;
}
result.push(child);
}
}
if (group) {
result.push(createSyntaxList(group));
}
return result;
}
/**
* Splits sibling nodes into up to four partitions:
* 1) everything left of the first node matched by `pivotOn`,
* 2) the first node matched by `pivotOn`,
* 3) everything right of the first node matched by `pivotOn`,
* 4) a trailing semicolon, if `separateTrailingSemicolon` is enabled.
* The left and right groups, if not empty, will each be grouped into their own containing SyntaxList.
* @param children The sibling nodes to split.
* @param pivotOn The predicate function to match the node to be the pivot. The first node that matches
* the predicate will be used; any others that may match will be included into the right-hand group.
* @param separateTrailingSemicolon If the last token is a semicolon, it will be returned as a separate
* child rather than be included in the right-hand group.
*/
function splitChildren(children: Node[], pivotOn: (child: Node) => boolean, separateTrailingSemicolon = true): Node[] {
if (children.length < 2) {
return children;
}
const splitTokenIndex = findIndex(children, pivotOn);
if (splitTokenIndex === -1) {
return children;
}
const leftChildren = children.slice(0, splitTokenIndex);
const splitToken = children[splitTokenIndex];
const lastToken = last(children);
const separateLastToken = separateTrailingSemicolon && lastToken.kind === SyntaxKind.SemicolonToken;
const rightChildren = children.slice(splitTokenIndex + 1, separateLastToken ? children.length - 1 : undefined);
const result = compact([
leftChildren.length ? createSyntaxList(leftChildren) : undefined,
splitToken,
rightChildren.length ? createSyntaxList(rightChildren) : undefined,
]);
return separateLastToken ? result.concat(lastToken) : result;
}
function createSyntaxList(children: Node[]): SyntaxList {
Debug.assertGreaterThanOrEqual(children.length, 1);
const syntaxList = createNode(SyntaxKind.SyntaxList, children[0].pos, last(children).end) as SyntaxList;
syntaxList._children = children;
return syntaxList;
}
function isListOpener(token: Node | undefined): token is Node {
const kind = token && token.kind;
return kind === SyntaxKind.OpenBraceToken
|| kind === SyntaxKind.OpenBracketToken
|| kind === SyntaxKind.OpenParenToken
|| kind === SyntaxKind.JsxOpeningElement;
}
function isListCloser(token: Node | undefined): token is Node {
const kind = token && token.kind;
return kind === SyntaxKind.CloseBraceToken
|| kind === SyntaxKind.CloseBracketToken
|| kind === SyntaxKind.CloseParenToken
|| kind === SyntaxKind.JsxClosingElement;
}
}
+12 -2
View File
@@ -21,7 +21,17 @@ namespace ts.Completions.StringCompletions {
return convertPathCompletions(completion.paths);
case StringLiteralCompletionKind.Properties: {
const entries: CompletionEntry[] = [];
getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String, preferences); // Target will not be used, so arbitrary
getCompletionEntriesFromSymbols(
completion.symbols,
entries,
sourceFile,
sourceFile,
checker,
ScriptTarget.ESNext,
log,
CompletionKind.String,
preferences
); // Target will not be used, so arbitrary
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: completion.hasIndexSignature, entries };
}
case StringLiteralCompletionKind.Types: {
@@ -60,7 +70,7 @@ namespace ts.Completions.StringCompletions {
const isGlobalCompletion = false; // We don't want the editor to offer any other completions, such as snippets, inside a comment.
const isNewIdentifierLocation = true; // The user may type in a path that doesn't yet exist, creating a "new identifier" with respect to the collection of identifiers the server is aware of.
const entries = pathCompletions.map(({ name, kind, span, extension }): CompletionEntry =>
({ name, kind, kindModifiers: kindModifiersFromExtension(extension), sortText: "0", replacementSpan: span }));
({ name, kind, kindModifiers: kindModifiersFromExtension(extension), sortText: SortText.LocationPriority, replacementSpan: span }));
return { isGlobalCompletion, isMemberCompletion: false, isNewIdentifierLocation, entries };
}
function kindModifiersFromExtension(extension: Extension | undefined): ScriptElementKindModifier {
+143 -104
View File
@@ -222,6 +222,12 @@ namespace ts.textChanges {
export type TypeAnnotatable = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature;
export type ThisTypeAnnotatable = FunctionDeclaration | FunctionExpression;
export function isThisTypeAnnotatable(containingFunction: FunctionLike): containingFunction is ThisTypeAnnotatable {
return isFunctionExpression(containingFunction) || isFunctionDeclaration(containingFunction);
}
export class ChangeTracker {
private readonly changes: Change[] = [];
private readonly newFiles: { readonly oldFile: SourceFile | undefined, readonly fileName: string, readonly statements: ReadonlyArray<Statement> }[] = [];
@@ -393,6 +399,13 @@ namespace ts.textChanges {
this.insertNodeAt(sourceFile, endNode.end, type, { prefix: ": " });
}
public tryInsertThisTypeAnnotation(sourceFile: SourceFile, node: ThisTypeAnnotatable, type: TypeNode): void {
const start = findChildOfKind(node, SyntaxKind.OpenParenToken, sourceFile)!.getStart(sourceFile) + 1;
const suffix = node.parameters.length ? ", " : "";
this.insertNodeAt(sourceFile, start, type, { prefix: "this: ", suffix });
}
public insertTypeParameters(sourceFile: SourceFile, node: SignatureDeclaration, typeParameters: ReadonlyArray<TypeParameterDeclaration>): void {
// If no `(`, is an arrow function `x => x`, so use the pos of the first parameter
const start = (findChildOfKind(node, SyntaxKind.OpenParenToken, sourceFile) || first(node.parameters)).getStart(sourceFile);
@@ -837,9 +850,10 @@ namespace ts.textChanges {
/** Note: output node may be mutated input node. */
export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLineCharacter: string): { text: string, node: Node } {
const writer = new Writer(newLineCharacter);
const omitTrailingSemicolon = !!sourceFile && !probablyUsesSemicolons(sourceFile);
const writer = createWriter(newLineCharacter, omitTrailingSemicolon);
const newLine = newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed;
createPrinter({ newLine, neverAsciiEscape: true }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer);
createPrinter({ newLine, neverAsciiEscape: true, omitTrailingSemicolon }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer);
return { text: writer.getText(), node: assignPositionsToNode(node) };
}
}
@@ -877,143 +891,168 @@ namespace ts.textChanges {
return nodeArray;
}
class Writer implements EmitTextWriter, PrintHandlers {
private lastNonTriviaPosition = 0;
private readonly writer: EmitTextWriter;
interface TextChangesWriter extends EmitTextWriter, PrintHandlers {}
public readonly onEmitNode: PrintHandlers["onEmitNode"];
public readonly onBeforeEmitNodeArray: PrintHandlers["onBeforeEmitNodeArray"];
public readonly onAfterEmitNodeArray: PrintHandlers["onAfterEmitNodeArray"];
public readonly onBeforeEmitToken: PrintHandlers["onBeforeEmitToken"];
public readonly onAfterEmitToken: PrintHandlers["onAfterEmitToken"];
function createWriter(newLine: string, omitTrailingSemicolon?: boolean): TextChangesWriter {
let lastNonTriviaPosition = 0;
constructor(newLine: string) {
this.writer = createTextWriter(newLine);
this.onEmitNode = (hint, node, printCallback) => {
if (node) {
setPos(node, this.lastNonTriviaPosition);
}
printCallback(hint, node);
if (node) {
setEnd(node, this.lastNonTriviaPosition);
}
};
this.onBeforeEmitNodeArray = nodes => {
if (nodes) {
setPos(nodes, this.lastNonTriviaPosition);
}
};
this.onAfterEmitNodeArray = nodes => {
if (nodes) {
setEnd(nodes, this.lastNonTriviaPosition);
}
};
this.onBeforeEmitToken = node => {
if (node) {
setPos(node, this.lastNonTriviaPosition);
}
};
this.onAfterEmitToken = node => {
if (node) {
setEnd(node, this.lastNonTriviaPosition);
}
};
}
private setLastNonTriviaPosition(s: string, force: boolean) {
const writer = omitTrailingSemicolon ? getTrailingSemicolonOmittingWriter(createTextWriter(newLine)) : createTextWriter(newLine);
const onEmitNode: PrintHandlers["onEmitNode"] = (hint, node, printCallback) => {
if (node) {
setPos(node, lastNonTriviaPosition);
}
printCallback(hint, node);
if (node) {
setEnd(node, lastNonTriviaPosition);
}
};
const onBeforeEmitNodeArray: PrintHandlers["onBeforeEmitNodeArray"] = nodes => {
if (nodes) {
setPos(nodes, lastNonTriviaPosition);
}
};
const onAfterEmitNodeArray: PrintHandlers["onAfterEmitNodeArray"] = nodes => {
if (nodes) {
setEnd(nodes, lastNonTriviaPosition);
}
};
const onBeforeEmitToken: PrintHandlers["onBeforeEmitToken"] = node => {
if (node) {
setPos(node, lastNonTriviaPosition);
}
};
const onAfterEmitToken: PrintHandlers["onAfterEmitToken"] = node => {
if (node) {
setEnd(node, lastNonTriviaPosition);
}
};
function setLastNonTriviaPosition(s: string, force: boolean) {
if (force || !isTrivia(s)) {
this.lastNonTriviaPosition = this.writer.getTextPos();
lastNonTriviaPosition = writer.getTextPos();
let i = 0;
while (isWhiteSpaceLike(s.charCodeAt(s.length - i - 1))) {
i++;
}
// trim trailing whitespaces
this.lastNonTriviaPosition -= i;
lastNonTriviaPosition -= i;
}
}
write(s: string): void {
this.writer.write(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
function write(s: string): void {
writer.write(s);
setLastNonTriviaPosition(s, /*force*/ false);
}
writeComment(s: string): void {
this.writer.writeComment(s);
function writeComment(s: string): void {
writer.writeComment(s);
}
writeKeyword(s: string): void {
this.writer.writeKeyword(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
function writeKeyword(s: string): void {
writer.writeKeyword(s);
setLastNonTriviaPosition(s, /*force*/ false);
}
writeOperator(s: string): void {
this.writer.writeOperator(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
function writeOperator(s: string): void {
writer.writeOperator(s);
setLastNonTriviaPosition(s, /*force*/ false);
}
writePunctuation(s: string): void {
this.writer.writePunctuation(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
function writePunctuation(s: string): void {
writer.writePunctuation(s);
setLastNonTriviaPosition(s, /*force*/ false);
}
writeTrailingSemicolon(s: string): void {
this.writer.writeTrailingSemicolon(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
function writeTrailingSemicolon(s: string): void {
writer.writeTrailingSemicolon(s);
setLastNonTriviaPosition(s, /*force*/ false);
}
writeParameter(s: string): void {
this.writer.writeParameter(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
function writeParameter(s: string): void {
writer.writeParameter(s);
setLastNonTriviaPosition(s, /*force*/ false);
}
writeProperty(s: string): void {
this.writer.writeProperty(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
function writeProperty(s: string): void {
writer.writeProperty(s);
setLastNonTriviaPosition(s, /*force*/ false);
}
writeSpace(s: string): void {
this.writer.writeSpace(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
function writeSpace(s: string): void {
writer.writeSpace(s);
setLastNonTriviaPosition(s, /*force*/ false);
}
writeStringLiteral(s: string): void {
this.writer.writeStringLiteral(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
function writeStringLiteral(s: string): void {
writer.writeStringLiteral(s);
setLastNonTriviaPosition(s, /*force*/ false);
}
writeSymbol(s: string, sym: Symbol): void {
this.writer.writeSymbol(s, sym);
this.setLastNonTriviaPosition(s, /*force*/ false);
function writeSymbol(s: string, sym: Symbol): void {
writer.writeSymbol(s, sym);
setLastNonTriviaPosition(s, /*force*/ false);
}
writeLine(): void {
this.writer.writeLine();
function writeLine(): void {
writer.writeLine();
}
increaseIndent(): void {
this.writer.increaseIndent();
function increaseIndent(): void {
writer.increaseIndent();
}
decreaseIndent(): void {
this.writer.decreaseIndent();
function decreaseIndent(): void {
writer.decreaseIndent();
}
getText(): string {
return this.writer.getText();
function getText(): string {
return writer.getText();
}
rawWrite(s: string): void {
this.writer.rawWrite(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
function rawWrite(s: string): void {
writer.rawWrite(s);
setLastNonTriviaPosition(s, /*force*/ false);
}
writeLiteral(s: string): void {
this.writer.writeLiteral(s);
this.setLastNonTriviaPosition(s, /*force*/ true);
function writeLiteral(s: string): void {
writer.writeLiteral(s);
setLastNonTriviaPosition(s, /*force*/ true);
}
getTextPos(): number {
return this.writer.getTextPos();
function getTextPos(): number {
return writer.getTextPos();
}
getLine(): number {
return this.writer.getLine();
function getLine(): number {
return writer.getLine();
}
getColumn(): number {
return this.writer.getColumn();
function getColumn(): number {
return writer.getColumn();
}
getIndent(): number {
return this.writer.getIndent();
function getIndent(): number {
return writer.getIndent();
}
isAtStartOfLine(): boolean {
return this.writer.isAtStartOfLine();
function isAtStartOfLine(): boolean {
return writer.isAtStartOfLine();
}
clear(): void {
this.writer.clear();
this.lastNonTriviaPosition = 0;
function clear(): void {
writer.clear();
lastNonTriviaPosition = 0;
}
return {
onEmitNode,
onBeforeEmitNodeArray,
onAfterEmitNodeArray,
onBeforeEmitToken,
onAfterEmitToken,
write,
writeComment,
writeKeyword,
writeOperator,
writePunctuation,
writeTrailingSemicolon,
writeParameter,
writeProperty,
writeSpace,
writeStringLiteral,
writeSymbol,
writeLine,
increaseIndent,
decreaseIndent,
getText,
rawWrite,
writeLiteral,
getTextPos,
getLine,
getColumn,
getIndent,
isAtStartOfLine,
clear
};
}
function getInsertionPositionAtSourceFileTop(sourceFile: SourceFile): number {
+1
View File
@@ -28,6 +28,7 @@
"patternMatcher.ts",
"preProcess.ts",
"rename.ts",
"smartSelection.ts",
"signatureHelp.ts",
"sourcemaps.ts",
"suggestionDiagnostics.ts",
+15
View File
@@ -296,6 +296,8 @@ namespace ts {
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo;
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): ReadonlyArray<RenameLocation> | undefined;
getSmartSelectionRange(fileName: string, position: number): SelectionRange;
getDefinitionAtPosition(fileName: string, position: number): ReadonlyArray<DefinitionInfo> | undefined;
getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined;
getTypeDefinitionAtPosition(fileName: string, position: number): ReadonlyArray<DefinitionInfo> | undefined;
@@ -611,6 +613,13 @@ namespace ts {
*/
originalTextSpan?: TextSpan;
originalFileName?: string;
/**
* If DocumentSpan.textSpan is the span for name of the declaration,
* then this is the span for relevant declaration
*/
contextSpan?: TextSpan;
originalContextSpan?: TextSpan;
}
export interface RenameLocation extends DocumentSpan {
@@ -645,6 +654,7 @@ namespace ts {
fileName?: string;
isInString?: true;
textSpan: TextSpan;
contextSpan?: TextSpan;
kind: HighlightSpanKind;
}
@@ -848,6 +858,11 @@ namespace ts {
isOptional: boolean;
}
export interface SelectionRange {
textSpan: TextSpan;
parent?: SelectionRange;
}
/**
* Represents a single signature to show in signature help.
* The id is used for subsequent calls into the language service to ask questions about the
+49 -2
View File
@@ -1190,8 +1190,8 @@ namespace ts {
return !!range && shouldBeReference === tripleSlashDirectivePrefixRegex.test(sourceFile.text.substring(range.pos, range.end));
}
export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile): TextSpan {
return createTextSpanFromBounds(node.getStart(sourceFile), node.getEnd());
export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile, endNode?: Node): TextSpan {
return createTextSpanFromBounds(node.getStart(sourceFile), (endNode || node).getEnd());
}
export function createTextRangeFromNode(node: Node, sourceFile: SourceFile): TextRange {
@@ -1991,4 +1991,51 @@ namespace ts {
});
return typeIsAccessible ? res : undefined;
}
export function syntaxUsuallyHasTrailingSemicolon(kind: SyntaxKind) {
return kind === SyntaxKind.VariableStatement
|| kind === SyntaxKind.ExpressionStatement
|| kind === SyntaxKind.DoStatement
|| kind === SyntaxKind.ContinueStatement
|| kind === SyntaxKind.BreakStatement
|| kind === SyntaxKind.ReturnStatement
|| kind === SyntaxKind.ThrowStatement
|| kind === SyntaxKind.DebuggerStatement
|| kind === SyntaxKind.PropertyDeclaration
|| kind === SyntaxKind.TypeAliasDeclaration
|| kind === SyntaxKind.ImportDeclaration
|| kind === SyntaxKind.ImportEqualsDeclaration
|| kind === SyntaxKind.ExportDeclaration;
}
export function probablyUsesSemicolons(sourceFile: SourceFile): boolean {
let withSemicolon = 0;
let withoutSemicolon = 0;
const nStatementsToObserve = 5;
forEachChild(sourceFile, function visit(node): boolean | undefined {
if (syntaxUsuallyHasTrailingSemicolon(node.kind)) {
const lastToken = node.getLastToken(sourceFile);
if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) {
withSemicolon++;
}
else {
withoutSemicolon++;
}
}
if (withSemicolon + withoutSemicolon >= nStatementsToObserve) {
return true;
}
return forEachChild(node, visit);
});
// One statement missing a semicolon isnt sufficient evidence to say the user
// doesnt want semicolons, because they may not even be done writing that statement.
if (withSemicolon === 0 && withoutSemicolon <= 1) {
return true;
}
// If even 2/5 places have a semicolon, the user probably wants semicolons
return withSemicolon / withoutSemicolon > 1 / nStatementsToObserve;
}
}
+1
View File
@@ -143,6 +143,7 @@ class CompilerTest {
this.tsConfigFiles = [];
if (testCaseContent.tsConfig) {
assert.equal(testCaseContent.tsConfig.fileNames.length, 0, `list of files in tsconfig is not currently supported`);
assert.equal(testCaseContent.tsConfig.raw.exclude, undefined, `exclude in tsconfig is not currently supported`);
tsConfigOptions = ts.cloneCompilerOptions(testCaseContent.tsConfig.options);
this.tsConfigFiles.push(this.createHarnessTestFile(testCaseContent.tsConfigFileUnitData!, rootDir, ts.combinePaths(rootDir, tsConfigOptions.configFilePath!)));
+99 -1
View File
@@ -24,7 +24,7 @@ abstract class ExternalCompileRunnerBase extends RunnerBase {
*/
initializeTests(): void {
// Read in and evaluate the test list
const testList = this.tests && this.tests.length ? this.tests : this.enumerateTestFiles();
const testList = this.tests && this.tests.length ? this.tests : this.getTestFiles();
// tslint:disable-next-line:no-this-assignment
const cls = this;
@@ -106,6 +106,104 @@ ${stripAbsoluteImportPaths(result.stderr.toString().replace(/\r\n/g, "\n"))}`;
}
}
class DockerfileRunner extends ExternalCompileRunnerBase {
readonly testDir = "tests/cases/docker/";
kind(): TestRunnerKind {
return "docker";
}
initializeTests(): void {
// Read in and evaluate the test list
const testList = this.tests && this.tests.length ? this.tests : this.getTestFiles();
// tslint:disable-next-line:no-this-assignment
const cls = this;
describe(`${this.kind()} code samples`, function(this: Mocha.ISuiteCallbackContext) {
this.timeout(cls.timeout); // 20 minutes
before(() => {
cls.exec("docker", ["build", ".", "-t", "typescript/typescript"], { cwd: Harness.IO.getWorkspaceRoot() }); // cached because workspace is hashed to determine cacheability
});
for (const test of testList) {
const directory = typeof test === "string" ? test : test.file;
const cwd = path.join(Harness.IO.getWorkspaceRoot(), cls.testDir, directory);
it(`should build ${directory} successfully`, () => {
const imageName = `tstest/${directory}`;
cls.exec("docker", ["build", "--no-cache", ".", "-t", imageName], { cwd }); // --no-cache so the latest version of the repos referenced is always fetched
const cp: typeof import("child_process") = require("child_process");
Harness.Baseline.runBaseline(`${cls.kind()}/${directory}.log`, cls.report(cp.spawnSync(`docker`, ["run", imageName], { cwd, timeout: cls.timeout, shell: true })));
});
}
});
}
private timeout = 1_200_000; // 20 minutes;
private exec(command: string, args: string[], options: { cwd: string, timeout?: number }): void {
const cp: typeof import("child_process") = require("child_process");
const stdio = isWorker ? "pipe" : "inherit";
const res = cp.spawnSync(command, args, { timeout: this.timeout, shell: true, stdio, ...options });
if (res.status !== 0) {
throw new Error(`${command} ${args.join(" ")} for ${options.cwd} failed: ${res.stderr && res.stderr.toString()}`);
}
}
report(result: ExecResult) {
// tslint:disable-next-line:no-null-keyword
return result.status === 0 && !result.stdout.length && !result.stderr.length ? null : `Exit Code: ${result.status}
Standard output:
${sanitizeDockerfileOutput(result.stdout.toString())}
Standard error:
${sanitizeDockerfileOutput(result.stderr.toString())}`;
}
}
function sanitizeDockerfileOutput(result: string): string {
return [
normalizeNewlines,
stripANSIEscapes,
stripRushStageNumbers,
sanitizeVersionSpecifiers,
sanitizeTimestamps,
sanitizeUnimportantGulpOutput,
stripAbsoluteImportPaths,
].reduce((result, f) => f(result), result);
}
function normalizeNewlines(result: string): string {
return result.replace(/\r\n/g, "\n");
}
function stripANSIEscapes(result: string): string {
return result.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
}
function stripRushStageNumbers(result: string): string {
return result.replace(/\d+ of \d+:/g, "XX of XX:");
}
/**
* Gulp's output order within a `parallel` block is nondeterministic (and there's no way to configure it to execute in series),
* so we purge as much of the gulp output as we can
*/
function sanitizeUnimportantGulpOutput(result: string): string {
return result.replace(/^.*(\] (Starting)|(Finished)).*$/gm, "") // task start/end messages (nondeterministic order)
.replace(/^.*\] Respawned to PID: \d+.*$/gm, "") // PID of child is OS and system-load dependent (likely stableish in a container but still dangerous)
.replace(/\n+/g, "\n");
}
function sanitizeTimestamps(result: string): string {
return result.replace(/\[\d?\d:\d\d:\d\d (A|P)M\]/g, "[XX:XX:XX XM]")
.replace(/\[\d?\d:\d\d:\d\d\]/g, "[XX:XX:XX]")
.replace(/\d+(\.\d+)? sec(onds?)?/g, "? seconds")
.replace(/\d+(\.\d+)? min(utes?)?/g, "")
.replace(/\d+(\.\d+)?( m)?s/g, "?s");
}
function sanitizeVersionSpecifiers(result: string): string {
return result
.replace(/\d+.\d+.\d+-insiders.\d\d\d\d\d\d\d\d/g, "X.X.X-insiders.xxxxxxxx")
.replace(/([@v\()])\d+\.\d+\.\d+/g, "$1X.X.X");
}
/**
* Import types and some other error messages use absolute paths in errors as they have no context to be written relative to;
* This is problematic for error baselines, so we grep for them and strip them out.
+28 -2
View File
@@ -14,7 +14,7 @@ namespace Harness.Parallel.Host {
const isatty = tty.isatty(1) && tty.isatty(2);
const path = require("path") as typeof import("path");
const { fork } = require("child_process") as typeof import("child_process");
const { statSync } = require("fs") as typeof import("fs");
const { statSync, readFileSync } = require("fs") as typeof import("fs");
// NOTE: paths for module and types for FailedTestReporter _do not_ line up due to our use of --outFile for run.js
// tslint:disable-next-line:variable-name
@@ -192,12 +192,37 @@ namespace Harness.Parallel.Host {
return `tsrunner-${runner}://${test}`;
}
function skipCostlyTests(tasks: Task[]) {
if (statSync("tests/.test-cost.json")) {
const costs = JSON.parse(readFileSync("tests/.test-cost.json", "utf8")) as {
totalTime: number,
totalEdits: number,
data: { name: string, time: number, edits: number, costs: number }[]
};
let skippedEdits = 0;
let skippedTime = 0;
const skippedTests = new Set<string>();
let i = 0;
for (; i < costs.data.length && (skippedEdits / costs.totalEdits) < (skipPercent / 100); i++) {
skippedEdits += costs.data[i].edits;
skippedTime += costs.data[i].time;
skippedTests.add(costs.data[i].name);
}
console.log(`Skipped ${i} expensive tests; estimated time savings of ${(skippedTime / costs.totalTime * 100).toFixed(2)}% with --skipPercent=${skipPercent.toFixed(2)} chance of missing a test.`);
return tasks.filter(t => !skippedTests.has(t.file));
}
else {
console.log("No cost analysis discovered.");
return tasks;
}
}
function startDelayed(perfData: { [testHash: string]: number } | undefined, totalCost: number) {
console.log(`Discovered ${tasks.length} unittest suites` + (newTasks.length ? ` and ${newTasks.length} new suites.` : "."));
console.log("Discovering runner-based tests...");
const discoverStart = +(new Date());
for (const runner of runners) {
for (const test of runner.enumerateTestFiles()) {
for (const test of runner.getTestFiles()) {
const file = typeof test === "string" ? test : test.file;
let size: number;
if (!perfData) {
@@ -231,6 +256,7 @@ namespace Harness.Parallel.Host {
}
tasks.sort((a, b) => a.size - b.size);
tasks = tasks.concat(newTasks);
tasks = skipCostlyTests(tasks);
const batchCount = workerCount;
const packfraction = 0.9;
const chunkSize = 1000; // ~1KB or 1s for sending batches near the end of a test
+5 -1
View File
@@ -32,7 +32,11 @@ namespace project {
export class ProjectRunner extends RunnerBase {
public enumerateTestFiles() {
return this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true });
const all = this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true });
if (shards === 1) {
return all;
}
return all.filter((_val, idx) => idx % shards === (shardId - 1));
}
public kind(): TestRunnerKind {
+22
View File
@@ -40,6 +40,8 @@ function createRunner(kind: TestRunnerKind): RunnerBase {
return new UserCodeRunner();
case "dt":
return new DefinitelyTypedRunner();
case "docker":
return new DockerfileRunner();
}
return ts.Debug.fail(`Unknown runner kind ${kind}`);
}
@@ -63,6 +65,7 @@ let runUnitTests: boolean | undefined;
let stackTraceLimit: number | "full" | undefined;
let noColors = false;
let keepFailed = false;
let skipPercent = 5;
interface TestConfig {
light?: boolean;
@@ -76,6 +79,9 @@ interface TestConfig {
noColors?: boolean;
timeout?: number;
keepFailed?: boolean;
skipPercent?: number;
shardId?: number;
shards?: number;
}
interface TaskSet {
@@ -107,6 +113,15 @@ function handleTestConfig() {
if (testConfig.keepFailed) {
keepFailed = true;
}
if (testConfig.skipPercent !== undefined) {
skipPercent = testConfig.skipPercent;
}
if (testConfig.shardId) {
shardId = testConfig.shardId;
}
if (testConfig.shards) {
shards = testConfig.shards;
}
if (testConfig.stackTraceLimit === "full") {
(<any>Error).stackTraceLimit = Infinity;
@@ -122,6 +137,9 @@ function handleTestConfig() {
const runnerConfig = testConfig.runners || testConfig.test;
if (runnerConfig && runnerConfig.length > 0) {
if (testConfig.runners) {
runUnitTests = runnerConfig.indexOf("unittest") !== -1;
}
for (const option of runnerConfig) {
if (!option) {
continue;
@@ -172,6 +190,9 @@ function handleTestConfig() {
case "dt":
runners.push(new DefinitelyTypedRunner());
break;
case "docker":
runners.push(new DockerfileRunner());
break;
}
}
}
@@ -194,6 +215,7 @@ function handleTestConfig() {
// CRON-only tests
if (process.env.TRAVIS_EVENT_TYPE === "cron") {
runners.push(new UserCodeRunner());
runners.push(new DockerfileRunner());
}
}
if (runUnitTests === undefined) {
+1 -1
View File
@@ -225,7 +225,7 @@ class RWCRunner extends RunnerBase {
*/
public initializeTests(): void {
// Read in and evaluate the test list
for (const test of this.tests && this.tests.length ? this.tests : this.enumerateTestFiles()) {
for (const test of this.tests && this.tests.length ? this.tests : this.getTestFiles()) {
this.runTest(typeof test === "string" ? test : test.file);
}
}
+1 -1
View File
@@ -97,7 +97,7 @@ class Test262BaselineRunner extends RunnerBase {
public initializeTests() {
// this will set up a series of describe/it blocks to run between the setup and cleanup phases
if (this.tests.length === 0) {
const testFiles = this.enumerateTestFiles();
const testFiles = this.getTestFiles();
testFiles.forEach(fn => {
this.runTest(fn);
});
+2
View File
@@ -90,6 +90,7 @@
"unittests/services/textChanges.ts",
"unittests/services/transpile.ts",
"unittests/tsbuild/amdModulesWithOut.ts",
"unittests/tsbuild/containerOnlyReferenced.ts",
"unittests/tsbuild/emptyFiles.ts",
"unittests/tsbuild/graphOrdering.ts",
"unittests/tsbuild/inferredTypeFromTransitiveModule.ts",
@@ -142,6 +143,7 @@
"unittests/tsserver/reload.ts",
"unittests/tsserver/rename.ts",
"unittests/tsserver/resolutionCache.ts",
"unittests/tsserver/smartSelection.ts",
"unittests/tsserver/session.ts",
"unittests/tsserver/skipLibCheck.ts",
"unittests/tsserver/symLinks.ts",
@@ -486,6 +486,16 @@ namespace ts {
});
});
it("parse build with --locale en-us", () => {
// --lib es6 0.ts
assertParseResult(["--locale", "en-us", "src"],
{
errors: [],
projects: ["src"],
buildOptions: { locale: "en-us" }
});
});
it("parse build with --tsBuildInfoFile", () => {
// --lib es6 0.ts
assertParseResult(["--tsBuildInfoFile", "build.tsbuildinfo", "tests"],
@@ -197,13 +197,13 @@ namespace ts {
const caseSensitiveBasePath = "/dev/";
const caseSensitiveHost = new fakes.ParseConfigHost(createFileSystem(/*ignoreCase*/ false, caseSensitiveBasePath, "/"));
function verifyDiagnostics(actual: Diagnostic[], expected: {code: number, category: DiagnosticCategory, messageText: string}[]) {
function verifyDiagnostics(actual: Diagnostic[], expected: { code: number; messageText: string; }[]) {
assert.isTrue(expected.length === actual.length, `Expected error: ${JSON.stringify(expected)}. Actual error: ${JSON.stringify(actual)}.`);
for (let i = 0; i < actual.length; i++) {
const actualError = actual[i];
const expectedError = expected[i];
assert.equal(actualError.code, expectedError.code, "Error code mismatch");
assert.equal(actualError.category, expectedError.category, "Category mismatch");
assert.equal(actualError.category, DiagnosticCategory.Error, "Category mismatch"); // Should always be error
assert.equal(flattenDiagnosticMessageText(actualError.messageText, "\n"), expectedError.messageText);
}
}
@@ -246,7 +246,7 @@ namespace ts {
});
}
function testFailure(name: string, entry: string, expectedDiagnostics: { code: number, category: DiagnosticCategory, messageText: string }[]) {
function testFailure(name: string, entry: string, expectedDiagnostics: { code: number; messageText: string; }[]) {
it(name, () => {
const parsed = getParseCommandLine(entry);
verifyDiagnostics(parsed.errors, expectedDiagnostics);
@@ -280,26 +280,22 @@ namespace ts {
testFailure("can report errors on circular imports", "circular.json", [
{
code: 18000,
category: DiagnosticCategory.Error,
messageText: `Circularity detected while resolving configuration: ${[combinePaths(basePath, "circular.json"), combinePaths(basePath, "circular2.json"), combinePaths(basePath, "circular.json")].join(" -> ")}`
}
]);
testFailure("can report missing configurations", "missing.json", [{
code: 6096,
category: DiagnosticCategory.Message,
messageText: `File './missing2' does not exist.`
code: 6053,
messageText: `File './missing2' not found.`
}]);
testFailure("can report errors in extended configs", "failure.json", [{
code: 6114,
category: DiagnosticCategory.Error,
messageText: `Unknown option 'excludes'. Did you mean 'exclude'?`
}]);
testFailure("can error when 'extends' is not a string", "extends.json", [{
code: 5024,
category: DiagnosticCategory.Error,
messageText: `Compiler option 'extends' requires a value of type string.`
}]);
@@ -194,7 +194,7 @@ namespace ts {
testProjectReferences(spec, "/primary/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
assertHasError("Reports an error about b.ts not being in the list", errs, Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern);
assertHasError("Reports an error about b.ts not being in the list", errs, Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern);
});
});
@@ -325,7 +325,7 @@ namespace ts {
};
testProjectReferences(spec, "/alpha/tsconfig.json", (program, host) => {
program.emit();
assert.deepEqual(host.outputs.map(e => e.file).sort(), ["/alpha/bin/src/a.d.ts", "/alpha/bin/src/a.js"]);
assert.deepEqual(host.outputs.map(e => e.file).sort(), ["/alpha/bin/src/a.d.ts", "/alpha/bin/src/a.js", "/alpha/bin/tsconfig.tsbuildinfo"]);
});
});
});
@@ -344,7 +344,7 @@ namespace ts {
};
testProjectReferences(spec, "/alpha/tsconfig.json", (program) => {
assertHasError("Issues an error about the rootDir", program.getOptionsDiagnostics(), Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files);
assertHasError("Issues an error about the fileList", program.getOptionsDiagnostics(), Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern);
assertHasError("Issues an error about the fileList", program.getOptionsDiagnostics(), Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern);
});
});
});
+1 -1
View File
@@ -1,7 +1,7 @@
namespace ts {
describe("unittests:: FactoryAPI", () => {
function assertSyntaxKind(node: Node, expected: SyntaxKind) {
assert.strictEqual(node.kind, expected, `Actual: ${Debug.showSyntaxKind(node)} Expected: ${(ts as any).SyntaxKind[expected]}`);
assert.strictEqual(node.kind, expected, `Actual: ${Debug.formatSyntaxKind(node.kind)} Expected: ${Debug.formatSyntaxKind(expected)}`);
}
describe("createExportAssignment", () => {
it("parenthesizes default export if necessary", () => {
+17 -1
View File
@@ -530,7 +530,7 @@ export = C;
});
});
describe("unittests:: moduleResolution:: Files with different casing", () => {
describe("unittests:: moduleResolution:: Files with different casing with forceConsistentCasingInFileNames", () => {
let library: SourceFile;
function test(files: Map<string>, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void {
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
@@ -649,6 +649,22 @@ import b = require("./moduleB");
});
test(files, { module: ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], []);
});
it("should succeed when the two files in program differ only in drive letter in their names", () => {
const files = createMapFromTemplate({
"d:/someFolder/moduleA.ts": `import a = require("D:/someFolder/moduleC")`,
"d:/someFolder/moduleB.ts": `import a = require("./moduleC")`,
"D:/someFolder/moduleC.ts": "export const x = 10",
});
test(
files,
{ module: ModuleKind.CommonJS, forceConsistentCasingInFileNames: true },
"d:/someFolder",
/*useCaseSensitiveFileNames*/ false,
["d:/someFolder/moduleA.ts", "d:/someFolder/moduleB.ts"],
[]
);
});
});
describe("unittests:: moduleResolution:: baseUrl augmented module resolution", () => {
@@ -1,5 +1,5 @@
namespace ts {
describe("unittests:: services:: Organize imports", () => {
describe("unittests:: services:: organizeImports", () => {
describe("Sort imports", () => {
it("Sort - non-relative vs non-relative", () => {
assertSortsBefore(
@@ -343,6 +343,36 @@ import { } from "lib";
},
libFile);
testOrganizeImports("Unused_false_positive_module_augmentation",
{
path: "/test.d.ts",
content: `
import foo from 'foo';
import { Caseless } from 'caseless';
declare module 'foo' {}
declare module 'caseless' {
interface Caseless {
test(name: KeyType): boolean;
}
}`
});
testOrganizeImports("Unused_preserve_imports_for_module_augmentation_in_non_declaration_file",
{
path: "/test.ts",
content: `
import foo from 'foo';
import { Caseless } from 'caseless';
declare module 'foo' {}
declare module 'caseless' {
interface Caseless {
test(name: KeyType): boolean;
}
}`
});
testOrganizeImports("Unused_false_positive_shorthand_assignment",
{
path: "/test.ts",
@@ -0,0 +1,48 @@
namespace ts {
describe("unittests:: tsbuild:: when containerOnly project is referenced", () => {
let projFs: vfs.FileSystem;
before(() => {
projFs = loadProjectFromDisk("tests/projects/containerOnlyReferenced");
});
after(() => {
projFs = undefined!; // Release the contents
});
function outputs(folder: string) {
return [
`${folder}/index.js`,
`${folder}/index.d.ts`,
`${folder}/tsconfig.tsbuildinfo`
];
}
it("verify that subsequent builds after initial build doesnt build anything", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
createSolutionBuilder(host, ["/src"], { verbose: true }).build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/src/folder/tsconfig.json", "src/src/folder2/tsconfig.json", "src/src/tsconfig.json", "src/tests/tsconfig.json", "src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/folder/tsconfig.json", "src/src/folder/index.js"],
[Diagnostics.Building_project_0, "/src/src/folder/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/folder2/tsconfig.json", "src/src/folder2/index.js"],
[Diagnostics.Building_project_0, "/src/src/folder2/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tests/tsconfig.json", "src/tests/index.js"],
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"],
);
verifyOutputsPresent(fs, [
...outputs("/src/src/folder"),
...outputs("/src/src/folder2"),
...outputs("/src/tests"),
]);
host.clearDiagnostics();
createSolutionBuilder(host, ["/src"], { verbose: true }).build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/src/folder/tsconfig.json", "src/src/folder2/tsconfig.json", "src/src/tsconfig.json", "src/tests/tsconfig.json", "src/tsconfig.json"),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/src/folder/tsconfig.json", "src/src/folder/index.ts", "src/src/folder/index.js"],
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/src/folder2/tsconfig.json", "src/src/folder2/index.ts", "src/src/folder2/index.js"],
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/tests/tsconfig.json", "src/tests/index.ts", "src/tests/index.js"],
);
});
});
}
@@ -14,13 +14,11 @@ namespace ts {
const builder = createSolutionBuilder(host, ["/src/no-references"], { dry: false, force: false, verbose: false });
host.clearDiagnostics();
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages([Diagnostics.The_files_list_in_config_file_0_is_empty, "/src/no-references/tsconfig.json"]);
// Check for outputs to not be written.
for (const output of allExpectedOutputs) {
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
}
verifyOutputsAbsent(fs, allExpectedOutputs);
});
it("does not have empty files diagnostic when files is empty and references are provided", () => {
@@ -29,13 +27,11 @@ namespace ts {
const builder = createSolutionBuilder(host, ["/src/with-references"], { dry: false, force: false, verbose: false });
host.clearDiagnostics();
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(/*empty*/);
// Check for outputs to be written.
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, allExpectedOutputs);
});
});
}
@@ -22,34 +22,32 @@ namespace ts {
});
it("orders the graph correctly - specify two roots", () => {
checkGraphOrdering(["A", "G"], ["A", "B", "C", "D", "E", "G"]);
checkGraphOrdering(["A", "G"], ["D", "E", "C", "B", "A", "G"]);
});
it("orders the graph correctly - multiple parts of the same graph in various orders", () => {
checkGraphOrdering(["A"], ["A", "B", "C", "D", "E"]);
checkGraphOrdering(["A", "C", "D"], ["A", "B", "C", "D", "E"]);
checkGraphOrdering(["D", "C", "A"], ["A", "B", "C", "D", "E"]);
checkGraphOrdering(["A"], ["D", "E", "C", "B", "A"]);
checkGraphOrdering(["A", "C", "D"], ["D", "E", "C", "B", "A"]);
checkGraphOrdering(["D", "C", "A"], ["D", "E", "C", "B", "A"]);
});
it("orders the graph correctly - other orderings", () => {
checkGraphOrdering(["F"], ["F", "E"]);
checkGraphOrdering(["F"], ["E", "F"]);
checkGraphOrdering(["E"], ["E"]);
checkGraphOrdering(["F", "C", "A"], ["A", "B", "C", "D", "E", "F"]);
checkGraphOrdering(["F", "C", "A"], ["E", "F", "D", "C", "B", "A"]);
});
function checkGraphOrdering(rootNames: string[], expectedBuildSet: string[]) {
const builder = createSolutionBuilder(host!, rootNames, { dry: true, force: false, verbose: false });
const builder = createSolutionBuilder(host!, rootNames.map(getProjectFileName), { dry: true, force: false, verbose: false });
const buildQueue = builder.getBuildOrder();
const projFileNames = rootNames.map(getProjectFileName);
const graph = builder.getBuildGraph(projFileNames);
assert.sameMembers(graph.buildQueue, expectedBuildSet.map(getProjectFileName));
assert.deepEqual(buildQueue, expectedBuildSet.map(getProjectFileName));
for (const dep of deps) {
const child = getProjectFileName(dep[0]);
if (graph.buildQueue.indexOf(child) < 0) continue;
if (buildQueue.indexOf(child) < 0) continue;
const parent = getProjectFileName(dep[1]);
assert.isAbove(graph.buildQueue.indexOf(child), graph.buildQueue.indexOf(parent), `Expecting child ${child} to be built after parent ${parent}`);
assert.isAbove(buildQueue.indexOf(child), buildQueue.indexOf(parent), `Expecting child ${child} to be built after parent ${parent}`);
}
}

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