Jake Bailey
2022-11-07 13:57:46 -08:00
committed by GitHub
656 changed files with 303397 additions and 305539 deletions
+1 -1
View File
@@ -4,4 +4,4 @@
ARG VARIANT="14-buster"
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
RUN sudo -u node npm install -g gulp-cli
RUN sudo -u node npm install -g hereby
+1 -1
View File
@@ -1,5 +1,5 @@
**/node_modules/**
/built/local/**
/built/**
/tests/**
/lib/**
/src/lib/*.generated.d.ts
+1 -1
View File
@@ -87,7 +87,7 @@
"local/simple-indent": "error",
"local/debug-assert": "error",
"local/no-keywords": "error",
"local/one-namespace-per-file": "error",
"local/jsdoc-format": "error",
// eslint-plugin-import
"import/no-extraneous-dependencies": ["error", { "optionalDependencies": false }],
+8
View File
@@ -0,0 +1,8 @@
# Generated module conversion step - inlineImports
07758c08ab72481885e662c98d67a0e3a071b032
# Generated module conversion step - stripNamespaces
b6c053882696af8ddd94a600429f30584d303d7f
# Generated module conversion step - explicitify
9a0b85ce2a3f85f498ab2c05474b4c0b96b111c9
# Generated module conversion step - unindent
94724a8c2e68a4c7e267072ca79971f317c45e4a
+26
View File
@@ -6,3 +6,29 @@ paths:
- Gulpfile.mjs
paths-ignore:
- src/lib
# These queries appear to time out after the module conversion.
# https://github.com/github/codeql/issues/10937
query-filters:
- exclude:
id: js/path-injection # TaintedPath.ql
- exclude:
id: js/command-line-injection # CommandInjection.ql
- exclude:
id: js/code-injection # CodeInjection.ql
- exclude:
id: js/bad-code-sanitization # ImproperCodeSanitization.ql
- exclude:
id: js/unsafe-dynamic-method-access # UnsafeDynamicMethodAccess.ql
- exclude:
id: js/clear-text-logging # CleartextLogging.ql
- exclude:
id: js/regex-injection # RegExpInjection.ql
- exclude:
id: js/unvalidated-dynamic-method-call # UnvalidatedDynamicMethodCall.ql
- exclude:
id: js/insecure-download # InsecureDownload.ql
- exclude:
id: js/prototype-polluting-assignment # PrototypePollutingAssignment.ql
- exclude:
id: js/request-forgery # RequestForgery.ql
+1 -1
View File
@@ -4,7 +4,7 @@ Thank you for submitting a pull request!
Please verify that:
* [ ] There is an associated issue in the `Backlog` milestone (**required**)
* [ ] Code is up-to-date with the `main` branch
* [ ] You've successfully run `gulp runtests` locally
* [ ] You've successfully run `hereby runtests` locally
* [ ] There are new or updated unit tests validating the change
Refer to CONTRIBUTING.MD for more details.
@@ -17,8 +17,8 @@ jobs:
git config user.name "TypeScript Bot"
npm install
git rm -r --quiet tests/baselines/reference :^tests/baselines/reference/docker :^tests/baselines/reference/user
gulp runtests-parallel --ci --fix || true
gulp baseline-accept
npx hereby runtests-parallel --ci --fix || true
npx hereby baseline-accept
git add ./src
git add ./tests/baselines/reference
git diff --cached
+46 -7
View File
@@ -21,6 +21,13 @@ jobs:
- "*"
- lts/*
- lts/-1
bundle:
- "true"
include:
- node-version: "*"
bundle: "false"
name: Test Node ${{ matrix.node-version }} with --bundle=${{ matrix.bundle }}
steps:
- uses: actions/checkout@v3
@@ -32,7 +39,7 @@ jobs:
- run: npm ci
- name: Tests
run: npm test
run: npm run test -- --bundle=${{ matrix.bundle }}
lint:
runs-on: ubuntu-latest
@@ -62,11 +69,23 @@ jobs:
- name: Adding playwright
run: npm install --no-save --no-package-lock playwright
- name: Build local
run: gulp local
- name: Validate the browser can import TypeScript
run: gulp test-browser-integration
run: npx hereby test-browser-integration
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: "*"
check-latest: true
- run: npm ci
- name: Build src
run: npx hereby build-src
misc:
runs-on: ubuntu-latest
@@ -80,7 +99,27 @@ jobs:
- run: npm ci
- name: Build scripts
run: gulp scripts
run: npx hereby scripts
- name: ESLint tests
run: gulp run-eslint-rules-tests
run: npx hereby run-eslint-rules-tests
self-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: "*"
check-latest: true
- run: npm ci
- name: Build tsc
run: npx hereby tsc
- name: Clean
run: npx hereby clean-src
- name: Self build
run: npx hereby build-src --built
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
sed -i -e 's/const versionMajorMinor = ".*"/const versionMajorMinor = "${{ github.event.client_payload.core_major_minor }}"/g' tests/baselines/reference/api/tsserverlibrary.d.ts
sed -i -e 's/const version\(: string\)\{0,1\} = `${versionMajorMinor}.0-.*`/const version = `${versionMajorMinor}.0-${{ github.event.client_payload.core_tag || 'dev' }}`/g' src/compiler/corePublic.ts
npm ci
gulp LKG
npx hereby LKG
npm test
git diff
git add package.json
+4 -4
View File
@@ -23,10 +23,10 @@ jobs:
run: |
npm whoami
npm ci
gulp configure-nightly
gulp LKG
gulp runtests-parallel
gulp clean
npx hereby configure-nightly
npx hereby LKG
npx hereby runtests-parallel
npx hereby clean
npm publish --tag next
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
@@ -19,11 +19,11 @@ jobs:
- name: Adding playwright
run: npm install --no-save --no-package-lock playwright
- name: Validate the browser can import TypeScript
run: gulp test-browser-integration
run: npx hereby test-browser-integration
- name: LKG, clean, and pack
run: |
gulp LKG
gulp clean
npx hereby LKG
npx hereby clean
npm pack ./
mv typescript-*.tgz typescript.tgz
- name: Upload built tarfile
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
sed -i -e 's/const versionMajorMinor = ".*"/const versionMajorMinor = "${{ github.event.client_payload.core_major_minor }}"/g' tests/baselines/reference/api/tsserverlibrary.d.ts
sed -i -e 's/const version\(: string\)\{0,1\} = .*;/const version = "${{ github.event.client_payload.package_version }}" as string;/g' src/compiler/corePublic.ts
npm ci
gulp LKG
npx hereby LKG
npm test
git diff
git add package.json
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
git config user.email "typescriptbot@microsoft.com"
git config user.name "TypeScript Bot"
npm ci
gulp LKG
npx hereby LKG
npm test
git diff
git add ./lib
+19
View File
@@ -0,0 +1,19 @@
const cp = require("child_process");
const path = require("path");
const chalk = require("chalk");
const argv = process.argv.slice(2);
// --tasks-simple is used by VS Code to infer a task list; try and keep that working.
if (!argv.includes("--tasks-simple")) {
console.error(chalk.yellowBright("Warning: using gulp shim; please consider running hereby directly."));
}
const args = [
...process.execArgv,
path.join(__dirname, "node_modules", "hereby", "bin", "hereby.js"),
...argv,
];
const { status } = cp.spawnSync(process.execPath, args, { stdio: "inherit" });
process.exit(status ?? 1);
+3 -7
View File
@@ -20,7 +20,6 @@
"configurations": [
{
"type": "node",
"protocol": "inspector",
"request": "launch",
"name": "Mocha Tests (currently opened test)",
"runtimeArgs": ["--nolazy"],
@@ -43,12 +42,9 @@
},
"sourceMaps": true,
"smartStep": true,
"preLaunchTask": "gulp: tests",
"preLaunchTask": "npm: build:tests",
"console": "integratedTerminal",
"outFiles": [
"${workspaceRoot}/built/local/run.js"
],
"customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue",
"customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue"
},
{
// See: https://github.com/microsoft/TypeScript/wiki/Debugging-Language-Service-in-VS-Code
@@ -56,7 +52,7 @@
"request": "attach",
"name": "Attach to VS Code TS Server via Port",
"processId": "${command:PickProcess}",
"customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue",
"customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue"
}
]
}
+14
View File
@@ -6,4 +6,18 @@
// To use the locally built compiler, after 'npm run build':
// "typescript.tsdk": "built/local"
// To ignore commits listed in .git-blame-ignore-revs in GitLens:
// "gitlens.advanced.blame.customArguments": [
// "--ignore-revs-file",
// ".git-blame-ignore-revs"
// ]
// These options search the repo recursively and slow down
// the build task menu. We define our own in tasks.json.
"typescript.tsc.autoDetect": "off",
"npm.autoDetect": "off",
"grunt.autoDetect": "off",
"jake.autoDetect": "off",
"gulp.autoDetect": "off"
}
+38 -29
View File
@@ -4,42 +4,51 @@
"version": "2.0.0",
"tasks": [
{
"type": "gulp",
"label": "gulp: local",
"task": "local",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$tsc"
]
},
{
"type": "gulp",
"label": "gulp: tsc",
"task": "tsc",
"group": "build",
"problemMatcher": [
"$tsc"
]
},
{
"type": "gulp",
// Kept for backwards compat for old launch.json files so it's
// less annoying if moving up to the new build or going back to
// the old build.
//
// This is first because the acutal "npm: build:tests" task
// below has the same script value, and VS Code ignores labels
// and deduplicates them.
// https://github.com/microsoft/vscode/issues/93001
"label": "gulp: tests",
"task": "tests",
"type": "npm",
"script": "build:tests",
"group": "build",
"hide": true,
"problemMatcher": [
"$tsc"
]
},
{
"label": "tsc: watch ./src",
"type": "shell",
"command": "node",
"args": ["${workspaceFolder}/lib/tsc.js", "--build", "${workspaceFolder}/src", "--watch"],
"group": "build",
"isBackground": true,
"problemMatcher": [
"$tsc-watch"
]
},
{
"label": "npm: build:compiler",
"type": "npm",
"script": "build:compiler",
"group": "build",
"problemMatcher": [
"$tsc"
]
},
{
"type": "gulp",
"task": "services",
"label": "gulp: services",
"label": "npm: build:tests",
"type": "npm",
"script": "build:tests",
"group": "build",
"problemMatcher": [
"$tsc"
],
}
]
},
]
}
}
+33 -33
View File
@@ -54,42 +54,42 @@ In general, things we find useful when reviewing suggestions are:
2. A copy of the TypeScript code. See the next steps for instructions.
3. [Node](https://nodejs.org), which runs JavaScript locally. Current or LTS will both work.
4. An editor. [VS Code](https://code.visualstudio.com) is the best place to start for TypeScript.
5. The gulp command line tool, for building and testing changes. See the next steps for how to install it.
5. The hereby command line tool, for building and testing changes. See the next steps for how to install it.
## Get Started
1. Install node using the version you downloaded from [nodejs.org](https://nodejs.org).
2. Open a terminal.
3. Make a fork—your own copy—of TypeScript on your GitHub account, then make a clone—a local copy—on your computer. ([Here are some step-by-step instructions](https://github.com/anitab-org/mentorship-android/wiki/Fork%2C-Clone-%26-Remote)). Add `--depth=1` to the end of the `git clone` command to save time.
4. Install the gulp command line tool: `npm install -g gulp-cli`
4. Install the hereby command line tool: `npm install -g hereby`
5. Change to the TypeScript folder you made: `cd TypeScript`
6. Install dependencies: `npm ci`
7. Make sure everything builds and tests pass: `gulp runtests-parallel`
7. Make sure everything builds and tests pass: `hereby runtests-parallel`
8. Open the TypeScript folder in your editor.
9. Follow the directions below to add and debug a test.
## Helpful tasks
Running `gulp --tasks --depth 1` provides the full listing, but here are a few common tasks you might use.
Running `hereby --tasks` provides the full listing, but here are a few common tasks you might use.
```
gulp local # Build the compiler into built/local.
gulp clean # Delete the built compiler.
gulp LKG # Replace the last known good with the built one.
# Bootstrapping step to be executed when the built compiler reaches a stable state.
gulp tests # Build the test infrastructure using the built compiler.
gulp runtests # Run tests using the built compiler and test infrastructure.
# You can override the specific suite runner used or specify a test for this command.
# Use --tests=<testPath> for a specific test and/or --runner=<runnerName> for a specific suite.
# Valid runners include conformance, compiler, fourslash, project, user, and docker
# The user and docker runners are extended test suite runners - the user runner
# works on disk in the tests/cases/user directory, while the docker runner works in containers.
# You'll need to have the docker executable in your system path for the docker runner to work.
gulp runtests-parallel # Like runtests, but split across multiple threads. Uses a number of threads equal to the system
# core count by default. Use --workers=<number> to adjust this.
gulp baseline-accept # This replaces the baseline test results with the results obtained from gulp runtests.
gulp lint # Runs eslint on the TypeScript source.
gulp help # List the above commands.
hereby local # Build the compiler into built/local.
hereby clean # Delete the built compiler.
hereby LKG # Replace the last known good with the built one.
# Bootstrapping step to be executed when the built compiler reaches a stable state.
hereby tests # Build the test infrastructure using the built compiler.
hereby runtests # Run tests using the built compiler and test infrastructure.
# You can override the specific suite runner used or specify a test for this command.
# Use --tests=<testPath> for a specific test and/or --runner=<runnerName> for a specific suite.
# Valid runners include conformance, compiler, fourslash, project, user, and docker
# The user and docker runners are extended test suite runners - the user runner
# works on disk in the tests/cases/user directory, while the docker runner works in containers.
# You'll need to have the docker executable in your system path for the docker runner to work.
hereby runtests-parallel # Like runtests, but split across multiple threads. Uses a number of threads equal to the system
# core count by default. Use --workers=<number> to adjust this.
hereby baseline-accept # This replaces the baseline test results with the results obtained from hereby runtests.
hereby lint # Runs eslint on the TypeScript source.
hereby help # List the above commands.
```
## Tips
@@ -111,7 +111,7 @@ You might need to run `git config --global core.longpaths true` before cloning T
### Using local builds
Run `gulp` to build a version of the compiler/language service that reflects changes you've made. You can then run `node <repo-root>/built/local/tsc.js` in place of `tsc` in your project. For example, to run `tsc --watch` from within the root of the repository on a file called `test.ts`, you can run `node ./built/local/tsc.js --watch test.ts`.
Run `hereby` to build a version of the compiler/language service that reflects changes you've made. You can then run `node <repo-root>/built/local/tsc.js` in place of `tsc` in your project. For example, to run `tsc --watch` from within the root of the repository on a file called `test.ts`, you can run `node ./built/local/tsc.js --watch test.ts`.
## Contributing bug fixes
@@ -163,7 +163,7 @@ Any changes should be made to [src/lib](https://github.com/Microsoft/TypeScript/
Library files in `built/local/` are updated automatically by running the standard build task:
```sh
gulp
hereby
```
The files in `lib/` are used to bootstrap compilation and usually **should not** be updated unless publishing a new version or updating the LKG.
@@ -178,36 +178,36 @@ If you need a head start understanding how the compiler works, or how the code i
## Running the Tests
To run all tests, invoke the `runtests-parallel` target using gulp:
To run all tests, invoke the `runtests-parallel` target using hereby:
```Shell
gulp runtests-parallel
hereby runtests-parallel
```
This will run all tests; to run only a specific subset of tests, use:
```Shell
gulp runtests --tests=<regex>
hereby runtests --tests=<regex>
```
e.g. to run all compiler baseline tests:
```Shell
gulp runtests --tests=compiler
hereby runtests --tests=compiler
```
or to run a specific test: `tests\cases\compiler\2dArrays.ts`
```Shell
gulp runtests --tests=2dArrays
hereby runtests --tests=2dArrays
```
## Debugging the tests
You can debug with VS Code or Node instead with `gulp runtests -i`:
You can debug with VS Code or Node instead with `hereby runtests -i`:
```Shell
gulp runtests --tests=2dArrays -i
hereby runtests --tests=2dArrays -i
```
You can also use the [provided VS Code launch configuration](./.vscode/launch.template.json) to launch a debug session for an open test file. Rename the file 'launch.json', open the test file of interest, and launch the debugger from the debug panel (or press F5).
@@ -257,12 +257,12 @@ When a change in the baselines is detected, the test will fail. To inspect chang
git diff --diff-filter=AM --no-index ./tests/baselines/reference ./tests/baselines/local
```
Alternatively, you can set the `DIFF` environment variable and run `gulp diff`, or manually run your favorite folder diffing tool between `tests/baselines/reference` and `tests/baselines/local`. Our team largely uses Beyond Compare and WinMerge.
Alternatively, you can set the `DIFF` environment variable and run `hereby diff`, or manually run your favorite folder diffing tool between `tests/baselines/reference` and `tests/baselines/local`. Our team largely uses Beyond Compare and WinMerge.
After verifying that the changes in the baselines are correct, run
```Shell
gulp baseline-accept
hereby baseline-accept
```
This will change the files in `tests\baselines\reference`, which should be included as part of your commit.
@@ -271,6 +271,6 @@ Be sure to validate the changes carefully -- apparently unrelated changes to bas
## Localization
All strings the user may see are stored in [`diagnosticMessages.json`](./src/compiler/diagnosticMessages.json).
If you make changes to it, run `gulp generate-diagnostics` to push them to the `Diagnostic` interface in `diagnosticInformationMap.generated.ts`.
If you make changes to it, run `hereby generate-diagnostics` to push them to the `Diagnostic` interface in `diagnosticInformationMap.generated.ts`.
See [coding guidelines on diagnostic messages](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#diagnostic-messages).
+1 -2
View File
@@ -3,5 +3,4 @@ FROM node:current
COPY . /typescript
WORKDIR /typescript
RUN npm ci
RUN npm i -g gulp-cli
RUN gulp configure-insiders && gulp LKG && gulp clean && npm pack .
RUN npx hereby configure-insiders && npx hereby LKG && npx hereby clean && npm pack .
-588
View File
@@ -1,588 +0,0 @@
// @ts-check
import path from "path";
import fs from "fs";
import log from "fancy-log";
import newer from "gulp-newer";
import sourcemaps from "gulp-sourcemaps";
import del from "del";
import rename from "gulp-rename";
import concat from "gulp-concat";
import merge2 from "merge2";
import gulp from "gulp";
import { append, transform } from "gulp-insert";
import { prependFile } from "./scripts/build/prepend.mjs";
import { exec, readJson, needsUpdate, getDiffTool, getDirSize, rm } from "./scripts/build/utils.mjs";
import { runConsoleTests, refBaseline, localBaseline, refRwcBaseline, localRwcBaseline } from "./scripts/build/tests.mjs";
import { buildProject, cleanProject, watchProject } from "./scripts/build/projects.mjs";
import cmdLineOptions from "./scripts/build/options.mjs";
const { src, dest, task, parallel, series, watch } = gulp;
const copyright = "CopyrightNotice.txt";
const cleanTasks = [];
const buildScripts = () => buildProject("scripts");
task("scripts", buildScripts);
task("scripts").description = "Builds files in the 'scripts' folder.";
/** @type {{ libs: string[]; paths: Record<string, string | undefined>; }} */
const libraries = readJson("./src/lib/libs.json");
const libs = libraries.libs.map(lib => {
const relativeSources = ["header.d.ts", lib + ".d.ts"];
const relativeTarget = libraries.paths && libraries.paths[lib] || ("lib." + lib + ".d.ts");
const sources = relativeSources.map(s => path.posix.join("src/lib", s));
const target = `built/local/${relativeTarget}`;
return { target, relativeTarget, sources };
});
const generateLibs = () => {
return merge2(libs.map(({ sources, target, relativeTarget }) =>
src([copyright, ...sources])
.pipe(newer(target))
.pipe(concat(relativeTarget, { newLine: "\n\n" }))
.pipe(dest("built/local"))));
};
task("lib", generateLibs);
task("lib").description = "Builds the library targets";
const cleanLib = () => del(libs.map(lib => lib.target));
cleanTasks.push(cleanLib);
const watchLib = () => watch(["src/lib/**/*"], generateLibs);
const diagnosticInformationMapTs = "src/compiler/diagnosticInformationMap.generated.ts";
const diagnosticMessagesJson = "src/compiler/diagnosticMessages.json";
const diagnosticMessagesGeneratedJson = "src/compiler/diagnosticMessages.generated.json";
const generateDiagnostics = async () => {
if (needsUpdate(diagnosticMessagesJson, [diagnosticMessagesGeneratedJson, diagnosticInformationMapTs])) {
await exec(process.execPath, ["scripts/processDiagnosticMessages.mjs", diagnosticMessagesJson]);
}
};
task("generate-diagnostics", generateDiagnostics);
task("generate-diagnostics").description = "Generates a diagnostic file in TypeScript based on an input JSON file";
const cleanDiagnostics = () => del([diagnosticInformationMapTs, diagnosticMessagesGeneratedJson]);
cleanTasks.push(cleanDiagnostics);
const watchDiagnostics = () => watch(["src/compiler/diagnosticMessages.json"], task("generate-diagnostics"));
// Localize diagnostics
/**
* .lcg file is what localization team uses to know what messages to localize.
* The file is always generated in 'enu/diagnosticMessages.generated.json.lcg'
*/
const generatedLCGFile = "built/local/enu/diagnosticMessages.generated.json.lcg";
/**
* The localization target produces the two following transformations:
* 1. 'src\loc\lcl\<locale>\diagnosticMessages.generated.json.lcl' => 'built\local\<locale>\diagnosticMessages.generated.json'
* convert localized resources into a .json file the compiler can understand
* 2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg'
* generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json
*/
const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-br", "ru", "tr", "zh-cn", "zh-tw"]
.map(f => f.toLowerCase())
.map(f => `built/local/${f}/diagnosticMessages.generated.json`)
.concat(generatedLCGFile);
const localize = async () => {
if (needsUpdate(diagnosticMessagesGeneratedJson, generatedLCGFile)) {
return exec(process.execPath, ["scripts/generateLocalizedDiagnosticMessages.mjs", "src/loc/lcl", "built/local", diagnosticMessagesGeneratedJson], { ignoreExitCode: true });
}
};
const buildDebugTools = () => buildProject("src/debug");
const cleanDebugTools = () => cleanProject("src/debug");
cleanTasks.push(cleanDebugTools);
// Pre-build steps when targeting the LKG compiler
const lkgPreBuild = parallel(generateLibs, series(generateDiagnostics, buildDebugTools));
const buildTsc = () => buildProject("src/tsc");
task("tsc", series(lkgPreBuild, buildTsc));
task("tsc").description = "Builds the command-line compiler";
const cleanTsc = () => cleanProject("src/tsc");
cleanTasks.push(cleanTsc);
task("clean-tsc", cleanTsc);
task("clean-tsc").description = "Cleans outputs for the command-line compiler";
const watchTsc = () => watchProject("src/tsc");
task("watch-tsc", series(lkgPreBuild, parallel(watchLib, watchDiagnostics, watchTsc)));
task("watch-tsc").description = "Watch for changes and rebuild the command-line compiler only.";
// Pre-build steps when targeting the built/local compiler.
const localPreBuild = parallel(generateLibs, series(generateDiagnostics, buildDebugTools, buildTsc));
// Pre-build steps to use based on supplied options.
const preBuild = cmdLineOptions.lkg ? lkgPreBuild : localPreBuild;
const buildServices = (() => {
// build typescriptServices.out.js
const buildTypescriptServicesOut = () => buildProject("src/typescriptServices/tsconfig.json", cmdLineOptions);
// create typescriptServices.js
const createTypescriptServicesJs = () => src("built/local/typescriptServices.out.js")
.pipe(newer("built/local/typescriptServices.js"))
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(prependFile(copyright))
.pipe(rename("typescriptServices.js"))
.pipe(sourcemaps.write(".", { includeContent: false, destPath: "built/local" }))
.pipe(dest("built/local"));
// create typescriptServices.d.ts
const createTypescriptServicesDts = () => src("built/local/typescriptServices.out.d.ts")
.pipe(newer("built/local/typescriptServices.d.ts"))
.pipe(prependFile(copyright))
.pipe(transform(content => content.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4")))
.pipe(rename("typescriptServices.d.ts"))
.pipe(dest("built/local"));
// create typescript.js
const createTypescriptJs = () => src("built/local/typescriptServices.js")
.pipe(newer("built/local/typescript.js"))
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(rename("typescript.js"))
.pipe(sourcemaps.write(".", { includeContent: false, destPath: "built/local" }))
.pipe(dest("built/local"));
// create typescript.d.ts
const createTypescriptDts = () => src("built/local/typescriptServices.d.ts")
.pipe(newer("built/local/typescript.d.ts"))
.pipe(append("\nexport = ts;"))
.pipe(rename("typescript.d.ts"))
.pipe(dest("built/local"));
// create typescript_standalone.d.ts
const createTypescriptStandaloneDts = () => src("built/local/typescriptServices.d.ts")
.pipe(newer("built/local/typescript_standalone.d.ts"))
.pipe(transform(content => content.replace(/declare (namespace|module) ts/g, 'declare module "typescript"')))
.pipe(rename("typescript_standalone.d.ts"))
.pipe(dest("built/local"));
return series(
buildTypescriptServicesOut,
createTypescriptServicesJs,
createTypescriptServicesDts,
createTypescriptJs,
createTypescriptDts,
createTypescriptStandaloneDts,
);
})();
task("services", series(preBuild, buildServices));
task("services").description = "Builds the language service";
task("services").flags = {
" --built": "Compile using the built version of the compiler."
};
const cleanServices = async () => {
if (fs.existsSync("built/local/typescriptServices.tsconfig.json")) {
await cleanProject("built/local/typescriptServices.tsconfig.json");
}
await del([
"built/local/typescriptServices.out.js",
"built/local/typescriptServices.out.d.ts",
"built/local/typescriptServices.out.tsbuildinfo",
"built/local/typescriptServices.js",
"built/local/typescript.js",
"built/local/typescript.d.ts",
"built/local/typescript_standalone.d.ts"
]);
};
cleanTasks.push(cleanServices);
task("clean-services", cleanServices);
task("clean-services").description = "Cleans outputs for the language service";
const watchServices = () => watch([
"src/compiler/tsconfig.json",
"src/compiler/**/*.ts",
"src/jsTyping/tsconfig.json",
"src/jsTyping/**/*.ts",
"src/services/tsconfig.json",
"src/services/**/*.ts",
], series(preBuild, buildServices));
task("watch-services", series(preBuild, parallel(watchLib, watchDiagnostics, watchServices)));
task("watch-services").description = "Watches for changes and rebuild language service only";
task("watch-services").flags = {
" --built": "Compile using the built version of the compiler."
};
const buildDynamicImportCompat = () => buildProject("src/dynamicImportCompat", cmdLineOptions);
task("dynamicImportCompat", buildDynamicImportCompat);
const buildServerMain = () => buildProject("src/tsserver", cmdLineOptions);
const buildServer = series(buildDynamicImportCompat, buildServerMain);
buildServer.displayName = "buildServer";
task("tsserver", series(preBuild, buildServer));
task("tsserver").description = "Builds the language server";
task("tsserver").flags = {
" --built": "Compile using the built version of the compiler."
};
const cleanDynamicImportCompat = () => cleanProject("src/dynamicImportCompat");
const cleanServerMain = () => cleanProject("src/tsserver");
const cleanServer = series(cleanDynamicImportCompat, cleanServerMain);
cleanServer.displayName = "cleanServer";
cleanTasks.push(cleanServer);
task("clean-tsserver", cleanServer);
task("clean-tsserver").description = "Cleans outputs for the language server";
const watchDynamicImportCompat = () => watchProject("src/dynamicImportCompat", cmdLineOptions);
const watchServer = () => watchProject("src/tsserver", cmdLineOptions);
task("watch-tsserver", series(preBuild, parallel(watchLib, watchDiagnostics, watchDynamicImportCompat, watchServer)));
task("watch-tsserver").description = "Watch for changes and rebuild the language server only";
task("watch-tsserver").flags = {
" --built": "Compile using the built version of the compiler."
};
task("min", series(preBuild, parallel(buildTsc, buildServer)));
task("min").description = "Builds only tsc and tsserver";
task("min").flags = {
" --built": "Compile using the built version of the compiler."
};
task("clean-min", series(cleanTsc, cleanServer));
task("clean-min").description = "Cleans outputs for tsc and tsserver";
task("watch-min", series(preBuild, parallel(watchLib, watchDiagnostics, watchTsc, watchServer)));
task("watch-min").description = "Watches for changes to a tsc and tsserver only";
task("watch-min").flags = {
" --built": "Compile using the built version of the compiler."
};
const buildLssl = (() => {
// build tsserverlibrary.out.js
const buildServerLibraryOut = () => buildProject("src/tsserverlibrary/tsconfig.json", cmdLineOptions);
// create tsserverlibrary.js
const createServerLibraryJs = () => src("built/local/tsserverlibrary.out.js")
.pipe(newer("built/local/tsserverlibrary.js"))
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(prependFile(copyright))
.pipe(rename("tsserverlibrary.js"))
.pipe(sourcemaps.write(".", { includeContent: false, destPath: "built/local" }))
.pipe(dest("built/local"));
// create tsserverlibrary.d.ts
const createServerLibraryDts = () => src("built/local/tsserverlibrary.out.d.ts")
.pipe(newer("built/local/tsserverlibrary.d.ts"))
.pipe(prependFile(copyright))
.pipe(transform(content => content.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4")))
.pipe(append("\nexport = ts;\nexport as namespace ts;"))
.pipe(rename("tsserverlibrary.d.ts"))
.pipe(dest("built/local"));
return series(
buildServerLibraryOut,
createServerLibraryJs,
createServerLibraryDts,
);
})();
task("lssl", series(preBuild, buildLssl));
task("lssl").description = "Builds language service server library";
task("lssl").flags = {
" --built": "Compile using the built version of the compiler."
};
const cleanLssl = async () => {
if (fs.existsSync("built/local/tsserverlibrary.tsconfig.json")) {
await cleanProject("built/local/tsserverlibrary.tsconfig.json");
}
await del([
"built/local/tsserverlibrary.out.js",
"built/local/tsserverlibrary.out.d.ts",
"built/local/tsserverlibrary.out.tsbuildinfo",
"built/local/tsserverlibrary.js",
"built/local/tsserverlibrary.d.ts",
]);
};
cleanTasks.push(cleanLssl);
task("clean-lssl", cleanLssl);
task("clean-lssl").description = "Clean outputs for the language service server library";
const watchLssl = () => watch([
"src/compiler/tsconfig.json",
"src/compiler/**/*.ts",
"src/jsTyping/tsconfig.json",
"src/jsTyping/**/*.ts",
"src/services/tsconfig.json",
"src/services/**/*.ts",
"src/server/tsconfig.json",
"src/server/**/*.ts",
"src/webServer/tsconfig.json",
"src/webServer/**/*.ts",
"src/tsserver/tsconfig.json",
"src/tsserver/**/*.ts",
], buildLssl);
task("watch-lssl", series(preBuild, parallel(watchLib, watchDiagnostics, watchLssl)));
task("watch-lssl").description = "Watch for changes and rebuild tsserverlibrary only";
task("watch-lssl").flags = {
" --built": "Compile using the built version of the compiler."
};
const buildTests = () => buildProject("src/testRunner");
task("tests", series(preBuild, parallel(buildLssl, buildTests)));
task("tests").description = "Builds the test infrastructure";
task("tests").flags = {
" --built": "Compile using the built version of the compiler."
};
const cleanTests = () => cleanProject("src/testRunner");
cleanTasks.push(cleanTests);
task("clean-tests", cleanTests);
task("clean-tests").description = "Cleans the outputs for the test infrastructure";
const watchTests = () => watchProject("src/testRunner", cmdLineOptions);
const runEslintRulesTests = () => runConsoleTests("scripts/eslint/tests", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false);
task("run-eslint-rules-tests", runEslintRulesTests);
task("run-eslint-rules-tests").description = "Runs the eslint rule tests";
/** @type { (folder: string) => { (): Promise<any>; displayName?: string } } */
const eslint = (folder) => async () => {
const formatter = cmdLineOptions.ci ? "stylish" : "autolinkable-stylish";
const args = [
"node_modules/eslint/bin/eslint",
"--cache",
"--cache-location", `${folder}/.eslintcache`,
"--format", formatter,
];
if (cmdLineOptions.fix) {
args.push("--fix");
}
args.push(folder);
log(`Linting: ${args.join(" ")}`);
return exec(process.execPath, args);
};
const lint = eslint(".");
lint.displayName = "lint";
task("lint", lint);
task("lint").description = "Runs eslint on the compiler and scripts sources.";
const buildCancellationToken = () => buildProject("src/cancellationToken");
const cleanCancellationToken = () => cleanProject("src/cancellationToken");
cleanTasks.push(cleanCancellationToken);
const buildTypingsInstaller = () => buildProject("src/typingsInstaller");
const cleanTypingsInstaller = () => cleanProject("src/typingsInstaller");
cleanTasks.push(cleanTypingsInstaller);
const buildWatchGuard = () => buildProject("src/watchGuard");
const cleanWatchGuard = () => cleanProject("src/watchGuard");
cleanTasks.push(cleanWatchGuard);
const generateTypesMap = () => src("src/server/typesMap.json")
.pipe(newer("built/local/typesMap.json"))
.pipe(transform(contents => (JSON.parse(contents), contents))) // validates typesMap.json is valid JSON
.pipe(dest("built/local"));
task("generate-types-map", generateTypesMap);
const cleanTypesMap = () => del("built/local/typesMap.json");
cleanTasks.push(cleanTypesMap);
// Drop a copy of diagnosticMessages.generated.json into the built/local folder. This allows
// it to be synced to the Azure DevOps repo, so that it can get picked up by the build
// pipeline that generates the localization artifacts that are then fed into the translation process.
const builtLocalDiagnosticMessagesGeneratedJson = "built/local/diagnosticMessages.generated.json";
const copyBuiltLocalDiagnosticMessages = () => src(diagnosticMessagesGeneratedJson)
.pipe(newer(builtLocalDiagnosticMessagesGeneratedJson))
.pipe(dest("built/local"));
const cleanBuiltLocalDiagnosticMessages = () => del(builtLocalDiagnosticMessagesGeneratedJson);
cleanTasks.push(cleanBuiltLocalDiagnosticMessages);
const buildOtherOutputs = parallel(buildCancellationToken, buildTypingsInstaller, buildWatchGuard, generateTypesMap, copyBuiltLocalDiagnosticMessages);
task("other-outputs", series(preBuild, buildOtherOutputs));
task("other-outputs").description = "Builds miscelaneous scripts and documents distributed with the LKG";
task("local", series(preBuild, parallel(localize, buildTsc, buildServer, buildServices, buildLssl, buildOtherOutputs)));
task("local").description = "Builds the full compiler and services";
task("local").flags = {
" --built": "Compile using the built version of the compiler."
};
task("watch-local", series(preBuild, parallel(watchLib, watchDiagnostics, watchTsc, watchServices, watchServer, watchLssl)));
task("watch-local").description = "Watches for changes to projects in src/ (but does not execute tests).";
task("watch-local").flags = {
" --built": "Compile using the built version of the compiler."
};
const preTest = parallel(buildTsc, buildTests, buildServices, buildLssl);
preTest.displayName = "preTest";
const runTests = () => runConsoleTests("built/local/run.js", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false);
task("runtests", series(preBuild, preTest, runTests));
task("runtests").description = "Runs the tests using the built run.js file.";
task("runtests").flags = {
"-t --tests=<regex>": "Pattern for tests to run.",
" --failed": "Runs tests listed in '.failed-tests'.",
"-r --reporter=<reporter>": "The mocha reporter to use.",
"-i --break": "Runs tests in inspector mode (NodeJS 8 and later)",
" --keepFailed": "Keep tests in .failed-tests even if they pass",
" --light": "Run tests in light mode (fewer verifications, but tests run faster)",
" --dirty": "Run tests without first cleaning test output directories",
" --stackTraceLimit=<limit>": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
" --no-color": "Disables color",
" --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*/ cmdLineOptions.workers > 1, /*watchMode*/ false);
task("runtests-parallel", series(preBuild, preTest, runTestsParallel));
task("runtests-parallel").description = "Runs all the tests in parallel using the built run.js file.";
task("runtests-parallel").flags = {
" --light": "Run tests in light mode (fewer verifications, but tests run faster).",
" --keepFailed": "Keep tests in .failed-tests even if they pass.",
" --dirty": "Run tests without first cleaning test output directories.",
" --stackTraceLimit=<limit>": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
" --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.",
" --shards": "Total number of shards running tests (default: 1)",
" --shardId": "1-based ID of this shard (default: 1)",
};
task("test-browser-integration", () => exec(process.execPath, ["scripts/browserIntegrationTest.mjs"]));
task("test-browser-integration").description = "Runs scripts/browserIntegrationTest.mjs which tests that typescript.js loads in a browser";
task("diff", () => exec(getDiffTool(), [refBaseline, localBaseline], { ignoreExitCode: true, waitForExit: false }));
task("diff").description = "Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment variable";
task("diff-rwc", () => exec(getDiffTool(), [refRwcBaseline, localRwcBaseline], { ignoreExitCode: true, waitForExit: false }));
task("diff-rwc").description = "Diffs the RWC baselines using the diff tool specified by the 'DIFF' environment variable";
/**
* @param {string} localBaseline Path to the local copy of the baselines
* @param {string} refBaseline Path to the reference copy of the baselines
*/
const baselineAccept = (localBaseline, refBaseline) => merge2(
src([`${localBaseline}/**`, `!${localBaseline}/**/*.delete`], { base: localBaseline })
.pipe(dest(refBaseline)),
src([`${localBaseline}/**/*.delete`], { base: localBaseline, read: false })
.pipe(rm())
.pipe(rename({ extname: "" }))
.pipe(rm(refBaseline)));
task("baseline-accept", () => baselineAccept(localBaseline, refBaseline));
task("baseline-accept").description = "Makes the most recent test results the new baseline, overwriting the old baseline";
task("baseline-accept-rwc", () => baselineAccept(localRwcBaseline, refRwcBaseline));
task("baseline-accept-rwc").description = "Makes the most recent rwc test results the new baseline, overwriting the old baseline";
// TODO(rbuckton): Determine if we still need this task. Depending on a relative
// path here seems like a bad idea.
const updateSublime = () => src(["built/local/tsserver.js", "built/local/tsserver.js.map"])
.pipe(dest("../TypeScript-Sublime-Plugin/tsserver/"));
task("update-sublime", updateSublime);
task("update-sublime").description = "Updates the sublime plugin's tsserver";
// TODO(rbuckton): Should the path to DefinitelyTyped be configurable via an environment variable?
const importDefinitelyTypedTests = () => exec(process.execPath, ["scripts/importDefinitelyTypedTests.mjs", "./", "../DefinitelyTyped"]);
task("importDefinitelyTypedTests", importDefinitelyTypedTests);
task("importDefinitelyTypedTests").description = "Runs the importDefinitelyTypedTests script to copy DT's tests to the TS-internal RWC tests";
const buildReleaseTsc = () => buildProject("src/tsc/tsconfig.release.json");
const cleanReleaseTsc = () => cleanProject("src/tsc/tsconfig.release.json");
cleanTasks.push(cleanReleaseTsc);
const cleanBuilt = () => del("built");
const produceLKG = async () => {
const expectedFiles = [
"built/local/tsc.release.js",
"built/local/typescriptServices.js",
"built/local/typescriptServices.d.ts",
"built/local/tsserver.js",
"built/local/dynamicImportCompat.js",
"built/local/typescript.js",
"built/local/typescript.d.ts",
"built/local/tsserverlibrary.js",
"built/local/tsserverlibrary.d.ts",
"built/local/typingsInstaller.js",
"built/local/cancellationToken.js"
].concat(libs.map(lib => lib.target));
const missingFiles = expectedFiles
.concat(localizationTargets)
.filter(f => !fs.existsSync(f));
if (missingFiles.length > 0) {
throw new Error("Cannot replace the LKG unless all built targets are present in directory 'built/local/'. The following files are missing:\n" + missingFiles.join("\n"));
}
const sizeBefore = getDirSize("lib");
await exec(process.execPath, ["scripts/produceLKG.mjs"]);
const sizeAfter = getDirSize("lib");
if (sizeAfter > (sizeBefore * 1.10)) {
throw new Error("The lib folder increased by 10% or more. This likely indicates a bug.");
}
};
task("LKG", series(lkgPreBuild, parallel(localize, buildTsc, buildServer, buildServices, buildLssl, buildOtherOutputs, buildReleaseTsc), produceLKG));
task("LKG").description = "Makes a new LKG out of the built js files";
task("LKG").flags = {
" --built": "Compile using the built version of the compiler.",
};
task("lkg", series("LKG"));
const generateSpec = () => exec("cscript", ["//nologo", "scripts/word2md.mjs", path.resolve("doc/TypeScript Language Specification - ARCHIVED.docx"), path.resolve("doc/spec-ARCHIVED.md")]);
task("generate-spec", generateSpec);
task("generate-spec").description = "Generates a Markdown version of the Language Specification";
task("clean", series(parallel(cleanTasks), cleanBuilt));
task("clean").description = "Cleans build outputs";
const configureNightly = () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "dev", "package.json", "src/compiler/corePublic.ts"]);
task("configure-nightly", series(buildScripts, configureNightly));
task("configure-nightly").description = "Runs scripts/configurePrerelease.ts to prepare a build for nightly publishing";
const configureInsiders = () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "insiders", "package.json", "src/compiler/corePublic.ts"]);
task("configure-insiders", configureInsiders);
task("configure-insiders").description = "Runs scripts/configurePrerelease.mjs to prepare a build for insiders publishing";
const configureExperimental = () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "experimental", "package.json", "src/compiler/corePublic.ts"]);
task("configure-experimental", configureExperimental);
task("configure-experimental").description = "Runs scripts/configurePrerelease.mjs to prepare a build for experimental publishing";
const publishNightly = () => exec("npm", ["publish", "--tag", "next"]);
task("publish-nightly", series(task("clean"), task("LKG"), task("clean"), task("runtests-parallel"), publishNightly));
task("publish-nightly").description = "Runs `npm publish --tag next` to create a new nightly build on npm";
// TODO(rbuckton): The problem with watching in this way is that a change in compiler/ will result
// in cascading changes in other projects that may take differing amounts of times to complete. As
// a result, the watch may accidentally trigger early, so we have to set a significant delay. An
// alternative approach would be to leverage a builder API, or to have 'tsc -b' have an option to
// write some kind of trigger file that indicates build completion that we could listen for instead.
const watchRuntests = () => watch(["built/local/*.js", "tests/cases/**/*.ts", "tests/cases/**/tsconfig.json"], { delay: 5000 }, async () => {
if (cmdLineOptions.tests || cmdLineOptions.failed) {
await runConsoleTests("built/local/run.js", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ true);
}
else {
await runConsoleTests("built/local/run.js", "min", /*runInParallel*/ true, /*watchMode*/ true);
}
});
task("watch", series(preBuild, preTest, parallel(watchLib, watchDiagnostics, watchServices, watchLssl, watchTests, watchRuntests)));
task("watch").description = "Watches for changes and rebuilds and runs tests in parallel.";
task("watch").flags = {
"-t --tests=<regex>": "Pattern for tests to run. Forces tests to be run in a single worker.",
" --failed": "Runs tests listed in '.failed-tests'. Forces tests to be run in a single worker.",
"-r --reporter=<reporter>": "The mocha reporter to use.",
" --keepFailed": "Keep tests in .failed-tests even if they pass",
" --light": "Run tests in light mode (fewer verifications, but tests run faster)",
" --dirty": "Run tests without first cleaning test output directories",
" --stackTraceLimit=<limit>": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
" --no-color": "Disables color",
" --timeout=<ms>": "Overrides the default test timeout.",
" --workers=<number>": "The number of parallel workers to use.",
" --built": "Compile using the built version of the compiler.",
};
task("default", series("local"));
task("default").description = "Runs 'local'";
task("help", () => exec("gulp", ["--tasks", "--depth", "1", "--sort-tasks"], { hidePrompt: true }));
task("help").description = "Prints the top-level tasks.";
+798
View File
@@ -0,0 +1,798 @@
// @ts-check
import path from "path";
import fs from "fs";
import del from "del";
import { task } from "hereby";
import _glob from "glob";
import util from "util";
import chalk from "chalk";
import { exec, readJson, getDiffTool, getDirSize, memoize, needsUpdate } from "./scripts/build/utils.mjs";
import { runConsoleTests, refBaseline, localBaseline, refRwcBaseline, localRwcBaseline } from "./scripts/build/tests.mjs";
import { buildProject as realBuildProject, cleanProject, watchProject } from "./scripts/build/projects.mjs";
import { localizationDirectories } from "./scripts/build/localization.mjs";
import cmdLineOptions from "./scripts/build/options.mjs";
import esbuild from "esbuild";
const glob = util.promisify(_glob);
/** @typedef {ReturnType<typeof task>} Task */
void 0;
const copyrightFilename = "CopyrightNotice.txt";
const copyright = memoize(async () => {
const contents = await fs.promises.readFile(copyrightFilename, "utf-8");
return contents.replace(/\r\n/g, "\n");
});
// TODO(jakebailey): This is really gross. If the build is cancelled (i.e. Ctrl+C), the modification will persist.
// Waiting on: https://github.com/microsoft/TypeScript/issues/51164
let currentlyBuilding = 0;
let oldTsconfigBase;
/** @type {typeof realBuildProject} */
const buildProjectWithEmit = async (...args) => {
const tsconfigBasePath = "./src/tsconfig-base.json";
// Not using fs.promises here, to ensure we are synchronous until running the real build.
if (currentlyBuilding === 0) {
oldTsconfigBase = fs.readFileSync(tsconfigBasePath, "utf-8");
fs.writeFileSync(tsconfigBasePath, oldTsconfigBase.replace(`"emitDeclarationOnly": true,`, `"emitDeclarationOnly": false, // DO NOT COMMIT`));
}
currentlyBuilding++;
await realBuildProject(...args);
currentlyBuilding--;
if (currentlyBuilding === 0) {
fs.writeFileSync(tsconfigBasePath, oldTsconfigBase);
}
};
const buildProject = cmdLineOptions.bundle ? realBuildProject : buildProjectWithEmit;
export const buildScripts = task({
name: "scripts",
description: "Builds files in the 'scripts' folder.",
run: () => buildProject("scripts")
});
const libs = memoize(() => {
/** @type {{ libs: string[]; paths: Record<string, string | undefined>; }} */
const libraries = readJson("./src/lib/libs.json");
const libs = libraries.libs.map(lib => {
const relativeSources = ["header.d.ts", lib + ".d.ts"];
const relativeTarget = libraries.paths && libraries.paths[lib] || ("lib." + lib + ".d.ts");
const sources = relativeSources.map(s => path.posix.join("src/lib", s));
const target = `built/local/${relativeTarget}`;
return { target, sources };
});
return libs;
});
export const generateLibs = task({
name: "lib",
description: "Builds the library targets",
run: async () => {
await fs.promises.mkdir("./built/local", { recursive: true });
for (const lib of libs()) {
let output = await copyright();
for (const source of lib.sources) {
const contents = await fs.promises.readFile(source, "utf-8");
// TODO(jakebailey): "\n\n" is for compatibility with our current tests; our test baselines
// are sensitive to the positions of things in the lib files. Eventually remove this,
// or remove lib.d.ts line numbers from our baselines.
output += "\n\n" + contents.replace(/\r\n/g, "\n");
}
await fs.promises.writeFile(lib.target, output);
}
},
});
const diagnosticInformationMapTs = "src/compiler/diagnosticInformationMap.generated.ts";
const diagnosticMessagesJson = "src/compiler/diagnosticMessages.json";
const diagnosticMessagesGeneratedJson = "src/compiler/diagnosticMessages.generated.json";
export const generateDiagnostics = task({
name: "generate-diagnostics",
description: "Generates a diagnostic file in TypeScript based on an input JSON file",
run: async () => {
await exec(process.execPath, ["scripts/processDiagnosticMessages.mjs", diagnosticMessagesJson]);
}
});
const cleanDiagnostics = task({
name: "clean-diagnostics",
description: "Generates a diagnostic file in TypeScript based on an input JSON file",
hiddenFromTaskList: true,
run: () => del([diagnosticInformationMapTs, diagnosticMessagesGeneratedJson]),
});
// Localize diagnostics
/**
* .lcg file is what localization team uses to know what messages to localize.
* The file is always generated in 'enu/diagnosticMessages.generated.json.lcg'
*/
const generatedLCGFile = "built/local/enu/diagnosticMessages.generated.json.lcg";
/**
* The localization target produces the two following transformations:
* 1. 'src\loc\lcl\<locale>\diagnosticMessages.generated.json.lcl' => 'built\local\<locale>\diagnosticMessages.generated.json'
* convert localized resources into a .json file the compiler can understand
* 2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg'
* generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json
*/
const localizationTargets = localizationDirectories
.map(f => `built/local/${f}/diagnosticMessages.generated.json`)
.concat(generatedLCGFile);
const localize = task({
name: "localize",
dependencies: [generateDiagnostics],
run: async () => {
if (needsUpdate(diagnosticMessagesGeneratedJson, generatedLCGFile)) {
return exec(process.execPath, ["scripts/generateLocalizedDiagnosticMessages.mjs", "src/loc/lcl", "built/local", diagnosticMessagesGeneratedJson], { ignoreExitCode: true });
}
}
});
export const buildSrc = task({
name: "build-src",
description: "Builds the src project (all code)",
dependencies: [generateDiagnostics],
run: () => buildProject("src"),
});
export const watchSrc = task({
name: "watch-src",
description: "Watches the src project (all code)",
hiddenFromTaskList: true,
dependencies: [generateDiagnostics],
run: () => watchProject("src"),
});
export const cleanSrc = task({
name: "clean-src",
hiddenFromTaskList: true,
run: () => cleanProject("src"),
});
/**
* @param {string} entrypoint
* @param {string} output
*/
async function runDtsBundler(entrypoint, output) {
await exec(process.execPath, [
"./scripts/dtsBundler.mjs",
"--entrypoint",
entrypoint,
"--output",
output,
]);
}
/**
* @param {string} entrypoint
* @param {string} outfile
* @param {BundlerTaskOptions} [taskOptions]
*
* @typedef BundlerTaskOptions
* @property {string[]} [external]
* @property {boolean} [exportIsTsObject]
* @property {boolean} [treeShaking]
*/
function createBundler(entrypoint, outfile, taskOptions = {}) {
const getOptions = memoize(async () => {
/** @type {esbuild.BuildOptions} */
const options = {
entryPoints: [entrypoint],
banner: { js: await copyright() },
bundle: true,
outfile,
platform: "node",
target: "es2018",
format: "cjs",
sourcemap: "linked",
sourcesContent: false,
treeShaking: taskOptions.treeShaking,
external: [
...(taskOptions.external ?? []),
"source-map-support",
],
logLevel: "warning",
// legalComments: "none", // If we add copyright headers to the source files, uncomment.
plugins: [
{
name: "no-node-modules",
setup: (build) => {
build.onLoad({ filter: /[\\/]node_modules[\\/]/ }, () => {
// Ideally, we'd use "--external:./node_modules/*" here, but that doesn't work; we
// will instead end up with paths to node_modules rather than the package names.
// Instead, we'll return a load error when we see that we're trying to bundle from
// node_modules, then explicitly declare which external dependencies we rely on, which
// ensures that the correct module specifier is kept in the output (the non-wildcard
// form works properly). It also helps us keep tabs on what external dependencies we
// may be importing, which is handy.
//
// See: https://github.com/evanw/esbuild/issues/1958
return {
errors: [{ text: 'Attempted to bundle from node_modules; ensure "external" is set correctly.' }]
};
});
}
},
{
name: "fix-require",
setup: (build) => {
build.onEnd(async () => {
// esbuild converts calls to "require" to "__require"; this function
// calls the real require if it exists, or throws if it does not (rather than
// throwing an error like "require not defined"). But, since we want typescript
// to be consumable by other bundlers, we need to convert these calls back to
// require so our imports are visible again.
//
// The leading spaces are to keep the offsets the same within the files to keep
// source maps working (though this only really matters for the line the require is on).
//
// See: https://github.com/evanw/esbuild/issues/1905
let contents = await fs.promises.readFile(outfile, "utf-8");
contents = contents.replace(/__require\(/g, " require(");
await fs.promises.writeFile(outfile, contents);
});
},
}
]
};
if (taskOptions.exportIsTsObject) {
// We use an IIFE so we can inject the footer, and so that "ts" is global if not loaded as a module.
options.format = "iife";
// Name the variable ts, matching our old big bundle and so we can use the code below.
options.globalName = "ts";
// If we are in a CJS context, export the ts namespace.
options.footer = { js: `\nif (typeof module !== "undefined" && module.exports) { module.exports = ts; }` };
}
return options;
});
return {
build: async () => esbuild.build(await getOptions()),
watch: async () => esbuild.build({ ...await getOptions(), watch: true, logLevel: "info" }),
};
}
let printedWatchWarning = false;
/**
* @param {object} options
* @param {string} options.name
* @param {string} [options.description]
* @param {Task[]} [options.buildDeps]
* @param {string} options.project
* @param {string} options.srcEntrypoint
* @param {string} options.builtEntrypoint
* @param {string} options.output
* @param {Task[]} [options.mainDeps]
* @param {BundlerTaskOptions} [options.bundlerOptions]
*/
function entrypointBuildTask(options) {
const build = task({
name: `build-${options.name}`,
dependencies: options.buildDeps,
run: () => buildProject(options.project),
});
const bundler = createBundler(options.srcEntrypoint, options.output, options.bundlerOptions);
// If we ever need to bundle our own output, change this to depend on build
// and run esbuild on builtEntrypoint.
const bundle = task({
name: `bundle-${options.name}`,
dependencies: options.buildDeps,
run: () => bundler.build(),
});
/**
* Writes a CJS module that reexports another CJS file. E.g. given
* `options.builtEntrypoint = "./built/local/tsc/tsc.js"` and
* `options.output = "./built/local/tsc.js"`, this will create a file
* named "./built/local/tsc.js" containing:
*
* ```
* module.exports = require("./tsc/tsc.js")
* ```
*/
const shim = task({
name: `shim-${options.name}`,
run: async () => {
const outDir = path.dirname(options.output);
await fs.promises.mkdir(outDir, { recursive: true });
const moduleSpecifier = path.relative(outDir, options.builtEntrypoint);
await fs.promises.writeFile(options.output, `module.exports = require("./${moduleSpecifier}")`);
},
});
const main = task({
name: options.name,
description: options.description,
dependencies: (options.mainDeps ?? []).concat(cmdLineOptions.bundle ? [bundle] : [build, shim]),
});
const watch = task({
name: `watch-${options.name}`,
hiddenFromTaskList: true, // This is best effort.
dependencies: (options.buildDeps ?? []).concat(options.mainDeps ?? []).concat(cmdLineOptions.bundle ? [] : [shim]),
run: () => {
// These watch functions return promises that resolve once watch mode has started,
// allowing them to operate as regular tasks, while creating unresolved promises
// in the background that keep the process running after all tasks have exited.
if (!printedWatchWarning) {
console.error(chalk.yellowBright("Warning: watch mode is incomplete and may not work as expected. Use at your own risk."));
printedWatchWarning = true;
}
if (!cmdLineOptions.bundle) {
return watchProject(options.project);
}
return bundler.watch();
}
});
return { build, bundle, shim, main, watch };
}
const { main: tsc, watch: watchTsc } = entrypointBuildTask({
name: "tsc",
description: "Builds the command-line compiler",
buildDeps: [generateDiagnostics],
project: "src/tsc",
srcEntrypoint: "./src/tsc/tsc.ts",
builtEntrypoint: "./built/local/tsc/tsc.js",
output: "./built/local/tsc.js",
mainDeps: [generateLibs],
});
export { tsc, watchTsc };
const { main: services, build: buildServices, watch: watchServices } = entrypointBuildTask({
name: "services",
description: "Builds the typescript.js library",
buildDeps: [generateDiagnostics],
project: "src/typescript",
srcEntrypoint: "./src/typescript/typescript.ts",
builtEntrypoint: "./built/local/typescript/typescript.js",
output: "./built/local/typescript.js",
mainDeps: [generateLibs],
bundlerOptions: { exportIsTsObject: true },
});
export { services, watchServices };
export const dtsServices = task({
name: "dts-services",
description: "Bundles typescript.d.ts",
dependencies: [buildServices],
run: async () => {
if (needsUpdate("./built/local/typescript/tsconfig.tsbuildinfo", ["./built/local/typescript.d.ts", "./built/local/typescript.internal.d.ts"])) {
runDtsBundler("./built/local/typescript/typescript.d.ts", "./built/local/typescript.d.ts");
}
},
});
const { main: tsserver, watch: watchTsserver } = entrypointBuildTask({
name: "tsserver",
description: "Builds the language server",
buildDeps: [generateDiagnostics],
project: "src/tsserver",
srcEntrypoint: "./src/tsserver/server.ts",
builtEntrypoint: "./built/local/tsserver/server.js",
output: "./built/local/tsserver.js",
mainDeps: [generateLibs],
// Even though this seems like an exectuable, so could be the default CJS,
// this is used in the browser too. Do the same thing that we do for our
// libraries and generate an IIFE with name `ts`, as to not pollute the global
// scope.
bundlerOptions: { exportIsTsObject: true },
});
export { tsserver, watchTsserver };
export const min = task({
name: "min",
description: "Builds only tsc and tsserver",
dependencies: [tsc, tsserver],
});
export const watchMin = task({
name: "watch-min",
description: "Watches only tsc and tsserver",
hiddenFromTaskList: true,
dependencies: [watchTsc, watchTsserver],
});
const { main: lssl, build: buildLssl, watch: watchLssl } = entrypointBuildTask({
name: "lssl",
description: "Builds language service server library",
buildDeps: [generateDiagnostics],
project: "src/tsserverlibrary",
srcEntrypoint: "./src/tsserverlibrary/tsserverlibrary.ts",
builtEntrypoint: "./built/local/tsserverlibrary/tsserverlibrary.js",
output: "./built/local/tsserverlibrary.js",
mainDeps: [generateLibs],
bundlerOptions: { exportIsTsObject: true },
});
export { lssl, watchLssl };
export const dtsLssl = task({
name: "dts-lssl",
description: "Bundles tsserverlibrary.d.ts",
dependencies: [buildLssl],
run: async () => {
if (needsUpdate("./built/local/tsserverlibrary/tsconfig.tsbuildinfo", ["./built/local/tsserverlibrary.d.ts", "./built/local/tsserverlibrary.internal.d.ts"])) {
await runDtsBundler("./built/local/tsserverlibrary/tsserverlibrary.d.ts", "./built/local/tsserverlibrary.d.ts");
}
}
});
export const dts = task({
name: "dts",
dependencies: [dtsServices, dtsLssl],
});
const testRunner = "./built/local/run.js";
const { main: tests, watch: watchTests } = entrypointBuildTask({
name: "tests",
description: "Builds the test infrastructure",
buildDeps: [generateDiagnostics],
project: "src/testRunner",
srcEntrypoint: "./src/testRunner/_namespaces/Harness.ts",
builtEntrypoint: "./built/local/testRunner/runner.js",
output: testRunner,
mainDeps: [generateLibs],
bundlerOptions: {
// Ensure we never drop any dead code, which might be helpful while debugging.
treeShaking: false,
// These are directly imported via import statements and should not be bundled.
external: [
"chai",
"del",
"diff",
"mocha",
"ms",
],
},
});
export { tests, watchTests };
export const runEslintRulesTests = task({
name: "run-eslint-rules-tests",
description: "Runs the eslint rule tests",
run: () => runConsoleTests("scripts/eslint/tests", "mocha-fivemat-progress-reporter", /*runInParallel*/ false),
});
export const lint = task({
name: "lint",
description: "Runs eslint on the compiler and scripts sources.",
run: async () => {
const folder = ".";
const formatter = cmdLineOptions.ci ? "stylish" : "autolinkable-stylish";
const args = [
"node_modules/eslint/bin/eslint",
"--cache",
"--cache-location", `${folder}/.eslintcache`,
"--format", formatter,
];
if (cmdLineOptions.fix) {
args.push("--fix");
}
args.push(folder);
console.log(`Linting: ${args.join(" ")}`);
return exec(process.execPath, args);
}
});
const { main: cancellationToken, watch: watchCancellationToken } = entrypointBuildTask({
name: "cancellation-token",
project: "src/cancellationToken",
srcEntrypoint: "./src/cancellationToken/cancellationToken.ts",
builtEntrypoint: "./built/local/cancellationToken/cancellationToken.js",
output: "./built/local/cancellationToken.js",
});
const { main: typingsInstaller, watch: watchTypingsInstaller } = entrypointBuildTask({
name: "typings-installer",
buildDeps: [generateDiagnostics],
project: "src/typingsInstaller",
srcEntrypoint: "./src/typingsInstaller/nodeTypingsInstaller.ts",
builtEntrypoint: "./built/local/typingsInstaller/nodeTypingsInstaller.js",
output: "./built/local/typingsInstaller.js",
});
const { main: watchGuard, watch: watchWatchGuard } = entrypointBuildTask({
name: "watch-guard",
project: "src/watchGuard",
srcEntrypoint: "./src/watchGuard/watchGuard.ts",
builtEntrypoint: "./built/local/watchGuard/watchGuard.js",
output: "./built/local/watchGuard.js",
});
export const generateTypesMap = task({
name: "generate-types-map",
run: async () => {
const source = "src/server/typesMap.json";
const target = "built/local/typesMap.json";
const contents = await fs.promises.readFile(source, "utf-8");
JSON.parse(contents); // Validates that the JSON parses.
await fs.promises.writeFile(target, contents);
}
});
// Drop a copy of diagnosticMessages.generated.json into the built/local folder. This allows
// it to be synced to the Azure DevOps repo, so that it can get picked up by the build
// pipeline that generates the localization artifacts that are then fed into the translation process.
const builtLocalDiagnosticMessagesGeneratedJson = "built/local/diagnosticMessages.generated.json";
const copyBuiltLocalDiagnosticMessages = task({
name: "copy-built-local-diagnostic-messages",
dependencies: [generateDiagnostics],
run: async () => {
const contents = await fs.promises.readFile(diagnosticMessagesGeneratedJson, "utf-8");
JSON.parse(contents); // Validates that the JSON parses.
await fs.promises.writeFile(builtLocalDiagnosticMessagesGeneratedJson, contents);
}
});
export const otherOutputs = task({
name: "other-outputs",
description: "Builds miscelaneous scripts and documents distributed with the LKG",
dependencies: [cancellationToken, typingsInstaller, watchGuard, generateTypesMap, copyBuiltLocalDiagnosticMessages],
});
export const watchOtherOutputs = task({
name: "watch-other-outputs",
description: "Builds miscelaneous scripts and documents distributed with the LKG",
hiddenFromTaskList: true,
dependencies: [watchCancellationToken, watchTypingsInstaller, watchWatchGuard, generateTypesMap, copyBuiltLocalDiagnosticMessages],
});
export const local = task({
name: "local",
description: "Builds the full compiler and services",
dependencies: [localize, tsc, tsserver, services, lssl, otherOutputs, dts],
});
export default local;
export const watchLocal = task({
name: "watch-local",
description: "Watches the full compiler and services",
hiddenFromTaskList: true,
dependencies: [localize, watchTsc, watchTsserver, watchServices, watchLssl, watchOtherOutputs, dts],
});
export const runTests = task({
name: "runtests",
description: "Runs the tests using the built run.js file.",
dependencies: [tests, generateLibs, dts],
run: () => runConsoleTests(testRunner, "mocha-fivemat-progress-reporter", /*runInParallel*/ false),
});
// task("runtests").flags = {
// "-t --tests=<regex>": "Pattern for tests to run.",
// " --failed": "Runs tests listed in '.failed-tests'.",
// "-r --reporter=<reporter>": "The mocha reporter to use.",
// "-i --break": "Runs tests in inspector mode (NodeJS 8 and later)",
// " --keepFailed": "Keep tests in .failed-tests even if they pass",
// " --light": "Run tests in light mode (fewer verifications, but tests run faster)",
// " --dirty": "Run tests without first cleaning test output directories",
// " --stackTraceLimit=<limit>": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
// " --no-color": "Disables color",
// " --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)",
// };
export const runTestsParallel = task({
name: "runtests-parallel",
description: "Runs all the tests in parallel using the built run.js file.",
dependencies: [tests, generateLibs, dts],
run: () => runConsoleTests(testRunner, "min", /*runInParallel*/ cmdLineOptions.workers > 1),
});
// task("runtests-parallel").flags = {
// " --light": "Run tests in light mode (fewer verifications, but tests run faster).",
// " --keepFailed": "Keep tests in .failed-tests even if they pass.",
// " --dirty": "Run tests without first cleaning test output directories.",
// " --stackTraceLimit=<limit>": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
// " --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.",
// " --shards": "Total number of shards running tests (default: 1)",
// " --shardId": "1-based ID of this shard (default: 1)",
// };
export const testBrowserIntegration = task({
name: "test-browser-integration",
description: "Runs scripts/browserIntegrationTest.mjs which tests that typescript.js loads in a browser",
dependencies: [services],
run: () => exec(process.execPath, ["scripts/browserIntegrationTest.mjs"]),
});
export const diff = task({
name: "diff",
description: "Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment variable",
run: () => exec(getDiffTool(), [refBaseline, localBaseline], { ignoreExitCode: true, waitForExit: false }),
});
export const diffRwc = task({
name: "diff-rwc",
description: "Diffs the RWC baselines using the diff tool specified by the 'DIFF' environment variable",
run: () => exec(getDiffTool(), [refRwcBaseline, localRwcBaseline], { ignoreExitCode: true, waitForExit: false }),
});
/**
* @param {string} localBaseline Path to the local copy of the baselines
* @param {string} refBaseline Path to the reference copy of the baselines
*/
function baselineAcceptTask(localBaseline, refBaseline) {
/**
* @param {string} p
*/
function localPathToRefPath(p) {
const relative = path.relative(localBaseline, p);
return path.join(refBaseline, relative);
}
return async () => {
const toCopy = await glob(`${localBaseline}/**`, { nodir: true, ignore: `${localBaseline}/**/*.delete` });
for (const p of toCopy) {
const out = localPathToRefPath(p);
await fs.promises.mkdir(path.dirname(out), { recursive: true });
await fs.promises.copyFile(p, out);
}
const toDelete = await glob(`${localBaseline}/**/*.delete`, { nodir: true });
for (const p of toDelete) {
const out = localPathToRefPath(p);
await fs.promises.rm(out);
}
};
}
export const baselineAccept = task({
name: "baseline-accept",
description: "Makes the most recent test results the new baseline, overwriting the old baseline",
run: baselineAcceptTask(localBaseline, refBaseline),
});
export const baselineAcceptRwc = task({
name: "baseline-accept-rwc",
description: "Makes the most recent rwc test results the new baseline, overwriting the old baseline",
run: baselineAcceptTask(localRwcBaseline, refRwcBaseline),
});
// TODO(rbuckton): Determine if we still need this task. Depending on a relative
// path here seems like a bad idea.
export const updateSublime = task({
name: "update-sublime",
description: "Updates the sublime plugin's tsserver",
dependencies: [tsserver],
run: async () => {
for (const file of ["built/local/tsserver.js", "built/local/tsserver.js.map"]) {
await fs.promises.copyFile(file, path.resolve("../TypeScript-Sublime-Plugin/tsserver/", path.basename(file)));
}
}
});
// TODO(rbuckton): Should the path to DefinitelyTyped be configurable via an environment variable?
export const importDefinitelyTypedTests = task({
name: "importDefinitelyTypedTests",
description: "Runs the importDefinitelyTypedTests script to copy DT's tests to the TS-internal RWC tests",
run: () => exec(process.execPath, ["scripts/importDefinitelyTypedTests.mjs", "./", "../DefinitelyTyped"]),
});
export const produceLKG = task({
name: "LKG",
description: "Makes a new LKG out of the built js files",
dependencies: [localize, tsc, tsserver, services, lssl, otherOutputs, dts],
run: async () => {
if (!cmdLineOptions.bundle) {
throw new Error("LKG cannot be created when --bundle=false");
}
const expectedFiles = [
"built/local/cancellationToken.js",
"built/local/tsc.js",
"built/local/tsserver.js",
"built/local/tsserverlibrary.js",
"built/local/tsserverlibrary.d.ts",
"built/local/typescript.js",
"built/local/typescript.d.ts",
"built/local/typingsInstaller.js",
"built/local/watchGuard.js",
].concat(libs().map(lib => lib.target));
const missingFiles = expectedFiles
.concat(localizationTargets)
.filter(f => !fs.existsSync(f));
if (missingFiles.length > 0) {
throw new Error("Cannot replace the LKG unless all built targets are present in directory 'built/local/'. The following files are missing:\n" + missingFiles.join("\n"));
}
const sizeBefore = getDirSize("lib");
await exec(process.execPath, ["scripts/produceLKG.mjs"]);
const sizeAfter = getDirSize("lib");
if (sizeAfter > (sizeBefore * 1.10)) {
throw new Error("The lib folder increased by 10% or more. This likely indicates a bug.");
}
}
});
export const lkg = task({
name: "lkg",
hiddenFromTaskList: true,
dependencies: [produceLKG],
});
export const generateSpec = task({
name: "generate-spec",
description: "Generates a Markdown version of the Language Specification",
hiddenFromTaskList: true,
run: () => exec("cscript", ["//nologo", "scripts/word2md.mjs", path.resolve("doc/TypeScript Language Specification - ARCHIVED.docx"), path.resolve("doc/spec-ARCHIVED.md")]),
});
export const cleanBuilt = task({
name: "clean-built",
hiddenFromTaskList: true,
run: () => del("built"),
});
export const clean = task({
name: "clean",
description: "Cleans build outputs",
dependencies: [cleanBuilt, cleanDiagnostics],
});
export const configureNightly = task({
name: "configure-nightly",
description: "Runs scripts/configurePrerelease.mjs to prepare a build for nightly publishing",
run: () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "dev", "package.json", "src/compiler/corePublic.ts"]),
});
export const configureInsiders = task({
name: "configure-insiders",
description: "Runs scripts/configurePrerelease.mjs to prepare a build for insiders publishing",
run: () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "insiders", "package.json", "src/compiler/corePublic.ts"]),
});
export const configureExperimental = task({
name: "configure-experimental",
description: "Runs scripts/configurePrerelease.mjs to prepare a build for experimental publishing",
run: () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "experimental", "package.json", "src/compiler/corePublic.ts"]),
});
export const help = task({
name: "help",
description: "Prints the top-level tasks.",
hiddenFromTaskList: true,
run: () => exec("hereby", ["--tasks"], { hidePrompt: true }),
});
+1 -1
View File
@@ -2,4 +2,4 @@
**These files are not meant to be edited by hand.**
If you need to make modifications, the respective files should be changed within the repository's top-level `src` directory.
Running `gulp LKG` will then appropriately update the files in this directory.
Running `hereby LKG` will then appropriately update the files in this directory.
+1106 -8877
View File
File diff suppressed because it is too large Load Diff
+12 -31
View File
@@ -41,24 +41,15 @@
"devDependencies": {
"@octokit/rest": "latest",
"@types/chai": "latest",
"@types/fancy-log": "^2.0.0",
"@types/fs-extra": "^9.0.13",
"@types/glob": "latest",
"@types/gulp": "^4.0.9",
"@types/gulp-concat": "latest",
"@types/gulp-newer": "latest",
"@types/gulp-rename": "latest",
"@types/gulp-sourcemaps": "latest",
"@types/merge2": "latest",
"@types/microsoft__typescript-etw": "latest",
"@types/minimist": "latest",
"@types/mkdirp": "latest",
"@types/mocha": "latest",
"@types/ms": "latest",
"@types/node": "latest",
"@types/source-map-support": "latest",
"@types/which": "^2.0.1",
"@types/xml2js": "^0.4.11",
"@typescript-eslint/eslint-plugin": "^5.33.1",
"@typescript-eslint/parser": "^5.33.1",
"@typescript-eslint/utils": "^5.33.1",
@@ -67,47 +58,37 @@
"chalk": "^4.1.2",
"del": "^6.1.1",
"diff": "^5.1.0",
"esbuild": "^0.15.13",
"eslint": "^8.22.0",
"eslint-formatter-autolinkable-stylish": "^1.2.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsdoc": "^39.3.6",
"eslint-plugin-local": "^1.0.0",
"eslint-plugin-no-null": "^1.0.2",
"fancy-log": "latest",
"fast-xml-parser": "^4.0.11",
"fs-extra": "^9.1.0",
"glob": "latest",
"gulp": "^4.0.2",
"gulp-concat": "latest",
"gulp-insert": "latest",
"gulp-newer": "latest",
"gulp-rename": "latest",
"gulp-sourcemaps": "latest",
"merge2": "latest",
"hereby": "^1.6.4",
"jsonc-parser": "^3.2.0",
"minimist": "latest",
"mkdirp": "latest",
"mocha": "latest",
"mocha-fivemat-progress-reporter": "latest",
"ms": "^2.1.3",
"node-fetch": "^3.2.10",
"source-map-support": "latest",
"typescript": "^4.8.4",
"vinyl": "latest",
"which": "^2.0.2",
"xml2js": "^0.4.23"
},
"overrides": {
"es5-ext": "0.10.53"
"which": "^2.0.2"
},
"scripts": {
"test": "gulp runtests-parallel --light=false",
"test:eslint-rules": "gulp run-eslint-rules-tests",
"test": "hereby runtests-parallel --light=false",
"test:eslint-rules": "hereby run-eslint-rules-tests",
"build": "npm run build:compiler && npm run build:tests",
"build:compiler": "gulp local",
"build:tests": "gulp tests",
"build:compiler": "hereby local",
"build:tests": "hereby tests",
"start": "node lib/tsc",
"clean": "gulp clean",
"gulp": "gulp",
"lint": "gulp lint",
"clean": "hereby clean",
"gulp": "hereby",
"lint": "hereby lint",
"setup-hooks": "node scripts/link-hooks.mjs"
},
"browser": {
+1 -1
View File
@@ -26,4 +26,4 @@ export function findUpFile(name) {
/** @type {string | undefined} */
let findUpRootCache;
export const findUpRoot = () => findUpRootCache || (findUpRootCache = dirname(findUpFile("Gulpfile.mjs")));
export const findUpRoot = () => findUpRootCache || (findUpRootCache = dirname(findUpFile("Herebyfile.mjs")));
+1
View File
@@ -0,0 +1 @@
export const localizationDirectories = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-br", "ru", "tr", "zh-cn", "zh-tw"].map(f => f.toLowerCase());
+4 -1
View File
@@ -4,7 +4,7 @@ import os from "os";
const ci = ["1", "true"].includes(process.env.CI ?? "");
const parsed = minimist(process.argv.slice(2), {
boolean: ["dirty", "light", "colors", "lkg", "soft", "fix", "failed", "keepFailed", "force", "built", "ci"],
boolean: ["dirty", "light", "colors", "lkg", "soft", "fix", "failed", "keepFailed", "force", "built", "ci", "bundle"],
string: ["browser", "tests", "break", "host", "reporter", "stackTraceLimit", "timeout", "shards", "shardId"],
alias: {
/* eslint-disable quote-props */
@@ -39,6 +39,7 @@ const parsed = minimist(process.argv.slice(2), {
dirty: false,
built: false,
ci,
bundle: true
}
});
@@ -77,5 +78,7 @@ export default options;
* @property {boolean} ci
* @property {string} shards
* @property {string} shardId
* @property {string} break
* @property {boolean} bundle
*/
void 0;
-61
View File
@@ -1,61 +0,0 @@
import stream from "stream";
import ts from "../../lib/typescript.js";
import fs from "fs";
import { base64VLQFormatEncode } from "./sourcemaps.mjs";
/**
* @param {string | ((file: import("vinyl")) => string)} data
*/
export function prepend(data) {
return new stream.Transform({
objectMode: true,
/**
* @param {string | Buffer | import("vinyl")} input
* @param {(error: Error | null, data?: any) => void} cb
*/
transform(input, _, cb) {
if (typeof input === "string" || Buffer.isBuffer(input)) return cb(new Error("Only Vinyl files are supported."));
if (!input.isBuffer()) return cb(new Error("Streams not supported."));
try {
const output = input.clone();
const prependContent = typeof data === "function" ? data(input) : data;
output.contents = Buffer.concat([Buffer.from(prependContent, "utf8"), input.contents]);
if (input.sourceMap) {
if (typeof input.sourceMap === "string") input.sourceMap = /**@type {import("./sourcemaps.mjs").RawSourceMap}*/(JSON.parse(input.sourceMap));
const lineStarts = /**@type {*}*/(ts).computeLineStarts(prependContent);
let prependMappings = "";
for (let i = 1; i < lineStarts.length; i++) {
prependMappings += ";";
}
const offset = prependContent.length - lineStarts[lineStarts.length - 1];
if (offset > 0) {
prependMappings += base64VLQFormatEncode(offset) + ",";
}
output.sourceMap = {
version: input.sourceMap.version,
file: input.sourceMap.file,
sources: input.sourceMap.sources,
sourceRoot: input.sourceMap.sourceRoot,
mappings: prependMappings + input.sourceMap.mappings,
names: input.names,
sourcesContent: input.sourcesContent
};
}
// eslint-disable-next-line local/boolean-trivia, no-null/no-null
return cb(null, output);
}
catch (e) {
return cb(/** @type {Error} */(e));
}
}
});
}
/**
* @param {string | ((file: import("vinyl")) => string)} file
*/
export function prependFile(file) {
const data = typeof file === "string" ? fs.readFileSync(file, "utf8") :
(/** @type {import("vinyl")} */ vinyl) => fs.readFileSync(file(vinyl), "utf8");
return prepend(data);
}
+22 -34
View File
@@ -1,69 +1,57 @@
import { exec, Debouncer } from "./utils.mjs";
import { resolve } from "path";
import { findUpRoot } from "./findUpDir.mjs";
import assert from "assert";
import cmdLineOptions from "./options.mjs";
class ProjectQueue {
/**
* @param {(projects: string[], lkg: boolean, force: boolean) => Promise<any>} action
* @param {(projects: string[]) => Promise<any>} action
*/
constructor(action) {
/** @type {{ lkg: boolean, force: boolean, projects?: string[], debouncer: Debouncer }[]} */
this._debouncers = [];
this._action = action;
/** @type {string[] | undefined} */
this._projects = undefined;
this._debouncer = new Debouncer(100, async () => {
const projects = this._projects;
if (projects) {
this._projects = undefined;
await action(projects);
}
});
}
/**
* @param {string} project
* @param {{ lkg?: boolean; force?: boolean; }} options
*/
enqueue(project, { lkg = true, force = false } = {}) {
let entry = this._debouncers.find(entry => entry.lkg === lkg && entry.force === force);
if (!entry) {
const debouncer = new Debouncer(100, async () => {
assert(entry);
const projects = entry.projects;
if (projects) {
entry.projects = undefined;
await this._action(projects, lkg, force);
}
});
this._debouncers.push(entry = { lkg, force, debouncer });
}
if (!entry.projects) entry.projects = [];
entry.projects.push(project);
return entry.debouncer.enqueue();
enqueue(project) {
if (!this._projects) this._projects = [];
this._projects.push(project);
return this._debouncer.enqueue();
}
}
const execTsc = (/** @type {boolean} */ lkg, /** @type {string[]} */ ...args) =>
const execTsc = (/** @type {string[]} */ ...args) =>
exec(process.execPath,
[resolve(findUpRoot(), lkg ? "./lib/tsc" : "./built/local/tsc"),
[resolve(findUpRoot(), cmdLineOptions.lkg ? "./lib/tsc.js" : "./built/local/tsc.js"),
"-b", ...args],
{ hidePrompt: true });
const projectBuilder = new ProjectQueue((projects, lkg, force) => execTsc(lkg, ...(force ? ["--force"] : []), ...projects));
const projectBuilder = new ProjectQueue((projects) => execTsc(...projects));
/**
* @param {string} project
* @param {object} options
* @param {boolean} [options.lkg=true]
* @param {boolean} [options.force=false]
*/
export const buildProject = (project, { lkg, force } = {}) => projectBuilder.enqueue(project, { lkg, force });
export const buildProject = (project) => projectBuilder.enqueue(project);
const projectCleaner = new ProjectQueue((projects, lkg) => execTsc(lkg, "--clean", ...projects));
const projectCleaner = new ProjectQueue((projects) => execTsc("--clean", ...projects));
/**
* @param {string} project
*/
export const cleanProject = (project) => projectCleaner.enqueue(project);
const projectWatcher = new ProjectQueue((projects) => execTsc(/*lkg*/ true, "--watch", ...projects));
const projectWatcher = new ProjectQueue((projects) => execTsc("--watch", "--preserveWatchOutput", ...projects));
/**
* @param {string} project
* @param {object} options
* @param {boolean} [options.lkg=true]
*/
export const watchProject = (project, { lkg } = {}) => projectWatcher.enqueue(project, { lkg });
export const watchProject = (project) => projectWatcher.enqueue(project);
-48
View File
@@ -1,48 +0,0 @@
/**
* @param {string} message
* @returns {never}
*/
function fail(message) {
throw new Error(message);
}
/**
* @param {number} value
*/
function base64FormatEncode(value) {
return value < 0 ? fail("Invalid value") :
value < 26 ? 0x41 /*A*/ + value :
value < 52 ? 0x61 /*a*/ + value - 26 :
value < 62 ? 0x30 /*0*/ + value - 52 :
value === 62 ? 0x2B /*+*/ :
value === 63 ? 0x2F /*/*/ :
fail("Invalid value");
}
/**
* @param {number} value
*/
export function base64VLQFormatEncode(value) {
if (value < 0) {
value = ((-value) << 1) + 1;
}
else {
value = value << 1;
}
// Encode 5 bits at a time starting from least significant bits
let result = "";
do {
let currentDigit = value & 31; // 11111
value = value >> 5;
if (value > 0) {
// There are still more digits to decode, set the msb (6th bit)
currentDigit = currentDigit | 32;
}
result += String.fromCharCode(base64FormatEncode(currentDigit));
} while (value > 0);
return result;
}
/** @typedef {object} RawSourceMap */
+5 -19
View File
@@ -2,8 +2,6 @@ import del from "del";
import fs from "fs";
import os from "os";
import path from "path";
import mkdirP from "mkdirp";
import log from "fancy-log";
import cmdLineOptions from "./options.mjs";
import { exec } from "./utils.mjs";
import { findUpFile, findUpRoot } from "./findUpDir.mjs";
@@ -19,9 +17,8 @@ export const localTest262Baseline = "internal/baselines/test262/local";
* @param {string} runJs
* @param {string} defaultReporter
* @param {boolean} runInParallel
* @param {boolean} _watchMode
*/
export async function runConsoleTests(runJs, defaultReporter, runInParallel, _watchMode) {
export async function runConsoleTests(runJs, defaultReporter, runInParallel) {
let testTimeout = cmdLineOptions.timeout;
const tests = cmdLineOptions.tests;
const inspect = cmdLineOptions.break || cmdLineOptions.inspect;
@@ -122,17 +119,6 @@ export async function runConsoleTests(runJs, defaultReporter, runInParallel, _wa
errorStatus = exitCode;
error = new Error(`Process exited with status code ${errorStatus}.`);
}
else if (cmdLineOptions.ci && runJs.startsWith("built")) {
// finally, do a sanity check and build the compiler with the built version of itself
log.info("Starting sanity check build...");
// Cleanup everything except lint rules (we'll need those later and would rather not waste time rebuilding them)
await exec("gulp", ["clean-tsc", "clean-services", "clean-tsserver", "clean-lssl", "clean-tests"]);
const { exitCode } = await exec("gulp", ["local", "--lkg=false"]);
if (exitCode !== 0) {
errorStatus = exitCode;
error = new Error(`Sanity check build process exited with status code ${errorStatus}.`);
}
}
}
catch (e) {
errorStatus = undefined;
@@ -153,12 +139,12 @@ export async function runConsoleTests(runJs, defaultReporter, runInParallel, _wa
export async function cleanTestDirs() {
await del([localBaseline, localRwcBaseline]);
mkdirP.sync(localRwcBaseline);
mkdirP.sync(localBaseline);
await fs.promises.mkdir(localRwcBaseline, { recursive: true });
await fs.promises.mkdir(localBaseline, { recursive: true });
}
/**
* used to pass data from gulp command line directly to run.js
* used to pass data from command line directly to run.js
* @param {string} tests
* @param {string} runners
* @param {boolean} light
@@ -184,7 +170,7 @@ export function writeTestConfigFile(tests, runners, light, taskConfigsFolder, wo
shards,
shardId
});
log.info("Running tests with config: " + testConfigContents);
console.info("Running tests with config: " + testConfigContents);
fs.writeFileSync("test.config", testConfigContents);
}
+21 -121
View File
@@ -1,18 +1,12 @@
/* eslint-disable no-restricted-globals */
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="../types/ambient.d.ts" />
import fs from "fs";
import path from "path";
import log from "fancy-log";
import del from "del";
import File from "vinyl";
import ts from "../../lib/typescript.js";
import chalk from "chalk";
import which from "which";
import { spawn } from "child_process";
import { Duplex } from "stream";
import assert from "assert";
import JSONC from "jsonc-parser";
/**
* Executes the provided command once with the supplied arguments.
@@ -29,7 +23,7 @@ export async function exec(cmd, args, options = {}) {
return /**@type {Promise<{exitCode?: number}>}*/(new Promise((resolve, reject) => {
const { ignoreExitCode, waitForExit = true } = options;
if (!options.hidePrompt) log(`> ${chalk.green(cmd)} ${args.join(" ")}`);
if (!options.hidePrompt) console.log(`> ${chalk.green(cmd)} ${args.join(" ")}`);
const proc = spawn(which.sync(cmd), args, { stdio: waitForExit ? "inherit" : "ignore" });
if (waitForExit) {
proc.on("exit", exitCode => {
@@ -52,48 +46,13 @@ export async function exec(cmd, args, options = {}) {
}));
}
/**
* @param {ts.Diagnostic[]} diagnostics
* @param {{ cwd?: string, pretty?: boolean }} [options]
*/
function formatDiagnostics(diagnostics, options) {
return options && options.pretty
? ts.formatDiagnosticsWithColorAndContext(diagnostics, getFormatDiagnosticsHost(options && options.cwd))
: ts.formatDiagnostics(diagnostics, getFormatDiagnosticsHost(options && options.cwd));
}
/**
* @param {ts.Diagnostic[]} diagnostics
* @param {{ cwd?: string }} [options]
*/
function reportDiagnostics(diagnostics, options) {
log(formatDiagnostics(diagnostics, { cwd: options && options.cwd, pretty: process.stdout.isTTY }));
}
/**
* @param {string | undefined} cwd
* @returns {ts.FormatDiagnosticsHost}
*/
function getFormatDiagnosticsHost(cwd) {
return {
getCanonicalFileName: fileName => fileName,
getCurrentDirectory: () => cwd ?? process.cwd(),
getNewLine: () => ts.sys.newLine,
};
}
/**
* Reads JSON data with optional comments using the LKG TypeScript compiler
* @param {string} jsonPath
*/
export function readJson(jsonPath) {
const jsonText = fs.readFileSync(jsonPath, "utf8");
const result = ts.parseConfigFileTextToJson(jsonPath, jsonText);
if (result.error) {
reportDiagnostics([result.error]);
throw new Error("An error occurred during parse.");
}
return result.config;
return JSONC.parse(jsonText);
}
/**
@@ -167,7 +126,7 @@ export function needsUpdate(source, dest) {
export function getDiffTool() {
const program = process.env.DIFF;
if (!program) {
log.warn("Add the 'DIFF' environment variable to the path of the program you want to use.");
console.warn("Add the 'DIFF' environment variable to the path of the program you want to use.");
process.exit(1);
}
return program;
@@ -191,82 +150,6 @@ export function getDirSize(root) {
.reduce((acc, num) => acc + num, 0);
}
/**
* @param {string | ((file: File) => string) | { cwd?: string }} [dest]
* @param {{ cwd?: string }} [opts]
*/
export function rm(dest, opts) {
if (dest && typeof dest === "object") {
opts = dest;
dest = undefined;
}
let failed = false;
const cwd = path.resolve(opts && opts.cwd || process.cwd());
/** @type {{ file: File, deleted: boolean, promise: Promise<any>, cb: Function }[]} */
const pending = [];
const processDeleted = () => {
if (failed) return;
while (pending.length && pending[0].deleted) {
const fileAndCallback = pending.shift();
assert(fileAndCallback);
const { file, cb } = fileAndCallback;
duplex.push(file);
cb();
}
};
const duplex = new Duplex({
objectMode: true,
/**
* @param {string|Buffer|File} file
*/
write(file, _, cb) {
if (failed) return;
if (typeof file === "string" || Buffer.isBuffer(file)) return cb(new Error("Only Vinyl files are supported."));
const basePath = typeof dest === "string" ? path.resolve(cwd, dest) :
typeof dest === "function" ? path.resolve(cwd, dest(file)) :
file.base;
const filePath = path.resolve(basePath, file.relative);
file.cwd = cwd;
file.base = basePath;
file.path = filePath;
const entry = {
file,
deleted: false,
cb,
promise: del(file.path).then(() => {
entry.deleted = true;
processDeleted();
}, err => {
failed = true;
pending.length = 0;
cb(err);
})
};
pending.push(entry);
},
final(cb) {
// eslint-disable-next-line no-null/no-null
const endThenCb = () => (duplex.push(null), cb()); // signal end of read queue
processDeleted();
if (pending.length) {
Promise
.all(pending.map(entry => entry.promise))
.then(() => processDeleted())
.then(() => endThenCb(), endThenCb);
return;
}
endThenCb();
},
read() {
}
});
return duplex;
}
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
@@ -317,3 +200,20 @@ export class Debouncer {
}
}
}
const unset = Symbol();
/**
* @template T
* @param {() => T} fn
* @returns {() => T}
*/
export function memoize(fn) {
/** @type {T | unset} */
let value = unset;
return () => {
if (value === unset) {
value = fn();
}
return value;
};
}
-260
View File
@@ -1,260 +0,0 @@
import ts from "../lib/typescript.js";
import path from "path";
import assert from "assert";
/**
*
* @param {string} s
* @param {string} suffix
* @returns {boolean}
*/
function endsWith(s, suffix) {
return s.lastIndexOf(suffix, s.length - suffix.length) !== -1;
}
/**
* @param {ts.EnumDeclaration} declaration
* @returns {boolean}
*/
function isStringEnum(declaration) {
return !!declaration.members.length && declaration.members.every(m => !!m.initializer && m.initializer.kind === ts.SyntaxKind.StringLiteral);
}
class DeclarationsWalker {
/**
* @type {ts.Type[]}
* @private
*/
visitedTypes = [];
/**
* @type {string}
* @private
*/
text = "";
/**
* @type {ts.Type[]}
* @private
*/
removedTypes = [];
/**
* @param {ts.TypeChecker} typeChecker
* @param {ts.SourceFile} protocolFile
* @private
*/
constructor(typeChecker, protocolFile) {
this.typeChecker = typeChecker;
this.protocolFile = protocolFile;
}
/**
*
* @param {ts.TypeChecker} typeChecker
* @param {ts.SourceFile} protocolFile
* @returns {string}
*/
static getExtraDeclarations(typeChecker, protocolFile) {
const walker = new DeclarationsWalker(typeChecker, protocolFile);
walker.visitTypeNodes(protocolFile);
let text = walker.text
? `declare namespace ts.server.protocol {\n${walker.text}}`
: "";
if (walker.removedTypes) {
text += "\ndeclare namespace ts {\n";
text += " // these types are empty stubs for types from services and should not be used directly\n";
for (const type of walker.removedTypes) {
text += ` export type ${type.symbol.name} = never;\n`;
}
text += "}";
}
return text;
}
/**
* @param {ts.Type} type
* @returns {void}
* @private
*/
processType(type) {
if (this.visitedTypes.indexOf(type) >= 0) {
return;
}
this.visitedTypes.push(type);
const s = type.aliasSymbol || type.getSymbol();
if (!s) {
return;
}
if (s.name === "Array" || s.name === "ReadOnlyArray") {
// we should process type argument instead
return this.processType(/** @type {any} */(type).typeArguments[0]);
}
else {
const declarations = s.getDeclarations();
if (declarations) {
for (const decl of declarations) {
const sourceFile = decl.getSourceFile();
if (sourceFile === this.protocolFile || /lib(\..+)?\.d.ts/.test(path.basename(sourceFile.fileName))) {
return;
}
if (ts.isEnumDeclaration(decl) && !isStringEnum(decl)) {
this.removedTypes.push(type);
return;
}
else {
// splice declaration in final d.ts file
const text = decl.getFullText();
this.text += `${text}\n`;
// recursively pull all dependencies into result dts file
this.visitTypeNodes(decl);
}
}
}
}
}
/**
* @param {ts.Node} node
* @private
*/
visitTypeNodes(node) {
if (node.parent) {
switch (node.parent.kind) {
case ts.SyntaxKind.VariableDeclaration:
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.MethodSignature:
case ts.SyntaxKind.PropertyDeclaration:
case ts.SyntaxKind.PropertySignature:
case ts.SyntaxKind.Parameter:
case ts.SyntaxKind.IndexSignature:
const parent = /** @type {ts.VariableDeclaration | ts.MethodDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration | ts.PropertySignature | ts.MethodSignature | ts.IndexSignatureDeclaration} */ (node.parent);
if (parent.type === node) {
this.processTypeOfNode(node);
}
break;
case ts.SyntaxKind.InterfaceDeclaration:
const heritageClauses = /** @type {ts.InterfaceDeclaration} */ (node.parent).heritageClauses;
if (heritageClauses) {
if (heritageClauses[0].token !== ts.SyntaxKind.ExtendsKeyword) {
throw new Error(`Unexpected kind of heritage clause: ${ts.SyntaxKind[heritageClauses[0].kind]}`);
}
for (const type of heritageClauses[0].types) {
this.processTypeOfNode(type);
}
}
break;
}
}
ts.forEachChild(node, n => this.visitTypeNodes(n));
}
/**
* @param {ts.Node} node
* @private
*/
processTypeOfNode(node) {
if (node.kind === ts.SyntaxKind.UnionType) {
for (const t of /** @type {ts.UnionTypeNode} */ (node).types) {
this.processTypeOfNode(t);
}
}
else {
const type = this.typeChecker.getTypeAtLocation(node);
if (type && !(type.flags & (ts.TypeFlags.TypeParameter))) {
this.processType(type);
}
}
}
}
/**
* @param {string} outputFile
* @param {string} protocolTs
* @param {string} typeScriptServicesDts
*/
function writeProtocolFile(outputFile, protocolTs, typeScriptServicesDts) {
/** @type {ts.CompilerOptions} */
const options = { target: ts.ScriptTarget.ES5, declaration: true, noResolve: false, types: [], stripInternal: true };
/**
* 1st pass - generate a program from protocol.ts and typescriptservices.d.ts and emit core version of protocol.d.ts with all internal members stripped
* @return text of protocol.d.t.s
*/
function getInitialDtsFileForProtocol() {
const program = ts.createProgram([protocolTs, typeScriptServicesDts, path.join(typeScriptServicesDts, "../lib.es5.d.ts")], options);
/** @type {string | undefined} */
let protocolDts;
const emitResult = program.emit(program.getSourceFile(protocolTs), (file, content) => {
if (endsWith(file, ".d.ts")) {
protocolDts = content;
}
});
if (protocolDts === undefined) {
/** @type {ts.FormatDiagnosticsHost} */
const diagHost = {
getCanonicalFileName(f) { return f; },
getCurrentDirectory() { return "."; },
getNewLine() { return "\r\n"; }
};
const diags = emitResult.diagnostics.map(d => ts.formatDiagnostic(d, diagHost)).join("\r\n");
throw new Error(`Declaration file for protocol.ts is not generated:\r\n${diags}`);
}
return protocolDts;
}
const protocolFileName = "protocol.d.ts";
/**
* Second pass - generate a program from protocol.d.ts and typescriptservices.d.ts, then augment core protocol.d.ts with extra types from typescriptservices.d.ts
* @param {string} protocolDts
* @param {boolean} includeTypeScriptServices
*/
function getProgramWithProtocolText(protocolDts, includeTypeScriptServices) {
const host = ts.createCompilerHost(options);
const originalGetSourceFile = host.getSourceFile;
host.getSourceFile = (fileName) => {
if (fileName === protocolFileName) {
assert(options.target !== undefined);
return ts.createSourceFile(fileName, protocolDts, options.target);
}
return originalGetSourceFile.apply(host, [fileName, ts.ScriptTarget.Latest]);
};
const rootFiles = includeTypeScriptServices ? [protocolFileName, typeScriptServicesDts] : [protocolFileName];
return ts.createProgram(rootFiles, options, host);
}
let protocolDts = getInitialDtsFileForProtocol();
const program = getProgramWithProtocolText(protocolDts, /*includeTypeScriptServices*/ true);
const protocolFile = program.getSourceFile("protocol.d.ts");
assert(protocolFile);
const extraDeclarations = DeclarationsWalker.getExtraDeclarations(program.getTypeChecker(), protocolFile);
if (extraDeclarations) {
protocolDts += extraDeclarations;
}
protocolDts += "\nimport protocol = ts.server.protocol;";
protocolDts += "\nexport = protocol;";
protocolDts += "\nexport as namespace protocol;";
// do sanity check and try to compile generated text as standalone program
const sanityCheckProgram = getProgramWithProtocolText(protocolDts, /*includeTypeScriptServices*/ false);
const diagnostics = [...sanityCheckProgram.getSyntacticDiagnostics(), ...sanityCheckProgram.getSemanticDiagnostics(), ...sanityCheckProgram.getGlobalDiagnostics()];
ts.sys.writeFile(outputFile, protocolDts);
if (diagnostics.length) {
const flattenedDiagnostics = diagnostics.map(d => `${ts.flattenDiagnosticMessageText(d.messageText, "\n")} at ${d.file ? d.file.fileName : "<unknown>"} line ${d.start}`).join("\n");
throw new Error(`Unexpected errors during sanity check: ${flattenedDiagnostics}`);
}
}
if (process.argv.length < 5) {
console.log(`Expected 3 arguments: path to 'protocol.ts', path to 'typescriptservices.d.ts' and path to output file`);
process.exit(1);
}
const protocolTs = process.argv[2];
const typeScriptServicesDts = process.argv[3];
const outputFile = process.argv[4];
writeProtocolFile(outputFile, protocolTs, typeScriptServicesDts);
+413
View File
@@ -0,0 +1,413 @@
/**
* WARNING: this is a very, very rudimentary d.ts bundler; it only works
* in the TS project thanks to our history using namespaces, which has
* prevented us from duplicating names across files, and allows us to
* bundle as namespaces again, even though the project is modules.
*/
import fs from "fs";
import path from "path";
import minimist from "minimist";
import url from "url";
import ts from "../lib/typescript.js";
import assert, { fail } from "assert";
const __filename = url.fileURLToPath(new URL(import.meta.url));
const __dirname = path.dirname(__filename);
// /** @type {any} */ (ts).Debug.enableDebugInfo();
const dotDts = ".d.ts";
const options = minimist(process.argv.slice(2), {
string: ["project", "entrypoint", "output"],
});
const entrypoint = options.entrypoint;
const output = options.output;
assert(typeof entrypoint === "string" && entrypoint);
assert(typeof output === "string" && output);
assert(output.endsWith(dotDts));
const internalOutput = output.substring(0, output.length - dotDts.length) + ".internal" + dotDts;
console.log(`Bundling ${entrypoint} to ${output} and ${internalOutput}`);
const newLineKind = ts.NewLineKind.LineFeed;
const newLine = newLineKind === ts.NewLineKind.LineFeed ? "\n" : "\r\n";
/** @type {(node: ts.Node) => node is ts.DeclarationStatement} */
function isDeclarationStatement(node) {
return /** @type {any} */ (ts).isDeclarationStatement(node);
}
/** @type {(node: ts.Node) => boolean} */
function isInternalDeclaration(node) {
return /** @type {any} */ (ts).isInternalDeclaration(node, node.getSourceFile());
}
/**
*
* @param {ts.VariableDeclaration} node
* @returns {ts.VariableStatement}
*/
function getParentVariableStatement(node) {
const declarationList = node.parent;
assert(ts.isVariableDeclarationList(declarationList), `expected VariableDeclarationList at ${nodeToLocation(node)}`);
assert(declarationList.declarations.length === 1, `expected VariableDeclarationList of length 1 at ${nodeToLocation(node)}`);
const variableStatement = declarationList.parent;
assert(ts.isVariableStatement(variableStatement), `expected VariableStatement at ${nodeToLocation(node)}`);
return variableStatement;
}
/**
*
* @param {ts.Declaration} node
* @returns {ts.Statement | undefined}
*/
function getDeclarationStatement(node) {
if (ts.isVariableDeclaration(node)) {
return getParentVariableStatement(node);
}
else if (isDeclarationStatement(node)) {
return node;
}
return undefined;
}
/** @type {ts.TransformationContext} */
const nullTransformationContext = /** @type {any} */ (ts).nullTransformationContext;
const program = ts.createProgram([entrypoint], { target: ts.ScriptTarget.ES5 });
const typeChecker = program.getTypeChecker();
const sourceFile = program.getSourceFile(entrypoint);
assert(sourceFile, "Failed to load source file");
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
assert(moduleSymbol, "Failed to get module's symbol");
const printer = ts.createPrinter({ newLine: newLineKind });
/** @type {string[]} */
const publicLines = [];
/** @type {string[]} */
const internalLines = [];
const indent = " ";
let currentIndent = "";
function increaseIndent() {
currentIndent += indent;
}
function decreaseIndent() {
currentIndent = currentIndent.slice(indent.length);
}
/**
* @enum {number}
*/
const WriteTarget = {
Public: 1 << 0,
Internal: 1 << 1,
Both: (1 << 0) | (1 << 1),
};
/**
* @param {string} s
* @param {WriteTarget} target
*/
function write(s, target) {
if (!target) {
return;
}
const toPush = !s ? [""] : s.split(/\r?\n/).filter(line => line).map(line => (currentIndent + line).trimEnd());
if (target & WriteTarget.Public) {
publicLines.push(...toPush);
}
if (target & WriteTarget.Internal) {
internalLines.push(...toPush);
}
}
/**
* @param {ts.Node} node
* @param {ts.SourceFile} sourceFile
* @param {WriteTarget} target
*/
function writeNode(node, sourceFile, target) {
write(printer.printNode(ts.EmitHint.Unspecified, node, sourceFile), target);
}
/** @type {Map<ts.Symbol, boolean>} */
const containsPublicAPICache = new Map();
/**
* @param {ts.Symbol} symbol
* @returns {boolean}
*/
function containsPublicAPI(symbol) {
const cached = containsPublicAPICache.get(symbol);
if (cached !== undefined) {
return cached;
}
const result = containsPublicAPIWorker();
containsPublicAPICache.set(symbol, result);
return result;
function containsPublicAPIWorker() {
if (!symbol.declarations?.length) {
return false;
}
if (symbol.flags & ts.SymbolFlags.Alias) {
const resolved = typeChecker.getAliasedSymbol(symbol);
return containsPublicAPI(resolved);
}
// Namespace barrel; actual namespaces are checked below.
if (symbol.flags & ts.SymbolFlags.ValueModule && symbol.valueDeclaration?.kind === ts.SyntaxKind.SourceFile) {
for (const me of typeChecker.getExportsOfModule(symbol)) {
if (containsPublicAPI(me)) {
return true;
}
}
return false;
}
for (const decl of symbol.declarations) {
const statement = getDeclarationStatement(decl);
if (statement && !isInternalDeclaration(statement)) {
return true;
}
}
return false;
}
}
/**
* @param {ts.Node} node
*/
function nodeToLocation(node) {
const sourceFile = node.getSourceFile();
const lc = sourceFile.getLineAndCharacterOfPosition(node.pos);
return `${sourceFile.fileName}:${lc.line+1}:${lc.character+1}`;
}
/**
* @param {ts.Node} node
* @returns {ts.Node | undefined}
*/
function removeDeclareConstExport(node) {
switch (node.kind) {
case ts.SyntaxKind.DeclareKeyword: // No need to emit this in d.ts files.
case ts.SyntaxKind.ConstKeyword: // Remove const from const enums.
case ts.SyntaxKind.ExportKeyword: // No export modifier; we are already in the namespace.
return undefined;
}
return node;
}
/** @type {Map<string, ts.Symbol>[]} */
const scopeStack = [];
/**
* @param {string} name
*/
function findInScope(name) {
for (let i = scopeStack.length-1; i >= 0; i--) {
const scope = scopeStack[i];
const symbol = scope.get(name);
if (symbol) {
return symbol;
}
}
return undefined;
}
/** @type {(symbol: ts.Symbol | undefined, excludes?: ts.SymbolFlags) => boolean} */
function isNonLocalAlias(symbol, excludes = ts.SymbolFlags.Value | ts.SymbolFlags.Type | ts.SymbolFlags.Namespace) {
if (!symbol) return false;
return (symbol.flags & (ts.SymbolFlags.Alias | excludes)) === ts.SymbolFlags.Alias || !!(symbol.flags & ts.SymbolFlags.Alias && symbol.flags & ts.SymbolFlags.Assignment);
}
/**
* @param {ts.Symbol} symbol
*/
function resolveAlias(symbol) {
return typeChecker.getAliasedSymbol(symbol);
}
/**
* @param {ts.Symbol} symbol
* @param {boolean | undefined} [dontResolveAlias]
*/
function resolveSymbol(symbol, dontResolveAlias = undefined) {
return !dontResolveAlias && isNonLocalAlias(symbol) ? resolveAlias(symbol) : symbol;
}
/**
* @param {ts.Symbol} symbol
* @returns {ts.Symbol}
*/
function getMergedSymbol(symbol) {
return /** @type {any} */ (typeChecker).getMergedSymbol(symbol);
}
/**
* @param {ts.Symbol} s1
* @param {ts.Symbol} s2
*/
function symbolsConflict(s1, s2) {
// See getSymbolIfSameReference in checker.ts
s1 = getMergedSymbol(resolveSymbol(getMergedSymbol(s1)));
s2 = getMergedSymbol(resolveSymbol(getMergedSymbol(s2)));
if (s1 === s2) {
return false;
}
const s1Flags = s1.flags & (ts.SymbolFlags.Type | ts.SymbolFlags.Value);
const s2Flags = s2.flags & (ts.SymbolFlags.Type | ts.SymbolFlags.Value);
// If the two symbols differ by type/value space, ignore.
if (!(s1Flags & s2Flags)) {
return false;
}
return true;
}
/**
* @param {ts.Node} node
* @returns {boolean}
*/
function isPartOfTypeNode(node) {
return /** @type {any} */ (ts).isPartOfTypeNode(node);
}
/**
* @param {ts.Statement} decl
*/
function verifyMatchingSymbols(decl) {
ts.visitEachChild(decl, /** @type {(node: ts.Node) => ts.Node} */ function visit(node) {
if (ts.isIdentifier(node) && isPartOfTypeNode(node)) {
if (ts.isQualifiedName(node.parent) && node !== node.parent.left) {
return node;
}
if (ts.isParameter(node.parent) && node === node.parent.name) {
return node;
}
if (ts.isNamedTupleMember(node.parent) && node === node.parent.name) {
return node;
}
const symbolOfNode = typeChecker.getSymbolAtLocation(node);
if (!symbolOfNode) {
fail(`No symbol for node at ${nodeToLocation(node)}`);
}
const symbolInScope = findInScope(symbolOfNode.name);
if (!symbolInScope) {
// We didn't find the symbol in scope at all. Just allow it and we'll fail at test time.
return node;
}
if (symbolsConflict(symbolOfNode, symbolInScope)) {
fail(`Declaration at ${nodeToLocation(decl)}\n references ${symbolOfNode.name} at ${symbolOfNode.declarations && nodeToLocation(symbolOfNode.declarations[0])},\n but containing scope contains a symbol with the same name declared at ${symbolInScope.declarations && nodeToLocation(symbolInScope.declarations[0])}`);
}
}
return ts.visitEachChild(node, visit, nullTransformationContext);
}, nullTransformationContext);
}
/**
* @param {string} name
* @param {ts.Symbol} moduleSymbol
*/
function emitAsNamespace(name, moduleSymbol) {
assert(moduleSymbol.flags & ts.SymbolFlags.ValueModule, "moduleSymbol is not a module");
scopeStack.push(new Map());
const currentScope = scopeStack[scopeStack.length-1];
const target = containsPublicAPI(moduleSymbol) ? WriteTarget.Both : WriteTarget.Internal;
if (name === "ts") {
// We will write `export = ts` at the end.
write(`declare namespace ${name} {`, target);
}
else {
// No export modifier; we are already in the namespace.
write(`namespace ${name} {`, target);
}
increaseIndent();
const moduleExports = typeChecker.getExportsOfModule(moduleSymbol);
for (const me of moduleExports) {
currentScope.set(me.name, me);
}
for (const me of moduleExports) {
assert(me.declarations?.length);
if (me.flags & ts.SymbolFlags.Alias) {
const resolved = typeChecker.getAliasedSymbol(me);
emitAsNamespace(me.name, resolved);
continue;
}
for (const decl of me.declarations) {
const statement = getDeclarationStatement(decl);
const sourceFile = decl.getSourceFile();
if (!statement) {
fail(`Unhandled declaration for ${me.name} at ${nodeToLocation(decl)}`);
}
verifyMatchingSymbols(statement);
const isInternal = isInternalDeclaration(statement);
if (!isInternal) {
const publicStatement = ts.visitEachChild(statement, (node) => {
// No @internal comments in the public API.
if (isInternalDeclaration(node)) {
return undefined;
}
return removeDeclareConstExport(node);
}, nullTransformationContext);
writeNode(publicStatement, sourceFile, WriteTarget.Public);
}
const internalStatement = ts.visitEachChild(statement, removeDeclareConstExport, nullTransformationContext);
writeNode(internalStatement, sourceFile, WriteTarget.Internal);
}
}
scopeStack.pop();
decreaseIndent();
write(`}`, target);
}
emitAsNamespace("ts", moduleSymbol);
write("export = ts;", WriteTarget.Both);
const copyrightNotice = fs.readFileSync(path.join(__dirname, "..", "CopyrightNotice.txt"), "utf-8");
const publicContents = copyrightNotice + publicLines.join(newLine);
const internalContents = copyrightNotice + internalLines.join(newLine);
if (publicContents.includes("@internal")) {
console.error("Output includes untrimmed @internal nodes!");
}
fs.writeFileSync(output, publicContents);
fs.writeFileSync(internalOutput, internalContents);
+110
View File
@@ -0,0 +1,110 @@
const { TSESTree } = require("@typescript-eslint/utils");
const { createRule } = require("./utils.cjs");
module.exports = createRule({
name: "jsdoc-format",
meta: {
docs: {
description: ``,
recommended: "error",
},
messages: {
internalCommentInNonJSDocError: `@internal should not appear in non-JSDoc comment for declaration.`,
internalCommentNotLastError: `@internal should only appear in final JSDoc comment for declaration.`,
multipleJSDocError: `Declaration has multiple JSDoc comments.`,
internalCommentOnParameterProperty: `@internal cannot appear on a JSDoc comment; use a declared property and an assignment in the constructor instead.`,
},
schema: [],
type: "problem",
},
defaultOptions: [],
create(context) {
const sourceCode = context.getSourceCode();
const atInternal = "@internal";
const jsdocStart = "/**";
/** @type {(c: TSESTree.Comment, indexInComment: number) => TSESTree.SourceLocation} */
const getAtInternalLoc = (c, indexInComment) => {
const line = c.loc.start.line;
return {
start: {
line,
column: c.loc.start.column + indexInComment,
},
end: {
line,
column: c.loc.start.column + indexInComment + atInternal.length,
},
};
};
/** @type {(c: TSESTree.Comment) => TSESTree.SourceLocation} */
const getJSDocStartLoc = (c) => {
return {
start: c.loc.start,
end: {
line: c.loc.start.line,
column: c.loc.start.column + jsdocStart.length,
},
};
};
/** @type {(node: TSESTree.Node) => void} */
const checkJSDocFormat = (node) => {
const blockComments = sourceCode.getCommentsBefore(node).filter(c => c.type === "Block");
if (blockComments.length === 0) {
return;
}
const last = blockComments.length - 1;
let seenJSDoc = false;
for (let i = 0; i < blockComments.length; i++) {
const c = blockComments[i];
const rawComment = sourceCode.getText(c);
const isJSDoc = rawComment.startsWith(jsdocStart);
if (isJSDoc && seenJSDoc) {
context.report({ messageId: "multipleJSDocError", node: c, loc: getJSDocStartLoc(c) });
}
seenJSDoc = seenJSDoc || isJSDoc;
const indexInComment = rawComment.indexOf(atInternal);
if (indexInComment === -1) {
continue;
}
if (!isJSDoc) {
context.report({ messageId: "internalCommentInNonJSDocError", node: c, loc: getAtInternalLoc(c, indexInComment) });
}
else if (i !== last) {
context.report({ messageId: "internalCommentNotLastError", node: c, loc: getAtInternalLoc(c, indexInComment) });
}
else if (node.type === "TSParameterProperty") {
context.report({ messageId: "internalCommentOnParameterProperty", node: c, loc: getAtInternalLoc(c, indexInComment) });
}
}
};
return {
ClassDeclaration: checkJSDocFormat,
FunctionDeclaration: checkJSDocFormat,
TSEnumDeclaration: checkJSDocFormat,
TSModuleDeclaration: checkJSDocFormat,
VariableDeclaration: checkJSDocFormat,
TSInterfaceDeclaration: checkJSDocFormat,
TSTypeAliasDeclaration: checkJSDocFormat,
TSCallSignatureDeclaration: checkJSDocFormat,
ExportAllDeclaration: checkJSDocFormat,
ExportNamedDeclaration: checkJSDocFormat,
TSImportEqualsDeclaration: checkJSDocFormat,
TSNamespaceExportDeclaration: checkJSDocFormat,
TSConstructSignatureDeclaration: checkJSDocFormat,
ExportDefaultDeclaration: checkJSDocFormat,
TSPropertySignature: checkJSDocFormat,
TSIndexSignature: checkJSDocFormat,
TSMethodSignature: checkJSDocFormat,
TSParameterProperty: checkJSDocFormat,
};
},
});
@@ -1,45 +0,0 @@
const { AST_NODE_TYPES, TSESTree } = require("@typescript-eslint/utils");
const { createRule } = require("./utils.cjs");
module.exports = createRule({
name: "one-namespace-per-file",
meta: {
docs: {
description: `Limits each file to having at most one top-level namespace declaration`,
recommended: "error",
},
messages: {
excessNamespaceError: `All but one of these namespaces should be moved into separate files.`,
},
schema: [],
type: "problem",
},
defaultOptions: [],
create(context) {
/** @type {(node: TSESTree.Node) => node is TSESTree.TSModuleDeclaration} */
const isNamespaceDeclaration = (node) => node.type === AST_NODE_TYPES.TSModuleDeclaration;
/** @type {(node: TSESTree.Program) => void} */
const checkSourceFile = (node) => {
if (context.getFilename().endsWith(".d.ts")) {
return;
}
const members = node.body;
const namespaces = members.filter(isNamespaceDeclaration);
if (namespaces.length <= 1) {
return;
}
namespaces.forEach(n => {
context.report({
messageId: "excessNamespaceError", node: n
});
});
};
return {
Program: checkSourceFile,
};
},
});
+1
View File
@@ -10,6 +10,7 @@ const os = require("os");
reporter?: Mocha.ReporterConstructor | keyof Mocha.reporters;
reporterOptions?: any; // TODO(jakebailey): what?
}} ReporterOptions */
void 0;
/**
* .failed-tests reporter
+59 -83
View File
@@ -1,8 +1,28 @@
import fs from "fs";
import path from "path";
import xml2js from "xml2js";
import { XMLParser } from "fast-xml-parser";
function main() {
/** @typedef {{
LCX: {
$_TgtCul: string;
Item: {
Item: {
Item: {
$_ItemId: string;
Str: {
Val: string;
Tgt: {
Val: string;
};
};
}[];
};
};
}
}} ParsedLCL */
void 0;
async function main() {
const args = process.argv.slice(2);
if (args.length !== 3) {
console.log("Usage:");
@@ -15,38 +35,33 @@ function main() {
const diagnosticsMapFilePath = args[2];
// generate the lcg file for enu
generateLCGFile();
await generateLCGFile();
// generate other langs
fs.readdir(inputPath, (err, files) => {
handleError(err);
files.forEach(visitDirectory);
});
const files = await fs.promises.readdir(inputPath);
await Promise.all(files.map(visitDirectory));
return;
/**
* @param {string} name
*/
function visitDirectory(name) {
async function visitDirectory(name) {
const inputFilePath = path.join(inputPath, name, "diagnosticMessages", "diagnosticMessages.generated.json.lcl");
fs.readFile(inputFilePath, (err, data) => {
handleError(err);
xml2js.parseString(data.toString(), (err, result) => {
handleError(err);
if (!result || !result.LCX || !result.LCX.$ || !result.LCX.$.TgtCul) {
console.error("Unexpected XML file structure. Expected to find result.LCX.$.TgtCul.");
process.exit(1);
}
const outputDirectoryName = getPreferredLocaleName(result.LCX.$.TgtCul).toLowerCase();
if (!outputDirectoryName) {
console.error(`Invalid output locale name for '${result.LCX.$.TgtCul}'.`);
process.exit(1);
}
writeFile(path.join(outputPath, outputDirectoryName, "diagnosticMessages.generated.json"), xmlObjectToString(result));
});
});
const contents = await fs.promises.readFile(inputFilePath);
/** @type {ParsedLCL} */
// eslint-disable-next-line local/object-literal-surrounding-space
const result = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "$_"}).parse(contents);
if (!result || !result.LCX || !result.LCX.$_TgtCul) {
console.error("Unexpected XML file structure. Expected to find result.LCX.$_TgtCul.");
process.exit(1);
}
const outputDirectoryName = getPreferredLocaleName(result.LCX.$_TgtCul).toLowerCase();
if (!outputDirectoryName) {
console.error(`Invalid output locale name for '${result.LCX.$_TgtCul}'.`);
process.exit(1);
}
await writeFile(path.join(outputPath, outputDirectoryName, "diagnosticMessages.generated.json"), xmlObjectToString(result));
}
/**
@@ -81,24 +96,14 @@ function main() {
}
/**
* @param {null | object} err
*/
function handleError(err) {
if (err) {
console.error(err);
process.exit(1);
}
}
/**
* @param {any} o
* @param {ParsedLCL} o
*/
function xmlObjectToString(o) {
/** @type {any} */
const out = {};
for (const item of o.LCX.Item[0].Item[0].Item) {
let ItemId = item.$.ItemId;
let val = item.Str[0].Tgt ? item.Str[0].Tgt[0].Val[0] : item.Str[0].Val[0];
for (const item of o.LCX.Item.Item.Item) {
let ItemId = item.$_ItemId;
let val = item.Str.Tgt ? item.Str.Tgt.Val : item.Str.Val;
if (typeof ItemId !== "string" || typeof val !== "string") {
console.error("Unexpected XML file structure");
@@ -115,55 +120,26 @@ function main() {
return JSON.stringify(out, undefined, 2);
}
/**
* @param {string} directoryPath
* @param {() => void} action
*/
function ensureDirectoryExists(directoryPath, action) {
fs.exists(directoryPath, exists => {
if (!exists) {
const basePath = path.dirname(directoryPath);
if (basePath !== directoryPath) {
return ensureDirectoryExists(basePath, () => fs.mkdir(directoryPath, action));
}
}
action();
});
}
/**
* @param {string} fileName
* @param {string} contents
*/
function writeFile(fileName, contents) {
ensureDirectoryExists(path.dirname(fileName), () => {
fs.writeFile(fileName, contents, handleError);
});
async function writeFile(fileName, contents) {
await fs.promises.mkdir(path.dirname(fileName), { recursive: true });
await fs.promises.writeFile(fileName, contents);
}
/**
* @param {Record<string, string>} o
*/
function objectToList(o) {
const list = [];
for (const key in o) {
list.push({ key, value: o[key] });
}
return list;
}
function generateLCGFile() {
return fs.readFile(diagnosticsMapFilePath, (err, data) => {
handleError(err);
writeFile(
path.join(outputPath, "enu", "diagnosticMessages.generated.json.lcg"),
getLCGFileXML(
objectToList(JSON.parse(data.toString()))
.sort((a, b) => a.key > b.key ? 1 : -1) // lcg sorted by property keys
.reduce((s, { key, value }) => s + getItemXML(key, value), "")
));
});
async function generateLCGFile() {
const contents = await fs.promises.readFile(diagnosticsMapFilePath, "utf-8");
await writeFile(
path.join(outputPath, "enu", "diagnosticMessages.generated.json.lcg"),
getLCGFileXML(
Object.entries(JSON.parse(contents))
.sort((a, b) => a[0] > b[0] ? 1 : -1) // lcg sorted by property keys
.reduce((s, [key, value]) => s + getItemXML(key, value), "")
),
);
return;
/**
* @param {string} key
@@ -204,4 +180,4 @@ function main() {
}
}
main();
await main();
+1 -1
View File
@@ -1,2 +1,2 @@
#!/bin/sh
npm run gulp -- generate-diagnostics
npm run npx hereby -- generate-diagnostics
+1 -1
View File
@@ -71,7 +71,7 @@ ${logText.trim()}`;
]);
if (produceLKG) {
runSequence([
["gulp", ["LKG"]],
["node", ["./node_modules/hereby/dist/cli.js", "LKG"]],
["git", ["add", "lib"]],
["git", ["commit", "-m", `"Update LKG"`]]
]);
+1 -1
View File
@@ -15,7 +15,7 @@ runSequence([
["git", ["checkout", "."]], // reset any changes
["git", ["fetch", baseRef === "main" ? "origin" : "fork", baseRef]], // fetch target ref in case it's not present locally
["git", ["checkout", baseRef]], // move head to target
["node", ["./node_modules/gulp/bin/gulp.js", "baseline-accept"]], // accept baselines
["node", ["./node_modules/hereby/dist/cli.js", "baseline-accept"]], // accept baselines
["git", ["checkout", "-b", branchName]], // create a branch
["git", ["add", "."]], // Add all changes
["git", ["commit", "-m", `"Update user baselines${+(process.env.SOURCE_ISSUE ?? 0) === 33716 ? " +cc @sandersn" : ""}"`]], // Commit all changes (ping nathan if we would post to CI thread)
+36 -26
View File
@@ -9,10 +9,11 @@ import fs from "fs";
isEarly?: boolean;
elidedInCompatabilityPyramid?: boolean;
}} DiagnosticDetails */
void 0;
/** @typedef {Map<string, DiagnosticDetails>} InputDiagnosticMessageTable */
function main() {
async function main() {
if (process.argv.length < 3) {
console.log("Usage:");
console.log("\tnode processDiagnosticMessages.mjs <diagnostic-json-input-file>");
@@ -23,15 +24,24 @@ function main() {
* @param {string} fileName
* @param {string} contents
*/
function writeFile(fileName, contents) {
fs.writeFile(path.join(path.dirname(inputFilePath), fileName), contents, { encoding: "utf-8" }, err => {
if (err) throw err;
});
async function writeFile(fileName, contents) {
const filePath = path.join(path.dirname(inputFilePath), fileName);
try {
const existingContents = await fs.promises.readFile(filePath, "utf-8");
if (existingContents === contents) {
return;
}
}
catch {
// Just write the file.
}
await fs.promises.writeFile(filePath, contents, { encoding: "utf-8" });
}
const inputFilePath = process.argv[2].replace(/\\/g, "/");
console.log(`Reading diagnostics from ${inputFilePath}`);
const inputStr = fs.readFileSync(inputFilePath, { encoding: "utf-8" });
const inputStr = await fs.promises.readFile(inputFilePath, { encoding: "utf-8" });
/** @type {{ [key: string]: DiagnosticDetails }} */
const diagnosticMessagesJson = JSON.parse(inputStr);
@@ -44,15 +54,12 @@ function main() {
}
}
const outputFilesDir = path.dirname(inputFilePath);
const thisFilePathRel = path.relative(process.cwd(), outputFilesDir);
const infoFileOutput = buildInfoFileOutput(diagnosticMessages, `./${path.basename(inputFilePath)}`, thisFilePathRel);
const infoFileOutput = buildInfoFileOutput(diagnosticMessages, inputFilePath);
checkForUniqueCodes(diagnosticMessages);
writeFile("diagnosticInformationMap.generated.ts", infoFileOutput);
await writeFile("diagnosticInformationMap.generated.ts", infoFileOutput);
const messageOutput = buildDiagnosticMessageOutput(diagnosticMessages);
writeFile("diagnosticMessages.generated.json", messageOutput);
await writeFile("diagnosticMessages.generated.json", messageOutput);
}
/**
@@ -72,31 +79,34 @@ function checkForUniqueCodes(diagnosticTable) {
/**
* @param {InputDiagnosticMessageTable} messageTable
* @param {string} inputFilePathRel
* @param {string} thisFilePathRel
* @returns {string}
*/
function buildInfoFileOutput(messageTable, inputFilePathRel, thisFilePathRel) {
let result =
"// <auto-generated />\r\n" +
"// generated from '" + inputFilePathRel + "' in '" + thisFilePathRel.replace(/\\/g, "/") + "'\r\n" +
"/* @internal */\r\n" +
"namespace ts {\r\n" +
" function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean, reportsDeprecated?: {}): DiagnosticMessage {\r\n" +
" return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated };\r\n" +
" }\r\n" +
" export const Diagnostics = {\r\n";
function buildInfoFileOutput(messageTable, inputFilePathRel) {
const result = [
"// <auto-generated />",
`// generated from '${inputFilePathRel}'`,
"",
"import { DiagnosticCategory, DiagnosticMessage } from \"./types\";",
"",
"function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean, reportsDeprecated?: {}): DiagnosticMessage {",
" return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated };",
"}",
"",
"/** @internal */",
"export const Diagnostics = {",
];
messageTable.forEach(({ code, category, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated }, name) => {
const propName = convertPropertyName(name);
const argReportsUnnecessary = reportsUnnecessary ? `, /*reportsUnnecessary*/ ${reportsUnnecessary}` : "";
const argElidedInCompatabilityPyramid = elidedInCompatabilityPyramid ? `${!reportsUnnecessary ? ", /*reportsUnnecessary*/ undefined" : ""}, /*elidedInCompatabilityPyramid*/ ${elidedInCompatabilityPyramid}` : "";
const argReportsDeprecated = reportsDeprecated ? `${!argElidedInCompatabilityPyramid ? ", /*reportsUnnecessary*/ undefined, /*elidedInCompatabilityPyramid*/ undefined" : ""}, /*reportsDeprecated*/ ${reportsDeprecated}` : "";
result += ` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}${argReportsDeprecated}),\r\n`;
result.push(` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}${argReportsDeprecated}),`);
});
result += " };\r\n}";
result.push("};");
return result;
return result.join("\r\n");
}
/**
+15 -56
View File
@@ -1,8 +1,9 @@
import childProcess from "child_process";
import fs from "fs-extra";
import path from "path";
import glob from "glob";
import url from "url";
import del from "del";
import { localizationDirectories } from "./build/localization.mjs";
const __filename = url.fileURLToPath(new URL(import.meta.url));
const __dirname = path.dirname(__filename);
@@ -10,16 +11,16 @@ const __dirname = path.dirname(__filename);
const root = path.join(__dirname, "..");
const source = path.join(root, "built/local");
const dest = path.join(root, "lib");
const copyright = fs.readFileSync(path.join(__dirname, "../CopyrightNotice.txt"), "utf-8");
async function produceLKG() {
console.log(`Building LKG from ${source} to ${dest}`);
await del(`${dest.replace(/\\/g, "/")}/**`, { ignore: ["**/README.md"] });
await fs.mkdirp(dest);
await copyLibFiles();
await copyLocalizedDiagnostics();
await copyTypesMap();
await copyScriptOutputs();
await copyDeclarationOutputs();
await buildProtocol();
await writeGitAttributes();
}
@@ -28,15 +29,9 @@ async function copyLibFiles() {
}
async function copyLocalizedDiagnostics() {
const dir = await fs.readdir(source);
const ignoredFolders = ["enu"];
for (const d of dir) {
for (const d of localizationDirectories) {
const fileName = path.join(source, d);
if (
fs.statSync(fileName).isDirectory() &&
ignoredFolders.indexOf(d) < 0
) {
if (fs.statSync(fileName).isDirectory()) {
await fs.copy(fileName, path.join(dest, d));
}
}
@@ -46,51 +41,25 @@ async function copyTypesMap() {
await copyFromBuiltLocal("typesMap.json"); // Cannot accommodate copyright header
}
async function buildProtocol() {
const protocolScript = path.join(__dirname, "buildProtocol.mjs");
if (!fs.existsSync(protocolScript)) {
throw new Error(`Expected protocol script ${protocolScript} to exist`);
}
const protocolInput = path.join(__dirname, "../src/server/protocol.ts");
const protocolServices = path.join(source, "typescriptServices.d.ts");
const protocolOutput = path.join(dest, "protocol.d.ts");
console.log(`Building ${protocolOutput}...`);
await exec(protocolScript, [protocolInput, protocolServices, protocolOutput]);
}
async function copyScriptOutputs() {
await copyWithCopyright("cancellationToken.js");
await copyWithCopyright("tsc.release.js", "tsc.js");
await copyWithCopyright("tsserver.js");
await copyWithCopyright("dynamicImportCompat.js");
await copyFromBuiltLocal("tsserverlibrary.js"); // copyright added by build
await copyFromBuiltLocal("typescript.js"); // copyright added by build
await copyFromBuiltLocal("typescriptServices.js"); // copyright added by build
await copyWithCopyright("typingsInstaller.js");
await copyWithCopyright("watchGuard.js");
await copyFromBuiltLocal("cancellationToken.js");
await copyFromBuiltLocal("tsc.js");
await copyFromBuiltLocal("tsserver.js");
await copyFromBuiltLocal("tsserverlibrary.js");
await copyFromBuiltLocal("typescript.js");
await copyFromBuiltLocal("typingsInstaller.js");
await copyFromBuiltLocal("watchGuard.js");
}
async function copyDeclarationOutputs() {
await copyFromBuiltLocal("tsserverlibrary.d.ts"); // copyright added by build
await copyFromBuiltLocal("typescript.d.ts"); // copyright added by build
await copyFromBuiltLocal("typescriptServices.d.ts"); // copyright added by build
await copyFromBuiltLocal("tsserverlibrary.d.ts");
await copyFromBuiltLocal("typescript.d.ts");
}
async function writeGitAttributes() {
await fs.writeFile(path.join(dest, ".gitattributes"), `* text eol=lf`, "utf-8");
}
/**
* @param {string} fileName
* @param {string} destName
*/
async function copyWithCopyright(fileName, destName = fileName) {
const content = await fs.readFile(path.join(source, fileName), "utf-8");
await fs.writeFile(path.join(dest, destName), copyright + "\n" + content);
}
/**
* @param {string} fileName
*/
@@ -109,16 +78,6 @@ async function copyFilesWithGlob(pattern) {
console.log(`Copied ${files.length} files matching pattern ${pattern}`);
}
/**
* @param {string} path
* @param {string[]} args
*/
async function exec(path, args = []) {
const cmdLine = ["node", path, ...args].join(" ");
console.log(cmdLine);
childProcess.execSync(cmdLine);
}
process.on("unhandledRejection", err => {
throw err;
});
-90
View File
@@ -1,90 +0,0 @@
import { TaskFunction } from "gulp";
declare module "gulp-insert" {
export function append(text: string | Buffer): NodeJS.ReadWriteStream;
export function prepend(text: string | Buffer): NodeJS.ReadWriteStream;
export function wrap(text: string | Buffer, tail: string | Buffer): NodeJS.ReadWriteStream;
export function transform(cb: (contents: string, file: {path: string, relative: string}) => string): NodeJS.ReadWriteStream; // file is a vinyl file
}
declare module "sorcery";
declare module "vinyl" {
// NOTE: This makes it possible to correctly type vinyl Files under @ts-check.
export = File;
declare class File<T extends File.Contents = File.Contents> {
constructor(options?: File.VinylOptions<T>);
cwd: string;
base: string;
path: string;
readonly history: readonly string[];
contents: T;
relative: string;
dirname: string;
basename: string;
stem: string;
extname: string;
symlink: string | null;
stat: import("fs").Stats | null;
sourceMap?: import("./sourcemaps").RawSourceMap | string;
[custom: string]: any;
isBuffer(): this is T extends Buffer ? File<Buffer> : never;
isStream(): this is T extends NodeJS.ReadableStream ? File<NodeJS.ReadableStream> : never;
isNull(): this is T extends null ? File<null> : never;
isDirectory(): this is T extends null ? File.Directory : never;
isSymbolic(): this is T extends null ? File.Symbolic : never;
clone(opts?: { contents?: boolean, deep?: boolean }): this;
}
namespace File {
export interface VinylOptions<T extends Contents = Contents> {
cwd?: string;
base?: string;
path?: string;
history?: readonly string[];
stat?: import("fs").Stats;
contents?: T;
sourceMap?: import("./sourcemaps").RawSourceMap | string;
[custom: string]: any;
}
export type Contents = Buffer | NodeJS.ReadableStream | null;
export type File = import("./vinyl");
export type NullFile = File<null>;
export type BufferFile = File<Buffer>;
export type StreamFile = File<NodeJS.ReadableStream>;
export interface Directory extends NullFile {
isNull(): true;
isDirectory(): true;
isSymbolic(): this is never;
}
export interface Symbolic extends NullFile {
isNull(): true;
isDirectory(): this is never;
isSymbolic(): true;
}
}
}
declare module "undertaker" {
interface TaskFunctionParams {
flags?: Record<string, string>;
}
interface TaskFunctionWrapped {
description: string
flags: { [name: string]: string }
}
}
declare module "gulp-sourcemaps" {
interface WriteOptions {
destPath?: string;
}
}
+1
View File
@@ -60,6 +60,7 @@ const sys = (() => {
subscript?: boolean;
};
}} FindReplaceOptions */
void 0;
/**
* @param {Word.Document} doc
+23 -2
View File
@@ -6,7 +6,21 @@
},
"rules": {
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error"
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"no-restricted-globals": ["error",
{ "name": "setTimeout" },
{ "name": "clearTimeout" },
{ "name": "setInterval" },
{ "name": "clearInterval" },
{ "name": "setImmediate" },
{ "name": "clearImmediate" },
{ "name": "performance" },
{ "name": "Iterator" },
{ "name": "Map" },
{ "name": "ReadonlyMap" },
{ "name": "Set" },
{ "name": "ReadonlySet" }
]
},
"overrides": [
{
@@ -20,7 +34,8 @@
"local/no-keywords": "off",
// eslint
"no-var": "off"
"no-var": "off",
"no-restricted-globals": "off"
}
},
{
@@ -28,6 +43,12 @@
"rules": {
"@typescript-eslint/array-type": "off"
}
},
{
"files": ["debug/**", "harness/**", "testRunner/**"],
"rules": {
"no-restricted-globals": "off"
}
}
]
}
+1 -3
View File
@@ -1,6 +1,4 @@
/// <reference types="node"/>
import fs = require("fs");
import * as fs from "fs";
interface ServerCancellationToken {
isCancellationRequested(): boolean;
+2 -11
View File
@@ -1,13 +1,6 @@
{
"extends": "../tsconfig-noncomposite-base",
"extends": "../tsconfig-base",
"compilerOptions": {
"outDir": "../../built/local/",
"rootDir": ".",
"composite": false,
"declaration": false,
"declarationMap": false,
"removeComments": true,
"incremental": false,
"module": "commonjs",
"types": [
"node"
@@ -16,7 +9,5 @@
"es6"
]
},
"files": [
"cancellationToken.ts"
]
"include": ["**/*"]
}
@@ -0,0 +1,3 @@
/* Generated file to emulate the ts.moduleSpecifiers namespace. */
export * from "../moduleSpecifiers";
@@ -0,0 +1,3 @@
/* Generated file to emulate the ts.performance namespace. */
export * from "../performance";
+74
View File
@@ -0,0 +1,74 @@
/* Generated file to emulate the ts namespace. */
export * from "../corePublic";
export * from "../core";
export * from "../debug";
export * from "../semver";
export * from "../performanceCore";
export * from "../perfLogger";
export * from "../tracing";
export * from "../types";
export * from "../sys";
export * from "../path";
export * from "../diagnosticInformationMap.generated";
export * from "../scanner";
export * from "../utilitiesPublic";
export * from "../utilities";
export * from "../factory/baseNodeFactory";
export * from "../factory/parenthesizerRules";
export * from "../factory/nodeConverters";
export * from "../factory/nodeFactory";
export * from "../factory/emitNode";
export * from "../factory/emitHelpers";
export * from "../factory/nodeTests";
export * from "../factory/utilities";
export * from "../factory/utilitiesPublic";
export * from "../parser";
export * from "../commandLineParser";
export * from "../moduleNameResolver";
export * from "../binder";
export * from "../symbolWalker";
export * from "../checker";
export * from "../visitorPublic";
export * from "../sourcemap";
export * from "../transformers/utilities";
export * from "../transformers/destructuring";
export * from "../transformers/taggedTemplate";
export * from "../transformers/ts";
export * from "../transformers/classFields";
export * from "../transformers/typeSerializer";
export * from "../transformers/legacyDecorators";
export * from "../transformers/es2017";
export * from "../transformers/es2018";
export * from "../transformers/es2019";
export * from "../transformers/es2020";
export * from "../transformers/es2021";
export * from "../transformers/esnext";
export * from "../transformers/jsx";
export * from "../transformers/es2016";
export * from "../transformers/es2015";
export * from "../transformers/es5";
export * from "../transformers/generators";
export * from "../transformers/module/module";
export * from "../transformers/module/system";
export * from "../transformers/module/esnextAnd2015";
export * from "../transformers/module/node";
export * from "../transformers/declarations/diagnostics";
export * from "../transformers/declarations";
export * from "../transformer";
export * from "../emitter";
export * from "../watchUtilities";
export * from "../program";
export * from "../builderStatePublic";
export * from "../builderState";
export * from "../builder";
export * from "../builderPublic";
export * from "../resolutionCache";
export * from "../watch";
export * from "../watchPublic";
export * from "../tsbuild";
export * from "../tsbuildPublic";
import * as moduleSpecifiers from "./ts.moduleSpecifiers";
export { moduleSpecifiers };
import * as performance from "./ts.performance";
export { performance };
+3314 -3252
View File
File diff suppressed because it is too large Load Diff
+1644 -1576
View File
File diff suppressed because it is too large Load Diff
+176 -166
View File
@@ -1,181 +1,191 @@
namespace ts {
export type AffectedFileResult<T> = { result: T; affected: SourceFile | Program; } | undefined;
import {
BuilderProgramKind, CancellationToken, CompilerHost, CompilerOptions, createBuilderProgram,
createRedirectedBuilderProgram, CustomTransformers, Diagnostic, DiagnosticWithLocation, EmitResult,
getBuilderCreationParameters, Program, ProjectReference, ReusableBuilderProgramState, SavedBuildProgramEmitState,
SourceFile, WriteFileCallback,
} from "./_namespaces/ts";
export interface BuilderProgramHost {
/**
* return true if file names are treated with case sensitivity
*/
useCaseSensitiveFileNames(): boolean;
/**
* If provided this would be used this hash instead of actual file shape text for detecting changes
*/
createHash?: (data: string) => string;
/**
* When emit or emitNextAffectedFile are called without writeFile,
* this callback if present would be used to write files
*/
writeFile?: WriteFileCallback;
/**
* disable using source file version as signature for testing
*/
/*@internal*/
disableUseFileVersionAsSignature?: boolean;
/**
* Store the list of files that update signature during the emit
*/
/*@internal*/
storeFilesChangingSignatureDuringEmit?: boolean;
/**
* Gets the current time
*/
/*@internal*/
now?(): Date;
}
export type AffectedFileResult<T> = { result: T; affected: SourceFile | Program; } | undefined;
export interface BuilderProgramHost {
/**
* Builder to manage the program state changes
* return true if file names are treated with case sensitivity
*/
export interface BuilderProgram {
/*@internal*/
getState(): ReusableBuilderProgramState;
/*@internal*/
saveEmitState(): SavedBuildProgramEmitState;
/*@internal*/
restoreEmitState(saved: SavedBuildProgramEmitState): void;
/*@internal*/
hasChangedEmitSignature?(): boolean;
/**
* Returns current program
*/
getProgram(): Program;
/**
* Returns current program that could be undefined if the program was released
*/
/*@internal*/
getProgramOrUndefined(): Program | undefined;
/**
* Releases reference to the program, making all the other operations that need program to fail.
*/
/*@internal*/
releaseProgram(): void;
/**
* Get compiler options of the program
*/
getCompilerOptions(): CompilerOptions;
/**
* Get the source file in the program with file name
*/
getSourceFile(fileName: string): SourceFile | undefined;
/**
* Get a list of files in the program
*/
getSourceFiles(): readonly SourceFile[];
/**
* Get the diagnostics for compiler options
*/
getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Get the diagnostics that dont belong to any file
*/
getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Get the diagnostics from config file parsing
*/
getConfigFileParsingDiagnostics(): readonly Diagnostic[];
/**
* Get the syntax diagnostics, for all source files if source file is not supplied
*/
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Get the declaration diagnostics, for all source files if source file is not supplied
*/
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[];
/**
* Get all the dependencies of the file
*/
getAllDependencies(sourceFile: SourceFile): readonly string[];
/**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here
* Note that it is assumed that when asked about semantic diagnostics through this API,
* the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
* In case of SemanticDiagnosticsBuilderProgram if the source file is not provided,
* it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics
*/
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Emits the JavaScript and declaration files.
* When targetSource file is specified, emits the files corresponding to that source file,
* otherwise for the whole program.
* In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified,
* it is assumed that that file is handled from affected file list. If targetSourceFile is not specified,
* it will only emit all the affected files instead of whole program
*
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
* in that order would be used to write the files
*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
/*@internal*/
emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
/**
* Get the current directory of the program
*/
getCurrentDirectory(): string;
/*@internal*/
close(): void;
}
useCaseSensitiveFileNames(): boolean;
/**
* The builder that caches the semantic diagnostics for the program and handles the changed files and affected files
* If provided this would be used this hash instead of actual file shape text for detecting changes
*/
export interface SemanticDiagnosticsBuilderProgram extends BuilderProgram {
/**
* Gets the semantic diagnostics from the program for the next affected file and caches it
* Returns undefined if the iteration is complete
*/
getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult<readonly Diagnostic[]>;
}
createHash?: (data: string) => string;
/**
* 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
* When emit or emitNextAffectedFile are called without writeFile,
* this callback if present would be used to write files
*/
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
* in that order would be used to write the files
*/
emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult>;
}
writeFile?: WriteFileCallback;
/**
* Create the builder to manage semantic diagnostics and cache them
* disable using source file version as signature for testing
*
* @internal
*/
export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]) {
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
}
disableUseFileVersionAsSignature?: boolean;
/**
* Create the builder that can handle the changes in program and iterate through changed files
* to emit the those files and manage semantic diagnostics cache as well
* Store the list of files that update signature during the emit
*
* @internal
*/
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]) {
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
}
storeFilesChangingSignatureDuringEmit?: boolean;
/**
* Creates a builder thats just abstraction over program and can be used with watch
* Gets the current time
*
* @internal
*/
export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): BuilderProgram;
export function createAbstractBuilder(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderProgram;
export function createAbstractBuilder(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderProgram {
const { newProgram, configFileParsingDiagnostics: newConfigFileParsingDiagnostics } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences);
return createRedirectedBuilderProgram(() => ({ program: newProgram, compilerOptions: newProgram.getCompilerOptions() }), newConfigFileParsingDiagnostics);
}
now?(): Date;
}
/**
* Builder to manage the program state changes
*/
export interface BuilderProgram {
/** @internal */
getState(): ReusableBuilderProgramState;
/** @internal */
saveEmitState(): SavedBuildProgramEmitState;
/** @internal */
restoreEmitState(saved: SavedBuildProgramEmitState): void;
/** @internal */
hasChangedEmitSignature?(): boolean;
/**
* Returns current program
*/
getProgram(): Program;
/**
* Returns current program that could be undefined if the program was released
*
* @internal
*/
getProgramOrUndefined(): Program | undefined;
/**
* Releases reference to the program, making all the other operations that need program to fail.
*
* @internal
*/
releaseProgram(): void;
/**
* Get compiler options of the program
*/
getCompilerOptions(): CompilerOptions;
/**
* Get the source file in the program with file name
*/
getSourceFile(fileName: string): SourceFile | undefined;
/**
* Get a list of files in the program
*/
getSourceFiles(): readonly SourceFile[];
/**
* Get the diagnostics for compiler options
*/
getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Get the diagnostics that dont belong to any file
*/
getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Get the diagnostics from config file parsing
*/
getConfigFileParsingDiagnostics(): readonly Diagnostic[];
/**
* Get the syntax diagnostics, for all source files if source file is not supplied
*/
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Get the declaration diagnostics, for all source files if source file is not supplied
*/
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[];
/**
* Get all the dependencies of the file
*/
getAllDependencies(sourceFile: SourceFile): readonly string[];
/**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here
* Note that it is assumed that when asked about semantic diagnostics through this API,
* the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
* In case of SemanticDiagnosticsBuilderProgram if the source file is not provided,
* it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics
*/
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Emits the JavaScript and declaration files.
* When targetSource file is specified, emits the files corresponding to that source file,
* otherwise for the whole program.
* In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified,
* it is assumed that that file is handled from affected file list. If targetSourceFile is not specified,
* it will only emit all the affected files instead of whole program
*
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
* in that order would be used to write the files
*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
/** @internal */
emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
/**
* Get the current directory of the program
*/
getCurrentDirectory(): string;
/** @internal */
close(): void;
}
/**
* The builder that caches the semantic diagnostics for the program and handles the changed files and affected files
*/
export interface SemanticDiagnosticsBuilderProgram extends BuilderProgram {
/**
* Gets the semantic diagnostics from the program for the next affected file and caches it
* Returns undefined if the iteration is complete
*/
getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult<readonly Diagnostic[]>;
}
/**
* 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 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
* in that order would be used to write the files
*/
emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult>;
}
/**
* Create the builder to manage semantic diagnostics and cache them
*/
export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]) {
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
}
/**
* Create the builder that can handle the changes in program and iterate through changed files
* to emit the those files and manage semantic diagnostics cache as well
*/
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]) {
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
}
/**
* Creates a builder thats just abstraction over program and can be used with watch
*/
export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): BuilderProgram;
export function createAbstractBuilder(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderProgram;
export function createAbstractBuilder(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderProgram {
const { newProgram, configFileParsingDiagnostics: newConfigFileParsingDiagnostics } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences);
return createRedirectedBuilderProgram(() => ({ program: newProgram, compilerOptions: newProgram.getCompilerOptions() }), newConfigFileParsingDiagnostics);
}
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -1,14 +1,14 @@
namespace ts {
export interface EmitOutput {
outputFiles: OutputFile[];
emitSkipped: boolean;
/* @internal */ diagnostics: readonly Diagnostic[];
}
import { Diagnostic, WriteFileCallbackData } from "./_namespaces/ts";
export interface OutputFile {
name: string;
writeByteOrderMark: boolean;
text: string;
/* @internal */ data?: WriteFileCallbackData;
}
export interface EmitOutput {
outputFiles: OutputFile[];
emitSkipped: boolean;
/** @internal */ diagnostics: readonly Diagnostic[];
}
export interface OutputFile {
name: string;
writeByteOrderMark: boolean;
text: string;
/** @internal */ data?: WriteFileCallbackData;
}
+44898 -44690
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2748 -2351
View File
File diff suppressed because it is too large Load Diff
+153 -155
View File
@@ -1,157 +1,155 @@
namespace ts {
// WARNING: The script `configurePrerelease.ts` uses a regexp to parse out these values.
// If changing the text in this section, be sure to test `configurePrerelease` too.
export const versionMajorMinor = "5.0";
// The following is baselined as a literal template type without intervention
/** The version of the TypeScript compiler release */
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
export const version: string = `${versionMajorMinor}.0-dev`;
// WARNING: The script `configurePrerelease.ts` uses a regexp to parse out these values.
// If changing the text in this section, be sure to test `configurePrerelease` too.
export const versionMajorMinor = "5.0";
// The following is baselined as a literal template type without intervention
/** The version of the TypeScript compiler release */
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
export const version: string = `${versionMajorMinor}.0-dev`;
/**
* Type of objects whose values are all of the same type.
* The `in` and `for-in` operators can *not* be safely used,
* since `Object.prototype` may be modified by outside code.
*/
export interface MapLike<T> {
[index: string]: T;
}
export interface SortedReadonlyArray<T> extends ReadonlyArray<T> {
" __sortedArrayBrand": any;
}
export interface SortedArray<T> extends Array<T> {
" __sortedArrayBrand": any;
}
/** Common read methods for ES6 Map/Set. */
export interface ReadonlyCollection<K> {
readonly size: number;
has(key: K): boolean;
keys(): Iterator<K>;
}
/** Common write methods for ES6 Map/Set. */
export interface Collection<K> extends ReadonlyCollection<K> {
delete(key: K): boolean;
clear(): void;
}
/** ES6 Map interface, only read methods included. */
export interface ReadonlyESMap<K, V> extends ReadonlyCollection<K> {
get(key: K): V | undefined;
values(): Iterator<V>;
entries(): Iterator<[K, V]>;
forEach(action: (value: V, key: K) => void): void;
}
/**
* ES6 Map interface, only read methods included.
*/
export interface ReadonlyMap<T> extends ReadonlyESMap<string, T> {
}
/** ES6 Map interface. */
export interface ESMap<K, V> extends ReadonlyESMap<K, V>, Collection<K> {
set(key: K, value: V): this;
}
/**
* ES6 Map interface.
*/
export interface Map<T> extends ESMap<string, T> {
}
/* @internal */
export interface MapConstructor {
// eslint-disable-next-line @typescript-eslint/prefer-function-type
new <K, V>(iterable?: readonly (readonly [K, V])[] | ReadonlyESMap<K, V>): ESMap<K, V>;
}
/** ES6 Set interface, only read methods included. */
export interface ReadonlySet<T> extends ReadonlyCollection<T> {
has(value: T): boolean;
values(): Iterator<T>;
entries(): Iterator<[T, T]>;
forEach(action: (value: T, key: T) => void): void;
}
/** ES6 Set interface. */
export interface Set<T> extends ReadonlySet<T>, Collection<T> {
add(value: T): this;
delete(value: T): boolean;
}
/* @internal */
export interface SetConstructor {
// eslint-disable-next-line @typescript-eslint/prefer-function-type
new <T>(iterable?: readonly T[] | ReadonlySet<T>): Set<T>;
}
/** ES6 Iterator type. */
export interface Iterator<T> {
next(): { value: T, done?: false } | { value: void, done: true };
}
/** Array that is only intended to be pushed to, never read. */
export interface Push<T> {
push(...values: T[]): void;
/* @internal*/ readonly length: number;
}
/* @internal */
export type EqualityComparer<T> = (a: T, b: T) => boolean;
/* @internal */
export type Comparer<T> = (a: T, b: T) => Comparison;
/* @internal */
export const enum Comparison {
LessThan = -1,
EqualTo = 0,
GreaterThan = 1
}
/* @internal */
namespace NativeCollections {
declare const self: any;
const globals = typeof globalThis !== "undefined" ? globalThis :
typeof global !== "undefined" ? global :
typeof self !== "undefined" ? self :
undefined;
/**
* Returns the native Map implementation if it is available and compatible (i.e. supports iteration).
*/
export function tryGetNativeMap(): MapConstructor {
// Internet Explorer's Map doesn't support iteration, so don't use it.
const gMap = globals?.Map;
// eslint-disable-next-line local/no-in-operator
const constructor = typeof gMap !== "undefined" && "entries" in gMap.prototype && new gMap([[0, 0]]).size === 1 ? gMap : undefined;
if (!constructor) {
throw new Error("No compatible Map implementation found.");
}
return constructor;
}
/**
* Returns the native Set implementation if it is available and compatible (i.e. supports iteration).
*/
export function tryGetNativeSet(): SetConstructor {
// Internet Explorer's Set doesn't support iteration, so don't use it.
const gSet = globals?.Set;
// eslint-disable-next-line local/no-in-operator
const constructor = typeof gSet !== "undefined" && "entries" in gSet.prototype && new gSet([0]).size === 1 ? gSet : undefined;
if (!constructor) {
throw new Error("No compatible Set implementation found.");
}
return constructor;
}
}
/* @internal */
export const Map = NativeCollections.tryGetNativeMap();
/* @internal */
export const Set = NativeCollections.tryGetNativeSet();
/**
* Type of objects whose values are all of the same type.
* The `in` and `for-in` operators can *not* be safely used,
* since `Object.prototype` may be modified by outside code.
*/
export interface MapLike<T> {
[index: string]: T;
}
export interface SortedReadonlyArray<T> extends ReadonlyArray<T> {
" __sortedArrayBrand": any;
}
export interface SortedArray<T> extends Array<T> {
" __sortedArrayBrand": any;
}
/** Common read methods for ES6 Map/Set. */
export interface ReadonlyCollection<K> {
readonly size: number;
has(key: K): boolean;
keys(): Iterator<K>;
}
/** Common write methods for ES6 Map/Set. */
export interface Collection<K> extends ReadonlyCollection<K> {
delete(key: K): boolean;
clear(): void;
}
/** ES6 Map interface, only read methods included. */
export interface ReadonlyESMap<K, V> extends ReadonlyCollection<K> {
get(key: K): V | undefined;
values(): Iterator<V>;
entries(): Iterator<[K, V]>;
forEach(action: (value: V, key: K) => void): void;
}
/**
* ES6 Map interface, only read methods included.
*/
export interface ReadonlyMap<T> extends ReadonlyESMap<string, T> {
}
/** ES6 Map interface. */
export interface ESMap<K, V> extends ReadonlyESMap<K, V>, Collection<K> {
set(key: K, value: V): this;
}
/**
* ES6 Map interface.
*/
export interface Map<T> extends ESMap<string, T> {
}
/** @internal */
export interface MapConstructor {
// eslint-disable-next-line @typescript-eslint/prefer-function-type
new <K, V>(iterable?: readonly (readonly [K, V])[] | ReadonlyESMap<K, V>): ESMap<K, V>;
}
/** ES6 Set interface, only read methods included. */
export interface ReadonlySet<T> extends ReadonlyCollection<T> {
has(value: T): boolean;
values(): Iterator<T>;
entries(): Iterator<[T, T]>;
forEach(action: (value: T, key: T) => void): void;
}
/** ES6 Set interface. */
export interface Set<T> extends ReadonlySet<T>, Collection<T> {
add(value: T): this;
delete(value: T): boolean;
}
/** @internal */
export interface SetConstructor {
// eslint-disable-next-line @typescript-eslint/prefer-function-type
new <T>(iterable?: readonly T[] | ReadonlySet<T>): Set<T>;
}
/** ES6 Iterator type. */
export interface Iterator<T> {
next(): { value: T, done?: false } | { value: void, done: true };
}
/** Array that is only intended to be pushed to, never read. */
export interface Push<T> {
push(...values: T[]): void;
/** @internal */ readonly length: number;
}
/** @internal */
export type EqualityComparer<T> = (a: T, b: T) => boolean;
/** @internal */
export type Comparer<T> = (a: T, b: T) => Comparison;
/** @internal */
export const enum Comparison {
LessThan = -1,
EqualTo = 0,
GreaterThan = 1
}
/** @internal */
namespace NativeCollections {
declare const self: any;
const globals = typeof globalThis !== "undefined" ? globalThis :
typeof global !== "undefined" ? global :
typeof self !== "undefined" ? self :
undefined;
/**
* Returns the native Map implementation if it is available and compatible (i.e. supports iteration).
*/
export function tryGetNativeMap(): MapConstructor {
// Internet Explorer's Map doesn't support iteration, so don't use it.
const gMap = globals?.Map;
// eslint-disable-next-line local/no-in-operator
const constructor = typeof gMap !== "undefined" && "entries" in gMap.prototype && new gMap([[0, 0]]).size === 1 ? gMap : undefined;
if (!constructor) {
throw new Error("No compatible Map implementation found.");
}
return constructor;
}
/**
* Returns the native Set implementation if it is available and compatible (i.e. supports iteration).
*/
export function tryGetNativeSet(): SetConstructor {
// Internet Explorer's Set doesn't support iteration, so don't use it.
const gSet = globals?.Set;
// eslint-disable-next-line local/no-in-operator
const constructor = typeof gSet !== "undefined" && "entries" in gSet.prototype && new gSet([0]).size === 1 ? gSet : undefined;
if (!constructor) {
throw new Error("No compatible Set implementation found.");
}
return constructor;
}
}
/** @internal */
export const Map = NativeCollections.tryGetNativeMap();
/** @internal */
export const Set = NativeCollections.tryGetNativeSet();
+1116 -753
View File
File diff suppressed because it is too large Load Diff
+5794 -5709
View File
File diff suppressed because it is too large Load Diff
+49 -47
View File
@@ -1,54 +1,56 @@
/* @internal */
namespace ts {
/**
* A `BaseNodeFactory` is an abstraction over an `ObjectAllocator` that handles caching `Node` constructors
* and allocating `Node` instances based on a set of predefined types.
*/
/* @internal */
export interface BaseNodeFactory {
createBaseSourceFileNode(kind: SyntaxKind): Node;
createBaseIdentifierNode(kind: SyntaxKind): Node;
createBasePrivateIdentifierNode(kind: SyntaxKind): Node;
createBaseTokenNode(kind: SyntaxKind): Node;
createBaseNode(kind: SyntaxKind): Node;
import { Node, objectAllocator, SyntaxKind } from "../_namespaces/ts";
/**
* A `BaseNodeFactory` is an abstraction over an `ObjectAllocator` that handles caching `Node` constructors
* and allocating `Node` instances based on a set of predefined types.
*
* @internal
*/
export interface BaseNodeFactory {
createBaseSourceFileNode(kind: SyntaxKind): Node;
createBaseIdentifierNode(kind: SyntaxKind): Node;
createBasePrivateIdentifierNode(kind: SyntaxKind): Node;
createBaseTokenNode(kind: SyntaxKind): Node;
createBaseNode(kind: SyntaxKind): Node;
}
/**
* Creates a `BaseNodeFactory` which can be used to create `Node` instances from the constructors provided by the object allocator.
*
* @internal
*/
export function createBaseNodeFactory(): BaseNodeFactory {
let NodeConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let PrivateIdentifierConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
return {
createBaseSourceFileNode,
createBaseIdentifierNode,
createBasePrivateIdentifierNode,
createBaseTokenNode,
createBaseNode
};
function createBaseSourceFileNode(kind: SyntaxKind): Node {
return new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, /*pos*/ -1, /*end*/ -1);
}
/**
* Creates a `BaseNodeFactory` which can be used to create `Node` instances from the constructors provided by the object allocator.
*/
export function createBaseNodeFactory(): BaseNodeFactory {
let NodeConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let PrivateIdentifierConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
function createBaseIdentifierNode(kind: SyntaxKind): Node {
return new (IdentifierConstructor || (IdentifierConstructor = objectAllocator.getIdentifierConstructor()))(kind, /*pos*/ -1, /*end*/ -1);
}
return {
createBaseSourceFileNode,
createBaseIdentifierNode,
createBasePrivateIdentifierNode,
createBaseTokenNode,
createBaseNode
};
function createBasePrivateIdentifierNode(kind: SyntaxKind): Node {
return new (PrivateIdentifierConstructor || (PrivateIdentifierConstructor = objectAllocator.getPrivateIdentifierConstructor()))(kind, /*pos*/ -1, /*end*/ -1);
}
function createBaseSourceFileNode(kind: SyntaxKind): Node {
return new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, /*pos*/ -1, /*end*/ -1);
}
function createBaseTokenNode(kind: SyntaxKind): Node {
return new (TokenConstructor || (TokenConstructor = objectAllocator.getTokenConstructor()))(kind, /*pos*/ -1, /*end*/ -1);
}
function createBaseIdentifierNode(kind: SyntaxKind): Node {
return new (IdentifierConstructor || (IdentifierConstructor = objectAllocator.getIdentifierConstructor()))(kind, /*pos*/ -1, /*end*/ -1);
}
function createBasePrivateIdentifierNode(kind: SyntaxKind): Node {
return new (PrivateIdentifierConstructor || (PrivateIdentifierConstructor = objectAllocator.getPrivateIdentifierConstructor()))(kind, /*pos*/ -1, /*end*/ -1);
}
function createBaseTokenNode(kind: SyntaxKind): Node {
return new (TokenConstructor || (TokenConstructor = objectAllocator.getTokenConstructor()))(kind, /*pos*/ -1, /*end*/ -1);
}
function createBaseNode(kind: SyntaxKind): Node {
return new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, /*pos*/ -1, /*end*/ -1);
}
function createBaseNode(kind: SyntaxKind): Node {
return new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, /*pos*/ -1, /*end*/ -1);
}
}
File diff suppressed because it is too large Load Diff
+285 -276
View File
@@ -1,294 +1,303 @@
namespace ts {
/**
* Associates a node with the current transformation, initializing
* various transient transformation properties.
* @internal
*/
export function getOrCreateEmitNode(node: Node): EmitNode {
if (!node.emitNode) {
if (isParseTreeNode(node)) {
// To avoid holding onto transformation artifacts, we keep track of any
// parse tree node we are annotating. This allows us to clean them up after
// all transformations have completed.
if (node.kind === SyntaxKind.SourceFile) {
return node.emitNode = { annotatedNodes: [node] } as EmitNode;
}
import {
AccessExpression, append, appendIfUnique, Debug, EmitFlags, EmitHelper, EmitNode, getParseTreeNode,
getSourceFileOfNode, isParseTreeNode, Node, orderedRemoveItem, SnippetElement, some, SourceFile, SourceMapRange,
SyntaxKind, SynthesizedComment, TextRange, TypeNode,
} from "../_namespaces/ts";
const sourceFile = getSourceFileOfNode(getParseTreeNode(getSourceFileOfNode(node))) ?? Debug.fail("Could not determine parsed source file.");
getOrCreateEmitNode(sourceFile).annotatedNodes!.push(node);
/**
* Associates a node with the current transformation, initializing
* various transient transformation properties.
* @internal
*/
export function getOrCreateEmitNode(node: Node): EmitNode {
if (!node.emitNode) {
if (isParseTreeNode(node)) {
// To avoid holding onto transformation artifacts, we keep track of any
// parse tree node we are annotating. This allows us to clean them up after
// all transformations have completed.
if (node.kind === SyntaxKind.SourceFile) {
return node.emitNode = { annotatedNodes: [node] } as EmitNode;
}
node.emitNode = {} as EmitNode;
}
else {
Debug.assert(!(node.emitNode.flags & EmitFlags.Immutable), "Invalid attempt to mutate an immutable node.");
}
return node.emitNode;
}
/**
* Clears any `EmitNode` entries from parse-tree nodes.
* @param sourceFile A source file.
*/
export function disposeEmitNodes(sourceFile: SourceFile | undefined) {
// During transformation we may need to annotate a parse tree node with transient
// transformation properties. As parse tree nodes live longer than transformation
// nodes, we need to make sure we reclaim any memory allocated for custom ranges
// from these nodes to ensure we do not hold onto entire subtrees just for position
// information. We also need to reset these nodes to a pre-transformation state
// for incremental parsing scenarios so that we do not impact later emit.
const annotatedNodes = getSourceFileOfNode(getParseTreeNode(sourceFile))?.emitNode?.annotatedNodes;
if (annotatedNodes) {
for (const node of annotatedNodes) {
node.emitNode = undefined;
}
}
}
/**
* 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;
}
/**
* Sets flags that control emit behavior of a node.
*/
export function setEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
getOrCreateEmitNode(node).flags = emitFlags;
return node;
}
/**
* Sets flags that control emit behavior of a node.
*/
/* @internal */
export function addEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
const emitNode = getOrCreateEmitNode(node);
emitNode.flags = emitNode.flags | emitFlags;
return node;
}
/**
* Gets a custom text range to use when emitting source maps.
*/
export function getSourceMapRange(node: Node): SourceMapRange {
return node.emitNode?.sourceMapRange ?? node;
}
/**
* Sets a custom text range to use when emitting source maps.
*/
export function setSourceMapRange<T extends Node>(node: T, range: SourceMapRange | undefined) {
getOrCreateEmitNode(node).sourceMapRange = range;
return node;
}
/**
* Gets the TextRange to use for source maps for a token of a node.
*/
export function getTokenSourceMapRange(node: Node, token: SyntaxKind): SourceMapRange | undefined {
return node.emitNode?.tokenSourceMapRanges?.[token];
}
/**
* Sets the TextRange to use for source maps for a token of a node.
*/
export function setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: SourceMapRange | undefined) {
const emitNode = getOrCreateEmitNode(node);
const tokenSourceMapRanges = emitNode.tokenSourceMapRanges ?? (emitNode.tokenSourceMapRanges = []);
tokenSourceMapRanges[token] = range;
return node;
}
/**
* Gets a custom text range to use when emitting comments.
*/
/*@internal*/
export function getStartsOnNewLine(node: Node) {
return node.emitNode?.startsOnNewLine;
}
/**
* Sets a custom text range to use when emitting comments.
*/
/*@internal*/
export function setStartsOnNewLine<T extends Node>(node: T, newLine: boolean) {
getOrCreateEmitNode(node).startsOnNewLine = newLine;
return node;
}
/**
* Gets a custom text range to use when emitting comments.
*/
export function getCommentRange(node: Node): TextRange {
return node.emitNode?.commentRange ?? node;
}
/**
* Sets a custom text range to use when emitting comments.
*/
export function setCommentRange<T extends Node>(node: T, range: TextRange) {
getOrCreateEmitNode(node).commentRange = range;
return node;
}
export function getSyntheticLeadingComments(node: Node): SynthesizedComment[] | undefined {
return node.emitNode?.leadingComments;
}
export function setSyntheticLeadingComments<T extends Node>(node: T, comments: SynthesizedComment[] | undefined) {
getOrCreateEmitNode(node).leadingComments = comments;
return node;
}
export function addSyntheticLeadingComment<T extends Node>(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) {
return setSyntheticLeadingComments(node, append<SynthesizedComment>(getSyntheticLeadingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text }));
}
export function getSyntheticTrailingComments(node: Node): SynthesizedComment[] | undefined {
return node.emitNode?.trailingComments;
}
export function setSyntheticTrailingComments<T extends Node>(node: T, comments: SynthesizedComment[] | undefined) {
getOrCreateEmitNode(node).trailingComments = comments;
return node;
}
export function addSyntheticTrailingComment<T extends Node>(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) {
return setSyntheticTrailingComments(node, append<SynthesizedComment>(getSyntheticTrailingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text }));
}
export function moveSyntheticComments<T extends Node>(node: T, original: Node): T {
setSyntheticLeadingComments(node, getSyntheticLeadingComments(original));
setSyntheticTrailingComments(node, getSyntheticTrailingComments(original));
const emit = getOrCreateEmitNode(original);
emit.leadingComments = undefined;
emit.trailingComments = undefined;
return node;
}
/**
* Gets the constant value to emit for an expression representing an enum.
*/
export function getConstantValue(node: AccessExpression): string | number | undefined {
return node.emitNode?.constantValue;
}
/**
* Sets the constant value to emit for an expression.
*/
export function setConstantValue(node: AccessExpression, value: string | number): AccessExpression {
const emitNode = getOrCreateEmitNode(node);
emitNode.constantValue = value;
return node;
}
/**
* Adds an EmitHelper to a node.
*/
export function addEmitHelper<T extends Node>(node: T, helper: EmitHelper): T {
const emitNode = getOrCreateEmitNode(node);
emitNode.helpers = append(emitNode.helpers, helper);
return node;
}
/**
* Add EmitHelpers to a node.
*/
export function addEmitHelpers<T extends Node>(node: T, helpers: EmitHelper[] | undefined): T {
if (some(helpers)) {
const emitNode = getOrCreateEmitNode(node);
for (const helper of helpers) {
emitNode.helpers = appendIfUnique(emitNode.helpers, helper);
}
}
return node;
}
/**
* Removes an EmitHelper from a node.
*/
export function removeEmitHelper(node: Node, helper: EmitHelper): boolean {
const helpers = node.emitNode?.helpers;
if (helpers) {
return orderedRemoveItem(helpers, helper);
}
return false;
}
/**
* Gets the EmitHelpers of a node.
*/
export function getEmitHelpers(node: Node): EmitHelper[] | undefined {
return node.emitNode?.helpers;
}
/**
* Moves matching emit helpers from a source node to a target node.
*/
export function moveEmitHelpers(source: Node, target: Node, predicate: (helper: EmitHelper) => boolean) {
const sourceEmitNode = source.emitNode;
const sourceEmitHelpers = sourceEmitNode && sourceEmitNode.helpers;
if (!some(sourceEmitHelpers)) return;
const targetEmitNode = getOrCreateEmitNode(target);
let helpersRemoved = 0;
for (let i = 0; i < sourceEmitHelpers.length; i++) {
const helper = sourceEmitHelpers[i];
if (predicate(helper)) {
helpersRemoved++;
targetEmitNode.helpers = appendIfUnique(targetEmitNode.helpers, helper);
}
else if (helpersRemoved > 0) {
sourceEmitHelpers[i - helpersRemoved] = helper;
}
const sourceFile = getSourceFileOfNode(getParseTreeNode(getSourceFileOfNode(node))) ?? Debug.fail("Could not determine parsed source file.");
getOrCreateEmitNode(sourceFile).annotatedNodes!.push(node);
}
if (helpersRemoved > 0) {
sourceEmitHelpers.length -= helpersRemoved;
node.emitNode = {} as EmitNode;
}
else {
Debug.assert(!(node.emitNode.flags & EmitFlags.Immutable), "Invalid attempt to mutate an immutable node.");
}
return node.emitNode;
}
/**
* Clears any `EmitNode` entries from parse-tree nodes.
* @param sourceFile A source file.
*/
export function disposeEmitNodes(sourceFile: SourceFile | undefined) {
// During transformation we may need to annotate a parse tree node with transient
// transformation properties. As parse tree nodes live longer than transformation
// nodes, we need to make sure we reclaim any memory allocated for custom ranges
// from these nodes to ensure we do not hold onto entire subtrees just for position
// information. We also need to reset these nodes to a pre-transformation state
// for incremental parsing scenarios so that we do not impact later emit.
const annotatedNodes = getSourceFileOfNode(getParseTreeNode(sourceFile))?.emitNode?.annotatedNodes;
if (annotatedNodes) {
for (const node of annotatedNodes) {
node.emitNode = undefined;
}
}
}
/**
* Gets the SnippetElement of a node.
*/
/* @internal */
export function getSnippetElement(node: Node): SnippetElement | undefined {
return node.emitNode?.snippetElement;
}
/**
* 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;
}
/**
* Sets flags that control emit behavior of a node.
*/
export function setEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
getOrCreateEmitNode(node).flags = emitFlags;
return node;
}
/**
* Sets flags that control emit behavior of a node.
*
* @internal
*/
export function addEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
const emitNode = getOrCreateEmitNode(node);
emitNode.flags = emitNode.flags | emitFlags;
return node;
}
/**
* Gets a custom text range to use when emitting source maps.
*/
export function getSourceMapRange(node: Node): SourceMapRange {
return node.emitNode?.sourceMapRange ?? node;
}
/**
* Sets a custom text range to use when emitting source maps.
*/
export function setSourceMapRange<T extends Node>(node: T, range: SourceMapRange | undefined) {
getOrCreateEmitNode(node).sourceMapRange = range;
return node;
}
/**
* Gets the TextRange to use for source maps for a token of a node.
*/
export function getTokenSourceMapRange(node: Node, token: SyntaxKind): SourceMapRange | undefined {
return node.emitNode?.tokenSourceMapRanges?.[token];
}
/**
* Sets the TextRange to use for source maps for a token of a node.
*/
export function setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: SourceMapRange | undefined) {
const emitNode = getOrCreateEmitNode(node);
const tokenSourceMapRanges = emitNode.tokenSourceMapRanges ?? (emitNode.tokenSourceMapRanges = []);
tokenSourceMapRanges[token] = range;
return node;
}
/**
* Gets a custom text range to use when emitting comments.
*
* @internal
*/
export function getStartsOnNewLine(node: Node) {
return node.emitNode?.startsOnNewLine;
}
/**
* Sets a custom text range to use when emitting comments.
*
* @internal
*/
export function setStartsOnNewLine<T extends Node>(node: T, newLine: boolean) {
getOrCreateEmitNode(node).startsOnNewLine = newLine;
return node;
}
/**
* Gets a custom text range to use when emitting comments.
*/
export function getCommentRange(node: Node): TextRange {
return node.emitNode?.commentRange ?? node;
}
/**
* Sets a custom text range to use when emitting comments.
*/
export function setCommentRange<T extends Node>(node: T, range: TextRange) {
getOrCreateEmitNode(node).commentRange = range;
return node;
}
export function getSyntheticLeadingComments(node: Node): SynthesizedComment[] | undefined {
return node.emitNode?.leadingComments;
}
export function setSyntheticLeadingComments<T extends Node>(node: T, comments: SynthesizedComment[] | undefined) {
getOrCreateEmitNode(node).leadingComments = comments;
return node;
}
export function addSyntheticLeadingComment<T extends Node>(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) {
return setSyntheticLeadingComments(node, append<SynthesizedComment>(getSyntheticLeadingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text }));
}
export function getSyntheticTrailingComments(node: Node): SynthesizedComment[] | undefined {
return node.emitNode?.trailingComments;
}
export function setSyntheticTrailingComments<T extends Node>(node: T, comments: SynthesizedComment[] | undefined) {
getOrCreateEmitNode(node).trailingComments = comments;
return node;
}
export function addSyntheticTrailingComment<T extends Node>(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) {
return setSyntheticTrailingComments(node, append<SynthesizedComment>(getSyntheticTrailingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text }));
}
export function moveSyntheticComments<T extends Node>(node: T, original: Node): T {
setSyntheticLeadingComments(node, getSyntheticLeadingComments(original));
setSyntheticTrailingComments(node, getSyntheticTrailingComments(original));
const emit = getOrCreateEmitNode(original);
emit.leadingComments = undefined;
emit.trailingComments = undefined;
return node;
}
/**
* Gets the constant value to emit for an expression representing an enum.
*/
export function getConstantValue(node: AccessExpression): string | number | undefined {
return node.emitNode?.constantValue;
}
/**
* Sets the constant value to emit for an expression.
*/
export function setConstantValue(node: AccessExpression, value: string | number): AccessExpression {
const emitNode = getOrCreateEmitNode(node);
emitNode.constantValue = value;
return node;
}
/**
* Adds an EmitHelper to a node.
*/
export function addEmitHelper<T extends Node>(node: T, helper: EmitHelper): T {
const emitNode = getOrCreateEmitNode(node);
emitNode.helpers = append(emitNode.helpers, helper);
return node;
}
/**
* Sets the SnippetElement of a node.
*/
/* @internal */
export function setSnippetElement<T extends Node>(node: T, snippet: SnippetElement): T {
/**
* Add EmitHelpers to a node.
*/
export function addEmitHelpers<T extends Node>(node: T, helpers: EmitHelper[] | undefined): T {
if (some(helpers)) {
const emitNode = getOrCreateEmitNode(node);
emitNode.snippetElement = snippet;
return node;
for (const helper of helpers) {
emitNode.helpers = appendIfUnique(emitNode.helpers, helper);
}
}
return node;
}
/* @internal */
export function ignoreSourceNewlines<T extends Node>(node: T): T {
getOrCreateEmitNode(node).flags |= EmitFlags.IgnoreSourceNewlines;
return node;
/**
* Removes an EmitHelper from a node.
*/
export function removeEmitHelper(node: Node, helper: EmitHelper): boolean {
const helpers = node.emitNode?.helpers;
if (helpers) {
return orderedRemoveItem(helpers, helper);
}
return false;
}
/**
* Gets the EmitHelpers of a node.
*/
export function getEmitHelpers(node: Node): EmitHelper[] | undefined {
return node.emitNode?.helpers;
}
/* @internal */
export function setTypeNode<T extends Node>(node: T, type: TypeNode): T {
const emitNode = getOrCreateEmitNode(node);
emitNode.typeNode = type;
return node;
/**
* Moves matching emit helpers from a source node to a target node.
*/
export function moveEmitHelpers(source: Node, target: Node, predicate: (helper: EmitHelper) => boolean) {
const sourceEmitNode = source.emitNode;
const sourceEmitHelpers = sourceEmitNode && sourceEmitNode.helpers;
if (!some(sourceEmitHelpers)) return;
const targetEmitNode = getOrCreateEmitNode(target);
let helpersRemoved = 0;
for (let i = 0; i < sourceEmitHelpers.length; i++) {
const helper = sourceEmitHelpers[i];
if (predicate(helper)) {
helpersRemoved++;
targetEmitNode.helpers = appendIfUnique(targetEmitNode.helpers, helper);
}
else if (helpersRemoved > 0) {
sourceEmitHelpers[i - helpersRemoved] = helper;
}
}
/* @internal */
export function getTypeNode<T extends Node>(node: T): TypeNode | undefined {
return node.emitNode?.typeNode;
if (helpersRemoved > 0) {
sourceEmitHelpers.length -= helpersRemoved;
}
}
/**
* Gets the SnippetElement of a node.
*
* @internal
*/
export function getSnippetElement(node: Node): SnippetElement | undefined {
return node.emitNode?.snippetElement;
}
/**
* Sets the SnippetElement of a node.
*
* @internal
*/
export function setSnippetElement<T extends Node>(node: T, snippet: SnippetElement): T {
const emitNode = getOrCreateEmitNode(node);
emitNode.snippetElement = snippet;
return node;
}
/** @internal */
export function ignoreSourceNewlines<T extends Node>(node: T): T {
getOrCreateEmitNode(node).flags |= EmitFlags.IgnoreSourceNewlines;
return node;
}
/** @internal */
export function setTypeNode<T extends Node>(node: T, type: TypeNode): T {
const emitNode = getOrCreateEmitNode(node);
emitNode.typeNode = type;
return node;
}
/** @internal */
export function getTypeNode<T extends Node>(node: T): TypeNode | undefined {
return node.emitNode?.typeNode;
}
+130 -122
View File
@@ -1,137 +1,145 @@
/* @internal */
namespace ts {
export function createNodeConverters(factory: NodeFactory): NodeConverters {
return {
convertToFunctionBlock,
convertToFunctionExpression,
convertToArrayAssignmentElement,
convertToObjectAssignmentElement,
convertToAssignmentPattern,
convertToObjectAssignmentPattern,
convertToArrayAssignmentPattern,
convertToAssignmentElementTarget,
};
import {
ArrayBindingOrAssignmentElement, ArrayBindingOrAssignmentPattern, AssignmentPattern,
BindingOrAssignmentElementTarget, BindingOrAssignmentPattern, Block, cast, ConciseBody, Debug, Expression,
FunctionDeclaration, getStartsOnNewLine, isArrayBindingPattern, isArrayLiteralExpression, isBindingElement,
isBindingPattern, isBlock, isExpression, isIdentifier, isObjectBindingPattern, isObjectLiteralElementLike,
isObjectLiteralExpression, map, NodeConverters, NodeFactory, notImplemented, ObjectBindingOrAssignmentElement,
ObjectBindingOrAssignmentPattern, setOriginalNode, setStartsOnNewLine, setTextRange, SyntaxKind,
} from "../_namespaces/ts";
function convertToFunctionBlock(node: ConciseBody, multiLine?: boolean): Block {
if (isBlock(node)) return node;
const returnStatement = factory.createReturnStatement(node);
setTextRange(returnStatement, node);
const body = factory.createBlock([returnStatement], multiLine);
setTextRange(body, node);
return body;
/** @internal */
export function createNodeConverters(factory: NodeFactory): NodeConverters {
return {
convertToFunctionBlock,
convertToFunctionExpression,
convertToArrayAssignmentElement,
convertToObjectAssignmentElement,
convertToAssignmentPattern,
convertToObjectAssignmentPattern,
convertToArrayAssignmentPattern,
convertToAssignmentElementTarget,
};
function convertToFunctionBlock(node: ConciseBody, multiLine?: boolean): Block {
if (isBlock(node)) return node;
const returnStatement = factory.createReturnStatement(node);
setTextRange(returnStatement, node);
const body = factory.createBlock([returnStatement], multiLine);
setTextRange(body, node);
return body;
}
function convertToFunctionExpression(node: FunctionDeclaration) {
if (!node.body) return Debug.fail(`Cannot convert a FunctionDeclaration without a body`);
const updated = factory.createFunctionExpression(
node.modifiers,
node.asteriskToken,
node.name,
node.typeParameters,
node.parameters,
node.type,
node.body
);
setOriginalNode(updated, node);
setTextRange(updated, node);
if (getStartsOnNewLine(node)) {
setStartsOnNewLine(updated, /*newLine*/ true);
}
return updated;
}
function convertToFunctionExpression(node: FunctionDeclaration) {
if (!node.body) return Debug.fail(`Cannot convert a FunctionDeclaration without a body`);
const updated = factory.createFunctionExpression(
node.modifiers,
node.asteriskToken,
node.name,
node.typeParameters,
node.parameters,
node.type,
node.body
);
setOriginalNode(updated, node);
setTextRange(updated, node);
if (getStartsOnNewLine(node)) {
setStartsOnNewLine(updated, /*newLine*/ true);
}
return updated;
}
function convertToArrayAssignmentElement(element: ArrayBindingOrAssignmentElement) {
if (isBindingElement(element)) {
if (element.dotDotDotToken) {
Debug.assertNode(element.name, isIdentifier);
return setOriginalNode(setTextRange(factory.createSpreadElement(element.name), element), element);
}
const expression = convertToAssignmentElementTarget(element.name);
return element.initializer
? setOriginalNode(
setTextRange(
factory.createAssignment(expression, element.initializer),
element
),
element
)
: expression;
}
return cast(element, isExpression);
}
function convertToObjectAssignmentElement(element: ObjectBindingOrAssignmentElement) {
if (isBindingElement(element)) {
if (element.dotDotDotToken) {
Debug.assertNode(element.name, isIdentifier);
return setOriginalNode(setTextRange(factory.createSpreadAssignment(element.name), element), element);
}
if (element.propertyName) {
const expression = convertToAssignmentElementTarget(element.name);
return setOriginalNode(setTextRange(factory.createPropertyAssignment(element.propertyName, element.initializer ? factory.createAssignment(expression, element.initializer) : expression), element), element);
}
function convertToArrayAssignmentElement(element: ArrayBindingOrAssignmentElement) {
if (isBindingElement(element)) {
if (element.dotDotDotToken) {
Debug.assertNode(element.name, isIdentifier);
return setOriginalNode(setTextRange(factory.createShorthandPropertyAssignment(element.name, element.initializer), element), element);
return setOriginalNode(setTextRange(factory.createSpreadElement(element.name), element), element);
}
return cast(element, isObjectLiteralElementLike);
}
function convertToAssignmentPattern(node: BindingOrAssignmentPattern): AssignmentPattern {
switch (node.kind) {
case SyntaxKind.ArrayBindingPattern:
case SyntaxKind.ArrayLiteralExpression:
return convertToArrayAssignmentPattern(node);
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ObjectLiteralExpression:
return convertToObjectAssignmentPattern(node);
}
}
function convertToObjectAssignmentPattern(node: ObjectBindingOrAssignmentPattern) {
if (isObjectBindingPattern(node)) {
return setOriginalNode(
const expression = convertToAssignmentElementTarget(element.name);
return element.initializer
? setOriginalNode(
setTextRange(
factory.createObjectLiteralExpression(map(node.elements, convertToObjectAssignmentElement)),
node
factory.createAssignment(expression, element.initializer),
element
),
node
);
element
)
: expression;
}
return cast(element, isExpression);
}
function convertToObjectAssignmentElement(element: ObjectBindingOrAssignmentElement) {
if (isBindingElement(element)) {
if (element.dotDotDotToken) {
Debug.assertNode(element.name, isIdentifier);
return setOriginalNode(setTextRange(factory.createSpreadAssignment(element.name), element), element);
}
return cast(node, isObjectLiteralExpression);
if (element.propertyName) {
const expression = convertToAssignmentElementTarget(element.name);
return setOriginalNode(setTextRange(factory.createPropertyAssignment(element.propertyName, element.initializer ? factory.createAssignment(expression, element.initializer) : expression), element), element);
}
Debug.assertNode(element.name, isIdentifier);
return setOriginalNode(setTextRange(factory.createShorthandPropertyAssignment(element.name, element.initializer), element), element);
}
function convertToArrayAssignmentPattern(node: ArrayBindingOrAssignmentPattern) {
if (isArrayBindingPattern(node)) {
return setOriginalNode(
setTextRange(
factory.createArrayLiteralExpression(map(node.elements, convertToArrayAssignmentElement)),
node
),
node
);
}
return cast(node, isArrayLiteralExpression);
}
return cast(element, isObjectLiteralElementLike);
}
function convertToAssignmentElementTarget(node: BindingOrAssignmentElementTarget): Expression {
if (isBindingPattern(node)) {
return convertToAssignmentPattern(node);
}
function convertToAssignmentPattern(node: BindingOrAssignmentPattern): AssignmentPattern {
switch (node.kind) {
case SyntaxKind.ArrayBindingPattern:
case SyntaxKind.ArrayLiteralExpression:
return convertToArrayAssignmentPattern(node);
return cast(node, isExpression);
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ObjectLiteralExpression:
return convertToObjectAssignmentPattern(node);
}
}
export const nullNodeConverters: NodeConverters = {
convertToFunctionBlock: notImplemented,
convertToFunctionExpression: notImplemented,
convertToArrayAssignmentElement: notImplemented,
convertToObjectAssignmentElement: notImplemented,
convertToAssignmentPattern: notImplemented,
convertToObjectAssignmentPattern: notImplemented,
convertToArrayAssignmentPattern: notImplemented,
convertToAssignmentElementTarget: notImplemented,
};
}
function convertToObjectAssignmentPattern(node: ObjectBindingOrAssignmentPattern) {
if (isObjectBindingPattern(node)) {
return setOriginalNode(
setTextRange(
factory.createObjectLiteralExpression(map(node.elements, convertToObjectAssignmentElement)),
node
),
node
);
}
return cast(node, isObjectLiteralExpression);
}
function convertToArrayAssignmentPattern(node: ArrayBindingOrAssignmentPattern) {
if (isArrayBindingPattern(node)) {
return setOriginalNode(
setTextRange(
factory.createArrayLiteralExpression(map(node.elements, convertToArrayAssignmentElement)),
node
),
node
);
}
return cast(node, isArrayLiteralExpression);
}
function convertToAssignmentElementTarget(node: BindingOrAssignmentElementTarget): Expression {
if (isBindingPattern(node)) {
return convertToAssignmentPattern(node);
}
return cast(node, isExpression);
}
}
/** @internal */
export const nullNodeConverters: NodeConverters = {
convertToFunctionBlock: notImplemented,
convertToFunctionExpression: notImplemented,
convertToArrayAssignmentElement: notImplemented,
convertToObjectAssignmentElement: notImplemented,
convertToAssignmentPattern: notImplemented,
convertToObjectAssignmentPattern: notImplemented,
convertToArrayAssignmentPattern: notImplemented,
convertToAssignmentElementTarget: notImplemented,
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+42 -42
View File
@@ -1,45 +1,45 @@
namespace ts {
export function setTextRange<T extends TextRange>(range: T, location: TextRange | undefined): T {
return location ? setTextRangePosEnd(range, location.pos, location.end) : range;
}
import { HasDecorators, HasModifiers, Node, setTextRangePosEnd, SyntaxKind, TextRange } from "../_namespaces/ts";
export function canHaveModifiers(node: Node): node is HasModifiers {
const kind = node.kind;
return kind === SyntaxKind.TypeParameter
|| kind === SyntaxKind.Parameter
|| kind === SyntaxKind.PropertySignature
|| kind === SyntaxKind.PropertyDeclaration
|| kind === SyntaxKind.MethodSignature
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.Constructor
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor
|| kind === SyntaxKind.IndexSignature
|| kind === SyntaxKind.ConstructorType
|| kind === SyntaxKind.FunctionExpression
|| kind === SyntaxKind.ArrowFunction
|| kind === SyntaxKind.ClassExpression
|| kind === SyntaxKind.VariableStatement
|| kind === SyntaxKind.FunctionDeclaration
|| kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.InterfaceDeclaration
|| kind === SyntaxKind.TypeAliasDeclaration
|| kind === SyntaxKind.EnumDeclaration
|| kind === SyntaxKind.ModuleDeclaration
|| kind === SyntaxKind.ImportEqualsDeclaration
|| kind === SyntaxKind.ImportDeclaration
|| kind === SyntaxKind.ExportAssignment
|| kind === SyntaxKind.ExportDeclaration;
}
export function setTextRange<T extends TextRange>(range: T, location: TextRange | undefined): T {
return location ? setTextRangePosEnd(range, location.pos, location.end) : range;
}
export function canHaveDecorators(node: Node): node is HasDecorators {
const kind = node.kind;
return kind === SyntaxKind.Parameter
|| kind === SyntaxKind.PropertyDeclaration
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor
|| kind === SyntaxKind.ClassExpression
|| kind === SyntaxKind.ClassDeclaration;
}
export function canHaveModifiers(node: Node): node is HasModifiers {
const kind = node.kind;
return kind === SyntaxKind.TypeParameter
|| kind === SyntaxKind.Parameter
|| kind === SyntaxKind.PropertySignature
|| kind === SyntaxKind.PropertyDeclaration
|| kind === SyntaxKind.MethodSignature
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.Constructor
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor
|| kind === SyntaxKind.IndexSignature
|| kind === SyntaxKind.ConstructorType
|| kind === SyntaxKind.FunctionExpression
|| kind === SyntaxKind.ArrowFunction
|| kind === SyntaxKind.ClassExpression
|| kind === SyntaxKind.VariableStatement
|| kind === SyntaxKind.FunctionDeclaration
|| kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.InterfaceDeclaration
|| kind === SyntaxKind.TypeAliasDeclaration
|| kind === SyntaxKind.EnumDeclaration
|| kind === SyntaxKind.ModuleDeclaration
|| kind === SyntaxKind.ImportEqualsDeclaration
|| kind === SyntaxKind.ImportDeclaration
|| kind === SyntaxKind.ExportAssignment
|| kind === SyntaxKind.ExportDeclaration;
}
export function canHaveDecorators(node: Node): node is HasDecorators {
const kind = node.kind;
return kind === SyntaxKind.Parameter
|| kind === SyntaxKind.PropertyDeclaration
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor
|| kind === SyntaxKind.ClassExpression
|| kind === SyntaxKind.ClassDeclaration;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+9662 -9593
View File
File diff suppressed because it is too large Load Diff
+895 -791
View File
File diff suppressed because it is too large Load Diff
+67 -41
View File
@@ -1,43 +1,69 @@
/* @internal */
namespace ts {
type PerfLogger = typeof import("@microsoft/typescript-etw");
const nullLogger: PerfLogger = {
logEvent: noop,
logErrEvent: noop,
logPerfEvent: noop,
logInfoEvent: noop,
logStartCommand: noop,
logStopCommand: noop,
logStartUpdateProgram: noop,
logStopUpdateProgram: noop,
logStartUpdateGraph: noop,
logStopUpdateGraph: noop,
logStartResolveModule: noop,
logStopResolveModule: noop,
logStartParseSourceFile: noop,
logStopParseSourceFile: noop,
logStartReadFile: noop,
logStopReadFile: noop,
logStartBindFile: noop,
logStopBindFile: noop,
logStartScheduledOperation: noop,
logStopScheduledOperation: noop,
};
import { noop } from "./_namespaces/ts";
// Load optional module to enable Event Tracing for Windows
// See https://github.com/microsoft/typescript-etw for more information
let etwModule;
try {
const etwModulePath = process.env.TS_ETW_MODULE_PATH ?? "./node_modules/@microsoft/typescript-etw";
// require() will throw an exception if the module is not found
// It may also return undefined if not installed properly
etwModule = require(etwModulePath);
}
catch (e) {
etwModule = undefined;
}
/** Performance logger that will generate ETW events if possible - check for `logEvent` member, as `etwModule` will be `{}` when browserified */
export const perfLogger: PerfLogger = etwModule && etwModule.logEvent ? etwModule : nullLogger;
/** @internal */
export interface PerfLogger {
logEvent(msg: string): void;
logErrEvent(msg: string): void;
logPerfEvent(msg: string): void;
logInfoEvent(msg: string): void;
logStartCommand(command: string, msg: string): void;
logStopCommand(command: string, msg: string): void;
logStartUpdateProgram(msg: string): void;
logStopUpdateProgram(msg: string): void;
logStartUpdateGraph(): void;
logStopUpdateGraph(): void;
logStartResolveModule(name: string): void;
logStopResolveModule(success: string): void;
logStartParseSourceFile(filename: string): void;
logStopParseSourceFile(): void;
logStartReadFile(filename: string): void;
logStopReadFile(): void;
logStartBindFile(filename: string): void;
logStopBindFile(): void;
logStartScheduledOperation(operationId: string): void;
logStopScheduledOperation(): void;
}
const nullLogger: PerfLogger = {
logEvent: noop,
logErrEvent: noop,
logPerfEvent: noop,
logInfoEvent: noop,
logStartCommand: noop,
logStopCommand: noop,
logStartUpdateProgram: noop,
logStopUpdateProgram: noop,
logStartUpdateGraph: noop,
logStopUpdateGraph: noop,
logStartResolveModule: noop,
logStopResolveModule: noop,
logStartParseSourceFile: noop,
logStopParseSourceFile: noop,
logStartReadFile: noop,
logStopReadFile: noop,
logStartBindFile: noop,
logStopBindFile: noop,
logStartScheduledOperation: noop,
logStopScheduledOperation: noop,
};
// Load optional module to enable Event Tracing for Windows
// See https://github.com/microsoft/typescript-etw for more information
let etwModule: typeof import("@microsoft/typescript-etw") | undefined;
try {
const etwModulePath = process.env.TS_ETW_MODULE_PATH ?? "./node_modules/@microsoft/typescript-etw";
// require() will throw an exception if the module is not found
// It may also return undefined if not installed properly
etwModule = require(etwModulePath);
}
catch (e) {
etwModule = undefined;
}
/**
* Performance logger that will generate ETW events if possible - check for `logEvent` member, as `etwModule` will be `{}` when browserified
*
* @internal
*/
export const perfLogger: PerfLogger = etwModule?.logEvent ? etwModule : nullLogger;
+184 -155
View File
@@ -1,168 +1,197 @@
/*@internal*/
import {
Debug, Map, noop, Performance, PerformanceHooks, sys, System, timestamp, tryGetNativePerformanceHooks,
} from "./_namespaces/ts";
/** Performance measurements for the compiler. */
namespace ts.performance {
let perfHooks: PerformanceHooks | undefined;
// when set, indicates the implementation of `Performance` to use for user timing.
// when unset, indicates user timing is unavailable or disabled.
let performanceImpl: Performance | undefined;
export interface Timer {
enter(): void;
exit(): void;
}
let perfHooks: PerformanceHooks | undefined;
// when set, indicates the implementation of `Performance` to use for user timing.
// when unset, indicates user timing is unavailable or disabled.
let performanceImpl: Performance | undefined;
export function createTimerIf(condition: boolean, measureName: string, startMarkName: string, endMarkName: string) {
return condition ? createTimer(measureName, startMarkName, endMarkName) : nullTimer;
}
/** @internal */
export interface Timer {
enter(): void;
exit(): void;
}
export function createTimer(measureName: string, startMarkName: string, endMarkName: string): Timer {
let enterCount = 0;
return {
enter,
exit
};
/** @internal */
export function createTimerIf(condition: boolean, measureName: string, startMarkName: string, endMarkName: string) {
return condition ? createTimer(measureName, startMarkName, endMarkName) : nullTimer;
}
function enter() {
if (++enterCount === 1) {
mark(startMarkName);
}
}
/** @internal */
export function createTimer(measureName: string, startMarkName: string, endMarkName: string): Timer {
let enterCount = 0;
return {
enter,
exit
};
function exit() {
if (--enterCount === 0) {
mark(endMarkName);
measure(measureName, startMarkName, endMarkName);
}
else if (enterCount < 0) {
Debug.fail("enter/exit count does not match.");
}
function enter() {
if (++enterCount === 1) {
mark(startMarkName);
}
}
export const nullTimer: Timer = { enter: noop, exit: noop };
let enabled = false;
let timeorigin = timestamp();
const marks = new Map<string, number>();
const counts = new Map<string, number>();
const durations = new Map<string, number>();
/**
* Marks a performance event.
*
* @param markName The name of the mark.
*/
export function mark(markName: string) {
if (enabled) {
const count = counts.get(markName) ?? 0;
counts.set(markName, count + 1);
marks.set(markName, timestamp());
performanceImpl?.mark(markName);
function exit() {
if (--enterCount === 0) {
mark(endMarkName);
measure(measureName, startMarkName, endMarkName);
}
}
/**
* Adds a performance measurement with the specified name.
*
* @param measureName The name of the performance measurement.
* @param startMarkName The name of the starting mark. If not supplied, the point at which the
* profiler was enabled is used.
* @param endMarkName The name of the ending mark. If not supplied, the current timestamp is
* used.
*/
export function measure(measureName: string, startMarkName?: string, endMarkName?: string) {
if (enabled) {
const end = (endMarkName !== undefined ? marks.get(endMarkName) : undefined) ?? timestamp();
const start = (startMarkName !== undefined ? marks.get(startMarkName) : undefined) ?? timeorigin;
const previousDuration = durations.get(measureName) || 0;
durations.set(measureName, previousDuration + (end - start));
performanceImpl?.measure(measureName, startMarkName, endMarkName);
}
}
/**
* Gets the number of times a marker was encountered.
*
* @param markName The name of the mark.
*/
export function getCount(markName: string) {
return counts.get(markName) || 0;
}
/**
* Gets the total duration of all measurements with the supplied name.
*
* @param measureName The name of the measure whose durations should be accumulated.
*/
export function getDuration(measureName: string) {
return durations.get(measureName) || 0;
}
/**
* Iterate over each measure, performing some action
*
* @param cb The action to perform for each measure
*/
export function forEachMeasure(cb: (measureName: string, duration: number) => void) {
durations.forEach((duration, measureName) => cb(measureName, duration));
}
export function forEachMark(cb: (markName: string) => void) {
marks.forEach((_time, markName) => cb(markName));
}
export function clearMeasures(name?: string) {
if (name !== undefined) durations.delete(name);
else durations.clear();
performanceImpl?.clearMeasures(name);
}
export function clearMarks(name?: string) {
if (name !== undefined) {
counts.delete(name);
marks.delete(name);
}
else {
counts.clear();
marks.clear();
}
performanceImpl?.clearMarks(name);
}
/**
* Indicates whether the performance API is enabled.
*/
export function isEnabled() {
return enabled;
}
/** Enables (and resets) performance measurements for the compiler. */
export function enable(system: System = sys) {
if (!enabled) {
enabled = true;
perfHooks ||= tryGetNativePerformanceHooks();
if (perfHooks) {
timeorigin = perfHooks.performance.timeOrigin;
// NodeJS's Web Performance API is currently slower than expected, but we'd still like
// to be able to leverage native trace events when node is run with either `--cpu-prof`
// or `--prof`, if we're running with our own `--generateCpuProfile` flag, or when
// running in debug mode (since its possible to generate a cpu profile while debugging).
if (perfHooks.shouldWriteNativeEvents || system?.cpuProfilingEnabled?.() || system?.debugMode) {
performanceImpl = perfHooks.performance;
}
}
}
return true;
}
/** Disables performance measurements for the compiler. */
export function disable() {
if (enabled) {
marks.clear();
counts.clear();
durations.clear();
performanceImpl = undefined;
enabled = false;
else if (enterCount < 0) {
Debug.fail("enter/exit count does not match.");
}
}
}
/** @internal */
export const nullTimer: Timer = { enter: noop, exit: noop };
let enabled = false;
let timeorigin = timestamp();
const marks = new Map<string, number>();
const counts = new Map<string, number>();
const durations = new Map<string, number>();
/**
* Marks a performance event.
*
* @param markName The name of the mark.
*
* @internal
*/
export function mark(markName: string) {
if (enabled) {
const count = counts.get(markName) ?? 0;
counts.set(markName, count + 1);
marks.set(markName, timestamp());
performanceImpl?.mark(markName);
}
}
/**
* Adds a performance measurement with the specified name.
*
* @param measureName The name of the performance measurement.
* @param startMarkName The name of the starting mark. If not supplied, the point at which the
* profiler was enabled is used.
* @param endMarkName The name of the ending mark. If not supplied, the current timestamp is
* used.
*
* @internal
*/
export function measure(measureName: string, startMarkName?: string, endMarkName?: string) {
if (enabled) {
const end = (endMarkName !== undefined ? marks.get(endMarkName) : undefined) ?? timestamp();
const start = (startMarkName !== undefined ? marks.get(startMarkName) : undefined) ?? timeorigin;
const previousDuration = durations.get(measureName) || 0;
durations.set(measureName, previousDuration + (end - start));
performanceImpl?.measure(measureName, startMarkName, endMarkName);
}
}
/**
* Gets the number of times a marker was encountered.
*
* @param markName The name of the mark.
*
* @internal
*/
export function getCount(markName: string) {
return counts.get(markName) || 0;
}
/**
* Gets the total duration of all measurements with the supplied name.
*
* @param measureName The name of the measure whose durations should be accumulated.
*
* @internal
*/
export function getDuration(measureName: string) {
return durations.get(measureName) || 0;
}
/**
* Iterate over each measure, performing some action
*
* @param cb The action to perform for each measure
*
* @internal
*/
export function forEachMeasure(cb: (measureName: string, duration: number) => void) {
durations.forEach((duration, measureName) => cb(measureName, duration));
}
/** @internal */
export function forEachMark(cb: (markName: string) => void) {
marks.forEach((_time, markName) => cb(markName));
}
/** @internal */
export function clearMeasures(name?: string) {
if (name !== undefined) durations.delete(name);
else durations.clear();
performanceImpl?.clearMeasures(name);
}
/** @internal */
export function clearMarks(name?: string) {
if (name !== undefined) {
counts.delete(name);
marks.delete(name);
}
else {
counts.clear();
marks.clear();
}
performanceImpl?.clearMarks(name);
}
/**
* Indicates whether the performance API is enabled.
*
* @internal
*/
export function isEnabled() {
return enabled;
}
/**
* Enables (and resets) performance measurements for the compiler.
*
* @internal
*/
export function enable(system: System = sys) {
if (!enabled) {
enabled = true;
perfHooks ||= tryGetNativePerformanceHooks();
if (perfHooks) {
timeorigin = perfHooks.performance.timeOrigin;
// NodeJS's Web Performance API is currently slower than expected, but we'd still like
// to be able to leverage native trace events when node is run with either `--cpu-prof`
// or `--prof`, if we're running with our own `--generateCpuProfile` flag, or when
// running in debug mode (since its possible to generate a cpu profile while debugging).
if (perfHooks.shouldWriteNativeEvents || system?.cpuProfilingEnabled?.() || system?.debugMode) {
performanceImpl = perfHooks.performance;
}
}
}
return true;
}
/**
* Disables performance measurements for the compiler.
*
* @internal
*/
export function disable() {
if (enabled) {
marks.clear();
counts.clear();
durations.clear();
performanceImpl = undefined;
enabled = false;
}
}
+135 -124
View File
@@ -1,135 +1,146 @@
/*@internal*/
namespace ts {
// The following definitions provide the minimum compatible support for the Web Performance User Timings API
// between browsers and NodeJS:
import { isNodeLikeSystem, Version, VersionRange } from "./_namespaces/ts";
export interface PerformanceHooks {
/** Indicates whether we should write native performance events */
shouldWriteNativeEvents: boolean;
performance: Performance;
PerformanceObserver: PerformanceObserverConstructor;
// The following definitions provide the minimum compatible support for the Web Performance User Timings API
// between browsers and NodeJS:
/** @internal */
export interface PerformanceHooks {
/** Indicates whether we should write native performance events */
shouldWriteNativeEvents: boolean;
performance: Performance;
PerformanceObserver: PerformanceObserverConstructor;
}
/** @internal */
export interface Performance {
mark(name: string): void;
measure(name: string, startMark?: string, endMark?: string): void;
clearMeasures(name?: string): void;
clearMarks(name?: string): void;
now(): number;
timeOrigin: number;
}
/** @internal */
export interface PerformanceEntry {
name: string;
entryType: string;
startTime: number;
duration: number;
}
/** @internal */
export interface PerformanceObserverEntryList {
getEntries(): PerformanceEntryList;
getEntriesByName(name: string, type?: string): PerformanceEntryList;
getEntriesByType(type: string): PerformanceEntryList;
}
/** @internal */
export interface PerformanceObserver {
disconnect(): void;
observe(options: { entryTypes: readonly ("mark" | "measure")[] }): void;
}
/** @internal */
export type PerformanceObserverConstructor = new (callback: (list: PerformanceObserverEntryList, observer: PerformanceObserver) => void) => PerformanceObserver;
/** @internal */
export type PerformanceEntryList = PerformanceEntry[];
// Browser globals for the Web Performance User Timings API
declare const process: any;
declare const performance: Performance | undefined;
declare const PerformanceObserver: PerformanceObserverConstructor | undefined;
// eslint-disable-next-line @typescript-eslint/naming-convention
function hasRequiredAPI(performance: Performance | undefined, PerformanceObserver: PerformanceObserverConstructor | undefined) {
return typeof performance === "object" &&
typeof performance.timeOrigin === "number" &&
typeof performance.mark === "function" &&
typeof performance.measure === "function" &&
typeof performance.now === "function" &&
typeof performance.clearMarks === "function" &&
typeof performance.clearMeasures === "function" &&
typeof PerformanceObserver === "function";
}
function tryGetWebPerformanceHooks(): PerformanceHooks | undefined {
if (typeof performance === "object" &&
typeof PerformanceObserver === "function" &&
hasRequiredAPI(performance, PerformanceObserver)) {
return {
// For now we always write native performance events when running in the browser. We may
// make this conditional in the future if we find that native web performance hooks
// in the browser also slow down compilation.
shouldWriteNativeEvents: true,
performance,
PerformanceObserver
};
}
}
export interface Performance {
mark(name: string): void;
measure(name: string, startMark?: string, endMark?: string): void;
clearMeasures(name?: string): void;
clearMarks(name?: string): void;
now(): number;
timeOrigin: number;
}
export interface PerformanceEntry {
name: string;
entryType: string;
startTime: number;
duration: number;
}
export interface PerformanceObserverEntryList {
getEntries(): PerformanceEntryList;
getEntriesByName(name: string, type?: string): PerformanceEntryList;
getEntriesByType(type: string): PerformanceEntryList;
}
export interface PerformanceObserver {
disconnect(): void;
observe(options: { entryTypes: readonly ("mark" | "measure")[] }): void;
}
export type PerformanceObserverConstructor = new (callback: (list: PerformanceObserverEntryList, observer: PerformanceObserver) => void) => PerformanceObserver;
export type PerformanceEntryList = PerformanceEntry[];
// Browser globals for the Web Performance User Timings API
declare const process: any;
declare const performance: Performance | undefined;
declare const PerformanceObserver: PerformanceObserverConstructor | undefined;
// eslint-disable-next-line @typescript-eslint/naming-convention
function hasRequiredAPI(performance: Performance | undefined, PerformanceObserver: PerformanceObserverConstructor | undefined) {
return typeof performance === "object" &&
typeof performance.timeOrigin === "number" &&
typeof performance.mark === "function" &&
typeof performance.measure === "function" &&
typeof performance.now === "function" &&
typeof performance.clearMarks === "function" &&
typeof performance.clearMeasures === "function" &&
typeof PerformanceObserver === "function";
}
function tryGetWebPerformanceHooks(): PerformanceHooks | undefined {
if (typeof performance === "object" &&
typeof PerformanceObserver === "function" &&
hasRequiredAPI(performance, PerformanceObserver)) {
return {
// For now we always write native performance events when running in the browser. We may
// make this conditional in the future if we find that native web performance hooks
// in the browser also slow down compilation.
shouldWriteNativeEvents: true,
performance,
PerformanceObserver
};
}
}
function tryGetNodePerformanceHooks(): PerformanceHooks | undefined {
if (typeof process !== "undefined" && process.nextTick && !process.browser && typeof module === "object" && typeof require === "function") {
try {
let performance: Performance;
const { performance: nodePerformance, PerformanceObserver } = require("perf_hooks") as typeof import("perf_hooks");
if (hasRequiredAPI(nodePerformance as unknown as Performance, PerformanceObserver)) {
performance = nodePerformance as unknown as Performance;
// There is a bug in Node's performance.measure prior to 12.16.3/13.13.0 that does not
// match the Web Performance API specification. Node's implementation did not allow
// optional `start` and `end` arguments for `performance.measure`.
// See https://github.com/nodejs/node/pull/32651 for more information.
const version = new Version(process.versions.node);
const range = new VersionRange("<12.16.3 || 13 <13.13");
if (range.test(version)) {
performance = {
get timeOrigin() { return nodePerformance.timeOrigin; },
now() { return nodePerformance.now(); },
mark(name) { return nodePerformance.mark(name); },
measure(name, start = "nodeStart", end?) {
if (end === undefined) {
end = "__performance.measure-fix__";
nodePerformance.mark(end);
}
nodePerformance.measure(name, start, end);
if (end === "__performance.measure-fix__") {
nodePerformance.clearMarks("__performance.measure-fix__");
}
},
clearMarks(name) { return nodePerformance.clearMarks(name); },
clearMeasures(name) { return (nodePerformance as unknown as Performance).clearMeasures(name); },
};
}
return {
// By default, only write native events when generating a cpu profile or using the v8 profiler.
shouldWriteNativeEvents: false,
performance,
PerformanceObserver
function tryGetNodePerformanceHooks(): PerformanceHooks | undefined {
if (isNodeLikeSystem()) {
try {
let performance: Performance;
const { performance: nodePerformance, PerformanceObserver } = require("perf_hooks") as typeof import("perf_hooks");
if (hasRequiredAPI(nodePerformance as unknown as Performance, PerformanceObserver)) {
performance = nodePerformance as unknown as Performance;
// There is a bug in Node's performance.measure prior to 12.16.3/13.13.0 that does not
// match the Web Performance API specification. Node's implementation did not allow
// optional `start` and `end` arguments for `performance.measure`.
// See https://github.com/nodejs/node/pull/32651 for more information.
const version = new Version(process.versions.node);
const range = new VersionRange("<12.16.3 || 13 <13.13");
if (range.test(version)) {
performance = {
get timeOrigin() { return nodePerformance.timeOrigin; },
now() { return nodePerformance.now(); },
mark(name) { return nodePerformance.mark(name); },
measure(name, start = "nodeStart", end?) {
if (end === undefined) {
end = "__performance.measure-fix__";
nodePerformance.mark(end);
}
nodePerformance.measure(name, start, end);
if (end === "__performance.measure-fix__") {
nodePerformance.clearMarks("__performance.measure-fix__");
}
},
clearMarks(name) { return nodePerformance.clearMarks(name); },
clearMeasures(name) { return (nodePerformance as unknown as Performance).clearMeasures(name); },
};
}
}
catch {
// ignore errors
return {
// By default, only write native events when generating a cpu profile or using the v8 profiler.
shouldWriteNativeEvents: false,
performance,
PerformanceObserver
};
}
}
catch {
// ignore errors
}
}
}
// Unlike with the native Map/Set 'tryGet' functions in corePublic.ts, we eagerly evaluate these
// since we will need them for `timestamp`, below.
const nativePerformanceHooks = tryGetWebPerformanceHooks() || tryGetNodePerformanceHooks();
const nativePerformance = nativePerformanceHooks?.performance;
// Unlike with the native Map/Set 'tryGet' functions in corePublic.ts, we eagerly evaluate these
// since we will need them for `timestamp`, below.
const nativePerformanceHooks = tryGetWebPerformanceHooks() || tryGetNodePerformanceHooks();
const nativePerformance = nativePerformanceHooks?.performance;
export function tryGetNativePerformanceHooks() {
return nativePerformanceHooks;
}
/** @internal */
export function tryGetNativePerformanceHooks() {
return nativePerformanceHooks;
}
/** Gets a timestamp with (at least) ms resolution */
export const timestamp =
nativePerformance ? () => nativePerformance.now() :
Date.now ? Date.now :
() => +(new Date());
}
/**
* Gets a timestamp with (at least) ms resolution
*
* @internal
*/
export const timestamp =
nativePerformance ? () => nativePerformance.now() :
Date.now ? Date.now :
() => +(new Date());
+4203 -4139
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2457 -2452
View File
File diff suppressed because one or more lines are too long
+368 -363
View File
@@ -1,416 +1,421 @@
/* @internal */
namespace ts {
// https://semver.org/#spec-item-2
// > A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative
// > integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor
// > version, and Z is the patch version. Each element MUST increase numerically.
//
// NOTE: We differ here in that we allow X and X.Y, with missing parts having the default
// value of `0`.
const versionRegExp = /^(0|[1-9]\d*)(?:\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*)(?:\-([a-z0-9-.]+))?(?:\+([a-z0-9-.]+))?)?)?$/i;
import {
compareStringsCaseSensitive, compareValues, Comparison, Debug, emptyArray, every, isArray, map, some, trimString,
} from "./_namespaces/ts";
// https://semver.org/#spec-item-9
// > A pre-release version MAY be denoted by appending a hyphen and a series of dot separated
// > identifiers immediately following the patch version. Identifiers MUST comprise only ASCII
// > alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers
// > MUST NOT include leading zeroes.
const prereleaseRegExp = /^(?:0|[1-9]\d*|[a-z-][a-z0-9-]*)(?:\.(?:0|[1-9]\d*|[a-z-][a-z0-9-]*))*$/i;
const prereleasePartRegExp = /^(?:0|[1-9]\d*|[a-z-][a-z0-9-]*)$/i;
// https://semver.org/#spec-item-2
// > A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative
// > integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor
// > version, and Z is the patch version. Each element MUST increase numerically.
//
// NOTE: We differ here in that we allow X and X.Y, with missing parts having the default
// value of `0`.
const versionRegExp = /^(0|[1-9]\d*)(?:\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*)(?:\-([a-z0-9-.]+))?(?:\+([a-z0-9-.]+))?)?)?$/i;
// https://semver.org/#spec-item-10
// > Build metadata MAY be denoted by appending a plus sign and a series of dot separated
// > identifiers immediately following the patch or pre-release version. Identifiers MUST
// > comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty.
const buildRegExp = /^[a-z0-9-]+(?:\.[a-z0-9-]+)*$/i;
const buildPartRegExp = /^[a-z0-9-]+$/i;
// https://semver.org/#spec-item-9
// > A pre-release version MAY be denoted by appending a hyphen and a series of dot separated
// > identifiers immediately following the patch version. Identifiers MUST comprise only ASCII
// > alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers
// > MUST NOT include leading zeroes.
const prereleaseRegExp = /^(?:0|[1-9]\d*|[a-z-][a-z0-9-]*)(?:\.(?:0|[1-9]\d*|[a-z-][a-z0-9-]*))*$/i;
const prereleasePartRegExp = /^(?:0|[1-9]\d*|[a-z-][a-z0-9-]*)$/i;
// https://semver.org/#spec-item-9
// > Numeric identifiers MUST NOT include leading zeroes.
const numericIdentifierRegExp = /^(0|[1-9]\d*)$/;
// https://semver.org/#spec-item-10
// > Build metadata MAY be denoted by appending a plus sign and a series of dot separated
// > identifiers immediately following the patch or pre-release version. Identifiers MUST
// > comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty.
const buildRegExp = /^[a-z0-9-]+(?:\.[a-z0-9-]+)*$/i;
const buildPartRegExp = /^[a-z0-9-]+$/i;
/**
* Describes a precise semantic version number, https://semver.org
*/
export class Version {
static readonly zero = new Version(0, 0, 0, ["0"]);
// https://semver.org/#spec-item-9
// > Numeric identifiers MUST NOT include leading zeroes.
const numericIdentifierRegExp = /^(0|[1-9]\d*)$/;
readonly major: number;
readonly minor: number;
readonly patch: number;
readonly prerelease: readonly string[];
readonly build: readonly string[];
/**
* Describes a precise semantic version number, https://semver.org
*
* @internal
*/
export class Version {
static readonly zero = new Version(0, 0, 0, ["0"]);
constructor(text: string);
constructor(major: number, minor?: number, patch?: number, prerelease?: string | readonly string[], build?: string | readonly string[]);
constructor(major: number | string, minor = 0, patch = 0, prerelease: string | readonly string[] = "", build: string | readonly string[] = "") {
if (typeof major === "string") {
const result = Debug.checkDefined(tryParseComponents(major), "Invalid version");
({ major, minor, patch, prerelease, build } = result);
}
readonly major: number;
readonly minor: number;
readonly patch: number;
readonly prerelease: readonly string[];
readonly build: readonly string[];
Debug.assert(major >= 0, "Invalid argument: major");
Debug.assert(minor >= 0, "Invalid argument: minor");
Debug.assert(patch >= 0, "Invalid argument: patch");
const prereleaseArray = prerelease ? isArray(prerelease) ? prerelease : prerelease.split(".") : emptyArray;
const buildArray = build ? isArray(build) ? build : build.split(".") : emptyArray;
Debug.assert(every(prereleaseArray, s => prereleasePartRegExp.test(s)), "Invalid argument: prerelease");
Debug.assert(every(buildArray, s => buildPartRegExp.test(s)), "Invalid argument: build");
this.major = major;
this.minor = minor;
this.patch = patch;
this.prerelease = prereleaseArray;
this.build = buildArray;
constructor(text: string);
constructor(major: number, minor?: number, patch?: number, prerelease?: string | readonly string[], build?: string | readonly string[]);
constructor(major: number | string, minor = 0, patch = 0, prerelease: string | readonly string[] = "", build: string | readonly string[] = "") {
if (typeof major === "string") {
const result = Debug.checkDefined(tryParseComponents(major), "Invalid version");
({ major, minor, patch, prerelease, build } = result);
}
static tryParse(text: string) {
const result = tryParseComponents(text);
if (!result) return undefined;
Debug.assert(major >= 0, "Invalid argument: major");
Debug.assert(minor >= 0, "Invalid argument: minor");
Debug.assert(patch >= 0, "Invalid argument: patch");
const { major, minor, patch, prerelease, build } = result;
return new Version(major, minor, patch, prerelease, build);
}
const prereleaseArray = prerelease ? isArray(prerelease) ? prerelease : prerelease.split(".") : emptyArray;
const buildArray = build ? isArray(build) ? build : build.split(".") : emptyArray;
compareTo(other: Version | undefined) {
// https://semver.org/#spec-item-11
// > Precedence is determined by the first difference when comparing each of these
// > identifiers from left to right as follows: Major, minor, and patch versions are
// > always compared numerically.
//
// https://semver.org/#spec-item-11
// > Precedence for two pre-release versions with the same major, minor, and patch version
// > MUST be determined by comparing each dot separated identifier from left to right until
// > a difference is found [...]
//
// https://semver.org/#spec-item-11
// > Build metadata does not figure into precedence
if (this === other) return Comparison.EqualTo;
if (other === undefined) return Comparison.GreaterThan;
return compareValues(this.major, other.major)
|| compareValues(this.minor, other.minor)
|| compareValues(this.patch, other.patch)
|| comparePrereleaseIdentifiers(this.prerelease, other.prerelease);
}
Debug.assert(every(prereleaseArray, s => prereleasePartRegExp.test(s)), "Invalid argument: prerelease");
Debug.assert(every(buildArray, s => buildPartRegExp.test(s)), "Invalid argument: build");
increment(field: "major" | "minor" | "patch") {
switch (field) {
case "major": return new Version(this.major + 1, 0, 0);
case "minor": return new Version(this.major, this.minor + 1, 0);
case "patch": return new Version(this.major, this.minor, this.patch + 1);
default: return Debug.assertNever(field);
}
}
with(fields: { major?: number, minor?: number, patch?: number, prerelease?: string | readonly string[], build?: string | readonly string[] }) {
const {
major = this.major,
minor = this.minor,
patch = this.patch,
prerelease = this.prerelease,
build = this.build
} = fields;
return new Version(major, minor, patch, prerelease, build);
}
toString() {
let result = `${this.major}.${this.minor}.${this.patch}`;
if (some(this.prerelease)) result += `-${this.prerelease.join(".")}`;
if (some(this.build)) result += `+${this.build.join(".")}`;
return result;
}
this.major = major;
this.minor = minor;
this.patch = patch;
this.prerelease = prereleaseArray;
this.build = buildArray;
}
function tryParseComponents(text: string) {
const match = versionRegExp.exec(text);
if (!match) return undefined;
static tryParse(text: string) {
const result = tryParseComponents(text);
if (!result) return undefined;
const [, major, minor = "0", patch = "0", prerelease = "", build = ""] = match;
if (prerelease && !prereleaseRegExp.test(prerelease)) return undefined;
if (build && !buildRegExp.test(build)) return undefined;
return {
major: parseInt(major, 10),
minor: parseInt(minor, 10),
patch: parseInt(patch, 10),
prerelease,
build
};
const { major, minor, patch, prerelease, build } = result;
return new Version(major, minor, patch, prerelease, build);
}
function comparePrereleaseIdentifiers(left: readonly string[], right: readonly string[]) {
compareTo(other: Version | undefined) {
// https://semver.org/#spec-item-11
// > When major, minor, and patch are equal, a pre-release version has lower precedence
// > than a normal version.
if (left === right) return Comparison.EqualTo;
if (left.length === 0) return right.length === 0 ? Comparison.EqualTo : Comparison.GreaterThan;
if (right.length === 0) return Comparison.LessThan;
// > Precedence is determined by the first difference when comparing each of these
// > identifiers from left to right as follows: Major, minor, and patch versions are
// > always compared numerically.
//
// https://semver.org/#spec-item-11
// > Precedence for two pre-release versions with the same major, minor, and patch version
// > MUST be determined by comparing each dot separated identifier from left to right until
// > a difference is found [...]
const length = Math.min(left.length, right.length);
for (let i = 0; i < length; i++) {
const leftIdentifier = left[i];
const rightIdentifier = right[i];
if (leftIdentifier === rightIdentifier) continue;
const leftIsNumeric = numericIdentifierRegExp.test(leftIdentifier);
const rightIsNumeric = numericIdentifierRegExp.test(rightIdentifier);
if (leftIsNumeric || rightIsNumeric) {
// https://semver.org/#spec-item-11
// > Numeric identifiers always have lower precedence than non-numeric identifiers.
if (leftIsNumeric !== rightIsNumeric) return leftIsNumeric ? Comparison.LessThan : Comparison.GreaterThan;
// https://semver.org/#spec-item-11
// > identifiers consisting of only digits are compared numerically
const result = compareValues(+leftIdentifier, +rightIdentifier);
if (result) return result;
}
else {
// https://semver.org/#spec-item-11
// > identifiers with letters or hyphens are compared lexically in ASCII sort order.
const result = compareStringsCaseSensitive(leftIdentifier, rightIdentifier);
if (result) return result;
}
}
//
// https://semver.org/#spec-item-11
// > A larger set of pre-release fields has a higher precedence than a smaller set, if all
// > of the preceding identifiers are equal.
return compareValues(left.length, right.length);
// > Build metadata does not figure into precedence
if (this === other) return Comparison.EqualTo;
if (other === undefined) return Comparison.GreaterThan;
return compareValues(this.major, other.major)
|| compareValues(this.minor, other.minor)
|| compareValues(this.patch, other.patch)
|| comparePrereleaseIdentifiers(this.prerelease, other.prerelease);
}
increment(field: "major" | "minor" | "patch") {
switch (field) {
case "major": return new Version(this.major + 1, 0, 0);
case "minor": return new Version(this.major, this.minor + 1, 0);
case "patch": return new Version(this.major, this.minor, this.patch + 1);
default: return Debug.assertNever(field);
}
}
with(fields: { major?: number, minor?: number, patch?: number, prerelease?: string | readonly string[], build?: string | readonly string[] }) {
const {
major = this.major,
minor = this.minor,
patch = this.patch,
prerelease = this.prerelease,
build = this.build
} = fields;
return new Version(major, minor, patch, prerelease, build);
}
toString() {
let result = `${this.major}.${this.minor}.${this.patch}`;
if (some(this.prerelease)) result += `-${this.prerelease.join(".")}`;
if (some(this.build)) result += `+${this.build.join(".")}`;
return result;
}
}
function tryParseComponents(text: string) {
const match = versionRegExp.exec(text);
if (!match) return undefined;
const [, major, minor = "0", patch = "0", prerelease = "", build = ""] = match;
if (prerelease && !prereleaseRegExp.test(prerelease)) return undefined;
if (build && !buildRegExp.test(build)) return undefined;
return {
major: parseInt(major, 10),
minor: parseInt(minor, 10),
patch: parseInt(patch, 10),
prerelease,
build
};
}
function comparePrereleaseIdentifiers(left: readonly string[], right: readonly string[]) {
// https://semver.org/#spec-item-11
// > When major, minor, and patch are equal, a pre-release version has lower precedence
// > than a normal version.
if (left === right) return Comparison.EqualTo;
if (left.length === 0) return right.length === 0 ? Comparison.EqualTo : Comparison.GreaterThan;
if (right.length === 0) return Comparison.LessThan;
// https://semver.org/#spec-item-11
// > Precedence for two pre-release versions with the same major, minor, and patch version
// > MUST be determined by comparing each dot separated identifier from left to right until
// > a difference is found [...]
const length = Math.min(left.length, right.length);
for (let i = 0; i < length; i++) {
const leftIdentifier = left[i];
const rightIdentifier = right[i];
if (leftIdentifier === rightIdentifier) continue;
const leftIsNumeric = numericIdentifierRegExp.test(leftIdentifier);
const rightIsNumeric = numericIdentifierRegExp.test(rightIdentifier);
if (leftIsNumeric || rightIsNumeric) {
// https://semver.org/#spec-item-11
// > Numeric identifiers always have lower precedence than non-numeric identifiers.
if (leftIsNumeric !== rightIsNumeric) return leftIsNumeric ? Comparison.LessThan : Comparison.GreaterThan;
// https://semver.org/#spec-item-11
// > identifiers consisting of only digits are compared numerically
const result = compareValues(+leftIdentifier, +rightIdentifier);
if (result) return result;
}
else {
// https://semver.org/#spec-item-11
// > identifiers with letters or hyphens are compared lexically in ASCII sort order.
const result = compareStringsCaseSensitive(leftIdentifier, rightIdentifier);
if (result) return result;
}
}
// https://semver.org/#spec-item-11
// > A larger set of pre-release fields has a higher precedence than a smaller set, if all
// > of the preceding identifiers are equal.
return compareValues(left.length, right.length);
}
/**
* Describes a semantic version range, per https://github.com/npm/node-semver#ranges
*
* @internal
*/
export class VersionRange {
private _alternatives: readonly (readonly Comparator[])[];
constructor(spec: string) {
this._alternatives = spec ? Debug.checkDefined(parseRange(spec), "Invalid range spec.") : emptyArray;
}
static tryParse(text: string) {
const sets = parseRange(text);
if (sets) {
const range = new VersionRange("");
range._alternatives = sets;
return range;
}
return undefined;
}
/**
* Describes a semantic version range, per https://github.com/npm/node-semver#ranges
* Tests whether a version matches the range. This is equivalent to `satisfies(version, range, { includePrerelease: true })`.
* in `node-semver`.
*/
export class VersionRange {
private _alternatives: readonly (readonly Comparator[])[];
constructor(spec: string) {
this._alternatives = spec ? Debug.checkDefined(parseRange(spec), "Invalid range spec.") : emptyArray;
}
static tryParse(text: string) {
const sets = parseRange(text);
if (sets) {
const range = new VersionRange("");
range._alternatives = sets;
return range;
}
return undefined;
}
/**
* Tests whether a version matches the range. This is equivalent to `satisfies(version, range, { includePrerelease: true })`.
* in `node-semver`.
*/
test(version: Version | string) {
if (typeof version === "string") version = new Version(version);
return testDisjunction(version, this._alternatives);
}
toString() {
return formatDisjunction(this._alternatives);
}
test(version: Version | string) {
if (typeof version === "string") version = new Version(version);
return testDisjunction(version, this._alternatives);
}
interface Comparator {
readonly operator: "<" | "<=" | ">" | ">=" | "=";
readonly operand: Version;
toString() {
return formatDisjunction(this._alternatives);
}
}
// https://github.com/npm/node-semver#range-grammar
//
// range-set ::= range ( logical-or range ) *
// range ::= hyphen | simple ( ' ' simple ) * | ''
// logical-or ::= ( ' ' ) * '||' ( ' ' ) *
const logicalOrRegExp = /\|\|/g;
const whitespaceRegExp = /\s+/g;
interface Comparator {
readonly operator: "<" | "<=" | ">" | ">=" | "=";
readonly operand: Version;
}
// https://github.com/npm/node-semver#range-grammar
//
// partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
// xr ::= 'x' | 'X' | '*' | nr
// nr ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
// qualifier ::= ( '-' pre )? ( '+' build )?
// pre ::= parts
// build ::= parts
// parts ::= part ( '.' part ) *
// part ::= nr | [-0-9A-Za-z]+
const partialRegExp = /^([xX*0]|[1-9]\d*)(?:\.([xX*0]|[1-9]\d*)(?:\.([xX*0]|[1-9]\d*)(?:-([a-z0-9-.]+))?(?:\+([a-z0-9-.]+))?)?)?$/i;
// https://github.com/npm/node-semver#range-grammar
//
// range-set ::= range ( logical-or range ) *
// range ::= hyphen | simple ( ' ' simple ) * | ''
// logical-or ::= ( ' ' ) * '||' ( ' ' ) *
const logicalOrRegExp = /\|\|/g;
const whitespaceRegExp = /\s+/g;
// https://github.com/npm/node-semver#range-grammar
//
// hyphen ::= partial ' - ' partial
const hyphenRegExp = /^\s*([a-z0-9-+.*]+)\s+-\s+([a-z0-9-+.*]+)\s*$/i;
// https://github.com/npm/node-semver#range-grammar
//
// partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
// xr ::= 'x' | 'X' | '*' | nr
// nr ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
// qualifier ::= ( '-' pre )? ( '+' build )?
// pre ::= parts
// build ::= parts
// parts ::= part ( '.' part ) *
// part ::= nr | [-0-9A-Za-z]+
const partialRegExp = /^([xX*0]|[1-9]\d*)(?:\.([xX*0]|[1-9]\d*)(?:\.([xX*0]|[1-9]\d*)(?:-([a-z0-9-.]+))?(?:\+([a-z0-9-.]+))?)?)?$/i;
// https://github.com/npm/node-semver#range-grammar
//
// simple ::= primitive | partial | tilde | caret
// primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
// tilde ::= '~' partial
// caret ::= '^' partial
const rangeRegExp = /^(~|\^|<|<=|>|>=|=)?\s*([a-z0-9-+.*]+)$/i;
// https://github.com/npm/node-semver#range-grammar
//
// hyphen ::= partial ' - ' partial
const hyphenRegExp = /^\s*([a-z0-9-+.*]+)\s+-\s+([a-z0-9-+.*]+)\s*$/i;
function parseRange(text: string) {
const alternatives: Comparator[][] = [];
for (let range of trimString(text).split(logicalOrRegExp)) {
if (!range) continue;
const comparators: Comparator[] = [];
range = trimString(range);
const match = hyphenRegExp.exec(range);
if (match) {
if (!parseHyphen(match[1], match[2], comparators)) return undefined;
}
else {
for (const simple of range.split(whitespaceRegExp)) {
const match = rangeRegExp.exec(trimString(simple));
if (!match || !parseComparator(match[1], match[2], comparators)) return undefined;
}
}
alternatives.push(comparators);
// https://github.com/npm/node-semver#range-grammar
//
// simple ::= primitive | partial | tilde | caret
// primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
// tilde ::= '~' partial
// caret ::= '^' partial
const rangeRegExp = /^(~|\^|<|<=|>|>=|=)?\s*([a-z0-9-+.*]+)$/i;
function parseRange(text: string) {
const alternatives: Comparator[][] = [];
for (let range of trimString(text).split(logicalOrRegExp)) {
if (!range) continue;
const comparators: Comparator[] = [];
range = trimString(range);
const match = hyphenRegExp.exec(range);
if (match) {
if (!parseHyphen(match[1], match[2], comparators)) return undefined;
}
return alternatives;
}
function parsePartial(text: string) {
const match = partialRegExp.exec(text);
if (!match) return undefined;
const [, major, minor = "*", patch = "*", prerelease, build] = match;
const version = new Version(
isWildcard(major) ? 0 : parseInt(major, 10),
isWildcard(major) || isWildcard(minor) ? 0 : parseInt(minor, 10),
isWildcard(major) || isWildcard(minor) || isWildcard(patch) ? 0 : parseInt(patch, 10),
prerelease,
build);
return { version, major, minor, patch };
}
function parseHyphen(left: string, right: string, comparators: Comparator[]) {
const leftResult = parsePartial(left);
if (!leftResult) return false;
const rightResult = parsePartial(right);
if (!rightResult) return false;
if (!isWildcard(leftResult.major)) {
comparators.push(createComparator(">=", leftResult.version));
}
if (!isWildcard(rightResult.major)) {
comparators.push(
isWildcard(rightResult.minor) ? createComparator("<", rightResult.version.increment("major")) :
isWildcard(rightResult.patch) ? createComparator("<", rightResult.version.increment("minor")) :
createComparator("<=", rightResult.version));
}
return true;
}
function parseComparator(operator: string, text: string, comparators: Comparator[]) {
const result = parsePartial(text);
if (!result) return false;
const { version, major, minor, patch } = result;
if (!isWildcard(major)) {
switch (operator) {
case "~":
comparators.push(createComparator(">=", version));
comparators.push(createComparator("<", version.increment(
isWildcard(minor) ? "major" :
"minor")));
break;
case "^":
comparators.push(createComparator(">=", version));
comparators.push(createComparator("<", version.increment(
version.major > 0 || isWildcard(minor) ? "major" :
version.minor > 0 || isWildcard(patch) ? "minor" :
"patch")));
break;
case "<":
case ">=":
comparators.push(
isWildcard(minor) || isWildcard(patch) ? createComparator(operator, version.with({ prerelease: "0" })) :
createComparator(operator, version));
break;
case "<=":
case ">":
comparators.push(
isWildcard(minor) ? createComparator(operator === "<=" ? "<" : ">=", version.increment("major").with({ prerelease: "0" })) :
isWildcard(patch) ? createComparator(operator === "<=" ? "<" : ">=", version.increment("minor").with({ prerelease: "0" })) :
createComparator(operator, version));
break;
case "=":
case undefined:
if (isWildcard(minor) || isWildcard(patch)) {
comparators.push(createComparator(">=", version.with({ prerelease: "0" })));
comparators.push(createComparator("<", version.increment(isWildcard(minor) ? "major" : "minor").with({ prerelease: "0" })));
}
else {
comparators.push(createComparator("=", version));
}
break;
default:
// unrecognized
return false;
else {
for (const simple of range.split(whitespaceRegExp)) {
const match = rangeRegExp.exec(trimString(simple));
if (!match || !parseComparator(match[1], match[2], comparators)) return undefined;
}
}
else if (operator === "<" || operator === ">") {
comparators.push(createComparator("<", Version.zero));
}
alternatives.push(comparators);
}
return alternatives;
}
return true;
function parsePartial(text: string) {
const match = partialRegExp.exec(text);
if (!match) return undefined;
const [, major, minor = "*", patch = "*", prerelease, build] = match;
const version = new Version(
isWildcard(major) ? 0 : parseInt(major, 10),
isWildcard(major) || isWildcard(minor) ? 0 : parseInt(minor, 10),
isWildcard(major) || isWildcard(minor) || isWildcard(patch) ? 0 : parseInt(patch, 10),
prerelease,
build);
return { version, major, minor, patch };
}
function parseHyphen(left: string, right: string, comparators: Comparator[]) {
const leftResult = parsePartial(left);
if (!leftResult) return false;
const rightResult = parsePartial(right);
if (!rightResult) return false;
if (!isWildcard(leftResult.major)) {
comparators.push(createComparator(">=", leftResult.version));
}
function isWildcard(part: string) {
return part === "*" || part === "x" || part === "X";
if (!isWildcard(rightResult.major)) {
comparators.push(
isWildcard(rightResult.minor) ? createComparator("<", rightResult.version.increment("major")) :
isWildcard(rightResult.patch) ? createComparator("<", rightResult.version.increment("minor")) :
createComparator("<=", rightResult.version));
}
function createComparator(operator: Comparator["operator"], operand: Version) {
return { operator, operand };
}
return true;
}
function testDisjunction(version: Version, alternatives: readonly (readonly Comparator[])[]) {
// an empty disjunction is treated as "*" (all versions)
if (alternatives.length === 0) return true;
for (const alternative of alternatives) {
if (testAlternative(version, alternative)) return true;
}
return false;
}
function parseComparator(operator: string, text: string, comparators: Comparator[]) {
const result = parsePartial(text);
if (!result) return false;
function testAlternative(version: Version, comparators: readonly Comparator[]) {
for (const comparator of comparators) {
if (!testComparator(version, comparator.operator, comparator.operand)) return false;
}
return true;
}
function testComparator(version: Version, operator: Comparator["operator"], operand: Version) {
const cmp = version.compareTo(operand);
const { version, major, minor, patch } = result;
if (!isWildcard(major)) {
switch (operator) {
case "<": return cmp < 0;
case "<=": return cmp <= 0;
case ">": return cmp > 0;
case ">=": return cmp >= 0;
case "=": return cmp === 0;
default: return Debug.assertNever(operator);
case "~":
comparators.push(createComparator(">=", version));
comparators.push(createComparator("<", version.increment(
isWildcard(minor) ? "major" :
"minor")));
break;
case "^":
comparators.push(createComparator(">=", version));
comparators.push(createComparator("<", version.increment(
version.major > 0 || isWildcard(minor) ? "major" :
version.minor > 0 || isWildcard(patch) ? "minor" :
"patch")));
break;
case "<":
case ">=":
comparators.push(
isWildcard(minor) || isWildcard(patch) ? createComparator(operator, version.with({ prerelease: "0" })) :
createComparator(operator, version));
break;
case "<=":
case ">":
comparators.push(
isWildcard(minor) ? createComparator(operator === "<=" ? "<" : ">=", version.increment("major").with({ prerelease: "0" })) :
isWildcard(patch) ? createComparator(operator === "<=" ? "<" : ">=", version.increment("minor").with({ prerelease: "0" })) :
createComparator(operator, version));
break;
case "=":
case undefined:
if (isWildcard(minor) || isWildcard(patch)) {
comparators.push(createComparator(">=", version.with({ prerelease: "0" })));
comparators.push(createComparator("<", version.increment(isWildcard(minor) ? "major" : "minor").with({ prerelease: "0" })));
}
else {
comparators.push(createComparator("=", version));
}
break;
default:
// unrecognized
return false;
}
}
function formatDisjunction(alternatives: readonly (readonly Comparator[])[]) {
return map(alternatives, formatAlternative).join(" || ") || "*";
else if (operator === "<" || operator === ">") {
comparators.push(createComparator("<", Version.zero));
}
function formatAlternative(comparators: readonly Comparator[]) {
return map(comparators, formatComparator).join(" ");
}
return true;
}
function formatComparator(comparator: Comparator) {
return `${comparator.operator}${comparator.operand}`;
function isWildcard(part: string) {
return part === "*" || part === "x" || part === "X";
}
function createComparator(operator: Comparator["operator"], operand: Version) {
return { operator, operand };
}
function testDisjunction(version: Version, alternatives: readonly (readonly Comparator[])[]) {
// an empty disjunction is treated as "*" (all versions)
if (alternatives.length === 0) return true;
for (const alternative of alternatives) {
if (testAlternative(version, alternative)) return true;
}
return false;
}
function testAlternative(version: Version, comparators: readonly Comparator[]) {
for (const comparator of comparators) {
if (!testComparator(version, comparator.operator, comparator.operand)) return false;
}
return true;
}
function testComparator(version: Version, operator: Comparator["operator"], operand: Version) {
const cmp = version.compareTo(operand);
switch (operator) {
case "<": return cmp < 0;
case "<=": return cmp <= 0;
case ">": return cmp > 0;
case ">=": return cmp >= 0;
case "=": return cmp === 0;
default: return Debug.assertNever(operator);
}
}
function formatDisjunction(alternatives: readonly (readonly Comparator[])[]) {
return map(alternatives, formatAlternative).join(" || ") || "*";
}
function formatAlternative(comparators: readonly Comparator[]) {
return map(comparators, formatComparator).join(" ");
}
function formatComparator(comparator: Comparator) {
return `${comparator.operator}${comparator.operand}`;
}
+742 -717
View File
File diff suppressed because it is too large Load Diff
+176 -172
View File
@@ -1,190 +1,194 @@
import {
clear, EntityNameOrEntityNameExpression, forEach, getOwnValues, getSymbolId, Identifier, IndexedAccessType,
IndexType, InterfaceType, MappedType, Node, ObjectFlags, ObjectType, ResolvedType, Signature, Symbol, SymbolWalker,
SyntaxKind, Type, TypeFlags, TypeParameter, TypePredicate, TypeQueryNode, TypeReference, UnionOrIntersectionType,
} from "./_namespaces/ts";
/** @internal */
namespace ts {
export function createGetSymbolWalker(
getRestTypeOfSignature: (sig: Signature) => Type,
getTypePredicateOfSignature: (sig: Signature) => TypePredicate | undefined,
getReturnTypeOfSignature: (sig: Signature) => Type,
getBaseTypes: (type: Type) => Type[],
resolveStructuredTypeMembers: (type: ObjectType) => ResolvedType,
getTypeOfSymbol: (sym: Symbol) => Type,
getResolvedSymbol: (node: Node) => Symbol,
getConstraintOfTypeParameter: (typeParameter: TypeParameter) => Type | undefined,
getFirstIdentifier: (node: EntityNameOrEntityNameExpression) => Identifier,
getTypeArguments: (type: TypeReference) => readonly Type[]) {
export function createGetSymbolWalker(
getRestTypeOfSignature: (sig: Signature) => Type,
getTypePredicateOfSignature: (sig: Signature) => TypePredicate | undefined,
getReturnTypeOfSignature: (sig: Signature) => Type,
getBaseTypes: (type: Type) => Type[],
resolveStructuredTypeMembers: (type: ObjectType) => ResolvedType,
getTypeOfSymbol: (sym: Symbol) => Type,
getResolvedSymbol: (node: Node) => Symbol,
getConstraintOfTypeParameter: (typeParameter: TypeParameter) => Type | undefined,
getFirstIdentifier: (node: EntityNameOrEntityNameExpression) => Identifier,
getTypeArguments: (type: TypeReference) => readonly Type[]) {
return getSymbolWalker;
return getSymbolWalker;
function getSymbolWalker(accept: (symbol: Symbol) => boolean = () => true): SymbolWalker {
const visitedTypes: Type[] = []; // Sparse array from id to type
const visitedSymbols: Symbol[] = []; // Sparse array from id to symbol
function getSymbolWalker(accept: (symbol: Symbol) => boolean = () => true): SymbolWalker {
const visitedTypes: Type[] = []; // Sparse array from id to type
const visitedSymbols: Symbol[] = []; // Sparse array from id to symbol
return {
walkType: type => {
try {
visitType(type);
return { visitedTypes: getOwnValues(visitedTypes), visitedSymbols: getOwnValues(visitedSymbols) };
}
finally {
clear(visitedTypes);
clear(visitedSymbols);
}
},
walkSymbol: symbol => {
try {
visitSymbol(symbol);
return { visitedTypes: getOwnValues(visitedTypes), visitedSymbols: getOwnValues(visitedSymbols) };
}
finally {
clear(visitedTypes);
clear(visitedSymbols);
}
},
};
return {
walkType: type => {
try {
visitType(type);
return { visitedTypes: getOwnValues(visitedTypes), visitedSymbols: getOwnValues(visitedSymbols) };
}
finally {
clear(visitedTypes);
clear(visitedSymbols);
}
},
walkSymbol: symbol => {
try {
visitSymbol(symbol);
return { visitedTypes: getOwnValues(visitedTypes), visitedSymbols: getOwnValues(visitedSymbols) };
}
finally {
clear(visitedTypes);
clear(visitedSymbols);
}
},
};
function visitType(type: Type | undefined): void {
if (!type) {
return;
}
if (visitedTypes[type.id]) {
return;
}
visitedTypes[type.id] = type;
// Reuse visitSymbol to visit the type's symbol,
// but be sure to bail on recuring into the type if accept declines the symbol.
const shouldBail = visitSymbol(type.symbol);
if (shouldBail) return;
// Visit the type's related types, if any
if (type.flags & TypeFlags.Object) {
const objectType = type as ObjectType;
const objectFlags = objectType.objectFlags;
if (objectFlags & ObjectFlags.Reference) {
visitTypeReference(type as TypeReference);
}
if (objectFlags & ObjectFlags.Mapped) {
visitMappedType(type as MappedType);
}
if (objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) {
visitInterfaceType(type as InterfaceType);
}
if (objectFlags & (ObjectFlags.Tuple | ObjectFlags.Anonymous)) {
visitObjectType(objectType);
}
}
if (type.flags & TypeFlags.TypeParameter) {
visitTypeParameter(type as TypeParameter);
}
if (type.flags & TypeFlags.UnionOrIntersection) {
visitUnionOrIntersectionType(type as UnionOrIntersectionType);
}
if (type.flags & TypeFlags.Index) {
visitIndexType(type as IndexType);
}
if (type.flags & TypeFlags.IndexedAccess) {
visitIndexedAccessType(type as IndexedAccessType);
}
function visitType(type: Type | undefined): void {
if (!type) {
return;
}
function visitTypeReference(type: TypeReference): void {
visitType(type.target);
forEach(getTypeArguments(type), visitType);
if (visitedTypes[type.id]) {
return;
}
visitedTypes[type.id] = type;
function visitTypeParameter(type: TypeParameter): void {
visitType(getConstraintOfTypeParameter(type));
}
// Reuse visitSymbol to visit the type's symbol,
// but be sure to bail on recuring into the type if accept declines the symbol.
const shouldBail = visitSymbol(type.symbol);
if (shouldBail) return;
function visitUnionOrIntersectionType(type: UnionOrIntersectionType): void {
forEach(type.types, visitType);
}
function visitIndexType(type: IndexType): void {
visitType(type.type);
}
function visitIndexedAccessType(type: IndexedAccessType): void {
visitType(type.objectType);
visitType(type.indexType);
visitType(type.constraint);
}
function visitMappedType(type: MappedType): void {
visitType(type.typeParameter);
visitType(type.constraintType);
visitType(type.templateType);
visitType(type.modifiersType);
}
function visitSignature(signature: Signature): void {
const typePredicate = getTypePredicateOfSignature(signature);
if (typePredicate) {
visitType(typePredicate.type);
// Visit the type's related types, if any
if (type.flags & TypeFlags.Object) {
const objectType = type as ObjectType;
const objectFlags = objectType.objectFlags;
if (objectFlags & ObjectFlags.Reference) {
visitTypeReference(type as TypeReference);
}
forEach(signature.typeParameters, visitType);
for (const parameter of signature.parameters) {
visitSymbol(parameter);
if (objectFlags & ObjectFlags.Mapped) {
visitMappedType(type as MappedType);
}
visitType(getRestTypeOfSignature(signature));
visitType(getReturnTypeOfSignature(signature));
}
function visitInterfaceType(interfaceT: InterfaceType): void {
visitObjectType(interfaceT);
forEach(interfaceT.typeParameters, visitType);
forEach(getBaseTypes(interfaceT), visitType);
visitType(interfaceT.thisType);
}
function visitObjectType(type: ObjectType): void {
const resolved = resolveStructuredTypeMembers(type);
for (const info of resolved.indexInfos) {
visitType(info.keyType);
visitType(info.type);
if (objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) {
visitInterfaceType(type as InterfaceType);
}
for (const signature of resolved.callSignatures) {
visitSignature(signature);
}
for (const signature of resolved.constructSignatures) {
visitSignature(signature);
}
for (const p of resolved.properties) {
visitSymbol(p);
if (objectFlags & (ObjectFlags.Tuple | ObjectFlags.Anonymous)) {
visitObjectType(objectType);
}
}
function visitSymbol(symbol: Symbol | undefined): boolean {
if (!symbol) {
return false;
}
const symbolId = getSymbolId(symbol);
if (visitedSymbols[symbolId]) {
return false;
}
visitedSymbols[symbolId] = symbol;
if (!accept(symbol)) {
return true;
}
const t = getTypeOfSymbol(symbol);
visitType(t); // Should handle members on classes and such
if (symbol.exports) {
symbol.exports.forEach(visitSymbol);
}
forEach(symbol.declarations, d => {
// Type queries are too far resolved when we just visit the symbol's type
// (their type resolved directly to the member deeply referenced)
// So to get the intervening symbols, we need to check if there's a type
// query node on any of the symbol's declarations and get symbols there
if ((d as any).type && (d as any).type.kind === SyntaxKind.TypeQuery) {
const query = (d as any).type as TypeQueryNode;
const entity = getResolvedSymbol(getFirstIdentifier(query.exprName));
visitSymbol(entity);
}
});
return false;
if (type.flags & TypeFlags.TypeParameter) {
visitTypeParameter(type as TypeParameter);
}
if (type.flags & TypeFlags.UnionOrIntersection) {
visitUnionOrIntersectionType(type as UnionOrIntersectionType);
}
if (type.flags & TypeFlags.Index) {
visitIndexType(type as IndexType);
}
if (type.flags & TypeFlags.IndexedAccess) {
visitIndexedAccessType(type as IndexedAccessType);
}
}
function visitTypeReference(type: TypeReference): void {
visitType(type.target);
forEach(getTypeArguments(type), visitType);
}
function visitTypeParameter(type: TypeParameter): void {
visitType(getConstraintOfTypeParameter(type));
}
function visitUnionOrIntersectionType(type: UnionOrIntersectionType): void {
forEach(type.types, visitType);
}
function visitIndexType(type: IndexType): void {
visitType(type.type);
}
function visitIndexedAccessType(type: IndexedAccessType): void {
visitType(type.objectType);
visitType(type.indexType);
visitType(type.constraint);
}
function visitMappedType(type: MappedType): void {
visitType(type.typeParameter);
visitType(type.constraintType);
visitType(type.templateType);
visitType(type.modifiersType);
}
function visitSignature(signature: Signature): void {
const typePredicate = getTypePredicateOfSignature(signature);
if (typePredicate) {
visitType(typePredicate.type);
}
forEach(signature.typeParameters, visitType);
for (const parameter of signature.parameters) {
visitSymbol(parameter);
}
visitType(getRestTypeOfSignature(signature));
visitType(getReturnTypeOfSignature(signature));
}
function visitInterfaceType(interfaceT: InterfaceType): void {
visitObjectType(interfaceT);
forEach(interfaceT.typeParameters, visitType);
forEach(getBaseTypes(interfaceT), visitType);
visitType(interfaceT.thisType);
}
function visitObjectType(type: ObjectType): void {
const resolved = resolveStructuredTypeMembers(type);
for (const info of resolved.indexInfos) {
visitType(info.keyType);
visitType(info.type);
}
for (const signature of resolved.callSignatures) {
visitSignature(signature);
}
for (const signature of resolved.constructSignatures) {
visitSignature(signature);
}
for (const p of resolved.properties) {
visitSymbol(p);
}
}
function visitSymbol(symbol: Symbol | undefined): boolean {
if (!symbol) {
return false;
}
const symbolId = getSymbolId(symbol);
if (visitedSymbols[symbolId]) {
return false;
}
visitedSymbols[symbolId] = symbol;
if (!accept(symbol)) {
return true;
}
const t = getTypeOfSymbol(symbol);
visitType(t); // Should handle members on classes and such
if (symbol.exports) {
symbol.exports.forEach(visitSymbol);
}
forEach(symbol.declarations, d => {
// Type queries are too far resolved when we just visit the symbol's type
// (their type resolved directly to the member deeply referenced)
// So to get the intervening symbols, we need to check if there's a type
// query node on any of the symbol's declarations and get symbols there
if ((d as any).type && (d as any).type.kind === SyntaxKind.TypeQuery) {
const query = (d as any).type as TypeQueryNode;
const entity = getResolvedSymbol(getFirstIdentifier(query.exprName));
visitSymbol(entity);
}
});
return false;
}
}
}
}
+1855 -1839
View File
File diff suppressed because it is too large Load Diff
+334 -323
View File
@@ -1,349 +1,360 @@
import {
combinePaths, ConditionalType, Debug, EvolvingArrayType, getLineAndCharacterOfPosition, getSourceFileOfNode,
IndexedAccessType, IndexType, IntersectionType, LineAndCharacter, Map, Node, ObjectFlags, Path, ReverseMappedType,
SubstitutionType, timestamp, Type, TypeFlags, TypeReference, unescapeLeadingUnderscores, UnionType,
} from "./_namespaces/ts";
import * as performance from "./_namespaces/ts.performance";
/* Tracing events for the compiler. */
/*@internal*/
namespace ts { // eslint-disable-line local/one-namespace-per-file
// should be used as tracing?.___
export let tracing: typeof tracingEnabled | undefined;
// enable the above using startTracing()
// should be used as tracing?.___
/** @internal */
export let tracing: typeof tracingEnabled | undefined;
// enable the above using startTracing()
// `tracingEnabled` should never be used directly, only through the above
namespace tracingEnabled { // eslint-disable-line local/one-namespace-per-file
type Mode = "project" | "build" | "server";
/**
* Do not use this directly; instead @see {tracing}.
* @internal
*/
export namespace tracingEnabled {
type Mode = "project" | "build" | "server";
let fs: typeof import("fs");
let fs: typeof import("fs");
let traceCount = 0;
let traceFd = 0;
let traceCount = 0;
let traceFd = 0;
let mode: Mode;
let mode: Mode;
const typeCatalog: Type[] = []; // NB: id is index + 1
const typeCatalog: Type[] = []; // NB: id is index + 1
let legendPath: string | undefined;
const legend: TraceRecord[] = [];
let legendPath: string | undefined;
const legend: TraceRecord[] = [];
// The actual constraint is that JSON.stringify be able to serialize it without throwing.
interface Args {
[key: string]: string | number | boolean | null | undefined | Args | readonly (string | number | boolean | null | undefined | Args)[];
}
// The actual constraint is that JSON.stringify be able to serialize it without throwing.
interface Args {
[key: string]: string | number | boolean | null | undefined | Args | readonly (string | number | boolean | null | undefined | Args)[];
}
/** Starts tracing for the given project. */
export function startTracing(tracingMode: Mode, traceDir: string, configFilePath?: string) {
Debug.assert(!tracing, "Tracing already started");
/** Starts tracing for the given project. */
export function startTracing(tracingMode: Mode, traceDir: string, configFilePath?: string) {
Debug.assert(!tracing, "Tracing already started");
if (fs === undefined) {
try {
fs = require("fs");
}
catch (e) {
throw new Error(`tracing requires having fs\n(original error: ${e.message || e})`);
}
if (fs === undefined) {
try {
fs = require("fs");
}
mode = tracingMode;
typeCatalog.length = 0;
if (legendPath === undefined) {
legendPath = combinePaths(traceDir, "legend.json");
}
// Note that writing will fail later on if it exists and is not a directory
if (!fs.existsSync(traceDir)) {
fs.mkdirSync(traceDir, { recursive: true });
}
const countPart =
mode === "build" ? `.${process.pid}-${++traceCount}`
: mode === "server" ? `.${process.pid}`
: ``;
const tracePath = combinePaths(traceDir, `trace${countPart}.json`);
const typesPath = combinePaths(traceDir, `types${countPart}.json`);
legend.push({
configFilePath,
tracePath,
typesPath,
});
traceFd = fs.openSync(tracePath, "w");
tracing = tracingEnabled; // only when traceFd is properly set
// Start with a prefix that contains some metadata that the devtools profiler expects (also avoids a warning on import)
const meta = { cat: "__metadata", ph: "M", ts: 1000 * timestamp(), pid: 1, tid: 1 };
fs.writeSync(traceFd,
"[\n"
+ [{ name: "process_name", args: { name: "tsc" }, ...meta },
{ name: "thread_name", args: { name: "Main" }, ...meta },
{ name: "TracingStartedInBrowser", ...meta, cat: "disabled-by-default-devtools.timeline" }]
.map(v => JSON.stringify(v)).join(",\n"));
}
/** Stops tracing for the in-progress project and dumps the type catalog. */
export function stopTracing() {
Debug.assert(tracing, "Tracing is not in progress");
Debug.assert(!!typeCatalog.length === (mode !== "server")); // Have a type catalog iff not in server mode
fs.writeSync(traceFd, `\n]\n`);
fs.closeSync(traceFd);
tracing = undefined;
if (typeCatalog.length) {
dumpTypes(typeCatalog);
}
else {
// We pre-computed this path for convenience, but clear it
// now that the file won't be created.
legend[legend.length - 1].typesPath = undefined;
catch (e) {
throw new Error(`tracing requires having fs\n(original error: ${e.message || e})`);
}
}
export function recordType(type: Type): void {
if (mode !== "server") {
typeCatalog.push(type);
}
mode = tracingMode;
typeCatalog.length = 0;
if (legendPath === undefined) {
legendPath = combinePaths(traceDir, "legend.json");
}
export const enum Phase {
Parse = "parse",
Program = "program",
Bind = "bind",
Check = "check", // Before we get into checking types (e.g. checkSourceFile)
CheckTypes = "checkTypes",
Emit = "emit",
Session = "session",
// Note that writing will fail later on if it exists and is not a directory
if (!fs.existsSync(traceDir)) {
fs.mkdirSync(traceDir, { recursive: true });
}
export function instant(phase: Phase, name: string, args?: Args) {
writeEvent("I", phase, name, args, `"s":"g"`);
const countPart =
mode === "build" ? `.${process.pid}-${++traceCount}`
: mode === "server" ? `.${process.pid}`
: ``;
const tracePath = combinePaths(traceDir, `trace${countPart}.json`);
const typesPath = combinePaths(traceDir, `types${countPart}.json`);
legend.push({
configFilePath,
tracePath,
typesPath,
});
traceFd = fs.openSync(tracePath, "w");
tracing = tracingEnabled; // only when traceFd is properly set
// Start with a prefix that contains some metadata that the devtools profiler expects (also avoids a warning on import)
const meta = { cat: "__metadata", ph: "M", ts: 1000 * timestamp(), pid: 1, tid: 1 };
fs.writeSync(traceFd,
"[\n"
+ [{ name: "process_name", args: { name: "tsc" }, ...meta },
{ name: "thread_name", args: { name: "Main" }, ...meta },
{ name: "TracingStartedInBrowser", ...meta, cat: "disabled-by-default-devtools.timeline" }]
.map(v => JSON.stringify(v)).join(",\n"));
}
/** Stops tracing for the in-progress project and dumps the type catalog. */
export function stopTracing() {
Debug.assert(tracing, "Tracing is not in progress");
Debug.assert(!!typeCatalog.length === (mode !== "server")); // Have a type catalog iff not in server mode
fs.writeSync(traceFd, `\n]\n`);
fs.closeSync(traceFd);
tracing = undefined;
if (typeCatalog.length) {
dumpTypes(typeCatalog);
}
const eventStack: { phase: Phase, name: string, args?: Args, time: number, separateBeginAndEnd: boolean }[] = [];
/**
* @param separateBeginAndEnd - used for special cases where we need the trace point even if the event
* never terminates (typically for reducing a scenario too big to trace to one that can be completed).
* In the future we might implement an exit handler to dump unfinished events which would deprecate
* these operations.
*/
export function push(phase: Phase, name: string, args?: Args, separateBeginAndEnd = false) {
if (separateBeginAndEnd) {
writeEvent("B", phase, name, args);
}
eventStack.push({ phase, name, args, time: 1000 * timestamp(), separateBeginAndEnd });
}
export function pop(results?: Args) {
Debug.assert(eventStack.length > 0);
writeStackEvent(eventStack.length - 1, 1000 * timestamp(), results);
eventStack.length--;
}
export function popAll() {
const endTime = 1000 * timestamp();
for (let i = eventStack.length - 1; i >= 0; i--) {
writeStackEvent(i, endTime);
}
eventStack.length = 0;
}
// sample every 10ms
const sampleInterval = 1000 * 10;
function writeStackEvent(index: number, endTime: number, results?: Args) {
const { phase, name, args, time, separateBeginAndEnd } = eventStack[index];
if (separateBeginAndEnd) {
Debug.assert(!results, "`results` are not supported for events with `separateBeginAndEnd`");
writeEvent("E", phase, name, args, /*extras*/ undefined, endTime);
}
// test if [time,endTime) straddles a sampling point
else if (sampleInterval - (time % sampleInterval) <= endTime - time) {
writeEvent("X", phase, name, { ...args, results }, `"dur":${endTime - time}`, time);
}
}
function writeEvent(eventType: string, phase: Phase, name: string, args: Args | undefined, extras?: string,
time: number = 1000 * timestamp()) {
// In server mode, there's no easy way to dump type information, so we drop events that would require it.
if (mode === "server" && phase === Phase.CheckTypes) return;
performance.mark("beginTracing");
fs.writeSync(traceFd, `,\n{"pid":1,"tid":1,"ph":"${eventType}","cat":"${phase}","ts":${time},"name":"${name}"`);
if (extras) fs.writeSync(traceFd, `,${extras}`);
if (args) fs.writeSync(traceFd, `,"args":${JSON.stringify(args)}`);
fs.writeSync(traceFd, `}`);
performance.mark("endTracing");
performance.measure("Tracing", "beginTracing", "endTracing");
}
function getLocation(node: Node | undefined) {
const file = getSourceFileOfNode(node);
return !file
? undefined
: {
path: file.path,
start: indexFromOne(getLineAndCharacterOfPosition(file, node!.pos)),
end: indexFromOne(getLineAndCharacterOfPosition(file, node!.end)),
};
function indexFromOne(lc: LineAndCharacter): LineAndCharacter {
return {
line: lc.line + 1,
character: lc.character + 1,
};
}
}
function dumpTypes(types: readonly Type[]) {
performance.mark("beginDumpTypes");
const typesPath = legend[legend.length - 1].typesPath!;
const typesFd = fs.openSync(typesPath, "w");
const recursionIdentityMap = new Map<object, number>();
// Cleverness: no line break here so that the type ID will match the line number
fs.writeSync(typesFd, "[");
const numTypes = types.length;
for (let i = 0; i < numTypes; i++) {
const type = types[i];
const objectFlags = (type as any).objectFlags;
const symbol = type.aliasSymbol ?? type.symbol;
// It's slow to compute the display text, so skip it unless it's really valuable (or cheap)
let display: string | undefined;
if ((objectFlags & ObjectFlags.Anonymous) | (type.flags & TypeFlags.Literal)) {
try {
display = type.checker?.typeToString(type);
}
catch {
display = undefined;
}
}
let indexedAccessProperties: object = {};
if (type.flags & TypeFlags.IndexedAccess) {
const indexedAccessType = type as IndexedAccessType;
indexedAccessProperties = {
indexedAccessObjectType: indexedAccessType.objectType?.id,
indexedAccessIndexType: indexedAccessType.indexType?.id,
};
}
let referenceProperties: object = {};
if (objectFlags & ObjectFlags.Reference) {
const referenceType = type as TypeReference;
referenceProperties = {
instantiatedType: referenceType.target?.id,
typeArguments: referenceType.resolvedTypeArguments?.map(t => t.id),
referenceLocation: getLocation(referenceType.node),
};
}
let conditionalProperties: object = {};
if (type.flags & TypeFlags.Conditional) {
const conditionalType = type as ConditionalType;
conditionalProperties = {
conditionalCheckType: conditionalType.checkType?.id,
conditionalExtendsType: conditionalType.extendsType?.id,
conditionalTrueType: conditionalType.resolvedTrueType?.id ?? -1,
conditionalFalseType: conditionalType.resolvedFalseType?.id ?? -1,
};
}
let substitutionProperties: object = {};
if (type.flags & TypeFlags.Substitution) {
const substitutionType = type as SubstitutionType;
substitutionProperties = {
substitutionBaseType: substitutionType.baseType?.id,
constraintType: substitutionType.constraint?.id,
};
}
let reverseMappedProperties: object = {};
if (objectFlags & ObjectFlags.ReverseMapped) {
const reverseMappedType = type as ReverseMappedType;
reverseMappedProperties = {
reverseMappedSourceType: reverseMappedType.source?.id,
reverseMappedMappedType: reverseMappedType.mappedType?.id,
reverseMappedConstraintType: reverseMappedType.constraintType?.id,
};
}
let evolvingArrayProperties: object = {};
if (objectFlags & ObjectFlags.EvolvingArray) {
const evolvingArrayType = type as EvolvingArrayType;
evolvingArrayProperties = {
evolvingArrayElementType: evolvingArrayType.elementType.id,
evolvingArrayFinalType: evolvingArrayType.finalArrayType?.id,
};
}
// We can't print out an arbitrary object, so just assign each one a unique number.
// Don't call it an "id" so people don't treat it as a type id.
let recursionToken: number | undefined;
const recursionIdentity = type.checker.getRecursionIdentity(type);
if (recursionIdentity) {
recursionToken = recursionIdentityMap.get(recursionIdentity);
if (!recursionToken) {
recursionToken = recursionIdentityMap.size;
recursionIdentityMap.set(recursionIdentity, recursionToken);
}
}
const descriptor = {
id: type.id,
intrinsicName: (type as any).intrinsicName,
symbolName: symbol?.escapedName && unescapeLeadingUnderscores(symbol.escapedName),
recursionId: recursionToken,
isTuple: objectFlags & ObjectFlags.Tuple ? true : undefined,
unionTypes: (type.flags & TypeFlags.Union) ? (type as UnionType).types?.map(t => t.id) : undefined,
intersectionTypes: (type.flags & TypeFlags.Intersection) ? (type as IntersectionType).types.map(t => t.id) : undefined,
aliasTypeArguments: type.aliasTypeArguments?.map(t => t.id),
keyofType: (type.flags & TypeFlags.Index) ? (type as IndexType).type?.id : undefined,
...indexedAccessProperties,
...referenceProperties,
...conditionalProperties,
...substitutionProperties,
...reverseMappedProperties,
...evolvingArrayProperties,
destructuringPattern: getLocation(type.pattern),
firstDeclaration: getLocation(symbol?.declarations?.[0]),
flags: Debug.formatTypeFlags(type.flags).split("|"),
display,
};
fs.writeSync(typesFd, JSON.stringify(descriptor));
if (i < numTypes - 1) {
fs.writeSync(typesFd, ",\n");
}
}
fs.writeSync(typesFd, "]\n");
fs.closeSync(typesFd);
performance.mark("endDumpTypes");
performance.measure("Dump types", "beginDumpTypes", "endDumpTypes");
}
export function dumpLegend() {
if (!legendPath) {
return;
}
fs.writeFileSync(legendPath, JSON.stringify(legend));
}
interface TraceRecord {
configFilePath?: string;
tracePath: string;
typesPath?: string;
else {
// We pre-computed this path for convenience, but clear it
// now that the file won't be created.
legend[legend.length - 1].typesPath = undefined;
}
}
// define after tracingEnabled is initialized
export const startTracing = tracingEnabled.startTracing;
export const dumpTracingLegend = tracingEnabled.dumpLegend;
export function recordType(type: Type): void {
if (mode !== "server") {
typeCatalog.push(type);
}
}
export interface TracingNode {
tracingPath?: Path;
export const enum Phase {
Parse = "parse",
Program = "program",
Bind = "bind",
Check = "check", // Before we get into checking types (e.g. checkSourceFile)
CheckTypes = "checkTypes",
Emit = "emit",
Session = "session",
}
export function instant(phase: Phase, name: string, args?: Args) {
writeEvent("I", phase, name, args, `"s":"g"`);
}
const eventStack: { phase: Phase, name: string, args?: Args, time: number, separateBeginAndEnd: boolean }[] = [];
/**
* @param separateBeginAndEnd - used for special cases where we need the trace point even if the event
* never terminates (typically for reducing a scenario too big to trace to one that can be completed).
* In the future we might implement an exit handler to dump unfinished events which would deprecate
* these operations.
*/
export function push(phase: Phase, name: string, args?: Args, separateBeginAndEnd = false) {
if (separateBeginAndEnd) {
writeEvent("B", phase, name, args);
}
eventStack.push({ phase, name, args, time: 1000 * timestamp(), separateBeginAndEnd });
}
export function pop(results?: Args) {
Debug.assert(eventStack.length > 0);
writeStackEvent(eventStack.length - 1, 1000 * timestamp(), results);
eventStack.length--;
}
export function popAll() {
const endTime = 1000 * timestamp();
for (let i = eventStack.length - 1; i >= 0; i--) {
writeStackEvent(i, endTime);
}
eventStack.length = 0;
}
// sample every 10ms
const sampleInterval = 1000 * 10;
function writeStackEvent(index: number, endTime: number, results?: Args) {
const { phase, name, args, time, separateBeginAndEnd } = eventStack[index];
if (separateBeginAndEnd) {
Debug.assert(!results, "`results` are not supported for events with `separateBeginAndEnd`");
writeEvent("E", phase, name, args, /*extras*/ undefined, endTime);
}
// test if [time,endTime) straddles a sampling point
else if (sampleInterval - (time % sampleInterval) <= endTime - time) {
writeEvent("X", phase, name, { ...args, results }, `"dur":${endTime - time}`, time);
}
}
function writeEvent(eventType: string, phase: Phase, name: string, args: Args | undefined, extras?: string,
time: number = 1000 * timestamp()) {
// In server mode, there's no easy way to dump type information, so we drop events that would require it.
if (mode === "server" && phase === Phase.CheckTypes) return;
performance.mark("beginTracing");
fs.writeSync(traceFd, `,\n{"pid":1,"tid":1,"ph":"${eventType}","cat":"${phase}","ts":${time},"name":"${name}"`);
if (extras) fs.writeSync(traceFd, `,${extras}`);
if (args) fs.writeSync(traceFd, `,"args":${JSON.stringify(args)}`);
fs.writeSync(traceFd, `}`);
performance.mark("endTracing");
performance.measure("Tracing", "beginTracing", "endTracing");
}
function getLocation(node: Node | undefined) {
const file = getSourceFileOfNode(node);
return !file
? undefined
: {
path: file.path,
start: indexFromOne(getLineAndCharacterOfPosition(file, node!.pos)),
end: indexFromOne(getLineAndCharacterOfPosition(file, node!.end)),
};
function indexFromOne(lc: LineAndCharacter): LineAndCharacter {
return {
line: lc.line + 1,
character: lc.character + 1,
};
}
}
function dumpTypes(types: readonly Type[]) {
performance.mark("beginDumpTypes");
const typesPath = legend[legend.length - 1].typesPath!;
const typesFd = fs.openSync(typesPath, "w");
const recursionIdentityMap = new Map<object, number>();
// Cleverness: no line break here so that the type ID will match the line number
fs.writeSync(typesFd, "[");
const numTypes = types.length;
for (let i = 0; i < numTypes; i++) {
const type = types[i];
const objectFlags = (type as any).objectFlags;
const symbol = type.aliasSymbol ?? type.symbol;
// It's slow to compute the display text, so skip it unless it's really valuable (or cheap)
let display: string | undefined;
if ((objectFlags & ObjectFlags.Anonymous) | (type.flags & TypeFlags.Literal)) {
try {
display = type.checker?.typeToString(type);
}
catch {
display = undefined;
}
}
let indexedAccessProperties: object = {};
if (type.flags & TypeFlags.IndexedAccess) {
const indexedAccessType = type as IndexedAccessType;
indexedAccessProperties = {
indexedAccessObjectType: indexedAccessType.objectType?.id,
indexedAccessIndexType: indexedAccessType.indexType?.id,
};
}
let referenceProperties: object = {};
if (objectFlags & ObjectFlags.Reference) {
const referenceType = type as TypeReference;
referenceProperties = {
instantiatedType: referenceType.target?.id,
typeArguments: referenceType.resolvedTypeArguments?.map(t => t.id),
referenceLocation: getLocation(referenceType.node),
};
}
let conditionalProperties: object = {};
if (type.flags & TypeFlags.Conditional) {
const conditionalType = type as ConditionalType;
conditionalProperties = {
conditionalCheckType: conditionalType.checkType?.id,
conditionalExtendsType: conditionalType.extendsType?.id,
conditionalTrueType: conditionalType.resolvedTrueType?.id ?? -1,
conditionalFalseType: conditionalType.resolvedFalseType?.id ?? -1,
};
}
let substitutionProperties: object = {};
if (type.flags & TypeFlags.Substitution) {
const substitutionType = type as SubstitutionType;
substitutionProperties = {
substitutionBaseType: substitutionType.baseType?.id,
constraintType: substitutionType.constraint?.id,
};
}
let reverseMappedProperties: object = {};
if (objectFlags & ObjectFlags.ReverseMapped) {
const reverseMappedType = type as ReverseMappedType;
reverseMappedProperties = {
reverseMappedSourceType: reverseMappedType.source?.id,
reverseMappedMappedType: reverseMappedType.mappedType?.id,
reverseMappedConstraintType: reverseMappedType.constraintType?.id,
};
}
let evolvingArrayProperties: object = {};
if (objectFlags & ObjectFlags.EvolvingArray) {
const evolvingArrayType = type as EvolvingArrayType;
evolvingArrayProperties = {
evolvingArrayElementType: evolvingArrayType.elementType.id,
evolvingArrayFinalType: evolvingArrayType.finalArrayType?.id,
};
}
// We can't print out an arbitrary object, so just assign each one a unique number.
// Don't call it an "id" so people don't treat it as a type id.
let recursionToken: number | undefined;
const recursionIdentity = type.checker.getRecursionIdentity(type);
if (recursionIdentity) {
recursionToken = recursionIdentityMap.get(recursionIdentity);
if (!recursionToken) {
recursionToken = recursionIdentityMap.size;
recursionIdentityMap.set(recursionIdentity, recursionToken);
}
}
const descriptor = {
id: type.id,
intrinsicName: (type as any).intrinsicName,
symbolName: symbol?.escapedName && unescapeLeadingUnderscores(symbol.escapedName),
recursionId: recursionToken,
isTuple: objectFlags & ObjectFlags.Tuple ? true : undefined,
unionTypes: (type.flags & TypeFlags.Union) ? (type as UnionType).types?.map(t => t.id) : undefined,
intersectionTypes: (type.flags & TypeFlags.Intersection) ? (type as IntersectionType).types.map(t => t.id) : undefined,
aliasTypeArguments: type.aliasTypeArguments?.map(t => t.id),
keyofType: (type.flags & TypeFlags.Index) ? (type as IndexType).type?.id : undefined,
...indexedAccessProperties,
...referenceProperties,
...conditionalProperties,
...substitutionProperties,
...reverseMappedProperties,
...evolvingArrayProperties,
destructuringPattern: getLocation(type.pattern),
firstDeclaration: getLocation(symbol?.declarations?.[0]),
flags: Debug.formatTypeFlags(type.flags).split("|"),
display,
};
fs.writeSync(typesFd, JSON.stringify(descriptor));
if (i < numTypes - 1) {
fs.writeSync(typesFd, ",\n");
}
}
fs.writeSync(typesFd, "]\n");
fs.closeSync(typesFd);
performance.mark("endDumpTypes");
performance.measure("Dump types", "beginDumpTypes", "endDumpTypes");
}
export function dumpLegend() {
if (!legendPath) {
return;
}
fs.writeFileSync(legendPath, JSON.stringify(legend));
}
interface TraceRecord {
configFilePath?: string;
tracePath: string;
typesPath?: string;
}
}
// define after tracingEnabled is initialized
/** @internal */
export const startTracing = tracingEnabled.startTracing;
/** @internal */
export const dumpTracingLegend = tracingEnabled.dumpLegend;
/** @internal */
export interface TracingNode {
tracingPath?: Path;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,80 +1,198 @@
/* @internal */
namespace ts {
export type GetSymbolAccessibilityDiagnostic = (symbolAccessibilityResult: SymbolAccessibilityResult) => (SymbolAccessibilityDiagnostic | undefined);
import {
BindingElement, CallSignatureDeclaration, ConstructorDeclaration, ConstructSignatureDeclaration, Debug, Declaration,
DeclarationName, DiagnosticMessage, Diagnostics, ExpressionWithTypeArguments, FunctionDeclaration,
GetAccessorDeclaration, getNameOfDeclaration, hasSyntacticModifier, ImportEqualsDeclaration,
IndexSignatureDeclaration, isBindingElement, isCallSignatureDeclaration, isClassDeclaration,
isConstructorDeclaration, isConstructSignatureDeclaration, isExpressionWithTypeArguments, isFunctionDeclaration,
isGetAccessor, isHeritageClause, isImportEqualsDeclaration, isIndexSignatureDeclaration, isJSDocTypeAlias,
isMethodDeclaration, isMethodSignature, isParameter, isParameterPropertyDeclaration, isPropertyAccessExpression,
isPropertyDeclaration, isPropertySignature, isSetAccessor, isStatic, isTypeAliasDeclaration,
isTypeParameterDeclaration, isVariableDeclaration, JSDocCallbackTag, JSDocEnumTag, JSDocTypedefTag,
MethodDeclaration, MethodSignature, ModifierFlags, NamedDeclaration, Node, ParameterDeclaration,
PropertyAccessExpression, PropertyDeclaration, PropertySignature, QualifiedName, SetAccessorDeclaration,
SymbolAccessibility, SymbolAccessibilityResult, SyntaxKind, TypeAliasDeclaration, TypeParameterDeclaration,
VariableDeclaration,
} from "../../_namespaces/ts";
export interface SymbolAccessibilityDiagnostic {
errorNode: Node;
diagnosticMessage: DiagnosticMessage;
typeName?: DeclarationName | QualifiedName;
/** @internal */
export type GetSymbolAccessibilityDiagnostic = (symbolAccessibilityResult: SymbolAccessibilityResult) => (SymbolAccessibilityDiagnostic | undefined);
/** @internal */
export interface SymbolAccessibilityDiagnostic {
errorNode: Node;
diagnosticMessage: DiagnosticMessage;
typeName?: DeclarationName | QualifiedName;
}
/** @internal */
export type DeclarationDiagnosticProducing =
| VariableDeclaration
| PropertyDeclaration
| PropertySignature
| BindingElement
| SetAccessorDeclaration
| GetAccessorDeclaration
| ConstructSignatureDeclaration
| CallSignatureDeclaration
| MethodDeclaration
| MethodSignature
| FunctionDeclaration
| ParameterDeclaration
| TypeParameterDeclaration
| ExpressionWithTypeArguments
| ImportEqualsDeclaration
| TypeAliasDeclaration
| ConstructorDeclaration
| IndexSignatureDeclaration
| PropertyAccessExpression
| JSDocTypedefTag
| JSDocCallbackTag
| JSDocEnumTag;
/** @internal */
export function canProduceDiagnostics(node: Node): node is DeclarationDiagnosticProducing {
return isVariableDeclaration(node) ||
isPropertyDeclaration(node) ||
isPropertySignature(node) ||
isBindingElement(node) ||
isSetAccessor(node) ||
isGetAccessor(node) ||
isConstructSignatureDeclaration(node) ||
isCallSignatureDeclaration(node) ||
isMethodDeclaration(node) ||
isMethodSignature(node) ||
isFunctionDeclaration(node) ||
isParameter(node) ||
isTypeParameterDeclaration(node) ||
isExpressionWithTypeArguments(node) ||
isImportEqualsDeclaration(node) ||
isTypeAliasDeclaration(node) ||
isConstructorDeclaration(node) ||
isIndexSignatureDeclaration(node) ||
isPropertyAccessExpression(node) ||
isJSDocTypeAlias(node);
}
/** @internal */
export function createGetSymbolAccessibilityDiagnosticForNodeName(node: DeclarationDiagnosticProducing) {
if (isSetAccessor(node) || isGetAccessor(node)) {
return getAccessorNameVisibilityError;
}
else if (isMethodSignature(node) || isMethodDeclaration(node)) {
return getMethodNameVisibilityError;
}
else {
return createGetSymbolAccessibilityDiagnosticForNode(node);
}
function getAccessorNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) {
const diagnosticMessage = getAccessorNameVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: node,
typeName: (node as NamedDeclaration).name
} : undefined;
}
export type DeclarationDiagnosticProducing =
| VariableDeclaration
| PropertyDeclaration
| PropertySignature
| BindingElement
| SetAccessorDeclaration
| GetAccessorDeclaration
| ConstructSignatureDeclaration
| CallSignatureDeclaration
| MethodDeclaration
| MethodSignature
| FunctionDeclaration
| ParameterDeclaration
| TypeParameterDeclaration
| ExpressionWithTypeArguments
| ImportEqualsDeclaration
| TypeAliasDeclaration
| ConstructorDeclaration
| IndexSignatureDeclaration
| PropertyAccessExpression
| JSDocTypedefTag
| JSDocCallbackTag
| JSDocEnumTag;
export function canProduceDiagnostics(node: Node): node is DeclarationDiagnosticProducing {
return isVariableDeclaration(node) ||
isPropertyDeclaration(node) ||
isPropertySignature(node) ||
isBindingElement(node) ||
isSetAccessor(node) ||
isGetAccessor(node) ||
isConstructSignatureDeclaration(node) ||
isCallSignatureDeclaration(node) ||
isMethodDeclaration(node) ||
isMethodSignature(node) ||
isFunctionDeclaration(node) ||
isParameter(node) ||
isTypeParameterDeclaration(node) ||
isExpressionWithTypeArguments(node) ||
isImportEqualsDeclaration(node) ||
isTypeAliasDeclaration(node) ||
isConstructorDeclaration(node) ||
isIndexSignatureDeclaration(node) ||
isPropertyAccessExpression(node) ||
isJSDocTypeAlias(node);
}
export function createGetSymbolAccessibilityDiagnosticForNodeName(node: DeclarationDiagnosticProducing) {
if (isSetAccessor(node) || isGetAccessor(node)) {
return getAccessorNameVisibilityError;
function getAccessorNameVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) {
if (isStatic(node)) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1;
}
else if (isMethodSignature(node) || isMethodDeclaration(node)) {
return getMethodNameVisibilityError;
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1;
}
else {
return createGetSymbolAccessibilityDiagnosticForNode(node);
}
function getAccessorNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) {
const diagnosticMessage = getAccessorNameVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: node,
typeName: (node as NamedDeclaration).name
} : undefined;
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1;
}
}
function getAccessorNameVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) {
function getMethodNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic | undefined {
const diagnosticMessage = getMethodNameVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: node,
typeName: (node as NamedDeclaration).name
} : undefined;
}
function getMethodNameVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) {
if (isStatic(node)) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_method_0_of_exported_class_has_or_is_using_private_name_1;
}
else {
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Method_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Method_0_of_exported_interface_has_or_is_using_private_name_1;
}
}
}
/** @internal */
export function createGetSymbolAccessibilityDiagnosticForNode(node: DeclarationDiagnosticProducing): GetSymbolAccessibilityDiagnostic {
if (isVariableDeclaration(node) || isPropertyDeclaration(node) || isPropertySignature(node) || isPropertyAccessExpression(node) || isBindingElement(node) || isConstructorDeclaration(node)) {
return getVariableDeclarationTypeVisibilityError;
}
else if (isSetAccessor(node) || isGetAccessor(node)) {
return getAccessorDeclarationTypeVisibilityError;
}
else if (isConstructSignatureDeclaration(node) || isCallSignatureDeclaration(node) || isMethodDeclaration(node) || isMethodSignature(node) || isFunctionDeclaration(node) || isIndexSignatureDeclaration(node)) {
return getReturnTypeVisibilityError;
}
else if (isParameter(node)) {
if (isParameterPropertyDeclaration(node, node.parent) && hasSyntacticModifier(node.parent, ModifierFlags.Private)) {
return getVariableDeclarationTypeVisibilityError;
}
return getParameterDeclarationTypeVisibilityError;
}
else if (isTypeParameterDeclaration(node)) {
return getTypeParameterConstraintVisibilityError;
}
else if (isExpressionWithTypeArguments(node)) {
return getHeritageClauseVisibilityError;
}
else if (isImportEqualsDeclaration(node)) {
return getImportEntityNameVisibilityError;
}
else if (isTypeAliasDeclaration(node) || isJSDocTypeAlias(node)) {
return getTypeAliasDeclarationVisibilityError;
}
else {
return Debug.assertNever(node, `Attempted to set a declaration diagnostic context for unhandled node kind: ${Debug.formatSyntaxKind((node as Node).kind)}`);
}
function getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) {
if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Exported_variable_0_has_or_is_using_private_name_1;
}
// This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit
// The only exception here is if the constructor was marked as private. we are not emitting the constructor parameters at all.
else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.PropertySignature ||
(node.kind === SyntaxKind.Parameter && hasSyntacticModifier(node.parent, ModifierFlags.Private))) {
// TODO(jfreeman): Deal with computed properties in error reporting.
if (isStatic(node)) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
@@ -82,7 +200,7 @@ namespace ts {
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
else if (node.parent.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.Parameter) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
@@ -90,402 +208,303 @@ namespace ts {
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1;
}
else {
// Interfaces cannot have types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1;
}
}
function getMethodNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic | undefined {
const diagnosticMessage = getMethodNameVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: node,
typeName: (node as NamedDeclaration).name
} : undefined;
}
function getMethodNameVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) {
if (isStatic(node)) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_method_0_of_exported_class_has_or_is_using_private_name_1;
}
else {
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Method_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Method_0_of_exported_interface_has_or_is_using_private_name_1;
}
}
}
export function createGetSymbolAccessibilityDiagnosticForNode(node: DeclarationDiagnosticProducing): GetSymbolAccessibilityDiagnostic {
if (isVariableDeclaration(node) || isPropertyDeclaration(node) || isPropertySignature(node) || isPropertyAccessExpression(node) || isBindingElement(node) || isConstructorDeclaration(node)) {
return getVariableDeclarationTypeVisibilityError;
}
else if (isSetAccessor(node) || isGetAccessor(node)) {
return getAccessorDeclarationTypeVisibilityError;
}
else if (isConstructSignatureDeclaration(node) || isCallSignatureDeclaration(node) || isMethodDeclaration(node) || isMethodSignature(node) || isFunctionDeclaration(node) || isIndexSignatureDeclaration(node)) {
return getReturnTypeVisibilityError;
}
else if (isParameter(node)) {
if (isParameterPropertyDeclaration(node, node.parent) && hasSyntacticModifier(node.parent, ModifierFlags.Private)) {
return getVariableDeclarationTypeVisibilityError;
function getVariableDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic | undefined {
const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: node,
typeName: (node as NamedDeclaration).name
} : undefined;
}
function getAccessorDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
if (node.kind === SyntaxKind.SetAccessor) {
// Getters can infer the return type from the returned expression, but setters cannot, so the
// "_from_external_module_1_but_cannot_be_named" case cannot occur.
if (isStatic(node)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_private_name_1;
}
return getParameterDeclarationTypeVisibilityError;
}
else if (isTypeParameterDeclaration(node)) {
return getTypeParameterConstraintVisibilityError;
}
else if (isExpressionWithTypeArguments(node)) {
return getHeritageClauseVisibilityError;
}
else if (isImportEqualsDeclaration(node)) {
return getImportEntityNameVisibilityError;
}
else if (isTypeAliasDeclaration(node) || isJSDocTypeAlias(node)) {
return getTypeAliasDeclarationVisibilityError;
}
else {
return Debug.assertNever(node, `Attempted to set a declaration diagnostic context for unhandled node kind: ${Debug.formatSyntaxKind((node as Node).kind)}`);
if (isStatic(node)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_private_name_1;
}
}
return {
diagnosticMessage,
errorNode: (node as NamedDeclaration).name!,
typeName: (node as NamedDeclaration).name
};
}
function getReturnTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
switch (node.kind) {
case SyntaxKind.ConstructSignature:
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_0;
break;
case SyntaxKind.CallSignature:
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_name_0;
break;
case SyntaxKind.IndexSignature:
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_name_0;
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (isStatic(node)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_name_0;
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_private_name_0;
}
else {
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_private_name_0;
}
break;
case SyntaxKind.FunctionDeclaration:
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_exported_function_has_or_is_using_private_name_0;
break;
default:
return Debug.fail("This is unknown kind for signature: " + node.kind);
}
function getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) {
if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) {
return {
diagnosticMessage,
errorNode: (node as NamedDeclaration).name || node
};
}
function getParameterDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic | undefined {
const diagnosticMessage: DiagnosticMessage = getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: node,
typeName: (node as NamedDeclaration).name
} : undefined;
}
function getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult): DiagnosticMessage {
switch (node.parent.kind) {
case SyntaxKind.Constructor:
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Exported_variable_0_has_or_is_using_private_name_1;
}
// This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit
// The only exception here is if the constructor was marked as private. we are not emitting the constructor parameters at all.
else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.PropertySignature ||
(node.kind === SyntaxKind.Parameter && hasSyntacticModifier(node.parent, ModifierFlags.Private))) {
// TODO(jfreeman): Deal with computed properties in error reporting.
if (isStatic(node)) {
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_name_1;
case SyntaxKind.ConstructSignature:
case SyntaxKind.ConstructorType:
// Interfaces cannot have parameter types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1;
case SyntaxKind.CallSignature:
// Interfaces cannot have parameter types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1;
case SyntaxKind.IndexSignature:
// Interfaces cannot have parameter types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_private_name_1;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (isStatic(node.parent)) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1;
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.Parameter) {
else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1;
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1;
}
else {
// Interfaces cannot have types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1;
}
}
}
function getVariableDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic | undefined {
const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: node,
typeName: (node as NamedDeclaration).name
} : undefined;
}
function getAccessorDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
if (node.kind === SyntaxKind.SetAccessor) {
// Getters can infer the return type from the returned expression, but setters cannot, so the
// "_from_external_module_1_but_cannot_be_named" case cannot occur.
if (isStatic(node)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_private_name_1;
}
}
else {
if (isStatic(node)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_private_name_1;
}
}
return {
diagnosticMessage,
errorNode: (node as NamedDeclaration).name!,
typeName: (node as NamedDeclaration).name
};
}
function getReturnTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
switch (node.kind) {
case SyntaxKind.ConstructSignature:
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_0;
break;
case SyntaxKind.CallSignature:
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_name_0;
break;
case SyntaxKind.IndexSignature:
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_name_0;
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (isStatic(node)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_name_0;
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_private_name_0;
}
else {
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_private_name_0;
}
break;
case SyntaxKind.FunctionDeclaration:
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_exported_function_has_or_is_using_private_name_0;
break;
default:
return Debug.fail("This is unknown kind for signature: " + node.kind);
}
return {
diagnosticMessage,
errorNode: (node as NamedDeclaration).name || node
};
}
function getParameterDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic | undefined {
const diagnosticMessage: DiagnosticMessage = getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: node,
typeName: (node as NamedDeclaration).name
} : undefined;
}
function getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult): DiagnosticMessage {
switch (node.parent.kind) {
case SyntaxKind.Constructor:
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_name_1;
case SyntaxKind.ConstructSignature:
case SyntaxKind.ConstructorType:
// Interfaces cannot have parameter types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1;
Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1;
}
case SyntaxKind.CallSignature:
// Interfaces cannot have parameter types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1;
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionType:
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_private_name_1;
case SyntaxKind.SetAccessor:
case SyntaxKind.GetAccessor:
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_accessor_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_accessor_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_accessor_has_or_is_using_private_name_1;
case SyntaxKind.IndexSignature:
// Interfaces cannot have parameter types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_private_name_1;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (isStatic(node.parent)) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1;
}
else {
// Interfaces cannot have parameter types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1;
}
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionType:
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_private_name_1;
case SyntaxKind.SetAccessor:
case SyntaxKind.GetAccessor:
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_accessor_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_accessor_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_accessor_has_or_is_using_private_name_1;
default:
return Debug.fail(`Unknown parent for parameter: ${Debug.formatSyntaxKind(node.parent.kind)}`);
}
}
function getTypeParameterConstraintVisibilityError(): SymbolAccessibilityDiagnostic {
// Type parameter constraints are named by user so we should always be able to name it
let diagnosticMessage: DiagnosticMessage;
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_class_has_or_is_using_private_name_1;
break;
case SyntaxKind.InterfaceDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.MappedType:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_mapped_object_type_is_using_private_name_1;
break;
case SyntaxKind.ConstructorType:
case SyntaxKind.ConstructSignature:
diagnosticMessage = Diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.CallSignature:
diagnosticMessage = Diagnostics.Type_parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (isStatic(node.parent)) {
diagnosticMessage = Diagnostics.Type_parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
diagnosticMessage = Diagnostics.Type_parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = Diagnostics.Type_parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1;
}
break;
case SyntaxKind.FunctionType:
case SyntaxKind.FunctionDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1;
break;
case SyntaxKind.TypeAliasDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_type_alias_has_or_is_using_private_name_1;
break;
default:
return Debug.fail("This is unknown parent for type parameter: " + node.parent.kind);
}
return {
diagnosticMessage,
errorNode: node,
typeName: (node as NamedDeclaration).name
};
}
function getHeritageClauseVisibilityError(): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
// Heritage clause is written by user so it can always be named
if (isClassDeclaration(node.parent.parent)) {
// Class or Interface implemented/extended is inaccessible
diagnosticMessage = isHeritageClause(node.parent) && node.parent.token === SyntaxKind.ImplementsKeyword ?
Diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_private_name_1 :
node.parent.parent.name ? Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1 :
Diagnostics.extends_clause_of_exported_class_has_or_is_using_private_name_0;
}
else {
// interface is inaccessible
diagnosticMessage = Diagnostics.extends_clause_of_exported_interface_0_has_or_is_using_private_name_1;
}
return {
diagnosticMessage,
errorNode: node,
typeName: getNameOfDeclaration(node.parent.parent as Declaration)
};
}
function getImportEntityNameVisibilityError(): SymbolAccessibilityDiagnostic {
return {
diagnosticMessage: Diagnostics.Import_declaration_0_is_using_private_name_1,
errorNode: node,
typeName: (node as NamedDeclaration).name
};
}
function getTypeAliasDeclarationVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
return {
diagnosticMessage: symbolAccessibilityResult.errorModuleName
? Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1_from_module_2
: Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1,
errorNode: isJSDocTypeAlias(node) ? Debug.checkDefined(node.typeExpression) : (node as TypeAliasDeclaration).type,
typeName: isJSDocTypeAlias(node) ? getNameOfDeclaration(node) : (node as TypeAliasDeclaration).name,
};
default:
return Debug.fail(`Unknown parent for parameter: ${Debug.formatSyntaxKind(node.parent.kind)}`);
}
}
function getTypeParameterConstraintVisibilityError(): SymbolAccessibilityDiagnostic {
// Type parameter constraints are named by user so we should always be able to name it
let diagnosticMessage: DiagnosticMessage;
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_class_has_or_is_using_private_name_1;
break;
case SyntaxKind.InterfaceDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.MappedType:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_mapped_object_type_is_using_private_name_1;
break;
case SyntaxKind.ConstructorType:
case SyntaxKind.ConstructSignature:
diagnosticMessage = Diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.CallSignature:
diagnosticMessage = Diagnostics.Type_parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (isStatic(node.parent)) {
diagnosticMessage = Diagnostics.Type_parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
diagnosticMessage = Diagnostics.Type_parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = Diagnostics.Type_parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1;
}
break;
case SyntaxKind.FunctionType:
case SyntaxKind.FunctionDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1;
break;
case SyntaxKind.TypeAliasDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_type_alias_has_or_is_using_private_name_1;
break;
default:
return Debug.fail("This is unknown parent for type parameter: " + node.parent.kind);
}
return {
diagnosticMessage,
errorNode: node,
typeName: (node as NamedDeclaration).name
};
}
function getHeritageClauseVisibilityError(): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
// Heritage clause is written by user so it can always be named
if (isClassDeclaration(node.parent.parent)) {
// Class or Interface implemented/extended is inaccessible
diagnosticMessage = isHeritageClause(node.parent) && node.parent.token === SyntaxKind.ImplementsKeyword ?
Diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_private_name_1 :
node.parent.parent.name ? Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1 :
Diagnostics.extends_clause_of_exported_class_has_or_is_using_private_name_0;
}
else {
// interface is inaccessible
diagnosticMessage = Diagnostics.extends_clause_of_exported_interface_0_has_or_is_using_private_name_1;
}
return {
diagnosticMessage,
errorNode: node,
typeName: getNameOfDeclaration(node.parent.parent as Declaration)
};
}
function getImportEntityNameVisibilityError(): SymbolAccessibilityDiagnostic {
return {
diagnosticMessage: Diagnostics.Import_declaration_0_is_using_private_name_1,
errorNode: node,
typeName: (node as NamedDeclaration).name
};
}
function getTypeAliasDeclarationVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
return {
diagnosticMessage: symbolAccessibilityResult.errorModuleName
? Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1_from_module_2
: Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1,
errorNode: isJSDocTypeAlias(node) ? Debug.checkDefined(node.typeExpression) : (node as TypeAliasDeclaration).type,
typeName: isJSDocTypeAlias(node) ? getNameOfDeclaration(node) : (node as TypeAliasDeclaration).name,
};
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+99 -95
View File
@@ -1,105 +1,109 @@
/*@internal*/
namespace ts {
export function transformES2016(context: TransformationContext) {
const {
factory,
hoistVariableDeclaration
} = context;
import {
BinaryExpression, Bundle, chainBundle, Expression, isElementAccessExpression, isExpression, isPropertyAccessExpression,
Node, setTextRange, SourceFile, SyntaxKind, TransformationContext, TransformFlags, visitEachChild, visitNode,
VisitResult,
} from "../_namespaces/ts";
return chainBundle(context, transformSourceFile);
/** @internal */
export function transformES2016(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle {
const {
factory,
hoistVariableDeclaration
} = context;
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
return chainBundle(context, transformSourceFile);
return visitEachChild(node, visitor, context);
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsES2016) === 0) {
return node;
}
switch (node.kind) {
case SyntaxKind.BinaryExpression:
return visitBinaryExpression(node as BinaryExpression);
default:
return visitEachChild(node, visitor, context);
}
}
return visitEachChild(node, visitor, context);
}
function visitBinaryExpression(node: BinaryExpression): Expression {
switch (node.operatorToken.kind) {
case SyntaxKind.AsteriskAsteriskEqualsToken:
return visitExponentiationAssignmentExpression(node);
case SyntaxKind.AsteriskAsteriskToken:
return visitExponentiationExpression(node);
default:
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsES2016) === 0) {
return node;
}
function visitExponentiationAssignmentExpression(node: BinaryExpression) {
let target: Expression;
let value: Expression;
const left = visitNode(node.left, visitor, isExpression);
const right = visitNode(node.right, visitor, isExpression);
if (isElementAccessExpression(left)) {
// Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)`
const expressionTemp = factory.createTempVariable(hoistVariableDeclaration);
const argumentExpressionTemp = factory.createTempVariable(hoistVariableDeclaration);
target = setTextRange(
factory.createElementAccessExpression(
setTextRange(factory.createAssignment(expressionTemp, left.expression), left.expression),
setTextRange(factory.createAssignment(argumentExpressionTemp, left.argumentExpression), left.argumentExpression)
),
left
);
value = setTextRange(
factory.createElementAccessExpression(
expressionTemp,
argumentExpressionTemp
),
left
);
}
else if (isPropertyAccessExpression(left)) {
// Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)`
const expressionTemp = factory.createTempVariable(hoistVariableDeclaration);
target = setTextRange(
factory.createPropertyAccessExpression(
setTextRange(factory.createAssignment(expressionTemp, left.expression), left.expression),
left.name
),
left
);
value = setTextRange(
factory.createPropertyAccessExpression(
expressionTemp,
left.name
),
left
);
}
else {
// Transforms `a **= b` into `a = Math.pow(a, b)`
target = left;
value = left;
}
return setTextRange(
factory.createAssignment(
target,
setTextRange(factory.createGlobalMethodCall("Math", "pow", [value, right]), node)
),
node
);
}
function visitExponentiationExpression(node: BinaryExpression) {
// Transforms `a ** b` into `Math.pow(a, b)`
const left = visitNode(node.left, visitor, isExpression);
const right = visitNode(node.right, visitor, isExpression);
return setTextRange(factory.createGlobalMethodCall("Math", "pow", [left, right]), node);
switch (node.kind) {
case SyntaxKind.BinaryExpression:
return visitBinaryExpression(node as BinaryExpression);
default:
return visitEachChild(node, visitor, context);
}
}
function visitBinaryExpression(node: BinaryExpression): Expression {
switch (node.operatorToken.kind) {
case SyntaxKind.AsteriskAsteriskEqualsToken:
return visitExponentiationAssignmentExpression(node);
case SyntaxKind.AsteriskAsteriskToken:
return visitExponentiationExpression(node);
default:
return visitEachChild(node, visitor, context);
}
}
function visitExponentiationAssignmentExpression(node: BinaryExpression) {
let target: Expression;
let value: Expression;
const left = visitNode(node.left, visitor, isExpression);
const right = visitNode(node.right, visitor, isExpression);
if (isElementAccessExpression(left)) {
// Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)`
const expressionTemp = factory.createTempVariable(hoistVariableDeclaration);
const argumentExpressionTemp = factory.createTempVariable(hoistVariableDeclaration);
target = setTextRange(
factory.createElementAccessExpression(
setTextRange(factory.createAssignment(expressionTemp, left.expression), left.expression),
setTextRange(factory.createAssignment(argumentExpressionTemp, left.argumentExpression), left.argumentExpression)
),
left
);
value = setTextRange(
factory.createElementAccessExpression(
expressionTemp,
argumentExpressionTemp
),
left
);
}
else if (isPropertyAccessExpression(left)) {
// Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)`
const expressionTemp = factory.createTempVariable(hoistVariableDeclaration);
target = setTextRange(
factory.createPropertyAccessExpression(
setTextRange(factory.createAssignment(expressionTemp, left.expression), left.expression),
left.name
),
left
);
value = setTextRange(
factory.createPropertyAccessExpression(
expressionTemp,
left.name
),
left
);
}
else {
// Transforms `a **= b` into `a = Math.pow(a, b)`
target = left;
value = left;
}
return setTextRange(
factory.createAssignment(
target,
setTextRange(factory.createGlobalMethodCall("Math", "pow", [value, right]), node)
),
node
);
}
function visitExponentiationExpression(node: BinaryExpression) {
// Transforms `a ** b` into `Math.pow(a, b)`
const left = visitNode(node.left, visitor, isExpression);
const right = visitNode(node.right, visitor, isExpression);
return setTextRange(factory.createGlobalMethodCall("Math", "pow", [left, right]), node);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+34 -30
View File
@@ -1,38 +1,42 @@
/*@internal*/
namespace ts {
export function transformES2019(context: TransformationContext) {
const factory = context.factory;
return chainBundle(context, transformSourceFile);
import {
Bundle,
CatchClause, chainBundle, isBlock, Node, SourceFile, SyntaxKind, TransformationContext, TransformFlags,
visitEachChild, visitNode, VisitResult,
} from "../_namespaces/ts";
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
/** @internal */
export function transformES2019(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle {
const factory = context.factory;
return chainBundle(context, transformSourceFile);
return visitEachChild(node, visitor, context);
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsES2019) === 0) {
return node;
}
switch (node.kind) {
case SyntaxKind.CatchClause:
return visitCatchClause(node as CatchClause);
default:
return visitEachChild(node, visitor, context);
}
}
return visitEachChild(node, visitor, context);
}
function visitCatchClause(node: CatchClause): CatchClause {
if (!node.variableDeclaration) {
return factory.updateCatchClause(
node,
factory.createVariableDeclaration(factory.createTempVariable(/*recordTempVariable*/ undefined)),
visitNode(node.block, visitor, isBlock)
);
}
return visitEachChild(node, visitor, context);
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsES2019) === 0) {
return node;
}
switch (node.kind) {
case SyntaxKind.CatchClause:
return visitCatchClause(node as CatchClause);
default:
return visitEachChild(node, visitor, context);
}
}
function visitCatchClause(node: CatchClause): CatchClause {
if (!node.variableDeclaration) {
return factory.updateCatchClause(
node,
factory.createVariableDeclaration(factory.createTempVariable(/*recordTempVariable*/ undefined)),
visitNode(node.block, visitor, isBlock)
);
}
return visitEachChild(node, visitor, context);
}
}
+216 -209
View File
@@ -1,222 +1,229 @@
/*@internal*/
namespace ts {
export function transformES2020(context: TransformationContext) {
const {
factory,
hoistVariableDeclaration,
} = context;
import {
AccessExpression, addEmitFlags, BinaryExpression, Bundle, CallExpression, cast, chainBundle, Debug, DeleteExpression,
EmitFlags, Expression, isCallChain, isExpression, isGeneratedIdentifier, isIdentifier, isNonNullChain,
isOptionalChain, isParenthesizedExpression, isSimpleCopiableExpression, isSyntheticReference,
isTaggedTemplateExpression, Node, OptionalChain, OuterExpressionKinds, ParenthesizedExpression, setOriginalNode,
setTextRange, skipParentheses, skipPartiallyEmittedExpressions, SourceFile, SyntaxKind, TransformationContext,
TransformFlags, visitEachChild, visitNode, visitNodes, VisitResult,
} from "../_namespaces/ts";
return chainBundle(context, transformSourceFile);
/** @internal */
export function transformES2020(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle {
const {
factory,
hoistVariableDeclaration,
} = context;
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
return chainBundle(context, transformSourceFile);
return visitEachChild(node, visitor, context);
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsES2020) === 0) {
return node;
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsES2020) === 0) {
return node;
}
switch (node.kind) {
case SyntaxKind.CallExpression: {
const updated = visitNonOptionalCallExpression(node as CallExpression, /*captureThisArg*/ false);
Debug.assertNotNode(updated, isSyntheticReference);
return updated;
}
switch (node.kind) {
case SyntaxKind.CallExpression: {
const updated = visitNonOptionalCallExpression(node as CallExpression, /*captureThisArg*/ false);
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
if (isOptionalChain(node)) {
const updated = visitOptionalExpression(node, /*captureThisArg*/ false, /*isDelete*/ false);
Debug.assertNotNode(updated, isSyntheticReference);
return updated;
}
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
if (isOptionalChain(node)) {
const updated = visitOptionalExpression(node, /*captureThisArg*/ false, /*isDelete*/ false);
Debug.assertNotNode(updated, isSyntheticReference);
return updated;
}
return visitEachChild(node, visitor, context);
case SyntaxKind.BinaryExpression:
if ((node as BinaryExpression).operatorToken.kind === SyntaxKind.QuestionQuestionToken) {
return transformNullishCoalescingExpression(node as BinaryExpression);
}
return visitEachChild(node, visitor, context);
case SyntaxKind.DeleteExpression:
return visitDeleteExpression(node as DeleteExpression);
default:
return visitEachChild(node, visitor, context);
}
}
function flattenChain(chain: OptionalChain) {
Debug.assertNotNode(chain, isNonNullChain);
const links: OptionalChain[] = [chain];
while (!chain.questionDotToken && !isTaggedTemplateExpression(chain)) {
chain = cast(skipPartiallyEmittedExpressions(chain.expression), isOptionalChain);
Debug.assertNotNode(chain, isNonNullChain);
links.unshift(chain);
}
return { expression: chain.expression, chain: links };
}
function visitNonOptionalParenthesizedExpression(node: ParenthesizedExpression, captureThisArg: boolean, isDelete: boolean): Expression {
const expression = visitNonOptionalExpression(node.expression, captureThisArg, isDelete);
if (isSyntheticReference(expression)) {
// `(a.b)` -> { expression `((_a = a).b)`, thisArg: `_a` }
// `(a[b])` -> { expression `((_a = a)[b])`, thisArg: `_a` }
return factory.createSyntheticReferenceExpression(factory.updateParenthesizedExpression(node, expression.expression), expression.thisArg);
}
return factory.updateParenthesizedExpression(node, expression);
}
function visitNonOptionalPropertyOrElementAccessExpression(node: AccessExpression, captureThisArg: boolean, isDelete: boolean): Expression {
if (isOptionalChain(node)) {
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
return visitOptionalExpression(node, captureThisArg, isDelete);
}
let expression: Expression = visitNode(node.expression, visitor, isExpression);
Debug.assertNotNode(expression, isSyntheticReference);
let thisArg: Expression | undefined;
if (captureThisArg) {
if (!isSimpleCopiableExpression(expression)) {
thisArg = factory.createTempVariable(hoistVariableDeclaration);
expression = factory.createAssignment(thisArg, expression);
return visitEachChild(node, visitor, context);
case SyntaxKind.BinaryExpression:
if ((node as BinaryExpression).operatorToken.kind === SyntaxKind.QuestionQuestionToken) {
return transformNullishCoalescingExpression(node as BinaryExpression);
}
else {
thisArg = expression;
}
}
expression = node.kind === SyntaxKind.PropertyAccessExpression
? factory.updatePropertyAccessExpression(node, expression, visitNode(node.name, visitor, isIdentifier))
: factory.updateElementAccessExpression(node, expression, visitNode(node.argumentExpression, visitor, isExpression));
return thisArg ? factory.createSyntheticReferenceExpression(expression, thisArg) : expression;
}
function visitNonOptionalCallExpression(node: CallExpression, captureThisArg: boolean): Expression {
if (isOptionalChain(node)) {
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
return visitOptionalExpression(node, captureThisArg, /*isDelete*/ false);
}
if (isParenthesizedExpression(node.expression) && isOptionalChain(skipParentheses(node.expression))) {
// capture thisArg for calls of parenthesized optional chains like `(foo?.bar)()`
const expression = visitNonOptionalParenthesizedExpression(node.expression, /*captureThisArg*/ true, /*isDelete*/ false);
const args = visitNodes(node.arguments, visitor, isExpression);
if (isSyntheticReference(expression)) {
return setTextRange(factory.createFunctionCallCall(expression.expression, expression.thisArg, args), node);
}
return factory.updateCallExpression(node, expression, /*typeArguments*/ undefined, args);
}
return visitEachChild(node, visitor, context);
}
function visitNonOptionalExpression(node: Expression, captureThisArg: boolean, isDelete: boolean): Expression {
switch (node.kind) {
case SyntaxKind.ParenthesizedExpression: return visitNonOptionalParenthesizedExpression(node as ParenthesizedExpression, captureThisArg, isDelete);
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression: return visitNonOptionalPropertyOrElementAccessExpression(node as AccessExpression, captureThisArg, isDelete);
case SyntaxKind.CallExpression: return visitNonOptionalCallExpression(node as CallExpression, captureThisArg);
default: return visitNode(node, visitor, isExpression);
}
}
function visitOptionalExpression(node: OptionalChain, captureThisArg: boolean, isDelete: boolean): Expression {
const { expression, chain } = flattenChain(node);
const left = visitNonOptionalExpression(skipPartiallyEmittedExpressions(expression), isCallChain(chain[0]), /*isDelete*/ false);
let leftThisArg = isSyntheticReference(left) ? left.thisArg : undefined;
let capturedLeft = isSyntheticReference(left) ? left.expression : left;
let leftExpression = factory.restoreOuterExpressions(expression, capturedLeft, OuterExpressionKinds.PartiallyEmittedExpressions);
if (!isSimpleCopiableExpression(capturedLeft)) {
capturedLeft = factory.createTempVariable(hoistVariableDeclaration);
leftExpression = factory.createAssignment(capturedLeft, leftExpression);
}
let rightExpression = capturedLeft;
let thisArg: Expression | undefined;
for (let i = 0; i < chain.length; i++) {
const segment = chain[i];
switch (segment.kind) {
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
if (i === chain.length - 1 && captureThisArg) {
if (!isSimpleCopiableExpression(rightExpression)) {
thisArg = factory.createTempVariable(hoistVariableDeclaration);
rightExpression = factory.createAssignment(thisArg, rightExpression);
}
else {
thisArg = rightExpression;
}
}
rightExpression = segment.kind === SyntaxKind.PropertyAccessExpression
? factory.createPropertyAccessExpression(rightExpression, visitNode(segment.name, visitor, isIdentifier))
: factory.createElementAccessExpression(rightExpression, visitNode(segment.argumentExpression, visitor, isExpression));
break;
case SyntaxKind.CallExpression:
if (i === 0 && leftThisArg) {
if (!isGeneratedIdentifier(leftThisArg)) {
leftThisArg = factory.cloneNode(leftThisArg);
addEmitFlags(leftThisArg, EmitFlags.NoComments);
}
rightExpression = factory.createFunctionCallCall(
rightExpression,
leftThisArg.kind === SyntaxKind.SuperKeyword ? factory.createThis() : leftThisArg,
visitNodes(segment.arguments, visitor, isExpression)
);
}
else {
rightExpression = factory.createCallExpression(
rightExpression,
/*typeArguments*/ undefined,
visitNodes(segment.arguments, visitor, isExpression)
);
}
break;
}
setOriginalNode(rightExpression, segment);
}
const target = isDelete
? factory.createConditionalExpression(createNotNullCondition(leftExpression, capturedLeft, /*invert*/ true), /*questionToken*/ undefined, factory.createTrue(), /*colonToken*/ undefined, factory.createDeleteExpression(rightExpression))
: factory.createConditionalExpression(createNotNullCondition(leftExpression, capturedLeft, /*invert*/ true), /*questionToken*/ undefined, factory.createVoidZero(), /*colonToken*/ undefined, rightExpression);
setTextRange(target, node);
return thisArg ? factory.createSyntheticReferenceExpression(target, thisArg) : target;
}
function createNotNullCondition(left: Expression, right: Expression, invert?: boolean) {
return factory.createBinaryExpression(
factory.createBinaryExpression(
left,
factory.createToken(invert ? SyntaxKind.EqualsEqualsEqualsToken : SyntaxKind.ExclamationEqualsEqualsToken),
factory.createNull()
),
factory.createToken(invert ? SyntaxKind.BarBarToken : SyntaxKind.AmpersandAmpersandToken),
factory.createBinaryExpression(
right,
factory.createToken(invert ? SyntaxKind.EqualsEqualsEqualsToken : SyntaxKind.ExclamationEqualsEqualsToken),
factory.createVoidZero()
)
);
}
function transformNullishCoalescingExpression(node: BinaryExpression) {
let left = visitNode(node.left, visitor, isExpression);
let right = left;
if (!isSimpleCopiableExpression(left)) {
right = factory.createTempVariable(hoistVariableDeclaration);
left = factory.createAssignment(right, left);
}
return setTextRange(factory.createConditionalExpression(
createNotNullCondition(left, right),
/*questionToken*/ undefined,
right,
/*colonToken*/ undefined,
visitNode(node.right, visitor, isExpression),
), node);
}
function visitDeleteExpression(node: DeleteExpression) {
return isOptionalChain(skipParentheses(node.expression))
? setOriginalNode(visitNonOptionalExpression(node.expression, /*captureThisArg*/ false, /*isDelete*/ true), node)
: factory.updateDeleteExpression(node, visitNode(node.expression, visitor, isExpression));
return visitEachChild(node, visitor, context);
case SyntaxKind.DeleteExpression:
return visitDeleteExpression(node as DeleteExpression);
default:
return visitEachChild(node, visitor, context);
}
}
function flattenChain(chain: OptionalChain) {
Debug.assertNotNode(chain, isNonNullChain);
const links: OptionalChain[] = [chain];
while (!chain.questionDotToken && !isTaggedTemplateExpression(chain)) {
chain = cast(skipPartiallyEmittedExpressions(chain.expression), isOptionalChain);
Debug.assertNotNode(chain, isNonNullChain);
links.unshift(chain);
}
return { expression: chain.expression, chain: links };
}
function visitNonOptionalParenthesizedExpression(node: ParenthesizedExpression, captureThisArg: boolean, isDelete: boolean): Expression {
const expression = visitNonOptionalExpression(node.expression, captureThisArg, isDelete);
if (isSyntheticReference(expression)) {
// `(a.b)` -> { expression `((_a = a).b)`, thisArg: `_a` }
// `(a[b])` -> { expression `((_a = a)[b])`, thisArg: `_a` }
return factory.createSyntheticReferenceExpression(factory.updateParenthesizedExpression(node, expression.expression), expression.thisArg);
}
return factory.updateParenthesizedExpression(node, expression);
}
function visitNonOptionalPropertyOrElementAccessExpression(node: AccessExpression, captureThisArg: boolean, isDelete: boolean): Expression {
if (isOptionalChain(node)) {
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
return visitOptionalExpression(node, captureThisArg, isDelete);
}
let expression: Expression = visitNode(node.expression, visitor, isExpression);
Debug.assertNotNode(expression, isSyntheticReference);
let thisArg: Expression | undefined;
if (captureThisArg) {
if (!isSimpleCopiableExpression(expression)) {
thisArg = factory.createTempVariable(hoistVariableDeclaration);
expression = factory.createAssignment(thisArg, expression);
}
else {
thisArg = expression;
}
}
expression = node.kind === SyntaxKind.PropertyAccessExpression
? factory.updatePropertyAccessExpression(node, expression, visitNode(node.name, visitor, isIdentifier))
: factory.updateElementAccessExpression(node, expression, visitNode(node.argumentExpression, visitor, isExpression));
return thisArg ? factory.createSyntheticReferenceExpression(expression, thisArg) : expression;
}
function visitNonOptionalCallExpression(node: CallExpression, captureThisArg: boolean): Expression {
if (isOptionalChain(node)) {
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
return visitOptionalExpression(node, captureThisArg, /*isDelete*/ false);
}
if (isParenthesizedExpression(node.expression) && isOptionalChain(skipParentheses(node.expression))) {
// capture thisArg for calls of parenthesized optional chains like `(foo?.bar)()`
const expression = visitNonOptionalParenthesizedExpression(node.expression, /*captureThisArg*/ true, /*isDelete*/ false);
const args = visitNodes(node.arguments, visitor, isExpression);
if (isSyntheticReference(expression)) {
return setTextRange(factory.createFunctionCallCall(expression.expression, expression.thisArg, args), node);
}
return factory.updateCallExpression(node, expression, /*typeArguments*/ undefined, args);
}
return visitEachChild(node, visitor, context);
}
function visitNonOptionalExpression(node: Expression, captureThisArg: boolean, isDelete: boolean): Expression {
switch (node.kind) {
case SyntaxKind.ParenthesizedExpression: return visitNonOptionalParenthesizedExpression(node as ParenthesizedExpression, captureThisArg, isDelete);
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression: return visitNonOptionalPropertyOrElementAccessExpression(node as AccessExpression, captureThisArg, isDelete);
case SyntaxKind.CallExpression: return visitNonOptionalCallExpression(node as CallExpression, captureThisArg);
default: return visitNode(node, visitor, isExpression);
}
}
function visitOptionalExpression(node: OptionalChain, captureThisArg: boolean, isDelete: boolean): Expression {
const { expression, chain } = flattenChain(node);
const left = visitNonOptionalExpression(skipPartiallyEmittedExpressions(expression), isCallChain(chain[0]), /*isDelete*/ false);
let leftThisArg = isSyntheticReference(left) ? left.thisArg : undefined;
let capturedLeft = isSyntheticReference(left) ? left.expression : left;
let leftExpression = factory.restoreOuterExpressions(expression, capturedLeft, OuterExpressionKinds.PartiallyEmittedExpressions);
if (!isSimpleCopiableExpression(capturedLeft)) {
capturedLeft = factory.createTempVariable(hoistVariableDeclaration);
leftExpression = factory.createAssignment(capturedLeft, leftExpression);
}
let rightExpression = capturedLeft;
let thisArg: Expression | undefined;
for (let i = 0; i < chain.length; i++) {
const segment = chain[i];
switch (segment.kind) {
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
if (i === chain.length - 1 && captureThisArg) {
if (!isSimpleCopiableExpression(rightExpression)) {
thisArg = factory.createTempVariable(hoistVariableDeclaration);
rightExpression = factory.createAssignment(thisArg, rightExpression);
}
else {
thisArg = rightExpression;
}
}
rightExpression = segment.kind === SyntaxKind.PropertyAccessExpression
? factory.createPropertyAccessExpression(rightExpression, visitNode(segment.name, visitor, isIdentifier))
: factory.createElementAccessExpression(rightExpression, visitNode(segment.argumentExpression, visitor, isExpression));
break;
case SyntaxKind.CallExpression:
if (i === 0 && leftThisArg) {
if (!isGeneratedIdentifier(leftThisArg)) {
leftThisArg = factory.cloneNode(leftThisArg);
addEmitFlags(leftThisArg, EmitFlags.NoComments);
}
rightExpression = factory.createFunctionCallCall(
rightExpression,
leftThisArg.kind === SyntaxKind.SuperKeyword ? factory.createThis() : leftThisArg,
visitNodes(segment.arguments, visitor, isExpression)
);
}
else {
rightExpression = factory.createCallExpression(
rightExpression,
/*typeArguments*/ undefined,
visitNodes(segment.arguments, visitor, isExpression)
);
}
break;
}
setOriginalNode(rightExpression, segment);
}
const target = isDelete
? factory.createConditionalExpression(createNotNullCondition(leftExpression, capturedLeft, /*invert*/ true), /*questionToken*/ undefined, factory.createTrue(), /*colonToken*/ undefined, factory.createDeleteExpression(rightExpression))
: factory.createConditionalExpression(createNotNullCondition(leftExpression, capturedLeft, /*invert*/ true), /*questionToken*/ undefined, factory.createVoidZero(), /*colonToken*/ undefined, rightExpression);
setTextRange(target, node);
return thisArg ? factory.createSyntheticReferenceExpression(target, thisArg) : target;
}
function createNotNullCondition(left: Expression, right: Expression, invert?: boolean) {
return factory.createBinaryExpression(
factory.createBinaryExpression(
left,
factory.createToken(invert ? SyntaxKind.EqualsEqualsEqualsToken : SyntaxKind.ExclamationEqualsEqualsToken),
factory.createNull()
),
factory.createToken(invert ? SyntaxKind.BarBarToken : SyntaxKind.AmpersandAmpersandToken),
factory.createBinaryExpression(
right,
factory.createToken(invert ? SyntaxKind.EqualsEqualsEqualsToken : SyntaxKind.ExclamationEqualsEqualsToken),
factory.createVoidZero()
)
);
}
function transformNullishCoalescingExpression(node: BinaryExpression) {
let left = visitNode(node.left, visitor, isExpression);
let right = left;
if (!isSimpleCopiableExpression(left)) {
right = factory.createTempVariable(hoistVariableDeclaration);
left = factory.createAssignment(right, left);
}
return setTextRange(factory.createConditionalExpression(
createNotNullCondition(left, right),
/*questionToken*/ undefined,
right,
/*colonToken*/ undefined,
visitNode(node.right, visitor, isExpression),
), node);
}
function visitDeleteExpression(node: DeleteExpression) {
return isOptionalChain(skipParentheses(node.expression))
? setOriginalNode(visitNonOptionalExpression(node.expression, /*captureThisArg*/ false, /*isDelete*/ true), node)
: factory.updateDeleteExpression(node, visitNode(node.expression, visitor, isExpression));
}
}
+88 -82
View File
@@ -1,91 +1,97 @@
/*@internal*/
namespace ts {
export function transformES2021(context: TransformationContext) {
const {
hoistVariableDeclaration,
factory
} = context;
return chainBundle(context, transformSourceFile);
import {
AssignmentExpression, BinaryExpression, Bundle, chainBundle, getNonAssignmentOperatorForCompoundAssignment,
isAccessExpression, isExpression, isLeftHandSideExpression, isLogicalOrCoalescingAssignmentExpression,
isPropertyAccessExpression, isSimpleCopiableExpression, LogicalOrCoalescingAssignmentOperator, Node,
skipParentheses, SourceFile, SyntaxKind, Token, TransformationContext, TransformFlags, visitEachChild, visitNode,
VisitResult,
} from "../_namespaces/ts";
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
/** @internal */
export function transformES2021(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle {
const {
hoistVariableDeclaration,
factory
} = context;
return chainBundle(context, transformSourceFile);
return visitEachChild(node, visitor, context);
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsES2021) === 0) {
return node;
}
switch (node.kind) {
case SyntaxKind.BinaryExpression:
const binaryExpression = node as BinaryExpression;
if (isLogicalOrCoalescingAssignmentExpression(binaryExpression)) {
return transformLogicalAssignment(binaryExpression);
}
// falls through
default:
return visitEachChild(node, visitor, context);
}
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsES2021) === 0) {
return node;
}
function transformLogicalAssignment(binaryExpression: AssignmentExpression<Token<LogicalOrCoalescingAssignmentOperator>>): VisitResult<Node> {
const operator = binaryExpression.operatorToken;
const nonAssignmentOperator = getNonAssignmentOperatorForCompoundAssignment(operator.kind);
let left = skipParentheses(visitNode(binaryExpression.left, visitor, isLeftHandSideExpression));
let assignmentTarget = left;
const right = skipParentheses(visitNode(binaryExpression.right, visitor, isExpression));
if (isAccessExpression(left)) {
const propertyAccessTargetSimpleCopiable = isSimpleCopiableExpression(left.expression);
const propertyAccessTarget = propertyAccessTargetSimpleCopiable ? left.expression :
factory.createTempVariable(hoistVariableDeclaration);
const propertyAccessTargetAssignment = propertyAccessTargetSimpleCopiable ? left.expression : factory.createAssignment(
propertyAccessTarget,
left.expression
);
if (isPropertyAccessExpression(left)) {
assignmentTarget = factory.createPropertyAccessExpression(
propertyAccessTarget,
left.name
);
left = factory.createPropertyAccessExpression(
propertyAccessTargetAssignment,
left.name
);
switch (node.kind) {
case SyntaxKind.BinaryExpression:
const binaryExpression = node as BinaryExpression;
if (isLogicalOrCoalescingAssignmentExpression(binaryExpression)) {
return transformLogicalAssignment(binaryExpression);
}
else {
const elementAccessArgumentSimpleCopiable = isSimpleCopiableExpression(left.argumentExpression);
const elementAccessArgument = elementAccessArgumentSimpleCopiable ? left.argumentExpression :
factory.createTempVariable(hoistVariableDeclaration);
assignmentTarget = factory.createElementAccessExpression(
propertyAccessTarget,
elementAccessArgument
);
left = factory.createElementAccessExpression(
propertyAccessTargetAssignment,
elementAccessArgumentSimpleCopiable ? left.argumentExpression : factory.createAssignment(
elementAccessArgument,
left.argumentExpression
)
);
}
}
return factory.createBinaryExpression(
left,
nonAssignmentOperator,
factory.createParenthesizedExpression(
factory.createAssignment(
assignmentTarget,
right
)
)
);
// falls through
default:
return visitEachChild(node, visitor, context);
}
}
function transformLogicalAssignment(binaryExpression: AssignmentExpression<Token<LogicalOrCoalescingAssignmentOperator>>): VisitResult<Node> {
const operator = binaryExpression.operatorToken;
const nonAssignmentOperator = getNonAssignmentOperatorForCompoundAssignment(operator.kind);
let left = skipParentheses(visitNode(binaryExpression.left, visitor, isLeftHandSideExpression));
let assignmentTarget = left;
const right = skipParentheses(visitNode(binaryExpression.right, visitor, isExpression));
if (isAccessExpression(left)) {
const propertyAccessTargetSimpleCopiable = isSimpleCopiableExpression(left.expression);
const propertyAccessTarget = propertyAccessTargetSimpleCopiable ? left.expression :
factory.createTempVariable(hoistVariableDeclaration);
const propertyAccessTargetAssignment = propertyAccessTargetSimpleCopiable ? left.expression : factory.createAssignment(
propertyAccessTarget,
left.expression
);
if (isPropertyAccessExpression(left)) {
assignmentTarget = factory.createPropertyAccessExpression(
propertyAccessTarget,
left.name
);
left = factory.createPropertyAccessExpression(
propertyAccessTargetAssignment,
left.name
);
}
else {
const elementAccessArgumentSimpleCopiable = isSimpleCopiableExpression(left.argumentExpression);
const elementAccessArgument = elementAccessArgumentSimpleCopiable ? left.argumentExpression :
factory.createTempVariable(hoistVariableDeclaration);
assignmentTarget = factory.createElementAccessExpression(
propertyAccessTarget,
elementAccessArgument
);
left = factory.createElementAccessExpression(
propertyAccessTargetAssignment,
elementAccessArgumentSimpleCopiable ? left.argumentExpression : factory.createAssignment(
elementAccessArgument,
left.argumentExpression
)
);
}
}
return factory.createBinaryExpression(
left,
nonAssignmentOperator,
factory.createParenthesizedExpression(
factory.createAssignment(
assignmentTarget,
right
)
)
);
}
}
+113 -106
View File
@@ -1,122 +1,129 @@
/*@internal*/
namespace ts {
import {
Bundle,
chainBundle, EmitHint, Expression, getOriginalNodeId, Identifier, idText, isIdentifier, isPrivateIdentifier,
isPropertyAccessExpression, isPropertyAssignment, JsxClosingElement, JsxEmit, JsxOpeningElement,
JsxSelfClosingElement, Node, nodeIsSynthesized, PropertyAccessExpression, PropertyAssignment, setTextRange,
SourceFile, stringToToken, SyntaxKind, TransformationContext,
} from "../_namespaces/ts";
/**
* Transforms ES5 syntax into ES3 syntax.
*
* @param context Context and state information for the transformation.
*
* @internal
*/
export function transformES5(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle {
const { factory } = context;
const compilerOptions = context.getCompilerOptions();
// enable emit notification only if using --jsx preserve or react-native
let previousOnEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
let noSubstitution: boolean[];
if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) {
previousOnEmitNode = context.onEmitNode;
context.onEmitNode = onEmitNode;
context.enableEmitNotification(SyntaxKind.JsxOpeningElement);
context.enableEmitNotification(SyntaxKind.JsxClosingElement);
context.enableEmitNotification(SyntaxKind.JsxSelfClosingElement);
noSubstitution = [];
}
const previousOnSubstituteNode = context.onSubstituteNode;
context.onSubstituteNode = onSubstituteNode;
context.enableSubstitution(SyntaxKind.PropertyAccessExpression);
context.enableSubstitution(SyntaxKind.PropertyAssignment);
return chainBundle(context, transformSourceFile);
/**
* Transforms ES5 syntax into ES3 syntax.
* Transforms an ES5 source file to ES3.
*
* @param context Context and state information for the transformation.
* @param node A SourceFile
*/
export function transformES5(context: TransformationContext) {
const { factory } = context;
const compilerOptions = context.getCompilerOptions();
function transformSourceFile(node: SourceFile) {
return node;
}
// enable emit notification only if using --jsx preserve or react-native
let previousOnEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
let noSubstitution: boolean[];
if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) {
previousOnEmitNode = context.onEmitNode;
context.onEmitNode = onEmitNode;
context.enableEmitNotification(SyntaxKind.JsxOpeningElement);
context.enableEmitNotification(SyntaxKind.JsxClosingElement);
context.enableEmitNotification(SyntaxKind.JsxSelfClosingElement);
noSubstitution = [];
/**
* Called by the printer just before a node is printed.
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emitCallback A callback used to emit the node.
*/
function onEmitNode(hint: EmitHint, node: Node, emitCallback: (emitContext: EmitHint, node: Node) => void) {
switch (node.kind) {
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxClosingElement:
case SyntaxKind.JsxSelfClosingElement:
const tagName = (node as JsxOpeningElement | JsxClosingElement | JsxSelfClosingElement).tagName;
noSubstitution[getOriginalNodeId(tagName)] = true;
break;
}
const previousOnSubstituteNode = context.onSubstituteNode;
context.onSubstituteNode = onSubstituteNode;
context.enableSubstitution(SyntaxKind.PropertyAccessExpression);
context.enableSubstitution(SyntaxKind.PropertyAssignment);
return chainBundle(context, transformSourceFile);
previousOnEmitNode(hint, node, emitCallback);
}
/**
* Transforms an ES5 source file to ES3.
*
* @param node A SourceFile
*/
function transformSourceFile(node: SourceFile) {
/**
* Hooks node substitutions.
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to substitute.
*/
function onSubstituteNode(hint: EmitHint, node: Node) {
if (node.id && noSubstitution && noSubstitution[node.id]) {
return previousOnSubstituteNode(hint, node);
}
node = previousOnSubstituteNode(hint, node);
if (isPropertyAccessExpression(node)) {
return substitutePropertyAccessExpression(node);
}
else if (isPropertyAssignment(node)) {
return substitutePropertyAssignment(node);
}
return node;
}
/**
* Substitutes a PropertyAccessExpression whose name is a reserved word.
*
* @param node A PropertyAccessExpression
*/
function substitutePropertyAccessExpression(node: PropertyAccessExpression): Expression {
if (isPrivateIdentifier(node.name)) {
return node;
}
/**
* Called by the printer just before a node is printed.
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emitCallback A callback used to emit the node.
*/
function onEmitNode(hint: EmitHint, node: Node, emitCallback: (emitContext: EmitHint, node: Node) => void) {
switch (node.kind) {
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxClosingElement:
case SyntaxKind.JsxSelfClosingElement:
const tagName = (node as JsxOpeningElement | JsxClosingElement | JsxSelfClosingElement).tagName;
noSubstitution[getOriginalNodeId(tagName)] = true;
break;
}
previousOnEmitNode(hint, node, emitCallback);
const literalName = trySubstituteReservedName(node.name);
if (literalName) {
return setTextRange(factory.createElementAccessExpression(node.expression, literalName), node);
}
return node;
}
/**
* Hooks node substitutions.
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to substitute.
*/
function onSubstituteNode(hint: EmitHint, node: Node) {
if (node.id && noSubstitution && noSubstitution[node.id]) {
return previousOnSubstituteNode(hint, node);
}
node = previousOnSubstituteNode(hint, node);
if (isPropertyAccessExpression(node)) {
return substitutePropertyAccessExpression(node);
}
else if (isPropertyAssignment(node)) {
return substitutePropertyAssignment(node);
}
return node;
/**
* Substitutes a PropertyAssignment whose name is a reserved word.
*
* @param node A PropertyAssignment
*/
function substitutePropertyAssignment(node: PropertyAssignment): PropertyAssignment {
const literalName = isIdentifier(node.name) && trySubstituteReservedName(node.name);
if (literalName) {
return factory.updatePropertyAssignment(node, literalName, node.initializer);
}
return node;
}
/**
* Substitutes a PropertyAccessExpression whose name is a reserved word.
*
* @param node A PropertyAccessExpression
*/
function substitutePropertyAccessExpression(node: PropertyAccessExpression): Expression {
if (isPrivateIdentifier(node.name)) {
return node;
}
const literalName = trySubstituteReservedName(node.name);
if (literalName) {
return setTextRange(factory.createElementAccessExpression(node.expression, literalName), node);
}
return node;
}
/**
* Substitutes a PropertyAssignment whose name is a reserved word.
*
* @param node A PropertyAssignment
*/
function substitutePropertyAssignment(node: PropertyAssignment): PropertyAssignment {
const literalName = isIdentifier(node.name) && trySubstituteReservedName(node.name);
if (literalName) {
return factory.updatePropertyAssignment(node, literalName, node.initializer);
}
return node;
}
/**
* If an identifier name is a reserved word, returns a string literal for the name.
*
* @param name An Identifier
*/
function trySubstituteReservedName(name: Identifier) {
const token = name.originalKeywordKind || (nodeIsSynthesized(name) ? stringToToken(idText(name)) : undefined);
if (token !== undefined && token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord) {
return setTextRange(factory.createStringLiteralFromNode(name), name);
}
return undefined;
/**
* If an identifier name is a reserved word, returns a string literal for the name.
*
* @param name An Identifier
*/
function trySubstituteReservedName(name: Identifier) {
const token = name.originalKeywordKind || (nodeIsSynthesized(name) ? stringToToken(idText(name)) : undefined);
if (token !== undefined && token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord) {
return setTextRange(factory.createStringLiteralFromNode(name), name);
}
return undefined;
}
}
+20 -17
View File
@@ -1,24 +1,27 @@
/*@internal*/
namespace ts {
export function transformESNext(context: TransformationContext) {
return chainBundle(context, transformSourceFile);
import {
Bundle,
chainBundle, Node, SourceFile, TransformationContext, TransformFlags, visitEachChild, VisitResult,
} from "../_namespaces/ts";
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
/** @internal */
export function transformESNext(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle {
return chainBundle(context, transformSourceFile);
return visitEachChild(node, visitor, context);
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsESNext) === 0) {
return node;
}
switch (node.kind) {
default:
return visitEachChild(node, visitor, context);
}
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsESNext) === 0) {
return node;
}
switch (node.kind) {
default:
return visitEachChild(node, visitor, context);
}
}
}

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