Merge branch 'main' into no-objectAllocator

This commit is contained in:
Ron Buckton
2024-04-17 18:27:41 -04:00
6224 changed files with 446345 additions and 154143 deletions
@@ -17,10 +17,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
token: ${{ secrets.TS_BOT_GITHUB_TOKEN }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 'lts/*'
- name: Configure Git, Run Tests, Update Baselines, Apply Fixes
run: |
+12 -12
View File
@@ -48,7 +48,7 @@ jobs:
name: Test Node ${{ matrix.node-version }} on ${{ matrix.os }}${{ (!matrix.bundle && ' with --no-bundle') || '' }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Use node version ${{ matrix.node-version }}
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
@@ -72,7 +72,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '*'
@@ -86,14 +86,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '*'
check-latest: true
- run: npm ci
- uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: ~/.cache/dprint
key: ${{ runner.os }}-dprint-${{ hashFiles('package-lock.json', '.dprint.jsonc') }}
@@ -107,7 +107,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '*'
@@ -124,7 +124,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '*'
@@ -138,7 +138,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
@@ -179,11 +179,11 @@ jobs:
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
path: pr
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
path: base
ref: ${{ github.base_ref }}
@@ -221,7 +221,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '*'
@@ -238,7 +238,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '*'
@@ -258,7 +258,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '*'
+4 -4
View File
@@ -42,11 +42,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
uses: github/codeql-action/init@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10
with:
config-file: ./.github/codeql/codeql-configuration.yml
# Override language selection by uncommenting this and choosing your languages
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below).
- name: Autobuild
uses: github/codeql-action/autobuild@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
uses: github/codeql-action/autobuild@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -70,4 +70,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
uses: github/codeql-action/analyze@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
if: github.repository == 'microsoft/TypeScript'
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
filter: blob:none # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/
fetch-depth: 0 # Default is 1; need to set to 0 to get the benefits of blob:none.
+5 -2
View File
@@ -20,8 +20,10 @@ jobs:
if: github.repository == 'microsoft/TypeScript'
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 'lts/*'
- run: |
npm --version
# corepack enable npm
@@ -40,9 +42,10 @@ jobs:
if: github.repository == 'microsoft/TypeScript'
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 'lts/*'
# Use NODE_AUTH_TOKEN environment variable to authenticate to this registry.
registry-url: https://registry.npmjs.org/
- run: |
+3 -1
View File
@@ -27,11 +27,13 @@ jobs:
exit 1
fi
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
ref: ${{ inputs.branch_name }}
token: ${{ secrets.TS_BOT_GITHUB_TOKEN }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 'lts/*'
- run: |
npm --version
# corepack enable npm
+3 -1
View File
@@ -50,12 +50,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
filter: blob:none # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/
fetch-depth: 0 # Default is 1; need to set to 0 to get the benefits of blob:none.
token: ${{ secrets.TS_BOT_GITHUB_TOKEN }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 'lts/*'
- run: |
npm --version
# corepack enable npm
+5 -2
View File
@@ -21,8 +21,10 @@ jobs:
if: github.repository == 'microsoft/TypeScript'
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 'lts/*'
- run: |
npm --version
# corepack enable npm
@@ -40,9 +42,10 @@ jobs:
if: github.repository == 'microsoft/TypeScript'
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 'lts/*'
# Use NODE_AUTH_TOKEN environment variable to authenticate to this registry.
registry-url: https://registry.npmjs.org/
- run: |
@@ -19,8 +19,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 'lts/*'
- run: |
npm --version
# corepack enable npm
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
steps:
- name: 'Checkout code'
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
persist-credentials: false
@@ -55,6 +55,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
uses: github/codeql-action/upload-sarif@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10
with:
sarif_file: results.sarif
+3 -1
View File
@@ -49,11 +49,13 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
ref: ${{ inputs.branch_name }}
token: ${{ secrets.TS_BOT_GITHUB_TOKEN }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 'lts/*'
- run: |
npm --version
# corepack enable npm
+3 -1
View File
@@ -43,7 +43,9 @@ jobs:
steps:
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
node-version: 'lts/*'
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
ref: ${{ inputs.branch_name }}
filter: blob:none # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
- name: Get repo name
run: R=${GITHUB_REPOSITORY%?wiki}; echo "BASENAME=${R##*/}" >> $GITHUB_ENV
- name: Checkout ${{ env.BASENAME }}-wiki
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
repository: '${{ GITHUB.repository_owner }}/${{ env.BASENAME }}-wiki'
token: ${{ secrets.TS_BOT_GITHUB_TOKEN }}
+4 -2
View File
@@ -49,13 +49,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- if: ${{ github.event.inputs.bisect }}
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
filter: blob:none # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/
fetch-depth: 0 # Default is 1; need to set to 0 to get the benefits of blob:none.
- if: ${{ !github.event.inputs.bisect }}
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 'lts/*'
- uses: microsoft/TypeScript-Twoslash-Repro-Action@8680b5b290d48a7badbc7ba65971d526c61b86b8 # master
with:
github-token: ${{ secrets.TS_BOT_GITHUB_TOKEN }}
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
if: github.repository == 'microsoft/TypeScript'
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
token: ${{ secrets.TS_BOT_GITHUB_TOKEN }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+277 -213
View File
@@ -813,16 +813,16 @@
}
},
"node_modules/@octokit/core": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.1.0.tgz",
"integrity": "sha512-BDa2VAMLSh3otEiaMJ/3Y36GU4qf6GI+VivQ/P41NC6GHcdxpKlqV0ikSZ5gdQsmS3ojXeRx5vasgNTinF0Q4g==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz",
"integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==",
"dev": true,
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.0.0",
"@octokit/request": "^8.0.2",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"@octokit/graphql": "^7.1.0",
"@octokit/request": "^8.3.1",
"@octokit/request-error": "^5.1.0",
"@octokit/types": "^13.0.0",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
},
@@ -831,12 +831,12 @@
}
},
"node_modules/@octokit/endpoint": {
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz",
"integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==",
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz",
"integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==",
"dev": true,
"dependencies": {
"@octokit/types": "^12.0.0",
"@octokit/types": "^13.1.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
@@ -844,13 +844,13 @@
}
},
"node_modules/@octokit/graphql": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz",
"integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz",
"integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==",
"dev": true,
"dependencies": {
"@octokit/request": "^8.0.1",
"@octokit/types": "^12.0.0",
"@octokit/request": "^8.3.0",
"@octokit/types": "^13.0.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
@@ -858,9 +858,9 @@
}
},
"node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
"version": "22.1.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.1.0.tgz",
"integrity": "sha512-pGUdSP+eEPfZiQHNkZI0U01HLipxncisdJQB4G//OAmfeO8sqTQ9KRa0KF03TUPCziNsoXUrTg4B2Q1EX++T0Q==",
"dev": true
},
"node_modules/@octokit/plugin-paginate-rest": {
@@ -878,6 +878,21 @@
"@octokit/core": "5"
}
},
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
"dev": true
},
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"dev": true,
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
}
},
"node_modules/@octokit/plugin-request-log": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz",
@@ -905,15 +920,30 @@
"@octokit/core": "5"
}
},
"node_modules/@octokit/request": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.2.0.tgz",
"integrity": "sha512-exPif6x5uwLqv1N1irkLG1zZNJkOtj8bZxuVHd71U5Ftuxf2wGNvAJyNBcPbPC+EBzwYEbBDdSFb8EPcjpYxPQ==",
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
"dev": true
},
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"dev": true,
"dependencies": {
"@octokit/endpoint": "^9.0.0",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"@octokit/openapi-types": "^20.0.0"
}
},
"node_modules/@octokit/request": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz",
"integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==",
"dev": true,
"dependencies": {
"@octokit/endpoint": "^9.0.1",
"@octokit/request-error": "^5.1.0",
"@octokit/types": "^13.1.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
@@ -921,12 +951,12 @@
}
},
"node_modules/@octokit/request-error": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz",
"integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz",
"integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==",
"dev": true,
"dependencies": {
"@octokit/types": "^12.0.0",
"@octokit/types": "^13.1.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
},
@@ -950,12 +980,12 @@
}
},
"node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"version": "13.4.1",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.4.1.tgz",
"integrity": "sha512-Y73oOAzRBAUzR/iRAbGULzpNkX8vaxKCqEtg6K74Ff3w9f5apFnWtE/2nade7dMWWW3bS5Kkd6DJS4HF04xreg==",
"dev": true,
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
"@octokit/openapi-types": "^22.1.0"
}
},
"node_modules/@pkgjs/parseargs": {
@@ -1023,9 +1053,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "20.12.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz",
"integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==",
"version": "20.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
@@ -1053,22 +1083,22 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz",
"integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.0.tgz",
"integrity": "sha512-GJWR0YnfrKnsRoluVO3PRb9r5aMZriiMMM/RHj5nnTrBy1/wIgk76XCtCKcnXGjpZQJQRFtGV9/0JJ6n30uwpQ==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "7.5.0",
"@typescript-eslint/type-utils": "7.5.0",
"@typescript-eslint/utils": "7.5.0",
"@typescript-eslint/visitor-keys": "7.5.0",
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "7.7.0",
"@typescript-eslint/type-utils": "7.7.0",
"@typescript-eslint/utils": "7.7.0",
"@typescript-eslint/visitor-keys": "7.7.0",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
"semver": "^7.6.0",
"ts-api-utils": "^1.3.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -1088,15 +1118,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz",
"integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.0.tgz",
"integrity": "sha512-fNcDm3wSwVM8QYL4HKVBggdIPAy9Q41vcvC/GtDobw3c4ndVT3K6cqudUmjHPw8EAp4ufax0o58/xvWaP2FmTg==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "7.5.0",
"@typescript-eslint/types": "7.5.0",
"@typescript-eslint/typescript-estree": "7.5.0",
"@typescript-eslint/visitor-keys": "7.5.0",
"@typescript-eslint/scope-manager": "7.7.0",
"@typescript-eslint/types": "7.7.0",
"@typescript-eslint/typescript-estree": "7.7.0",
"@typescript-eslint/visitor-keys": "7.7.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1116,13 +1146,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz",
"integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.0.tgz",
"integrity": "sha512-/8INDn0YLInbe9Wt7dK4cXLDYp0fNHP5xKLHvZl3mOT5X17rK/YShXaiNmorl+/U4VKCVIjJnx4Ri5b0y+HClw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.5.0",
"@typescript-eslint/visitor-keys": "7.5.0"
"@typescript-eslint/types": "7.7.0",
"@typescript-eslint/visitor-keys": "7.7.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -1133,15 +1163,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz",
"integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.0.tgz",
"integrity": "sha512-bOp3ejoRYrhAlnT/bozNQi3nio9tIgv3U5C0mVDdZC7cpcQEDZXvq8inrHYghLVwuNABRqrMW5tzAv88Vy77Sg==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "7.5.0",
"@typescript-eslint/utils": "7.5.0",
"@typescript-eslint/typescript-estree": "7.7.0",
"@typescript-eslint/utils": "7.7.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
"ts-api-utils": "^1.3.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -1160,9 +1190,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz",
"integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.0.tgz",
"integrity": "sha512-G01YPZ1Bd2hn+KPpIbrAhEWOn5lQBrjxkzHkWvP6NucMXFtfXoevK82hzQdpfuQYuhkvFDeQYbzXCjR1z9Z03w==",
"dev": true,
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -1173,19 +1203,19 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz",
"integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.0.tgz",
"integrity": "sha512-8p71HQPE6CbxIBy2kWHqM1KGrC07pk6RJn40n0DSc6bMOBBREZxSDJ+BmRzc8B5OdaMh1ty3mkuWRg4sCFiDQQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.5.0",
"@typescript-eslint/visitor-keys": "7.5.0",
"@typescript-eslint/types": "7.7.0",
"@typescript-eslint/visitor-keys": "7.7.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"minimatch": "9.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^1.3.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -1201,18 +1231,18 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz",
"integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.0.tgz",
"integrity": "sha512-LKGAXMPQs8U/zMRFXDZOzmMKgFv3COlxUQ+2NMPhbqgVm6R1w+nU1i4836Pmxu9jZAuIeyySNrN/6Rc657ggig==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "7.5.0",
"@typescript-eslint/types": "7.5.0",
"@typescript-eslint/typescript-estree": "7.5.0",
"semver": "^7.5.4"
"@types/json-schema": "^7.0.15",
"@types/semver": "^7.5.8",
"@typescript-eslint/scope-manager": "7.7.0",
"@typescript-eslint/types": "7.7.0",
"@typescript-eslint/typescript-estree": "7.7.0",
"semver": "^7.6.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -1226,13 +1256,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz",
"integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.0.tgz",
"integrity": "sha512-h0WHOj8MhdhY8YWkzIF30R379y0NqyOHExI9N9KCzvmu05EgG4FumeYa3ccfKUSphyWkWQE1ybVrgz/Pbam6YA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.5.0",
"eslint-visitor-keys": "^3.4.1"
"@typescript-eslint/types": "7.7.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -3026,9 +3056,9 @@
}
},
"node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
"integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -3483,12 +3513,12 @@
}
},
"node_modules/playwright": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.0.tgz",
"integrity": "sha512-SiOKHbVjTSf6wHuGCbqrEyzlm6qvXcv7mENP+OZon1I07brfZLGdfWV0l/efAzVx7TF3Z45ov1gPEkku9q25YQ==",
"version": "1.43.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz",
"integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==",
"dev": true,
"dependencies": {
"playwright-core": "1.43.0"
"playwright-core": "1.43.1"
},
"bin": {
"playwright": "cli.js"
@@ -3501,9 +3531,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz",
"integrity": "sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==",
"version": "1.43.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz",
"integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
@@ -3575,9 +3605,9 @@
}
},
"node_modules/qs": {
"version": "6.12.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz",
"integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==",
"version": "6.12.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
"integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
"dev": true,
"dependencies": {
"side-channel": "^1.0.6"
@@ -4180,9 +4210,9 @@
}
},
"node_modules/typescript": {
"version": "5.4.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz",
"integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==",
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -4919,45 +4949,45 @@
"dev": true
},
"@octokit/core": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.1.0.tgz",
"integrity": "sha512-BDa2VAMLSh3otEiaMJ/3Y36GU4qf6GI+VivQ/P41NC6GHcdxpKlqV0ikSZ5gdQsmS3ojXeRx5vasgNTinF0Q4g==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz",
"integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==",
"dev": true,
"requires": {
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.0.0",
"@octokit/request": "^8.0.2",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"@octokit/graphql": "^7.1.0",
"@octokit/request": "^8.3.1",
"@octokit/request-error": "^5.1.0",
"@octokit/types": "^13.0.0",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/endpoint": {
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz",
"integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==",
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz",
"integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==",
"dev": true,
"requires": {
"@octokit/types": "^12.0.0",
"@octokit/types": "^13.1.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/graphql": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz",
"integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz",
"integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==",
"dev": true,
"requires": {
"@octokit/request": "^8.0.1",
"@octokit/types": "^12.0.0",
"@octokit/request": "^8.3.0",
"@octokit/types": "^13.0.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
"version": "22.1.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.1.0.tgz",
"integrity": "sha512-pGUdSP+eEPfZiQHNkZI0U01HLipxncisdJQB4G//OAmfeO8sqTQ9KRa0KF03TUPCziNsoXUrTg4B2Q1EX++T0Q==",
"dev": true
},
"@octokit/plugin-paginate-rest": {
@@ -4967,6 +4997,23 @@
"dev": true,
"requires": {
"@octokit/types": "^12.6.0"
},
"dependencies": {
"@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
"dev": true
},
"@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"dev": true,
"requires": {
"@octokit/openapi-types": "^20.0.0"
}
}
}
},
"@octokit/plugin-request-log": {
@@ -4983,27 +5030,44 @@
"dev": true,
"requires": {
"@octokit/types": "^12.6.0"
},
"dependencies": {
"@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
"dev": true
},
"@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"dev": true,
"requires": {
"@octokit/openapi-types": "^20.0.0"
}
}
}
},
"@octokit/request": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.2.0.tgz",
"integrity": "sha512-exPif6x5uwLqv1N1irkLG1zZNJkOtj8bZxuVHd71U5Ftuxf2wGNvAJyNBcPbPC+EBzwYEbBDdSFb8EPcjpYxPQ==",
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz",
"integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==",
"dev": true,
"requires": {
"@octokit/endpoint": "^9.0.0",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"@octokit/endpoint": "^9.0.1",
"@octokit/request-error": "^5.1.0",
"@octokit/types": "^13.1.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/request-error": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz",
"integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz",
"integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==",
"dev": true,
"requires": {
"@octokit/types": "^12.0.0",
"@octokit/types": "^13.1.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
@@ -5021,12 +5085,12 @@
}
},
"@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"version": "13.4.1",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.4.1.tgz",
"integrity": "sha512-Y73oOAzRBAUzR/iRAbGULzpNkX8vaxKCqEtg6K74Ff3w9f5apFnWtE/2nade7dMWWW3bS5Kkd6DJS4HF04xreg==",
"dev": true,
"requires": {
"@octokit/openapi-types": "^20.0.0"
"@octokit/openapi-types": "^22.1.0"
}
},
"@pkgjs/parseargs": {
@@ -5088,9 +5152,9 @@
"dev": true
},
"@types/node": {
"version": "20.12.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz",
"integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==",
"version": "20.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
"dev": true,
"requires": {
"undici-types": "~5.26.4"
@@ -5118,104 +5182,104 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz",
"integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.0.tgz",
"integrity": "sha512-GJWR0YnfrKnsRoluVO3PRb9r5aMZriiMMM/RHj5nnTrBy1/wIgk76XCtCKcnXGjpZQJQRFtGV9/0JJ6n30uwpQ==",
"dev": true,
"requires": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "7.5.0",
"@typescript-eslint/type-utils": "7.5.0",
"@typescript-eslint/utils": "7.5.0",
"@typescript-eslint/visitor-keys": "7.5.0",
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "7.7.0",
"@typescript-eslint/type-utils": "7.7.0",
"@typescript-eslint/utils": "7.7.0",
"@typescript-eslint/visitor-keys": "7.7.0",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
"semver": "^7.6.0",
"ts-api-utils": "^1.3.0"
}
},
"@typescript-eslint/parser": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz",
"integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.0.tgz",
"integrity": "sha512-fNcDm3wSwVM8QYL4HKVBggdIPAy9Q41vcvC/GtDobw3c4ndVT3K6cqudUmjHPw8EAp4ufax0o58/xvWaP2FmTg==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "7.5.0",
"@typescript-eslint/types": "7.5.0",
"@typescript-eslint/typescript-estree": "7.5.0",
"@typescript-eslint/visitor-keys": "7.5.0",
"@typescript-eslint/scope-manager": "7.7.0",
"@typescript-eslint/types": "7.7.0",
"@typescript-eslint/typescript-estree": "7.7.0",
"@typescript-eslint/visitor-keys": "7.7.0",
"debug": "^4.3.4"
}
},
"@typescript-eslint/scope-manager": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz",
"integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.0.tgz",
"integrity": "sha512-/8INDn0YLInbe9Wt7dK4cXLDYp0fNHP5xKLHvZl3mOT5X17rK/YShXaiNmorl+/U4VKCVIjJnx4Ri5b0y+HClw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "7.5.0",
"@typescript-eslint/visitor-keys": "7.5.0"
"@typescript-eslint/types": "7.7.0",
"@typescript-eslint/visitor-keys": "7.7.0"
}
},
"@typescript-eslint/type-utils": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz",
"integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.0.tgz",
"integrity": "sha512-bOp3ejoRYrhAlnT/bozNQi3nio9tIgv3U5C0mVDdZC7cpcQEDZXvq8inrHYghLVwuNABRqrMW5tzAv88Vy77Sg==",
"dev": true,
"requires": {
"@typescript-eslint/typescript-estree": "7.5.0",
"@typescript-eslint/utils": "7.5.0",
"@typescript-eslint/typescript-estree": "7.7.0",
"@typescript-eslint/utils": "7.7.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
"ts-api-utils": "^1.3.0"
}
},
"@typescript-eslint/types": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz",
"integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.0.tgz",
"integrity": "sha512-G01YPZ1Bd2hn+KPpIbrAhEWOn5lQBrjxkzHkWvP6NucMXFtfXoevK82hzQdpfuQYuhkvFDeQYbzXCjR1z9Z03w==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz",
"integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.0.tgz",
"integrity": "sha512-8p71HQPE6CbxIBy2kWHqM1KGrC07pk6RJn40n0DSc6bMOBBREZxSDJ+BmRzc8B5OdaMh1ty3mkuWRg4sCFiDQQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "7.5.0",
"@typescript-eslint/visitor-keys": "7.5.0",
"@typescript-eslint/types": "7.7.0",
"@typescript-eslint/visitor-keys": "7.7.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"minimatch": "9.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^1.3.0"
}
},
"@typescript-eslint/utils": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz",
"integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.0.tgz",
"integrity": "sha512-LKGAXMPQs8U/zMRFXDZOzmMKgFv3COlxUQ+2NMPhbqgVm6R1w+nU1i4836Pmxu9jZAuIeyySNrN/6Rc657ggig==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "7.5.0",
"@typescript-eslint/types": "7.5.0",
"@typescript-eslint/typescript-estree": "7.5.0",
"semver": "^7.5.4"
"@types/json-schema": "^7.0.15",
"@types/semver": "^7.5.8",
"@typescript-eslint/scope-manager": "7.7.0",
"@typescript-eslint/types": "7.7.0",
"@typescript-eslint/typescript-estree": "7.7.0",
"semver": "^7.6.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz",
"integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.0.tgz",
"integrity": "sha512-h0WHOj8MhdhY8YWkzIF30R379y0NqyOHExI9N9KCzvmu05EgG4FumeYa3ccfKUSphyWkWQE1ybVrgz/Pbam6YA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "7.5.0",
"eslint-visitor-keys": "^3.4.1"
"@typescript-eslint/types": "7.7.0",
"eslint-visitor-keys": "^3.4.3"
}
},
"@ungap/structured-clone": {
@@ -6524,9 +6588,9 @@
}
},
"minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
"integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
"dev": true,
"requires": {
"brace-expansion": "^2.0.1"
@@ -6843,13 +6907,13 @@
"dev": true
},
"playwright": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.0.tgz",
"integrity": "sha512-SiOKHbVjTSf6wHuGCbqrEyzlm6qvXcv7mENP+OZon1I07brfZLGdfWV0l/efAzVx7TF3Z45ov1gPEkku9q25YQ==",
"version": "1.43.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz",
"integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==",
"dev": true,
"requires": {
"fsevents": "2.3.2",
"playwright-core": "1.43.0"
"playwright-core": "1.43.1"
},
"dependencies": {
"fsevents": {
@@ -6862,9 +6926,9 @@
}
},
"playwright-core": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz",
"integrity": "sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==",
"version": "1.43.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz",
"integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==",
"dev": true
},
"plur": {
@@ -6898,9 +6962,9 @@
"dev": true
},
"qs": {
"version": "6.12.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz",
"integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==",
"version": "6.12.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
"integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
"dev": true,
"requires": {
"side-channel": "^1.0.6"
@@ -7322,9 +7386,9 @@
}
},
"typescript": {
"version": "5.4.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz",
"integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==",
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"dev": true
},
"typical": {
+32 -3
View File
@@ -11,6 +11,7 @@ module.exports = createRule({
internalCommentNotLastError: `@internal should only appear in final JSDoc comment for declaration.`,
multipleJSDocError: `Declaration has multiple JSDoc comments.`,
internalCommentOnParameterProperty: `@internal cannot appear on a JSDoc comment; use a declared property and an assignment in the constructor instead.`,
internalCommentOnUnexported: `@internal should not appear on an unexported declaration.`,
},
schema: [],
type: "problem",
@@ -23,6 +24,31 @@ module.exports = createRule({
const atInternal = "@internal";
const jsdocStart = "/**";
/** @type {Map<import("@typescript-eslint/utils").TSESTree.Node, boolean>} */
const isExportedCache = new Map();
/** @type {(node: import("@typescript-eslint/utils").TSESTree.Node) => boolean} */
function isExported(node) {
const exported = isExportedCache.get(node);
if (exported !== undefined) {
return exported;
}
/** @type {import("@typescript-eslint/utils").TSESTree.Node | undefined} */
let current = node;
while (current) {
// https://github.com/typescript-eslint/typescript-eslint/blob/e44a1a280f08f9fd0d29f74e5c3e73b7b64a9606/packages/eslint-plugin/src/util/collectUnusedVariables.ts#L440
if (current.type.startsWith("Export")) {
isExportedCache.set(node, true);
return true;
}
isExportedCache.set(current, false);
current = current.parent;
}
return false;
}
/** @type {(text: string) => boolean} */
function isJSDocText(text) {
return text.startsWith(jsdocStart);
@@ -81,12 +107,15 @@ module.exports = createRule({
if (!isJSDoc) {
context.report({ messageId: "internalCommentInNonJSDocError", node: c, loc: getAtInternalLoc(c, indexInComment) });
}
else if (i !== last) {
context.report({ messageId: "internalCommentNotLastError", node: c, loc: getAtInternalLoc(c, indexInComment) });
}
else if (node.type === "TSParameterProperty") {
context.report({ messageId: "internalCommentOnParameterProperty", node: c, loc: getAtInternalLoc(c, indexInComment) });
}
else if (!isExported(node)) {
context.report({ messageId: "internalCommentOnUnexported", node: c, loc: getAtInternalLoc(c, indexInComment) });
}
else if (i !== last) {
context.report({ messageId: "internalCommentNotLastError", node: c, loc: getAtInternalLoc(c, indexInComment) });
}
}
};
+1 -1
View File
@@ -54,7 +54,7 @@ export * from "../transformers/generators";
export * from "../transformers/module/module";
export * from "../transformers/module/system";
export * from "../transformers/module/esnextAnd2015";
export * from "../transformers/module/node";
export * from "../transformers/module/impliedNodeFormatDependent";
export * from "../transformers/declarations/diagnostics";
export * from "../transformers/declarations";
export * from "../transformer";
+465 -857
View File
File diff suppressed because it is too large Load Diff
+143 -17
View File
@@ -53,6 +53,7 @@ import {
getFileMatcherPatterns,
getLocaleSpecificMessage,
getNormalizedAbsolutePath,
getOwnKeys,
getRegexFromPattern,
getRegularExpressionForWildcard,
getRegularExpressionsForWildcards,
@@ -313,6 +314,7 @@ export const optionsForWatch: CommandLineOption[] = [
isFilePath: true,
extraValidation: specToDiagnostic,
},
allowConfigDirTemplateSubstitution: true,
category: Diagnostics.Watch_and_Build_Modes,
description: Diagnostics.Remove_a_list_of_directories_from_the_watch_process,
},
@@ -325,6 +327,7 @@ export const optionsForWatch: CommandLineOption[] = [
isFilePath: true,
extraValidation: specToDiagnostic,
},
allowConfigDirTemplateSubstitution: true,
category: Diagnostics.Watch_and_Build_Modes,
description: Diagnostics.Remove_a_list_of_files_from_the_watch_mode_s_processing,
},
@@ -534,6 +537,7 @@ export const targetOptionDeclaration: CommandLineOptionOfCustomType = {
es2020: ScriptTarget.ES2020,
es2021: ScriptTarget.ES2021,
es2022: ScriptTarget.ES2022,
es2023: ScriptTarget.ES2023,
esnext: ScriptTarget.ESNext,
})),
affectsSourceFile: true,
@@ -1033,6 +1037,7 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
name: "paths",
type: "object",
affectsModuleResolution: true,
allowConfigDirTemplateSubstitution: true,
isTSConfigOnly: true,
category: Diagnostics.Modules,
description: Diagnostics.Specify_a_set_of_entries_that_re_map_imports_to_additional_lookup_locations,
@@ -1050,6 +1055,7 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
isFilePath: true,
},
affectsModuleResolution: true,
allowConfigDirTemplateSubstitution: true,
category: Diagnostics.Modules,
description: Diagnostics.Allow_multiple_folders_to_be_treated_as_one_when_resolving_modules,
transpileOptionValue: undefined,
@@ -1064,6 +1070,7 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
isFilePath: true,
},
affectsModuleResolution: true,
allowConfigDirTemplateSubstitution: true,
category: Diagnostics.Modules,
description: Diagnostics.Specify_multiple_folders_that_act_like_Slashnode_modules_Slash_types,
},
@@ -1599,6 +1606,15 @@ export const optionsAffectingProgramStructure: readonly CommandLineOption[] = op
/** @internal */
export const transpileOptionValueCompilerOptions: readonly CommandLineOption[] = optionDeclarations.filter(option => hasProperty(option, "transpileOptionValue"));
/** @internal */
export const configDirTemplateSubstitutionOptions: readonly CommandLineOption[] = optionDeclarations.filter(
option => option.allowConfigDirTemplateSubstitution || (!option.isCommandLineOnly && option.isFilePath),
);
/** @internal */
export const configDirTemplateSubstitutionWatchOptions: readonly CommandLineOption[] = optionsForWatch.filter(
option => option.allowConfigDirTemplateSubstitution || (!option.isCommandLineOnly && option.isFilePath),
);
// Build related options
/** @internal */
export const optionsForBuild: CommandLineOption[] = [
@@ -2627,6 +2643,9 @@ function serializeOptionBaseObject(
if (pathOptions && optionDefinition.isFilePath) {
result.set(name, getRelativePathFromFile(pathOptions.configFilePath, getNormalizedAbsolutePath(value as string, getDirectoryPath(pathOptions.configFilePath)), getCanonicalFileName!));
}
else if (pathOptions && optionDefinition.type === "list" && optionDefinition.element.isFilePath) {
result.set(name, (value as string[]).map(v => getRelativePathFromFile(pathOptions.configFilePath, getNormalizedAbsolutePath(v, getDirectoryPath(pathOptions.configFilePath)), getCanonicalFileName!)));
}
else {
result.set(name, value);
}
@@ -2889,17 +2908,23 @@ function parseJsonConfigFileContentWorker(
const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors, extendedConfigCache);
const { raw } = parsedConfig;
const options = extend(existingOptions, parsedConfig.options || {});
const watchOptions = existingWatchOptions && parsedConfig.watchOptions ?
extend(existingWatchOptions, parsedConfig.watchOptions) :
parsedConfig.watchOptions || existingWatchOptions;
const options = handleOptionConfigDirTemplateSubstitution(
extend(existingOptions, parsedConfig.options || {}),
configDirTemplateSubstitutionOptions,
basePath,
) as CompilerOptions;
const watchOptions = handleWatchOptionsConfigDirTemplateSubstitution(
existingWatchOptions && parsedConfig.watchOptions ?
extend(existingWatchOptions, parsedConfig.watchOptions) :
parsedConfig.watchOptions || existingWatchOptions,
basePath,
);
options.configFilePath = configFileName && normalizeSlashes(configFileName);
const basePathForFileNames = normalizePath(configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath);
const configFileSpecs = getConfigFileSpecs();
if (sourceFile) sourceFile.configFileSpecs = configFileSpecs;
setConfigFileInOptions(options, sourceFile);
const basePathForFileNames = normalizePath(configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath);
return {
options,
watchOptions,
@@ -2954,6 +2979,7 @@ function parseJsonConfigFileContentWorker(
includeSpecs = [defaultIncludeSpec];
isDefaultIncludeSpec = true;
}
let validatedIncludeSpecsBeforeSubstitution: readonly string[] | undefined, validatedExcludeSpecsBeforeSubstitution: readonly string[] | undefined;
let validatedIncludeSpecs: readonly string[] | undefined, validatedExcludeSpecs: readonly string[] | undefined;
// The exclude spec list is converted into a regular expression, which allows us to quickly
@@ -2961,20 +2987,37 @@ function parseJsonConfigFileContentWorker(
// file system.
if (includeSpecs) {
validatedIncludeSpecs = validateSpecs(includeSpecs, errors, /*disallowTrailingRecursion*/ true, sourceFile, "include");
validatedIncludeSpecsBeforeSubstitution = validateSpecs(includeSpecs, errors, /*disallowTrailingRecursion*/ true, sourceFile, "include");
validatedIncludeSpecs = getSubstitutedStringArrayWithConfigDirTemplate(
validatedIncludeSpecsBeforeSubstitution,
basePathForFileNames,
) || validatedIncludeSpecsBeforeSubstitution;
}
if (excludeSpecs) {
validatedExcludeSpecs = validateSpecs(excludeSpecs, errors, /*disallowTrailingRecursion*/ false, sourceFile, "exclude");
validatedExcludeSpecsBeforeSubstitution = validateSpecs(excludeSpecs, errors, /*disallowTrailingRecursion*/ false, sourceFile, "exclude");
validatedExcludeSpecs = getSubstitutedStringArrayWithConfigDirTemplate(
validatedExcludeSpecsBeforeSubstitution,
basePathForFileNames,
) || validatedExcludeSpecsBeforeSubstitution;
}
const validatedFilesSpecBeforeSubstitution = filter(filesSpecs, isString);
const validatedFilesSpec = getSubstitutedStringArrayWithConfigDirTemplate(
validatedFilesSpecBeforeSubstitution,
basePathForFileNames,
) || validatedFilesSpecBeforeSubstitution;
return {
filesSpecs,
includeSpecs,
excludeSpecs,
validatedFilesSpec: filter(filesSpecs, isString),
validatedFilesSpec,
validatedIncludeSpecs,
validatedExcludeSpecs,
validatedFilesSpecBeforeSubstitution,
validatedIncludeSpecsBeforeSubstitution,
validatedExcludeSpecsBeforeSubstitution,
pathPatterns: undefined, // Initialized on first use
isDefaultIncludeSpec,
};
@@ -3042,6 +3085,84 @@ function parseJsonConfigFileContentWorker(
}
}
/** @internal */
export function handleWatchOptionsConfigDirTemplateSubstitution(
watchOptions: WatchOptions | undefined,
basePath: string,
) {
return handleOptionConfigDirTemplateSubstitution(watchOptions, configDirTemplateSubstitutionWatchOptions, basePath) as WatchOptions | undefined;
}
function handleOptionConfigDirTemplateSubstitution(
options: OptionsBase | undefined,
optionDeclarations: readonly CommandLineOption[],
basePath: string,
) {
if (!options) return options;
let result: OptionsBase | undefined;
for (const option of optionDeclarations) {
if (options[option.name] !== undefined) {
const value = options[option.name];
switch (option.type) {
case "string":
Debug.assert(option.isFilePath);
if (startsWithConfigDirTemplate(value)) {
setOptionValue(option, getSubstitutedPathWithConfigDirTemplate(value, basePath));
}
break;
case "list":
Debug.assert(option.element.isFilePath);
const listResult = getSubstitutedStringArrayWithConfigDirTemplate(value as string[], basePath);
if (listResult) setOptionValue(option, listResult);
break;
case "object":
Debug.assert(option.name === "paths");
const objectResult = getSubstitutedMapLikeOfStringArrayWithConfigDirTemplate(value as MapLike<string[]>, basePath);
if (objectResult) setOptionValue(option, objectResult);
break;
default:
Debug.fail("option type not supported");
}
}
}
return result || options;
function setOptionValue(option: CommandLineOption, value: CompilerOptionsValue) {
(result ??= assign({}, options))[option.name] = value;
}
}
const configDirTemplate = `\${configDir}`;
function startsWithConfigDirTemplate(value: any): value is string {
return isString(value) && startsWith(value, configDirTemplate, /*ignoreCase*/ true);
}
function getSubstitutedPathWithConfigDirTemplate(value: string, basePath: string) {
return getNormalizedAbsolutePath(value.replace(configDirTemplate, "./"), basePath);
}
function getSubstitutedStringArrayWithConfigDirTemplate(list: readonly string[] | undefined, basePath: string) {
if (!list) return list;
let result: string[] | undefined;
list.forEach((element, index) => {
if (!startsWithConfigDirTemplate(element)) return;
(result ??= list.slice())[index] = getSubstitutedPathWithConfigDirTemplate(element, basePath);
});
return result;
}
function getSubstitutedMapLikeOfStringArrayWithConfigDirTemplate(mapLike: MapLike<string[]>, basePath: string) {
let result: MapLike<string[]> | undefined;
const ownKeys = getOwnKeys(mapLike);
ownKeys.forEach(key => {
if (!isArray(mapLike[key])) return;
const subStitution = getSubstitutedStringArrayWithConfigDirTemplate(mapLike[key], basePath);
if (!subStitution) return;
(result ??= assign({}, mapLike))[key] = subStitution;
});
return result;
}
function isErrorNoInputFiles(error: Diagnostic) {
return error.code === Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code;
}
@@ -3143,9 +3264,10 @@ function parseConfig(
else {
ownConfig.extendedConfigPath.forEach(extendedConfigPath => applyExtendedConfig(result, extendedConfigPath));
}
if (!ownConfig.raw.include && result.include) ownConfig.raw.include = result.include;
if (!ownConfig.raw.exclude && result.exclude) ownConfig.raw.exclude = result.exclude;
if (!ownConfig.raw.files && result.files) ownConfig.raw.files = result.files;
if (result.include) ownConfig.raw.include = result.include;
if (result.exclude) ownConfig.raw.exclude = result.exclude;
if (result.files) ownConfig.raw.files = result.files;
if (ownConfig.raw.compileOnSave === undefined && result.compileOnSave) ownConfig.raw.compileOnSave = result.compileOnSave;
if (sourceFile && result.extendedSourceFiles) sourceFile.extendedSourceFiles = arrayFrom(result.extendedSourceFiles.keys());
@@ -3162,12 +3284,15 @@ function parseConfig(
const extendsRaw = extendedConfig.raw;
let relativeDifference: string | undefined;
const setPropertyInResultIfNotUndefined = (propertyName: "include" | "exclude" | "files") => {
if (ownConfig.raw[propertyName]) return; // No need to calculate if already set in own config
if (extendsRaw[propertyName]) {
result[propertyName] = map(extendsRaw[propertyName], (path: string) =>
isRootedDiskPath(path) ? path : combinePaths(
relativeDifference ||= convertToRelativePath(getDirectoryPath(extendedConfigPath), basePath, createGetCanonicalFileName(host.useCaseSensitiveFileNames)),
path,
));
startsWithConfigDirTemplate(path) || isRootedDiskPath(path) ?
path :
combinePaths(
relativeDifference ||= convertToRelativePath(getDirectoryPath(extendedConfigPath), basePath, createGetCanonicalFileName(host.useCaseSensitiveFileNames)),
path,
));
}
};
setPropertyInResultIfNotUndefined("include");
@@ -3526,7 +3651,8 @@ export function convertJsonOption(
function normalizeNonListOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue {
if (option.isFilePath) {
value = getNormalizedAbsolutePath(value, basePath);
value = normalizeSlashes(value);
value = !startsWithConfigDirTemplate(value) ? getNormalizedAbsolutePath(value, basePath) : value;
if (value === "") {
value = ".";
}
+4
View File
@@ -967,6 +967,10 @@
"category": "Error",
"code": 1292
},
"ESM syntax is not allowed in a CommonJS module when 'module' is set to 'preserve'.": {
"category": "Error",
"code": 1293
},
"'with' statements are not allowed in an async function block.": {
"category": "Error",
+3
View File
@@ -130,6 +130,7 @@ import {
getEmitFlags,
getEmitHelpers,
getEmitModuleKind,
getEmitModuleResolutionKind,
getEmitScriptTarget,
getExternalModuleName,
getIdentifierTypeArguments,
@@ -800,6 +801,7 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
newLine: compilerOptions.newLine,
noEmitHelpers: compilerOptions.noEmitHelpers,
module: getEmitModuleKind(compilerOptions),
moduleResolution: getEmitModuleResolutionKind(compilerOptions),
target: getEmitScriptTarget(compilerOptions),
sourceMap: compilerOptions.sourceMap,
inlineSourceMap: compilerOptions.inlineSourceMap,
@@ -867,6 +869,7 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
newLine: compilerOptions.newLine,
noEmitHelpers: true,
module: compilerOptions.module,
moduleResolution: compilerOptions.moduleResolution,
target: compilerOptions.target,
sourceMap: !forceDtsEmit && compilerOptions.declarationMap,
inlineSourceMap: compilerOptions.inlineSourceMap,
+4 -4
View File
@@ -53,10 +53,12 @@ import {
getAllAccessorDeclarations,
getEmitFlags,
getEmitHelpers,
getEmitModuleFormatOfFileWorker,
getEmitModuleKind,
getESModuleInterop,
getExternalModuleName,
getExternalModuleNameFromPath,
getImpliedNodeFormatForEmitWorker,
getJSDocType,
getJSDocTypeTag,
getModifiers,
@@ -712,7 +714,7 @@ export function createExternalHelpersImportDeclarationIfNeeded(nodeFactory: Node
if (compilerOptions.importHelpers && isEffectiveExternalModule(sourceFile, compilerOptions)) {
let namedBindings: NamedImportBindings | undefined;
const moduleKind = getEmitModuleKind(compilerOptions);
if ((moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) || sourceFile.impliedNodeFormat === ModuleKind.ESNext) {
if ((moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) || getImpliedNodeFormatForEmitWorker(sourceFile, compilerOptions) === ModuleKind.ESNext) {
// use named imports
const helpers = getEmitHelpers(sourceFile);
if (helpers) {
@@ -769,10 +771,8 @@ export function getOrCreateExternalHelpersModuleNameIfNeeded(factory: NodeFactor
return externalHelpersModuleName;
}
const moduleKind = getEmitModuleKind(compilerOptions);
let create = (hasExportStarsToExportValues || (getESModuleInterop(compilerOptions) && hasImportStarOrImportDefault))
&& moduleKind !== ModuleKind.System
&& (moduleKind < ModuleKind.ES2015 || node.impliedNodeFormat === ModuleKind.CommonJS);
&& getEmitModuleFormatOfFileWorker(node, compilerOptions) < ModuleKind.System;
if (!create) {
const helpers = getEmitHelpers(node);
if (helpers) {
+6 -1
View File
@@ -2703,7 +2703,7 @@ function loadModuleFromImportsOrExports(extensions: Extensions, state: ModuleRes
const target = (lookupTable as { [idx: string]: unknown; })[moduleName];
return loadModuleFromTargetImportOrExport(target, /*subpath*/ "", /*pattern*/ false, moduleName);
}
const expandingKeys = sort(filter(getOwnKeys(lookupTable as MapLike<unknown>), k => k.includes("*") || endsWith(k, "/")), comparePatternKeys);
const expandingKeys = sort(filter(getOwnKeys(lookupTable as MapLike<unknown>), k => hasOneAsterisk(k) || endsWith(k, "/")), comparePatternKeys);
for (const potentialTarget of expandingKeys) {
if (state.features & NodeResolutionFeatures.ExportsPatternTrailers && matchesPatternWithTrailer(potentialTarget, moduleName)) {
const target = (lookupTable as { [idx: string]: unknown; })[potentialTarget];
@@ -2731,6 +2731,11 @@ function loadModuleFromImportsOrExports(extensions: Extensions, state: ModuleRes
}
}
function hasOneAsterisk(patternKey: string): boolean {
const firstStar = patternKey.indexOf("*");
return firstStar !== -1 && firstStar === patternKey.lastIndexOf("*");
}
/**
* Gets the self-recursive function specialized to retrieving the targeted import/export element for the given resolution configuration
*/
+56 -23
View File
@@ -32,12 +32,13 @@ import {
flatten,
forEach,
forEachAncestorDirectory,
FutureSourceFile,
getBaseFileName,
GetCanonicalFileName,
getConditions,
getDefaultResolutionModeForFileWorker,
getDirectoryPath,
getEmitModuleResolutionKind,
getModeForResolutionAtIndex,
getModuleNameStringLiteralAt,
getModuleSpecifierEndingPreference,
getNodeModulePathParts,
@@ -65,6 +66,7 @@ import {
isDeclarationFileName,
isExternalModuleAugmentation,
isExternalModuleNameRelative,
isFullSourceFile,
isMissingPackageJsonInfo,
isModuleBlock,
isModuleDeclaration,
@@ -134,14 +136,15 @@ export interface ModuleSpecifierPreferences {
/**
* @param syntaxImpliedNodeFormat Used when the import syntax implies ESM or CJS irrespective of the mode of the file.
*/
getAllowedEndingsInPreferredOrder(syntaxImpliedNodeFormat?: SourceFile["impliedNodeFormat"]): ModuleSpecifierEnding[];
getAllowedEndingsInPreferredOrder(syntaxImpliedNodeFormat?: ResolutionMode): ModuleSpecifierEnding[];
}
/** @internal */
export function getModuleSpecifierPreferences(
{ importModuleSpecifierPreference, importModuleSpecifierEnding }: UserPreferences,
host: Pick<ModuleSpecifierResolutionHost, "getDefaultResolutionModeForFile">,
compilerOptions: CompilerOptions,
importingSourceFile: SourceFile,
importingSourceFile: Pick<SourceFile, "fileName" | "impliedNodeFormat">,
oldImportSpecifier?: string,
): ModuleSpecifierPreferences {
const filePreferredEnding = getPreferredEnding();
@@ -154,8 +157,10 @@ export function getModuleSpecifierPreferences(
importModuleSpecifierPreference === "project-relative" ? RelativePreference.ExternalNonRelative :
RelativePreference.Shortest,
getAllowedEndingsInPreferredOrder: syntaxImpliedNodeFormat => {
const preferredEnding = syntaxImpliedNodeFormat !== importingSourceFile.impliedNodeFormat ? getPreferredEnding(syntaxImpliedNodeFormat) : filePreferredEnding;
if ((syntaxImpliedNodeFormat ?? importingSourceFile.impliedNodeFormat) === ModuleKind.ESNext) {
const impliedNodeFormat = getDefaultResolutionModeForFile(importingSourceFile, host, compilerOptions);
const preferredEnding = syntaxImpliedNodeFormat !== impliedNodeFormat ? getPreferredEnding(syntaxImpliedNodeFormat) : filePreferredEnding;
const moduleResolution = getEmitModuleResolutionKind(compilerOptions);
if ((syntaxImpliedNodeFormat ?? impliedNodeFormat) === ModuleKind.ESNext && ModuleResolutionKind.Node16 <= moduleResolution && moduleResolution <= ModuleResolutionKind.NodeNext) {
if (shouldAllowImportingTsExtension(compilerOptions, importingSourceFile.fileName)) {
return [ModuleSpecifierEnding.TsExtension, ModuleSpecifierEnding.JsExtension];
}
@@ -195,9 +200,9 @@ export function getModuleSpecifierPreferences(
}
return getModuleSpecifierEndingPreference(
importModuleSpecifierEnding,
resolutionMode ?? importingSourceFile.impliedNodeFormat,
resolutionMode ?? getDefaultResolutionModeForFile(importingSourceFile, host, compilerOptions),
compilerOptions,
importingSourceFile,
isFullSourceFile(importingSourceFile) ? importingSourceFile : undefined,
);
}
}
@@ -216,7 +221,7 @@ export function updateModuleSpecifier(
oldImportSpecifier: string,
options: ModuleSpecifierOptions = {},
): string | undefined {
const res = getModuleSpecifierWorker(compilerOptions, importingSourceFile, importingSourceFileName, toFileName, host, getModuleSpecifierPreferences({}, compilerOptions, importingSourceFile, oldImportSpecifier), {}, options);
const res = getModuleSpecifierWorker(compilerOptions, importingSourceFile, importingSourceFileName, toFileName, host, getModuleSpecifierPreferences({}, host, compilerOptions, importingSourceFile, oldImportSpecifier), {}, options);
if (res === oldImportSpecifier) return undefined;
return res;
}
@@ -230,19 +235,19 @@ export function updateModuleSpecifier(
/** @internal */
export function getModuleSpecifier(
compilerOptions: CompilerOptions,
importingSourceFile: SourceFile,
importingSourceFile: SourceFile | FutureSourceFile,
importingSourceFileName: string,
toFileName: string,
host: ModuleSpecifierResolutionHost,
options: ModuleSpecifierOptions = {},
): string {
return getModuleSpecifierWorker(compilerOptions, importingSourceFile, importingSourceFileName, toFileName, host, getModuleSpecifierPreferences({}, compilerOptions, importingSourceFile), {}, options);
return getModuleSpecifierWorker(compilerOptions, importingSourceFile, importingSourceFileName, toFileName, host, getModuleSpecifierPreferences({}, host, compilerOptions, importingSourceFile), {}, options);
}
/** @internal */
export function getNodeModulesPackageName(
compilerOptions: CompilerOptions,
importingSourceFile: SourceFile,
importingSourceFile: SourceFile | FutureSourceFile,
nodeModulesFileName: string,
host: ModuleSpecifierResolutionHost,
preferences: UserPreferences,
@@ -255,7 +260,7 @@ export function getNodeModulesPackageName(
function getModuleSpecifierWorker(
compilerOptions: CompilerOptions,
importingSourceFile: SourceFile,
importingSourceFile: SourceFile | FutureSourceFile,
importingSourceFileName: string,
toFileName: string,
host: ModuleSpecifierResolutionHost,
@@ -266,13 +271,13 @@ function getModuleSpecifierWorker(
const info = getInfo(importingSourceFileName, host);
const modulePaths = getAllModulePaths(info, toFileName, host, userPreferences, options);
return firstDefined(modulePaths, modulePath => tryGetModuleNameAsNodeModule(modulePath, info, importingSourceFile, host, compilerOptions, userPreferences, /*packageNameOnly*/ undefined, options.overrideImportMode)) ||
getLocalModuleSpecifier(toFileName, info, compilerOptions, host, options.overrideImportMode || importingSourceFile.impliedNodeFormat, preferences);
getLocalModuleSpecifier(toFileName, info, compilerOptions, host, options.overrideImportMode || getDefaultResolutionModeForFile(importingSourceFile, host, compilerOptions), preferences);
}
/** @internal */
export function tryGetModuleSpecifiersFromCache(
moduleSymbol: Symbol,
importingSourceFile: SourceFile,
importingSourceFile: SourceFile | FutureSourceFile,
host: ModuleSpecifierResolutionHost,
userPreferences: UserPreferences,
options: ModuleSpecifierOptions = {},
@@ -288,7 +293,7 @@ export function tryGetModuleSpecifiersFromCache(
function tryGetModuleSpecifiersFromCacheWorker(
moduleSymbol: Symbol,
importingSourceFile: SourceFile,
importingSourceFile: SourceFile | FutureSourceFile,
host: ModuleSpecifierResolutionHost,
userPreferences: UserPreferences,
options: ModuleSpecifierOptions = {},
@@ -334,7 +339,7 @@ export function getModuleSpecifiersWithCacheInfo(
moduleSymbol: Symbol,
checker: TypeChecker,
compilerOptions: CompilerOptions,
importingSourceFile: SourceFile,
importingSourceFile: SourceFile | FutureSourceFile,
host: ModuleSpecifierResolutionHost,
userPreferences: UserPreferences,
options: ModuleSpecifierOptions = {},
@@ -370,25 +375,49 @@ export function getModuleSpecifiersWithCacheInfo(
return { moduleSpecifiers: result, computedWithoutCache };
}
/** @internal */
export function getLocalModuleSpecifierBetweenFileNames(
importingFile: Pick<SourceFile, "fileName" | "impliedNodeFormat">,
targetFileName: string,
compilerOptions: CompilerOptions,
host: ModuleSpecifierResolutionHost,
options: ModuleSpecifierOptions = {},
): string {
const info = getInfo(importingFile.fileName, host);
const importMode = options.overrideImportMode ?? importingFile.impliedNodeFormat;
return getLocalModuleSpecifier(
targetFileName,
info,
compilerOptions,
host,
importMode,
getModuleSpecifierPreferences({}, host, compilerOptions, importingFile),
);
}
function computeModuleSpecifiers(
modulePaths: readonly ModulePath[],
compilerOptions: CompilerOptions,
importingSourceFile: SourceFile,
importingSourceFile: SourceFile | FutureSourceFile,
host: ModuleSpecifierResolutionHost,
userPreferences: UserPreferences,
options: ModuleSpecifierOptions = {},
forAutoImport: boolean,
): readonly string[] {
const info = getInfo(importingSourceFile.fileName, host);
const preferences = getModuleSpecifierPreferences(userPreferences, compilerOptions, importingSourceFile);
const existingSpecifier = forEach(modulePaths, modulePath =>
const preferences = getModuleSpecifierPreferences(userPreferences, host, compilerOptions, importingSourceFile);
const existingSpecifier = isFullSourceFile(importingSourceFile) && forEach(modulePaths, modulePath =>
forEach(
host.getFileIncludeReasons().get(toPath(modulePath.path, host.getCurrentDirectory(), info.getCanonicalFileName)),
reason => {
if (reason.kind !== FileIncludeKind.Import || reason.file !== importingSourceFile.path) return undefined;
// If the candidate import mode doesn't match the mode we're generating for, don't consider it
// TODO: maybe useful to keep around as an alternative option for certain contexts where the mode is overridable
if (importingSourceFile.impliedNodeFormat && importingSourceFile.impliedNodeFormat !== getModeForResolutionAtIndex(importingSourceFile, reason.index, compilerOptions)) return undefined;
const existingMode = host.getModeForResolutionAtIndex(importingSourceFile, reason.index);
const targetMode = options.overrideImportMode ?? host.getDefaultResolutionModeForFile(importingSourceFile);
if (existingMode !== targetMode && existingMode !== undefined && targetMode !== undefined) {
return undefined;
}
const specifier = getModuleNameStringLiteralAt(importingSourceFile, reason.index).text;
// If the preference is for non relative and the module specifier is relative, ignore it
return preferences.relativePreference !== RelativePreference.NonRelative || !pathIsRelative(specifier) ?
@@ -1014,7 +1043,7 @@ function tryGetModuleNameFromRootDirs(rootDirs: readonly string[], moduleFileNam
return processEnding(shortest, allowedEndings, compilerOptions);
}
function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCanonicalFileName, canonicalSourceDirectory }: Info, importingSourceFile: SourceFile, host: ModuleSpecifierResolutionHost, options: CompilerOptions, userPreferences: UserPreferences, packageNameOnly?: boolean, overrideMode?: ResolutionMode): string | undefined {
function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCanonicalFileName, canonicalSourceDirectory }: Info, importingSourceFile: SourceFile | FutureSourceFile, host: ModuleSpecifierResolutionHost, options: CompilerOptions, userPreferences: UserPreferences, packageNameOnly?: boolean, overrideMode?: ResolutionMode): string | undefined {
if (!host.fileExists || !host.readFile) {
return undefined;
}
@@ -1025,7 +1054,7 @@ function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCan
// Simplify the full file path to something that can be resolved by Node.
const preferences = getModuleSpecifierPreferences(userPreferences, options, importingSourceFile);
const preferences = getModuleSpecifierPreferences(userPreferences, host, options, importingSourceFile);
const allowedEndings = preferences.getAllowedEndingsInPreferredOrder();
let moduleSpecifier = path;
let isPackageRootPath = false;
@@ -1085,7 +1114,7 @@ function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCan
const cachedPackageJson = host.getPackageJsonInfoCache?.()?.getPackageJsonInfo(packageJsonPath);
if (isPackageJsonInfo(cachedPackageJson) || cachedPackageJson === undefined && host.fileExists(packageJsonPath)) {
const packageJsonContent: Record<string, any> | undefined = cachedPackageJson?.contents.packageJsonContent || tryParseJson(host.readFile!(packageJsonPath)!);
const importMode = overrideMode || importingSourceFile.impliedNodeFormat;
const importMode = overrideMode || getDefaultResolutionModeForFile(importingSourceFile, host, options);
if (getResolvePackageJsonExports(options)) {
// The package name that we found in node_modules could be different from the package
// name in the package.json content via url/filepath dependency specifiers. We need to
@@ -1280,3 +1309,7 @@ function getRelativePathIfInSameVolume(path: string, directoryPath: string, getC
function isPathRelativeToParent(path: string): boolean {
return startsWith(path, "..");
}
function getDefaultResolutionModeForFile(file: Pick<SourceFile, "fileName" | "impliedNodeFormat" | "packageJsonScope">, host: Pick<ModuleSpecifierResolutionHost, "getDefaultResolutionModeForFile">, compilerOptions: CompilerOptions) {
return isFullSourceFile(file) ? host.getDefaultResolutionModeForFile(file) : getDefaultResolutionModeForFileWorker(file, compilerOptions);
}
+147 -36
View File
@@ -168,6 +168,7 @@ import {
ImportClause,
ImportDeclaration,
ImportOrExportSpecifier,
importSyntaxAffectsModuleResolution,
InternalEmitFlags,
inverseJsxOptionMap,
isAmbientModule,
@@ -834,9 +835,11 @@ export function flattenDiagnosticMessageText(diag: string | DiagnosticMessageCha
* @internal
*/
export interface SourceFileImportsList {
/** @internal */ imports: SourceFile["imports"];
/** @internal */ moduleAugmentations: SourceFile["moduleAugmentations"];
imports: SourceFile["imports"];
moduleAugmentations: SourceFile["moduleAugmentations"];
impliedNodeFormat?: ResolutionMode;
fileName: string;
packageJsonScope?: SourceFile["packageJsonScope"];
}
/**
@@ -882,22 +885,47 @@ export function isExclusivelyTypeOnlyImportOrExport(decl: ImportDeclaration | Ex
/**
* Use `program.getModeForUsageLocation`, which retrieves the correct `compilerOptions`, instead of this function whenever possible.
* Calculates the final resolution mode for a given module reference node. This is the resolution mode explicitly provided via import
* attributes, if present, or the syntax the usage would have if emitted to JavaScript. In `--module node16` or `nodenext`, this may
* depend on the file's `impliedNodeFormat`. In `--module preserve`, it depends only on the input syntax of the reference. In other
* `module` modes, when overriding import attributes are not provided, this function returns `undefined`, as the result would have no
* impact on module resolution, emit, or type checking.
* Calculates the final resolution mode for a given module reference node. This function only returns a result when module resolution
* settings allow differing resolution between ESM imports and CJS requires, or when a mode is explicitly provided via import attributes,
* which cause an `import` or `require` condition to be used during resolution regardless of module resolution settings. In absence of
* overriding attributes, and in modes that support differing resolution, the result indicates the syntax the usage would emit to JavaScript.
* Some examples:
*
* ```ts
* // tsc foo.mts --module nodenext
* import {} from "mod";
* // Result: ESNext - the import emits as ESM due to `impliedNodeFormat` set by .mts file extension
*
* // tsc foo.cts --module nodenext
* import {} from "mod";
* // Result: CommonJS - the import emits as CJS due to `impliedNodeFormat` set by .cts file extension
*
* // tsc foo.ts --module preserve --moduleResolution bundler
* import {} from "mod";
* // Result: ESNext - the import emits as ESM due to `--module preserve` and `--moduleResolution bundler`
* // supports conditional imports/exports
*
* // tsc foo.ts --module preserve --moduleResolution node10
* import {} from "mod";
* // Result: undefined - the import emits as ESM due to `--module preserve`, but `--moduleResolution node10`
* // does not support conditional imports/exports
*
* // tsc foo.ts --module commonjs --moduleResolution node10
* import type {} from "mod" with { "resolution-mode": "import" };
* // Result: ESNext - conditional imports/exports always supported with "resolution-mode" attribute
* ```
*
* @param file The file the import or import-like reference is contained within
* @param usage The module reference string
* @param compilerOptions The compiler options for the program that owns the file. If the file belongs to a referenced project, the compiler options
* should be the options of the referenced project, not the referencing project.
* @returns The final resolution mode of the import
*/
export function getModeForUsageLocation(file: { impliedNodeFormat?: ResolutionMode; }, usage: StringLiteralLike, compilerOptions: CompilerOptions) {
export function getModeForUsageLocation(file: SourceFile, usage: StringLiteralLike, compilerOptions: CompilerOptions) {
return getModeForUsageLocationWorker(file, usage, compilerOptions);
}
function getModeForUsageLocationWorker(file: { impliedNodeFormat?: ResolutionMode; }, usage: StringLiteralLike, compilerOptions?: CompilerOptions) {
function getModeForUsageLocationWorker(file: Pick<SourceFile, "fileName" | "impliedNodeFormat" | "packageJsonScope">, usage: StringLiteralLike, compilerOptions?: CompilerOptions) {
if ((isImportDeclaration(usage.parent) || isExportDeclaration(usage.parent))) {
const isTypeOnly = isExclusivelyTypeOnlyImportOrExport(usage.parent);
if (isTypeOnly) {
@@ -913,20 +941,36 @@ function getModeForUsageLocationWorker(file: { impliedNodeFormat?: ResolutionMod
return override;
}
}
if (compilerOptions && getEmitModuleKind(compilerOptions) === ModuleKind.Preserve) {
return (usage.parent.parent && isImportEqualsDeclaration(usage.parent.parent) || isRequireCall(usage.parent, /*requireStringLiteralLikeArgument*/ false))
? ModuleKind.CommonJS
: ModuleKind.ESNext;
if (compilerOptions && importSyntaxAffectsModuleResolution(compilerOptions)) {
return getEmitSyntaxForUsageLocationWorker(file, usage, compilerOptions);
}
if (file.impliedNodeFormat === undefined) return undefined;
if (file.impliedNodeFormat !== ModuleKind.ESNext) {
// in cjs files, import call expressions are esm format, otherwise everything is cjs
return isImportCall(walkUpParenthesizedExpressions(usage.parent)) ? ModuleKind.ESNext : ModuleKind.CommonJS;
}
function getEmitSyntaxForUsageLocationWorker(file: Pick<SourceFile, "fileName" | "impliedNodeFormat" | "packageJsonScope">, usage: StringLiteralLike, compilerOptions?: CompilerOptions): ResolutionMode {
if (!compilerOptions) {
// This should always be provided, but we try to fail somewhat
// gracefully to allow projects like ts-node time to update.
return undefined;
}
// in esm files, import=require statements are cjs format, otherwise everything is esm
// imports are only parent'd up to their containing declaration/expression, so access farther parents with care
const exprParentParent = walkUpParenthesizedExpressions(usage.parent)?.parent;
return exprParentParent && isImportEqualsDeclaration(exprParentParent) ? ModuleKind.CommonJS : ModuleKind.ESNext;
if (exprParentParent && isImportEqualsDeclaration(exprParentParent) || isRequireCall(usage.parent, /*requireStringLiteralLikeArgument*/ false)) {
return ModuleKind.CommonJS;
}
if (isImportCall(walkUpParenthesizedExpressions(usage.parent))) {
return shouldTransformImportCallWorker(file, compilerOptions) ? ModuleKind.CommonJS : ModuleKind.ESNext;
}
// If we're in --module preserve on an input file, we know that an import
// is an import. But if this is a declaration file, we'd prefer to use the
// impliedNodeFormat. Since we want things to be consistent between the two,
// we need to issue errors when the user writes ESM syntax in a definitely-CJS
// file, until/unless declaration emit can indicate a true ESM import. On the
// other hand, writing CJS syntax in a definitely-ESM file is fine, since declaration
// emit preserves the CJS syntax.
const fileEmitMode = getEmitModuleFormatOfFileWorker(file, compilerOptions);
return fileEmitMode === ModuleKind.CommonJS ? ModuleKind.CommonJS :
emitModuleKindIsNonNodeESM(fileEmitMode) || fileEmitMode === ModuleKind.Preserve ? ModuleKind.ESNext :
undefined;
}
/** @internal */
@@ -1017,7 +1061,7 @@ function getTypeReferenceResolutionName<T extends FileReference | string>(entry:
const typeReferenceResolutionNameAndModeGetter: ResolutionNameAndModeGetter<FileReference | string, SourceFile | undefined> = {
getName: getTypeReferenceResolutionName,
getMode: (entry, file) => getModeForFileReference(entry, file?.impliedNodeFormat),
getMode: (entry, file, compilerOptions) => getModeForFileReference(entry, file && getDefaultResolutionModeForFileWorker(file, compilerOptions)),
};
/** @internal */
@@ -1339,16 +1383,11 @@ export function getImpliedNodeFormatForFileWorker(
host: ModuleResolutionHost,
options: CompilerOptions,
) {
switch (getEmitModuleResolutionKind(options)) {
case ModuleResolutionKind.Node16:
case ModuleResolutionKind.NodeNext:
return fileExtensionIsOneOf(fileName, [Extension.Dmts, Extension.Mts, Extension.Mjs]) ? ModuleKind.ESNext :
fileExtensionIsOneOf(fileName, [Extension.Dcts, Extension.Cts, Extension.Cjs]) ? ModuleKind.CommonJS :
fileExtensionIsOneOf(fileName, [Extension.Dts, Extension.Ts, Extension.Tsx, Extension.Js, Extension.Jsx]) ? lookupFromPackageJson() :
undefined; // other extensions, like `json` or `tsbuildinfo`, are set as `undefined` here but they should never be fed through the transformer pipeline
default:
return undefined;
}
return fileExtensionIsOneOf(fileName, [Extension.Dmts, Extension.Mts, Extension.Mjs]) ? ModuleKind.ESNext :
fileExtensionIsOneOf(fileName, [Extension.Dcts, Extension.Cts, Extension.Cjs]) ? ModuleKind.CommonJS :
fileExtensionIsOneOf(fileName, [Extension.Dts, Extension.Ts, Extension.Tsx, Extension.Js, Extension.Jsx]) ? lookupFromPackageJson() :
undefined; // other extensions, like `json` or `tsbuildinfo`, are set as `undefined` here but they should never be fed through the transformer pipeline
function lookupFromPackageJson(): Partial<CreateSourceFileOptions> {
const state = getTemporaryModuleResolutionState(packageJsonInfoCache, host, options);
const packageJsonLocations: string[] = [];
@@ -1899,6 +1938,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
isSourceFileFromExternalLibrary,
isSourceFileDefaultLibrary,
getModeForUsageLocation,
getEmitSyntaxForUsageLocation,
getModeForResolutionAtIndex,
getSourceFileFromReference,
getLibFileFromReference,
@@ -1926,6 +1966,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
forEachResolvedProjectReference,
isSourceOfProjectReferenceRedirect,
getRedirectReferenceForResolutionFromSourceOfProject,
getCompilerOptionsForFile,
getDefaultResolutionModeForFile,
getEmitModuleFormatOfFile,
getImpliedNodeFormatForEmit,
shouldTransformImportCall,
emitBuildInfo,
fileExists,
readFile,
@@ -2640,6 +2685,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
getSymlinkCache,
writeFile: writeFileCallback || writeFile,
isEmitBlocked,
shouldTransformImportCall,
getEmitModuleFormatOfFile,
getDefaultResolutionModeForFile,
getModeForResolutionAtIndex,
readFile: f => host.readFile(f),
fileExists: f => {
// Use local caches
@@ -3871,11 +3920,15 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
// store resolved type directive on the file
const fileName = toFileNameLowerCase(ref.fileName);
resolutionsInFile.set(fileName, getModeForFileReference(ref, file.impliedNodeFormat), resolvedTypeReferenceDirective);
const mode = ref.resolutionMode || file.impliedNodeFormat;
const mode = ref.resolutionMode || getDefaultResolutionModeForFile(file);
processTypeReferenceDirective(fileName, mode, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index });
}
}
function getCompilerOptionsForFile(file: SourceFile): CompilerOptions {
return getRedirectReferenceForResolution(file)?.commandLine.options || options;
}
function processTypeReferenceDirective(
typeReferenceDirective: string,
mode: ResolutionMode,
@@ -4033,7 +4086,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
const resolutions = resolvedModulesProcessing?.get(file.path) ||
resolveModuleNamesReusingOldState(moduleNames, file);
Debug.assert(resolutions.length === moduleNames.length);
const optionsForFile = getRedirectReferenceForResolution(file)?.commandLine.options || options;
const optionsForFile = getCompilerOptionsForFile(file);
const resolutionsInFile = createModeAwareCache<ResolutionWithFailedLookupLocations>();
(resolvedModules ??= new Map()).set(file.path, resolutionsInFile);
for (let index = 0; index < moduleNames.length; index++) {
@@ -4604,7 +4657,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
if (locationReason && fileIncludeReasons?.length === 1) fileIncludeReasons = undefined;
const location = locationReason && getReferencedFileLocation(program, locationReason);
const fileIncludeReasonDetails = fileIncludeReasons && chainDiagnosticMessages(fileIncludeReasons, Diagnostics.The_file_is_in_the_program_because_Colon);
const redirectInfo = file && explainIfFileIsRedirectAndImpliedFormat(file);
const optionsForFile = file && getCompilerOptionsForFile(file) || options;
const redirectInfo = file && explainIfFileIsRedirectAndImpliedFormat(file, optionsForFile);
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) :
@@ -4934,13 +4988,70 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}
function getModeForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode {
const optionsForFile = getRedirectReferenceForResolution(file)?.commandLine.options || options;
return getModeForUsageLocationWorker(file, usage, optionsForFile);
return getModeForUsageLocationWorker(file, usage, getCompilerOptionsForFile(file));
}
function getEmitSyntaxForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode {
return getEmitSyntaxForUsageLocationWorker(file, usage, getCompilerOptionsForFile(file));
}
function getModeForResolutionAtIndex(file: SourceFile, index: number): ResolutionMode {
return getModeForUsageLocation(file, getModuleNameStringLiteralAt(file, index));
}
function getDefaultResolutionModeForFile(sourceFile: SourceFile): ResolutionMode {
return getDefaultResolutionModeForFileWorker(sourceFile, getCompilerOptionsForFile(sourceFile));
}
function getImpliedNodeFormatForEmit(sourceFile: SourceFile): ResolutionMode {
return getImpliedNodeFormatForEmitWorker(sourceFile, getCompilerOptionsForFile(sourceFile));
}
function getEmitModuleFormatOfFile(sourceFile: SourceFile): ModuleKind {
return getEmitModuleFormatOfFileWorker(sourceFile, getCompilerOptionsForFile(sourceFile));
}
function shouldTransformImportCall(sourceFile: SourceFile): boolean {
return shouldTransformImportCallWorker(sourceFile, getCompilerOptionsForFile(sourceFile));
}
}
function shouldTransformImportCallWorker(sourceFile: Pick<SourceFile, "fileName" | "impliedNodeFormat" | "packageJsonScope">, options: CompilerOptions): boolean {
const moduleKind = getEmitModuleKind(options);
if (ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext || moduleKind === ModuleKind.Preserve) {
return false;
}
return getEmitModuleFormatOfFileWorker(sourceFile, options) < ModuleKind.ES2015;
}
/** @internal Prefer `program.getEmitModuleFormatOfFile` when possible. */
export function getEmitModuleFormatOfFileWorker(sourceFile: Pick<SourceFile, "fileName" | "impliedNodeFormat" | "packageJsonScope">, options: CompilerOptions): ModuleKind {
return getImpliedNodeFormatForEmitWorker(sourceFile, options) ?? getEmitModuleKind(options);
}
/** @internal Prefer `program.getImpliedNodeFormatForEmit` when possible. */
export function getImpliedNodeFormatForEmitWorker(sourceFile: Pick<SourceFile, "fileName" | "impliedNodeFormat" | "packageJsonScope">, options: CompilerOptions): ResolutionMode {
const moduleKind = getEmitModuleKind(options);
if (ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext) {
return sourceFile.impliedNodeFormat;
}
if (
sourceFile.impliedNodeFormat === ModuleKind.CommonJS
&& (sourceFile.packageJsonScope?.contents.packageJsonContent.type === "commonjs"
|| fileExtensionIsOneOf(sourceFile.fileName, [Extension.Cjs, Extension.Cts]))
) {
return ModuleKind.CommonJS;
}
if (
sourceFile.impliedNodeFormat === ModuleKind.ESNext
&& (sourceFile.packageJsonScope?.contents.packageJsonContent.type === "module"
|| fileExtensionIsOneOf(sourceFile.fileName, [Extension.Mjs, Extension.Mts]))
) {
return ModuleKind.ESNext;
}
return undefined;
}
/** @internal Prefer `program.getDefaultResolutionModeForFile` when possible. */
export function getDefaultResolutionModeForFileWorker(sourceFile: Pick<SourceFile, "fileName" | "impliedNodeFormat" | "packageJsonScope">, options: CompilerOptions): ResolutionMode {
return importSyntaxAffectsModuleResolution(options) ? getImpliedNodeFormatForEmitWorker(sourceFile, options) : undefined;
}
interface HostForUseSourceOfProjectReferenceRedirect {
+168 -12
View File
@@ -98,6 +98,8 @@ export interface ResolutionCache {
resolutionsWithOnlyAffectingLocations: Set<ResolutionWithFailedLookupLocations>;
directoryWatchesOfFailedLookups: Map<string, DirectoryWatchesOfFailedLookup>;
fileWatchesOfAffectingLocations: Map<string, FileWatcherOfAffectingLocation>;
packageDirWatchers: Map<Path, PackageDirWatcher>;
dirPathToSymlinkPackageRefCount: Map<Path, number>;
startRecordingFilesWithChangedResolutions(): void;
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;
@@ -236,12 +238,24 @@ export interface DirectoryWatchesOfFailedLookup {
/** is the directory watched being non recursive */
nonRecursive?: boolean;
}
/** @internal */
export interface DirPathToWatcherOfPackageDirWatcher {
watcher: DirectoryWatchesOfFailedLookup;
refCount: number;
}
/** @internal */
export interface PackageDirWatcher {
dirPathToWatcher: Map<Path, DirPathToWatcherOfPackageDirWatcher>;
isSymlink: boolean;
}
/** @internal */
export interface DirectoryOfFailedLookupWatch {
dir: string;
dirPath: Path;
nonRecursive?: boolean;
packageDir?: string;
packageDirPath?: Path;
}
/** @internal */
@@ -347,10 +361,16 @@ export function getDirectoryToWatchFailedLookupLocation(
// If directory path contains node module, get the most parent node_modules directory for watching
const nodeModulesIndex = failedLookupPathComponents.indexOf("node_modules" as Path);
if (nodeModulesIndex !== -1 && nodeModulesIndex + 1 <= perceivedOsRootLength + 1) return undefined; // node_modules not at position where it can be watched
const lastNodeModulesIndex = failedLookupPathComponents.lastIndexOf("node_modules" as Path);
if (isInDirectoryPath(rootPathComponents, failedLookupPathComponents)) {
if (failedLookupPathComponents.length > rootPathComponents.length + 1) {
// Instead of watching root, watch directory in root to avoid watching excluded directories not needed for module resolution
return getDirectoryOfFailedLookupWatch(failedLookupComponents, failedLookupPathComponents, Math.max(rootPathComponents.length + 1, perceivedOsRootLength + 1));
return getDirectoryOfFailedLookupWatch(
failedLookupComponents,
failedLookupPathComponents,
Math.max(rootPathComponents.length + 1, perceivedOsRootLength + 1),
lastNodeModulesIndex,
);
}
else {
// Always watch root directory non recursively
@@ -369,6 +389,7 @@ export function getDirectoryToWatchFailedLookupLocation(
perceivedOsRootLength,
nodeModulesIndex,
rootPathComponents,
lastNodeModulesIndex,
);
}
@@ -379,11 +400,17 @@ function getDirectoryToWatchFromFailedLookupLocationDirectory(
perceivedOsRootLength: number,
nodeModulesIndex: number,
rootPathComponents: Readonly<PathPathComponents>,
lastNodeModulesIndex: number,
): DirectoryOfFailedLookupWatch | undefined {
// If directory path contains node module, get the most parent node_modules directory for watching
if (nodeModulesIndex !== -1) {
// If the directory is node_modules use it to watch, always watch it recursively
return getDirectoryOfFailedLookupWatch(dirComponents, dirPathComponents, nodeModulesIndex + 1);
return getDirectoryOfFailedLookupWatch(
dirComponents,
dirPathComponents,
nodeModulesIndex + 1,
lastNodeModulesIndex,
);
}
// Use some ancestor of the root directory
let nonRecursive = true;
@@ -395,19 +422,37 @@ function getDirectoryToWatchFromFailedLookupLocationDirectory(
break;
}
}
return getDirectoryOfFailedLookupWatch(dirComponents, dirPathComponents, length, nonRecursive);
return getDirectoryOfFailedLookupWatch(
dirComponents,
dirPathComponents,
length,
lastNodeModulesIndex,
nonRecursive,
);
}
function getDirectoryOfFailedLookupWatch(
dirComponents: readonly string[],
dirPathComponents: Readonly<PathPathComponents>,
length: number,
lastNodeModulesIndex: number,
nonRecursive?: boolean,
): DirectoryOfFailedLookupWatch {
let packageDirLength;
if (lastNodeModulesIndex !== -1 && lastNodeModulesIndex + 1 >= length && lastNodeModulesIndex + 2 < dirPathComponents.length) {
if (!startsWith(dirPathComponents[lastNodeModulesIndex + 1], "@")) {
packageDirLength = lastNodeModulesIndex + 2;
}
else if (lastNodeModulesIndex + 3 < dirPathComponents.length) {
packageDirLength = lastNodeModulesIndex + 3;
}
}
return {
dir: getPathFromPathComponents(dirComponents, length),
dirPath: getPathFromPathComponents(dirPathComponents, length),
nonRecursive,
packageDir: packageDirLength !== undefined ? getPathFromPathComponents(dirComponents, packageDirLength) : undefined,
packageDirPath: packageDirLength !== undefined ? getPathFromPathComponents(dirPathComponents, packageDirLength) : undefined,
};
}
@@ -433,6 +478,7 @@ export function getDirectoryToWatchFailedLookupLocationFromTypeRoot(
perceivedOsRootLengthForWatching(typeRootPathComponents, typeRootPathComponents.length),
typeRootPathComponents.indexOf("node_modules" as Path),
rootPathComponents,
typeRootPathComponents.lastIndexOf("node_modules" as Path),
);
return toWatch && filterCustomPath(toWatch.dirPath) ? toWatch.dirPath : undefined;
}
@@ -543,7 +589,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
let isInDirectoryChecks: Set<Path> | undefined;
let allModuleAndTypeResolutionsAreInvalidated = false;
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory!()); // TODO: GH#18217
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory!());
const cachedDirectoryStructureHost = resolutionHost.getCachedDirectoryStructureHost();
// The resolvedModuleNames and resolvedTypeReferenceDirectives are the cache of resolutions per file.
@@ -579,6 +625,10 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
const rootPath = resolutionHost.toPath(rootDir);
const rootPathComponents = getPathComponents(rootPath);
const isSymlinkCache = new Map<Path, boolean>();
const packageDirWatchers = new Map<Path, PackageDirWatcher>(); // Watching packageDir if symlink otherwise watching dirPath
const dirPathToSymlinkPackageRefCount = new Map<Path, number>(); // Refcount for dirPath watches when watching symlinked packageDir
// TypeRoot watches for the types that get added as part of getAutomaticTypeDirectiveNames
const typeRootsWatches = new Map<string, FileWatcher>();
@@ -592,6 +642,8 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
resolutionsWithOnlyAffectingLocations,
directoryWatchesOfFailedLookups,
fileWatchesOfAffectingLocations,
packageDirWatchers,
dirPathToSymlinkPackageRefCount,
watchFailedLookupLocationsOfExternalModuleResolutions,
getModuleResolutionCache: () => moduleResolutionCache,
startRecordingFilesWithChangedResolutions,
@@ -629,6 +681,9 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
function clear() {
clearMap(directoryWatchesOfFailedLookups, closeFileWatcherOf);
clearMap(fileWatchesOfAffectingLocations, closeFileWatcherOf);
isSymlinkCache.clear();
packageDirWatchers.clear();
dirPathToSymlinkPackageRefCount.clear();
nonRelativeExternalModuleResolutions.clear();
closeTypeRootsWatch();
resolvedModuleNames.clear();
@@ -712,6 +767,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
// (between startCachingPerDirectoryResolution and finishCachingPerDirectoryResolution)
nonRelativeExternalModuleResolutions.forEach(watchFailedLookupLocationOfNonRelativeModuleResolutions);
nonRelativeExternalModuleResolutions.clear();
isSymlinkCache.clear();
}
function cleanupLibResolutionWatching(newProgram: Program | undefined) {
@@ -759,11 +815,19 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
}
directoryWatchesOfFailedLookups.forEach(closeDirectoryWatchesOfFailedLookup);
fileWatchesOfAffectingLocations.forEach(closeFileWatcherOfAffectingLocation);
packageDirWatchers.forEach(closePackageDirWatcher);
hasChangedAutomaticTypeDirectiveNames = false;
moduleResolutionCache.isReadonly = true;
typeReferenceDirectiveResolutionCache.isReadonly = true;
libraryResolutionCache.isReadonly = true;
moduleResolutionCache.getPackageJsonInfoCache().isReadonly = true;
isSymlinkCache.clear();
}
function closePackageDirWatcher(watcher: PackageDirWatcher, packageDirPath: Path) {
if (watcher.dirPathToWatcher.size === 0) {
packageDirWatchers.delete(packageDirPath);
}
}
function closeDirectoryWatchesOfFailedLookup(watcher: DirectoryWatchesOfFailedLookup, path: Path) {
@@ -1078,13 +1142,14 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
getCurrentDirectory,
);
if (toWatch) {
const { dir, dirPath, nonRecursive } = toWatch;
const { dir, dirPath, nonRecursive, packageDir, packageDirPath } = toWatch;
if (dirPath === rootPath) {
Debug.assert(nonRecursive);
Debug.assert(!packageDir);
setAtRoot = true;
}
else {
setDirectoryWatcher(dir, dirPath, nonRecursive);
setDirectoryWatcher(dir, dirPath, packageDir, packageDirPath, nonRecursive);
}
}
return setAtRoot;
@@ -1106,7 +1171,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
if (alternateResult) setAtRoot = watchFailedLookupLocation(alternateResult, setAtRoot);
if (setAtRoot) {
// This is always non recursive
setDirectoryWatcher(rootDir, rootPath, /*nonRecursive*/ true);
setDirectoryWatcher(rootDir, rootPath, /*packageDir*/ undefined, /*packageDirPath*/ undefined, /*nonRecursive*/ true);
}
watchAffectingLocationsOfResolution(resolution, !failedLookupLocations?.length && !alternateResult);
}
@@ -1197,15 +1262,87 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
}
}
function setDirectoryWatcher(dir: string, dirPath: Path, nonRecursive?: boolean) {
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
function createDirectoryWatcherForPackageDir(
dir: string,
dirPath: Path,
packageDir: string,
packageDirPath: Path,
nonRecursive: boolean | undefined,
) {
Debug.assert(!nonRecursive);
// Check if this is symlink:
let isSymlink = isSymlinkCache.get(packageDirPath);
let packageDirWatcher = packageDirWatchers.get(packageDirPath);
if (isSymlink === undefined) {
const realPath = resolutionHost.realpath!(packageDir);
isSymlink = realPath !== packageDir && resolutionHost.toPath(realPath) !== packageDirPath;
isSymlinkCache.set(packageDirPath, isSymlink);
if (!packageDirWatcher) {
packageDirWatchers.set(
packageDirPath,
packageDirWatcher = {
dirPathToWatcher: new Map(),
isSymlink,
},
);
}
else if (packageDirWatcher.isSymlink !== isSymlink) {
// Handle the change
packageDirWatcher.dirPathToWatcher.forEach(watcher => {
removeDirectoryWatcher(packageDirWatcher!.isSymlink ? packageDirPath : dirPath, /*syncDirWatcherRemove*/ false);
watcher.watcher = createDirPathToWatcher();
});
packageDirWatcher.isSymlink = isSymlink;
}
}
else {
Debug.assertIsDefined(packageDirWatcher);
Debug.assert(isSymlink === packageDirWatcher.isSymlink);
}
const forDirPath = packageDirWatcher.dirPathToWatcher.get(dirPath);
if (forDirPath) {
forDirPath.refCount++;
}
else {
packageDirWatcher.dirPathToWatcher.set(dirPath, {
watcher: createDirPathToWatcher(),
refCount: 1,
});
if (isSymlink) dirPathToSymlinkPackageRefCount.set(dirPath, (dirPathToSymlinkPackageRefCount.get(dirPath) ?? 0) + 1);
}
function createDirPathToWatcher() {
return isSymlink ?
createOrAddRefToDirectoryWatchOfFailedLookups(packageDir, packageDirPath, nonRecursive) :
createOrAddRefToDirectoryWatchOfFailedLookups(dir, dirPath, nonRecursive);
}
}
function setDirectoryWatcher(
dir: string,
dirPath: Path,
packageDir: string | undefined,
packageDirPath: Path | undefined,
nonRecursive: boolean | undefined,
) {
if (!packageDirPath || !resolutionHost.realpath) {
createOrAddRefToDirectoryWatchOfFailedLookups(dir, dirPath, nonRecursive);
}
else {
createDirectoryWatcherForPackageDir(dir, dirPath, packageDir!, packageDirPath, nonRecursive);
}
}
function createOrAddRefToDirectoryWatchOfFailedLookups(dir: string, dirPath: Path, nonRecursive: boolean | undefined) {
let dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
if (dirWatcher) {
Debug.assert(!!nonRecursive === !!dirWatcher.nonRecursive);
dirWatcher.refCount++;
}
else {
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath, nonRecursive), refCount: 1, nonRecursive });
directoryWatchesOfFailedLookups.set(dirPath, dirWatcher = { watcher: createDirectoryWatcher(dir, dirPath, nonRecursive), refCount: 1, nonRecursive });
}
return dirWatcher;
}
function stopWatchFailedLookupLocation(failedLookupLocation: string, removeAtRoot: boolean, syncDirWatcherRemove: boolean | undefined) {
@@ -1219,10 +1356,29 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
getCurrentDirectory,
);
if (toWatch) {
const { dirPath } = toWatch;
const { dirPath, packageDirPath } = toWatch;
if (dirPath === rootPath) {
removeAtRoot = true;
}
else if (packageDirPath && resolutionHost.realpath) {
const packageDirWatcher = packageDirWatchers.get(packageDirPath)!;
const forDirPath = packageDirWatcher.dirPathToWatcher.get(dirPath)!;
forDirPath.refCount--;
if (forDirPath.refCount === 0) {
removeDirectoryWatcher(packageDirWatcher.isSymlink ? packageDirPath : dirPath, syncDirWatcherRemove);
packageDirWatcher.dirPathToWatcher.delete(dirPath);
if (packageDirWatcher.isSymlink) {
const refCount = dirPathToSymlinkPackageRefCount.get(dirPath)! - 1;
if (refCount === 0) {
dirPathToSymlinkPackageRefCount.delete(dirPath);
}
else {
dirPathToSymlinkPackageRefCount.set(dirPath, refCount);
}
}
if (syncDirWatcherRemove) closePackageDirWatcher(packageDirWatcher, packageDirPath);
}
}
else {
removeDirectoryWatcher(dirPath, syncDirWatcherRemove);
}
@@ -1506,7 +1662,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
rootPath,
rootPathComponents,
getCurrentDirectory,
dirPath => directoryWatchesOfFailedLookups.has(dirPath),
dirPath => directoryWatchesOfFailedLookups.has(dirPath) || dirPathToSymlinkPackageRefCount.has(dirPath),
);
if (dirPath) {
scheduleInvalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath);
+8 -14
View File
@@ -1768,18 +1768,6 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
const ch = codePointAt(text, pos);
if (pos === 0) {
// If a file isn't valid text at all, it will usually be apparent
// in the first few characters because UTF-8 decode will fail and produce U+FFFD.
// If that happens, just issue one error and refuse to try to scan further;
// this is likely a binary file that cannot be parsed.
//
// It's safe to slice the text; U+FFFD can only be produced by an invalid decode,
// so even if we cut a surrogate pair in half, they wouldn't be U+FFFD.
if (text.slice(0, 256).includes("\uFFFD")) {
error(Diagnostics.File_appears_to_be_binary);
pos = end;
return token = SyntaxKind.NonTextFileMarkerTrivia;
}
// Special handling for shebang
if (ch === CharacterCodes.hash && isShebangTrivia(text, pos)) {
pos = scanShebangTrivia(text, pos);
@@ -2242,6 +2230,10 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
error(Diagnostics.Invalid_character, pos++, charSize(ch));
}
return token = SyntaxKind.PrivateIdentifier;
case CharacterCodes.replacementCharacter:
error(Diagnostics.File_appears_to_be_binary, 0, 0);
pos = end;
return token = SyntaxKind.NonTextFileMarkerTrivia;
default:
const identifierKind = scanIdentifier(ch, languageVersion);
if (identifierKind) {
@@ -2656,6 +2648,10 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
return token = SyntaxKind.OpenBracketToken;
case CharacterCodes.closeBracket:
return token = SyntaxKind.CloseBracketToken;
case CharacterCodes.openParen:
return token = SyntaxKind.OpenParenToken;
case CharacterCodes.closeParen:
return token = SyntaxKind.CloseParenToken;
case CharacterCodes.lessThan:
return token = SyntaxKind.LessThanToken;
case CharacterCodes.greaterThan:
@@ -2809,13 +2805,11 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
}
}
/** @internal */
function codePointAt(s: string, i: number): number {
// TODO(jakebailey): this is wrong and should have ?? 0; but all users are okay with it
return s.codePointAt(i)!;
}
/** @internal */
function charSize(ch: number) {
if (ch >= 0x10000) {
return 2;
+70 -23
View File
@@ -18,6 +18,7 @@ import {
getDirectoryPath,
getFallbackOptions,
getNormalizedAbsolutePath,
getRelativePathFromDirectory,
getRelativePathToDirectoryOrUrl,
getRootLength,
getStringComparer,
@@ -607,15 +608,17 @@ function createDirectoryWatcherSupportingRecursive({
watcher: FileWatcher;
childWatches: ChildWatches;
refCount: number;
targetWatcher: ChildDirectoryWatcher | undefined;
links: Set<string> | undefined;
}
const cache = new Map<string, HostDirectoryWatcher>();
const cache = new Map<Path, HostDirectoryWatcher>();
const callbackCache = createMultiMap<Path, { dirName: string; callback: DirectoryWatcherCallback; }>();
const cacheToUpdateChildWatches = new Map<Path, { dirName: string; options: WatchOptions | undefined; fileNames: string[]; }>();
let timerToUpdateChildWatches: any;
const filePathComparer = getStringComparer(!useCaseSensitiveFileNames);
const toCanonicalFilePath = createGetCanonicalFileName(useCaseSensitiveFileNames);
const toCanonicalFilePath = createGetCanonicalFileName(useCaseSensitiveFileNames) as (fileName: string) => Path;
return (dirName, callback, recursive, options) =>
recursive ?
@@ -625,8 +628,13 @@ function createDirectoryWatcherSupportingRecursive({
/**
* Create the directory watcher for the dirPath.
*/
function createDirectoryWatcher(dirName: string, options: WatchOptions | undefined, callback?: DirectoryWatcherCallback): ChildDirectoryWatcher {
const dirPath = toCanonicalFilePath(dirName) as Path;
function createDirectoryWatcher(
dirName: string,
options: WatchOptions | undefined,
callback?: DirectoryWatcherCallback,
link?: string,
): ChildDirectoryWatcher {
const dirPath = toCanonicalFilePath(dirName);
let directoryWatcher = cache.get(dirPath);
if (directoryWatcher) {
directoryWatcher.refCount++;
@@ -640,7 +648,7 @@ function createDirectoryWatcherSupportingRecursive({
if (options?.synchronousWatchDirectory) {
// Call the actual callback
invokeCallbacks(dirPath, fileName);
if (!cache.get(dirPath)?.targetWatcher) invokeCallbacks(dirName, dirPath, fileName);
// Iterate through existing children and update the watches if needed
updateChildWatches(dirName, dirPath, options);
@@ -654,11 +662,15 @@ function createDirectoryWatcherSupportingRecursive({
),
refCount: 1,
childWatches: emptyArray,
targetWatcher: undefined,
links: undefined,
};
cache.set(dirPath, directoryWatcher);
updateChildWatches(dirName, dirPath, options);
}
if (link) (directoryWatcher.links ??= new Set()).add(link);
const callbackToAdd = callback && { dirName, callback };
if (callbackToAdd) {
callbackCache.add(dirPath, callbackToAdd);
@@ -669,21 +681,24 @@ function createDirectoryWatcherSupportingRecursive({
close: () => {
const directoryWatcher = Debug.checkDefined(cache.get(dirPath));
if (callbackToAdd) callbackCache.remove(dirPath, callbackToAdd);
if (link) directoryWatcher.links?.delete(link);
directoryWatcher.refCount--;
if (directoryWatcher.refCount) return;
cache.delete(dirPath);
directoryWatcher.links = undefined;
closeFileWatcherOf(directoryWatcher);
closeTargetWatcher(directoryWatcher);
directoryWatcher.childWatches.forEach(closeFileWatcher);
},
};
}
type InvokeMap = Map<Path, string[] | true>;
function invokeCallbacks(dirPath: Path, fileName: string): void;
function invokeCallbacks(dirPath: Path, invokeMap: InvokeMap, fileNames: string[] | undefined): void;
function invokeCallbacks(dirPath: Path, fileNameOrInvokeMap: string | InvokeMap, fileNames?: string[]) {
function invokeCallbacks(dirName: string, dirPath: Path, fileName: string): void;
function invokeCallbacks(dirName: string, dirPath: Path, invokeMap: InvokeMap, fileNames: string[] | undefined): void;
function invokeCallbacks(dirName: string, dirPath: Path, fileNameOrInvokeMap: string | InvokeMap, fileNames?: string[]) {
let fileName: string | undefined;
let invokeMap: InvokeMap | undefined;
if (isString(fileNameOrInvokeMap)) {
@@ -715,6 +730,15 @@ function createDirectoryWatcherSupportingRecursive({
}
}
});
cache.get(dirPath)?.links?.forEach(link => {
const toPathInLink = (fileName: string) => combinePaths(link, getRelativePathFromDirectory(dirName, fileName, toCanonicalFilePath));
if (invokeMap) {
invokeCallbacks(link, toCanonicalFilePath(link), invokeMap, fileNames?.map(toPathInLink));
}
else {
invokeCallbacks(link, toCanonicalFilePath(link), toPathInLink(fileName!));
}
});
}
function nonSyncUpdateChildWatches(dirName: string, dirPath: Path, fileName: string, options: WatchOptions | undefined) {
@@ -727,7 +751,8 @@ function createDirectoryWatcherSupportingRecursive({
}
// Call the actual callbacks and remove child watches
invokeCallbacks(dirPath, fileName);
invokeCallbacks(dirName, dirPath, fileName);
closeTargetWatcher(parentWatcher);
removeChildWatches(parentWatcher);
}
@@ -760,7 +785,7 @@ function createDirectoryWatcherSupportingRecursive({
// 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
const hasChanges = updateChildWatches(dirName, dirPath, options);
invokeCallbacks(dirPath, invokeMap, hasChanges ? undefined : fileNames);
if (!cache.get(dirPath)?.targetWatcher) invokeCallbacks(dirName, dirPath, invokeMap, hasChanges ? undefined : fileNames);
}
sysLog(`sysLog:: invokingWatchers:: Elapsed:: ${timestamp() - start}ms:: ${cacheToUpdateChildWatches.size}`);
@@ -792,24 +817,46 @@ function createDirectoryWatcherSupportingRecursive({
}
}
function closeTargetWatcher(watcher: HostDirectoryWatcher | undefined) {
if (watcher?.targetWatcher) {
watcher.targetWatcher.close();
watcher.targetWatcher = undefined;
}
}
function updateChildWatches(parentDir: string, parentDirPath: Path, options: WatchOptions | undefined) {
// Iterate through existing children and update the watches if needed
const parentWatcher = cache.get(parentDirPath);
if (!parentWatcher) return false;
const target = normalizePath(realpath(parentDir));
let hasChanges;
let newChildWatches: ChildDirectoryWatcher[] | undefined;
const hasChanges = enumerateInsertsAndDeletes<string, ChildDirectoryWatcher>(
fileSystemEntryExists(parentDir, FileSystemEntryKind.Directory) ? 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, options) && filePathComparer(childFullName, normalizePath(realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined;
}) : emptyArray,
parentWatcher.childWatches,
(child, childWatcher) => filePathComparer(child, childWatcher.dirName),
createAndAddChildDirectoryWatcher,
closeFileWatcher,
addChildDirectoryWatcher,
);
if (filePathComparer(target, parentDir) === Comparison.EqualTo) {
// if (parentWatcher.target) closeFileWatcher
hasChanges = enumerateInsertsAndDeletes<string, ChildDirectoryWatcher>(
fileSystemEntryExists(parentDir, FileSystemEntryKind.Directory) ? 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, options) && filePathComparer(childFullName, normalizePath(realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined;
}) : emptyArray,
parentWatcher.childWatches,
(child, childWatcher) => filePathComparer(child, childWatcher.dirName),
createAndAddChildDirectoryWatcher,
closeFileWatcher,
addChildDirectoryWatcher,
);
}
else if (parentWatcher.targetWatcher && filePathComparer(target, parentWatcher.targetWatcher.dirName) === Comparison.EqualTo) {
hasChanges = false;
Debug.assert(parentWatcher.childWatches === emptyArray);
}
else {
closeTargetWatcher(parentWatcher);
parentWatcher.targetWatcher = createDirectoryWatcher(target, options, /*callback*/ undefined, parentDir);
parentWatcher.childWatches.forEach(closeFileWatcher);
hasChanges = true;
}
parentWatcher.childWatches = newChildWatches || emptyArray;
return hasChanges;
+12 -6
View File
@@ -65,10 +65,10 @@ import {
transformESDecorators,
transformESNext,
transformGenerators,
transformImpliedNodeFormatDependentModule,
transformJsx,
transformLegacyDecorators,
transformModule,
transformNodeModule,
transformSystemModule,
transformTypeScript,
VariableDeclaration,
@@ -77,17 +77,23 @@ import * as performance from "./_namespaces/ts.performance";
function getModuleTransformer(moduleKind: ModuleKind): TransformerFactory<SourceFile | Bundle> {
switch (moduleKind) {
case ModuleKind.Preserve:
// `transformECMAScriptModule` contains logic for preserving
// CJS input syntax in `--module preserve`
return transformECMAScriptModule;
case ModuleKind.ESNext:
case ModuleKind.ES2022:
case ModuleKind.ES2020:
case ModuleKind.ES2015:
case ModuleKind.Preserve:
return transformECMAScriptModule;
case ModuleKind.System:
return transformSystemModule;
case ModuleKind.Node16:
case ModuleKind.NodeNext:
return transformNodeModule;
case ModuleKind.CommonJS:
// Wraps `transformModule` and `transformECMAScriptModule` and
// selects between them based on the `impliedNodeFormat` of the
// source file.
return transformImpliedNodeFormatDependentModule;
case ModuleKind.System:
return transformSystemModule;
default:
return transformModule;
}
+1 -1
View File
@@ -638,7 +638,7 @@ export function transformDeclarations(context: TransformationContext) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.BindingElement:
case SyntaxKind.VariableDeclaration:
typeNode = resolver.createTypeOfDeclaration(node, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker, shouldAddImplicitUndefined);
typeNode = resolver.createTypeOfDeclaration(node, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker);
break;
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ConstructSignature:
@@ -14,7 +14,7 @@ import {
} from "../../_namespaces/ts";
/** @internal */
export function transformNodeModule(context: TransformationContext) {
export function transformImpliedNodeFormatDependentModule(context: TransformationContext) {
const previousOnSubstituteNode = context.onSubstituteNode;
const previousOnEmitNode = context.onEmitNode;
@@ -30,6 +30,7 @@ export function transformNodeModule(context: TransformationContext) {
const cjsOnSubstituteNode = context.onSubstituteNode;
const cjsOnEmitNode = context.onEmitNode;
const getEmitModuleFormatOfFile = (file: SourceFile) => context.getEmitHost().getEmitModuleFormatOfFile(file);
context.onSubstituteNode = onSubstituteNode;
context.onEmitNode = onEmitNode;
@@ -51,7 +52,7 @@ export function transformNodeModule(context: TransformationContext) {
if (!currentSourceFile) {
return previousOnSubstituteNode(hint, node);
}
if (currentSourceFile.impliedNodeFormat === ModuleKind.ESNext) {
if (getEmitModuleFormatOfFile(currentSourceFile) >= ModuleKind.ES2015) {
return esmOnSubstituteNode(hint, node);
}
return cjsOnSubstituteNode(hint, node);
@@ -65,14 +66,14 @@ export function transformNodeModule(context: TransformationContext) {
if (!currentSourceFile) {
return previousOnEmitNode(hint, node, emitCallback);
}
if (currentSourceFile.impliedNodeFormat === ModuleKind.ESNext) {
if (getEmitModuleFormatOfFile(currentSourceFile) >= ModuleKind.ES2015) {
return esmOnEmitNode(hint, node, emitCallback);
}
return cjsOnEmitNode(hint, node, emitCallback);
}
function getModuleTransformForFile(file: SourceFile): typeof esmTransform {
return file.impliedNodeFormat === ModuleKind.ESNext ? esmTransform : cjsTransform;
return getEmitModuleFormatOfFile(file) >= ModuleKind.ES2015 ? esmTransform : cjsTransform;
}
function transformSourceFile(node: SourceFile) {
+5 -1
View File
@@ -56,6 +56,7 @@ import {
getOriginalNodeId,
getStrictOptionValue,
getTextOfIdentifierOrLiteral,
hasJSFileExtension,
hasJsonModuleEmitEnabled,
hasSyntacticModifier,
Identifier,
@@ -243,6 +244,9 @@ export function transformModule(context: TransformationContext): (x: SourceFile
}
function shouldEmitUnderscoreUnderscoreESModule() {
if (hasJSFileExtension(currentSourceFile.fileName) && currentSourceFile.commonJsModuleIndicator && (!currentSourceFile.externalModuleIndicator || currentSourceFile.externalModuleIndicator === true)) {
return false;
}
if (!currentModuleInfo.exportEquals && isExternalModule(currentSourceFile)) {
return true;
}
@@ -785,7 +789,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
case SyntaxKind.PartiallyEmittedExpression:
return visitPartiallyEmittedExpression(node as PartiallyEmittedExpression, valueIsDiscarded);
case SyntaxKind.CallExpression:
if (isImportCall(node) && currentSourceFile.impliedNodeFormat === undefined) {
if (isImportCall(node) && host.shouldTransformImportCall(currentSourceFile)) {
return visitImportCallExpression(node);
}
break;
+105 -12
View File
@@ -763,6 +763,8 @@ export type JSDocSyntaxKind =
| SyntaxKind.GreaterThanToken
| SyntaxKind.OpenBracketToken
| SyntaxKind.CloseBracketToken
| SyntaxKind.OpenParenToken
| SyntaxKind.CloseParenToken
| SyntaxKind.EqualsToken
| SyntaxKind.CommaToken
| SyntaxKind.DotToken
@@ -4242,6 +4244,18 @@ export interface SourceFileLike {
getLineAndCharacterOfPosition(pos: number): LineAndCharacter;
}
/** @internal */
export interface FutureSourceFile {
readonly path: Path;
readonly fileName: string;
readonly impliedNodeFormat?: ResolutionMode;
readonly packageJsonScope?: PackageJsonInfo;
readonly externalModuleIndicator?: true | undefined;
readonly commonJsModuleIndicator?: true | undefined;
readonly statements: readonly never[];
readonly imports: readonly never[];
}
/** @internal */
export interface RedirectInfo {
/** Source file this redirects to. */
@@ -4769,21 +4783,79 @@ export interface Program extends ScriptReferenceHost {
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
isSourceFileDefaultLibrary(file: SourceFile): boolean;
/**
* Calculates the final resolution mode for a given module reference node. This is the resolution mode explicitly provided via import
* attributes, if present, or the syntax the usage would have if emitted to JavaScript. In `--module node16` or `nodenext`, this may
* depend on the file's `impliedNodeFormat`. In `--module preserve`, it depends only on the input syntax of the reference. In other
* `module` modes, when overriding import attributes are not provided, this function returns `undefined`, as the result would have no
* impact on module resolution, emit, or type checking.
* Calculates the final resolution mode for a given module reference node. This function only returns a result when module resolution
* settings allow differing resolution between ESM imports and CJS requires, or when a mode is explicitly provided via import attributes,
* which cause an `import` or `require` condition to be used during resolution regardless of module resolution settings. In absence of
* overriding attributes, and in modes that support differing resolution, the result indicates the syntax the usage would emit to JavaScript.
* Some examples:
*
* ```ts
* // tsc foo.mts --module nodenext
* import {} from "mod";
* // Result: ESNext - the import emits as ESM due to `impliedNodeFormat` set by .mts file extension
*
* // tsc foo.cts --module nodenext
* import {} from "mod";
* // Result: CommonJS - the import emits as CJS due to `impliedNodeFormat` set by .cts file extension
*
* // tsc foo.ts --module preserve --moduleResolution bundler
* import {} from "mod";
* // Result: ESNext - the import emits as ESM due to `--module preserve` and `--moduleResolution bundler`
* // supports conditional imports/exports
*
* // tsc foo.ts --module preserve --moduleResolution node10
* import {} from "mod";
* // Result: undefined - the import emits as ESM due to `--module preserve`, but `--moduleResolution node10`
* // does not support conditional imports/exports
*
* // tsc foo.ts --module commonjs --moduleResolution node10
* import type {} from "mod" with { "resolution-mode": "import" };
* // Result: ESNext - conditional imports/exports always supported with "resolution-mode" attribute
* ```
*/
getModeForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode;
/**
* Calculates the final resolution mode for an import at some index within a file's `imports` list. This is the resolution mode
* explicitly provided via import attributes, if present, or the syntax the usage would have if emitted to JavaScript. In
* `--module node16` or `nodenext`, this may depend on the file's `impliedNodeFormat`. In `--module preserve`, it depends only on the
* input syntax of the reference. In other `module` modes, when overriding import attributes are not provided, this function returns
* `undefined`, as the result would have no impact on module resolution, emit, or type checking.
* Calculates the final resolution mode for an import at some index within a file's `imports` list. This function only returns a result
* when module resolution settings allow differing resolution between ESM imports and CJS requires, or when a mode is explicitly provided
* via import attributes, which cause an `import` or `require` condition to be used during resolution regardless of module resolution
* settings. In absence of overriding attributes, and in modes that support differing resolution, the result indicates the syntax the
* usage would emit to JavaScript. Some examples:
*
* ```ts
* // tsc foo.mts --module nodenext
* import {} from "mod";
* // Result: ESNext - the import emits as ESM due to `impliedNodeFormat` set by .mts file extension
*
* // tsc foo.cts --module nodenext
* import {} from "mod";
* // Result: CommonJS - the import emits as CJS due to `impliedNodeFormat` set by .cts file extension
*
* // tsc foo.ts --module preserve --moduleResolution bundler
* import {} from "mod";
* // Result: ESNext - the import emits as ESM due to `--module preserve` and `--moduleResolution bundler`
* // supports conditional imports/exports
*
* // tsc foo.ts --module preserve --moduleResolution node10
* import {} from "mod";
* // Result: undefined - the import emits as ESM due to `--module preserve`, but `--moduleResolution node10`
* // does not support conditional imports/exports
*
* // tsc foo.ts --module commonjs --moduleResolution node10
* import type {} from "mod" with { "resolution-mode": "import" };
* // Result: ESNext - conditional imports/exports always supported with "resolution-mode" attribute
* ```
*/
getModeForResolutionAtIndex(file: SourceFile, index: number): ResolutionMode;
/**
* @internal
* The resolution mode to use for module resolution or module specifier resolution
* outside the context of an existing module reference, where
* `program.getModeForUsageLocation` should be used instead.
*/
getDefaultResolutionModeForFile(sourceFile: SourceFile): ResolutionMode;
/** @internal */ getImpliedNodeFormatForEmit(sourceFile: SourceFile): ResolutionMode;
/** @internal */ getEmitModuleFormatOfFile(sourceFile: SourceFile): ModuleKind;
/** @internal */ shouldTransformImportCall(sourceFile: SourceFile): boolean;
// For testing purposes only.
// This is set on created program to let us know how the program was created using old program
@@ -4838,6 +4910,7 @@ export interface Program extends ScriptReferenceHost {
/** @internal */ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined;
/** @internal */ getRedirectReferenceForResolutionFromSourceOfProject(filePath: Path): ResolvedProjectReference | undefined;
/** @internal */ isSourceOfProjectReferenceRedirect(fileName: string): boolean;
/** @internal */ getCompilerOptionsForFile(file: SourceFile): CompilerOptions;
/** @internal */ getBuildInfo?(): BuildInfo;
/** @internal */ emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
/**
@@ -4953,8 +5026,12 @@ export interface TypeCheckerHost extends ModuleSpecifierResolutionHost {
getSourceFile(fileName: string): SourceFile | undefined;
getProjectReferenceRedirect(fileName: string): string | undefined;
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
getEmitSyntaxForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode;
getRedirectReferenceForResolutionFromSourceOfProject(filePath: Path): ResolvedProjectReference | undefined;
getModeForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode;
getDefaultResolutionModeForFile(sourceFile: SourceFile): ResolutionMode;
getImpliedNodeFormatForEmit(sourceFile: SourceFile): ResolutionMode;
getEmitModuleFormatOfFile(sourceFile: SourceFile): ModuleKind;
getResolvedModule(f: SourceFile, moduleName: string, mode: ResolutionMode): ResolvedModuleWithFailedLookupLocations | undefined;
@@ -5609,6 +5686,9 @@ export interface RequireVariableDeclarationList extends VariableDeclarationList
readonly declarations: NodeArray<VariableDeclarationInitializedTo<RequireOrImportCall>>;
}
/** @internal */
export type CanHaveModuleSpecifier = AnyImportOrBareOrAccessedRequire | AliasDeclarationNode | ExportDeclaration | ImportTypeNode;
/** @internal */
export type LateVisibilityPaintedStatement =
| AnyImportOrJsDocImport
@@ -5714,7 +5794,7 @@ export interface EmitResolver {
requiresAddingImplicitUndefined(node: ParameterDeclaration): boolean;
isExpandoFunctionDeclaration(node: FunctionDeclaration): boolean;
getPropertiesOfContainerFunction(node: Declaration): Symbol[];
createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression | BinaryExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined;
createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression | BinaryExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
createReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
createTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, tracker: SymbolTracker): Expression;
@@ -7498,6 +7578,7 @@ export const enum ScriptTarget {
ES2020 = 7,
ES2021 = 8,
ES2022 = 9,
ES2023 = 10,
ESNext = 99,
JSON = 100,
Latest = ESNext,
@@ -7540,6 +7621,9 @@ export interface ConfigFileSpecs {
validatedFilesSpec: readonly string[] | undefined;
validatedIncludeSpecs: readonly string[] | undefined;
validatedExcludeSpecs: readonly string[] | undefined;
validatedFilesSpecBeforeSubstitution: readonly string[] | undefined;
validatedIncludeSpecsBeforeSubstitution: readonly string[] | undefined;
validatedExcludeSpecsBeforeSubstitution: readonly string[] | undefined;
pathPatterns: readonly (string | Pattern)[] | undefined;
isDefaultIncludeSpec: boolean;
}
@@ -7586,7 +7670,8 @@ export interface CommandLineOptionBase {
affectsBuildInfo?: true; // true if this options should be emitted in buildInfo
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
disallowNullOrUndefined?: true; // If set option does not allow setting null
disallowNullOrUndefined?: true; // If set option does not allow setting null
allowConfigDirTemplateSubstitution?: true; // If set option allows substitution of `${configDir}` in the value
}
/** @internal */
@@ -7677,6 +7762,9 @@ export const enum CharacterCodes {
mathematicalSpace = 0x205F,
ogham = 0x1680,
// Unicode replacement character produced when a byte sequence is invalid
replacementCharacter = 0xFFFD,
_ = 0x5F,
$ = 0x24,
@@ -8392,6 +8480,8 @@ export interface EmitHost extends ScriptReferenceHost, ModuleSpecifierResolution
getCanonicalFileName(fileName: string): string;
isEmitBlocked(emitFileName: string): boolean;
shouldTransformImportCall(sourceFile: SourceFile): boolean;
getEmitModuleFormatOfFile(sourceFile: SourceFile): ModuleKind;
writeFile: WriteFileCallback;
getBuildInfo(): BuildInfo | undefined;
@@ -9632,6 +9722,7 @@ export interface PrinterOptions {
omitTrailingSemicolon?: boolean;
noEmitHelpers?: boolean;
/** @internal */ module?: CompilerOptions["module"];
/** @internal */ moduleResolution?: CompilerOptions["moduleResolution"];
/** @internal */ target?: CompilerOptions["target"];
/** @internal */ sourceMap?: boolean;
/** @internal */ inlineSourceMap?: boolean;
@@ -9766,6 +9857,8 @@ export interface ModuleSpecifierResolutionHost {
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
getFileIncludeReasons(): MultiMap<Path, FileIncludeReason>;
getCommonSourceDirectory(): string;
getDefaultResolutionModeForFile(sourceFile: SourceFile): ResolutionMode;
getModeForResolutionAtIndex(file: SourceFile, index: number): ResolutionMode;
}
/** @internal */
+650 -12
View File
@@ -5,12 +5,12 @@ import {
addRange,
affectsDeclarationPathOptionDeclarations,
affectsEmitOptionDeclarations,
AliasDeclarationNode,
AllAccessorDeclarations,
AmbientModuleDeclaration,
AmpersandAmpersandEqualsToken,
AnyImportOrBareOrAccessedRequire,
AnyImportOrReExport,
AnyImportOrRequireStatement,
AnyImportSyntax,
AnyValidImportOrReExport,
append,
@@ -38,7 +38,9 @@ import {
CallLikeExpression,
CallSignatureDeclaration,
canHaveDecorators,
canHaveLocals,
canHaveModifiers,
type CanHaveModuleSpecifier,
CaseBlock,
CaseClause,
CaseOrDefaultClause,
@@ -165,6 +167,7 @@ import {
getCommonSourceDirectory,
getContainerFlags,
getDirectoryPath,
getImpliedNodeFormatForEmitWorker,
getJSDocAugmentsTag,
getJSDocDeprecatedTagNoCache,
getJSDocImplementsTags,
@@ -232,16 +235,19 @@ import {
IndexInfo,
indexOfAnyCharCode,
IndexSignatureDeclaration,
InferTypeNode,
InitializedVariableDeclaration,
insertSorted,
InstanceofExpression,
InterfaceDeclaration,
InternalEmitFlags,
InternalSymbolName,
isAccessor,
isAnyDirectorySeparator,
isArray,
isArrayLiteralExpression,
isArrowFunction,
isAssertionExpression,
isAutoAccessorPropertyDeclaration,
isBigIntLiteral,
isBinaryExpression,
@@ -256,6 +262,7 @@ import {
isCommaListExpression,
isComputedPropertyName,
isConstructorDeclaration,
isConstTypeReference,
isDeclaration,
isDecorator,
isElementAccessExpression,
@@ -292,6 +299,7 @@ import {
isJSDocOverloadTag,
isJSDocParameterTag,
isJSDocPropertyLikeTag,
isJSDocReturnTag,
isJSDocSatisfiesTag,
isJSDocSignature,
isJSDocTag,
@@ -313,15 +321,19 @@ import {
isMethodOrAccessor,
isModifierLike,
isModuleDeclaration,
isModuleOrEnumDeclaration,
isNamedDeclaration,
isNamespaceExport,
isNamespaceExportDeclaration,
isNamespaceImport,
isNonNullExpression,
isNoSubstitutionTemplateLiteral,
isNullishCoalesce,
isNumericLiteral,
isObjectBindingPattern,
isObjectLiteralExpression,
isOmittedExpression,
isOptionalChain,
isParameter,
isParameterPropertyDeclaration,
isParenthesizedExpression,
@@ -347,6 +359,7 @@ import {
isTypeLiteralNode,
isTypeNode,
isTypeParameterDeclaration,
isTypeQueryNode,
isTypeReferenceNode,
isVariableDeclaration,
isVariableStatement,
@@ -474,7 +487,9 @@ import {
ResolvedModuleWithFailedLookupLocations,
ResolvedTypeReferenceDirective,
ResolvedTypeReferenceDirectiveWithFailedLookupLocations,
returnFalse,
ReturnStatement,
returnUndefined,
SatisfiesExpression,
ScriptKind,
ScriptTarget,
@@ -1743,7 +1758,7 @@ function canUseOriginalText(node: LiteralLikeNode, flags: GetLiteralTextFlags):
/** @internal */
export function getTextOfConstantValue(value: string | number) {
return isString(value) ? '"' + escapeNonAsciiString(value) + '"' : "" + value;
return isString(value) ? `"${escapeString(value)}"` : "" + value;
}
// Make an identifier from an external module name by extracting the string after the last "/" and replacing
@@ -1981,6 +1996,11 @@ export function isAnyImportOrBareOrAccessedRequire(node: Node): node is AnyImpor
return isAnyImportSyntax(node) || isVariableDeclarationInitializedToBareOrAccessedRequire(node);
}
/** @internal */
export function isAnyImportOrRequireStatement(node: Node): node is AnyImportOrRequireStatement {
return isAnyImportSyntax(node) || isRequireVariableStatement(node);
}
/** @internal */
export function isLateVisibilityPaintedStatement(node: Node): node is LateVisibilityPaintedStatement {
switch (node.kind) {
@@ -2296,6 +2316,11 @@ export function getErrorSpanForNode(sourceFile: SourceFile, node: Node): TextSpa
return createTextSpanFromBounds(pos, errorNode.end);
}
/** @internal */
export function isGlobalSourceFile(node: Node) {
return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node as SourceFile);
}
/** @internal */
export function isExternalOrCommonJsModule(file: SourceFile): boolean {
return (file.externalModuleIndicator || file.commonJsModuleIndicator) !== undefined;
@@ -3457,6 +3482,11 @@ export function isInternalModuleImportEqualsDeclaration(node: Node): node is Imp
return node.kind === SyntaxKind.ImportEqualsDeclaration && (node as ImportEqualsDeclaration).moduleReference.kind !== SyntaxKind.ExternalModuleReference;
}
/** @internal */
export function isFullSourceFile(sourceFile: object): sourceFile is SourceFile {
return (sourceFile as Partial<SourceFile>)?.kind === SyntaxKind.SourceFile;
}
/** @internal */
export function isSourceFileJS(file: SourceFile): boolean {
return isInJSFile(file);
@@ -3984,7 +4014,26 @@ export function isFunctionSymbol(symbol: Symbol | undefined) {
}
/** @internal */
export function tryGetModuleSpecifierFromDeclaration(node: AnyImportOrBareOrAccessedRequire | AliasDeclarationNode | ExportDeclaration | ImportTypeNode | JSDocImportTag): StringLiteralLike | undefined {
export function canHaveModuleSpecifier(node: Node | undefined): node is CanHaveModuleSpecifier {
switch (node?.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.BindingElement:
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportClause:
case SyntaxKind.NamespaceExport:
case SyntaxKind.NamespaceImport:
case SyntaxKind.ExportSpecifier:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ImportType:
return true;
}
return false;
}
/** @internal */
export function tryGetModuleSpecifierFromDeclaration(node: CanHaveModuleSpecifier | JSDocImportTag): StringLiteralLike | undefined {
switch (node.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.BindingElement:
@@ -8441,11 +8490,13 @@ function isFileModuleFromUsingJSXTag(file: SourceFile): Node | undefined {
* Note that this requires file.impliedNodeFormat be set already; meaning it must be set very early on
* in SourceFile construction.
*/
function isFileForcedToBeModuleByFormat(file: SourceFile): true | undefined {
function isFileForcedToBeModuleByFormat(file: SourceFile, options: CompilerOptions): true | undefined {
// Excludes declaration files - they still require an explicit `export {}` or the like
// for back compat purposes. The only non-declaration files _not_ forced to be a module are `.js` files
// that aren't esm-mode (meaning not in a `type: module` scope).
return (file.impliedNodeFormat === ModuleKind.ESNext || (fileExtensionIsOneOf(file.fileName, [Extension.Cjs, Extension.Cts, Extension.Mjs, Extension.Mts]))) && !file.isDeclarationFile ? true : undefined;
//
// TODO: extension check never considered compilerOptions; should impliedNodeFormat?
return (getImpliedNodeFormatForEmitWorker(file, options) === ModuleKind.ESNext || (fileExtensionIsOneOf(file.fileName, [Extension.Cjs, Extension.Cts, Extension.Mjs, Extension.Mts]))) && !file.isDeclarationFile ? true : undefined;
}
/** @internal */
@@ -8466,17 +8517,29 @@ export function getSetExternalModuleIndicator(options: CompilerOptions): (file:
// If module is nodenext or node16, all esm format files are modules
// If jsx is react-jsx or react-jsxdev then jsx tags force module-ness
// otherwise, the presence of import or export statments (or import.meta) implies module-ness
const checks: ((file: SourceFile) => Node | true | undefined)[] = [isFileProbablyExternalModule];
const checks: ((file: SourceFile, options: CompilerOptions) => Node | true | undefined)[] = [isFileProbablyExternalModule];
if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
checks.push(isFileModuleFromUsingJSXTag);
}
checks.push(isFileForcedToBeModuleByFormat);
const combined = or(...checks);
const callback = (file: SourceFile) => void (file.externalModuleIndicator = combined(file));
const callback = (file: SourceFile) => void (file.externalModuleIndicator = combined(file, options));
return callback;
}
}
/**
* @internal
* Returns true if an `import` and a `require` of the same module specifier
* can resolve to a different file.
*/
export function importSyntaxAffectsModuleResolution(options: CompilerOptions) {
const moduleResolution = getEmitModuleResolutionKind(options);
return ModuleResolutionKind.Node16 <= moduleResolution && moduleResolution <= ModuleResolutionKind.NodeNext
|| getResolvePackageJsonExports(options)
|| getResolvePackageJsonImports(options);
}
type CompilerOptionKeys = keyof { [K in keyof CompilerOptions as string extends K ? never : K]: any; };
function createComputedCompilerOptions<T extends Record<string, CompilerOptionKeys[]>>(
options: {
@@ -9457,7 +9520,7 @@ export function usesExtensionsOnImports({ imports }: SourceFile, hasExtension: (
}
/** @internal */
export function getModuleSpecifierEndingPreference(preference: UserPreferences["importModuleSpecifierEnding"], resolutionMode: ResolutionMode, compilerOptions: CompilerOptions, sourceFile: SourceFile): ModuleSpecifierEnding {
export function getModuleSpecifierEndingPreference(preference: UserPreferences["importModuleSpecifierEnding"], resolutionMode: ResolutionMode, compilerOptions: CompilerOptions, sourceFile?: SourceFile): ModuleSpecifierEnding {
const moduleResolution = getEmitModuleResolutionKind(compilerOptions);
const moduleResolutionIsNodeNext = ModuleResolutionKind.Node16 <= moduleResolution && moduleResolution <= ModuleResolutionKind.NodeNext;
if (preference === "js" || resolutionMode === ModuleKind.ESNext && moduleResolutionIsNodeNext) {
@@ -9484,22 +9547,22 @@ export function getModuleSpecifierEndingPreference(preference: UserPreferences["
// accurately, and more importantly, literally nobody wants `Index` and its existence is a mystery.
if (!shouldAllowImportingTsExtension(compilerOptions)) {
// If .ts imports are not valid, we only need to see one .js import to go with that.
return usesExtensionsOnImports(sourceFile) ? ModuleSpecifierEnding.JsExtension : ModuleSpecifierEnding.Minimal;
return sourceFile && usesExtensionsOnImports(sourceFile) ? ModuleSpecifierEnding.JsExtension : ModuleSpecifierEnding.Minimal;
}
return inferPreference();
function inferPreference() {
let usesJsExtensions = false;
const specifiers = sourceFile.imports.length ? sourceFile.imports :
isSourceFileJS(sourceFile) ? getRequiresAtTopOfFile(sourceFile).map(r => r.arguments[0]) :
const specifiers = sourceFile?.imports.length ? sourceFile.imports :
sourceFile && isSourceFileJS(sourceFile) ? getRequiresAtTopOfFile(sourceFile).map(r => r.arguments[0]) :
emptyArray;
for (const specifier of specifiers) {
if (pathIsRelative(specifier.text)) {
if (
moduleResolutionIsNodeNext &&
resolutionMode === ModuleKind.CommonJS &&
getModeForUsageLocation(sourceFile, specifier, compilerOptions) === ModuleKind.ESNext
getModeForUsageLocation(sourceFile!, specifier, compilerOptions) === ModuleKind.ESNext
) {
// We're trying to decide a preference for a CommonJS module specifier, but looking at an ESM import.
continue;
@@ -10667,3 +10730,578 @@ export function createEvaluator({ evaluateElementAccessExpression, evaluateEntit
}
return evaluate;
}
/** @internal */
export function isConstAssertion(location: Node) {
return (isAssertionExpression(location) && isConstTypeReference(location.type))
|| (isJSDocTypeTag(location) && isConstTypeReference(location.typeExpression));
}
/** @internal */
export function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration | undefined {
const members = node.members;
for (const member of members) {
if (member.kind === SyntaxKind.Constructor && nodeIsPresent((member as ConstructorDeclaration).body)) {
return member as ConstructorDeclaration;
}
}
}
/** @internal */
export function createNameResolver({
compilerOptions,
requireSymbol,
argumentsSymbol,
error,
getSymbolOfDeclaration,
globals,
lookup,
setRequiresScopeChangeCache = returnUndefined,
getRequiresScopeChangeCache = returnUndefined,
onPropertyWithInvalidInitializer = returnFalse,
onFailedToResolveSymbol = returnUndefined,
onSuccessfullyResolvedSymbol = returnUndefined,
}: {
compilerOptions: CompilerOptions;
getSymbolOfDeclaration: (node: Declaration) => Symbol;
error: (location: Node | undefined, message: DiagnosticMessage, ...args: DiagnosticArguments) => void;
globals: SymbolTable;
argumentsSymbol: Symbol;
requireSymbol: Symbol;
lookup: (symbols: SymbolTable, name: __String, meaning: SymbolFlags) => Symbol | undefined;
setRequiresScopeChangeCache: undefined | ((node: FunctionLikeDeclaration, value: boolean) => void);
getRequiresScopeChangeCache: undefined | ((node: FunctionLikeDeclaration) => boolean | undefined);
onPropertyWithInvalidInitializer?: (location: Node | undefined, name: __String, declaration: PropertyDeclaration, result: Symbol | undefined) => boolean;
onFailedToResolveSymbol?: (
location: Node | undefined,
name: __String | Identifier,
meaning: SymbolFlags,
nameNotFoundMessage: DiagnosticMessage,
) => void;
onSuccessfullyResolvedSymbol?: (
location: Node | undefined,
result: Symbol,
meaning: SymbolFlags,
lastLocation: Node | undefined,
associatedDeclarationForContainingInitializerOrBindingName: ParameterDeclaration | BindingElement | undefined,
withinDeferredContext: boolean,
) => void;
}) {
/* eslint-disable no-var */
var isolatedModulesLikeFlagName = compilerOptions.verbatimModuleSyntax ? "verbatimModuleSyntax" : "isolatedModules";
/* eslint-disable no-var */
var emitStandardClassFields = getEmitStandardClassFields(compilerOptions);
var emptySymbols = createSymbolTable();
return resolveNameHelper;
function resolveNameHelper(
location: Node | undefined,
nameArg: __String | Identifier,
meaning: SymbolFlags,
nameNotFoundMessage: DiagnosticMessage | undefined,
isUse: boolean,
excludeGlobals?: boolean,
): Symbol | undefined {
const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location
let result: Symbol | undefined;
let lastLocation: Node | undefined;
let lastSelfReferenceLocation: Declaration | undefined;
let propertyWithInvalidInitializer: PropertyDeclaration | undefined;
let associatedDeclarationForContainingInitializerOrBindingName: ParameterDeclaration | BindingElement | undefined;
let withinDeferredContext = false;
let grandparent: Node;
const name = isString(nameArg) ? nameArg : (nameArg as Identifier).escapedText;
loop:
while (location) {
if (name === "const" && isConstAssertion(location)) {
// `const` in an `as const` has no symbol, but issues no error because there is no *actual* lookup of the type
// (it refers to the constant type of the expression instead)
return undefined;
}
if (isModuleOrEnumDeclaration(location) && lastLocation && location.name === lastLocation) {
// If lastLocation is the name of a namespace or enum, skip the parent since it will have is own locals that could
// conflict.
lastLocation = location;
location = location.parent;
}
// Locals of a source file are not in scope (because they get merged into the global symbol table)
if (canHaveLocals(location) && location.locals && !isGlobalSourceFile(location)) {
if (result = lookup(location.locals, name, meaning)) {
let useResult = true;
if (isFunctionLike(location) && lastLocation && lastLocation !== (location as FunctionLikeDeclaration).body) {
// symbol lookup restrictions for function-like declarations
// - Type parameters of a function are in scope in the entire function declaration, including the parameter
// list and return type. However, local types are only in scope in the function body.
// - parameters are only in the scope of function body
// This restriction does not apply to JSDoc comment types because they are parented
// at a higher level than type parameters would normally be
if (meaning & result.flags & SymbolFlags.Type && lastLocation.kind !== SyntaxKind.JSDoc) {
useResult = result.flags & SymbolFlags.TypeParameter
// type parameters are visible in parameter list, return type and type parameter list
? lastLocation === (location as FunctionLikeDeclaration).type ||
lastLocation.kind === SyntaxKind.Parameter ||
lastLocation.kind === SyntaxKind.JSDocParameterTag ||
lastLocation.kind === SyntaxKind.JSDocReturnTag ||
lastLocation.kind === SyntaxKind.TypeParameter
// local types not visible outside the function body
: false;
}
if (meaning & result.flags & SymbolFlags.Variable) {
// expression inside parameter will lookup as normal variable scope when targeting es2015+
if (useOuterVariableScopeInParameter(result, location, lastLocation)) {
useResult = false;
}
else if (result.flags & SymbolFlags.FunctionScopedVariable) {
// parameters are visible only inside function body, parameter list and return type
// technically for parameter list case here we might mix parameters and variables declared in function,
// however it is detected separately when checking initializers of parameters
// to make sure that they reference no variables declared after them.
useResult = lastLocation.kind === SyntaxKind.Parameter ||
(
lastLocation === (location as FunctionLikeDeclaration).type &&
!!findAncestor(result.valueDeclaration, isParameter)
);
}
}
}
else if (location.kind === SyntaxKind.ConditionalType) {
// A type parameter declared using 'infer T' in a conditional type is visible only in
// the true branch of the conditional type.
useResult = lastLocation === location.trueType;
}
if (useResult) {
break loop;
}
else {
result = undefined;
}
}
}
withinDeferredContext = withinDeferredContext || getIsDeferredContext(location, lastLocation);
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalOrCommonJsModule(location as SourceFile)) break;
// falls through
case SyntaxKind.ModuleDeclaration:
const moduleExports = getSymbolOfDeclaration(location as SourceFile | ModuleDeclaration)?.exports || emptySymbols;
if (location.kind === SyntaxKind.SourceFile || (isModuleDeclaration(location) && location.flags & NodeFlags.Ambient && !isGlobalScopeAugmentation(location))) {
// It's an external module. First see if the module has an export default and if the local
// name of that export default matches.
if (result = moduleExports.get(InternalSymbolName.Default)) {
const localSymbol = getLocalSymbolForExportDefault(result);
if (localSymbol && (result.flags & meaning) && localSymbol.escapedName === name) {
break loop;
}
result = undefined;
}
// Because of module/namespace merging, a module's exports are in scope,
// yet we never want to treat an export specifier as putting a member in scope.
// Therefore, if the name we find is purely an export specifier, it is not actually considered in scope.
// Two things to note about this:
// 1. We have to check this without calling getSymbol. The problem with calling getSymbol
// on an export specifier is that it might find the export specifier itself, and try to
// resolve it as an alias. This will cause the checker to consider the export specifier
// a circular alias reference when it might not be.
// 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely*
// an alias. If we used &, we'd be throwing out symbols that have non alias aspects,
// which is not the desired behavior.
const moduleExport = moduleExports.get(name);
if (
moduleExport &&
moduleExport.flags === SymbolFlags.Alias &&
(getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier) || getDeclarationOfKind(moduleExport, SyntaxKind.NamespaceExport))
) {
break;
}
}
// ES6 exports are also visible locally (except for 'default'), but commonjs exports are not (except typedefs)
if (name !== InternalSymbolName.Default && (result = lookup(moduleExports, name, meaning & SymbolFlags.ModuleMember))) {
if (isSourceFile(location) && location.commonJsModuleIndicator && !result.declarations?.some(isJSDocTypeAlias)) {
result = undefined;
}
else {
break loop;
}
}
break;
case SyntaxKind.EnumDeclaration:
if (result = lookup(getSymbolOfDeclaration(location as EnumDeclaration)?.exports || emptySymbols, name, meaning & SymbolFlags.EnumMember)) {
if (nameNotFoundMessage && getIsolatedModules(compilerOptions) && !(location.flags & NodeFlags.Ambient) && getSourceFileOfNode(location) !== getSourceFileOfNode(result.valueDeclaration)) {
error(
originalLocation,
Diagnostics.Cannot_access_0_from_another_file_without_qualification_when_1_is_enabled_Use_2_instead,
unescapeLeadingUnderscores(name),
isolatedModulesLikeFlagName,
`${unescapeLeadingUnderscores(getSymbolOfDeclaration(location as EnumDeclaration).escapedName)}.${unescapeLeadingUnderscores(name)}`,
);
}
break loop;
}
break;
case SyntaxKind.PropertyDeclaration:
// TypeScript 1.0 spec (April 2014): 8.4.1
// Initializer expressions for instance member variables are evaluated in the scope
// of the class constructor body but are not permitted to reference parameters or
// local variables of the constructor. This effectively means that entities from outer scopes
// by the same name as a constructor parameter or local variable are inaccessible
// in initializer expressions for instance member variables.
if (!isStatic(location)) {
const ctor = findConstructorDeclaration(location.parent as ClassLikeDeclaration);
if (ctor && ctor.locals) {
if (lookup(ctor.locals, name, meaning & SymbolFlags.Value)) {
// Remember the property node, it will be used later to report appropriate error
Debug.assertNode(location, isPropertyDeclaration);
propertyWithInvalidInitializer = location;
}
}
}
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.InterfaceDeclaration:
// The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals
// These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would
// trigger resolving late-bound names, which we may already be in the process of doing while we're here!
if (result = lookup(getSymbolOfDeclaration(location as ClassLikeDeclaration | InterfaceDeclaration).members || emptySymbols, name, meaning & SymbolFlags.Type)) {
if (!isTypeParameterSymbolDeclaredInContainer(result, location)) {
// ignore type parameters not declared in this container
result = undefined;
break;
}
if (lastLocation && isStatic(lastLocation)) {
// TypeScript 1.0 spec (April 2014): 3.4.1
// The scope of a type parameter extends over the entire declaration with which the type
// parameter list is associated, with the exception of static member declarations in classes.
if (nameNotFoundMessage) {
error(originalLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters);
}
return undefined;
}
break loop;
}
if (isClassExpression(location) && meaning & SymbolFlags.Class) {
const className = location.name;
if (className && name === className.escapedText) {
result = location.symbol;
break loop;
}
}
break;
case SyntaxKind.ExpressionWithTypeArguments:
// The type parameters of a class are not in scope in the base class expression.
if (lastLocation === (location as ExpressionWithTypeArguments).expression && (location.parent as HeritageClause).token === SyntaxKind.ExtendsKeyword) {
const container = location.parent.parent;
if (isClassLike(container) && (result = lookup(getSymbolOfDeclaration(container).members!, name, meaning & SymbolFlags.Type))) {
if (nameNotFoundMessage) {
error(originalLocation, Diagnostics.Base_class_expressions_cannot_reference_class_type_parameters);
}
return undefined;
}
}
break;
// It is not legal to reference a class's own type parameters from a computed property name that
// belongs to the class. For example:
//
// function foo<T>() { return '' }
// class C<T> { // <-- Class's own type parameter T
// [foo<T>()]() { } // <-- Reference to T from class's own computed property
// }
//
case SyntaxKind.ComputedPropertyName:
grandparent = location.parent.parent;
if (isClassLike(grandparent) || grandparent.kind === SyntaxKind.InterfaceDeclaration) {
// A reference to this grandparent's type parameters would be an error
if (result = lookup(getSymbolOfDeclaration(grandparent as ClassLikeDeclaration | InterfaceDeclaration).members!, name, meaning & SymbolFlags.Type)) {
if (nameNotFoundMessage) {
error(originalLocation, Diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type);
}
return undefined;
}
}
break;
case SyntaxKind.ArrowFunction:
// when targeting ES6 or higher there is no 'arguments' in an arrow function
// for lower compile targets the resolved symbol is used to emit an error
if (getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015) {
break;
}
// falls through
case SyntaxKind.MethodDeclaration:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionDeclaration:
if (meaning & SymbolFlags.Variable && name === "arguments") {
result = argumentsSymbol;
break loop;
}
break;
case SyntaxKind.FunctionExpression:
if (meaning & SymbolFlags.Variable && name === "arguments") {
result = argumentsSymbol;
break loop;
}
if (meaning & SymbolFlags.Function) {
const functionName = (location as FunctionExpression).name;
if (functionName && name === functionName.escapedText) {
result = (location as FunctionExpression).symbol;
break loop;
}
}
break;
case SyntaxKind.Decorator:
// Decorators are resolved at the class declaration. Resolving at the parameter
// or member would result in looking up locals in the method.
//
// function y() {}
// class C {
// method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
// }
//
if (location.parent && location.parent.kind === SyntaxKind.Parameter) {
location = location.parent;
}
//
// function y() {}
// class C {
// @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method.
// }
//
// class Decorators are resolved outside of the class to avoid referencing type parameters of that class.
//
// type T = number;
// declare function y(x: T): any;
// @param(1 as T) // <-- T should resolve to the type alias outside of class C
// class C<T> {}
if (location.parent && (isClassElement(location.parent) || location.parent.kind === SyntaxKind.ClassDeclaration)) {
location = location.parent;
}
break;
case SyntaxKind.JSDocTypedefTag:
case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.JSDocEnumTag:
case SyntaxKind.JSDocImportTag:
// js type aliases do not resolve names from their host, so skip past it
const root = getJSDocRoot(location);
if (root) {
location = root.parent;
}
break;
case SyntaxKind.Parameter:
if (
lastLocation && (
lastLocation === (location as ParameterDeclaration).initializer ||
lastLocation === (location as ParameterDeclaration).name && isBindingPattern(lastLocation)
)
) {
if (!associatedDeclarationForContainingInitializerOrBindingName) {
associatedDeclarationForContainingInitializerOrBindingName = location as ParameterDeclaration;
}
}
break;
case SyntaxKind.BindingElement:
if (
lastLocation && (
lastLocation === (location as BindingElement).initializer ||
lastLocation === (location as BindingElement).name && isBindingPattern(lastLocation)
)
) {
if (isParameterDeclaration(location as BindingElement) && !associatedDeclarationForContainingInitializerOrBindingName) {
associatedDeclarationForContainingInitializerOrBindingName = location as BindingElement;
}
}
break;
case SyntaxKind.InferType:
if (meaning & SymbolFlags.TypeParameter) {
const parameterName = (location as InferTypeNode).typeParameter.name;
if (parameterName && name === parameterName.escapedText) {
result = (location as InferTypeNode).typeParameter.symbol;
break loop;
}
}
break;
case SyntaxKind.ExportSpecifier:
// External module export bindings shouldn't be resolved to local symbols.
if (
lastLocation &&
lastLocation === (location as ExportSpecifier).propertyName &&
(location as ExportSpecifier).parent.parent.moduleSpecifier
) {
location = location.parent.parent.parent;
}
break;
}
if (isSelfReferenceLocation(location)) {
lastSelfReferenceLocation = location;
}
lastLocation = location;
location = isJSDocTemplateTag(location) ? getEffectiveContainerForJSDocTemplateTag(location) || location.parent :
isJSDocParameterTag(location) || isJSDocReturnTag(location) ? getHostSignatureFromJSDoc(location) || location.parent :
location.parent;
}
// We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
// If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself.
// That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used.
if (isUse && result && (!lastSelfReferenceLocation || result !== lastSelfReferenceLocation.symbol)) {
result.isReferenced! |= meaning;
}
if (!result) {
if (lastLocation) {
Debug.assertNode(lastLocation, isSourceFile);
if (lastLocation.commonJsModuleIndicator && name === "exports" && meaning & lastLocation.symbol.flags) {
return lastLocation.symbol;
}
}
if (!excludeGlobals) {
result = lookup(globals, name, meaning);
}
}
if (!result) {
if (originalLocation && isInJSFile(originalLocation) && originalLocation.parent) {
if (isRequireCall(originalLocation.parent, /*requireStringLiteralLikeArgument*/ false)) {
return requireSymbol;
}
}
}
if (nameNotFoundMessage) {
if (propertyWithInvalidInitializer && onPropertyWithInvalidInitializer(originalLocation, name, propertyWithInvalidInitializer, result)) {
return undefined;
}
if (!result) {
onFailedToResolveSymbol(originalLocation, nameArg, meaning, nameNotFoundMessage);
}
else {
onSuccessfullyResolvedSymbol(originalLocation, result, meaning, lastLocation, associatedDeclarationForContainingInitializerOrBindingName, withinDeferredContext);
}
}
return result;
}
function useOuterVariableScopeInParameter(result: Symbol, location: Node, lastLocation: Node) {
const target = getEmitScriptTarget(compilerOptions);
const functionLocation = location as FunctionLikeDeclaration;
if (
isParameter(lastLocation)
&& functionLocation.body
&& result.valueDeclaration
&& result.valueDeclaration.pos >= functionLocation.body.pos
&& result.valueDeclaration.end <= functionLocation.body.end
) {
// check for several cases where we introduce temporaries that require moving the name/initializer of the parameter to the body
// - static field in a class expression
// - optional chaining pre-es2020
// - nullish coalesce pre-es2020
// - spread assignment in binding pattern pre-es2017
if (target >= ScriptTarget.ES2015) {
let declarationRequiresScopeChange = getRequiresScopeChangeCache(functionLocation);
if (declarationRequiresScopeChange === undefined) {
declarationRequiresScopeChange = forEach(functionLocation.parameters, requiresScopeChange) || false;
setRequiresScopeChangeCache(functionLocation, declarationRequiresScopeChange);
}
return !declarationRequiresScopeChange;
}
}
return false;
function requiresScopeChange(node: ParameterDeclaration): boolean {
return requiresScopeChangeWorker(node.name)
|| !!node.initializer && requiresScopeChangeWorker(node.initializer);
}
function requiresScopeChangeWorker(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ArrowFunction:
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.Constructor:
// do not descend into these
return false;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.PropertyAssignment:
return requiresScopeChangeWorker((node as MethodDeclaration | AccessorDeclaration | PropertyAssignment).name);
case SyntaxKind.PropertyDeclaration:
// static properties in classes introduce temporary variables
if (hasStaticModifier(node)) {
return !emitStandardClassFields;
}
return requiresScopeChangeWorker((node as PropertyDeclaration).name);
default:
// null coalesce and optional chain pre-es2020 produce temporary variables
if (isNullishCoalesce(node) || isOptionalChain(node)) {
return target < ScriptTarget.ES2020;
}
if (isBindingElement(node) && node.dotDotDotToken && isObjectBindingPattern(node.parent)) {
return target < ScriptTarget.ES2017;
}
if (isTypeNode(node)) return false;
return forEachChild(node, requiresScopeChangeWorker) || false;
}
}
}
function getIsDeferredContext(location: Node, lastLocation: Node | undefined): boolean {
if (location.kind !== SyntaxKind.ArrowFunction && location.kind !== SyntaxKind.FunctionExpression) {
// initializers in instance property declaration of class like entities are executed in constructor and thus deferred
return isTypeQueryNode(location) || ((
isFunctionLikeDeclaration(location) ||
(location.kind === SyntaxKind.PropertyDeclaration && !isStatic(location))
) && (!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;
}
// generator functions and async functions are not inlined in control flow when immediately invoked
if ((location as FunctionExpression | ArrowFunction).asteriskToken || hasSyntacticModifier(location, ModifierFlags.Async)) {
return true;
}
return !getImmediatelyInvokedFunctionExpression(location);
}
type SelfReferenceLocation =
| FunctionDeclaration
| ClassDeclaration
| InterfaceDeclaration
| EnumDeclaration
| TypeAliasDeclaration
| ModuleDeclaration;
function isSelfReferenceLocation(node: Node): node is SelfReferenceLocation {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.ModuleDeclaration: // For `namespace N { N; }`
return true;
default:
return false;
}
}
function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) {
if (symbol.declarations) {
for (const decl of symbol.declarations) {
if (decl.kind === SyntaxKind.TypeParameter) {
const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent;
if (parent === container) {
return !(isJSDocTemplateTag(decl.parent) && find((decl.parent.parent as JSDoc).tags, isJSDocTypeAlias));
}
}
}
}
return false;
}
}
+2
View File
@@ -311,6 +311,8 @@ export function getDefaultLibFileName(options: CompilerOptions): string {
switch (getEmitScriptTarget(options)) {
case ScriptTarget.ESNext:
return "lib.esnext.full.d.ts";
case ScriptTarget.ES2023:
return "lib.es2023.full.d.ts";
case ScriptTarget.ES2022:
return "lib.es2022.full.d.ts";
case ScriptTarget.ES2021:
+9 -4
View File
@@ -44,6 +44,7 @@ import {
FileWatcher,
filter,
find,
findIndex,
flattenDiagnosticMessageText,
forEach,
forEachEntry,
@@ -56,6 +57,7 @@ import {
getDefaultLibFileName,
getDirectoryPath,
getEmitScriptTarget,
getImpliedNodeFormatForEmitWorker,
getLineAndCharacterOfPosition,
getNewLineCharacter,
getNormalizedAbsolutePath,
@@ -352,13 +354,14 @@ export function explainFiles(program: Program, write: (s: string) => void) {
for (const file of program.getSourceFiles()) {
write(`${toFileName(file, relativeFileName)}`);
reasons.get(file.path)?.forEach(reason => write(` ${fileIncludeReasonToDiagnostics(program, reason, relativeFileName).messageText}`));
explainIfFileIsRedirectAndImpliedFormat(file, relativeFileName)?.forEach(d => write(` ${d.messageText}`));
explainIfFileIsRedirectAndImpliedFormat(file, program.getCompilerOptionsForFile(file), relativeFileName)?.forEach(d => write(` ${d.messageText}`));
}
}
/** @internal */
export function explainIfFileIsRedirectAndImpliedFormat(
file: SourceFile,
options: CompilerOptions,
fileNameConvertor?: (fileName: string) => string,
): DiagnosticMessageChain[] | undefined {
let result: DiagnosticMessageChain[] | undefined;
@@ -377,7 +380,7 @@ export function explainIfFileIsRedirectAndImpliedFormat(
));
}
if (isExternalOrCommonJsModule(file)) {
switch (file.impliedNodeFormat) {
switch (getImpliedNodeFormatForEmitWorker(file, options)) {
case ModuleKind.ESNext:
if (file.packageJsonScope) {
(result ??= []).push(chainDiagnosticMessages(
@@ -416,7 +419,8 @@ export function getMatchedFileSpec(program: Program, fileName: string) {
const filePath = program.getCanonicalFileName(fileName);
const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory()));
return find(configFile.configFileSpecs.validatedFilesSpec, fileSpec => program.getCanonicalFileName(getNormalizedAbsolutePath(fileSpec, basePath)) === filePath);
const index = findIndex(configFile.configFileSpecs.validatedFilesSpec, fileSpec => program.getCanonicalFileName(getNormalizedAbsolutePath(fileSpec, basePath)) === filePath);
return index !== -1 ? configFile.configFileSpecs.validatedFilesSpecBeforeSubstitution![index] : undefined;
}
/** @internal */
@@ -430,11 +434,12 @@ export function getMatchedIncludeSpec(program: Program, fileName: string) {
const isJsonFile = fileExtensionIs(fileName, Extension.Json);
const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory()));
const useCaseSensitiveFileNames = program.useCaseSensitiveFileNames();
return find(configFile?.configFileSpecs?.validatedIncludeSpecs, includeSpec => {
const index = findIndex(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);
});
return index !== -1 ? configFile.configFileSpecs.validatedIncludeSpecsBeforeSubstitution![index] : undefined;
}
/** @internal */
+35 -15
View File
@@ -786,21 +786,27 @@ export namespace Compiler {
const postPerformanceValues = getPerformanceBaselineValues();
if (!isSymbolBaseline) {
const perfStats: [name: string, reportThreshold: number, rounding: number, beforeValue: number, afterValue: number][] = [];
perfStats.push(["Strict subtype cache", 1000, 100, prePerformanceValues.strictSubtype, postPerformanceValues.strictSubtype]);
perfStats.push(["Subtype cache", 1000, 100, prePerformanceValues.subtype, postPerformanceValues.subtype]);
perfStats.push(["Identity cache", 1000, 100, prePerformanceValues.identity, postPerformanceValues.identity]);
perfStats.push(["Assignability cache", 1000, 100, prePerformanceValues.assignability, postPerformanceValues.assignability]);
perfStats.push(["Type Count", 1000, 100, prePerformanceValues.typeCount, postPerformanceValues.typeCount]);
perfStats.push(["Instantiation count", 1500, 500, prePerformanceValues.instantiation, postPerformanceValues.instantiation]);
perfStats.push(["Symbol count", 45000, 500, prePerformanceValues.symbol, postPerformanceValues.symbol]);
const perfStats: [name: string, reportThreshold: number, beforeValue: number, afterValue: number][] = [];
perfStats.push(["Strict subtype cache", 1000, prePerformanceValues.strictSubtype, postPerformanceValues.strictSubtype]);
perfStats.push(["Subtype cache", 1000, prePerformanceValues.subtype, postPerformanceValues.subtype]);
perfStats.push(["Identity cache", 1000, prePerformanceValues.identity, postPerformanceValues.identity]);
perfStats.push(["Assignability cache", 1000, prePerformanceValues.assignability, postPerformanceValues.assignability]);
perfStats.push(["Type Count", 1000, prePerformanceValues.typeCount, postPerformanceValues.typeCount]);
perfStats.push(["Instantiation count", 1500, prePerformanceValues.instantiation, postPerformanceValues.instantiation]);
perfStats.push(["Symbol count", 45000, prePerformanceValues.symbol, postPerformanceValues.symbol]);
if (perfStats.some(([, threshold, , , postValue]) => postValue >= threshold)) {
if (perfStats.some(([, threshold, , postValue]) => postValue >= threshold)) {
perfLines.push(`=== Performance Stats ===`);
for (const [name, _, rounding, preValue, postValue] of perfStats) {
const preDisplay = valueToString(preValue, rounding);
if (preDisplay !== "0") {
perfLines.push(`${name}: ${preDisplay} / ${valueToString(postValue, rounding)} (nearest ${rounding})`);
for (const [name, threshold, preValue, postValue] of perfStats) {
if (postValue >= threshold) {
const preString = valueToString(preValue);
const postString = valueToString(postValue);
if (preString === postString) {
perfLines.push(`${name}: ${preString}`);
}
else {
perfLines.push(`${name}: ${preString} -> ${postString}`);
}
}
}
perfLines.push("");
@@ -809,10 +815,24 @@ export namespace Compiler {
}
return result ? (`//// [${header}] ////\r\n\r\n${perfLines.join("\n")}${result}`) : null; // eslint-disable-line no-null/no-null
function valueToString(value: number) {
return roundToHumanLogarithm(value).toLocaleString("en-US");
}
}
function valueToString(value: number, rounding: number) {
return (Math.round(value / rounding) * rounding).toLocaleString("en-US");
/**
* Rounds to a number like 10, 25, 50, 100, 250, 500, 1000, etc
*/
function roundToHumanLogarithm(n: number) {
if (n < 10) return 0;
const powerOfTen = Math.floor(Math.log10(n));
const basePowerOfTen = Math.pow(10, powerOfTen);
const multipliers = [1, 2.5, 5, 10];
const closestMultiplier = multipliers.reduce((prev, curr) => {
return Math.abs(curr * basePowerOfTen - n) < Math.abs(prev * basePowerOfTen - n) ? curr : prev;
});
return closestMultiplier * basePowerOfTen;
}
function getPerformanceBaselineValues() {
+47 -5
View File
@@ -271,6 +271,8 @@ export function verifyResolutionCache(
verifyResolutionSet(expected.resolutionsWithOnlyAffectingLocations, actual.resolutionsWithOnlyAffectingLocations, `resolutionsWithOnlyAffectingLocations`);
verifyDirectoryWatchesOfFailedLookups(expected.directoryWatchesOfFailedLookups, actual.directoryWatchesOfFailedLookups);
verifyFileWatchesOfAffectingLocations(expected.fileWatchesOfAffectingLocations, actual.fileWatchesOfAffectingLocations);
verifyPackageDirWatchers(expected.packageDirWatchers, actual.packageDirWatchers);
verifyDirPathToSymlinkPackageRefCount(expected.dirPathToSymlinkPackageRefCount, actual.dirPathToSymlinkPackageRefCount);
// Stop watching resolutions to verify everything gets closed.
expected.startCachingPerDirectoryResolution();
@@ -368,11 +370,17 @@ export function verifyResolutionCache(
}
function verifyDirectoryWatchesOfFailedLookups(expected: Map<string, ts.DirectoryWatchesOfFailedLookup>, actual: Map<string, ts.DirectoryWatchesOfFailedLookup>) {
verifyMap(expected, actual, (expected, actual, caption) => {
ts.Debug.assert(expected?.refCount === actual?.refCount, `${projectName}:: ${caption}:: refCount`);
ts.Debug.assert(!!expected?.refCount, `${projectName}:: ${caption}:: expected refCount to be non zero`);
ts.Debug.assert(expected?.nonRecursive === actual?.nonRecursive, `${projectName}:: ${caption}:: nonRecursive`);
}, "directoryWatchesOfFailedLookups");
verifyMap(expected, actual, verifyDirectoryWatchesOfFailedLookup, "directoryWatchesOfFailedLookups");
}
function verifyDirectoryWatchesOfFailedLookup(
expected: ts.DirectoryWatchesOfFailedLookup | undefined,
actual: ts.DirectoryWatchesOfFailedLookup | undefined,
caption: string,
) {
ts.Debug.assert(expected?.refCount === actual?.refCount, `${projectName}:: ${caption}:: refCount`);
ts.Debug.assert(!!expected?.refCount, `${projectName}:: ${caption}:: expected refCount to be non zero`);
ts.Debug.assert(expected?.nonRecursive === actual?.nonRecursive, `${projectName}:: ${caption}:: nonRecursive`);
}
function verifyFileWatchesOfAffectingLocations(
@@ -391,6 +399,40 @@ export function verifyResolutionCache(
ts.Debug.assert(expected?.files === actual?.files, `${projectName}:: ${caption}:: files`);
verifySet(expected?.symlinks, actual?.symlinks, `${projectName}:: ${caption}:: symlinks`);
}
function verifyPackageDirWatchers(
expected: Map<ts.Path, ts.PackageDirWatcher>,
actual: Map<ts.Path, ts.PackageDirWatcher>,
) {
verifyMap(expected, actual, verifyPackageDirWatcher, "packageDirWatchers");
}
function verifyPackageDirWatcher(
expected: ts.PackageDirWatcher | undefined,
actual: ts.PackageDirWatcher | undefined,
caption: string,
) {
ts.Debug.assert(expected?.isSymlink === actual?.isSymlink, `${projectName}:: ${caption}:: isSymlink`);
verifyMap(expected?.dirPathToWatcher, actual?.dirPathToWatcher, verfiyDirPathToWatcherOfPackageDirWatcher, `${projectName}:: ${caption}:: dirPathToWatcher`);
}
function verfiyDirPathToWatcherOfPackageDirWatcher(
expected: ts.DirPathToWatcherOfPackageDirWatcher | undefined,
actual: ts.DirPathToWatcherOfPackageDirWatcher | undefined,
caption: string,
) {
ts.Debug.assert(expected?.refCount === actual?.refCount, `${projectName}:: ${caption}:: refCount`);
verifyDirectoryWatchesOfFailedLookup(expected?.watcher, actual?.watcher, `${projectName}:: ${caption}:: directoryWatchesOfFailedLookup`);
}
function verifyDirPathToSymlinkPackageRefCount(
expected: Map<ts.Path, number>,
actual: Map<ts.Path, number>,
) {
verifyMap(expected, actual, (expected, actual, caption) => {
ts.Debug.assert(expected === actual, `${projectName}:: ${caption}`);
}, "dirPathToSymlinkPackageRefCount");
}
}
function verifyMap<Key extends string, Expected, Actual>(
+11 -2
View File
@@ -13,6 +13,7 @@ import {
import {
AutoImportProviderProject,
AuxiliaryProject,
ConfiguredProject,
isBackgroundProject,
isConfiguredProject,
LogLevel,
@@ -32,6 +33,7 @@ interface ProjectData {
isClosed: ReturnType<Project["isClosed"]>;
isOrphan: ReturnType<Project["isOrphan"]>;
noOpenRef: boolean;
deferredClose: ConfiguredProject["deferredClose"];
documentPositionMappers: SourceMapper["documentPositionMappers"];
autoImportProviderHost: Project["autoImportProviderHost"];
noDtsResolutionProject: Project["noDtsResolutionProject"];
@@ -46,6 +48,7 @@ interface ScriptInfoData {
open: ReturnType<ScriptInfo["isScriptOpen"]>;
version: ReturnType<TextStorage["getVersion"]>;
pendingReloadFromDisk: TextStorage["pendingReloadFromDisk"];
deferredDelete: ScriptInfo["deferredDelete"];
sourceMapFilePath: Exclude<ScriptInfo["sourceMapFilePath"], SourceMapFileWatcher> | SourceMapFileWatcherData | undefined;
declarationInfoPath: ScriptInfo["declarationInfoPath"];
sourceInfos: ScriptInfo["sourceInfos"];
@@ -101,6 +104,8 @@ export function patchServiceForStateBaseline(service: ProjectService) {
function baselineProjects(currentMappers: Set<DocumentPositionMapper>) {
const autoImportProviderProjects = [] as AutoImportProviderProject[];
const auxiliaryProjects = [] as AuxiliaryProject[];
const orphanConfiguredProjects = service.getOrphanConfiguredProjects(/*toRetainConfiguredProjects*/ undefined);
const noOpenRef = (project: Project) => isConfiguredProject(project) && (project.isClosed() || orphanConfiguredProjects.has(project));
return baselineState(
[service.externalProjects, service.configuredProjects, service.inferredProjects, autoImportProviderProjects, auxiliaryProjects],
projects,
@@ -115,7 +120,8 @@ export function patchServiceForStateBaseline(service: ProjectService) {
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "dirty", project.dirty, projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "isClosed", project.isClosed(), projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "isOrphan", !isBackgroundProject(project) && project.isOrphan(), projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "noOpenRef", isConfiguredProject(project) && !project.hasOpenRef(), projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "noOpenRef", noOpenRef(project), projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "deferredClose", isConfiguredProject(project) && project.deferredClose, projectDiff, projectPropertyLogs);
projectDiff = printMapPropertyValue(
PrintPropertyWhen.Changed,
data?.documentPositionMappers,
@@ -145,7 +151,8 @@ export function patchServiceForStateBaseline(service: ProjectService) {
dirty: project.dirty,
isClosed: project.isClosed(),
isOrphan: !isBackgroundProject(project) && project.isOrphan(),
noOpenRef: isConfiguredProject(project) && !project.hasOpenRef(),
noOpenRef: noOpenRef(project),
deferredClose: isConfiguredProject(project) && project.deferredClose,
autoImportProviderHost: project.autoImportProviderHost,
noDtsResolutionProject: project.noDtsResolutionProject,
originalConfiguredProjects: project.originalConfiguredProjects && new Set(project.originalConfiguredProjects),
@@ -166,6 +173,7 @@ export function patchServiceForStateBaseline(service: ProjectService) {
infoDiff = printProperty(PrintPropertyWhen.Changed, data, "open", isOpen, infoDiff, infoPropertyLogs);
infoDiff = printProperty(PrintPropertyWhen.Always, data, "version", info.textStorage.getVersion(), infoDiff, infoPropertyLogs);
infoDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "pendingReloadFromDisk", info.textStorage.pendingReloadFromDisk, infoDiff, infoPropertyLogs);
infoDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "deferredDelete", info.deferredDelete, infoDiff, infoPropertyLogs);
infoDiff = printScriptInfoSourceMapFilePath(data, info, infoDiff, infoPropertyLogs);
infoDiff = printProperty(PrintPropertyWhen.DefinedOrChangedOrNew, data, "declarationInfoPath", info.declarationInfoPath, infoDiff, infoPropertyLogs);
infoDiff = printSetPropertyValueWorker(PrintPropertyWhen.DefinedOrChangedOrNew, data?.sourceInfos, "sourceInfos", info.sourceInfos, infoDiff, infoPropertyLogs, identity);
@@ -200,6 +208,7 @@ export function patchServiceForStateBaseline(service: ProjectService) {
sourceInfos: info.sourceInfos && new Set(info.sourceInfos),
documentPositionMapper: info.documentPositionMapper,
containingProjects: new Set(info.containingProjects),
deferredDelete: info.deferredDelete,
}),
);
}
+5 -1
View File
@@ -44,11 +44,15 @@ function* forEachASTNode(node: ts.Node) {
}
}
function nodeIsFullySynthetic(node: ts.Node) {
return ts.nodeIsSynthesized(node) && !ts.getParseTreeNode(node);
}
const createSyntheticNodeUnderliningPrinter = memoize((): { printer: ts.Printer; writer: ts.EmitTextWriter; underliner: ts.EmitTextWriter; reset(): void; } => {
let underlining = false;
const printer = createPrinter({ removeComments: true }, {
onEmitNode: (hint, node, cb) => {
if (ts.nodeIsSynthesized(node) !== underlining) {
if (nodeIsFullySynthetic(node) !== underlining) {
// either node is synthetic and underlining needs to be enabled, or node is not synthetic and
// underlining needs to be disabled
underlining = !underlining;
+6 -2
View File
@@ -27,8 +27,12 @@ declare namespace Intl {
source: "startRange" | "endRange" | "shared";
}
type StringNumericLiteral = `${number}` | "Infinity" | "-Infinity" | "+Infinity";
interface NumberFormat {
formatRange(start: number | bigint, end: number | bigint): string;
formatRangeToParts(start: number | bigint, end: number | bigint): NumberRangeFormatPart[];
format(value: number | bigint | StringNumericLiteral): string;
formatToParts(value: number | bigint | StringNumericLiteral): NumberFormatPart[];
formatRange(start: number | bigint | StringNumericLiteral, end: number | bigint | StringNumericLiteral): string;
formatRangeToParts(start: number | bigint | StringNumericLiteral, end: number | bigint | StringNumericLiteral): NumberRangeFormatPart[];
}
}
File diff suppressed because it is too large Load Diff
+51 -3
View File
@@ -657,7 +657,12 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
}
private getOrCreateScriptInfoAndAttachToProject(fileName: string) {
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.currentDirectory, this.directoryStructureHost);
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(
fileName,
this.currentDirectory,
this.directoryStructureHost,
/*deferredDeleteOk*/ false,
);
if (scriptInfo) {
const existingValue = this.rootFilesMap.get(scriptInfo.path);
if (existingValue && existingValue.info !== scriptInfo) {
@@ -678,7 +683,12 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
getScriptVersion(filename: string) {
// Don't attach to the project if version is asked
const info = this.projectService.getOrCreateScriptInfoNotOpenedByClient(filename, this.currentDirectory, this.directoryStructureHost);
const info = this.projectService.getOrCreateScriptInfoNotOpenedByClient(
filename,
this.currentDirectory,
this.directoryStructureHost,
/*deferredDeleteOk*/ false,
);
return (info && info.getLatestVersion())!; // TODO: GH#18217
}
@@ -1298,6 +1308,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
}
}
/** @internal */
markAsDirty() {
if (!this.dirty) {
this.projectStateVersion++;
@@ -1658,7 +1669,12 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
// by the host for files in the program when the program is retrieved above but
// the program doesn't contain external files so this must be done explicitly.
inserted => {
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(inserted, this.currentDirectory, this.directoryStructureHost);
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(
inserted,
this.currentDirectory,
this.directoryStructureHost,
/*deferredDeleteOk*/ false,
);
scriptInfo?.attachToProject(this);
},
removed => this.detachScriptInfoFromProject(removed),
@@ -2584,6 +2600,7 @@ export class AutoImportProviderProject extends Project {
return !some(this.rootFileNames);
}
/** @internal */
override isOrphan() {
return true;
}
@@ -2619,6 +2636,7 @@ export class AutoImportProviderProject extends Project {
return !!this.rootFileNames?.length;
}
/** @internal */
override markAsDirty() {
this.rootFileNames = undefined;
super.markAsDirty();
@@ -2707,6 +2725,15 @@ export class ConfiguredProject extends Project {
/** @internal */
private compilerHost?: CompilerHost;
/** @internal */
hasConfigFileDiagnostics?: boolean;
/** @internal */
skipConfigDiagEvent?: true;
/** @internal */
deferredClose?: boolean;
/** @internal */
constructor(
configFileName: NormalizedPath,
@@ -2767,6 +2794,7 @@ export class ConfiguredProject extends Project {
* @returns: true if set of files in the project stays the same and false - otherwise.
*/
override updateGraph(): boolean {
if (this.deferredClose) return false;
const isInitialLoad = this.isInitialLoadPending();
this.isInitialLoadPending = returnFalse;
const updateLevel = this.pendingUpdateLevel;
@@ -2790,6 +2818,9 @@ export class ConfiguredProject extends Project {
this.compilerHost = undefined;
this.projectService.sendProjectLoadingFinishEvent(this);
this.projectService.sendProjectTelemetry(this);
if (!this.skipConfigDiagEvent && !result) { // If new program, send event if diagnostics presence has changed
this.projectService.sendConfigFileDiagEvent(this, /*triggerFile*/ undefined);
}
return result;
}
@@ -2883,6 +2914,12 @@ export class ConfiguredProject extends Project {
super.close();
}
/** @internal */
override markAsDirty() {
if (this.deferredClose) return;
super.markAsDirty();
}
/** @internal */
addExternalProjectReference() {
this.externalProjectRefCount++;
@@ -2932,6 +2969,7 @@ export class ConfiguredProject extends Project {
}
const configFileExistenceInfo = this.projectService.configFileExistenceInfoCache.get(this.canonicalConfigFilePath)!;
if (this.deferredClose) return !!configFileExistenceInfo.openFilesImpactedByConfigFile?.size;
if (this.projectService.hasPendingProjectUpdate(this)) {
// If there is pending update for this project,
// we dont know if this project would be needed by any of the open files impacted by this config file
@@ -2957,6 +2995,11 @@ export class ConfiguredProject extends Project {
) || false;
}
/** @internal */
override isOrphan(): boolean {
return !!this.deferredClose;
}
/** @internal */
hasExternalProjectRef() {
return !!this.externalProjectRefCount;
@@ -3014,3 +3057,8 @@ export function isExternalProject(project: Project): project is ExternalProject
export function isBackgroundProject(project: Project): project is AutoImportProviderProject | AuxiliaryProject {
return project.projectKind === ProjectKind.AutoImportProvider || project.projectKind === ProjectKind.Auxiliary;
}
/** @internal */
export function isProjectDeferredClose(project: Project): project is ConfiguredProject {
return isConfiguredProject(project) && !!project.deferredClose;
}
+1
View File
@@ -3142,6 +3142,7 @@ export const enum ScriptTarget {
ES2020 = "es2020",
ES2021 = "es2021",
ES2022 = "es2022",
ES2023 = "es2023",
ESNext = "esnext",
JSON = "json",
Latest = ESNext,
+19 -2
View File
@@ -25,6 +25,7 @@ import {
IScriptSnapshot,
isString,
LineInfo,
missingFileModifiedTime,
orderedRemoveItem,
Path,
ScriptKind,
@@ -44,6 +45,7 @@ import {
isConfiguredProject,
isExternalProject,
isInferredProject,
isProjectDeferredClose,
maxFileSize,
NormalizedPath,
Project,
@@ -180,6 +182,14 @@ export class TextStorage {
const reloaded = this.reload(newText);
this.fileSize = fileSize; // NB: after reload since reload clears it
this.ownFileText = !tempFileName || tempFileName === this.info.fileName;
// In case we update this text before mTime gets updated to present file modified time
// because its schedule to do that later, update the mTime so we dont re-update the text
// Eg. with npm ci where file gets created and editor calls say get error request before
// the timeout to update the file stamps in node_modules is run
// Test:: watching npm install in codespaces where workspaces folder is hosted at root
if (this.ownFileText && this.info.mTime === missingFileModifiedTime.getTime()) {
this.info.mTime = (this.host.getModifiedTime!(this.info.fileName) || missingFileModifiedTime).getTime();
}
return reloaded;
}
@@ -398,6 +408,9 @@ export class ScriptInfo {
/** @internal */
documentPositionMapper?: DocumentPositionMapper | false;
/** @internal */
deferredDelete?: boolean;
constructor(
private readonly host: ServerHost,
readonly fileName: NormalizedPath,
@@ -567,7 +580,10 @@ export class ScriptInfo {
case 0:
return Errors.ThrowNoProject();
case 1:
return ensurePrimaryProjectKind(this.containingProjects[0]);
return ensurePrimaryProjectKind(
!isProjectDeferredClose(this.containingProjects[0]) ?
this.containingProjects[0] : undefined,
);
default:
// If this file belongs to multiple projects, below is the order in which default project is used
// - for open script info, its default configured project during opening is default if info is part of it
@@ -583,6 +599,7 @@ export class ScriptInfo {
for (let index = 0; index < this.containingProjects.length; index++) {
const project = this.containingProjects[index];
if (isConfiguredProject(project)) {
if (project.deferredClose) continue;
if (!project.isSourceOfProjectReferenceRedirect(this.fileName)) {
// If we havent found default configuredProject and
// its not the last one, find it and use that one if there
@@ -676,7 +693,7 @@ export class ScriptInfo {
}
isOrphan() {
return !forEach(this.containingProjects, p => !p.isOrphan());
return this.deferredDelete || !forEach(this.containingProjects, p => !p.isOrphan());
}
/** @internal */
+2 -2
View File
@@ -591,7 +591,7 @@ export class LineNode implements LineCollection {
if (children.length) this.updateCounts();
}
isLeaf() {
isLeaf(): this is LineLeaf {
return false;
}
@@ -839,7 +839,7 @@ export class LineLeaf implements LineCollection {
constructor(public text: string) {
}
isLeaf() {
isLeaf(): this is LineLeaf {
return true;
}
+1
View File
@@ -1575,6 +1575,7 @@ export class Session<TMessage = string> implements EventSender {
fileNameToSearch,
noDtsProject.currentDirectory,
noDtsProject.directoryStructureHost,
/*deferredDeleteOk*/ false,
);
if (!info) continue;
if (!noDtsProject.containsScriptInfo(info)) {
+381 -70
View File
@@ -3,6 +3,7 @@ import {
AnyImportOrRequireStatement,
AnyImportSyntax,
arrayFrom,
BindingElement,
CancellationToken,
cast,
changeAnyExtension,
@@ -15,6 +16,7 @@ import {
compareValues,
Comparison,
CompilerOptions,
createFutureSourceFile,
createModuleSpecifierResolutionHost,
createMultiMap,
createPackageJsonImportFilter,
@@ -27,50 +29,57 @@ import {
ExportKind,
ExportMapInfoKey,
factory,
findAncestor,
first,
firstDefined,
flatMap,
flatMapIterator,
forEachExternalModuleToImportFrom,
formatting,
FutureSourceFile,
FutureSymbolExportInfo,
getAllowSyntheticDefaultImports,
getBaseFileName,
getDefaultExportInfoWorker,
getDefaultLikeExportInfo,
getDirectoryPath,
getEmitModuleFormatOfFileWorker,
getEmitModuleKind,
getEmitModuleResolutionKind,
getEmitScriptTarget,
getExportInfoMap,
getImpliedNodeFormatForEmitWorker,
getMeaningFromDeclaration,
getMeaningFromLocation,
getNameForExportedSymbol,
getNodeId,
getOutputExtension,
getQuoteFromPreference,
getQuotePreference,
getSourceFileOfNode,
getSymbolId,
getSynthesizedDeepClone,
getTokenAtPosition,
getTokenPosOfNode,
getTypeKeywordOfTypeOnlyImport,
getUniqueSymbolId,
hasJSFileExtension,
hostGetCanonicalFileName,
Identifier,
ImportClause,
ImportEqualsDeclaration,
importFromModuleSpecifier,
ImportKind,
ImportSpecifier,
insertImports,
InternalSymbolName,
isExternalModule,
isExternalModuleReference,
isFullSourceFile,
isIdentifier,
isIdentifierPart,
isIdentifierStart,
isImportableFile,
isImportDeclaration,
isImportEqualsDeclaration,
isInJSFile,
isIntrinsicJsxName,
isJSDocImportTag,
isJsxClosingElement,
@@ -79,6 +88,7 @@ import {
isJSXTagName,
isNamedImports,
isNamespaceImport,
isRequireVariableStatement,
isSourceFileJS,
isStringANonContextualKeyword,
isStringLiteral,
@@ -101,6 +111,7 @@ import {
MultiMap,
Mutable,
NamedImports,
NamespaceImport,
Node,
NodeFlags,
nodeIsMissing,
@@ -114,6 +125,7 @@ import {
QuotePreference,
removeFileExtension,
removeSuffix,
RequireOrImportCall,
RequireVariableStatement,
sameMap,
ScriptTarget,
@@ -140,6 +152,7 @@ import {
TypeChecker,
TypeOnlyAliasDeclaration,
UserPreferences,
VariableDeclarationInitializedTo,
} from "../_namespaces/ts";
import {
createCodeFixAction,
@@ -200,6 +213,14 @@ registerCodeFix({
},
});
/**
* The node kinds that may be the declaration of an alias symbol imported/required from an external module.
* `ImportClause` is the declaration for a syntactic default import. `VariableDeclaration` is the declaration
* for a non-destructured `require` call.
* @internal
*/
export type ImportOrRequireAliasDeclaration = ImportEqualsDeclaration | ImportClause | ImportSpecifier | NamespaceImport | VariableDeclarationInitializedTo<RequireOrImportCall> | BindingElement;
/**
* Computes multiple import additions to a file and writes them to a ChangeTracker.
*
@@ -208,12 +229,16 @@ registerCodeFix({
export interface ImportAdder {
hasFixes(): boolean;
addImportFromDiagnostic: (diagnostic: DiagnosticWithLocation, context: CodeFixContextBase) => void;
addImportFromExportedSymbol: (exportedSymbol: Symbol, isValidTypeOnlyUseSite?: boolean) => void;
addImportFromExportedSymbol: (exportedSymbol: Symbol, isValidTypeOnlyUseSite?: boolean, referenceImport?: ImportOrRequireAliasDeclaration) => void;
addImportForNonExistentExport: (exportName: string, exportingFileName: string, exportKind: ExportKind, exportedMeanings: SymbolFlags, isImportUsageValidAsTypeOnly: boolean) => void;
addImportForUnresolvedIdentifier: (context: CodeFixContextBase, symbolToken: Identifier, useAutoImportProvider: boolean) => void;
addVerbatimImport: (declaration: AnyImportOrRequireStatement | ImportOrRequireAliasDeclaration) => void;
removeExistingImport: (declaration: ImportOrRequireAliasDeclaration) => void;
writeFixes: (changeTracker: textChanges.ChangeTracker, oldFileQuotePreference?: QuotePreference) => void;
}
/** @internal */
export function createImportAdder(sourceFile: SourceFile, program: Program, preferences: UserPreferences, host: LanguageServiceHost, cancellationToken?: CancellationToken): ImportAdder {
export function createImportAdder(sourceFile: SourceFile | FutureSourceFile, program: Program, preferences: UserPreferences, host: LanguageServiceHost, cancellationToken?: CancellationToken): ImportAdder {
return createImportAdderWorker(sourceFile, program, /*useAutoImportProvider*/ false, preferences, host, cancellationToken);
}
@@ -223,18 +248,29 @@ interface AddToExistingState {
readonly namedImports: Map<string, AddAsTypeOnly>;
}
function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAutoImportProvider: boolean, preferences: UserPreferences, host: LanguageServiceHost, cancellationToken: CancellationToken | undefined): ImportAdder {
function createImportAdderWorker(sourceFile: SourceFile | FutureSourceFile, program: Program, useAutoImportProvider: boolean, preferences: UserPreferences, host: LanguageServiceHost, cancellationToken: CancellationToken | undefined): ImportAdder {
const compilerOptions = program.getCompilerOptions();
// Namespace fixes don't conflict, so just build a list.
const addToNamespace: FixUseNamespaceImport[] = [];
const importType: FixAddJsdocTypeImport[] = [];
/** Keys are import clause node IDs. */
const addToExisting = new Map<string, AddToExistingState>();
const addToExisting = new Map<ImportClause | ObjectBindingPattern, AddToExistingState>();
const removeExisting = new Set<ImportOrRequireAliasDeclaration>();
const verbatimImports = new Set<AnyImportOrRequireStatement | ImportOrRequireAliasDeclaration>();
type NewImportsKey = `${0 | 1}|${string}`;
/** Use `getNewImportEntry` for access */
const newImports = new Map<NewImportsKey, Mutable<ImportsCollection & { useRequire: boolean; }>>();
return { addImportFromDiagnostic, addImportFromExportedSymbol, writeFixes, hasFixes };
return { addImportFromDiagnostic, addImportFromExportedSymbol, writeFixes, hasFixes, addImportForUnresolvedIdentifier, addImportForNonExistentExport, removeExistingImport, addVerbatimImport };
function addVerbatimImport(declaration: AnyImportOrRequireStatement | ImportOrRequireAliasDeclaration) {
verbatimImports.add(declaration);
}
function addImportForUnresolvedIdentifier(context: CodeFixContextBase, symbolToken: Identifier, useAutoImportProvider: boolean) {
const info = getFixInfosWithoutDiagnostic(context, symbolToken, useAutoImportProvider);
if (!info || !info.length) return;
addImport(first(info));
}
function addImportFromDiagnostic(diagnostic: DiagnosticWithLocation, context: CodeFixContextBase) {
const info = getFixInfos(context, diagnostic.code, diagnostic.start, useAutoImportProvider);
@@ -242,19 +278,89 @@ function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAu
addImport(first(info));
}
function addImportFromExportedSymbol(exportedSymbol: Symbol, isValidTypeOnlyUseSite?: boolean) {
function addImportFromExportedSymbol(exportedSymbol: Symbol, isValidTypeOnlyUseSite?: boolean, referenceImport?: ImportOrRequireAliasDeclaration) {
const moduleSymbol = Debug.checkDefined(exportedSymbol.parent);
const symbolName = getNameForExportedSymbol(exportedSymbol, getEmitScriptTarget(compilerOptions));
const checker = program.getTypeChecker();
const symbol = checker.getMergedSymbol(skipAlias(exportedSymbol, checker));
const exportInfo = getAllExportInfoForSymbol(sourceFile, symbol, symbolName, moduleSymbol, /*preferCapitalized*/ false, program, host, preferences, cancellationToken);
const useRequire = shouldUseRequire(sourceFile, program);
const fix = getImportFixForSymbol(sourceFile, Debug.checkDefined(exportInfo), program, /*position*/ undefined, !!isValidTypeOnlyUseSite, useRequire, host, preferences);
let fix = getImportFixForSymbol(sourceFile, Debug.checkDefined(exportInfo), program, /*position*/ undefined, !!isValidTypeOnlyUseSite, useRequire, host, preferences);
if (fix) {
addImport({ fix, symbolName, errorIdentifierText: undefined });
const localName = tryCast(referenceImport?.name, isIdentifier)?.text ?? symbolName;
if (
referenceImport
&& isTypeOnlyImportDeclaration(referenceImport)
&& (fix.kind === ImportFixKind.AddNew || fix.kind === ImportFixKind.AddToExisting)
&& fix.addAsTypeOnly === AddAsTypeOnly.Allowed
) {
// Copy the type-only status from the reference import
fix = { ...fix, addAsTypeOnly: AddAsTypeOnly.Required };
}
addImport({ fix, symbolName: localName ?? symbolName, errorIdentifierText: undefined });
}
}
function addImportForNonExistentExport(exportName: string, exportingFileName: string, exportKind: ExportKind, exportedMeanings: SymbolFlags, isImportUsageValidAsTypeOnly: boolean) {
const exportingSourceFile = program.getSourceFile(exportingFileName);
const useRequire = shouldUseRequire(sourceFile, program);
if (exportingSourceFile && exportingSourceFile.symbol) {
const { fixes } = getImportFixes(
[{
exportKind,
isFromPackageJson: false,
moduleFileName: exportingFileName,
moduleSymbol: exportingSourceFile.symbol,
targetFlags: exportedMeanings,
}],
/*usagePosition*/ undefined,
isImportUsageValidAsTypeOnly,
useRequire,
program,
sourceFile,
host,
preferences,
);
if (fixes.length) {
addImport({ fix: fixes[0], symbolName: exportName, errorIdentifierText: exportName });
}
}
else {
// File does not exist yet or has no exports, so all imports added will be "new"
const futureExportingSourceFile = createFutureSourceFile(exportingFileName, ModuleKind.ESNext, program, host);
const moduleSpecifier = moduleSpecifiers.getLocalModuleSpecifierBetweenFileNames(
sourceFile,
exportingFileName,
compilerOptions,
createModuleSpecifierResolutionHost(program, host),
);
const importKind = getImportKind(futureExportingSourceFile, exportKind, program);
const addAsTypeOnly = getAddAsTypeOnly(
isImportUsageValidAsTypeOnly,
/*isForNewImportDeclaration*/ true,
/*symbol*/ undefined,
exportedMeanings,
program.getTypeChecker(),
compilerOptions,
);
const fix: FixAddNewImport = {
kind: ImportFixKind.AddNew,
moduleSpecifier,
importKind,
addAsTypeOnly,
useRequire,
};
addImport({ fix, symbolName: exportName, errorIdentifierText: exportName });
}
}
function removeExistingImport(declaration: ImportOrRequireAliasDeclaration) {
if (declaration.kind === SyntaxKind.ImportClause) {
Debug.assertIsDefined(declaration.name, "ImportClause should have a name if it's being removed");
}
removeExisting.add(declaration);
}
function addImport(info: FixInfo) {
const { fix, symbolName } = info;
switch (fix.kind) {
@@ -266,10 +372,9 @@ function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAu
break;
case ImportFixKind.AddToExisting: {
const { importClauseOrBindingPattern, importKind, addAsTypeOnly } = fix;
const key = String(getNodeId(importClauseOrBindingPattern));
let entry = addToExisting.get(key);
let entry = addToExisting.get(importClauseOrBindingPattern);
if (!entry) {
addToExisting.set(key, entry = { importClauseOrBindingPattern, defaultImport: undefined, namedImports: new Map() });
addToExisting.set(importClauseOrBindingPattern, entry = { importClauseOrBindingPattern, defaultImport: undefined, namedImports: new Map() });
}
if (importKind === ImportKind.Named) {
const prevValue = entry?.namedImports.get(symbolName);
@@ -362,7 +467,7 @@ function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAu
function writeFixes(changeTracker: textChanges.ChangeTracker, oldFileQuotePreference?: QuotePreference) {
let quotePreference: QuotePreference;
if (sourceFile.imports.length === 0 && oldFileQuotePreference !== undefined) {
if (isFullSourceFile(sourceFile) && sourceFile.imports.length === 0 && oldFileQuotePreference !== undefined) {
// If the target file has no imports, we must use the same quote preference as the file we are importing from.
quotePreference = oldFileQuotePreference;
}
@@ -370,18 +475,102 @@ function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAu
quotePreference = getQuotePreference(sourceFile, preferences);
}
for (const fix of addToNamespace) {
addNamespaceQualifier(changeTracker, sourceFile, fix);
// Any modifications to existing syntax imply SourceFile already exists
addNamespaceQualifier(changeTracker, sourceFile as SourceFile, fix);
}
for (const fix of importType) {
addImportType(changeTracker, sourceFile, fix, quotePreference);
// Any modifications to existing syntax imply SourceFile already exists
addImportType(changeTracker, sourceFile as SourceFile, fix, quotePreference);
}
let importSpecifiersToRemoveWhileAdding: Set<ImportSpecifier | BindingElement> | undefined;
if (removeExisting.size) {
Debug.assert(isFullSourceFile(sourceFile), "Cannot remove imports from a future source file");
const importDeclarationsWithRemovals = new Set(mapDefined([...removeExisting], d => findAncestor(d, isImportDeclaration)!));
const variableDeclarationsWithRemovals = new Set(mapDefined([...removeExisting], d => findAncestor(d, isVariableDeclarationInitializedToRequire)!));
const emptyImportDeclarations = [...importDeclarationsWithRemovals].filter(d =>
// nothing added to the import declaration
!addToExisting.has(d.importClause!) &&
// no default, or default is being removed
(!d.importClause?.name || removeExisting.has(d.importClause)) &&
// no namespace import, or namespace import is being removed
(!tryCast(d.importClause?.namedBindings, isNamespaceImport) || removeExisting.has(d.importClause!.namedBindings as NamespaceImport)) &&
// no named imports, or all named imports are being removed
(!tryCast(d.importClause?.namedBindings, isNamedImports) || every((d.importClause!.namedBindings as NamedImports).elements, e => removeExisting.has(e)))
);
const emptyVariableDeclarations = [...variableDeclarationsWithRemovals].filter(d =>
// no binding elements being added to the variable declaration
(d.name.kind !== SyntaxKind.ObjectBindingPattern || !addToExisting.has(d.name)) &&
// no binding elements, or all binding elements are being removed
(d.name.kind !== SyntaxKind.ObjectBindingPattern || every(d.name.elements, e => removeExisting.has(e)))
);
const namedBindingsToDelete = [...importDeclarationsWithRemovals].filter(d =>
// has named bindings
d.importClause?.namedBindings &&
// is not being fully removed
emptyImportDeclarations.indexOf(d) === -1 &&
// is not gaining named imports
!addToExisting.get(d.importClause)?.namedImports &&
// all named imports are being removed
(d.importClause.namedBindings.kind === SyntaxKind.NamespaceImport || every(d.importClause.namedBindings.elements, e => removeExisting.has(e)))
);
for (const declaration of [...emptyImportDeclarations, ...emptyVariableDeclarations]) {
changeTracker.delete(sourceFile, declaration);
}
for (const declaration of namedBindingsToDelete) {
changeTracker.replaceNode(
sourceFile,
declaration.importClause!,
factory.updateImportClause(
declaration.importClause!,
declaration.importClause!.isTypeOnly,
declaration.importClause!.name,
/*namedBindings*/ undefined,
),
);
}
for (const declaration of removeExisting) {
const importDeclaration = findAncestor(declaration, isImportDeclaration);
if (
importDeclaration &&
emptyImportDeclarations.indexOf(importDeclaration) === -1 &&
namedBindingsToDelete.indexOf(importDeclaration) === -1
) {
if (declaration.kind === SyntaxKind.ImportClause) {
changeTracker.delete(sourceFile, declaration.name!);
}
else {
Debug.assert(declaration.kind === SyntaxKind.ImportSpecifier, "NamespaceImport should have been handled earlier");
if (addToExisting.get(importDeclaration.importClause!)?.namedImports) {
// Handle combined inserts/deletes in `doAddExistingFix`
(importSpecifiersToRemoveWhileAdding ??= new Set()).add(declaration);
}
else {
changeTracker.delete(sourceFile, declaration);
}
}
}
else if (declaration.kind === SyntaxKind.BindingElement) {
if (addToExisting.get(declaration.parent as ObjectBindingPattern)?.namedImports) {
// Handle combined inserts/deletes in `doAddExistingFix`
(importSpecifiersToRemoveWhileAdding ??= new Set()).add(declaration);
}
else {
changeTracker.delete(sourceFile, declaration);
}
}
else if (declaration.kind === SyntaxKind.ImportEqualsDeclaration) {
changeTracker.delete(sourceFile, declaration);
}
}
}
addToExisting.forEach(({ importClauseOrBindingPattern, defaultImport, namedImports }) => {
doAddExistingFix(
changeTracker,
sourceFile,
sourceFile as SourceFile,
importClauseOrBindingPattern,
defaultImport,
arrayFrom(namedImports.entries(), ([name, addAsTypeOnly]) => ({ addAsTypeOnly, name })),
importSpecifiersToRemoveWhileAdding,
preferences,
);
});
@@ -401,13 +590,84 @@ function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAu
);
newDeclarations = combine(newDeclarations, declarations);
});
newDeclarations = combine(newDeclarations, getCombinedVerbatimImports());
if (newDeclarations) {
insertImports(changeTracker, sourceFile, newDeclarations, /*blankLineBetween*/ true, preferences);
}
}
function getCombinedVerbatimImports(): AnyImportOrRequireStatement[] | undefined {
if (!verbatimImports.size) return undefined;
const importDeclarations = new Set(mapDefined([...verbatimImports], d => findAncestor(d, isImportDeclaration)));
const requireStatements = new Set(mapDefined([...verbatimImports], d => findAncestor(d, isRequireVariableStatement)));
return [
...mapDefined([...verbatimImports], d =>
d.kind === SyntaxKind.ImportEqualsDeclaration
? getSynthesizedDeepClone(d, /*includeTrivia*/ true)
: undefined),
...[...importDeclarations].map(d => {
if (verbatimImports.has(d)) {
return getSynthesizedDeepClone(d, /*includeTrivia*/ true);
}
return getSynthesizedDeepClone(
factory.updateImportDeclaration(
d,
d.modifiers,
d.importClause && factory.updateImportClause(
d.importClause,
d.importClause.isTypeOnly,
verbatimImports.has(d.importClause) ? d.importClause.name : undefined,
verbatimImports.has(d.importClause.namedBindings as NamespaceImport)
? d.importClause.namedBindings as NamespaceImport :
tryCast(d.importClause.namedBindings, isNamedImports)?.elements.some(e => verbatimImports.has(e))
? factory.updateNamedImports(
d.importClause.namedBindings as NamedImports,
(d.importClause.namedBindings as NamedImports).elements.filter(e => verbatimImports.has(e)),
)
: undefined,
),
d.moduleSpecifier,
d.attributes,
),
/*includeTrivia*/ true,
);
}),
...[...requireStatements].map(s => {
if (verbatimImports.has(s)) {
return getSynthesizedDeepClone(s, /*includeTrivia*/ true);
}
return getSynthesizedDeepClone(
factory.updateVariableStatement(
s,
s.modifiers,
factory.updateVariableDeclarationList(
s.declarationList,
mapDefined(s.declarationList.declarations, d => {
if (verbatimImports.has(d)) {
return d;
}
return factory.updateVariableDeclaration(
d,
d.name.kind === SyntaxKind.ObjectBindingPattern
? factory.updateObjectBindingPattern(
d.name,
d.name.elements.filter(e => verbatimImports.has(e)),
) : d.name,
d.exclamationToken,
d.type,
d.initializer,
);
}),
),
),
/*includeTrivia*/ true,
) as RequireVariableStatement;
}),
];
}
function hasFixes() {
return addToNamespace.length > 0 || importType.length > 0 || addToExisting.size > 0 || newImports.size > 0;
return addToNamespace.length > 0 || importType.length > 0 || addToExisting.size > 0 || newImports.size > 0 || verbatimImports.size > 0 || removeExisting.size > 0;
}
}
@@ -422,13 +682,13 @@ export interface ImportSpecifierResolver {
position: number,
isValidTypeOnlyUseSite: boolean,
fromCacheOnly?: boolean,
): { exportInfo?: SymbolExportInfo; moduleSpecifier: string; computedWithoutCacheCount: number; } | undefined;
): { exportInfo?: SymbolExportInfo | FutureSymbolExportInfo; moduleSpecifier: string; computedWithoutCacheCount: number; } | undefined;
}
/** @internal */
export function createImportSpecifierResolver(importingFile: SourceFile, program: Program, host: LanguageServiceHost, preferences: UserPreferences): ImportSpecifierResolver {
const packageJsonImportFilter = createPackageJsonImportFilter(importingFile, preferences, host);
const importMap = createExistingImportMap(program.getTypeChecker(), importingFile, program.getCompilerOptions());
const importMap = createExistingImportMap(importingFile, program);
return { getModuleSpecifierForBestExportInfo };
function getModuleSpecifierForBestExportInfo(
@@ -436,7 +696,7 @@ export function createImportSpecifierResolver(importingFile: SourceFile, program
position: number,
isValidTypeOnlyUseSite: boolean,
fromCacheOnly?: boolean,
): { exportInfo?: SymbolExportInfo; moduleSpecifier: string; computedWithoutCacheCount: number; } | undefined {
): { exportInfo?: SymbolExportInfo | FutureSymbolExportInfo; moduleSpecifier: string; computedWithoutCacheCount: number; } | undefined {
const { fixes, computedWithoutCacheCount } = getImportFixes(
exportInfo,
position,
@@ -477,7 +737,7 @@ type ImportFixWithModuleSpecifier = FixUseNamespaceImport | FixAddJsdocTypeImpor
// Properties are be undefined if fix is derived from an existing import
interface ImportFixBase {
readonly isReExport?: boolean;
readonly exportInfo?: SymbolExportInfo;
readonly exportInfo?: SymbolExportInfo | FutureSymbolExportInfo;
readonly moduleSpecifier: string;
}
interface Qualification {
@@ -491,7 +751,7 @@ interface FixAddJsdocTypeImport extends ImportFixBase {
readonly kind: ImportFixKind.JsdocTypeImport;
readonly usagePosition: number;
readonly isReExport: boolean;
readonly exportInfo: SymbolExportInfo;
readonly exportInfo: SymbolExportInfo | FutureSymbolExportInfo;
}
interface FixAddToExistingImport extends ImportFixBase {
readonly kind: ImportFixKind.AddToExisting;
@@ -516,7 +776,7 @@ interface FixAddToExistingImportInfo {
readonly declaration: AnyImportOrRequire;
readonly importKind: ImportKind;
readonly targetFlags: SymbolFlags;
readonly symbol: Symbol;
readonly symbol?: Symbol;
}
/** @internal */
@@ -584,7 +844,7 @@ export function getPromoteTypeOnlyCompletionAction(sourceFile: SourceFile, symbo
));
}
function getImportFixForSymbol(sourceFile: SourceFile, exportInfos: readonly SymbolExportInfo[], program: Program, position: number | undefined, isValidTypeOnlyUseSite: boolean, useRequire: boolean, host: LanguageServiceHost, preferences: UserPreferences) {
function getImportFixForSymbol(sourceFile: SourceFile | FutureSourceFile, exportInfos: readonly SymbolExportInfo[], program: Program, position: number | undefined, isValidTypeOnlyUseSite: boolean, useRequire: boolean, host: LanguageServiceHost, preferences: UserPreferences) {
const packageJsonImportFilter = createPackageJsonImportFilter(sourceFile, preferences, host);
return getBestFix(getImportFixes(exportInfos, position, isValidTypeOnlyUseSite, useRequire, program, sourceFile, host, preferences).fixes, sourceFile, program, packageJsonImportFilter, host);
}
@@ -593,7 +853,7 @@ function codeFixActionToCodeAction({ description, changes, commands }: CodeFixAc
return { description, changes, commands };
}
function getAllExportInfoForSymbol(importingFile: SourceFile, symbol: Symbol, symbolName: string, moduleSymbol: Symbol, preferCapitalized: boolean, program: Program, host: LanguageServiceHost, preferences: UserPreferences, cancellationToken: CancellationToken | undefined): readonly SymbolExportInfo[] | undefined {
function getAllExportInfoForSymbol(importingFile: SourceFile | FutureSourceFile, symbol: Symbol, symbolName: string, moduleSymbol: Symbol, preferCapitalized: boolean, program: Program, host: LanguageServiceHost, preferences: UserPreferences, cancellationToken: CancellationToken | undefined): readonly SymbolExportInfo[] | undefined {
const getChecker = createGetChecker(program, host);
return getExportInfoMap(importingFile, host, program, preferences, cancellationToken)
.search(importingFile.path, preferCapitalized, name => name === symbolName, info => {
@@ -624,20 +884,24 @@ function getSingleExportInfoForSymbol(symbol: Symbol, symbolName: string, module
}
}
function isFutureSymbolExportInfoArray(info: readonly SymbolExportInfo[] | readonly FutureSymbolExportInfo[]): info is readonly FutureSymbolExportInfo[] {
return info[0].symbol === undefined;
}
function getImportFixes(
exportInfos: readonly SymbolExportInfo[],
exportInfos: readonly SymbolExportInfo[] | readonly FutureSymbolExportInfo[],
usagePosition: number | undefined,
isValidTypeOnlyUseSite: boolean,
useRequire: boolean,
program: Program,
sourceFile: SourceFile,
sourceFile: SourceFile | FutureSourceFile,
host: LanguageServiceHost,
preferences: UserPreferences,
importMap = createExistingImportMap(program.getTypeChecker(), sourceFile, program.getCompilerOptions()),
importMap = isFullSourceFile(sourceFile) ? createExistingImportMap(sourceFile, program) : undefined,
fromCacheOnly?: boolean,
): { computedWithoutCacheCount: number; fixes: readonly ImportFixWithModuleSpecifier[]; } {
const checker = program.getTypeChecker();
const existingImports = flatMap(exportInfos, importMap.getImportsForExportInfo);
const existingImports = importMap && !isFutureSymbolExportInfoArray(exportInfos) ? flatMap(exportInfos, importMap.getImportsForExportInfo) : emptyArray;
const useNamespace = usagePosition !== undefined && tryUseExistingNamespaceImport(existingImports, usagePosition);
const addToExisting = tryAddToExistingImport(existingImports, isValidTypeOnlyUseSite, checker, program.getCompilerOptions());
if (addToExisting) {
@@ -706,7 +970,7 @@ function getNamespaceLikeImportText(declaration: AnyImportOrRequire) {
function getAddAsTypeOnly(
isValidTypeOnlyUseSite: boolean,
isForNewImportDeclaration: boolean,
symbol: Symbol,
symbol: Symbol | undefined,
targetFlags: SymbolFlags,
checker: TypeChecker,
compilerOptions: CompilerOptions,
@@ -716,6 +980,7 @@ function getAddAsTypeOnly(
return AddAsTypeOnly.NotAllowed;
}
if (
symbol &&
compilerOptions.verbatimModuleSyntax &&
(!(targetFlags & SymbolFlags.Value) || !!checker.getTypeOnlyAliasDeclaration(symbol))
) {
@@ -798,7 +1063,8 @@ function tryAddToExistingImport(existingImports: readonly FixAddToExistingImport
}
}
function createExistingImportMap(checker: TypeChecker, importingFile: SourceFile, compilerOptions: CompilerOptions) {
function createExistingImportMap(importingFile: SourceFile, program: Program) {
const checker = program.getTypeChecker();
let importMap: MultiMap<SymbolId, AnyImportOrRequire> | undefined;
for (const moduleSpecifier of importingFile.imports) {
const i = importFromModuleSpecifier(moduleSpecifier);
@@ -828,15 +1094,15 @@ function createExistingImportMap(checker: TypeChecker, importingFile: SourceFile
&& !every(matchingDeclarations, isJSDocImportTag)
) return emptyArray;
const importKind = getImportKind(importingFile, exportKind, compilerOptions);
const importKind = getImportKind(importingFile, exportKind, program);
return matchingDeclarations.map(declaration => ({ declaration, importKind, symbol, targetFlags }));
},
};
}
function shouldUseRequire(sourceFile: SourceFile, program: Program): boolean {
function shouldUseRequire(sourceFile: SourceFile | FutureSourceFile, program: Program): boolean {
// 1. TypeScript files don't use require variable declarations
if (!isSourceFileJS(sourceFile)) {
if (!hasJSFileExtension(sourceFile.fileName)) {
return false;
}
@@ -852,8 +1118,9 @@ function shouldUseRequire(sourceFile: SourceFile, program: Program): boolean {
// 4. In --module nodenext, assume we're not emitting JS -> JS, so use
// whatever syntax Node expects based on the detected module kind
if (sourceFile.impliedNodeFormat === ModuleKind.CommonJS) return true;
if (sourceFile.impliedNodeFormat === ModuleKind.ESNext) return false;
// TODO: consider removing `impliedNodeFormatForEmit`
if (getImpliedNodeFormatForEmit(sourceFile, program) === ModuleKind.CommonJS) return true;
if (getImpliedNodeFormatForEmit(sourceFile, program) === ModuleKind.ESNext) return false;
// 5. Match the first other JS file in the program that's unambiguously CJS or ESM
for (const otherFile of program.getSourceFiles()) {
@@ -872,29 +1139,29 @@ function createGetChecker(program: Program, host: LanguageServiceHost) {
function getNewImportFixes(
program: Program,
sourceFile: SourceFile,
sourceFile: SourceFile | FutureSourceFile,
usagePosition: number | undefined,
isValidTypeOnlyUseSite: boolean,
useRequire: boolean,
exportInfo: readonly SymbolExportInfo[],
exportInfo: readonly (SymbolExportInfo | FutureSymbolExportInfo)[],
host: LanguageServiceHost,
preferences: UserPreferences,
fromCacheOnly?: boolean,
): { computedWithoutCacheCount: number; fixes: readonly (FixAddNewImport | FixAddJsdocTypeImport)[]; } {
const isJs = isSourceFileJS(sourceFile);
const isJs = hasJSFileExtension(sourceFile.fileName);
const compilerOptions = program.getCompilerOptions();
const moduleSpecifierResolutionHost = createModuleSpecifierResolutionHost(program, host);
const getChecker = createGetChecker(program, host);
const moduleResolution = getEmitModuleResolutionKind(compilerOptions);
const rejectNodeModulesRelativePaths = moduleResolutionUsesNodeModules(moduleResolution);
const getModuleSpecifiers = fromCacheOnly
? (moduleSymbol: Symbol) => ({ moduleSpecifiers: moduleSpecifiers.tryGetModuleSpecifiersFromCache(moduleSymbol, sourceFile, moduleSpecifierResolutionHost, preferences), computedWithoutCache: false })
: (moduleSymbol: Symbol, checker: TypeChecker) => moduleSpecifiers.getModuleSpecifiersWithCacheInfo(moduleSymbol, checker, compilerOptions, sourceFile, moduleSpecifierResolutionHost, preferences, /*options*/ undefined, /*forAutoImport*/ true);
? (exportInfo: SymbolExportInfo | FutureSymbolExportInfo) => ({ moduleSpecifiers: moduleSpecifiers.tryGetModuleSpecifiersFromCache(exportInfo.moduleSymbol, sourceFile, moduleSpecifierResolutionHost, preferences), computedWithoutCache: false })
: (exportInfo: SymbolExportInfo | FutureSymbolExportInfo, checker: TypeChecker) => moduleSpecifiers.getModuleSpecifiersWithCacheInfo(exportInfo.moduleSymbol, checker, compilerOptions, sourceFile, moduleSpecifierResolutionHost, preferences, /*options*/ undefined, /*forAutoImport*/ true);
let computedWithoutCacheCount = 0;
const fixes = flatMap(exportInfo, (exportInfo, i) => {
const checker = getChecker(exportInfo.isFromPackageJson);
const { computedWithoutCache, moduleSpecifiers } = getModuleSpecifiers(exportInfo.moduleSymbol, checker);
const { computedWithoutCache, moduleSpecifiers } = getModuleSpecifiers(exportInfo, checker);
const importedSymbolHasValueMeaning = !!(exportInfo.targetFlags & SymbolFlags.Value);
const addAsTypeOnly = getAddAsTypeOnly(isValidTypeOnlyUseSite, /*isForNewImportDeclaration*/ true, exportInfo.symbol, exportInfo.targetFlags, checker, compilerOptions);
computedWithoutCacheCount += computedWithoutCache ? 1 : 0;
@@ -906,7 +1173,7 @@ function getNewImportFixes(
// `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types.
return { kind: ImportFixKind.JsdocTypeImport, moduleSpecifier, usagePosition, exportInfo, isReExport: i > 0 };
}
const importKind = getImportKind(sourceFile, exportInfo.exportKind, compilerOptions);
const importKind = getImportKind(sourceFile, exportInfo.exportKind, program);
let qualification: Qualification | undefined;
if (usagePosition !== undefined && importKind === ImportKind.CommonJS && exportInfo.exportKind === ExportKind.Named) {
// Compiler options are restricting our import options to a require, but we need to access
@@ -944,10 +1211,10 @@ function getNewImportFixes(
}
function getFixesForAddImport(
exportInfos: readonly SymbolExportInfo[],
exportInfos: readonly SymbolExportInfo[] | readonly FutureSymbolExportInfo[],
existingImports: readonly FixAddToExistingImportInfo[],
program: Program,
sourceFile: SourceFile,
sourceFile: SourceFile | FutureSourceFile,
usagePosition: number | undefined,
isValidTypeOnlyUseSite: boolean,
useRequire: boolean,
@@ -1011,7 +1278,13 @@ function sortFixInfo(fixes: readonly (FixInfo & { fix: ImportFixWithModuleSpecif
compareModuleSpecifiers(a.fix, b.fix, sourceFile, program, packageJsonImportFilter.allowsImportingSpecifier, _toPath));
}
function getBestFix(fixes: readonly ImportFixWithModuleSpecifier[], sourceFile: SourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter, host: LanguageServiceHost): ImportFixWithModuleSpecifier | undefined {
function getFixInfosWithoutDiagnostic(context: CodeFixContextBase, symbolToken: Identifier, useAutoImportProvider: boolean): readonly FixInfo[] | undefined {
const info = getFixesInfoForNonUMDImport(context, symbolToken, useAutoImportProvider);
const packageJsonImportFilter = createPackageJsonImportFilter(context.sourceFile, context.preferences, context.host);
return info && sortFixInfo(info, context.sourceFile, context.program, packageJsonImportFilter, context.host);
}
function getBestFix(fixes: readonly ImportFixWithModuleSpecifier[], sourceFile: SourceFile | FutureSourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter, host: LanguageServiceHost): ImportFixWithModuleSpecifier | undefined {
if (!some(fixes)) return;
// These will always be placed first if available, and are better than other kinds
if (fixes[0].kind === ImportFixKind.UseNamespace || fixes[0].kind === ImportFixKind.AddToExisting) {
@@ -1035,7 +1308,7 @@ function getBestFix(fixes: readonly ImportFixWithModuleSpecifier[], sourceFile:
function compareModuleSpecifiers(
a: ImportFixWithModuleSpecifier,
b: ImportFixWithModuleSpecifier,
importingFile: SourceFile,
importingFile: SourceFile | FutureSourceFile,
program: Program,
allowsImportingSpecifier: (specifier: string) => boolean,
toPath: (fileName: string) => Path,
@@ -1044,8 +1317,8 @@ function compareModuleSpecifiers(
return compareBooleans(allowsImportingSpecifier(b.moduleSpecifier), allowsImportingSpecifier(a.moduleSpecifier))
|| compareNodeCoreModuleSpecifiers(a.moduleSpecifier, b.moduleSpecifier, importingFile, program)
|| compareBooleans(
isFixPossiblyReExportingImportingFile(a, importingFile, program.getCompilerOptions(), toPath),
isFixPossiblyReExportingImportingFile(b, importingFile, program.getCompilerOptions(), toPath),
isFixPossiblyReExportingImportingFile(a, importingFile.path, toPath),
isFixPossiblyReExportingImportingFile(b, importingFile.path, toPath),
)
|| compareNumberOfDirectorySeparators(a.moduleSpecifier, b.moduleSpecifier);
}
@@ -1056,14 +1329,14 @@ function compareModuleSpecifiers(
// E.g., do not `import { Foo } from ".."` when you could `import { Foo } from "../Foo"`.
// This can produce false positives or negatives if re-exports cross into sibling directories
// (e.g. `export * from "../whatever"`) or are not named "index".
function isFixPossiblyReExportingImportingFile(fix: ImportFixWithModuleSpecifier, importingFile: SourceFile, compilerOptions: CompilerOptions, toPath: (fileName: string) => Path): boolean {
function isFixPossiblyReExportingImportingFile(fix: ImportFixWithModuleSpecifier, importingFilePath: Path, toPath: (fileName: string) => Path): boolean {
if (
fix.isReExport &&
fix.exportInfo?.moduleFileName &&
isIndexFileName(fix.exportInfo.moduleFileName)
) {
const reExportDir = toPath(getDirectoryPath(fix.exportInfo.moduleFileName));
return startsWith(importingFile.path, reExportDir);
return startsWith(importingFilePath, reExportDir);
}
return false;
}
@@ -1072,7 +1345,7 @@ function isIndexFileName(fileName: string) {
return getBaseFileName(fileName, [".js", ".jsx", ".d.ts", ".ts", ".tsx"], /*ignoreCase*/ true) === "index";
}
function compareNodeCoreModuleSpecifiers(a: string, b: string, importingFile: SourceFile, program: Program): Comparison {
function compareNodeCoreModuleSpecifiers(a: string, b: string, importingFile: SourceFile | FutureSourceFile, program: Program): Comparison {
if (startsWith(a, "node:") && !startsWith(b, "node:")) return shouldUseUriStyleNodeCoreModules(importingFile, program) ? Comparison.LessThan : Comparison.GreaterThan;
if (startsWith(b, "node:") && !startsWith(a, "node:")) return shouldUseUriStyleNodeCoreModules(importingFile, program) ? Comparison.GreaterThan : Comparison.LessThan;
return Comparison.EqualTo;
@@ -1117,8 +1390,8 @@ function getUmdSymbol(token: Node, checker: TypeChecker): Symbol | undefined {
*
* @internal
*/
export function getImportKind(importingFile: SourceFile, exportKind: ExportKind, compilerOptions: CompilerOptions, forceImportKeyword?: boolean): ImportKind {
if (compilerOptions.verbatimModuleSyntax && (getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS || importingFile.impliedNodeFormat === ModuleKind.CommonJS)) {
export function getImportKind(importingFile: SourceFile | FutureSourceFile, exportKind: ExportKind, program: Program, forceImportKeyword?: boolean): ImportKind {
if (program.getCompilerOptions().verbatimModuleSyntax && getEmitModuleFormatOfFile(importingFile, program) === ModuleKind.CommonJS) {
// TODO: if the exporting file is ESM under nodenext, or `forceImport` is given in a JS file, this is impossible
return ImportKind.CommonJS;
}
@@ -1128,28 +1401,28 @@ export function getImportKind(importingFile: SourceFile, exportKind: ExportKind,
case ExportKind.Default:
return ImportKind.Default;
case ExportKind.ExportEquals:
return getExportEqualsImportKind(importingFile, compilerOptions, !!forceImportKeyword);
return getExportEqualsImportKind(importingFile, program.getCompilerOptions(), !!forceImportKeyword);
case ExportKind.UMD:
return getUmdImportKind(importingFile, compilerOptions, !!forceImportKeyword);
return getUmdImportKind(importingFile, program, !!forceImportKeyword);
default:
return Debug.assertNever(exportKind);
}
}
function getUmdImportKind(importingFile: SourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind {
function getUmdImportKind(importingFile: SourceFile | FutureSourceFile, program: Program, forceImportKeyword: boolean): ImportKind {
// Import a synthetic `default` if enabled.
if (getAllowSyntheticDefaultImports(compilerOptions)) {
if (getAllowSyntheticDefaultImports(program.getCompilerOptions())) {
return ImportKind.Default;
}
// When a synthetic `default` is unavailable, use `import..require` if the module kind supports it.
const moduleKind = getEmitModuleKind(compilerOptions);
const moduleKind = getEmitModuleKind(program.getCompilerOptions());
switch (moduleKind) {
case ModuleKind.AMD:
case ModuleKind.CommonJS:
case ModuleKind.UMD:
if (isInJSFile(importingFile)) {
return isExternalModule(importingFile) || forceImportKeyword ? ImportKind.Namespace : ImportKind.CommonJS;
if (hasJSFileExtension(importingFile.fileName)) {
return importingFile.externalModuleIndicator || forceImportKeyword ? ImportKind.Namespace : ImportKind.CommonJS;
}
return ImportKind.CommonJS;
case ModuleKind.System:
@@ -1163,7 +1436,7 @@ function getUmdImportKind(importingFile: SourceFile, compilerOptions: CompilerOp
return ImportKind.Namespace;
case ModuleKind.Node16:
case ModuleKind.NodeNext:
return importingFile.impliedNodeFormat === ModuleKind.ESNext ? ImportKind.Namespace : ImportKind.CommonJS;
return getImpliedNodeFormatForEmit(importingFile, program) === ModuleKind.ESNext ? ImportKind.Namespace : ImportKind.CommonJS;
default:
return Debug.assertNever(moduleKind, `Unexpected moduleKind ${moduleKind}`);
}
@@ -1265,9 +1538,9 @@ function getExportInfos(
return originalSymbolToExportInfos;
}
function getExportEqualsImportKind(importingFile: SourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind {
function getExportEqualsImportKind(importingFile: SourceFile | FutureSourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind {
const allowSyntheticDefaults = getAllowSyntheticDefaultImports(compilerOptions);
const isJS = isInJSFile(importingFile);
const isJS = hasJSFileExtension(importingFile.fileName);
// 1. 'import =' will not work in es2015+ TS files, so the decision is between a default
// and a namespace import, based on allowSyntheticDefaultImports/esModuleInterop.
if (!isJS && getEmitModuleKind(compilerOptions) >= ModuleKind.ES2015) {
@@ -1276,14 +1549,14 @@ function getExportEqualsImportKind(importingFile: SourceFile, compilerOptions: C
// 2. 'import =' will not work in JavaScript, so the decision is between a default import,
// a namespace import, and const/require.
if (isJS) {
return isExternalModule(importingFile) || forceImportKeyword
return importingFile.externalModuleIndicator || forceImportKeyword
? allowSyntheticDefaults ? ImportKind.Default : ImportKind.Namespace
: ImportKind.CommonJS;
}
// 3. At this point the most correct choice is probably 'import =', but people
// really hate that, so look to see if the importing file has any precedent
// on how to handle it.
for (const statement of importingFile.statements) {
for (const statement of importingFile.statements ?? emptyArray) {
// `import foo` parses as an ImportEqualsDeclaration even though it could be an ImportDeclaration
if (isImportEqualsDeclaration(statement) && !nodeIsMissing(statement.moduleReference)) {
return ImportKind.CommonJS;
@@ -1334,6 +1607,7 @@ function codeActionForFixWorker(
importClauseOrBindingPattern,
importKind === ImportKind.Default ? { name: symbolName, addAsTypeOnly } : undefined,
importKind === ImportKind.Named ? [{ name: symbolName, addAsTypeOnly }] : emptyArray,
/*removeExistingImportSpecifiers*/ undefined,
preferences,
);
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier);
@@ -1474,9 +1748,25 @@ function doAddExistingFix(
clause: ImportClause | ObjectBindingPattern,
defaultImport: Import | undefined,
namedImports: readonly Import[],
removeExistingImportSpecifiers: Set<ImportSpecifier | BindingElement> | undefined,
preferences: UserPreferences,
): void {
if (clause.kind === SyntaxKind.ObjectBindingPattern) {
if (removeExistingImportSpecifiers && clause.elements.some(e => removeExistingImportSpecifiers.has(e))) {
// If we're both adding and removing elements, just replace and reprint the whole
// node. The change tracker doesn't understand all the operations and can insert or
// leave behind stray commas.
changes.replaceNode(
sourceFile,
clause,
factory.createObjectBindingPattern([
...clause.elements.filter(e => !removeExistingImportSpecifiers.has(e)),
...defaultImport ? [factory.createBindingElement(/*dotDotDotToken*/ undefined, /*propertyName*/ "default", defaultImport.name)] : emptyArray,
...namedImports.map(i => factory.createBindingElement(/*dotDotDotToken*/ undefined, /*propertyName*/ undefined, i.name)),
]),
);
return;
}
if (defaultImport) {
addElementToBindingPattern(clause, defaultImport.name, "default");
}
@@ -1508,6 +1798,19 @@ function doAddExistingFix(
specifierComparer,
);
if (removeExistingImportSpecifiers) {
// If we're both adding and removing specifiers, just replace and reprint the whole
// node. The change tracker doesn't understand all the operations and can insert or
// leave behind stray commas.
changes.replaceNode(
sourceFile,
clause.namedBindings!,
factory.updateNamedImports(
clause.namedBindings as NamedImports,
stableSort([...existingSpecifiers!.filter(s => !removeExistingImportSpecifiers.has(s)), ...newSpecifiers], specifierComparer),
),
);
}
// The sorting preference computed earlier may or may not have validated that these particular
// import specifiers are sorted. If they aren't, `getImportSpecifierInsertionIndex` will return
// nonsense. So if there are existing specifiers, even if we know the sorting preference, we
@@ -1515,7 +1818,7 @@ function doAddExistingFix(
// to do a sorted insertion.
// changed to check if existing specifiers are sorted
if (existingSpecifiers?.length && isSorted !== false) {
else if (existingSpecifiers?.length && isSorted !== false) {
// if we're promoting the clause from type-only, we need to transform the existing imports before attempting to insert the new named imports
const transformedExistingSpecifiers = (promoteFromTypeOnly && existingSpecifiers) ? factory.updateNamedImports(
clause.namedBindings as NamedImports,
@@ -1736,3 +2039,11 @@ export function moduleSpecifierToValidIdentifier(moduleSpecifier: string, target
// Need `|| "_"` to ensure result isn't empty.
return !isStringANonContextualKeyword(res) ? res || "_" : `_${res}`;
}
function getImpliedNodeFormatForEmit(file: SourceFile | FutureSourceFile, program: Program) {
return isFullSourceFile(file) ? program.getImpliedNodeFormatForEmit(file) : getImpliedNodeFormatForEmitWorker(file, program.getCompilerOptions());
}
function getEmitModuleFormatOfFile(file: SourceFile | FutureSourceFile, program: Program) {
return isFullSourceFile(file) ? program.getEmitModuleFormatOfFile(file) : getEmitModuleFormatOfFileWorker(file, program.getCompilerOptions());
}
+14 -7
View File
@@ -73,6 +73,7 @@ import {
forEach,
formatting,
FunctionLikeDeclaration,
FutureSymbolExportInfo,
getAllSuperTypeNodes,
getAncestor,
getCombinedLocalAndExportSymbolFlags,
@@ -609,7 +610,7 @@ interface ModuleSpecifierResolutionContext {
}
type ModuleSpecifierResolutionResult = "skipped" | "failed" | {
exportInfo?: SymbolExportInfo;
exportInfo?: SymbolExportInfo | FutureSymbolExportInfo;
moduleSpecifier: string;
};
@@ -1157,11 +1158,13 @@ function getJSDocParamAnnotation(
? createSnippetPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
})
: createPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
});
setEmitFlags(typeNode, EmitFlags.SingleLine);
@@ -1455,6 +1458,7 @@ function getExhaustiveCaseSnippets(
const printer = createSnippetPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
newLine: getNewLineKind(newLineChar),
});
@@ -1716,7 +1720,7 @@ function createCompletionEntry(
if (originIsResolvedExport(origin)) {
sourceDisplay = [textPart(origin.moduleSpecifier)];
if (importStatementCompletion) {
({ insertText, replacementSpan } = getInsertTextAndReplacementSpanForImportCompletion(name, importStatementCompletion, origin, useSemicolons, sourceFile, options, preferences));
({ insertText, replacementSpan } = getInsertTextAndReplacementSpanForImportCompletion(name, importStatementCompletion, origin, useSemicolons, sourceFile, program, preferences));
isSnippet = preferences.includeCompletionsWithSnippetText ? true : undefined;
}
}
@@ -1942,6 +1946,7 @@ function getEntryForMemberCompletion(
const printer = createSnippetPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
omitTrailingSemicolon: false,
newLine: getNewLineKind(getNewLineOrDefaultFromHost(host, formatContext?.options)),
@@ -2168,6 +2173,7 @@ function getEntryForObjectLiteralMethodCompletion(
const printer = createSnippetPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
omitTrailingSemicolon: false,
newLine: getNewLineKind(getNewLineOrDefaultFromHost(host, formatContext?.options)),
@@ -2182,6 +2188,7 @@ function getEntryForObjectLiteralMethodCompletion(
const signaturePrinter = createPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
omitTrailingSemicolon: true,
});
@@ -2484,14 +2491,14 @@ function completionEntryDataToSymbolOriginInfo(data: CompletionEntryData, comple
return unresolvedOrigin;
}
function getInsertTextAndReplacementSpanForImportCompletion(name: string, importStatementCompletion: ImportStatementCompletionInfo, origin: SymbolOriginInfoResolvedExport, useSemicolons: boolean, sourceFile: SourceFile, options: CompilerOptions, preferences: UserPreferences) {
function getInsertTextAndReplacementSpanForImportCompletion(name: string, importStatementCompletion: ImportStatementCompletionInfo, origin: SymbolOriginInfoResolvedExport, useSemicolons: boolean, sourceFile: SourceFile, program: Program, preferences: UserPreferences) {
const replacementSpan = importStatementCompletion.replacementSpan;
const quotedModuleSpecifier = escapeSnippetText(quote(sourceFile, preferences, origin.moduleSpecifier));
const exportKind = origin.isDefaultExport ? ExportKind.Default :
origin.exportName === InternalSymbolName.ExportEquals ? ExportKind.ExportEquals :
ExportKind.Named;
const tabStop = preferences.includeCompletionsWithSnippetText ? "$1" : "";
const importKind = codefix.getImportKind(sourceFile, exportKind, options, /*forceImportKeyword*/ true);
const importKind = codefix.getImportKind(sourceFile, exportKind, program, /*forceImportKeyword*/ true);
const isImportSpecifierTypeOnly = importStatementCompletion.couldBeTypeOnlyImportSpecifier;
const topLevelTypeOnlyText = importStatementCompletion.isTopLevelTypeOnly ? ` ${tokenToString(SyntaxKind.TypeKeyword)} ` : " ";
const importSpecifierTypeOnlyText = isImportSpecifierTypeOnly ? `${tokenToString(SyntaxKind.TypeKeyword)} ` : "";
@@ -4079,20 +4086,20 @@ function getCompletionData(
// it should be identical regardless of which one is used. During the subsequent
// `CompletionEntryDetails` request, we'll get all the ExportInfos again and pick
// the best one based on the module specifier it produces.
let exportInfo = info[0], moduleSpecifier;
let exportInfo: SymbolExportInfo | FutureSymbolExportInfo = info[0], moduleSpecifier;
if (result !== "skipped") {
({ exportInfo = info[0], moduleSpecifier } = result);
}
const isDefaultExport = exportInfo.exportKind === ExportKind.Default;
const symbol = isDefaultExport && getLocalSymbolForExportDefault(exportInfo.symbol) || exportInfo.symbol;
const symbol = isDefaultExport && getLocalSymbolForExportDefault(Debug.checkDefined(exportInfo.symbol)) || Debug.checkDefined(exportInfo.symbol);
pushAutoImportSymbol(symbol, {
kind: moduleSpecifier ? SymbolOriginInfoKind.ResolvedExport : SymbolOriginInfoKind.Export,
moduleSpecifier,
symbolName,
exportMapKey,
exportName: exportInfo.exportKind === ExportKind.ExportEquals ? InternalSymbolName.ExportEquals : exportInfo.symbol.name,
exportName: exportInfo.exportKind === ExportKind.ExportEquals ? InternalSymbolName.ExportEquals : Debug.checkDefined(exportInfo.symbol).name,
fileName: exportInfo.moduleFileName,
isDefaultExport,
moduleSymbol: exportInfo.moduleSymbol,
+8 -1
View File
@@ -13,6 +13,7 @@ import {
firstDefined,
forEachAncestorDirectory,
forEachEntry,
FutureSourceFile,
getBaseFileName,
GetCanonicalFileName,
getDirectoryPath,
@@ -90,6 +91,12 @@ export interface SymbolExportInfo {
isFromPackageJson: boolean;
}
/**
* @internal
* ExportInfo for an export that does not exist yet, so does not have a symbol.
*/
export type FutureSymbolExportInfo = Omit<SymbolExportInfo, "symbol"> & { readonly symbol?: undefined; };
interface CachedSymbolExportInfo {
// Used to rehydrate `symbol` and `moduleSymbol` when transient
id: number;
@@ -477,7 +484,7 @@ function forEachExternalModule(checker: TypeChecker, allSourceFiles: readonly So
}
/** @internal */
export function getExportInfoMap(importingFile: SourceFile, host: LanguageServiceHost, program: Program, preferences: UserPreferences, cancellationToken: CancellationToken | undefined): ExportInfoMap {
export function getExportInfoMap(importingFile: SourceFile | FutureSourceFile, host: LanguageServiceHost, program: Program, preferences: UserPreferences, cancellationToken: CancellationToken | undefined): ExportInfoMap {
const start = timestamp();
// Pulling the AutoImportProvider project will trigger its updateGraph if pending,
// which will invalidate the export map cache if things change, so pull it before
+1 -1
View File
@@ -342,7 +342,7 @@ export function getReferenceAtPosition(sourceFile: SourceFile, position: number,
const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position);
if (typeReferenceDirective) {
const reference = program.getResolvedTypeReferenceDirectives().get(typeReferenceDirective.fileName, typeReferenceDirective.resolutionMode || sourceFile.impliedNodeFormat)?.resolvedTypeReferenceDirective;
const reference = program.getResolvedTypeReferenceDirectives().get(typeReferenceDirective.fileName, typeReferenceDirective.resolutionMode || program.getDefaultResolutionModeForFile(sourceFile))?.resolvedTypeReferenceDirective;
const file = reference && program.getSourceFile(reference.resolvedFileName!); // TODO:GH#18217
return file && { reference: typeReferenceDirective, fileName: file.fileName, file, unverified: false };
}
+1 -1
View File
@@ -480,7 +480,7 @@ export function findModuleReferences(program: Program, sourceFiles: readonly Sou
}
}
for (const ref of referencingFile.typeReferenceDirectives) {
const referenced = program.getResolvedTypeReferenceDirectives().get(ref.fileName, ref.resolutionMode || referencingFile.impliedNodeFormat)?.resolvedTypeReferenceDirective;
const referenced = program.getResolvedTypeReferenceDirectives().get(ref.fileName, ref.resolutionMode || program.getDefaultResolutionModeForFile(referencingFile))?.resolvedTypeReferenceDirective;
if (referenced !== undefined && referenced.resolvedFileName === (searchSourceFile as SourceFile).fileName) {
refs.push({ kind: "reference", referencingFile, ref });
}
+6 -2
View File
@@ -329,7 +329,11 @@ class ServicesSymbolObjectInternals extends SymbolObjectInternals {
override getJsDocTags(symbol: SymbolObject, checker?: TypeChecker): JSDocTagInfo[] {
const extra = ensureSymbolExtraFields(symbol);
return extra.tags ??= getJsDocTagsOfDeclarations(symbol.declarations, checker);
if (!extra.tags) {
extra.tags = emptyArray; // Set temporarily to avoid an infinite loop finding inherited tags
extra.tags = getJsDocTagsOfDeclarations(symbol.declarations, checker);
}
return extra.tags;
}
override getContextualJsDocTags(symbol: SymbolObject, context: Node | undefined, checker: TypeChecker | undefined): JSDocTagInfo[] {
@@ -402,7 +406,7 @@ function getJsDocTagsOfDeclarations(declarations: Declaration[] | undefined, che
if (declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) {
return symbol.getContextualJsDocTags(declaration, checker);
}
return symbol.declarations?.length === 1 ? symbol.getJsDocTags() : undefined;
return symbol.declarations?.length === 1 ? symbol.getJsDocTags(checker) : undefined;
}
});
if (inheritedTags) {
+42 -30
View File
@@ -197,7 +197,7 @@ export function getStringLiteralCompletions(
includeSymbol: boolean,
): CompletionInfo | undefined {
if (isInReferenceComment(sourceFile, position)) {
const entries = getTripleSlashReferenceCompletion(sourceFile, position, options, host);
const entries = getTripleSlashReferenceCompletion(sourceFile, position, program, host);
return entries && convertPathCompletions(entries);
}
if (isInString(sourceFile, position, contextToken)) {
@@ -593,8 +593,8 @@ function getStringLiteralCompletionsFromModuleNamesWorker(sourceFile: SourceFile
const extensionOptions = getExtensionOptions(compilerOptions, ReferenceKind.ModuleSpecifier, sourceFile, typeChecker, preferences, mode);
return isPathRelativeToScript(literalValue) || !compilerOptions.baseUrl && !compilerOptions.paths && (isRootedDiskPath(literalValue) || isUrl(literalValue))
? getCompletionEntriesForRelativeModules(literalValue, scriptDirectory, compilerOptions, host, scriptPath, extensionOptions)
: getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, mode, compilerOptions, host, extensionOptions, typeChecker);
? getCompletionEntriesForRelativeModules(literalValue, scriptDirectory, program, host, scriptPath, extensionOptions)
: getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, mode, program, host, extensionOptions);
}
interface ExtensionOptions {
@@ -614,20 +614,21 @@ function getExtensionOptions(compilerOptions: CompilerOptions, referenceKind: Re
resolutionMode,
};
}
function getCompletionEntriesForRelativeModules(literalValue: string, scriptDirectory: string, compilerOptions: CompilerOptions, host: LanguageServiceHost, scriptPath: Path, extensionOptions: ExtensionOptions) {
function getCompletionEntriesForRelativeModules(literalValue: string, scriptDirectory: string, program: Program, host: LanguageServiceHost, scriptPath: Path, extensionOptions: ExtensionOptions) {
const compilerOptions = program.getCompilerOptions();
if (compilerOptions.rootDirs) {
return getCompletionEntriesForDirectoryFragmentWithRootDirs(
compilerOptions.rootDirs,
literalValue,
scriptDirectory,
extensionOptions,
compilerOptions,
program,
host,
scriptPath,
);
}
else {
return arrayFrom(getCompletionEntriesForDirectoryFragment(literalValue, scriptDirectory, extensionOptions, host, /*moduleSpecifierIsRelative*/ true, scriptPath).values());
return arrayFrom(getCompletionEntriesForDirectoryFragment(literalValue, scriptDirectory, extensionOptions, program, host, /*moduleSpecifierIsRelative*/ true, scriptPath).values());
}
}
@@ -665,12 +666,13 @@ function getBaseDirectoriesFromRootDirs(rootDirs: string[], basePath: string, sc
);
}
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptDirectory: string, extensionOptions: ExtensionOptions, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude: string): readonly NameAndKind[] {
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptDirectory: string, extensionOptions: ExtensionOptions, program: Program, host: LanguageServiceHost, exclude: string): readonly NameAndKind[] {
const compilerOptions = program.getCompilerOptions();
const basePath = compilerOptions.project || host.getCurrentDirectory();
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptDirectory, ignoreCase);
return deduplicate<NameAndKind>(
flatMap(baseDirectories, baseDirectory => arrayFrom(getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensionOptions, host, /*moduleSpecifierIsRelative*/ true, exclude).values())),
flatMap(baseDirectories, baseDirectory => arrayFrom(getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensionOptions, program, host, /*moduleSpecifierIsRelative*/ true, exclude).values())),
(itemA, itemB) => itemA.name === itemB.name && itemA.kind === itemB.kind && itemA.extension === itemB.extension,
);
}
@@ -686,6 +688,7 @@ function getCompletionEntriesForDirectoryFragment(
fragment: string,
scriptDirectory: string,
extensionOptions: ExtensionOptions,
program: Program,
host: LanguageServiceHost,
moduleSpecifierIsRelative: boolean,
exclude?: string,
@@ -725,7 +728,7 @@ function getCompletionEntriesForDirectoryFragment(
if (versionPaths) {
const packageDirectory = getDirectoryPath(packageJsonPath);
const pathInPackage = absolutePath.slice(ensureTrailingDirectorySeparator(packageDirectory).length);
if (addCompletionEntriesFromPaths(result, pathInPackage, packageDirectory, extensionOptions, host, versionPaths)) {
if (addCompletionEntriesFromPaths(result, pathInPackage, packageDirectory, extensionOptions, program, host, versionPaths)) {
// A true result means one of the `versionPaths` was matched, which will block relative resolution
// to files and folders from here. All reachable paths given the pattern match are already added.
return result;
@@ -748,7 +751,7 @@ function getCompletionEntriesForDirectoryFragment(
continue;
}
const { name, extension } = getFilenameWithExtensionOption(getBaseFileName(filePath), host.getCompilationSettings(), extensionOptions, /*isExportsWildcard*/ false);
const { name, extension } = getFilenameWithExtensionOption(getBaseFileName(filePath), program, extensionOptions, /*isExportsWildcard*/ false);
result.add(nameAndKind(name, ScriptElementKind.scriptElement, extension));
}
}
@@ -768,7 +771,7 @@ function getCompletionEntriesForDirectoryFragment(
return result;
}
function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerOptions, extensionOptions: ExtensionOptions, isExportsWildcard: boolean): { name: string; extension: Extension | undefined; } {
function getFilenameWithExtensionOption(name: string, program: Program, extensionOptions: ExtensionOptions, isExportsWildcard: boolean): { name: string; extension: Extension | undefined; } {
const nonJsResult = moduleSpecifiers.tryGetRealFileNameForNonJsDeclarationFileName(name);
if (nonJsResult) {
return { name: nonJsResult, extension: tryGetExtensionFromPath(nonJsResult) };
@@ -779,7 +782,8 @@ function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerO
let allowedEndings = getModuleSpecifierPreferences(
{ importModuleSpecifierEnding: extensionOptions.endingPreference },
compilerOptions,
program,
program.getCompilerOptions(),
extensionOptions.importingSourceFile,
).getAllowedEndingsInPreferredOrder(extensionOptions.resolutionMode);
@@ -793,7 +797,7 @@ function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerO
if (fileExtensionIsOneOf(name, supportedTSImplementationExtensions)) {
return { name, extension: tryGetExtensionFromPath(name) };
}
const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(name, compilerOptions);
const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(name, program.getCompilerOptions());
return outputExtension
? { name: changeExtension(name, outputExtension), extension: outputExtension }
: { name, extension: tryGetExtensionFromPath(name) };
@@ -807,7 +811,7 @@ function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerO
return { name: removeFileExtension(name), extension: tryGetExtensionFromPath(name) };
}
const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(name, compilerOptions);
const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(name, program.getCompilerOptions());
return outputExtension
? { name: changeExtension(name, outputExtension), extension: outputExtension }
: { name, extension: tryGetExtensionFromPath(name) };
@@ -819,6 +823,7 @@ function addCompletionEntriesFromPaths(
fragment: string,
baseDirectory: string,
extensionOptions: ExtensionOptions,
program: Program,
host: LanguageServiceHost,
paths: MapLike<string[]>,
) {
@@ -830,7 +835,7 @@ function addCompletionEntriesFromPaths(
const lengthB = typeof patternB === "object" ? patternB.prefix.length : b.length;
return compareValues(lengthB, lengthA);
};
return addCompletionEntriesFromPathsOrExports(result, /*isExports*/ false, fragment, baseDirectory, extensionOptions, host, getOwnKeys(paths), getPatternsForKey, comparePaths);
return addCompletionEntriesFromPathsOrExports(result, /*isExports*/ false, fragment, baseDirectory, extensionOptions, program, host, getOwnKeys(paths), getPatternsForKey, comparePaths);
}
/** @returns whether `fragment` was a match for any `paths` (which should indicate whether any other path completions should be offered) */
@@ -840,6 +845,7 @@ function addCompletionEntriesFromPathsOrExports(
fragment: string,
baseDirectory: string,
extensionOptions: ExtensionOptions,
program: Program,
host: LanguageServiceHost,
keys: readonly string[],
getPatternsForKey: (key: string) => string[] | undefined,
@@ -874,7 +880,7 @@ function addCompletionEntriesFromPathsOrExports(
if (typeof pathPattern === "string" || matchedPath === undefined || comparePaths(key, matchedPath) !== Comparison.GreaterThan) {
pathResults.push({
matchedPattern: isMatch,
results: getCompletionsForPathMapping(keyWithoutLeadingDotSlash, patterns, fragment, baseDirectory, extensionOptions, isExports && isMatch, host)
results: getCompletionsForPathMapping(keyWithoutLeadingDotSlash, patterns, fragment, baseDirectory, extensionOptions, isExports && isMatch, program, host)
.map(({ name, kind, extension }) => nameAndKind(name, kind, extension)),
});
}
@@ -896,11 +902,12 @@ function getCompletionEntriesForNonRelativeModules(
fragment: string,
scriptPath: string,
mode: ResolutionMode,
compilerOptions: CompilerOptions,
program: Program,
host: LanguageServiceHost,
extensionOptions: ExtensionOptions,
typeChecker: TypeChecker,
): readonly NameAndKind[] {
const typeChecker = program.getTypeChecker();
const compilerOptions = program.getCompilerOptions();
const { baseUrl, paths } = compilerOptions;
const result = createNameAndKindSet();
@@ -908,12 +915,12 @@ function getCompletionEntriesForNonRelativeModules(
if (baseUrl) {
const absolute = normalizePath(combinePaths(host.getCurrentDirectory(), baseUrl));
getCompletionEntriesForDirectoryFragment(fragment, absolute, extensionOptions, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
getCompletionEntriesForDirectoryFragment(fragment, absolute, extensionOptions, program, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
}
if (paths) {
const absolute = getPathsBasePath(compilerOptions, host)!;
addCompletionEntriesFromPaths(result, fragment, absolute, extensionOptions, host, paths);
addCompletionEntriesFromPaths(result, fragment, absolute, extensionOptions, program, host, paths);
}
const fragmentDirectory = getFragmentDirectory(fragment);
@@ -921,7 +928,7 @@ function getCompletionEntriesForNonRelativeModules(
result.add(nameAndKind(ambientName, ScriptElementKind.externalModuleName, /*extension*/ undefined));
}
getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, fragmentDirectory, extensionOptions, result);
getCompletionEntriesFromTypings(host, program, scriptPath, fragmentDirectory, extensionOptions, result);
if (moduleResolutionUsesNodeModules(moduleResolution)) {
// If looking for a global package name, don't just include everything in `node_modules` because that includes dependencies' own dependencies.
@@ -940,7 +947,7 @@ function getCompletionEntriesForNonRelativeModules(
let ancestorLookup: (directory: string) => void | undefined = ancestor => {
const nodeModules = combinePaths(ancestor, "node_modules");
if (tryDirectoryExists(host, nodeModules)) {
getCompletionEntriesForDirectoryFragment(fragment, nodeModules, extensionOptions, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
getCompletionEntriesForDirectoryFragment(fragment, nodeModules, extensionOptions, program, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
}
};
if (fragmentDirectory && getResolvePackageJsonExports(compilerOptions)) {
@@ -977,6 +984,7 @@ function getCompletionEntriesForNonRelativeModules(
fragmentSubpath,
packageDirectory,
extensionOptions,
program,
host,
keys,
key => singleElementArray(getPatternFromFirstMatchingCondition(exports[key], conditions)),
@@ -1020,6 +1028,7 @@ function getCompletionsForPathMapping(
packageDirectory: string,
extensionOptions: ExtensionOptions,
isExportsWildcard: boolean,
program: Program,
host: LanguageServiceHost,
): readonly NameAndKind[] {
if (!endsWith(path, "*")) {
@@ -1031,9 +1040,9 @@ function getCompletionsForPathMapping(
const remainingFragment = tryRemovePrefix(fragment, pathPrefix);
if (remainingFragment === undefined) {
const starIsFullPathComponent = path[path.length - 2] === "/";
return starIsFullPathComponent ? justPathMappingName(pathPrefix, ScriptElementKind.directory) : flatMap(patterns, pattern => getModulesForPathsPattern("", packageDirectory, pattern, extensionOptions, isExportsWildcard, host)?.map(({ name, ...rest }) => ({ name: pathPrefix + name, ...rest })));
return starIsFullPathComponent ? justPathMappingName(pathPrefix, ScriptElementKind.directory) : flatMap(patterns, pattern => getModulesForPathsPattern("", packageDirectory, pattern, extensionOptions, isExportsWildcard, program, host)?.map(({ name, ...rest }) => ({ name: pathPrefix + name, ...rest })));
}
return flatMap(patterns, pattern => getModulesForPathsPattern(remainingFragment, packageDirectory, pattern, extensionOptions, isExportsWildcard, host));
return flatMap(patterns, pattern => getModulesForPathsPattern(remainingFragment, packageDirectory, pattern, extensionOptions, isExportsWildcard, program, host));
function justPathMappingName(name: string, kind: ScriptElementKind.directory | ScriptElementKind.scriptElement): readonly NameAndKind[] {
return startsWith(name, fragment) ? [{ name: removeTrailingDirectorySeparator(name), kind, extension: undefined }] : emptyArray;
@@ -1046,6 +1055,7 @@ function getModulesForPathsPattern(
pattern: string,
extensionOptions: ExtensionOptions,
isExportsWildcard: boolean,
program: Program,
host: LanguageServiceHost,
): readonly NameAndKind[] | undefined {
if (!host.readDirectory) {
@@ -1094,7 +1104,7 @@ function getModulesForPathsPattern(
if (containsSlash(trimmedWithPattern)) {
return directoryResult(getPathComponents(removeLeadingDirectorySeparator(trimmedWithPattern))[1]);
}
const { name, extension } = getFilenameWithExtensionOption(trimmedWithPattern, host.getCompilationSettings(), extensionOptions, isExportsWildcard);
const { name, extension } = getFilenameWithExtensionOption(trimmedWithPattern, program, extensionOptions, isExportsWildcard);
return nameAndKind(name, ScriptElementKind.scriptElement, extension);
}
});
@@ -1138,7 +1148,8 @@ function getAmbientModuleCompletions(fragment: string, fragmentDirectory: string
return nonRelativeModuleNames;
}
function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): readonly PathCompletion[] | undefined {
function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, program: Program, host: LanguageServiceHost): readonly PathCompletion[] | undefined {
const compilerOptions = program.getCompilerOptions();
const token = getTokenAtPosition(sourceFile, position);
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
const range = commentRanges && find(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end);
@@ -1153,13 +1164,14 @@ function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: num
const [, prefix, kind, toComplete] = match;
const scriptPath = getDirectoryPath(sourceFile.path);
const names = kind === "path" ? getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getExtensionOptions(compilerOptions, ReferenceKind.Filename, sourceFile), host, /*moduleSpecifierIsRelative*/ true, sourceFile.path)
: kind === "types" ? getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, getFragmentDirectory(toComplete), getExtensionOptions(compilerOptions, ReferenceKind.ModuleSpecifier, sourceFile))
const names = kind === "path" ? getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getExtensionOptions(compilerOptions, ReferenceKind.Filename, sourceFile), program, host, /*moduleSpecifierIsRelative*/ true, sourceFile.path)
: kind === "types" ? getCompletionEntriesFromTypings(host, program, scriptPath, getFragmentDirectory(toComplete), getExtensionOptions(compilerOptions, ReferenceKind.ModuleSpecifier, sourceFile))
: Debug.fail();
return addReplacementSpans(toComplete, range.pos + prefix.length, arrayFrom(names.values()));
}
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, fragmentDirectory: string | undefined, extensionOptions: ExtensionOptions, result = createNameAndKindSet()): NameAndKindSet {
function getCompletionEntriesFromTypings(host: LanguageServiceHost, program: Program, scriptPath: string, fragmentDirectory: string | undefined, extensionOptions: ExtensionOptions, result = createNameAndKindSet()): NameAndKindSet {
const options = program.getCompilerOptions();
// Check for typings specified in compiler options
const seen = new Map<string, true>();
@@ -1194,7 +1206,7 @@ function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: Com
const baseDirectory = combinePaths(directory, typeDirectoryName);
const remainingFragment = tryRemoveDirectoryPrefix(fragmentDirectory, packageName, hostGetCanonicalFileName(host));
if (remainingFragment !== undefined) {
getCompletionEntriesForDirectoryFragment(remainingFragment, baseDirectory, extensionOptions, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
getCompletionEntriesForDirectoryFragment(remainingFragment, baseDirectory, extensionOptions, program, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
}
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ export function computeSuggestionDiagnostics(sourceFile: SourceFile, program: Pr
program.getSemanticDiagnostics(sourceFile, cancellationToken);
const diags: DiagnosticWithLocation[] = [];
const checker = program.getTypeChecker();
const isCommonJSFile = sourceFile.impliedNodeFormat === ModuleKind.CommonJS || fileExtensionIsOneOf(sourceFile.fileName, [Extension.Cts, Extension.Cjs]);
const isCommonJSFile = program.getImpliedNodeFormatForEmit(sourceFile) === ModuleKind.CommonJS || fileExtensionIsOneOf(sourceFile.fileName, [Extension.Cts, Extension.Cjs]);
if (
!isCommonJSFile &&
+1 -1
View File
@@ -655,7 +655,7 @@ export class ChangeTracker {
this.insertNodesAt(sourceFile, pos, insert, options);
}
private insertStatementsInNewFile(fileName: string, statements: readonly (Statement | SyntaxKind.NewLineTrivia)[], oldFile?: SourceFile): void {
public insertStatementsInNewFile(fileName: string, statements: readonly (Statement | SyntaxKind.NewLineTrivia)[], oldFile?: SourceFile): void {
if (!this.newFileChanges) {
this.newFileChanges = createMultiMap<string, NewFileInsertion>();
}
+55 -13
View File
@@ -90,13 +90,16 @@ import {
FunctionDeclaration,
FunctionExpression,
FunctionLikeDeclaration,
FutureSourceFile,
getAssignmentDeclarationKind,
getCombinedNodeFlagsAlwaysIncludeJSDoc,
getDirectoryPath,
getEmitModuleKind,
getEmitScriptTarget,
getExternalModuleImportEqualsDeclarationExpression,
getImpliedNodeFormatForEmitWorker,
getImpliedNodeFormatForFile,
getImpliedNodeFormatForFileWorker,
getIndentString,
getJSDocEnumTag,
getLastChild,
@@ -106,6 +109,7 @@ import {
getModuleInstanceState,
getNameOfDeclaration,
getNodeId,
getOriginalNode,
getPackageNameFromTypesPackageName,
getPathComponents,
getRootDeclaration,
@@ -166,6 +170,7 @@ import {
isFileLevelUniqueName,
isForInStatement,
isForOfStatement,
isFullSourceFile,
isFunctionBlock,
isFunctionDeclaration,
isFunctionExpression,
@@ -278,6 +283,7 @@ import {
ModuleDeclaration,
ModuleInstanceState,
ModuleKind,
ModuleResolutionHost,
ModuleResolutionKind,
ModuleSpecifierResolutionHost,
moduleSpecifiers,
@@ -2469,6 +2475,8 @@ export function createModuleSpecifierResolutionHost(program: Program, host: Lang
getNearestAncestorDirectoryWithPackageJson: maybeBind(host, host.getNearestAncestorDirectoryWithPackageJson),
getFileIncludeReasons: () => program.getFileIncludeReasons(),
getCommonSourceDirectory: () => program.getCommonSourceDirectory(),
getDefaultResolutionModeForFile: file => program.getDefaultResolutionModeForFile(file),
getModeForResolutionAtIndex: (file, index) => program.getModeForResolutionAtIndex(file, index),
};
}
@@ -2521,13 +2529,13 @@ export function quotePreferenceFromString(str: StringLiteral, sourceFile: Source
}
/** @internal */
export function getQuotePreference(sourceFile: SourceFile, preferences: UserPreferences): QuotePreference {
export function getQuotePreference(sourceFile: SourceFile | FutureSourceFile, preferences: UserPreferences): QuotePreference {
if (preferences.quotePreference && preferences.quotePreference !== "auto") {
return preferences.quotePreference === "single" ? QuotePreference.Single : QuotePreference.Double;
}
else {
// ignore synthetic import added when importHelpers: true
const firstModuleSpecifier = sourceFile.imports &&
const firstModuleSpecifier = isFullSourceFile(sourceFile) && sourceFile.imports &&
find(sourceFile.imports, n => isStringLiteral(n) && !nodeIsSynthesized(n.parent)) as StringLiteral;
return firstModuleSpecifier ? quotePreferenceFromString(firstModuleSpecifier, sourceFile) : QuotePreference.Double;
}
@@ -2615,16 +2623,28 @@ export function findModifier(node: Node, kind: Modifier["kind"]): Modifier | und
}
/** @internal */
export function insertImports(changes: textChanges.ChangeTracker, sourceFile: SourceFile, imports: AnyImportOrRequireStatement | readonly AnyImportOrRequireStatement[], blankLineBetween: boolean, preferences: UserPreferences): void {
export function insertImports(changes: textChanges.ChangeTracker, sourceFile: SourceFile | FutureSourceFile, imports: AnyImportOrRequireStatement | readonly AnyImportOrRequireStatement[], blankLineBetween: boolean, preferences: UserPreferences): void {
const decl = isArray(imports) ? imports[0] : imports;
const importKindPredicate: (node: Node) => node is AnyImportOrRequireStatement = decl.kind === SyntaxKind.VariableStatement ? isRequireVariableStatement : isAnyImportSyntax;
const existingImportStatements = filter(sourceFile.statements, importKindPredicate);
const { comparer, isSorted } = OrganizeImports.getOrganizeImportsStringComparerWithDetection(existingImportStatements, preferences);
const sortedNewImports = isArray(imports) ? stableSort(imports, (a, b) => OrganizeImports.compareImportsOrRequireStatements(a, b, comparer)) : [imports];
if (!existingImportStatements.length) {
changes.insertNodesAtTopOfFile(sourceFile, sortedNewImports, blankLineBetween);
if (!existingImportStatements?.length) {
if (isFullSourceFile(sourceFile)) {
changes.insertNodesAtTopOfFile(sourceFile, sortedNewImports, blankLineBetween);
}
else {
for (const newImport of sortedNewImports) {
// Insert one at a time to send correct original source file for accurate text reuse
// when some imports are cloned from existing ones in other files.
changes.insertStatementsInNewFile(sourceFile.fileName, [newImport], getOriginalNode(newImport)?.getSourceFile());
}
}
return;
}
else if (existingImportStatements && isSorted) {
Debug.assert(isFullSourceFile(sourceFile));
if (existingImportStatements && isSorted) {
for (const newImport of sortedNewImports) {
const insertionIndex = OrganizeImports.getImportDeclarationInsertionIndex(existingImportStatements, newImport, comparer);
if (insertionIndex === 0) {
@@ -3749,7 +3769,7 @@ export interface PackageJsonImportFilter {
}
/** @internal */
export function createPackageJsonImportFilter(fromFile: SourceFile, preferences: UserPreferences, host: LanguageServiceHost): PackageJsonImportFilter {
export function createPackageJsonImportFilter(fromFile: SourceFile | FutureSourceFile, preferences: UserPreferences, host: LanguageServiceHost): PackageJsonImportFilter {
const packageJsons = (
(host.getPackageJsonsVisibleToFile && host.getPackageJsonsVisibleToFile(fromFile.fileName)) || getPackageJsonsVisibleToFile(fromFile.fileName, host)
).filter(p => p.parseable);
@@ -3848,7 +3868,7 @@ export function createPackageJsonImportFilter(fromFile: SourceFile, preferences:
// from Node core modules or not. We can start by seeing if the user is actually using
// any node core modules, as opposed to simply having @types/node accidentally as a
// dependency of a dependency.
if (isSourceFileJS(fromFile) && JsTyping.nodeCoreModules.has(moduleSpecifier)) {
if (isFullSourceFile(fromFile) && isSourceFileJS(fromFile) && JsTyping.nodeCoreModules.has(moduleSpecifier)) {
if (usesNodeCoreModules === undefined) {
usesNodeCoreModules = consumesNodeCoreModules(fromFile);
}
@@ -4109,7 +4129,7 @@ export function isDeprecatedDeclaration(decl: Declaration) {
}
/** @internal */
export function shouldUseUriStyleNodeCoreModules(file: SourceFile, program: Program): boolean {
export function shouldUseUriStyleNodeCoreModules(file: SourceFile | FutureSourceFile, program: Program): boolean {
const decisionFromFile = firstDefined(file.imports, node => {
if (JsTyping.nodeCoreModules.has(node.text)) {
return startsWith(node.text, "node:");
@@ -4233,11 +4253,13 @@ export function fileShouldUseJavaScriptRequire(file: SourceFile | string, progra
if (!hasJSFileExtension(fileName)) {
return false;
}
const compilerOptions = program.getCompilerOptions();
const compilerOptions = typeof file === "string" ? program.getCompilerOptions() : program.getCompilerOptionsForFile(file);
const moduleKind = getEmitModuleKind(compilerOptions);
const impliedNodeFormat = typeof file === "string"
? getImpliedNodeFormatForFile(toPath(file, host.getCurrentDirectory(), hostGetCanonicalFileName(host)), program.getPackageJsonInfoCache?.(), host, compilerOptions)
: file.impliedNodeFormat;
const sourceFileLike = typeof file === "string" ? {
fileName: file,
impliedNodeFormat: getImpliedNodeFormatForFile(toPath(file, host.getCurrentDirectory(), hostGetCanonicalFileName(host)), program.getPackageJsonInfoCache?.(), host, compilerOptions),
} : file;
const impliedNodeFormat = getImpliedNodeFormatForEmitWorker(sourceFileLike, compilerOptions);
if (impliedNodeFormat === ModuleKind.ESNext) {
return false;
@@ -4281,3 +4303,23 @@ export function isBlockLike(node: Node): node is BlockLike {
return false;
}
}
/** @internal */
export function createFutureSourceFile(fileName: string, syntaxModuleIndicator: ModuleKind.ESNext | ModuleKind.CommonJS | undefined, program: Program, moduleResolutionHost: ModuleResolutionHost): FutureSourceFile {
const result = getImpliedNodeFormatForFileWorker(fileName, program.getPackageJsonInfoCache?.(), moduleResolutionHost, program.getCompilerOptions());
let impliedNodeFormat, packageJsonScope;
if (typeof result === "object") {
impliedNodeFormat = result.impliedNodeFormat;
packageJsonScope = result.packageJsonScope;
}
return {
path: toPath(fileName, program.getCurrentDirectory(), program.getCanonicalFileName),
fileName,
externalModuleIndicator: syntaxModuleIndicator === ModuleKind.ESNext ? true : undefined,
commonJsModuleIndicator: syntaxModuleIndicator === ModuleKind.CommonJS ? true : undefined,
impliedNodeFormat,
packageJsonScope,
statements: emptyArray,
imports: emptyArray,
};
}
+2
View File
@@ -98,6 +98,7 @@ import "./unittests/tsbuild/sample";
import "./unittests/tsbuild/transitiveReferences";
import "./unittests/tsbuildWatch/configFileErrors";
import "./unittests/tsbuildWatch/demo";
import "./unittests/tsbuildWatch/extends";
import "./unittests/tsbuildWatch/libraryResolution";
import "./unittests/tsbuildWatch/moduleResolution";
import "./unittests/tsbuildWatch/noEmit";
@@ -134,6 +135,7 @@ import "./unittests/tscWatch/projectsWithReferences";
import "./unittests/tscWatch/resolutionCache";
import "./unittests/tscWatch/resolveJsonModuleWithIncremental";
import "./unittests/tscWatch/sourceOfProjectReferenceRedirect";
import "./unittests/tscWatch/symlinks";
import "./unittests/tscWatch/watchApi";
import "./unittests/tscWatch/watchEnvironment";
import "./unittests/tsserver/applyChangesToOpenFiles";
+37 -27
View File
@@ -55,34 +55,44 @@ describe("unittests:: canWatch::", () => {
scenario: string,
forPath: "node_modules" | "node_modules/@types" | "",
) {
baselineCanWatch(
scenario,
() => `Determines whether to watch given failed lookup location (file that didnt exist) when resolving module.\r\nIt also determines the directory to watch and whether to watch it recursively or not.`,
(paths, longestPathLength, baseline) => {
const recursive = "Recursive";
const maxLength = longestPathLength + ts.combinePaths(forPath, "dir/subdir/somefile.d.ts").length;
const maxLengths = [maxLength, maxLength, recursive.length] as const;
baselineCanWatchForRoot(paths, baseline, (rootPathCompoments, root) => {
pushHeader(baseline, ["Location", "getDirectoryToWatchFailedLookupLocation", recursive], maxLengths);
paths.forEach(path => {
baselineGetDirectoryToWatchFailedLookupLocation(combinePaths(path, forPath, "somefile.d.ts"), root, rootPathCompoments, maxLengths);
baselineGetDirectoryToWatchFailedLookupLocation(combinePaths(path, forPath, "dir/somefile.d.ts"), root, rootPathCompoments, maxLengths);
baselineGetDirectoryToWatchFailedLookupLocation(combinePaths(path, forPath, "dir/subdir/somefile.d.ts"), root, rootPathCompoments, maxLengths);
["file", "dir", "subDir"].forEach(type => {
baselineCanWatch(
`${scenario}In${type}`,
() => `Determines whether to watch given failed lookup location (file that didnt exist) when resolving module.\r\nIt also determines the directory to watch and whether to watch it recursively or not.`,
(paths, longestPathLength, baseline) => {
const recursive = "Recursive";
const maxLength = longestPathLength + ts.combinePaths(forPath, "dir/subdir/somefile.d.ts").length;
const maxLengths = [maxLength, maxLength, recursive.length, maxLength] as const;
baselineCanWatchForRoot(paths, baseline, (rootPathCompoments, root) => {
pushHeader(baseline, ["Location", "getDirectoryToWatchFailedLookupLocation", recursive, "Location if not symlink"], maxLengths);
paths.forEach(path => {
let subPath;
switch (type) {
case "file":
subPath = "somefile.d.ts";
break;
case "dir":
subPath = "dir/somefile.d.ts";
break;
case "subDir":
subPath = "dir/subdir/somefile.d.ts";
break;
}
const testPath = combinePaths(path, forPath, subPath);
const result = ts.getDirectoryToWatchFailedLookupLocation(
testPath,
testPath,
root,
root,
rootPathCompoments,
ts.returnUndefined,
);
pushRow(baseline, [testPath, result ? result.packageDir ?? result.dir : "", result ? `${!result.nonRecursive}` : "", result?.packageDir ? result.dir : ""], maxLengths);
});
});
});
function baselineGetDirectoryToWatchFailedLookupLocation(path: ts.Path, root: ts.Path, rootPathCompoments: Readonly<ts.PathPathComponents>, maxLengths: readonly number[]) {
const result = ts.getDirectoryToWatchFailedLookupLocation(
path,
path,
root,
root,
rootPathCompoments,
ts.returnUndefined,
);
pushRow(baseline, [path, result ? result.dir : "", result ? `${!result.nonRecursive}` : ""], maxLengths);
}
},
);
},
);
});
}
baselineCanWatch(
@@ -253,6 +253,46 @@ function createFileSystem(ignoreCase: boolean, cwd: string, root: string) {
},
}),
"dev/extendsArrayFails2.json": jsonToReadableText({ extends: [42] }),
"dev/configs/template.json": jsonToReadableText({
include: ["${configDir}/../supplemental.*"], // eslint-disable-line no-template-curly-in-string
files: ["${configDir}/main.ts"], // eslint-disable-line no-template-curly-in-string,
compilerOptions: {
declarationDir: "${configDir}/decls", // eslint-disable-line no-template-curly-in-string
rootDirs: ["root1", "${configDir}/root2", "root3"], // eslint-disable-line no-template-curly-in-string
paths: {
"something": ["${configDir}/something"], // eslint-disable-line no-template-curly-in-string
"something/*": ["${configDir}/something/*"], // eslint-disable-line no-template-curly-in-string
"other/*": ["./other/*"],
},
},
}),
"dev/configs/templateandextends.json": jsonToReadableText({
extends: "./first/templateextends.json",
compilerOptions: {
strict: true,
baseUrl: "./src",
},
}),
"dev/configs/first/templateextends.json": jsonToReadableText({
extends: "../second/templateextends.json",
include: ["${configDir}/../supplemental.*"], // eslint-disable-line no-template-curly-in-string
compilerOptions: {
rootDirs: ["root1", "${configDir}/root2", "root3"], // eslint-disable-line no-template-curly-in-string
},
}),
"dev/configs/second/templateextends.json": jsonToReadableText({
files: ["${configDir}/main.ts"], // eslint-disable-line no-template-curly-in-string,
compilerOptions: {
outDir: "./insecond",
declarationDir: "${configDir}/decls", // eslint-disable-line no-template-curly-in-string
paths: {
"something": ["${configDir}/something"], // eslint-disable-line no-template-curly-in-string
"something/*": ["${configDir}/something/*"], // eslint-disable-line no-template-curly-in-string
"other/*": ["./other/*"],
},
},
}),
},
},
});
@@ -302,6 +342,9 @@ describe("unittests:: config:: configurationExtension", () => {
baselineParsedCommandLine("can report missing configurations", "extendsArrayFails.json");
baselineParsedCommandLine("can error when 'extends' is not a string or Array2", "extendsArrayFails2.json");
baselineParsedCommandLine("handle configDir template", "configs/template.json");
baselineParsedCommandLine("handle configDir template", "configs/templateandextends.json");
baselineParseConfig({
scenario: "configurationExtension",
subScenario: testName,
@@ -317,7 +360,7 @@ describe("unittests:: config:: configurationExtension", () => {
baseline.push("CompilerOptions::");
baseline.push(jsonToReadableText(parsed.options));
baseline.push("FileNames::");
baseline.push(parsed.fileNames.join());
baseline.push(...parsed.fileNames);
},
})),
skipFs: true,
+4 -4
View File
@@ -15,9 +15,9 @@ function getParsedCommandJson(
return ts.parseJsonConfigFileContent(
parsed.config,
host,
basePath ?? host.sys.getCurrentDirectory(),
basePath ?? ts.getNormalizedAbsolutePath(ts.getDirectoryPath(configFileName), host.sys.getCurrentDirectory()),
existingOptions,
configFileName,
ts.getNormalizedAbsolutePath(configFileName, host.sys.getCurrentDirectory()),
/*resolutionStack*/ undefined,
/*extraFileExtensions*/ undefined,
/*extendedConfigCache*/ undefined,
@@ -37,9 +37,9 @@ function getParsedCommandJsonSourceFile(
return ts.parseJsonSourceFileConfigFileContent(
parsed,
host,
basePath ?? host.sys.getCurrentDirectory(),
basePath ?? ts.getNormalizedAbsolutePath(ts.getDirectoryPath(configFileName), host.sys.getCurrentDirectory()),
existingOptions,
configFileName,
ts.getNormalizedAbsolutePath(configFileName, host.sys.getCurrentDirectory()),
/*resolutionStack*/ undefined,
/*extraFileExtensions*/ undefined,
/*extendedConfigCache*/ undefined,
@@ -115,6 +115,21 @@ describe("unittests:: config:: showConfig", () => {
],
});
showTSConfigCorrectly("Show TSConfig with configDir template template", ["-p", "tsconfig.json"], {
compilerOptions: {
outDir: "${configDir}/outDir", // eslint-disable-line no-template-curly-in-string
typeRoots: ["root1", "${configDir}/root2", "root3"], // eslint-disable-line no-template-curly-in-string
paths: {
"@myscope/*": ["${configDir}/types/*"], // eslint-disable-line no-template-curly-in-string
"other/*": ["other/*"],
},
},
include: [
"${configDir}/src/**/*", // eslint-disable-line no-template-curly-in-string
],
files: ["${configDir}/main.ts"], // eslint-disable-line no-template-curly-in-string
});
// Bulk validation of all option declarations
for (const option of ts.optionDeclarations) {
baselineOption(option, /*isCompilerOptions*/ true);
@@ -97,6 +97,18 @@ describe("unittests:: config:: tsconfigParsingWatchOptions:: parseConfigFileText
}),
},
},
{
json: {
extends: "./base/tsconfig.json",
},
additionalFiles: {
"/base/tsconfig.json": jsonToReadableText({
watchOptions: {
excludeFiles: ["${configDir}/temp/*.ts"], // eslint-disable-line no-template-curly-in-string
},
}),
},
},
]);
verifyWatchOptions("different options", () => [
@@ -1,5 +1,6 @@
import { dedent } from "../../_namespaces/Utils";
import { jsonToReadableText } from "../helpers";
import { FsContents } from "./contents";
import {
createServerHost,
createWatchedSystem,
@@ -31,3 +32,70 @@ export function getSymlinkedExtendsSys(forTsserver?: true): TestServerHost {
[libFile.path]: libFile.content,
}, { currentDirectory: "/users/user/projects/myproject" });
}
export function getConfigDirExtendsSys(): FsContents {
return {
"/home/src/projects/configs/first/tsconfig.json": jsonToReadableText({
extends: "../second/tsconfig.json",
include: ["${configDir}/src"], // eslint-disable-line no-template-curly-in-string
compilerOptions: {
typeRoots: ["root1", "${configDir}/root2", "root3"], // eslint-disable-line no-template-curly-in-string
types: [],
},
}),
"/home/src/projects/configs/second/tsconfig.json": jsonToReadableText({
files: ["${configDir}/main.ts"], // eslint-disable-line no-template-curly-in-string,
compilerOptions: {
declarationDir: "${configDir}/decls", // eslint-disable-line no-template-curly-in-string
paths: {
"@myscope/*": ["${configDir}/types/*"], // eslint-disable-line no-template-curly-in-string
"other/*": ["other/*"],
},
baseUrl: "${configDir}", // eslint-disable-line no-template-curly-in-string
},
watchOptions: {
excludeFiles: ["${configDir}/main.ts"], // eslint-disable-line no-template-curly-in-string
},
}),
"/home/src/projects/myproject/tsconfig.json": jsonToReadableText({
extends: "../configs/first/tsconfig.json",
compilerOptions: {
declaration: true,
outDir: "outDir",
traceResolution: true,
},
}),
"/home/src/projects/myproject/main.ts": dedent`
// some comment
export const y = 10;
import { x } from "@myscope/sometype";
`,
"/home/src/projects/myproject/src/secondary.ts": dedent`
// some comment
export const z = 10;
import { k } from "other/sometype2";
`,
"/home/src/projects/myproject/types/sometype.ts": dedent`
export const x = 10;
`,
"/home/src/projects/myproject/root2/other/sometype2/index.d.ts": dedent`
export const k = 10;
`,
[libFile.path]: libFile.content,
};
}
export function modifyFirstExtendedConfigOfConfigDirExtendsSys(sys: TestServerHost) {
sys.modifyFile(
"/home/src/projects/configs/first/tsconfig.json",
jsonToReadableText({
extends: "../second/tsconfig.json",
include: ["${configDir}/src"], // eslint-disable-line no-template-curly-in-string
compilerOptions: {
typeRoots: ["${configDir}/root2"], // eslint-disable-line no-template-curly-in-string
types: [],
},
}),
);
}
@@ -0,0 +1,66 @@
import { dedent } from "../../_namespaces/Utils";
import { jsonToReadableText } from "../helpers";
import { libContent } from "./contents";
import { solutionBuildWithBaseline } from "./solutionBuilder";
import {
createServerHost,
createWatchedSystem,
TestServerHost,
TestServerHostOsFlavor,
} from "./virtualFileSystemWithWatch";
export function getMonorepoSymlinkedSiblingPackagesSys(forTsserver: boolean, built: boolean, osFlavor?: TestServerHostOsFlavor): TestServerHost {
const configText = jsonToReadableText({
compilerOptions: {
target: "es2016",
module: "commonjs",
rootDir: "./src",
declaration: true,
outDir: "./dist",
esModuleInterop: true,
forceConsistentCasingInFileNames: true,
strict: true,
skipLibCheck: true,
traceResolution: true,
},
exclude: [
"tests/**/*",
"dist/**/*",
],
});
const sys = (!forTsserver ? createWatchedSystem : createServerHost)({
"/home/src/projects/project/packages/package1/package.json": getPackageJson("package1"),
"/home/src/projects/project/packages/package1/tsconfig.json": configText,
"/home/src/projects/project/packages/package1/src/index.ts": dedent`
export type FooType = "foo";
export type BarType = "bar";
`,
"/home/src/projects/project/packages/package2/package.json": getPackageJson("package2"),
"/home/src/projects/project/packages/package2/tsconfig.json": configText,
"/home/src/projects/project/packages/package2/src/index.ts": dedent`
import { FooType, BarType } from "package1"
type MyFooType = FooType;
type MyBarType = BarType;
`,
"/home/src/projects/project/node_modules/package1": { symLink: "/home/src/projects/project/packages/package1" },
"/a/lib/lib.es2016.full.d.ts": libContent,
}, { currentDirectory: "/home/src/projects/project", osFlavor });
if (built) buildMonorepoSymlinkedSiblingPackage1(sys);
return sys;
}
function getPackageJson(packageName: string) {
return jsonToReadableText({
name: packageName,
version: "1.0.0",
main: "dist/index.js",
});
}
export function buildMonorepoSymlinkedSiblingPackage1(host: TestServerHost) {
solutionBuildWithBaseline(host, ["packages/package1"]);
}
export function cleanMonorepoSymlinkedSiblingPackage1(host: TestServerHost) {
host.deleteFolder("/home/src/projects/project/packages/package1/dist", /*recursive*/ true);
}
@@ -35,21 +35,27 @@ export function ensureErrorFreeBuild(host: TestServerHost, rootNames: readonly s
}
export function solutionBuildWithBaseline(sys: TestServerHost, solutionRoots: readonly string[], originalRead?: TestServerHost["readFile"]) {
const originalReadFile = sys.readFile;
const originalWrite = sys.write;
const originalWriteFile = sys.writeFile;
ts.Debug.assert(sys.writtenFiles === undefined);
const solutionBuilder = createSolutionBuilder(
changeToHostTrackingWrittenFiles(
fakes.patchHostForBuildInfoReadWrite(sys),
),
solutionRoots,
originalRead,
);
solutionBuilder.build();
sys.readFile = originalReadFile;
sys.write = originalWrite;
sys.writeFile = originalWriteFile;
sys.writtenFiles = undefined;
return sys;
if (sys.writtenFiles === undefined) {
const originalReadFile = sys.readFile;
const originalWrite = sys.write;
const originalWriteFile = sys.writeFile;
const solutionBuilder = createSolutionBuilder(
changeToHostTrackingWrittenFiles(
fakes.patchHostForBuildInfoReadWrite(sys),
),
solutionRoots,
originalRead,
);
solutionBuilder.build();
sys.readFile = originalReadFile;
sys.write = originalWrite;
sys.writeFile = originalWriteFile;
sys.writtenFiles = undefined;
return sys;
}
else {
const solutionBuilder = createSolutionBuilder(sys, solutionRoots);
solutionBuilder.build();
return sys;
}
}
+2 -1
View File
@@ -4,6 +4,7 @@ import {
createLoggerWithInMemoryLogs,
LoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import { patchHostForBuildInfoReadWrite } from "../../_namespaces/fakes";
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
import { ensureErrorFreeBuild } from "./solutionBuilder";
@@ -126,7 +127,7 @@ export class TestSession extends ts.server.Session {
if (typingsInstaller) typingsInstaller.session = this;
this.serverCancellationToken = cancellationToken as TestServerCancellationToken;
patchHostTimeouts(
changeToHostTrackingWrittenFiles(this.host),
changeToHostTrackingWrittenFiles(patchHostForBuildInfoReadWrite(this.host)),
this,
this.logger,
);
@@ -1203,3 +1203,16 @@ export function changeToHostTrackingWrittenFiles(inputHost: TestServerHost) {
};
return host;
}
export function osFlavorToString(osFlavor: TestServerHostOsFlavor) {
switch (osFlavor) {
case TestServerHostOsFlavor.Windows:
return "Windows";
case TestServerHostOsFlavor.MacOs:
return "MacOs";
case TestServerHostOsFlavor.Linux:
return "Linux";
default:
Debug.assertNever(osFlavor);
}
}
@@ -9,6 +9,7 @@ import {
import {
createWatchedSystem,
FileOrFolderOrSymLinkMap,
osFlavorToString,
TestServerHostOsFlavor,
} from "../helpers/virtualFileSystemWithWatch";
describe("unittests:: sys:: symlinkWatching::", () => {
@@ -148,19 +149,6 @@ describe("unittests:: sys:: symlinkWatching::", () => {
(ts.isString(expected.event) ? actual.event === expected.event : ts.contains(expected.event, actual.event));
}
function osFlavorToString(osFlavor: TestServerHostOsFlavor) {
switch (osFlavor) {
case TestServerHostOsFlavor.Windows:
return "Windows";
case TestServerHostOsFlavor.MacOs:
return "MacOs";
case TestServerHostOsFlavor.Linux:
return "Linux";
default:
ts.Debug.assertNever(osFlavor);
}
}
interface FsEventsForWatchDirectory extends Record<string, readonly ExpectedEventAndFileName[] | undefined> {
// The first time events are most of the time are not predictable, so just create random file for that reason
init?: readonly ExpectedEventAndFileName[];
+13 -1
View File
@@ -1,5 +1,10 @@
import { getSymlinkedExtendsSys } from "../helpers/extends";
import {
getConfigDirExtendsSys,
getSymlinkedExtendsSys,
} from "../helpers/extends";
import { verifyTsc } from "../helpers/tsc";
import { verifyTscWatch } from "../helpers/tscWatch";
import { loadProjectFromFiles } from "../helpers/vfs";
describe("unittests:: tsbuild:: extends::", () => {
verifyTscWatch({
@@ -8,4 +13,11 @@ describe("unittests:: tsbuild:: extends::", () => {
sys: getSymlinkedExtendsSys,
commandLineArgs: ["-b", "src", "--extendedDiagnostics"],
});
verifyTsc({
scenario: "extends",
subScenario: "configDir template",
fs: () => loadProjectFromFiles(getConfigDirExtendsSys(), { cwd: "/home/src/projects/myproject" }),
commandLineArgs: ["-b", "/home/src/projects/myproject", "--explainFiles", "--v"],
});
});
@@ -0,0 +1,20 @@
import {
getConfigDirExtendsSys,
modifyFirstExtendedConfigOfConfigDirExtendsSys,
} from "../helpers/extends";
import { verifyTscWatch } from "../helpers/tscWatch";
import { createWatchedSystem } from "../helpers/virtualFileSystemWithWatch";
describe("unittests:: tsbuildWatch:: watchMode:: extends::", () => {
verifyTscWatch({
scenario: "extends",
subScenario: "configDir template",
sys: () => createWatchedSystem(getConfigDirExtendsSys(), { currentDirectory: "/home/src/projects/myproject" }),
commandLineArgs: ["-b", "-w", "--extendedDiagnostics", "--explainFiles", "-v"],
edits: [{
caption: "edit extended config file",
edit: modifyFirstExtendedConfigOfConfigDirExtendsSys,
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
}],
});
});
+27 -1
View File
@@ -1,5 +1,10 @@
import { getSymlinkedExtendsSys } from "../helpers/extends";
import {
getConfigDirExtendsSys,
getSymlinkedExtendsSys,
} from "../helpers/extends";
import { verifyTsc } from "../helpers/tsc";
import { verifyTscWatch } from "../helpers/tscWatch";
import { loadProjectFromFiles } from "../helpers/vfs";
describe("unittests:: tsc:: extends::", () => {
verifyTscWatch({
@@ -8,4 +13,25 @@ describe("unittests:: tsc:: extends::", () => {
sys: getSymlinkedExtendsSys,
commandLineArgs: ["-p", "src", "--extendedDiagnostics"],
});
verifyTsc({
scenario: "extends",
subScenario: "configDir template",
fs: () => loadProjectFromFiles(getConfigDirExtendsSys(), { cwd: "/home/src/projects/myproject" }),
commandLineArgs: ["-p", "/home/src/projects/myproject", "--explainFiles"],
});
verifyTsc({
scenario: "extends",
subScenario: "configDir template showConfig",
fs: () => loadProjectFromFiles(getConfigDirExtendsSys(), { cwd: "/home/src/projects/myproject" }),
commandLineArgs: ["-p", "/home/src/projects/myproject", "--showConfig"],
});
verifyTsc({
scenario: "extends",
subScenario: "configDir template with commandline",
fs: () => loadProjectFromFiles(getConfigDirExtendsSys(), { cwd: "/home/src/projects/myproject" }),
commandLineArgs: ["-p", "/home/src/projects/myproject", "--explainFiles", "--outDir", "${configDir}/outDir"], // eslint-disable-line no-template-curly-in-string
});
});
@@ -44,6 +44,51 @@ describe("unittests:: tsc:: projectReferences::", () => {
commandLineArgs: ["--p", "src/project"],
});
verifyTsc({
scenario: "projectReferences",
subScenario: "default import interop uses referenced project settings",
fs: () =>
loadProjectFromFiles({
"/node_modules/ambiguous-package/package.json": `{ "name": "ambiguous-package" }`,
"/node_modules/ambiguous-package/index.d.ts": "export declare const ambiguous: number;",
"/node_modules/esm-package/package.json": `{ "name": "esm-package", "type": "module" }`,
"/node_modules/esm-package/index.d.ts": "export declare const esm: number;",
"/lib/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
declaration: true,
rootDir: "src",
outDir: "dist",
module: "esnext",
moduleResolution: "bundler",
},
include: ["src"],
}),
"/lib/src/a.ts": "export const a = 0;",
"/lib/dist/a.d.ts": "export declare const a = 0;",
"/app/tsconfig.json": jsonToReadableText({
compilerOptions: {
module: "esnext",
moduleResolution: "bundler",
rootDir: "src",
outDir: "dist",
},
include: ["src"],
references: [
{ path: "../lib" },
],
}),
"/app/src/local.ts": "export const local = 0;",
"/app/src/index.ts": `
import local from "./local"; // Error
import esm from "esm-package"; // Error
import referencedSource from "../../lib/src/a"; // Error
import referencedDeclaration from "../../lib/dist/a"; // Error
import ambiguous from "ambiguous-package"; // Ok`,
}),
commandLineArgs: ["--p", "app", "--pretty", "false"],
});
verifyTsc({
scenario: "projectReferences",
subScenario: "referencing ambient const enum from referenced project with preserveConstEnums",
+18 -1
View File
@@ -1,5 +1,10 @@
import { getSymlinkedExtendsSys } from "../helpers/extends";
import {
getConfigDirExtendsSys,
getSymlinkedExtendsSys,
modifyFirstExtendedConfigOfConfigDirExtendsSys,
} from "../helpers/extends";
import { verifyTscWatch } from "../helpers/tscWatch";
import { createWatchedSystem } from "../helpers/virtualFileSystemWithWatch";
describe("unittests:: tsc-watch:: extends::", () => {
verifyTscWatch({
@@ -8,4 +13,16 @@ describe("unittests:: tsc-watch:: extends::", () => {
sys: getSymlinkedExtendsSys,
commandLineArgs: ["-w", "-p", "src", "--extendedDiagnostics"],
});
verifyTscWatch({
scenario: "extends",
subScenario: "configDir template",
sys: () => createWatchedSystem(getConfigDirExtendsSys(), { currentDirectory: "/home/src/projects/myproject" }),
commandLineArgs: ["-w", "--extendedDiagnostics", "--explainFiles"],
edits: [{
caption: "edit extended config file",
edit: modifyFirstExtendedConfigOfConfigDirExtendsSys,
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
}],
});
});
@@ -178,19 +178,19 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
version: system.createHash(libFile.content),
signature: system.createHash(libFile.content),
affectsGlobalScope: true,
impliedFormat: undefined,
impliedFormat: ts.ModuleKind.CommonJS,
});
assert.deepEqual(state.fileInfos.get(file1.path as ts.Path), {
version: system.createHash(file1.content),
signature: system.createHash(file1.content),
affectsGlobalScope: undefined,
impliedFormat: undefined,
impliedFormat: ts.ModuleKind.CommonJS,
});
assert.deepEqual(state.fileInfos.get(file2.path as ts.Path), {
version: system.createHash(fileModified.content),
signature: system.createHash(fileModified.content),
affectsGlobalScope: undefined,
impliedFormat: undefined,
impliedFormat: ts.ModuleKind.CommonJS,
});
assert.deepEqual(state.compilerOptions, {
@@ -0,0 +1,65 @@
import {
buildMonorepoSymlinkedSiblingPackage1,
cleanMonorepoSymlinkedSiblingPackage1,
getMonorepoSymlinkedSiblingPackagesSys,
} from "../helpers/monorepoSymlinkedSiblingPackages";
import {
noopChange,
verifyTscWatch,
} from "../helpers/tscWatch";
import {
osFlavorToString,
TestServerHostOsFlavor,
} from "../helpers/virtualFileSystemWithWatch";
describe("unittests:: tsc-watch:: symlinks::", () => {
describe("monorepoSymlinkedSiblingPackages:: monorepo style sibling packages symlinked", () => {
verify(/*built*/ false);
verify(/*built*/ true);
verify(/*built*/ false, TestServerHostOsFlavor.Linux);
verify(/*built*/ true, TestServerHostOsFlavor.Linux);
function verify(built: boolean, osFlavor?: TestServerHostOsFlavor) {
verifyTscWatch({
scenario: "symlinks",
subScenario: `monorepo style sibling packages symlinked${built ? " package1 built" : ""}${osFlavor ? ` ${osFlavorToString(osFlavor)}` : ""}`,
commandLineArgs: ["--w", "-p", "packages/package2", "--extendedDiagnostics"],
sys: () => getMonorepoSymlinkedSiblingPackagesSys(/*forTsserver*/ false, built, osFlavor),
edits: [
built ? noopChange : {
caption: "Build package1",
edit: buildMonorepoSymlinkedSiblingPackage1,
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "Clean package1 build",
edit: cleanMonorepoSymlinkedSiblingPackage1,
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
},
},
osFlavor === TestServerHostOsFlavor.Linux ? {
caption: "After updating childs",
edit: noopChange.edit,
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
} : noopChange,
{
caption: "Build package1",
edit: buildMonorepoSymlinkedSiblingPackage1,
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
],
});
}
});
});
@@ -46,7 +46,7 @@ describe("unittests:: tsc-watch:: watchAPI:: tsc-watch with custom module resolu
system: sys,
cb,
});
const parsedCommandResult = ts.parseJsonConfigFileContent(configFileJson, sys, config.path);
const parsedCommandResult = ts.parseJsonConfigFileContent(configFileJson, sys, ts.getDirectoryPath(config.path));
host.resolveModuleNames = (moduleNames, containingFile) =>
moduleNames.map(m => {
const result = ts.resolveModuleName(m, containingFile, parsedCommandResult.options, host);
@@ -188,50 +188,74 @@ describe("unittests:: tsc-watch:: watchEnvironment:: tsc-watch with different po
verifyRenamingFileInSubFolder("uses non recursive dynamic polling when renaming file in subfolder", Tsc_WatchDirectory.DynamicPolling);
verifyTscWatch({
scenario,
subScenario: "watchDirectories/when there are symlinks to folders in recursive folders",
commandLineArgs: ["--w"],
sys: () => {
const cwd = "/home/user/projects/myproject";
const file1: File = {
path: `${cwd}/src/file.ts`,
content: `import * as a from "a"`,
};
const tsconfig: File = {
path: `${cwd}/tsconfig.json`,
content: `{ "compilerOptions": { "extendedDiagnostics": true, "traceResolution": true }}`,
};
const realA: File = {
path: `${cwd}/node_modules/reala/index.d.ts`,
content: `export {}`,
};
const realB: File = {
path: `${cwd}/node_modules/realb/index.d.ts`,
content: `export {}`,
};
const symLinkA: SymLink = {
path: `${cwd}/node_modules/a`,
symLink: `${cwd}/node_modules/reala`,
};
const symLinkB: SymLink = {
path: `${cwd}/node_modules/b`,
symLink: `${cwd}/node_modules/realb`,
};
const symLinkBInA: SymLink = {
path: `${cwd}/node_modules/reala/node_modules/b`,
symLink: `${cwd}/node_modules/b`,
};
const symLinkAInB: SymLink = {
path: `${cwd}/node_modules/realb/node_modules/a`,
symLink: `${cwd}/node_modules/a`,
};
const files = [libFile, file1, tsconfig, realA, realB, symLinkA, symLinkB, symLinkBInA, symLinkAInB];
const environmentVariables = new Map<string, string>();
environmentVariables.set("TSC_WATCHDIRECTORY", Tsc_WatchDirectory.NonRecursiveWatchDirectory);
return createWatchedSystem(files, { osFlavor: TestServerHostOsFlavor.Linux, environmentVariables, currentDirectory: cwd });
},
});
function verifySymlinks(synchronousWatchDirectory: boolean) {
verifyTscWatch({
scenario,
subScenario: `watchDirectories/when there are symlinks to folders in recursive folders${synchronousWatchDirectory ? " with synchronousWatchDirectory" : ""}`,
commandLineArgs: ["--w"],
sys: () => {
const cwd = "/home/user/projects/myproject";
const file1: File = {
path: `${cwd}/src/file.ts`,
content: `import * as a from "a"`,
};
const tsconfig: File = {
path: `${cwd}/tsconfig.json`,
content: jsonToReadableText({
compilerOptions: { extendedDiagnostics: true, traceResolution: true },
watchOptions: synchronousWatchDirectory ? { synchronousWatchDirectory } : undefined,
}),
};
const realA: File = {
path: `${cwd}/node_modules/reala/index.d.ts`,
content: `export {}`,
};
const realB: File = {
path: `${cwd}/node_modules/realb/index.d.ts`,
content: `export {}`,
};
const symLinkA: SymLink = {
path: `${cwd}/node_modules/a`,
symLink: `${cwd}/node_modules/reala`,
};
const symLinkB: SymLink = {
path: `${cwd}/node_modules/b`,
symLink: `${cwd}/node_modules/realb`,
};
const symLinkBInA: SymLink = {
path: `${cwd}/node_modules/reala/node_modules/b`,
symLink: `${cwd}/node_modules/b`,
};
const symLinkAInB: SymLink = {
path: `${cwd}/node_modules/realb/node_modules/a`,
symLink: `${cwd}/node_modules/a`,
};
const files = [libFile, file1, tsconfig, realA, realB, symLinkA, symLinkB, symLinkBInA, symLinkAInB];
const environmentVariables = new Map<string, string>();
environmentVariables.set("TSC_WATCHDIRECTORY", Tsc_WatchDirectory.NonRecursiveWatchDirectory);
return createWatchedSystem(files, { osFlavor: TestServerHostOsFlavor.Linux, environmentVariables, currentDirectory: cwd });
},
edits: [
{
caption: "delete reala/index.d.ts",
edit: sys => sys.deleteFile("/home/user/projects/myproject/node_modules/reala/index.d.ts"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
caption: "timeouts if any",
edit: ts.noop,
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
caption: "timeouts if any",
edit: ts.noop,
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
],
});
}
verifySymlinks(/*synchronousWatchDirectory*/ true);
verifySymlinks(/*synchronousWatchDirectory*/ false);
verifyTscWatch({
scenario,
@@ -1,5 +1,6 @@
import * as ts from "../../_namespaces/ts";
import { jsonToReadableText } from "../helpers";
import { compilerOptionsToConfigJson } from "../helpers/contents";
import { ensureErrorFreeBuild } from "../helpers/solutionBuilder";
import {
commonFile1,
@@ -23,13 +24,10 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
it("create configured project without file list", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `
{
"compilerOptions": {},
"exclude": [
"e"
]
}`,
content: jsonToReadableText({
compilerOptions: {},
exclude: ["e"],
}),
};
const file1: File = {
path: "/a/b/c/f1.ts",
@@ -53,11 +51,10 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
it("create configured project with the file list", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `
{
"compilerOptions": {},
"include": ["*.ts"]
}`,
content: jsonToReadableText({
compilerOptions: {},
include: ["*.ts"],
}),
};
const file1: File = {
path: "/a/b/f1.ts",
@@ -81,9 +78,7 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
it("add and then remove a config file in a folder with loose files", () => {
const configFile: File = {
path: `/user/username/projects/myproject/tsconfig.json`,
content: `{
"files": ["commonFile1.ts"]
}`,
content: jsonToReadableText({ files: ["commonFile1.ts"] }),
};
const commonFile1: File = {
path: `/user/username/projects/myproject/commonFile1.ts`,
@@ -97,6 +92,7 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
const host = createServerHost([libFile, commonFile1, commonFile2]);
const session = new TestSession(host);
// 1: when both files are open
openFilesForSession([commonFile1, commonFile2], session);
// Add a tsconfig file
@@ -107,9 +103,139 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
host.deleteFile(configFile.path);
host.runQueuedTimeoutCallbacks(); // Refresh inferred projects
// Add a tsconfig file
host.writeFile(configFile.path, configFile.content);
host.runQueuedTimeoutCallbacks(); // load configured project from disk + ensureProjectsForOpenFiles
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
// Check status when all files are closed
closeFilesForSession([commonFile1, commonFile2, "/random/random.ts"], session);
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
// 2: when file is opened while config file is deleted
closeFilesForSession(["/random/random.ts"], session);
openFilesForSession([commonFile1], session);
// remove the tsconfig file
host.deleteFile(configFile.path);
openFilesForSession([commonFile2], session);
// Add a tsconfig file
host.writeFile(configFile.path, configFile.content);
host.runQueuedTimeoutCallbacks(); // load configured project from disk + ensureProjectsForOpenFiles
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
// Check status when all files are closed
closeFilesForSession([commonFile1, commonFile2, "/random/random.ts"], session);
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
baselineTsserverLogs("configuredProjects", "add and then remove a config file in a folder with loose files", session);
});
it("add and then remove a config file when parent folder has config file", () => {
const configFile: File = {
path: `/user/username/projects/myproject/folder/tsconfig.json`,
content: jsonToReadableText({ files: ["commonFile1.ts"] }),
};
const parentConfigFile: File = {
path: `/user/username/projects/myproject/tsconfig.json`,
content: jsonToReadableText({ files: ["folder/commonFile2.ts"] }),
};
const commonFile1: File = {
path: `/user/username/projects/myproject/folder/commonFile1.ts`,
content: "let x = 1",
};
const commonFile2: File = {
path: `/user/username/projects/myproject/folder/commonFile2.ts`,
content: "let y = 1",
};
const host = createServerHost([libFile, commonFile1, commonFile2, configFile, parentConfigFile]);
const session = new TestSession(host);
// 1: When config file is deleted and then another file is opened
openFilesForSession([commonFile1], session);
// remove the tsconfig file
host.deleteFile(configFile.path);
openFilesForSession([commonFile2], session);
// Add a tsconfig file
host.writeFile(configFile.path, configFile.content);
host.runQueuedTimeoutCallbacks();
// Check the state after files collected
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
// Check status when all files are closed
closeFilesForSession([commonFile1, commonFile2, "/random/random.ts"], session);
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
// 2: When both files are open and config file is deleted
closeFilesForSession(["/random/random.ts"], session);
openFilesForSession([commonFile1, commonFile2], session);
// remove the tsconfig file
host.deleteFile(configFile.path);
host.runQueuedTimeoutCallbacks();
// Add a tsconfig file
host.writeFile(configFile.path, configFile.content);
host.runQueuedTimeoutCallbacks(); // load configured project from disk + ensureProjectsForOpenFiles
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
closeFilesForSession([commonFile1, commonFile2, "/random/random.ts"], session);
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
// 3: Check when both files are closed when config file is deleted
closeFilesForSession(["/random/random.ts"], session);
openFilesForSession([commonFile1], session);
// remove the tsconfig file
host.deleteFile(configFile.path);
openFilesForSession([commonFile2], session);
// State after open files are closed
closeFilesForSession([commonFile1, commonFile2], session);
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
// 4: Check when both files are closed one by one when file is deleted
host.writeFile(configFile.path, configFile.content);
closeFilesForSession(["/random/random.ts"], session);
openFilesForSession([commonFile1], session);
// remove the tsconfig file
host.deleteFile(configFile.path);
openFilesForSession([commonFile2], session);
// State after open files are closed
closeFilesForSession([commonFile1], session);
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
closeFilesForSession([commonFile2, "random/random.ts"], session);
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
// 5: Check when both files are closed one by one when file is deleted order changed
host.writeFile(configFile.path, configFile.content);
closeFilesForSession(["/random/random.ts"], session);
openFilesForSession([commonFile1], session);
// remove the tsconfig file
host.deleteFile(configFile.path);
openFilesForSession([commonFile2], session);
// State after open files are closed
closeFilesForSession([commonFile2], session);
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
closeFilesForSession([commonFile1, "random/random.ts"], session);
openFilesForSession([{ file: "/random/random.ts", content: "export const y = 10;" }], session);
baselineTsserverLogs("configuredProjects", "add and then remove a config file when parent folder has config file", session);
});
it("add new files to a configured project without file list", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
@@ -128,13 +254,13 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
it("should ignore non-existing files specified in the config file", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {},
"files": [
"commonFile1.ts",
"commonFile3.ts"
]
}`,
content: jsonToReadableText({
compilerOptions: {},
files: [
"commonFile1.ts",
"commonFile3.ts",
],
}),
};
const host = createServerHost([commonFile1, commonFile2, configFile]);
const session = new TestSession(host);
@@ -164,10 +290,10 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
it("files explicitly excluded in config file", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {},
"exclude": ["/a/c"]
}`,
content: jsonToReadableText({
compilerOptions: {},
exclude: ["/a/c"],
}),
};
const excludedFile1: File = {
path: "/a/c/excluedFile1.ts",
@@ -200,12 +326,10 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
"moduleResolution": "node"
},
"files": ["${file1.path}"]
}`,
content: jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson({ moduleResolution: ts.ModuleResolutionKind.Node10 }),
files: [file1.path],
}),
};
const files = [file1, nodeModuleFile, classicModuleFile, configFile, randomFile];
const host = createServerHost(files);
@@ -214,12 +338,10 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
host.writeFile(
configFile.path,
`{
"compilerOptions": {
"moduleResolution": "classic"
},
"files": ["${file1.path}"]
}`,
jsonToReadableText({
compilerOptions: compilerOptionsToConfigJson({ moduleResolution: ts.ModuleResolutionKind.Classic }),
files: [file1.path],
}),
);
host.runQueuedTimeoutCallbacks();
@@ -240,12 +362,12 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
"target": "es6"
},
"files": [ "main.ts" ]
}`,
content: jsonToReadableText({
compilerOptions: {
target: "es6",
},
files: ["main.ts"],
}),
};
const host = createServerHost([file1, file2, configFile]);
const session = new TestSession(host);
@@ -258,13 +380,13 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
it("should tolerate config file errors and still try to build a project", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
"target": "es6",
"allowAnything": true
},
"someOtherProperty": {}
}`,
content: jsonToReadableText({
compilerOptions: {
target: "es6",
allowAnything: true,
},
someOtherProperty: {},
}),
};
const host = createServerHost([commonFile1, commonFile2, libFile, configFile]);
const session = new TestSession(host);
@@ -283,12 +405,12 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
"target": "es6"
},
"files": [ "main.ts", "main2.ts" ]
}`,
content: jsonToReadableText({
compilerOptions: {
target: "es6",
},
files: ["main.ts", "main2.ts"],
}),
};
const host = createServerHost([file1, file2, configFile, libFile]);
const session = new TestSession({ host, useSingleInferredProject: true });
@@ -305,12 +427,12 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
"target": "es6"
},
"files": [ "main.ts" ]
}`,
content: jsonToReadableText({
compilerOptions: {
target: "es6",
},
files: ["main.ts"],
}),
};
const host = createServerHost([file1, configFile, libFile]);
const session = new TestSession({ host, useSingleInferredProject: true });
+24 -1
View File
@@ -1,9 +1,15 @@
import { getSymlinkedExtendsSys } from "../helpers/extends";
import * as ts from "../../_namespaces/ts";
import {
getConfigDirExtendsSys,
getSymlinkedExtendsSys,
modifyFirstExtendedConfigOfConfigDirExtendsSys,
} from "../helpers/extends";
import {
baselineTsserverLogs,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import { createServerHost } from "../helpers/virtualFileSystemWithWatch";
describe("unittests:: tsserver:: extends::", () => {
it("resolves the symlink path", () => {
@@ -12,4 +18,21 @@ describe("unittests:: tsserver:: extends::", () => {
openFilesForSession(["/users/user/projects/myproject/src/index.ts"], session);
baselineTsserverLogs("tsserver", "resolves the symlink path", session);
});
it("configDir template", () => {
const host = createServerHost(getConfigDirExtendsSys(), { currentDirectory: "/home/src/projects/myproject" });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: {
watchOptions: {
excludeDirectories: ["${configDir}/node_modules"], // eslint-disable-line no-template-curly-in-string
},
},
});
openFilesForSession(["/home/src/projects/myproject/src/secondary.ts"], session);
modifyFirstExtendedConfigOfConfigDirExtendsSys(host);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("tsserver", "configDir template", session);
});
});
@@ -1,5 +1,8 @@
import * as ts from "../../_namespaces/ts";
import { defer } from "../../_namespaces/Utils";
import {
defer,
Deferred,
} from "../../_namespaces/Utils";
import {
baselineTsserverLogs,
closeFilesForSession,
@@ -22,9 +25,11 @@ describe("unittests:: tsserver:: pluginsAsync:: async loaded plugins", () => {
const { host, session } = setup(["plugin-a"]);
let pluginModuleInstantiated = false;
let pluginInvoked = false;
host.importPlugin = async (_root: string, _moduleName: string): Promise<ts.server.ModuleImportResult> => {
host.importPlugin = async (_root: string, moduleName: string): Promise<ts.server.ModuleImportResult> => {
session.logger.log(`request import ${moduleName}`);
await Promise.resolve(); // simulate at least a single turn delay
pluginModuleInstantiated = true;
session.logger.log(`fulfill import ${moduleName}`);
return {
module: (() => {
pluginInvoked = true;
@@ -47,6 +52,7 @@ describe("unittests:: tsserver:: pluginsAsync:: async loaded plugins", () => {
session.logger.log(`pluginModuleInstantiated:: ${pluginModuleInstantiated}`);
await projectService.waitForPendingPlugins();
session.host.baselineHost("after waitForPendingPlugins");
session.logger.log(`at this point all plugin modules should have been instantiated and all plugins should have been invoked`);
session.logger.log(`pluginModuleInstantiated:: ${pluginModuleInstantiated}`);
@@ -85,14 +91,17 @@ describe("unittests:: tsserver:: pluginsAsync:: async loaded plugins", () => {
// wait for load to complete
await projectService.waitForPendingPlugins();
session.host.baselineHost("after waitForPendingPlugins ");
baselineTsserverLogs("pluginsAsync", "plugins evaluation in correct order even if imports resolve out of order", session);
});
it("sends projectsUpdatedInBackground event", async () => {
const { host, session } = setup(["plugin-a"]);
host.importPlugin = async (_root: string, _moduleName: string): Promise<ts.server.ModuleImportResult> => {
host.importPlugin = async (_root: string, moduleName: string): Promise<ts.server.ModuleImportResult> => {
session.logger.log(`request import ${moduleName}`);
await Promise.resolve(); // simulate at least a single turn delay
session.logger.log(`fulfill import ${moduleName}`);
return {
module: (() => ({ create: info => info.languageService })) as ts.server.PluginModuleFactory,
error: undefined,
@@ -103,6 +112,7 @@ describe("unittests:: tsserver:: pluginsAsync:: async loaded plugins", () => {
const projectService = session.getProjectService();
await projectService.waitForPendingPlugins();
session.host.baselineHost("after waitForPendingPlugins");
baselineTsserverLogs("pluginsAsync", "sends projectsUpdatedInBackground event", session);
});
@@ -110,10 +120,11 @@ describe("unittests:: tsserver:: pluginsAsync:: async loaded plugins", () => {
it("adds external files", async () => {
const { host, session } = setup(["plugin-a"]);
const pluginAShouldLoad = defer();
host.importPlugin = async (_root: string, _moduleName: string): Promise<ts.server.ModuleImportResult> => {
host.importPlugin = async (_root: string, moduleName: string): Promise<ts.server.ModuleImportResult> => {
session.logger.log(`request import ${moduleName}`);
// wait until the initial external files are requested from the project service.
await pluginAShouldLoad.promise;
session.logger.log(`fulfill import ${moduleName}`);
return {
module: (() => ({
create: info => info.languageService,
@@ -145,12 +156,16 @@ describe("unittests:: tsserver:: pluginsAsync:: async loaded plugins", () => {
const { host, session } = setup(["plugin-a"]);
const pluginALoaded = defer();
const projectClosed = defer();
host.importPlugin = async (_root: string, _moduleName: string): Promise<ts.server.ModuleImportResult> => {
host.importPlugin = async (_root: string, moduleName: string): Promise<ts.server.ModuleImportResult> => {
session.logger.log(`request import ${moduleName}`);
// mark that the plugin has started loading
pluginALoaded.resolve();
// wait until after a project close has been requested to continue
session.logger.log(`Awaiting project close`);
await projectClosed.promise;
session.logger.log(`fulfill import ${moduleName}`);
return {
module: (() => ({ create: info => info.languageService })) as ts.server.PluginModuleFactory,
error: undefined,
@@ -165,13 +180,75 @@ describe("unittests:: tsserver:: pluginsAsync:: async loaded plugins", () => {
// close the project
closeFilesForSession(["^memfs:/foo.ts"], session);
openFilesForSession([{ file: "/random/foo2.ts", content: "" }], session);
// continue loading the plugin
projectClosed.resolve();
await projectService.waitForPendingPlugins();
session.host.baselineHost("before waitForPendingPlugins");
await projectService.waitForPendingPlugins(); // For closed foo.ts
session.host.baselineHost("after waitForPendingPlugins for closed foo.ts");
await projectService.waitForPendingPlugins(); // For random file
session.host.baselineHost("after waitForPendingPlugins for random file");
// the project was closed before plugins were ready. no project update should have been requested
baselineTsserverLogs("pluginsAsync", "project is closed before plugins are loaded", session);
});
it("project is deferred closed before plugins are loaded", async () => {
const config = "/home/src/projects/project/tsconfig.json";
const file = "/home/src/projects/project/a.ts";
const host = createServerHost({
[config]: `{}`,
[file]: "export const a = 10;",
[libFile.path]: libFile.content,
});
const session = new TestSession({ host, globalPlugins: ["plugin-a"] });
const pluginALoaded = defer();
let configFileDeleted: Deferred<void> | undefined = defer();
host.importPlugin = async (_root: string, moduleName: string): Promise<ts.server.ModuleImportResult> => {
session.logger.log(`request import ${moduleName}`);
// mark that the plugin has started loading
pluginALoaded.resolve();
// wait until after a project close has been requested to continue
if (configFileDeleted) {
session.logger.log(`awaiting config file delete`);
await configFileDeleted.promise;
}
session.logger.log(`fulfill import ${moduleName}`);
return {
module: (() => ({ create: info => info.languageService })) as ts.server.PluginModuleFactory,
error: undefined,
};
};
openFilesForSession([file], session);
const projectService = session.getProjectService();
// wait for the plugin to start loading
await pluginALoaded.promise;
session.host.baselineHost("before deleteFile");
// close the project
host.deleteFile(config);
session.host.baselineHost("after deleteFile");
// continue loading the plugin
configFileDeleted.resolve();
session.host.baselineHost("before waitForPendingPlugins");
await projectService.waitForPendingPlugins();
session.host.baselineHost("after waitForPendingPlugins");
configFileDeleted = undefined;
host.writeFile(config, "{}");
host.runQueuedTimeoutCallbacks();
session.host.baselineHost("before enableRequestedPlugins");
projectService.enableRequestedPlugins();
session.host.baselineHost("before waitForPendingPlugins");
await projectService.waitForPendingPlugins();
session.host.baselineHost("after waitForPendingPlugins");
// the project was closed before plugins were ready. no project update should have been requested
baselineTsserverLogs("pluginsAsync", "project is deferred closed before plugins are loaded", session);
});
});
@@ -17,6 +17,7 @@ import {
File,
Folder,
libFile,
TestServerHostOsFlavor,
} from "../helpers/virtualFileSystemWithWatch";
describe("unittests:: tsserver:: projectErrors::", () => {
@@ -808,3 +809,27 @@ describe("unittests:: tsserver:: projectErrors:: with npm install when", () => {
verifyNpmInstall(/*timeoutDuringPartialInstallation*/ false);
});
});
describe("unittests:: tsserver:: projectErrors:: with file rename on wsl2::", () => {
it("rename a file", () => {
const host = createServerHost({
"/home/username/project/src/a.ts": `export const a = 10;`,
"/home/username/project/src/b.ts": `export const b = 10;`,
"/home/username/project/tsconfig.json": jsonToReadableText({
compilerOptions: {
strictNullChecks: true,
},
include: ["src/**/*.ts"],
}),
[libFile.path]: libFile.content,
}, { osFlavor: TestServerHostOsFlavor.Linux });
const session = new TestSession(host);
openFilesForSession([{ file: "/home/username/project/src/a.ts", projectRootPath: "/home/username/project" }], session);
host.renameFile("/home/username/project/src/b.ts", "/home/username/project/src/c.ts");
openFilesForSession([{ file: "/home/username/project/src/c.ts", content: `export const b = 10;`, projectRootPath: "/home/username/project" }], session);
host.runQueuedTimeoutCallbacks(); // Updates the project that c.ts now exists on the disk and schedules one more update
host.runQueuedTimeoutCallbacks();
closeFilesForSession(["/home/username/project/src/c.ts"], session);
baselineTsserverLogs("projectErrors", "file rename on wsl2", session);
});
});
@@ -1073,11 +1073,12 @@ export function bar() {}`,
}
function verifySolutionScenario(input: Setup) {
const { session } = setup(input);
const { session, host } = setup(input);
const info = session.getProjectService().getScriptInfoForPath(main.path as ts.Path)!;
const defaultProject = info.getDefaultProject();
session.logger.startGroup();
session.logger.info(`getDefaultProject for ${main.path}: ${info.getDefaultProject().projectName}`);
session.logger.info(`getDefaultProject for ${main.path}: ${defaultProject.projectName}`);
session.logger.info(`findDefaultConfiguredProject for ${main.path}: ${session.getProjectService().findDefaultConfiguredProject(info)!.projectName}`);
session.logger.endGroup();
@@ -1093,6 +1094,24 @@ export function bar() {}`,
closeFilesForSession([dummyFilePath], session);
openFilesForSession([dummyFilePath], session);
// Verify that tsconfig can be deleted and watched
if (ts.server.isConfiguredProject(defaultProject)) {
closeFilesForSession([dummyFilePath], session);
const config = defaultProject.projectName;
const content = host.readFile(config)!;
host.deleteFile(config);
host.runQueuedTimeoutCallbacks();
host.writeFile(config, content);
host.runQueuedTimeoutCallbacks();
host.deleteFile(config);
openFilesForSession([dummyFilePath], session);
host.writeFile(config, content);
host.runQueuedTimeoutCallbacks();
}
// Verify Reload projects
session.executeCommandSeq<ts.server.protocol.ReloadProjectsRequest>({
command: ts.server.protocol.CommandTypes.ReloadProjects,
@@ -15,7 +15,7 @@ import {
TestServerHost,
} from "../helpers/virtualFileSystemWithWatch";
describe("unittests:: tsserver:: with project references and tsbuild source map", () => {
describe("unittests:: tsserver:: projectReferencesSourcemap:: with project references and tsbuild source map", () => {
const dependecyLocation = `/user/username/projects/myproject/dependency`;
const dependecyDeclsLocation = `/user/username/projects/myproject/decls`;
const mainLocation = `/user/username/projects/myproject/main`;
@@ -71,10 +71,10 @@ fn5();
const files = [dependencyTs, dependencyConfig, mainTs, mainConfig, libFile, randomFile, randomConfig];
function changeDtsFile(session: TestSession) {
function changeDtsFile(session: TestSession, content?: string) {
session.host.writeFile(
dtsLocation,
session.host.readFile(dtsLocation)!.replace(
(content ?? session.host.readFile(dtsLocation)!).replace(
"//# sourceMappingURL=FnS.d.ts.map",
`export declare function fn6(): void;
//# sourceMappingURL=FnS.d.ts.map`,
@@ -147,14 +147,18 @@ fn5();
function createSessionWithoutProjectReferences(onHostCreate?: OnHostCreate) {
const host = createHostWithSolutionBuild(files, [mainConfig.path]);
// Erase project reference
writeConfigWithoutProjectReferences(host);
onHostCreate?.(host);
return new TestSession(host);
}
function writeConfigWithoutProjectReferences(host: TestServerHost) {
host.writeFile(
mainConfig.path,
jsonToReadableText({
compilerOptions: { composite: true, declarationMap: true },
}),
);
onHostCreate?.(host);
return new TestSession(host);
}
function createSessionWithProjectReferences(onHostCreate?: OnHostCreate) {
@@ -166,6 +170,12 @@ fn5();
function createSessionWithDisabledProjectReferences(onHostCreate?: OnHostCreate) {
const host = createHostWithSolutionBuild(files, [mainConfig.path]);
// Erase project reference
WithDisabledProjectReferences(host);
onHostCreate?.(host);
return new TestSession(host);
}
function WithDisabledProjectReferences(host: TestServerHost) {
host.writeFile(
mainConfig.path,
jsonToReadableText({
@@ -177,8 +187,6 @@ fn5();
references: [{ path: "../dependency" }],
}),
);
onHostCreate?.(host);
return new TestSession(host);
}
function makeChangeToMainTs(session: TestSession) {
@@ -209,12 +217,28 @@ fn5();
});
}
function setup(type: SessionType, openFiles: readonly File[], action: Action | Action[], max?: number, onHostCreate?: OnHostCreate) {
const session = type === SessionType.NoReference ? createSessionWithoutProjectReferences(onHostCreate) :
function verifyForAllSessionTypes<T>(worker: (type: SessionType, options: T) => void, options: T) {
describe("when main tsconfig doesnt have project reference", () => {
worker(SessionType.NoReference, options);
});
describe("when main tsconfig has project reference", () => {
worker(SessionType.ProjectReference, options);
});
describe("when main tsconfig has disableSourceOfProjectReferenceRedirect along with project reference", () => {
worker(SessionType.DisableSourceOfProjectReferenceRedirect, options);
});
}
function createSession(type: SessionType, onHostCreate?: OnHostCreate) {
return type === SessionType.NoReference ? createSessionWithoutProjectReferences(onHostCreate) :
type === SessionType.ProjectReference ? createSessionWithProjectReferences(onHostCreate) :
type === SessionType.DisableSourceOfProjectReferenceRedirect ?
createSessionWithDisabledProjectReferences(onHostCreate) :
ts.Debug.assertNever(type);
}
function setup(type: SessionType, openFiles: readonly File[], action: Action | Action[], max?: number, onHostCreate?: OnHostCreate) {
const session = createSession(type, onHostCreate);
openFilesForSession(openFiles, session);
runActions(session, action, max);
return session;
@@ -275,10 +299,60 @@ fn5();
openFiles: readonly File[];
action: Action | Action[];
}
function verifyFileRenames(options: VerifyFileRenamesOptions) {
function verifyFileChangeAndRenames(options: VerifyFileRenamesOptions) {
function file(options: VerifyFileRenamesOptions) {
return options.file === "dts" ? dtsLocation : dtsMapLocation;
}
enum ChangeAsRenameType {
NoTimeout = "no timeout",
TimeoutAfterDelete = "timeout after delete",
TimeoutAfterWrite = "timeout after write",
ActionBeforeWrite = "action before write",
}
function change(options: VerifyFileRenamesOptions) {
return options.file === "dts" ? changeDtsFile : changeDtsMapFile;
}
function verifyChangeAsRename(withChange: boolean) {
[
ChangeAsRenameType.NoTimeout,
ChangeAsRenameType.TimeoutAfterDelete,
ChangeAsRenameType.TimeoutAfterWrite,
ChangeAsRenameType.ActionBeforeWrite,
].forEach(changeAsRenameType => {
it(`with ${options.file} file, change as rename ${changeAsRenameType}`, () => {
const session = setup(options.type, options.openFiles, options.action, 1);
const location = file(options);
const fileContents = session.host.readFile(location)!;
session.host.deleteFile(location);
switch (changeAsRenameType) {
case ChangeAsRenameType.TimeoutAfterDelete:
session.host.runQueuedTimeoutCallbacks();
break;
case ChangeAsRenameType.ActionBeforeWrite:
runActions(session, options.action, 2);
break;
default:
session.host.baselineHost(`Before write ${location}`);
}
if (withChange) change(options)(session, fileContents);
else session.host.writeFile(location, fileContents);
if (changeAsRenameType === ChangeAsRenameType.TimeoutAfterWrite) session.host.runQueuedTimeoutCallbacks();
runActions(session, options.action, 2);
baselineTsserverLogs("projectReferencesSourcemap", `${options.scenarioLocation}/${options.type}/dependency ${options.file} ${withChange ? "change" : "rewrite"} as rename ${changeAsRenameType}`, session);
});
});
}
// Edit to add new fn
verifyScenarioWithChanges({
scenarioLocation: options.scenarioLocation,
type: options.type,
scenario: `dependency ${options.file} changes`,
openFiles: options.openFiles,
change: change(options),
action: options.action,
});
verifyChangeAsRename(/*withChange*/ true);
verifyChangeAsRename(/*withChange*/ false);
it(`with ${options.file} file, when file is not present`, () => {
const session = setup(options.type, options.openFiles, options.action, undefined, host => host.deleteFile(file(options)));
verifyScriptInfoCollectionWith(session, options.openFiles);
@@ -314,93 +388,63 @@ fn5();
referenceChange: (session: TestSession) => void;
referenceChangeAction?: Action | Action[];
}
function verifyScenarioWorker(options: VerifyScenario, type: SessionType) {
verifyAction({
scenarioLocation: options.scenarioLocation,
type,
scenario: options.scenario,
openFiles: options.openFiles,
action: options.action,
});
// Edit
verifyScenarioWithChanges({
scenarioLocation: options.scenarioLocation,
type,
scenario: "usage file changes",
openFiles: options.openFiles,
change: options.change,
action: options.action,
});
// Edit dts to add new fn
verifyScenarioWithChanges({
scenarioLocation: options.scenarioLocation,
type,
scenario: "dependency dts changes",
openFiles: options.openFiles,
change: changeDtsFile,
action: options.action,
});
// Edit map file to represent added new line
verifyScenarioWithChanges({
scenarioLocation: options.scenarioLocation,
type,
scenario: "dependency dtsMap changes",
openFiles: options.openFiles,
change: changeDtsMapFile,
action: options.action,
});
verifyFileRenames({
scenarioLocation: options.scenarioLocation,
type,
file: "dtsMap",
openFiles: options.openFiles,
action: options.action,
});
verifyFileRenames({
scenarioLocation: options.scenarioLocation,
type,
file: "dts",
openFiles: options.openFiles,
action: options.action,
});
if (type !== SessionType.ProjectReference) return;
verifyScenarioWithChanges({
scenarioLocation: options.scenarioLocation,
type,
scenario: "dependency source changes",
openFiles: options.openFiles,
change: options.referenceChange,
action: options.action,
actionAfterChange: options.referenceChangeAction,
});
it("when projects are not built", () => {
const host = createServerHost(files);
const session = new TestSession(host);
openFilesForSession(options.openFiles, session);
runActions(session, options.action);
verifyScriptInfoCollectionWith(session, options.openFiles);
baselineTsserverLogs("projectReferencesSourcemap", `${options.scenarioLocation}/${type}/when projects are not built`, session);
});
}
function verifyScenario(options: VerifyScenario) {
describe("when main tsconfig doesnt have project reference", () => {
verifyScenarioWorker(options, SessionType.NoReference);
});
describe("when main tsconfig has project reference", () => {
verifyScenarioWorker(options, SessionType.ProjectReference);
});
describe("when main tsconfig has disableSourceOfProjectReferenceRedirect along with project reference", () => {
verifyScenarioWorker(options, SessionType.DisableSourceOfProjectReferenceRedirect);
});
verifyForAllSessionTypes((type, options) => {
verifyAction({
scenarioLocation: options.scenarioLocation,
type,
scenario: options.scenario,
openFiles: options.openFiles,
action: options.action,
});
// Edit
verifyScenarioWithChanges({
scenarioLocation: options.scenarioLocation,
type,
scenario: "usage file changes",
openFiles: options.openFiles,
change: options.change,
action: options.action,
});
verifyFileChangeAndRenames({
scenarioLocation: options.scenarioLocation,
type,
file: "dtsMap",
openFiles: options.openFiles,
action: options.action,
});
verifyFileChangeAndRenames({
scenarioLocation: options.scenarioLocation,
type,
file: "dts",
openFiles: options.openFiles,
action: options.action,
});
if (type !== SessionType.ProjectReference) return;
verifyScenarioWithChanges({
scenarioLocation: options.scenarioLocation,
type,
scenario: "dependency source changes",
openFiles: options.openFiles,
change: options.referenceChange,
action: options.action,
actionAfterChange: options.referenceChangeAction,
});
it("when projects are not built", () => {
const host = createServerHost(files);
const session = new TestSession(host);
openFilesForSession(options.openFiles, session);
runActions(session, options.action);
verifyScriptInfoCollectionWith(session, options.openFiles);
baselineTsserverLogs("projectReferencesSourcemap", `${options.scenarioLocation}/${type}/when projects are not built`, session);
});
}, options);
}
describe("from project that uses dependency: goToDef", () => {
@@ -465,5 +509,46 @@ fn5();
}),
referenceChangeAction: [goToDefFromMainTs, renameFromDependencyTsWithDependencyChange],
});
verifyForAllSessionTypes(type => {
it("goto Definition in usage and rename locations, deleting config file", () => {
const session = createSession(type);
openFilesForSession([mainTs], session);
session.executeCommandSeq<ts.server.protocol.RenameRequest>({
command: ts.server.protocol.CommandTypes.Rename,
arguments: { file: mainTs.path, line: 2, offset: 17 },
});
verifyMainConfigDelete(mainConfig, /*runTimeoutAfterDelete*/ true, /*openRandomAfterDelete*/ false);
verifyMainConfigDelete(mainConfig, /*runTimeoutAfterDelete*/ true, /*openRandomAfterDelete*/ true);
verifyMainConfigDelete(mainConfig, /*runTimeoutAfterDelete*/ false, /*openRandomAfterDelete*/ false);
verifyMainConfigDelete(mainConfig, /*runTimeoutAfterDelete*/ false, /*openRandomAfterDelete*/ true);
verifyMainConfigDelete(dependencyConfig, /*runTimeoutAfterDelete*/ true, /*openRandomAfterDelete*/ false);
verifyMainConfigDelete(dependencyConfig, /*runTimeoutAfterDelete*/ false, /*openRandomAfterDelete*/ true);
verifyMainConfigDelete(dependencyConfig, /*runTimeoutAfterDelete*/ true, /*openRandomAfterDelete*/ true);
verifyMainConfigDelete(dependencyConfig, /*runTimeoutAfterDelete*/ false, /*openRandomAfterDelete*/ false);
baselineTsserverLogs("projectReferencesSourcemap", `dependencyAndUsage/${type}/goToDef and rename locations and deleting config file`, session);
function verifyMainConfigDelete(
config: File,
runTimeoutAfterDelete: boolean,
openRandomAfterDelete: boolean,
) {
const configContent = session.host.readFile(config.path)!;
session.host.deleteFile(config.path);
if (runTimeoutAfterDelete) session.host.runQueuedTimeoutCallbacks();
if (openRandomAfterDelete) {
openFilesForSession([randomFile], session);
closeFilesForSession([randomFile], session);
}
session.host.writeFile(config.path, configContent);
session.host.runQueuedTimeoutCallbacks();
openFilesForSession([randomFile], session);
closeFilesForSession([randomFile], session);
}
});
}, /*options*/ undefined);
});
});
+46 -1
View File
@@ -1,6 +1,11 @@
import * as ts from "../../_namespaces/ts";
import { dedent } from "../../_namespaces/Utils";
import { jsonToReadableText } from "../helpers";
import {
buildMonorepoSymlinkedSiblingPackage1,
cleanMonorepoSymlinkedSiblingPackage1,
getMonorepoSymlinkedSiblingPackagesSys,
} from "../helpers/monorepoSymlinkedSiblingPackages";
import {
baselineTsserverLogs,
closeFilesForSession,
@@ -13,11 +18,13 @@ import {
createServerHost,
File,
libFile,
osFlavorToString,
SymLink,
TestServerHost,
TestServerHostOsFlavor,
} from "../helpers/virtualFileSystemWithWatch";
describe("unittests:: tsserver:: symLinks", () => {
describe("unittests:: tsserver:: symLinks::", () => {
it("rename in common file renames all project", () => {
const folderA = `/users/username/projects/a`;
const aFile: File = {
@@ -264,4 +271,42 @@ new C();`,
openFilesForSession(["c:/temp/replay/axios-src/lib/core/settle.js"], session);
baselineTsserverLogs("symLinks", "when not symlink but differs in casing", session);
});
describe("monorepoSymlinkedSiblingPackages:: monorepo style sibling packages symlinked", () => {
verify(/*built*/ false);
verify(/*built*/ true);
verify(/*built*/ false, TestServerHostOsFlavor.Linux);
verify(/*built*/ true, TestServerHostOsFlavor.Linux);
function verify(built: boolean, osFlavor?: TestServerHostOsFlavor) {
it(`monorepo style sibling packages symlinked${built ? " package1 built" : ""}${osFlavor ? ` ${osFlavorToString(osFlavor)}` : ""}`, () => {
const indexFile = "/home/src/projects/project/packages/package2/src/index.ts";
const host = getMonorepoSymlinkedSiblingPackagesSys(/*forTsserver*/ true, built, osFlavor);
const session = new TestSession(host);
openFilesForSession([indexFile], session);
verifyGetErrRequest({ session, files: [indexFile] });
if (!built) {
buildMonorepoSymlinkedSiblingPackage1(host);
host.runQueuedTimeoutCallbacks();
host.runQueuedTimeoutCallbacks();
host.runQueuedTimeoutCallbacks();
verifyGetErrRequest({ session, files: [indexFile] });
}
cleanMonorepoSymlinkedSiblingPackage1(host);
host.runQueuedTimeoutCallbacks();
host.runQueuedTimeoutCallbacks();
host.runQueuedTimeoutCallbacks();
verifyGetErrRequest({ session, files: [indexFile] });
buildMonorepoSymlinkedSiblingPackage1(host);
host.runQueuedTimeoutCallbacks();
host.runQueuedTimeoutCallbacks();
host.runQueuedTimeoutCallbacks();
verifyGetErrRequest({ session, files: [indexFile] });
baselineTsserverLogs("symLinks", `monorepo style sibling packages symlinked${built ? " package1 built" : ""}${osFlavor ? ` ${osFlavorToString(osFlavor)}` : ""}`, session);
});
}
});
});
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -35,7 +35,7 @@ class Board {
>this.ships.every(function (val) { return val.isSunk; }) : boolean
> : ^^^^^^^
>this.ships.every : { <S extends Ship>(predicate: (value: Ship, index: number, array: Ship[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: Ship, index: number, array: Ship[]) => unknown, thisArg?: any): boolean; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^
>this.ships : Ship[]
> : ^^^^^^
>this : this
@@ -43,9 +43,9 @@ class Board {
>ships : Ship[]
> : ^^^^^^
>every : { <S extends Ship>(predicate: (value: Ship, index: number, array: Ship[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: Ship, index: number, array: Ship[]) => unknown, thisArg?: any): boolean; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^
>function (val) { return val.isSunk; } : (val: Ship) => boolean
> : ^^^^^^^^^^^^^^^^^^^^^^
> : ^ ^^^^^^^^^^^^^^^^^^
>val : Ship
> : ^^^^
>val.isSunk : boolean
@@ -3,9 +3,9 @@
=== ArrowFunction1.ts ===
var v = (a: ) => {
>v : (a: any) => void
> : ^^^^^^^^^^^^^^^^
> : ^ ^^^^^^^^^^^^^^
>(a: ) => { } : (a: any) => void
> : ^^^^^^^^^^^^^^^^
> : ^ ^^^^^^^^^^^^^^
>a : any
> : ^^^
@@ -3,9 +3,9 @@
=== ArrowFunction4.ts ===
var v = (a, b) => {
>v : (a: any, b: any) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^ ^^^^^^^ ^^^^^^^^^^^^^^
>(a, b) => { } : (a: any, b: any) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^ ^^^^^^^ ^^^^^^^^^^^^^^
>a : any
>b : any
@@ -3,9 +3,9 @@
=== ArrowFunctionExpression1.ts ===
var v = (public x: string) => { };
>v : (x: string) => void
> : ^^^^ ^^^^^^^^^
> : ^ ^^ ^^^^^^^^^
>(public x: string) => { } : (x: string) => void
> : ^^^^ ^^^^^^^^^
> : ^ ^^ ^^^^^^^^^
>x : string
> : ^^^^^^
@@ -22,7 +22,7 @@ module clodule1 {
function f(x: T) { }
>f : (x: T) => void
> : ^^^^^^^^^^^^^^
> : ^ ^^^^^^^^^^^^
>x : T
> : ^
}
@@ -15,7 +15,7 @@ class clodule<T> {
static fn<U>(id: U) { }
>fn : <U>(id: U) => void
> : ^ ^^^^^^ ^^^^^^^^^
> : ^ ^^ ^^ ^^^^^^^^^
>id : U
> : ^
}
@@ -27,7 +27,7 @@ module clodule {
// error: duplicate identifier expected
export function fn<T>(x: T, y: T): T {
>fn : <T>(x: T, y: T) => T
> : ^ ^^^^^ ^^^^^ ^^^^^
> : ^ ^^ ^^ ^^ ^^ ^^^^^
>x : T
> : ^
>y : T
@@ -15,7 +15,7 @@ class clodule<T> {
static fn(id: string) { }
>fn : (id: string) => void
> : ^^^^^ ^^^^^^^^^
> : ^ ^^ ^^^^^^^^^
>id : string
> : ^^^^^^
}
@@ -27,7 +27,7 @@ module clodule {
// error: duplicate identifier expected
export function fn<T>(x: T, y: T): T {
>fn : <T>(x: T, y: T) => T
> : ^ ^^^^^ ^^^^^ ^^^^^
> : ^ ^^ ^^ ^^ ^^ ^^^^^
>x : T
> : ^
>y : T
@@ -15,7 +15,7 @@ class clodule<T> {
private static sfn(id: string) { return 42; }
>sfn : (id: string) => number
> : ^^^^^ ^^^^^^^^^^^
> : ^ ^^ ^^^^^^^^^^^
>id : string
> : ^^^^^^
>42 : 42
@@ -29,7 +29,7 @@ module clodule {
// error: duplicate identifier expected
export function fn<T>(x: T, y: T): number {
>fn : <T>(x: T, y: T) => number
> : ^ ^^^^^ ^^^^^ ^^^^^
> : ^ ^^ ^^ ^^ ^^ ^^^^^
>x : T
> : ^
>y : T
@@ -39,11 +39,11 @@ module clodule {
>clodule.sfn('a') : number
> : ^^^^^^
>clodule.sfn : (id: string) => number
> : ^^^^^^^^^^^^^^^^^^^^^^
> : ^ ^^ ^^^^^^^^^^^
>clodule : typeof clodule
> : ^^^^^^^^^^^^^^
>sfn : (id: string) => number
> : ^^^^^^^^^^^^^^^^^^^^^^
> : ^ ^^ ^^^^^^^^^^^
>'a' : "a"
> : ^^^
}
+2 -2
View File
@@ -17,11 +17,11 @@ for (var v of ['a', 'b', 'c']) {
>console.log(v) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>v : string
> : ^^^^^^
}
+2 -2
View File
@@ -23,11 +23,11 @@ for (var x of [1, 2, 3]) {
>console.log(x) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>x : number
> : ^^^^^^
}
+2 -2
View File
@@ -23,11 +23,11 @@ for (var x of [1, 2, 3]) {
>console.log(x) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>x : number
> : ^^^^^^
}
+2 -2
View File
@@ -17,11 +17,11 @@ for (var v of ['a', 'b', 'c']) {
>console.log(v) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>v : string
> : ^^^^^^
}
+4 -4
View File
@@ -54,11 +54,11 @@ for (const i of [0, 1, 2, 3, 4]) {
>console.log(i) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>i : number
> : ^^^^^^
@@ -69,11 +69,11 @@ for (const i of [0, 1, 2, 3, 4]) {
>console.log('E %s %s', i, err) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^
> : ^^^^ ^^ ^^^^^^^^^
>'E %s %s' : "E %s %s"
> : ^^^^^^^^^
>i : number

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