diff --git a/.eslintrc.json b/.eslintrc.json index 0bdba32ded551..affed4dbc0108 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -17,7 +17,9 @@ "@typescript-eslint/adjacent-overload-signatures": "error", "@typescript-eslint/array-type": "error", - "camelcase": "off", + "brace-style": "off", + "@typescript-eslint/brace-style": ["error", "stroustrup", { "allowSingleLine": true }], + "@typescript-eslint/naming-convention": [ "error", { "selector": "typeLike", "format": ["PascalCase"], "filter": { "regex": "^(__String|[A-Za-z]+_[A-Za-z]+)$", "match": false } }, @@ -32,6 +34,10 @@ ], "@typescript-eslint/consistent-type-definitions": ["error", "interface"], + + "no-duplicate-imports": "off", + "@typescript-eslint/no-duplicate-imports": "error", + "@typescript-eslint/no-inferrable-types": "error", "@typescript-eslint/no-misused-new": "error", "@typescript-eslint/no-this-alias": "error", @@ -86,7 +92,6 @@ "jsdoc/check-alignment": "error", // eslint - "brace-style": ["error", "stroustrup", { "allowSingleLine": true }], "constructor-super": "error", "curly": ["error", "multi-line"], "dot-notation": "error", @@ -95,7 +100,6 @@ "new-parens": "error", "no-caller": "error", "no-duplicate-case": "error", - "no-duplicate-imports": "error", "no-empty": "error", "no-eval": "error", "no-extra-bind": "error", diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 56427fe58a36d..6cc9e4cd7c87a 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -4,41 +4,70 @@ about: Create a report to help us improve TypeScript title: '' labels: '' assignees: '' - --- +# Bug Report - -Please help us by doing the following steps before logging an issue: - * Search: https://github.com/Microsoft/TypeScript/search?type=Issues - * Read the FAQ: https://github.com/Microsoft/TypeScript/wiki/FAQ +### 🔎 Search Terms -Please fill in the *entire* template below. + - -**TypeScript Version:** 3.7.x-dev.201xxxxx +- This is a crash +- This changed between versions ______ and _______ +- This is the behavior in every version I tried, and I reviewed the FAQ for entries about _________ +- I was unable to test this on prior versions because _______ + +### ⏯ Playground Link - -**Search Terms:** + +[Playground link with relevant code](https://www.typescriptlang.org/play?#code/PTAEFkE9QYwewCYFNQHM5IM6gBZIE5JA) -**Code** +### 💻 Code + ```ts -// A *self-contained* demonstration of the problem follows... -// Test this by running `tsc` on the command-line, rather than through another build tool such as Gulp, Webpack, etc. +// We can quickly address your report if: +// - The code sample is short. Nearly all TypeScript bugs can be demonstrated in 20-30 lines of code! +// - It doesn't use external libraries. These are often issues with the type definitions rather than TypeScript bugs. +// - The incorrectness of the behavior is readily apparent from reading the sample. +// Reports are slower to investigate if: +// - We have to pare too much extraneous code. +// - We have to clone a large repo and validate that the problem isn't elsewhere. +// - The sample is confusing or doesn't clearly demonstrate what's wrong. ``` -**Expected behavior:** +### 🙁 Actual behavior -**Actual behavior:** + -**Playground Link:** +### 🙂 Expected behavior -**Related Issues:** + diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index e5f21faeafebb..cbc58730a7e30 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -4,44 +4,56 @@ about: Suggest an idea title: '' labels: '' assignees: '' - --- +# Suggestion - -## Search Terms +## 🔍 Search Terms - + - +List of keywords you searched for before creating this issue. Write them down here so that others can find this suggestion more easily and help provide feedback. -## Use Cases +## ✅ Viability Checklist - -## Examples - - - -## Checklist - My suggestion meets these guidelines: * [ ] This wouldn't be a breaking change in existing TypeScript/JavaScript code * [ ] This wouldn't change the runtime behavior of existing JavaScript code * [ ] This could be implemented without emitting different JS based on the types of the expressions -* [ ] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.) +* [ ] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.) * [ ] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals). + + +## ⭐ Suggestion + + + +## 📃 Motivating Example + + + +## 💻 Use Cases + + diff --git a/.github/ISSUE_TEMPLATE/lib_change.md b/.github/ISSUE_TEMPLATE/lib_change.md new file mode 100644 index 0000000000000..e1f81555a1253 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/lib_change.md @@ -0,0 +1,55 @@ +--- +name: Library change +about: Fix or improve issues with built-in type definitions like `lib.dom.d.ts`, `lib.es6.d.ts`, etc. +title: '' +labels: '' +assignees: '' +--- +# lib Update Request + + + +## Configuration Check + + +My compilation *target* is `ES2015` and my *lib* is `the default`. + +## Missing / Incorrect Definition + + + +## Sample Code + + + +## Documentation Link + + diff --git a/.github/pr_owners.txt b/.github/pr_owners.txt index 0d0dab4f3e008..6177279468b6d 100644 --- a/.github/pr_owners.txt +++ b/.github/pr_owners.txt @@ -6,3 +6,8 @@ RyanCavanaugh sheetalkamat orta rbuckton +ahejlsberg +amcasey +jessetrinity +minestarks +uniqueiniquity diff --git a/.github/workflows/accept-baselines-fix-lints.yaml b/.github/workflows/accept-baselines-fix-lints.yaml new file mode 100644 index 0000000000000..d92b1ae1836fd --- /dev/null +++ b/.github/workflows/accept-baselines-fix-lints.yaml @@ -0,0 +1,29 @@ +name: Accept Baselines and Fix Lints + +on: + workflow_dispatch: {} + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use node version 12 + uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + + - name: Configure Git, Run Tests, Update Baselines, Apply Fixes + run: | + git config user.email "typescriptbot@microsoft.com" + git config user.name "TypeScript Bot" + npm install + gulp runtests-parallel --ci --fix || true + gulp baseline-accept + git add ./src + git add ./tests/baselines/reference + git diff --cached + git commit -m "Update Baselines and/or Applied Lint Fixes" + git push diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89d895e5d8ce9..ae0e4fbdaa9bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,9 @@ jobs: - name: Linter run: npm run lint:ci + - name: Adding playwright + run: npm install --no-save --no-package-lock playwright + - name: Validate the browser can import TypeScript run: gulp test-browser-integration diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 24c3ed774f339..abe704bb564d8 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -22,7 +22,7 @@ jobs: - name: Setup and publish nightly run: | npm whoami - npm i + npm ci gulp configure-nightly gulp LKG gulp runtests-parallel diff --git a/.github/workflows/release-branch-artifact.yaml b/.github/workflows/release-branch-artifact.yaml index 7683b7623acee..363d03fc2f69b 100644 --- a/.github/workflows/release-branch-artifact.yaml +++ b/.github/workflows/release-branch-artifact.yaml @@ -25,6 +25,8 @@ jobs: npm test env: CI: true + - name: Adding playwright + run: npm install --no-save --no-package-lock playwright - name: Validate the browser can import TypeScript run: gulp test-browser-integration - name: LKG, clean, and pack diff --git a/.github/workflows/sync-branch.yaml b/.github/workflows/sync-branch.yaml index 6be9650a2ed9e..a8dd55b3c708f 100644 --- a/.github/workflows/sync-branch.yaml +++ b/.github/workflows/sync-branch.yaml @@ -21,6 +21,7 @@ jobs: - uses: actions/checkout@v2 with: ref: ${{ github.event.inputs.branch_name || github.event.client_payload.branch_name }} + fetch-depth: 0 # This does a test post-merge and only pushes the result if the test succeeds # required client_payload members: # branch_name - the target branch diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 459b0793f8c45..95437f807ce2c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -189,16 +189,25 @@ import { f as g } from "file1"; var x = g(); ``` -## Managing the Baselines +## Managing the baselines -Compiler tests generate baselines: one file each for the emitted `.js`, the errors produced by the compiler, the type of each expression, and symbol for each identifier. Additionally, some tests generate baselines for the source map output. +Most tests generate "baselines" to find differences in output. +As an example, compiler tests usually emit one file each for + +- the `.js` and `.d.ts` output (all in the same `.js` output file), +- the errors produced by the compiler (in an `.errors.txt` file), +- the types of each expression (in a `.types` file), +- the symbols for each identifier (in a `.symbols` file), and +- the source map outputs for files if a test opts into them (in a `.js.map` file). When a change in the baselines is detected, the test will fail. To inspect changes vs the expected baselines, use ```Shell -gulp diff +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. + After verifying that the changes in the baselines are correct, run ```Shell diff --git a/Gulpfile.js b/Gulpfile.js index 82af1630c5cfc..729b49b8f4ff6 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -308,6 +308,8 @@ const watchLssl = () => watch([ "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); @@ -590,6 +592,7 @@ 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.js", path.resolve("doc/TypeScript Language Specification - ARCHIVED.docx"), path.resolve("doc/spec-ARCHIVED.md")]); task("generate-spec", series(buildScripts, generateSpec)); diff --git a/README.md b/README.md index bb0452217c160..8bb85ada6bb97 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ There are many ways to [contribute](https://github.com/microsoft/TypeScript/blob * Help each other in the [TypeScript Community Discord](https://discord.gg/typescript). * Join the [#typescript](https://twitter.com/search?q=%23TypeScript) discussion on Twitter. * [Contribute bug fixes](https://github.com/microsoft/TypeScript/blob/master/CONTRIBUTING.md). -* Read the language specification ([docx](https://github.com/microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.docx?raw=true), - [pdf](https://github.com/microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true), [md](https://github.com/microsoft/TypeScript/blob/master/doc/spec.md)). +* Read the archived language specification ([docx](https://github.com/microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification%20-%20ARCHIVED.docx?raw=true), + [pdf](https://github.com/microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification%20-%20ARCHIVED.pdf?raw=true), [md](https://github.com/microsoft/TypeScript/blob/master/doc/spec-ARCHIVED.md)). This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) @@ -78,8 +78,6 @@ 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. - # Some low-value tests are skipped when not on a CI machine - you can use the - # --skipPercent=0 command to override this behavior and run all tests locally. # You can override the specific suite runner used or specify a test for this command. # Use --tests= for a specific test and/or --runner= for a specific suite. # Valid runners include conformance, compiler, fourslash, project, user, and docker diff --git a/doc/handbook/README.md b/doc/handbook/README.md index 2d2e0e83a466d..bfe230f160432 100644 --- a/doc/handbook/README.md +++ b/doc/handbook/README.md @@ -1,4 +1,5 @@ # The TypeScript Handbook -The contents of the TypeScript Handbook can be read from [its GitHub repository](https://github.com/Microsoft/TypeScript-Handbook). -Issues and pull requests should be directed there. \ No newline at end of file +The contents of the TypeScript Handbook can be found in the +[TypeScript website repository](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/documentation). +Issues and pull requests should be directed there. diff --git a/package-lock.json b/package-lock.json index aa2ba5e2ad234..5e8e62d31c7d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "typescript", - "version": "4.1.0", + "version": "4.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -141,34 +141,68 @@ "tslib": "^1.9.3" } }, + "@eslint/eslintrc": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", + "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, "@gulp-sourcemaps/identity-map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", - "integrity": "sha512-ciiioYMLdo16ShmfHBXJBOFm3xPC4AuwO4xeRpFeHz7WK9PYsWCmigagG2XyzZpubK4a3qNKoUBDhbzHfa50LQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", + "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", "dev": true, "requires": { - "acorn": "^5.0.3", - "css": "^2.2.1", - "normalize-path": "^2.1.1", + "acorn": "^6.4.1", + "normalize-path": "^3.0.0", + "postcss": "^7.0.16", "source-map": "^0.6.0", - "through2": "^2.0.3" + "through2": "^3.0.1" }, "dependencies": { "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "dev": true }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -176,13 +210,13 @@ "dev": true }, "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "inherits": "^2.0.4", + "readable-stream": "2 || 3" } } } @@ -245,84 +279,90 @@ } }, "@octokit/auth-token": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.2.tgz", - "integrity": "sha512-jE/lE/IKIz2v1+/P0u4fJqv0kYwXOTujKemJMFr6FeopsxlIK3+wKDCJGnysg81XID5TgZQbIfuJ5J0lnTiuyQ==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.4.tgz", + "integrity": "sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q==", "dev": true, "requires": { - "@octokit/types": "^5.0.0" + "@octokit/types": "^6.0.0" } }, "@octokit/core": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.1.2.tgz", - "integrity": "sha512-AInOFULmwOa7+NFi9F8DlDkm5qtZVmDQayi7TUgChE3yeIGPq0Y+6cAEXPexQ3Ea+uZy66hKEazR7DJyU+4wfw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.2.4.tgz", + "integrity": "sha512-d9dTsqdePBqOn7aGkyRFe7pQpCXdibSJ5SFnrTr0axevObZrpz3qkWm7t/NjYv5a66z6vhfteriaq4FRz3e0Qg==", "dev": true, "requires": { - "@octokit/auth-token": "^2.4.0", - "@octokit/graphql": "^4.3.1", - "@octokit/request": "^5.4.0", - "@octokit/types": "^5.0.0", + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.4.12", + "@octokit/types": "^6.0.3", "before-after-hook": "^2.1.0", "universal-user-agent": "^6.0.0" } }, "@octokit/endpoint": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.6.tgz", - "integrity": "sha512-7Cc8olaCoL/mtquB7j/HTbPM+sY6Ebr4k2X2y4JoXpVKQ7r5xB4iGQE0IoO58wIPsUk4AzoT65AMEpymSbWTgQ==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.10.tgz", + "integrity": "sha512-9+Xef8nT7OKZglfkOMm7IL6VwxXUQyR7DUSU0LH/F7VNqs8vyd7es5pTfz9E7DwUIx7R3pGscxu1EBhYljyu7Q==", "dev": true, "requires": { - "@octokit/types": "^5.0.0", + "@octokit/types": "^6.0.0", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" } }, "@octokit/graphql": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.6.tgz", - "integrity": "sha512-Rry+unqKTa3svswT2ZAuqenpLrzJd+JTv89LTeVa5UM/5OX8o4KTkPL7/1ABq4f/ZkELb0XEK/2IEoYwykcLXg==", + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.8.tgz", + "integrity": "sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA==", "dev": true, "requires": { "@octokit/request": "^5.3.0", - "@octokit/types": "^5.0.0", + "@octokit/types": "^6.0.0", "universal-user-agent": "^6.0.0" } }, + "@octokit/openapi-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-2.0.1.tgz", + "integrity": "sha512-9AuC04PUnZrjoLiw3uPtwGh9FE4Q3rTqs51oNlQ0rkwgE8ftYsOC+lsrQyvCvWm85smBbSc0FNRKKumvGyb44Q==", + "dev": true + }, "@octokit/plugin-paginate-rest": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.4.0.tgz", - "integrity": "sha512-YT6Klz3LLH6/nNgi0pheJnUmTFW4kVnxGft+v8Itc41IIcjl7y1C8TatmKQBbCSuTSNFXO5pCENnqg6sjwpJhg==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.6.2.tgz", + "integrity": "sha512-3Dy7/YZAwdOaRpGQoNHPeT0VU1fYLpIUdPyvR37IyFLgd6XSij4j9V/xN/+eSjF2KKvmfIulEh9LF1tRPjIiDA==", "dev": true, "requires": { - "@octokit/types": "^5.5.0" + "@octokit/types": "^6.0.1" } }, "@octokit/plugin-request-log": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz", - "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz", + "integrity": "sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg==", "dev": true }, "@octokit/plugin-rest-endpoint-methods": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.1.4.tgz", - "integrity": "sha512-Y2tVpSa7HjV3DGIQrQOJcReJ2JtcN9FaGr9jDa332Flro923/h3/Iu9e7Y4GilnzfLclHEh5iCQoCkHm7tWOcg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz", + "integrity": "sha512-+v5PcvrUcDeFXf8hv1gnNvNLdm4C0+2EiuWt9EatjjUmfriM1pTMM+r4j1lLHxeBQ9bVDmbywb11e3KjuavieA==", "dev": true, "requires": { - "@octokit/types": "^5.4.1", + "@octokit/types": "^6.1.0", "deprecation": "^2.3.1" } }, "@octokit/request": { - "version": "5.4.9", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.9.tgz", - "integrity": "sha512-CzwVvRyimIM1h2n9pLVYfTDmX9m+KHSgCpqPsY8F1NdEK8IaWqXhSBXsdjOBFZSpEcxNEeg4p0UO9cQ8EnOCLA==", + "version": "5.4.12", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.12.tgz", + "integrity": "sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg==", "dev": true, "requires": { "@octokit/endpoint": "^6.0.1", "@octokit/request-error": "^2.0.0", - "@octokit/types": "^5.0.0", + "@octokit/types": "^6.0.3", "deprecation": "^2.0.0", "is-plain-object": "^5.0.0", "node-fetch": "^2.6.1", @@ -339,34 +379,35 @@ } }, "@octokit/request-error": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.2.tgz", - "integrity": "sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.4.tgz", + "integrity": "sha512-LjkSiTbsxIErBiRh5wSZvpZqT4t0/c9+4dOe0PII+6jXR+oj/h66s7E4a/MghV7iT8W9ffoQ5Skoxzs96+gBPA==", "dev": true, "requires": { - "@octokit/types": "^5.0.1", + "@octokit/types": "^6.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "@octokit/rest": { - "version": "18.0.5", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.5.tgz", - "integrity": "sha512-SPKI24tQXrr1XsnaIjv2x0rl4M5eF1+hj8+vMe3d/exZ7NnL5sTe1BuFyCyJyrc+j1HkXankvgGN9zT0rwBwtg==", + "version": "18.0.12", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.12.tgz", + "integrity": "sha512-hNRCZfKPpeaIjOVuNJzkEL6zacfZlBPV8vw8ReNeyUkVvbuCvvrrx8K8Gw2eyHHsmd4dPlAxIXIZ9oHhJfkJpw==", "dev": true, "requires": { - "@octokit/core": "^3.0.0", - "@octokit/plugin-paginate-rest": "^2.2.0", - "@octokit/plugin-request-log": "^1.0.0", - "@octokit/plugin-rest-endpoint-methods": "4.1.4" + "@octokit/core": "^3.2.3", + "@octokit/plugin-paginate-rest": "^2.6.2", + "@octokit/plugin-request-log": "^1.0.2", + "@octokit/plugin-rest-endpoint-methods": "4.4.1" } }, "@octokit/types": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz", - "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.1.2.tgz", + "integrity": "sha512-LPCpcLbcky7fWfHCTuc7tMiSHFpFlrThJqVdaHgowBTMS0ijlZFfonQC/C1PrZOjD4xRCYgBqH9yttEATGE/nw==", "dev": true, "requires": { + "@octokit/openapi-types": "^2.0.1", "@types/node": ">= 8" } }, @@ -381,15 +422,9 @@ } }, "@types/chai": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz", - "integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ==", - "dev": true - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.14.tgz", + "integrity": "sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==", "dev": true }, "@types/convert-source-map": { @@ -398,12 +433,6 @@ "integrity": "sha512-laiDIXqqthjJlyAMYAXOtN3N8+UlbM+KvZi4BaY5ZOekmVkBs/UxfK5O0HWeJVG2eW8F+Mu2ww13fTX+kY1FlQ==", "dev": true }, - "@types/eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", - "dev": true - }, "@types/expect": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", @@ -496,9 +525,15 @@ } }, "@types/json-schema": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", - "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, "@types/merge2": { @@ -511,9 +546,9 @@ } }, "@types/microsoft__typescript-etw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@types/microsoft__typescript-etw/-/microsoft__typescript-etw-0.1.0.tgz", - "integrity": "sha512-ShankhBwAF3LZAkkby3w++HNhwPjWJDv0rSjrHNXO/Nlt/63mSZKNhfu99PNq+Nmyy1xGNknreKacpEuvpeWNA==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@types/microsoft__typescript-etw/-/microsoft__typescript-etw-0.1.1.tgz", + "integrity": "sha512-zdgHyZJEwbFKI6zhOqWPsNMhlrAk6qMrn9VMA6VQtRt/F+jNJKeaHIMysuO9oTLv0fWcli0gwUrMv8MeFyb3Sw==", "dev": true }, "@types/minimatch": { @@ -523,9 +558,9 @@ "dev": true }, "@types/minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", "dev": true }, "@types/mkdirp": { @@ -538,9 +573,9 @@ } }, "@types/mocha": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz", - "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", + "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", "dev": true }, "@types/ms": { @@ -550,9 +585,9 @@ "dev": true }, "@types/node": { - "version": "14.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.10.1.tgz", - "integrity": "sha512-aYNbO+FZ/3KGeQCEkNhHFRIzBOUgc7QvcVNKXbfnhDkSfwUv91JsQQa10rDgKSTSLkXZ1UIyPe4FJJNVgw1xWQ==", + "version": "14.14.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.19.tgz", + "integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==", "dev": true }, "@types/node-fetch": { @@ -650,12 +685,13 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.9.1.tgz", - "integrity": "sha512-XIr+Mfv7i4paEdBf0JFdIl9/tVxyj+rlilWIfZ97Be0lZ7hPvUbS5iHt9Glc8kRI53dsr0PcAEudbf8rO2wGgg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.5.0.tgz", + "integrity": "sha512-mjb/gwNcmDKNt+6mb7Aj/TjKzIJjOPcoCJpjBQC9ZnTRnBt1p4q5dJSSmIqAtsZ/Pff5N+hJlbiPc5bl6QN4OQ==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "3.9.1", + "@typescript-eslint/experimental-utils": "4.5.0", + "@typescript-eslint/scope-manager": "4.5.0", "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", @@ -664,58 +700,95 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } } } }, "@typescript-eslint/experimental-utils": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.9.1.tgz", - "integrity": "sha512-lkiZ8iBBaYoyEKhCkkw4SAeatXyBq9Ece5bZXdLe1LWBUwTszGbmbiqmQbwWA8cSYDnjWXp9eDbXpf9Sn0hLAg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.5.0.tgz", + "integrity": "sha512-bW9IpSAKYvkqDGRZzayBXIgPsj2xmmVHLJ+flGSoN0fF98pGoKFhbunIol0VF2Crka7z984EEhFi623Rl7e6gg==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/types": "3.9.1", - "@typescript-eslint/typescript-estree": "3.9.1", + "@typescript-eslint/scope-manager": "4.5.0", + "@typescript-eslint/types": "4.5.0", + "@typescript-eslint/typescript-estree": "4.5.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" } }, "@typescript-eslint/parser": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.9.1.tgz", - "integrity": "sha512-y5QvPFUn4Vl4qM40lI+pNWhTcOWtpZAJ8pOEQ21fTTW4xTJkRplMjMRje7LYTXqVKKX9GJhcyweMz2+W1J5bMg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.5.0.tgz", + "integrity": "sha512-xb+gmyhQcnDWe+5+xxaQk5iCw6KqXd8VQxGiTeELTMoYeRjpocZYYRP1gFVM2C8Yl0SpUvLa1lhprwqZ00w3Iw==", "dev": true, "requires": { - "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "3.9.1", - "@typescript-eslint/types": "3.9.1", - "@typescript-eslint/typescript-estree": "3.9.1", - "eslint-visitor-keys": "^1.1.0" + "@typescript-eslint/scope-manager": "4.5.0", + "@typescript-eslint/types": "4.5.0", + "@typescript-eslint/typescript-estree": "4.5.0", + "debug": "^4.1.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.5.0.tgz", + "integrity": "sha512-C0cEO0cTMPJ/w4RA/KVe4LFFkkSh9VHoFzKmyaaDWAnPYIEzVCtJ+Un8GZoJhcvq+mPFXEsXa01lcZDHDG6Www==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.5.0", + "@typescript-eslint/visitor-keys": "4.5.0" } }, "@typescript-eslint/types": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.9.1.tgz", - "integrity": "sha512-15JcTlNQE1BsYy5NBhctnEhEoctjXOjOK+Q+rk8ugC+WXU9rAcS2BYhoh6X4rOaXJEpIYDl+p7ix+A5U0BqPTw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.5.0.tgz", + "integrity": "sha512-n2uQoXnyWNk0Les9MtF0gCK3JiWd987JQi97dMSxBOzVoLZXCNtxFckVqt1h8xuI1ix01t+iMY4h4rFMj/303g==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.9.1.tgz", - "integrity": "sha512-IqM0gfGxOmIKPhiHW/iyAEXwSVqMmR2wJ9uXHNdFpqVvPaQ3dWg302vW127sBpAiqM9SfHhyS40NKLsoMpN2KA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.5.0.tgz", + "integrity": "sha512-gN1mffq3zwRAjlYWzb5DanarOPdajQwx5MEWkWCk0XvqC8JpafDTeioDoow2L4CA/RkYZu7xEsGZRhqrTsAG8w==", "dev": true, "requires": { - "@typescript-eslint/types": "3.9.1", - "@typescript-eslint/visitor-keys": "3.9.1", + "@typescript-eslint/types": "4.5.0", + "@typescript-eslint/visitor-keys": "4.5.0", "debug": "^4.1.1", - "glob": "^7.1.6", + "globby": "^11.0.1", "is-glob": "^4.0.1", "lodash": "^4.17.15", "semver": "^7.3.2", @@ -723,25 +796,54 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" } } } }, "@typescript-eslint/visitor-keys": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.9.1.tgz", - "integrity": "sha512-zxdtUjeoSh+prCpogswMwVUJfEFmCOjdzK9rpNjNBfm6EyPt99x3RrJoBOGZO23FCt0WPKUCOL5mb/9D5LjdwQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.5.0.tgz", + "integrity": "sha512-UHq4FSa55NDZqscRU//O5ROFhHa9Hqn9KWTEvJGTArtTQp5GKv9Zqf6d/Q3YXXcFv4woyBml7fJQlQ+OuqRcHA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" + "@typescript-eslint/types": "4.5.0", + "eslint-visitor-keys": "^2.0.0" } }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -759,15 +861,15 @@ "dev": true }, "acorn": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", - "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true }, "acorn-jsx": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", - "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", "dev": true }, "acorn-node": { @@ -787,15 +889,6 @@ "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, "aggregate-error": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", @@ -807,9 +900,9 @@ } }, "ajv": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", - "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -843,23 +936,6 @@ "ansi-wrap": "0.1.0" } }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - }, - "dependencies": { - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - } - } - }, "ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", @@ -885,12 +961,11 @@ "dev": true }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -987,6 +1062,12 @@ "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", "dev": true }, + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + }, "array-includes": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", @@ -1080,18 +1161,6 @@ "es-abstract": "^1.17.0-next.1" } }, - "array.prototype.map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", - "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.4" - } - }, "asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -1181,12 +1250,6 @@ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, "async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", @@ -1214,6 +1277,15 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "dev": true, + "requires": { + "array-filter": "^1.0.0" + } + }, "azure-devops-node-api": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-10.1.1.tgz", @@ -1304,9 +1376,9 @@ } }, "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, "before-after-hook": { @@ -1424,9 +1496,9 @@ "dev": true }, "browserify": { - "version": "16.5.2", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.5.2.tgz", - "integrity": "sha512-TkOR1cQGdmXU9zW4YukWzWVSJwrxmNdADFbqbE3HFgQWe5wqZmOawqZ7J/8MPCwk/W8yY7Y0h+7mOtcZxLP23g==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", + "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", "dev": true, "requires": { "JSONStream": "^1.0.3", @@ -1441,31 +1513,31 @@ "constants-browserify": "~1.0.0", "crypto-browserify": "^3.0.0", "defined": "^1.0.0", - "deps-sort": "^2.0.0", + "deps-sort": "^2.0.1", "domain-browser": "^1.2.0", "duplexer2": "~0.1.2", - "events": "^2.0.0", + "events": "^3.0.0", "glob": "^7.1.0", "has": "^1.0.0", "htmlescape": "^1.1.0", "https-browserify": "^1.0.0", "inherits": "~2.0.1", - "insert-module-globals": "^7.0.0", + "insert-module-globals": "^7.2.1", "labeled-stream-splicer": "^2.0.0", "mkdirp-classic": "^0.5.2", "module-deps": "^6.2.3", "os-browserify": "~0.3.0", "parents": "^1.0.1", - "path-browserify": "~0.0.0", + "path-browserify": "^1.0.0", "process": "~0.11.0", "punycode": "^1.3.2", "querystring-es3": "~0.2.0", "read-only-stream": "^2.0.0", "readable-stream": "^2.0.2", "resolve": "^1.1.4", - "shasum": "^1.0.0", + "shasum-object": "^1.0.0", "shell-quote": "^1.6.1", - "stream-browserify": "^2.0.0", + "stream-browserify": "^3.0.0", "stream-http": "^3.0.0", "string_decoder": "^1.1.1", "subarg": "^1.0.0", @@ -1474,7 +1546,7 @@ "timers-browserify": "^1.0.1", "tty-browserify": "0.0.1", "url": "~0.11.0", - "util": "~0.10.1", + "util": "~0.12.0", "vm-browserify": "^1.0.0", "xtend": "^4.0.0" }, @@ -1489,16 +1561,26 @@ }, "dependencies": { "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } } } }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -1549,21 +1631,13 @@ } }, "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", "dev": true, "requires": { - "bn.js": "^4.1.0", + "bn.js": "^5.0.0", "randombytes": "^2.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } } }, "browserify-sign": { @@ -1621,12 +1695,6 @@ "ieee754": "^1.1.4" } }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true - }, "buffer-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", @@ -1674,6 +1742,16 @@ "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", "dev": true }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1710,12 +1788,6 @@ "supports-color": "^7.1.0" } }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -1781,21 +1853,6 @@ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -2143,15 +2200,14 @@ } }, "css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", "dev": true, "requires": { - "inherits": "^2.0.3", + "inherits": "^2.0.4", "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" + "source-map-resolve": "^0.6.0" }, "dependencies": { "source-map": { @@ -2159,6 +2215,16 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } } } }, @@ -2207,9 +2273,9 @@ }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -2271,7 +2337,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -2523,9 +2588,9 @@ } }, "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "end-of-stream": { @@ -2537,6 +2602,23 @@ "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + } + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2547,50 +2629,74 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" - } - }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, - "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", - "dev": true, - "requires": { - "es-abstract": "^1.17.4", - "has-symbols": "^1.0.1", - "is-arguments": "^1.0.4", - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-string": "^1.0.5", - "isarray": "^2.0.5" }, "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } } } }, @@ -2598,7 +2704,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -2627,21 +2732,6 @@ "es6-symbol": "^3.1.1" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, "es6-symbol": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", @@ -2708,22 +2798,24 @@ } }, "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.12.1.tgz", + "integrity": "sha512-HlMTEdr/LicJfN08LB3nM1rRYliDXOmfoO4vj39xN6BLpFzF00hbwBoqHk8UcJ2M/3nlARZWy/mslvGEuZFvsg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.2.1", "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.0", + "esquery": "^1.2.0", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", @@ -2732,77 +2824,50 @@ "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", + "levn": "^0.4.1", + "lodash": "^4.17.19", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.3", + "optionator": "^0.9.1", "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", "table": "^5.2.3", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "glob-parent": { @@ -2814,70 +2879,91 @@ "is-glob": "^4.0.1" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { - "minimist": "^1.2.5" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" } } } }, "eslint-formatter-autolinkable-stylish": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/eslint-formatter-autolinkable-stylish/-/eslint-formatter-autolinkable-stylish-1.1.2.tgz", - "integrity": "sha512-vgBkDdzxRsr6/NnWqv7I3MhBBNNIMAs+aGgkIrR2NYtgI4qKbeFlajPEgWdSS5wdQDNGPdtkaxZEvYUnw6wsAg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/eslint-formatter-autolinkable-stylish/-/eslint-formatter-autolinkable-stylish-1.1.4.tgz", + "integrity": "sha512-aw8TKZLTiSMMstbfWNQiFZ0Em0sBW32NP8O8RUVPiF9kPjoYWg6M1lXcBr6MGijt39tmSFrdibgWQ2S6HYVsMA==", "dev": true, "requires": { - "chalk": "^3.0.0", - "plur": "^3.1.1" - }, - "dependencies": { - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } + "chalk": "^4.1.0", + "plur": "^4.0.0" } }, "eslint-import-resolver-node": { @@ -2891,11 +2977,12 @@ }, "dependencies": { "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", "dev": true, "requires": { + "is-core-module": "^2.0.0", "path-parse": "^1.0.6" } } @@ -2912,23 +2999,24 @@ } }, "eslint-plugin-import": { - "version": "2.20.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz", - "integrity": "sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg==", + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", "dev": true, "requires": { - "array-includes": "^3.0.3", - "array.prototype.flat": "^1.2.1", + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", "contains-path": "^0.1.0", "debug": "^2.6.9", "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.1", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", "has": "^1.0.3", "minimatch": "^3.0.4", - "object.values": "^1.1.0", + "object.values": "^1.1.1", "read-pkg-up": "^2.0.0", - "resolve": "^1.12.0" + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" }, "dependencies": { "doctrine": { @@ -2942,45 +3030,48 @@ } }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", "dev": true, "requires": { + "is-core-module": "^2.0.0", "path-parse": "^1.0.6" } } } }, "eslint-plugin-jsdoc": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-22.1.0.tgz", - "integrity": "sha512-54NdbICM7KrxsGUqQsev9aIMqPXyvyBx2218Qcm0TQ16P9CtBI+YY4hayJR6adrxlq4Ej0JLpgfUXWaQVFqmQg==", + "version": "30.7.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-30.7.6.tgz", + "integrity": "sha512-w18IOiS/9ahKgRfQOuHbce+EQYx3fwIkZhUZDEK+augNlhJkzgTSZkrBkzaflSbFNZ9/Tk4xzUABEaTbsBSzew==", "dev": true, "requires": { - "comment-parser": "^0.7.2", - "debug": "^4.1.1", - "jsdoctypeparser": "^6.1.0", - "lodash": "^4.17.15", - "regextras": "^0.7.0", - "semver": "^6.3.0", - "spdx-expression-parse": "^3.0.0" + "comment-parser": "^0.7.6", + "debug": "^4.2.0", + "jsdoctypeparser": "^9.0.0", + "lodash": "^4.17.20", + "regextras": "^0.7.1", + "semver": "^7.3.2", + "spdx-expression-parse": "^3.0.1" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true } } }, @@ -2991,12 +3082,12 @@ "dev": true }, "eslint-scope": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", - "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { - "esrecurse": "^4.1.0", + "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, @@ -3007,23 +3098,39 @@ "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", "dev": true }, "espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", + "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", "dev": true, "requires": { - "acorn": "^7.1.1", + "acorn": "^7.4.0", "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "esprima": { @@ -3050,12 +3157,20 @@ } }, "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } } }, "estraverse": { @@ -3081,9 +3196,9 @@ } }, "events": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz", - "integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", "dev": true }, "evp_bytestokey": { @@ -3208,17 +3323,6 @@ } } }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -3284,29 +3388,6 @@ } } }, - "extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "dev": true, - "requires": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, "fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", @@ -3420,24 +3501,6 @@ "reusify": "^1.0.4" } }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", @@ -3529,21 +3592,10 @@ "dev": true }, "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - } - } + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true }, "flat-cache": { "version": "2.0.1", @@ -3598,6 +3650,12 @@ "for-in": "^1.0.1" } }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, "form-data": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", @@ -3672,8 +3730,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -3699,6 +3756,17 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-intrinsic": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", + "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -4033,28 +4101,28 @@ "dev": true }, "gulp-sourcemaps": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", - "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", - "dev": true, - "requires": { - "@gulp-sourcemaps/identity-map": "1.X", - "@gulp-sourcemaps/map-sources": "1.X", - "acorn": "5.X", - "convert-source-map": "1.X", - "css": "2.X", - "debug-fabulous": "1.X", - "detect-newline": "2.X", - "graceful-fs": "4.X", - "source-map": "~0.6.0", - "strip-bom-string": "1.X", - "through2": "2.X" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", + "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", + "dev": true, + "requires": { + "@gulp-sourcemaps/identity-map": "^2.0.1", + "@gulp-sourcemaps/map-sources": "^1.0.0", + "acorn": "^6.4.1", + "convert-source-map": "^1.0.0", + "css": "^3.0.0", + "debug-fabulous": "^1.0.0", + "detect-newline": "^2.0.0", + "graceful-fs": "^4.0.0", + "source-map": "^0.6.0", + "strip-bom-string": "^1.0.0", + "through2": "^2.0.0" }, "dependencies": { "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "dev": true }, "source-map": { @@ -4109,7 +4177,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -4123,8 +4190,7 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "has-value": { "version": "1.0.0", @@ -4242,40 +4308,10 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, - "https-proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", - "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, "ignore": { @@ -4285,9 +4321,9 @@ "dev": true }, "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -4337,42 +4373,10 @@ "source-map": "~0.5.3" } }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, "insert-module-globals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz", - "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", + "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", "dev": true, "requires": { "JSONStream": "^1.0.3", @@ -4412,9 +4416,9 @@ "dev": true }, "irregular-plurals": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-2.0.0.tgz", - "integrity": "sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.2.0.tgz", + "integrity": "sha512-YqTdPLfwP7YFN0SsD3QUVCkm9ZG2VzOXv3DOrw5G5mkMbVwptTwVcFv7/C0vOpBmgTxAeTG19XpUs1E522LW9Q==", "dev": true }, "is-absolute": { @@ -4448,10 +4452,13 @@ } }, "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } }, "is-arrayish": { "version": "0.2.1", @@ -4475,10 +4482,18 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", - "dev": true + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "is-core-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz", + "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } }, "is-data-descriptor": { "version": "0.1.4", @@ -4503,8 +4518,7 @@ "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" }, "is-descriptor": { "version": "0.1.6", @@ -4538,9 +4552,15 @@ "dev": true }, "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-generator-function": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz", + "integrity": "sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ==", "dev": true }, "is-glob": { @@ -4552,18 +4572,17 @@ "is-extglob": "^2.1.1" } }, - "is-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", - "dev": true - }, "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", "dev": true }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -4597,9 +4616,9 @@ "dev": true }, "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, "is-plain-object": { @@ -4618,7 +4637,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -4632,12 +4650,6 @@ "is-unc-path": "^1.0.0" } }, - "is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", - "dev": true - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -4654,24 +4666,70 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, "requires": { "has-symbols": "^1.0.1" } }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "is-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.4.tgz", + "integrity": "sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA==", "dev": true, "requires": { - "unc-path-regex": "^0.1.2" - } - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, "is-valid-glob": { @@ -4777,28 +4835,6 @@ } } }, - "iterate-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", - "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", - "dev": true - }, - "iterate-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", - "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", - "dev": true, - "requires": { - "es-get-iterator": "^1.0.2", - "iterate-iterator": "^1.0.1" - } - }, - "jpeg-js": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz", - "integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4816,9 +4852,9 @@ } }, "jsdoctypeparser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-6.1.0.tgz", - "integrity": "sha512-UCQBZ3xCUBv/PLfwKAJhp6jmGOSLFNKzrotXGNgbKhWvz27wPsCsVeP7gIcHPElQw2agBmynAitXqhxR58XAmA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz", + "integrity": "sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw==", "dev": true }, "json-schema-traverse": { @@ -4827,21 +4863,21 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "json-stable-stringify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", - "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", - "dev": true, - "requires": { - "jsonify": "~0.0.0" - } - }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, "jsonfile": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", @@ -4852,12 +4888,6 @@ "universalify": "^1.0.0" } }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -5269,15 +5299,16 @@ "dev": true }, "mocha": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz", - "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", "dev": true, "requires": { + "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.4.2", - "debug": "4.1.1", + "chokidar": "3.4.3", + "debug": "4.2.0", "diff": "4.0.2", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", @@ -5288,17 +5319,16 @@ "log-symbols": "4.0.0", "minimatch": "3.0.4", "ms": "2.1.2", - "object.assign": "4.1.0", - "promise.allsettled": "1.0.2", - "serialize-javascript": "4.0.0", - "strip-json-comments": "3.0.1", - "supports-color": "7.1.0", + "nanoid": "3.1.12", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "7.2.0", "which": "2.0.2", "wide-align": "1.1.3", - "workerpool": "6.0.0", + "workerpool": "6.0.2", "yargs": "13.3.2", "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.1" + "yargs-unparser": "2.0.0" }, "dependencies": { "ansi-colors": { @@ -5307,6 +5337,12 @@ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -5348,9 +5384,9 @@ "dev": true }, "chokidar": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", - "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -5360,7 +5396,7 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.5.0" } }, "cliui": { @@ -5390,20 +5426,14 @@ "dev": true }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5460,12 +5490,6 @@ "binary-extensions": "^2.0.0" } }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -5481,13 +5505,19 @@ "p-locate": "^5.0.0" } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "p-limit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", - "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { @@ -5512,9 +5542,9 @@ "dev": true }, "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -5526,23 +5556,15 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "ansi-regex": "^4.1.0" } }, - "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", - "dev": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5579,9 +5601,9 @@ } }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, "yargs": { @@ -5697,12 +5719,22 @@ "resolve": "^1.17.0" } }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } }, @@ -5719,9 +5751,9 @@ } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "mute-stdout": { @@ -5730,12 +5762,6 @@ "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", "dev": true }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, "nan": { "version": "2.14.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", @@ -5743,6 +5769,12 @@ "dev": true, "optional": true }, + "nanoid": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", + "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "dev": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -5900,14 +5932,12 @@ "object-inspect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object-visit": { "version": "1.0.1", @@ -5992,15 +6022,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -6039,12 +6060,6 @@ "lcid": "^1.0.0" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -6172,9 +6187,9 @@ "dev": true }, "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "dev": true }, "path-dirname": { @@ -6253,12 +6268,6 @@ "sha.js": "^2.4.8" } }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -6295,43 +6304,6 @@ "find-up": "^2.1.0" } }, - "playwright": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-0.12.1.tgz", - "integrity": "sha512-icF4+I8y7A5HjhbTsa4Eqtl2fuGe3ECvW0Wrn6aRM5eL5/AqUIgIf2U/0e1S1bEsDfz1JVvClGl5Gqw4aI5H4w==", - "dev": true, - "requires": { - "playwright-core": "=0.12.1" - } - }, - "playwright-core": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-0.12.1.tgz", - "integrity": "sha512-NZ8Qe/kqsgAmFBxWZnUeE+MoZ04UzNI0DHOKA+I1p/5rbpaWhe1Vx5zVNa05A1iEvOtnKV1PdIEe4IPumG2y2w==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "extract-zip": "^1.6.6", - "https-proxy-agent": "^3.0.0", - "jpeg-js": "^0.3.6", - "pngjs": "^3.4.0", - "progress": "^2.0.3", - "proxy-from-env": "^1.1.0", - "rimraf": "^3.0.2", - "ws": "^6.1.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, "plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", @@ -6345,26 +6317,100 @@ } }, "plur": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz", - "integrity": "sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", + "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==", "dev": true, "requires": { - "irregular-plurals": "^2.0.0" + "irregular-plurals": "^3.2.0" } }, - "pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", - "dev": true - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -6405,25 +6451,6 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "promise.allsettled": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", - "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", - "dev": true, - "requires": { - "array.prototype.map": "^1.0.1", - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "iterate-value": "^1.0.0" - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -6868,16 +6895,6 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -6909,27 +6926,12 @@ "inherits": "^2.0.1" } }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, "run-parallel": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", "dev": true }, - "rxjs": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", - "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -6973,9 +6975,9 @@ } }, "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -7029,16 +7031,6 @@ "safe-buffer": "^5.0.1" } }, - "shasum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", - "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", - "dev": true, - "requires": { - "json-stable-stringify": "~0.0.0", - "sha.js": "~2.4.4" - } - }, "shasum-object": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", @@ -7121,12 +7113,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true } } }, @@ -7361,13 +7347,26 @@ } }, "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", "dev": true, "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "stream-combiner2": { @@ -7463,45 +7462,113 @@ } }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^4.1.0" } } } }, "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", + "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", + "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } } }, "string_decoder": { @@ -7514,20 +7581,12 @@ } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } + "ansi-regex": "^5.0.0" } }, "strip-bom": { @@ -7564,9 +7623,9 @@ } }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -7601,31 +7660,6 @@ "lodash": "^4.17.14", "slice-ansi": "^2.1.0", "string-width": "^3.0.0" - }, - "dependencies": { - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } } }, "text-table": { @@ -7709,15 +7743,6 @@ "next-tick": "1" } }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", @@ -7797,6 +7822,18 @@ "integrity": "sha1-/sAF+dyqJZo/lFnOWmkGq6TFRdo=", "dev": true }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", @@ -7875,9 +7912,9 @@ "dev": true }, "uglify-js": { - "version": "3.10.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.4.tgz", - "integrity": "sha512-kBFT3U4Dcj4/pJ52vfjCSfyLyvG9VYYuGYPmrPvAxRw/i7xHiT4VvCev+uiEMcEEiu6UNB6KgWmGtSUYIWScbw==", + "version": "3.12.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.4.tgz", + "integrity": "sha512-L5i5jg/SHkEqzN18gQMTWsZk3KelRsfD1wUVNqtq0kzqWQqcJjyL8yc1o8hJgRrWqrAl2mUFbhfznEIoi7zi2A==", "dev": true, "optional": true }, @@ -8025,9 +8062,9 @@ "dev": true }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -8072,20 +8109,17 @@ "dev": true }, "util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", + "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", "dev": true, "requires": { - "inherits": "2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" } }, "util-deprecate": { @@ -8095,9 +8129,9 @@ "dev": true }, "v8-compile-cache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", - "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", "dev": true }, "v8flags": { @@ -8126,9 +8160,9 @@ "dev": true }, "vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", "dev": true, "requires": { "clone": "^2.1.1", @@ -8232,6 +8266,55 @@ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, + "which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -8247,12 +8330,6 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -8287,9 +8364,9 @@ "dev": true }, "workerpool": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", - "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", + "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", "dev": true }, "wrap-ansi": { @@ -8365,15 +8442,6 @@ } } }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", @@ -8544,200 +8612,36 @@ } }, "yargs-unparser": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", - "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { - "camelcase": "^5.3.1", - "decamelize": "^1.2.0", - "flat": "^4.1.0", - "is-plain-obj": "^1.1.0", - "yargs": "^14.2.3" + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "y18n": { + "decamelize": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true - }, - "yargs": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", - "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^15.0.1" - } - }, - "yargs-parser": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", - "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index dc47a9b5f5d31..e02a3e9ee7676 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "typescript", "author": "Microsoft Corp.", "homepage": "https://www.typescriptlang.org/", - "version": "4.1.0", + "version": "4.2.0", "license": "Apache-2.0", "description": "TypeScript is a language for application scale JavaScript development", "keywords": [ @@ -54,9 +54,9 @@ "@types/through2": "latest", "@types/travis-fold": "latest", "@types/xml2js": "^0.4.0", - "@typescript-eslint/eslint-plugin": "^3.6.1-alpha.1", - "@typescript-eslint/experimental-utils": "^3.4.1-alpha.1", - "@typescript-eslint/parser": "^3.4.1-alpha.1", + "@typescript-eslint/eslint-plugin": "4.5.0", + "@typescript-eslint/experimental-utils": "4.5.0", + "@typescript-eslint/parser": "4.5.0", "async": "latest", "azure-devops-node-api": "^10.1.0", "browser-resolve": "^1.11.2", @@ -66,10 +66,10 @@ "convert-source-map": "latest", "del": "5.1.0", "diff": "^4.0.2", - "eslint": "6.8.0", - "eslint-formatter-autolinkable-stylish": "1.1.2", - "eslint-plugin-import": "2.20.2", - "eslint-plugin-jsdoc": "22.1.0", + "eslint": "7.12.1", + "eslint-formatter-autolinkable-stylish": "1.1.4", + "eslint-plugin-import": "2.22.1", + "eslint-plugin-jsdoc": "30.7.6", "eslint-plugin-no-null": "1.0.2", "fancy-log": "latest", "fs-extra": "^9.0.0", @@ -88,7 +88,6 @@ "mocha-fivemat-progress-reporter": "latest", "ms": "latest", "node-fetch": "^2.6.0", - "playwright": "0.12.1", "plugin-error": "latest", "pretty-hrtime": "^1.0.3", "prex": "^0.4.3", diff --git a/scripts/browserIntegrationTest.js b/scripts/browserIntegrationTest.js index 218cef8c0fb25..622af7cb43da0 100644 --- a/scripts/browserIntegrationTest.js +++ b/scripts/browserIntegrationTest.js @@ -1,14 +1,24 @@ -const playwright = require("playwright"); +// @ts-check const chalk = require("chalk"); const { join } = require("path"); const { readFileSync } = require("fs"); +try { + // eslint-disable-next-line import/no-extraneous-dependencies + require("playwright"); +} +catch (error) { + throw new Error("Playwright is expected to be installed manually before running this script"); +} + +// eslint-disable-next-line import/no-extraneous-dependencies +const playwright = require("playwright"); // Turning this on will leave the Chromium browser open, giving you the // chance to open up the web inspector. const debugging = false; (async () => { - for (const browserType of ["chromium", "firefox", "webkit"]) { + for (const browserType of ["chromium", "firefox"]) { const browser = await playwright[browserType].launch({ headless: !debugging }); const context = await browser.newContext(); const page = await context.newPage(); @@ -21,7 +31,6 @@ const debugging = false; page.on("error", errorCaught); page.on("pageerror", errorCaught); - page.on("console", log => console[log._type](log._text)); await page.setContent(` @@ -35,5 +44,14 @@ const debugging = false; else { console.log("Not closing the browser, you'll need to exit the process in your terminal manually"); } + console.log(`${browserType} :+1:`); } })(); + +process.on("unhandledRejection", (/** @type {any}*/ err) => { + if (err) { + console.error(err.stack || err.message); + } + process.exit(1); +}); + diff --git a/scripts/configurePrerelease.ts b/scripts/configurePrerelease.ts index 4eee5d2b34ea1..141f1d5cba88e 100644 --- a/scripts/configurePrerelease.ts +++ b/scripts/configurePrerelease.ts @@ -60,7 +60,7 @@ function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: st const parsedMajorMinor = majorMinorMatch![1]; assert(parsedMajorMinor === majorMinor, `versionMajorMinor does not match. ${tsFilePath}: '${parsedMajorMinor}'; package.json: '${majorMinor}'`); - const versionRgx = /export const version = `\$\{versionMajorMinor\}\.(\d)(-\w+)?`;/; + const versionRgx = /export const version(?:: string)? = `\$\{versionMajorMinor\}\.(\d)(-\w+)?`;/; const patchMatch = versionRgx.exec(tsFileContents); assert(patchMatch !== null, `The file '${tsFilePath}' seems to no longer have a string matching '${versionRgx.toString()}'.`); const parsedPatch = patchMatch![1]; @@ -68,7 +68,7 @@ function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: st throw new Error(`patch does not match. ${tsFilePath}: '${parsedPatch}; package.json: '${patch}'`); } - return tsFileContents.replace(versionRgx, `export const version = \`\${versionMajorMinor}.${nightlyPatch}\`;`); + return tsFileContents.replace(versionRgx, `export const version: string = \`\${versionMajorMinor}.${nightlyPatch}\`;`); } function parsePackageJsonVersion(versionString: string): { majorMinor: string, patch: string } { diff --git a/scripts/produceLKG.ts b/scripts/produceLKG.ts index 7439abe9fcedf..af99441208bd4 100644 --- a/scripts/produceLKG.ts +++ b/scripts/produceLKG.ts @@ -15,9 +15,9 @@ async function produceLKG() { await copyLibFiles(); await copyLocalizedDiagnostics(); await copyTypesMap(); - await buildProtocol(); await copyScriptOutputs(); await copyDeclarationOutputs(); + await buildProtocol(); await writeGitAttributes(); } diff --git a/scripts/tsconfig.eslint.json b/scripts/tsconfig.eslint.json deleted file mode 100644 index 84403f66a5bb1..0000000000000 --- a/scripts/tsconfig.eslint.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["*.ts", "types"] -} diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index aec962016a280..11b1955869d9d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -174,14 +174,14 @@ namespace ts { const binder = createBinder(); export function bindSourceFile(file: SourceFile, options: CompilerOptions) { - tracing.begin(tracing.Phase.Bind, "bindSourceFile", { path: file.path }); + tracing.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true); performance.mark("beforeBind"); perfLogger.logStartBindFile("" + file.fileName); binder(file, options); perfLogger.logStopBindFile(); performance.mark("afterBind"); performance.measure("Bind", "beforeBind", "afterBind"); - tracing.end(); + tracing.pop(); } function createBinder(): (file: SourceFile, options: CompilerOptions) => void { @@ -217,6 +217,9 @@ namespace ts { // or if compiler options contain alwaysStrict. let inStrictMode: boolean; + // If we are binding an assignment pattern, we will bind certain expressions differently. + let inAssignmentPattern = false; + let symbolCount = 0; let Symbol: new (flags: SymbolFlags, name: __String) => Symbol; @@ -274,6 +277,7 @@ namespace ts { currentExceptionTarget = undefined; activeLabelList = undefined; hasExplicitReturn = false; + inAssignmentPattern = false; emitFlags = NodeFlags.None; } @@ -586,6 +590,9 @@ namespace ts { } function jsdocTreatAsExported(node: Node) { + if (node.parent && isModuleDeclaration(node)) { + node = node.parent; + } if (!isJSDocTypeAlias(node)) return false; // jsdoc typedef handling is a bit of a doozy, but to summarize, treat the typedef as exported if: // 1. It has an explicit name (since by default typedefs are always directly exported, either at the top level or in a container), or @@ -662,7 +669,7 @@ namespace ts { } // We create a return control flow graph for IIFEs and constructors. For constructors // we use the return control flow graph in strict property initialization checks. - currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor || (isInJSFile && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined; + currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined; currentExceptionTarget = undefined; currentBreakTarget = undefined; currentContinueTarget = undefined; @@ -683,7 +690,7 @@ namespace ts { if (currentReturnTarget) { addAntecedent(currentReturnTarget, currentFlow); currentFlow = finishFlowLabel(currentReturnTarget); - if (node.kind === SyntaxKind.Constructor || (isInJSFile && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) { + if (node.kind === SyntaxKind.Constructor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) { (node).returnFlowNode = currentFlow; } } @@ -729,9 +736,14 @@ namespace ts { } function bindChildren(node: Node): void { + const saveInAssignmentPattern = inAssignmentPattern; + // Most nodes aren't valid in an assignment pattern, so we clear the value here + // and set it before we descend into nodes that could actually be part of an assignment pattern. + inAssignmentPattern = false; if (checkUnreachable(node)) { bindEachChild(node); bindJSDoc(node); + inAssignmentPattern = saveInAssignmentPattern; return; } if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && !options.allowUnreachableCode) { @@ -787,6 +799,13 @@ namespace ts { bindPostfixUnaryExpressionFlow(node); break; case SyntaxKind.BinaryExpression: + if (isDestructuringAssignment(node)) { + // Carry over whether we are in an assignment pattern to + // binary expressions that could actually be an initializer + inAssignmentPattern = saveInAssignmentPattern; + bindDestructuringAssignmentFlow(node); + return; + } bindBinaryExpressionFlow(node); break; case SyntaxKind.DeleteExpression: @@ -823,11 +842,23 @@ namespace ts { case SyntaxKind.ModuleBlock: bindEachFunctionsFirst((node as Block).statements); break; + case SyntaxKind.BindingElement: + bindBindingElementFlow(node); + break; + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.PropertyAssignment: + case SyntaxKind.SpreadElement: + // Carry over whether we are in an assignment pattern of Object and Array literals + // as well as their children that are valid assignment targets. + inAssignmentPattern = saveInAssignmentPattern; + // falls through default: bindEachChild(node); break; } bindJSDoc(node); + inAssignmentPattern = saveInAssignmentPattern; } function isNarrowingExpression(expr: Expression): boolean { @@ -841,7 +872,8 @@ namespace ts { case SyntaxKind.CallExpression: return hasNarrowableArgument(expr); case SyntaxKind.ParenthesizedExpression: - return isNarrowingExpression((expr).expression); + case SyntaxKind.NonNullExpression: + return isNarrowingExpression((expr).expression); case SyntaxKind.BinaryExpression: return isNarrowingBinaryExpression(expr); case SyntaxKind.PrefixUnaryExpression: @@ -855,6 +887,7 @@ namespace ts { function isNarrowableReference(expr: Expression): boolean { return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.PrivateIdentifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword || (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) || + isBinaryExpression(expr) && expr.operatorToken.kind === SyntaxKind.CommaToken && isNarrowableReference(expr.right) || isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) || isAssignmentExpression(expr) && isNarrowableReference(expr.left); } @@ -1331,10 +1364,14 @@ namespace ts { function bindExpressionStatement(node: ExpressionStatement): void { bind(node.expression); - // A top level call expression with a dotted function name and at least one argument + maybeBindExpressionFlowIfCall(node.expression); + } + + function maybeBindExpressionFlowIfCall(node: Expression) { + // A top level or LHS of comma expression call expression with a dotted function name and at least one argument // is potentially an assertion and is therefore included in the control flow. - if (node.expression.kind === SyntaxKind.CallExpression) { - const call = node.expression; + if (node.kind === SyntaxKind.CallExpression) { + const call = node; if (isDottedName(call.expression) && call.expression.kind !== SyntaxKind.SuperKeyword) { currentFlow = createFlowCall(currentFlow, call); } @@ -1445,6 +1482,24 @@ namespace ts { } } + function bindDestructuringAssignmentFlow(node: DestructuringAssignment) { + if (inAssignmentPattern) { + inAssignmentPattern = false; + bind(node.operatorToken); + bind(node.right); + inAssignmentPattern = true; + bind(node.left); + } + else { + inAssignmentPattern = true; + bind(node.left); + inAssignmentPattern = false; + bind(node.operatorToken); + bind(node.right); + } + bindAssignmentTargetFlow(node.left); + } + const enum BindBinaryExpressionFlowState { BindThenBindChildren, MaybeBindLeft, @@ -1505,6 +1560,9 @@ namespace ts { break; } case BindBinaryExpressionFlowState.BindToken: { + if (node.operatorToken.kind === SyntaxKind.CommaToken) { + maybeBindExpressionFlowIfCall(node.left); + } advanceState(BindBinaryExpressionFlowState.BindRight); maybeBind(node.operatorToken); break; @@ -1558,7 +1616,7 @@ namespace ts { * If `node` is a BinaryExpression, adds it to the local work stack, otherwise recursively binds it */ function maybeBind(node: Node) { - if (node && isBinaryExpression(node)) { + if (node && isBinaryExpression(node) && !isDestructuringAssignment(node)) { stackIndex++; workStacks.expr[stackIndex] = node; workStacks.state[stackIndex] = BindBinaryExpressionFlowState.BindThenBindChildren; @@ -1613,6 +1671,25 @@ namespace ts { } } + function bindBindingElementFlow(node: BindingElement) { + if (isBindingPattern(node.name)) { + // When evaluating a binding pattern, the initializer is evaluated before the binding pattern, per: + // - https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization + // - `BindingElement: BindingPattern Initializer?` + // - https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization + // - `BindingElement: BindingPattern Initializer?` + bindEach(node.decorators); + bindEach(node.modifiers); + bind(node.dotDotDotToken); + bind(node.propertyName); + bind(node.initializer); + bind(node.name); + } + else { + bindEachChild(node); + } + } + function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag) { setParent(node.tagName, node); if (node.kind !== SyntaxKind.JSDocEnumTag && node.fullName) { @@ -2070,8 +2147,8 @@ namespace ts { const saveCurrentFlow = currentFlow; for (const typeAlias of delayedTypeAliases) { const host = getJSDocHost(typeAlias); - container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file; - blockScopeContainer = getEnclosingBlockScopeContainer(host) || file; + container = (host && findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer))) || file; + blockScopeContainer = (host && getEnclosingBlockScopeContainer(host)) || file; currentFlow = initFlowNode({ flags: FlowFlags.Start }); parent = typeAlias; bind(typeAlias.typeExpression); @@ -2828,6 +2905,11 @@ namespace ts { return; } + if (isObjectLiteralExpression(assignedExpression) && every(assignedExpression.properties, isShorthandPropertyAssignment)) { + forEach(assignedExpression.properties, bindExportAssignedObjectMemberAlias); + return; + } + // 'module.exports = expr' assignment const flags = exportAssignmentIsAlias(node) ? SymbolFlags.Alias // An export= with an EntityNameExpression or a ClassExpression exports all meanings of that identifier or class @@ -2836,6 +2918,10 @@ namespace ts { setValueDeclaration(symbol, node); } + function bindExportAssignedObjectMemberAlias(node: ShorthandPropertyAssignment) { + declareSymbol(file.symbol.exports!, file.symbol, node, SymbolFlags.Alias | SymbolFlags.Assignment, SymbolFlags.None); + } + function bindThisPropertyAssignment(node: BindablePropertyAssignmentExpression | PropertyAccessExpression | LiteralLikeElementAccessExpression) { Debug.assert(isInJSFile(node)); // private identifiers *must* be declared (even in JS files) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 7765a75331281..8d09baa270540 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -274,7 +274,7 @@ namespace ts { result.relatedInformation = relatedInformation ? relatedInformation.length ? relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram, toPath)) : - emptyArray : + [] : undefined; return result; }); @@ -493,10 +493,10 @@ namespace ts { return !state.semanticDiagnosticsFromOldState.size; } - function isChangedSignagure(state: BuilderProgramState, path: Path) { + function isChangedSignature(state: BuilderProgramState, path: Path) { const newSignature = Debug.checkDefined(state.currentAffectedFilesSignatures).get(path); - const oldSignagure = Debug.checkDefined(state.fileInfos.get(path)).signature; - return newSignature !== oldSignagure; + const oldSignature = Debug.checkDefined(state.fileInfos.get(path)).signature; + return newSignature !== oldSignature; } /** @@ -509,7 +509,7 @@ namespace ts { return; } - if (!isChangedSignagure(state, affectedFile.resolvedPath)) return; + if (!isChangedSignature(state, affectedFile.resolvedPath)) return; // Since isolated modules dont change js files, files affected by change in signature is itself // But we need to cleanup semantic diagnostics and queue dts emit for affected files @@ -522,7 +522,7 @@ namespace ts { if (!seenFileNamesMap.has(currentPath)) { seenFileNamesMap.set(currentPath, true); const result = fn(state, currentPath); - if (result && isChangedSignagure(state, currentPath)) { + if (result && isChangedSignature(state, currentPath)) { const currentSourceFile = Debug.checkDefined(state.program).getSourceFileByPath(currentPath)!; queue.push(...BuilderState.getReferencedByPaths(state, currentSourceFile.resolvedPath)); } @@ -824,7 +824,7 @@ namespace ts { result.relatedInformation = relatedInformation ? relatedInformation.length ? relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r, relativeToBuildInfo)) : - emptyArray : + [] : undefined; return result; }); @@ -900,7 +900,7 @@ namespace ts { /** * Computing hash to for signature verification */ - const computeHash = host.createHash || generateDjb2Hash; + const computeHash = maybeBind(host, host.createHash); let state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState); let backupState: BuilderProgramState | undefined; newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state, getCanonicalFileName); @@ -1019,31 +1019,57 @@ namespace ts { * in that order would be used to write the files */ function emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult { + let restorePendingEmitOnHandlingNoEmitSuccess = false; + let savedAffectedFilesPendingEmit; + let savedAffectedFilesPendingEmitKind; + let savedAffectedFilesPendingEmitIndex; + // Backup and restore affected pendings emit state for non emit Builder if noEmitOnError is enabled and emitBuildInfo could be written in case there are errors + // This ensures pending files to emit is updated in tsbuildinfo + // Note that when there are no errors, emit proceeds as if everything is emitted as it is callers reponsibility to write the files to disk if at all (because its builder that doesnt track files to emit) + if (kind !== BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram && + !targetSourceFile && + !outFile(state.compilerOptions) && + !state.compilerOptions.noEmit && + state.compilerOptions.noEmitOnError) { + restorePendingEmitOnHandlingNoEmitSuccess = true; + savedAffectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice(); + savedAffectedFilesPendingEmitKind = state.affectedFilesPendingEmitKind && new Map(state.affectedFilesPendingEmitKind); + savedAffectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex; + } + if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) { assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile); - const result = handleNoEmitOptions(builderProgram, targetSourceFile, writeFile, cancellationToken); - if (result) return result; - if (!targetSourceFile) { - // Emit and report any errors we ran into. - let sourceMaps: SourceMapEmitResult[] = []; - let emitSkipped = false; - let diagnostics: Diagnostic[] | undefined; - let emittedFiles: string[] = []; - - let affectedEmitResult: AffectedFileResult; - while (affectedEmitResult = emitNextAffectedFile(writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers)) { - emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped; - diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics); - emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles); - sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps); - } - return { - emitSkipped, - diagnostics: diagnostics || emptyArray, - emittedFiles, - sourceMaps - }; + } + const result = handleNoEmitOptions(builderProgram, targetSourceFile, writeFile, cancellationToken); + if (result) return result; + + if (restorePendingEmitOnHandlingNoEmitSuccess) { + state.affectedFilesPendingEmit = savedAffectedFilesPendingEmit; + state.affectedFilesPendingEmitKind = savedAffectedFilesPendingEmitKind; + state.affectedFilesPendingEmitIndex = savedAffectedFilesPendingEmitIndex; + } + + // Emit only affected files if using builder for emit + if (!targetSourceFile && kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) { + // Emit and report any errors we ran into. + let sourceMaps: SourceMapEmitResult[] = []; + let emitSkipped = false; + let diagnostics: Diagnostic[] | undefined; + let emittedFiles: string[] = []; + + let affectedEmitResult: AffectedFileResult; + while (affectedEmitResult = emitNextAffectedFile(writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers)) { + emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped; + diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics); + emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles); + sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps); } + return { + emitSkipped, + diagnostics: diagnostics || emptyArray, + emittedFiles, + sourceMaps + }; } return Debug.checkDefined(state.program).emit(targetSourceFile, writeFile || maybeBind(host, host.writeFile), cancellationToken, emitOnlyDtsFiles, customTransformers); } @@ -1069,7 +1095,8 @@ namespace ts { } // Add file to affected file pending emit to handle for later emit time - if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) { + // Apart for emit builder do this for tsbuildinfo, do this for non emit builder when noEmit is set as tsbuildinfo is written and reused between emitters + if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram || state.compilerOptions.noEmit || state.compilerOptions.noEmitOnError) { addToAffectedFilesPendingEmit(state, (affected as SourceFile).resolvedPath, BuilderFileEmit.Full); } diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 1c26513897aee..72c5c37b23382 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -77,7 +77,7 @@ namespace ts { /** * Compute the hash to store the shape of the file */ - export type ComputeHash = (data: string) => string; + export type ComputeHash = ((data: string) => string) | undefined; /** * Exported modules to from declaration emit being computed. @@ -337,7 +337,7 @@ namespace ts { emitOutput.outputFiles.length > 0 ? emitOutput.outputFiles[0] : undefined; if (firstDts) { Debug.assert(fileExtensionIs(firstDts.name, Extension.Dts), "File extension for signature expected to be dts", () => `Found: ${getAnyExtensionFromPath(firstDts.name)} for ${firstDts.name}:: All output files: ${JSON.stringify(emitOutput.outputFiles.map(f => f.name))}`); - latestSignature = computeHash(firstDts.text); + latestSignature = (computeHash || generateDjb2Hash)(firstDts.text); if (exportedModulesMapCache && latestSignature !== prevSignature) { updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache); } @@ -521,7 +521,7 @@ namespace ts { /** * When program emits modular code, gets the files affected by the sourceFile whose shape has changed */ - function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: ESMap, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) { + function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: ESMap, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache: ComputingExportedModulesMap | undefined) { if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) { return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape); } @@ -544,13 +544,12 @@ namespace ts { if (!seenFileNamesMap.has(currentPath)) { const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath)!; seenFileNamesMap.set(currentPath, currentSourceFile); - if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken, computeHash!, exportedModulesMapCache)) { // TODO: GH#18217 + if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken, computeHash, exportedModulesMapCache)) { queue.push(...getReferencedByPaths(state, currentSourceFile.resolvedPath)); } } } - // Return array of values that needs emit // Return array of values that needs emit return arrayFrom(mapDefinedIterator(seenFileNamesMap.values(), value => value)); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e4b1cff46eefb..4cf37279a6063 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -256,6 +256,20 @@ namespace ts { VoidIsNonOptional = 1 << 1, } + const enum IntrinsicTypeKind { + Uppercase, + Lowercase, + Capitalize, + Uncapitalize + } + + const intrinsicTypeKinds: ReadonlyESMap = new Map(getEntries({ + Uppercase: IntrinsicTypeKind.Uppercase, + Lowercase: IntrinsicTypeKind.Lowercase, + Capitalize: IntrinsicTypeKind.Capitalize, + Uncapitalize: IntrinsicTypeKind.Uncapitalize + })); + function SymbolLinks(this: SymbolLinks) { } @@ -322,7 +336,6 @@ namespace ts { let totalInstantiationCount = 0; let instantiationCount = 0; let instantiationDepth = 0; - let constraintDepth = 0; let currentNode: Node | undefined; const typeCatalog: Type[] = []; // NB: id is index + 1 @@ -493,6 +506,7 @@ namespace ts { }, getAugmentedPropertiesOfType, getRootSymbols, + getSymbolOfExpando, getContextualType: (nodeIn: Expression, contextFlags?: ContextFlags) => { const node = getParseTreeNode(nodeIn, isExpression); if (!node) { @@ -631,6 +645,10 @@ namespace ts { return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false, excludeGlobals); }, getJsxNamespace: n => unescapeLeadingUnderscores(getJsxNamespace(n)), + getJsxFragmentFactory: n => { + const jsxFragmentFactory = getJsxFragmentFactoryEntity(n); + return jsxFragmentFactory && unescapeLeadingUnderscores(getFirstIdentifier(jsxFragmentFactory).escapedText); + }, getAccessibleSymbolChain, getTypePredicateOfSignature, resolveExternalModuleName: moduleSpecifierIn => { @@ -705,6 +723,7 @@ namespace ts { const literalTypes = new Map(); const indexedAccessTypes = new Map(); const templateLiteralTypes = new Map(); + const stringMappingTypes = new Map(); const substitutionTypes = new Map(); const evolvingArrayTypes: EvolvingArrayType[] = []; const undefinedProperties: SymbolTable = new Map(); @@ -717,6 +736,7 @@ namespace ts { const wildcardType = createIntrinsicType(TypeFlags.Any, "any"); const errorType = createIntrinsicType(TypeFlags.Any, "error"); const nonInferrableAnyType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.ContainsWideningType); + const intrinsicMarkerType = createIntrinsicType(TypeFlags.Any, "intrinsic"); const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown"); const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType); @@ -755,7 +775,7 @@ namespace ts { const stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]); const keyofConstraintType = keyofStringsOnly ? stringType : stringNumberSymbolType; const numberOrBigIntType = getUnionType([numberType, bigintType]); - const templateConstraintType = getUnionType([stringType, numberType, booleanType, bigintType]); + const templateConstraintType = getUnionType([stringType, numberType, booleanType, bigintType, nullType, undefinedType]) as UnionType; const restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(t) : t); const permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t); @@ -1074,6 +1094,16 @@ namespace ts { } } function errorOrSuggestion(isError: boolean, location: Node, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void { + // Pseudo-synthesized input node + if (location.pos < 0 || location.end < 0) { + if (!isError) { + return; // Drop suggestions (we have no span to suggest on) + } + // Issue errors globally + const file = getSourceFileOfNode(location); + addErrorOrSuggestion(isError, "message" in message ? createFileDiagnostic(file, 0, 0, message, arg0, arg1, arg2, arg3) : createDiagnosticForFileFromMessageChain(file, message)); // eslint-disable-line no-in-operator + return; + } addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); // eslint-disable-line no-in-operator } @@ -1918,7 +1948,10 @@ namespace ts { case SyntaxKind.JSDocCallbackTag: case SyntaxKind.JSDocEnumTag: // js type aliases do not resolve names from their host, so skip past it - location = getJSDocHost(location); + const root = getJSDocRoot(location); + if (root) { + location = root.parent; + } break; case SyntaxKind.Parameter: if (lastLocation && ( @@ -1938,6 +1971,15 @@ namespace ts { } } break; + case SyntaxKind.InferType: + if (meaning & SymbolFlags.TypeParameter) { + const parameterName = (location).typeParameter.name; + if (parameterName && name === parameterName.escapedText) { + result = (location).typeParameter.symbol; + break loop; + } + } + break; } if (isSelfReferenceLocation(location)) { lastSelfReferenceLocation = location; @@ -1997,7 +2039,15 @@ namespace ts { } } if (!suggestion) { - error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg!)); + if (nameArg) { + const lib = getSuggestedLibForNonExistentName(nameArg); + if (lib) { + error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg), lib); + } + else { + error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg)); + } + } } suggestionCount++; } @@ -2090,7 +2140,7 @@ namespace ts { return isTypeQueryNode(location) || (( isFunctionLikeDeclaration(location) || (location.kind === SyntaxKind.PropertyDeclaration && !hasSyntacticModifier(location, ModifierFlags.Static)) - ) && (!lastLocation || lastLocation !== (location as FunctionLike | PropertyDeclaration).name)); // A name is evaluated within the enclosing scope - so it shouldn't count as deferred + ) && (!lastLocation || lastLocation !== (location as SignatureDeclaration | PropertyDeclaration).name)); // A name is evaluated within the enclosing scope - so it shouldn't count as deferred } if (lastLocation && lastLocation === (location as FunctionExpression | ArrowFunction).name) { return false; @@ -2423,17 +2473,18 @@ namespace ts { } function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration | VariableDeclaration, dontResolveAlias: boolean): Symbol | undefined { - if (isVariableDeclaration(node) && node.initializer && isPropertyAccessExpression(node.initializer)) { - const name = (getLeftmostAccessExpression(node.initializer.expression) as CallExpression).arguments[0] as StringLiteral; - return isIdentifier(node.initializer.name) - ? resolveSymbol(getPropertyOfType(resolveExternalModuleTypeByLiteral(name), node.initializer.name.escapedText)) + const commonJSPropertyAccess = getCommonJSPropertyAccess(node); + if (commonJSPropertyAccess) { + const name = (getLeftmostAccessExpression(commonJSPropertyAccess.expression) as CallExpression).arguments[0] as StringLiteral; + return isIdentifier(commonJSPropertyAccess.name) + ? resolveSymbol(getPropertyOfType(resolveExternalModuleTypeByLiteral(name), commonJSPropertyAccess.name.escapedText)) : undefined; } if (isVariableDeclaration(node) || node.moduleReference.kind === SyntaxKind.ExternalModuleReference) { const immediate = resolveExternalModuleName( node, getExternalModuleRequireArgument(node) || getExternalModuleImportEqualsDeclarationExpression(node)); - const resolved = resolveExternalModuleSymbol(immediate); + const resolved = resolveExternalModuleSymbol(immediate); markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, /*overwriteEmpty*/ false); return resolved; } @@ -2443,7 +2494,7 @@ namespace ts { } function checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node: ImportEqualsDeclaration, resolved: Symbol | undefined) { - if (markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false)) { + if (markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false) && !node.isTypeOnly) { const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(getSymbolOfNode(node))!; const isExport = typeOnlyDeclarationIsExport(typeOnlyDeclaration); const message = isExport @@ -2462,10 +2513,7 @@ namespace ts { function resolveExportByName(moduleSymbol: Symbol, name: __String, sourceNode: TypeOnlyCompatibleAliasDeclaration | undefined, dontResolveAlias: boolean) { const exportValue = moduleSymbol.exports!.get(InternalSymbolName.ExportEquals); - if (exportValue) { - return getPropertyOfType(getTypeOfSymbol(exportValue), name); - } - const exportSymbol = moduleSymbol.exports!.get(name); + const exportSymbol = exportValue ? getPropertyOfType(getTypeOfSymbol(exportValue), name) : moduleSymbol.exports!.get(name); const resolved = resolveSymbol(exportSymbol, dontResolveAlias); markSymbolOfAliasDeclarationIfTypeOnly(sourceNode, exportSymbol, resolved, /*overwriteEmpty*/ false); return resolved; @@ -2621,12 +2669,8 @@ namespace ts { return result; } - function getExportOfModule(symbol: Symbol, specifier: ImportOrExportSpecifier | BindingElement, dontResolveAlias: boolean): Symbol | undefined { + function getExportOfModule(symbol: Symbol, name: Identifier, specifier: Declaration, dontResolveAlias: boolean): Symbol | undefined { if (symbol.flags & SymbolFlags.Module) { - const name = specifier.propertyName ?? specifier.name; - if (!isIdentifier(name)) { - return undefined; - } const exportSymbol = getExportsOfSymbol(symbol).get(name.escapedText); const resolved = resolveSymbol(exportSymbol, dontResolveAlias); markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportSymbol, resolved, /*overwriteEmpty*/ false); @@ -2643,10 +2687,10 @@ namespace ts { } } - function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration | VariableDeclaration, specifier: ImportOrExportSpecifier | BindingElement, dontResolveAlias = false): Symbol | undefined { + function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration | VariableDeclaration, specifier: ImportOrExportSpecifier | BindingElement | PropertyAccessExpression, dontResolveAlias = false): Symbol | undefined { const moduleSpecifier = getExternalModuleRequireArgument(node) || (node as ImportDeclaration | ExportDeclaration).moduleSpecifier!; const moduleSymbol = resolveExternalModuleName(node, moduleSpecifier)!; // TODO: GH#18217 - const name = specifier.propertyName || specifier.name; + const name = !isPropertyAccessExpression(specifier) && specifier.propertyName || specifier.name; if (!isIdentifier(name)) { return undefined; } @@ -2661,15 +2705,15 @@ namespace ts { let symbolFromVariable: Symbol | undefined; // First check if module was specified with "export=". If so, get the member from the resolved type if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get(InternalSymbolName.ExportEquals)) { - symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.escapedText); + symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.escapedText, /*skipObjectFunctionPropertyAugment*/ true); } else { symbolFromVariable = getPropertyOfVariable(targetSymbol, name.escapedText); } - // if symbolFromVariable is export - get its final target symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias); - let symbolFromModule = getExportOfModule(targetSymbol, specifier, dontResolveAlias); + + let symbolFromModule = getExportOfModule(targetSymbol, name, specifier, dontResolveAlias); if (symbolFromModule === undefined && name.escapedText === InternalSymbolName.Default) { const file = find(moduleSymbol.declarations, isSourceFile); if (canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias)) { @@ -2757,11 +2801,23 @@ namespace ts { } function getTargetOfImportSpecifier(node: ImportSpecifier | BindingElement, dontResolveAlias: boolean): Symbol | undefined { - const resolved = getExternalModuleMember(isBindingElement(node) ? getRootDeclaration(node) as VariableDeclaration : node.parent.parent.parent, node, dontResolveAlias); + const root = isBindingElement(node) ? getRootDeclaration(node) as VariableDeclaration : node.parent.parent.parent; + const commonJSPropertyAccess = getCommonJSPropertyAccess(root); + const resolved = getExternalModuleMember(root, commonJSPropertyAccess || node, dontResolveAlias); + const name = node.propertyName || node.name; + if (commonJSPropertyAccess && resolved && isIdentifier(name)) { + return resolveSymbol(getPropertyOfType(getTypeOfSymbol(resolved), name.escapedText), dontResolveAlias); + } markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false); return resolved; } + function getCommonJSPropertyAccess(node: Node) { + if (isVariableDeclaration(node) && node.initializer && isPropertyAccessExpression(node.initializer)) { + return node.initializer; + } + } + function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol { const resolved = resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias); markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false); @@ -2915,7 +2971,7 @@ namespace ts { finalTarget: Symbol | undefined, overwriteEmpty: boolean, ): boolean { - if (!aliasDeclaration) return false; + if (!aliasDeclaration || isPropertyAccessExpression(aliasDeclaration)) return false; // If the declaration itself is type-only, mark it and return. // No need to check what it resolves to. @@ -3107,7 +3163,8 @@ namespace ts { return; } const host = getJSDocHost(node); - if (isExpressionStatement(host) && + if (host && + isExpressionStatement(host) && isBinaryExpression(host.expression) && getAssignmentDeclarationKind(host.expression) === AssignmentDeclarationKind.PrototypeProperty) { // X.prototype.m = /** @param {K} p */ function () { } <-- look for K on X's declaration @@ -3116,7 +3173,7 @@ namespace ts { return getDeclarationOfJSPrototypeContainer(symbol); } } - if ((isObjectLiteralMethod(host) || isPropertyAssignment(host)) && + if (host && (isObjectLiteralMethod(host) || isPropertyAssignment(host)) && isBinaryExpression(host.parent.parent) && getAssignmentDeclarationKind(host.parent.parent) === AssignmentDeclarationKind.Prototype) { // X.prototype = { /** @param {K} p */m() { } } <-- look for K on X's declaration @@ -3277,7 +3334,7 @@ namespace ts { packageId.name, mangleScopedPackageName(packageId.name)) : chainDiagnosticMessages( /*details*/ undefined, - Diagnostics.Try_npm_install_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0, + Diagnostics.Try_npm_i_save_dev_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0, moduleReference, mangleScopedPackageName(packageId.name)) : undefined; @@ -4121,6 +4178,14 @@ namespace ts { && isDeclarationVisible(declaration.parent)) { return addVisibleAlias(declaration, declaration); } + else if (symbol.flags & SymbolFlags.Alias && isBindingElement(declaration) && isInJSFile(declaration) && declaration.parent?.parent // exported import-like top-level JS require statement + && isVariableDeclaration(declaration.parent.parent) + && declaration.parent.parent.parent?.parent && isVariableStatement(declaration.parent.parent.parent.parent) + && !hasSyntacticModifier(declaration.parent.parent.parent.parent, ModifierFlags.Export) + && declaration.parent.parent.parent.parent.parent // check if the thing containing the variable statement is visible (ie, the file) + && isDeclarationVisible(declaration.parent.parent.parent.parent.parent)) { + return addVisibleAlias(declaration, declaration.parent.parent.parent.parent); + } // Declaration is not visible return false; @@ -4344,7 +4409,7 @@ namespace ts { if (type.flags & TypeFlags.Any) { context.approximateLength += 3; - return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + return factory.createKeywordTypeNode(type === intrinsicMarkerType ? SyntaxKind.IntrinsicKeyword : SyntaxKind.AnyKeyword); } if (type.flags & TypeFlags.Unknown) { return factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword); @@ -4368,13 +4433,26 @@ namespace ts { if (type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union)) { const parentSymbol = getParentOfSymbol(type.symbol)!; const parentName = symbolToTypeNode(parentSymbol, context, SymbolFlags.Type); - const enumLiteralName = getDeclaredTypeOfSymbol(parentSymbol) === type - ? parentName - : appendReferenceToType( + if (getDeclaredTypeOfSymbol(parentSymbol) === type) { + return parentName; + } + const memberName = symbolName(type.symbol); + if (isIdentifierText(memberName, ScriptTarget.ES3)) { + return appendReferenceToType( parentName as TypeReferenceNode | ImportTypeNode, - factory.createTypeReferenceNode(symbolName(type.symbol), /*typeArguments*/ undefined) + factory.createTypeReferenceNode(memberName, /*typeArguments*/ undefined) ); - return enumLiteralName; + } + if (isImportTypeNode(parentName)) { + (parentName as any).isTypeOf = true; // mutably update, node is freshly manufactured anyhow + return factory.createIndexedAccessTypeNode(parentName, factory.createLiteralTypeNode(factory.createStringLiteral(memberName))); + } + else if (isTypeReferenceNode(parentName)) { + return factory.createIndexedAccessTypeNode(factory.createTypeQueryNode(parentName.typeName), factory.createLiteralTypeNode(factory.createStringLiteral(memberName))); + } + else { + return Debug.fail("Unhandled type node kind returned from `symbolToTypeNode`."); + } } if (type.flags & TypeFlags.EnumLike) { return symbolToTypeNode(type.symbol, context, SymbolFlags.Type); @@ -4505,17 +4583,19 @@ namespace ts { } if (type.flags & TypeFlags.TemplateLiteral) { const texts = (type).texts; - const casings = (type).casings; const types = (type).types; const templateHead = factory.createTemplateHead(texts[0]); const templateSpans = factory.createNodeArray( map(types, (t, i) => factory.createTemplateLiteralTypeSpan( - casings[i], typeToTypeNodeHelper(t, context), (i < types.length - 1 ? factory.createTemplateMiddle : factory.createTemplateTail)(texts[i + 1])))); context.approximateLength += 2; return factory.createTemplateLiteralType(templateHead, templateSpans); } + if (type.flags & TypeFlags.StringMapping) { + const typeNode = typeToTypeNodeHelper((type).type, context); + return symbolToTypeNode((type).symbol, context, SymbolFlags.Type, [typeNode]); + } if (type.flags & TypeFlags.IndexedAccess) { const objectTypeNode = typeToTypeNodeHelper((type).objectType, context); const indexTypeNode = typeToTypeNodeHelper((type).indexType, context); @@ -4920,7 +5000,7 @@ namespace ts { anyType : getTypeOfSymbol(propertySymbol); const saveEnclosingDeclaration = context.enclosingDeclaration; context.enclosingDeclaration = undefined; - if (context.tracker.trackSymbol && getCheckFlags(propertySymbol) & CheckFlags.Late) { + if (context.tracker.trackSymbol && getCheckFlags(propertySymbol) & CheckFlags.Late && isLateBoundName(propertySymbol.escapedName)) { const decl = first(propertySymbol.declarations); if (hasLateBindableName(decl)) { if (isBinaryExpression(decl)) { @@ -5101,7 +5181,9 @@ namespace ts { typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context)); } - const parameters = getExpandedParameters(signature, /*skipUnionExpanding*/ true)[0].map(parameter => symbolToParameterDeclaration(parameter, context, kind === SyntaxKind.Constructor, options?.privateSymbolVisitor, options?.bundledImports)); + const expandedParams = getExpandedParameters(signature, /*skipUnionExpanding*/ true)[0]; + // If the expanded parameter list had a variadic in a non-trailing position, don't expand it + const parameters = (some(expandedParams, p => p !== expandedParams[expandedParams.length - 1] && !!(getCheckFlags(p) & CheckFlags.RestParameter)) ? signature.parameters : expandedParams).map(parameter => symbolToParameterDeclaration(parameter, context, kind === SyntaxKind.Constructor, options?.privateSymbolVisitor, options?.bundledImports)); if (signature.thisParameter) { const thisParameter = symbolToParameterDeclaration(signature.thisParameter, context); parameters.unshift(thisParameter); @@ -5421,10 +5503,11 @@ namespace ts { const specifierCompilerOptions = isBundle ? { ...compilerOptions, baseUrl: moduleResolverHost.getCommonSourceDirectory() } : compilerOptions; specifier = first(moduleSpecifiers.getModuleSpecifiers( symbol, + checker, specifierCompilerOptions, contextFile, moduleResolverHost, - { importModuleSpecifierPreference: isBundle ? "non-relative" : "relative" }, + { importModuleSpecifierPreference: isBundle ? "non-relative" : "relative", importModuleSpecifierEnding: isBundle ? "minimal" : undefined }, )); links.specifierCache ??= new Map(); links.specifierCache.set(contextFile.path, specifier); @@ -5765,7 +5848,7 @@ namespace ts { } const oldFlags = context.flags; if (type.flags & TypeFlags.UniqueESSymbol && - type.symbol === symbol) { + type.symbol === symbol && (!context.enclosingDeclaration || some(symbol.declarations, d => getSourceFileOfNode(d) === getSourceFileOfNode(context.enclosingDeclaration!)))) { context.flags |= NodeBuilderFlags.AllowUniqueESSymbolType; } const result = typeToTypeNodeHelper(type, context); @@ -5786,6 +5869,32 @@ namespace ts { return typeToTypeNodeHelper(type, context); } + function trackExistingEntityName(node: T, context: NodeBuilderContext, includePrivateSymbol?: (s: Symbol) => void) { + let introducesError = false; + const leftmost = getFirstIdentifier(node); + if (isInJSFile(node) && (isExportsIdentifier(leftmost) || isModuleExportsAccessExpression(leftmost.parent) || (isQualifiedName(leftmost.parent) && isModuleIdentifier(leftmost.parent.left) && isExportsIdentifier(leftmost.parent.right)))) { + introducesError = true; + return { introducesError, node }; + } + const sym = resolveEntityName(leftmost, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveALias*/ true); + if (sym) { + if (isSymbolAccessible(sym, context.enclosingDeclaration, SymbolFlags.All, /*shouldComputeAliasesToMakeVisible*/ false).accessibility !== SymbolAccessibility.Accessible) { + introducesError = true; + } + else { + context.tracker?.trackSymbol?.(sym, context.enclosingDeclaration, SymbolFlags.All); + includePrivateSymbol?.(sym); + } + if (isIdentifier(node)) { + const name = sym.flags & SymbolFlags.TypeParameter ? typeParameterToName(getDeclaredTypeOfSymbol(sym), context) : factory.cloneNode(node); + name.symbol = sym; // for quickinfo, which uses identifier symbol information + return { introducesError, node: setEmitFlags(setOriginalNode(name, node), EmitFlags.NoAsciiEscaping) }; + } + } + + return { introducesError, node }; + } + function serializeExistingTypeNode(context: NodeBuilderContext, existing: TypeNode, includePrivateSymbol?: (s: Symbol) => void, bundled?: boolean) { if (cancellationToken && cancellationToken.throwIfCancellationRequested) { cancellationToken.throwIfCancellationRequested(); @@ -5827,7 +5936,7 @@ namespace ts { return factory.createPropertySignature( /*modifiers*/ undefined, name, - t.typeExpression && isJSDocOptionalType(t.typeExpression.type) ? factory.createToken(SyntaxKind.QuestionToken) : undefined, + t.isBracketed || t.typeExpression && isJSDocOptionalType(t.typeExpression.type) ? factory.createToken(SyntaxKind.QuestionToken) : undefined, overrideTypeNode || (t.typeExpression && visitNode(t.typeExpression.type, visitExistingNodeTreeSymbols)) || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword) ); })); @@ -5888,6 +5997,18 @@ namespace ts { return setOriginalNode(typeToTypeNodeHelper(getTypeFromTypeNode(node), context), node); } if (isLiteralImportTypeNode(node)) { + const nodeSymbol = getNodeLinks(node).resolvedSymbol; + if (isInJSDoc(node) && + nodeSymbol && + ( + // The import type resolved using jsdoc fallback logic + (!node.isTypeOf && !(nodeSymbol.flags & SymbolFlags.Type)) || + // The import type had type arguments autofilled by js fallback logic + !(length(node.typeArguments) >= getMinTypeArgumentCount(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(nodeSymbol))) + ) + ) { + return setOriginalNode(typeToTypeNodeHelper(getTypeFromTypeNode(node), context), node); + } return factory.updateImportTypeNode( node, factory.updateLiteralTypeNode(node.argument, rewriteModuleSpecifier(node, node.argument.literal)), @@ -5898,25 +6019,10 @@ namespace ts { } if (isEntityName(node) || isEntityNameExpression(node)) { - const leftmost = getFirstIdentifier(node); - if (isInJSFile(node) && (isExportsIdentifier(leftmost) || isModuleExportsAccessExpression(leftmost.parent) || (isQualifiedName(leftmost.parent) && isModuleIdentifier(leftmost.parent.left) && isExportsIdentifier(leftmost.parent.right)))) { - hadError = true; - return node; - } - const sym = resolveEntityName(leftmost, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveALias*/ true); - if (sym) { - if (isSymbolAccessible(sym, context.enclosingDeclaration, SymbolFlags.All, /*shouldComputeAliasesToMakeVisible*/ false).accessibility !== SymbolAccessibility.Accessible) { - hadError = true; - } - else { - context.tracker?.trackSymbol?.(sym, context.enclosingDeclaration, SymbolFlags.All); - includePrivateSymbol?.(sym); - } - if (isIdentifier(node)) { - const name = sym.flags & SymbolFlags.TypeParameter ? typeParameterToName(getDeclaredTypeOfSymbol(sym), context) : factory.cloneNode(node); - name.symbol = sym; // for quickinfo, which uses identifier symbol information - return setEmitFlags(setOriginalNode(name, node), EmitFlags.NoAsciiEscaping); - } + const { introducesError, node: result } = trackExistingEntityName(node, context, includePrivateSymbol); + hadError = hadError || introducesError; + if (result !== node) { + return result; } } @@ -6463,8 +6569,12 @@ namespace ts { const commentText = jsdocAliasDecl ? jsdocAliasDecl.comment || jsdocAliasDecl.parent.comment : undefined; const oldFlags = context.flags; context.flags |= NodeBuilderFlags.InTypeAlias; + const typeNode = jsdocAliasDecl && jsdocAliasDecl.typeExpression + && isJSDocTypeExpression(jsdocAliasDecl.typeExpression) + && serializeExistingTypeNode(context, jsdocAliasDecl.typeExpression.type, includePrivateSymbol, bundled) + || typeToTypeNodeHelper(aliasType, context); addResult(setSyntheticLeadingComments( - factory.createTypeAliasDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, getInternalSymbolName(symbol, symbolName), typeParamDecls, typeToTypeNodeHelper(aliasType, context)), + factory.createTypeAliasDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, getInternalSymbolName(symbol, symbolName), typeParamDecls, typeNode), !commentText ? [] : [{ kind: SyntaxKind.MultiLineCommentTrivia, text: "*\n * " + commentText.replace(/\n/g, "\n * ") + "\n ", pos: -1, end: -1, hasTrailingNewLine: true }] ), modifierFlags); context.flags = oldFlags; @@ -6643,12 +6753,50 @@ namespace ts { !(p.flags & SymbolFlags.Prototype || p.escapedName === "prototype" || p.valueDeclaration && getEffectiveModifierFlags(p.valueDeclaration) & ModifierFlags.Static && isClassLike(p.valueDeclaration.parent)); } + function sanitizeJSDocImplements(clauses: readonly ExpressionWithTypeArguments[]): ExpressionWithTypeArguments[] | undefined { + const result = mapDefined(clauses, e => { + const oldEnclosing = context.enclosingDeclaration; + context.enclosingDeclaration = e; + let expr = e.expression; + if (isEntityNameExpression(expr)) { + if (isIdentifier(expr) && idText(expr) === "") { + return cleanup(/*result*/ undefined); // Empty heritage clause, should be an error, but prefer emitting no heritage clauses to reemitting the empty one + } + let introducesError: boolean; + ({ introducesError, node: expr } = trackExistingEntityName(expr, context, includePrivateSymbol)); + if (introducesError) { + return cleanup(/*result*/ undefined); + } + } + return cleanup(factory.createExpressionWithTypeArguments(expr, + map(e.typeArguments, a => + serializeExistingTypeNode(context, a, includePrivateSymbol, bundled) + || typeToTypeNodeHelper(getTypeFromTypeNode(a), context) + ) + )); + + function cleanup(result: T): T { + context.enclosingDeclaration = oldEnclosing; + return result; + } + }); + if (result.length === clauses.length) { + return result; + } + return undefined; + } + function serializeAsClass(symbol: Symbol, localName: string, modifierFlags: ModifierFlags) { + const originalDecl = find(symbol.declarations, isClassLike); + const oldEnclosing = context.enclosingDeclaration; + context.enclosingDeclaration = originalDecl || oldEnclosing; const localParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); const typeParamDecls = map(localParams, p => typeParameterToDeclaration(p, context)); const classType = getDeclaredTypeOfClassOrInterface(symbol); const baseTypes = getBaseTypes(classType); - const implementsExpressions = mapDefined(getImplementsTypes(classType), serializeImplementedType); + const originalImplements = originalDecl && getEffectiveImplementsTypeNodes(originalDecl); + const implementsExpressions = originalImplements && sanitizeJSDocImplements(originalImplements) + || mapDefined(getImplementsTypes(classType), serializeImplementedType); const staticType = getTypeOfSymbol(symbol); const isClass = !!staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration); const staticBaseType = isClass @@ -6699,8 +6847,9 @@ namespace ts { !some(getSignaturesOfType(staticType, SignatureKind.Construct)); const constructors = isNonConstructableClassLikeInJsFile ? [factory.createConstructorDeclaration(/*decorators*/ undefined, factory.createModifiersFromModifierFlags(ModifierFlags.Private), [], /*body*/ undefined)] : - serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[]; + serializeSignatures(SignatureKind.Construct, staticType, staticBaseType, SyntaxKind.Constructor) as ConstructorDeclaration[]; const indexSignatures = serializeIndexSignatures(classType, baseTypes[0]); + context.enclosingDeclaration = oldEnclosing; addResult(setTextRange(factory.createClassDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, @@ -6729,24 +6878,54 @@ namespace ts { const targetName = getInternalSymbolName(target, verbatimTargetName); includePrivateSymbol(target); // the target may be within the same scope - attempt to serialize it first switch (node.kind) { + case SyntaxKind.BindingElement: + if (node.parent?.parent?.kind === SyntaxKind.VariableDeclaration) { + // const { SomeClass } = require('./lib'); + const specifier = getSpecifierForModuleSymbol(target.parent || target, context); // './lib' + const { propertyName } = node as BindingElement; + addResult(factory.createImportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + factory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, factory.createNamedImports([factory.createImportSpecifier( + propertyName && isIdentifier(propertyName) ? factory.createIdentifier(idText(propertyName)) : undefined, + factory.createIdentifier(localName) + )])), + factory.createStringLiteral(specifier) + ), ModifierFlags.None); + break; + } + // We don't know how to serialize this (nested?) binding element + Debug.failBadSyntaxKind(node.parent?.parent || node, "Unhandled binding element grandparent kind in declaration serialization"); + break; + case SyntaxKind.ShorthandPropertyAssignment: + if (node.parent?.parent?.kind === SyntaxKind.BinaryExpression) { + // module.exports = { SomeClass } + serializeExportSpecifier( + unescapeLeadingUnderscores(symbol.escapedName), + targetName + ); + } + break; case SyntaxKind.VariableDeclaration: // commonjs require: const x = require('y') if (isPropertyAccessExpression((node as VariableDeclaration).initializer!)) { // const x = require('y').z const initializer = (node as VariableDeclaration).initializer! as PropertyAccessExpression; // require('y').z - const uniqueName = factory.createUniqueName((getExternalModuleRequireArgument(node) as StringLiteral).text); // _y + const uniqueName = factory.createUniqueName(localName); // _x const specifier = getSpecifierForModuleSymbol(target.parent || target, context); // 'y' - // import _y = require('y'); + // import _x = require('y'); addResult(factory.createImportEqualsDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, + /*isTypeOnly*/ false, uniqueName, factory.createExternalModuleReference(factory.createStringLiteral(specifier)) ), ModifierFlags.None); - // import x = _y.z + // import x = _x.z addResult(factory.createImportEqualsDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, + /*isTypeOnly*/ false, factory.createIdentifier(localName), factory.createQualifiedName(uniqueName, initializer.name as Identifier), ), modifierFlags); @@ -6754,7 +6933,9 @@ namespace ts { } // else fall through and treat commonjs require just like import= case SyntaxKind.ImportEqualsDeclaration: - if (target.escapedName === InternalSymbolName.ExportEquals) { + // This _specifically_ only exists to handle json declarations - where we make aliases, but since + // we emit no declarations for the json document, must not refer to it in the declarations + if (target.escapedName === InternalSymbolName.ExportEquals && some(target.declarations, isJsonSourceFile)) { serializeMaybeAliasAssignment(symbol); break; } @@ -6764,6 +6945,7 @@ namespace ts { addResult(factory.createImportEqualsDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, + /*isTypeOnly*/ false, factory.createIdentifier(localName), isLocalImport ? symbolToName(target, context, SymbolFlags.All, /*expectsIdentifier*/ false) @@ -6873,7 +7055,7 @@ namespace ts { const name = unescapeLeadingUnderscores(symbol.escapedName); const isExportEquals = name === InternalSymbolName.ExportEquals; const isDefault = name === InternalSymbolName.Default; - const isExportAssignment = isExportEquals || isDefault; + const isExportAssignmentCompatibleSymbolName = isExportEquals || isDefault; // synthesize export = ref // ref should refer to either be a locally scoped symbol which we need to emit, or // a reference to another namespace/module which we may need to emit an `import` statement for @@ -6885,8 +7067,8 @@ namespace ts { // In case `target` refers to a namespace member, look at the declaration and serialize the leftmost symbol in it // eg, `namespace A { export class B {} }; exports = A.B;` // Technically, this is all that's required in the case where the assignment is an entity name expression - const expr = isExportAssignment ? getExportAssignmentExpression(aliasDecl as ExportAssignment | BinaryExpression) : getPropertyAssignmentAliasLikeExpression(aliasDecl as ShorthandPropertyAssignment | PropertyAssignment | PropertyAccessExpression); - const first = isEntityNameExpression(expr) ? getFirstNonModuleExportsIdentifier(expr) : undefined; + const expr = aliasDecl && ((isExportAssignment(aliasDecl) || isBinaryExpression(aliasDecl)) ? getExportAssignmentExpression(aliasDecl) : getPropertyAssignmentAliasLikeExpression(aliasDecl as ShorthandPropertyAssignment | PropertyAssignment | PropertyAccessExpression)); + const first = expr && isEntityNameExpression(expr) ? getFirstNonModuleExportsIdentifier(expr) : undefined; const referenced = first && resolveEntityName(first, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, enclosingDeclaration); if (referenced || target) { includePrivateSymbol(referenced || target); @@ -6899,7 +7081,7 @@ namespace ts { // into the containing scope anyway, so we want to skip the visibility checks. const oldTrack = context.tracker.trackSymbol; context.tracker.trackSymbol = noop; - if (isExportAssignment) { + if (isExportAssignmentCompatibleSymbolName) { results.push(factory.createExportAssignment( /*decorators*/ undefined, /*modifiers*/ undefined, @@ -6908,11 +7090,11 @@ namespace ts { )); } else { - if (first === expr) { + if (first === expr && first) { // serialize as `export {target as name}` serializeExportSpecifier(name, idText(first)); } - else if (isClassExpression(expr)) { + else if (expr && isClassExpression(expr)) { serializeExportSpecifier(name, getInternalSymbolName(target, symbolName(target))); } else { @@ -6921,6 +7103,7 @@ namespace ts { addResult(factory.createImportEqualsDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, + /*isTypeOnly*/ false, factory.createIdentifier(varName), symbolToName(target, context, SymbolFlags.All, /*expectsIdentifier*/ false) ), ModifierFlags.None); @@ -6938,7 +7121,7 @@ namespace ts { const typeToSerialize = getWidenedType(getTypeOfSymbol(getMergedSymbol(symbol))); if (isTypeRepresentableAsFunctionNamespaceMerge(typeToSerialize, symbol)) { // If there are no index signatures and `typeToSerialize` is an object type, emit as a namespace instead of a const - serializeAsFunctionNamespaceMerge(typeToSerialize, symbol, varName, isExportAssignment ? ModifierFlags.None : ModifierFlags.Export); + serializeAsFunctionNamespaceMerge(typeToSerialize, symbol, varName, isExportAssignmentCompatibleSymbolName ? ModifierFlags.None : ModifierFlags.Export); } else { const statement = factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([ @@ -6951,7 +7134,7 @@ namespace ts { : name === varName ? ModifierFlags.Export : ModifierFlags.None); } - if (isExportAssignment) { + if (isExportAssignmentCompatibleSymbolName) { results.push(factory.createExportAssignment( /*decorators*/ undefined, /*modifiers*/ undefined, @@ -7764,9 +7947,13 @@ namespace ts { const propName = getDestructuringPropertyName(node); if (propName) { const literal = setTextRange(parseNodeFactory.createStringLiteral(propName), node); - const result = setTextRange(parseNodeFactory.createElementAccessExpression(parentAccess, literal), node); + const lhsExpr = isLeftHandSideExpression(parentAccess) ? parentAccess : parseNodeFactory.createParenthesizedExpression(parentAccess); + const result = setTextRange(parseNodeFactory.createElementAccessExpression(lhsExpr, literal), node); setParent(literal, result); setParent(result, node); + if (lhsExpr !== parentAccess) { + setParent(lhsExpr, result); + } result.flowNode = parentAccess.flowNode; return result; } @@ -7841,7 +8028,7 @@ namespace ts { // Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form) const name = declaration.propertyName || declaration.name; const indexType = getLiteralTypeFromPropertyName(name); - const declaredType = getConstraintForLocation(getIndexedAccessType(parentType, indexType, name, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, AccessFlags.ExpressionPosition), declaration.name); + const declaredType = getConstraintForLocation(getIndexedAccessType(parentType, indexType, /*noUncheckedIndexedAccessCandidate*/ undefined, name, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, AccessFlags.ExpressionPosition), declaration.name); type = getFlowTypeOfDestructuring(declaration, declaredType); } } @@ -7862,7 +8049,7 @@ namespace ts { else if (isArrayLikeType(parentType)) { const indexType = getLiteralType(index); const accessFlags = hasDefaultValue(declaration) ? AccessFlags.NoTupleBoundsCheck : 0; - const declaredType = getConstraintForLocation(getIndexedAccessTypeOrUndefined(parentType, indexType, declaration.name, accessFlags | AccessFlags.ExpressionPosition) || errorType, declaration.name); + const declaredType = getConstraintForLocation(getIndexedAccessTypeOrUndefined(parentType, indexType, /*noUncheckedIndexedAccessCandidate*/ undefined, declaration.name, accessFlags | AccessFlags.ExpressionPosition) || errorType, declaration.name); type = getFlowTypeOfDestructuring(declaration, declaredType); } else { @@ -8696,9 +8883,9 @@ namespace ts { let links = getSymbolLinks(symbol); const originalLinks = links; if (!links.type) { - const jsDeclaration = symbol.valueDeclaration && getDeclarationOfExpando(symbol.valueDeclaration); - if (jsDeclaration) { - const merged = mergeJSSymbols(symbol, getSymbolOfNode(jsDeclaration)); + const expando = symbol.valueDeclaration && getSymbolOfExpando(symbol.valueDeclaration, /*allowDeclaration*/ false); + if (expando) { + const merged = mergeJSSymbols(symbol, expando); if (merged) { // note:we overwrite links because we just cloned the symbol symbol = links = merged; @@ -10104,7 +10291,7 @@ namespace ts { if (signatures !== masterList) { const signature = signatures[0]; Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass"); - results = signature.typeParameters && some(results, s => !!s.typeParameters) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature)); + results = signature.typeParameters && some(results, s => !!s.typeParameters && !compareTypeParametersIdentical(signature.typeParameters!, s.typeParameters)) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature)); if (!results) { break; } @@ -10115,18 +10302,39 @@ namespace ts { return result || emptyArray; } - function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined): Symbol | undefined { + function compareTypeParametersIdentical(sourceParams: readonly TypeParameter[], targetParams: readonly TypeParameter[]): boolean { + if (sourceParams.length !== targetParams.length) { + return false; + } + + const mapper = createTypeMapper(targetParams, sourceParams); + for (let i = 0; i < sourceParams.length; i++) { + const source = sourceParams[i]; + const target = targetParams[i]; + if (source === target) continue; + // We instantiate the target type parameter constraints into the source types so we can recognize `` as the same as `` + if (!isTypeIdenticalTo(getConstraintFromTypeParameter(source) || unknownType, instantiateType(getConstraintFromTypeParameter(target) || unknownType, mapper))) return false; + // We don't compare defaults - we just use the type parameter defaults from the first signature that seems to match. + // It might make sense to combine these defaults in the future, but doing so intelligently requires knowing + // if the parameter is used covariantly or contravariantly (so we intersect if it's used like a parameter or union if used like a return type) + // and, since it's just an inference _default_, just picking one arbitrarily works OK. + } + + return true; + } + + function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined, mapper: TypeMapper | undefined): Symbol | undefined { if (!left || !right) { return left || right; } // A signature `this` type might be a read or a write position... It's very possible that it should be invariant // and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be // permissive when calling, for now, we'll intersect the `this` types just like we do for param types in union signatures. - const thisType = getIntersectionType([getTypeOfSymbol(left), getTypeOfSymbol(right)]); + const thisType = getIntersectionType([getTypeOfSymbol(left), instantiateType(getTypeOfSymbol(right), mapper)]); return createSymbolWithType(left, thisType); } - function combineUnionParameters(left: Signature, right: Signature) { + function combineUnionParameters(left: Signature, right: Signature, mapper: TypeMapper | undefined) { const leftCount = getParameterCount(left); const rightCount = getParameterCount(right); const longest = leftCount >= rightCount ? left : right; @@ -10136,8 +10344,14 @@ namespace ts { const needsExtraRestElement = eitherHasEffectiveRest && !hasEffectiveRestParameter(longest); const params = new Array(longestCount + (needsExtraRestElement ? 1 : 0)); for (let i = 0; i < longestCount; i++) { - const longestParamType = tryGetTypeAtPosition(longest, i)!; - const shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType; + let longestParamType = tryGetTypeAtPosition(longest, i)!; + if (longest === right) { + longestParamType = instantiateType(longestParamType, mapper); + } + let shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType; + if (shorter === right) { + shorterParamType = instantiateType(shorterParamType, mapper); + } const unionParamType = getIntersectionType([longestParamType, shorterParamType]); const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1); const isOptional = i >= getMinArgumentCount(longest) && i >= getMinArgumentCount(shorter); @@ -10158,19 +10372,28 @@ namespace ts { if (needsExtraRestElement) { const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String); restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, longestCount)); + if (shorter === right) { + restParamSymbol.type = instantiateType(restParamSymbol.type, mapper); + } params[longestCount] = restParamSymbol; } return params; } function combineSignaturesOfUnionMembers(left: Signature, right: Signature): Signature { + const typeParams = left.typeParameters || right.typeParameters; + let paramMapper: TypeMapper | undefined; + if (left.typeParameters && right.typeParameters) { + paramMapper = createTypeMapper(right.typeParameters, left.typeParameters); + // We just use the type parameter defaults from the first signature + } const declaration = left.declaration; - const params = combineUnionParameters(left, right); - const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter); + const params = combineUnionParameters(left, right, paramMapper); + const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter, paramMapper); const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount); const result = createSignature( declaration, - left.typeParameters || right.typeParameters, + typeParams, thisParam, params, /*resolvedReturnType*/ undefined, @@ -10179,6 +10402,9 @@ namespace ts { (left.flags | right.flags) & SignatureFlags.PropagatingFlags ); result.unionSignatures = concatenate(left.unionSignatures || [left], [right]); + if (paramMapper) { + result.mapper = left.mapper && left.unionSignatures ? combineTypeMappers(left.mapper, paramMapper) : paramMapper; + } return result; } @@ -10468,7 +10694,7 @@ namespace ts { existingProp.keyType = getUnionType([existingProp.keyType, keyType]); } else { - const modifiersProp = getPropertyOfType(modifiersType, propName); + const modifiersProp = isTypeUsableAsPropertyName(keyType) ? getPropertyOfType(modifiersType, getPropertyNameFromType(keyType)) : undefined; const isOptional = !!(templateModifiers & MappedTypeModifiers.IncludeOptional || !(templateModifiers & MappedTypeModifiers.ExcludeOptional) && modifiersProp && modifiersProp.flags & SymbolFlags.Optional); const isReadonly = !!(templateModifiers & MappedTypeModifiers.IncludeReadonly || @@ -10736,14 +10962,14 @@ namespace ts { function getConstraintFromIndexedAccess(type: IndexedAccessType) { const indexConstraint = getSimplifiedTypeOrConstraint(type.indexType); if (indexConstraint && indexConstraint !== type.indexType) { - const indexedAccess = getIndexedAccessTypeOrUndefined(type.objectType, indexConstraint); + const indexedAccess = getIndexedAccessTypeOrUndefined(type.objectType, indexConstraint, type.noUncheckedIndexedAccessCandidate); if (indexedAccess) { return indexedAccess; } } const objectConstraint = getSimplifiedTypeOrConstraint(type.objectType); if (objectConstraint && objectConstraint !== type.objectType) { - return getIndexedAccessTypeOrUndefined(objectConstraint, type.indexType); + return getIndexedAccessTypeOrUndefined(objectConstraint, type.indexType, type.noUncheckedIndexedAccessCandidate); } return undefined; } @@ -10834,7 +11060,7 @@ namespace ts { } function getBaseConstraintOfType(type: Type): Type | undefined { - if (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral)) { + if (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) { const constraint = getResolvedBaseConstraint(type); return constraint !== noConstraintType && constraint !== circularConstraintType ? constraint : undefined; } @@ -10859,28 +11085,29 @@ namespace ts { * circularly references the type variable. */ function getResolvedBaseConstraint(type: InstantiableType | UnionOrIntersectionType): Type { - let nonTerminating = false; - return type.resolvedBaseConstraint || - (type.resolvedBaseConstraint = getTypeWithThisArgument(getImmediateBaseConstraint(type), type)); + if (type.resolvedBaseConstraint) { + return type.resolvedBaseConstraint; + } + const stack: Type[] = []; + return type.resolvedBaseConstraint = getTypeWithThisArgument(getImmediateBaseConstraint(type), type); function getImmediateBaseConstraint(t: Type): Type { if (!t.immediateBaseConstraint) { if (!pushTypeResolution(t, TypeSystemPropertyName.ImmediateBaseConstraint)) { return circularConstraintType; } - if (constraintDepth >= 50) { - // We have reached 50 recursive invocations of getImmediateBaseConstraint and there is a - // very high likelihood we're dealing with an infinite generic type that perpetually generates - // new type identities as we descend into it. We stop the recursion here and mark this type - // and the outer types as having circular constraints. - tracing.instant(tracing.Phase.Check, "getImmediateBaseConstraint_DepthLimit", { typeId: t.id, originalTypeId: type.id, depth: constraintDepth }); - error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite); - nonTerminating = true; - return t.immediateBaseConstraint = noConstraintType; - } - constraintDepth++; - let result = computeBaseConstraint(getSimplifiedType(t, /*writing*/ false)); - constraintDepth--; + let result; + // We always explore at least 10 levels of nested constraints. Thereafter, we continue to explore + // up to 50 levels of nested constraints provided there are no "deeply nested" types on the stack + // (i.e. no types for which five instantiations have been recorded on the stack). If we reach 50 + // levels of nesting, we are presumably exploring a repeating pattern with a long cycle that hasn't + // yet triggered the deeply nested limiter. We have no test cases that actually get to 50 levels of + // nesting, so it is effectively just a safety stop. + if (stack.length < 10 || stack.length < 50 && !isDeeplyNestedType(t, stack, stack.length)) { + stack.push(t); + result = computeBaseConstraint(getSimplifiedType(t, /*writing*/ false)); + stack.pop(); + } if (!popTypeResolution()) { if (t.flags & TypeFlags.TypeParameter) { const errorNode = getConstraintDeclaration(t); @@ -10893,9 +11120,6 @@ namespace ts { } result = circularConstraintType; } - if (nonTerminating) { - result = circularConstraintType; - } t.immediateBaseConstraint = result || noConstraintType; } return t.immediateBaseConstraint; @@ -10932,20 +11156,21 @@ namespace ts { if (t.flags & TypeFlags.TemplateLiteral) { const types = (t).types; const constraints = mapDefined(types, getBaseConstraint); - return constraints.length === types.length ? getTemplateLiteralType((t).texts, (t).casings, constraints) : stringType; + return constraints.length === types.length ? getTemplateLiteralType((t).texts, constraints) : stringType; + } + if (t.flags & TypeFlags.StringMapping) { + const constraint = getBaseConstraint((t).type); + return constraint ? getStringMappingType((t).symbol, constraint) : stringType; } if (t.flags & TypeFlags.IndexedAccess) { const baseObjectType = getBaseConstraint((t).objectType); const baseIndexType = getBaseConstraint((t).indexType); - const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType); + const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, (t).noUncheckedIndexedAccessCandidate); return baseIndexedAccess && getBaseConstraint(baseIndexedAccess); } if (t.flags & TypeFlags.Conditional) { const constraint = getConstraintFromConditionalType(t); - constraintDepth++; // Penalize repeating conditional types (this captures the recursion within getConstraintFromConditionalType and carries it forward) - const result = constraint && getBaseConstraint(constraint); - constraintDepth--; - return result; + return constraint && getBaseConstraint(constraint); } if (t.flags & TypeFlags.Substitution) { return getBaseConstraint((t).substitute); @@ -11048,7 +11273,7 @@ namespace ts { return getReducedType(getApparentType(getReducedType(type))); } - function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol | undefined { + function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined { let singleProp: Symbol | undefined; let propSet: ESMap | undefined; let indexTypes: Type[] | undefined; @@ -11060,7 +11285,7 @@ namespace ts { for (const current of containingType.types) { const type = getApparentType(current); if (!(type === errorType || type.flags & TypeFlags.Never)) { - const prop = getPropertyOfType(type, name); + const prop = getPropertyOfType(type, name, skipObjectFunctionPropertyAugment); const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0; if (prop) { if (isUnion) { @@ -11177,20 +11402,23 @@ namespace ts { // constituents, in which case the isPartial flag is set when the containing type is union type. We need // these partial properties when identifying discriminant properties, but otherwise they are filtered out // and do not appear to be present in the union type. - function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: __String): Symbol | undefined { - const properties = type.propertyCache || (type.propertyCache = createSymbolTable()); - let property = properties.get(name); + function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined { + let property = type.propertyCacheWithoutObjectFunctionPropertyAugment?.get(name) || + !skipObjectFunctionPropertyAugment ? type.propertyCache?.get(name) : undefined; if (!property) { - property = createUnionOrIntersectionProperty(type, name); + property = createUnionOrIntersectionProperty(type, name, skipObjectFunctionPropertyAugment); if (property) { + const properties = skipObjectFunctionPropertyAugment ? + type.propertyCacheWithoutObjectFunctionPropertyAugment ||= createSymbolTable() : + type.propertyCache ||= createSymbolTable(); properties.set(name, property); } } return property; } - function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String): Symbol | undefined { - const property = getUnionOrIntersectionProperty(type, name); + function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined { + const property = getUnionOrIntersectionProperty(type, name, skipObjectFunctionPropertyAugment); // We need to filter out partial properties in union types return property && !(getCheckFlags(property) & CheckFlags.ReadPartial) ? property : undefined; } @@ -11268,7 +11496,7 @@ namespace ts { * @param type a type to look up property from * @param name a name of property to look up in a given type */ - function getPropertyOfType(type: Type, name: __String): Symbol | undefined { + function getPropertyOfType(type: Type, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined { type = getReducedApparentType(type); if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); @@ -11276,6 +11504,7 @@ namespace ts { if (symbol && symbolIsValue(symbol)) { return symbol; } + if (skipObjectFunctionPropertyAugment) return undefined; const functionType = resolved === anyFunctionType ? globalFunctionType : resolved.callSignatures.length ? globalCallableFunctionType : resolved.constructSignatures.length ? globalNewableFunctionType : @@ -11289,7 +11518,7 @@ namespace ts { return getPropertyOfObjectType(globalObjectType, name); } if (type.flags & TypeFlags.UnionOrIntersection) { - return getPropertyOfUnionOrIntersectionType(type, name); + return getPropertyOfUnionOrIntersectionType(type, name, skipObjectFunctionPropertyAugment); } return undefined; } @@ -11614,7 +11843,7 @@ namespace ts { if (!node) return false; switch (node.kind) { case SyntaxKind.Identifier: - return (node).escapedText === "arguments" && isExpressionNode(node); + return (node).escapedText === argumentsSymbol.escapedName && getResolvedSymbol(node) === argumentsSymbol; case SyntaxKind.PropertyDeclaration: case SyntaxKind.MethodDeclaration: @@ -11623,6 +11852,10 @@ namespace ts { return (node).name!.kind === SyntaxKind.ComputedPropertyName && traverse((node).name!); + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + return traverse((node).expression); + default: return !nodeStartsNewLexicalEnvironment(node) && !isPartOfTypeNode(node) && !!forEachChild(node, traverse); } @@ -11709,7 +11942,7 @@ namespace ts { return errorType; } let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper) : - signature.unionSignatures ? getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype) : + signature.unionSignatures ? instantiateType(getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype), signature.mapper) : getReturnTypeFromAnnotation(signature.declaration!) || (nodeIsMissing((signature.declaration).body) ? anyType : getReturnTypeFromBody(signature.declaration)); if (signature.flags & SignatureFlags.IsInnerCallChain) { @@ -11924,12 +12157,12 @@ namespace ts { // (such as 'Foo'), T's constraint is inferred from the constraint of the // corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are // present, we form an intersection of the inferred constraint types. - const grandParent = declaration.parent.parent; + const [childTypeParameter = declaration.parent, grandParent] = walkUpParenthesizedTypesAndGetParentAndChild(declaration.parent.parent); if (grandParent.kind === SyntaxKind.TypeReference) { const typeReference = grandParent; const typeParameters = getTypeParametersForTypeReference(typeReference); if (typeParameters) { - const index = typeReference.typeArguments!.indexOf(declaration.parent); + const index = typeReference.typeArguments!.indexOf(childTypeParameter); if (index < typeParameters.length) { const declaredConstraint = getConstraintOfTypeParameter(typeParameters[index]); if (declaredConstraint) { @@ -11960,6 +12193,11 @@ namespace ts { else if (grandParent.kind === SyntaxKind.TemplateLiteralTypeSpan) { inferences = append(inferences, stringType); } + // When an 'infer T' declaration is in the constraint position of a mapped type, we infer a 'keyof any' + // constraint. + else if (grandParent.kind === SyntaxKind.TypeParameter && grandParent.parent.kind === SyntaxKind.MappedType) { + inferences = append(inferences, keyofConstraintType); + } } } } @@ -12142,6 +12380,9 @@ namespace ts { function getTypeAliasInstantiation(symbol: Symbol, typeArguments: readonly Type[] | undefined): Type { const type = getDeclaredTypeOfSymbol(symbol); + if (type === intrinsicMarkerType && intrinsicTypeKinds.has(symbol.escapedName as string) && typeArguments && typeArguments.length === 1) { + return getStringMappingType(symbol, typeArguments[0]); + } const links = getSymbolLinks(symbol); const typeParameters = links.typeParameters!; const id = getTypeListId(typeArguments); @@ -12245,7 +12486,7 @@ namespace ts { if (symbol.valueDeclaration) { const isImportTypeWithQualifier = node.kind === SyntaxKind.ImportType && (node as ImportTypeNode).qualifier; // valueType might not have a symbol, eg, {import('./b').STRING_LITERAL} - if (valueType.symbol && isImportTypeWithQualifier) { + if (valueType.symbol && valueType.symbol !== symbol && isImportTypeWithQualifier) { typeType = getTypeReferenceType(node, valueType.symbol); } } @@ -12967,7 +13208,7 @@ namespace ts { // caps union types at 5000 unique literal types and 1000 unique object types. const estimatedCount = (count / (len - i)) * len; if (estimatedCount > (primitivesOnly ? 25000000 : 1000000)) { - tracing.instant(tracing.Phase.Check, "removeSubtypes_DepthLimit", { typeIds: types.map(t => t.id) }); + tracing.instant(tracing.Phase.CheckTypes, "removeSubtypes_DepthLimit", { typeIds: types.map(t => t.id) }); error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent); return false; } @@ -12992,7 +13233,7 @@ namespace ts { i--; const t = types[i]; const remove = - t.flags & TypeFlags.StringLiteral && includes & TypeFlags.String || + t.flags & TypeFlags.StringLikeLiteral && includes & TypeFlags.String || t.flags & TypeFlags.NumberLiteral && includes & TypeFlags.Number || t.flags & TypeFlags.BigIntLiteral && includes & TypeFlags.BigInt || t.flags & TypeFlags.UniqueESSymbol && includes & TypeFlags.ESSymbol || @@ -13003,6 +13244,20 @@ namespace ts { } } + function removeStringLiteralsMatchedByTemplateLiterals(types: Type[]) { + const templates = filter(types, isPatternLiteralType); + if (templates.length) { + let i = types.length; + while (i > 0) { + i--; + const t = types[i]; + if (t.flags & TypeFlags.StringLiteral && some(templates, template => isTypeSubtypeOf(t, template))) { + orderedRemoveItemAt(types, i); + } + } + } + } + // We sort and deduplicate the constituent types based on object identity. If the subtypeReduction // flag is specified we also reduce the constituent type set to only include types that aren't subtypes // of other types. Subtype reduction is expensive for large union types and is possible only when union @@ -13025,9 +13280,12 @@ namespace ts { } switch (unionReduction) { case UnionReduction.Literal: - if (includes & (TypeFlags.Literal | TypeFlags.UniqueESSymbol)) { + if (includes & (TypeFlags.FreshableLiteral | TypeFlags.UniqueESSymbol)) { removeRedundantLiteralTypes(typeSet, includes); } + if (includes & TypeFlags.StringLiteral && includes & TypeFlags.TemplateLiteral) { + removeStringLiteralsMatchedByTemplateLiterals(typeSet); + } break; case UnionReduction.Subtype: if (!removeSubtypes(typeSet, !(includes & TypeFlags.IncludesStructuredOrInstantiable))) { @@ -13187,6 +13445,30 @@ namespace ts { return true; } + /** + * Returns `true` if the intersection of the template literals and string literals is the empty set, eg `get${string}` & "setX", and should reduce to `never` + */ + function extractRedundantTemplateLiterals(types: Type[]): boolean { + let i = types.length; + const literals = filter(types, t => !!(t.flags & TypeFlags.StringLiteral)); + while (i > 0) { + i--; + const t = types[i]; + if (!(t.flags & TypeFlags.TemplateLiteral)) continue; + for (const t2 of literals) { + if (isTypeSubtypeOf(t2, t)) { + // eg, ``get${T}` & "getX"` is just `"getX"` + orderedRemoveItemAt(types, i); + break; + } + else if (isPatternLiteralType(t)) { + return true; + } + } + } + return false; + } + function extractIrreducible(types: Type[], flag: TypeFlags) { if (every(types, t => !!(t.flags & TypeFlags.Union) && some((t as UnionType).types, tt => !!(tt.flags & flag)))) { for (let i = 0; i < types.length; i++) { @@ -13284,6 +13566,9 @@ namespace ts { includes & TypeFlags.VoidLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.VoidLike)) { return neverType; } + if (includes & TypeFlags.TemplateLiteral && includes & TypeFlags.StringLiteral && extractRedundantTemplateLiterals(typeSet)) { + return neverType; + } if (includes & TypeFlags.Any) { return includes & TypeFlags.IncludesWildcard ? wildcardType : anyType; } @@ -13345,6 +13630,7 @@ namespace ts { function checkCrossProductUnion(types: readonly Type[]) { const size = reduceLeft(types, (n, t) => n * (t.flags & TypeFlags.Union ? (t).types.length : t.flags & TypeFlags.Never ? 0 : 1), 1); if (size >= 100000) { + tracing.instant(tracing.Phase.CheckTypes, "checkCrossProductUnion_DepthLimit", { typeIds: types.map(t => t.id), size }); error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent); return false; } @@ -13376,11 +13662,25 @@ namespace ts { function getIndexTypeForMappedType(type: MappedType, noIndexSignatures: boolean | undefined) { const constraint = filterType(getConstraintTypeFromMappedType(type), t => !(noIndexSignatures && t.flags & (TypeFlags.Any | TypeFlags.String))); - return type.declaration.nameType ? - instantiateType(getTypeFromTypeNode(type.declaration.nameType), appendTypeMapping(type.mapper, getTypeParameterFromMappedType(type), constraint)) : + const nameType = type.declaration.nameType && getTypeFromTypeNode(type.declaration.nameType); + return nameType ? + mapType(constraint, t => instantiateType(nameType, appendTypeMapping(type.mapper, getTypeParameterFromMappedType(type), t))) : constraint; } + // Ordinarily we reduce a keyof M, where M is a mapped type { [P in K as N

]: X }, to simply N. This however presumes + // that N distributes over union types, i.e. that N is equivalent to N | N | N. That presumption may not + // be true when N is a non-distributive conditional type or an instantiable type with a non-distributive conditional type as + // a constituent. In those cases, we cannot reduce keyof M and need to preserve it as is. + function maybeNonDistributiveNameType(type: Type | undefined): boolean { + return !!(type && ( + type.flags & TypeFlags.Conditional && (!(type).root.isDistributive || maybeNonDistributiveNameType((type).checkType)) || + type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral) && some((type).types, maybeNonDistributiveNameType) || + type.flags & (TypeFlags.Index | TypeFlags.StringMapping) && maybeNonDistributiveNameType((type).type) || + type.flags & TypeFlags.IndexedAccess && maybeNonDistributiveNameType((type).indexType) || + type.flags & TypeFlags.Substitution && maybeNonDistributiveNameType((type).substitute))); + } + function getLiteralTypeFromPropertyName(name: PropertyName) { if (isPrivateIdentifier(name)) { return neverType; @@ -13428,7 +13728,7 @@ namespace ts { type = getReducedType(type); return type.flags & TypeFlags.Union ? getIntersectionType(map((type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) : type.flags & TypeFlags.Intersection ? getUnionType(map((type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) : - type.flags & TypeFlags.InstantiableNonPrimitive || isGenericTupleType(type) ? getIndexTypeForGenericType(type, stringsOnly) : + type.flags & TypeFlags.InstantiableNonPrimitive || isGenericTupleType(type) || isGenericMappedType(type) && maybeNonDistributiveNameType(getNameTypeFromMappedType(type)) ? getIndexTypeForGenericType(type, stringsOnly) : getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(type, noIndexSignatures) : type === wildcardType ? wildcardType : type.flags & TypeFlags.Unknown ? neverType : @@ -13479,50 +13779,65 @@ namespace ts { if (!links.resolvedType) { links.resolvedType = getTemplateLiteralType( [node.head.text, ...map(node.templateSpans, span => span.literal.text)], - map(node.templateSpans, span => span.casing), map(node.templateSpans, span => getTypeFromTypeNode(span.type))); } return links.resolvedType; } - function getTemplateLiteralType(texts: readonly string[], casings: readonly TemplateCasing[], types: readonly Type[]): Type { + function getTemplateLiteralType(texts: readonly string[], types: readonly Type[]): Type { const unionIndex = findIndex(types, t => !!(t.flags & (TypeFlags.Never | TypeFlags.Union))); if (unionIndex >= 0) { return checkCrossProductUnion(types) ? - mapType(types[unionIndex], t => getTemplateLiteralType(texts, casings, replaceElement(types, unionIndex, t))) : + mapType(types[unionIndex], t => getTemplateLiteralType(texts, replaceElement(types, unionIndex, t))) : errorType; } - const newTypes = []; - const newCasings = []; - const newTexts = []; + if (contains(types, wildcardType)) { + return wildcardType; + } + const newTypes: Type[] = []; + const newTexts: string[] = []; let text = texts[0]; - for (let i = 0; i < types.length; i++) { - const t = types[i]; - if (t.flags & TypeFlags.Literal) { - const s = applyTemplateCasing(getTemplateStringForType(t) || "", casings[i]); - text += s; - text += texts[i + 1]; - } - else if (isGenericIndexType(t)) { - newTypes.push(t); - newCasings.push(casings[i]); - newTexts.push(text); - text = texts[i + 1]; - } - else { - return stringType; - } + if (!addSpans(texts, types)) { + return stringType; } if (newTypes.length === 0) { return getLiteralType(text); } newTexts.push(text); - const id = `${getTypeListId(newTypes)}|${newCasings.join(",")}|${map(newTexts, t => t.length).join(",")}|${newTexts.join("")}`; + if (every(newTexts, t => t === "") && every(newTypes, t => !!(t.flags & TypeFlags.String))) { + return stringType; + } + const id = `${getTypeListId(newTypes)}|${map(newTexts, t => t.length).join(",")}|${newTexts.join("")}`; let type = templateLiteralTypes.get(id); if (!type) { - templateLiteralTypes.set(id, type = createTemplateLiteralType(newTexts, newCasings, newTypes)); + templateLiteralTypes.set(id, type = createTemplateLiteralType(newTexts, newTypes)); + type.regularType = type; } return type; + + function addSpans(texts: readonly string[], types: readonly Type[]): boolean { + for (let i = 0; i < types.length; i++) { + const t = types[i]; + if (t.flags & (TypeFlags.Literal | TypeFlags.Null | TypeFlags.Undefined)) { + text += getTemplateStringForType(t) || ""; + text += texts[i + 1]; + } + else if (t.flags & TypeFlags.TemplateLiteral) { + text += (t).texts[0]; + if (!addSpans((t).texts, (t).types)) return false; + text += texts[i + 1]; + } + else if (isGenericIndexType(t) || isPatternLiteralPlaceholderType(t)) { + newTypes.push(t); + newTexts.push(text); + text = texts[i + 1]; + } + else { + return false; + } + } + return true; + } } function getTemplateStringForType(type: Type) { @@ -13530,33 +13845,58 @@ namespace ts { type.flags & TypeFlags.NumberLiteral ? "" + (type).value : type.flags & TypeFlags.BigIntLiteral ? pseudoBigIntToString((type).value) : type.flags & TypeFlags.BooleanLiteral ? (type).intrinsicName : + type.flags & TypeFlags.Null ? "null" : + type.flags & TypeFlags.Undefined ? "undefined" : undefined; } - function applyTemplateCasing(str: string, casing: TemplateCasing) { - switch (casing) { - case TemplateCasing.Uppercase: return str.toUpperCase(); - case TemplateCasing.Lowercase: return str.toLowerCase(); - case TemplateCasing.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1); - case TemplateCasing.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1); - } - return str; - } - - function createTemplateLiteralType(texts: readonly string[], casings: readonly TemplateCasing[], types: readonly Type[]) { + function createTemplateLiteralType(texts: readonly string[], types: readonly Type[]) { const type = createType(TypeFlags.TemplateLiteral); type.texts = texts; - type.casings = casings; type.types = types; return type; } - function createIndexedAccessType(objectType: Type, indexType: Type, aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined) { + function getStringMappingType(symbol: Symbol, type: Type): Type { + return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getStringMappingType(symbol, t)) : + isGenericIndexType(type) ? getStringMappingTypeForGenericType(symbol, type) : + type.flags & TypeFlags.StringLiteral ? getLiteralType(applyStringMapping(symbol, (type).value)) : + type; + } + + function applyStringMapping(symbol: Symbol, str: string) { + switch (intrinsicTypeKinds.get(symbol.escapedName as string)) { + case IntrinsicTypeKind.Uppercase: return str.toUpperCase(); + case IntrinsicTypeKind.Lowercase: return str.toLowerCase(); + case IntrinsicTypeKind.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1); + case IntrinsicTypeKind.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1); + } + return str; + } + + function getStringMappingTypeForGenericType(symbol: Symbol, type: Type): Type { + const id = `${getSymbolId(symbol)},${getTypeId(type)}`; + let result = stringMappingTypes.get(id); + if (!result) { + stringMappingTypes.set(id, result = createStringMappingType(symbol, type)); + } + return result; + } + + function createStringMappingType(symbol: Symbol, type: Type) { + const result = createType(TypeFlags.StringMapping); + result.symbol = symbol; + result.type = type; + return result; + } + + function createIndexedAccessType(objectType: Type, indexType: Type, aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined, shouldIncludeUndefined: boolean) { const type = createType(TypeFlags.IndexedAccess); type.objectType = objectType; type.indexType = indexType; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; + type.noUncheckedIndexedAccessCandidate = shouldIncludeUndefined; return type; } @@ -13601,18 +13941,24 @@ namespace ts { } function isUncalledFunctionReference(node: Node, symbol: Symbol) { - return !(symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) - || !isCallLikeExpression(findAncestor(node, n => !isAccessExpression(n)) || node.parent) - && every(symbol.declarations, d => !isFunctionLike(d) || !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated)); + if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) { + const parent = findAncestor(node.parent, n => !isAccessExpression(n)) || node.parent; + if (isCallLikeExpression(parent)) { + return isCallOrNewExpression(parent) && isIdentifier(node) && hasMatchingArgument(parent, node); + } + return every(symbol.declarations, d => !isFunctionLike(d) || !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated)); + } + return true; } - function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags, reportDeprecated?: boolean) { + function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags, noUncheckedIndexedAccessCandidate?: boolean, reportDeprecated?: boolean) { const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; const propName = accessNode && isPrivateIdentifier(accessNode) ? undefined : getPropertyNameFromIndex(indexType, accessNode); + if (propName !== undefined) { const prop = getPropertyOfType(objectType, propName); if (prop) { - if (reportDeprecated && accessNode && prop.valueDeclaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) { + if (reportDeprecated && accessNode && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) { const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode); errorOrSuggestion(/* isError */ false, deprecatedNode, Diagnostics._0_is_deprecated, propName as string); } @@ -13646,7 +13992,10 @@ namespace ts { } } errorIfWritingToReadonlyIndex(getIndexInfoOfType(objectType, IndexKind.Number)); - return mapType(objectType, t => getRestTypeOfTupleType(t) || undefinedType); + return mapType(objectType, t => { + const restType = getRestTypeOfTupleType(t) || undefinedType; + return noUncheckedIndexedAccessCandidate ? getUnionType([restType, undefinedType]) : restType; + }); } } if (!(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike)) { @@ -13662,16 +14011,13 @@ namespace ts { } return undefined; } - const shouldIncludeUndefined = - compilerOptions.noUncheckedIndexedAccess && - (accessFlags & (AccessFlags.Writing | AccessFlags.ExpressionPosition)) === AccessFlags.ExpressionPosition; if (accessNode && !isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) { const indexNode = getIndexNodeForAccessExpression(accessNode); error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType)); - return shouldIncludeUndefined ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type; + return noUncheckedIndexedAccessCandidate ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type; } errorIfWritingToReadonlyIndex(indexInfo); - return shouldIncludeUndefined ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type; + return noUncheckedIndexedAccessCandidate ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type; } if (indexType.flags & TypeFlags.Never) { return neverType; @@ -13680,12 +14026,26 @@ namespace ts { return anyType; } if (accessExpression && !isConstEnumObjectType(objectType)) { + if (isObjectLiteralType(objectType)) { + if (noImplicitAny && indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) { + diagnostics.add(createDiagnosticForNode(accessExpression, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType as StringLiteralType).value, typeToString(objectType))); + return undefinedType; + } + else if (indexType.flags & (TypeFlags.Number | TypeFlags.String)) { + const types = map((objectType).properties, property => { + return getTypeOfSymbol(property); + }); + return getUnionType(append(types, undefinedType)); + } + } + if (objectType.symbol === globalThisSymbol && propName !== undefined && globalThisSymbol.exports!.has(propName) && (globalThisSymbol.exports!.get(propName)!.flags & SymbolFlags.BlockScoped)) { error(accessExpression, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(propName), typeToString(objectType)); } else if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors && !suppressNoImplicitAnyError) { if (propName !== undefined && typeHasStaticProperty(propName, objectType)) { - error(accessExpression, Diagnostics.Property_0_is_a_static_member_of_type_1, propName as string, typeToString(objectType)); + const typeName = typeToString(objectType); + error(accessExpression, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_to_access_the_static_member_2_instead, propName as string, typeName, typeName + "[" + getTextOfNode(accessExpression.argumentExpression) + "]"); } else if (getIndexTypeOfType(objectType, IndexKind.Number)) { error(accessExpression.argumentExpression, Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number); @@ -13767,6 +14127,14 @@ namespace ts { accessNode; } + function isPatternLiteralPlaceholderType(type: Type) { + return templateConstraintType.types.indexOf(type) !== -1 || !!(type.flags & TypeFlags.Any); + } + + function isPatternLiteralType(type: Type) { + return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType); + } + function isGenericObjectType(type: Type): boolean { if (type.flags & TypeFlags.UnionOrIntersection) { if (!((type).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) { @@ -13786,7 +14154,7 @@ namespace ts { } return !!((type).objectFlags & ObjectFlags.IsGenericIndexType); } - return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral)); + return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) && !isPatternLiteralType(type); } function isThisTypeParameter(type: Type): boolean { @@ -13818,13 +14186,6 @@ namespace ts { } } - function unwrapSubstitution(type: Type): Type { - if (type.flags & TypeFlags.Substitution) { - return (type as SubstitutionType).substitute; - } - return type; - } - // Transform an indexed access to a simpler form, if possible. Return the simpler form, or return // the type itself if no transformation is possible. The writing flag indicates that the type is // the target of an assignment. @@ -13836,7 +14197,7 @@ namespace ts { type[cache] = circularConstraintType; // We recursively simplify the object type as it may in turn be an indexed access type. For example, with // '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type. - const objectType = unwrapSubstitution(getSimplifiedType(type.objectType, writing)); + const objectType = getSimplifiedType(type.objectType, writing); const indexType = getSimplifiedType(type.indexType, writing); // T[A | B] -> T[A] | T[B] (reading) // T[A | B] -> T[A] & T[B] (writing) @@ -13913,8 +14274,8 @@ namespace ts { return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper); } - function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], accessFlags = AccessFlags.None): Type { - return getIndexedAccessTypeOrUndefined(objectType, indexType, accessNode, accessFlags, aliasSymbol, aliasTypeArguments) || (accessNode ? errorType : unknownType); + function getIndexedAccessType(objectType: Type, indexType: Type, noUncheckedIndexedAccessCandidate?: boolean, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], accessFlags = AccessFlags.None): Type { + return getIndexedAccessTypeOrUndefined(objectType, indexType, noUncheckedIndexedAccessCandidate, accessNode, accessFlags, aliasSymbol, aliasTypeArguments) || (accessNode ? errorType : unknownType); } function indexTypeLessThan(indexType: Type, limit: number) { @@ -13930,14 +14291,14 @@ namespace ts { }); } - function getIndexedAccessTypeOrUndefined(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, accessFlags = AccessFlags.None, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type | undefined { + function getIndexedAccessTypeOrUndefined(objectType: Type, indexType: Type, noUncheckedIndexedAccessCandidate?: boolean, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, accessFlags = AccessFlags.None, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type | undefined { if (objectType === wildcardType || indexType === wildcardType) { return wildcardType; } - const shouldIncludeUndefined = - compilerOptions.noUncheckedIndexSignatures && - (accessFlags & (AccessFlags.Writing | AccessFlags.ExpressionPosition)) === AccessFlags.ExpressionPosition; + const shouldIncludeUndefined = noUncheckedIndexedAccessCandidate || + (!!compilerOptions.noUncheckedIndexedAccess && + (accessFlags & (AccessFlags.Writing | AccessFlags.ExpressionPosition)) === AccessFlags.ExpressionPosition); // If the object type has a string index signature and no other members we know that the result will // always be the type of that index signature and we can simplify accordingly. @@ -13957,13 +14318,13 @@ namespace ts { return objectType; } // Defer the operation by creating an indexed access type. - const id = objectType.id + "," + indexType.id; + const id = objectType.id + "," + indexType.id + (shouldIncludeUndefined ? "?" : ""); let type = indexedAccessTypes.get(id); if (!type) { - indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType, aliasSymbol, aliasTypeArguments)); + indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType, aliasSymbol, aliasTypeArguments, shouldIncludeUndefined)); } - return shouldIncludeUndefined ? getUnionType([type, undefinedType]) : type; + return type; } // In the following we resolve T[K] to the type of the property in T selected by K. // We treat boolean as different from other unions to improve errors; @@ -13973,7 +14334,7 @@ namespace ts { const propTypes: Type[] = []; let wasMissingProp = false; for (const t of (indexType).types) { - const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, wasMissingProp, accessNode, accessFlags); + const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, wasMissingProp, accessNode, accessFlags, shouldIncludeUndefined); if (propType) { propTypes.push(propType); } @@ -13993,7 +14354,7 @@ namespace ts { ? getIntersectionType(propTypes, aliasSymbol, aliasTypeArguments) : getUnionType(propTypes, UnionReduction.Literal, aliasSymbol, aliasTypeArguments); } - return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, /* supressNoImplicitAnyError */ false, accessNode, accessFlags | AccessFlags.CacheSymbol, /* reportDeprecated */ true); + return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, /* supressNoImplicitAnyError */ false, accessNode, accessFlags | AccessFlags.CacheSymbol, shouldIncludeUndefined, /* reportDeprecated */ true); } function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) { @@ -14002,7 +14363,7 @@ namespace ts { const objectType = getTypeFromTypeNode(node.objectType); const indexType = getTypeFromTypeNode(node.indexType); const potentialAlias = getAliasSymbolForTypeNode(node); - const resolved = getIndexedAccessType(objectType, indexType, node, potentialAlias, getTypeArgumentsForAliasSymbol(potentialAlias)); + const resolved = getIndexedAccessType(objectType, indexType, /*noUncheckedIndexedAccessCandidate*/ undefined, node, potentialAlias, getTypeArgumentsForAliasSymbol(potentialAlias)); links.resolvedType = resolved.flags & TypeFlags.IndexedAccess && (resolved).objectType === objectType && (resolved).indexType === indexType ? @@ -14054,11 +14415,7 @@ namespace ts { let combinedMapper: TypeMapper | undefined; if (root.inferTypeParameters) { const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None); - // We skip inference of the possible `infer` types unles the `extendsType` _is_ an infer type - // if it was, it's trivial to say that extendsType = checkType, however such a pattern is used to - // "reset" the type being build up during constraint calculation and avoid making an apparently "infinite" constraint - // so in those cases we refain from performing inference and retain the uninfered type parameter - if (!checkTypeInstantiable || !some(root.inferTypeParameters, t => t === extendsType)) { + if (!checkTypeInstantiable) { // We don't want inferences from constraints as they may cause us to eagerly resolve the // conditional type instead of deferring resolution. Also, we always want strict function // types rules (i.e. proper contravariance) for inferences. @@ -14103,10 +14460,9 @@ namespace ts { } } // Return a deferred type for a check that is neither definitely true nor definitely false - const erasedCheckType = getActualTypeVariable(checkType); result = createType(TypeFlags.Conditional); result.root = root; - result.checkType = erasedCheckType; + result.checkType = checkType; result.extendsType = extendsType; result.mapper = mapper; result.combinedMapper = combinedMapper; @@ -14213,7 +14569,13 @@ namespace ts { let current: Identifier | undefined; while (current = nameStack.shift()) { const meaning = nameStack.length ? SymbolFlags.Namespace : targetMeaning; - const next = getSymbol(getExportsOfSymbol(getMergedSymbol(resolveSymbol(currentNamespace))), current.escapedText, meaning); + // typeof a.b.c is normally resolved using `checkExpression` which in turn defers to `checkQualifiedName` + // That, in turn, ultimately uses `getPropertyOfType` on the type of the symbol, which differs slightly from + // the `exports` lookup process that only looks up namespace members which is used for most type references + const mergedResolvedSymbol = getMergedSymbol(resolveSymbol(currentNamespace)); + const next = node.isTypeOf + ? getPropertyOfType(getTypeOfSymbol(mergedResolvedSymbol), current.escapedText) + : getSymbol(getExportsOfSymbol(mergedResolvedSymbol), current.escapedText, meaning); if (!next) { error(current, Diagnostics.Namespace_0_has_no_exported_member_1, getFullyQualifiedName(currentNamespace), declarationNameToString(current)); return links.resolvedType = errorType; @@ -14277,7 +14639,7 @@ namespace ts { function getAliasSymbolForTypeNode(node: Node) { let host = node.parent; - while (isParenthesizedTypeNode(host) || isTypeOperatorNode(host) && host.operator === SyntaxKind.ReadonlyKeyword) { + while (isParenthesizedTypeNode(host) || isJSDocTypeExpression(host) || isTypeOperatorNode(host) && host.operator === SyntaxKind.ReadonlyKeyword) { host = host.parent; } return isTypeAlias(host) ? getSymbolOfNode(host) : undefined; @@ -14292,13 +14654,7 @@ namespace ts { } function isEmptyObjectTypeOrSpreadsIntoEmptyObject(type: Type) { - return isEmptyObjectType(type) || !!(type.flags & (TypeFlags.Null | TypeFlags.Undefined | TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral)); - } - - function isSinglePropertyAnonymousObjectType(type: Type) { - return !!(type.flags & TypeFlags.Object) && - !!(getObjectFlags(type) & ObjectFlags.Anonymous) && - (length(getPropertiesOfType(type)) === 1 || every(getPropertiesOfType(type), p => !!(p.flags & SymbolFlags.Optional))); + return isEmptyObjectType(type) || !!(type.flags & (TypeFlags.Null | TypeFlags.Undefined | TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index)); } function tryMergeUnionOfObjectTypeAndEmptyObject(type: UnionType, readonly: boolean): Type | undefined { @@ -14308,10 +14664,10 @@ namespace ts { if (every(type.types, isEmptyObjectTypeOrSpreadsIntoEmptyObject)) { return isEmptyObjectType(firstType) ? firstType : isEmptyObjectType(secondType) ? secondType : emptyObjectType; } - if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(firstType) && isSinglePropertyAnonymousObjectType(secondType)) { + if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(firstType)) { return getAnonymousPartialType(secondType); } - if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(secondType) && isSinglePropertyAnonymousObjectType(firstType)) { + if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(secondType)) { return getAnonymousPartialType(firstType); } } @@ -14369,16 +14725,20 @@ namespace ts { if (merged) { return getSpreadType(merged, right, symbol, objectFlags, readonly); } - return mapType(left, t => getSpreadType(t, right, symbol, objectFlags, readonly)); + return checkCrossProductUnion([left, right]) + ? mapType(left, t => getSpreadType(t, right, symbol, objectFlags, readonly)) + : errorType; } if (right.flags & TypeFlags.Union) { const merged = tryMergeUnionOfObjectTypeAndEmptyObject(right as UnionType, readonly); if (merged) { return getSpreadType(left, merged, symbol, objectFlags, readonly); } - return mapType(right, t => getSpreadType(left, t, symbol, objectFlags, readonly)); + return checkCrossProductUnion([left, right]) + ? mapType(right, t => getSpreadType(left, t, symbol, objectFlags, readonly)) + : errorType; } - if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral)) { + if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index)) { return left; } @@ -14490,28 +14850,32 @@ namespace ts { } function getFreshTypeOfLiteralType(type: Type): Type { - if (type.flags & TypeFlags.Literal) { - if (!(type).freshType) { - const freshType = createLiteralType(type.flags, (type).value, (type).symbol); - freshType.regularType = type; + if (type.flags & TypeFlags.FreshableLiteral) { + if (!(type).freshType) { + const freshType = type.flags & TypeFlags.TemplateLiteral ? + createTemplateLiteralType((type).texts, (type).types) : + createLiteralType(type.flags, (type).value, (type).symbol); + freshType.regularType = type; freshType.freshType = freshType; - (type).freshType = freshType; + (type).freshType = freshType; } - return (type).freshType; + return (type).freshType; } return type; } function getRegularTypeOfLiteralType(type: Type): Type { - return type.flags & TypeFlags.Literal ? (type).regularType : + return type.flags & TypeFlags.FreshableLiteral ? (type).regularType : type.flags & TypeFlags.Union ? ((type).regularType || ((type).regularType = getUnionType(sameMap((type).types, getRegularTypeOfLiteralType)) as UnionType)) : type; } function isFreshLiteralType(type: Type) { - return !!(type.flags & TypeFlags.Literal) && (type).freshType === type; + return !!(type.flags & TypeFlags.FreshableLiteral) && (type).freshType === type; } + function getLiteralType(value: string): StringLiteralType; + function getLiteralType(value: string | number | PseudoBigInt, enumId?: number, symbol?: Symbol): LiteralType; function getLiteralType(value: string | number | PseudoBigInt, enumId?: number, symbol?: Symbol) { // We store all literal types in a single map with keys of the form '#NNN' and '@SSS', // where NNN is the text representation of a numeric literal and SSS are the characters @@ -14656,6 +15020,8 @@ namespace ts { return neverType; case SyntaxKind.ObjectKeyword: return node.flags & NodeFlags.JavaScriptFile && !noImplicitAny ? anyType : nonPrimitiveType; + case SyntaxKind.IntrinsicKeyword: + return intrinsicMarkerType; case SyntaxKind.ThisType: case SyntaxKind.ThisKeyword as TypeNodeSyntaxKind: // TODO(rbuckton): `ThisKeyword` is no longer a `TypeNode`, but we defensively allow it here because of incorrect casts in the Language Service and because of `isPartOfTypeNode`. @@ -14903,9 +15269,10 @@ namespace ts { } function getObjectTypeInstantiation(type: AnonymousType | DeferredTypeReference, mapper: TypeMapper) { - const target = type.objectFlags & ObjectFlags.Instantiated ? type.target! : type; const declaration = type.objectFlags & ObjectFlags.Reference ? (type).node! : type.symbol.declarations[0]; const links = getNodeLinks(declaration); + const target = type.objectFlags & ObjectFlags.Reference ? links.resolvedType! : + type.objectFlags & ObjectFlags.Instantiated ? type.target! : type; let typeParameters = links.outerTypeParameters; if (!typeParameters) { // The first time an anonymous type is instantiated we compute and store a list of the type @@ -14922,10 +15289,6 @@ namespace ts { filter(typeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) : typeParameters; links.outerTypeParameters = typeParameters; - if (typeParameters.length) { - links.instantiations = new Map(); - links.instantiations.set(getTypeListId(typeParameters), target); - } } if (typeParameters.length) { // We are instantiating an anonymous type that has one or more type parameters in scope. Apply the @@ -14934,13 +15297,17 @@ namespace ts { const combinedMapper = combineTypeMappers(type.mapper, mapper); const typeArguments = map(typeParameters, t => getMappedType(t, combinedMapper)); const id = getTypeListId(typeArguments); - let result = links.instantiations!.get(id); + if (!target.instantiations) { + target.instantiations = new Map(); + target.instantiations.set(getTypeListId(typeParameters), target); + } + let result = target.instantiations.get(id); if (!result) { const newMapper = createTypeMapper(typeParameters, typeArguments); result = target.objectFlags & ObjectFlags.Reference ? createDeferredTypeReference((type).target, (type).node, newMapper) : target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(target, newMapper) : instantiateAnonymousType(target, newMapper); - links.instantiations!.set(id, result); + target.instantiations.set(id, result); } return result; } @@ -15026,7 +15393,8 @@ namespace ts { }); } } - return instantiateAnonymousType(type, mapper); + // If the constraint type of the instantiation is the wildcard type, return the wildcard type. + return instantiateType(getConstraintTypeFromMappedType(type), mapper) === wildcardType ? wildcardType : instantiateAnonymousType(type, mapper); } function getModifiedReadonlyState(state: boolean, modifiers: MappedTypeModifiers) { @@ -15139,7 +15507,7 @@ namespace ts { // We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing // with a combination of infinite generic types that perpetually generate new type identities. We stop // the recursion here by yielding the error type. - tracing.instant(tracing.Phase.Check, "instantiateType_DepthLimit", { typeId: type.id, instantiationDepth, instantiationCount }); + tracing.instant(tracing.Phase.CheckTypes, "instantiateType_DepthLimit", { typeId: type.id, instantiationDepth, instantiationCount }); error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite); return errorType; } @@ -15180,10 +15548,13 @@ namespace ts { return getIndexType(instantiateType((type).type, mapper)); } if (flags & TypeFlags.TemplateLiteral) { - return getTemplateLiteralType((type).texts, (type).casings, instantiateTypes((type).types, mapper)); + return getTemplateLiteralType((type).texts, instantiateTypes((type).types, mapper)); + } + if (flags & TypeFlags.StringMapping) { + return getStringMappingType((type).symbol, instantiateType((type).type, mapper)); } if (flags & TypeFlags.IndexedAccess) { - return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper), /*accessNode*/ undefined, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); + return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper), (type).noUncheckedIndexedAccessCandidate, /*accessNode*/ undefined, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); } if (flags & TypeFlags.Conditional) { return getConditionalTypeInstantiation(type, combineTypeMappers((type).mapper, mapper)); @@ -15363,7 +15734,7 @@ namespace ts { source.flags & TypeFlags.InstantiableNonPrimitive ? isTypeDerivedFrom(getBaseConstraintOfType(source) || unknownType, target) : target === globalObjectType ? !!(source.flags & (TypeFlags.Object | TypeFlags.NonPrimitive)) : target === globalFunctionType ? !!(source.flags & TypeFlags.Object) && isFunctionObjectType(source as ObjectType) : - hasBaseType(source, getTargetType(target)); + hasBaseType(source, getTargetType(target)) || (isArrayType(target) && !isReadonlyArrayType(target) && isTypeDerivedFrom(source, globalReadonlyArrayType)); } /** @@ -15680,10 +16051,6 @@ namespace ts { } } - function getSemanticJsxChildren(children: NodeArray) { - return filter(children, i => !isJsxText(i) || !i.containsOnlyTriviaWhiteSpaces); - } - function elaborateJsxComponents( node: JsxAttributes, source: Type, @@ -15945,36 +16312,38 @@ namespace ts { const restIndex = sourceRestType || targetRestType ? paramCount - 1 : -1; for (let i = 0; i < paramCount; i++) { - const sourceType = i === restIndex ? getRestTypeAtPosition(source, i) : getTypeAtPosition(source, i); - const targetType = i === restIndex ? getRestTypeAtPosition(target, i) : getTypeAtPosition(target, i); - // In order to ensure that any generic type Foo is at least co-variant with respect to T no matter - // how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions, - // they naturally relate only contra-variantly). However, if the source and target parameters both have - // function types with a single call signature, we know we are relating two callback parameters. In - // that case it is sufficient to only relate the parameters of the signatures co-variantly because, - // similar to return values, callback parameters are output positions. This means that a Promise, - // where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant) - // with respect to T. - const sourceSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(sourceType)); - const targetSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(targetType)); - const callbacks = sourceSig && targetSig && !getTypePredicateOfSignature(sourceSig) && !getTypePredicateOfSignature(targetSig) && - (getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable); - let related = callbacks ? - compareSignaturesRelated(targetSig!, sourceSig!, (checkMode & SignatureCheckMode.StrictArity) | (strictVariance ? SignatureCheckMode.StrictCallback : SignatureCheckMode.BivariantCallback), reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) : - !(checkMode & SignatureCheckMode.Callback) && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors); - // With strict arity, (x: number | undefined) => void is a subtype of (x?: number | undefined) => void - if (related && checkMode & SignatureCheckMode.StrictArity && i >= getMinArgumentCount(source) && i < getMinArgumentCount(target) && compareTypes(sourceType, targetType, /*reportErrors*/ false)) { - related = Ternary.False; - } - if (!related) { - if (reportErrors) { - errorReporter!(Diagnostics.Types_of_parameters_0_and_1_are_incompatible, - unescapeLeadingUnderscores(getParameterNameAtPosition(source, i)), - unescapeLeadingUnderscores(getParameterNameAtPosition(target, i))); + const sourceType = i === restIndex ? getRestTypeAtPosition(source, i) : tryGetTypeAtPosition(source, i); + const targetType = i === restIndex ? getRestTypeAtPosition(target, i) : tryGetTypeAtPosition(target, i); + if (sourceType && targetType) { + // In order to ensure that any generic type Foo is at least co-variant with respect to T no matter + // how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions, + // they naturally relate only contra-variantly). However, if the source and target parameters both have + // function types with a single call signature, we know we are relating two callback parameters. In + // that case it is sufficient to only relate the parameters of the signatures co-variantly because, + // similar to return values, callback parameters are output positions. This means that a Promise, + // where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant) + // with respect to T. + const sourceSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(sourceType)); + const targetSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(targetType)); + const callbacks = sourceSig && targetSig && !getTypePredicateOfSignature(sourceSig) && !getTypePredicateOfSignature(targetSig) && + (getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable); + let related = callbacks ? + compareSignaturesRelated(targetSig!, sourceSig!, (checkMode & SignatureCheckMode.StrictArity) | (strictVariance ? SignatureCheckMode.StrictCallback : SignatureCheckMode.BivariantCallback), reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) : + !(checkMode & SignatureCheckMode.Callback) && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors); + // With strict arity, (x: number | undefined) => void is a subtype of (x?: number | undefined) => void + if (related && checkMode & SignatureCheckMode.StrictArity && i >= getMinArgumentCount(source) && i < getMinArgumentCount(target) && compareTypes(sourceType, targetType, /*reportErrors*/ false)) { + related = Ternary.False; } - return Ternary.False; + if (!related) { + if (reportErrors) { + errorReporter!(Diagnostics.Types_of_parameters_0_and_1_are_incompatible, + unescapeLeadingUnderscores(getParameterNameAtPosition(source, i)), + unescapeLeadingUnderscores(getParameterNameAtPosition(target, i))); + } + return Ternary.False; + } + result &= related; } - result &= related; } if (!(checkMode & SignatureCheckMode.IgnoreReturnTypes)) { @@ -16261,7 +16630,7 @@ namespace ts { reportIncompatibleStack(); } if (overflow) { - tracing.instant(tracing.Phase.Check, "checkTypeRelatedTo_DepthLimit", { sourceId: source.id, targetId: target.id, depth }); + tracing.instant(tracing.Phase.CheckTypes, "checkTypeRelatedTo_DepthLimit", { sourceId: source.id, targetId: target.id, depth }); const diag = error(errorNode || currentNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target)); if (errorOutputContainer) { (errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag); @@ -16421,7 +16790,7 @@ namespace ts { } for (const [msg, ...args] of secondaryRootErrors) { const originalValue = msg.elidedInCompatabilityPyramid; - msg.elidedInCompatabilityPyramid = false; // Teporarily override elision to ensure error is reported + msg.elidedInCompatabilityPyramid = false; // Temporarily override elision to ensure error is reported reportError(msg, ...args); msg.elidedInCompatabilityPyramid = originalValue; } @@ -16572,7 +16941,6 @@ namespace ts { return isIdenticalTo(source, target); } - // We fastpath comparing a type parameter to exactly its constraint, as this is _super_ common, // and otherwise, for type parameters in large unions, causes us to need to compare the union to itself, // as we break down the _target_ union first, _then_ get the source constraint - so for every @@ -16633,6 +17001,8 @@ namespace ts { return Ternary.False; } + traceUnionsOrIntersectionsTooLarge(source, target); + let result = Ternary.False; const saveErrorInfo = captureErrorCalculationState(); @@ -16768,11 +17138,41 @@ namespace ts { } } + function traceUnionsOrIntersectionsTooLarge(source: Type, target: Type): void { + if (!tracing.isTracing()) { + return; + } + + if ((source.flags & TypeFlags.UnionOrIntersection) && (target.flags & TypeFlags.UnionOrIntersection)) { + const sourceUnionOrIntersection = source as UnionOrIntersectionType; + const targetUnionOrIntersection = target as UnionOrIntersectionType; + + if (sourceUnionOrIntersection.objectFlags & targetUnionOrIntersection.objectFlags & ObjectFlags.PrimitiveUnion) { + // There's a fast path for comparing primitive unions + return; + } + + const sourceSize = sourceUnionOrIntersection.types.length; + const targetSize = targetUnionOrIntersection.types.length; + if (sourceSize * targetSize > 1E6) { + tracing.instant(tracing.Phase.CheckTypes, "traceUnionsOrIntersectionsTooLarge_DepthLimit", { + sourceId: source.id, + sourceSize, + targetId: target.id, + targetSize, + pos: errorNode?.pos, + end: errorNode?.end + }); + } + } + } + function isIdenticalTo(source: Type, target: Type): Ternary { const flags = source.flags & target.flags; if (!(flags & TypeFlags.Substructure)) { return Ternary.False; } + traceUnionsOrIntersectionsTooLarge(source, target); if (flags & TypeFlags.UnionOrIntersection) { let result = eachTypeRelatedToSomeType(source, target); if (result) { @@ -16938,14 +17338,31 @@ namespace ts { return Ternary.False; } + function getUndefinedStrippedTargetIfNeeded(source: Type, target: Type) { + // As a builtin type, `undefined` is a very low type ID - making it almsot always first, making this a very fast check to see + // if we need to strip `undefined` from the target + if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union && + !((source as UnionType).types[0].flags & TypeFlags.Undefined) && (target as UnionType).types[0].flags & TypeFlags.Undefined) { + return extractTypesOfKind(target, ~TypeFlags.Undefined); + } + return target; + } + function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary { let result = Ternary.True; const sourceTypes = source.types; + // We strip `undefined` from the target if the `source` trivially doesn't contain it for our correspondence-checking fastpath + // since `undefined` is frequently added by optionality and would otherwise spoil a potentially useful correspondence + const undefinedStrippedTarget = getUndefinedStrippedTargetIfNeeded(source, target as UnionType); for (let i = 0; i < sourceTypes.length; i++) { const sourceType = sourceTypes[i]; - if (target.flags & TypeFlags.Union && (target as UnionType).types.length === sourceTypes.length) { + if (undefinedStrippedTarget.flags & TypeFlags.Union && sourceTypes.length >= (undefinedStrippedTarget as UnionType).types.length && sourceTypes.length % (undefinedStrippedTarget as UnionType).types.length === 0) { // many unions are mappings of one another; in such cases, simply comparing members at the same index can shortcut the comparison - const related = isRelatedTo(sourceType, (target as UnionType).types[i], /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState); + // such unions will have identical lengths, and their corresponding elements will match up. Another common scenario is where a large + // union has a union of objects intersected with it. In such cases, if the input was, eg `("a" | "b" | "c") & (string | boolean | {} | {whatever})`, + // the result will have the structure `"a" | "b" | "c" | "a" & {} | "b" & {} | "c" & {} | "a" & {whatever} | "b" & {whatever} | "c" & {whatever}` + // - the resulting union has a length which is a multiple of the original union, and the elements correspond modulo the length of the original union + const related = isRelatedTo(sourceType, (undefinedStrippedTarget as UnionType).types[i % (undefinedStrippedTarget as UnionType).types.length], /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState); if (related) { result &= related; continue; @@ -17084,7 +17501,7 @@ namespace ts { } if (expandingFlags === ExpandingFlags.Both) { - tracing.instant(tracing.Phase.Check, "recursiveTypeRelatedTo_DepthLimit", { + tracing.instant(tracing.Phase.CheckTypes, "recursiveTypeRelatedTo_DepthLimit", { sourceId: source.id, sourceIdStack: sourceStack.map(t => t.id), targetId: target.id, @@ -17101,9 +17518,12 @@ namespace ts { depth--; if (result) { if (result === Ternary.True || depth === 0) { - // If result is definitely true, record all maybe keys as having succeeded - for (let i = maybeStart; i < maybeCount; i++) { - relation.set(maybeKeys[i], RelationComparisonResult.Succeeded | propagatingVarianceFlags); + if (result === Ternary.True || result === Ternary.Maybe) { + // If result is definitely true, record all maybe keys as having succeeded. Also, record Ternary.Maybe + // results as having succeeded once we reach depth 0, but never record Ternary.Unknown results. + for (let i = maybeStart; i < maybeCount; i++) { + relation.set(maybeKeys[i], RelationComparisonResult.Succeeded | propagatingVarianceFlags); + } } maybeCount = maybeStart; } @@ -17118,9 +17538,9 @@ namespace ts { } function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary { - tracing.begin(tracing.Phase.Check, "structuredTypeRelatedTo", { sourceId: source.id, targetId: target.id }); + tracing.push(tracing.Phase.CheckTypes, "structuredTypeRelatedTo", { sourceId: source.id, targetId: target.id }); const result = structuredTypeRelatedToWorker(source, target, reportErrors, intersectionState); - tracing.end(); + tracing.pop(); return result; } @@ -17173,7 +17593,7 @@ namespace ts { !(source.aliasTypeArgumentsContainsMarker || target.aliasTypeArgumentsContainsMarker)) { const variances = getAliasVariances(source.aliasSymbol); if (variances === emptyArray) { - return Ternary.Maybe; + return Ternary.Unknown; } const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, intersectionState); if (varianceResult !== undefined) { @@ -17241,7 +17661,7 @@ namespace ts { const baseIndexType = getBaseConstraintOfType(indexType) || indexType; if (!isGenericObjectType(baseObjectType) && !isGenericIndexType(baseIndexType)) { const accessFlags = AccessFlags.Writing | (baseObjectType !== objectType ? AccessFlags.NoIndexSignatures : 0); - const constraint = getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, /*accessNode*/ undefined, accessFlags); + const constraint = getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, (target).noUncheckedIndexedAccessCandidate, /*accessNode*/ undefined, accessFlags); if (constraint && (result = isRelatedTo(source, constraint, reportErrors))) { return result; } @@ -17291,6 +17711,15 @@ namespace ts { } } } + else if (target.flags & TypeFlags.TemplateLiteral && source.flags & TypeFlags.StringLiteral) { + if (isPatternLiteralType(target)) { + // match all non-`string` segments + const result = inferLiteralsFromTemplateLiteralType(source as StringLiteralType, target as TemplateLiteralType); + if (result && every(result, (r, i) => isStringLiteralTypeValueParsableAsType(r, (target as TemplateLiteralType).types[i]))) { + return Ternary.True; + } + } + } if (source.flags & TypeFlags.TypeVariable) { if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) { @@ -17331,10 +17760,35 @@ namespace ts { } } else if (source.flags & TypeFlags.TemplateLiteral) { - const constraint = getBaseConstraintOfType(source); - if (constraint && (result = isRelatedTo(constraint, target, reportErrors))) { - resetErrorInfo(saveErrorInfo); - return result; + if (target.flags & TypeFlags.TemplateLiteral) { + if ((source as TemplateLiteralType).texts.length === (target as TemplateLiteralType).texts.length && + (source as TemplateLiteralType).types.length === (target as TemplateLiteralType).types.length && + every((source as TemplateLiteralType).texts, (t, i) => t === (target as TemplateLiteralType).texts[i]) && + every((instantiateType(source, makeFunctionTypeMapper(reportUnreliableMarkers)) as TemplateLiteralType).types, (t, i) => !!((target as TemplateLiteralType).types[i].flags & (TypeFlags.Any | TypeFlags.String)) || !!isRelatedTo(t, (target as TemplateLiteralType).types[i], /*reportErrors*/ false))) { + return Ternary.True; + } + } + else { + const constraint = getBaseConstraintOfType(source); + if (result = isRelatedTo(constraint && constraint !== source ? constraint : stringType, target, reportErrors)) { + resetErrorInfo(saveErrorInfo); + return result; + } + } + } + else if (source.flags & TypeFlags.StringMapping) { + if (target.flags & TypeFlags.StringMapping && (source).symbol === (target).symbol) { + if (result = isRelatedTo((source).type, (target).type, reportErrors)) { + resetErrorInfo(saveErrorInfo); + return result; + } + } + else { + const constraint = getBaseConstraintOfType(source); + if (constraint && (result = isRelatedTo(constraint, target, reportErrors))) { + resetErrorInfo(saveErrorInfo); + return result; + } } } else if (source.flags & TypeFlags.Conditional) { @@ -17415,7 +17869,7 @@ namespace ts { // effectively means we measure variance only from type parameter occurrences that aren't nested in // recursive instantiations of the generic type. if (variances === emptyArray) { - return Ternary.Maybe; + return Ternary.Unknown; } const varianceResult = relateVariances(getTypeArguments(source), getTypeArguments(target), variances, intersectionState); if (varianceResult !== undefined) { @@ -17583,7 +18037,7 @@ namespace ts { numCombinations *= countTypes(getTypeOfSymbol(sourceProperty)); if (numCombinations > 25) { // We've reached the complexity limit. - tracing.instant(tracing.Phase.Check, "typeRelatedToDiscriminatedType_DepthLimit", { sourceId: source.id, targetId: target.id, numCombinations }); + tracing.instant(tracing.Phase.CheckTypes, "typeRelatedToDiscriminatedType_DepthLimit", { sourceId: source.id, targetId: target.id, numCombinations }); return Ternary.False; } } @@ -18240,12 +18694,12 @@ namespace ts { if (type.flags & TypeFlags.Instantiable) { const constraint = getConstraintOfType(type); - if (constraint) { + if (constraint && constraint !== type) { return typeCouldHaveTopLevelSingletonTypes(constraint); } } - return isUnitType(type); + return isUnitType(type) || !!(type.flags & TypeFlags.TemplateLiteral); } function getBestMatchingType(source: Type, target: UnionOrIntersectionType, isRelatedTo = compareTypesAssignable) { @@ -18280,8 +18734,18 @@ namespace ts { } } const match = discriminable.indexOf(/*searchElement*/ true); + if (match === -1) { + return defaultValue; + } // make sure exactly 1 matches before returning it - return match === -1 || discriminable.indexOf(/*searchElement*/ true, match + 1) !== -1 ? defaultValue : target.types[match]; + let nextMatch = discriminable.indexOf(/*searchElement*/ true, match + 1); + while (nextMatch !== -1) { + if (!isTypeIdenticalTo(target.types[match], target.types[nextMatch])) { + return defaultValue; + } + nextMatch = discriminable.indexOf(/*searchElement*/ true, nextMatch + 1); + } + return target.types[match]; } /** @@ -18336,7 +18800,7 @@ namespace ts { function getVariancesWorker(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] { let variances = cache.variances; if (!variances) { - tracing.begin(tracing.Phase.Check, "getVariancesWorker", { arity: typeParameters.length, id: (cache as any).id ?? (cache as any).declaredType?.id ?? -1 }); + tracing.push(tracing.Phase.CheckTypes, "getVariancesWorker", { arity: typeParameters.length, id: (cache as any).id ?? (cache as any).declaredType?.id ?? -1 }); // The emptyArray singleton is used to signal a recursive invocation. cache.variances = emptyArray; variances = []; @@ -18371,7 +18835,7 @@ namespace ts { variances.push(variance); } cache.variances = variances; - tracing.end(); + tracing.pop(); } return variances; } @@ -18788,7 +19252,7 @@ namespace ts { function getBaseTypeOfLiteralType(type: Type): Type { return type.flags & TypeFlags.EnumLiteral ? getBaseTypeOfEnumLiteralType(type) : - type.flags & TypeFlags.StringLiteral ? stringType : + type.flags & TypeFlags.StringLikeLiteral ? stringType : type.flags & TypeFlags.NumberLiteral ? numberType : type.flags & TypeFlags.BigIntLiteral ? bigintType : type.flags & TypeFlags.BooleanLiteral ? booleanType : @@ -18798,7 +19262,7 @@ namespace ts { function getWidenedLiteralType(type: Type): Type { return type.flags & TypeFlags.EnumLiteral && isFreshLiteralType(type) ? getBaseTypeOfEnumLiteralType(type) : - type.flags & TypeFlags.StringLiteral && isFreshLiteralType(type) ? stringType : + type.flags & TypeFlags.StringLikeLiteral && isFreshLiteralType(type) ? stringType : type.flags & TypeFlags.NumberLiteral && isFreshLiteralType(type) ? numberType : type.flags & TypeFlags.BigIntLiteral && isFreshLiteralType(type) ? bigintType : type.flags & TypeFlags.BooleanLiteral && isFreshLiteralType(type) ? booleanType : @@ -19516,7 +19980,8 @@ namespace ts { // arrow function, but is considered partially inferable because property 'a' has an inferable type. function isPartiallyInferableType(type: Type): boolean { return !(getObjectFlags(type) & ObjectFlags.NonInferrableType) || - isObjectLiteralType(type) && some(getPropertiesOfType(type), prop => isPartiallyInferableType(getTypeOfSymbol(prop))); + isObjectLiteralType(type) && some(getPropertiesOfType(type), prop => isPartiallyInferableType(getTypeOfSymbol(prop))) || + isTupleType(type) && some(getTypeArguments(type), isPartiallyInferableType); } function createReverseMappedType(source: Type, target: MappedType, constraint: IndexType) { @@ -19598,7 +20063,7 @@ namespace ts { // Two object types that each have a property that is unmatched in the other are definitely unrelated. return isTupleType(source) && isTupleType(target) ? tupleTypesDefinitelyUnrelated(source, target) : !!getUnmatchedProperty(source, target, /*requireOptionalProperties*/ false, /*matchDiscriminantProperties*/ true) && - !!getUnmatchedProperty(target, source, /*requireOptionalProperties*/ false, /*matchDiscriminantProperties*/ true); + !!getUnmatchedProperty(target, source, /*requireOptionalProperties*/ false, /*matchDiscriminantProperties*/ false); } function getTypeFromInference(inference: InferenceInfo) { @@ -19615,6 +20080,63 @@ namespace ts { return !!(type.symbol && some(type.symbol.declarations, hasSkipDirectInferenceFlag)); } + function isValidBigIntString(s: string): boolean { + const scanner = createScanner(ScriptTarget.ESNext, /*skipTrivia*/ false); + let success = true; + scanner.setOnError(() => success = false); + scanner.setText(s + "n"); + let result = scanner.scan(); + if (result === SyntaxKind.MinusToken) { + result = scanner.scan(); + } + const flags = scanner.getTokenFlags(); + // validate that + // * scanning proceeded without error + // * a bigint can be scanned, and that when it is scanned, it is + // * the full length of the input string (so the scanner is one character beyond the augmented input length) + // * it does not contain a numeric seperator (the `BigInt` constructor does not accept a numeric seperator in its input) + return success && result === SyntaxKind.BigIntLiteral && scanner.getTextPos() === (s.length + 1) && !(flags & TokenFlags.ContainsSeparator); + } + + function isStringLiteralTypeValueParsableAsType(s: StringLiteralType, target: Type): boolean { + if (target.flags & TypeFlags.Union) { + return !!forEachType(target, t => isStringLiteralTypeValueParsableAsType(s, t)); + } + switch (target) { + case stringType: return true; + case numberType: return s.value !== "" && isFinite(+(s.value)); + case bigintType: return s.value !== "" && isValidBigIntString(s.value); + // the next 4 should be handled in `getTemplateLiteralType`, as they are all exactly one value, but are here for completeness, just in case + // this function is ever used on types which don't come from template literal holes + case trueType: return s.value === "true"; + case falseType: return s.value === "false"; + case undefinedType: return s.value === "undefined"; + case nullType: return s.value === "null"; + default: return !!(target.flags & TypeFlags.Any); + } + } + + function inferLiteralsFromTemplateLiteralType(source: StringLiteralType, target: TemplateLiteralType): StringLiteralType[] | undefined { + const value = source.value; + const texts = target.texts; + const lastIndex = texts.length - 1; + const startText = texts[0]; + const endText = texts[lastIndex]; + if (!(value.startsWith(startText) && value.slice(startText.length).endsWith(endText))) return undefined; + const matches = []; + const str = value.slice(startText.length, value.length - endText.length); + let pos = 0; + for (let i = 1; i < lastIndex; i++) { + const delim = texts[i]; + const delimPos = delim.length > 0 ? str.indexOf(delim, pos) : pos < str.length ? pos + 1 : -1; + if (delimPos < 0) return undefined; + matches.push(getLiteralType(str.slice(pos, delimPos))); + pos = delimPos + delim.length; + } + matches.push(getLiteralType(str.slice(pos))); + return matches; + } + function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) { let bivariant = false; let propagationType: Type; @@ -19783,6 +20305,11 @@ namespace ts { inferFromTypes((source).objectType, (target).objectType); inferFromTypes((source).indexType, (target).indexType); } + else if (source.flags & TypeFlags.StringMapping && target.flags & TypeFlags.StringMapping) { + if ((source).symbol === (target).symbol) { + inferFromTypes((source).type, (target).type); + } + } else if (target.flags & TypeFlags.Conditional) { invokeOnce(source, target, inferToConditionalType); } @@ -19823,12 +20350,6 @@ namespace ts { invokeOnce(source, target, inferFromObjectTypes); } } - if (source.flags & TypeFlags.Simplifiable) { - const simplified = getSimplifiedType(source, contravariant); - if (simplified !== source) { - inferFromTypes(simplified, target); - } - } } function inferWithPriority(source: Type, target: Type, newPriority: InferencePriority) { @@ -20079,7 +20600,7 @@ namespace ts { function inferToTemplateLiteralType(source: Type, target: TemplateLiteralType) { const matches = source.flags & TypeFlags.StringLiteral ? inferLiteralsFromTemplateLiteralType(source, target) : - source.flags & TypeFlags.TemplateLiteral && arraysEqual((source).texts, target.texts) && arraysEqual((source).casings, target.casings)? (source).types : + source.flags & TypeFlags.TemplateLiteral && arraysEqual((source).texts, target.texts) ? (source).types : undefined; const types = target.types; for (let i = 0; i < types.length; i++) { @@ -20087,27 +20608,6 @@ namespace ts { } } - function inferLiteralsFromTemplateLiteralType(source: StringLiteralType, target: TemplateLiteralType): Type[] | undefined { - const value = source.value; - const texts = target.texts; - const lastIndex = texts.length - 1; - const startText = texts[0]; - const endText = texts[lastIndex]; - if (!(value.startsWith(startText) && value.endsWith(endText))) return undefined; - const matches = []; - const str = value.slice(startText.length, value.length - endText.length); - let pos = 0; - for (let i = 1; i < lastIndex; i++) { - const delim = texts[i]; - const delimPos = delim.length > 0 ? str.indexOf(delim, pos) : pos < str.length ? pos + 1 : -1; - if (delimPos < 0) return undefined; - matches.push(getLiteralType(str.slice(pos, delimPos))); - pos = delimPos + delim.length; - } - matches.push(getLiteralType(str.slice(pos))); - return matches; - } - function inferFromObjectTypes(source: Type, target: Type) { if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( (source).target === (target).target || isArrayType(source) && isArrayType(target))) { @@ -20263,7 +20763,7 @@ namespace ts { } function isTypeOrBaseIdenticalTo(s: Type, t: Type) { - return isTypeIdenticalTo(s, t) || !!(t.flags & TypeFlags.String && s.flags & TypeFlags.StringLiteral || t.flags & TypeFlags.Number && s.flags & TypeFlags.NumberLiteral); + return isTypeIdenticalTo(s, t) || !!(t.flags & TypeFlags.String && s.flags & TypeFlags.StringLikeLiteral || t.flags & TypeFlags.Number && s.flags & TypeFlags.NumberLiteral); } function isTypeCloselyMatchedBy(s: Type, t: Type) { @@ -20273,7 +20773,7 @@ namespace ts { function hasPrimitiveConstraint(type: TypeParameter): boolean { const constraint = getConstraintOfTypeParameter(type); - return !!constraint && maybeTypeOfKind(constraint.flags & TypeFlags.Conditional ? getDefaultConstraintOfConditionalType(constraint as ConditionalType) : constraint, TypeFlags.Primitive | TypeFlags.Index | TypeFlags.TemplateLiteral); + return !!constraint && maybeTypeOfKind(constraint.flags & TypeFlags.Conditional ? getDefaultConstraintOfConditionalType(constraint as ConditionalType) : constraint, TypeFlags.Primitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping); } function isObjectLiteralType(type: Type) { @@ -20395,22 +20895,22 @@ namespace ts { return Diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_include_dom; case "$": return compilerOptions.types - ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_types_Slashjquery_and_then_add_jquery_to_the_types_field_in_your_tsconfig - : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_types_Slashjquery; + ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slashjquery_and_then_add_jquery_to_the_types_field_in_your_tsconfig + : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slashjquery; case "describe": case "suite": case "it": case "test": return compilerOptions.types - ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_types_Slashjest_or_npm_i_types_Slashmocha_and_then_add_jest_or_mocha_to_the_types_field_in_your_tsconfig - : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_types_Slashjest_or_npm_i_types_Slashmocha; + ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_save_dev_types_Slashjest_or_npm_i_save_dev_types_Slashmocha_and_then_add_jest_or_mocha_to_the_types_field_in_your_tsconfig + : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_save_dev_types_Slashjest_or_npm_i_save_dev_types_Slashmocha; case "process": case "require": case "Buffer": case "module": return compilerOptions.types - ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_types_Slashnode_and_then_add_node_to_the_types_field_in_your_tsconfig - : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_types_Slashnode; + ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashnode_and_then_add_node_to_the_types_field_in_your_tsconfig + : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashnode; case "Map": case "Set": case "Promise": @@ -20419,7 +20919,17 @@ namespace ts { case "WeakSet": case "Iterator": case "AsyncIterator": - return Diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later; + case "SharedArrayBuffer": + case "Atomics": + case "AsyncIterable": + case "AsyncIterableIterator": + case "AsyncGenerator": + case "AsyncGeneratorFunction": + case "BigInt": + case "Reflect": + case "BigInt64Array": + case "BigUint64Array": + return Diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_1_or_later; default: if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { return Diagnostics.No_value_exists_in_scope_for_the_shorthand_property_0_Either_declare_one_or_provide_an_initializer; @@ -20489,7 +20999,8 @@ namespace ts { case SyntaxKind.NonNullExpression: return isMatchingReference(source, (target as NonNullExpression | ParenthesizedExpression).expression); case SyntaxKind.BinaryExpression: - return isAssignmentExpression(target) && isMatchingReference(source, target.left); + return (isAssignmentExpression(target) && isMatchingReference(source, target.left)) || + (isBinaryExpression(target) && target.operatorToken.kind === SyntaxKind.CommaToken && isMatchingReference(source, target.right)); } switch (source.kind) { case SyntaxKind.Identifier: @@ -20509,6 +21020,8 @@ namespace ts { return isAccessExpression(target) && getAccessedPropertyName(source) === getAccessedPropertyName(target) && isMatchingReference((source).expression, target.expression); + case SyntaxKind.BinaryExpression: + return (isBinaryExpression(source) && source.operatorToken.kind === SyntaxKind.CommaToken && isMatchingReference(source.right, target)); } return false; } @@ -20579,16 +21092,16 @@ namespace ts { return isMatchingReference(source, target) || containsMatchingReference(source, target); } - function hasMatchingArgument(callExpression: CallExpression, reference: Node) { - if (callExpression.arguments) { - for (const argument of callExpression.arguments) { + function hasMatchingArgument(expression: CallExpression | NewExpression, reference: Node) { + if (expression.arguments) { + for (const argument of expression.arguments) { if (isOrContainsMatchingReference(reference, argument)) { return true; } } } - if (callExpression.expression.kind === SyntaxKind.PropertyAccessExpression && - isOrContainsMatchingReference(reference, (callExpression.expression).expression)) { + if (expression.expression.kind === SyntaxKind.PropertyAccessExpression && + isOrContainsMatchingReference(reference, (expression.expression).expression)) { return true; } return false; @@ -20713,7 +21226,8 @@ namespace ts { return TypeFacts.None; } if (flags & TypeFlags.Instantiable) { - return getTypeFacts(getBaseConstraintOfType(type) || unknownType); + return !isPatternLiteralType(type) ? getTypeFacts(getBaseConstraintOfType(type) || unknownType) : + strictNullChecks ? TypeFacts.NonEmptyStringStrictFacts : TypeFacts.NonEmptyStringFacts; } if (flags & TypeFlags.UnionOrIntersection) { return getTypeFactsOfTypes((type).types); @@ -20751,7 +21265,7 @@ namespace ts { function includeUndefinedInIndexSignature(type: Type | undefined): Type | undefined { if (!type) return type; - return compilerOptions.noUncheckedIndexSignatures ? + return compilerOptions.noUncheckedIndexedAccess ? getUnionType([type, undefinedType]) : type; } @@ -21119,6 +21633,12 @@ namespace ts { return getTypeOfSymbol(symbol); } if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { + if (getCheckFlags(symbol) & CheckFlags.Mapped) { + const origin = (symbol).syntheticOrigin; + if (origin && getExplicitTypeOfSymbol(origin)) { + return getTypeOfSymbol(symbol); + } + } const declaration = symbol.valueDeclaration; if (declaration) { if (isDeclarationWithExplicitTypeAnnotation(declaration)) { @@ -21375,12 +21895,13 @@ namespace ts { if (flowDepth === 2000) { // We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error // and disable further control flow analysis in the containing function or module body. - tracing.instant(tracing.Phase.Check, "getTypeAtFlowNode_DepthLimit", { flowId: flow.id }); + tracing.instant(tracing.Phase.CheckTypes, "getTypeAtFlowNode_DepthLimit", { flowId: flow.id }); flowAnalysisDisabled = true; reportFlowControlError(reference); return errorType; } flowDepth++; + let sharedFlow: FlowNode | undefined; while (true) { const flags = flow.flags; if (flags & FlowFlags.Shared) { @@ -21393,6 +21914,7 @@ namespace ts { return sharedFlowTypes[i]; } } + sharedFlow = flow; } let type: FlowType | undefined; if (flags & FlowFlags.Assignment) { @@ -21456,9 +21978,9 @@ namespace ts { // simply return the non-auto declared type to reduce follow-on errors. type = convertAutoToAny(declaredType); } - if (flags & FlowFlags.Shared) { + if (sharedFlow) { // Record visited node and the associated type in the cache. - sharedFlowNodes[sharedFlowCount] = flow; + sharedFlowNodes[sharedFlowCount] = sharedFlow; sharedFlowTypes[sharedFlowCount] = type; sharedFlowCount++; } @@ -22306,7 +22828,8 @@ namespace ts { case SyntaxKind.CallExpression: return narrowTypeByCallExpression(type, expr, assumeTrue); case SyntaxKind.ParenthesizedExpression: - return narrowType(type, (expr).expression, assumeTrue); + case SyntaxKind.NonNullExpression: + return narrowType(type, (expr).expression, assumeTrue); case SyntaxKind.BinaryExpression: return narrowTypeByBinaryExpression(type, expr, assumeTrue); case SyntaxKind.PrefixUnaryExpression: @@ -22447,11 +22970,14 @@ namespace ts { function markAliasReferenced(symbol: Symbol, location: Node) { if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !getTypeOnlyAliasDeclaration(symbol)) { - if (compilerOptions.preserveConstEnums && isExportOrExportExpression(location) || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) { - markAliasSymbolAsReferenced(symbol); - } - else { - markConstEnumAliasAsReferenced(symbol); + const target = resolveAlias(symbol); + if (target.flags & SymbolFlags.Value) { + if (compilerOptions.preserveConstEnums && isExportOrExportExpression(location) || !isConstEnumOrConstEnumOnlyModule(target)) { + markAliasSymbolAsReferenced(symbol); + } + else { + markConstEnumAliasAsReferenced(symbol); + } } } } @@ -22490,11 +23016,12 @@ namespace ts { } const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol); - let declaration: Declaration | undefined = localOrExportSymbol.valueDeclaration; - - if (declaration && getCombinedNodeFlags(declaration) & NodeFlags.Deprecated && isUncalledFunctionReference(node.parent, localOrExportSymbol)) { - errorOrSuggestion(/* isError */ false, node, Diagnostics._0_is_deprecated, node.escapedText as string);; + const sourceSymbol = localOrExportSymbol.flags & SymbolFlags.Alias ? resolveAlias(localOrExportSymbol) : localOrExportSymbol; + if (getDeclarationNodeFlagsFromSymbol(sourceSymbol) & NodeFlags.Deprecated && isUncalledFunctionReference(node, sourceSymbol)) { + errorOrSuggestion(/* isError */ false, node, Diagnostics._0_is_deprecated, node.escapedText as string); } + + let declaration: Declaration | undefined = localOrExportSymbol.valueDeclaration; if (localOrExportSymbol.flags & SymbolFlags.Class) { // Due to the emit for class decorators, any reference to the class from inside of the class body // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind @@ -23667,7 +24194,10 @@ namespace ts { function getContextualTypeForElementExpression(arrayContextualType: Type | undefined, index: number): Type | undefined { return arrayContextualType && ( getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String) - || getIteratedTypeOrElementType(IterationUse.Element, arrayContextualType, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false)); + || mapType( + arrayContextualType, + t => getIteratedTypeOrElementType(IterationUse.Element, t, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false), + /*noReductions*/ true)); } // In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type. @@ -23678,7 +24208,7 @@ namespace ts { function getContextualTypeForChildJsxExpression(node: JsxElement, child: JsxChild) { const attributesType = getApparentTypeOfContextualType(node.openingElement.tagName); - // JSX expression is in children of JSX Element, we will look for an "children" atttribute (we get the name from JSX.ElementAttributesProperty) + // JSX expression is in children of JSX Element, we will look for an "children" attribute (we get the name from JSX.ElementAttributesProperty) const jsxChildrenPropertyName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node)); if (!(attributesType && !isTypeAny(attributesType) && jsxChildrenPropertyName && jsxChildrenPropertyName !== "")) { return undefined; @@ -23911,10 +24441,7 @@ namespace ts { return undefined; function tryFindWhenConstTypeReference(node: Expression) { - if(isCallLikeExpression(node.parent)){ - return getContextualTypeForArgument(node.parent, node); - } - return undefined; + return getContextualType(node); } } @@ -24587,11 +25114,7 @@ namespace ts { return getJsxElementTypeAt(node) || anyType; } - /** - * Returns true iff the JSX element name would be a valid JS identifier, ignoring restrictions about keywords not being identifiers - */ function isUnhyphenatedJsxName(name: string | __String) { - // - is the only character supported in JSX attribute names that isn't valid in JavaScript identifiers return !stringContains(name as string, "-"); } @@ -24634,7 +25157,7 @@ namespace ts { const exprType = checkJsxAttribute(attributeDecl, checkMode); objectFlags |= getObjectFlags(exprType) & ObjectFlags.PropagatingFlags; - const attributeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.escapedName); + const attributeSymbol = createSymbol(SymbolFlags.Property | member.flags, member.escapedName); attributeSymbol.declarations = member.declarations; attributeSymbol.parent = member.parent; if (member.valueDeclaration) { @@ -24693,7 +25216,7 @@ namespace ts { const contextualType = getApparentTypeOfContextualType(openingLikeElement.attributes); const childrenContextualType = contextualType && getTypeOfPropertyOfContextualType(contextualType, jsxChildrenPropertyName); // If there are children in the body of JSX element, create dummy attribute "children" with the union of children types so that it will pass the attribute checking process - const childrenPropSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, jsxChildrenPropertyName); + const childrenPropSymbol = createSymbol(SymbolFlags.Property, jsxChildrenPropertyName); childrenPropSymbol.type = childrenTypes.length === 1 ? childrenTypes[0] : childrenContextualType && forEachType(childrenContextualType, isTupleLikeType) ? createTupleType(childrenTypes) : createArrayType(getUnionType(childrenTypes)); @@ -24740,6 +25263,9 @@ namespace ts { childrenTypes.push(stringType); } } + else if (child.kind === SyntaxKind.JsxExpression && !child.expression) { + continue; // empty jsx expressions don't *really* count as present children + } else { childrenTypes.push(checkExpressionForMutableLocation(child, checkMode)); } @@ -24814,29 +25340,63 @@ namespace ts { return links.resolvedSymbol; } + function getJsxNamespaceContainerForImplicitImport(location: Node | undefined): Symbol | undefined { + const file = location && getSourceFileOfNode(location); + const links = file && getNodeLinks(file); + if (links && links.jsxImplicitImportContainer === false) { + return undefined; + } + if (links && links.jsxImplicitImportContainer) { + return links.jsxImplicitImportContainer; + } + const runtimeImportSpecifier = getJSXRuntimeImport(getJSXImplicitImportBase(compilerOptions, file), compilerOptions); + if (!runtimeImportSpecifier) { + return undefined; + } + const isClassic = getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Classic; + const errorMessage = isClassic + ? Diagnostics.Cannot_find_module_0_Did_you_mean_to_set_the_moduleResolution_option_to_node_or_to_add_aliases_to_the_paths_option + : Diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations; + const mod = resolveExternalModule(location!, runtimeImportSpecifier, errorMessage, location!); + const result = mod && mod !== unknownSymbol ? getMergedSymbol(resolveSymbol(mod)) : undefined; + if (links) { + links.jsxImplicitImportContainer = result || false; + } + return result; + } + function getJsxNamespaceAt(location: Node | undefined): Symbol { const links = location && getNodeLinks(location); if (links && links.jsxNamespace) { return links.jsxNamespace; } if (!links || links.jsxNamespace !== false) { - const namespaceName = getJsxNamespace(location); - const resolvedNamespace = resolveName(location, namespaceName, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined, namespaceName, /*isUse*/ false); + let resolvedNamespace = getJsxNamespaceContainerForImplicitImport(location); + + if (!resolvedNamespace || resolvedNamespace === unknownSymbol) { + const namespaceName = getJsxNamespace(location); + resolvedNamespace = resolveName(location, namespaceName, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined, namespaceName, /*isUse*/ false); + } + if (resolvedNamespace) { const candidate = resolveSymbol(getSymbol(getExportsOfSymbol(resolveSymbol(resolvedNamespace)), JsxNames.JSX, SymbolFlags.Namespace)); - if (candidate) { + if (candidate && candidate !== unknownSymbol) { if (links) { links.jsxNamespace = candidate; } return candidate; } - if (links) { - links.jsxNamespace = false; - } + } + if (links) { + links.jsxNamespace = false; } } // JSX global fallback - return getGlobalSymbol(JsxNames.JSX, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined)!; // TODO: GH#18217 + const s = resolveSymbol(getGlobalSymbol(JsxNames.JSX, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined)); + if (s === unknownSymbol) { + return undefined!; // TODO: GH#18217 + } + return s!; // TODO: GH#18217 } /** @@ -25040,26 +25600,29 @@ namespace ts { } checkJsxPreconditions(node); - // The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import. - // And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error. - const jsxFactoryRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined; - const jsxFactoryNamespace = getJsxNamespace(node); - const jsxFactoryLocation = isNodeOpeningLikeElement ? (node).tagName : node; - // allow null as jsxFragmentFactory - let jsxFactorySym: Symbol | undefined; - if (!(isJsxOpeningFragment(node) && jsxFactoryNamespace === "null")) { - jsxFactorySym = resolveName(jsxFactoryLocation, jsxFactoryNamespace, SymbolFlags.Value, jsxFactoryRefErr, jsxFactoryNamespace, /*isUse*/ true); - } + if (!getJsxNamespaceContainerForImplicitImport(node)) { + // The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import. + // And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error. + const jsxFactoryRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined; + const jsxFactoryNamespace = getJsxNamespace(node); + const jsxFactoryLocation = isNodeOpeningLikeElement ? (node).tagName : node; - if (jsxFactorySym) { - // Mark local symbol as referenced here because it might not have been marked - // if jsx emit was not jsxFactory as there wont be error being emitted - jsxFactorySym.isReferenced = SymbolFlags.All; + // allow null as jsxFragmentFactory + let jsxFactorySym: Symbol | undefined; + if (!(isJsxOpeningFragment(node) && jsxFactoryNamespace === "null")) { + jsxFactorySym = resolveName(jsxFactoryLocation, jsxFactoryNamespace, SymbolFlags.Value, jsxFactoryRefErr, jsxFactoryNamespace, /*isUse*/ true); + } + + if (jsxFactorySym) { + // Mark local symbol as referenced here because it might not have been marked + // if jsx emit was not jsxFactory as there wont be error being emitted + jsxFactorySym.isReferenced = SymbolFlags.All; - // If react/jsxFactory symbol is alias, mark it as refereced - if (jsxFactorySym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(jsxFactorySym)) { - markAliasSymbolAsReferenced(jsxFactorySym); + // If react/jsxFactory symbol is alias, mark it as refereced + if (jsxFactorySym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(jsxFactorySym)) { + markAliasSymbolAsReferenced(jsxFactorySym); + } } } @@ -25494,13 +26057,15 @@ namespace ts { error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType)); } - propType = (compilerOptions.noUncheckedIndexSignatures && !isAssignmentTarget(node)) ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type; + propType = (compilerOptions.noUncheckedIndexedAccess && !isAssignmentTarget(node)) ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type; + if (compilerOptions.noPropertyAccessFromIndexSignature && isPropertyAccessExpression(node)) { + error(right, Diagnostics.Property_0_comes_from_an_index_signature_so_it_must_be_accessed_with_0, unescapeLeadingUnderscores(right.escapedText)); + } } else { - if (prop.valueDeclaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) { + if (getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) { errorOrSuggestion(/* isError */ false, right, Diagnostics._0_is_deprecated, right.escapedText as string); } - checkPropertyNotUsedBeforeDeclaration(prop, node, right); markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword); getNodeLinks(node).resolvedSymbol = prop; @@ -25651,7 +26216,9 @@ namespace ts { } } if (typeHasStaticProperty(propNode.escapedText, containingType)) { - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_is_a_static_member_of_type_1, declarationNameToString(propNode), typeToString(containingType)); + const propName = declarationNameToString(propNode); + const typeName = typeToString(containingType); + errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_to_access_the_static_member_2_instead, propName, typeName, typeName + "." + propName); } else { const promisedType = getPromisedTypeOfPromise(containingType); @@ -25660,14 +26227,22 @@ namespace ts { relatedInfo = createDiagnosticForNode(propNode, Diagnostics.Did_you_forget_to_use_await); } else { - const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType); - if (suggestion !== undefined) { - const suggestedName = symbolName(suggestion); - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestedName); - relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName); + const missingProperty = declarationNameToString(propNode); + const container = typeToString(containingType); + const libSuggestion = getSuggestedLibForNonExistentProperty(missingProperty, containingType); + if (libSuggestion !== undefined) { + errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_2_or_later, missingProperty, container, libSuggestion); } else { - errorInfo = chainDiagnosticMessages(elaborateNeverIntersection(errorInfo, containingType), Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType)); + const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType); + if (suggestion !== undefined) { + const suggestedName = symbolName(suggestion); + errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, missingProperty, container, suggestedName); + relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName); + } + else { + errorInfo = chainDiagnosticMessages(elaborateNeverIntersection(errorInfo, containingType), Diagnostics.Property_0_does_not_exist_on_type_1, missingProperty, container); + } } } } @@ -25683,6 +26258,34 @@ namespace ts { return prop !== undefined && prop.valueDeclaration && hasSyntacticModifier(prop.valueDeclaration, ModifierFlags.Static); } + function getSuggestedLibForNonExistentName(name: __String | Identifier) { + const missingName = diagnosticName(name); + const allFeatures = getScriptTargetFeatures(); + const libTargets = getOwnKeys(allFeatures); + for (const libTarget of libTargets) { + const containingTypes = getOwnKeys(allFeatures[libTarget]); + if (containingTypes !== undefined && contains(containingTypes, missingName)) { + return libTarget; + } + } + } + + function getSuggestedLibForNonExistentProperty(missingProperty: string, containingType: Type) { + const container = getApparentType(containingType).symbol; + if (!container) { + return undefined; + } + const allFeatures = getScriptTargetFeatures(); + const libTargets = getOwnKeys(allFeatures); + for (const libTarget of libTargets) { + const featuresOfLib = allFeatures[libTarget]; + const featuresOfContainingType = featuresOfLib[symbolName(container)]; + if (featuresOfContainingType !== undefined && contains(featuresOfContainingType, missingProperty)) { + return libTarget; + } + } + } + function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value); } @@ -25772,6 +26375,7 @@ namespace ts { */ function getSpellingSuggestionForName(name: string, symbols: Symbol[], meaning: SymbolFlags): Symbol | undefined { return getSpellingSuggestion(name, symbols, getCandidateName); + function getCandidateName(candidate: Symbol) { const candidateName = symbolName(candidate); if (startsWith(candidateName, "\"")) { @@ -25934,7 +26538,7 @@ namespace ts { const accessFlags = isAssignmentTarget(node) ? AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) : AccessFlags.None; - const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, node, accessFlags | AccessFlags.ExpressionPosition) || errorType; + const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, /*noUncheckedIndexedAccessCandidate*/ undefined, node, accessFlags | AccessFlags.ExpressionPosition) || errorType; return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node); } @@ -26326,7 +26930,7 @@ namespace ts { else { const contextualType = getIndexedAccessType(restType, getLiteralType(i - index)); const argType = checkExpressionWithContextualType(arg, contextualType, context, checkMode); - const hasPrimitiveContextualType = maybeTypeOfKind(contextualType, TypeFlags.Primitive | TypeFlags.Index | TypeFlags.TemplateLiteral); + const hasPrimitiveContextualType = maybeTypeOfKind(contextualType, TypeFlags.Primitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping); types.push(hasPrimitiveContextualType ? getRegularTypeOfLiteralType(argType) : getWidenedLiteralType(argType)); flags.push(ElementFlags.Required); } @@ -26410,6 +27014,9 @@ namespace ts { errorOutputContainer); function checkTagNameDoesNotExpectTooManyArguments(): boolean { + if (getJsxNamespaceContainerForImplicitImport(node)) { + return true; // factory is implicitly jsx/jsxdev - assume it fits the bill, since we don't strongly look for the jsx/jsxs/jsxDEV factory APIs anywhere else (at least not yet) + } const tagType = isJsxOpeningElement(node) || isJsxSelfClosingElement(node) && !isJsxIntrinsicIdentifier(node.tagName) ? checkExpression(node.tagName) : undefined; if (!tagType) { return true; @@ -26726,6 +27333,22 @@ namespace ts { } } + function isPromiseResolveArityError(node: CallLikeExpression) { + if (!isCallExpression(node) || !isIdentifier(node.expression)) return false; + + const symbol = resolveName(node.expression, node.expression.escapedText, SymbolFlags.Value, undefined, undefined, false); + const decl = symbol?.valueDeclaration; + if (!decl || !isParameter(decl) || !isFunctionExpressionOrArrowFunction(decl.parent) || !isNewExpression(decl.parent.parent) || !isIdentifier(decl.parent.parent.expression)) { + return false; + } + + const globalPromiseSymbol = getGlobalPromiseConstructorSymbol(/*reportErrors*/ false); + if (!globalPromiseSymbol) return false; + + const constructorSymbol = getSymbolAtLocation(decl.parent.parent.expression, /*ignoreErrors*/ true); + return constructorSymbol === globalPromiseSymbol; + } + function getArgumentArityError(node: CallLikeExpression, signatures: readonly Signature[], args: readonly Expression[]) { let min = Number.POSITIVE_INFINITY; let max = Number.NEGATIVE_INFINITY; @@ -26758,9 +27381,15 @@ namespace ts { let spanArray: NodeArray; let related: DiagnosticWithLocation | undefined; - const error = hasRestParameter || hasSpreadArgument ? hasRestParameter && hasSpreadArgument ? Diagnostics.Expected_at_least_0_arguments_but_got_1_or_more : - hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1 : - Diagnostics.Expected_0_arguments_but_got_1_or_more : Diagnostics.Expected_0_arguments_but_got_1; + const error = hasRestParameter || hasSpreadArgument ? + hasRestParameter && hasSpreadArgument ? + Diagnostics.Expected_at_least_0_arguments_but_got_1_or_more : + hasRestParameter ? + Diagnostics.Expected_at_least_0_arguments_but_got_1 : + Diagnostics.Expected_0_arguments_but_got_1_or_more : + paramRange === 1 && argCount === 0 && isPromiseResolveArityError(node) ? + Diagnostics.Expected_0_arguments_but_got_1_Did_you_forget_to_include_void_in_your_type_argument_to_Promise : + Diagnostics.Expected_0_arguments_but_got_1; if (closestSignature && getMinArgumentCount(closestSignature) > argCount && closestSignature.declaration) { const paramDecl = closestSignature.declaration.parameters[closestSignature.thisParameter ? argCount + 1 : argCount]; @@ -26918,10 +27547,10 @@ namespace ts { // is just important for choosing the best signature. So in the case where there is only one // signature, the subtype pass is useless. So skipping it is an optimization. if (candidates.length > 1) { - result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma); + result = chooseOverload(candidates, subtypeRelation, isSingleNonGenericCandidate, signatureHelpTrailingComma); } if (!result) { - result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma); + result = chooseOverload(candidates, assignableRelation, isSingleNonGenericCandidate, signatureHelpTrailingComma); } if (result) { return result; @@ -26946,6 +27575,7 @@ namespace ts { if (last.declaration && candidatesForArgumentError.length > 3) { addRelatedInfo(d, createDiagnosticForNode(last.declaration, Diagnostics.The_last_overload_is_declared_here)); } + addImplementationSuccessElaboration(last, d); diagnostics.add(d); } } @@ -26981,14 +27611,19 @@ namespace ts { const chain = chainDiagnosticMessages( map(diags, d => typeof d.messageText === "string" ? (d as DiagnosticMessageChain) : d.messageText), Diagnostics.No_overload_matches_this_call); - const related = flatMap(diags, d => (d as Diagnostic).relatedInformation) as DiagnosticRelatedInformation[]; + // The below is a spread to guarantee we get a new (mutable) array - our `flatMap` helper tries to do "smart" optimizations where it reuses input + // arrays and the emptyArray singleton where possible, which is decidedly not what we want while we're still constructing this diagnostic + const related = [...flatMap(diags, d => (d as Diagnostic).relatedInformation) as DiagnosticRelatedInformation[]]; + let diag: Diagnostic; if (every(diags, d => d.start === diags[0].start && d.length === diags[0].length && d.file === diags[0].file)) { const { file, start, length } = diags[0]; - diagnostics.add({ file, start, length, code: chain.code, category: chain.category, messageText: chain, relatedInformation: related }); + diag = { file, start, length, code: chain.code, category: chain.category, messageText: chain, relatedInformation: related }; } else { - diagnostics.add(createDiagnosticForNodeFromMessageChain(node, chain, related)); + diag = createDiagnosticForNodeFromMessageChain(node, chain, related); } + addImplementationSuccessElaboration(candidatesForArgumentError[0], diag); + diagnostics.add(diag); } } else if (candidateForArgumentArityError) { @@ -27013,7 +27648,28 @@ namespace ts { return getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray); - function chooseOverload(candidates: Signature[], relation: ESMap, signatureHelpTrailingComma = false) { + function addImplementationSuccessElaboration(failed: Signature, diagnostic: Diagnostic) { + const oldCandidatesForArgumentError = candidatesForArgumentError; + const oldCandidateForArgumentArityError = candidateForArgumentArityError; + const oldCandidateForTypeArgumentError = candidateForTypeArgumentError; + + const failedSignatureDeclarations = failed.declaration?.symbol?.declarations || emptyArray; + const isOverload = failedSignatureDeclarations.length > 1; + const implDecl = isOverload ? find(failedSignatureDeclarations, d => isFunctionLikeDeclaration(d) && nodeIsPresent(d.body)) : undefined; + if (implDecl) { + const candidate = getSignatureFromDeclaration(implDecl as FunctionLikeDeclaration); + const isSingleNonGenericCandidate = !candidate.typeParameters; + if (chooseOverload([candidate], assignableRelation, isSingleNonGenericCandidate)) { + addRelatedInfo(diagnostic, createDiagnosticForNode(implDecl, Diagnostics.The_call_would_have_succeeded_against_this_implementation_but_implementation_signatures_of_overloads_are_not_externally_visible)); + } + } + + candidatesForArgumentError = oldCandidatesForArgumentError; + candidateForArgumentArityError = oldCandidateForArgumentArityError; + candidateForTypeArgumentError = oldCandidateForTypeArgumentError; + } + + function chooseOverload(candidates: Signature[], relation: ESMap, isSingleNonGenericCandidate: boolean, signatureHelpTrailingComma = false) { candidatesForArgumentError = undefined; candidateForArgumentArityError = undefined; candidateForTypeArgumentError = undefined; @@ -27635,6 +28291,12 @@ namespace ts { } if (!callSignatures.length) { + if (isArrayLiteralExpression(node.parent)) { + const diagnostic = createDiagnosticForNode(node.tag, Diagnostics.It_is_likely_that_you_are_missing_a_comma_to_separate_these_two_template_expressions_They_form_a_tagged_template_expression_which_cannot_be_invoked); + diagnostics.add(diagnostic); + return resolveErrorCall(node); + } + invocationError(node.tag, apparentType, SignatureKind.Call); return resolveErrorCall(node); } @@ -27864,15 +28526,59 @@ namespace ts { } function getAssignedClassSymbol(decl: Declaration): Symbol | undefined { - const assignmentSymbol = decl && decl.parent && - (isFunctionDeclaration(decl) && getSymbolOfNode(decl) || - isBinaryExpression(decl.parent) && getSymbolOfNode(decl.parent.left) || - isVariableDeclaration(decl.parent) && getSymbolOfNode(decl.parent)); - const prototype = assignmentSymbol && assignmentSymbol.exports && assignmentSymbol.exports.get("prototype" as __String); - const init = prototype && prototype.valueDeclaration && getAssignedJSPrototype(prototype.valueDeclaration); + const assignmentSymbol = decl && getSymbolOfExpando(decl, /*allowDeclaration*/ true); + const prototype = assignmentSymbol?.exports?.get("prototype" as __String); + const init = prototype?.valueDeclaration && getAssignedJSPrototype(prototype.valueDeclaration); return init ? getSymbolOfNode(init) : undefined; } + function getSymbolOfExpando(node: Node, allowDeclaration: boolean): Symbol | undefined { + if (!node.parent) { + return undefined; + } + let name: Expression | BindingName | undefined; + let decl: Node | undefined; + if (isVariableDeclaration(node.parent) && node.parent.initializer === node) { + if (!isInJSFile(node) && !(isVarConst(node.parent) && isFunctionLikeDeclaration(node))) { + return undefined; + } + name = node.parent.name; + decl = node.parent; + } + else if (isBinaryExpression(node.parent)) { + const parentNode = node.parent; + const parentNodeOperator = node.parent.operatorToken.kind; + if (parentNodeOperator === SyntaxKind.EqualsToken && (allowDeclaration || parentNode.right === node)) { + name = parentNode.left; + decl = name; + } + else if (parentNodeOperator === SyntaxKind.BarBarToken || parentNodeOperator === SyntaxKind.QuestionQuestionToken) { + if (isVariableDeclaration(parentNode.parent) && parentNode.parent.initializer === parentNode) { + name = parentNode.parent.name; + decl = parentNode.parent; + } + else if (isBinaryExpression(parentNode.parent) && parentNode.parent.operatorToken.kind === SyntaxKind.EqualsToken && (allowDeclaration || parentNode.parent.right === parentNode)) { + name = parentNode.parent.left; + decl = name; + } + + if (!name || !isBindableStaticNameExpression(name) || !isSameEntityName(name, parentNode.left)) { + return undefined; + } + } + } + else if (allowDeclaration && isFunctionDeclaration(node)) { + name = node.name; + decl = node; + } + + if (!decl || !name || (!allowDeclaration && !getExpandoInitializer(node, isPrototypeAccess(name)))) { + return undefined; + } + return getSymbolOfNode(decl); + } + + function getAssignedJSPrototype(node: Node) { if (!node.parent) { return false; @@ -27949,14 +28655,11 @@ namespace ts { } if (isInJSFile(node)) { - const decl = getDeclarationOfExpando(node); - if (decl) { - const jsSymbol = getSymbolOfNode(decl); - if (jsSymbol?.exports?.size) { - const jsAssignmentType = createAnonymousType(jsSymbol, jsSymbol.exports, emptyArray, emptyArray, undefined, undefined); - jsAssignmentType.objectFlags |= ObjectFlags.JSLiteral; - return getIntersectionType([returnType, jsAssignmentType]); - } + const jsSymbol = getSymbolOfExpando(node, /*allowDeclaration*/ false); + if (jsSymbol?.exports?.size) { + const jsAssignmentType = createAnonymousType(jsSymbol, jsSymbol.exports, emptyArray, emptyArray, undefined, undefined); + jsAssignmentType.objectFlags |= ObjectFlags.JSLiteral; + return getIntersectionType([returnType, jsAssignmentType]); } } @@ -28051,6 +28754,7 @@ namespace ts { if (hasSyntheticDefault) { const memberTable = createSymbolTable(); const newSymbol = createSymbol(SymbolFlags.Alias, InternalSymbolName.Default); + newSymbol.parent = originalSymbol; newSymbol.nameType = getLiteralType("default"); newSymbol.target = resolveSymbol(symbol); memberTable.set(InternalSymbolName.Default, newSymbol); @@ -28121,6 +28825,7 @@ namespace ts { case SyntaxKind.FalseKeyword: case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.TemplateExpression: return true; case SyntaxKind.ParenthesizedExpression: return isValidConstAssertionArgument((node).expression); @@ -28206,8 +28911,8 @@ namespace ts { } function checkImportMetaProperty(node: MetaProperty) { - if (moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System) { - error(node, Diagnostics.The_import_meta_meta_property_is_only_allowed_when_the_module_option_is_esnext_or_system); + if (moduleKind !== ModuleKind.ES2020 && moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System) { + error(node, Diagnostics.The_import_meta_meta_property_is_only_allowed_when_the_module_option_is_es2020_esnext_or_system); } const file = getSourceFileOfNode(node); Debug.assert(!!(file.flags & NodeFlags.PossiblyContainsImportMeta), "Containing file is missing import meta node flag."); @@ -29369,7 +30074,7 @@ namespace ts { // and the right operand to be of type Any, an object type, or a type parameter type. // The result is always of the Boolean primitive type. if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) || - isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.TypeParameter))) { + isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) { error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); } if (!allTypesAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive)) { @@ -29404,7 +30109,7 @@ namespace ts { checkPropertyAccessibility(property, /*isSuper*/ false, objectLiteralType, prop); } } - const elementType = getIndexedAccessType(objectLiteralType, exprType, name, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, AccessFlags.ExpressionPosition); + const elementType = getIndexedAccessType(objectLiteralType, exprType, /*noUncheckedIndexedAccessCandidate*/ undefined, name, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, AccessFlags.ExpressionPosition); const type = getFlowTypeOfDestructuring(property, elementType); return checkDestructuringAssignment(property.kind === SyntaxKind.ShorthandPropertyAssignment ? property : property.initializer, type); } @@ -29443,7 +30148,7 @@ namespace ts { // present (aka the tuple element property). This call also checks that the parentType is in // fact an iterable or array (depending on target language). const possiblyOutOfBoundsType = checkIteratedTypeOrElementType(IterationUse.Destructuring | IterationUse.PossiblyOutOfBounds, sourceType, undefinedType, node) || errorType; - let inBoundsType: Type | undefined = compilerOptions.noUncheckedIndexSignatures ? undefined: possiblyOutOfBoundsType; + let inBoundsType: Type | undefined = compilerOptions.noUncheckedIndexedAccess ? undefined: possiblyOutOfBoundsType; for (let i = 0; i < elements.length; i++) { let type = possiblyOutOfBoundsType; if (node.elements[i].kind === SyntaxKind.SpreadElement) { @@ -29465,7 +30170,7 @@ namespace ts { // We create a synthetic expression so that getIndexedAccessType doesn't get confused // when the element is a SyntaxKind.ElementAccessExpression. const accessFlags = AccessFlags.ExpressionPosition | (hasDefaultValue(element) ? AccessFlags.NoTupleBoundsCheck : 0); - const elementType = getIndexedAccessTypeOrUndefined(sourceType, indexType, createSyntheticExpression(element, indexType), accessFlags) || errorType; + const elementType = getIndexedAccessTypeOrUndefined(sourceType, indexType, /*noUncheckedIndexedAccessCandidate*/ undefined, createSyntheticExpression(element, indexType), accessFlags) || errorType; const assignedType = hasDefaultValue(element) ? getTypeWithFacts(elementType, TypeFacts.NEUndefined) : elementType; const type = getFlowTypeOfDestructuring(element, assignedType); return checkDestructuringAssignment(element, type, checkMode); @@ -29653,6 +30358,10 @@ namespace ts { workStacks.leftType[stackIndex] = leftType; const operator = node.operatorToken.kind; if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.QuestionQuestionToken) { + if (operator === SyntaxKind.AmpersandAmpersandToken) { + const parent = walkUpParenthesizedExpressions(node.parent); + checkTestingKnownTruthyCallableType(node.left, leftType, isIfStatement(parent) ? parent.thenStatement : undefined); + } checkTruthinessOfType(leftType, node.left); } advanceState(CheckBinaryExpressionState.FinishCheck); @@ -30180,31 +30889,39 @@ namespace ts { return getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, returnType, isAsync) || anyType; } - - return getContextualIterationType(IterationTypeKind.Next, func) || anyType; + let type = getContextualIterationType(IterationTypeKind.Next, func); + if (!type) { + type = anyType; + if (produceDiagnostics && noImplicitAny && !expressionResultIsUnused(node)) { + const contextualType = getContextualType(node); + if (!contextualType || isTypeAny(contextualType)) { + error(node, Diagnostics.yield_expression_implicitly_results_in_an_any_type_because_its_containing_generator_lacks_a_return_type_annotation); + } + } + } + return type; } function checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type { const type = checkTruthinessExpression(node.condition); - checkTestingKnownTruthyCallableType(node.condition, node.whenTrue, type); + checkTestingKnownTruthyCallableType(node.condition, type, node.whenTrue); const type1 = checkExpression(node.whenTrue, checkMode); const type2 = checkExpression(node.whenFalse, checkMode); return getUnionType([type1, type2], UnionReduction.Subtype); } function checkTemplateExpression(node: TemplateExpression): Type { - // We just want to check each expressions, but we are unconcerned with - // the type of each expression, as any value may be coerced into a string. - // It is worth asking whether this is what we really want though. - // A place where we actually *are* concerned with the expressions' types are - // in tagged templates. - forEach(node.templateSpans, templateSpan => { - if (maybeTypeOfKind(checkExpression(templateSpan.expression), TypeFlags.ESSymbolLike)) { - error(templateSpan.expression, Diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String); + const texts = [node.head.text]; + const types = []; + for (const span of node.templateSpans) { + const type = checkExpression(span.expression); + if (maybeTypeOfKind(type, TypeFlags.ESSymbolLike)) { + error(span.expression, Diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String); } - }); - - return stringType; + texts.push(span.literal.text); + types.push(isTypeAssignableTo(type, templateConstraintType) ? type : stringType); + } + return getFreshTypeOfLiteralType(getTemplateLiteralType(texts, types)); } function getContextNode(node: Expression): Node { @@ -30225,7 +30942,7 @@ namespace ts { // We strip literal freshness when an appropriate contextual type is present such that contextually typed // literals always preserve their literal types (otherwise they might widen during type inference). An alternative // here would be to not mark contextually typed literals as fresh in the first place. - const result = maybeTypeOfKind(type, TypeFlags.Literal) && isLiteralOfContextualType(type, instantiateContextualType(contextualType, node)) ? + const result = maybeTypeOfKind(type, TypeFlags.FreshableLiteral) && isLiteralOfContextualType(type, instantiateContextualType(contextualType, node)) ? getRegularTypeOfLiteralType(type) : type; return result; } @@ -30315,7 +31032,7 @@ namespace ts { // this a literal context for literals of that primitive type. For example, given a // type parameter 'T extends string', infer string literal types for T. const constraint = getBaseConstraintOfType(contextualType) || unknownType; - return maybeTypeOfKind(constraint, TypeFlags.String) && maybeTypeOfKind(candidateType, TypeFlags.StringLiteral) || + return maybeTypeOfKind(constraint, TypeFlags.String) && maybeTypeOfKind(candidateType, TypeFlags.StringLikeLiteral) || maybeTypeOfKind(constraint, TypeFlags.Number) && maybeTypeOfKind(candidateType, TypeFlags.NumberLiteral) || maybeTypeOfKind(constraint, TypeFlags.BigInt) && maybeTypeOfKind(candidateType, TypeFlags.BigIntLiteral) || maybeTypeOfKind(constraint, TypeFlags.ESSymbol) && maybeTypeOfKind(candidateType, TypeFlags.UniqueESSymbol) || @@ -30323,7 +31040,7 @@ namespace ts { } // If the contextual type is a literal of a particular primitive type, we consider this a // literal context for all literals of that primitive type. - return !!(contextualType.flags & (TypeFlags.StringLiteral | TypeFlags.Index | TypeFlags.TemplateLiteral) && maybeTypeOfKind(candidateType, TypeFlags.StringLiteral) || + return !!(contextualType.flags & (TypeFlags.StringLikeLiteral | TypeFlags.Index | TypeFlags.StringMapping) && maybeTypeOfKind(candidateType, TypeFlags.StringLikeLiteral) || contextualType.flags & TypeFlags.NumberLiteral && maybeTypeOfKind(candidateType, TypeFlags.NumberLiteral) || contextualType.flags & TypeFlags.BigIntLiteral && maybeTypeOfKind(candidateType, TypeFlags.BigIntLiteral) || contextualType.flags & TypeFlags.BooleanLiteral && maybeTypeOfKind(candidateType, TypeFlags.BooleanLiteral) || @@ -30336,7 +31053,7 @@ namespace ts { const parent = node.parent; return isAssertionExpression(parent) && isConstTypeReference(parent.type) || (isParenthesizedExpression(parent) || isArrayLiteralExpression(parent) || isSpreadElement(parent)) && isConstContext(parent) || - (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent)) && isConstContext(parent.parent); + (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent) || isTemplateSpan(parent)) && isConstContext(parent.parent); } function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, forceTuple?: boolean): Type { @@ -30594,7 +31311,7 @@ namespace ts { } function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type { - tracing.begin(tracing.Phase.Check, "checkExpression", { kind: node.kind, pos: node.pos, end: node.end }); + tracing.push(tracing.Phase.Check, "checkExpression", { kind: node.kind, pos: node.pos, end: node.end }); const saveCurrentNode = currentNode; currentNode = node; instantiationCount = 0; @@ -30604,7 +31321,7 @@ namespace ts { checkConstEnumAccess(node, type); } currentNode = saveCurrentNode; - tracing.end(); + tracing.pop(); return type; } @@ -31578,9 +32295,6 @@ namespace ts { checkSourceElement(span.type); const type = getTypeFromTypeNode(span.type); checkTypeAssignableTo(type, templateConstraintType, span.type); - if (!everyType(type, t => !!(t.flags & TypeFlags.Literal) || isGenericIndexType(t))) { - error(span.type, Diagnostics.Template_literal_type_argument_0_is_not_literal_type_or_a_generic_type, typeToString(type)); - } } getTypeFromTypeNode(node); } @@ -31763,7 +32477,7 @@ namespace ts { for (const current of declarations) { const node = current; const inAmbientContext = node.flags & NodeFlags.Ambient; - const inAmbientContextOrInterface = node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral || inAmbientContext; + const inAmbientContextOrInterface = node.parent && (node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral) || inAmbientContext; if (inAmbientContextOrInterface) { // check if declarations are consecutive only if they are non-ambient // 1. ambient declarations can be interleaved @@ -32689,7 +33403,7 @@ namespace ts { if (isInJSFile(node)) { const typeTag = getJSDocTypeTag(node); if (typeTag && typeTag.typeExpression && !getContextualCallSignature(getTypeFromTypeNode(typeTag.typeExpression), node)) { - error(typeTag, Diagnostics.The_type_of_a_function_declaration_must_match_the_function_s_signature); + error(typeTag.typeExpression.type, Diagnostics.The_type_of_a_function_declaration_must_match_the_function_s_signature); } } } @@ -32829,15 +33543,16 @@ namespace ts { const { parent } = typeParameter; if (parent.kind !== SyntaxKind.InferType && parent.typeParameters!.every(isTypeParameterUnused)) { if (tryAddToSet(seenParentsWithEveryUnused, parent)) { + const sourceFile = getSourceFileOfNode(parent); const range = isJSDocTemplateTag(parent) // Whole @template tag ? rangeOfNode(parent) // Include the `<>` in the error message - : rangeOfTypeParameters(parent.typeParameters!); + : rangeOfTypeParameters(sourceFile, parent.typeParameters!); const only = parent.typeParameters!.length === 1; const message = only ? Diagnostics._0_is_declared_but_its_value_is_never_read : Diagnostics.All_type_parameters_are_unused; const arg0 = only ? name : undefined; - addDiagnostic(typeParameter, UnusedKind.Parameter, createFileDiagnostic(getSourceFileOfNode(parent), range.pos, range.end - range.pos, message, arg0)); + addDiagnostic(typeParameter, UnusedKind.Parameter, createFileDiagnostic(sourceFile, range.pos, range.end - range.pos, message, arg0)); } } else { @@ -32879,7 +33594,7 @@ namespace ts { function checkUnusedLocalsAndParameters(nodeWithLocals: Node, addDiagnostic: AddUnusedDiagnostic): void { // Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value. const unusedImports = new Map(); - const unusedDestructures = new Map(); + const unusedDestructures = new Map(); const unusedVariables = new Map(); nodeWithLocals.locals!.forEach(local => { // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`. @@ -32911,7 +33626,12 @@ namespace ts { const name = local.valueDeclaration && getNameOfDeclaration(local.valueDeclaration); if (parameter && name) { if (!isParameterPropertyDeclaration(parameter, parameter.parent) && !parameterIsThisKeyword(parameter) && !isIdentifierThatStartsWithUnderscore(name)) { - addDiagnostic(parameter, UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local))); + if (isBindingElement(declaration) && isArrayBindingPattern(declaration.parent)) { + addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId); + } + else { + addDiagnostic(parameter, UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local))); + } } } else { @@ -33397,10 +34117,10 @@ namespace ts { } function checkVariableDeclaration(node: VariableDeclaration) { - tracing.begin(tracing.Phase.Check, "checkVariableDeclaration", { kind: node.kind, pos: node.pos, end: node.end }); + tracing.push(tracing.Phase.Check, "checkVariableDeclaration", { kind: node.kind, pos: node.pos, end: node.end }); checkGrammarVariableDeclaration(node); checkVariableLikeDeclaration(node); - tracing.end(); + tracing.pop(); } function checkBindingElement(node: BindingElement) { @@ -33425,7 +34145,7 @@ namespace ts { // Grammar checking checkGrammarStatementInAmbientContext(node); const type = checkTruthinessExpression(node.expression); - checkTestingKnownTruthyCallableType(node.expression, node.thenStatement, type); + checkTestingKnownTruthyCallableType(node.expression, type, node.thenStatement); checkSourceElement(node.thenStatement); if (node.thenStatement.kind === SyntaxKind.EmptyStatement) { @@ -33435,16 +34155,16 @@ namespace ts { checkSourceElement(node.elseStatement); } - function checkTestingKnownTruthyCallableType(condExpr: Expression, body: Statement | Expression, type: Type) { + function checkTestingKnownTruthyCallableType(condExpr: Expression, type: Type, body: Statement | Expression | undefined) { if (!strictNullChecks) { return; } - const testedNode = isIdentifier(condExpr) - ? condExpr - : isPropertyAccessExpression(condExpr) - ? condExpr.name - : undefined; + const location = isBinaryExpression(condExpr) ? condExpr.right : condExpr; + const testedNode = isIdentifier(location) ? location + : isPropertyAccessExpression(location) ? location.name + : isBinaryExpression(location) && isIdentifier(location.right) ? location.right + : undefined; if (!testedNode) { return; @@ -33465,50 +34185,73 @@ namespace ts { return; } - const testedFunctionSymbol = getSymbolAtLocation(testedNode); - if (!testedFunctionSymbol) { + const testedSymbol = getSymbolAtLocation(testedNode); + if (!testedSymbol) { return; } - const functionIsUsedInBody = forEachChild(body, function check(childNode): boolean | undefined { + const isUsed = isBinaryExpression(condExpr.parent) && isFunctionUsedInBinaryExpressionChain(condExpr.parent, testedSymbol) + || body && isFunctionUsedInConditionBody(condExpr, body, testedNode, testedSymbol); + if (!isUsed) { + error(location, Diagnostics.This_condition_will_always_return_true_since_the_function_is_always_defined_Did_you_mean_to_call_it_instead); + } + } + + function isFunctionUsedInConditionBody(expr: Expression, body: Statement | Expression, testedNode: Node, testedSymbol: Symbol): boolean { + return !!forEachChild(body, function check(childNode): boolean | undefined { if (isIdentifier(childNode)) { const childSymbol = getSymbolAtLocation(childNode); - if (childSymbol && childSymbol === testedFunctionSymbol) { + if (childSymbol && childSymbol === testedSymbol) { // If the test was a simple identifier, the above check is sufficient - if (isIdentifier(condExpr)) { + if (isIdentifier(expr)) { return true; } // Otherwise we need to ensure the symbol is called on the same target let testedExpression = testedNode.parent; let childExpression = childNode.parent; while (testedExpression && childExpression) { - if (isIdentifier(testedExpression) && isIdentifier(childExpression) || - testedExpression.kind === SyntaxKind.ThisKeyword && childExpression.kind === SyntaxKind.ThisKeyword - ) { + testedExpression.kind === SyntaxKind.ThisKeyword && childExpression.kind === SyntaxKind.ThisKeyword) { return getSymbolAtLocation(testedExpression) === getSymbolAtLocation(childExpression); } - - if (isPropertyAccessExpression(testedExpression) && isPropertyAccessExpression(childExpression)) { + else if (isPropertyAccessExpression(testedExpression) && isPropertyAccessExpression(childExpression)) { if (getSymbolAtLocation(testedExpression.name) !== getSymbolAtLocation(childExpression.name)) { return false; } childExpression = childExpression.expression; testedExpression = testedExpression.expression; } + else if (isCallExpression(testedExpression) && isCallExpression(childExpression)) { + childExpression = childExpression.expression; + testedExpression = testedExpression.expression; + } else { return false; } } } } - return forEachChild(childNode, check); }); + } - if (!functionIsUsedInBody) { - error(condExpr, Diagnostics.This_condition_will_always_return_true_since_the_function_is_always_defined_Did_you_mean_to_call_it_instead); + function isFunctionUsedInBinaryExpressionChain(node: Node, testedSymbol: Symbol): boolean { + while (isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) { + const isUsed = forEachChild(node.right, function visit(child): boolean | undefined { + if (isIdentifier(child)) { + const symbol = getSymbolAtLocation(child); + if (symbol && symbol === testedSymbol) { + return true; + } + } + return forEachChild(child, visit); + }); + if (isUsed) { + return true; + } + node = node.parent; } + return false; } function checkDoStatement(node: DoStatement) { @@ -33706,7 +34449,7 @@ namespace ts { const uplevelIteration = languageVersion >= ScriptTarget.ES2015; const downlevelIteration = !uplevelIteration && compilerOptions.downlevelIteration; - const possibleOutOfBounds = compilerOptions.noUncheckedIndexSignatures && !!(use & IterationUse.PossiblyOutOfBounds); + const possibleOutOfBounds = compilerOptions.noUncheckedIndexedAccess && !!(use & IterationUse.PossiblyOutOfBounds); // Get the iterated type of an `Iterable` or `IterableIterator` only in ES2015 // or higher, when inside of an async generator or for-await-if, or when @@ -33801,7 +34544,7 @@ namespace ts { const arrayElementType = getIndexTypeOfType(arrayType, IndexKind.Number); if (hasStringConstituent && arrayElementType) { // This is just an optimization for the case where arrayOrStringType is string | string[] - if (arrayElementType.flags & TypeFlags.StringLike && !compilerOptions.noUncheckedIndexSignatures) { + if (arrayElementType.flags & TypeFlags.StringLike && !compilerOptions.noUncheckedIndexedAccess) { return stringType; } @@ -34946,7 +35689,7 @@ namespace ts { const implementedTypeNodes = getEffectiveImplementsTypeNodes(node); if (implementedTypeNodes) { for (const typeRefNode of implementedTypeNodes) { - if (!isEntityNameExpression(typeRefNode.expression)) { + if (!isEntityNameExpression(typeRefNode.expression) || isOptionalChain(typeRefNode.expression)) { error(typeRefNode.expression, Diagnostics.A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments); } checkTypeReferenceNode(typeRefNode); @@ -35295,7 +36038,7 @@ namespace ts { checkObjectTypeForDuplicateDeclarations(node); } forEach(getInterfaceBaseTypeNodes(node), heritageElement => { - if (!isEntityNameExpression(heritageElement.expression)) { + if (!isEntityNameExpression(heritageElement.expression) || isOptionalChain(heritageElement.expression)) { error(heritageElement.expression, Diagnostics.An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments); } checkTypeReferenceNode(heritageElement); @@ -35312,12 +36055,18 @@ namespace ts { function checkTypeAliasDeclaration(node: TypeAliasDeclaration) { // Grammar checking checkGrammarDecoratorsAndModifiers(node); - checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0); checkExportsOnMergedDeclarations(node); checkTypeParameters(node.typeParameters); - checkSourceElement(node.type); - registerForUnusedIdentifiersCheck(node); + if (node.type.kind === SyntaxKind.IntrinsicKeyword) { + if (!intrinsicTypeKinds.has(node.name.escapedText as string) || length(node.typeParameters) !== 1) { + error(node.type, Diagnostics.The_intrinsic_keyword_can_only_be_used_to_declare_compiler_provided_intrinsic_types); + } + } + else { + checkSourceElement(node.type); + registerForUnusedIdentifiersCheck(node); + } } function computeEnumMemberValues(node: EnumDeclaration) { @@ -35897,9 +36646,12 @@ namespace ts { checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0); } } + if (node.isTypeOnly) { + grammarErrorOnNode(node, Diagnostics.An_import_alias_cannot_use_import_type); + } } else { - if (moduleKind >= ModuleKind.ES2015 && !(node.flags & NodeFlags.Ambient)) { + if (moduleKind >= ModuleKind.ES2015 && !node.isTypeOnly && !(node.flags & NodeFlags.Ambient)) { // Import equals declaration is deprecated in es6 or above grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead); } @@ -35990,19 +36742,30 @@ namespace ts { }); } + function canConvertImportDeclarationToTypeOnly(statement: Statement) { + return isImportDeclaration(statement) && + statement.importClause && + !statement.importClause.isTypeOnly && + importClauseContainsReferencedImport(statement.importClause) && + !isReferencedAliasDeclaration(statement.importClause, /*checkChildren*/ true) && + !importClauseContainsConstEnumUsedAsValue(statement.importClause); + } + + function canConvertImportEqualsDeclarationToTypeOnly(statement: Statement) { + return isImportEqualsDeclaration(statement) && + isExternalModuleReference(statement.moduleReference) && + !statement.isTypeOnly && + getSymbolOfNode(statement).isReferenced && + !isReferencedAliasDeclaration(statement, /*checkChildren*/ false) && + !getSymbolLinks(getSymbolOfNode(statement)).constEnumReferenced; + } + function checkImportsForTypeOnlyConversion(sourceFile: SourceFile) { for (const statement of sourceFile.statements) { - if ( - isImportDeclaration(statement) && - statement.importClause && - !statement.importClause.isTypeOnly && - importClauseContainsReferencedImport(statement.importClause) && - !isReferencedAliasDeclaration(statement.importClause, /*checkChildren*/ true) && - !importClauseContainsConstEnumUsedAsValue(statement.importClause) - ) { + if (canConvertImportDeclarationToTypeOnly(statement) || canConvertImportEqualsDeclarationToTypeOnly(statement)) { error( statement, - Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValues_is_set_to_error); + Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_importsNotUsedAsValues_is_set_to_error); } } } @@ -36435,6 +37198,7 @@ namespace ts { } function checkDeferredNode(node: Node) { + tracing.push(tracing.Phase.Check, "checkDeferredNode", { kind: node.kind, pos: node.pos, end: node.end }); const saveCurrentNode = currentNode; currentNode = node; instantiationCount = 0; @@ -36470,15 +37234,16 @@ namespace ts { break; } currentNode = saveCurrentNode; + tracing.pop(); } function checkSourceFile(node: SourceFile) { - tracing.begin(tracing.Phase.Check, "checkSourceFile", { path: node.path }); + tracing.push(tracing.Phase.Check, "checkSourceFile", { path: node.path }, /*separateBeginAndEnd*/ true); performance.mark("beforeCheck"); checkSourceFileWorker(node); performance.mark("afterCheck"); performance.measure("Check", "beforeCheck", "afterCheck"); - tracing.end(); + tracing.pop(); } function unusedIsError(kind: UnusedKind, isAmbient: boolean): boolean { @@ -37808,7 +38573,8 @@ namespace ts { function isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean { if (isDeclarationReadonly(node) || isVariableDeclaration(node) && isVarConst(node)) { - return isFreshLiteralType(getTypeOfSymbol(getSymbolOfNode(node))); + const type = getTypeOfSymbol(getSymbolOfNode(node)); + return !!(type.flags & TypeFlags.Literal) && isFreshLiteralType(type); } return false; } @@ -37917,7 +38683,10 @@ namespace ts { isOptionalParameter, moduleExportsSomeValue, isArgumentsLocalBinding, - getExternalModuleFileFromDeclaration, + getExternalModuleFileFromDeclaration: nodeIn => { + const node = getParseTreeNode(nodeIn, hasPossibleExternalModuleReference); + return node && getExternalModuleFileFromDeclaration(node); + }, getTypeReferenceDirectivesForEntityName, getTypeReferenceDirectivesForSymbol, isLiteralConstDeclaration, @@ -38078,7 +38847,7 @@ namespace ts { } } - function getExternalModuleFileFromDeclaration(declaration: AnyImportOrReExport | ModuleDeclaration | ImportTypeNode): SourceFile | undefined { + function getExternalModuleFileFromDeclaration(declaration: AnyImportOrReExport | ModuleDeclaration | ImportTypeNode | ImportCall): SourceFile | undefined { const specifier = declaration.kind === SyntaxKind.ModuleDeclaration ? tryCast(declaration.name, isStringLiteral) : getExternalModuleName(declaration); const moduleSymbol = resolveExternalModuleNameWorker(specifier!, specifier!, /*moduleNotFoundError*/ undefined); // TODO: GH#18217 if (!moduleSymbol) { @@ -38412,7 +39181,7 @@ namespace ts { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "async"); } else if (isClassLike(node.parent)) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "export"); + return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_class_elements_of_this_kind, "export"); } else if (node.kind === SyntaxKind.Parameter) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "export"); @@ -38435,7 +39204,7 @@ namespace ts { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async"); } else if (isClassLike(node.parent) && !isPropertyDeclaration(node)) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "declare"); + return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_class_elements_of_this_kind, "declare"); } else if (node.kind === SyntaxKind.Parameter) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "declare"); @@ -39396,11 +40165,15 @@ namespace ts { } if (node.exclamationToken && (node.parent.parent.kind !== SyntaxKind.VariableStatement || !node.type || node.initializer || node.flags & NodeFlags.Ambient)) { - return grammarErrorOnNode(node.exclamationToken, Diagnostics.Definite_assignment_assertions_can_only_be_used_along_with_a_type_annotation); + const message = node.initializer + ? Diagnostics.Declarations_with_initializers_cannot_also_have_definite_assignment_assertions + : !node.type + ? Diagnostics.Declarations_with_definite_assignment_assertions_must_also_have_type_annotations + : Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context; + return grammarErrorOnNode(node.exclamationToken, message); } const moduleKind = getEmitModuleKind(compilerOptions); - if (moduleKind < ModuleKind.ES2015 && moduleKind !== ModuleKind.System && !(node.parent.parent.flags & NodeFlags.Ambient) && hasSyntacticModifier(node.parent.parent, ModifierFlags.Export)) { checkESModuleMarker(node.name); @@ -39600,7 +40373,12 @@ namespace ts { if (isPropertyDeclaration(node) && node.exclamationToken && (!isClassLike(node.parent) || !node.type || node.initializer || node.flags & NodeFlags.Ambient || hasSyntacticModifier(node, ModifierFlags.Static | ModifierFlags.Abstract))) { - return grammarErrorOnNode(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context); + const message = node.initializer + ? Diagnostics.Declarations_with_initializers_cannot_also_have_definite_assignment_assertions + : !node.type + ? Diagnostics.Declarations_with_definite_assignment_assertions_must_also_have_type_annotations + : Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context; + return grammarErrorOnNode(node.exclamationToken, message); } } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index beb601b0b8f6f..d62ca2afd14d8 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -73,7 +73,8 @@ namespace ts { ["esnext.intl", "lib.esnext.intl.d.ts"], ["esnext.bigint", "lib.es2020.bigint.d.ts"], ["esnext.string", "lib.esnext.string.d.ts"], - ["esnext.promise", "lib.esnext.promise.d.ts"] + ["esnext.promise", "lib.esnext.promise.d.ts"], + ["esnext.weakref", "lib.esnext.weakref.d.ts"] ]; /** @@ -132,6 +133,30 @@ namespace ts { category: Diagnostics.Advanced_Options, description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively, }, + { + name: "excludeDirectories", + type: "list", + element: { + name: "excludeDirectory", + type: "string", + isFilePath: true, + extraValidation: specToDiagnostic + }, + category: Diagnostics.Advanced_Options, + description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively, + }, + { + name: "excludeFiles", + type: "list", + element: { + name: "excludeFile", + type: "string", + isFilePath: true, + extraValidation: specToDiagnostic + }, + category: Diagnostics.Advanced_Options, + description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively, + }, ]; /* @internal */ @@ -171,6 +196,11 @@ namespace ts { description: Diagnostics.Print_names_of_files_part_of_the_compilation }, { + name: "explainFiles", + type: "boolean", + category: Diagnostics.Advanced_Options, + description: Diagnostics.Print_names_of_files_and_the_reason_they_are_part_of_the_compilation + }, { name: "listEmittedFiles", type: "boolean", category: Diagnostics.Advanced_Options, @@ -243,6 +273,31 @@ namespace ts { }, ]; + /* @internal */ + export const targetOptionDeclaration: CommandLineOptionOfCustomType = { + name: "target", + shortName: "t", + type: new Map(getEntries({ + es3: ScriptTarget.ES3, + es5: ScriptTarget.ES5, + es6: ScriptTarget.ES2015, + es2015: ScriptTarget.ES2015, + es2016: ScriptTarget.ES2016, + es2017: ScriptTarget.ES2017, + es2018: ScriptTarget.ES2018, + es2019: ScriptTarget.ES2019, + es2020: ScriptTarget.ES2020, + esnext: ScriptTarget.ESNext, + })), + affectsSourceFile: true, + affectsModuleResolution: true, + affectsEmit: true, + paramType: Diagnostics.VERSION, + showInSimplifiedHelpView: true, + category: Diagnostics.Basic_Options, + description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_ES2015_ES2016_ES2017_ES2018_ES2019_ES2020_or_ESNEXT, + }; + /* @internal */ export const optionDeclarations: CommandLineOption[] = [ // CommandLine only options @@ -305,29 +360,7 @@ namespace ts { }, // Basic - { - name: "target", - shortName: "t", - type: new Map(getEntries({ - es3: ScriptTarget.ES3, - es5: ScriptTarget.ES5, - es6: ScriptTarget.ES2015, - es2015: ScriptTarget.ES2015, - es2016: ScriptTarget.ES2016, - es2017: ScriptTarget.ES2017, - es2018: ScriptTarget.ES2018, - es2019: ScriptTarget.ES2019, - es2020: ScriptTarget.ES2020, - esnext: ScriptTarget.ESNext, - })), - affectsSourceFile: true, - affectsModuleResolution: true, - affectsEmit: true, - paramType: Diagnostics.VERSION, - showInSimplifiedHelpView: true, - category: Diagnostics.Basic_Options, - description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_ES2015_ES2016_ES2017_ES2018_ES2019_ES2020_or_ESNEXT, - }, + targetOptionDeclaration, { name: "module", shortName: "m", @@ -380,10 +413,12 @@ namespace ts { name: "jsx", type: jsxOptionMap, affectsSourceFile: true, + affectsEmit: true, + affectsModuleResolution: true, paramType: Diagnostics.KIND, showInSimplifiedHelpView: true, category: Diagnostics.Basic_Options, - description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_react_native_or_react, + description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_react_native_react_react_jsx_or_react_jsxdev, }, { name: "declaration", @@ -632,6 +667,13 @@ namespace ts { category: Diagnostics.Additional_Checks, description: Diagnostics.Include_undefined_in_index_signature_results }, + { + name: "noPropertyAccessFromIndexSignature", + type: "boolean", + showInSimplifiedHelpView: false, + category: Diagnostics.Additional_Checks, + description: Diagnostics.Require_undeclared_properties_from_index_signatures_to_use_element_accesses + }, // Module Resolution { @@ -800,6 +842,9 @@ namespace ts { { name: "jsxImportSource", type: "string", + affectsSemanticDiagnostics: true, + affectsEmit: true, + affectsModuleResolution: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Specify_the_module_specifier_to_be_used_to_import_the_jsx_and_jsxs_factory_functions_from_eg_react }, @@ -1132,7 +1177,11 @@ namespace ts { name: "exclude", type: "string" } - } + }, + { + name: "disableFilenameBasedTypeAcquisition", + type: "boolean", + }, ]; /* @internal */ @@ -1212,9 +1261,9 @@ namespace ts { const values = value.split(","); switch (opt.element.type) { case "number": - return map(values, parseInt); + return mapDefined(values, v => validateJsonOptionValue(opt.element, parseInt(v), errors)); case "string": - return map(values, v => v || ""); + return mapDefined(values, v => validateJsonOptionValue(opt.element, v || "", errors)); default: return mapDefined(values, v => parseCustomTypeOption(opt.element, v, errors)); } @@ -1344,7 +1393,7 @@ namespace ts { } else if (opt.type === "boolean") { if (optValue === "false") { - options[opt.name] = false; + options[opt.name] = validateJsonOptionValue(opt, /*value*/ false, errors); i++; } else { @@ -1366,20 +1415,20 @@ namespace ts { if (args[i] !== "null") { switch (opt.type) { case "number": - options[opt.name] = parseInt(args[i]); + options[opt.name] = validateJsonOptionValue(opt, parseInt(args[i]), errors); i++; break; case "boolean": // boolean flag has optional value true, false, others const optValue = args[i]; - options[opt.name] = optValue !== "false"; + options[opt.name] = validateJsonOptionValue(opt, optValue !== "false", errors); // consume next argument as boolean flag value if (optValue === "false" || optValue === "true") { i++; } break; case "string": - options[opt.name] = args[i] || ""; + options[opt.name] = validateJsonOptionValue(opt, args[i] || "", errors); i++; break; case "list": @@ -1572,7 +1621,7 @@ namespace ts { */ export function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): TsConfigSourceFile { const textOrDiagnostic = tryReadFile(fileName, readFile); - return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : { parseDiagnostics: [textOrDiagnostic] }; + return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : { fileName, parseDiagnostics: [textOrDiagnostic] }; } /*@internal*/ @@ -1824,9 +1873,10 @@ namespace ts { function convertArrayLiteralExpressionToJson( elements: NodeArray, elementOption: CommandLineOption | undefined - ): any[] | void { + ) { if (!returnValue) { - return elements.forEach(element => convertPropertyValueToJson(element, elementOption)); + elements.forEach(element => convertPropertyValueToJson(element, elementOption)); + return undefined; } // Filter out invalid values @@ -1834,18 +1884,19 @@ namespace ts { } function convertPropertyValueToJson(valueExpression: Expression, option: CommandLineOption | undefined): any { + let invalidReported: boolean | undefined; switch (valueExpression.kind) { case SyntaxKind.TrueKeyword: reportInvalidOptionValue(option && option.type !== "boolean"); - return true; + return validateValue(/*value*/ true); case SyntaxKind.FalseKeyword: reportInvalidOptionValue(option && option.type !== "boolean"); - return false; + return validateValue(/*value*/ false); case SyntaxKind.NullKeyword: reportInvalidOptionValue(option && option.name === "extends"); // "extends" is the only option we don't allow null/undefined for - return null; // eslint-disable-line no-null/no-null + return validateValue(/*value*/ null); // eslint-disable-line no-null/no-null case SyntaxKind.StringLiteral: if (!isDoubleQuotedString(valueExpression)) { @@ -1863,20 +1914,21 @@ namespace ts { (message, arg0, arg1) => createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, message, arg0, arg1) ) ); + invalidReported = true; } } - return text; + return validateValue(text); case SyntaxKind.NumericLiteral: reportInvalidOptionValue(option && option.type !== "number"); - return Number((valueExpression).text); + return validateValue(Number((valueExpression).text)); case SyntaxKind.PrefixUnaryExpression: if ((valueExpression).operator !== SyntaxKind.MinusToken || (valueExpression).operand.kind !== SyntaxKind.NumericLiteral) { break; // not valid JSON syntax } reportInvalidOptionValue(option && option.type !== "number"); - return -Number(((valueExpression).operand).text); + return validateValue(-Number(((valueExpression).operand).text)); case SyntaxKind.ObjectLiteralExpression: reportInvalidOptionValue(option && option.type !== "object"); @@ -1890,20 +1942,20 @@ namespace ts { // If need arises, we can modify this interface and callbacks as needed if (option) { const { elementOptions, extraKeyDiagnostics, name: optionName } = option; - return convertObjectLiteralExpressionToJson(objectLiteralExpression, - elementOptions, extraKeyDiagnostics, optionName); + return validateValue(convertObjectLiteralExpressionToJson(objectLiteralExpression, + elementOptions, extraKeyDiagnostics, optionName)); } else { - return convertObjectLiteralExpressionToJson( + return validateValue(convertObjectLiteralExpressionToJson( objectLiteralExpression, /* knownOptions*/ undefined, - /*extraKeyDiagnosticMessage */ undefined, /*parentOption*/ undefined); + /*extraKeyDiagnosticMessage */ undefined, /*parentOption*/ undefined)); } case SyntaxKind.ArrayLiteralExpression: reportInvalidOptionValue(option && option.type !== "list"); - return convertArrayLiteralExpressionToJson( + return validateValue(convertArrayLiteralExpressionToJson( (valueExpression).elements, - option && (option).element); + option && (option).element)); } // Not in expected format @@ -1916,9 +1968,21 @@ namespace ts { return undefined; + function validateValue(value: CompilerOptionsValue) { + if (!invalidReported) { + const diagnostic = option?.extraValidation?.(value); + if (diagnostic) { + errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, ...diagnostic)); + return undefined; + } + } + return value; + } + function reportInvalidOptionValue(isError: boolean | undefined) { if (isError) { errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.Compiler_option_0_requires_a_value_of_type_1, option!.name, getCompilerOptionValueTypeString(option!))); + invalidReported = true; } } } @@ -1974,10 +2038,10 @@ namespace ts { const files = map( filter( configParseResult.fileNames, - (!configParseResult.configFileSpecs || !configParseResult.configFileSpecs.validatedIncludeSpecs) ? _ => true : matchesSpecs( + !configParseResult.options.configFile?.configFileSpecs?.validatedIncludeSpecs ? returnTrue : matchesSpecs( configFileName, - configParseResult.configFileSpecs.validatedIncludeSpecs, - configParseResult.configFileSpecs.validatedExcludeSpecs, + configParseResult.options.configFile.configFileSpecs.validatedIncludeSpecs, + configParseResult.options.configFile.configFileSpecs.validatedExcludeSpecs, host, ) ), @@ -2002,9 +2066,9 @@ namespace ts { watchOptions: watchOptionMap && optionMapToObject(watchOptionMap), references: map(configParseResult.projectReferences, r => ({ ...r, path: r.originalPath ? r.originalPath : "", originalPath: undefined })), files: length(files) ? files : undefined, - ...(configParseResult.configFileSpecs ? { - include: filterSameAsDefaultInclude(configParseResult.configFileSpecs.validatedIncludeSpecs), - exclude: configParseResult.configFileSpecs.validatedExcludeSpecs + ...(configParseResult.options.configFile?.configFileSpecs ? { + include: filterSameAsDefaultInclude(configParseResult.options.configFile.configFileSpecs.validatedIncludeSpecs), + exclude: configParseResult.options.configFile.configFileSpecs.validatedExcludeSpecs } : {}), compileOnSave: !!configParseResult.compileOnSave ? true : undefined }; @@ -2025,7 +2089,7 @@ namespace ts { } function matchesSpecs(path: string, includeSpecs: readonly string[] | undefined, excludeSpecs: readonly string[] | undefined, host: ConvertToTSConfigHost): (path: string) => boolean { - if (!includeSpecs) return _ => true; + if (!includeSpecs) return returnTrue; const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, host.useCaseSensitiveFileNames, host.getCurrentDirectory()); const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, host.useCaseSensitiveFileNames); const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, host.useCaseSensitiveFileNames); @@ -2038,7 +2102,7 @@ namespace ts { if (excludeRe) { return path => excludeRe.test(path); } - return _ => true; + return returnTrue; } function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): ESMap | undefined { @@ -2331,40 +2395,29 @@ namespace ts { parsedConfig.watchOptions || existingWatchOptions; options.configFilePath = configFileName && normalizeSlashes(configFileName); + const configFileSpecs = getConfigFileSpecs(); + if (sourceFile) sourceFile.configFileSpecs = configFileSpecs; setConfigFileInOptions(options, sourceFile); - let projectReferences: ProjectReference[] | undefined; - const { fileNames, wildcardDirectories, spec } = getFileNames(); + + const basePathForFileNames = normalizePath(configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath); return { options, watchOptions, - fileNames, - projectReferences, + fileNames: getFileNames(basePathForFileNames), + projectReferences: getProjectReferences(basePathForFileNames), typeAcquisition: parsedConfig.typeAcquisition || getDefaultTypeAcquisition(), raw, errors, - wildcardDirectories, + // Wildcard directories (provided as part of a wildcard path) are stored in a + // file map that marks whether it was a regular wildcard match (with a `*` or `?` token), + // or a recursive directory. This information is used by filesystem watchers to monitor for + // new entries in these paths. + wildcardDirectories: getWildcardDirectories(configFileSpecs, basePathForFileNames, host.useCaseSensitiveFileNames), compileOnSave: !!raw.compileOnSave, - configFileSpecs: spec }; - function getFileNames(): ExpandResult { + function getConfigFileSpecs(): ConfigFileSpecs { const referencesOfRaw = getPropFromRaw("references", element => typeof element === "object", "object"); - if (isArray(referencesOfRaw)) { - for (const ref of referencesOfRaw) { - if (typeof ref.path !== "string") { - createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "reference.path", "string"); - } - else { - (projectReferences || (projectReferences = [])).push({ - path: getNormalizedAbsolutePath(ref.path, basePath), - originalPath: ref.path, - prepend: ref.prepend, - circular: ref.circular - }); - } - } - } - const filesSpecs = toPropValue(getSpecsFromRaw("files")); if (filesSpecs) { const hasZeroOrNoReferences = referencesOfRaw === "no-prop" || isArray(referencesOfRaw) && referencesOfRaw.length === 0; @@ -2401,13 +2454,57 @@ namespace ts { if (filesSpecs === undefined && includeSpecs === undefined) { includeSpecs = ["**/*"]; } + let validatedIncludeSpecs: readonly string[] | undefined, validatedExcludeSpecs: readonly string[] | undefined; - const result = matchFileNames(filesSpecs, includeSpecs, excludeSpecs, configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath, options, host, errors, extraFileExtensions, sourceFile); - if (shouldReportNoInputFiles(result, canJsonReportNoInputFiles(raw), resolutionStack)) { - errors.push(getErrorForNoInputFiles(result.spec, configFileName)); + // The exclude spec list is converted into a regular expression, which allows us to quickly + // test whether a file or directory should be excluded before recursively traversing the + // file system. + + if (includeSpecs) { + validatedIncludeSpecs = validateSpecs(includeSpecs, errors, /*disallowTrailingRecursion*/ true, sourceFile, "include"); } - return result; + if (excludeSpecs) { + validatedExcludeSpecs = validateSpecs(excludeSpecs, errors, /*disallowTrailingRecursion*/ false, sourceFile, "exclude"); + } + + return { + filesSpecs, + includeSpecs, + excludeSpecs, + validatedFilesSpec: filter(filesSpecs, isString), + validatedIncludeSpecs, + validatedExcludeSpecs, + }; + } + + function getFileNames(basePath: string): string[] { + const fileNames = getFileNamesFromConfigSpecs(configFileSpecs, basePath, options, host, extraFileExtensions); + if (shouldReportNoInputFiles(fileNames, canJsonReportNoInputFiles(raw), resolutionStack)) { + errors.push(getErrorForNoInputFiles(configFileSpecs, configFileName)); + } + return fileNames; + } + + function getProjectReferences(basePath: string): readonly ProjectReference[] | undefined { + let projectReferences: ProjectReference[] | undefined; + const referencesOfRaw = getPropFromRaw("references", element => typeof element === "object", "object"); + if (isArray(referencesOfRaw)) { + for (const ref of referencesOfRaw) { + if (typeof ref.path !== "string") { + createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "reference.path", "string"); + } + else { + (projectReferences || (projectReferences = [])).push({ + path: getNormalizedAbsolutePath(ref.path, basePath), + originalPath: ref.path, + prepend: ref.prepend, + circular: ref.circular + }); + } + } + } + return projectReferences; } type PropOfRaw = readonly T[] | "not-array" | "no-prop"; @@ -2455,8 +2552,8 @@ namespace ts { JSON.stringify(excludeSpecs || [])); } - function shouldReportNoInputFiles(result: ExpandResult, canJsonReportNoInutFiles: boolean, resolutionStack?: Path[]) { - return result.fileNames.length === 0 && canJsonReportNoInutFiles && (!resolutionStack || resolutionStack.length === 0); + function shouldReportNoInputFiles(fileNames: string[], canJsonReportNoInutFiles: boolean, resolutionStack?: Path[]) { + return fileNames.length === 0 && canJsonReportNoInutFiles && (!resolutionStack || resolutionStack.length === 0); } /*@internal*/ @@ -2465,9 +2562,9 @@ namespace ts { } /*@internal*/ - export function updateErrorForNoInputFiles(result: ExpandResult, configFileName: string, configFileSpecs: ConfigFileSpecs, configParseDiagnostics: Diagnostic[], canJsonReportNoInutFiles: boolean) { + export function updateErrorForNoInputFiles(fileNames: string[], configFileName: string, configFileSpecs: ConfigFileSpecs, configParseDiagnostics: Diagnostic[], canJsonReportNoInutFiles: boolean) { const existingErrors = configParseDiagnostics.length; - if (shouldReportNoInputFiles(result, canJsonReportNoInutFiles)) { + if (shouldReportNoInputFiles(fileNames, canJsonReportNoInutFiles)) { configParseDiagnostics.push(getErrorForNoInputFiles(configFileSpecs, configFileName)); } else { @@ -2827,7 +2924,8 @@ namespace ts { return defaultOptions; } - function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Push): CompilerOptionsValue { + /*@internal*/ + export function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Push): CompilerOptionsValue { if (isCompilerOptionsValue(opt, value)) { const optType = opt.type; if (optType === "list" && isArray(value)) { @@ -2836,7 +2934,8 @@ namespace ts { else if (!isString(optType)) { return convertJsonOptionOfCustomType(opt, value, errors); } - return normalizeNonListOptionValue(opt, basePath, value); + const validatedValue = validateJsonOptionValue(opt, value, errors); + return isNullOrUndefined(validatedValue) ? validatedValue : normalizeNonListOptionValue(opt, basePath, validatedValue); } else { errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, opt.name, getCompilerOptionValueTypeString(opt))); @@ -2868,12 +2967,20 @@ namespace ts { return value; } + function validateJsonOptionValue(opt: CommandLineOption, value: T, errors: Push): T | undefined { + if (isNullOrUndefined(value)) return undefined; + const d = opt.extraValidation?.(value); + if (!d) return value; + errors.push(createCompilerDiagnostic(...d)); + return undefined; + } + function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Push) { if (isNullOrUndefined(value)) return undefined; const key = value.toLowerCase(); const val = opt.type.get(key); if (val !== undefined) { - return val; + return validateJsonOptionValue(opt, val, errors); } else { errors.push(createCompilerDiagnosticForInvalidCustomType(opt)); @@ -2945,65 +3052,10 @@ namespace ts { */ const wildcardDirectoryPattern = /^[^*?]*(?=\/[^/]*[*?])/; - /** - * Expands an array of file specifications. - * - * @param filesSpecs The literal file names to include. - * @param includeSpecs The wildcard file specifications to include. - * @param excludeSpecs The wildcard file specifications to exclude. - * @param basePath The base path for any relative file specifications. - * @param options Compiler options. - * @param host The host used to resolve files and directories. - * @param errors An array for diagnostic reporting. - */ - function matchFileNames( - filesSpecs: readonly string[] | undefined, - includeSpecs: readonly string[] | undefined, - excludeSpecs: readonly string[] | undefined, - basePath: string, - options: CompilerOptions, - host: ParseConfigHost, - errors: Push, - extraFileExtensions: readonly FileExtensionInfo[], - jsonSourceFile: TsConfigSourceFile | undefined - ): ExpandResult { - basePath = normalizePath(basePath); - let validatedIncludeSpecs: readonly string[] | undefined, validatedExcludeSpecs: readonly string[] | undefined; - - // The exclude spec list is converted into a regular expression, which allows us to quickly - // test whether a file or directory should be excluded before recursively traversing the - // file system. - - if (includeSpecs) { - validatedIncludeSpecs = validateSpecs(includeSpecs, errors, /*allowTrailingRecursion*/ false, jsonSourceFile, "include"); - } - - if (excludeSpecs) { - validatedExcludeSpecs = validateSpecs(excludeSpecs, errors, /*allowTrailingRecursion*/ true, jsonSourceFile, "exclude"); - } - - // Wildcard directories (provided as part of a wildcard path) are stored in a - // file map that marks whether it was a regular wildcard match (with a `*` or `?` token), - // or a recursive directory. This information is used by filesystem watchers to monitor for - // new entries in these paths. - const wildcardDirectories = getWildcardDirectories(validatedIncludeSpecs, validatedExcludeSpecs, basePath, host.useCaseSensitiveFileNames); - - const spec: ConfigFileSpecs = { - filesSpecs, - includeSpecs, - excludeSpecs, - validatedFilesSpec: filter(filesSpecs, isString), - validatedIncludeSpecs, - validatedExcludeSpecs, - wildcardDirectories - }; - return getFileNamesFromConfigSpecs(spec, basePath, options, host, extraFileExtensions); - } - /** * Gets the file names from the provided config file specs that contain, files, include, exclude and * other properties needed to resolve the file names - * @param spec The config file specs extracted with file names to include, wildcards to include/exclude and other details + * @param configFileSpecs The config file specs extracted with file names to include, wildcards to include/exclude and other details * @param basePath The base path for any relative file specifications. * @param options Compiler options. * @param host The host used to resolve files and directories. @@ -3011,12 +3063,12 @@ namespace ts { */ /* @internal */ export function getFileNamesFromConfigSpecs( - spec: ConfigFileSpecs, + configFileSpecs: ConfigFileSpecs, basePath: string, options: CompilerOptions, host: ParseConfigHost, extraFileExtensions: readonly FileExtensionInfo[] = emptyArray - ): ExpandResult { + ): string[] { basePath = normalizePath(basePath); const keyMapper = createGetCanonicalFileName(host.useCaseSensitiveFileNames); @@ -3035,7 +3087,7 @@ namespace ts { // file map with a possibly case insensitive key. We use this map to store paths matched // via wildcard of *.json kind const wildCardJsonFileMap = new Map(); - const { validatedFilesSpec, validatedIncludeSpecs, validatedExcludeSpecs, wildcardDirectories } = spec; + const { validatedFilesSpec, validatedIncludeSpecs, validatedExcludeSpecs } = configFileSpecs; // Rather than requery this for each file and filespec, we query the supported extensions // once and store it on the expansion context. @@ -3096,11 +3148,7 @@ namespace ts { const literalFiles = arrayFrom(literalFileMap.values()); const wildcardFiles = arrayFrom(wildcardFileMap.values()); - return { - fileNames: literalFiles.concat(wildcardFiles, arrayFrom(wildCardJsonFileMap.values())), - wildcardDirectories, - spec - }; + return literalFiles.concat(wildcardFiles, arrayFrom(wildCardJsonFileMap.values())); } /* @internal */ @@ -3123,19 +3171,44 @@ namespace ts { } } - const excludePattern = getRegularExpressionForWildcard(validatedExcludeSpecs, combinePaths(normalizePath(currentDirectory), basePath), "exclude"); + return matchesExcludeWorker(pathToCheck, validatedExcludeSpecs, useCaseSensitiveFileNames, currentDirectory, basePath); + } + + /* @internal */ + export function matchesExclude( + pathToCheck: string, + excludeSpecs: readonly string[] | undefined, + useCaseSensitiveFileNames: boolean, + currentDirectory: string + ) { + return matchesExcludeWorker( + pathToCheck, + filter(excludeSpecs, spec => !invalidDotDotAfterRecursiveWildcardPattern.test(spec)), + useCaseSensitiveFileNames, + currentDirectory + ); + } + + function matchesExcludeWorker( + pathToCheck: string, + excludeSpecs: readonly string[] | undefined, + useCaseSensitiveFileNames: boolean, + currentDirectory: string, + basePath?: string + ) { + const excludePattern = getRegularExpressionForWildcard(excludeSpecs, combinePaths(normalizePath(currentDirectory), basePath), "exclude"); const excludeRegex = excludePattern && getRegexFromPattern(excludePattern, useCaseSensitiveFileNames); if (!excludeRegex) return false; if (excludeRegex.test(pathToCheck)) return true; return !hasExtension(pathToCheck) && excludeRegex.test(ensureTrailingDirectorySeparator(pathToCheck)); } - function validateSpecs(specs: readonly string[], errors: Push, allowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile | undefined, specKey: string): readonly string[] { + function validateSpecs(specs: readonly string[], errors: Push, disallowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile | undefined, specKey: string): readonly string[] { return specs.filter(spec => { if (!isString(spec)) return false; - const diag = specToDiagnostic(spec, allowTrailingRecursion); + const diag = specToDiagnostic(spec, disallowTrailingRecursion); if (diag !== undefined) { - errors.push(createDiagnostic(diag, spec)); + errors.push(createDiagnostic(...diag)); } return diag === undefined; }); @@ -3148,19 +3221,19 @@ namespace ts { } } - function specToDiagnostic(spec: string, allowTrailingRecursion: boolean): DiagnosticMessage | undefined { - if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) { - return Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0; + function specToDiagnostic(spec: string, disallowTrailingRecursion?: boolean): [DiagnosticMessage, string] | undefined { + if (disallowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) { + return [Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec]; } else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) { - return Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0; + return [Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec]; } } /** * Gets directories in a set of include patterns that should be watched for changes. */ - function getWildcardDirectories(include: readonly string[] | undefined, exclude: readonly string[] | undefined, path: string, useCaseSensitiveFileNames: boolean): MapLike { + function getWildcardDirectories({ validatedIncludeSpecs: include, validatedExcludeSpecs: exclude }: ConfigFileSpecs, path: string, useCaseSensitiveFileNames: boolean): MapLike { // We watch a directory recursively if it contains a wildcard anywhere in a directory segment // of the pattern: // diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 13a661d423f4b..e66ea4c2597bf 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1195,7 +1195,7 @@ namespace ts { * @param keyComparer A callback used to compare two keys in a sorted array. * @param offset An offset into `array` at which to start the search. */ - export function binarySearchKey(array: readonly T[], key: U, keySelector: (v: T) => U, keyComparer: Comparer, offset?: number): number { + export function binarySearchKey(array: readonly T[], key: U, keySelector: (v: T, i: number) => U, keyComparer: Comparer, offset?: number): number { if (!some(array)) { return -1; } @@ -1204,7 +1204,7 @@ namespace ts { let high = array.length - 1; while (low <= high) { const middle = low + ((high - low) >> 1); - const midKey = keySelector(array[middle]); + const midKey = keySelector(array[middle], middle); switch (keyComparer(midKey, key)) { case Comparison.LessThan: low = middle + 1; @@ -1591,7 +1591,7 @@ namespace ts { // | 1. | i | 105 | Ascii i | // | 2. | I | 73 | Ascii I | // |-------- Special characters ------------------------------------------------------------------------| - // | 3. | \u0130 | 304 | Uppper case I with dot above | + // | 3. | \u0130 | 304 | Upper case I with dot above | // | 4. | i,\u0307 | 105,775 | i, followed by 775: Lower case of (3rd item) | // | 5. | I,\u0307 | 73,775 | I, followed by 775: Upper case of (4th item), lower case is (4th item) | // | 6. | \u0131 | 305 | Lower case i without dot, upper case is I (2nd item) | @@ -1927,11 +1927,9 @@ namespace ts { /** * Given a name and a list of names that are *not* equal to the name, return a spelling suggestion if there is one that is close enough. - * Names less than length 3 only check for case-insensitive equality, not Levenshtein distance. + * Names less than length 3 only check for case-insensitive equality. * - * If there is a candidate that's the same except for case, return that. - * If there is a candidate that's within one edit of the name, return that. - * Otherwise, return the candidate with the smallest Levenshtein distance, + * find the candidate with the smallest Levenshtein distance, * except for candidates: * * With no name * * Whose length differs from the target name by more than 0.34 of the length of the name. @@ -1941,41 +1939,28 @@ namespace ts { */ export function getSpellingSuggestion(name: string, candidates: T[], getName: (candidate: T) => string | undefined): T | undefined { const maximumLengthDifference = Math.min(2, Math.floor(name.length * 0.34)); - let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result isn't better than this, don't bother. + let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result is worse than this, don't bother. let bestCandidate: T | undefined; - let justCheckExactMatches = false; - const nameLowerCase = name.toLowerCase(); for (const candidate of candidates) { const candidateName = getName(candidate); - if (candidateName !== undefined && Math.abs(candidateName.length - nameLowerCase.length) <= maximumLengthDifference) { - const candidateNameLowerCase = candidateName.toLowerCase(); - if (candidateNameLowerCase === nameLowerCase) { - if (candidateName === name) { - continue; - } - return candidate; - } - if (justCheckExactMatches) { + if (candidateName !== undefined && Math.abs(candidateName.length - name.length) <= maximumLengthDifference) { + if (candidateName === name) { continue; } - if (candidateName.length < 3) { - // Don't bother, user would have noticed a 2-character name having an extra character + // Only consider candidates less than 3 characters long when they differ by case. + // Otherwise, don't bother, since a user would usually notice differences of a 2-character name. + if (candidateName.length < 3 && candidateName.toLowerCase() !== name.toLowerCase()) { continue; } - // Only care about a result better than the best so far. - const distance = levenshteinWithMax(nameLowerCase, candidateNameLowerCase, bestDistance - 1); + + const distance = levenshteinWithMax(name, candidateName, bestDistance - 0.1); if (distance === undefined) { continue; } - if (distance < 3) { - justCheckExactMatches = true; - bestCandidate = candidate; - } - else { - Debug.assert(distance < bestDistance); // Else `levenshteinWithMax` should return undefined - bestDistance = distance; - bestCandidate = candidate; - } + + Debug.assert(distance < bestDistance); // Else `levenshteinWithMax` should return undefined + bestDistance = distance; + bestCandidate = candidate; } } return bestCandidate; @@ -1985,7 +1970,7 @@ namespace ts { let previous = new Array(s2.length + 1); let current = new Array(s2.length + 1); /** Represents any value > max. We don't care about the particular value. */ - const big = max + 1; + const big = max + 0.01; for (let i = 0; i <= s2.length; i++) { previous[i] = i; @@ -1993,8 +1978,8 @@ namespace ts { for (let i = 1; i <= s1.length; i++) { const c1 = s1.charCodeAt(i - 1); - const minJ = i > max ? i - max : 1; - const maxJ = s2.length > max + i ? max + i : s2.length; + const minJ = Math.ceil(i > max ? i - max : 1); + const maxJ = Math.floor(s2.length > max + i ? max + i : s2.length); current[0] = i; /** Smallest value of the matrix in the ith column. */ let colMin = i; @@ -2002,9 +1987,13 @@ namespace ts { current[j] = big; } for (let j = minJ; j <= maxJ; j++) { + // case difference should be significantly cheaper than other differences + const substitutionDistance = s1[i - 1].toLowerCase() === s2[j-1].toLowerCase() + ? (previous[j - 1] + 0.1) + : (previous[j - 1] + 2); const dist = c1 === s2.charCodeAt(j - 1) ? previous[j - 1] - : Math.min(/*delete*/ previous[j] + 1, /*insert*/ current[j - 1] + 1, /*substitute*/ previous[j - 1] + 2); + : Math.min(/*delete*/ previous[j] + 1, /*insert*/ current[j - 1] + 1, /*substitute*/ substitutionDistance); current[j] = dist; colMin = Math.min(colMin, dist); } diff --git a/src/compiler/corePublic.ts b/src/compiler/corePublic.ts index ecf3f929fae35..31c852bdee6d4 100644 --- a/src/compiler/corePublic.ts +++ b/src/compiler/corePublic.ts @@ -1,9 +1,11 @@ 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 = "4.1"; + export const versionMajorMinor = "4.2"; + // The following is baselined as a literal template type without intervention /** The version of the TypeScript compiler release */ - export const version = `${versionMajorMinor}.0-dev`; + // 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. @@ -88,7 +90,7 @@ namespace ts { /** ES6 Iterator type. */ export interface Iterator { - next(): { value: T, done?: false } | { value: never, done: true }; + next(): { value: T, done?: false } | { value: void, done: true }; } /** Array that is only intended to be pushed to, never read. */ diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index bce40bdcf5f41..634d872c7883a 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -99,7 +99,7 @@ "category": "Error", "code": 1030 }, - "'{0}' modifier cannot appear on a class element.": { + "'{0}' modifier cannot appear on class elements of this kind.": { "category": "Error", "code": 1031 }, @@ -851,10 +851,6 @@ "category": "Error", "code": 1257 }, - "Definite assignment assertions can only be used along with a type annotation.": { - "category": "Error", - "code": 1258 - }, "Module '{0}' can only be default-imported using the '{1}' flag": { "category": "Error", "code": 1259 @@ -871,6 +867,14 @@ "category": "Error", "code": 1262 }, + "Declarations with initializers cannot also have definite assignment assertions.": { + "category": "Error", + "code": 1263 + }, + "Declarations with definite assignment assertions must also have type annotations.": { + "category": "Error", + "code": 1264 + }, "'with' statements are not allowed in an async function block.": { "category": "Error", @@ -1000,7 +1004,7 @@ "category": "Error", "code": 1342 }, - "The 'import.meta' meta-property is only allowed when the '--module' option is 'esnext' or 'system'.": { + "The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'esnext', or 'system'.": { "category": "Error", "code": 1343 }, @@ -1108,11 +1112,7 @@ "category": "Message", "code": 1369 }, - "Only ECMAScript imports may use 'import type'.": { - "category": "Error", - "code": 1370 - }, - "This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'.": { + "This import is never used as a value and must use 'import type' because 'importsNotUsedAsValues' is set to 'error'.": { "category": "Error", "code": 1371 }, @@ -1192,6 +1192,162 @@ "category": "Error", "code": 1391 }, + "An import alias cannot use 'import type'": { + "category": "Error", + "code": 1392 + }, + "Imported via {0} from file '{1}'": { + "category": "Message", + "code": 1393 + }, + "Imported via {0} from file '{1}' with packageId '{2}'": { + "category": "Message", + "code": 1394 + }, + "Imported via {0} from file '{1}' to import 'importHelpers' as specified in compilerOptions": { + "category": "Message", + "code": 1395 + }, + "Imported via {0} from file '{1}' with packageId '{2}' to import 'importHelpers' as specified in compilerOptions": { + "category": "Message", + "code": 1396 + }, + "Imported via {0} from file '{1}' to import 'jsx' and 'jsxs' factory functions": { + "category": "Message", + "code": 1397 + }, + "Imported via {0} from file '{1}' with packageId '{2}' to import 'jsx' and 'jsxs' factory functions": { + "category": "Message", + "code": 1398 + }, + "File is included via import here.": { + "category": "Message", + "code": 1399 + }, + "Referenced via '{0}' from file '{1}'": { + "category": "Message", + "code": 1400 + }, + "File is included via reference here.": { + "category": "Message", + "code": 1401 + }, + "Type library referenced via '{0}' from file '{1}'": { + "category": "Message", + "code": 1402 + }, + "Type library referenced via '{0}' from file '{1}' with packageId '{2}'": { + "category": "Message", + "code": 1403 + }, + "File is included via type library reference here.": { + "category": "Message", + "code": 1404 + }, + "Library referenced via '{0}' from file '{1}'": { + "category": "Message", + "code": 1405 + }, + "File is included via library reference here.": { + "category": "Message", + "code": 1406 + }, + "Matched by include pattern '{0}' in '{1}'": { + "category": "Message", + "code": 1407 + }, + "File is matched by include pattern specified here.": { + "category": "Message", + "code": 1408 + }, + "Part of 'files' list in tsconfig.json": { + "category": "Message", + "code": 1409 + }, + "File is matched by 'files' list specified here.": { + "category": "Message", + "code": 1410 + }, + "Output from referenced project '{0}' included because '{1}' specified": { + "category": "Message", + "code": 1411 + }, + "Output from referenced project '{0}' included because '--module' is specified as 'none'": { + "category": "Message", + "code": 1412 + }, + "File is output from referenced project specified here.": { + "category": "Message", + "code": 1413 + }, + "Source from referenced project '{0}' included because '{1}' specified": { + "category": "Message", + "code": 1414 + }, + "Source from referenced project '{0}' included because '--module' is specified as 'none'": { + "category": "Message", + "code": 1415 + }, + "File is source from referenced project specified here.": { + "category": "Message", + "code": 1416 + }, + "Entry point of type library '{0}' specified in compilerOptions": { + "category": "Message", + "code": 1417 + }, + "Entry point of type library '{0}' specified in compilerOptions with packageId '{1}'": { + "category": "Message", + "code": 1418 + }, + "File is entry point of type library specified here.": { + "category": "Message", + "code": 1419 + }, + "Entry point for implicit type library '{0}'": { + "category": "Message", + "code": 1420 + }, + "Entry point for implicit type library '{0}' with packageId '{1}'": { + "category": "Message", + "code": 1421 + }, + "Library '{0}' specified in compilerOptions": { + "category": "Message", + "code": 1422 + }, + "File is library specified here.": { + "category": "Message", + "code": 1423 + }, + "Default library": { + "category": "Message", + "code": 1424 + }, + "Default library for target '{0}'": { + "category": "Message", + "code": 1425 + }, + "File is default library for target specified here.": { + "category": "Message", + "code": 1426 + }, + "Root file specified for compilation": { + "category": "Message", + "code": 1427 + }, + "File is output of project reference source '{0}'": { + "category": "Message", + "code": 1428 + }, + "File redirects to file '{0}'": { + "category": "Message", + "code": 1429 + }, + "The file is in the program because:": { + "category": "Message", + "code": 1430 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", "code": 2200 @@ -2193,6 +2349,10 @@ "category": "Error", "code": 2549 }, + "Property '{0}' does not exist on type '{1}'. Do you need to change your target library? Try changing the `lib` compiler option to '{2}' or later.": { + "category": "Error", + "code": 2550 + }, "Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?": { "category": "Error", "code": 2551 @@ -2285,7 +2445,7 @@ "category": "Error", "code": 2575 }, - "Property '{0}' is a static member of type '{1}'.": { + "Property '{0}' does not exist on type '{1}'. Did you mean to access the static member '{2}' instead?": { "category": "Error", "code": 2576 }, @@ -2297,19 +2457,19 @@ "category": "Error", "code": 2578 }, - "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i @types/node`.": { + "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node`.": { "category": "Error", "code": 2580 }, - "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i @types/jquery`.": { + "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i --save-dev @types/jquery`.": { "category": "Error", "code": 2581 }, - "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i @types/jest` or `npm i @types/mocha`.": { + "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.": { "category": "Error", "code": 2582 }, - "Cannot find name '{0}'. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.": { + "Cannot find name '{0}'. Do you need to change your target library? Try changing the `lib` compiler option to '{1}' or later.": { "category": "Error", "code": 2583 }, @@ -2341,15 +2501,15 @@ "category": "Error", "code": 2590 }, - "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.": { + "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add `node` to the types field in your tsconfig.": { "category": "Error", "code": 2591 }, - "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i @types/jquery` and then add `jquery` to the types field in your tsconfig.": { + "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i --save-dev @types/jquery` and then add `jquery` to the types field in your tsconfig.": { "category": "Error", "code": 2592 }, - "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i @types/jest` or `npm i @types/mocha` and then add `jest` or `mocha` to the types field in your tsconfig.": { + "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add `jest` or `mocha` to the types field in your tsconfig.": { "category": "Error", "code": 2593 }, @@ -3035,14 +3195,26 @@ "category": "Error", "code": 2792 }, - "Template literal type argument '{0}' is not literal type or a generic type.": { + "The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.": { "category": "Error", "code": 2793 }, - "A mixin class that extends from a type variable containing an abstract construct signature must also be declared 'abstract'.": { + "Expected {0} arguments, but got {1}. Did you forget to include 'void' in your type argument to 'Promise'?": { "category": "Error", "code": 2794 }, + "The 'intrinsic' keyword can only be used to declare compiler provided intrinsic types.": { + "category": "Error", + "code": 2795 + }, + "It is likely that you are missing a comma to separate these two template expressions. They form a tagged template expression which cannot be invoked.": { + "category": "Error", + "code": 2796 + }, + "A mixin class that extends from a type variable containing an abstract construct signature must also be declared 'abstract'.": { + "category": "Error", + "code": 2797 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", @@ -3412,6 +3584,10 @@ "category": "Error", "code": 4110 }, + "Property '{0}' comes from an index signature, so it must be accessed with ['{0}'].": { + "category": "Error", + "code": 4111 + }, "The current host does not support the '{0}' option.": { "category": "Error", @@ -3890,7 +4066,7 @@ "category": "Message", "code": 6079 }, - "Specify JSX code generation: 'preserve', 'react-native', or 'react'.": { + "Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'.": { "category": "Message", "code": 6080 }, @@ -4703,6 +4879,10 @@ "code": 6385, "reportsDeprecated": true }, + "Performance timings for '--diagnostics' or '--extendedDiagnostics' are not available in this session. A native implementation of the Web Performance API could not be found.": { + "category": "Message", + "code": 6386 + }, "The expected type comes from property '{0}' which is declared here on type '{1}'": { "category": "Message", @@ -4724,6 +4904,14 @@ "category": "Error", "code": 6504 }, + "Print names of files and the reason they are part of the compilation.": { + "category": "Message", + "code": 6505 + }, + "Require undeclared properties from index signatures to use element accesses.": { + "category": "Error", + "code": 6803 + }, "Include 'undefined' in index signature results": { "category": "Message", @@ -4840,7 +5028,7 @@ "category": "Error", "code": 7034 }, - "Try `npm install @types/{1}` if it exists or add a new declaration (.d.ts) file containing `declare module '{0}';`": { + "Try `npm i --save-dev @types/{1}` if it exists or add a new declaration (.d.ts) file containing `declare module '{0}';`": { "category": "Error", "code": 7035 }, @@ -4928,6 +5116,10 @@ "category": "Error", "code": 7056 }, + "'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.": { + "category": "Error", + "code": 7057 + }, "You cannot rename this element.": { "category": "Error", @@ -5931,6 +6123,58 @@ "category": "Message", "code": 95142 }, + "Add 'void' to Promise resolved without a value": { + "category": "Message", + "code": 95143 + }, + "Add 'void' to all Promises resolved without a value": { + "category": "Message", + "code": 95144 + }, + "Use element access for '{0}'": { + "category": "Message", + "code": 95145 + }, + "Use element access for all undeclared properties.": { + "category": "Message", + "code": 95146 + }, + "Delete all unused imports": { + "category": "Message", + "code": 95147 + }, + "Infer function return type": { + "category": "Message", + "code": 95148 + }, + "Return type must be inferred from a function": { + "category": "Message", + "code": 95149 + }, + "Could not determine function return type": { + "category": "Message", + "code": 95150 + }, + "Could not convert to arrow function": { + "category": "Message", + "code": 95151 + }, + "Could not convert to named function": { + "category": "Message", + "code": 95152 + }, + "Could not convert to anonymous function": { + "category": "Message", + "code": 95153 + }, + "Can only convert string concatenation": { + "category": "Message", + "code": 95154 + }, + "Selection is not a valid statement or statements": { + "category": "Message", + "code": 95155 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a491cf6f36870..8d495dfebda0f 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -130,36 +130,32 @@ namespace ts { return Extension.Js; } - function rootDirOfOptions(configFile: ParsedCommandLine) { - return configFile.options.rootDir || getDirectoryPath(Debug.checkDefined(configFile.options.configFilePath)); - } - - function getOutputPathWithoutChangingExt(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, outputDir: string | undefined) { + function getOutputPathWithoutChangingExt(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, outputDir: string | undefined, getCommonSourceDirectory?: () => string) { return outputDir ? resolvePath( outputDir, - getRelativePathFromDirectory(rootDirOfOptions(configFile), inputFileName, ignoreCase) + getRelativePathFromDirectory(getCommonSourceDirectory ? getCommonSourceDirectory() : getCommonSourceDirectoryOfConfig(configFile, ignoreCase), inputFileName, ignoreCase) ) : inputFileName; } /* @internal */ - export function getOutputDeclarationFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) { + export function getOutputDeclarationFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, getCommonSourceDirectory?: () => string) { Debug.assert(!fileExtensionIs(inputFileName, Extension.Dts) && !fileExtensionIs(inputFileName, Extension.Json)); return changeExtension( - getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.declarationDir || configFile.options.outDir), + getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.declarationDir || configFile.options.outDir, getCommonSourceDirectory), Extension.Dts ); } - function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) { + function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, getCommonSourceDirectory?: () => string) { if (configFile.options.emitDeclarationOnly) return undefined; const isJsonFile = fileExtensionIs(inputFileName, Extension.Json); const outputFileName = changeExtension( - getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir), + getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir, getCommonSourceDirectory), isJsonFile ? Extension.Json : - fileExtensionIs(inputFileName, Extension.Tsx) && configFile.options.jsx === JsxEmit.Preserve ? + configFile.options.jsx === JsxEmit.Preserve && (fileExtensionIs(inputFileName, Extension.Tsx) || fileExtensionIs(inputFileName, Extension.Jsx)) ? Extension.Jsx : Extension.Js ); @@ -190,16 +186,16 @@ namespace ts { addOutput(buildInfoPath); } - function getOwnOutputFileNames(configFile: ParsedCommandLine, inputFileName: string, ignoreCase: boolean, addOutput: ReturnType["addOutput"]) { + function getOwnOutputFileNames(configFile: ParsedCommandLine, inputFileName: string, ignoreCase: boolean, addOutput: ReturnType["addOutput"], getCommonSourceDirectory?: () => string) { if (fileExtensionIs(inputFileName, Extension.Dts)) return; - const js = getOutputJSFileName(inputFileName, configFile, ignoreCase); + const js = getOutputJSFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory); addOutput(js); if (fileExtensionIs(inputFileName, Extension.Json)) return; if (js && configFile.options.sourceMap) { addOutput(`${js}.map`); } if (getEmitDeclarations(configFile.options)) { - const dts = getOutputDeclarationFileName(inputFileName, configFile, ignoreCase); + const dts = getOutputDeclarationFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory); addOutput(dts); if (configFile.options.declarationMap) { addOutput(`${dts}.map`); @@ -207,6 +203,48 @@ namespace ts { } } + /*@internal*/ + export function getCommonSourceDirectory( + options: CompilerOptions, + emittedFiles: () => readonly string[], + currentDirectory: string, + getCanonicalFileName: GetCanonicalFileName, + checkSourceFilesBelongToPath?: (commonSourceDirectory: string) => void + ): string { + let commonSourceDirectory; + if (options.rootDir) { + // If a rootDir is specified use it as the commonSourceDirectory + commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory); + checkSourceFilesBelongToPath?.(options.rootDir); + } + else if (options.composite && options.configFilePath) { + // Project compilations never infer their root from the input source paths + commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath)); + checkSourceFilesBelongToPath?.(commonSourceDirectory); + } + else { + commonSourceDirectory = computeCommonSourceDirectoryOfFilenames(emittedFiles(), currentDirectory, getCanonicalFileName); + } + + if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) { + // Make sure directory path ends with directory separator so this string can directly + // used to replace with "" to get the relative path of the source file and the relative path doesn't + // start with / making it rooted path + commonSourceDirectory += directorySeparator; + } + return commonSourceDirectory; + } + + /*@internal*/ + export function getCommonSourceDirectoryOfConfig({ options, fileNames }: ParsedCommandLine, ignoreCase: boolean): string { + return getCommonSourceDirectory( + options, + () => filter(fileNames, file => !(options.noEmitForJsFiles && fileExtensionIsOneOf(file, supportedJSExtensions)) && !fileExtensionIs(file, Extension.Dts)), + getDirectoryPath(normalizeSlashes(Debug.checkDefined(options.configFilePath))), + createGetCanonicalFileName(!ignoreCase) + ); + } + /*@internal*/ export function getAllProjectOutputs(configFile: ParsedCommandLine, ignoreCase: boolean): readonly string[] { const { addOutput, getOutputs } = createAddOutput(); @@ -214,8 +252,9 @@ namespace ts { getSingleOutputFileNames(configFile, addOutput); } else { + const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(configFile, ignoreCase)); for (const inputFileName of configFile.fileNames) { - getOwnOutputFileNames(configFile, inputFileName, ignoreCase, addOutput); + getOwnOutputFileNames(configFile, inputFileName, ignoreCase, addOutput, getCommonSourceDirectory); } addOutput(getTsBuildInfoEmitOutputFilePath(configFile.options)); } @@ -242,13 +281,14 @@ namespace ts { return Debug.checkDefined(jsFilePath, `project ${configFile.options.configFilePath} expected to have at least one output`); } + const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(configFile, ignoreCase)); for (const inputFileName of configFile.fileNames) { if (fileExtensionIs(inputFileName, Extension.Dts)) continue; - const jsFilePath = getOutputJSFileName(inputFileName, configFile, ignoreCase); + const jsFilePath = getOutputJSFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory); if (jsFilePath) return jsFilePath; if (fileExtensionIs(inputFileName, Extension.Json)) continue; if (getEmitDeclarations(configFile.options)) { - return getOutputDeclarationFileName(inputFileName, configFile, ignoreCase); + return getOutputDeclarationFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory); } } const buildInfoPath = getTsBuildInfoEmitOutputFilePath(configFile.options); @@ -300,17 +340,17 @@ namespace ts { sourceFiles: sourceFileOrBundle.sourceFiles.map(file => relativeToBuildInfo(getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory()))) }; } - tracing.begin(tracing.Phase.Emit, "emitJsFileOrBundle", { jsFilePath }); + tracing.push(tracing.Phase.Emit, "emitJsFileOrBundle", { jsFilePath }); emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath, relativeToBuildInfo); - tracing.end(); + tracing.pop(); - tracing.begin(tracing.Phase.Emit, "emitDeclarationFileOrBundle", { declarationFilePath }); + tracing.push(tracing.Phase.Emit, "emitDeclarationFileOrBundle", { declarationFilePath }); emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath, relativeToBuildInfo); - tracing.end(); + tracing.pop(); - tracing.begin(tracing.Phase.Emit, "emitBuildInfo", { buildInfoPath }); + tracing.push(tracing.Phase.Emit, "emitBuildInfo", { buildInfoPath }); emitBuildInfo(bundleBuildInfo, buildInfoPath); - tracing.end(); + tracing.pop(); if (!emitSkipped && emittedFilesList) { if (!emitOnlyDtsFiles) { @@ -606,18 +646,19 @@ namespace ts { if (getRootLength(sourceMapDir) === 0) { // The relative paths are relative to the common directory sourceMapDir = combinePaths(host.getCommonSourceDirectory(), sourceMapDir); - return getRelativePathToDirectoryOrUrl( - getDirectoryPath(normalizePath(filePath)), // get the relative sourceMapDir path based on jsFilePath - combinePaths(sourceMapDir, sourceMapFile), // this is where user expects to see sourceMap - host.getCurrentDirectory(), - host.getCanonicalFileName, - /*isAbsolutePathAnUrl*/ true); + return encodeURI( + getRelativePathToDirectoryOrUrl( + getDirectoryPath(normalizePath(filePath)), // get the relative sourceMapDir path based on jsFilePath + combinePaths(sourceMapDir, sourceMapFile), // this is where user expects to see sourceMap + host.getCurrentDirectory(), + host.getCanonicalFileName, + /*isAbsolutePathAnUrl*/ true)); } else { - return combinePaths(sourceMapDir, sourceMapFile); + return encodeURI(combinePaths(sourceMapDir, sourceMapFile)); } } - return sourceMapFile; + return encodeURI(sourceMapFile); } } @@ -2023,15 +2064,6 @@ namespace ts { } function emitTemplateTypeSpan(node: TemplateLiteralTypeSpan) { - const keyword = node.casing === TemplateCasing.Uppercase ? "uppercase" : - node.casing === TemplateCasing.Lowercase ? "lowercase" : - node.casing === TemplateCasing.Capitalize ? "capitalize" : - node.casing === TemplateCasing.Uncapitalize ? "uncapitalize" : - undefined; - if (keyword) { - writeKeyword(keyword); - writeSpace(); - } emit(node.type); emit(node.literal); } @@ -2821,7 +2853,7 @@ namespace ts { if (isSimilarNode && currentSourceFile) { pos = skipTrivia(currentSourceFile.text, pos); } - if (emitLeadingCommentsOfPosition && isSimilarNode && contextNode.pos !== startPos) { + if (isSimilarNode && contextNode.pos !== startPos) { const needsIndent = indentLeading && currentSourceFile && !positionsAreOnSameLine(startPos, pos, currentSourceFile); if (needsIndent) { increaseIndent(); @@ -2832,8 +2864,9 @@ namespace ts { } } pos = writeTokenText(token, writer, pos); - if (emitTrailingCommentsOfPosition && isSimilarNode && contextNode.end !== pos) { - emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ true); + if (isSimilarNode && contextNode.end !== pos) { + const isJsxExprContext = contextNode.kind === SyntaxKind.JsxExpression; + emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ !isJsxExprContext, /*forceNoNewline*/ isJsxExprContext); } return pos; } @@ -3164,6 +3197,10 @@ namespace ts { emitModifiers(node, node.modifiers); emitTokenWithComment(SyntaxKind.ImportKeyword, node.modifiers ? node.modifiers.end : node.pos, writeKeyword, node); writeSpace(); + if (node.isTypeOnly) { + emitTokenWithComment(SyntaxKind.TypeKeyword, node.pos, writeKeyword, node); + writeSpace(); + } emit(node.name); writeSpace(); emitTokenWithComment(SyntaxKind.EqualsToken, node.name.end, writePunctuation, node); @@ -3386,12 +3423,35 @@ namespace ts { writePunctuation("}"); } + function hasTrailingCommentsAtPosition(pos: number) { + let result = false; + forEachTrailingCommentRange(currentSourceFile?.text || "", pos + 1, () => result = true); + return result; + } + + function hasLeadingCommentsAtPosition(pos: number) { + let result = false; + forEachLeadingCommentRange(currentSourceFile?.text || "", pos + 1, () => result = true); + return result; + } + + function hasCommentsAtPosition(pos: number) { + return hasTrailingCommentsAtPosition(pos) || hasLeadingCommentsAtPosition(pos); + } + function emitJsxExpression(node: JsxExpression) { - if (node.expression) { - writePunctuation("{"); + if (node.expression || (!commentsDisabled && !nodeIsSynthesized(node) && hasCommentsAtPosition(node.pos))) { // preserve empty expressions if they contain comments! + const isMultiline = currentSourceFile && !nodeIsSynthesized(node) && getLineAndCharacterOfPosition(currentSourceFile, node.pos).line !== getLineAndCharacterOfPosition(currentSourceFile, node.end).line; + if (isMultiline) { + writer.increaseIndent(); + } + const end = emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, node); emit(node.dotDotDotToken); emitExpression(node.expression); - writePunctuation("}"); + emitTokenWithComment(SyntaxKind.CloseBraceToken, node.expression?.end || end, writePunctuation, node); + if (isMultiline) { + writer.decreaseIndent(); + } } } @@ -3478,7 +3538,7 @@ namespace ts { // "comment1" is not considered to be leading comment for node.initializer // but rather a trailing comment on the previous node. const initializer = node.initializer; - if (emitTrailingCommentsOfPosition && (getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { + if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { const commentRange = getCommentRange(initializer); emitTrailingCommentsOfPosition(commentRange.pos); } @@ -4174,20 +4234,26 @@ namespace ts { } // Write a trailing comma, if requested. - const hasTrailingComma = (format & ListFormat.AllowTrailingComma) && children!.hasTrailingComma; - if (format & ListFormat.CommaDelimited && hasTrailingComma) { - writePunctuation(","); + const emitFlags = previousSibling ? getEmitFlags(previousSibling) : 0; + const skipTrailingComments = commentsDisabled || !!(emitFlags & EmitFlags.NoTrailingComments); + const hasTrailingComma = children?.hasTrailingComma && (format & ListFormat.AllowTrailingComma) && (format & ListFormat.CommaDelimited); + if (hasTrailingComma) { + if (previousSibling && !skipTrailingComments) { + emitTokenWithComment(SyntaxKind.CommaToken, previousSibling.end, writePunctuation, previousSibling); + } + else { + writePunctuation(","); + } } - // Emit any trailing comment of the last element in the list // i.e // var array = [... // 2 // /* end of element 2 */ // ]; - if (previousSibling && format & ListFormat.DelimitersMask && previousSibling.end !== parentNode.end && !(getEmitFlags(previousSibling) & EmitFlags.NoTrailingComments)) { - emitLeadingCommentsOfPosition(previousSibling.end); + if (previousSibling && parentNode.end !== previousSibling.end && (format & ListFormat.DelimitersMask) && !skipTrailingComments) { + emitLeadingCommentsOfPosition(hasTrailingComma && children?.end ? children.end : previousSibling.end); } // Decrease the indent, if requested. @@ -4603,7 +4669,8 @@ namespace ts { const flags = (neverAsciiEscape ? GetLiteralTextFlags.NeverAsciiEscape : 0) | (jsxAttributeEscape ? GetLiteralTextFlags.JsxAttributeEscape : 0) - | (printerOptions.terminateUnterminatedLiterals ? GetLiteralTextFlags.TerminateUnterminatedLiterals : 0); + | (printerOptions.terminateUnterminatedLiterals ? GetLiteralTextFlags.TerminateUnterminatedLiterals : 0) + | (printerOptions.target && printerOptions.target === ScriptTarget.ESNext ? GetLiteralTextFlags.AllowNumericSeparator : 0); return getLiteralText(node, currentSourceFile!, flags); } @@ -5145,7 +5212,12 @@ namespace ts { hasWrittenComment = false; if (isEmittedNode) { - forEachLeadingCommentToEmit(pos, emitLeadingComment); + if (pos === 0 && currentSourceFile?.isDeclarationFile) { + forEachLeadingCommentToEmit(pos, emitNonTripleSlashLeadingComment); + } + else { + forEachLeadingCommentToEmit(pos, emitLeadingComment); + } } else if (pos === 0) { // If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node, @@ -5166,6 +5238,12 @@ namespace ts { } } + function emitNonTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) { + if (!isTripleSlashComment(commentPos, commentEnd)) { + emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); + } + } + function shouldWriteComment(text: string, pos: number) { if (printerOptions.onlyPrintJsDocStyle) { return (isJSDocLikeText(text, pos) || isPinnedComment(text, pos)); @@ -5221,15 +5299,27 @@ namespace ts { } } - function emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean) { + function emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean, forceNoNewline?: boolean) { if (commentsDisabled) { return; } enterComment(); - forEachTrailingCommentToEmit(pos, prefixSpace ? emitTrailingComment : emitTrailingCommentOfPosition); + forEachTrailingCommentToEmit(pos, prefixSpace ? emitTrailingComment : forceNoNewline ? emitTrailingCommentOfPositionNoNewline : emitTrailingCommentOfPosition); exitComment(); } + function emitTrailingCommentOfPositionNoNewline(commentPos: number, commentEnd: number, kind: SyntaxKind) { + // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space + + emitPos(commentPos); + writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); + emitPos(commentEnd); + + if (kind === SyntaxKind.SingleLineCommentTrivia) { + writer.writeLine(); // still write a newline for single-line comments, so closing tokens aren't written on the same line + } + } + function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) { // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index 212c141a6f93a..615365700baf1 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -585,6 +585,8 @@ namespace ts { }; return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 2890df304a3db..5f3a19a8949ac 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -1605,9 +1605,8 @@ namespace ts { } // @api - function createTemplateLiteralTypeSpan(casing: TemplateCasing, type: TypeNode, literal: TemplateMiddle | TemplateTail) { + function createTemplateLiteralTypeSpan(type: TypeNode, literal: TemplateMiddle | TemplateTail) { const node = createBaseNode(SyntaxKind.TemplateLiteralTypeSpan); - node.casing = casing; node.type = type; node.literal = literal; node.transformFlags = TransformFlags.ContainsTypeScript; @@ -1615,11 +1614,10 @@ namespace ts { } // @api - function updateTemplateLiteralTypeSpan(casing: TemplateCasing, node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddle | TemplateTail) { - return node.casing !== casing - || node.type !== type + function updateTemplateLiteralTypeSpan(node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddle | TemplateTail) { + return node.type !== type || node.literal !== literal - ? update(createTemplateLiteralTypeSpan(casing, type, literal), node) + ? update(createTemplateLiteralTypeSpan(type, literal), node) : node; } @@ -2699,12 +2697,14 @@ namespace ts { node.transformFlags |= TransformFlags.ContainsES2015 | TransformFlags.ContainsES2018 | - TransformFlags.ContainsDestructuringAssignment; + TransformFlags.ContainsDestructuringAssignment | + propagateAssignmentPatternFlags(node.left); } else if (isArrayLiteralExpression(node.left)) { node.transformFlags |= TransformFlags.ContainsES2015 | - TransformFlags.ContainsDestructuringAssignment; + TransformFlags.ContainsDestructuringAssignment | + propagateAssignmentPatternFlags(node.left); } } else if (operatorKind === SyntaxKind.AsteriskAsteriskToken || operatorKind === SyntaxKind.AsteriskAsteriskEqualsToken) { @@ -2716,6 +2716,27 @@ namespace ts { return node; } + function propagateAssignmentPatternFlags(node: AssignmentPattern): TransformFlags { + if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) return TransformFlags.ContainsObjectRestOrSpread; + if (node.transformFlags & TransformFlags.ContainsES2018) { + // check for nested spread assignments, otherwise '{ x: { a, ...b } = foo } = c' + // will not be correctly interpreted by the ES2018 transformer + for (const element of getElementsOfBindingOrAssignmentPattern(node)) { + const target = getTargetOfBindingOrAssignmentElement(element); + if (target && isAssignmentPattern(target)) { + if (target.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { + return TransformFlags.ContainsObjectRestOrSpread; + } + if (target.transformFlags & TransformFlags.ContainsES2018) { + const flags = propagateAssignmentPatternFlags(target); + if (flags) return flags; + } + } + } + } + return TransformFlags.None; + } + // @api function updateBinaryExpression(node: BinaryExpression, left: Expression, operator: BinaryOperatorToken, right: Expression) { return node.left !== left @@ -3789,6 +3810,7 @@ namespace ts { function createImportEqualsDeclaration( decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, + isTypeOnly: boolean, name: string | Identifier, moduleReference: ModuleReference ) { @@ -3798,6 +3820,7 @@ namespace ts { modifiers, name ); + node.isTypeOnly = isTypeOnly; node.moduleReference = moduleReference; node.transformFlags |= propagateChildFlags(node.moduleReference); if (!isExternalModuleReference(node.moduleReference)) node.transformFlags |= TransformFlags.ContainsTypeScript; @@ -3810,14 +3833,16 @@ namespace ts { node: ImportEqualsDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, + isTypeOnly: boolean, name: Identifier, moduleReference: ModuleReference ) { return node.decorators !== decorators || node.modifiers !== modifiers + || node.isTypeOnly !== isTypeOnly || node.name !== name || node.moduleReference !== moduleReference - ? update(createImportEqualsDeclaration(decorators, modifiers, name, moduleReference), node) + ? update(createImportEqualsDeclaration(decorators, modifiers, isTypeOnly, name, moduleReference), node) : node; } @@ -5811,7 +5836,7 @@ namespace ts { isTypeAliasDeclaration(node) ? updateTypeAliasDeclaration(node, node.decorators, modifiers, node.name, node.typeParameters, node.type) : isEnumDeclaration(node) ? updateEnumDeclaration(node, node.decorators, modifiers, node.name, node.members) : isModuleDeclaration(node) ? updateModuleDeclaration(node, node.decorators, modifiers, node.name, node.body) : - isImportEqualsDeclaration(node) ? updateImportEqualsDeclaration(node, node.decorators, modifiers, node.name, node.moduleReference) : + isImportEqualsDeclaration(node) ? updateImportEqualsDeclaration(node, node.decorators, modifiers, node.isTypeOnly, node.name, node.moduleReference) : isImportDeclaration(node) ? updateImportDeclaration(node, node.decorators, modifiers, node.importClause, node.moduleSpecifier) : isExportAssignment(node) ? updateExportAssignment(node, node.decorators, modifiers, node.expression) : isExportDeclaration(node) ? updateExportDeclaration(node, node.decorators, modifiers, node.isTypeOnly, node.exportClause, node.moduleSpecifier) : diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index b7da2127f2aae..71b329e46d65c 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -233,6 +233,14 @@ namespace ts { return node.kind === SyntaxKind.ImportType; } + export function isTemplateLiteralTypeSpan(node: Node): node is TemplateLiteralTypeSpan { + return node.kind === SyntaxKind.TemplateLiteralTypeSpan; + } + + export function isTemplateLiteralTypeNode(node: Node): node is TemplateLiteralTypeNode { + return node.kind === SyntaxKind.TemplateLiteralType; + } + // Binding patterns export function isObjectBindingPattern(node: Node): node is ObjectBindingPattern { @@ -779,8 +787,12 @@ namespace ts { export function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag { return node.kind === SyntaxKind.JSDocDeprecatedTag; + } + export function isJSDocSeeTag(node: Node): node is JSDocSeeTag { + return node.kind === SyntaxKind.JSDocSeeTag; } + export function isJSDocEnumTag(node: Node): node is JSDocEnumTag { return node.kind === SyntaxKind.JSDocEnumTag; } diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts index 770600d94b71d..8b18c3152e3ff 100644 --- a/src/compiler/factory/utilities.ts +++ b/src/compiler/factory/utilities.ts @@ -46,7 +46,7 @@ namespace ts { } } - function createJsxFactoryExpression(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { + export function createJsxFactoryExpression(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { return jsxFactoryEntity ? createJsxFactoryExpressionFromEntityName(factory, jsxFactoryEntity, parent) : factory.createPropertyAccessExpression( @@ -64,7 +64,7 @@ namespace ts { ); } - export function createExpressionForJsxElement(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, parentElement: JsxOpeningLikeElement, location: TextRange): LeftHandSideExpression { + export function createExpressionForJsxElement(factory: NodeFactory, callee: Expression, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, location: TextRange): LeftHandSideExpression { const argumentsList = [tagName]; if (props) { argumentsList.push(props); @@ -88,7 +88,7 @@ namespace ts { return setTextRange( factory.createCallExpression( - createJsxFactoryExpression(factory, jsxFactoryEntity, reactNamespace, parentElement), + callee, /*typeArguments*/ undefined, argumentsList ), @@ -511,12 +511,12 @@ namespace ts { * 3- The containing SourceFile has an entry in renamedDependencies for the import as requested by some module loaders (e.g. System). * Otherwise, a new StringLiteral node representing the module name will be returned. */ - export function getExternalModuleNameLiteral(factory: NodeFactory, importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration, sourceFile: SourceFile, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) { - const moduleName = getExternalModuleName(importNode)!; // TODO: GH#18217 - if (moduleName.kind === SyntaxKind.StringLiteral) { + export function getExternalModuleNameLiteral(factory: NodeFactory, importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration | ImportCall, sourceFile: SourceFile, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) { + const moduleName = getExternalModuleName(importNode); + if (moduleName && isStringLiteral(moduleName)) { return tryGetModuleNameFromDeclaration(importNode, host, factory, resolver, compilerOptions) - || tryRenameExternalModule(factory, moduleName, sourceFile) - || factory.cloneNode(moduleName); + || tryRenameExternalModule(factory, moduleName, sourceFile) + || factory.cloneNode(moduleName); } return undefined; @@ -528,7 +528,7 @@ namespace ts { */ function tryRenameExternalModule(factory: NodeFactory, moduleName: LiteralExpression, sourceFile: SourceFile) { const rename = sourceFile.renamedDependencies && sourceFile.renamedDependencies.get(moduleName.text); - return rename && factory.createStringLiteral(rename); + return rename ? factory.createStringLiteral(rename) : undefined; } /** @@ -551,7 +551,7 @@ namespace ts { return undefined; } - function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration, host: EmitHost, factory: NodeFactory, resolver: EmitResolver, compilerOptions: CompilerOptions) { + function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ImportCall, host: EmitHost, factory: NodeFactory, resolver: EmitResolver, compilerOptions: CompilerOptions) { return tryGetModuleNameFromFile(factory, resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions); } diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index fbd4f57a4a0fb..6329387b281d7 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -1023,7 +1023,7 @@ namespace ts { /** * This will be called on the successfully resolved path from `loadModuleFromFile`. - * (Not neeeded for `loadModuleFromNodeModules` as that looks up the `package.json` as part of resolution.) + * (Not needed for `loadModuleFromNodeModules` as that looks up the `package.json` as part of resolution.) * * packageDirectory is the directory of the package itself. * For `blah/node_modules/foo/index.d.ts` this is packageDirectory: "foo" diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 0848679ba9a40..d2fc45ee9913d 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -1,7 +1,7 @@ // Used by importFixes, getEditsForFileRename, and declaration emit to synthesize import module specifiers. /* @internal */ namespace ts.moduleSpecifiers { - const enum RelativePreference { Relative, NonRelative, Auto } + const enum RelativePreference { Relative, NonRelative, Shortest, ExternalNonRelative } // See UserPreferences#importPathEnding const enum Ending { Minimal, Index, JsExtension } @@ -13,7 +13,11 @@ namespace ts.moduleSpecifiers { function getPreferences({ importModuleSpecifierPreference, importModuleSpecifierEnding }: UserPreferences, compilerOptions: CompilerOptions, importingSourceFile: SourceFile): Preferences { return { - relativePreference: importModuleSpecifierPreference === "relative" ? RelativePreference.Relative : importModuleSpecifierPreference === "non-relative" ? RelativePreference.NonRelative : RelativePreference.Auto, + relativePreference: + importModuleSpecifierPreference === "relative" ? RelativePreference.Relative : + importModuleSpecifierPreference === "non-relative" ? RelativePreference.NonRelative : + importModuleSpecifierPreference === "project-relative" ? RelativePreference.ExternalNonRelative : + RelativePreference.Shortest, ending: getEnding(), }; function getEnding(): Ending { @@ -88,12 +92,13 @@ namespace ts.moduleSpecifiers { /** Returns an import for each symlink and for the realpath. */ export function getModuleSpecifiers( moduleSymbol: Symbol, + checker: TypeChecker, compilerOptions: CompilerOptions, importingSourceFile: SourceFile, host: ModuleSpecifierResolutionHost, userPreferences: UserPreferences, ): readonly string[] { - const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol); + const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol, checker); if (ambient) return [ambient]; const info = getInfo(importingSourceFile.path, host); @@ -147,17 +152,19 @@ namespace ts.moduleSpecifiers { interface Info { readonly getCanonicalFileName: GetCanonicalFileName; + readonly importingSourceFileName: Path readonly sourceDirectory: Path; } // importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path function getInfo(importingSourceFileName: Path, host: ModuleSpecifierResolutionHost): Info { const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : true); const sourceDirectory = getDirectoryPath(importingSourceFileName); - return { getCanonicalFileName, sourceDirectory }; + return { getCanonicalFileName, importingSourceFileName, sourceDirectory }; } - function getLocalModuleSpecifier(moduleFileName: string, { getCanonicalFileName, sourceDirectory }: Info, compilerOptions: CompilerOptions, host: ModuleSpecifierResolutionHost, { ending, relativePreference }: Preferences): string { + function getLocalModuleSpecifier(moduleFileName: string, info: Info, compilerOptions: CompilerOptions, host: ModuleSpecifierResolutionHost, { ending, relativePreference }: Preferences): string { const { baseUrl, paths, rootDirs, bundledPackageName } = compilerOptions; + const { sourceDirectory, getCanonicalFileName } = info; const relativePath = rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName, ending, compilerOptions) || removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), ending, compilerOptions); @@ -174,13 +181,51 @@ namespace ts.moduleSpecifiers { const bundledPkgReference = bundledPackageName ? combinePaths(bundledPackageName, relativeToBaseUrl) : relativeToBaseUrl; const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(bundledPkgReference, ending, compilerOptions); const fromPaths = paths && tryGetModuleNameFromPaths(removeFileExtension(bundledPkgReference), importRelativeToBaseUrl, paths); - const nonRelative = fromPaths === undefined ? importRelativeToBaseUrl : fromPaths; + const nonRelative = fromPaths === undefined && baseUrl !== undefined ? importRelativeToBaseUrl : fromPaths; + if (!nonRelative) { + return relativePath; + } if (relativePreference === RelativePreference.NonRelative) { return nonRelative; } - if (relativePreference !== RelativePreference.Auto) Debug.assertNever(relativePreference); + if (relativePreference === RelativePreference.ExternalNonRelative) { + const projectDirectory = host.getCurrentDirectory(); + const modulePath = toPath(moduleFileName, projectDirectory, getCanonicalFileName); + const sourceIsInternal = startsWith(sourceDirectory, projectDirectory); + const targetIsInternal = startsWith(modulePath, projectDirectory); + if (sourceIsInternal && !targetIsInternal || !sourceIsInternal && targetIsInternal) { + // 1. The import path crosses the boundary of the tsconfig.json-containing directory. + // + // src/ + // tsconfig.json + // index.ts ------- + // lib/ | (path crosses tsconfig.json) + // imported.ts <--- + // + return nonRelative; + } + + const nearestTargetPackageJson = getNearestAncestorDirectoryWithPackageJson(host, getDirectoryPath(modulePath)); + const nearestSourcePackageJson = getNearestAncestorDirectoryWithPackageJson(host, sourceDirectory); + if (nearestSourcePackageJson !== nearestTargetPackageJson) { + // 2. The importing and imported files are part of different packages. + // + // packages/a/ + // package.json + // index.ts -------- + // packages/b/ | (path crosses package.json) + // package.json | + // component.ts <--- + // + return nonRelative; + } + + return relativePath; + } + + if (relativePreference !== RelativePreference.Shortest) Debug.assertNever(relativePreference); // Prefer a relative import over a baseUrl import if it has fewer components. return isPathRelativeToParent(nonRelative) || countPathComponents(relativePath) < countPathComponents(nonRelative) ? relativePath : nonRelative; @@ -210,6 +255,15 @@ namespace ts.moduleSpecifiers { ); } + function getNearestAncestorDirectoryWithPackageJson(host: ModuleSpecifierResolutionHost, fileName: string) { + if (host.getNearestAncestorDirectoryWithPackageJson) { + return host.getNearestAncestorDirectoryWithPackageJson(fileName); + } + return !!forEachAncestorDirectory(fileName, directory => { + return host.fileExists(combinePaths(directory, "package.json")) ? true : undefined; + }); + } + export function forEachFileNameOfModule( importingFileName: string, importedFileName: string, @@ -232,7 +286,7 @@ namespace ts.moduleSpecifiers { : discoverProbableSymlinks(host.getSourceFiles(), getCanonicalFileName, cwd); const symlinkedDirectories = links.getSymlinkedDirectories(); - const compareStrings = (!host.useCaseSensitiveFileNames || host.useCaseSensitiveFileNames()) ? compareStringsCaseSensitive : compareStringsCaseInsensitive; + const useCaseSensitiveFileNames = !host.useCaseSensitiveFileNames || host.useCaseSensitiveFileNames(); const result = symlinkedDirectories && forEachEntry(symlinkedDirectories, (resolved, path) => { if (resolved === false) return undefined; if (startsWithDirectory(importingFileName, resolved.realPath, getCanonicalFileName)) { @@ -240,7 +294,7 @@ namespace ts.moduleSpecifiers { } return forEach(targets, target => { - if (compareStrings(target.slice(0, resolved.real.length), resolved.real) !== Comparison.EqualTo) { + if (!containsPath(resolved.real, target, !useCaseSensitiveFileNames)) { return; } @@ -316,13 +370,49 @@ namespace ts.moduleSpecifiers { return sortedPaths; } - function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol): string | undefined { + function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol, checker: TypeChecker): string | undefined { const decl = find(moduleSymbol.declarations, d => isNonGlobalAmbientModule(d) && (!isExternalModuleAugmentation(d) || !isExternalModuleNameRelative(getTextOfIdentifierOrLiteral(d.name))) ) as (ModuleDeclaration & { name: StringLiteral }) | undefined; if (decl) { return decl.name.text; } + + // the module could be a namespace, which is export through "export=" from an ambient module. + /** + * declare module "m" { + * namespace ns { + * class c {} + * } + * export = ns; + * } + */ + // `import {c} from "m";` is valid, in which case, `moduleSymbol` is "ns", but the module name should be "m" + const ambientModuleDeclareCandidates = mapDefined(moduleSymbol.declarations, + d => { + if (!isModuleDeclaration(d)) return; + const topNamespace = getTopNamespace(d); + if (!(topNamespace?.parent?.parent + && isModuleBlock(topNamespace.parent) && isAmbientModule(topNamespace.parent.parent) && isSourceFile(topNamespace.parent.parent.parent))) return; + const exportAssignment = ((topNamespace.parent.parent.symbol.exports?.get("export=" as __String)?.valueDeclaration as ExportAssignment)?.expression as PropertyAccessExpression | Identifier); + if (!exportAssignment) return; + const exportSymbol = checker.getSymbolAtLocation(exportAssignment); + if (!exportSymbol) return; + const originalExportSymbol = exportSymbol?.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(exportSymbol) : exportSymbol; + if (originalExportSymbol === d.symbol) return topNamespace.parent.parent; + + function getTopNamespace(namespaceDeclaration: ModuleDeclaration) { + while (namespaceDeclaration.flags & NodeFlags.NestedNamespace) { + namespaceDeclaration = namespaceDeclaration.parent as ModuleDeclaration; + } + return namespaceDeclaration; + } + } + ); + const ambientModuleDeclare = ambientModuleDeclareCandidates[0] as (AmbientModuleDeclaration & { name: StringLiteral }) | undefined; + if (ambientModuleDeclare) { + return ambientModuleDeclare.name.text; + } } function tryGetModuleNameFromPaths(relativeToBaseUrlWithIndex: string, relativeToBaseUrl: string, paths: MapLike): string | undefined { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d036497cde19d..6e6f1dcc19777 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -21,7 +21,7 @@ namespace ts { let SourceFileConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node; /** - * NOTE: You should not use this, it is only exported to support `createNode` in `~/src/compat/deprecations.ts`. + * NOTE: You should not use this, it is only exported to support `createNode` in `~/src/deprecatedCompat/deprecations.ts`. */ /* @internal */ export const parseBaseNodeFactory: BaseNodeFactory = { @@ -614,7 +614,7 @@ namespace ts { } export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { - tracing.begin(tracing.Phase.Parse, "createSourceFile", { path: fileName }); + tracing.push(tracing.Phase.Parse, "createSourceFile", { path: fileName }, /*separateBeginAndEnd*/ true); performance.mark("beforeParse"); let result: SourceFile; @@ -629,7 +629,7 @@ namespace ts { performance.mark("afterParse"); performance.measure("Parse", "beforeParse", "afterParse"); - tracing.end(); + tracing.pop(); return result; } @@ -1767,6 +1767,7 @@ namespace ts { case SyntaxKind.DefaultKeyword: return nextTokenCanFollowDefaultKeyword(); case SyntaxKind.StaticKeyword: + return nextTokenIsOnSameLineAndCanFollowModifier(); case SyntaxKind.GetKeyword: case SyntaxKind.SetKeyword: nextToken(); @@ -2618,7 +2619,6 @@ namespace ts { const pos = getNodePos(); return finishNode( factory.createTemplateLiteralTypeSpan( - parseTemplateCasing(), parseType(), parseLiteralOfTemplateSpan(/*isTaggedTemplate*/ false) ), @@ -2626,14 +2626,6 @@ namespace ts { ); } - function parseTemplateCasing(): TemplateCasing { - return parseOptional(SyntaxKind.UppercaseKeyword) ? TemplateCasing.Uppercase : - parseOptional(SyntaxKind.LowercaseKeyword) ? TemplateCasing.Lowercase : - parseOptional(SyntaxKind.CapitalizeKeyword) ? TemplateCasing.Capitalize : - parseOptional(SyntaxKind.UncapitalizeKeyword) ? TemplateCasing.Uncapitalize : - TemplateCasing.None; - } - function parseLiteralOfTemplateSpan(isTaggedTemplate: boolean) { if (token() === SyntaxKind.CloseBraceToken) { reScanTemplateToken(isTaggedTemplate); @@ -4936,7 +4928,7 @@ namespace ts { } const tagName = parseJsxElementName(); - const typeArguments = tryParseTypeArguments(); + const typeArguments = (contextFlags & NodeFlags.JavaScriptFile) === 0 ? tryParseTypeArguments() : undefined; const attributes = parseJsxAttributes(); let node: JsxOpeningLikeElement; @@ -5201,7 +5193,8 @@ namespace ts { expression = parseMemberExpressionRest(pos, expression, /*allowOptionalChain*/ true); const questionDotToken = parseOptionalToken(SyntaxKind.QuestionDotToken); // handle 'foo<()' - if (token() === SyntaxKind.LessThanToken || token() === SyntaxKind.LessThanLessThanToken) { + // parse template arguments only in TypeScript files (not in JavaScript files). + if ((contextFlags & NodeFlags.JavaScriptFile) === 0 && (token() === SyntaxKind.LessThanToken || token() === SyntaxKind.LessThanLessThanToken)) { // See if this is the start of a generic invocation. If so, consume it and // keep checking for postfix expressions. Otherwise, it's just a '<' that's // part of an arithmetic expression. Break out so we consume it higher in the @@ -5247,6 +5240,11 @@ namespace ts { } function parseTypeArgumentsInExpression() { + if ((contextFlags & NodeFlags.JavaScriptFile) !== 0) { + // TypeArguments must not be parsed in JavaScript files to avoid ambiguity with binary operators. + return undefined; + } + if (reScanLessThanToken() !== SyntaxKind.LessThanToken) { return undefined; } @@ -6770,7 +6768,7 @@ namespace ts { const name = parseIdentifier(); const typeParameters = parseTypeParameters(); parseExpected(SyntaxKind.EqualsToken); - const type = parseType(); + const type = token() === SyntaxKind.IntrinsicKeyword && tryParse(parseKeywordAndNoDot) || parseType(); parseSemicolon(); const node = factory.createTypeAliasDeclaration(decorators, modifiers, name, typeParameters, type); return withJSDoc(finishNode(node, pos), hasJSDoc); @@ -6950,11 +6948,8 @@ namespace ts { parseExpected(SyntaxKind.EqualsToken); const moduleReference = parseModuleReference(); parseSemicolon(); - const node = factory.createImportEqualsDeclaration(decorators, modifiers, identifier, moduleReference); + const node = factory.createImportEqualsDeclaration(decorators, modifiers, isTypeOnly, identifier, moduleReference); const finished = withJSDoc(finishNode(node, pos), hasJSDoc); - if (isTypeOnly) { - parseErrorAtRange(finished, Diagnostics.Only_ECMAScript_imports_may_use_import_type); - } return finished; } @@ -7764,63 +7759,31 @@ namespace ts { } function parseAuthorTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocAuthorTag { - const authorInfoWithEmail = tryParse(() => tryParseAuthorNameAndEmail()); - if (!authorInfoWithEmail) { - const end = getNodePos(); - return finishNode(factory.createJSDocAuthorTag(tagName, parseTrailingTagComments(start, end, indent, indentText)), start, end); - } - - let comments = authorInfoWithEmail; - if (lookAhead(() => nextToken() !== SyntaxKind.NewLineTrivia)) { - const comment = parseTagComments(indent); - if (comment) { - comments += comment; - } - } - - return finishNode(factory.createJSDocAuthorTag(tagName, comments), start); + const comments = parseAuthorNameAndEmail() + (parseTrailingTagComments(start, end, indent, indentText) || ""); + return finishNode(factory.createJSDocAuthorTag(tagName, comments || undefined), start); } - function tryParseAuthorNameAndEmail(): string | undefined { + function parseAuthorNameAndEmail(): string { const comments: string[] = []; - let seenLessThan = false; - let seenGreaterThan = false; + let inEmail = false; let token = scanner.getToken(); - - loop: while (true) { - switch (token) { - case SyntaxKind.Identifier: - case SyntaxKind.WhitespaceTrivia: - case SyntaxKind.DotToken: - case SyntaxKind.AtToken: - comments.push(scanner.getTokenText()); - break; - case SyntaxKind.LessThanToken: - if (seenLessThan || seenGreaterThan) { - return; - } - seenLessThan = true; - comments.push(scanner.getTokenText()); - break; - case SyntaxKind.GreaterThanToken: - if (!seenLessThan || seenGreaterThan) { - return; - } - seenGreaterThan = true; - comments.push(scanner.getTokenText()); - scanner.setTextPos(scanner.getTokenPos() + 1); - break loop; - case SyntaxKind.NewLineTrivia: - case SyntaxKind.EndOfFileToken: - break loop; + while (token !== SyntaxKind.EndOfFileToken && token !== SyntaxKind.NewLineTrivia) { + if (token === SyntaxKind.LessThanToken) { + inEmail = true; } - + else if (token === SyntaxKind.AtToken && !inEmail) { + break; + } + else if (token === SyntaxKind.GreaterThanToken && inEmail) { + comments.push(scanner.getTokenText()); + scanner.setTextPos(scanner.getTokenPos() + 1); + break; + } + comments.push(scanner.getTokenText()); token = nextTokenJSDoc(); } - if (seenLessThan && seenGreaterThan) { - return comments.length === 0 ? undefined : comments.join(""); - } + return comments.join(""); } function parseImplementsTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocImplementsTag { diff --git a/src/compiler/path.ts b/src/compiler/path.ts index d7df5eefb2f4f..d4ee8600d7190 100644 --- a/src/compiler/path.ts +++ b/src/compiler/path.ts @@ -6,7 +6,7 @@ namespace ts { * we expect the host to correctly handle paths in our specified format. */ export const directorySeparator = "/"; - const altDirectorySeparator = "\\"; + export const altDirectorySeparator = "\\"; const urlSchemeSeparator = "://"; const backslashRegExp = /\\/g; diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index dcee41c2d2c70..e1e26831cb18f 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -1,16 +1,11 @@ /*@internal*/ /** Performance measurements for the compiler. */ namespace ts.performance { - declare const onProfilerEvent: { (markName: string): void; profiler: boolean; }; - - // NOTE: cannot use ts.noop as core.ts loads after this - const profilerEvent: (markName: string) => void = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true ? onProfilerEvent : () => { /*empty*/ }; - - let enabled = false; - let profilerStart = 0; - let counts: ESMap; - let marks: ESMap; - let measures: ESMap; + let perfHooks: PerformanceHooks | undefined; + let perfObserver: PerformanceObserver | 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; @@ -46,6 +41,8 @@ namespace ts.performance { } export const nullTimer: Timer = { enter: noop, exit: noop }; + const counts = new Map(); + const durations = new Map(); /** * Marks a performance event. @@ -53,11 +50,7 @@ namespace ts.performance { * @param markName The name of the mark. */ export function mark(markName: string) { - if (enabled) { - marks.set(markName, timestamp()); - counts.set(markName, (counts.get(markName) || 0) + 1); - profilerEvent(markName); - } + performanceImpl?.mark(markName); } /** @@ -70,11 +63,7 @@ namespace ts.performance { * used. */ export function measure(measureName: string, startMarkName?: string, endMarkName?: string) { - if (enabled) { - const end = endMarkName && marks.get(endMarkName) || timestamp(); - const start = startMarkName && marks.get(startMarkName) || profilerStart; - measures.set(measureName, (measures.get(measureName) || 0) + (end - start)); - } + performanceImpl?.measure(measureName, startMarkName, endMarkName); } /** @@ -83,7 +72,7 @@ namespace ts.performance { * @param markName The name of the mark. */ export function getCount(markName: string) { - return counts && counts.get(markName) || 0; + return counts.get(markName) || 0; } /** @@ -92,7 +81,7 @@ namespace ts.performance { * @param measureName The name of the measure whose durations should be accumulated. */ export function getDuration(measureName: string) { - return measures && measures.get(measureName) || 0; + return durations.get(measureName) || 0; } /** @@ -101,22 +90,42 @@ namespace ts.performance { * @param cb The action to perform for each measure */ export function forEachMeasure(cb: (measureName: string, duration: number) => void) { - measures.forEach((measure, key) => { - cb(key, measure); - }); + durations.forEach((duration, measureName) => cb(measureName, duration)); + } + + /** + * Indicates whether the performance API is enabled. + */ + export function isEnabled() { + return !!performanceImpl; } /** Enables (and resets) performance measurements for the compiler. */ export function enable() { - counts = new Map(); - marks = new Map(); - measures = new Map(); - enabled = true; - profilerStart = timestamp(); + if (!performanceImpl) { + perfHooks ||= tryGetNativePerformanceHooks(); + if (!perfHooks) return false; + perfObserver ||= new perfHooks.PerformanceObserver(updateStatisticsFromList); + perfObserver.observe({ entryTypes: ["mark", "measure"] }); + performanceImpl = perfHooks.performance; + } + return true; } /** Disables performance measurements for the compiler. */ export function disable() { - enabled = false; + perfObserver?.disconnect(); + performanceImpl = undefined; + counts.clear(); + durations.clear(); + } + + function updateStatisticsFromList(list: PerformanceObserverEntryList) { + for (const mark of list.getEntriesByType("mark")) { + counts.set(mark.name, (counts.get(mark.name) || 0) + 1); + } + for (const measure of list.getEntriesByType("measure")) { + durations.set(measure.name, (durations.get(measure.name) || 0) + measure.duration); + } } } diff --git a/src/compiler/performanceCore.ts b/src/compiler/performanceCore.ts new file mode 100644 index 0000000000000..acec7f4ce5700 --- /dev/null +++ b/src/compiler/performanceCore.ts @@ -0,0 +1,121 @@ +/*@internal*/ +namespace ts { + // The following definitions provide the minimum compatible support for the Web Performance User Timings API + // between browsers and NodeJS: + + export interface PerformanceHooks { + performance: Performance; + PerformanceObserver: PerformanceObserverConstructor; + } + + export interface Performance { + mark(name: string): void; + measure(name: string, startMark?: string, endMark?: 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 string[] }): 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 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 PerformanceObserver === "function"; + } + + function tryGetWebPerformanceHooks(): PerformanceHooks | undefined { + if (typeof performance === "object" && + typeof PerformanceObserver === "function" && + hasRequiredAPI(performance, PerformanceObserver)) { + return { + performance, + PerformanceObserver + }; + } + } + + function tryGetNodePerformanceHooks(): PerformanceHooks | undefined { + if (typeof module === "object" && typeof require === "function") { + try { + const { performance, PerformanceObserver } = require("perf_hooks") as typeof import("perf_hooks"); + if (hasRequiredAPI(performance, PerformanceObserver)) { + // 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)) { + return { + performance: { + get timeOrigin() { return performance.timeOrigin; }, + now() { return performance.now(); }, + mark(name) { return performance.mark(name); }, + measure(name, start = "nodeStart", end?) { + if (end === undefined) { + end = "__performance.measure-fix__"; + performance.mark(end); + } + performance.measure(name, start, end); + if (end === "__performance.measure-fix__") { + performance.clearMarks("__performance.measure-fix__"); + } + } + }, + PerformanceObserver + }; + } + return { + 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; + + 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()); +} \ No newline at end of file diff --git a/src/compiler/performanceTimestamp.ts b/src/compiler/performanceTimestamp.ts deleted file mode 100644 index 044e7a65c0992..0000000000000 --- a/src/compiler/performanceTimestamp.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*@internal*/ -namespace ts { - declare const performance: { now?(): number } | undefined; - /** Gets a timestamp with (at least) ms resolution */ - export const timestamp = typeof performance !== "undefined" && performance.now ? () => performance.now!() : Date.now ? Date.now : () => +(new Date()); -} \ No newline at end of file diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 76fa4b62aab9c..7b71f011700c1 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -13,7 +13,7 @@ namespace ts { } /* @internal */ - export function computeCommonSourceDirectoryOfFilenames(fileNames: string[], currentDirectory: string, getCanonicalFileName: GetCanonicalFileName): string { + export function computeCommonSourceDirectoryOfFilenames(fileNames: readonly string[], currentDirectory: string, getCanonicalFileName: GetCanonicalFileName): string { let commonPathComponents: string[] | undefined; const failed = forEach(fileNames, sourceFile => { // Each file contributes into common source file path @@ -73,6 +73,7 @@ namespace ts { export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost { const existingDirectories = new Map(); const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames); + const computeHash = maybeBind(system, system.createHash) || generateDjb2Hash; function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile | undefined { let text: string | undefined; try { @@ -128,7 +129,7 @@ namespace ts { let outputFingerprints: ESMap; function writeFileWorker(fileName: string, data: string, writeByteOrderMark: boolean) { - if (!isWatchSet(options) || !system.createHash || !system.getModifiedTime) { + if (!isWatchSet(options) || !system.getModifiedTime) { system.writeFile(fileName, data, writeByteOrderMark); return; } @@ -137,7 +138,7 @@ namespace ts { outputFingerprints = new Map(); } - const hash = system.createHash(data); + const hash = computeHash(data); const mtimeBefore = system.getModifiedTime(fileName); if (mtimeBefore) { @@ -465,20 +466,19 @@ namespace ts { if (diagnostic.file) { output += host.getNewLine(); output += formatCodeSpan(diagnostic.file, diagnostic.start!, diagnostic.length!, "", getCategoryFormat(diagnostic.category), host); // TODO: GH#18217 - if (diagnostic.relatedInformation) { - output += host.getNewLine(); - for (const { file, start, length, messageText } of diagnostic.relatedInformation) { - if (file) { - output += host.getNewLine(); - output += halfIndent + formatLocation(file, start!, host); // TODO: GH#18217 - output += formatCodeSpan(file, start!, length!, indent, ForegroundColorEscapeSequences.Cyan, host); // TODO: GH#18217 - } + } + if (diagnostic.relatedInformation) { + output += host.getNewLine(); + for (const { file, start, length, messageText } of diagnostic.relatedInformation) { + if (file) { output += host.getNewLine(); - output += indent + flattenDiagnosticMessageText(messageText, host.getNewLine()); + output += halfIndent + formatLocation(file, start!, host); // TODO: GH#18217 + output += formatCodeSpan(file, start!, length!, indent, ForegroundColorEscapeSequences.Cyan, host); // TODO: GH#18217 } + output += host.getNewLine(); + output += indent + flattenDiagnosticMessageText(messageText, host.getNewLine()); } } - output += host.getNewLine(); } return output; @@ -529,6 +529,51 @@ namespace ts { return resolutions; } + /* @internal */ + export function forEachResolvedProjectReference( + resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined, + cb: (resolvedProjectReference: ResolvedProjectReference, parent: ResolvedProjectReference | undefined) => T | undefined + ): T | undefined { + return forEachProjectReference(/*projectReferences*/ undefined, resolvedProjectReferences, (resolvedRef, parent) => resolvedRef && cb(resolvedRef, parent)); + } + + function forEachProjectReference( + projectReferences: readonly ProjectReference[] | undefined, + resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined, + cbResolvedRef: (resolvedRef: ResolvedProjectReference | undefined, parent: ResolvedProjectReference | undefined, index: number) => T | undefined, + cbRef?: (projectReferences: readonly ProjectReference[] | undefined, parent: ResolvedProjectReference | undefined) => T | undefined + ): T | undefined { + let seenResolvedRefs: Set | undefined; + + return worker(projectReferences, resolvedProjectReferences, /*parent*/ undefined); + + function worker( + projectReferences: readonly ProjectReference[] | undefined, + resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined, + parent: ResolvedProjectReference | undefined, + ): T | undefined { + + // Visit project references first + if (cbRef) { + const result = cbRef(projectReferences, parent); + if (result) { return result; } + } + + return forEach(resolvedProjectReferences, (resolvedRef, index) => { + if (resolvedRef && seenResolvedRefs?.has(resolvedRef.sourceFile.path)) { + // ignore recursives + return undefined; + } + + const result = cbResolvedRef(resolvedRef, parent, index); + if (result || !resolvedRef) return result; + + (seenResolvedRefs ||= new Set()).add(resolvedRef.sourceFile.path); + return worker(resolvedRef.commandLine.projectReferences, resolvedRef.references, resolvedRef); + }); + } + } + /* @internal */ export const inferredTypesContainingFile = "__inferred type names__.ts"; @@ -537,10 +582,66 @@ namespace ts { allDiagnostics?: readonly T[]; } - interface RefFile extends TextRange { - kind: RefFileKind; - index: number; + /*@internal*/ + export function isReferencedFile(reason: FileIncludeReason | undefined): reason is ReferencedFile { + switch (reason?.kind) { + case FileIncludeKind.Import: + case FileIncludeKind.ReferenceFile: + case FileIncludeKind.TypeReferenceDirective: + case FileIncludeKind.LibReferenceDirective: + return true; + default: + return false; + } + } + + /*@internal*/ + export interface ReferenceFileLocation { + file: SourceFile; + pos: number; + end: number; + packageId: PackageId | undefined; + } + + /*@internal*/ + export interface SyntheticReferenceFileLocation { file: SourceFile; + packageId: PackageId | undefined; + text: string; + } + + /*@internal*/ + export function isReferenceFileLocation(location: ReferenceFileLocation | SyntheticReferenceFileLocation): location is ReferenceFileLocation { + return (location as ReferenceFileLocation).pos !== undefined; + } + + /*@internal*/ + export function getReferencedFileLocation(getSourceFileByPath: (path: Path) => SourceFile | undefined, ref: ReferencedFile): ReferenceFileLocation | SyntheticReferenceFileLocation { + const file = Debug.checkDefined(getSourceFileByPath(ref.file)); + const { kind, index } = ref; + let pos: number | undefined, end: number | undefined, packageId: PackageId | undefined; + switch (kind) { + case FileIncludeKind.Import: + const importLiteral = getModuleNameStringLiteralAt(file, index); + packageId = file.resolvedModules?.get(importLiteral.text)?.packageId; + if (importLiteral.pos === -1) return { file, packageId, text: importLiteral.text }; + pos = skipTrivia(file.text, importLiteral.pos); + end = importLiteral.end; + break; + case FileIncludeKind.ReferenceFile: + ({ pos, end } = file.referencedFiles[index]); + break; + case FileIncludeKind.TypeReferenceDirective: + ({ pos, end } = file.typeReferenceDirectives[index]); + packageId = file.resolvedTypeReferenceDirectiveNames?.get(toFileNameLowerCase(file.typeReferenceDirectives[index].fileName))?.packageId; + break; + case FileIncludeKind.LibReferenceDirective: + ({ pos, end } = file.libReferenceDirectives[index]); + break; + default: + return Debug.assertNever(kind); + } + return { file, pos, end, packageId }; } /** @@ -708,14 +809,12 @@ namespace ts { let noDiagnosticsTypeChecker: TypeChecker; let classifiableNames: Set<__String>; const ambientModuleNameToUnmodifiedFileName = new Map(); - // Todo:: Use this to report why file was included in --extendedDiagnostics - let refFileMap: MultiMap | undefined; - + let fileReasons = createMultiMap(); const cachedBindAndCheckDiagnosticsForFile: DiagnosticCache = {}; const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {}; let resolvedTypeReferenceDirectives = new Map(); - let fileProcessingDiagnostics = createDiagnosticCollection(); + let fileProcessingDiagnostics: FilePreprocessingDiagnostics[] | undefined; // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules. // This works as imported modules are discovered recursively in a depth first manner, specifically: @@ -734,7 +833,7 @@ namespace ts { // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled. const sourceFilesFoundSearchingNodeModules = new Map(); - tracing.begin(tracing.Phase.Program, "createProgram", {}); + tracing.push(tracing.Phase.Program, "createProgram", { configFilePath: options.configFilePath, rootDir: options.rootDir }, /*separateBeginAndEnd*/ true); performance.mark("beforeProgram"); const host = createProgramOptions.host || createCompilerHost(options); @@ -750,7 +849,7 @@ namespace ts { // Map storing if there is emit blocking diagnostics for given input const hasEmitBlockingDiagnostics = new Map(); - let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression | null | undefined; + let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression | false | undefined; let moduleResolutionCache: ModuleResolutionCache | undefined; let actualResolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[]; @@ -820,12 +919,16 @@ namespace ts { forEachResolvedProjectReference }); + tracing.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram }); const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options); + tracing.pop(); // We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks // `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`. - let structuralIsReused: StructureIsReused | undefined; - structuralIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const - if (structuralIsReused !== StructureIsReused.Completely) { + let structureIsReused: StructureIsReused; + tracing.push(tracing.Phase.Program, "tryReuseStructureFromOldProgram", {}); + structureIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const + tracing.pop(); + if (structureIsReused !== StructureIsReused.Completely) { processingDefaultLibFiles = []; processingOtherFiles = []; @@ -834,45 +937,50 @@ namespace ts { resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile); } if (rootNames.length) { - for (const parsedRef of resolvedProjectReferences) { - if (!parsedRef) continue; + resolvedProjectReferences?.forEach((parsedRef, index) => { + if (!parsedRef) return; const out = outFile(parsedRef.commandLine.options); if (useSourceOfProjectReferenceRedirect) { if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) { for (const fileName of parsedRef.commandLine.fileNames) { - processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined); + processProjectReferenceFile(fileName, { kind: FileIncludeKind.SourceFromProjectReference, index }); } } } else { if (out) { - processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined); + processProjectReferenceFile(changeExtension(out, ".d.ts"), { kind: FileIncludeKind.OutputFromProjectReference, index }); } else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) { + const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(parsedRef.commandLine, !host.useCaseSensitiveFileNames())); for (const fileName of parsedRef.commandLine.fileNames) { if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) { - processSourceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames()), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined); + processProjectReferenceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory), { kind: FileIncludeKind.OutputFromProjectReference, index }); } } } } - } + }); } } - forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false)); + tracing.push(tracing.Phase.Program, "processRootFiles", { count: rootNames.length }); + forEach(rootNames, (name, index) => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.RootFile, index })); + tracing.pop(); // load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders const typeReferences: string[] = rootNames.length ? getAutomaticTypeDirectiveNames(options, host) : emptyArray; if (typeReferences.length) { + tracing.push(tracing.Phase.Program, "processTypeReferences", { count: typeReferences.length }); // This containingFilename needs to match with the one used in managed-side const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory(); const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile); const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename); for (let i = 0; i < typeReferences.length; i++) { - processTypeReferenceDirective(typeReferences[i], resolutions[i]); + processTypeReferenceDirective(typeReferences[i], resolutions[i], { kind: FileIncludeKind.AutomaticTypeDirectiveFile, typeReference: typeReferences[i], packageId: resolutions[i]?.packageId }); } + tracing.pop(); } // Do not process the default library if: @@ -884,11 +992,11 @@ namespace ts { // otherwise, using options specified in '--lib' instead of '--target' default library file const defaultLibraryFileName = getDefaultLibraryFileName(); if (!options.lib && defaultLibraryFileName) { - processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false); + processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile }); } else { - forEach(options.lib, libFileName => { - processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false); + forEach(options.lib, (libFileName, index) => { + processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile, index }); }); } } @@ -913,8 +1021,8 @@ namespace ts { host.onReleaseOldSourceFile(oldSourceFile, oldProgram.getCompilerOptions(), !!getSourceFileByPath(oldSourceFile.path)); } } - oldProgram.forEachResolvedProjectReference((resolvedProjectReference, resolvedProjectReferencePath) => { - if (resolvedProjectReference && !getResolvedProjectReferenceByPath(resolvedProjectReferencePath)) { + oldProgram.forEachResolvedProjectReference(resolvedProjectReference => { + if (!getResolvedProjectReferenceByPath(resolvedProjectReference.sourceFile.path)) { host.onReleaseOldSourceFile!(resolvedProjectReference.sourceFile, oldProgram!.getCompilerOptions(), /*hasSourceFileByPath*/ false); } }); @@ -929,13 +1037,13 @@ namespace ts { getSourceFileByPath, getSourceFiles: () => files, getMissingFilePaths: () => missingFilePaths!, // TODO: GH#18217 - getRefFileMap: () => refFileMap, getFilesByNameMap: () => filesByName, getCompilerOptions: () => options, getSyntacticDiagnostics, getOptionsDiagnostics, getGlobalDiagnostics, getSemanticDiagnostics, + getCachedSemanticDiagnostics, getSuggestionDiagnostics, getDeclarationDiagnostics, getBindAndCheckDiagnostics, @@ -978,32 +1086,89 @@ namespace ts { getSymlinkCache, realpath: host.realpath?.bind(host), useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(), + getFileIncludeReasons: () => fileReasons, + structureIsReused, }; onProgramCreateComplete(); + + // Add file processingDiagnostics + fileProcessingDiagnostics?.forEach(diagnostic => { + switch (diagnostic.kind) { + case FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic: + return programDiagnostics.add(createDiagnosticExplainingFile(diagnostic.file && getSourceFileByPath(diagnostic.file), diagnostic.fileProcessingReason, diagnostic.diagnostic, diagnostic.args || emptyArray)); + case FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic: + const { file, pos, end } = getReferencedFileLocation(getSourceFileByPath, diagnostic.reason) as ReferenceFileLocation; + return programDiagnostics.add(createFileDiagnostic(file, Debug.checkDefined(pos), Debug.checkDefined(end) - pos, diagnostic.diagnostic, ...diagnostic.args || emptyArray)); + default: + Debug.assertNever(diagnostic); + } + }); + verifyCompilerOptions(); performance.mark("afterProgram"); performance.measure("Program", "beforeProgram", "afterProgram"); - tracing.end(); + tracing.pop(); return program; - function resolveModuleNamesWorker(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) { + function resolveModuleNamesWorker(moduleNames: string[], containingFile: SourceFile, reusedNames: string[] | undefined): readonly ResolvedModuleFull[] { + if (!moduleNames.length) return emptyArray; + const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory); + const redirectedReference = getRedirectReferenceForResolution(containingFile); + tracing.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName }); performance.mark("beforeResolveModule"); - const result = actualResolveModuleNamesWorker(moduleNames, containingFile, reusedNames, redirectedReference); + const result = actualResolveModuleNamesWorker(moduleNames, containingFileName, reusedNames, redirectedReference); performance.mark("afterResolveModule"); performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule"); + tracing.pop(); return result; } - function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference) { + function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[], containingFile: string | SourceFile): readonly (ResolvedTypeReferenceDirective | undefined)[] { + if (!typeDirectiveNames.length) return []; + const containingFileName = !isString(containingFile) ? getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory) : containingFile; + const redirectedReference = !isString(containingFile) ? getRedirectReferenceForResolution(containingFile) : undefined; + tracing.push(tracing.Phase.Program, "resolveTypeReferenceDirectiveNamesWorker", { containingFileName }); performance.mark("beforeResolveTypeReference"); - const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFile, redirectedReference); + const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference); performance.mark("afterResolveTypeReference"); performance.measure("ResolveTypeReference", "beforeResolveTypeReference", "afterResolveTypeReference"); + tracing.pop(); return result; } + function getRedirectReferenceForResolution(file: SourceFile) { + const redirect = getResolvedProjectReferenceToRedirect(file.originalFileName); + if (redirect || !fileExtensionIs(file.originalFileName, Extension.Dts)) return redirect; + + // The originalFileName could not be actual source file name if file found was d.ts from referecned project + // So in this case try to look up if this is output from referenced project, if it is use the redirected project in that case + const resultFromDts = getRedirectReferenceForResolutionFromSourceOfProject(file.originalFileName, file.path); + if (resultFromDts) return resultFromDts; + + // If preserveSymlinks is true, module resolution wont jump the symlink + // but the resolved real path may be the .d.ts from project reference + // Note:: Currently we try the real path only if the + // file is from node_modules to avoid having to run real path on all file paths + if (!host.realpath || !options.preserveSymlinks || !stringContains(file.originalFileName, nodeModulesPathPart)) return undefined; + const realDeclarationFileName = host.realpath(file.originalFileName); + const realDeclarationPath = toPath(realDeclarationFileName); + return realDeclarationPath === file.path ? undefined : getRedirectReferenceForResolutionFromSourceOfProject(realDeclarationFileName, realDeclarationPath); + } + + function getRedirectReferenceForResolutionFromSourceOfProject(fileName: string, filePath: Path) { + const source = getSourceOfProjectReferenceRedirect(fileName); + if (isString(source)) return getResolvedProjectReferenceToRedirect(source); + if (!source) return undefined; + // Output of .d.ts file so return resolved ref that matches the out file name + return forEachResolvedProjectReference(resolvedRef => { + const out = outFile(resolvedRef.commandLine.options); + if (!out) return undefined; + return toPath(out) === filePath ? resolvedRef : undefined; + }); + } + function compareDefaultLibFiles(a: SourceFile, b: SourceFile) { return compareValues(getDefaultLibFilePriority(a), getDefaultLibFilePriority(b)); } @@ -1030,25 +1195,13 @@ namespace ts { function getCommonSourceDirectory() { if (commonSourceDirectory === undefined) { const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, program)); - if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) { - // If a rootDir is specified use it as the commonSourceDirectory - commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory); - } - else if (options.composite && options.configFilePath) { - // Project compilations never infer their root from the input source paths - commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath)); - checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory); - } - else { - commonSourceDirectory = computeCommonSourceDirectory(emittedFiles); - } - - if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) { - // Make sure directory path ends with directory separator so this string can directly - // used to replace with "" to get the relative path of the source file and the relative path doesn't - // start with / making it rooted path - commonSourceDirectory += directorySeparator; - } + commonSourceDirectory = ts.getCommonSourceDirectory( + options, + () => mapDefined(emittedFiles, file => file.isDeclarationFile ? undefined : file.fileName), + currentDirectory, + getCanonicalFileName, + commonSourceDirectory => checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory) + ); } return commonSourceDirectory; } @@ -1067,14 +1220,14 @@ namespace ts { return classifiableNames; } - function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile) { - if (structuralIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) { + function resolveModuleNamesReusingOldState(moduleNames: string[], file: SourceFile): readonly ResolvedModuleFull[] { + if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) { // If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules, // the best we can do is fallback to the default logic. - return resolveModuleNamesWorker(moduleNames, containingFile, /*reusedNames*/ undefined, getResolvedProjectReferenceToRedirect(file.originalFileName)); + return resolveModuleNamesWorker(moduleNames, file, /*reusedNames*/ undefined); } - const oldSourceFile = oldProgram && oldProgram.getSourceFile(containingFile); + const oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName); if (oldSourceFile !== file && file.resolvedModules) { // `file` was created for the new program. // @@ -1119,7 +1272,7 @@ namespace ts { const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName); if (oldResolvedModule) { if (isTraceEnabled(options, host)) { - trace(host, Diagnostics.Reusing_resolution_of_module_0_to_file_1_from_old_program, moduleName, containingFile); + trace(host, Diagnostics.Reusing_resolution_of_module_0_to_file_1_from_old_program, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory)); } (result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule; (reusedNames || (reusedNames = [])).push(moduleName); @@ -1134,7 +1287,7 @@ namespace ts { if (contains(file.ambientModuleNames, moduleName)) { resolvesToAmbientModuleInNonModifiedFile = true; if (isTraceEnabled(options, host)) { - trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, containingFile); + trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory)); } } else { @@ -1151,7 +1304,7 @@ namespace ts { } const resolutions = unknownModuleNames && unknownModuleNames.length - ? resolveModuleNamesWorker(unknownModuleNames, containingFile, reusedNames, getResolvedProjectReferenceToRedirect(file.originalFileName)) + ? resolveModuleNamesWorker(unknownModuleNames, file, reusedNames) : emptyArray; // Combine results of resolutions and predicted results @@ -1210,7 +1363,7 @@ namespace ts { return !forEachProjectReference( oldProgram!.getProjectReferences(), oldProgram!.getResolvedProjectReferences(), - (oldResolvedRef, index, parent) => { + (oldResolvedRef, parent, index) => { const newRef = (parent ? parent.commandLine.projectReferences : projectReferences)![index]; const newResolvedRef = parseProjectReferenceConfigFile(newRef); if (oldResolvedRef) { @@ -1239,22 +1392,22 @@ namespace ts { // if any of these properties has changed - structure cannot be reused const oldOptions = oldProgram.getCompilerOptions(); if (changesAffectModuleResolution(oldOptions, options)) { - return oldProgram.structureIsReused = StructureIsReused.Not; + return StructureIsReused.Not; } // there is an old program, check if we can reuse its structure const oldRootNames = oldProgram.getRootFileNames(); if (!arrayIsEqualTo(oldRootNames, rootNames)) { - return oldProgram.structureIsReused = StructureIsReused.Not; + return StructureIsReused.Not; } if (!arrayIsEqualTo(options.types, oldOptions.types)) { - return oldProgram.structureIsReused = StructureIsReused.Not; + return StructureIsReused.Not; } // Check if any referenced project tsconfig files are different if (!canReuseProjectReferences()) { - return oldProgram.structureIsReused = StructureIsReused.Not; + return StructureIsReused.Not; } if (projectReferences) { resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile); @@ -1263,13 +1416,13 @@ namespace ts { // check if program source files has changed in the way that can affect structure of the program const newSourceFiles: SourceFile[] = []; const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = []; - oldProgram.structureIsReused = StructureIsReused.Completely; + structureIsReused = StructureIsReused.Completely; // If the missing file paths are now present, it can change the progam structure, // and hence cant reuse the structure. // This is same as how we dont reuse the structure if one of the file from old program is now missing if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) { - return oldProgram.structureIsReused = StructureIsReused.Not; + return StructureIsReused.Not; } const oldSourceFiles = oldProgram.getSourceFiles(); @@ -1282,7 +1435,7 @@ namespace ts { : host.getSourceFile(oldSourceFile.fileName, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile); // TODO: GH#18217 if (!newSourceFile) { - return oldProgram.structureIsReused = StructureIsReused.Not; + return StructureIsReused.Not; } Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`"); @@ -1293,7 +1446,7 @@ namespace ts { // This lets us know if the unredirected file has changed. If it has we should break the redirect. if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) { // Underlying file has changed. Might not redirect anymore. Must rebuild program. - return oldProgram.structureIsReused = StructureIsReused.Not; + return StructureIsReused.Not; } fileChanged = false; newSourceFile = oldSourceFile; // Use the redirect. @@ -1301,7 +1454,7 @@ namespace ts { else if (oldProgram.redirectTargetsMap.has(oldSourceFile.path)) { // If a redirected-to source file changes, the redirect may be broken. if (newSourceFile !== oldSourceFile) { - return oldProgram.structureIsReused = StructureIsReused.Not; + return StructureIsReused.Not; } fileChanged = false; } @@ -1322,7 +1475,7 @@ namespace ts { const prevKind = seenPackageNames.get(packageName); const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists; if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) { - return oldProgram.structureIsReused = StructureIsReused.Not; + return StructureIsReused.Not; } seenPackageNames.set(packageName, newKind); } @@ -1332,39 +1485,39 @@ namespace ts { if (!arrayIsEqualTo(oldSourceFile.libReferenceDirectives, newSourceFile.libReferenceDirectives, fileReferenceIsEqualTo)) { // 'lib' references has changed. Matches behavior in changesAffectModuleResolution - return oldProgram.structureIsReused = StructureIsReused.Not; + return StructureIsReused.Not; } if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) { // value of no-default-lib has changed // this will affect if default library is injected into the list of files - oldProgram.structureIsReused = StructureIsReused.SafeModules; + structureIsReused = StructureIsReused.SafeModules; } // check tripleslash references if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) { // tripleslash references has changed - oldProgram.structureIsReused = StructureIsReused.SafeModules; + structureIsReused = StructureIsReused.SafeModules; } // check imports and module augmentations collectExternalModuleReferences(newSourceFile); if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) { // imports has changed - oldProgram.structureIsReused = StructureIsReused.SafeModules; + structureIsReused = StructureIsReused.SafeModules; } if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) { // moduleAugmentations has changed - oldProgram.structureIsReused = StructureIsReused.SafeModules; + structureIsReused = StructureIsReused.SafeModules; } if ((oldSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags) !== (newSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags)) { // dynamicImport has changed - oldProgram.structureIsReused = StructureIsReused.SafeModules; + structureIsReused = StructureIsReused.SafeModules; } if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) { // 'types' references has changed - oldProgram.structureIsReused = StructureIsReused.SafeModules; + structureIsReused = StructureIsReused.SafeModules; } // tentatively approve the file @@ -1372,7 +1525,7 @@ namespace ts { } else if (hasInvalidatedResolution(oldSourceFile.path)) { // 'module/types' references could have changed - oldProgram.structureIsReused = StructureIsReused.SafeModules; + structureIsReused = StructureIsReused.SafeModules; // add file to the modified list so that we will resolve it later modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile }); @@ -1382,8 +1535,8 @@ namespace ts { newSourceFiles.push(newSourceFile); } - if (oldProgram.structureIsReused !== StructureIsReused.Completely) { - return oldProgram.structureIsReused; + if (structureIsReused !== StructureIsReused.Completely) { + return structureIsReused; } const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile); @@ -1396,44 +1549,40 @@ namespace ts { } // try to verify results of module resolution for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) { - const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.originalFileName, currentDirectory); const moduleNames = getModuleNames(newSourceFile); - const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile); + const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFile); // ensure that module resolution results are still correct const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo); if (resolutionsChanged) { - oldProgram.structureIsReused = StructureIsReused.SafeModules; + structureIsReused = StructureIsReused.SafeModules; newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions); } else { newSourceFile.resolvedModules = oldSourceFile.resolvedModules; } - if (resolveTypeReferenceDirectiveNamesWorker) { - // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. - const typesReferenceDirectives = map(newSourceFile.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName)); - const resolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFilePath, getResolvedProjectReferenceToRedirect(newSourceFile.originalFileName)); - // ensure that types resolutions are still correct - const resolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, resolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo); - if (resolutionsChanged) { - oldProgram.structureIsReused = StructureIsReused.SafeModules; - newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, resolutions); - } - else { - newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames; - } + // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. + const typesReferenceDirectives = map(newSourceFile.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName)); + const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFile); + // ensure that types resolutions are still correct + const typeReferenceEesolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo); + if (typeReferenceEesolutionsChanged) { + structureIsReused = StructureIsReused.SafeModules; + newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, typeReferenceResolutions); + } + else { + newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames; } } - if (oldProgram.structureIsReused !== StructureIsReused.Completely) { - return oldProgram.structureIsReused; + if (structureIsReused !== StructureIsReused.Completely) { + return structureIsReused; } if (host.hasChangedAutomaticTypeDirectiveNames?.()) { - return oldProgram.structureIsReused = StructureIsReused.SafeModules; + return StructureIsReused.SafeModules; } missingFilePaths = oldProgram.getMissingFilePaths(); - refFileMap = oldProgram.getRefFileMap(); // update fileName -> file mapping Debug.assert(newSourceFiles.length === oldProgram.getSourceFiles().length); @@ -1457,17 +1606,14 @@ namespace ts { }); files = newSourceFiles; + fileReasons = oldProgram.getFileIncludeReasons(); fileProcessingDiagnostics = oldProgram.getFileProcessingDiagnostics(); - - for (const modifiedFile of modifiedSourceFiles) { - fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile); - } resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives(); sourceFileToPackageName = oldProgram.sourceFileToPackageName; redirectTargetsMap = oldProgram.redirectTargetsMap; - return oldProgram.structureIsReused = StructureIsReused.Completely; + return StructureIsReused.Completely; } function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost { @@ -1508,7 +1654,7 @@ namespace ts { function emitBuildInfo(writeFileCallback?: WriteFileCallback): EmitResult { Debug.assert(!outFile(options)); - tracing.begin(tracing.Phase.Emit, "emitBuildInfo", {}); + tracing.push(tracing.Phase.Emit, "emitBuildInfo", {}, /*separateBeginAndEnd*/ true); performance.mark("beforeEmit"); const emitResult = emitFiles( notImplementedResolver, @@ -1521,7 +1667,7 @@ namespace ts { performance.mark("afterEmit"); performance.measure("Emit", "beforeEmit", "afterEmit"); - tracing.end(); + tracing.pop(); return emitResult; } @@ -1582,9 +1728,9 @@ namespace ts { } function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult { - tracing.begin(tracing.Phase.Emit, "emit", { path: sourceFile?.path }); + tracing.push(tracing.Phase.Emit, "emit", { path: sourceFile?.path }, /*separateBeginAndEnd*/ true); const result = runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers, forceDtsEmit)); - tracing.end(); + tracing.pop(); return result; } @@ -1656,6 +1802,12 @@ namespace ts { return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken); } + function getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined { + return sourceFile + ? cachedBindAndCheckDiagnosticsForFile.perFile?.get(sourceFile.path) + : cachedBindAndCheckDiagnosticsForFile.allDiagnostics; + } + function getBindAndCheckDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] { return getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken); } @@ -1665,19 +1817,12 @@ namespace ts { return emptyArray; } - const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics.getDiagnostics(sourceFile.fileName); const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName); - - return getMergedProgramDiagnostics(sourceFile, fileProcessingDiagnosticsInFile, programDiagnosticsInFile); - } - - function getMergedProgramDiagnostics(sourceFile: SourceFile, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) { - const flatDiagnostics = flatten(allDiagnostics); if (!sourceFile.commentDirectives?.length) { - return flatDiagnostics; + return programDiagnosticsInFile; } - return getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics).diagnostics; + return getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, programDiagnosticsInFile).diagnostics; } function getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] { @@ -2059,11 +2204,8 @@ namespace ts { function getOptionsDiagnostics(): SortedReadonlyArray { return sortAndDeduplicateDiagnostics(concatenate( - fileProcessingDiagnostics.getGlobalDiagnostics(), - concatenate( - programDiagnostics.getGlobalDiagnostics(), - getOptionsDiagnosticsOfConfigFile() - ) + programDiagnostics.getGlobalDiagnostics(), + getOptionsDiagnosticsOfConfigFile() )); } @@ -2071,9 +2213,7 @@ namespace ts { if (!options.configFile) { return emptyArray; } let diagnostics = programDiagnostics.getDiagnostics(options.configFile.fileName); forEachResolvedProjectReference(resolvedRef => { - if (resolvedRef) { - diagnostics = concatenate(diagnostics, programDiagnostics.getDiagnostics(resolvedRef.sourceFile.fileName)); - } + diagnostics = concatenate(diagnostics, programDiagnostics.getDiagnostics(resolvedRef.sourceFile.fileName)); }); return diagnostics; } @@ -2086,8 +2226,8 @@ namespace ts { return configFileParsingDiagnostics || emptyArray; } - function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean) { - processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined); + function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason) { + processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined, reason); } function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean { @@ -2100,6 +2240,19 @@ namespace ts { : b.kind === SyntaxKind.StringLiteral && a.text === b.text; } + function createSyntheticImport(text: string, file: SourceFile) { + const externalHelpersModuleReference = factory.createStringLiteral(text); + const importDecl = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference); + addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper); + setParent(externalHelpersModuleReference, importDecl); + setParent(importDecl, file); + // explicitly unset the synthesized flag on these declarations so the checker API will answer questions about them + // (which is required to build the dependency graph for incremental emit) + (externalHelpersModuleReference as Mutable).flags &= ~NodeFlags.Synthesized; + (importDecl as Mutable).flags &= ~NodeFlags.Synthesized; + return externalHelpersModuleReference; + } + function collectExternalModuleReferences(file: SourceFile): void { if (file.imports) { return; @@ -2115,16 +2268,17 @@ namespace ts { // If we are importing helpers, we need to add a synthetic reference to resolve the // helpers library. - if (options.importHelpers - && (options.isolatedModules || isExternalModuleFile) + if ((options.isolatedModules || isExternalModuleFile) && !file.isDeclarationFile) { - // synthesize 'import "tslib"' declaration - const externalHelpersModuleReference = factory.createStringLiteral(externalHelpersModuleNameText); - const importDecl = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference); - addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper); - setParent(externalHelpersModuleReference, importDecl); - setParent(importDecl, file); - imports = [externalHelpersModuleReference]; + if (options.importHelpers) { + // synthesize 'import "tslib"' declaration + imports = [createSyntheticImport(externalHelpersModuleNameText, file)]; + } + const jsxImport = getJSXRuntimeImport(getJSXImplicitImportBase(options, file), options); + if (jsxImport) { + // synthesize `import "base/jsx-runtime"` declaration + (imports ||= []).push(createSyntheticImport(jsxImport, file)); + } } for (const node of file.statements) { @@ -2228,14 +2382,14 @@ namespace ts { /** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */ function getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined { - return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), fileName => filesByName.get(toPath(fileName)) || undefined); + return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), getSourceFile); } function getSourceFileFromReferenceWorker( fileName: string, getSourceFile: (fileName: string) => SourceFile | undefined, fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void, - refFile?: SourceFile): SourceFile | undefined { + reason?: FileIncludeReason): SourceFile | undefined { if (hasExtension(fileName)) { const canonicalFileName = host.getCanonicalFileName(fileName); @@ -2262,7 +2416,7 @@ namespace ts { fail(Diagnostics.File_0_not_found, fileName); } } - else if (refFile && canonicalFileName === host.getCanonicalFileName(refFile.fileName)) { + else if (isReferencedFile(reason) && canonicalFileName === host.getCanonicalFileName(getSourceFileByPath(reason.file)!.fileName)) { fail(Diagnostics.A_file_cannot_have_a_reference_to_itself); } } @@ -2284,34 +2438,27 @@ namespace ts { } /** This has side effects through `findSourceFile`. */ - function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, refFile?: RefFile): void { + function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason): void { getSourceFileFromReferenceWorker( fileName, - fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, refFile, packageId), // TODO: GH#18217 - (diagnostic, ...args) => fileProcessingDiagnostics.add( - createRefFileDiagnostic(refFile, diagnostic, ...args) - ), - refFile && refFile.file + fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217 + (diagnostic, ...args) => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, diagnostic, args), + reason ); } - function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFile: SourceFile, refFile: RefFile | undefined): void { - const refs = !refFile ? refFileMap && refFileMap.get(existingFile.path) : undefined; - const refToReportErrorOn = refs && find(refs, ref => ref.referencedFileName === existingFile.fileName); - fileProcessingDiagnostics.add(refToReportErrorOn ? - createFileDiagnosticAtReference( - refToReportErrorOn, - Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing, - existingFile.fileName, - fileName, - ) : - createRefFileDiagnostic( - refFile, - Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, - fileName, - existingFile.fileName - ) - ); + function processProjectReferenceFile(fileName: string, reason: ProjectReferenceFile) { + return processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, reason); + } + + function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFile: SourceFile, reason: FileIncludeReason): void { + const hasExistingReasonToReportErrorOn = !isReferencedFile(reason) && some(fileReasons.get(existingFile.path), isReferencedFile); + if (hasExistingReasonToReportErrorOn) { + addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing, [existingFile.fileName, fileName]); + } + else { + addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, [fileName, existingFile.fileName]); + } } function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path, resolvedPath: Path, originalFileName: string): SourceFile { @@ -2336,7 +2483,18 @@ namespace ts { } // Get source file from normalized fileName - function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile: RefFile | undefined, packageId: PackageId | undefined): SourceFile | undefined { + function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined { + tracing.push(tracing.Phase.Program, "findSourceFile", { + fileName, + isDefaultLib: isDefaultLib || undefined, + fileIncludeKind: (FileIncludeKind as any)[reason.kind], + }); + const result = findSourceFileWorker(fileName, path, isDefaultLib, ignoreNoDefaultLib, reason, packageId); + tracing.pop(); + return result; + } + + function findSourceFileWorker(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined { if (useSourceOfProjectReferenceRedirect) { let source = getSourceOfProjectReferenceRedirect(fileName); // If preserveSymlinks is true, module resolution wont jump the symlink @@ -2353,7 +2511,7 @@ namespace ts { } if (source) { const file = isString(source) ? - findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, refFile, packageId) : + findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, reason, packageId) : undefined; if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined); return file; @@ -2362,7 +2520,7 @@ namespace ts { const originalFileName = fileName; if (filesByName.has(path)) { const file = filesByName.get(path); - addFileToRefFileMap(fileName, file || undefined, refFile); + addFileIncludeReason(file || undefined, reason); // try to check if we've already seen this file but with a different casing in path // NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected if (file && options.forceConsistentCasingInFileNames) { @@ -2375,7 +2533,7 @@ namespace ts { const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory); const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(fileName, currentDirectory); if (checkedAbsolutePath !== inputAbsolutePath) { - reportFileNamesDifferOnlyInCasingError(fileName, file, refFile); + reportFileNamesDifferOnlyInCasingError(fileName, file, reason); } } @@ -2406,7 +2564,7 @@ namespace ts { } let redirectedPath: Path | undefined; - if (refFile && !useSourceOfProjectReferenceRedirect) { + if (isReferencedFile(reason) && !useSourceOfProjectReferenceRedirect) { const redirectProject = getProjectReferenceRedirectProject(fileName); if (redirectProject) { if (outFile(redirectProject.commandLine.options)) { @@ -2428,12 +2586,7 @@ namespace ts { const file = host.getSourceFile( fileName, options.target!, - hostErrorMessage => fileProcessingDiagnostics.add(createRefFileDiagnostic( - refFile, - Diagnostics.Cannot_read_file_0_Colon_1, - fileName, - hostErrorMessage - )), + hostErrorMessage => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_read_file_0_Colon_1, [fileName, hostErrorMessage]), shouldCreateNewSourceFile ); @@ -2446,6 +2599,7 @@ namespace ts { const dupFile = createRedirectSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName); // TODO: GH#18217 redirectTargetsMap.add(fileFromPackageId.path, fileName); addFileToFilesByName(dupFile, path, redirectedPath); + addFileIncludeReason(dupFile, reason); sourceFileToPackageName.set(path, packageId.name); processingOtherFiles!.push(dupFile); return dupFile; @@ -2464,14 +2618,14 @@ namespace ts { file.path = path; file.resolvedPath = toPath(fileName); file.originalFileName = originalFileName; - addFileToRefFileMap(fileName, file, refFile); + addFileIncludeReason(file, reason); if (host.useCaseSensitiveFileNames()) { const pathLowerCase = toFileNameLowerCase(path); // for case-sensitive file systems check if we've already seen some file with similar filename ignoring case const existingFile = filesByNameIgnoreCase!.get(pathLowerCase); if (existingFile) { - reportFileNamesDifferOnlyInCasingError(fileName, existingFile, refFile); + reportFileNamesDifferOnlyInCasingError(fileName, existingFile, reason); } else { filesByNameIgnoreCase!.set(pathLowerCase, file); @@ -2502,15 +2656,8 @@ namespace ts { return file; } - function addFileToRefFileMap(referencedFileName: string, file: SourceFile | undefined, refFile: RefFile | undefined) { - if (refFile && file) { - (refFileMap || (refFileMap = createMultiMap())).add(file.path, { - referencedFileName, - kind: refFile.kind, - index: refFile.index, - file: refFile.file.path - }); - } + function addFileIncludeReason(file: SourceFile | undefined, reason: FileIncludeReason) { + if (file) fileReasons.add(file.path, reason); } function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) { @@ -2553,12 +2700,11 @@ namespace ts { function getResolvedProjectReferenceToRedirect(fileName: string) { if (mapFromFileToProjectReferenceRedirects === undefined) { mapFromFileToProjectReferenceRedirects = new Map(); - forEachResolvedProjectReference((referencedProject, referenceProjectPath) => { + forEachResolvedProjectReference(referencedProject => { // not input file from the referenced project, ignore - if (referencedProject && - toPath(options.configFilePath!) !== referenceProjectPath) { + if (toPath(options.configFilePath!) !== referencedProject.sourceFile.path) { referencedProject.commandLine.fileNames.forEach(f => - mapFromFileToProjectReferenceRedirects!.set(toPath(f), referenceProjectPath)); + mapFromFileToProjectReferenceRedirects!.set(toPath(f), referencedProject.sourceFile.path)); } }); } @@ -2568,13 +2714,9 @@ namespace ts { } function forEachResolvedProjectReference( - cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined + cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined ): T | undefined { - return forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, index, parent) => { - const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index]; - const resolvedRefPath = toPath(resolveProjectReferencePath(ref)); - return cb(resolvedRef, resolvedRefPath); - }); + return ts.forEachResolvedProjectReference(resolvedProjectReferences, cb); } function getSourceOfProjectReferenceRedirect(file: string) { @@ -2582,21 +2724,20 @@ namespace ts { if (mapFromToProjectReferenceRedirectSource === undefined) { mapFromToProjectReferenceRedirectSource = new Map(); forEachResolvedProjectReference(resolvedRef => { - if (resolvedRef) { - const out = outFile(resolvedRef.commandLine.options); - if (out) { - // Dont know which source file it means so return true? - const outputDts = changeExtension(out, Extension.Dts); - mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true); - } - else { - forEach(resolvedRef.commandLine.fileNames, fileName => { - if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) { - const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, host.useCaseSensitiveFileNames()); - mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName); - } - }); - } + const out = outFile(resolvedRef.commandLine.options); + if (out) { + // Dont know which source file it means so return true? + const outputDts = changeExtension(out, Extension.Dts); + mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true); + } + else { + const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(resolvedRef.commandLine, !host.useCaseSensitiveFileNames())); + forEach(resolvedRef.commandLine.fileNames, fileName => { + if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) { + const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory); + mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName); + } + }); } }); } @@ -2607,49 +2748,6 @@ namespace ts { return useSourceOfProjectReferenceRedirect && !!getResolvedProjectReferenceToRedirect(fileName); } - function forEachProjectReference( - projectReferences: readonly ProjectReference[] | undefined, - resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined, - cbResolvedRef: (resolvedRef: ResolvedProjectReference | undefined, index: number, parent: ResolvedProjectReference | undefined) => T | undefined, - cbRef?: (projectReferences: readonly ProjectReference[] | undefined, parent: ResolvedProjectReference | undefined) => T | undefined - ): T | undefined { - let seenResolvedRefs: ResolvedProjectReference[] | undefined; - - return worker(projectReferences, resolvedProjectReferences, /*parent*/ undefined, cbResolvedRef, cbRef); - - function worker( - projectReferences: readonly ProjectReference[] | undefined, - resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined, - parent: ResolvedProjectReference | undefined, - cbResolvedRef: (resolvedRef: ResolvedProjectReference | undefined, index: number, parent: ResolvedProjectReference | undefined) => T | undefined, - cbRef?: (projectReferences: readonly ProjectReference[] | undefined, parent: ResolvedProjectReference | undefined) => T | undefined, - ): T | undefined { - - // Visit project references first - if (cbRef) { - const result = cbRef(projectReferences, parent); - if (result) { return result; } - } - - return forEach(resolvedProjectReferences, (resolvedRef, index) => { - if (contains(seenResolvedRefs, resolvedRef)) { - // ignore recursives - return undefined; - } - - const result = cbResolvedRef(resolvedRef, index, parent); - if (result) { - return result; - } - - if (!resolvedRef) return undefined; - - (seenResolvedRefs || (seenResolvedRefs = [])).push(resolvedRef); - return worker(resolvedRef.commandLine.projectReferences, resolvedRef.references, resolvedRef, cbResolvedRef, cbRef); - }); - } - } - function getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined { if (!projectReferenceRedirects) { return undefined; @@ -2660,19 +2758,12 @@ namespace ts { function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) { forEach(file.referencedFiles, (ref, index) => { - const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName); processSourceFile( - referencedFileName, + resolveTripleslashReference(ref.fileName, file.fileName), isDefaultLib, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, - { - kind: RefFileKind.ReferenceFile, - index, - file, - pos: ref.pos, - end: ref.end - } + { kind: FileIncludeKind.ReferenceFile, file: file.path, index, } ); }); } @@ -2684,32 +2775,31 @@ namespace ts { return; } - const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.originalFileName, getResolvedProjectReferenceToRedirect(file.originalFileName)); - - for (let i = 0; i < typeDirectives.length; i++) { - const ref = file.typeReferenceDirectives[i]; - const resolvedTypeReferenceDirective = resolutions[i]; + const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file); + for (let index = 0; index < typeDirectives.length; index++) { + const ref = file.typeReferenceDirectives[index]; + const resolvedTypeReferenceDirective = resolutions[index]; // store resolved type directive on the file const fileName = toFileNameLowerCase(ref.fileName); setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective); - processTypeReferenceDirective( - fileName, - resolvedTypeReferenceDirective, - { - kind: RefFileKind.TypeReferenceDirective, - index: i, - file, - pos: ref.pos, - end: ref.end - } - ); + processTypeReferenceDirective(fileName, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, }); } } function processTypeReferenceDirective( typeReferenceDirective: string, - resolvedTypeReferenceDirective?: ResolvedTypeReferenceDirective, - refFile?: RefFile + resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined, + reason: FileIncludeReason + ): void { + tracing.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolveModuleNamesReusingOldState, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined }); + processTypeReferenceDirectiveWorker(typeReferenceDirective, resolvedTypeReferenceDirective, reason); + tracing.pop(); + } + + function processTypeReferenceDirectiveWorker( + typeReferenceDirective: string, + resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined, + reason: FileIncludeReason ): void { // If we already found this library as a primary reference - nothing to do @@ -2723,7 +2813,7 @@ namespace ts { if (resolvedTypeReferenceDirective.primary) { // resolved from the primary path - processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile); // TODO: GH#18217 + processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); // TODO: GH#18217 } else { // If we already resolved to this file, it must have been a secondary reference. Check file contents @@ -2734,25 +2824,11 @@ namespace ts { const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName!); const existingFile = getSourceFile(previousResolution.resolvedFileName!)!; if (otherFileText !== existingFile.text) { - // Try looking up ref for original file - const refs = !refFile ? refFileMap && refFileMap.get(existingFile.path) : undefined; - const refToReportErrorOn = refs && find(refs, ref => ref.referencedFileName === existingFile.fileName); - fileProcessingDiagnostics.add( - refToReportErrorOn ? - createFileDiagnosticAtReference( - refToReportErrorOn, - Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict, - typeReferenceDirective, - resolvedTypeReferenceDirective.resolvedFileName, - previousResolution.resolvedFileName - ) : - createRefFileDiagnostic( - refFile, - Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict, - typeReferenceDirective, - resolvedTypeReferenceDirective.resolvedFileName, - previousResolution.resolvedFileName - ) + addFilePreprocessingFileExplainingDiagnostic( + existingFile, + reason, + Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict, + [typeReferenceDirective, resolvedTypeReferenceDirective.resolvedFileName, previousResolution.resolvedFileName] ); } } @@ -2761,18 +2837,14 @@ namespace ts { } else { // First resolution of this library - processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile); + processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); } } if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--; } else { - fileProcessingDiagnostics.add(createRefFileDiagnostic( - refFile, - Diagnostics.Cannot_find_type_definition_file_for_0, - typeReferenceDirective - )); + addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_find_type_definition_file_for_0, [typeReferenceDirective]); } if (saveResolution) { @@ -2781,38 +2853,27 @@ namespace ts { } function processLibReferenceDirectives(file: SourceFile) { - forEach(file.libReferenceDirectives, libReference => { + forEach(file.libReferenceDirectives, (libReference, index) => { const libName = toFileNameLowerCase(libReference.fileName); const libFileName = libMap.get(libName); if (libFileName) { // we ignore any 'no-default-lib' reference set on this file. - processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true); + processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }); } else { const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts"); const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity); - const message = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0; - fileProcessingDiagnostics.add(createFileDiagnostic( - file, - libReference.pos, - libReference.end - libReference.pos, - message, - libName, - suggestion - )); + const diagnostic = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0; + (fileProcessingDiagnostics ||= []).push({ + kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic, + reason: { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }, + diagnostic, + args: [libName, suggestion] + }); } }); } - function createRefFileDiagnostic(refFile: RefFile | undefined, message: DiagnosticMessage, ...args: any[]): Diagnostic { - if (!refFile) { - return createCompilerDiagnostic(message, ...args); - } - else { - return createFileDiagnostic(refFile.file, refFile.pos, refFile.end - refFile.pos, message, ...args); - } - } - function getCanonicalFileName(fileName: string): string { return host.getCanonicalFileName(fileName); } @@ -2822,11 +2883,11 @@ namespace ts { if (file.imports.length || file.moduleAugmentations.length) { // Because global augmentation doesn't have string literal name, we can check for global augmentation as such. const moduleNames = getModuleNames(file); - const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.originalFileName, currentDirectory), file); + const resolutions = resolveModuleNamesReusingOldState(moduleNames, file); Debug.assert(resolutions.length === moduleNames.length); - for (let i = 0; i < moduleNames.length; i++) { - const resolution = resolutions[i]; - setResolvedModule(file, moduleNames[i], resolution); + for (let index = 0; index < moduleNames.length; index++) { + const resolution = resolutions[index]; + setResolvedModule(file, moduleNames[index], resolution); if (!resolution) { continue; @@ -2852,30 +2913,23 @@ namespace ts { const shouldAddFile = resolvedFileName && !getResolutionDiagnostic(options, resolution) && !options.noResolve - && i < file.imports.length + && index < file.imports.length && !elideImport && !(isJsFile && !getAllowJSCompilerOption(options)) - && (isInJSFile(file.imports[i]) || !(file.imports[i].flags & NodeFlags.JSDoc)); + && (isInJSFile(file.imports[index]) || !(file.imports[index].flags & NodeFlags.JSDoc)); if (elideImport) { modulesWithElidedImports.set(file.path, true); } else if (shouldAddFile) { const path = toPath(resolvedFileName); - const pos = skipTrivia(file.text, file.imports[i].pos); findSourceFile( resolvedFileName, path, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, - { - kind: RefFileKind.Import, - index: i, - file, - pos, - end: file.imports[i].end - }, - resolution.packageId + { kind: FileIncludeKind.Import, file: file.path, index, }, + resolution.packageId, ); } @@ -2890,27 +2944,17 @@ namespace ts { } } - function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string { - const fileNames = mapDefined(sourceFiles, file => file.isDeclarationFile ? undefined : file.fileName); - return computeCommonSourceDirectoryOfFilenames(fileNames, currentDirectory, getCanonicalFileName); - } - function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean { let allFilesBelongToPath = true; const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory)); - let rootPaths: Set | undefined; - for (const sourceFile of sourceFiles) { if (!sourceFile.isDeclarationFile) { const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory)); if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) { - if (!rootPaths) rootPaths = new Set(rootNames.map(toPath)); - addProgramDiagnosticAtRefPath( + addProgramDiagnosticExplainingFile( sourceFile, - rootPaths, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, - sourceFile.fileName, - rootDirectory + [sourceFile.fileName, rootDirectory] ); allFilesBelongToPath = false; } @@ -3021,12 +3065,10 @@ namespace ts { for (const file of files) { // Ignore file that is not emitted if (sourceFileMayBeEmitted(file, program) && !rootPaths.has(file.path)) { - addProgramDiagnosticAtRefPath( + addProgramDiagnosticExplainingFile( file, - rootPaths, Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, - file.fileName, - options.configFilePath || "" + [file.fileName, options.configFilePath || ""] ); } } @@ -3270,41 +3312,144 @@ namespace ts { } } - function createFileDiagnosticAtReference(refPathToReportErrorOn: ts.RefFile, message: DiagnosticMessage, ...args: (string | number | undefined)[]) { - const refFile = Debug.checkDefined(getSourceFileByPath(refPathToReportErrorOn.file)); - const { kind, index } = refPathToReportErrorOn; - let pos: number, end: number; - switch (kind) { - case RefFileKind.Import: - pos = skipTrivia(refFile.text, refFile.imports[index].pos); - end = refFile.imports[index].end; + function createDiagnosticExplainingFile(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason | undefined, diagnostic: DiagnosticMessage, args: (string | number | undefined)[] | undefined): Diagnostic { + let fileIncludeReasons: DiagnosticMessageChain[] | undefined; + let relatedInfo: Diagnostic[] | undefined; + let locationReason = isReferencedFile(fileProcessingReason) ? fileProcessingReason : undefined; + if (file) fileReasons.get(file.path)?.forEach(processReason); + if (fileProcessingReason) processReason(fileProcessingReason); + // If we have location and there is only one reason file is in which is the location, dont add details for file include + if (locationReason && fileIncludeReasons?.length === 1) fileIncludeReasons = undefined; + const location = locationReason && getReferencedFileLocation(getSourceFileByPath, locationReason); + const fileIncludeReasonDetails = fileIncludeReasons && chainDiagnosticMessages(fileIncludeReasons, Diagnostics.The_file_is_in_the_program_because_Colon); + const redirectInfo = file && explainIfFileIsRedirect(file); + const chain = chainDiagnosticMessages(redirectInfo ? fileIncludeReasonDetails ? [fileIncludeReasonDetails, ...redirectInfo] : redirectInfo : fileIncludeReasonDetails, diagnostic, ...args || emptyArray); + return location && isReferenceFileLocation(location) ? + createFileDiagnosticFromMessageChain(location.file, location.pos, location.end - location.pos, chain, relatedInfo) : + createCompilerDiagnosticFromMessageChain(chain, relatedInfo); + + function processReason(reason: FileIncludeReason) { + (fileIncludeReasons ||= []).push(fileIncludeReasonToDiagnostics(program, reason)); + if (!locationReason && isReferencedFile(reason)) { + // Report error at first reference file or file currently in processing and dont report in related information + locationReason = reason; + } + else if (locationReason !== reason) { + relatedInfo = append(relatedInfo, fileIncludeReasonToRelatedInformation(reason)); + } + // Remove fileProcessingReason if its already included in fileReasons of the program + if (reason === fileProcessingReason) fileProcessingReason = undefined; + } + } + + function addFilePreprocessingFileExplainingDiagnostic(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) { + (fileProcessingDiagnostics ||= []).push({ + kind: FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic, + file: file && file.path, + fileProcessingReason, + diagnostic, + args + }); + } + + function addProgramDiagnosticExplainingFile(file: SourceFile, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) { + programDiagnostics.add(createDiagnosticExplainingFile(file, /*fileProcessingReason*/ undefined, diagnostic, args)); + } + + function fileIncludeReasonToRelatedInformation(reason: FileIncludeReason): DiagnosticWithLocation | undefined { + if (isReferencedFile(reason)) { + const referenceLocation = getReferencedFileLocation(getSourceFileByPath, reason); + let message: DiagnosticMessage; + switch (reason.kind) { + case FileIncludeKind.Import: + message = Diagnostics.File_is_included_via_import_here; + break; + case FileIncludeKind.ReferenceFile: + message = Diagnostics.File_is_included_via_reference_here; + break; + case FileIncludeKind.TypeReferenceDirective: + message = Diagnostics.File_is_included_via_type_library_reference_here; + break; + case FileIncludeKind.LibReferenceDirective: + message = Diagnostics.File_is_included_via_library_reference_here; + break; + default: + Debug.assertNever(reason); + } + return isReferenceFileLocation(referenceLocation) ? createFileDiagnostic( + referenceLocation.file, + referenceLocation.pos, + referenceLocation.end - referenceLocation.pos, + message, + ) : undefined; + } + + if (!options.configFile) return undefined; + let configFileNode: Node | undefined; + let message: DiagnosticMessage; + switch (reason.kind) { + case FileIncludeKind.RootFile: + if (!options.configFile.configFileSpecs) return undefined; + const fileName = getNormalizedAbsolutePath(rootNames[reason.index], currentDirectory); + const matchedByFiles = getMatchedFileSpec(program, fileName); + if (matchedByFiles) { + configFileNode = getTsConfigPropArrayElementValue(options.configFile, "files", matchedByFiles); + message = Diagnostics.File_is_matched_by_files_list_specified_here; + break; + } + const matchedByInclude = getMatchedIncludeSpec(program, fileName); + // Could be additional files specified as roots + if (!matchedByInclude) return undefined; + configFileNode = getTsConfigPropArrayElementValue(options.configFile, "include", matchedByInclude); + message = Diagnostics.File_is_matched_by_include_pattern_specified_here; break; - case RefFileKind.ReferenceFile: - ({ pos, end } = refFile.referencedFiles[index]); + case FileIncludeKind.SourceFromProjectReference: + case FileIncludeKind.OutputFromProjectReference: + const referencedResolvedRef = Debug.checkDefined(resolvedProjectReferences?.[reason.index]); + const referenceInfo = forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => + resolvedRef === referencedResolvedRef ? { sourceFile: parent?.sourceFile || options.configFile!, index } : undefined + ); + if (!referenceInfo) return undefined; + const { sourceFile, index } = referenceInfo; + const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile as TsConfigSourceFile, "references"), + property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined); + return referencesSyntax && referencesSyntax.elements.length > index ? + createDiagnosticForNodeInSourceFile( + sourceFile, + referencesSyntax.elements[index], + reason.kind === FileIncludeKind.OutputFromProjectReference ? + Diagnostics.File_is_output_from_referenced_project_specified_here : + Diagnostics.File_is_source_from_referenced_project_specified_here, + ) : + undefined; + case FileIncludeKind.AutomaticTypeDirectiveFile: + if (!options.types) return undefined; + configFileNode = getOptionsSyntaxByArrayElementValue("types", reason.typeReference); + message = Diagnostics.File_is_entry_point_of_type_library_specified_here; break; - case RefFileKind.TypeReferenceDirective: - ({ pos, end } = refFile.typeReferenceDirectives[index]); + case FileIncludeKind.LibFile: + if (reason.index !== undefined) { + configFileNode = getOptionsSyntaxByArrayElementValue("lib", options.lib![reason.index]); + message = Diagnostics.File_is_library_specified_here; + break; + } + const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === options.target ? key : undefined); + configFileNode = target ? getOptionsSyntaxByValue("target", target) : undefined; + message = Diagnostics.File_is_default_library_for_target_specified_here; break; default: - return Debug.assertNever(kind); + Debug.assertNever(reason); } - return createFileDiagnostic(refFile, pos, end - pos, message, ...args); - } - - function addProgramDiagnosticAtRefPath(file: SourceFile, rootPaths: Set, message: DiagnosticMessage, ...args: (string | number | undefined)[]) { - const refPaths = refFileMap?.get(file.path); - const refPathToReportErrorOn = forEach(refPaths, refPath => rootPaths.has(refPath.file) ? refPath : undefined) || - elementAt(refPaths, 0); - programDiagnostics.add( - refPathToReportErrorOn ? - createFileDiagnosticAtReference(refPathToReportErrorOn, message, ...args) : - createCompilerDiagnostic(message, ...args) + return configFileNode && createDiagnosticForNodeInSourceFile( + options.configFile, + configFileNode, + message, ); } function verifyProjectReferences() { const buildInfoPath = !options.suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath(options) : undefined; - forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, index, parent) => { + forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => { const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index]; const parentFile = parent && parent.sourceFile as JsonSourceFile; if (!resolvedRef) { @@ -3374,16 +3519,23 @@ namespace ts { } } - function getOptionsSyntaxByName(name: string): object | undefined { + function getOptionsSyntaxByName(name: string) { const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); - if (compilerOptionsObjectLiteralSyntax) { - return getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name); - } - return undefined; + return compilerOptionsObjectLiteralSyntax && getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name); + } + + function getOptionPathsSyntax() { + return getOptionsSyntaxByName("paths") || emptyArray; } - function getOptionPathsSyntax(): PropertyAssignment[] { - return getOptionsSyntaxByName("paths") as PropertyAssignment[] || emptyArray; + function getOptionsSyntaxByValue(name: string, value: string) { + const syntaxByName = getOptionsSyntaxByName(name); + return syntaxByName && firstDefined(syntaxByName, property => isStringLiteral(property.initializer) && property.initializer.text === value ? property.initializer : undefined); + } + + function getOptionsSyntaxByArrayElementValue(name: string, value: string) { + const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); + return compilerOptionsObjectLiteralSyntax && getPropertyArrayElementValue(compilerOptionsObjectLiteralSyntax, name, value); } function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string, option3?: string) { @@ -3417,7 +3569,7 @@ namespace ts { function getCompilerOptionsObjectLiteralSyntax() { if (_compilerOptionsObjectLiteralSyntax === undefined) { - _compilerOptionsObjectLiteralSyntax = null; // eslint-disable-line no-null/no-null + _compilerOptionsObjectLiteralSyntax = false; const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile); if (jsonObjectLiteral) { for (const prop of getPropertyAssignment(jsonObjectLiteral, "compilerOptions")) { @@ -3428,7 +3580,7 @@ namespace ts { } } } - return _compilerOptionsObjectLiteralSyntax; + return _compilerOptionsObjectLiteralSyntax || undefined; } function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number): boolean { @@ -3502,7 +3654,7 @@ namespace ts { toPath(fileName: string): Path; getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined; getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined; - forEachResolvedProjectReference(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined; + forEachResolvedProjectReference(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined; } function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSourceOfProjectReferenceRedirect) { @@ -3532,7 +3684,6 @@ namespace ts { if (!setOfDeclarationDirectories) { setOfDeclarationDirectories = new Set(); host.forEachResolvedProjectReference(ref => { - if (!ref) return; const out = outFile(ref.commandLine.options); if (out) { setOfDeclarationDirectories!.add(getDirectoryPath(host.toPath(out))); @@ -3671,8 +3822,8 @@ namespace ts { export const emitSkippedWithNoDiagnostics: EmitResult = { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true }; /*@internal*/ - export function handleNoEmitOptions( - program: ProgramToEmitFilesAndReportErrors, + export function handleNoEmitOptions( + program: Program | T, sourceFile: SourceFile | undefined, writeFile: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined @@ -3821,4 +3972,17 @@ namespace ts { } return res; } + + function getModuleNameStringLiteralAt({ imports, moduleAugmentations }: SourceFile, index: number): StringLiteralLike { + if (index < imports.length) return imports[index]; + let augIndex = imports.length; + for (const aug of moduleAugmentations) { + if (aug.kind === SyntaxKind.StringLiteral) { + if (index === augIndex) return aug; + augIndex++; + } + // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`. + } + Debug.fail("should never ask for module name at index higher than possible module name"); + } } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 5f5248e1073f3..b15e1ae6468ff 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -111,6 +111,7 @@ namespace ts { infer: SyntaxKind.InferKeyword, instanceof: SyntaxKind.InstanceOfKeyword, interface: SyntaxKind.InterfaceKeyword, + intrinsic: SyntaxKind.IntrinsicKeyword, is: SyntaxKind.IsKeyword, keyof: SyntaxKind.KeyOfKeyword, let: SyntaxKind.LetKeyword, @@ -151,10 +152,6 @@ namespace ts { yield: SyntaxKind.YieldKeyword, async: SyntaxKind.AsyncKeyword, await: SyntaxKind.AwaitKeyword, - uppercase: SyntaxKind.UppercaseKeyword, - lowercase: SyntaxKind.LowercaseKeyword, - capitalize: SyntaxKind.CapitalizeKeyword, - uncapitalize: SyntaxKind.UncapitalizeKeyword, of: SyntaxKind.OfKeyword, }; @@ -2305,9 +2302,11 @@ namespace ts { // they allow dashes function scanJsxIdentifier(): SyntaxKind { if (tokenIsIdentifierOrKeyword(token)) { - // An identifier or keyword has already been parsed - check for a `-` and then append it and everything after it to the token + // An identifier or keyword has already been parsed - check for a `-` or a single instance of `:` and then append it and + // everything after it to the token // Do note that this means that `scanJsxIdentifier` effectively _mutates_ the visible token without advancing to a new token // Any caller should be expecting this behavior and should only read the pos or token value after calling it. + let namespaceSeparator = false; while (pos < end) { const ch = text.charCodeAt(pos); if (ch === CharacterCodes.minus) { @@ -2315,12 +2314,23 @@ namespace ts { pos++; continue; } + else if (ch === CharacterCodes.colon && !namespaceSeparator) { + tokenValue += ":"; + pos++; + namespaceSeparator = true; + continue; + } const oldPos = pos; tokenValue += scanIdentifierParts(); // reuse `scanIdentifierParts` so unicode escapes are handled if (pos === oldPos) { break; } } + // Do not include a trailing namespace separator in the token, since this is against the spec. + if (tokenValue.slice(-1) === ":") { + tokenValue = tokenValue.slice(0, -1); + pos--; + } } return token; } diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index efa51678f4da0..d6990c3462df4 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -449,6 +449,7 @@ namespace ts { export interface RecursiveDirectoryWatcherHost { watchDirectory: HostWatchDirectory; useCaseSensitiveFileNames: boolean; + getCurrentDirectory: System["getCurrentDirectory"]; getAccessibleSortedChildDirectories(path: string): readonly string[]; directoryExists(dir: string): boolean; realpath(s: string): string; @@ -462,7 +463,16 @@ namespace ts { * (eg on OS that dont support recursive watch using fs.watch use fs.watchFile) */ /*@internal*/ - export function createDirectoryWatcherSupportingRecursive(host: RecursiveDirectoryWatcherHost): HostWatchDirectory { + export function createDirectoryWatcherSupportingRecursive({ + watchDirectory, + useCaseSensitiveFileNames, + getCurrentDirectory, + getAccessibleSortedChildDirectories, + directoryExists, + realpath, + setTimeout, + clearTimeout + }: RecursiveDirectoryWatcherHost): HostWatchDirectory { interface ChildDirectoryWatcher extends FileWatcher { dirName: string; } @@ -478,12 +488,12 @@ namespace ts { const cacheToUpdateChildWatches = new Map(); let timerToUpdateChildWatches: any; - const filePathComparer = getStringComparer(!host.useCaseSensitiveFileNames); - const toCanonicalFilePath = createGetCanonicalFileName(host.useCaseSensitiveFileNames); + const filePathComparer = getStringComparer(!useCaseSensitiveFileNames); + const toCanonicalFilePath = createGetCanonicalFileName(useCaseSensitiveFileNames); return (dirName, callback, recursive, options) => recursive ? createDirectoryWatcher(dirName, options, callback) : - host.watchDirectory(dirName, callback, recursive, options); + watchDirectory(dirName, callback, recursive, options); /** * Create the directory watcher for the dirPath. @@ -496,8 +506,8 @@ namespace ts { } else { directoryWatcher = { - watcher: host.watchDirectory(dirName, fileName => { - if (isIgnoredPath(fileName)) return; + watcher: watchDirectory(dirName, fileName => { + if (isIgnoredPath(fileName, options)) return; if (options?.synchronousWatchDirectory) { // Call the actual callback @@ -578,7 +588,7 @@ namespace ts { function nonSyncUpdateChildWatches(dirName: string, dirPath: Path, fileName: string, options: WatchOptions | undefined) { // Iterate through existing children and update the watches if needed const parentWatcher = cache.get(dirPath); - if (parentWatcher && host.directoryExists(dirName)) { + if (parentWatcher && directoryExists(dirName)) { // Schedule the update and postpone invoke for callbacks scheduleUpdateChildWatches(dirName, dirPath, fileName, options); return; @@ -598,10 +608,10 @@ namespace ts { cacheToUpdateChildWatches.set(dirPath, { dirName, options, fileNames: [fileName] }); } if (timerToUpdateChildWatches) { - host.clearTimeout(timerToUpdateChildWatches); + clearTimeout(timerToUpdateChildWatches); timerToUpdateChildWatches = undefined; } - timerToUpdateChildWatches = host.setTimeout(onTimerToUpdateChildWatches, 1000); + timerToUpdateChildWatches = setTimeout(onTimerToUpdateChildWatches, 1000); } function onTimerToUpdateChildWatches() { @@ -611,8 +621,9 @@ namespace ts { const invokeMap = new Map(); while (!timerToUpdateChildWatches && cacheToUpdateChildWatches.size) { - const { value: [dirPath, { dirName, options, fileNames }], done } = cacheToUpdateChildWatches.entries().next(); - Debug.assert(!done); + const result = cacheToUpdateChildWatches.entries().next(); + Debug.assert(!result.done); + const { value: [dirPath, { dirName, options, fileNames }] } = result; cacheToUpdateChildWatches.delete(dirPath); // Because the child refresh is fresh, we would need to invalidate whole root directory being watched // to ensure that all the changes are reflected at this time @@ -620,7 +631,7 @@ namespace ts { invokeCallbacks(dirPath, invokeMap, hasChanges ? undefined : fileNames); } - sysLog(`sysLog:: invokingWatchers:: ${timestamp() - start}ms:: ${cacheToUpdateChildWatches.size}`); + sysLog(`sysLog:: invokingWatchers:: Elapsed:: ${timestamp() - start}ms:: ${cacheToUpdateChildWatches.size}`); callbackCache.forEach((callbacks, rootDirName) => { const existing = invokeMap.get(rootDirName); if (existing) { @@ -636,7 +647,7 @@ namespace ts { }); const elapsed = timestamp() - start; - sysLog(`sysLog:: Elapsed ${elapsed}ms:: onTimerToUpdateChildWatches:: ${cacheToUpdateChildWatches.size} ${timerToUpdateChildWatches}`); + sysLog(`sysLog:: Elapsed:: ${elapsed}ms:: onTimerToUpdateChildWatches:: ${cacheToUpdateChildWatches.size} ${timerToUpdateChildWatches}`); } function removeChildWatches(parentWatcher: HostDirectoryWatcher | undefined) { @@ -655,11 +666,11 @@ namespace ts { if (!parentWatcher) return false; let newChildWatches: ChildDirectoryWatcher[] | undefined; const hasChanges = enumerateInsertsAndDeletes( - host.directoryExists(parentDir) ? mapDefined(host.getAccessibleSortedChildDirectories(parentDir), child => { + directoryExists(parentDir) ? mapDefined(getAccessibleSortedChildDirectories(parentDir), child => { const childFullName = getNormalizedAbsolutePath(child, parentDir); // Filter our the symbolic link directories since those arent included in recursive watch // which is same behaviour when recursive: true is passed to fs.watch - return !isIgnoredPath(childFullName) && filePathComparer(childFullName, normalizePath(host.realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined; + return !isIgnoredPath(childFullName, options) && filePathComparer(childFullName, normalizePath(realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined; }) : emptyArray, parentWatcher.childWatches, (child, childWatcher) => filePathComparer(child, childWatcher.dirName), @@ -686,13 +697,14 @@ namespace ts { } } - function isIgnoredPath(path: string) { - return some(ignoredPaths, searchPath => isInPath(path, searchPath)); + function isIgnoredPath(path: string, options: WatchOptions | undefined) { + return some(ignoredPaths, searchPath => isInPath(path, searchPath)) || + isIgnoredByWatchOptions(path, options, useCaseSensitiveFileNames, getCurrentDirectory); } function isInPath(path: string, searchPath: string) { if (stringContains(path, searchPath)) return true; - if (host.useCaseSensitiveFileNames) return false; + if (useCaseSensitiveFileNames) return false; return stringContains(toCanonicalFilePath(path), searchPath); } } @@ -729,14 +741,35 @@ namespace ts { }; } - function createFsWatchCallbackForDirectoryWatcherCallback(directoryName: string, callback: DirectoryWatcherCallback): FsWatchCallback { + function isIgnoredByWatchOptions( + pathToCheck: string, + options: WatchOptions | undefined, + useCaseSensitiveFileNames: boolean, + getCurrentDirectory: System["getCurrentDirectory"], + ) { + return (options?.excludeDirectories || options?.excludeFiles) && ( + matchesExclude(pathToCheck, options?.excludeFiles, useCaseSensitiveFileNames, getCurrentDirectory()) || + matchesExclude(pathToCheck, options?.excludeDirectories, useCaseSensitiveFileNames, getCurrentDirectory()) + ); + } + + function createFsWatchCallbackForDirectoryWatcherCallback( + directoryName: string, + callback: DirectoryWatcherCallback, + options: WatchOptions | undefined, + useCaseSensitiveFileNames: boolean, + getCurrentDirectory: System["getCurrentDirectory"], + ): FsWatchCallback { return (eventName, relativeFileName) => { // In watchDirectory we only care about adding and removing files (when event name is // "rename"); changes made within files are handled by corresponding fileWatchers (when // event name is "change") if (eventName === "rename") { // When deleting a file, the passed baseFileName is null - callback(!relativeFileName ? directoryName : normalizePath(combinePaths(directoryName, relativeFileName))); + const fileName = !relativeFileName ? directoryName : normalizePath(combinePaths(directoryName, relativeFileName)); + if (!relativeFileName || !isIgnoredByWatchOptions(fileName, options, useCaseSensitiveFileNames, getCurrentDirectory)) { + callback(fileName); + } } }; } @@ -753,6 +786,7 @@ namespace ts { fsWatch: FsWatch; fileExists: System["fileExists"]; useCaseSensitiveFileNames: boolean; + getCurrentDirectory: System["getCurrentDirectory"]; fsSupportsRecursiveFsWatch: boolean; directoryExists: System["directoryExists"]; getAccessibleSortedChildDirectories(path: string): readonly string[]; @@ -772,6 +806,7 @@ namespace ts { fsWatch, fileExists, useCaseSensitiveFileNames, + getCurrentDirectory, fsSupportsRecursiveFsWatch, directoryExists, getAccessibleSortedChildDirectories, @@ -868,7 +903,7 @@ namespace ts { return fsWatch( directoryName, FileSystemEntryKind.Directory, - createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback), + createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback, options, useCaseSensitiveFileNames, getCurrentDirectory), recursive, PollingInterval.Medium, getFallbackOptions(options) @@ -878,6 +913,7 @@ namespace ts { if (!hostRecursiveDirectoryWatcher) { hostRecursiveDirectoryWatcher = createDirectoryWatcherSupportingRecursive({ useCaseSensitiveFileNames, + getCurrentDirectory, directoryExists, getAccessibleSortedChildDirectories, watchDirectory: nonRecursiveWatchDirectory, @@ -891,8 +927,8 @@ namespace ts { function nonRecursiveWatchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined): FileWatcher { Debug.assert(!recursive); - options = updateOptionsForWatchDirectory(options); - const watchDirectoryKind = Debug.checkDefined(options.watchDirectory); + const watchDirectoryOptions = updateOptionsForWatchDirectory(options); + const watchDirectoryKind = Debug.checkDefined(watchDirectoryOptions.watchDirectory); switch (watchDirectoryKind) { case WatchDirectoryKind.FixedPollingInterval: return pollingWatchFile( @@ -912,10 +948,10 @@ namespace ts { return fsWatch( directoryName, FileSystemEntryKind.Directory, - createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback), + createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback, options, useCaseSensitiveFileNames, getCurrentDirectory), recursive, PollingInterval.Medium, - getFallbackOptions(options) + getFallbackOptions(watchDirectoryOptions) ); default: Debug.assertNever(watchDirectoryKind); @@ -1149,6 +1185,8 @@ namespace ts { let activeSession: import("inspector").Session | "stopping" | undefined; let profilePath = "./profile.cpuprofile"; + const realpathSync = _fs.realpathSync.native ?? _fs.realpathSync; + const Buffer: { new (input: string, encoding?: string): any; from?(input: string, encoding?: string): any; @@ -1161,6 +1199,7 @@ namespace ts { const platform: string = _os.platform(); const useCaseSensitiveFileNames = isFileSystemCaseSensitive(); const fsSupportsRecursiveFsWatch = isNode4OrLater && (process.platform === "win32" || process.platform === "darwin"); + const getCurrentDirectory = memoize(() => process.cwd()); const { watchFile, watchDirectory } = createSystemWatchFunctions({ pollingWatchFile: createSingleFileWatcherPerName(fsWatchFileWorker, useCaseSensitiveFileNames), getModifiedTime, @@ -1168,6 +1207,7 @@ namespace ts { clearTimeout, fsWatch, useCaseSensitiveFileNames, + getCurrentDirectory, fileExists, // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) @@ -1214,9 +1254,7 @@ namespace ts { getExecutingFilePath() { return __filename; }, - getCurrentDirectory() { - return process.cwd(); - }, + getCurrentDirectory, getDirectories, getEnvironmentVariable(name: string) { return process.env[name] || ""; @@ -1235,8 +1273,8 @@ namespace ts { }, getFileSize(path) { try { - const stat = _fs.statSync(path); - if (stat.isFile()) { + const stat = statSync(path); + if (stat?.isFile()) { return stat.size; } } @@ -1283,6 +1321,16 @@ namespace ts { }; return nodeSystem; + /** + * `throwIfNoEntry` was added so recently that it's not in the node types. + * This helper encapsulates the mitigating usage of `any`. + * See https://github.com/nodejs/node/pull/33716 + */ + function statSync(path: string): import("fs").Stats | undefined { + // throwIfNoEntry will be ignored by older versions of node + return (_fs as any).statSync(path, { throwIfNoEntry: false }); + } + /** * Uses the builtin inspector APIs to capture a CPU profile * See https://nodejs.org/api/inspector.html#inspector_example_usage for details @@ -1341,7 +1389,7 @@ namespace ts { activeSession.post("Profiler.stop", (err, { profile }) => { if (!err) { try { - if (_fs.statSync(profilePath).isDirectory()) { + if (statSync(profilePath)?.isDirectory()) { profilePath = _path.join(profilePath, `${(new Date()).toISOString().replace(/:/g, "-")}+P${process.pid}.cpuprofile`); } } @@ -1631,7 +1679,10 @@ namespace ts { const name = combinePaths(path, entry); try { - stat = _fs.statSync(name); + stat = statSync(name); + if (!stat) { + continue; + } } catch (e) { continue; @@ -1668,7 +1719,10 @@ namespace ts { Error.stackTraceLimit = 0; try { - const stat = _fs.statSync(path); + const stat = statSync(path); + if (!stat) { + return false; + } switch (entryKind) { case FileSystemEntryKind.File: return stat.isFile(); case FileSystemEntryKind.Directory: return stat.isDirectory(); @@ -1697,7 +1751,7 @@ namespace ts { function realpath(path: string): string { try { - return _fs.realpathSync(path); + return realpathSync(path); } catch { return path; @@ -1706,7 +1760,7 @@ namespace ts { function getModifiedTime(path: string) { try { - return _fs.statSync(path).mtime; + return statSync(path)?.mtime; } catch (e) { return undefined; diff --git a/src/compiler/tracing.ts b/src/compiler/tracing.ts index 30b90a2b0d1a2..e6e63ebf79a52 100644 --- a/src/compiler/tracing.ts +++ b/src/compiler/tracing.ts @@ -1,16 +1,29 @@ /*@internal*/ /** Tracing events for the compiler. */ namespace ts.tracing { + export const enum Mode { + Project, + Build, + Server, + } + let fs: typeof import("fs") | false | undefined; let traceCount = 0; let traceFd: number | undefined; + let mode: Mode; + 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)[]; + }; + /** Starts tracing for the given project (unless the `fs` module is unavailable). */ - export function startTracing(configFilePath: string | undefined, traceDir: string, isBuildMode: boolean) { + export function startTracing(tracingMode: Mode, traceDir: string, configFilePath?: string) { Debug.assert(!traceFd, "Tracing already started"); if (fs === undefined) { @@ -26,6 +39,8 @@ namespace ts.tracing { return; } + mode = tracingMode; + if (legendPath === undefined) { legendPath = combinePaths(traceDir, "legend.json"); } @@ -35,7 +50,10 @@ namespace ts.tracing { fs.mkdirSync(traceDir, { recursive: true }); } - const countPart = isBuildMode ? `.${++traceCount}` : ``; + const countPart = + mode === Mode.Build ? `.${process.pid}-${++traceCount}` : + mode === Mode.Server ? `.${process.pid}` : + ``; const tracePath = combinePaths(traceDir, `trace${countPart}.json`); const typesPath = combinePaths(traceDir, `types${countPart}.json`); @@ -46,23 +64,28 @@ namespace ts.tracing { }); traceFd = fs.openSync(tracePath, "w"); - fs.writeSync(traceFd, `[\n`); + + // 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 (unless the `fs` module is unavailable). */ - export function stopTracing(typeCatalog: readonly Type[]) { + export function stopTracing(typeCatalog?: readonly Type[]) { if (!traceFd) { Debug.assert(!fs, "Tracing is not in progress"); return; } Debug.assert(fs); + Debug.assert(!!typeCatalog === (mode !== Mode.Server)); // Have a type catalog iff not in server mode - // This both indicates that the trace is untruncated and conveniently - // ensures that the last array element won't have a trailing comma. - fs.writeSync(traceFd, `{"pid":1,"tid":1,"ph":"i","ts":${1000 * timestamp()},"name":"done","s":"g"}\n`); - fs.writeSync(traceFd, `]\n`); - + fs.writeSync(traceFd, `\n]\n`); fs.closeSync(traceFd); traceFd = undefined; @@ -84,42 +107,69 @@ namespace ts.tracing { Parse = "parse", Program = "program", Bind = "bind", - Check = "check", + Check = "check", // Before we get into checking types (e.g. checkSourceFile) + CheckTypes = "checkTypes", Emit = "emit", + Session = "session", } - export function begin(phase: Phase, name: string, args: object) { - if (!traceFd) { - return; - } - Debug.assert(fs); - - performance.mark("beginTracing"); - fs.writeSync(traceFd, `{"pid":1,"tid":1,"ph":"B","cat":"${phase}","ts":${1000 * timestamp()},"name":"${name}","args":{ "ts": ${JSON.stringify(args)} }},\n`); - performance.mark("endTracing"); - performance.measure("Tracing", "beginTracing", "endTracing"); + export function instant(phase: Phase, name: string, args?: Args) { + if (!traceFd) return; + writeEvent("I", phase, name, args, `"s":"g"`); } - export function end() { - if (!traceFd) { - return; + 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 (!traceFd) return; + if (separateBeginAndEnd) { + writeEvent("B", phase, name, args); } - Debug.assert(fs); - - performance.mark("beginTracing"); - fs.writeSync(traceFd, `{"pid":1,"tid":1,"ph":"E","ts":${1000 * timestamp()}},\n`); - performance.mark("endTracing"); - performance.measure("Tracing", "beginTracing", "endTracing"); + eventStack.push({ phase, name, args, time: 1000 * timestamp(), separateBeginAndEnd }); } - - export function instant(phase: Phase, name: string, args: object) { - if (!traceFd) { - return; + export function pop() { + if (!traceFd) return; + Debug.assert(eventStack.length > 0); + writeStackEvent(eventStack.length - 1, 1000 * timestamp()); + eventStack.length--; + } + export function popAll() { + if (!traceFd) return; + const endTime = 1000 * timestamp(); + for (let i = eventStack.length - 1; i >= 0; i--) { + writeStackEvent(i, endTime); + } + eventStack.length = 0; + } + function writeStackEvent(index: number, endTime: number) { + const { phase, name, args, time, separateBeginAndEnd } = eventStack[index]; + if (separateBeginAndEnd) { + writeEvent("E", phase, name, args, /*extras*/ undefined, endTime); + } + else { + writeEvent("X", phase, name, args, `"dur":${endTime - time}`, time); } + } + + function writeEvent(eventType: string, phase: Phase, name: string, args: Args | undefined, extras?: string, + time: number = 1000 * timestamp()) { + Debug.assert(traceFd); Debug.assert(fs); + // In server mode, there's no easy way to dump type information, so we drop events that would require it. + if (mode === Mode.Server && phase === Phase.CheckTypes) return; + performance.mark("beginTracing"); - fs.writeSync(traceFd, `{"pid":1,"tid":1,"ph":"i","cat":"${phase}","ts":${1000 * timestamp()},"name":"${name}","s":"g","args":{ "ts": ${JSON.stringify(args)} }},\n`); + 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"); } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index f76652df95eb6..efca6f7f187d3 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -223,9 +223,9 @@ namespace ts { // Transform each node. const transformed: T[] = []; for (const node of nodes) { - tracing.begin(tracing.Phase.Emit, "transformNodes", node.kind === SyntaxKind.SourceFile ? { path: (node as any as SourceFile).path } : { kind: node.kind, pos: node.pos, end: node.end }); + tracing.push(tracing.Phase.Emit, "transformNodes", node.kind === SyntaxKind.SourceFile ? { path: (node as any as SourceFile).path } : { kind: node.kind, pos: node.pos, end: node.end }); transformed.push((allowDtsFiles ? transformation : transformRoot)(node)); - tracing.end(); + tracing.pop(); } // prevent modification of the lexical environment. diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 01e3d7c7896a8..f9e539770dcdc 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -90,8 +90,6 @@ namespace ts { return visitPropertyDeclaration(node as PropertyDeclaration); case SyntaxKind.VariableStatement: return visitVariableStatement(node as VariableStatement); - case SyntaxKind.ComputedPropertyName: - return visitComputedPropertyName(node as ComputedPropertyName); case SyntaxKind.PropertyAccessExpression: return visitPropertyAccessExpression(node as PropertyAccessExpression); case SyntaxKind.PrefixUnaryExpression: @@ -184,7 +182,7 @@ namespace ts { let node = visitEachChild(name, visitor, context); if (some(pendingExpressions)) { const expressions = pendingExpressions; - expressions.push(name.expression); + expressions.push(node.expression); pendingExpressions = []; node = factory.updateComputedPropertyName( node, @@ -579,7 +577,7 @@ namespace ts { } function isPropertyDeclarationThatRequiresConstructorStatement(member: ClassElement): member is PropertyDeclaration { - if (!isPropertyDeclaration(member) || hasStaticModifier(member)) { + if (!isPropertyDeclaration(member) || hasStaticModifier(member) || hasSyntacticModifier(getOriginalNode(member), ModifierFlags.Abstract)) { return false; } if (context.getCompilerOptions().useDefineForClassFields) { @@ -779,6 +777,9 @@ namespace ts { } const propertyOriginalNode = getOriginalNode(property); + if (hasSyntacticModifier(propertyOriginalNode, ModifierFlags.Abstract)) { + return undefined; + } const initializer = property.initializer || emitAssignment ? visitNode(property.initializer, visitor, isExpression) ?? factory.createVoidZero() : isParameterPropertyDeclaration(propertyOriginalNode, propertyOriginalNode.parent) && isIdentifier(propertyName) ? propertyName : factory.createVoidZero(); diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index ae8a58191a890..0ca8840059801 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -14,8 +14,8 @@ namespace ts { export function isInternalDeclaration(node: Node, currentSourceFile: SourceFile) { const parseTreeNode = getParseTreeNode(node); if (parseTreeNode && parseTreeNode.kind === SyntaxKind.Parameter) { - const paramIdx = (parseTreeNode.parent as FunctionLike).parameters.indexOf(parseTreeNode as ParameterDeclaration); - const previousSibling = paramIdx > 0 ? (parseTreeNode.parent as FunctionLike).parameters[paramIdx - 1] : undefined; + const paramIdx = (parseTreeNode.parent as SignatureDeclaration).parameters.indexOf(parseTreeNode as ParameterDeclaration); + const previousSibling = paramIdx > 0 ? (parseTreeNode.parent as SignatureDeclaration).parameters[paramIdx - 1] : undefined; const text = currentSourceFile.text; const commentRanges = previousSibling ? concatenate( @@ -681,6 +681,7 @@ namespace ts { decl, /*decorators*/ undefined, decl.modifiers, + decl.isTypeOnly, decl.name, factory.updateExternalModuleReference(decl.moduleReference, rewriteModuleSpecifier(decl, specifier)) ); @@ -1372,6 +1373,8 @@ namespace ts { } const hasPrivateIdentifier = some(input.members, member => !!member.name && isPrivateIdentifier(member.name)); + // When the class has at least one private identifier, create a unique constant identifier to retain the nominal typing behavior + // Prevents other classes with the same public members from being used in place of the current class const privateIdentifier = hasPrivateIdentifier ? [ factory.createPropertyDeclaration( /*decorators*/ undefined, diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 1dcbc0814750d..0e7eaac22c9af 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -5,6 +5,7 @@ namespace ts { level: FlattenLevel; downlevelIteration: boolean; hoistTempVariables: boolean; + hasTransformedPriorElement?: boolean; // indicates whether we've transformed a prior declaration emitExpression: (value: Expression) => void; emitBindingOrAssignment: (target: BindingOrAssignmentElementTarget, value: Expression, location: TextRange, original: Node | undefined) => void; createArrayBindingOrAssignmentPattern: (elements: BindingOrAssignmentElement[]) => ArrayBindingOrAssignmentPattern; @@ -198,7 +199,7 @@ namespace ts { bindingOrAssignmentElementContainsNonLiteralComputedName(node))) { // If the right-hand value of the assignment is also an assignment target then // we need to cache the right-hand value. - initializer = ensureIdentifier(flattenContext, initializer, /*reuseIdentifierExpressions*/ false, initializer); + initializer = ensureIdentifier(flattenContext, visitNode(initializer, flattenContext.visitor), /*reuseIdentifierExpressions*/ false, initializer); node = context.factory.updateVariableDeclaration(node, node.name, /*exclamationToken*/ undefined, /*type*/ undefined, initializer); } } @@ -265,18 +266,27 @@ namespace ts { value: Expression | undefined, location: TextRange, skipInitializer?: boolean) { + const bindingTarget = getTargetOfBindingOrAssignmentElement(element)!; // TODO: GH#18217 if (!skipInitializer) { const initializer = visitNode(getInitializerOfBindingOrAssignmentElement(element), flattenContext.visitor, isExpression); if (initializer) { // Combine value and initializer - value = value ? createDefaultValueCheck(flattenContext, value, initializer, location) : initializer; + if (value) { + value = createDefaultValueCheck(flattenContext, value, initializer, location); + // If 'value' is not a simple expression, it could contain side-effecting code that should evaluate before an object or array binding pattern. + if (!isSimpleInlineableExpression(initializer) && isBindingOrAssignmentPattern(bindingTarget)) { + value = ensureIdentifier(flattenContext, value, /*reuseIdentifierExpressions*/ true, location); + } + } + else { + value = initializer; + } } else if (!value) { // Use 'void 0' in absence of value and initializer value = flattenContext.context.factory.createVoidZero(); } } - const bindingTarget = getTargetOfBindingOrAssignmentElement(element)!; // TODO: GH#18217 if (isObjectBindingOrAssignmentPattern(bindingTarget)) { flattenObjectBindingOrAssignmentPattern(flattenContext, element, bindingTarget, value!, location); } @@ -393,7 +403,8 @@ namespace ts { if (flattenContext.level >= FlattenLevel.ObjectRest) { // If an array pattern contains an ObjectRest, we must cache the result so that we // can perform the ObjectRest destructuring in a different declaration - if (element.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { + if (element.transformFlags & TransformFlags.ContainsObjectRestOrSpread || flattenContext.hasTransformedPriorElement && !isSimpleBindingOrAssignmentElement(element)) { + flattenContext.hasTransformedPriorElement = true; const temp = flattenContext.context.factory.createTempVariable(/*recordTempVariable*/ undefined); if (flattenContext.hoistTempVariables) { flattenContext.context.hoistVariableDeclaration(temp); @@ -428,6 +439,17 @@ namespace ts { } } + function isSimpleBindingOrAssignmentElement(element: BindingOrAssignmentElement): boolean { + const target = getTargetOfBindingOrAssignmentElement(element); + if (!target || isOmittedExpression(target)) return true; + const propertyName = tryGetPropertyNameOfBindingOrAssignmentElement(element); + if (propertyName && !isPropertyNameLiteral(propertyName)) return false; + const initializer = getInitializerOfBindingOrAssignmentElement(element); + if (initializer && !isSimpleInlineableExpression(initializer)) return false; + if (isBindingOrAssignmentPattern(target)) return every(getElementsOfBindingOrAssignmentPattern(target), isSimpleBindingOrAssignmentElement); + return isIdentifier(target); + } + /** * Creates an expression used to provide a default value if a value is `undefined` at runtime. * diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 813ce25672b87..60274fb6b452d 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -355,12 +355,11 @@ namespace ts { } function visitor(node: Node): VisitResult { - if (shouldVisitNode(node)) { - return visitJavaScript(node); - } - else { - return node; - } + return shouldVisitNode(node) ? visitorWorker(node, /*expressionResultIsUnused*/ false) : node; + } + + function visitorWithUnusedExpressionResult(node: Node): VisitResult { + return shouldVisitNode(node) ? visitorWorker(node, /*expressionResultIsUnused*/ true) : node; } function callExpressionVisitor(node: Node): VisitResult { @@ -370,7 +369,7 @@ namespace ts { return visitor(node); } - function visitJavaScript(node: Node): VisitResult { + function visitorWorker(node: Node, expressionResultIsUnused: boolean): VisitResult { switch (node.kind) { case SyntaxKind.StaticKeyword: return undefined; // elide static keyword @@ -456,10 +455,13 @@ namespace ts { return visitNewExpression(node); case SyntaxKind.ParenthesizedExpression: - return visitParenthesizedExpression(node, /*needsDestructuringValue*/ true); + return visitParenthesizedExpression(node, expressionResultIsUnused); case SyntaxKind.BinaryExpression: - return visitBinaryExpression(node, /*needsDestructuringValue*/ true); + return visitBinaryExpression(node, expressionResultIsUnused); + + case SyntaxKind.CommaListExpression: + return visitCommaListExpression(node, expressionResultIsUnused); case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.TemplateHead: @@ -507,6 +509,9 @@ namespace ts { case SyntaxKind.ReturnStatement: return visitReturnStatement(node); + case SyntaxKind.VoidExpression: + return visitVoidExpression(node as VoidExpression); + default: return visitEachChild(node, visitor, context); } @@ -596,6 +601,10 @@ namespace ts { return node; } + function visitVoidExpression(node: VoidExpression): Expression { + return visitEachChild(node, visitorWithUnusedExpressionResult, context); + } + function visitIdentifier(node: Identifier): Identifier { if (!convertedLoopState) { return node; @@ -1975,47 +1984,28 @@ namespace ts { * @param node An ExpressionStatement node. */ function visitExpressionStatement(node: ExpressionStatement): Statement { - // If we are here it is most likely because our expression is a destructuring assignment. - switch (node.expression.kind) { - case SyntaxKind.ParenthesizedExpression: - return factory.updateExpressionStatement(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); - case SyntaxKind.BinaryExpression: - return factory.updateExpressionStatement(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); - } - return visitEachChild(node, visitor, context); + return visitEachChild(node, visitorWithUnusedExpressionResult, context); } /** * Visits a ParenthesizedExpression that may contain a destructuring assignment. * * @param node A ParenthesizedExpression node. - * @param needsDestructuringValue A value indicating whether we need to hold onto the rhs - * of a destructuring assignment. + * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the + * expression of an `ExpressionStatement`). */ - function visitParenthesizedExpression(node: ParenthesizedExpression, needsDestructuringValue: boolean): ParenthesizedExpression { - // If we are here it is most likely because our expression is a destructuring assignment. - if (!needsDestructuringValue) { - // By default we always emit the RHS at the end of a flattened destructuring - // expression. If we are in a state where we do not need the destructuring value, - // we pass that information along to the children that care about it. - switch (node.expression.kind) { - case SyntaxKind.ParenthesizedExpression: - return factory.updateParenthesizedExpression(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); - case SyntaxKind.BinaryExpression: - return factory.updateParenthesizedExpression(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); - } - } - return visitEachChild(node, visitor, context); + function visitParenthesizedExpression(node: ParenthesizedExpression, expressionResultIsUnused: boolean): ParenthesizedExpression { + return visitEachChild(node, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, context); } /** * Visits a BinaryExpression that contains a destructuring assignment. * * @param node A BinaryExpression node. - * @param needsDestructuringValue A value indicating whether we need to hold onto the rhs - * of a destructuring assignment. + * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the + * expression of an `ExpressionStatement`). */ - function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { + function visitBinaryExpression(node: BinaryExpression, expressionResultIsUnused: boolean): Expression { // If we are here it is because this is a destructuring assignment. if (isDestructuringAssignment(node)) { return flattenDestructuringAssignment( @@ -2023,11 +2013,40 @@ namespace ts { visitor, context, FlattenLevel.All, - needsDestructuringValue); + !expressionResultIsUnused); + } + if (node.operatorToken.kind === SyntaxKind.CommaToken) { + return factory.updateBinaryExpression( + node, + visitNode(node.left, visitorWithUnusedExpressionResult, isExpression), + node.operatorToken, + visitNode(node.right, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, isExpression) + ); } return visitEachChild(node, visitor, context); } + /** + * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the + * expression of an `ExpressionStatement`). + */ + function visitCommaListExpression(node: CommaListExpression, expressionResultIsUnused: boolean): Expression { + if (expressionResultIsUnused) { + return visitEachChild(node, visitorWithUnusedExpressionResult, context); + } + let result: Expression[] | undefined; + for (let i = 0; i < node.elements.length; i++) { + const element = node.elements[i]; + const visited = visitNode(element, i < node.elements.length - 1 ? visitorWithUnusedExpressionResult : visitor, isExpression); + if (result || visited !== element) { + result ||= node.elements.slice(0, i); + result.push(visited); + } + } + const elements = result ? setTextRange(factory.createNodeArray(result), node.elements) : node.elements; + return factory.updateCommaListExpression(node, elements); + } + function isVariableStatementOfTypeScriptClassWrapper(node: VariableStatement) { return node.declarationList.declarations.length === 1 && !!node.declarationList.declarations[0].initializer @@ -2288,6 +2307,16 @@ namespace ts { outermostLabeledStatement); } + function visitEachChildOfForStatement(node: ForStatement) { + return factory.updateForStatement( + node, + visitNode(node.initializer, visitorWithUnusedExpressionResult, isForInitializer), + visitNode(node.condition, visitor, isExpression), + visitNode(node.incrementor, visitorWithUnusedExpressionResult, isExpression), + visitNode(node.statement, visitor, isStatement, factory.liftToBlock) + ); + } + function visitForInStatement(node: ForInStatement, outermostLabeledStatement: LabeledStatement | undefined) { return visitIterationStatementWithFacts( HierarchyFacts.ForInOrForOfStatementExcludes, @@ -2371,7 +2400,7 @@ namespace ts { // evaluated on every iteration. const assignment = factory.createAssignment(initializer, boundValue); if (isDestructuringAssignment(assignment)) { - statements.push(factory.createExpressionStatement(visitBinaryExpression(assignment, /*needsDestructuringValue*/ false))); + statements.push(factory.createExpressionStatement(visitBinaryExpression(assignment, /*expressionResultIsUnused*/ true))); } else { setTextRangeEnd(assignment, initializer.end); @@ -2714,7 +2743,10 @@ namespace ts { const result = convert ? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined, ancestorFacts) - : factory.restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement, convertedLoopState && resetLabel); + : factory.restoreEnclosingLabel( + isForStatement(node) ? visitEachChildOfForStatement(node) : visitEachChild(node, visitor, context), + outermostLabeledStatement, + convertedLoopState && resetLabel); if (convertedLoopState) { convertedLoopState.allowedNonLabeledJumps = saveAllowedNonLabeledJumps; @@ -2777,9 +2809,9 @@ namespace ts { const shouldConvertIncrementor = shouldConvertCondition || node.incrementor && shouldConvertPartOfIterationStatement(node.incrementor); return factory.updateForStatement( node, - visitNode(initializerFunction ? initializerFunction.part : node.initializer, visitor, isForInitializer), + visitNode(initializerFunction ? initializerFunction.part : node.initializer, visitorWithUnusedExpressionResult, isForInitializer), visitNode(shouldConvertCondition ? undefined : node.condition, visitor, isExpression), - visitNode(shouldConvertIncrementor ? undefined : node.incrementor, visitor, isExpression), + visitNode(shouldConvertIncrementor ? undefined : node.incrementor, visitorWithUnusedExpressionResult, isExpression), convertedLoopBody ); } @@ -3123,7 +3155,7 @@ namespace ts { const containsYield = (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0; - let emitFlags: EmitFlags = 0; + let emitFlags: EmitFlags = EmitFlags.ReuseTempVariableScope; if (currentState.containsLexicalThis) emitFlags |= EmitFlags.CapturesThis; if (containsYield && (hierarchyFacts & HierarchyFacts.AsyncFunctionBody) !== 0) emitFlags |= EmitFlags.AsyncFunctionBody; diff --git a/src/compiler/transformers/es2018.ts b/src/compiler/transformers/es2018.ts index f6c3ae6635431..41fcc6c25d9b9 100644 --- a/src/compiler/transformers/es2018.ts +++ b/src/compiler/transformers/es2018.ts @@ -119,11 +119,11 @@ namespace ts { } function visitor(node: Node): VisitResult { - return visitorWorker(node, /*noDestructuringValue*/ false); + return visitorWorker(node, /*expressionResultIsUnused*/ false); } - function visitorNoDestructuringValue(node: Node): VisitResult { - return visitorWorker(node, /*noDestructuringValue*/ true); + function visitorWithUnusedExpressionResult(node: Node): VisitResult { + return visitorWorker(node, /*expressionResultIsUnused*/ true); } function visitorNoAsyncModifier(node: Node): VisitResult { @@ -147,7 +147,11 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitorWorker(node: Node, noDestructuringValue: boolean): VisitResult { + /** + * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the + * expression of an `ExpressionStatement`). + */ + function visitorWorker(node: Node, expressionResultIsUnused: boolean): VisitResult { if ((node.transformFlags & TransformFlags.ContainsES2018) === 0) { return node; } @@ -163,7 +167,9 @@ namespace ts { case SyntaxKind.ObjectLiteralExpression: return visitObjectLiteralExpression(node as ObjectLiteralExpression); case SyntaxKind.BinaryExpression: - return visitBinaryExpression(node as BinaryExpression, noDestructuringValue); + return visitBinaryExpression(node as BinaryExpression, expressionResultIsUnused); + case SyntaxKind.CommaListExpression: + return visitCommaListExpression(node as CommaListExpression, expressionResultIsUnused); case SyntaxKind.CatchClause: return visitCatchClause(node as CatchClause); case SyntaxKind.VariableStatement: @@ -235,7 +241,7 @@ namespace ts { case SyntaxKind.ExpressionStatement: return visitExpressionStatement(node as ExpressionStatement); case SyntaxKind.ParenthesizedExpression: - return visitParenthesizedExpression(node as ParenthesizedExpression, noDestructuringValue); + return visitParenthesizedExpression(node as ParenthesizedExpression, expressionResultIsUnused); case SyntaxKind.TaggedTemplateExpression: return visitTaggedTemplateExpression(node as TaggedTemplateExpression); case SyntaxKind.PropertyAccessExpression: @@ -411,11 +417,15 @@ namespace ts { } function visitExpressionStatement(node: ExpressionStatement): ExpressionStatement { - return visitEachChild(node, visitorNoDestructuringValue, context); + return visitEachChild(node, visitorWithUnusedExpressionResult, context); } - function visitParenthesizedExpression(node: ParenthesizedExpression, noDestructuringValue: boolean): ParenthesizedExpression { - return visitEachChild(node, noDestructuringValue ? visitorNoDestructuringValue : visitor, context); + /** + * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the + * expression of an `ExpressionStatement`). + */ + function visitParenthesizedExpression(node: ParenthesizedExpression, expressionResultIsUnused: boolean): ParenthesizedExpression { + return visitEachChild(node, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, context); } function visitSourceFile(node: SourceFile): SourceFile { @@ -450,28 +460,51 @@ namespace ts { * Visits a BinaryExpression that contains a destructuring assignment. * * @param node A BinaryExpression node. + * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the + * expression of an `ExpressionStatement`). */ - function visitBinaryExpression(node: BinaryExpression, noDestructuringValue: boolean): Expression { + function visitBinaryExpression(node: BinaryExpression, expressionResultIsUnused: boolean): Expression { if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { return flattenDestructuringAssignment( node, visitor, context, FlattenLevel.ObjectRest, - !noDestructuringValue + !expressionResultIsUnused ); } - else if (node.operatorToken.kind === SyntaxKind.CommaToken) { + if (node.operatorToken.kind === SyntaxKind.CommaToken) { return factory.updateBinaryExpression( node, - visitNode(node.left, visitorNoDestructuringValue, isExpression), + visitNode(node.left, visitorWithUnusedExpressionResult, isExpression), node.operatorToken, - visitNode(node.right, noDestructuringValue ? visitorNoDestructuringValue : visitor, isExpression) + visitNode(node.right, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, isExpression) ); } return visitEachChild(node, visitor, context); } + /** + * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the + * expression of an `ExpressionStatement`). + */ + function visitCommaListExpression(node: CommaListExpression, expressionResultIsUnused: boolean): Expression { + if (expressionResultIsUnused) { + return visitEachChild(node, visitorWithUnusedExpressionResult, context); + } + let result: Expression[] | undefined; + for (let i = 0; i < node.elements.length; i++) { + const element = node.elements[i]; + const visited = visitNode(element, i < node.elements.length - 1 ? visitorWithUnusedExpressionResult : visitor, isExpression); + if (result || visited !== element) { + result ||= node.elements.slice(0, i); + result.push(visited); + } + } + const elements = result ? setTextRange(factory.createNodeArray(result), node.elements) : node.elements; + return factory.updateCommaListExpression(node, elements); + } + function visitCatchClause(node: CatchClause) { if (node.variableDeclaration && isBindingPattern(node.variableDeclaration.name) && @@ -539,15 +572,15 @@ namespace ts { function visitForStatement(node: ForStatement): VisitResult { return factory.updateForStatement( node, - visitNode(node.initializer, visitorNoDestructuringValue, isForInitializer), + visitNode(node.initializer, visitorWithUnusedExpressionResult, isForInitializer), visitNode(node.condition, visitor, isExpression), - visitNode(node.incrementor, visitor, isExpression), + visitNode(node.incrementor, visitorWithUnusedExpressionResult, isExpression), visitNode(node.statement, visitor, isStatement) ); } function visitVoidExpression(node: VoidExpression) { - return visitEachChild(node, visitorNoDestructuringValue, context); + return visitEachChild(node, visitorWithUnusedExpressionResult, context); } /** diff --git a/src/compiler/transformers/es2020.ts b/src/compiler/transformers/es2020.ts index a3a87df91ed33..bd0d12cf8d772 100644 --- a/src/compiler/transformers/es2020.ts +++ b/src/compiler/transformers/es2020.ts @@ -77,7 +77,6 @@ namespace ts { if (!isSimpleCopiableExpression(expression)) { thisArg = factory.createTempVariable(hoistVariableDeclaration); expression = factory.createAssignment(thisArg, expression); - // if (inParameterInitializer) tempVariableInParameter = true; } else { thisArg = expression; @@ -117,7 +116,6 @@ namespace ts { if (!isSimpleCopiableExpression(leftExpression)) { capturedLeft = factory.createTempVariable(hoistVariableDeclaration); leftExpression = factory.createAssignment(capturedLeft, leftExpression); - // if (inParameterInitializer) tempVariableInParameter = true; } let rightExpression = capturedLeft; let thisArg: Expression | undefined; @@ -130,7 +128,6 @@ namespace ts { if (!isSimpleCopiableExpression(rightExpression)) { thisArg = factory.createTempVariable(hoistVariableDeclaration); rightExpression = factory.createAssignment(thisArg, rightExpression); - // if (inParameterInitializer) tempVariableInParameter = true; } else { thisArg = rightExpression; @@ -163,6 +160,7 @@ namespace ts { 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; } @@ -188,15 +186,14 @@ namespace ts { if (!isSimpleCopiableExpression(left)) { right = factory.createTempVariable(hoistVariableDeclaration); left = factory.createAssignment(right, left); - // if (inParameterInitializer) tempVariableInParameter = true; } - return factory.createConditionalExpression( + return setTextRange(factory.createConditionalExpression( createNotNullCondition(left, right), /*questionToken*/ undefined, right, /*colonToken*/ undefined, visitNode(node.right, visitor, isExpression), - ); + ), node); } function visitDeleteExpression(node: DeleteExpression) { diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index e988100903b0e..6ae08275e3a02 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -397,6 +397,8 @@ namespace ts { switch (node.kind) { case SyntaxKind.BinaryExpression: return visitBinaryExpression(node); + case SyntaxKind.CommaListExpression: + return visitCommaListExpression(node); case SyntaxKind.ConditionalExpression: return visitConditionalExpression(node); case SyntaxKind.YieldExpression: @@ -772,6 +774,65 @@ namespace ts { return visitEachChild(node, visitor, context); } + /** + * Visits a comma expression containing `yield`. + * + * @param node The node to visit. + */ + function visitCommaExpression(node: BinaryExpression) { + // [source] + // x = a(), yield, b(); + // + // [intermediate] + // a(); + // .yield resumeLabel + // .mark resumeLabel + // x = %sent%, b(); + + let pendingExpressions: Expression[] = []; + visit(node.left); + visit(node.right); + return factory.inlineExpressions(pendingExpressions); + + function visit(node: Expression) { + if (isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.CommaToken) { + visit(node.left); + visit(node.right); + } + else { + if (containsYield(node) && pendingExpressions.length > 0) { + emitWorker(OpCode.Statement, [factory.createExpressionStatement(factory.inlineExpressions(pendingExpressions))]); + pendingExpressions = []; + } + + pendingExpressions.push(visitNode(node, visitor, isExpression)); + } + } + } + + /** + * Visits a comma-list expression. + * + * @param node The node to visit. + */ + function visitCommaListExpression(node: CommaListExpression) { + // flattened version of `visitCommaExpression` + let pendingExpressions: Expression[] = []; + for (const elem of node.elements) { + if (isBinaryExpression(elem) && elem.operatorToken.kind === SyntaxKind.CommaToken) { + pendingExpressions.push(visitCommaExpression(elem)); + } + else { + if (containsYield(elem) && pendingExpressions.length > 0) { + emitWorker(OpCode.Statement, [factory.createExpressionStatement(factory.inlineExpressions(pendingExpressions))]); + pendingExpressions = []; + } + pendingExpressions.push(visitNode(elem, visitor, isExpression)); + } + } + return factory.inlineExpressions(pendingExpressions); + } + /** * Visits a logical binary expression containing `yield`. * @@ -825,42 +886,6 @@ namespace ts { return resultLocal; } - /** - * Visits a comma expression containing `yield`. - * - * @param node The node to visit. - */ - function visitCommaExpression(node: BinaryExpression) { - // [source] - // x = a(), yield, b(); - // - // [intermediate] - // a(); - // .yield resumeLabel - // .mark resumeLabel - // x = %sent%, b(); - - let pendingExpressions: Expression[] = []; - visit(node.left); - visit(node.right); - return factory.inlineExpressions(pendingExpressions); - - function visit(node: Expression) { - if (isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.CommaToken) { - visit(node.left); - visit(node.right); - } - else { - if (containsYield(node) && pendingExpressions.length > 0) { - emitWorker(OpCode.Statement, [factory.createExpressionStatement(factory.inlineExpressions(pendingExpressions))]); - pendingExpressions = []; - } - - pendingExpressions.push(visitNode(node, visitor, isExpression)); - } - } - } - /** * Visits a conditional expression containing `yield`. * diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 0766cfbbfcf16..9814f29b2a28f 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -4,7 +4,7 @@ namespace ts { interface PerFileState { importSpecifier?: string; filenameDeclaration?: VariableDeclaration & { name: Identifier; }; - utilizedImplicitRuntimeImports?: Map; + utilizedImplicitRuntimeImports?: Map>; } const { @@ -26,12 +26,12 @@ namespace ts { return currentFileState.filenameDeclaration.name; } - function getJsxFactoryCalleePrimitive(children: readonly JsxChild[] | undefined): "jsx" | "jsxs" | "jsxDEV" { - return compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsxDEV" : children && children.length > 1 ? "jsxs" : "jsx"; + function getJsxFactoryCalleePrimitive(childrenLength: number): "jsx" | "jsxs" | "jsxDEV" { + return compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsxDEV" : childrenLength > 1 ? "jsxs" : "jsx"; } - function getJsxFactoryCallee(children: readonly JsxChild[] | undefined) { - const type = getJsxFactoryCalleePrimitive(children); + function getJsxFactoryCallee(childrenLength: number) { + const type = getJsxFactoryCalleePrimitive(childrenLength); return getImplicitImportForName(type); } @@ -40,17 +40,25 @@ namespace ts { } function getImplicitImportForName(name: string) { - const existing = currentFileState.utilizedImplicitRuntimeImports?.get(name); + const importSource = name === "createElement" + ? currentFileState.importSpecifier! + : getJSXRuntimeImport(currentFileState.importSpecifier, compilerOptions)!; + const existing = currentFileState.utilizedImplicitRuntimeImports?.get(importSource)?.get(name); if (existing) { return existing.name; } if (!currentFileState.utilizedImplicitRuntimeImports) { currentFileState.utilizedImplicitRuntimeImports = createMap(); } + let specifierSourceImports = currentFileState.utilizedImplicitRuntimeImports.get(importSource); + if (!specifierSourceImports) { + specifierSourceImports = createMap(); + currentFileState.utilizedImplicitRuntimeImports.set(importSource, specifierSourceImports); + } const generatedName = factory.createUniqueName(`_${name}`, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel | GeneratedIdentifierFlags.AllowNameSubstitution); const specifier = factory.createImportSpecifier(factory.createIdentifier(name), generatedName); generatedName.generatedImportReference = specifier; - currentFileState.utilizedImplicitRuntimeImports.set(name, specifier); + specifierSourceImports.set(name, specifier); return generatedName; } @@ -73,29 +81,30 @@ namespace ts { if (currentFileState.filenameDeclaration) { statements = insertStatementAfterCustomPrologue(statements.slice(), factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([currentFileState.filenameDeclaration], NodeFlags.Const))); } - if (currentFileState.utilizedImplicitRuntimeImports && currentFileState.utilizedImplicitRuntimeImports.size && currentFileState.importSpecifier !== undefined) { - const specifier = `${currentFileState.importSpecifier}/${compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime" : "jsx-runtime"}`; - if (isExternalModule(node)) { - // Add `import` statement - const importStatement = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, factory.createImportClause(/*typeOnly*/ false, /*name*/ undefined, factory.createNamedImports(arrayFrom(currentFileState.utilizedImplicitRuntimeImports.values()))), factory.createStringLiteral(specifier)); - setParentRecursive(importStatement, /*incremental*/ false); - statements = insertStatementAfterCustomPrologue(statements.slice(), importStatement); - } - else if (isExternalOrCommonJsModule(node)) { - // Add `require` statement - const requireStatement = factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([ - factory.createVariableDeclaration( - factory.createObjectBindingPattern(map(arrayFrom(currentFileState.utilizedImplicitRuntimeImports.values()), s => factory.createBindingElement(/*dotdotdot*/ undefined, s.propertyName, s.name))), - /*exclaimationToken*/ undefined, - /*type*/ undefined, - factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, [factory.createStringLiteral(specifier)]) - ) - ], NodeFlags.Const)); - setParentRecursive(requireStatement, /*incremental*/ false); - statements = insertStatementAfterCustomPrologue(statements.slice(), requireStatement); - } - else { - // Do nothing (script file) - consider an error in the checker? + if (currentFileState.utilizedImplicitRuntimeImports) { + for (const [importSource, importSpecifiersMap] of arrayFrom(currentFileState.utilizedImplicitRuntimeImports.entries())) { + if (isExternalModule(node)) { + // Add `import` statement + const importStatement = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, factory.createImportClause(/*typeOnly*/ false, /*name*/ undefined, factory.createNamedImports(arrayFrom(importSpecifiersMap.values()))), factory.createStringLiteral(importSource)); + setParentRecursive(importStatement, /*incremental*/ false); + statements = insertStatementAfterCustomPrologue(statements.slice(), importStatement); + } + else if (isExternalOrCommonJsModule(node)) { + // Add `require` statement + const requireStatement = factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([ + factory.createVariableDeclaration( + factory.createObjectBindingPattern(map(arrayFrom(importSpecifiersMap.values()), s => factory.createBindingElement(/*dotdotdot*/ undefined, s.propertyName, s.name))), + /*exclaimationToken*/ undefined, + /*type*/ undefined, + factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, [factory.createStringLiteral(importSource)]) + ) + ], NodeFlags.Const)); + setParentRecursive(requireStatement, /*incremental*/ false); + statements = insertStatementAfterCustomPrologue(statements.slice(), requireStatement); + } + else { + // Do nothing (script file) - consider an error in the checker? + } } } if (statements !== visited.statements) { @@ -191,8 +200,9 @@ namespace ts { } function convertJsxChildrenToChildrenPropObject(children: readonly JsxChild[]) { - if (children.length === 1) { - const result = transformJsxChildToExpression(children[0]); + const nonWhitespaceChildren = getSemanticJsxChildren(children); + if (length(nonWhitespaceChildren) === 1) { + const result = transformJsxChildToExpression(nonWhitespaceChildren[0]); return result && factory.createObjectLiteralExpression([ factory.createPropertyAssignment("children", result) ]); @@ -208,48 +218,51 @@ namespace ts { let objectProperties: Expression; const keyAttr = find(node.attributes.properties, p => !!p.name && isIdentifier(p.name) && p.name.escapedText === "key") as JsxAttribute | undefined; const attrs = keyAttr ? filter(node.attributes.properties, p => p !== keyAttr) : node.attributes.properties; - if (attrs.length === 0) { - objectProperties = factory.createObjectLiteralExpression([]); - // When there are no attributes, React wants {} - } - else { + + let segments: Expression[] = []; + if (attrs.length) { // Map spans of JsxAttribute nodes into object literals and spans // of JsxSpreadAttribute nodes into expressions. - const segments = flatten( + segments = flatten( spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread ? map(attrs, transformJsxSpreadAttributeToExpression) : factory.createObjectLiteralExpression(map(attrs, transformJsxAttributeToObjectLiteralElement)) ) ); - if (children && children.length) { - const result = convertJsxChildrenToChildrenPropObject(children); - if (result) { - segments.push(result); - } - } - if (isJsxSpreadAttribute(attrs[0])) { // We must always emit at least one object literal before a spread // argument.factory.createObjectLiteral segments.unshift(factory.createObjectLiteralExpression()); } + } + if (children && children.length) { + const result = convertJsxChildrenToChildrenPropObject(children); + if (result) { + segments.push(result); + } + } + if (segments.length === 0) { + objectProperties = factory.createObjectLiteralExpression([]); + // When there are no attributes, React wants {} + } + else { // Either emit one big object literal (no spread attribs), or // a call to the __assign helper. objectProperties = singleOrUndefined(segments) || emitHelpers().createAssignHelper(segments); } - return visitJsxOpeningLikeElementOrFragmentJSX(tagName, objectProperties, keyAttr, children, isChild, location); + return visitJsxOpeningLikeElementOrFragmentJSX(tagName, objectProperties, keyAttr, length(getSemanticJsxChildren(children || emptyArray)), isChild, location); } - function visitJsxOpeningLikeElementOrFragmentJSX(tagName: Expression, objectProperties: Expression, keyAttr: JsxAttribute | undefined, children: readonly JsxChild[] | undefined, isChild: boolean, location: TextRange) { + function visitJsxOpeningLikeElementOrFragmentJSX(tagName: Expression, objectProperties: Expression, keyAttr: JsxAttribute | undefined, childrenLength: number, isChild: boolean, location: TextRange) { const args: Expression[] = [tagName, objectProperties, !keyAttr ? factory.createVoidZero() : transformJsxAttributeInitializer(keyAttr.initializer)]; if (compilerOptions.jsx === JsxEmit.ReactJSXDev) { const originalFile = getOriginalNode(currentSourceFile); if (originalFile && isSourceFile(originalFile)) { // isStaticChildren development flag - args.push(children && children.length > 1 ? factory.createTrue() : factory.createFalse()); + args.push(childrenLength > 1 ? factory.createTrue() : factory.createFalse()); // __source development flag const lineCol = getLineAndCharacterOfPosition(originalFile, location.pos); args.push(factory.createObjectLiteralExpression([ @@ -261,7 +274,7 @@ namespace ts { args.push(factory.createThis()); } } - const element = setTextRange(factory.createCallExpression(getJsxFactoryCallee(children), /*typeArguments*/ undefined, args), location); + const element = setTextRange(factory.createCallExpression(getJsxFactoryCallee(childrenLength), /*typeArguments*/ undefined, args), location); if (isChild) { startOnNewLine(element); @@ -302,14 +315,21 @@ namespace ts { } } + const callee = currentFileState.importSpecifier === undefined + ? createJsxFactoryExpression( + factory, + context.getEmitResolver().getJsxFactoryEntity(currentSourceFile), + compilerOptions.reactNamespace!, // TODO: GH#18217 + node + ) + : getImplicitImportForName("createElement"); + const element = createExpressionForJsxElement( factory, - context.getEmitResolver().getJsxFactoryEntity(currentSourceFile), - compilerOptions.reactNamespace!, // TODO: GH#18217 + callee, tagName, objectProperties, mapDefined(children, transformJsxChildToExpression), - node, location ); @@ -332,7 +352,7 @@ namespace ts { getImplicitJsxFragmentReference(), childrenProps || factory.createObjectLiteralExpression([]), /*keyAttr*/ undefined, - children, + length(getSemanticJsxChildren(children)), isChild, location ); diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index b3053f7b13793..d2f4917d50b7b 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -609,7 +609,10 @@ namespace ts { } function visitImportCallExpression(node: ImportCall): Expression { - const argument = visitNode(firstOrUndefined(node.arguments), moduleExpressionElementVisitor); + const externalModuleName = getExternalModuleNameLiteral(factory, node, currentSourceFile, host, resolver, compilerOptions); + const firstArgument = visitNode(firstOrUndefined(node.arguments), moduleExpressionElementVisitor); + // Only use the external module name if it differs from the first argument. This allows us to preserve the quote style of the argument on output. + const argument = externalModuleName && (!firstArgument || !isStringLiteral(firstArgument) || firstArgument.text !== externalModuleName.text) ? externalModuleName : firstArgument; const containsLexicalThis = !!(node.transformFlags & TransformFlags.ContainsLexicalThis); switch (compilerOptions.module) { case ModuleKind.AMD: @@ -1790,7 +1793,7 @@ namespace ts { const name = importDeclaration.propertyName || importDeclaration.name; return setTextRange( factory.createPropertyAccessExpression( - factory.getGeneratedNameForNode(importDeclaration.parent.parent.parent), + factory.getGeneratedNameForNode(importDeclaration.parent?.parent?.parent || importDeclaration), factory.cloneNode(name) ), /*location*/ node diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 07e99ed12610f..a5e0c9bbbf4ef 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1495,13 +1495,17 @@ namespace ts { // } // }; // }); + const externalModuleName = getExternalModuleNameLiteral(factory, node, currentSourceFile, host, resolver, compilerOptions); + const firstArgument = visitNode(firstOrUndefined(node.arguments), destructuringAndImportCallVisitor); + // Only use the external module name if it differs from the first argument. This allows us to preserve the quote style of the argument on output. + const argument = externalModuleName && (!firstArgument || !isStringLiteral(firstArgument) || firstArgument.text !== externalModuleName.text) ? externalModuleName : firstArgument; return factory.createCallExpression( factory.createPropertyAccessExpression( contextObject, factory.createIdentifier("import") ), /*typeArguments*/ undefined, - some(node.arguments) ? [visitNode(node.arguments[0], destructuringAndImportCallVisitor)] : [] + argument ? [argument] : [] ); } @@ -1678,7 +1682,7 @@ namespace ts { factory.createPropertyAssignment( factory.cloneNode(name), factory.createPropertyAccessExpression( - factory.getGeneratedNameForNode(importDeclaration.parent.parent.parent), + factory.getGeneratedNameForNode(importDeclaration.parent?.parent?.parent || importDeclaration), factory.cloneNode(importDeclaration.propertyName || importDeclaration.name) ), ), @@ -1747,7 +1751,7 @@ namespace ts { else if (isImportSpecifier(importDeclaration)) { return setTextRange( factory.createPropertyAccessExpression( - factory.getGeneratedNameForNode(importDeclaration.parent.parent.parent), + factory.getGeneratedNameForNode(importDeclaration.parent?.parent?.parent || importDeclaration), factory.cloneNode(importDeclaration.propertyName || importDeclaration.name) ), /*location*/ node diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 3e2e789259915..9cc9c1c3fc92e 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1900,7 +1900,7 @@ namespace ts { } function visitPropertyDeclaration(node: PropertyDeclaration) { - if (node.flags & NodeFlags.Ambient) { + if (node.flags & NodeFlags.Ambient || hasSyntacticModifier(node, ModifierFlags.Abstract)) { return undefined; } const updated = factory.updatePropertyDeclaration( @@ -2973,6 +2973,11 @@ namespace ts { * @param node The import equals declaration node. */ function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult { + // Always elide type-only imports + if (node.isTypeOnly) { + return undefined; + } + if (isExternalModuleImportEqualsDeclaration(node)) { const isReferenced = resolver.isReferencedAliasDeclaration(node); // If the alias is unreferenced but we want to keep the import, replace with 'import "mod"'. diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 8d1c59fb3fd80..0f019b3f9c6d3 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -14,6 +14,7 @@ namespace ts { /*@internal*/ preserveWatchOutput?: boolean; /*@internal*/ listEmittedFiles?: boolean; /*@internal*/ listFiles?: boolean; + /*@internal*/ explainFiles?: boolean; /*@internal*/ pretty?: boolean; incremental?: boolean; assumeChangesOnlyAffectDirectDependencies?: boolean; @@ -209,13 +210,13 @@ namespace ts { originalGetSourceFile: CompilerHost["getSourceFile"]; } - interface SolutionBuilderState { + interface SolutionBuilderState extends WatchFactory { readonly host: SolutionBuilderHost; readonly hostWithWatch: SolutionBuilderWithWatchHost; readonly currentDirectory: string; readonly getCanonicalFileName: GetCanonicalFileName; readonly parseConfigFileHost: ParseConfigFileHost; - readonly writeFileName: ((s: string) => void) | undefined; + readonly write: ((s: string) => void) | undefined; // State of solution readonly options: BuildOptions; @@ -253,12 +254,10 @@ namespace ts { readonly allWatchedWildcardDirectories: ESMap>; readonly allWatchedInputFiles: ESMap>; readonly allWatchedConfigFiles: ESMap; + readonly allWatchedExtendedConfigFiles: ESMap>; timerToBuildInvalidatedProject: any; reportFileChangeDetected: boolean; - watchFile: WatchFile; - watchFilePath: WatchFilePath; - watchDirectory: WatchDirectory; writeLog: (s: string) => void; } @@ -282,7 +281,7 @@ namespace ts { loadWithLocalCache(Debug.checkEachDefined(moduleNames), containingFile, redirectedReference, loader); } - const { watchFile, watchFilePath, watchDirectory, writeLog } = createWatchFactory(hostWithWatch, options); + const { watchFile, watchDirectory, writeLog } = createWatchFactory(hostWithWatch, options); const state: SolutionBuilderState = { host, @@ -290,7 +289,7 @@ namespace ts { currentDirectory, getCanonicalFileName, parseConfigFileHost: parseConfigHostFromCompilerHostLike(host), - writeFileName: host.trace ? (s: string) => host.trace!(s) : undefined, + write: maybeBind(host, host.trace), // State of solution options, @@ -327,11 +326,11 @@ namespace ts { allWatchedWildcardDirectories: new Map(), allWatchedInputFiles: new Map(), allWatchedConfigFiles: new Map(), + allWatchedExtendedConfigFiles: new Map(), timerToBuildInvalidatedProject: undefined, reportFileChangeDetected: false, watchFile, - watchFilePath, watchDirectory, writeLog, }; @@ -465,6 +464,15 @@ namespace ts { { onDeleteValue: closeFileWatcher } ); + state.allWatchedExtendedConfigFiles.forEach(watcher => { + watcher.projects.forEach(project => { + if (!currentProjects.has(project)) { + watcher.projects.delete(project); + } + }); + watcher.close(); + }); + mutateMapSkippingNewValues( state.allWatchedWildcardDirectories, currentProjects, @@ -896,7 +904,7 @@ namespace ts { const { emitResult } = emitFilesAndReportErrors( program, reportDeclarationDiagnostics, - /*writeFileName*/ undefined, + /*write*/ undefined, /*reportSummary*/ undefined, (name, text, writeByteOrderMark) => outputFiles.push({ name, text, writeByteOrderMark }), cancellationToken, @@ -969,7 +977,7 @@ namespace ts { buildResult = BuildResultFlags.EmitErrors & buildResult!; } - if (emitResult.emittedFiles && state.writeFileName) { + if (emitResult.emittedFiles && state.write) { emitResult.emittedFiles.forEach(name => listEmittedFile(state, config, name)); } afterProgramDone(state, program, config); @@ -999,7 +1007,7 @@ namespace ts { return emitDiagnostics; } - if (state.writeFileName) { + if (state.write) { emittedOutputs.forEach(name => listEmittedFile(state, config, name)); } @@ -1168,14 +1176,14 @@ namespace ts { if (reloadLevel === ConfigFileProgramReloadLevel.Full) { watchConfigFile(state, project, projectPath, config); + watchExtendedConfigFiles(state, projectPath, config); watchWildCardDirectories(state, project, projectPath, config); watchInputFiles(state, project, projectPath, config); } else if (reloadLevel === ConfigFileProgramReloadLevel.Partial) { // Update file names - const result = getFileNamesFromConfigSpecs(config.configFileSpecs!, getDirectoryPath(project), config.options, state.parseConfigFileHost); - updateErrorForNoInputFiles(result, project, config.configFileSpecs!, config.errors, canJsonReportNoInputFiles(config.raw)); - config.fileNames = result.fileNames; + config.fileNames = getFileNamesFromConfigSpecs(config.options.configFile!.configFileSpecs!, getDirectoryPath(project), config.options, state.parseConfigFileHost); + updateErrorForNoInputFiles(config.fileNames, project, config.options.configFile!.configFileSpecs!, config.errors, canJsonReportNoInputFiles(config.raw)); watchInputFiles(state, project, projectPath, config); } @@ -1244,9 +1252,9 @@ namespace ts { return undefined; } - function listEmittedFile({ writeFileName }: SolutionBuilderState, proj: ParsedCommandLine, file: string) { - if (writeFileName && proj.options.listEmittedFiles) { - writeFileName(`TSFILE: ${file}`); + function listEmittedFile({ write }: SolutionBuilderState, proj: ParsedCommandLine, file: string) { + if (write && proj.options.listEmittedFiles) { + write(`TSFILE: ${file}`); } } @@ -1263,7 +1271,7 @@ namespace ts { config: ParsedCommandLine ) { if (program) { - if (program && state.writeFileName) listFiles(program, state.writeFileName); + if (program && state.write) listFiles(program, state.write); if (state.host.afterProgramEmitAndDiagnostics) { state.host.afterProgramEmitAndDiagnostics(program); } @@ -1287,7 +1295,6 @@ namespace ts { const canEmitBuildInfo = !(buildResult & BuildResultFlags.SyntaxErrors) && program && !outFile(program.getCompilerOptions()); reportAndStoreErrors(state, resolvedPath, diagnostics); - // List files if any other build error using program (emit errors already report files) state.projectStatus.set(resolvedPath, { type: UpToDateStatusType.Unbuildable, reason: `${errorType} errors` }); if (canEmitBuildInfo) return { buildResult, step: BuildStep.EmitBuildInfo }; afterProgramDone(state, program, config); @@ -1783,7 +1790,6 @@ namespace ts { function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) { if (!state.watch || state.allWatchedConfigFiles.has(resolvedPath)) return; state.allWatchedConfigFiles.set(resolvedPath, state.watchFile( - state.hostWithWatch, resolved, () => { invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.Full); @@ -1795,13 +1801,30 @@ namespace ts { )); } + function watchExtendedConfigFiles(state: SolutionBuilderState, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) { + updateSharedExtendedConfigFileWatcher( + resolvedPath, + parsed, + state.allWatchedExtendedConfigFiles, + (extendedConfigFileName, extendedConfigFilePath) => state.watchFile( + extendedConfigFileName, + () => state.allWatchedExtendedConfigFiles.get(extendedConfigFilePath)?.projects.forEach(projectConfigFilePath => + invalidateProjectAndScheduleBuilds(state, projectConfigFilePath, ConfigFileProgramReloadLevel.Full) + ), + PollingInterval.High, + parsed?.watchOptions, + WatchType.ExtendedConfigFile, + ), + fileName => toPath(state, fileName), + ); + } + function watchWildCardDirectories(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine) { if (!state.watch) return; updateWatchingWildcardDirectories( getOrCreateValueMapFromConfigFileMap(state.allWatchedWildcardDirectories, resolvedPath), - new Map(getEntries(parsed.configFileSpecs!.wildcardDirectories)), + new Map(getEntries(parsed.wildcardDirectories!)), (dir, flags) => state.watchDirectory( - state.hostWithWatch, dir, fileOrDirectory => { if (isIgnoredFileFromWildCardWatching({ @@ -1809,7 +1832,6 @@ namespace ts { fileOrDirectory, fileOrDirectoryPath: toPath(state, fileOrDirectory), configFileName: resolved, - configFileSpecs: parsed.configFileSpecs!, currentDirectory: state.currentDirectory, options: parsed.options, program: state.builderPrograms.get(resolvedPath), @@ -1833,13 +1855,11 @@ namespace ts { getOrCreateValueMapFromConfigFileMap(state.allWatchedInputFiles, resolvedPath), arrayToMap(parsed.fileNames, fileName => toPath(state, fileName)), { - createNewValue: (path, input) => state.watchFilePath( - state.hostWithWatch, + createNewValue: (_path, input) => state.watchFile( input, () => invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.None), PollingInterval.Low, parsed?.watchOptions, - path as Path, WatchType.SourceFile, resolved ), @@ -1856,6 +1876,7 @@ namespace ts { const cfg = parseConfigFile(state, resolved, resolvedPath); // Watch this file watchConfigFile(state, resolved, resolvedPath, cfg); + watchExtendedConfigFiles(state, resolvedPath, cfg); if (cfg) { // Update watchers for wildcard directories watchWildCardDirectories(state, resolved, resolvedPath, cfg); @@ -1868,6 +1889,10 @@ namespace ts { function stopWatching(state: SolutionBuilderState) { clearMap(state.allWatchedConfigFiles, closeFileWatcher); + clearMap(state.allWatchedExtendedConfigFiles, watcher => { + watcher.projects.clear(); + watcher.close(); + }); clearMap(state.allWatchedWildcardDirectories, watchedWildcardDirectories => clearMap(watchedWildcardDirectories, closeFileWatcherOf)); clearMap(state.allWatchedInputFiles, watchedWildcardDirectories => clearMap(watchedWildcardDirectories, closeFileWatcher)); } @@ -1914,9 +1939,7 @@ namespace ts { } function reportWatchStatus(state: SolutionBuilderState, message: DiagnosticMessage, ...args: (string | number | undefined)[]) { - if (state.hostWithWatch.onWatchStatusChange) { - state.hostWithWatch.onWatchStatusChange(createCompilerDiagnostic(message, ...args), state.host.getNewLine(), state.baseCompilerOptions); - } + state.hostWithWatch.onWatchStatusChange?.(createCompilerDiagnostic(message, ...args), state.host.getNewLine(), state.baseCompilerOptions); } function reportErrors({ host }: SolutionBuilderState, errors: readonly Diagnostic[]) { diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index 1edb2bbd63da3..ed64c2c26d593 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -13,10 +13,10 @@ "corePublic.ts", "core.ts", "debug.ts", - "performanceTimestamp.ts", + "semver.ts", + "performanceCore.ts", "performance.ts", "perfLogger.ts", - "semver.ts", "tracing.ts", "types.ts", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 198e81b6722a2..5f7a2c0394478 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -167,6 +167,7 @@ namespace ts { DeclareKeyword, GetKeyword, InferKeyword, + IntrinsicKeyword, IsKeyword, KeyOfKeyword, ModuleKeyword, @@ -186,10 +187,6 @@ namespace ts { FromKeyword, GlobalKeyword, BigIntKeyword, - UppercaseKeyword, - LowercaseKeyword, - CapitalizeKeyword, - UncapitalizeKeyword, OfKeyword, // LastKeyword and LastToken and LastContextualKeyword // Parse tree nodes @@ -544,7 +541,6 @@ namespace ts { | SyntaxKind.BigIntKeyword | SyntaxKind.BooleanKeyword | SyntaxKind.BreakKeyword - | SyntaxKind.CapitalizeKeyword | SyntaxKind.CaseKeyword | SyntaxKind.CatchKeyword | SyntaxKind.ClassKeyword @@ -574,10 +570,10 @@ namespace ts { | SyntaxKind.InKeyword | SyntaxKind.InstanceOfKeyword | SyntaxKind.InterfaceKeyword + | SyntaxKind.IntrinsicKeyword | SyntaxKind.IsKeyword | SyntaxKind.KeyOfKeyword | SyntaxKind.LetKeyword - | SyntaxKind.LowercaseKeyword | SyntaxKind.ModuleKeyword | SyntaxKind.NamespaceKeyword | SyntaxKind.NeverKeyword @@ -605,11 +601,9 @@ namespace ts { | SyntaxKind.TryKeyword | SyntaxKind.TypeKeyword | SyntaxKind.TypeOfKeyword - | SyntaxKind.UncapitalizeKeyword | SyntaxKind.UndefinedKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.UnknownKeyword - | SyntaxKind.UppercaseKeyword | SyntaxKind.VarKeyword | SyntaxKind.VoidKeyword | SyntaxKind.WhileKeyword @@ -635,6 +629,7 @@ namespace ts { | SyntaxKind.AnyKeyword | SyntaxKind.BigIntKeyword | SyntaxKind.BooleanKeyword + | SyntaxKind.IntrinsicKeyword | SyntaxKind.NeverKeyword | SyntaxKind.NumberKeyword | SyntaxKind.ObjectKeyword @@ -1665,19 +1660,10 @@ namespace ts { export interface TemplateLiteralTypeSpan extends TypeNode { readonly kind: SyntaxKind.TemplateLiteralTypeSpan, readonly parent: TemplateLiteralTypeNode; - readonly casing: TemplateCasing; readonly type: TypeNode; readonly literal: TemplateMiddle | TemplateTail; } - export const enum TemplateCasing { - None, - Uppercase, - Lowercase, - Capitalize, - Uncapitalize, - } - // Note: 'brands' in our syntax nodes serve to give us a small amount of nominal typing. // Consider 'Expression'. Without the brand, 'Expression' is actually no different // (structurally) than 'Node'. Because of this you can pass any Node to a function that @@ -2939,6 +2925,7 @@ namespace ts { readonly kind: SyntaxKind.ImportEqualsDeclaration; readonly parent: SourceFile | ModuleBlock; readonly name: Identifier; + readonly isTypeOnly: boolean; // 'EntityName' for an internal module reference, 'ExternalModuleReference' for an external // module reference. @@ -3051,6 +3038,7 @@ namespace ts { export type TypeOnlyCompatibleAliasDeclaration = | ImportClause + | ImportEqualsDeclaration | NamespaceImport | ImportOrExportSpecifier ; @@ -3632,6 +3620,7 @@ namespace ts { export interface TsConfigSourceFile extends JsonSourceFile { extendedSourceFiles?: string[]; + /*@internal*/ configFileSpecs?: ConfigFileSpecs; } export interface JsonMinusNumericLiteral extends PrefixUnaryExpression { @@ -3701,20 +3690,94 @@ namespace ts { } /*@internal*/ - export enum RefFileKind { + export enum FileIncludeKind { + RootFile, + SourceFromProjectReference, + OutputFromProjectReference, Import, ReferenceFile, - TypeReferenceDirective + TypeReferenceDirective, + LibFile, + LibReferenceDirective, + AutomaticTypeDirectiveFile } /*@internal*/ - export interface RefFile { - referencedFileName: string; - kind: RefFileKind; + export interface RootFile { + kind: FileIncludeKind.RootFile, index: number; + } + + /*@internal*/ + export interface LibFile { + kind: FileIncludeKind.LibFile; + index?: number; + } + + /*@internal*/ + export type ProjectReferenceFileKind = FileIncludeKind.SourceFromProjectReference | + FileIncludeKind.OutputFromProjectReference; + + /*@internal*/ + export interface ProjectReferenceFile { + kind: ProjectReferenceFileKind; + index: number; + } + + /*@internal*/ + export type ReferencedFileKind = FileIncludeKind.Import | + FileIncludeKind.ReferenceFile | + FileIncludeKind.TypeReferenceDirective | + FileIncludeKind.LibReferenceDirective; + + /*@internal*/ + export interface ReferencedFile { + kind: ReferencedFileKind; file: Path; + index: number; + } + + /*@internal*/ + export interface AutomaticTypeDirectiveFile { + kind: FileIncludeKind.AutomaticTypeDirectiveFile; + typeReference: string; + packageId: PackageId | undefined; + } + + /*@internal*/ + export type FileIncludeReason = + RootFile | + LibFile | + ProjectReferenceFile | + ReferencedFile | + AutomaticTypeDirectiveFile; + + /*@internal*/ + export const enum FilePreprocessingDiagnosticsKind { + FilePreprocessingReferencedDiagnostic, + FilePreprocessingFileExplainingDiagnostic + } + + /*@internal*/ + export interface FilePreprocessingReferencedDiagnostic { + kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic; + reason: ReferencedFile; + diagnostic: DiagnosticMessage; + args?: (string | number | undefined)[]; + } + + /*@internal*/ + export interface FilePreprocessingFileExplainingDiagnostic { + kind: FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic; + file?: Path; + fileProcessingReason: FileIncludeReason; + diagnostic: DiagnosticMessage; + args?: (string | number | undefined)[]; } + /*@internal*/ + export type FilePreprocessingDiagnostics = FilePreprocessingReferencedDiagnostic | FilePreprocessingFileExplainingDiagnostic; + export interface Program extends ScriptReferenceHost { getCurrentDirectory(): string; /** @@ -3734,8 +3797,6 @@ namespace ts { /* @internal */ getMissingFilePaths(): readonly Path[]; /* @internal */ - getRefFileMap(): MultiMap | undefined; - /* @internal */ getFilesByNameMap(): ESMap; /** @@ -3776,6 +3837,8 @@ namespace ts { /* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker; /* @internal */ dropDiagnosticsProducingTypeChecker(): void; + /* @internal */ getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined; + /* @internal */ getClassifiableNames(): Set<__String>; getTypeCatalog(): readonly Type[]; @@ -3787,13 +3850,14 @@ namespace ts { getInstantiationCount(): number; getRelationCacheSizes(): { assignable: number, identity: number, subtype: number, strictSubtype: number }; - /* @internal */ getFileProcessingDiagnostics(): DiagnosticCollection; + /* @internal */ getFileProcessingDiagnostics(): FilePreprocessingDiagnostics[] | undefined; /* @internal */ getResolvedTypeReferenceDirectives(): ESMap; isSourceFileFromExternalLibrary(file: SourceFile): boolean; isSourceFileDefaultLibrary(file: SourceFile): boolean; // For testing purposes only. - /* @internal */ structureIsReused?: StructureIsReused; + // This is set on created program to let us know how the program was created using old program + /* @internal */ readonly structureIsReused: StructureIsReused; /* @internal */ getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined; /* @internal */ getLibFileFromReference(ref: FileReference): SourceFile | undefined; @@ -3804,6 +3868,8 @@ namespace ts { /* @internal */ redirectTargetsMap: MultiMap; /** Is the file emitted file */ /* @internal */ isEmittedFile(file: string): boolean; + /* @internal */ getFileIncludeReasons(): MultiMap; + /* @internal */ useCaseSensitiveFileNames(): boolean; /* @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined; @@ -3811,7 +3877,7 @@ namespace ts { getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined; /*@internal*/ getProjectReferenceRedirect(fileName: string): string | undefined; /*@internal*/ getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined; - /*@internal*/ forEachResolvedProjectReference(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined; + /*@internal*/ forEachResolvedProjectReference(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined; /*@internal*/ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined; /*@internal*/ isSourceOfProjectReferenceRedirect(fileName: string): boolean; /*@internal*/ getProgramBuildInfo?(): ProgramBuildInfo | undefined; @@ -4018,6 +4084,7 @@ namespace ts { getAugmentedPropertiesOfType(type: Type): Symbol[]; getRootSymbols(symbol: Symbol): readonly Symbol[]; + getSymbolOfExpando(node: Node, allowDeclaration: boolean): Symbol | undefined; getContextualType(node: Expression): Type | undefined; /* @internal */ getContextualType(node: Expression, contextFlags?: ContextFlags): Type | undefined; // eslint-disable-line @typescript-eslint/unified-signatures /* @internal */ getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike): Type | undefined; @@ -4141,6 +4208,7 @@ namespace ts { /* @internal */ getAllPossiblePropertiesOfTypes(type: readonly Type[]): Symbol[]; /* @internal */ resolveName(name: string, location: Node | undefined, meaning: SymbolFlags, excludeGlobals: boolean): Symbol | undefined; /* @internal */ getJsxNamespace(location?: Node): string; + /* @internal */ getJsxFragmentFactory(location: Node): string | undefined; /** * Note that this will return undefined in the following case: @@ -4184,7 +4252,7 @@ namespace ts { export const enum UnionReduction { None = 0, Literal, - Subtype + Subtype, } /* @internal */ @@ -4521,7 +4589,7 @@ namespace ts { isDeclarationVisible(node: Declaration | AnyImportSyntax): boolean; isLateBound(node: Declaration): node is LateBoundDeclaration; collectLinkedAliases(node: Identifier, setVisibility?: boolean): Node[] | undefined; - isImplementationOfOverload(node: FunctionLike): boolean | undefined; + isImplementationOfOverload(node: SignatureDeclaration): boolean | undefined; isRequiredInitializedParameter(node: ParameterDeclaration): boolean; isOptionalUninitializedParameterProperty(node: ParameterDeclaration): boolean; isExpandoFunctionDeclaration(node: FunctionDeclaration): boolean; @@ -4539,7 +4607,7 @@ namespace ts { isOptionalParameter(node: ParameterDeclaration): boolean; moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean; isArgumentsLocalBinding(node: Identifier): boolean; - getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): SourceFile | undefined; + getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode | ImportCall): SourceFile | undefined; getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): string[] | undefined; getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[] | undefined; isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean; @@ -4853,11 +4921,11 @@ namespace ts { resolvedJSDocType?: Type; // Resolved type of a JSDoc type reference switchTypes?: Type[]; // Cached array of switch case expression types jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node + jsxImplicitImportContainer?: Symbol | false; // Resolved module symbol the implicit jsx import of this file should refer to contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive deferredNodes?: ESMap; // Set of nodes whose checking has been deferred capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type - instantiations?: ESMap; // Instantiations of generic type alias (undefined if non-generic) isExhaustive?: boolean; // Is node an exhaustive switch statement skipDirectInference?: true; // Flag set by the API `getContextualType` call on a node when `Completions` is passed to force the checker to skip making inferences to a node's type declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter. @@ -4892,6 +4960,7 @@ namespace ts { Substitution = 1 << 25, // Type parameter substitution NonPrimitive = 1 << 26, // intrinsic object type TemplateLiteral = 1 << 27, // Template literal type + StringMapping = 1 << 28, // Uppercase/Lowercase type /* @internal */ AnyOrUnknown = Any | Unknown, @@ -4901,6 +4970,10 @@ namespace ts { Unit = Literal | UniqueESSymbol | Nullable, StringOrNumberLiteral = StringLiteral | NumberLiteral, /* @internal */ + StringLikeLiteral = StringLiteral | TemplateLiteral, + /* @internal */ + FreshableLiteral = Literal | TemplateLiteral, + /* @internal */ StringOrNumberLiteralOrUnique = StringLiteral | NumberLiteral | UniqueESSymbol, /* @internal */ DefinitelyFalsy = StringLiteral | NumberLiteral | BigIntLiteral | BooleanLiteral | Void | Undefined | Null, @@ -4909,7 +4982,7 @@ namespace ts { Intrinsic = Any | Unknown | String | Number | BigInt | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive, /* @internal */ Primitive = String | Number | BigInt | Boolean | Enum | EnumLiteral | ESSymbol | Void | Undefined | Null | Literal | UniqueESSymbol, - StringLike = String | StringLiteral | TemplateLiteral, + StringLike = String | StringLiteral | TemplateLiteral | StringMapping, NumberLike = Number | NumberLiteral | Enum, BigIntLike = BigInt | BigIntLiteral, BooleanLike = Boolean | BooleanLiteral, @@ -4922,7 +4995,7 @@ namespace ts { StructuredType = Object | Union | Intersection, TypeVariable = TypeParameter | IndexedAccess, InstantiableNonPrimitive = TypeVariable | Conditional | Substitution, - InstantiablePrimitive = Index | TemplateLiteral, + InstantiablePrimitive = Index | TemplateLiteral | StringMapping, Instantiable = InstantiableNonPrimitive | InstantiablePrimitive, StructuredOrInstantiable = StructuredType | Instantiable, /* @internal */ @@ -4930,7 +5003,7 @@ namespace ts { /* @internal */ Simplifiable = IndexedAccess | Conditional, /* @internal */ - Substructure = Object | Union | Intersection | Index | IndexedAccess | Conditional | Substitution | TemplateLiteral, + Substructure = Object | Union | Intersection | Index | IndexedAccess | Conditional | Substitution | TemplateLiteral | StringMapping, // 'Narrowable' types are types where narrowing actually narrows. // This *should* be every type other than null, undefined, void, and never Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive, @@ -4938,7 +5011,7 @@ namespace ts { NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | StructuredOrInstantiable, // The following flags are aggregated during union and intersection type construction /* @internal */ - IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive, + IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive | TemplateLiteral, // The following flags are used for different purposes during union and intersection type construction /* @internal */ IncludesStructuredOrInstantiable = TypeParameter, @@ -4994,7 +5067,9 @@ namespace ts { } /* @internal */ - export type FreshableType = LiteralType | FreshableIntrinsicType; + export type FreshableLiteralType = LiteralType | TemplateLiteralType; + /* @internal */ + export type FreshableType = FreshableLiteralType | FreshableIntrinsicType; // String literal types (TypeFlags.StringLiteral) // Numeric literal types (TypeFlags.NumberLiteral) @@ -5150,6 +5225,8 @@ namespace ts { node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode; /* @internal */ mapper?: TypeMapper; + /* @internal */ + instantiations?: ESMap; // Instantiations of generic type alias (undefined if non-generic) } /* @internal */ @@ -5200,7 +5277,9 @@ namespace ts { /* @internal */ objectFlags: ObjectFlags; /* @internal */ - propertyCache: SymbolTable; // Cache of resolved properties + propertyCache?: SymbolTable; // Cache of resolved properties + /* @internal */ + propertyCacheWithoutObjectFunctionPropertyAugment?: SymbolTable; // Cache of resolved properties that does not augment function or object type properties /* @internal */ resolvedProperties: Symbol[]; /* @internal */ @@ -5230,6 +5309,7 @@ namespace ts { export interface AnonymousType extends ObjectType { target?: AnonymousType; // Instantiation target mapper?: TypeMapper; // Instantiation mapper + instantiations?: ESMap; // Instantiations of generic type alias (undefined if non-generic) } /* @internal */ @@ -5335,6 +5415,12 @@ namespace ts { export interface IndexedAccessType extends InstantiableType { objectType: Type; indexType: Type; + /** + * @internal + * Indicates that --noUncheckedIndexedAccess may introduce 'undefined' into + * the resulting type, depending on how type variable constraints are resolved. + */ + noUncheckedIndexedAccessCandidate: boolean; constraint?: Type; simplifiedForReading?: Type; simplifiedForWriting?: Type; @@ -5379,9 +5465,15 @@ namespace ts { } export interface TemplateLiteralType extends InstantiableType { - texts: readonly string[]; // Always one element longer than casings/types - casings: readonly TemplateCasing[]; // Always at least one element + texts: readonly string[]; // Always one element longer than types types: readonly Type[]; // Always at least one element + freshType: TemplateLiteralType; // Fresh version of type + regularType: TemplateLiteralType; // Regular version of type + } + + export interface StringMappingType extends InstantiableType { + symbol: Symbol; + type: Type; } // Type parameter substitution (TypeFlags.Substitution) @@ -5533,17 +5625,17 @@ namespace ts { /** * Ternary values are defined such that - * x & y is False if either x or y is False. - * x & y is Maybe if either x or y is Maybe, but neither x or y is False. - * x & y is True if both x and y are True. - * x | y is False if both x and y are False. - * x | y is Maybe if either x or y is Maybe, but neither x or y is True. - * x | y is True if either x or y is True. + * x & y picks the lesser in the order False < Unknown < Maybe < True, and + * x | y picks the greater in the order False < Unknown < Maybe < True. + * Generally, Ternary.Maybe is used as the result of a relation that depends on itself, and + * Ternary.Unknown is used as the result of a variance check that depends on itself. We make + * a distinction because we don't want to cache circular variance check results. */ /* @internal */ export const enum Ternary { False = 0, - Maybe = 1, + Unknown = 1, + Maybe = 3, True = -1 } @@ -5765,6 +5857,7 @@ namespace ts { lib?: string[]; /*@internal*/listEmittedFiles?: boolean; /*@internal*/listFiles?: boolean; + /*@internal*/explainFiles?: boolean; /*@internal*/listFilesOnly?: boolean; locale?: string; mapRoot?: string; @@ -5785,6 +5878,7 @@ namespace ts { noUnusedLocals?: boolean; noUnusedParameters?: boolean; noImplicitUseStrict?: boolean; + noPropertyAccessFromIndexSignature?: boolean; assumeChangesOnlyAffectDirectDependencies?: boolean; noLib?: boolean; noResolve?: boolean; @@ -5844,6 +5938,8 @@ namespace ts { watchDirectory?: WatchDirectoryKind; fallbackPolling?: PollingWatchKind; synchronousWatchDirectory?: boolean; + excludeDirectories?: string[]; + excludeFiles?: string[]; [option: string]: CompilerOptionsValue | undefined; } @@ -5857,7 +5953,8 @@ namespace ts { enable?: boolean; include?: string[]; exclude?: string[]; - [option: string]: string[] | boolean | undefined; + disableFilenameBasedTypeAcquisition?: boolean; + [option: string]: CompilerOptionsValue | undefined; } export enum ModuleKind { @@ -5949,7 +6046,6 @@ namespace ts { errors: Diagnostic[]; wildcardDirectories?: MapLike; compileOnSave?: boolean; - /* @internal */ configFileSpecs?: ConfigFileSpecs; } export const enum WatchDirectoryFlags { @@ -5971,13 +6067,6 @@ namespace ts { validatedFilesSpec: readonly string[] | undefined; validatedIncludeSpecs: readonly string[] | undefined; validatedExcludeSpecs: readonly string[] | undefined; - wildcardDirectories: MapLike; - } - - export interface ExpandResult { - fileNames: string[]; - wildcardDirectories: MapLike; - /* @internal */ spec: ConfigFileSpecs; } /* @internal */ @@ -6013,6 +6102,7 @@ namespace ts { affectsSemanticDiagnostics?: true; // true if option affects semantic diagnostics affectsEmit?: true; // true if the options affects emit transpileOptionValue?: boolean | undefined; // If set this means that the option should be set to this value when transpiling + extraValidation?: (value: CompilerOptionsValue) => [DiagnosticMessage, ...string[]] | undefined; // Additional validation to be performed for the value to be valid } /* @internal */ @@ -6334,7 +6424,7 @@ namespace ts { /*@internal*/ export interface ResolvedProjectReferenceCallbacks { getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined; - forEachResolvedProjectReference(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined; + forEachResolvedProjectReference(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined; } /* @internal */ @@ -6779,8 +6869,8 @@ namespace ts { createIndexSignature(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration; /* @internal */ createIndexSignature(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined): IndexSignatureDeclaration; // eslint-disable-line @typescript-eslint/unified-signatures updateIndexSignature(node: IndexSignatureDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration; - createTemplateLiteralTypeSpan(casing: TemplateCasing, type: TypeNode, literal: TemplateMiddle | TemplateTail): TemplateLiteralTypeSpan; - updateTemplateLiteralTypeSpan(casing: TemplateCasing, node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddle | TemplateTail): TemplateLiteralTypeSpan; + createTemplateLiteralTypeSpan(type: TypeNode, literal: TemplateMiddle | TemplateTail): TemplateLiteralTypeSpan; + updateTemplateLiteralTypeSpan(node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddle | TemplateTail): TemplateLiteralTypeSpan; // // Types @@ -6998,8 +7088,8 @@ namespace ts { updateCaseBlock(node: CaseBlock, clauses: readonly CaseOrDefaultClause[]): CaseBlock; createNamespaceExportDeclaration(name: string | Identifier): NamespaceExportDeclaration; updateNamespaceExportDeclaration(node: NamespaceExportDeclaration, name: Identifier): NamespaceExportDeclaration; - createImportEqualsDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: string | Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration; - updateImportEqualsDeclaration(node: ImportEqualsDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration; + createImportEqualsDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, isTypeOnly: boolean, name: string | Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration; + updateImportEqualsDeclaration(node: ImportEqualsDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, isTypeOnly: boolean, name: Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration; createImportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression): ImportDeclaration; updateImportDeclaration(node: ImportDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression): ImportDeclaration; createImportClause(isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause; @@ -7514,8 +7604,8 @@ namespace ts { export type Visitor = (node: Node) => VisitResult; export interface NodeVisitor { - (nodes: T, visitor: Visitor | undefined, test?: (node: Node) => boolean, lift?: (node: NodeArray) => T): T; - (nodes: T | undefined, visitor: Visitor | undefined, test?: (node: Node) => boolean, lift?: (node: NodeArray) => T): T | undefined; + (nodes: T, visitor: Visitor | undefined, test?: (node: Node) => boolean, lift?: (node: readonly Node[]) => T): T; + (nodes: T | undefined, visitor: Visitor | undefined, test?: (node: Node) => boolean, lift?: (node: readonly Node[]) => T): T | undefined; } export interface NodesVisitor { @@ -7856,6 +7946,7 @@ namespace ts { realpath?(path: string): string; getSymlinkCache?(): SymlinkCache; getGlobalTypingsCacheLocation?(): string | undefined; + getNearestAncestorDirectoryWithPackageJson?(fileName: string, rootDir?: string): string | undefined; getSourceFiles(): readonly SourceFile[]; readonly redirectTargetsMap: RedirectTargetsMap; @@ -7908,8 +7999,6 @@ namespace ts { // Otherwise, returns all the diagnostics (global and file associated) in this collection. getDiagnostics(): Diagnostic[]; getDiagnostics(fileName: string): DiagnosticWithLocation[]; - - reattachFileDiagnostics(newFile: SourceFile): void; } // SyntaxKind.SyntaxList @@ -8157,7 +8246,7 @@ namespace ts { readonly includeCompletionsForModuleExports?: boolean; readonly includeAutomaticOptionalChainCompletions?: boolean; readonly includeCompletionsWithInsertText?: boolean; - readonly importModuleSpecifierPreference?: "auto" | "relative" | "non-relative"; + readonly importModuleSpecifierPreference?: "shortest" | "project-relative" | "relative" | "non-relative"; /** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */ readonly importModuleSpecifierEnding?: "auto" | "minimal" | "index" | "js"; readonly allowTextChangesInNewFiles?: boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index c7e403785e0e6..bd3126f05d173 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -100,28 +100,6 @@ namespace ts { !isJsonEqual(getCompilerOptionValue(oldOptions, o), getCompilerOptionValue(newOptions, o))); } - /** - * Iterates through the parent chain of a node and performs the callback on each parent until the callback - * returns a truthy value, then returns that value. - * If no such value is found, it applies the callback until the parent pointer is undefined or the callback returns "quit" - * At that point findAncestor returns undefined. - */ - export function findAncestor(node: Node | undefined, callback: (element: Node) => element is T): T | undefined; - export function findAncestor(node: Node | undefined, callback: (element: Node) => boolean | "quit"): Node | undefined; - export function findAncestor(node: Node, callback: (element: Node) => boolean | "quit"): Node | undefined { - while (node) { - const result = callback(node); - if (result === "quit") { - return undefined; - } - else if (result) { - return node; - } - node = node.parent; - } - return undefined; - } - export function forEachAncestor(node: Node, callback: (n: Node) => T | undefined | "quit"): T | undefined { while (true) { const res = callback(node); @@ -562,20 +540,89 @@ namespace ts { return emitNode && emitNode.flags || 0; } + interface ScriptTargetFeatures { + [key: string]: { [key: string]: string[] | undefined }; + }; + + export function getScriptTargetFeatures(): ScriptTargetFeatures { + return { + es2015: { + Array: ["find", "findIndex", "fill", "copyWithin", "entries", "keys", "values"], + RegExp: ["flags", "sticky", "unicode"], + Reflect: ["apply", "construct", "defineProperty", "deleteProperty", "get"," getOwnPropertyDescriptor", "getPrototypeOf", "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf"], + ArrayConstructor: ["from", "of"], + ObjectConstructor: ["assign", "getOwnPropertySymbols", "keys", "is", "setPrototypeOf"], + NumberConstructor: ["isFinite", "isInteger", "isNaN", "isSafeInteger", "parseFloat", "parseInt"], + Math: ["clz32", "imul", "sign", "log10", "log2", "log1p", "expm1", "cosh", "sinh", "tanh", "acosh", "asinh", "atanh", "hypot", "trunc", "fround", "cbrt"], + Map: ["entries", "keys", "values"], + Set: ["entries", "keys", "values"], + Promise: emptyArray, + PromiseConstructor: ["all", "race", "reject", "resolve"], + Symbol: ["for", "keyFor"], + WeakMap: ["entries", "keys", "values"], + WeakSet: ["entries", "keys", "values"], + Iterator: emptyArray, + AsyncIterator: emptyArray, + String: ["codePointAt", "includes", "endsWith", "normalize", "repeat", "startsWith", "anchor", "big", "blink", "bold", "fixed", "fontcolor", "fontsize", "italics", "link", "small", "strike", "sub", "sup"], + StringConstructor: ["fromCodePoint", "raw"] + }, + es2016: { + Array: ["includes"] + }, + es2017: { + Atomics: emptyArray, + SharedArrayBuffer: emptyArray, + String: ["padStart", "padEnd"], + ObjectConstructor: ["values", "entries", "getOwnPropertyDescriptors"], + DateTimeFormat: ["formatToParts"] + }, + es2018: { + Promise: ["finally"], + RegExpMatchArray: ["groups"], + RegExpExecArray: ["groups"], + RegExp: ["dotAll"], + Intl: ["PluralRules"], + AsyncIterable: emptyArray, + AsyncIterableIterator: emptyArray, + AsyncGenerator: emptyArray, + AsyncGeneratorFunction: emptyArray, + }, + es2019: { + Array: ["flat", "flatMap"], + ObjectConstructor: ["fromEntries"], + String: ["trimStart", "trimEnd", "trimLeft", "trimRight"], + Symbol: ["description"] + }, + es2020: { + BigInt: emptyArray, + BigInt64Array: emptyArray, + BigUint64Array: emptyArray, + PromiseConstructor: ["allSettled"], + SymbolConstructor: ["matchAll"], + String: ["matchAll"], + DataView: ["setBigInt64", "setBigUint64", "getBigInt64", "getBigUint64"], + RelativeTimeFormat: ["format", "formatToParts", "resolvedOptions"] + }, + esnext: { + PromiseConstructor: ["any"], + String: ["replaceAll"], + NumberFormat: ["formatToParts"] + } + }; + } + export const enum GetLiteralTextFlags { None = 0, NeverAsciiEscape = 1 << 0, JsxAttributeEscape = 1 << 1, TerminateUnterminatedLiterals = 1 << 2, + AllowNumericSeparator = 1 << 3 } export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile, flags: GetLiteralTextFlags) { // If we don't need to downlevel and we can reach the original source text using // the node's parent reference, then simply get the text as it was originally written. - if (!nodeIsSynthesized(node) && node.parent && !(flags & GetLiteralTextFlags.TerminateUnterminatedLiterals && node.isUnterminated) && !( - (isNumericLiteral(node) && node.numericLiteralFlags & TokenFlags.ContainsSeparator) || - isBigIntLiteral(node) - )) { + if (canUseOriginalText(node, flags)) { return getSourceTextOfNodeFromSourceFile(sourceFile, node); } @@ -628,6 +675,18 @@ namespace ts { return Debug.fail(`Literal kind '${node.kind}' not accounted for.`); } + function canUseOriginalText(node: LiteralLikeNode, flags: GetLiteralTextFlags): boolean { + if (nodeIsSynthesized(node) || !node.parent || (flags & GetLiteralTextFlags.TerminateUnterminatedLiterals && node.isUnterminated)) { + return false; + } + + if (isNumericLiteral(node) && node.numericLiteralFlags & TokenFlags.ContainsSeparator) { + return !!(flags & GetLiteralTextFlags.AllowNumericSeparator); + } + + return !isBigIntLiteral(node); + } + export function getTextOfConstantValue(value: string | number) { return isString(value) ? '"' + escapeNonAsciiString(value) + '"' : "" + value; } @@ -848,6 +907,10 @@ namespace ts { } } + export function hasPossibleExternalModuleReference(node: Node): node is AnyImportOrReExport | ModuleDeclaration | ImportTypeNode | ImportCall { + return isAnyImportOrReExport(node) || isModuleDeclaration(node) || isImportTypeNode(node) || isImportCall(node); + } + export function isAnyImportOrReExport(node: Node): node is AnyImportOrReExport { return isAnyImportSyntax(node) || isExportDeclaration(node); } @@ -929,10 +992,37 @@ namespace ts { export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation { const sourceFile = getSourceFileOfNode(node); const span = getErrorSpanForNode(sourceFile, node); + return createFileDiagnosticFromMessageChain(sourceFile, span.start, span.length, messageChain, relatedInformation); + } + + function assertDiagnosticLocation(file: SourceFile | undefined, start: number, length: number) { + Debug.assertGreaterThanOrEqual(start, 0); + Debug.assertGreaterThanOrEqual(length, 0); + + if (file) { + Debug.assertLessThanOrEqual(start, file.text.length); + Debug.assertLessThanOrEqual(start + length, file.text.length); + } + } + + export function createFileDiagnosticFromMessageChain(file: SourceFile, start: number, length: number, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation { + assertDiagnosticLocation(file, start, length); + return { + file, + start, + length, + code: messageChain.code, + category: messageChain.category, + messageText: messageChain.next ? messageChain : messageChain.messageText, + relatedInformation + }; + } + + export function createDiagnosticForFileFromMessageChain(sourceFile: SourceFile, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation { return { file: sourceFile, - start: span.start, - length: span.length, + start: 0, + length: 0, code: messageChain.code, category: messageChain.category, messageText: messageChain.next ? messageChain : messageChain.messageText, @@ -1425,6 +1515,13 @@ namespace ts { }); } + export function getPropertyArrayElementValue(objectLiteral: ObjectLiteralExpression, propKey: string, elementValue: string): StringLiteral | undefined { + return firstDefined(getPropertyAssignment(objectLiteral, propKey), property => + isArrayLiteralExpression(property.initializer) ? + find(property.initializer.elements, (element): element is StringLiteral => isStringLiteral(element) && element.text === elementValue) : + undefined); + } + export function getTsConfigObjectLiteralExpression(tsConfigSourceFile: TsConfigSourceFile | undefined): ObjectLiteralExpression | undefined { if (tsConfigSourceFile && tsConfigSourceFile.statements.length) { const expression = tsConfigSourceFile.statements[0].expression; @@ -1959,48 +2056,6 @@ namespace ts { return getSourceTextOfNodeFromSourceFile(sourceFile, str).charCodeAt(0) === CharacterCodes.doubleQuote; } - export function getDeclarationOfExpando(node: Node): Node | undefined { - if (!node.parent) { - return undefined; - } - let name: Expression | BindingName | undefined; - let decl: Node | undefined; - if (isVariableDeclaration(node.parent) && node.parent.initializer === node) { - if (!isInJSFile(node) && !isVarConst(node.parent)) { - return undefined; - } - name = node.parent.name; - decl = node.parent; - } - else if (isBinaryExpression(node.parent)) { - const parentNode = node.parent; - const parentNodeOperator = node.parent.operatorToken.kind; - if (parentNodeOperator === SyntaxKind.EqualsToken && parentNode.right === node) { - name = parentNode.left; - decl = name; - } - else if (parentNodeOperator === SyntaxKind.BarBarToken || parentNodeOperator === SyntaxKind.QuestionQuestionToken) { - if (isVariableDeclaration(parentNode.parent) && parentNode.parent.initializer === parentNode) { - name = parentNode.parent.name; - decl = parentNode.parent; - } - else if (isBinaryExpression(parentNode.parent) && parentNode.parent.operatorToken.kind === SyntaxKind.EqualsToken && parentNode.parent.right === parentNode) { - name = parentNode.parent.left; - decl = name; - } - - if (!name || !isBindableStaticNameExpression(name) || !isSameEntityName(name, parentNode.left)) { - return undefined; - } - } - } - - if (!name || !getExpandoInitializer(node, isPrototypeAccess(name))) { - return undefined; - } - return decl; - } - export function isAssignmentDeclaration(decl: Declaration) { return isBinaryExpression(decl) || isAccessExpression(decl) || isIdentifier(decl) || isCallExpression(decl); } @@ -2120,7 +2175,7 @@ namespace ts { * var min = window.min || {} * my.app = self.my.app || class { } */ - function isSameEntityName(name: Expression, initializer: Expression): boolean { + export function isSameEntityName(name: Expression, initializer: Expression): boolean { if (isPropertyNameLiteral(name) && isPropertyNameLiteral(initializer)) { return getTextOfIdentifierOrLiteral(name) === getTextOfIdentifierOrLiteral(initializer); } @@ -2375,7 +2430,7 @@ namespace ts { } } - export function getExternalModuleName(node: AnyImportOrReExport | ImportTypeNode): Expression | undefined { + export function getExternalModuleName(node: AnyImportOrReExport | ImportTypeNode | ImportCall): Expression | undefined { switch (node.kind) { case SyntaxKind.ImportDeclaration: case SyntaxKind.ExportDeclaration: @@ -2384,6 +2439,8 @@ namespace ts { return node.moduleReference.kind === SyntaxKind.ExternalModuleReference ? node.moduleReference.expression : undefined; case SyntaxKind.ImportType: return isLiteralImportTypeNode(node) ? node.argument.literal : undefined; + case SyntaxKind.CallExpression: + return node.arguments[0]; default: return Debug.assertNever(node); } @@ -2518,7 +2575,7 @@ namespace ts { return result || emptyArray; } - function getNextJSDocCommentLocation(node: Node) { + export function getNextJSDocCommentLocation(node: Node) { const parent = node.parent; if (parent.kind === SyntaxKind.PropertyAssignment || parent.kind === SyntaxKind.ExportAssignment || @@ -2571,18 +2628,31 @@ namespace ts { export function getEffectiveJSDocHost(node: Node): Node | undefined { const host = getJSDocHost(node); - const decl = getSourceOfDefaultedAssignment(host) || - getSourceOfAssignment(host) || - getSingleInitializerOfVariableStatementOrPropertyDeclaration(host) || - getSingleVariableOfVariableStatement(host) || - getNestedModuleDeclaration(host) || - host; - return decl; + if (host) { + return getSourceOfDefaultedAssignment(host) + || getSourceOfAssignment(host) + || getSingleInitializerOfVariableStatementOrPropertyDeclaration(host) + || getSingleVariableOfVariableStatement(host) + || getNestedModuleDeclaration(host) + || host; + } + } + + /** Use getEffectiveJSDocHost if you additionally need to look for jsdoc on parent nodes, like assignments. */ + export function getJSDocHost(node: Node): HasJSDoc | undefined { + const jsDoc = getJSDocRoot(node); + if (!jsDoc) { + return undefined; + } + + const host = jsDoc.parent; + if (host && host.jsDoc && jsDoc === lastOrUndefined(host.jsDoc)) { + return host; + } } - /** Use getEffectiveJSDocHost if you additionally need to look for jsdoc on parent nodes, like assignments. */ - export function getJSDocHost(node: Node): HasJSDoc { - return Debug.checkDefined(findAncestor(node.parent, isJSDoc)).parent; + export function getJSDocRoot(node: Node): JSDoc | undefined { + return findAncestor(node.parent, isJSDoc); } export function getTypeParameterFromJsDoc(node: TypeParameterDeclaration & { parent: JSDocTemplateTag }): TypeParameterDeclaration | undefined { @@ -2730,6 +2800,20 @@ namespace ts { return walkUp(node, SyntaxKind.ParenthesizedExpression); } + /** + * Walks up parenthesized types. + * It returns both the outermost parenthesized type and its parent. + * If given node is not a parenthesiezd type, undefined is return as the former. + */ + export function walkUpParenthesizedTypesAndGetParentAndChild(node: Node): [ParenthesizedTypeNode | undefined, Node] { + let child: ParenthesizedTypeNode | undefined; + while (node && node.kind === SyntaxKind.ParenthesizedType) { + child = node; + node = node.parent; + } + return [child, node]; + } + export function skipParentheses(node: Expression): Expression; export function skipParentheses(node: Node): Node; export function skipParentheses(node: Node): Node { @@ -2914,7 +2998,7 @@ namespace ts { } export function getEffectiveImplementsTypeNodes(node: ClassLikeDeclaration): undefined | readonly ExpressionWithTypeArguments[]{ - if(isInJSFile(node)) { + if (isInJSFile(node)) { return getJSDocImplementsTags(node).map(n => n.class); } else { @@ -2987,14 +3071,7 @@ namespace ts { return !!originalKeywordKind && !isContextualKeyword(originalKeywordKind); } - export type TriviaKind = - SyntaxKind.SingleLineCommentTrivia - | SyntaxKind.MultiLineCommentTrivia - | SyntaxKind.NewLineTrivia - | SyntaxKind.WhitespaceTrivia - | SyntaxKind.ShebangTrivia - | SyntaxKind.ConflictMarkerTrivia; - export function isTrivia(token: SyntaxKind): token is TriviaKind { + export function isTrivia(token: SyntaxKind): token is TriviaSyntaxKind { return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken; } @@ -3502,7 +3579,8 @@ namespace ts { } // TODO: Should prefix `++` and `--` be moved to the `Update` precedence? - // TODO: We are missing `TypeAssertionExpression` + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.NonNullExpression: case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.TypeOfExpression: case SyntaxKind.VoidExpression: @@ -3524,6 +3602,9 @@ namespace ts { case SyntaxKind.ElementAccessExpression: return OperatorPrecedence.Member; + case SyntaxKind.AsExpression: + return OperatorPrecedence.Relational; + case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.Identifier: @@ -3600,6 +3681,19 @@ namespace ts { return -1; } + export function getSemanticJsxChildren(children: readonly JsxChild[]) { + return filter(children, i => { + switch (i.kind) { + case SyntaxKind.JsxExpression: + return !!i.expression; + case SyntaxKind.JsxText: + return !i.containsOnlyTriviaWhiteSpaces; + default: + return true; + } + }); + } + export function createDiagnosticCollection(): DiagnosticCollection { let nonFileDiagnostics = [] as Diagnostic[] as SortedArray; // See GH#19873 const filesWithDiagnostics = [] as string[] as SortedArray; @@ -3611,13 +3705,8 @@ namespace ts { lookup, getGlobalDiagnostics, getDiagnostics, - reattachFileDiagnostics }; - function reattachFileDiagnostics(newFile: SourceFile): void { - forEach(fileDiagnostics.get(newFile.fileName), diagnostic => diagnostic.file = newFile); - } - function lookup(diagnostic: Diagnostic): Diagnostic | undefined { let diagnostics: SortedArray | undefined; if (diagnostic.file) { @@ -3810,13 +3899,15 @@ namespace ts { export function isIntrinsicJsxName(name: __String | string) { const ch = (name as string).charCodeAt(0); - return (ch >= CharacterCodes.a && ch <= CharacterCodes.z) || stringContains((name as string), "-"); + return (ch >= CharacterCodes.a && ch <= CharacterCodes.z) || stringContains((name as string), "-") || stringContains((name as string), ":"); } const indentStrings: string[] = ["", " "]; export function getIndentString(level: number) { - if (indentStrings[level] === undefined) { - indentStrings[level] = getIndentString(level - 1) + indentStrings[1]; + // prepopulate cache + const singleLevel = indentStrings[1]; + for (let current = indentStrings.length; current <= level; current++) { + indentStrings.push(indentStrings[current - 1] + singleLevel); } return indentStrings[level]; } @@ -5630,9 +5721,7 @@ namespace ts { export function createDetachedDiagnostic(fileName: string, start: number, length: number, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticWithDetachedLocation; export function createDetachedDiagnostic(fileName: string, start: number, length: number, message: DiagnosticMessage): DiagnosticWithDetachedLocation { - Debug.assertGreaterThanOrEqual(start, 0); - Debug.assertGreaterThanOrEqual(length, 0); - + assertDiagnosticLocation(/*file*/ undefined, start, length); let text = getLocaleSpecificMessage(message); if (arguments.length > 4) { @@ -5700,13 +5789,7 @@ namespace ts { export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticWithLocation; export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): DiagnosticWithLocation { - Debug.assertGreaterThanOrEqual(start, 0); - Debug.assertGreaterThanOrEqual(length, 0); - - if (file) { - Debug.assertLessThanOrEqual(start, file.text.length); - Debug.assertLessThanOrEqual(start + length, file.text.length); - } + assertDiagnosticLocation(file, start, length); let text = getLocaleSpecificMessage(message); @@ -5759,7 +5842,7 @@ namespace ts { }; } - export function createCompilerDiagnosticFromMessageChain(chain: DiagnosticMessageChain): Diagnostic { + export function createCompilerDiagnosticFromMessageChain(chain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): Diagnostic { return { file: undefined, start: undefined, @@ -5768,6 +5851,7 @@ namespace ts { code: chain.code, category: chain.category, messageText: chain.next ? chain : chain.messageText, + relatedInformation }; } @@ -5778,7 +5862,6 @@ namespace ts { if (arguments.length > 2) { text = formatStringFromArgs(text, arguments, 2); } - return { messageText: text, category: message.category, @@ -5969,8 +6052,8 @@ namespace ts { return jsx === JsxEmit.React || jsx === JsxEmit.ReactJSX || jsx === JsxEmit.ReactJSXDev; } - export function getJSXImplicitImportBase(compilerOptions: CompilerOptions, file: SourceFile): string | undefined { - const jsxImportSourcePragmas = file.pragmas.get("jsximportsource"); + export function getJSXImplicitImportBase(compilerOptions: CompilerOptions, file?: SourceFile): string | undefined { + const jsxImportSourcePragmas = file?.pragmas.get("jsximportsource"); const jsxImportSourcePragma = isArray(jsxImportSourcePragmas) ? jsxImportSourcePragmas[0] : jsxImportSourcePragmas; return compilerOptions.jsx === JsxEmit.ReactJSX || compilerOptions.jsx === JsxEmit.ReactJSXDev || @@ -5980,6 +6063,10 @@ namespace ts { undefined; } + export function getJSXRuntimeImport(base: string | undefined, options: CompilerOptions) { + return base ? `${base}/${options.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime" : "jsx-runtime"}` : undefined; + } + export function hasZeroOrOneAsteriskCharacter(str: string): boolean { let seenAsterisk = false; for (let i = 0; i < str.length; i++) { @@ -6156,6 +6243,11 @@ namespace ts { return !/[.*?]/.test(lastPathComponent); } + export function getPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude") { + const pattern = spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]); + return pattern && `^(${pattern})${usage === "exclude" ? "($|/)" : "$"}`; + } + function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined { let subpattern = ""; let hasWrittenComponent = false; @@ -6628,6 +6720,7 @@ namespace ts { if (!diagnostic.relatedInformation) { diagnostic.relatedInformation = []; } + Debug.assert(diagnostic.relatedInformation !== emptyArray, "Diagnostic had empty array singleton for related info, but is still being constructed!"); diagnostic.relatedInformation.push(...relatedInformation); return diagnostic; } @@ -6664,9 +6757,11 @@ namespace ts { return { pos: getTokenPosOfNode(node), end: node.end }; } - export function rangeOfTypeParameters(typeParameters: NodeArray): TextRange { + export function rangeOfTypeParameters(sourceFile: SourceFile, typeParameters: NodeArray): TextRange { // Include the `<>` - return { pos: typeParameters.pos - 1, end: typeParameters.end + 1 }; + const pos = typeParameters.pos - 1; + const end = skipTrivia(sourceFile.text, typeParameters.end) + 1; + return { pos, end }; } export interface HostWithIsSourceOfProjectReferenceRedirect { @@ -6932,4 +7027,42 @@ namespace ts { return bindParentToChildIgnoringJSDoc(child, parent) || bindJSDoc(child); } } + + /** + * Indicates whether the result of an `Expression` will be unused. + * + * NOTE: This requires a node with a valid `parent` pointer. + */ + export function expressionResultIsUnused(node: Expression): boolean { + Debug.assertIsDefined(node.parent); + while (true) { + const parent: Node = node.parent; + // walk up parenthesized expressions, but keep a pointer to the top-most parenthesized expression + if (isParenthesizedExpression(parent)) { + node = parent; + continue; + } + // result is unused in an expression statement, `void` expression, or the initializer or incrementer of a `for` loop + if (isExpressionStatement(parent) || + isVoidExpression(parent) || + isForStatement(parent) && (parent.initializer === node || parent.incrementor === node)) { + return true; + } + if (isCommaListExpression(parent)) { + // left side of comma is always unused + if (node !== last(parent.elements)) return true; + // right side of comma is unused if parent is unused + node = parent; + continue; + } + if (isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.CommaToken) { + // left side of comma is always unused + if (node === parent.left) return true; + // right side of comma is unused if parent is unused + node = parent; + continue; + } + return false; + } + } } diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index e9d28e8fab5c1..6722e7ad5be1f 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -403,6 +403,28 @@ namespace ts { return !nodeTest || nodeTest(node) ? node : undefined; } + /** + * Iterates through the parent chain of a node and performs the callback on each parent until the callback + * returns a truthy value, then returns that value. + * If no such value is found, it applies the callback until the parent pointer is undefined or the callback returns "quit" + * At that point findAncestor returns undefined. + */ + export function findAncestor(node: Node | undefined, callback: (element: Node) => element is T): T | undefined; + export function findAncestor(node: Node | undefined, callback: (element: Node) => boolean | "quit"): Node | undefined; + export function findAncestor(node: Node, callback: (element: Node) => boolean | "quit"): Node | undefined { + while (node) { + const result = callback(node); + if (result === "quit") { + return undefined; + } + else if (result) { + return node; + } + node = node.parent; + } + return undefined; + } + /** * Gets a value indicating whether a node originated in the parse tree. * @@ -1085,7 +1107,8 @@ namespace ts { case SyntaxKind.NamespaceImport: return (node as NamespaceImport).parent.isTypeOnly; case SyntaxKind.ImportClause: - return (node as ImportClause).isTypeOnly; + case SyntaxKind.ImportEqualsDeclaration: + return (node as ImportClause | ImportEqualsDeclaration).isTypeOnly; default: return false; } diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 5c8434c30b2ac..958b7b1bdd511 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -9,7 +9,7 @@ namespace ts { * @param test A callback to execute to verify the Node is valid. * @param lift An optional callback to execute to lift a NodeArray into a valid Node. */ - export function visitNode(node: T, visitor: Visitor | undefined, test?: (node: Node) => boolean, lift?: (node: NodeArray) => T): T; + export function visitNode(node: T, visitor: Visitor | undefined, test?: (node: Node) => boolean, lift?: (node: readonly Node[]) => T): T; /** * Visits a Node using the supplied visitor, possibly returning a new Node in its place. @@ -19,9 +19,9 @@ namespace ts { * @param test A callback to execute to verify the Node is valid. * @param lift An optional callback to execute to lift a NodeArray into a valid Node. */ - export function visitNode(node: T | undefined, visitor: Visitor | undefined, test?: (node: Node) => boolean, lift?: (node: NodeArray) => T): T | undefined; + export function visitNode(node: T | undefined, visitor: Visitor | undefined, test?: (node: Node) => boolean, lift?: (node: readonly Node[]) => T): T | undefined; - export function visitNode(node: T | undefined, visitor: Visitor | undefined, test?: (node: Node) => boolean, lift?: (node: NodeArray) => T): T | undefined { + export function visitNode(node: T | undefined, visitor: Visitor | undefined, test?: (node: Node) => boolean, lift?: (node: readonly Node[]) => T): T | undefined { if (node === undefined || visitor === undefined) { return node; } @@ -574,6 +574,16 @@ namespace ts { return factory.updateLiteralTypeNode(node, nodeVisitor((node).literal, visitor, isExpression)); + case SyntaxKind.TemplateLiteralType: + return factory.updateTemplateLiteralType(node, + nodeVisitor((node).head, visitor, isTemplateHead), + nodesVisitor((node).templateSpans, visitor, isTemplateLiteralTypeSpan)); + + case SyntaxKind.TemplateLiteralTypeSpan: + return factory.updateTemplateLiteralTypeSpan(node, + nodeVisitor((node).type, visitor, isTypeNode), + nodeVisitor((node).literal, visitor, isTemplateMiddleOrTemplateTail)); + // Binding patterns case SyntaxKind.ObjectBindingPattern: return factory.updateObjectBindingPattern(node, @@ -928,6 +938,7 @@ namespace ts { return factory.updateImportEqualsDeclaration(node, nodesVisitor((node).decorators, visitor, isDecorator), nodesVisitor((node).modifiers, visitor, isModifier), + (node).isTypeOnly, nodeVisitor((node).name, visitor, isIdentifier), nodeVisitor((node).moduleReference, visitor, isModuleReference)); @@ -940,7 +951,7 @@ namespace ts { case SyntaxKind.ImportClause: return factory.updateImportClause(node, - (node as ImportClause).isTypeOnly, + (node).isTypeOnly, nodeVisitor((node).name, visitor, isIdentifier), nodeVisitor((node).namedBindings, visitor, isNamedImportBindings)); @@ -971,7 +982,7 @@ namespace ts { return factory.updateExportDeclaration(node, nodesVisitor((node).decorators, visitor, isDecorator), nodesVisitor((node).modifiers, visitor, isModifier), - (node as ExportDeclaration).isTypeOnly, + (node).isTypeOnly, nodeVisitor((node).exportClause, visitor, isNamedExportBindings), nodeVisitor((node).moduleSpecifier, visitor, isExpression)); diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 724d57f48b29d..85594b103f01c 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -113,38 +113,197 @@ namespace ts { return `${newLine}${flattenDiagnosticMessageText(d.messageText, newLine)}${newLine}${newLine}`; } - /** - * Program structure needed to emit the files and report diagnostics - */ - export interface ProgramToEmitFilesAndReportErrors { - getCurrentDirectory(): string; - getCompilerOptions(): CompilerOptions; - getSourceFiles(): readonly SourceFile[]; - getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[]; - getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[]; - getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[]; - getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[]; - getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; - getConfigFileParsingDiagnostics(): readonly Diagnostic[]; - emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult; - emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult; - } - - export function listFiles(program: ProgramToEmitFilesAndReportErrors, writeFileName: (s: string) => void) { - if (program.getCompilerOptions().listFiles || program.getCompilerOptions().listFilesOnly) { + export function isBuilderProgram(program: Program | T): program is T { + return !!(program as T).getState; + } + + export function listFiles(program: Program | T, write: (s: string) => void) { + const options = program.getCompilerOptions(); + if (options.explainFiles) { + explainFiles(isBuilderProgram(program) ? program.getProgram() : program, write); + } + else if (options.listFiles || options.listFilesOnly) { forEach(program.getSourceFiles(), file => { - writeFileName(file.fileName); + write(file.fileName); }); } } + export function explainFiles(program: Program, write: (s: string) => void) { + const reasons = program.getFileIncludeReasons(); + const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames()); + const relativeFileName = (fileName: string) => convertToRelativePath(fileName, program.getCurrentDirectory(), getCanonicalFileName); + for (const file of program.getSourceFiles()) { + write(`${toFileName(file, relativeFileName)}`); + reasons.get(file.path)?.forEach(reason => write(` ${fileIncludeReasonToDiagnostics(program, reason, relativeFileName).messageText}`)); + explainIfFileIsRedirect(file, relativeFileName)?.forEach(d => write(` ${d.messageText}`)); + } + } + + export function explainIfFileIsRedirect(file: SourceFile, fileNameConvertor?: (fileName: string) => string): DiagnosticMessageChain[] | undefined { + let result: DiagnosticMessageChain[] | undefined; + if (file.path !== file.resolvedPath) { + (result ||= []).push(chainDiagnosticMessages( + /*details*/ undefined, + Diagnostics.File_is_output_of_project_reference_source_0, + toFileName(file.originalFileName, fileNameConvertor) + )); + } + if (file.redirectInfo) { + (result ||= []).push(chainDiagnosticMessages( + /*details*/ undefined, + Diagnostics.File_redirects_to_file_0, + toFileName(file.redirectInfo.redirectTarget, fileNameConvertor) + )); + } + return result; + } + + export function getMatchedFileSpec(program: Program, fileName: string) { + const configFile = program.getCompilerOptions().configFile; + if (!configFile?.configFileSpecs?.validatedFilesSpec) return undefined; + + const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames()); + const filePath = getCanonicalFileName(fileName); + const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory())); + return find(configFile.configFileSpecs.validatedFilesSpec, fileSpec => getCanonicalFileName(getNormalizedAbsolutePath(fileSpec, basePath)) === filePath); + } + + export function getMatchedIncludeSpec(program: Program, fileName: string) { + const configFile = program.getCompilerOptions().configFile; + if (!configFile?.configFileSpecs?.validatedIncludeSpecs) return undefined; + + const isJsonFile = fileExtensionIs(fileName, Extension.Json); + const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory())); + const useCaseSensitiveFileNames = program.useCaseSensitiveFileNames(); + return find(configFile?.configFileSpecs?.validatedIncludeSpecs, includeSpec => { + if (isJsonFile && !endsWith(includeSpec, Extension.Json)) return false; + const pattern = getPatternFromSpec(includeSpec, basePath, "files"); + return !!pattern && getRegexFromPattern(`(${pattern})$`, useCaseSensitiveFileNames).test(fileName); + }); + } + + export function fileIncludeReasonToDiagnostics(program: Program, reason: FileIncludeReason, fileNameConvertor?: (fileName: string) => string,): DiagnosticMessageChain { + const options = program.getCompilerOptions(); + if (isReferencedFile(reason)) { + const referenceLocation = getReferencedFileLocation(path => program.getSourceFileByPath(path), reason); + const referenceText = isReferenceFileLocation(referenceLocation) ? referenceLocation.file.text.substring(referenceLocation.pos, referenceLocation.end) : `"${referenceLocation.text}"`; + let message: DiagnosticMessage; + Debug.assert(isReferenceFileLocation(referenceLocation) || reason.kind === FileIncludeKind.Import, "Only synthetic references are imports"); + switch (reason.kind) { + case FileIncludeKind.Import: + if (isReferenceFileLocation(referenceLocation)) { + message = referenceLocation.packageId ? + Diagnostics.Imported_via_0_from_file_1_with_packageId_2 : + Diagnostics.Imported_via_0_from_file_1; + } + else if (referenceLocation.text === externalHelpersModuleNameText) { + message = referenceLocation.packageId ? + Diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_importHelpers_as_specified_in_compilerOptions : + Diagnostics.Imported_via_0_from_file_1_to_import_importHelpers_as_specified_in_compilerOptions; + } + else { + message = referenceLocation.packageId ? + Diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_jsx_and_jsxs_factory_functions : + Diagnostics.Imported_via_0_from_file_1_to_import_jsx_and_jsxs_factory_functions; + } + break; + case FileIncludeKind.ReferenceFile: + Debug.assert(!referenceLocation.packageId); + message = Diagnostics.Referenced_via_0_from_file_1; + break; + case FileIncludeKind.TypeReferenceDirective: + message = referenceLocation.packageId ? + Diagnostics.Type_library_referenced_via_0_from_file_1_with_packageId_2 : + Diagnostics.Type_library_referenced_via_0_from_file_1; + break; + case FileIncludeKind.LibReferenceDirective: + Debug.assert(!referenceLocation.packageId); + message = Diagnostics.Library_referenced_via_0_from_file_1; + break; + default: + Debug.assertNever(reason); + } + return chainDiagnosticMessages( + /*details*/ undefined, + message, + referenceText, + toFileName(referenceLocation.file, fileNameConvertor), + referenceLocation.packageId && packageIdToString(referenceLocation.packageId) + ); + } + switch (reason.kind) { + case FileIncludeKind.RootFile: + if (!options.configFile?.configFileSpecs) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Root_file_specified_for_compilation); + const fileName = getNormalizedAbsolutePath(program.getRootFileNames()[reason.index], program.getCurrentDirectory()); + const matchedByFiles = getMatchedFileSpec(program, fileName); + if (matchedByFiles) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Part_of_files_list_in_tsconfig_json); + const matchedByInclude = getMatchedIncludeSpec(program, fileName); + return matchedByInclude ? + chainDiagnosticMessages( + /*details*/ undefined, + Diagnostics.Matched_by_include_pattern_0_in_1, + matchedByInclude, + toFileName(options.configFile, fileNameConvertor) + ) : + // Could be additional files specified as roots + chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Root_file_specified_for_compilation); + case FileIncludeKind.SourceFromProjectReference: + case FileIncludeKind.OutputFromProjectReference: + const isOutput = reason.kind === FileIncludeKind.OutputFromProjectReference; + const referencedResolvedRef = Debug.checkDefined(program.getResolvedProjectReferences()?.[reason.index]); + return chainDiagnosticMessages( + /*details*/ undefined, + outFile(options) ? + isOutput ? + Diagnostics.Output_from_referenced_project_0_included_because_1_specified : + Diagnostics.Source_from_referenced_project_0_included_because_1_specified : + isOutput ? + Diagnostics.Output_from_referenced_project_0_included_because_module_is_specified_as_none : + Diagnostics.Source_from_referenced_project_0_included_because_module_is_specified_as_none, + toFileName(referencedResolvedRef.sourceFile.fileName, fileNameConvertor), + options.outFile ? "--outFile" : "--out", + ); + case FileIncludeKind.AutomaticTypeDirectiveFile: + return chainDiagnosticMessages( + /*details*/ undefined, + options.types ? + reason.packageId ? + Diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions_with_packageId_1 : + Diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions : + reason.packageId ? + Diagnostics.Entry_point_for_implicit_type_library_0_with_packageId_1 : + Diagnostics.Entry_point_for_implicit_type_library_0, + reason.typeReference, + reason.packageId && packageIdToString(reason.packageId), + ); + case FileIncludeKind.LibFile: + if (reason.index !== undefined) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Library_0_specified_in_compilerOptions, options.lib![reason.index]); + const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === options.target ? key : undefined); + return chainDiagnosticMessages( + /*details*/ undefined, + target ? + Diagnostics.Default_library_for_target_0 : + Diagnostics.Default_library, + target, + ); + default: + Debug.assertNever(reason); + } + } + + function toFileName(file: SourceFile | string, fileNameConvertor?: (fileName: string) => string) { + const fileName = isString(file) ? file : file.fileName; + return fileNameConvertor ? fileNameConvertor(fileName) : fileName; + } + /** * Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options */ - export function emitFilesAndReportErrors( - program: ProgramToEmitFilesAndReportErrors, + export function emitFilesAndReportErrors( + program: Program | T, reportDiagnostic: DiagnosticReporter, - writeFileName?: (s: string) => void, + write?: (s: string) => void, reportSummary?: ReportEmitErrorSummary, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, @@ -181,13 +340,13 @@ namespace ts { const diagnostics = sortAndDeduplicateDiagnostics(allDiagnostics); diagnostics.forEach(reportDiagnostic); - if (writeFileName) { + if (write) { const currentDir = program.getCurrentDirectory(); forEach(emittedFiles, file => { const filepath = getNormalizedAbsolutePath(file, currentDir); - writeFileName(`TSFILE: ${filepath}`); + write(`TSFILE: ${filepath}`); }); - listFiles(program, writeFileName); + listFiles(program, write); } if (reportSummary) { @@ -200,10 +359,10 @@ namespace ts { }; } - export function emitFilesAndReportErrorsAndGetExitStatus( - program: ProgramToEmitFilesAndReportErrors, + export function emitFilesAndReportErrorsAndGetExitStatus( + program: Program | T, reportDiagnostic: DiagnosticReporter, - writeFileName?: (s: string) => void, + write?: (s: string) => void, reportSummary?: ReportEmitErrorSummary, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, @@ -213,7 +372,7 @@ namespace ts { const { emitResult, diagnostics } = emitFilesAndReportErrors( program, reportDiagnostic, - writeFileName, + write, reportSummary, writeFile, cancellationToken, @@ -250,6 +409,7 @@ namespace ts { export type WatchType = WatchTypeRegistry[keyof WatchTypeRegistry]; export const WatchType: WatchTypeRegistry = { ConfigFile: "Config file", + ExtendedConfigFile: "Extended config file", SourceFile: "Source file", MissingFile: "Missing file", WildcardDirectory: "Wild card directory", @@ -259,6 +419,7 @@ namespace ts { export interface WatchTypeRegistry { ConfigFile: "Config file", + ExtendedConfigFile: "Extended config file", SourceFile: "Source file", MissingFile: "Missing file", WildcardDirectory: "Wild card directory", @@ -270,10 +431,10 @@ namespace ts { writeLog: (s: string) => void; } - export function createWatchFactory(host: { trace?(s: string): void; }, options: { extendedDiagnostics?: boolean; diagnostics?: boolean; }) { + export function createWatchFactory(host: WatchFactoryHost & { trace?(s: string): void; }, options: { extendedDiagnostics?: boolean; diagnostics?: boolean; }) { const watchLogLevel = host.trace ? options.extendedDiagnostics ? WatchLogLevel.Verbose : options.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None : WatchLogLevel.None; const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => host.trace!(s)) : noop; - const result = getWatchFactory(watchLogLevel, writeLog) as WatchFactory; + const result = getWatchFactory(host, watchLogLevel, writeLog) as WatchFactory; result.writeLog = writeLog; return result; } @@ -345,11 +506,11 @@ namespace ts { export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: { createHash?(data: string): string; }) { const originalGetSourceFile = compilerHost.getSourceFile; - const computeHash = host.createHash || generateDjb2Hash; + const computeHash = maybeBind(host, host.createHash) || generateDjb2Hash; compilerHost.getSourceFile = (...args) => { const result = originalGetSourceFile.call(compilerHost, ...args); if (result) { - result.version = computeHash.call(host, result.text); + result.version = computeHash(result.text); } return result; }; @@ -385,7 +546,7 @@ namespace ts { * Creates the watch compiler host that can be extended with config file or root file names and options host */ function createWatchCompilerHost(system = sys, createProgram: CreateProgram | undefined, reportDiagnostic: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHost { - const writeFileName = (s: string) => system.write(s + system.newLine); + const write = (s: string) => system.write(s + system.newLine); const result = createProgramHost(system, createProgram) as WatchCompilerHost; copyProperties(result, createWatchHost(system, reportWatchStatus)); result.afterProgramCreate = builderProgram => { @@ -395,7 +556,7 @@ namespace ts { emitFilesAndReportErrors( builderProgram, reportDiagnostic, - writeFileName, + write, errorCount => result.onWatchStatusChange!( createCompilerDiagnostic(getWatchErrorSummaryDiagnosticMessage(errorCount), errorCount), newLine, diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index 2673b11681050..25bb26332d48e 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -246,6 +246,7 @@ namespace ts { let builderProgram: T; let reloadLevel: ConfigFileProgramReloadLevel; // level to indicate if the program needs to be reloaded from config file/just filenames etc + let extendedConfigFilesMap: ESMap; // Map of file watchers for the extended config files let missingFilesMap: ESMap; // Map of file watchers for the missing files let watchedWildcardDirectories: ESMap; // map of watchers for the wild card directories in the config file let timerToUpdateProgram: any; // timer callback to recompile the program @@ -253,14 +254,14 @@ namespace ts { const sourceFilesCache = new Map(); // Cache that stores the source file and version info - let missingFilePathsRequestedForRelease: Path[] | undefined; // These paths are held temparirly so that we can remove the entry from source file cache if the file is not tracked by missing files + let missingFilePathsRequestedForRelease: Path[] | undefined; // These paths are held temporarily so that we can remove the entry from source file cache if the file is not tracked by missing files let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames(); const currentDirectory = host.getCurrentDirectory(); const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {}, watchOptionsToExtend, extraFileExtensions, createProgram } = host; let { rootFiles: rootFileNames, options: compilerOptions, watchOptions, projectReferences } = host; - let configFileSpecs: ConfigFileSpecs; + let wildcardDirectories: MapLike | undefined; let configFileParsingDiagnostics: Diagnostic[] | undefined; let canConfigFileJsonReportNoInputFiles = false; let hasChangedConfigFileParsingErrors = false; @@ -283,13 +284,13 @@ namespace ts { newLine = updateNewLine(); } - const { watchFile, watchFilePath, watchDirectory, writeLog } = createWatchFactory(host, compilerOptions); + const { watchFile, watchDirectory, writeLog } = createWatchFactory(host, compilerOptions); const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); writeLog(`Current directory: ${currentDirectory} CaseSensitiveFileNames: ${useCaseSensitiveFileNames}`); let configFileWatcher: FileWatcher | undefined; if (configFileName) { - configFileWatcher = watchFile(host, configFileName, scheduleProgramReload, PollingInterval.High, watchOptions, WatchType.ConfigFile); + configFileWatcher = watchFile(configFileName, scheduleProgramReload, PollingInterval.High, watchOptions, WatchType.ConfigFile); } const compilerHost = createCompilerHostFromProgramHost(host, () => compilerOptions, directoryStructureHost) as CompilerHost & ResolutionCacheHost; @@ -305,8 +306,8 @@ namespace ts { compilerHost.toPath = toPath; compilerHost.getCompilationSettings = () => compilerOptions; compilerHost.useSourceOfProjectReferenceRedirect = maybeBind(host, host.useSourceOfProjectReferenceRedirect); - compilerHost.watchDirectoryOfFailedLookupLocation = (dir, cb, flags) => watchDirectory(host, dir, cb, flags, watchOptions, WatchType.FailedLookupLocations); - compilerHost.watchTypeRootsDirectory = (dir, cb, flags) => watchDirectory(host, dir, cb, flags, watchOptions, WatchType.TypeRoots); + compilerHost.watchDirectoryOfFailedLookupLocation = (dir, cb, flags) => watchDirectory(dir, cb, flags, watchOptions, WatchType.FailedLookupLocations); + compilerHost.watchTypeRootsDirectory = (dir, cb, flags) => watchDirectory(dir, cb, flags, watchOptions, WatchType.TypeRoots); compilerHost.getCachedDirectoryStructureHost = () => cachedDirectoryStructureHost; compilerHost.scheduleInvalidateResolutionsOfFailedLookupLocations = scheduleInvalidateResolutionsOfFailedLookupLocations; compilerHost.onInvalidatedResolution = scheduleProgramUpdate; @@ -337,6 +338,9 @@ namespace ts { // Update the wild card directory watch watchConfigFileWildCardDirectories(); + // Update extended config file watch + watchExtendedConfigFiles(); + return configFileName ? { getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, close } : { getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, updateRootFileNames, close }; @@ -354,6 +358,10 @@ namespace ts { configFileWatcher.close(); configFileWatcher = undefined; } + if (extendedConfigFilesMap) { + clearMap(extendedConfigFilesMap, closeFileWatcher); + extendedConfigFilesMap = undefined!; + } if (watchedWildcardDirectories) { clearMap(watchedWildcardDirectories, closeFileWatcherOf); watchedWildcardDirectories = undefined!; @@ -488,7 +496,7 @@ namespace ts { (hostSourceFile as FilePresentOnHost).sourceFile = sourceFile; hostSourceFile.version = sourceFile.version; if (!hostSourceFile.fileWatcher) { - hostSourceFile.fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, path, WatchType.SourceFile); + hostSourceFile.fileWatcher = watchFilePath(path, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, WatchType.SourceFile); } } else { @@ -501,7 +509,7 @@ namespace ts { } else { if (sourceFile) { - const fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, path, WatchType.SourceFile); + const fileWatcher = watchFilePath(path, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, WatchType.SourceFile); sourceFilesCache.set(path, { sourceFile, version: sourceFile.version, fileWatcher }); } else { @@ -635,11 +643,10 @@ namespace ts { function reloadFileNamesFromConfigFile() { writeLog("Reloading new file names and options"); - const result = getFileNamesFromConfigSpecs(configFileSpecs, getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), compilerOptions, parseConfigFileHost); - if (updateErrorForNoInputFiles(result, getNormalizedAbsolutePath(configFileName, currentDirectory), configFileSpecs, configFileParsingDiagnostics!, canConfigFileJsonReportNoInputFiles)) { + rootFileNames = getFileNamesFromConfigSpecs(compilerOptions.configFile!.configFileSpecs!, getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), compilerOptions, parseConfigFileHost, extraFileExtensions); + if (updateErrorForNoInputFiles(rootFileNames, getNormalizedAbsolutePath(configFileName, currentDirectory), compilerOptions.configFile!.configFileSpecs!, configFileParsingDiagnostics!, canConfigFileJsonReportNoInputFiles)) { hasChangedConfigFileParsingErrors = true; } - rootFileNames = result.fileNames; // Update the program synchronizeProgram(); @@ -658,6 +665,9 @@ namespace ts { // Update the wild card directory watch watchConfigFileWildCardDirectories(); + + // Update extended config file watch + watchExtendedConfigFiles(); } function parseConfigFile() { @@ -668,13 +678,24 @@ namespace ts { rootFileNames = configFileParseResult.fileNames; compilerOptions = configFileParseResult.options; watchOptions = configFileParseResult.watchOptions; - configFileSpecs = configFileParseResult.configFileSpecs!; // TODO: GH#18217 projectReferences = configFileParseResult.projectReferences; + wildcardDirectories = configFileParseResult.wildcardDirectories; configFileParsingDiagnostics = getConfigFileParsingDiagnostics(configFileParseResult).slice(); canConfigFileJsonReportNoInputFiles = canJsonReportNoInputFiles(configFileParseResult.raw); hasChangedConfigFileParsingErrors = true; } + function watchFilePath( + path: Path, + file: string, + callback: (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void, + pollingInterval: PollingInterval, + options: WatchOptions | undefined, + watchType: WatchType + ): FileWatcher { + return watchFile(file, (fileName, eventKind) => callback(fileName, eventKind, path), pollingInterval, options, watchType); + } + function onSourceFileChange(fileName: string, eventKind: FileWatcherEventKind, path: Path) { updateCachedSystemWithFile(fileName, path, eventKind); @@ -696,7 +717,7 @@ namespace ts { } function watchMissingFilePath(missingFilePath: Path) { - return watchFilePath(host, missingFilePath, onMissingFileChange, PollingInterval.Medium, watchOptions, missingFilePath, WatchType.MissingFile); + return watchFilePath(missingFilePath, missingFilePath, onMissingFileChange, PollingInterval.Medium, watchOptions, WatchType.MissingFile); } function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) { @@ -715,10 +736,10 @@ namespace ts { } function watchConfigFileWildCardDirectories() { - if (configFileSpecs) { + if (wildcardDirectories) { updateWatchingWildcardDirectories( watchedWildcardDirectories || (watchedWildcardDirectories = new Map()), - new Map(getEntries(configFileSpecs.wildcardDirectories)), + new Map(getEntries(wildcardDirectories)), watchWildcardDirectory ); } @@ -729,7 +750,6 @@ namespace ts { function watchWildcardDirectory(directory: string, flags: WatchDirectoryFlags) { return watchDirectory( - host, directory, fileOrDirectory => { Debug.assert(!!configFileName); @@ -747,7 +767,7 @@ namespace ts { fileOrDirectory, fileOrDirectoryPath, configFileName, - configFileSpecs, + extraFileExtensions, options: compilerOptions, program: getCurrentBuilderProgram(), currentDirectory, @@ -768,5 +788,23 @@ namespace ts { WatchType.WildcardDirectory ); } + + function watchExtendedConfigFiles() { + // Update the extended config files watcher + mutateMap( + extendedConfigFilesMap ||= new Map(), + arrayToMap(compilerOptions.configFile?.extendedSourceFiles || emptyArray, toPath), + { + // Watch the extended config files + createNewValue: watchExtendedConfigFile, + // Config files that are no longer extended should no longer be watched. + onDeleteValue: closeFileWatcher + } + ); + } + + function watchExtendedConfigFile(extendedConfigFile: Path) { + return watchFile(extendedConfigFile, scheduleProgramReload, PollingInterval.High, watchOptions, WatchType.ExtendedConfigFile); + } } } diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index 6d4f02c1f380f..80fd5fe7a8f98 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -257,6 +257,51 @@ namespace ts { Full } + export interface SharedExtendedConfigFileWatcher extends FileWatcher { + fileWatcher: FileWatcher; + projects: Set; + } + + /** + * Updates the map of shared extended config file watches with a new set of extended config files from a base config file of the project + */ + export function updateSharedExtendedConfigFileWatcher( + projectPath: T, + parsed: ParsedCommandLine | undefined, + extendedConfigFilesMap: ESMap>, + createExtendedConfigFileWatch: (extendedConfigPath: string, extendedConfigFilePath: Path) => FileWatcher, + toPath: (fileName: string) => Path, + ) { + const extendedConfigs = arrayToMap(parsed?.options.configFile?.extendedSourceFiles || emptyArray, toPath); + // remove project from all unrelated watchers + extendedConfigFilesMap.forEach((watcher, extendedConfigFilePath) => { + if (!extendedConfigs.has(extendedConfigFilePath)) { + watcher.projects.delete(projectPath); + watcher.close(); + } + }); + // Update the extended config files watcher + extendedConfigs.forEach((extendedConfigFileName, extendedConfigFilePath) => { + const existing = extendedConfigFilesMap.get(extendedConfigFilePath); + if (existing) { + existing.projects.add(projectPath); + } + else { + // start watching previously unseen extended config + extendedConfigFilesMap.set(extendedConfigFilePath, { + projects: new Set([projectPath]), + fileWatcher: createExtendedConfigFileWatch(extendedConfigFileName, extendedConfigFilePath), + close: () => { + const existing = extendedConfigFilesMap.get(extendedConfigFilePath); + if (!existing || existing.projects.size !== 0) return; + existing.fileWatcher.close(); + extendedConfigFilesMap.delete(extendedConfigFilePath); + }, + }); + } + }); + } + /** * Updates the existing missing file watches with the new set of missing files after new program is created */ @@ -336,7 +381,6 @@ namespace ts { fileOrDirectoryPath: Path; configFileName: string; options: CompilerOptions; - configFileSpecs: ConfigFileSpecs; program: BuilderProgram | Program | undefined; extraFileExtensions?: readonly FileExtensionInfo[]; currentDirectory: string; @@ -346,7 +390,7 @@ namespace ts { /* @internal */ export function isIgnoredFileFromWildCardWatching({ watchedDirPath, fileOrDirectory, fileOrDirectoryPath, - configFileName, options, configFileSpecs, program, extraFileExtensions, + configFileName, options, program, extraFileExtensions, currentDirectory, useCaseSensitiveFileNames, writeLog, }: IsIgnoredFileFromWildCardWatchingInput): boolean { @@ -366,7 +410,7 @@ namespace ts { return true; } - if (isExcludedFile(fileOrDirectory, configFileSpecs, getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), useCaseSensitiveFileNames, currentDirectory)) { + if (isExcludedFile(fileOrDirectory, options.configFile!.configFileSpecs!, getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), useCaseSensitiveFileNames, currentDirectory)) { writeLog(`Project: ${configFileName} Detected excluded file: ${fileOrDirectory}`); return true; } @@ -421,117 +465,143 @@ namespace ts { Verbose } - export interface WatchFileHost { + export interface WatchFactoryHost { watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher; - } - export interface WatchDirectoryHost { watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher; - } - export type WatchFile = (host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher; - export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void; - export type WatchFilePath = (host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, path: Path, detailInfo1: X, detailInfo2?: Y) => FileWatcher; - export type WatchDirectory = (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher; - - export interface WatchFactory { - watchFile: WatchFile; - watchFilePath: WatchFilePath; - watchDirectory: WatchDirectory; + getCurrentDirectory?(): string; + useCaseSensitiveFileNames: boolean | (() => boolean); } - export function getWatchFactory(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFactory { - return getWatchFactoryWith(watchLogLevel, log, getDetailWatchInfo, watchFile, watchDirectory); + export interface WatchFactory { + watchFile: (file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher; + watchDirectory: (directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher; } - function getWatchFactoryWith( - watchLogLevel: WatchLogLevel, - log: (s: string) => void, - getDetailWatchInfo: GetDetailWatchInfo | undefined, - watchFile: (host: WatchFileHost, file: string, callback: FileWatcherCallback, watchPriority: PollingInterval, options: WatchOptions | undefined) => FileWatcher, - watchDirectory: (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined) => FileWatcher - ): WatchFactory { - const createFileWatcher: CreateFileWatcher = getCreateFileWatcher(watchLogLevel, watchFile); - const createFilePathWatcher: CreateFileWatcher = watchLogLevel === WatchLogLevel.None ? watchFilePath : createFileWatcher; - const createDirectoryWatcher: CreateFileWatcher = getCreateFileWatcher(watchLogLevel, watchDirectory); - if (watchLogLevel === WatchLogLevel.Verbose && sysLog === noop) { - setSysLog(s => log(s)); - } - return { - watchFile: (host, file, callback, pollingInterval, options, detailInfo1, detailInfo2) => - createFileWatcher(host, file, callback, pollingInterval, options, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo), - watchFilePath: (host, file, callback, pollingInterval, options, path, detailInfo1, detailInfo2) => - createFilePathWatcher(host, file, callback, pollingInterval, options, path, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo), - watchDirectory: (host, directory, callback, flags, options, detailInfo1, detailInfo2) => - createDirectoryWatcher(host, directory, callback, flags, options, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchDirectory, log, "DirectoryWatcher", getDetailWatchInfo) + export type GetDetailWatchInfo = (detailInfo1: X, detailInfo2: Y | undefined) => string; + export function getWatchFactory(host: WatchFactoryHost, watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFactory { + setSysLog(watchLogLevel === WatchLogLevel.Verbose ? log : noop); + const plainInvokeFactory: WatchFactory = { + watchFile: (file, callback, pollingInterval, options) => host.watchFile(file, callback, pollingInterval, options), + watchDirectory: (directory, callback, flags, options) => host.watchDirectory(directory, callback, (flags & WatchDirectoryFlags.Recursive) !== 0, options), }; - } - - function watchFile(host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined): FileWatcher { - return host.watchFile(file, callback, pollingInterval, options); - } - - function watchFilePath(host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, path: Path): FileWatcher { - return watchFile(host, file, (fileName, eventKind) => callback(fileName, eventKind, path), pollingInterval, options); - } + const triggerInvokingFactory: WatchFactory | undefined = watchLogLevel !== WatchLogLevel.None ? + { + watchFile: createTriggerLoggingAddWatch("watchFile"), + watchDirectory: createTriggerLoggingAddWatch("watchDirectory") + } : + undefined; + const factory = watchLogLevel === WatchLogLevel.Verbose ? + { + watchFile: createFileWatcherWithLogging, + watchDirectory: createDirectoryWatcherWithLogging + } : + triggerInvokingFactory || plainInvokeFactory; + const excludeWatcherFactory = watchLogLevel === WatchLogLevel.Verbose ? + createExcludeWatcherWithLogging : + returnNoopFileWatcher; - function watchDirectory(host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined): FileWatcher { - return host.watchDirectory(directory, callback, (flags & WatchDirectoryFlags.Recursive) !== 0, options); - } + return { + watchFile: createExcludeHandlingAddWatch("watchFile"), + watchDirectory: createExcludeHandlingAddWatch("watchDirectory") + }; - type WatchCallback = (fileName: string, cbOptional?: T, passThrough?: U) => void; - type AddWatch = (host: H, file: string, cb: WatchCallback, flags: T, options: WatchOptions | undefined, passThrough?: V, detailInfo1?: undefined, detailInfo2?: undefined) => FileWatcher; - export type GetDetailWatchInfo = (detailInfo1: X, detailInfo2: Y | undefined) => string; + function createExcludeHandlingAddWatch>(key: T): WatchFactory[T] { + return ( + file: string, + cb: FileWatcherCallback | DirectoryWatcherCallback, + flags: PollingInterval | WatchDirectoryFlags, + options: WatchOptions | undefined, + detailInfo1: X, + detailInfo2?: Y + ) => !matchesExclude(file, key === "watchFile" ? options?.excludeFiles : options?.excludeDirectories, useCaseSensitiveFileNames(), host.getCurrentDirectory?.() || "") ? + factory[key].call(/*thisArgs*/ undefined, file, cb, flags, options, detailInfo1, detailInfo2) : + excludeWatcherFactory(file, flags, options, detailInfo1, detailInfo2); + } + + function useCaseSensitiveFileNames() { + return typeof host.useCaseSensitiveFileNames === "boolean" ? + host.useCaseSensitiveFileNames : + host.useCaseSensitiveFileNames(); + } + + function createExcludeWatcherWithLogging( + file: string, + flags: PollingInterval | WatchDirectoryFlags, + options: WatchOptions | undefined, + detailInfo1: X, + detailInfo2?: Y + ) { + log(`ExcludeWatcher:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`); + return { + close: () => log(`ExcludeWatcher:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`) + }; + } - type CreateFileWatcher = (host: H, file: string, cb: WatchCallback, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | undefined) => FileWatcher; - function getCreateFileWatcher(watchLogLevel: WatchLogLevel, addWatch: AddWatch): CreateFileWatcher { - switch (watchLogLevel) { - case WatchLogLevel.None: - return addWatch; - case WatchLogLevel.TriggerOnly: - return createFileWatcherWithTriggerLogging; - case WatchLogLevel.Verbose: - return addWatch === watchDirectory ? createDirectoryWatcherWithLogging : createFileWatcherWithLogging; + function createFileWatcherWithLogging( + file: string, + cb: FileWatcherCallback, + flags: PollingInterval, + options: WatchOptions | undefined, + detailInfo1: X, + detailInfo2?: Y + ): FileWatcher { + log(`FileWatcher:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`); + const watcher = triggerInvokingFactory!.watchFile(file, cb, flags, options, detailInfo1, detailInfo2); + return { + close: () => { + log(`FileWatcher:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`); + watcher.close(); + } + }; } - } - function createFileWatcherWithLogging(host: H, file: string, cb: WatchCallback, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | undefined): FileWatcher { - log(`${watchCaption}:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`); - const watcher = createFileWatcherWithTriggerLogging(host, file, cb, flags, options, passThrough, detailInfo1, detailInfo2, addWatch, log, watchCaption, getDetailWatchInfo); - return { - close: () => { - log(`${watchCaption}:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`); - watcher.close(); - } - }; - } + function createDirectoryWatcherWithLogging( + file: string, + cb: DirectoryWatcherCallback, + flags: WatchDirectoryFlags, + options: WatchOptions | undefined, + detailInfo1: X, + detailInfo2?: Y + ): FileWatcher { + const watchInfo = `DirectoryWatcher:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`; + log(watchInfo); + const start = timestamp(); + const watcher = triggerInvokingFactory!.watchDirectory(file, cb, flags, options, detailInfo1, detailInfo2); + const elapsed = timestamp() - start; + log(`Elapsed:: ${elapsed}ms ${watchInfo}`); + return { + close: () => { + const watchInfo = `DirectoryWatcher:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`; + log(watchInfo); + const start = timestamp(); + watcher.close(); + const elapsed = timestamp() - start; + log(`Elapsed:: ${elapsed}ms ${watchInfo}`); + } + }; + } - function createDirectoryWatcherWithLogging(host: H, file: string, cb: WatchCallback, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | undefined): FileWatcher { - const watchInfo = `${watchCaption}:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`; - log(watchInfo); - const start = timestamp(); - const watcher = createFileWatcherWithTriggerLogging(host, file, cb, flags, options, passThrough, detailInfo1, detailInfo2, addWatch, log, watchCaption, getDetailWatchInfo); - const elapsed = timestamp() - start; - log(`Elapsed:: ${elapsed}ms ${watchInfo}`); - return { - close: () => { - const watchInfo = `${watchCaption}:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`; - log(watchInfo); + function createTriggerLoggingAddWatch>(key: T): WatchFactory[T] { + return ( + file: string, + cb: FileWatcherCallback | DirectoryWatcherCallback, + flags: PollingInterval | WatchDirectoryFlags, + options: WatchOptions | undefined, + detailInfo1: X, + detailInfo2?: Y + ) => plainInvokeFactory[key].call(/*thisArgs*/ undefined, file, (...args: any[]) => { + const triggerredInfo = `${key === "watchFile" ? "FileWatcher" : "DirectoryWatcher"}:: Triggered with ${args[0]} ${args[1] !== undefined ? args[1] : ""}:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`; + log(triggerredInfo); const start = timestamp(); - watcher.close(); + cb.call(/*thisArg*/ undefined, ...args); const elapsed = timestamp() - start; - log(`Elapsed:: ${elapsed}ms ${watchInfo}`); - } - }; - } + log(`Elapsed:: ${elapsed}ms ${triggerredInfo}`); + }, flags, options, detailInfo1, detailInfo2); + } - function createFileWatcherWithTriggerLogging(host: H, file: string, cb: WatchCallback, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | undefined): FileWatcher { - return addWatch(host, file, (fileName, cbOptional) => { - const triggerredInfo = `${watchCaption}:: Triggered with ${fileName} ${cbOptional !== undefined ? cbOptional : ""}:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`; - log(triggerredInfo); - const start = timestamp(); - cb(fileName, cbOptional, passThrough); - const elapsed = timestamp() - start; - log(`Elapsed:: ${elapsed}ms ${triggerredInfo}`); - }, flags, options); + function getWatchInfo(file: string, flags: T, options: WatchOptions | undefined, detailInfo1: X, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo | undefined) { + return `WatchInfo: ${file} ${flags} ${JSON.stringify(options)} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo2 === undefined ? detailInfo1 : `${detailInfo1} ${detailInfo2}`}`; + } } export function getFallbackOptions(options: WatchOptions | undefined): WatchOptions { @@ -543,10 +613,6 @@ namespace ts { }; } - function getWatchInfo(file: string, flags: T, options: WatchOptions | undefined, detailInfo1: X, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo | undefined) { - return `WatchInfo: ${file} ${flags} ${JSON.stringify(options)} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo2 === undefined ? detailInfo1 : `${detailInfo1} ${detailInfo2}`}`; - } - export function closeFileWatcherOf(objWithWatcher: T) { objWithWatcher.watcher.close(); } diff --git a/src/compat/deprecations.ts b/src/deprecatedCompat/deprecations.ts similarity index 91% rename from src/compat/deprecations.ts rename to src/deprecatedCompat/deprecations.ts index b5eb4c6658bc5..b0573ad4ab75d 100644 --- a/src/compat/deprecations.ts +++ b/src/deprecatedCompat/deprecations.ts @@ -296,22 +296,22 @@ namespace ts { /** @deprecated Use `factory.updateBindingElement` or the factory supplied by your transformation context instead. */ export const updateBindingElement = Debug.deprecate(factory.updateBindingElement, factoryDeprecation); - /** @deprecated Use `factory.createArrayLiteral` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createArrayLiteralExpression` or the factory supplied by your transformation context instead. */ export const createArrayLiteral = Debug.deprecate(factory.createArrayLiteralExpression, factoryDeprecation); - /** @deprecated Use `factory.updateArrayLiteral` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateArrayLiteralExpression` or the factory supplied by your transformation context instead. */ export const updateArrayLiteral = Debug.deprecate(factory.updateArrayLiteralExpression, factoryDeprecation); - /** @deprecated Use `factory.createObjectLiteral` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createObjectLiteralExpression` or the factory supplied by your transformation context instead. */ export const createObjectLiteral = Debug.deprecate(factory.createObjectLiteralExpression, factoryDeprecation); - /** @deprecated Use `factory.updateObjectLiteral` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateObjectLiteralExpression` or the factory supplied by your transformation context instead. */ export const updateObjectLiteral = Debug.deprecate(factory.updateObjectLiteralExpression, factoryDeprecation); - /** @deprecated Use `factory.createPropertyAccess` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createPropertyAccessExpression` or the factory supplied by your transformation context instead. */ export const createPropertyAccess = Debug.deprecate(factory.createPropertyAccessExpression, factoryDeprecation); - /** @deprecated Use `factory.updatePropertyAccess` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updatePropertyAccessExpression` or the factory supplied by your transformation context instead. */ export const updatePropertyAccess = Debug.deprecate(factory.updatePropertyAccessExpression, factoryDeprecation); /** @deprecated Use `factory.createPropertyAccessChain` or the factory supplied by your transformation context instead. */ @@ -320,10 +320,10 @@ namespace ts { /** @deprecated Use `factory.updatePropertyAccessChain` or the factory supplied by your transformation context instead. */ export const updatePropertyAccessChain = Debug.deprecate(factory.updatePropertyAccessChain, factoryDeprecation); - /** @deprecated Use `factory.createElementAccess` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createElementAccessExpression` or the factory supplied by your transformation context instead. */ export const createElementAccess = Debug.deprecate(factory.createElementAccessExpression, factoryDeprecation); - /** @deprecated Use `factory.updateElementAccess` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateElementAccessExpression` or the factory supplied by your transformation context instead. */ export const updateElementAccess = Debug.deprecate(factory.updateElementAccessExpression, factoryDeprecation); /** @deprecated Use `factory.createElementAccessChain` or the factory supplied by your transformation context instead. */ @@ -332,10 +332,10 @@ namespace ts { /** @deprecated Use `factory.updateElementAccessChain` or the factory supplied by your transformation context instead. */ export const updateElementAccessChain = Debug.deprecate(factory.updateElementAccessChain, factoryDeprecation); - /** @deprecated Use `factory.createCall` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createCallExpression` or the factory supplied by your transformation context instead. */ export const createCall = Debug.deprecate(factory.createCallExpression, factoryDeprecation); - /** @deprecated Use `factory.updateCall` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateCallExpression` or the factory supplied by your transformation context instead. */ export const updateCall = Debug.deprecate(factory.updateCallExpression, factoryDeprecation); /** @deprecated Use `factory.createCallChain` or the factory supplied by your transformation context instead. */ @@ -344,10 +344,10 @@ namespace ts { /** @deprecated Use `factory.updateCallChain` or the factory supplied by your transformation context instead. */ export const updateCallChain = Debug.deprecate(factory.updateCallChain, factoryDeprecation); - /** @deprecated Use `factory.createNew` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createNewExpression` or the factory supplied by your transformation context instead. */ export const createNew = Debug.deprecate(factory.createNewExpression, factoryDeprecation); - /** @deprecated Use `factory.updateNew` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateNewExpression` or the factory supplied by your transformation context instead. */ export const updateNew = Debug.deprecate(factory.updateNewExpression, factoryDeprecation); /** @deprecated Use `factory.createTypeAssertion` or the factory supplied by your transformation context instead. */ @@ -356,10 +356,10 @@ namespace ts { /** @deprecated Use `factory.updateTypeAssertion` or the factory supplied by your transformation context instead. */ export const updateTypeAssertion = Debug.deprecate(factory.updateTypeAssertion, factoryDeprecation); - /** @deprecated Use `factory.createParen` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createParenthesizedExpression` or the factory supplied by your transformation context instead. */ export const createParen = Debug.deprecate(factory.createParenthesizedExpression, factoryDeprecation); - /** @deprecated Use `factory.updateParen` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateParenthesizedExpression` or the factory supplied by your transformation context instead. */ export const updateParen = Debug.deprecate(factory.updateParenthesizedExpression, factoryDeprecation); /** @deprecated Use `factory.createFunctionExpression` or the factory supplied by your transformation context instead. */ @@ -368,46 +368,46 @@ namespace ts { /** @deprecated Use `factory.updateFunctionExpression` or the factory supplied by your transformation context instead. */ export const updateFunctionExpression = Debug.deprecate(factory.updateFunctionExpression, factoryDeprecation); - /** @deprecated Use `factory.createDelete` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createDeleteExpression` or the factory supplied by your transformation context instead. */ export const createDelete = Debug.deprecate(factory.createDeleteExpression, factoryDeprecation); - /** @deprecated Use `factory.updateDelete` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateDeleteExpression` or the factory supplied by your transformation context instead. */ export const updateDelete = Debug.deprecate(factory.updateDeleteExpression, factoryDeprecation); - /** @deprecated Use `factory.createTypeOf` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createTypeOfExpression` or the factory supplied by your transformation context instead. */ export const createTypeOf = Debug.deprecate(factory.createTypeOfExpression, factoryDeprecation); - /** @deprecated Use `factory.updateTypeOf` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateTypeOfExpression` or the factory supplied by your transformation context instead. */ export const updateTypeOf = Debug.deprecate(factory.updateTypeOfExpression, factoryDeprecation); - /** @deprecated Use `factory.createVoid` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createVoidExpression` or the factory supplied by your transformation context instead. */ export const createVoid = Debug.deprecate(factory.createVoidExpression, factoryDeprecation); - /** @deprecated Use `factory.updateVoid` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateVoidExpression` or the factory supplied by your transformation context instead. */ export const updateVoid = Debug.deprecate(factory.updateVoidExpression, factoryDeprecation); - /** @deprecated Use `factory.createAwait` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createAwaitExpression` or the factory supplied by your transformation context instead. */ export const createAwait = Debug.deprecate(factory.createAwaitExpression, factoryDeprecation); - /** @deprecated Use `factory.updateAwait` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateAwaitExpression` or the factory supplied by your transformation context instead. */ export const updateAwait = Debug.deprecate(factory.updateAwaitExpression, factoryDeprecation); - /** @deprecated Use `factory.createPrefix` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createPrefixExpression` or the factory supplied by your transformation context instead. */ export const createPrefix = Debug.deprecate(factory.createPrefixUnaryExpression, factoryDeprecation); - /** @deprecated Use `factory.updatePrefix` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updatePrefixExpression` or the factory supplied by your transformation context instead. */ export const updatePrefix = Debug.deprecate(factory.updatePrefixUnaryExpression, factoryDeprecation); - /** @deprecated Use `factory.createPostfix` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createPostfixUnaryExpression` or the factory supplied by your transformation context instead. */ export const createPostfix = Debug.deprecate(factory.createPostfixUnaryExpression, factoryDeprecation); - /** @deprecated Use `factory.updatePostfix` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updatePostfixUnaryExpression` or the factory supplied by your transformation context instead. */ export const updatePostfix = Debug.deprecate(factory.updatePostfixUnaryExpression, factoryDeprecation); - /** @deprecated Use `factory.createBinary` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createBinaryExpression` or the factory supplied by your transformation context instead. */ export const createBinary = Debug.deprecate(factory.createBinaryExpression, factoryDeprecation); - /** @deprecated Use `factory.updateConditional` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateConditionalExpression` or the factory supplied by your transformation context instead. */ export const updateConditional = Debug.deprecate(factory.updateConditionalExpression, factoryDeprecation); /** @deprecated Use `factory.createTemplateExpression` or the factory supplied by your transformation context instead. */ @@ -428,13 +428,13 @@ namespace ts { /** @deprecated Use `factory.createNoSubstitutionTemplateLiteral` or the factory supplied by your transformation context instead. */ export const createNoSubstitutionTemplateLiteral = Debug.deprecate(factory.createNoSubstitutionTemplateLiteral, factoryDeprecation); - /** @deprecated Use `factory.updateYield` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateYieldExpression` or the factory supplied by your transformation context instead. */ export const updateYield = Debug.deprecate(factory.updateYieldExpression, factoryDeprecation); - /** @deprecated Use `factory.createSpread` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createSpreadExpression` or the factory supplied by your transformation context instead. */ export const createSpread = Debug.deprecate(factory.createSpreadElement, factoryDeprecation); - /** @deprecated Use `factory.updateSpread` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateSpreadExpression` or the factory supplied by your transformation context instead. */ export const updateSpread = Debug.deprecate(factory.updateSpreadElement, factoryDeprecation); /** @deprecated Use `factory.createOmittedExpression` or the factory supplied by your transformation context instead. */ @@ -500,88 +500,88 @@ namespace ts { /** @deprecated Use `factory.updateExpressionStatement` or the factory supplied by your transformation context instead. */ export const updateStatement = Debug.deprecate(factory.updateExpressionStatement, factoryDeprecation); - /** @deprecated Use `factory.createIf` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createIfStatement` or the factory supplied by your transformation context instead. */ export const createIf = Debug.deprecate(factory.createIfStatement, factoryDeprecation); - /** @deprecated Use `factory.updateIf` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateIfStatement` or the factory supplied by your transformation context instead. */ export const updateIf = Debug.deprecate(factory.updateIfStatement, factoryDeprecation); - /** @deprecated Use `factory.createDo` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createDoStatement` or the factory supplied by your transformation context instead. */ export const createDo = Debug.deprecate(factory.createDoStatement, factoryDeprecation); - /** @deprecated Use `factory.updateDo` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateDoStatement` or the factory supplied by your transformation context instead. */ export const updateDo = Debug.deprecate(factory.updateDoStatement, factoryDeprecation); - /** @deprecated Use `factory.createWhile` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createWhileStatement` or the factory supplied by your transformation context instead. */ export const createWhile = Debug.deprecate(factory.createWhileStatement, factoryDeprecation); - /** @deprecated Use `factory.updateWhile` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateWhileStatement` or the factory supplied by your transformation context instead. */ export const updateWhile = Debug.deprecate(factory.updateWhileStatement, factoryDeprecation); - /** @deprecated Use `factory.createFor` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createForStatement` or the factory supplied by your transformation context instead. */ export const createFor = Debug.deprecate(factory.createForStatement, factoryDeprecation); - /** @deprecated Use `factory.updateFor` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateForStatement` or the factory supplied by your transformation context instead. */ export const updateFor = Debug.deprecate(factory.updateForStatement, factoryDeprecation); - /** @deprecated Use `factory.createForIn` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createForInStatement` or the factory supplied by your transformation context instead. */ export const createForIn = Debug.deprecate(factory.createForInStatement, factoryDeprecation); - /** @deprecated Use `factory.updateForIn` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateForInStatement` or the factory supplied by your transformation context instead. */ export const updateForIn = Debug.deprecate(factory.updateForInStatement, factoryDeprecation); - /** @deprecated Use `factory.createForOf` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createForOfStatement` or the factory supplied by your transformation context instead. */ export const createForOf = Debug.deprecate(factory.createForOfStatement, factoryDeprecation); - /** @deprecated Use `factory.updateForOf` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateForOfStatement` or the factory supplied by your transformation context instead. */ export const updateForOf = Debug.deprecate(factory.updateForOfStatement, factoryDeprecation); - /** @deprecated Use `factory.createContinue` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createContinueStatement` or the factory supplied by your transformation context instead. */ export const createContinue = Debug.deprecate(factory.createContinueStatement, factoryDeprecation); - /** @deprecated Use `factory.updateContinue` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateContinueStatement` or the factory supplied by your transformation context instead. */ export const updateContinue = Debug.deprecate(factory.updateContinueStatement, factoryDeprecation); - /** @deprecated Use `factory.createBreak` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createBreakStatement` or the factory supplied by your transformation context instead. */ export const createBreak = Debug.deprecate(factory.createBreakStatement, factoryDeprecation); - /** @deprecated Use `factory.updateBreak` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateBreakStatement` or the factory supplied by your transformation context instead. */ export const updateBreak = Debug.deprecate(factory.updateBreakStatement, factoryDeprecation); - /** @deprecated Use `factory.createReturn` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createReturnStatement` or the factory supplied by your transformation context instead. */ export const createReturn = Debug.deprecate(factory.createReturnStatement, factoryDeprecation); - /** @deprecated Use `factory.updateReturn` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateReturnStatement` or the factory supplied by your transformation context instead. */ export const updateReturn = Debug.deprecate(factory.updateReturnStatement, factoryDeprecation); - /** @deprecated Use `factory.createWith` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createWithStatement` or the factory supplied by your transformation context instead. */ export const createWith = Debug.deprecate(factory.createWithStatement, factoryDeprecation); - /** @deprecated Use `factory.updateWith` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateWithStatement` or the factory supplied by your transformation context instead. */ export const updateWith = Debug.deprecate(factory.updateWithStatement, factoryDeprecation); - /** @deprecated Use `factory.createSwitch` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createSwitchStatement` or the factory supplied by your transformation context instead. */ export const createSwitch = Debug.deprecate(factory.createSwitchStatement, factoryDeprecation); - /** @deprecated Use `factory.updateSwitch` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateSwitchStatement` or the factory supplied by your transformation context instead. */ export const updateSwitch = Debug.deprecate(factory.updateSwitchStatement, factoryDeprecation); - /** @deprecated Use `factory.createLabel` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createLabelStatement` or the factory supplied by your transformation context instead. */ export const createLabel = Debug.deprecate(factory.createLabeledStatement, factoryDeprecation); - /** @deprecated Use `factory.updateLabel` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateLabelStatement` or the factory supplied by your transformation context instead. */ export const updateLabel = Debug.deprecate(factory.updateLabeledStatement, factoryDeprecation); - /** @deprecated Use `factory.createThrow` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createThrowStatement` or the factory supplied by your transformation context instead. */ export const createThrow = Debug.deprecate(factory.createThrowStatement, factoryDeprecation); - /** @deprecated Use `factory.updateThrow` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateThrowStatement` or the factory supplied by your transformation context instead. */ export const updateThrow = Debug.deprecate(factory.updateThrowStatement, factoryDeprecation); - /** @deprecated Use `factory.createTry` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createTryStatement` or the factory supplied by your transformation context instead. */ export const createTry = Debug.deprecate(factory.createTryStatement, factoryDeprecation); - /** @deprecated Use `factory.updateTry` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateTryStatement` or the factory supplied by your transformation context instead. */ export const updateTry = Debug.deprecate(factory.updateTryStatement, factoryDeprecation); /** @deprecated Use `factory.createDebuggerStatement` or the factory supplied by your transformation context instead. */ @@ -893,10 +893,10 @@ namespace ts { /** @deprecated Use `factory.updatePartiallyEmittedExpression` or the factory supplied by your transformation context instead. */ export const updatePartiallyEmittedExpression = Debug.deprecate(factory.updatePartiallyEmittedExpression, factoryDeprecation); - /** @deprecated Use `factory.createCommaList` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.createCommaListExpression` or the factory supplied by your transformation context instead. */ export const createCommaList = Debug.deprecate(factory.createCommaListExpression, factoryDeprecation); - /** @deprecated Use `factory.updateCommaList` or the factory supplied by your transformation context instead. */ + /** @deprecated Use `factory.updateCommaListExpression` or the factory supplied by your transformation context instead. */ export const updateCommaList = Debug.deprecate(factory.updateCommaListExpression, factoryDeprecation); /** @deprecated Use `factory.createBundle` or the factory supplied by your transformation context instead. */ @@ -1306,14 +1306,14 @@ namespace ts { * NOTE: It is unsafe to change any properties of a `Node` that relate to its AST children, as those changes won't be * captured with respect to transformations. * - * @deprecated Use `factory.cloneNode` instead and use `setCommentRange` or `setSourceMapRange` and avoid setting `parent`. + * @deprecated Use an appropriate `factory.update...` method instead, use `setCommentRange` or `setSourceMapRange`, and avoid setting `parent`. */ export const getMutableClone = Debug.deprecate(function getMutableClone(node: T): T { const clone = factory.cloneNode(node); setTextRange(clone, node); setParent(clone, node.parent); return clone; - }, { since: "4.0", warnAfter: "4.1", message: "Use `factory.cloneNode` instead and use `setCommentRange` or `setSourceMapRange` and avoid setting `parent`." }); + }, { since: "4.0", warnAfter: "4.1", message: "Use an appropriate `factory.update...` method instead, use `setCommentRange` or `setSourceMapRange`, and avoid setting `parent`." }); // #endregion Node Factory top-level exports diff --git a/src/compat/tsconfig.json b/src/deprecatedCompat/tsconfig.json similarity index 71% rename from src/compat/tsconfig.json rename to src/deprecatedCompat/tsconfig.json index c9559ebc8651f..5217469c26c5f 100644 --- a/src/compat/tsconfig.json +++ b/src/deprecatedCompat/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-base", "compilerOptions": { - "outFile": "../../built/local/compat.js" + "outFile": "../../built/local/deprecatedCompat.js" }, "references": [ { "path": "../compiler" } diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index ce469755daf06..bf7d109e8382a 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -4,12 +4,57 @@ namespace ts { value: string; } - function countLines(program: Program): number { - let count = 0; + function countLines(program: Program): Map { + const counts = getCountsMap(); forEach(program.getSourceFiles(), file => { - count += getLineStarts(file).length; + const key = getCountKey(program, file); + const lineCount = getLineStarts(file).length; + counts.set(key, counts.get(key)! + lineCount); }); - return count; + return counts; + } + + function countNodes(program: Program): Map { + const counts = getCountsMap(); + forEach(program.getSourceFiles(), file => { + const key = getCountKey(program, file); + counts.set(key, counts.get(key)! + file.nodeCount); + }); + return counts; + } + + function getCountsMap() { + const counts = createMap(); + counts.set("Library", 0); + counts.set("Definitions", 0); + counts.set("TypeScript", 0); + counts.set("JavaScript", 0); + counts.set("JSON", 0); + counts.set("Other", 0); + return counts; + } + + function getCountKey(program: Program, file: SourceFile) { + if (program.isSourceFileDefaultLibrary(file)) { + return "Library"; + } + else if (file.isDeclarationFile) { + return "Definitions"; + } + + const path = file.path; + if (fileExtensionIsOneOf(path, supportedTSExtensions)) { + return "TypeScript"; + } + else if (fileExtensionIsOneOf(path, supportedJSExtensions)) { + return "JavaScript"; + } + else if (fileExtensionIs(path, Extension.Json)) { + return "JSON"; + } + else { + return "Other"; + } } function updateReportDiagnostic( @@ -621,7 +666,7 @@ namespace ts { } if (canTrace(system, compilerOptions)) { - tracing.startTracing(compilerOptions.configFilePath, compilerOptions.generateTrace!, isBuildMode); + tracing.startTracing(isBuildMode ? tracing.Mode.Build : tracing.Mode.Project, compilerOptions.generateTrace!, compilerOptions.configFilePath); } } @@ -637,8 +682,22 @@ namespace ts { statistics = []; const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; reportCountStatistic("Files", program.getSourceFiles().length); - reportCountStatistic("Lines", countLines(program)); - reportCountStatistic("Nodes", program.getNodeCount()); + + const lineCounts = countLines(program); + const nodeCounts = countNodes(program); + if (compilerOptions.extendedDiagnostics) { + for (const key of arrayFrom(lineCounts.keys())) { + reportCountStatistic("Lines of " + key, lineCounts.get(key)!); + } + for (const key of arrayFrom(nodeCounts.keys())) { + reportCountStatistic("Nodes of " + key, nodeCounts.get(key)!); + } + } + else { + reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0)); + reportCountStatistic("Nodes", reduceLeftIterator(nodeCounts.values(), (sum, count) => sum + count, 0)); + } + reportCountStatistic("Identifiers", program.getIdentifierCount()); reportCountStatistic("Symbols", program.getSymbolCount()); reportCountStatistic("Types", program.getTypeCount()); @@ -648,19 +707,22 @@ namespace ts { reportStatisticalValue("Memory used", Math.round(memoryUsed / 1000) + "K"); } - const programTime = performance.getDuration("Program"); - const bindTime = performance.getDuration("Bind"); - const checkTime = performance.getDuration("Check"); - const emitTime = performance.getDuration("Emit"); + const isPerformanceEnabled = performance.isEnabled(); + const programTime = isPerformanceEnabled ? performance.getDuration("Program") : 0; + const bindTime = isPerformanceEnabled ? performance.getDuration("Bind") : 0; + const checkTime = isPerformanceEnabled ? performance.getDuration("Check") : 0; + const emitTime = isPerformanceEnabled ? performance.getDuration("Emit") : 0; if (compilerOptions.extendedDiagnostics) { const caches = program.getRelationCacheSizes(); reportCountStatistic("Assignability cache size", caches.assignable); reportCountStatistic("Identity cache size", caches.identity); reportCountStatistic("Subtype cache size", caches.subtype); reportCountStatistic("Strict subtype cache size", caches.strictSubtype); - performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration)); + if (isPerformanceEnabled) { + performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration)); + } } - else { + else if (isPerformanceEnabled) { // Individual component times. // Note: To match the behavior of previous versions of the compiler, the reported parse time includes // I/O read time and processing time for triple-slash references and module imports, and the reported @@ -672,10 +734,16 @@ namespace ts { reportTimeStatistic("Check time", checkTime); reportTimeStatistic("Emit time", emitTime); } - reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); + if (isPerformanceEnabled) { + reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); + } reportStatistics(); - - performance.disable(); + if (!isPerformanceEnabled) { + sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n"); + } + else { + performance.disable(); + } } function reportStatistics() { diff --git a/src/harness/client.ts b/src/harness/client.ts index 230517b599db9..4bdb316b6b47d 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -343,9 +343,11 @@ namespace ts.server { })); } - findReferences(_fileName: string, _position: number): ReferencedSymbol[] { - // Not yet implemented. - return []; + findReferences(fileName: string, position: number): ReferencedSymbol[] { + const args = this.createFileLocationRequestArgs(fileName, position); + const request = this.processRequest(CommandNames.ReferencesFull, args); + const response = this.processResponse(request); + return response.body; } getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] { @@ -362,6 +364,18 @@ namespace ts.server { })); } + getFileReferences(fileName: string): ReferenceEntry[] { + const request = this.processRequest(CommandNames.FileReferences, { file: fileName }); + const response = this.processResponse(request); + + return response.body!.refs.map(entry => ({ // TODO: GH#18217 + fileName: entry.file, + textSpan: this.decodeSpan(entry), + isWriteAccess: entry.isWriteAccess, + isDefinition: entry.isDefinition, + })); + } + getEmitOutput(file: string): EmitOutput { const request = this.processRequest(protocol.CommandTypes.EmitOutput, { file }); const response = this.processResponse(request); diff --git a/src/harness/collectionsImpl.ts b/src/harness/collectionsImpl.ts index e149960b5e9f6..3d617901ddb77 100644 --- a/src/harness/collectionsImpl.ts +++ b/src/harness/collectionsImpl.ts @@ -50,6 +50,11 @@ namespace collections { return index >= 0 ? this._values[index] : undefined; } + public getEntry(key: K): [ K, V ] | undefined { + const index = ts.binarySearch(this._keys, key, ts.identity, this._comparer); + return index >= 0 ? [ this._keys[index], this._values[index] ] : undefined; + } + public set(key: K, value: V) { const index = ts.binarySearch(this._keys, key, ts.identity, this._comparer); if (index >= 0) { diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index f99eb118a0624..6355cb15acaba 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -271,6 +271,7 @@ namespace FourSlash { } } + let configParseResult: ts.ParsedCommandLine | undefined; if (configFileName) { const baseDir = ts.normalizePath(ts.getDirectoryPath(configFileName)); const files: vfs.FileSet = { [baseDir]: {} }; @@ -281,7 +282,8 @@ namespace FourSlash { const fs = new vfs.FileSystem(/*ignoreCase*/ true, { cwd: baseDir, files }); const host = new fakes.ParseConfigHost(fs); const jsonSourceFile = ts.parseJsonText(configFileName, this.inputFiles.get(configFileName)!); - compilationOptions = ts.parseJsonSourceFileConfigFileContent(jsonSourceFile, host, baseDir, compilationOptions, configFileName).options; + configParseResult = ts.parseJsonSourceFileConfigFileContent(jsonSourceFile, host, baseDir, compilationOptions, configFileName); + compilationOptions = configParseResult.options; } if (compilationOptions.typeRoots) { @@ -336,20 +338,30 @@ namespace FourSlash { // resolveReference file-option is not specified then do not resolve any files and include all inputFiles this.inputFiles.forEach((file, fileName) => { if (!Harness.isDefaultLibraryFile(fileName)) { - this.languageServiceAdapterHost.addScript(fileName, file, /*isRootFile*/ true); + // all files if config file not specified, otherwise root files from the config and typings cache files are root files + const isRootFile = !configParseResult || + ts.contains(configParseResult.fileNames, fileName) || + (ts.isDeclarationFileName(fileName) && ts.containsPath("/Library/Caches/typescript", fileName)); + this.languageServiceAdapterHost.addScript(fileName, file, isRootFile); } }); - if (!compilationOptions.noLib) { - this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName, - Harness.Compiler.getDefaultLibrarySourceFile()!.text, /*isRootFile*/ false); - compilationOptions.lib?.forEach(fileName => { + if (!compilationOptions.noLib) { + const seen = new Set(); + const addSourceFile = (fileName: string) => { + if (seen.has(fileName)) return; + seen.add(fileName); const libFile = Harness.Compiler.getDefaultLibrarySourceFile(fileName); ts.Debug.assertIsDefined(libFile, `Could not find lib file '${fileName}'`); - if (libFile) { - this.languageServiceAdapterHost.addScript(fileName, libFile.text, /*isRootFile*/ false); + this.languageServiceAdapterHost.addScript(fileName, libFile.text, /*isRootFile*/ false); + if (!ts.some(libFile.libReferenceDirectives)) return; + for (const directive of libFile.libReferenceDirectives) { + addSourceFile(`lib.${directive.fileName}.d.ts`); } - }); + }; + + addSourceFile(Harness.Compiler.defaultLibFileName); + compilationOptions.lib?.forEach(addSourceFile); } } @@ -753,7 +765,18 @@ namespace FourSlash { ts.zipWith(endMarkers, definitions, (endMarker, definition, i) => { const marker = this.getMarkerByName(endMarker); if (ts.comparePaths(marker.fileName, definition.fileName, /*ignoreCase*/ true) !== ts.Comparison.EqualTo || marker.position !== definition.textSpan.start) { - this.raiseError(`${testName} failed for definition ${endMarker} (${i}): expected ${marker.fileName} at ${marker.position}, got ${definition.fileName} at ${definition.textSpan.start}`); + const filesToDisplay = ts.deduplicate([marker.fileName, definition.fileName], ts.equateValues); + const markers = [{ text: "EXPECTED", fileName: marker.fileName, position: marker.position }, { text: "ACTUAL", fileName: definition.fileName, position: definition.textSpan.start }]; + const text = filesToDisplay.map(fileName => { + const markersToRender = markers.filter(m => m.fileName === fileName).sort((a, b) => b.position - a.position); + let fileContent = this.getFileContent(fileName); + for (const marker of markersToRender) { + fileContent = fileContent.slice(0, marker.position) + `\x1b[1;4m/*${marker.text}*/\x1b[0;31m` + fileContent.slice(marker.position); + } + return `// @Filename: ${fileName}\n${fileContent}`; + }).join("\n\n"); + + this.raiseError(`${testName} failed for definition ${endMarker} (${i}): expected ${marker.fileName} at ${marker.position}, got ${definition.fileName} at ${definition.textSpan.start}\n\n${text}\n`); } }); } @@ -771,7 +794,7 @@ namespace FourSlash { const fileContent = this.getFileContent(startFile); const spanContent = fileContent.slice(defs.textSpan.start, ts.textSpanEnd(defs.textSpan)); const spanContentWithMarker = spanContent.slice(0, marker.position - defs.textSpan.start) + `/*${startMarkerName}*/` + spanContent.slice(marker.position - defs.textSpan.start); - const suggestedFileContent = (fileContent.slice(0, defs.textSpan.start) + `\x1b[1;4m[|${spanContentWithMarker}|]\x1b[31m` + fileContent.slice(ts.textSpanEnd(defs.textSpan))) + const suggestedFileContent = (fileContent.slice(0, defs.textSpan.start) + `\x1b[1;4m[|${spanContentWithMarker}|]\x1b[0;31m` + fileContent.slice(ts.textSpanEnd(defs.textSpan))) .split(/\r?\n/).map(line => " ".repeat(6) + line).join(ts.sys.newLine); this.raiseError(`goToDefinitionsAndBoundSpan failed. Found a starting TextSpan around '${spanContent}' in '${startFile}' (at position ${defs.textSpan.start}). ` + `If this is the correct input span, put a fourslash range around it: \n\n${suggestedFileContent}\n`); @@ -884,11 +907,13 @@ namespace FourSlash { this.raiseError(`Expected completion insert text to be ${expected.insertText}, got ${actual.insertText}`); } const convertedReplacementSpan = expected.replacementSpan && ts.createTextSpanFromRange(expected.replacementSpan); - try { - assert.deepEqual(actual.replacementSpan, convertedReplacementSpan); - } - catch { - this.raiseError(`Expected completion replacementSpan to be ${stringify(convertedReplacementSpan)}, got ${stringify(actual.replacementSpan)}`); + if (convertedReplacementSpan?.length) { + try { + assert.deepEqual(actual.replacementSpan, convertedReplacementSpan); + } + catch { + this.raiseError(`Expected completion replacementSpan to be ${stringify(convertedReplacementSpan)}, got ${stringify(actual.replacementSpan)}`); + } } if (expected.kind !== undefined || expected.kindModifiers !== undefined) { @@ -1091,38 +1116,79 @@ namespace FourSlash { } } - public verifyBaselineFindAllReferences(markerName: string) { - const marker = this.getMarkerByName(markerName); - const references = this.languageService.findReferences(marker.fileName, marker.position); + public verifyBaselineFindAllReferences(...markerNames: string[]) { + ts.Debug.assert(markerNames.length > 0, "Must pass at least one marker name to `baselineFindAllReferences()`"); + const baseline = markerNames.map(markerName => { + this.goToMarker(markerName); + const marker = this.getMarkerByName(markerName); + const references = this.languageService.findReferences(marker.fileName, marker.position); + const refsByFile = references + ? ts.group(ts.sort(ts.flatMap(references, r => r.references), (a, b) => a.textSpan.start - b.textSpan.start), ref => ref.fileName) + : ts.emptyArray; + + // Write input files + const baselineContent = this.getBaselineContentForGroupedReferences(refsByFile, markerName); + + // Write response JSON + return baselineContent + JSON.stringify(references, undefined, 2); + }).join("\n\n"); + Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baseline); + } + + public verifyBaselineGetFileReferences(fileName: string) { + const references = this.languageService.getFileReferences(fileName); const refsByFile = references - ? ts.group(ts.sort(ts.flatMap(references, r => r.references), (a, b) => a.textSpan.start - b.textSpan.start), ref => ref.fileName) + ? ts.group(ts.sort(references, (a, b) => a.textSpan.start - b.textSpan.start), ref => ref.fileName) : ts.emptyArray; // Write input files + let baselineContent = this.getBaselineContentForGroupedReferences(refsByFile); + + // Write response JSON + baselineContent += JSON.stringify(references, undefined, 2); + Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent); + } + + private getBaselineContentForGroupedReferences(refsByFile: readonly (readonly ts.ReferenceEntry[])[], markerName?: string) { + const marker = markerName !== undefined ? this.getMarkerByName(markerName) : undefined; let baselineContent = ""; for (const group of refsByFile) { baselineContent += getBaselineContentForFile(group[0].fileName, this.getFileContent(group[0].fileName)); baselineContent += "\n\n"; } - - // Write response JSON - baselineContent += JSON.stringify(references, undefined, 2); - Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent); + return baselineContent; function getBaselineContentForFile(fileName: string, content: string) { let newContent = `=== ${fileName} ===\n`; let pos = 0; for (const { textSpan } of refsByFile.find(refs => refs[0].fileName === fileName) ?? ts.emptyArray) { - if (fileName === marker.fileName && ts.textSpanContainsPosition(textSpan, marker.position)) { - newContent += "/*FIND ALL REFS*/"; - } const end = textSpan.start + textSpan.length; newContent += content.slice(pos, textSpan.start); - newContent += "[|"; - newContent += content.slice(textSpan.start, end); + pos = textSpan.start; + // It's easier to read if the /*FIND ALL REFS*/ comment is outside the range markers, which makes + // this code a bit more verbose than it would be if I were less picky about the baseline format. + if (fileName === marker?.fileName && marker.position === textSpan.start) { + newContent += "/*FIND ALL REFS*/"; + newContent += "[|"; + } + else if (fileName === marker?.fileName && ts.textSpanContainsPosition(textSpan, marker.position)) { + newContent += "[|"; + newContent += content.slice(pos, marker.position); + newContent += "/*FIND ALL REFS*/"; + pos = marker.position; + } + else { + newContent += "[|"; + } + newContent += content.slice(pos, end); newContent += "|]"; pos = end; } + if (marker?.fileName === fileName && marker.position >= pos) { + newContent += content.slice(pos, marker.position); + newContent += "/*FIND ALL REFS*/"; + pos = marker.position; + } newContent += content.slice(pos); return newContent.split(/\r?\n/).map(l => "// " + l).join("\n"); } @@ -1136,7 +1202,7 @@ namespace FourSlash { } } - // Necessary to have this function since `findReferences` isn't implemented in `client.ts` + /** @deprecated - use `verify.baselineFindAllReferences()` instead. */ public verifyGetReferencesForServerTest(expected: readonly ts.ReferenceEntry[]): void { const refs = this.getReferencesAtCaret(); assert.deepEqual(refs, expected); @@ -2455,8 +2521,7 @@ namespace FourSlash { const { fileName } = this.activeFile; const before = this.getFileContent(fileName); this.formatDocument(); - const after = this.getFileContent(fileName); - this.assertObjectsEqual(after, before); + this.verifyFileContent(fileName, before); } public verifyTextAtCaretIs(text: string) { @@ -2733,7 +2798,12 @@ namespace FourSlash { const details = this.getCompletionEntryDetails(options.name, options.source, options.preferences); if (!details) { - return this.raiseError(`No completions were found for the given name, source, and preferences.`); + const completions = this.getCompletionListAtCaret(options.preferences)?.entries; + const matchingName = completions?.filter(e => e.name === options.name); + const detailMessage = matchingName?.length + ? `\n Found ${matchingName.length} with name '${options.name}' from source(s) ${matchingName.map(e => `'${e.source}'`).join(", ")}.` + : ""; + return this.raiseError(`No completions were found for the given name, source, and preferences.` + detailMessage); } const codeActions = details.codeActions; if (codeActions?.length !== 1) { @@ -2921,6 +2991,10 @@ namespace FourSlash { } const range = ts.firstOrUndefined(ranges); + if (preferences) { + this.configure(preferences); + } + const codeFixes = this.getCodeFixes(fileName, errorCode, preferences).filter(f => f.fixName === ts.codefix.importFixName); if (codeFixes.length === 0) { @@ -3346,6 +3420,12 @@ namespace FourSlash { } } + public verifyRefactorKindsAvailable(kind: string, expected: string[], preferences = ts.emptyOptions) { + const refactors = this.getApplicableRefactorsAtSelection("invoked", kind, preferences); + const availableKinds = ts.flatMap(refactors, refactor => refactor.actions).map(action => action.kind); + assert.deepEqual(availableKinds.sort(), expected.sort(), `Expected kinds to be equal`); + } + public verifyRefactorsAvailable(names: readonly string[]): void { assert.deepEqual(unique(this.getApplicableRefactorsAtSelection(), r => r.name), names); } @@ -3759,14 +3839,14 @@ namespace FourSlash { test(renameKeys(newFileContents, key => pathUpdater(key) || key), "with file moved"); } - private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit") { - return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, ts.emptyOptions, triggerReason); + private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit", kind?: string, preferences = ts.emptyOptions) { + return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, preferences, triggerReason, kind); } - private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit"): readonly ts.ApplicableRefactorInfo[] { - return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason); // eslint-disable-line no-in-operator + private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit", kind?: string): readonly ts.ApplicableRefactorInfo[] { + return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason, kind); // eslint-disable-line no-in-operator } - private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason): readonly ts.ApplicableRefactorInfo[] { - return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason) || ts.emptyArray; + private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason, kind?: string): readonly ts.ApplicableRefactorInfo[] { + return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason, kind) || ts.emptyArray; } public configurePlugin(pluginName: string, configuration: any): void { @@ -3878,7 +3958,7 @@ namespace FourSlash { const testData = parseTestData(absoluteBasePath, content, absoluteFileName); const state = new TestState(absoluteFileName, absoluteBasePath, testType, testData); const actualFileName = Harness.IO.resolvePath(fileName) || absoluteFileName; - const output = ts.transpileModule(content, { reportDiagnostics: true, fileName: actualFileName, compilerOptions: { target: ts.ScriptTarget.ES2015, inlineSourceMap: true } }); + const output = ts.transpileModule(content, { reportDiagnostics: true, fileName: actualFileName, compilerOptions: { target: ts.ScriptTarget.ES2015, inlineSourceMap: true, inlineSources: true } }); if (output.diagnostics!.length > 0) { throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics![0].messageText}`); } @@ -3888,7 +3968,7 @@ namespace FourSlash { function runCode(code: string, state: TestState, fileName: string): void { // Compile and execute the test const generatedFile = ts.changeExtension(fileName, ".js"); - const wrappedCode = `(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {${code}\n//# sourceURL=${generatedFile}\n})`; + const wrappedCode = `(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {${code}\n//# sourceURL=${ts.getBaseFileName(generatedFile)}\n})`; type SourceMapSupportModule = typeof import("source-map-support") & { // TODO(rbuckton): This is missing from the DT definitions and needs to be added. diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 096b1ea8e8dbc..2e64b2921e8b6 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -215,6 +215,10 @@ namespace FourSlashInterface { this.state.verifyRefactorAvailable(this.negative, triggerReason, name, actionName); } + public refactorKindAvailable(kind: string, expected: string[], preferences = ts.emptyOptions) { + this.state.verifyRefactorKindsAvailable(kind, expected, preferences); + } + public toggleLineComment(newFileContent: string) { this.state.toggleLineComment(newFileContent); } @@ -332,8 +336,12 @@ namespace FourSlashInterface { this.state.verifyTypeOfSymbolAtLocation(range, symbol, expected); } - public baselineFindAllReferences(markerName: string) { - this.state.verifyBaselineFindAllReferences(markerName); + public baselineFindAllReferences(...markerNames: string[]) { + this.state.verifyBaselineFindAllReferences(...markerNames); + } + + public baselineGetFileReferences(fileName: string) { + this.state.verifyBaselineGetFileReferences(fileName); } public referenceGroups(starts: ArrayOrSingle | ArrayOrSingle, parts: ReferenceGroup[]) { @@ -993,7 +1001,7 @@ namespace FourSlashInterface { export const keywords: readonly ExpectedCompletionEntryObject[] = keywordsWithUndefined.filter(k => k.name !== "undefined"); export const typeKeywords: readonly ExpectedCompletionEntryObject[] = - ["false", "null", "true", "void", "asserts", "any", "boolean", "keyof", "never", "readonly", "number", "object", "string", "symbol", "undefined", "unique", "unknown", "bigint"].map(keywordEntry); + ["false", "null", "true", "void", "asserts", "any", "boolean", "infer", "keyof", "never", "readonly", "number", "object", "string", "symbol", "undefined", "unique", "unknown", "bigint"].map(keywordEntry); const globalTypeDecls: readonly ExpectedCompletionEntryObject[] = [ interfaceEntry("Symbol"), @@ -1065,6 +1073,10 @@ namespace FourSlashInterface { typeEntry("ConstructorParameters"), typeEntry("ReturnType"), typeEntry("InstanceType"), + typeEntry("Uppercase"), + typeEntry("Lowercase"), + typeEntry("Capitalize"), + typeEntry("Uncapitalize"), interfaceEntry("ThisType"), varEntry("ArrayBuffer"), interfaceEntry("ArrayBufferTypes"), @@ -1250,6 +1262,7 @@ namespace FourSlashInterface { "await", "boolean", "declare", + "infer", "keyof", "module", "namespace", @@ -1370,6 +1383,7 @@ namespace FourSlashInterface { "let", "package", "yield", + "as", "async", "await", ].map(keywordEntry); @@ -1451,6 +1465,7 @@ namespace FourSlashInterface { "await", "boolean", "declare", + "infer", "keyof", "module", "namespace", @@ -1510,6 +1525,7 @@ namespace FourSlashInterface { "let", "package", "yield", + "as", "async", "await", ].map(keywordEntry); diff --git a/src/harness/harnessIO.ts b/src/harness/harnessIO.ts index 0c62fc78ec88d..f556b6e47f69c 100644 --- a/src/harness/harnessIO.ts +++ b/src/harness/harnessIO.ts @@ -1381,7 +1381,7 @@ namespace Harness { else { IO.writeFile(actualFileName, encodedActual); } - if (require && opts && opts.PrintDiff) { + if (!!require && opts && opts.PrintDiff) { const Diff = require("diff"); const patch = Diff.createTwoFilesPatch("Expected", "Actual", expected, actual, "The current baseline", "The new version"); throw new Error(`The baseline file ${relativeFileName} has changed.${ts.ForegroundColorEscapeSequences.Grey}\n\n${patch}`); diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index cc2f285fc5c8f..85164d58bb122 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -519,6 +519,9 @@ namespace Harness.LanguageService { findReferences(fileName: string, position: number): ts.ReferencedSymbol[] { return unwrapJSONCallResult(this.shim.findReferences(fileName, position)); } + getFileReferences(fileName: string): ts.ReferenceEntry[] { + return unwrapJSONCallResult(this.shim.getFileReferences(fileName)); + } getOccurrencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] { return unwrapJSONCallResult(this.shim.getOccurrencesAtPosition(fileName, position)); } diff --git a/src/harness/vfsUtil.ts b/src/harness/vfsUtil.ts index 974c164976c2a..c1fb99326bc36 100644 --- a/src/harness/vfsUtil.ts +++ b/src/harness/vfsUtil.ts @@ -1036,8 +1036,12 @@ namespace vfs { while (true) { if (depth >= 40) throw createIOError("ELOOP"); const lastStep = step === components.length - 1; - const basename = components[step]; - const node = links.get(basename); + let basename = components[step]; + const linkEntry = links.getEntry(basename); + if (linkEntry) { + components[step] = basename = linkEntry[0]; + } + const node = linkEntry?.[1]; if (lastStep && (noFollow || !isSymlink(node))) { return { realpath: vpath.format(components), basename, parent, links, node }; } diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index c0e66430e9a06..e17136d67e385 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -251,7 +251,7 @@ interface Array { length: number; [n: number]: T; }` else { recursiveOrExpectedDetails = recursiveOrEachDirectoryWatchCount as boolean; checkMap( - `fsWatches{recursive ? " recursive" : ""}`, + `fsWatches${recursiveOrExpectedDetails ? " recursive" : ""}`, recursiveOrExpectedDetails ? host.fsWatchesRecursive : host.fsWatches, expectedDirectories, [expectedDetails, ({ directoryName, fallbackPollingInterval, fallbackOptions }) => ({ directoryName, fallbackPollingInterval, fallbackOptions })] @@ -433,6 +433,7 @@ interface Array { length: number; [n: number]: T; }` fsWatch: this.fsWatch.bind(this), fileExists: this.fileExists.bind(this), useCaseSensitiveFileNames: this.useCaseSensitiveFileNames, + getCurrentDirectory: this.getCurrentDirectory.bind(this), fsSupportsRecursiveFsWatch: tscWatchDirectory ? false : !runWithoutRecursiveWatches, directoryExists: this.directoryExists.bind(this), getAccessibleSortedChildDirectories: path => this.getDirectories(path), @@ -827,9 +828,9 @@ interface Array { length: number; [n: number]: T; }` return undefined; } - const realpath = this.realpath(path); + const realpath = this.toPath(this.realpath(path)); if (path !== realpath) { - return this.getRealFsEntry(isFsEntry, this.toPath(realpath)); + return this.getRealFsEntry(isFsEntry, realpath); } return undefined; @@ -1011,6 +1012,10 @@ interface Array { length: number; [n: number]: T; }` } } + prependFile(path: string, content: string, options?: Partial): void { + this.modifyFile(path, content + this.readFile(path), options); + } + appendFile(path: string, content: string, options?: Partial): void { this.modifyFile(path, this.readFile(path) + content, options); } @@ -1092,7 +1097,8 @@ interface Array { length: number; [n: number]: T; }` return this.realpath(fsEntry.symLink); } - return realFullPath; + // realpath supports non-existent files, so there may not be an fsEntry + return fsEntry?.fullPath || realFullPath; } readonly exitMessage = "System Exit"; @@ -1203,9 +1209,11 @@ interface Array { length: number; [n: number]: T; }` } function baselineOutputs(baseline: string[], output: readonly string[], start: number, end = output.length) { + let baselinedOutput: string[] | undefined; for (let i = start; i < end; i++) { - baseline.push(output[i].replace(/Elapsed::\s[0-9]+ms/g, "Elapsed:: *ms")); + (baselinedOutput ||= []).push(output[i].replace(/Elapsed::\s[0-9]+(?:\.\d+)?ms/g, "Elapsed:: *ms")); } + if (baselinedOutput) baseline.push(baselinedOutput.join("")); } export type TestServerHostTrackingWrittenFiles = TestServerHost & { writtenFiles: ESMap; }; diff --git a/src/jsTyping/jsTyping.ts b/src/jsTyping/jsTyping.ts index ca765bd776fd3..10ef0cf957a97 100644 --- a/src/jsTyping/jsTyping.ts +++ b/src/jsTyping/jsTyping.ts @@ -149,8 +149,9 @@ namespace ts.JsTyping { const nodeModulesPath = combinePaths(searchDir, "node_modules"); getTypingNamesFromPackagesFolder(nodeModulesPath, filesToWatch); }); - getTypingNamesFromSourceFileNames(fileNames); - + if(!typeAcquisition.disableFilenameBasedTypeAcquisition) { + getTypingNamesFromSourceFileNames(fileNames); + } // add typings for unresolved imports if (unresolvedImports) { const module = deduplicate( diff --git a/src/jsTyping/types.ts b/src/jsTyping/types.ts index 34f69d0d624df..26d2c69795d96 100644 --- a/src/jsTyping/types.ts +++ b/src/jsTyping/types.ts @@ -83,8 +83,9 @@ declare namespace ts.server { useCaseSensitiveFileNames: boolean; writeFile(path: string, content: string): void; createDirectory(path: string): void; - watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: CompilerOptions): FileWatcher; - watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: CompilerOptions): FileWatcher; + getCurrentDirectory?(): string; + watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher; + watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher; } export interface SetTypings extends ProjectResponse { diff --git a/src/lib/dom.generated.d.ts b/src/lib/dom.generated.d.ts index 82c019c03a36e..def48d12b0645 100644 --- a/src/lib/dom.generated.d.ts +++ b/src/lib/dom.generated.d.ts @@ -594,6 +594,10 @@ interface ImageEncodeOptions { type?: string; } +interface ImportMeta { + url?: string; +} + interface InputEventInit extends UIEventInit { data?: string | null; inputType?: string; @@ -611,7 +615,7 @@ interface IntersectionObserverEntryInit { } interface IntersectionObserverInit { - root?: Element | null; + root?: Element | Document | null; rootMargin?: string; threshold?: number | number[]; } @@ -642,6 +646,8 @@ interface KeyAlgorithm { } interface KeyboardEventInit extends EventModifierInit { + /** @deprecated */ + charCode?: number; code?: string; isComposing?: boolean; key?: string; @@ -1023,18 +1029,13 @@ interface PermissionDescriptor { name: PermissionName; } -interface PipeOptions { - preventAbort?: boolean; - preventCancel?: boolean; - preventClose?: boolean; - signal?: AbortSignal; -} - interface PointerEventInit extends MouseEventInit { + coalescedEvents?: PointerEvent[]; height?: number; isPrimary?: boolean; pointerId?: number; pointerType?: string; + predictedEvents?: PointerEvent[]; pressure?: number; tangentialPressure?: number; tiltX?: number; @@ -1138,7 +1139,16 @@ interface PushSubscriptionOptionsInit { interface QueuingStrategy { highWaterMark?: number; - size?: QueuingStrategySizeCallback; + size?: QueuingStrategySize; +} + +interface QueuingStrategyInit { + /** + * Creates a new ByteLengthQueuingStrategy with the provided high water mark. + * + * Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw. + */ + highWaterMark: number; } interface RTCAnswerOptions extends RTCOfferAnswerOptions { @@ -1239,17 +1249,36 @@ interface RTCIceCandidatePair { interface RTCIceCandidatePairStats extends RTCStats { availableIncomingBitrate?: number; availableOutgoingBitrate?: number; + bytesDiscardedOnSend?: number; bytesReceived?: number; bytesSent?: number; + circuitBreakerTriggerCount?: number; + consentExpiredTimestamp?: number; + consentRequestsSent?: number; + currentRoundTripTime?: number; + currentRtt?: number; + firstRequestTimestamp?: number; + lastPacketReceivedTimestamp?: number; + lastPacketSentTimestamp?: number; + lastRequestTimestamp?: number; + lastResponseTimestamp?: number; localCandidateId?: string; nominated?: boolean; + packetsDiscardedOnSend?: number; + packetsReceived?: number; + packetsSent?: number; priority?: number; - readable?: boolean; remoteCandidateId?: string; - roundTripTime?: number; + requestsReceived?: number; + requestsSent?: number; + responsesReceived?: number; + responsesSent?: number; + retransmissionsReceived?: number; + retransmissionsSent?: number; state?: RTCStatsIceCandidatePairState; + totalRoundTripTime?: number; + totalRtt?: number; transportId?: string; - writable?: boolean; } interface RTCIceGatherOptions { @@ -1487,9 +1516,9 @@ interface RTCSsrcRange { } interface RTCStats { - id: string; - timestamp: number; - type: RTCStatsType; + id?: string; + timestamp?: number; + type?: RTCStatsType; } interface RTCStatsEventInit extends EventInit { @@ -1507,25 +1536,43 @@ interface RTCTrackEventInit extends EventInit { } interface RTCTransportStats extends RTCStats { - activeConnection?: boolean; bytesReceived?: number; bytesSent?: number; + dtlsCipher?: string; + dtlsState?: RTCDtlsTransportState; + iceRole?: RTCIceRole; localCertificateId?: string; + packetsReceived?: number; + packetsSent?: number; remoteCertificateId?: string; rtcpTransportStatsId?: string; + selectedCandidatePairChanges?: number; selectedCandidatePairId?: string; + srtpCipher?: string; + tlsGroup?: string; + tlsVersion?: string; } -interface ReadableStreamReadDoneResult { +interface ReadableStreamDefaultReadDoneResult { done: true; - value?: T; + value?: undefined; } -interface ReadableStreamReadValueResult { +interface ReadableStreamDefaultReadValueResult { done: false; value: T; } +interface ReadableWritablePair { + readable: ReadableStream; + /** + * Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + */ + writable: WritableStream; +} + interface RegistrationOptions { scope?: string; type?: WorkerType; @@ -1587,6 +1634,10 @@ interface RequestInit { window?: any; } +interface ResizeObserverOptions { + box?: ResizeObserverBoxOptions; +} + interface ResponseInit { headers?: HeadersInit; status?: number; @@ -1700,6 +1751,16 @@ interface ShareData { url?: string; } +interface SpeechRecognitionErrorEventInit extends EventInit { + error: SpeechRecognitionErrorCode; + message?: string; +} + +interface SpeechRecognitionEventInit extends EventInit { + resultIndex?: number; + results: SpeechRecognitionResultList; +} + interface SpeechSynthesisErrorEventInit extends SpeechSynthesisEventInit { error: SpeechSynthesisErrorCode; } @@ -1746,6 +1807,30 @@ interface StoreSiteSpecificExceptionsInformation extends StoreExceptionsInformat arrayOfDomainStrings?: string[]; } +interface StreamPipeOptions { + preventAbort?: boolean; + preventCancel?: boolean; + /** + * Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + * + * Errors and closures of the source and destination streams propagate as follows: + * + * An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination. + * + * An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source. + * + * When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error. + * + * If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source. + * + * The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set. + */ + preventClose?: boolean; + signal?: AbortSignal; +} + interface TextDecodeOptions { stream?: boolean; } @@ -1789,10 +1874,10 @@ interface TrackEventInit extends EventInit { } interface Transformer { - flush?: TransformStreamDefaultControllerCallback; + flush?: TransformerFlushCallback; readableType?: undefined; - start?: TransformStreamDefaultControllerCallback; - transform?: TransformStreamDefaultControllerTransformCallback; + start?: TransformerStartCallback; + transform?: TransformerTransformCallback; writableType?: undefined; } @@ -1812,26 +1897,18 @@ interface ULongRange { min?: number; } -interface UnderlyingByteSource { - autoAllocateChunkSize?: number; - cancel?: ReadableStreamErrorCallback; - pull?: ReadableByteStreamControllerCallback; - start?: ReadableByteStreamControllerCallback; - type: "bytes"; -} - interface UnderlyingSink { - abort?: WritableStreamErrorCallback; - close?: WritableStreamDefaultControllerCloseCallback; - start?: WritableStreamDefaultControllerStartCallback; + abort?: UnderlyingSinkAbortCallback; + close?: UnderlyingSinkCloseCallback; + start?: UnderlyingSinkStartCallback; type?: undefined; - write?: WritableStreamDefaultControllerWriteCallback; + write?: UnderlyingSinkWriteCallback; } interface UnderlyingSource { - cancel?: ReadableStreamErrorCallback; - pull?: ReadableStreamDefaultControllerCallback; - start?: ReadableStreamDefaultControllerCallback; + cancel?: UnderlyingSourceCancelCallback; + pull?: UnderlyingSourcePullCallback; + start?: UnderlyingSourceStartCallback; type?: undefined; } @@ -2320,7 +2397,9 @@ declare var AudioParamMap: { new(): AudioParamMap; }; -/** The Web Audio API events that occur when a ScriptProcessorNode input buffer is ready to be processed. */ +/** The Web Audio API events that occur when a ScriptProcessorNode input buffer is ready to be processed. + * @deprecated As of the August 29 2014 Web Audio API spec publication, this feature has been marked as deprecated, and is soon to be replaced by AudioWorklet. + */ interface AudioProcessingEvent extends Event { readonly inputBuffer: AudioBuffer; readonly outputBuffer: AudioBuffer; @@ -2563,13 +2642,13 @@ declare var BroadcastChannel: { /** This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. */ interface ByteLengthQueuingStrategy extends QueuingStrategy { - highWaterMark: number; - size(chunk: ArrayBufferView): number; + readonly highWaterMark: number; + readonly size: QueuingStrategySize; } declare var ByteLengthQueuingStrategy: { prototype: ByteLengthQueuingStrategy; - new(options: { highWaterMark: number }): ByteLengthQueuingStrategy; + new(init: QueuingStrategyInit): ByteLengthQueuingStrategy; }; /** A CDATA section that can be used within XML to include extended portions of unescaped text. The symbols < and & don’t need escaping as they normally do when inside a CDATA section. */ @@ -3614,13 +3693,13 @@ declare var ConvolverNode: { /** This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. */ interface CountQueuingStrategy extends QueuingStrategy { - highWaterMark: number; - size(chunk: any): 1; + readonly highWaterMark: number; + readonly size: QueuingStrategySize; } declare var CountQueuingStrategy: { prototype: CountQueuingStrategy; - new(options: { highWaterMark: number }): CountQueuingStrategy; + new(init: QueuingStrategyInit): CountQueuingStrategy; }; interface Credential { @@ -4680,6 +4759,7 @@ interface Document extends Node, DocumentAndElementEventHandlers, DocumentOrShad createEvent(eventInterface: "SVGZoomEvents"): SVGZoomEvent; createEvent(eventInterface: "SecurityPolicyViolationEvent"): SecurityPolicyViolationEvent; createEvent(eventInterface: "ServiceWorkerMessageEvent"): ServiceWorkerMessageEvent; + createEvent(eventInterface: "SpeechRecognitionErrorEvent"): SpeechRecognitionErrorEvent; createEvent(eventInterface: "SpeechRecognitionEvent"): SpeechRecognitionEvent; createEvent(eventInterface: "SpeechSynthesisErrorEvent"): SpeechSynthesisErrorEvent; createEvent(eventInterface: "SpeechSynthesisEvent"): SpeechSynthesisEvent; @@ -4747,14 +4827,14 @@ interface Document extends Node, DocumentAndElementEventHandlers, DocumentOrShad exitPointerLock(): void; getAnimations(): Animation[]; /** - * Returns a reference to the first object with the specified value of the ID or NAME attribute. - * @param elementId String that specifies the ID value. Case-insensitive. + * Returns a reference to the first object with the specified value of the ID attribute. + * @param elementId String that specifies the ID value. */ - getElementById(elementId: string): HTMLElement | null; + getElementById(elementId: string): E | null; /** * Returns a HTMLCollection of the elements in the object on which the method was invoked (a document or an element) that have all the classes given by classNames. The classNames argument is interpreted as a space-separated list of classes. */ - getElementsByClassName(classNames: string): HTMLCollectionOf; + getElementsByClassName(classNames: string): HTMLCollectionOf; /** * Gets a collection of objects based on the value of the NAME or ID attribute. * @param elementName Gets a collection of objects based on the value of the NAME or ID attribute. @@ -4929,6 +5009,7 @@ interface DocumentEvent { createEvent(eventInterface: "SVGZoomEvents"): SVGZoomEvent; createEvent(eventInterface: "SecurityPolicyViolationEvent"): SecurityPolicyViolationEvent; createEvent(eventInterface: "ServiceWorkerMessageEvent"): ServiceWorkerMessageEvent; + createEvent(eventInterface: "SpeechRecognitionErrorEvent"): SpeechRecognitionErrorEvent; createEvent(eventInterface: "SpeechRecognitionEvent"): SpeechRecognitionEvent; createEvent(eventInterface: "SpeechSynthesisErrorEvent"): SpeechSynthesisErrorEvent; createEvent(eventInterface: "SpeechSynthesisEvent"): SpeechSynthesisEvent; @@ -4949,7 +5030,7 @@ interface DocumentEvent { /** A minimal document object that has no parent. It is used as a lightweight version of Document that stores a segment of a document structure comprised of nodes just like a standard document. The key difference is that because the document fragment isn't part of the active document tree structure, changes made to the fragment don't affect the document, cause reflow, or incur any performance impact that can occur when changes are made. */ interface DocumentFragment extends Node, NonElementParentNode, ParentNode { readonly ownerDocument: Document; - getElementById(elementId: string): HTMLElement | null; + getElementById(elementId: string): E | null; } declare var DocumentFragment: { @@ -5057,7 +5138,6 @@ interface ElementEventMap { /** Element is the most general base class from which all objects in a Document inherit. It only has methods and properties common to all kinds of elements. More specific classes inherit from Element. */ interface Element extends Node, Animatable, ChildNode, InnerHTML, NonDocumentTypeChildNode, ParentNode, Slottable { - readonly assignedSlot: HTMLSlotElement | null; readonly attributes: NamedNodeMap; /** * Allows for manipulation of element's class content attribute as a set of whitespace-separated tokens through a DOMTokenList object. @@ -5136,7 +5216,7 @@ interface Element extends Node, Animatable, ChildNode, InnerHTML, NonDocumentTyp /** * Returns a HTMLCollection of the elements in the object on which the method was invoked (a document or an element) that have all the classes given by classNames. The classNames argument is interpreted as a space-separated list of classes. */ - getElementsByClassName(classNames: string): HTMLCollectionOf; + getElementsByClassName(classNames: string): HTMLCollectionOf; getElementsByTagName(qualifiedName: K): HTMLCollectionOf; getElementsByTagName(qualifiedName: K): HTMLCollectionOf; getElementsByTagName(qualifiedName: string): HTMLCollectionOf; @@ -5612,24 +5692,7 @@ declare var GamepadPose: { }; interface GenericTransformStream { - /** - * Returns a readable stream whose chunks are strings resulting from running encoding's decoder on the chunks written to writable. - */ readonly readable: ReadableStream; - /** - * Returns a writable stream which accepts [AllowShared] BufferSource chunks and runs them through encoding's decoder before making them available to readable. - * - * Typically this will be used via the pipeThrough() method on a ReadableStream source. - * - * ``` - * var decoder = new TextDecoderStream(encoding); - * byteReadable - * .pipeThrough(decoder) - * .pipeTo(textWritable); - * ``` - * - * If the error mode is "fatal" and encoding's decoder returns error, both readable and writable will be errored with a TypeError. - */ readonly writable: WritableStream; } @@ -5693,6 +5756,7 @@ interface GlobalEventHandlersEventMap { "animationiteration": AnimationEvent; "animationstart": AnimationEvent; "auxclick": MouseEvent; + "beforeinput": InputEvent; "blur": FocusEvent; "cancel": Event; "canplay": Event; @@ -5700,6 +5764,9 @@ interface GlobalEventHandlersEventMap { "change": Event; "click": MouseEvent; "close": Event; + "compositionend": CompositionEvent; + "compositionstart": CompositionEvent; + "compositionupdate": CompositionEvent; "contextmenu": MouseEvent; "cuechange": Event; "dblclick": MouseEvent; @@ -6604,6 +6671,7 @@ interface HTMLElement extends Element, DocumentAndElementEventHandlers, ElementC readonly offsetParent: Element | null; readonly offsetTop: number; readonly offsetWidth: number; + readonly parentElement: HTMLElement | null; spellcheck: boolean; title: string; translate: boolean; @@ -7339,7 +7407,7 @@ interface HTMLInputElement extends HTMLElement { * @param end The offset into the text field for the end of the selection. * @param direction The direction in which the selection is performed. */ - setSelectionRange(start: number, end: number, direction?: "forward" | "backward" | "none"): void; + setSelectionRange(start: number | null, end: number | null, direction?: "forward" | "backward" | "none"): void; /** * Decrements a range input control's value by the value given by the Step attribute. If the optional parameter is used, it will decrement the input control's step value multiplied by the parameter's value. * @param n Value to decrement the value by. @@ -8635,7 +8703,6 @@ declare var HTMLTableElement: { }; interface HTMLTableHeaderCellElement extends HTMLTableCellElement { - scope: string; addEventListener(type: K, listener: (this: HTMLTableHeaderCellElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: K, listener: (this: HTMLTableHeaderCellElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; @@ -8846,7 +8913,7 @@ interface HTMLTextAreaElement extends HTMLElement { * @param end The offset into the text field for the end of the selection. * @param direction The direction in which the selection is performed. */ - setSelectionRange(start: number, end: number, direction?: "forward" | "backward" | "none"): void; + setSelectionRange(start: number | null, end: number | null, direction?: "forward" | "backward" | "none"): void; addEventListener(type: K, listener: (this: HTMLTextAreaElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: K, listener: (this: HTMLTextAreaElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; @@ -9613,7 +9680,7 @@ declare var InputEvent: { /** provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport. */ interface IntersectionObserver { - readonly root: Element | null; + readonly root: Element | Document | null; readonly rootMargin: string; readonly thresholds: ReadonlyArray; disconnect(): void; @@ -10552,7 +10619,8 @@ declare var MouseEvent: { new(type: string, eventInitDict?: MouseEventInit): MouseEvent; }; -/** Provides event properties that are specific to modifications to the Document Object Model (DOM) hierarchy and nodes. */ +/** Provides event properties that are specific to modifications to the Document Object Model (DOM) hierarchy and nodes. + * @deprecated DOM4 [DOM] provides a new mechanism using a MutationObserver interface which addresses the use cases that mutation events solve, but in a more performant manner. Thus, this specification describes mutation events for reference and completeness of legacy behavior, but deprecates the use of the MutationEvent interface. */ interface MutationEvent extends Event { readonly attrChange: number; readonly attrName: string; @@ -10797,7 +10865,7 @@ interface Node extends EventTarget { /** * Returns the parent element. */ - readonly parentElement: HTMLElement | null; + readonly parentElement: Element | null; /** * Returns the parent. */ @@ -11030,7 +11098,6 @@ declare var NodeList: { }; interface NodeListOf extends NodeList { - length: number; item(index: number): TNode; /** * Performs the specified action for each node in an list. @@ -11056,7 +11123,7 @@ interface NonElementParentNode { /** * Returns the first element within node's descendants whose ID is elementId. */ - getElementById(elementId: string): Element | null; + getElementById(elementId: string): E | null; } interface NotificationEventMap { @@ -11539,7 +11606,9 @@ declare var PerformanceMeasure: { new(): PerformanceMeasure; }; -/** The legacy PerformanceNavigation interface represents information about how the navigation to the current document was done. */ +/** The legacy PerformanceNavigation interface represents information about how the navigation to the current document was done. + * @deprecated This interface is deprecated in the Navigation Timing Level 2 specification. Please use the PerformanceNavigationTiming interface instead. + */ interface PerformanceNavigation { readonly redirectCount: number; readonly type: number; @@ -11629,7 +11698,9 @@ declare var PerformanceResourceTiming: { new(): PerformanceResourceTiming; }; -/** A legacy interface kept for backwards compatibility and contains properties that offer performance timing information for various events which occur during the loading and use of the current page. You get a PerformanceTiming object describing your page using the window.performance.timing property. */ +/** A legacy interface kept for backwards compatibility and contains properties that offer performance timing information for various events which occur during the loading and use of the current page. You get a PerformanceTiming object describing your page using the window.performance.timing property. + * @deprecated This interface is deprecated in the Navigation Timing Level 2 specification. Please use the PerformanceNavigationTiming interface instead. + */ interface PerformanceTiming { readonly connectEnd: number; readonly connectStart: number; @@ -11772,6 +11843,8 @@ interface PointerEvent extends MouseEvent { readonly tiltY: number; readonly twist: number; readonly width: number; + getCoalescedEvents(): PointerEvent[]; + getPredictedEvents(): PointerEvent[]; } declare var PointerEvent: { @@ -12475,64 +12548,26 @@ declare var Range: { toString(): string; }; -interface ReadableByteStreamController { - readonly byobRequest: ReadableStreamBYOBRequest | undefined; - readonly desiredSize: number | null; - close(): void; - enqueue(chunk: ArrayBufferView): void; - error(error?: any): void; -} - -declare var ReadableByteStreamController: { - prototype: ReadableByteStreamController; - new(): ReadableByteStreamController; -}; - /** This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. */ interface ReadableStream { readonly locked: boolean; cancel(reason?: any): Promise; - getReader(options: { mode: "byob" }): ReadableStreamBYOBReader; getReader(): ReadableStreamDefaultReader; - pipeThrough({ writable, readable }: { writable: WritableStream, readable: ReadableStream }, options?: PipeOptions): ReadableStream; - pipeTo(dest: WritableStream, options?: PipeOptions): Promise; + pipeThrough(transform: ReadableWritablePair, options?: StreamPipeOptions): ReadableStream; + pipeTo(dest: WritableStream, options?: StreamPipeOptions): Promise; tee(): [ReadableStream, ReadableStream]; } declare var ReadableStream: { prototype: ReadableStream; - new(underlyingSource: UnderlyingByteSource, strategy?: { highWaterMark?: number, size?: undefined }): ReadableStream; new(underlyingSource?: UnderlyingSource, strategy?: QueuingStrategy): ReadableStream; }; -interface ReadableStreamBYOBReader { - readonly closed: Promise; - cancel(reason?: any): Promise; - read(view: T): Promise>; - releaseLock(): void; -} - -declare var ReadableStreamBYOBReader: { - prototype: ReadableStreamBYOBReader; - new(): ReadableStreamBYOBReader; -}; - -interface ReadableStreamBYOBRequest { - readonly view: ArrayBufferView; - respond(bytesWritten: number): void; - respondWithNewView(view: ArrayBufferView): void; -} - -declare var ReadableStreamBYOBRequest: { - prototype: ReadableStreamBYOBRequest; - new(): ReadableStreamBYOBRequest; -}; - interface ReadableStreamDefaultController { readonly desiredSize: number | null; close(): void; enqueue(chunk: R): void; - error(error?: any): void; + error(e?: any): void; } declare var ReadableStreamDefaultController: { @@ -12540,29 +12575,21 @@ declare var ReadableStreamDefaultController: { new(): ReadableStreamDefaultController; }; -interface ReadableStreamDefaultReader { - readonly closed: Promise; - cancel(reason?: any): Promise; - read(): Promise>; +interface ReadableStreamDefaultReader extends ReadableStreamGenericReader { + read(): Promise>; releaseLock(): void; } declare var ReadableStreamDefaultReader: { prototype: ReadableStreamDefaultReader; - new(): ReadableStreamDefaultReader; + new(stream: ReadableStream): ReadableStreamDefaultReader; }; -interface ReadableStreamReader { - cancel(): Promise; - read(): Promise>; - releaseLock(): void; +interface ReadableStreamGenericReader { + readonly closed: Promise; + cancel(reason?: any): Promise; } -declare var ReadableStreamReader: { - prototype: ReadableStreamReader; - new(): ReadableStreamReader; -}; - /** This Fetch API interface represents a resource request. */ interface Request extends Body { /** @@ -12633,6 +12660,39 @@ declare var Request: { new(input: RequestInfo, init?: RequestInit): Request; }; +interface ResizeObserver { + disconnect(): void; + observe(target: Element, options?: ResizeObserverOptions): void; + unobserve(target: Element): void; +} + +declare var ResizeObserver: { + prototype: ResizeObserver; + new(callback: ResizeObserverCallback): ResizeObserver; +}; + +interface ResizeObserverEntry { + readonly borderBoxSize: ReadonlyArray; + readonly contentBoxSize: ReadonlyArray; + readonly contentRect: DOMRectReadOnly; + readonly target: Element; +} + +declare var ResizeObserverEntry: { + prototype: ResizeObserverEntry; + new(): ResizeObserverEntry; +}; + +interface ResizeObserverSize { + readonly blockSize: number; + readonly inlineSize: number; +} + +declare var ResizeObserverSize: { + prototype: ResizeObserverSize; + new(): ResizeObserverSize; +}; + /** This Fetch API interface represents the response to a request. */ interface Response extends Body { readonly headers: Headers; @@ -12992,7 +13052,9 @@ interface SVGElement extends Element, DocumentAndElementEventHandlers, DocumentA /** @deprecated */ readonly className: any; readonly ownerSVGElement: SVGSVGElement | null; + readonly parentElement: SVGElement | null; readonly viewportElement: SVGElement | null; + getElementsByClassName(classNames: string): HTMLCollectionOf; addEventListener(type: K, listener: (this: SVGElement, ev: SVGElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: K, listener: (this: SVGElement, ev: SVGElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; @@ -13580,6 +13642,7 @@ interface SVGForeignObjectElement extends SVGGraphicsElement { readonly width: SVGAnimatedLength; readonly x: SVGAnimatedLength; readonly y: SVGAnimatedLength; + getElementsByClassName(classNames: string): HTMLCollectionOf; addEventListener(type: K, listener: (this: SVGForeignObjectElement, ev: SVGElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: K, listener: (this: SVGForeignObjectElement, ev: SVGElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; @@ -14383,7 +14446,7 @@ interface SVGSVGElement extends SVGGraphicsElement, DocumentEvent, SVGFitToViewB forceRedraw(): void; getComputedStyle(elt: Element, pseudoElt?: string | null): CSSStyleDeclaration; getCurrentTime(): number; - getElementById(elementId: string): Element; + getElementById(elementId: string): E | null; getEnclosureList(rect: SVGRect, referenceElement: SVGElement | null): NodeListOf; getIntersectionList(rect: SVGRect, referenceElement: SVGElement | null): NodeListOf; pauseAnimations(): void; @@ -14457,7 +14520,7 @@ declare var SVGStringList: { }; /** Corresponds to the SVG