mirror of https://github.com/vuejs/core.git
Merge tag 'v3.5.0-beta.1'
This commit is contained in:
commit
4468a2bea8
|
@ -44,7 +44,7 @@ This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
|
||||||
|
|
||||||
### Full Message Format
|
### Full Message Format
|
||||||
|
|
||||||
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
|
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
|
||||||
|
|
||||||
```
|
```
|
||||||
<type>(<scope>): <subject>
|
<type>(<scope>): <subject>
|
||||||
|
@ -74,9 +74,9 @@ The scope could be anything specifying the place of the commit change. For examp
|
||||||
|
|
||||||
The subject contains a succinct description of the change:
|
The subject contains a succinct description of the change:
|
||||||
|
|
||||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
- use the imperative, present tense: "change" not "changed" nor "changes"
|
||||||
* don't capitalize the first letter
|
- don't capitalize the first letter
|
||||||
* no dot (.) at the end
|
- no dot (.) at the end
|
||||||
|
|
||||||
### Body
|
### Body
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before
|
||||||
|
|
||||||
Another aspect of it is that large scale stylistic changes result in massive diffs that touch multiple files, adding noise to the git history and makes tracing behavior changes across commits more cumbersome.
|
Another aspect of it is that large scale stylistic changes result in massive diffs that touch multiple files, adding noise to the git history and makes tracing behavior changes across commits more cumbersome.
|
||||||
|
|
||||||
|
|
||||||
### Pull Request Checklist
|
### Pull Request Checklist
|
||||||
|
|
||||||
- Vue core has two primary work branches: `main` and `minor`.
|
- Vue core has two primary work branches: `main` and `minor`.
|
||||||
|
|
|
@ -80,6 +80,7 @@ Depending on the type of the PR, different considerations need to be taken into
|
||||||
- Make sure it doesn't accidentally cause dev-only or compiler-only code branches to be included in the runtime build. Notable case is that some functions in @vue/shared are compiler-only and should not be used in runtime code, e.g. `isHTMLTag` and `isSVGTag`.
|
- Make sure it doesn't accidentally cause dev-only or compiler-only code branches to be included in the runtime build. Notable case is that some functions in @vue/shared are compiler-only and should not be used in runtime code, e.g. `isHTMLTag` and `isSVGTag`.
|
||||||
|
|
||||||
- Performance
|
- Performance
|
||||||
|
|
||||||
- Be careful about code changes in "hot paths", in particular the Virtual DOM renderer (`runtime-core/src/renderer.ts`) and component instantiation code.
|
- Be careful about code changes in "hot paths", in particular the Virtual DOM renderer (`runtime-core/src/renderer.ts`) and component instantiation code.
|
||||||
|
|
||||||
- Potential Breakage
|
- Potential Breakage
|
||||||
|
|
|
@ -7,108 +7,7 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read # to fetch code (actions/checkout)
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
unit-test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
if: ${{ ! startsWith(github.event.head_commit.message, 'release:') && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
|
uses: ./.github/workflows/test.yml
|
||||||
env:
|
|
||||||
PUPPETEER_SKIP_DOWNLOAD: 'true'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4.0.0
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.node-version'
|
|
||||||
cache: 'pnpm'
|
|
||||||
|
|
||||||
- run: pnpm install
|
|
||||||
|
|
||||||
- name: Run unit tests
|
|
||||||
run: pnpm run test-unit
|
|
||||||
|
|
||||||
lint-and-test-dts:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
|
|
||||||
env:
|
|
||||||
PUPPETEER_SKIP_DOWNLOAD: 'true'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4.0.0
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.node-version'
|
|
||||||
cache: 'pnpm'
|
|
||||||
|
|
||||||
- run: pnpm install
|
|
||||||
|
|
||||||
- name: Run eslint
|
|
||||||
run: pnpm run lint
|
|
||||||
|
|
||||||
- name: Run prettier
|
|
||||||
run: pnpm run format-check
|
|
||||||
|
|
||||||
- name: Run type declaration tests
|
|
||||||
run: pnpm run test-dts
|
|
||||||
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [unit-test, lint-and-test-dts]
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
id-token: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v2
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.node-version'
|
|
||||||
registry-url: 'https://registry.npmjs.org'
|
|
||||||
cache: 'pnpm'
|
|
||||||
|
|
||||||
- run: pnpm install
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: pnpm build --withTypes
|
|
||||||
|
|
||||||
- name: Publish
|
|
||||||
run: pnpm dlx pkg-pr-new@0.0 publish './packages/*' --template './playground' --pnpm
|
|
||||||
|
|
||||||
# benchmarks:
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
# if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
|
|
||||||
# env:
|
|
||||||
# PUPPETEER_SKIP_DOWNLOAD: 'true'
|
|
||||||
# steps:
|
|
||||||
# - uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# - name: Install pnpm
|
|
||||||
# uses: pnpm/action-setup@v3.0.0
|
|
||||||
|
|
||||||
# - name: Install Node.js
|
|
||||||
# uses: actions/setup-node@v4
|
|
||||||
# with:
|
|
||||||
# node-version-file: '.node-version'
|
|
||||||
# cache: 'pnpm'
|
|
||||||
|
|
||||||
# - run: pnpm install
|
|
||||||
|
|
||||||
# - name: Run benchmarks
|
|
||||||
# uses: CodSpeedHQ/action@v2
|
|
||||||
# with:
|
|
||||||
# run: pnpm vitest bench --run
|
|
||||||
# token: ${{ secrets.CODSPEED_TOKEN }}
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
uses: ./.github/workflows/test.yml
|
||||||
|
|
||||||
|
release:
|
||||||
|
# prevents this action from running on forks
|
||||||
|
if: github.repository == 'vuejs/core'
|
||||||
|
needs: [test]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
id-token: write
|
||||||
|
# Use Release environment for deployment protection
|
||||||
|
environment: Release
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.node-version'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Build and publish
|
||||||
|
id: publish
|
||||||
|
run: |
|
||||||
|
pnpm release --publishOnly
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create GitHub release
|
||||||
|
id: release_tag
|
||||||
|
uses: yyx990803/release-tag@master
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref }}
|
||||||
|
body: |
|
||||||
|
For stable releases, please refer to [CHANGELOG.md](https://github.com/vuejs/core/blob/main/CHANGELOG.md) for details.
|
||||||
|
For pre-releases, please refer to [CHANGELOG.md](https://github.com/vuejs/core/blob/minor/CHANGELOG.md) of the `minor` branch.
|
|
@ -4,6 +4,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- minor
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
@ -53,12 +54,16 @@ jobs:
|
||||||
name: size-data
|
name: size-data
|
||||||
path: temp/size
|
path: temp/size
|
||||||
|
|
||||||
- name: Save PR number
|
- name: Save PR number & base branch
|
||||||
if: ${{github.event_name == 'pull_request'}}
|
if: ${{github.event_name == 'pull_request'}}
|
||||||
run: echo ${{ github.event.number }} > ./pr.txt
|
run: |
|
||||||
|
echo ${{ github.event.number }} > ./number.txt
|
||||||
|
echo ${{ github.base_ref }} > ./base.txt
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{github.event_name == 'pull_request'}}
|
if: ${{github.event_name == 'pull_request'}}
|
||||||
with:
|
with:
|
||||||
name: pr-number
|
name: pr-info
|
||||||
path: pr.txt
|
path: |
|
||||||
|
number.txt
|
||||||
|
base.txt
|
||||||
|
|
|
@ -35,18 +35,24 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Download PR number
|
- name: Download PR info
|
||||||
uses: dawidd6/action-download-artifact@v6
|
uses: dawidd6/action-download-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: pr-number
|
name: pr-info
|
||||||
run_id: ${{ github.event.workflow_run.id }}
|
run_id: ${{ github.event.workflow_run.id }}
|
||||||
path: /tmp/pr-number
|
path: /tmp/pr-info
|
||||||
|
|
||||||
- name: Read PR Number
|
- name: Read PR Number
|
||||||
id: pr-number
|
id: pr-number
|
||||||
uses: juliangruber/read-file-action@v1
|
uses: juliangruber/read-file-action@v1
|
||||||
with:
|
with:
|
||||||
path: /tmp/pr-number/pr.txt
|
path: /tmp/pr-info/number.txt
|
||||||
|
|
||||||
|
- name: Read PR base branch
|
||||||
|
id: pr-base
|
||||||
|
uses: juliangruber/read-file-action@v1
|
||||||
|
with:
|
||||||
|
path: /tmp/pr-info/base.txt
|
||||||
|
|
||||||
- name: Download Size Data
|
- name: Download Size Data
|
||||||
uses: dawidd6/action-download-artifact@v6
|
uses: dawidd6/action-download-artifact@v6
|
||||||
|
@ -58,7 +64,7 @@ jobs:
|
||||||
- name: Download Previous Size Data
|
- name: Download Previous Size Data
|
||||||
uses: dawidd6/action-download-artifact@v6
|
uses: dawidd6/action-download-artifact@v6
|
||||||
with:
|
with:
|
||||||
branch: main
|
branch: ${{ steps.pr-base.outputs.content }}
|
||||||
workflow: size-data.yml
|
workflow: size-data.yml
|
||||||
event: push
|
event: push
|
||||||
name: size-data
|
name: size-data
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
name: 'test'
|
||||||
|
|
||||||
|
on: workflow_call
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
unit-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
PUPPETEER_SKIP_DOWNLOAD: 'true'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4.0.0
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.node-version'
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- run: pnpm install
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: pnpm run test-unit
|
||||||
|
|
||||||
|
lint-and-test-dts:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
PUPPETEER_SKIP_DOWNLOAD: 'true'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4.0.0
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.node-version'
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- run: pnpm install
|
||||||
|
|
||||||
|
- name: Run eslint
|
||||||
|
run: pnpm run lint
|
||||||
|
|
||||||
|
- name: Run prettier
|
||||||
|
run: pnpm run format-check
|
||||||
|
|
||||||
|
- name: Run type declaration tests
|
||||||
|
run: pnpm run test-dts
|
||||||
|
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [unit-test, lint-and-test-dts]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4.0.0
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.node-version'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- run: pnpm install
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm build --withTypes
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
run: pnpm dlx pkg-pr-new@0.0 publish './packages/*' --template './playground' --pnpm
|
|
@ -1,4 +1,3 @@
|
||||||
dist
|
dist
|
||||||
*.md
|
|
||||||
*.html
|
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
|
CHANGELOG*.md
|
||||||
|
|
1019
CHANGELOG.md
1019
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
|
@ -10,6 +10,6 @@ Please note that we do not consider XSS via template expressions a valid attack
|
||||||
|
|
||||||
We would like to thank the following security researchers for responsibly disclosing security issues to us.
|
We would like to thank the following security researchers for responsibly disclosing security issues to us.
|
||||||
|
|
||||||
- Jeet Pal - [@jeetpal2007](https://github.com/jeetpal2007) | [Email](jeetpal2007@gmail.com) | [LinkedIn](https://in.linkedin.com/in/jeet-pal-22601a290 )
|
- Jeet Pal - [@jeetpal2007](https://github.com/jeetpal2007) | [Email](jeetpal2007@gmail.com) | [LinkedIn](https://in.linkedin.com/in/jeet-pal-22601a290)
|
||||||
- Mix - [@mnixry](https://github.com/mnixry)
|
- Mix - [@mnixry](https://github.com/mnixry)
|
||||||
- Aviv Keller - [@RedYetiDev](https://github.com/redyetidev) | [LinkedIn](https://www.linkedin.com/in/redyetidev) <redyetidev@gmail.com>
|
- Aviv Keller - [@RedYetiDev](https://github.com/redyetidev) | [LinkedIn](https://www.linkedin.com/in/redyetidev) <redyetidev@gmail.com>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "3.5.0-alpha.5",
|
"version": "3.5.0-beta.1",
|
||||||
"packageManager": "pnpm@9.6.0",
|
"packageManager": "pnpm@9.6.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -66,10 +66,11 @@
|
||||||
"@rollup/plugin-json": "^6.1.0",
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@rollup/plugin-replace": "5.0.4",
|
"@rollup/plugin-replace": "5.0.4",
|
||||||
"@swc/core": "^1.7.3",
|
"@swc/core": "^1.7.6",
|
||||||
"@types/hash-sum": "^1.0.2",
|
"@types/hash-sum": "^1.0.2",
|
||||||
"@types/node": "^20.14.13",
|
"@types/node": "^20.14.14",
|
||||||
"@types/semver": "^7.5.8",
|
"@types/semver": "^7.5.8",
|
||||||
|
"@types/serve-handler": "^6.1.4",
|
||||||
"@vitest/coverage-istanbul": "^1.6.0",
|
"@vitest/coverage-istanbul": "^1.6.0",
|
||||||
"@vitest/ui": "^1.6.0",
|
"@vitest/ui": "^1.6.0",
|
||||||
"@vue/consolidate": "1.0.0",
|
"@vue/consolidate": "1.0.0",
|
||||||
|
@ -82,30 +83,31 @@
|
||||||
"eslint-plugin-vitest": "^0.5.4",
|
"eslint-plugin-vitest": "^0.5.4",
|
||||||
"estree-walker": "catalog:",
|
"estree-walker": "catalog:",
|
||||||
"jsdom": "^24.1.1",
|
"jsdom": "^24.1.1",
|
||||||
"lint-staged": "^15.2.7",
|
"lint-staged": "^15.2.8",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"magic-string": "^0.30.10",
|
"magic-string": "^0.30.11",
|
||||||
"markdown-table": "^3.0.3",
|
"markdown-table": "^3.0.3",
|
||||||
"marked": "^12.0.2",
|
"marked": "^13.0.3",
|
||||||
"npm-run-all2": "^6.2.2",
|
"npm-run-all2": "^6.2.2",
|
||||||
"picocolors": "^1.0.1",
|
"picocolors": "^1.0.1",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3",
|
||||||
"puppeteer": "~22.14.0",
|
"puppeteer": "~22.15.0",
|
||||||
"rimraf": "^5.0.9",
|
"rimraf": "^6.0.1",
|
||||||
"rollup": "^4.19.1",
|
"rollup": "^4.20.0",
|
||||||
"rollup-plugin-dts": "^6.1.1",
|
"rollup-plugin-dts": "^6.1.1",
|
||||||
"rollup-plugin-esbuild": "^6.1.1",
|
"rollup-plugin-esbuild": "^6.1.1",
|
||||||
"rollup-plugin-polyfill-node": "^0.13.0",
|
"rollup-plugin-polyfill-node": "^0.13.0",
|
||||||
"semver": "^7.6.3",
|
"semver": "^7.6.3",
|
||||||
"serve": "^14.2.3",
|
"serve": "^14.2.3",
|
||||||
|
"serve-handler": "^6.1.5",
|
||||||
"simple-git-hooks": "^2.11.1",
|
"simple-git-hooks": "^2.11.1",
|
||||||
"todomvc-app-css": "^2.4.3",
|
"todomvc-app-css": "^2.4.3",
|
||||||
"tslib": "^2.6.3",
|
"tslib": "^2.6.3",
|
||||||
"tsx": "^4.16.2",
|
"tsx": "^4.16.5",
|
||||||
"typescript": "~5.4.5",
|
"typescript": "~5.5.4",
|
||||||
"typescript-eslint": "^7.17.0",
|
"typescript-eslint": "^8.0.0",
|
||||||
"vite": "catalog:",
|
"vite": "catalog:",
|
||||||
"vitest": "^1.6.0"
|
"vitest": "^1.6.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,44 +14,92 @@ return function render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: expression transform > bindingMetadata > should not prefix temp variable of for loop 1`] = `
|
exports[`compiler: expression transform > should allow leak of var declarations in for loop 1`] = `
|
||||||
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||||
|
|
||||||
return function render(_ctx, _cache, $props, $setup, $data, $options) {
|
return function render(_ctx, _cache) {
|
||||||
return (_openBlock(), _createElementBlock("div", {
|
return (_openBlock(), _createElementBlock("div", {
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
for (let i = 0; i < _ctx.list.length; i++) {
|
for (var i = 0; i < _ctx.list.length; i++) {
|
||||||
_ctx.log(i)
|
_ctx.log(i)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
_ctx.error(i)
|
||||||
|
}
|
||||||
}, null, 8 /* PROPS */, ["onClick"]))
|
}, null, 8 /* PROPS */, ["onClick"]))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: expression transform > bindingMetadata > should not prefix temp variable of for...in 1`] = `
|
exports[`compiler: expression transform > should not prefix catch block param 1`] = `
|
||||||
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||||
|
|
||||||
return function render(_ctx, _cache, $props, $setup, $data, $options) {
|
return function render(_ctx, _cache) {
|
||||||
return (_openBlock(), _createElementBlock("div", {
|
return (_openBlock(), _createElementBlock("div", {
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
for (const x in _ctx.list) {
|
try {} catch (err) { console.error(err) }
|
||||||
_ctx.log(x)
|
console.log(_ctx.err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, null, 8 /* PROPS */, ["onClick"]))
|
}, null, 8 /* PROPS */, ["onClick"]))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: expression transform > bindingMetadata > should not prefix temp variable of for...of 1`] = `
|
exports[`compiler: expression transform > should not prefix destructured catch block param 1`] = `
|
||||||
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||||
|
|
||||||
return function render(_ctx, _cache, $props, $setup, $data, $options) {
|
return function render(_ctx, _cache) {
|
||||||
return (_openBlock(), _createElementBlock("div", {
|
return (_openBlock(), _createElementBlock("div", {
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
for (const x of _ctx.list) {
|
try {
|
||||||
_ctx.log(x)
|
throw new Error('sup?')
|
||||||
}
|
} catch ({ message: { length } }) {
|
||||||
|
console.error(length)
|
||||||
}
|
}
|
||||||
|
console.log(_ctx.length)
|
||||||
|
}
|
||||||
|
}, null, 8 /* PROPS */, ["onClick"]))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: expression transform > should not prefix temp variable of for loop 1`] = `
|
||||||
|
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
return (_openBlock(), _createElementBlock("div", {
|
||||||
|
onClick: () => {
|
||||||
|
for (let i = 0; i < _ctx.list.length; i++) {
|
||||||
|
_ctx.log(i)
|
||||||
|
}
|
||||||
|
_ctx.error(_ctx.i)
|
||||||
|
}
|
||||||
|
}, null, 8 /* PROPS */, ["onClick"]))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: expression transform > should not prefix temp variable of for...in 1`] = `
|
||||||
|
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
return (_openBlock(), _createElementBlock("div", {
|
||||||
|
onClick: () => {
|
||||||
|
for (const x in _ctx.list) {
|
||||||
|
_ctx.log(x)
|
||||||
|
}
|
||||||
|
_ctx.error(_ctx.x)
|
||||||
|
}
|
||||||
|
}, null, 8 /* PROPS */, ["onClick"]))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: expression transform > should not prefix temp variable of for...of 1`] = `
|
||||||
|
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
return (_openBlock(), _createElementBlock("div", {
|
||||||
|
onClick: () => {
|
||||||
|
for (const x of _ctx.list) {
|
||||||
|
_ctx.log(x)
|
||||||
|
}
|
||||||
|
_ctx.error(_ctx.x)
|
||||||
|
}
|
||||||
}, null, 8 /* PROPS */, ["onClick"]))
|
}, null, 8 /* PROPS */, ["onClick"]))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -27,6 +27,10 @@ function parseWithExpressionTransform(
|
||||||
return ast.children[0]
|
return ast.children[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function compile(template: string) {
|
||||||
|
return baseCompile(template, { prefixIdentifiers: true })
|
||||||
|
}
|
||||||
|
|
||||||
describe('compiler: expression transform', () => {
|
describe('compiler: expression transform', () => {
|
||||||
test('interpolation (root)', () => {
|
test('interpolation (root)', () => {
|
||||||
const node = parseWithExpressionTransform(`{{ foo }}`) as InterpolationNode
|
const node = parseWithExpressionTransform(`{{ foo }}`) as InterpolationNode
|
||||||
|
@ -291,6 +295,7 @@ describe('compiler: expression transform', () => {
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should not prefix an object property key', () => {
|
test('should not prefix an object property key', () => {
|
||||||
const node = parseWithExpressionTransform(
|
const node = parseWithExpressionTransform(
|
||||||
`{{ { foo() { baz() }, value: bar } }}`,
|
`{{ { foo() { baz() }, value: bar } }}`,
|
||||||
|
@ -457,6 +462,90 @@ describe('compiler: expression transform', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should not prefix temp variable of for...in', () => {
|
||||||
|
const { code } = compile(
|
||||||
|
`<div @click="() => {
|
||||||
|
for (const x in list) {
|
||||||
|
log(x)
|
||||||
|
}
|
||||||
|
error(x)
|
||||||
|
}"/>`,
|
||||||
|
)
|
||||||
|
expect(code).not.toMatch(`log(_ctx.x)`)
|
||||||
|
expect(code).toMatch(`error(_ctx.x)`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not prefix temp variable of for...of', () => {
|
||||||
|
const { code } = compile(
|
||||||
|
`<div @click="() => {
|
||||||
|
for (const x of list) {
|
||||||
|
log(x)
|
||||||
|
}
|
||||||
|
error(x)
|
||||||
|
}"/>`,
|
||||||
|
)
|
||||||
|
expect(code).not.toMatch(`log(_ctx.x)`)
|
||||||
|
expect(code).toMatch(`error(_ctx.x)`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not prefix temp variable of for loop', () => {
|
||||||
|
const { code } = compile(
|
||||||
|
`<div @click="() => {
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
log(i)
|
||||||
|
}
|
||||||
|
error(i)
|
||||||
|
}"/>`,
|
||||||
|
)
|
||||||
|
expect(code).not.toMatch(`log(_ctx.i)`)
|
||||||
|
expect(code).toMatch(`error(_ctx.i)`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should allow leak of var declarations in for loop', () => {
|
||||||
|
const { code } = compile(
|
||||||
|
`<div @click="() => {
|
||||||
|
for (var i = 0; i < list.length; i++) {
|
||||||
|
log(i)
|
||||||
|
}
|
||||||
|
error(i)
|
||||||
|
}"/>`,
|
||||||
|
)
|
||||||
|
expect(code).not.toMatch(`log(_ctx.i)`)
|
||||||
|
expect(code).not.toMatch(`error(_ctx.i)`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not prefix catch block param', () => {
|
||||||
|
const { code } = compile(
|
||||||
|
`<div @click="() => {
|
||||||
|
try {} catch (err) { console.error(err) }
|
||||||
|
console.log(err)
|
||||||
|
}"/>`,
|
||||||
|
)
|
||||||
|
expect(code).not.toMatch(`console.error(_ctx.err)`)
|
||||||
|
expect(code).toMatch(`console.log(_ctx.err)`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not prefix destructured catch block param', () => {
|
||||||
|
const { code } = compile(
|
||||||
|
`<div @click="() => {
|
||||||
|
try {
|
||||||
|
throw new Error('sup?')
|
||||||
|
} catch ({ message: { length } }) {
|
||||||
|
console.error(length)
|
||||||
|
}
|
||||||
|
console.log(length)
|
||||||
|
}"/>`,
|
||||||
|
)
|
||||||
|
expect(code).not.toMatch(`console.error(_ctx.length)`)
|
||||||
|
expect(code).toMatch(`console.log(_ctx.length)`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
describe('ES Proposals support', () => {
|
describe('ES Proposals support', () => {
|
||||||
test('bigInt', () => {
|
test('bigInt', () => {
|
||||||
const node = parseWithExpressionTransform(
|
const node = parseWithExpressionTransform(
|
||||||
|
@ -555,42 +644,6 @@ describe('compiler: expression transform', () => {
|
||||||
expect(code).toMatchSnapshot()
|
expect(code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should not prefix temp variable of for...in', () => {
|
|
||||||
const { code } = compileWithBindingMetadata(
|
|
||||||
`<div @click="() => {
|
|
||||||
for (const x in list) {
|
|
||||||
log(x)
|
|
||||||
}
|
|
||||||
}"/>`,
|
|
||||||
)
|
|
||||||
expect(code).not.toMatch(`_ctx.x`)
|
|
||||||
expect(code).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should not prefix temp variable of for...of', () => {
|
|
||||||
const { code } = compileWithBindingMetadata(
|
|
||||||
`<div @click="() => {
|
|
||||||
for (const x of list) {
|
|
||||||
log(x)
|
|
||||||
}
|
|
||||||
}"/>`,
|
|
||||||
)
|
|
||||||
expect(code).not.toMatch(`_ctx.x`)
|
|
||||||
expect(code).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should not prefix temp variable of for loop', () => {
|
|
||||||
const { code } = compileWithBindingMetadata(
|
|
||||||
`<div @click="() => {
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
log(i)
|
|
||||||
}
|
|
||||||
}"/>`,
|
|
||||||
)
|
|
||||||
expect(code).not.toMatch(`_ctx.i`)
|
|
||||||
expect(code).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('inline mode', () => {
|
test('inline mode', () => {
|
||||||
const { code } = compileWithBindingMetadata(
|
const { code } = compileWithBindingMetadata(
|
||||||
`<div>{{ props }} {{ setup }} {{ setupConst }} {{ data }} {{ options }} {{ isNaN }}</div>`,
|
`<div>{{ props }} {{ setup }} {{ setupConst }} {{ data }} {{ options }} {{ isNaN }}</div>`,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/compiler-core",
|
"name": "@vue/compiler-core",
|
||||||
"version": "3.5.0-alpha.5",
|
"version": "3.5.0-beta.1",
|
||||||
"description": "@vue/compiler-core",
|
"description": "@vue/compiler-core",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "dist/compiler-core.esm-bundler.js",
|
"module": "dist/compiler-core.esm-bundler.js",
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "catalog:",
|
"@babel/parser": "catalog:",
|
||||||
"@vue/shared": "workspace:*",
|
"@vue/shared": "workspace:*",
|
||||||
"entities": "^4.5.0",
|
"entities": "^5.0.0",
|
||||||
"estree-walker": "catalog:",
|
"estree-walker": "catalog:",
|
||||||
"source-map-js": "catalog:"
|
"source-map-js": "catalog:"
|
||||||
},
|
},
|
||||||
|
|
|
@ -625,7 +625,7 @@ export function createVNodeCall(
|
||||||
isBlock: VNodeCall['isBlock'] = false,
|
isBlock: VNodeCall['isBlock'] = false,
|
||||||
disableTracking: VNodeCall['disableTracking'] = false,
|
disableTracking: VNodeCall['disableTracking'] = false,
|
||||||
isComponent: VNodeCall['isComponent'] = false,
|
isComponent: VNodeCall['isComponent'] = false,
|
||||||
loc = locStub,
|
loc: SourceLocation = locStub,
|
||||||
): VNodeCall {
|
): VNodeCall {
|
||||||
if (context) {
|
if (context) {
|
||||||
if (isBlock) {
|
if (isBlock) {
|
||||||
|
@ -858,18 +858,24 @@ export function createReturnStatement(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVNodeHelper(ssr: boolean, isComponent: boolean) {
|
export function getVNodeHelper(
|
||||||
|
ssr: boolean,
|
||||||
|
isComponent: boolean,
|
||||||
|
): typeof CREATE_VNODE | typeof CREATE_ELEMENT_VNODE {
|
||||||
return ssr || isComponent ? CREATE_VNODE : CREATE_ELEMENT_VNODE
|
return ssr || isComponent ? CREATE_VNODE : CREATE_ELEMENT_VNODE
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVNodeBlockHelper(ssr: boolean, isComponent: boolean) {
|
export function getVNodeBlockHelper(
|
||||||
|
ssr: boolean,
|
||||||
|
isComponent: boolean,
|
||||||
|
): typeof CREATE_BLOCK | typeof CREATE_ELEMENT_BLOCK {
|
||||||
return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK
|
return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertToBlock(
|
export function convertToBlock(
|
||||||
node: VNodeCall,
|
node: VNodeCall,
|
||||||
{ helper, removeHelper, inSSR }: TransformContext,
|
{ helper, removeHelper, inSSR }: TransformContext,
|
||||||
) {
|
): void {
|
||||||
if (!node.isBlock) {
|
if (!node.isBlock) {
|
||||||
node.isBlock = true
|
node.isBlock = true
|
||||||
removeHelper(getVNodeHelper(inSSR, node.isComponent))
|
removeHelper(getVNodeHelper(inSSR, node.isComponent))
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
// do not import runtime methods
|
// do not import runtime methods
|
||||||
import type {
|
import type {
|
||||||
BlockStatement,
|
BlockStatement,
|
||||||
|
ForInStatement,
|
||||||
|
ForOfStatement,
|
||||||
|
ForStatement,
|
||||||
Function,
|
Function,
|
||||||
Identifier,
|
Identifier,
|
||||||
Node,
|
Node,
|
||||||
|
@ -25,7 +28,7 @@ export function walkIdentifiers(
|
||||||
includeAll = false,
|
includeAll = false,
|
||||||
parentStack: Node[] = [],
|
parentStack: Node[] = [],
|
||||||
knownIds: Record<string, number> = Object.create(null),
|
knownIds: Record<string, number> = Object.create(null),
|
||||||
) {
|
): void {
|
||||||
if (__BROWSER__) {
|
if (__BROWSER__) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -77,6 +80,14 @@ export function walkIdentifiers(
|
||||||
markScopeIdentifier(node, id, knownIds),
|
markScopeIdentifier(node, id, knownIds),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else if (node.type === 'CatchClause' && node.param) {
|
||||||
|
for (const id of extractIdentifiers(node.param)) {
|
||||||
|
markScopeIdentifier(node, id, knownIds)
|
||||||
|
}
|
||||||
|
} else if (isForStatement(node)) {
|
||||||
|
walkForStatement(node, false, id =>
|
||||||
|
markScopeIdentifier(node, id, knownIds),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leave(node: Node & { scopeIds?: Set<string> }, parent: Node | null) {
|
leave(node: Node & { scopeIds?: Set<string> }, parent: Node | null) {
|
||||||
|
@ -97,7 +108,7 @@ export function isReferencedIdentifier(
|
||||||
id: Identifier,
|
id: Identifier,
|
||||||
parent: Node | null,
|
parent: Node | null,
|
||||||
parentStack: Node[],
|
parentStack: Node[],
|
||||||
) {
|
): boolean {
|
||||||
if (__BROWSER__) {
|
if (__BROWSER__) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -166,7 +177,7 @@ export function isInNewExpression(parentStack: Node[]): boolean {
|
||||||
export function walkFunctionParams(
|
export function walkFunctionParams(
|
||||||
node: Function,
|
node: Function,
|
||||||
onIdent: (id: Identifier) => void,
|
onIdent: (id: Identifier) => void,
|
||||||
) {
|
): void {
|
||||||
for (const p of node.params) {
|
for (const p of node.params) {
|
||||||
for (const id of extractIdentifiers(p)) {
|
for (const id of extractIdentifiers(p)) {
|
||||||
onIdent(id)
|
onIdent(id)
|
||||||
|
@ -177,7 +188,7 @@ export function walkFunctionParams(
|
||||||
export function walkBlockDeclarations(
|
export function walkBlockDeclarations(
|
||||||
block: BlockStatement | Program,
|
block: BlockStatement | Program,
|
||||||
onIdent: (node: Identifier) => void,
|
onIdent: (node: Identifier) => void,
|
||||||
) {
|
): void {
|
||||||
for (const stmt of block.body) {
|
for (const stmt of block.body) {
|
||||||
if (stmt.type === 'VariableDeclaration') {
|
if (stmt.type === 'VariableDeclaration') {
|
||||||
if (stmt.declare) continue
|
if (stmt.declare) continue
|
||||||
|
@ -192,18 +203,36 @@ export function walkBlockDeclarations(
|
||||||
) {
|
) {
|
||||||
if (stmt.declare || !stmt.id) continue
|
if (stmt.declare || !stmt.id) continue
|
||||||
onIdent(stmt.id)
|
onIdent(stmt.id)
|
||||||
} else if (
|
} else if (isForStatement(stmt)) {
|
||||||
stmt.type === 'ForOfStatement' ||
|
walkForStatement(stmt, true, onIdent)
|
||||||
stmt.type === 'ForInStatement' ||
|
}
|
||||||
stmt.type === 'ForStatement'
|
}
|
||||||
) {
|
}
|
||||||
const variable = stmt.type === 'ForStatement' ? stmt.init : stmt.left
|
|
||||||
if (variable && variable.type === 'VariableDeclaration') {
|
function isForStatement(
|
||||||
for (const decl of variable.declarations) {
|
stmt: Node,
|
||||||
for (const id of extractIdentifiers(decl.id)) {
|
): stmt is ForStatement | ForOfStatement | ForInStatement {
|
||||||
onIdent(id)
|
return (
|
||||||
}
|
stmt.type === 'ForOfStatement' ||
|
||||||
}
|
stmt.type === 'ForInStatement' ||
|
||||||
|
stmt.type === 'ForStatement'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function walkForStatement(
|
||||||
|
stmt: ForStatement | ForOfStatement | ForInStatement,
|
||||||
|
isVar: boolean,
|
||||||
|
onIdent: (id: Identifier) => void,
|
||||||
|
) {
|
||||||
|
const variable = stmt.type === 'ForStatement' ? stmt.init : stmt.left
|
||||||
|
if (
|
||||||
|
variable &&
|
||||||
|
variable.type === 'VariableDeclaration' &&
|
||||||
|
(variable.kind === 'var' ? isVar : !isVar)
|
||||||
|
) {
|
||||||
|
for (const decl of variable.declarations) {
|
||||||
|
for (const id of extractIdentifiers(decl.id)) {
|
||||||
|
onIdent(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,7 +313,7 @@ export const isStaticProperty = (node?: Node): node is ObjectProperty =>
|
||||||
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
|
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
|
||||||
!node.computed
|
!node.computed
|
||||||
|
|
||||||
export const isStaticPropertyKey = (node: Node, parent: Node) =>
|
export const isStaticPropertyKey = (node: Node, parent: Node): boolean =>
|
||||||
isStaticProperty(parent) && parent.key === node
|
isStaticProperty(parent) && parent.key === node
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -466,7 +495,7 @@ function isReferenced(node: Node, parent: Node, grandparent?: Node): boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TS_NODE_TYPES = [
|
export const TS_NODE_TYPES: string[] = [
|
||||||
'TSAsExpression', // foo as number
|
'TSAsExpression', // foo as number
|
||||||
'TSTypeAssertion', // (<number>foo)
|
'TSTypeAssertion', // (<number>foo)
|
||||||
'TSNonNullExpression', // foo!
|
'TSNonNullExpression', // foo!
|
||||||
|
|
|
@ -106,7 +106,7 @@ function getCompatValue(
|
||||||
export function isCompatEnabled(
|
export function isCompatEnabled(
|
||||||
key: CompilerDeprecationTypes,
|
key: CompilerDeprecationTypes,
|
||||||
context: MergedParserOptions | TransformContext,
|
context: MergedParserOptions | TransformContext,
|
||||||
) {
|
): boolean {
|
||||||
const mode = getCompatValue('MODE', context)
|
const mode = getCompatValue('MODE', context)
|
||||||
const value = getCompatValue(key, context)
|
const value = getCompatValue(key, context)
|
||||||
// in v3 mode, only enable if explicitly set to true
|
// in v3 mode, only enable if explicitly set to true
|
||||||
|
@ -132,7 +132,7 @@ export function warnDeprecation(
|
||||||
context: MergedParserOptions | TransformContext,
|
context: MergedParserOptions | TransformContext,
|
||||||
loc: SourceLocation | null,
|
loc: SourceLocation | null,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
) {
|
): void {
|
||||||
const val = getCompatValue(key, context)
|
const val = getCompatValue(key, context)
|
||||||
if (val === 'suppress-warning') {
|
if (val === 'suppress-warning') {
|
||||||
return
|
return
|
||||||
|
|
|
@ -9,11 +9,11 @@ export interface CoreCompilerError extends CompilerError {
|
||||||
code: ErrorCodes
|
code: ErrorCodes
|
||||||
}
|
}
|
||||||
|
|
||||||
export function defaultOnError(error: CompilerError) {
|
export function defaultOnError(error: CompilerError): never {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
export function defaultOnWarn(msg: CompilerError) {
|
export function defaultOnWarn(msg: CompilerError): void {
|
||||||
__DEV__ && console.warn(`[Vue warn] ${msg.message}`)
|
__DEV__ && console.warn(`[Vue warn] ${msg.message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ import {
|
||||||
isSimpleIdentifier,
|
isSimpleIdentifier,
|
||||||
isStaticArgOf,
|
isStaticArgOf,
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import { decodeHTML } from 'entities/lib/decode.js'
|
import { decodeHTML } from 'entities/dist/decode.js'
|
||||||
import {
|
import {
|
||||||
type ParserOptions as BabelOptions,
|
type ParserOptions as BabelOptions,
|
||||||
parse,
|
parse,
|
||||||
|
|
|
@ -1,51 +1,85 @@
|
||||||
export const FRAGMENT = Symbol(__DEV__ ? `Fragment` : ``)
|
export const FRAGMENT: unique symbol = Symbol(__DEV__ ? `Fragment` : ``)
|
||||||
export const TELEPORT = Symbol(__DEV__ ? `Teleport` : ``)
|
export const TELEPORT: unique symbol = Symbol(__DEV__ ? `Teleport` : ``)
|
||||||
export const SUSPENSE = Symbol(__DEV__ ? `Suspense` : ``)
|
export const SUSPENSE: unique symbol = Symbol(__DEV__ ? `Suspense` : ``)
|
||||||
export const KEEP_ALIVE = Symbol(__DEV__ ? `KeepAlive` : ``)
|
export const KEEP_ALIVE: unique symbol = Symbol(__DEV__ ? `KeepAlive` : ``)
|
||||||
export const BASE_TRANSITION = Symbol(__DEV__ ? `BaseTransition` : ``)
|
export const BASE_TRANSITION: unique symbol = Symbol(
|
||||||
export const OPEN_BLOCK = Symbol(__DEV__ ? `openBlock` : ``)
|
__DEV__ ? `BaseTransition` : ``,
|
||||||
export const CREATE_BLOCK = Symbol(__DEV__ ? `createBlock` : ``)
|
)
|
||||||
export const CREATE_ELEMENT_BLOCK = Symbol(__DEV__ ? `createElementBlock` : ``)
|
export const OPEN_BLOCK: unique symbol = Symbol(__DEV__ ? `openBlock` : ``)
|
||||||
export const CREATE_VNODE = Symbol(__DEV__ ? `createVNode` : ``)
|
export const CREATE_BLOCK: unique symbol = Symbol(__DEV__ ? `createBlock` : ``)
|
||||||
export const CREATE_ELEMENT_VNODE = Symbol(__DEV__ ? `createElementVNode` : ``)
|
export const CREATE_ELEMENT_BLOCK: unique symbol = Symbol(
|
||||||
export const CREATE_COMMENT = Symbol(__DEV__ ? `createCommentVNode` : ``)
|
__DEV__ ? `createElementBlock` : ``,
|
||||||
export const CREATE_TEXT = Symbol(__DEV__ ? `createTextVNode` : ``)
|
)
|
||||||
export const CREATE_STATIC = Symbol(__DEV__ ? `createStaticVNode` : ``)
|
export const CREATE_VNODE: unique symbol = Symbol(__DEV__ ? `createVNode` : ``)
|
||||||
export const RESOLVE_COMPONENT = Symbol(__DEV__ ? `resolveComponent` : ``)
|
export const CREATE_ELEMENT_VNODE: unique symbol = Symbol(
|
||||||
export const RESOLVE_DYNAMIC_COMPONENT = Symbol(
|
__DEV__ ? `createElementVNode` : ``,
|
||||||
|
)
|
||||||
|
export const CREATE_COMMENT: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `createCommentVNode` : ``,
|
||||||
|
)
|
||||||
|
export const CREATE_TEXT: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `createTextVNode` : ``,
|
||||||
|
)
|
||||||
|
export const CREATE_STATIC: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `createStaticVNode` : ``,
|
||||||
|
)
|
||||||
|
export const RESOLVE_COMPONENT: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `resolveComponent` : ``,
|
||||||
|
)
|
||||||
|
export const RESOLVE_DYNAMIC_COMPONENT: unique symbol = Symbol(
|
||||||
__DEV__ ? `resolveDynamicComponent` : ``,
|
__DEV__ ? `resolveDynamicComponent` : ``,
|
||||||
)
|
)
|
||||||
export const RESOLVE_DIRECTIVE = Symbol(__DEV__ ? `resolveDirective` : ``)
|
export const RESOLVE_DIRECTIVE: unique symbol = Symbol(
|
||||||
export const RESOLVE_FILTER = Symbol(__DEV__ ? `resolveFilter` : ``)
|
__DEV__ ? `resolveDirective` : ``,
|
||||||
export const WITH_DIRECTIVES = Symbol(__DEV__ ? `withDirectives` : ``)
|
)
|
||||||
export const RENDER_LIST = Symbol(__DEV__ ? `renderList` : ``)
|
export const RESOLVE_FILTER: unique symbol = Symbol(
|
||||||
export const RENDER_SLOT = Symbol(__DEV__ ? `renderSlot` : ``)
|
__DEV__ ? `resolveFilter` : ``,
|
||||||
export const CREATE_SLOTS = Symbol(__DEV__ ? `createSlots` : ``)
|
)
|
||||||
export const TO_DISPLAY_STRING = Symbol(__DEV__ ? `toDisplayString` : ``)
|
export const WITH_DIRECTIVES: unique symbol = Symbol(
|
||||||
export const MERGE_PROPS = Symbol(__DEV__ ? `mergeProps` : ``)
|
__DEV__ ? `withDirectives` : ``,
|
||||||
export const NORMALIZE_CLASS = Symbol(__DEV__ ? `normalizeClass` : ``)
|
)
|
||||||
export const NORMALIZE_STYLE = Symbol(__DEV__ ? `normalizeStyle` : ``)
|
export const RENDER_LIST: unique symbol = Symbol(__DEV__ ? `renderList` : ``)
|
||||||
export const NORMALIZE_PROPS = Symbol(__DEV__ ? `normalizeProps` : ``)
|
export const RENDER_SLOT: unique symbol = Symbol(__DEV__ ? `renderSlot` : ``)
|
||||||
export const GUARD_REACTIVE_PROPS = Symbol(__DEV__ ? `guardReactiveProps` : ``)
|
export const CREATE_SLOTS: unique symbol = Symbol(__DEV__ ? `createSlots` : ``)
|
||||||
export const TO_HANDLERS = Symbol(__DEV__ ? `toHandlers` : ``)
|
export const TO_DISPLAY_STRING: unique symbol = Symbol(
|
||||||
export const CAMELIZE = Symbol(__DEV__ ? `camelize` : ``)
|
__DEV__ ? `toDisplayString` : ``,
|
||||||
export const CAPITALIZE = Symbol(__DEV__ ? `capitalize` : ``)
|
)
|
||||||
export const TO_HANDLER_KEY = Symbol(__DEV__ ? `toHandlerKey` : ``)
|
export const MERGE_PROPS: unique symbol = Symbol(__DEV__ ? `mergeProps` : ``)
|
||||||
export const SET_BLOCK_TRACKING = Symbol(__DEV__ ? `setBlockTracking` : ``)
|
export const NORMALIZE_CLASS: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `normalizeClass` : ``,
|
||||||
|
)
|
||||||
|
export const NORMALIZE_STYLE: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `normalizeStyle` : ``,
|
||||||
|
)
|
||||||
|
export const NORMALIZE_PROPS: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `normalizeProps` : ``,
|
||||||
|
)
|
||||||
|
export const GUARD_REACTIVE_PROPS: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `guardReactiveProps` : ``,
|
||||||
|
)
|
||||||
|
export const TO_HANDLERS: unique symbol = Symbol(__DEV__ ? `toHandlers` : ``)
|
||||||
|
export const CAMELIZE: unique symbol = Symbol(__DEV__ ? `camelize` : ``)
|
||||||
|
export const CAPITALIZE: unique symbol = Symbol(__DEV__ ? `capitalize` : ``)
|
||||||
|
export const TO_HANDLER_KEY: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `toHandlerKey` : ``,
|
||||||
|
)
|
||||||
|
export const SET_BLOCK_TRACKING: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `setBlockTracking` : ``,
|
||||||
|
)
|
||||||
/**
|
/**
|
||||||
* @deprecated no longer needed in 3.5+ because we no longer hoist element nodes
|
* @deprecated no longer needed in 3.5+ because we no longer hoist element nodes
|
||||||
* but kept for backwards compat
|
* but kept for backwards compat
|
||||||
*/
|
*/
|
||||||
export const PUSH_SCOPE_ID = Symbol(__DEV__ ? `pushScopeId` : ``)
|
export const PUSH_SCOPE_ID: unique symbol = Symbol(__DEV__ ? `pushScopeId` : ``)
|
||||||
/**
|
/**
|
||||||
* @deprecated kept for backwards compat
|
* @deprecated kept for backwards compat
|
||||||
*/
|
*/
|
||||||
export const POP_SCOPE_ID = Symbol(__DEV__ ? `popScopeId` : ``)
|
export const POP_SCOPE_ID: unique symbol = Symbol(__DEV__ ? `popScopeId` : ``)
|
||||||
export const WITH_CTX = Symbol(__DEV__ ? `withCtx` : ``)
|
export const WITH_CTX: unique symbol = Symbol(__DEV__ ? `withCtx` : ``)
|
||||||
export const UNREF = Symbol(__DEV__ ? `unref` : ``)
|
export const UNREF: unique symbol = Symbol(__DEV__ ? `unref` : ``)
|
||||||
export const IS_REF = Symbol(__DEV__ ? `isRef` : ``)
|
export const IS_REF: unique symbol = Symbol(__DEV__ ? `isRef` : ``)
|
||||||
export const WITH_MEMO = Symbol(__DEV__ ? `withMemo` : ``)
|
export const WITH_MEMO: unique symbol = Symbol(__DEV__ ? `withMemo` : ``)
|
||||||
export const IS_MEMO_SAME = Symbol(__DEV__ ? `isMemoSame` : ``)
|
export const IS_MEMO_SAME: unique symbol = Symbol(__DEV__ ? `isMemoSame` : ``)
|
||||||
|
|
||||||
// Name mapping for runtime helpers that need to be imported from 'vue' in
|
// Name mapping for runtime helpers that need to be imported from 'vue' in
|
||||||
// generated code. Make sure these are correctly exported in the runtime!
|
// generated code. Make sure these are correctly exported in the runtime!
|
||||||
|
@ -91,7 +125,7 @@ export const helperNameMap: Record<symbol, string> = {
|
||||||
[IS_MEMO_SAME]: `isMemoSame`,
|
[IS_MEMO_SAME]: `isMemoSame`,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerRuntimeHelpers(helpers: Record<symbol, string>) {
|
export function registerRuntimeHelpers(helpers: Record<symbol, string>): void {
|
||||||
Object.getOwnPropertySymbols(helpers).forEach(s => {
|
Object.getOwnPropertySymbols(helpers).forEach(s => {
|
||||||
helperNameMap[s] = helpers[s]
|
helperNameMap[s] = helpers[s]
|
||||||
})
|
})
|
||||||
|
|
|
@ -36,7 +36,7 @@ import {
|
||||||
EntityDecoder,
|
EntityDecoder,
|
||||||
fromCodePoint,
|
fromCodePoint,
|
||||||
htmlDecodeTree,
|
htmlDecodeTree,
|
||||||
} from 'entities/lib/decode.js'
|
} from 'entities/dist/decode.js'
|
||||||
|
|
||||||
export enum ParseMode {
|
export enum ParseMode {
|
||||||
BASE,
|
BASE,
|
||||||
|
@ -213,7 +213,15 @@ export interface Callbacks {
|
||||||
* We don't have `Script`, `Style`, or `Title` here. Instead, we re-use the *End
|
* We don't have `Script`, `Style`, or `Title` here. Instead, we re-use the *End
|
||||||
* sequences with an increased offset.
|
* sequences with an increased offset.
|
||||||
*/
|
*/
|
||||||
export const Sequences = {
|
export const Sequences: {
|
||||||
|
Cdata: Uint8Array
|
||||||
|
CdataEnd: Uint8Array
|
||||||
|
CommentEnd: Uint8Array
|
||||||
|
ScriptEnd: Uint8Array
|
||||||
|
StyleEnd: Uint8Array
|
||||||
|
TitleEnd: Uint8Array
|
||||||
|
TextareaEnd: Uint8Array
|
||||||
|
} = {
|
||||||
Cdata: new Uint8Array([0x43, 0x44, 0x41, 0x54, 0x41, 0x5b]), // CDATA[
|
Cdata: new Uint8Array([0x43, 0x44, 0x41, 0x54, 0x41, 0x5b]), // CDATA[
|
||||||
CdataEnd: new Uint8Array([0x5d, 0x5d, 0x3e]), // ]]>
|
CdataEnd: new Uint8Array([0x5d, 0x5d, 0x3e]), // ]]>
|
||||||
CommentEnd: new Uint8Array([0x2d, 0x2d, 0x3e]), // `-->`
|
CommentEnd: new Uint8Array([0x2d, 0x2d, 0x3e]), // `-->`
|
||||||
|
@ -227,7 +235,7 @@ export const Sequences = {
|
||||||
|
|
||||||
export default class Tokenizer {
|
export default class Tokenizer {
|
||||||
/** The current state the tokenizer is in. */
|
/** The current state the tokenizer is in. */
|
||||||
public state = State.Text
|
public state: State = State.Text
|
||||||
/** The read buffer. */
|
/** The read buffer. */
|
||||||
private buffer = ''
|
private buffer = ''
|
||||||
/** The beginning of the section that is currently being read. */
|
/** The beginning of the section that is currently being read. */
|
||||||
|
@ -249,8 +257,8 @@ export default class Tokenizer {
|
||||||
|
|
||||||
private readonly entityDecoder?: EntityDecoder
|
private readonly entityDecoder?: EntityDecoder
|
||||||
|
|
||||||
public mode = ParseMode.BASE
|
public mode: ParseMode = ParseMode.BASE
|
||||||
public get inSFCRoot() {
|
public get inSFCRoot(): boolean {
|
||||||
return this.mode === ParseMode.SFC && this.stack.length === 0
|
return this.mode === ParseMode.SFC && this.stack.length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,7 +534,7 @@ export default class Tokenizer {
|
||||||
this.state = State.SpecialStartSequence
|
this.state = State.SpecialStartSequence
|
||||||
}
|
}
|
||||||
|
|
||||||
public enterRCDATA(sequence: Uint8Array, offset: number) {
|
public enterRCDATA(sequence: Uint8Array, offset: number): void {
|
||||||
this.inRCDATA = true
|
this.inRCDATA = true
|
||||||
this.currentSequence = sequence
|
this.currentSequence = sequence
|
||||||
this.sequenceIndex = offset
|
this.sequenceIndex = offset
|
||||||
|
@ -917,7 +925,7 @@ export default class Tokenizer {
|
||||||
*
|
*
|
||||||
* States that are more likely to be hit are higher up, as a performance improvement.
|
* States that are more likely to be hit are higher up, as a performance improvement.
|
||||||
*/
|
*/
|
||||||
public parse(input: string) {
|
public parse(input: string): void {
|
||||||
this.buffer = input
|
this.buffer = input
|
||||||
while (this.index < this.buffer.length) {
|
while (this.index < this.buffer.length) {
|
||||||
const c = this.buffer.charCodeAt(this.index)
|
const c = this.buffer.charCodeAt(this.index)
|
||||||
|
|
|
@ -326,7 +326,7 @@ export function createTransformContext(
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transform(root: RootNode, options: TransformOptions) {
|
export function transform(root: RootNode, options: TransformOptions): void {
|
||||||
const context = createTransformContext(root, options)
|
const context = createTransformContext(root, options)
|
||||||
traverseNode(root, context)
|
traverseNode(root, context)
|
||||||
if (options.hoistStatic) {
|
if (options.hoistStatic) {
|
||||||
|
@ -403,7 +403,7 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
|
||||||
export function traverseChildren(
|
export function traverseChildren(
|
||||||
parent: ParentNode,
|
parent: ParentNode,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
) {
|
): void {
|
||||||
let i = 0
|
let i = 0
|
||||||
const nodeRemoved = () => {
|
const nodeRemoved = () => {
|
||||||
i--
|
i--
|
||||||
|
@ -422,7 +422,7 @@ export function traverseChildren(
|
||||||
export function traverseNode(
|
export function traverseNode(
|
||||||
node: RootNode | TemplateChildNode,
|
node: RootNode | TemplateChildNode,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
) {
|
): void {
|
||||||
context.currentNode = node
|
context.currentNode = node
|
||||||
// apply transform plugins
|
// apply transform plugins
|
||||||
const { nodeTransforms } = context
|
const { nodeTransforms } = context
|
||||||
|
|
|
@ -31,7 +31,7 @@ import {
|
||||||
OPEN_BLOCK,
|
OPEN_BLOCK,
|
||||||
} from '../runtimeHelpers'
|
} from '../runtimeHelpers'
|
||||||
|
|
||||||
export function cacheStatic(root: RootNode, context: TransformContext) {
|
export function cacheStatic(root: RootNode, context: TransformContext): void {
|
||||||
walk(
|
walk(
|
||||||
root,
|
root,
|
||||||
undefined,
|
undefined,
|
||||||
|
|
|
@ -228,7 +228,7 @@ export function resolveComponentType(
|
||||||
node: ComponentNode,
|
node: ComponentNode,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
ssr = false,
|
ssr = false,
|
||||||
) {
|
): string | symbol | CallExpression {
|
||||||
let { tag } = node
|
let { tag } = node
|
||||||
|
|
||||||
// 1. dynamic component
|
// 1. dynamic component
|
||||||
|
@ -250,7 +250,7 @@ export function resolveComponentType(
|
||||||
exp = isProp.exp
|
exp = isProp.exp
|
||||||
if (!exp) {
|
if (!exp) {
|
||||||
// #10469 handle :is shorthand
|
// #10469 handle :is shorthand
|
||||||
exp = createSimpleExpression(`is`, false, isProp.loc)
|
exp = createSimpleExpression(`is`, false, isProp.arg!.loc)
|
||||||
if (!__BROWSER__) {
|
if (!__BROWSER__) {
|
||||||
exp = isProp.exp = processExpression(exp, context)
|
exp = isProp.exp = processExpression(exp, context)
|
||||||
}
|
}
|
||||||
|
@ -374,7 +374,7 @@ export type PropsExpression = ObjectExpression | CallExpression | ExpressionNode
|
||||||
export function buildProps(
|
export function buildProps(
|
||||||
node: ElementNode,
|
node: ElementNode,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
props: ElementNode['props'] = node.props,
|
props: ElementNode['props'] | undefined = node.props,
|
||||||
isComponent: boolean,
|
isComponent: boolean,
|
||||||
isDynamicComponent: boolean,
|
isDynamicComponent: boolean,
|
||||||
ssr = false,
|
ssr = false,
|
||||||
|
|
|
@ -99,7 +99,7 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
|
||||||
export const transformBindShorthand = (
|
export const transformBindShorthand = (
|
||||||
dir: DirectiveNode,
|
dir: DirectiveNode,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
) => {
|
): void => {
|
||||||
const arg = dir.arg!
|
const arg = dir.arg!
|
||||||
|
|
||||||
const propName = camelize((arg as SimpleExpressionNode).content)
|
const propName = camelize((arg as SimpleExpressionNode).content)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
type NodeTransform,
|
||||||
type TransformContext,
|
type TransformContext,
|
||||||
createStructuralDirectiveTransform,
|
createStructuralDirectiveTransform,
|
||||||
} from '../transform'
|
} from '../transform'
|
||||||
|
@ -49,7 +50,7 @@ import { validateBrowserExpression } from '../validateExpression'
|
||||||
import { PatchFlags } from '@vue/shared'
|
import { PatchFlags } from '@vue/shared'
|
||||||
import { transformBindShorthand } from './vBind'
|
import { transformBindShorthand } from './vBind'
|
||||||
|
|
||||||
export const transformFor = createStructuralDirectiveTransform(
|
export const transformFor: NodeTransform = createStructuralDirectiveTransform(
|
||||||
'for',
|
'for',
|
||||||
(node, dir, context) => {
|
(node, dir, context) => {
|
||||||
const { helper, removeHelper } = context
|
const { helper, removeHelper } = context
|
||||||
|
@ -299,7 +300,7 @@ export function processFor(
|
||||||
|
|
||||||
const onExit = processCodegen && processCodegen(forNode)
|
const onExit = processCodegen && processCodegen(forNode)
|
||||||
|
|
||||||
return () => {
|
return (): void => {
|
||||||
scopes.vFor--
|
scopes.vFor--
|
||||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||||
value && removeIdentifiers(value)
|
value && removeIdentifiers(value)
|
||||||
|
@ -313,7 +314,7 @@ export function processFor(
|
||||||
export function finalizeForParseResult(
|
export function finalizeForParseResult(
|
||||||
result: ForParseResult,
|
result: ForParseResult,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
) {
|
): void {
|
||||||
if (result.finalized) return
|
if (result.finalized) return
|
||||||
|
|
||||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
type NodeTransform,
|
||||||
type TransformContext,
|
type TransformContext,
|
||||||
createStructuralDirectiveTransform,
|
createStructuralDirectiveTransform,
|
||||||
traverseNode,
|
traverseNode,
|
||||||
|
@ -33,7 +34,7 @@ import { CREATE_COMMENT, FRAGMENT } from '../runtimeHelpers'
|
||||||
import { findDir, findProp, getMemoedVNodeCall, injectProp } from '../utils'
|
import { findDir, findProp, getMemoedVNodeCall, injectProp } from '../utils'
|
||||||
import { PatchFlagNames, PatchFlags } from '@vue/shared'
|
import { PatchFlagNames, PatchFlags } from '@vue/shared'
|
||||||
|
|
||||||
export const transformIf = createStructuralDirectiveTransform(
|
export const transformIf: NodeTransform = createStructuralDirectiveTransform(
|
||||||
/^(if|else|else-if)$/,
|
/^(if|else|else-if)$/,
|
||||||
(node, dir, context) => {
|
(node, dir, context) => {
|
||||||
return processIf(node, dir, context, (ifNode, branch, isRoot) => {
|
return processIf(node, dir, context, (ifNode, branch, isRoot) => {
|
||||||
|
@ -83,7 +84,7 @@ export function processIf(
|
||||||
branch: IfBranchNode,
|
branch: IfBranchNode,
|
||||||
isRoot: boolean,
|
isRoot: boolean,
|
||||||
) => (() => void) | undefined,
|
) => (() => void) | undefined,
|
||||||
) {
|
): (() => void) | undefined {
|
||||||
if (
|
if (
|
||||||
dir.name !== 'else' &&
|
dir.name !== 'else' &&
|
||||||
(!dir.exp || !(dir.exp as SimpleExpressionNode).content.trim())
|
(!dir.exp || !(dir.exp as SimpleExpressionNode).content.trim())
|
||||||
|
|
|
@ -153,18 +153,15 @@ export const isMemberExpressionBrowser = (path: string): boolean => {
|
||||||
return !currentOpenBracketCount && !currentOpenParensCount
|
return !currentOpenBracketCount && !currentOpenParensCount
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isMemberExpressionNode = __BROWSER__
|
export const isMemberExpressionNode: (
|
||||||
? (NOOP as any as (
|
path: string,
|
||||||
path: string,
|
context: Pick<TransformContext, 'expressionPlugins'>,
|
||||||
options: Pick<TransformContext, 'expressionPlugins'>,
|
) => boolean = __BROWSER__
|
||||||
) => boolean)
|
? (NOOP as any)
|
||||||
: (
|
: (path, context): boolean => {
|
||||||
path: string,
|
|
||||||
options: Pick<TransformContext, 'expressionPlugins'>,
|
|
||||||
): boolean => {
|
|
||||||
try {
|
try {
|
||||||
let ret: Expression = parseExpression(path, {
|
let ret: Expression = parseExpression(path, {
|
||||||
plugins: options.expressionPlugins,
|
plugins: context.expressionPlugins,
|
||||||
})
|
})
|
||||||
ret = unwrapTSNode(ret) as Expression
|
ret = unwrapTSNode(ret) as Expression
|
||||||
return (
|
return (
|
||||||
|
@ -177,9 +174,10 @@ export const isMemberExpressionNode = __BROWSER__
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isMemberExpression = __BROWSER__
|
export const isMemberExpression: (
|
||||||
? isMemberExpressionBrowser
|
path: string,
|
||||||
: isMemberExpressionNode
|
context: TransformContext,
|
||||||
|
) => boolean = __BROWSER__ ? isMemberExpressionBrowser : isMemberExpressionNode
|
||||||
|
|
||||||
export function advancePositionWithClone(
|
export function advancePositionWithClone(
|
||||||
pos: Position,
|
pos: Position,
|
||||||
|
@ -223,7 +221,7 @@ export function advancePositionWithMutation(
|
||||||
return pos
|
return pos
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assert(condition: boolean, msg?: string) {
|
export function assert(condition: boolean, msg?: string): void {
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
throw new Error(msg || `unexpected compiler condition`)
|
throw new Error(msg || `unexpected compiler condition`)
|
||||||
|
@ -338,7 +336,7 @@ export function injectProp(
|
||||||
node: VNodeCall | RenderSlotCall,
|
node: VNodeCall | RenderSlotCall,
|
||||||
prop: Property,
|
prop: Property,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
) {
|
): void {
|
||||||
let propsWithInjection: ObjectExpression | CallExpression | undefined
|
let propsWithInjection: ObjectExpression | CallExpression | undefined
|
||||||
/**
|
/**
|
||||||
* 1. mergeProps(...)
|
* 1. mergeProps(...)
|
||||||
|
@ -505,7 +503,9 @@ export function hasScopeRef(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMemoedVNodeCall(node: BlockCodegenNode | MemoExpression) {
|
export function getMemoedVNodeCall(
|
||||||
|
node: BlockCodegenNode | MemoExpression,
|
||||||
|
): VNodeCall | RenderSlotCall {
|
||||||
if (node.type === NodeTypes.JS_CALL_EXPRESSION && node.callee === WITH_MEMO) {
|
if (node.type === NodeTypes.JS_CALL_EXPRESSION && node.callee === WITH_MEMO) {
|
||||||
return node.arguments[1].returns as VNodeCall
|
return node.arguments[1].returns as VNodeCall
|
||||||
} else {
|
} else {
|
||||||
|
@ -513,4 +513,4 @@ export function getMemoedVNodeCall(node: BlockCodegenNode | MemoExpression) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+(\S[\s\S]*)/
|
export const forAliasRE: RegExp = /([\s\S]*?)\s+(?:in|of)\s+(\S[\s\S]*)/
|
||||||
|
|
|
@ -30,7 +30,7 @@ export function validateBrowserExpression(
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
asParams = false,
|
asParams = false,
|
||||||
asRawStatements = false,
|
asRawStatements = false,
|
||||||
) {
|
): void {
|
||||||
const exp = node.content
|
const exp = node.content
|
||||||
|
|
||||||
// empty expressions are validated per-directive since some directives
|
// empty expressions are validated per-directive since some directives
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
# @vue/compiler-dom
|
# @vue/compiler-dom
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/compiler-dom",
|
"name": "@vue/compiler-dom",
|
||||||
"version": "3.5.0-alpha.5",
|
"version": "3.5.0-beta.1",
|
||||||
"description": "@vue/compiler-dom",
|
"description": "@vue/compiler-dom",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "dist/compiler-dom.esm-bundler.js",
|
"module": "dist/compiler-dom.esm-bundler.js",
|
||||||
|
|
|
@ -1,18 +1,30 @@
|
||||||
import { registerRuntimeHelpers } from '@vue/compiler-core'
|
import { registerRuntimeHelpers } from '@vue/compiler-core'
|
||||||
|
|
||||||
export const V_MODEL_RADIO = Symbol(__DEV__ ? `vModelRadio` : ``)
|
export const V_MODEL_RADIO: unique symbol = Symbol(__DEV__ ? `vModelRadio` : ``)
|
||||||
export const V_MODEL_CHECKBOX = Symbol(__DEV__ ? `vModelCheckbox` : ``)
|
export const V_MODEL_CHECKBOX: unique symbol = Symbol(
|
||||||
export const V_MODEL_TEXT = Symbol(__DEV__ ? `vModelText` : ``)
|
__DEV__ ? `vModelCheckbox` : ``,
|
||||||
export const V_MODEL_SELECT = Symbol(__DEV__ ? `vModelSelect` : ``)
|
)
|
||||||
export const V_MODEL_DYNAMIC = Symbol(__DEV__ ? `vModelDynamic` : ``)
|
export const V_MODEL_TEXT: unique symbol = Symbol(__DEV__ ? `vModelText` : ``)
|
||||||
|
export const V_MODEL_SELECT: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `vModelSelect` : ``,
|
||||||
|
)
|
||||||
|
export const V_MODEL_DYNAMIC: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `vModelDynamic` : ``,
|
||||||
|
)
|
||||||
|
|
||||||
export const V_ON_WITH_MODIFIERS = Symbol(__DEV__ ? `vOnModifiersGuard` : ``)
|
export const V_ON_WITH_MODIFIERS: unique symbol = Symbol(
|
||||||
export const V_ON_WITH_KEYS = Symbol(__DEV__ ? `vOnKeysGuard` : ``)
|
__DEV__ ? `vOnModifiersGuard` : ``,
|
||||||
|
)
|
||||||
|
export const V_ON_WITH_KEYS: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `vOnKeysGuard` : ``,
|
||||||
|
)
|
||||||
|
|
||||||
export const V_SHOW = Symbol(__DEV__ ? `vShow` : ``)
|
export const V_SHOW: unique symbol = Symbol(__DEV__ ? `vShow` : ``)
|
||||||
|
|
||||||
export const TRANSITION = Symbol(__DEV__ ? `Transition` : ``)
|
export const TRANSITION: unique symbol = Symbol(__DEV__ ? `Transition` : ``)
|
||||||
export const TRANSITION_GROUP = Symbol(__DEV__ ? `TransitionGroup` : ``)
|
export const TRANSITION_GROUP: unique symbol = Symbol(
|
||||||
|
__DEV__ ? `TransitionGroup` : ``,
|
||||||
|
)
|
||||||
|
|
||||||
registerRuntimeHelpers({
|
registerRuntimeHelpers({
|
||||||
[V_MODEL_RADIO]: `vModelRadio`,
|
[V_MODEL_RADIO]: `vModelRadio`,
|
||||||
|
|
|
@ -425,5 +425,15 @@ h1 { color: red }
|
||||||
`At least one <template> or <script> is required in a single file component`,
|
`At least one <template> or <script> is required in a single file component`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should throw error if template functional is given', () => {
|
||||||
|
assertWarning(
|
||||||
|
parse(`<template functional></template>`).errors,
|
||||||
|
`<template functional> is no longer supported in Vue 3, since ` +
|
||||||
|
`functional components no longer have significant performance ` +
|
||||||
|
`difference from stateful ones. Just use a normal <template> ` +
|
||||||
|
`instead.`,
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/compiler-sfc",
|
"name": "@vue/compiler-sfc",
|
||||||
"version": "3.5.0-alpha.5",
|
"version": "3.5.0-beta.1",
|
||||||
"description": "@vue/compiler-sfc",
|
"description": "@vue/compiler-sfc",
|
||||||
"main": "dist/compiler-sfc.cjs.js",
|
"main": "dist/compiler-sfc.cjs.js",
|
||||||
"module": "dist/compiler-sfc.esm-browser.js",
|
"module": "dist/compiler-sfc.esm-browser.js",
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
"hash-sum": "^2.0.0",
|
"hash-sum": "^2.0.0",
|
||||||
"lru-cache": "10.1.0",
|
"lru-cache": "10.1.0",
|
||||||
"merge-source-map": "^1.1.0",
|
"merge-source-map": "^1.1.0",
|
||||||
"minimatch": "^9.0.5",
|
"minimatch": "~9.0.5",
|
||||||
"postcss-modules": "^6.0.0",
|
"postcss-modules": "^6.0.0",
|
||||||
"postcss-selector-parser": "^6.1.1",
|
"postcss-selector-parser": "^6.1.1",
|
||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const version = __VERSION__
|
export const version: string = __VERSION__
|
||||||
|
|
||||||
// API
|
// API
|
||||||
export { parse } from './parse'
|
export { parse } from './parse'
|
||||||
|
@ -18,7 +18,7 @@ import {
|
||||||
errorMessages as coreErrorMessages,
|
errorMessages as coreErrorMessages,
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
|
|
||||||
export const errorMessages = {
|
export const errorMessages: Record<number, string> = {
|
||||||
...coreErrorMessages,
|
...coreErrorMessages,
|
||||||
...DOMErrorMessages,
|
...DOMErrorMessages,
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { parseCssVars } from './style/cssVars'
|
||||||
import { createCache } from './cache'
|
import { createCache } from './cache'
|
||||||
import type { ImportBinding } from './compileScript'
|
import type { ImportBinding } from './compileScript'
|
||||||
import { isImportUsed } from './script/importUsageCheck'
|
import { isImportUsed } from './script/importUsageCheck'
|
||||||
|
import type { LRUCache } from 'lru-cache'
|
||||||
|
|
||||||
export const DEFAULT_FILENAME = 'anonymous.vue'
|
export const DEFAULT_FILENAME = 'anonymous.vue'
|
||||||
|
|
||||||
|
@ -105,7 +106,9 @@ export interface SFCParseResult {
|
||||||
errors: (CompilerError | SyntaxError)[]
|
errors: (CompilerError | SyntaxError)[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parseCache = createCache<SFCParseResult>()
|
export const parseCache:
|
||||||
|
| Map<string, SFCParseResult>
|
||||||
|
| LRUCache<string, SFCParseResult> = createCache<SFCParseResult>()
|
||||||
|
|
||||||
function genCacheKey(source: string, options: SFCParseOptions): string {
|
function genCacheKey(source: string, options: SFCParseOptions): string {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -17,11 +17,12 @@ export class ScriptCompileContext {
|
||||||
scriptAst: Program | null
|
scriptAst: Program | null
|
||||||
scriptSetupAst: Program | null
|
scriptSetupAst: Program | null
|
||||||
|
|
||||||
source = this.descriptor.source
|
source: string = this.descriptor.source
|
||||||
filename = this.descriptor.filename
|
filename: string = this.descriptor.filename
|
||||||
s = new MagicString(this.source)
|
s: MagicString = new MagicString(this.source)
|
||||||
startOffset = this.descriptor.scriptSetup?.loc.start.offset
|
startOffset: number | undefined =
|
||||||
endOffset = this.descriptor.scriptSetup?.loc.end.offset
|
this.descriptor.scriptSetup?.loc.start.offset
|
||||||
|
endOffset: number | undefined = this.descriptor.scriptSetup?.loc.end.offset
|
||||||
|
|
||||||
// import / type analysis
|
// import / type analysis
|
||||||
scope?: TypeScope
|
scope?: TypeScope
|
||||||
|
@ -163,7 +164,7 @@ export function resolveParserPlugins(
|
||||||
lang: string,
|
lang: string,
|
||||||
userPlugins?: ParserPlugin[],
|
userPlugins?: ParserPlugin[],
|
||||||
dts = false,
|
dts = false,
|
||||||
) {
|
): ParserPlugin[] {
|
||||||
const plugins: ParserPlugin[] = []
|
const plugins: ParserPlugin[] = []
|
||||||
if (
|
if (
|
||||||
!userPlugins ||
|
!userPlugins ||
|
||||||
|
|
|
@ -48,7 +48,7 @@ export function processDefineProps(
|
||||||
ctx: ScriptCompileContext,
|
ctx: ScriptCompileContext,
|
||||||
node: Node,
|
node: Node,
|
||||||
declId?: LVal,
|
declId?: LVal,
|
||||||
) {
|
): boolean {
|
||||||
if (!isCallOf(node, DEFINE_PROPS)) {
|
if (!isCallOf(node, DEFINE_PROPS)) {
|
||||||
return processWithDefaults(ctx, node, declId)
|
return processWithDefaults(ctx, node, declId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { DEFINE_PROPS } from './defineProps'
|
||||||
export function processPropsDestructure(
|
export function processPropsDestructure(
|
||||||
ctx: ScriptCompileContext,
|
ctx: ScriptCompileContext,
|
||||||
declId: ObjectPattern,
|
declId: ObjectPattern,
|
||||||
) {
|
): void {
|
||||||
if (ctx.options.propsDestructure === 'error') {
|
if (ctx.options.propsDestructure === 'error') {
|
||||||
ctx.error(`Props destructure is explicitly prohibited via config.`, declId)
|
ctx.error(`Props destructure is explicitly prohibited via config.`, declId)
|
||||||
} else if (ctx.options.propsDestructure === false) {
|
} else if (ctx.options.propsDestructure === false) {
|
||||||
|
@ -97,7 +97,7 @@ type Scope = Record<string, boolean>
|
||||||
export function transformDestructuredProps(
|
export function transformDestructuredProps(
|
||||||
ctx: ScriptCompileContext,
|
ctx: ScriptCompileContext,
|
||||||
vueImportAliases: Record<string, string>,
|
vueImportAliases: Record<string, string>,
|
||||||
) {
|
): void {
|
||||||
if (ctx.options.propsDestructure === false) {
|
if (ctx.options.propsDestructure === false) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,14 @@ import type { ScriptCompileContext } from './context'
|
||||||
import MagicString from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
import { rewriteDefaultAST } from '../rewriteDefault'
|
import { rewriteDefaultAST } from '../rewriteDefault'
|
||||||
import { genNormalScriptCssVarsCode } from '../style/cssVars'
|
import { genNormalScriptCssVarsCode } from '../style/cssVars'
|
||||||
|
import type { SFCScriptBlock } from '../parse'
|
||||||
|
|
||||||
export const normalScriptDefaultVar = `__default__`
|
export const normalScriptDefaultVar = `__default__`
|
||||||
|
|
||||||
export function processNormalScript(
|
export function processNormalScript(
|
||||||
ctx: ScriptCompileContext,
|
ctx: ScriptCompileContext,
|
||||||
scopeId: string,
|
scopeId: string,
|
||||||
) {
|
): SFCScriptBlock {
|
||||||
const script = ctx.descriptor.script!
|
const script = ctx.descriptor.script!
|
||||||
if (script.lang && !ctx.isJS && !ctx.isTS) {
|
if (script.lang && !ctx.isJS && !ctx.isTS) {
|
||||||
// do not process non js/ts script blocks
|
// do not process non js/ts script blocks
|
||||||
|
|
|
@ -823,7 +823,7 @@ let loadTS: (() => typeof TS) | undefined
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export function registerTS(_loadTS: () => typeof TS) {
|
export function registerTS(_loadTS: () => typeof TS): void {
|
||||||
loadTS = () => {
|
loadTS = () => {
|
||||||
try {
|
try {
|
||||||
return _loadTS()
|
return _loadTS()
|
||||||
|
@ -1107,7 +1107,7 @@ const fileToScopeCache = createCache<TypeScope>()
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export function invalidateTypeCache(filename: string) {
|
export function invalidateTypeCache(filename: string): void {
|
||||||
filename = normalizePath(filename)
|
filename = normalizePath(filename)
|
||||||
fileToScopeCache.delete(filename)
|
fileToScopeCache.delete(filename)
|
||||||
tsConfigCache.delete(filename)
|
tsConfigCache.delete(filename)
|
||||||
|
@ -1439,7 +1439,7 @@ function attachNamespace(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function recordImports(body: Statement[]) {
|
export function recordImports(body: Statement[]): Record<string, Import> {
|
||||||
const imports: TypeScope['imports'] = Object.create(null)
|
const imports: TypeScope['imports'] = Object.create(null)
|
||||||
for (const s of body) {
|
for (const s of body) {
|
||||||
recordImport(s, imports)
|
recordImport(s, imports)
|
||||||
|
@ -1462,7 +1462,7 @@ function recordImport(node: Node, imports: TypeScope['imports']) {
|
||||||
export function inferRuntimeType(
|
export function inferRuntimeType(
|
||||||
ctx: TypeResolveContext,
|
ctx: TypeResolveContext,
|
||||||
node: Node & MaybeWithScope,
|
node: Node & MaybeWithScope,
|
||||||
scope = node._ownerScope || ctxToScope(ctx),
|
scope: TypeScope = node._ownerScope || ctxToScope(ctx),
|
||||||
isKeyOf = false,
|
isKeyOf = false,
|
||||||
): string[] {
|
): string[] {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -39,7 +39,7 @@ export function processAwait(
|
||||||
node: AwaitExpression,
|
node: AwaitExpression,
|
||||||
needSemi: boolean,
|
needSemi: boolean,
|
||||||
isStatement: boolean,
|
isStatement: boolean,
|
||||||
) {
|
): void {
|
||||||
const argumentStart =
|
const argumentStart =
|
||||||
node.argument.extra && node.argument.extra.parenthesized
|
node.argument.extra && node.argument.extra.parenthesized
|
||||||
? (node.argument.extra.parenStart as number)
|
? (node.argument.extra.parenStart as number)
|
||||||
|
|
|
@ -12,7 +12,10 @@ import path from 'path'
|
||||||
|
|
||||||
export const UNKNOWN_TYPE = 'Unknown'
|
export const UNKNOWN_TYPE = 'Unknown'
|
||||||
|
|
||||||
export function resolveObjectKey(node: Node, computed: boolean) {
|
export function resolveObjectKey(
|
||||||
|
node: Node,
|
||||||
|
computed: boolean,
|
||||||
|
): string | undefined {
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case 'StringLiteral':
|
case 'StringLiteral':
|
||||||
case 'NumericLiteral':
|
case 'NumericLiteral':
|
||||||
|
@ -23,11 +26,13 @@ export function resolveObjectKey(node: Node, computed: boolean) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function concatStrings(strs: Array<string | null | undefined | false>) {
|
export function concatStrings(
|
||||||
|
strs: Array<string | null | undefined | false>,
|
||||||
|
): string {
|
||||||
return strs.filter((s): s is string => !!s).join(', ')
|
return strs.filter((s): s is string => !!s).join(', ')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isLiteralNode(node: Node) {
|
export function isLiteralNode(node: Node): boolean {
|
||||||
return node.type.endsWith('Literal')
|
return node.type.endsWith('Literal')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +51,7 @@ export function isCallOf(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toRuntimeTypeString(types: string[]) {
|
export function toRuntimeTypeString(types: string[]): string {
|
||||||
return types.length > 1 ? `[${types.join(', ')}]` : types[0]
|
return types.length > 1 ? `[${types.join(', ')}]` : types[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +60,7 @@ export function getImportedName(
|
||||||
| ImportSpecifier
|
| ImportSpecifier
|
||||||
| ImportDefaultSpecifier
|
| ImportDefaultSpecifier
|
||||||
| ImportNamespaceSpecifier,
|
| ImportNamespaceSpecifier,
|
||||||
) {
|
): string {
|
||||||
if (specifier.type === 'ImportSpecifier')
|
if (specifier.type === 'ImportSpecifier')
|
||||||
return specifier.imported.type === 'Identifier'
|
return specifier.imported.type === 'Identifier'
|
||||||
? specifier.imported.name
|
? specifier.imported.name
|
||||||
|
@ -89,7 +94,9 @@ function toFileNameLowerCase(x: string) {
|
||||||
* but TS does not expose it directly. This implementation is repllicated from
|
* but TS does not expose it directly. This implementation is repllicated from
|
||||||
* the TS source code.
|
* the TS source code.
|
||||||
*/
|
*/
|
||||||
export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean) {
|
export function createGetCanonicalFileName(
|
||||||
|
useCaseSensitiveFileNames: boolean,
|
||||||
|
): (str: string) => string {
|
||||||
return useCaseSensitiveFileNames ? identity : toFileNameLowerCase
|
return useCaseSensitiveFileNames ? identity : toFileNameLowerCase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,25 +104,31 @@ export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean) {
|
||||||
// posix behavior.
|
// posix behavior.
|
||||||
const normalize = (path.posix || path).normalize
|
const normalize = (path.posix || path).normalize
|
||||||
const windowsSlashRE = /\\/g
|
const windowsSlashRE = /\\/g
|
||||||
export function normalizePath(p: string) {
|
export function normalizePath(p: string): string {
|
||||||
return normalize(p.replace(windowsSlashRE, '/'))
|
return normalize(p.replace(windowsSlashRE, '/'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const joinPaths = (path.posix || path).join
|
export const joinPaths: (...paths: string[]) => string = (path.posix || path)
|
||||||
|
.join
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* key may contain symbols
|
* key may contain symbols
|
||||||
* e.g. onUpdate:modelValue -> "onUpdate:modelValue"
|
* e.g. onUpdate:modelValue -> "onUpdate:modelValue"
|
||||||
*/
|
*/
|
||||||
export const propNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~\-]/
|
export const propNameEscapeSymbolsRE: RegExp =
|
||||||
|
/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~\-]/
|
||||||
|
|
||||||
export function getEscapedPropName(key: string) {
|
export function getEscapedPropName(key: string): string {
|
||||||
return propNameEscapeSymbolsRE.test(key) ? JSON.stringify(key) : key
|
return propNameEscapeSymbolsRE.test(key) ? JSON.stringify(key) : key
|
||||||
}
|
}
|
||||||
|
|
||||||
export const cssVarNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g
|
export const cssVarNameEscapeSymbolsRE: RegExp =
|
||||||
|
/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g
|
||||||
|
|
||||||
export function getEscapedCssVarName(key: string, doubleEscape: boolean) {
|
export function getEscapedCssVarName(
|
||||||
|
key: string,
|
||||||
|
doubleEscape: boolean,
|
||||||
|
): string {
|
||||||
return key.replace(cssVarNameEscapeSymbolsRE, s =>
|
return key.replace(cssVarNameEscapeSymbolsRE, s =>
|
||||||
doubleEscape ? `\\\\${s}` : `\\${s}`,
|
doubleEscape ? `\\\\${s}` : `\\${s}`,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const hasWarned: Record<string, boolean> = {}
|
const hasWarned: Record<string, boolean> = {}
|
||||||
|
|
||||||
export function warnOnce(msg: string) {
|
export function warnOnce(msg: string): void {
|
||||||
const isNodeProd =
|
const isNodeProd =
|
||||||
typeof process !== 'undefined' && process.env.NODE_ENV === 'production'
|
typeof process !== 'undefined' && process.env.NODE_ENV === 'production'
|
||||||
if (!isNodeProd && !__TEST__ && !hasWarned[msg]) {
|
if (!isNodeProd && !__TEST__ && !hasWarned[msg]) {
|
||||||
|
@ -9,7 +9,7 @@ export function warnOnce(msg: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function warn(msg: string) {
|
export function warn(msg: string): void {
|
||||||
console.warn(
|
console.warn(
|
||||||
`\x1b[1m\x1b[33m[@vue/compiler-sfc]\x1b[0m\x1b[33m ${msg}\x1b[0m\n`,
|
`\x1b[1m\x1b[33m[@vue/compiler-sfc]\x1b[0m\x1b[33m ${msg}\x1b[0m\n`,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
# @vue/compiler-ssr
|
# @vue/compiler-ssr
|
||||||
|
|
|
@ -288,12 +288,27 @@ describe('ssr: element', () => {
|
||||||
}></div>\`"
|
}></div>\`"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('custom dir', () => {
|
describe('custom directives', () => {
|
||||||
|
// #8112 should respect textContent / innerHTML from directive getSSRProps
|
||||||
|
// if the element has no children
|
||||||
|
test('custom dir without children', () => {
|
||||||
expect(getCompiledString(`<div v-xxx:x.y="z" />`)).toMatchInlineSnapshot(`
|
expect(getCompiledString(`<div v-xxx:x.y="z" />`)).toMatchInlineSnapshot(`
|
||||||
|
"\`<div\${
|
||||||
|
_ssrRenderAttrs(_temp0 = _ssrGetDirectiveProps(_ctx, _directive_xxx, _ctx.z, "x", { y: true }))
|
||||||
|
}>\${
|
||||||
|
("textContent" in _temp0) ? _ssrInterpolate(_temp0.textContent) : _temp0.innerHTML ?? ''
|
||||||
|
}</div>\`"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('custom dir with children', () => {
|
||||||
|
expect(getCompiledString(`<div v-xxx:x.y="z">hello</div>`))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
"\`<div\${
|
"\`<div\${
|
||||||
_ssrRenderAttrs(_ssrGetDirectiveProps(_ctx, _directive_xxx, _ctx.z, "x", { y: true }))
|
_ssrRenderAttrs(_ssrGetDirectiveProps(_ctx, _directive_xxx, _ctx.z, "x", { y: true }))
|
||||||
}></div>\`"
|
}>hello</div>\`"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -301,30 +316,36 @@ describe('ssr: element', () => {
|
||||||
expect(getCompiledString(`<div class="foo" v-xxx />`))
|
expect(getCompiledString(`<div class="foo" v-xxx />`))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"\`<div\${
|
"\`<div\${
|
||||||
_ssrRenderAttrs(_mergeProps({ class: "foo" }, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
|
_ssrRenderAttrs(_temp0 = _mergeProps({ class: "foo" }, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
|
||||||
}></div>\`"
|
}>\${
|
||||||
|
("textContent" in _temp0) ? _ssrInterpolate(_temp0.textContent) : _temp0.innerHTML ?? ''
|
||||||
|
}</div>\`"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('custom dir with v-bind', () => {
|
test('custom dir with v-bind', () => {
|
||||||
expect(getCompiledString(`<div :title="foo" :class="bar" v-xxx />`))
|
expect(getCompiledString(`<div :title="foo" :class="bar" v-xxx />`))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"\`<div\${
|
"\`<div\${
|
||||||
_ssrRenderAttrs(_mergeProps({
|
_ssrRenderAttrs(_temp0 = _mergeProps({
|
||||||
title: _ctx.foo,
|
title: _ctx.foo,
|
||||||
class: _ctx.bar
|
class: _ctx.bar
|
||||||
}, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
|
}, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
|
||||||
}></div>\`"
|
}>\${
|
||||||
`)
|
("textContent" in _temp0) ? _ssrInterpolate(_temp0.textContent) : _temp0.innerHTML ?? ''
|
||||||
|
}</div>\`"
|
||||||
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('custom dir with object v-bind', () => {
|
test('custom dir with object v-bind', () => {
|
||||||
expect(getCompiledString(`<div v-bind="x" v-xxx />`))
|
expect(getCompiledString(`<div v-bind="x" v-xxx />`))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"\`<div\${
|
"\`<div\${
|
||||||
_ssrRenderAttrs(_mergeProps(_ctx.x, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
|
_ssrRenderAttrs(_temp0 = _mergeProps(_ctx.x, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
|
||||||
}></div>\`"
|
}>\${
|
||||||
`)
|
("textContent" in _temp0) ? _ssrInterpolate(_temp0.textContent) : _temp0.innerHTML ?? ''
|
||||||
|
}</div>\`"
|
||||||
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('custom dir with object v-bind + normal bindings', () => {
|
test('custom dir with object v-bind + normal bindings', () => {
|
||||||
|
@ -332,11 +353,13 @@ describe('ssr: element', () => {
|
||||||
getCompiledString(`<div v-bind="x" class="foo" v-xxx title="bar" />`),
|
getCompiledString(`<div v-bind="x" class="foo" v-xxx title="bar" />`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"\`<div\${
|
"\`<div\${
|
||||||
_ssrRenderAttrs(_mergeProps(_ctx.x, {
|
_ssrRenderAttrs(_temp0 = _mergeProps(_ctx.x, {
|
||||||
class: "foo",
|
class: "foo",
|
||||||
title: "bar"
|
title: "bar"
|
||||||
}, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
|
}, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
|
||||||
}></div>\`"
|
}>\${
|
||||||
|
("textContent" in _temp0) ? _ssrInterpolate(_temp0.textContent) : _temp0.innerHTML ?? ''
|
||||||
|
}</div>\`"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -38,6 +38,28 @@ describe('transition-group', () => {
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #11514
|
||||||
|
test('with static tag + comment', () => {
|
||||||
|
expect(
|
||||||
|
compile(
|
||||||
|
`<transition-group tag="ul"><div v-for="i in list"/><div v-if="false"></div></transition-group>`,
|
||||||
|
).code,
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require("vue/server-renderer")
|
||||||
|
|
||||||
|
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||||
|
_push(\`<ul\${_ssrRenderAttrs(_attrs)}>\`)
|
||||||
|
_ssrRenderList(_ctx.list, (i) => {
|
||||||
|
_push(\`<div></div>\`)
|
||||||
|
})
|
||||||
|
if (false) {
|
||||||
|
_push(\`<div></div>\`)
|
||||||
|
}
|
||||||
|
_push(\`</ul>\`)
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
test('with dynamic tag', () => {
|
test('with dynamic tag', () => {
|
||||||
expect(
|
expect(
|
||||||
compile(
|
compile(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/compiler-ssr",
|
"name": "@vue/compiler-ssr",
|
||||||
"version": "3.5.0-alpha.5",
|
"version": "3.5.0-beta.1",
|
||||||
"description": "@vue/compiler-ssr",
|
"description": "@vue/compiler-ssr",
|
||||||
"main": "dist/compiler-ssr.cjs.js",
|
"main": "dist/compiler-ssr.cjs.js",
|
||||||
"types": "dist/compiler-ssr.d.ts",
|
"types": "dist/compiler-ssr.d.ts",
|
||||||
|
|
|
@ -1,26 +1,34 @@
|
||||||
import { registerRuntimeHelpers } from '@vue/compiler-dom'
|
import { registerRuntimeHelpers } from '@vue/compiler-dom'
|
||||||
|
|
||||||
export const SSR_INTERPOLATE = Symbol(`ssrInterpolate`)
|
export const SSR_INTERPOLATE: unique symbol = Symbol(`ssrInterpolate`)
|
||||||
export const SSR_RENDER_VNODE = Symbol(`ssrRenderVNode`)
|
export const SSR_RENDER_VNODE: unique symbol = Symbol(`ssrRenderVNode`)
|
||||||
export const SSR_RENDER_COMPONENT = Symbol(`ssrRenderComponent`)
|
export const SSR_RENDER_COMPONENT: unique symbol = Symbol(`ssrRenderComponent`)
|
||||||
export const SSR_RENDER_SLOT = Symbol(`ssrRenderSlot`)
|
export const SSR_RENDER_SLOT: unique symbol = Symbol(`ssrRenderSlot`)
|
||||||
export const SSR_RENDER_SLOT_INNER = Symbol(`ssrRenderSlotInner`)
|
export const SSR_RENDER_SLOT_INNER: unique symbol = Symbol(`ssrRenderSlotInner`)
|
||||||
export const SSR_RENDER_CLASS = Symbol(`ssrRenderClass`)
|
export const SSR_RENDER_CLASS: unique symbol = Symbol(`ssrRenderClass`)
|
||||||
export const SSR_RENDER_STYLE = Symbol(`ssrRenderStyle`)
|
export const SSR_RENDER_STYLE: unique symbol = Symbol(`ssrRenderStyle`)
|
||||||
export const SSR_RENDER_ATTRS = Symbol(`ssrRenderAttrs`)
|
export const SSR_RENDER_ATTRS: unique symbol = Symbol(`ssrRenderAttrs`)
|
||||||
export const SSR_RENDER_ATTR = Symbol(`ssrRenderAttr`)
|
export const SSR_RENDER_ATTR: unique symbol = Symbol(`ssrRenderAttr`)
|
||||||
export const SSR_RENDER_DYNAMIC_ATTR = Symbol(`ssrRenderDynamicAttr`)
|
export const SSR_RENDER_DYNAMIC_ATTR: unique symbol =
|
||||||
export const SSR_RENDER_LIST = Symbol(`ssrRenderList`)
|
Symbol(`ssrRenderDynamicAttr`)
|
||||||
export const SSR_INCLUDE_BOOLEAN_ATTR = Symbol(`ssrIncludeBooleanAttr`)
|
export const SSR_RENDER_LIST: unique symbol = Symbol(`ssrRenderList`)
|
||||||
export const SSR_LOOSE_EQUAL = Symbol(`ssrLooseEqual`)
|
export const SSR_INCLUDE_BOOLEAN_ATTR: unique symbol = Symbol(
|
||||||
export const SSR_LOOSE_CONTAIN = Symbol(`ssrLooseContain`)
|
`ssrIncludeBooleanAttr`,
|
||||||
export const SSR_RENDER_DYNAMIC_MODEL = Symbol(`ssrRenderDynamicModel`)
|
)
|
||||||
export const SSR_GET_DYNAMIC_MODEL_PROPS = Symbol(`ssrGetDynamicModelProps`)
|
export const SSR_LOOSE_EQUAL: unique symbol = Symbol(`ssrLooseEqual`)
|
||||||
export const SSR_RENDER_TELEPORT = Symbol(`ssrRenderTeleport`)
|
export const SSR_LOOSE_CONTAIN: unique symbol = Symbol(`ssrLooseContain`)
|
||||||
export const SSR_RENDER_SUSPENSE = Symbol(`ssrRenderSuspense`)
|
export const SSR_RENDER_DYNAMIC_MODEL: unique symbol = Symbol(
|
||||||
export const SSR_GET_DIRECTIVE_PROPS = Symbol(`ssrGetDirectiveProps`)
|
`ssrRenderDynamicModel`,
|
||||||
|
)
|
||||||
|
export const SSR_GET_DYNAMIC_MODEL_PROPS: unique symbol = Symbol(
|
||||||
|
`ssrGetDynamicModelProps`,
|
||||||
|
)
|
||||||
|
export const SSR_RENDER_TELEPORT: unique symbol = Symbol(`ssrRenderTeleport`)
|
||||||
|
export const SSR_RENDER_SUSPENSE: unique symbol = Symbol(`ssrRenderSuspense`)
|
||||||
|
export const SSR_GET_DIRECTIVE_PROPS: unique symbol =
|
||||||
|
Symbol(`ssrGetDirectiveProps`)
|
||||||
|
|
||||||
export const ssrHelpers = {
|
export const ssrHelpers: Record<symbol, string> = {
|
||||||
[SSR_INTERPOLATE]: `ssrInterpolate`,
|
[SSR_INTERPOLATE]: `ssrInterpolate`,
|
||||||
[SSR_RENDER_VNODE]: `ssrRenderVNode`,
|
[SSR_RENDER_VNODE]: `ssrRenderVNode`,
|
||||||
[SSR_RENDER_COMPONENT]: `ssrRenderComponent`,
|
[SSR_RENDER_COMPONENT]: `ssrRenderComponent`,
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import {
|
import {
|
||||||
type BlockStatement,
|
type BlockStatement,
|
||||||
type CallExpression,
|
type CallExpression,
|
||||||
|
type CompilerError,
|
||||||
type CompilerOptions,
|
type CompilerOptions,
|
||||||
ElementTypes,
|
ElementTypes,
|
||||||
type IfStatement,
|
type IfStatement,
|
||||||
|
type JSChildNode,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
type RootNode,
|
type RootNode,
|
||||||
type TemplateChildNode,
|
type TemplateChildNode,
|
||||||
|
@ -33,7 +35,10 @@ import { SSRErrorCodes, createSSRCompilerError } from './errors'
|
||||||
// transform pass to convert the template AST into a fresh JS AST before
|
// transform pass to convert the template AST into a fresh JS AST before
|
||||||
// passing it to codegen.
|
// passing it to codegen.
|
||||||
|
|
||||||
export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
|
export function ssrCodegenTransform(
|
||||||
|
ast: RootNode,
|
||||||
|
options: CompilerOptions,
|
||||||
|
): void {
|
||||||
const context = createSSRTransformContext(ast, options)
|
const context = createSSRTransformContext(ast, options)
|
||||||
|
|
||||||
// inject SFC <style> CSS variables
|
// inject SFC <style> CSS variables
|
||||||
|
@ -70,14 +75,24 @@ export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
|
||||||
ast.helpers = new Set(Array.from(ast.helpers).filter(h => !(h in ssrHelpers)))
|
ast.helpers = new Set(Array.from(ast.helpers).filter(h => !(h in ssrHelpers)))
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SSRTransformContext = ReturnType<typeof createSSRTransformContext>
|
export interface SSRTransformContext {
|
||||||
|
root: RootNode
|
||||||
|
options: CompilerOptions
|
||||||
|
body: (JSChildNode | IfStatement)[]
|
||||||
|
helpers: Set<symbol>
|
||||||
|
withSlotScopeId: boolean
|
||||||
|
onError: (error: CompilerError) => void
|
||||||
|
helper<T extends symbol>(name: T): T
|
||||||
|
pushStringPart(part: TemplateLiteral['elements'][0]): void
|
||||||
|
pushStatement(statement: IfStatement | CallExpression): void
|
||||||
|
}
|
||||||
|
|
||||||
function createSSRTransformContext(
|
function createSSRTransformContext(
|
||||||
root: RootNode,
|
root: RootNode,
|
||||||
options: CompilerOptions,
|
options: CompilerOptions,
|
||||||
helpers: Set<symbol> = new Set(),
|
helpers: Set<symbol> = new Set(),
|
||||||
withSlotScopeId = false,
|
withSlotScopeId = false,
|
||||||
) {
|
): SSRTransformContext {
|
||||||
const body: BlockStatement['body'] = []
|
const body: BlockStatement['body'] = []
|
||||||
let currentString: TemplateLiteral | null = null
|
let currentString: TemplateLiteral | null = null
|
||||||
|
|
||||||
|
@ -96,7 +111,7 @@ function createSSRTransformContext(
|
||||||
helpers.add(name)
|
helpers.add(name)
|
||||||
return name
|
return name
|
||||||
},
|
},
|
||||||
pushStringPart(part: TemplateLiteral['elements'][0]) {
|
pushStringPart(part) {
|
||||||
if (!currentString) {
|
if (!currentString) {
|
||||||
const currentCall = createCallExpression(`_push`)
|
const currentCall = createCallExpression(`_push`)
|
||||||
body.push(currentCall)
|
body.push(currentCall)
|
||||||
|
@ -111,7 +126,7 @@ function createSSRTransformContext(
|
||||||
bufferedElements.push(part)
|
bufferedElements.push(part)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pushStatement(statement: IfStatement | CallExpression) {
|
pushStatement(statement) {
|
||||||
// close current string
|
// close current string
|
||||||
currentString = null
|
currentString = null
|
||||||
body.push(statement)
|
body.push(statement)
|
||||||
|
@ -142,7 +157,7 @@ export function processChildren(
|
||||||
asFragment = false,
|
asFragment = false,
|
||||||
disableNestedFragments = false,
|
disableNestedFragments = false,
|
||||||
disableCommentAsIfAlternate = false,
|
disableCommentAsIfAlternate = false,
|
||||||
) {
|
): void {
|
||||||
if (asFragment) {
|
if (asFragment) {
|
||||||
context.pushStringPart(`<!--[-->`)
|
context.pushStringPart(`<!--[-->`)
|
||||||
}
|
}
|
||||||
|
@ -231,7 +246,7 @@ export function processChildrenAsStatement(
|
||||||
parent: Container,
|
parent: Container,
|
||||||
parentContext: SSRTransformContext,
|
parentContext: SSRTransformContext,
|
||||||
asFragment = false,
|
asFragment = false,
|
||||||
withSlotScopeId = parentContext.withSlotScopeId,
|
withSlotScopeId: boolean = parentContext.withSlotScopeId,
|
||||||
): BlockStatement {
|
): BlockStatement {
|
||||||
const childContext = createChildContext(parentContext, withSlotScopeId)
|
const childContext = createChildContext(parentContext, withSlotScopeId)
|
||||||
processChildren(parent, childContext, asFragment)
|
processChildren(parent, childContext, asFragment)
|
||||||
|
|
|
@ -205,7 +205,7 @@ export function ssrProcessComponent(
|
||||||
node: ComponentNode,
|
node: ComponentNode,
|
||||||
context: SSRTransformContext,
|
context: SSRTransformContext,
|
||||||
parent: { children: TemplateChildNode[] },
|
parent: { children: TemplateChildNode[] },
|
||||||
) {
|
): void {
|
||||||
const component = componentTypeMap.get(node)!
|
const component = componentTypeMap.get(node)!
|
||||||
if (!node.ssrCodegenNode) {
|
if (!node.ssrCodegenNode) {
|
||||||
// this is a built-in component that fell-through.
|
// this is a built-in component that fell-through.
|
||||||
|
@ -268,7 +268,10 @@ export function ssrProcessComponent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const rawOptionsMap = new WeakMap<RootNode, CompilerOptions>()
|
export const rawOptionsMap: WeakMap<RootNode, CompilerOptions> = new WeakMap<
|
||||||
|
RootNode,
|
||||||
|
CompilerOptions
|
||||||
|
>()
|
||||||
|
|
||||||
const [baseNodeTransforms, baseDirectiveTransforms] =
|
const [baseNodeTransforms, baseDirectiveTransforms] =
|
||||||
getBaseTransformPreset(true)
|
getBaseTransformPreset(true)
|
||||||
|
|
|
@ -163,6 +163,25 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
} else if (directives.length && !node.children.length) {
|
||||||
|
const tempId = `_temp${context.temps++}`
|
||||||
|
propsExp.arguments = [
|
||||||
|
createAssignmentExpression(
|
||||||
|
createSimpleExpression(tempId, false),
|
||||||
|
mergedProps,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
rawChildrenMap.set(
|
||||||
|
node,
|
||||||
|
createConditionalExpression(
|
||||||
|
createSimpleExpression(`"textContent" in ${tempId}`, false),
|
||||||
|
createCallExpression(context.helper(SSR_INTERPOLATE), [
|
||||||
|
createSimpleExpression(`${tempId}.textContent`, false),
|
||||||
|
]),
|
||||||
|
createSimpleExpression(`${tempId}.innerHTML ?? ''`, false),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needTagForRuntime) {
|
if (needTagForRuntime) {
|
||||||
|
@ -417,7 +436,7 @@ function findVModel(node: PlainElementNode): DirectiveNode | undefined {
|
||||||
export function ssrProcessElement(
|
export function ssrProcessElement(
|
||||||
node: PlainElementNode,
|
node: PlainElementNode,
|
||||||
context: SSRTransformContext,
|
context: SSRTransformContext,
|
||||||
) {
|
): void {
|
||||||
const isVoidTag = context.options.isVoidTag || NO
|
const isVoidTag = context.options.isVoidTag || NO
|
||||||
const elementsToAdd = node.ssrCodegenNode!.elements
|
const elementsToAdd = node.ssrCodegenNode!.elements
|
||||||
for (let j = 0; j < elementsToAdd.length; j++) {
|
for (let j = 0; j < elementsToAdd.length; j++) {
|
||||||
|
|
|
@ -73,7 +73,7 @@ export const ssrTransformSlotOutlet: NodeTransform = (node, context) => {
|
||||||
export function ssrProcessSlotOutlet(
|
export function ssrProcessSlotOutlet(
|
||||||
node: SlotOutletNode,
|
node: SlotOutletNode,
|
||||||
context: SSRTransformContext,
|
context: SSRTransformContext,
|
||||||
) {
|
): void {
|
||||||
const renderCall = node.ssrCodegenNode!
|
const renderCall = node.ssrCodegenNode!
|
||||||
|
|
||||||
// has fallback content
|
// has fallback content
|
||||||
|
|
|
@ -29,7 +29,7 @@ export function ssrTransformSuspense(
|
||||||
node: ComponentNode,
|
node: ComponentNode,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
) {
|
) {
|
||||||
return () => {
|
return (): void => {
|
||||||
if (node.children.length) {
|
if (node.children.length) {
|
||||||
const wipEntry: WIPEntry = {
|
const wipEntry: WIPEntry = {
|
||||||
slotsExp: null!, // to be immediately set
|
slotsExp: null!, // to be immediately set
|
||||||
|
@ -62,7 +62,7 @@ export function ssrTransformSuspense(
|
||||||
export function ssrProcessSuspense(
|
export function ssrProcessSuspense(
|
||||||
node: ComponentNode,
|
node: ComponentNode,
|
||||||
context: SSRTransformContext,
|
context: SSRTransformContext,
|
||||||
) {
|
): void {
|
||||||
// complete wip slots with ssr code
|
// complete wip slots with ssr code
|
||||||
const wipEntry = wipMap.get(node)
|
const wipEntry = wipMap.get(node)
|
||||||
if (!wipEntry) {
|
if (!wipEntry) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { SSR_RENDER_TELEPORT } from '../runtimeHelpers'
|
||||||
export function ssrProcessTeleport(
|
export function ssrProcessTeleport(
|
||||||
node: ComponentNode,
|
node: ComponentNode,
|
||||||
context: SSRTransformContext,
|
context: SSRTransformContext,
|
||||||
) {
|
): void {
|
||||||
const targetProp = findProp(node, 'to')
|
const targetProp = findProp(node, 'to')
|
||||||
if (!targetProp) {
|
if (!targetProp) {
|
||||||
context.onError(
|
context.onError(
|
||||||
|
|
|
@ -15,7 +15,7 @@ export function ssrTransformTransition(
|
||||||
node: ComponentNode,
|
node: ComponentNode,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
) {
|
) {
|
||||||
return () => {
|
return (): void => {
|
||||||
const appear = findProp(node, 'appear', false, true)
|
const appear = findProp(node, 'appear', false, true)
|
||||||
wipMap.set(node, !!appear)
|
wipMap.set(node, !!appear)
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ export function ssrTransformTransition(
|
||||||
export function ssrProcessTransition(
|
export function ssrProcessTransition(
|
||||||
node: ComponentNode,
|
node: ComponentNode,
|
||||||
context: SSRTransformContext,
|
context: SSRTransformContext,
|
||||||
) {
|
): void {
|
||||||
// #5351: filter out comment children inside transition
|
// #5351: filter out comment children inside transition
|
||||||
node.children = node.children.filter(c => c.type !== NodeTypes.COMMENT)
|
node.children = node.children.filter(c => c.type !== NodeTypes.COMMENT)
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ export function ssrTransformTransitionGroup(
|
||||||
node: ComponentNode,
|
node: ComponentNode,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
) {
|
) {
|
||||||
return () => {
|
return (): void => {
|
||||||
const tag = findProp(node, 'tag')
|
const tag = findProp(node, 'tag')
|
||||||
if (tag) {
|
if (tag) {
|
||||||
const otherProps = node.props.filter(p => p !== tag)
|
const otherProps = node.props.filter(p => p !== tag)
|
||||||
|
@ -60,7 +60,7 @@ export function ssrTransformTransitionGroup(
|
||||||
export function ssrProcessTransitionGroup(
|
export function ssrProcessTransitionGroup(
|
||||||
node: ComponentNode,
|
node: ComponentNode,
|
||||||
context: SSRTransformContext,
|
context: SSRTransformContext,
|
||||||
) {
|
): void {
|
||||||
const entry = wipMap.get(node)
|
const entry = wipMap.get(node)
|
||||||
if (entry) {
|
if (entry) {
|
||||||
const { tag, propsExp, scopeId } = entry
|
const { tag, propsExp, scopeId } = entry
|
||||||
|
@ -108,7 +108,7 @@ export function ssrProcessTransitionGroup(
|
||||||
context.pushStringPart(` ${scopeId}`)
|
context.pushStringPart(` ${scopeId}`)
|
||||||
}
|
}
|
||||||
context.pushStringPart(`>`)
|
context.pushStringPart(`>`)
|
||||||
processChildren(node, context, false, true)
|
processChildren(node, context, false, true, true)
|
||||||
context.pushStringPart(`</${tag.value!.content}>`)
|
context.pushStringPart(`</${tag.value!.content}>`)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {
|
import {
|
||||||
type ForNode,
|
type ForNode,
|
||||||
|
type NodeTransform,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
createCallExpression,
|
createCallExpression,
|
||||||
createForLoopParams,
|
createForLoopParams,
|
||||||
|
@ -14,10 +15,8 @@ import {
|
||||||
import { SSR_RENDER_LIST } from '../runtimeHelpers'
|
import { SSR_RENDER_LIST } from '../runtimeHelpers'
|
||||||
|
|
||||||
// Plugin for the first transform pass, which simply constructs the AST node
|
// Plugin for the first transform pass, which simply constructs the AST node
|
||||||
export const ssrTransformFor = createStructuralDirectiveTransform(
|
export const ssrTransformFor: NodeTransform =
|
||||||
'for',
|
createStructuralDirectiveTransform('for', processFor)
|
||||||
processFor,
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is called during the 2nd transform pass to construct the SSR-specific
|
// This is called during the 2nd transform pass to construct the SSR-specific
|
||||||
// codegen nodes.
|
// codegen nodes.
|
||||||
|
@ -25,7 +24,7 @@ export function ssrProcessFor(
|
||||||
node: ForNode,
|
node: ForNode,
|
||||||
context: SSRTransformContext,
|
context: SSRTransformContext,
|
||||||
disableNestedFragments = false,
|
disableNestedFragments = false,
|
||||||
) {
|
): void {
|
||||||
const needFragmentWrapper =
|
const needFragmentWrapper =
|
||||||
!disableNestedFragments &&
|
!disableNestedFragments &&
|
||||||
(node.children.length !== 1 || node.children[0].type !== NodeTypes.ELEMENT)
|
(node.children.length !== 1 || node.children[0].type !== NodeTypes.ELEMENT)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {
|
||||||
type BlockStatement,
|
type BlockStatement,
|
||||||
type IfBranchNode,
|
type IfBranchNode,
|
||||||
type IfNode,
|
type IfNode,
|
||||||
|
type NodeTransform,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
createBlockStatement,
|
createBlockStatement,
|
||||||
createCallExpression,
|
createCallExpression,
|
||||||
|
@ -15,7 +16,7 @@ import {
|
||||||
} from '../ssrCodegenTransform'
|
} from '../ssrCodegenTransform'
|
||||||
|
|
||||||
// Plugin for the first transform pass, which simply constructs the AST node
|
// Plugin for the first transform pass, which simply constructs the AST node
|
||||||
export const ssrTransformIf = createStructuralDirectiveTransform(
|
export const ssrTransformIf: NodeTransform = createStructuralDirectiveTransform(
|
||||||
/^(if|else|else-if)$/,
|
/^(if|else|else-if)$/,
|
||||||
processIf,
|
processIf,
|
||||||
)
|
)
|
||||||
|
@ -27,7 +28,7 @@ export function ssrProcessIf(
|
||||||
context: SSRTransformContext,
|
context: SSRTransformContext,
|
||||||
disableNestedFragments = false,
|
disableNestedFragments = false,
|
||||||
disableCommentAsIfAlternate = false,
|
disableCommentAsIfAlternate = false,
|
||||||
) {
|
): void {
|
||||||
const [rootBranch] = node.branches
|
const [rootBranch] = node.branches
|
||||||
const ifStatement = createIfStatement(
|
const ifStatement = createIfStatement(
|
||||||
rootBranch.condition!,
|
rootBranch.condition!,
|
||||||
|
|
|
@ -1593,6 +1593,7 @@ describe('expose typing', () => {
|
||||||
import type {
|
import type {
|
||||||
AllowedComponentProps,
|
AllowedComponentProps,
|
||||||
ComponentCustomProps,
|
ComponentCustomProps,
|
||||||
|
ComponentInstance,
|
||||||
ComponentOptionsMixin,
|
ComponentOptionsMixin,
|
||||||
DefineComponent,
|
DefineComponent,
|
||||||
Directive,
|
Directive,
|
||||||
|
@ -1756,6 +1757,24 @@ describe('__typeEmits backdoor, call signature syntax', () => {
|
||||||
c.$emit('update', 123)
|
c.$emit('update', 123)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('__typeRefs backdoor, object syntax', () => {
|
||||||
|
type Refs = {
|
||||||
|
foo: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const Parent = defineComponent({
|
||||||
|
__typeRefs: {} as { child: ComponentInstance<typeof Child> },
|
||||||
|
})
|
||||||
|
const Child = defineComponent({
|
||||||
|
__typeRefs: {} as Refs,
|
||||||
|
})
|
||||||
|
const c = new Parent()
|
||||||
|
const refs = c.$refs
|
||||||
|
|
||||||
|
expectType<ComponentInstance<typeof Child>>(refs.child)
|
||||||
|
expectType<number>(refs.child.$refs.foo)
|
||||||
|
})
|
||||||
|
|
||||||
defineComponent({
|
defineComponent({
|
||||||
props: {
|
props: {
|
||||||
foo: [String, null],
|
foo: [String, null],
|
||||||
|
|
|
@ -68,7 +68,7 @@ describe('inject', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('defineCustomElement using defineComponent return type', () => {
|
describe('defineCustomElement using defineComponent return type', () => {
|
||||||
test('with emits', () => {
|
test('with object emits', () => {
|
||||||
const Comp1Vue = defineComponent({
|
const Comp1Vue = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
a: String,
|
a: String,
|
||||||
|
@ -80,6 +80,23 @@ describe('defineCustomElement using defineComponent return type', () => {
|
||||||
const Comp = defineCustomElement(Comp1Vue)
|
const Comp = defineCustomElement(Comp1Vue)
|
||||||
expectType<VueElementConstructor>(Comp)
|
expectType<VueElementConstructor>(Comp)
|
||||||
|
|
||||||
expectType<string | undefined>(new Comp().a)
|
const instance = new Comp()
|
||||||
|
expectType<string | undefined>(instance.a)
|
||||||
|
instance.a = ''
|
||||||
|
})
|
||||||
|
|
||||||
|
test('with array emits', () => {
|
||||||
|
const Comp1Vue = defineComponent({
|
||||||
|
props: {
|
||||||
|
a: Number,
|
||||||
|
},
|
||||||
|
emits: ['click'],
|
||||||
|
})
|
||||||
|
const Comp = defineCustomElement(Comp1Vue)
|
||||||
|
expectType<VueElementConstructor>(Comp)
|
||||||
|
|
||||||
|
const instance = new Comp()
|
||||||
|
expectType<number | undefined>(instance.a)
|
||||||
|
instance.a = 42
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {
|
||||||
type InjectionKey,
|
type InjectionKey,
|
||||||
type Ref,
|
type Ref,
|
||||||
createApp,
|
createApp,
|
||||||
|
defineComponent,
|
||||||
inject,
|
inject,
|
||||||
provide,
|
provide,
|
||||||
ref,
|
ref,
|
||||||
|
@ -52,3 +53,9 @@ provide<Cube>(123, { size: 'foo' })
|
||||||
const app = createApp({})
|
const app = createApp({})
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
app.provide(injectionKeyRef, ref({}))
|
app.provide(injectionKeyRef, ref({}))
|
||||||
|
|
||||||
|
defineComponent({
|
||||||
|
provide: {
|
||||||
|
[injectionKeyRef]: { size: 'foo' },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
type Ref,
|
type Ref,
|
||||||
type ShallowRef,
|
type ShallowRef,
|
||||||
type ToRefs,
|
type ToRefs,
|
||||||
|
type WritableComputedRef,
|
||||||
computed,
|
computed,
|
||||||
isRef,
|
isRef,
|
||||||
proxyRefs,
|
proxyRefs,
|
||||||
|
@ -181,6 +182,31 @@ describe('allow getter and setter types to be unrelated', <T>() => {
|
||||||
const d = {} as T
|
const d = {} as T
|
||||||
const e = ref(d)
|
const e = ref(d)
|
||||||
e.value = d
|
e.value = d
|
||||||
|
|
||||||
|
const f = ref(ref(0))
|
||||||
|
expectType<number>(f.value)
|
||||||
|
// @ts-expect-error
|
||||||
|
f.value = ref(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
// computed
|
||||||
|
describe('allow computed getter and setter types to be unrelated', () => {
|
||||||
|
const obj = ref({
|
||||||
|
name: 'foo',
|
||||||
|
})
|
||||||
|
|
||||||
|
const c = computed({
|
||||||
|
get() {
|
||||||
|
return JSON.stringify(obj.value)
|
||||||
|
},
|
||||||
|
set(val: typeof obj.value) {
|
||||||
|
obj.value = val
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
c.value = { name: 'bar' } // object
|
||||||
|
|
||||||
|
expectType<string>(c.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
// shallowRef
|
// shallowRef
|
||||||
|
@ -465,8 +491,21 @@ describe('toRef <-> toValue', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// unref
|
// unref
|
||||||
declare const text: ShallowRef<string> | ComputedRef<string> | MaybeRef<string>
|
// #8747
|
||||||
expectType<string>(unref(text))
|
declare const unref1: number | Ref<number> | ComputedRef<number>
|
||||||
|
expectType<number>(unref(unref1))
|
||||||
|
|
||||||
|
// #11356
|
||||||
|
declare const unref2:
|
||||||
|
| MaybeRef<string>
|
||||||
|
| ShallowRef<string>
|
||||||
|
| ComputedRef<string>
|
||||||
|
| WritableComputedRef<string>
|
||||||
|
expectType<string>(unref(unref2))
|
||||||
|
|
||||||
|
// toValue
|
||||||
|
expectType<number>(toValue(unref1))
|
||||||
|
expectType<string>(toValue(unref2))
|
||||||
|
|
||||||
// useTemplateRef
|
// useTemplateRef
|
||||||
const tRef = useTemplateRef('foo')
|
const tRef = useTemplateRef('foo')
|
||||||
|
|
|
@ -43,7 +43,8 @@ describe('defineProps w/ generics', () => {
|
||||||
test()
|
test()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('defineProps w/ type declaration + withDefaults', () => {
|
describe('defineProps w/ type declaration + withDefaults', <T extends
|
||||||
|
string>() => {
|
||||||
const res = withDefaults(
|
const res = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
number?: number
|
number?: number
|
||||||
|
@ -56,6 +57,7 @@ describe('defineProps w/ type declaration + withDefaults', () => {
|
||||||
z?: string
|
z?: string
|
||||||
bool?: boolean
|
bool?: boolean
|
||||||
boolAndUndefined: boolean | undefined
|
boolAndUndefined: boolean | undefined
|
||||||
|
foo?: T
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
number: 123,
|
number: 123,
|
||||||
|
@ -65,6 +67,7 @@ describe('defineProps w/ type declaration + withDefaults', () => {
|
||||||
genStr: () => '',
|
genStr: () => '',
|
||||||
y: undefined,
|
y: undefined,
|
||||||
z: 'string',
|
z: 'string',
|
||||||
|
foo: '' as any,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -81,6 +84,7 @@ describe('defineProps w/ type declaration + withDefaults', () => {
|
||||||
expectType<string | undefined>(res.x)
|
expectType<string | undefined>(res.x)
|
||||||
expectType<string | undefined>(res.y)
|
expectType<string | undefined>(res.y)
|
||||||
expectType<string>(res.z)
|
expectType<string>(res.z)
|
||||||
|
expectType<T>(res.foo)
|
||||||
|
|
||||||
expectType<boolean>(res.bool)
|
expectType<boolean>(res.bool)
|
||||||
expectType<boolean>(res.boolAndUndefined)
|
expectType<boolean>(res.boolAndUndefined)
|
||||||
|
@ -138,6 +142,31 @@ describe('defineProps w/ object union + withDefaults', () => {
|
||||||
>(props)
|
>(props)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('defineProps w/ generic discriminate union + withDefaults', () => {
|
||||||
|
interface B {
|
||||||
|
b?: string
|
||||||
|
}
|
||||||
|
interface S<T> extends B {
|
||||||
|
mode: 'single'
|
||||||
|
v: T
|
||||||
|
}
|
||||||
|
interface M<T> extends B {
|
||||||
|
mode: 'multiple'
|
||||||
|
v: T[]
|
||||||
|
}
|
||||||
|
type Props = S<string> | M<string>
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
b: 'b',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (props.mode === 'single') {
|
||||||
|
expectType<string>(props.v)
|
||||||
|
}
|
||||||
|
if (props.mode === 'multiple') {
|
||||||
|
expectType<string[]>(props.v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
describe('defineProps w/ generic type declaration + withDefaults', <T extends
|
describe('defineProps w/ generic type declaration + withDefaults', <T extends
|
||||||
number, TA extends {
|
number, TA extends {
|
||||||
a: string
|
a: string
|
||||||
|
|
|
@ -1282,4 +1282,48 @@ describe('reactivity/effect', () => {
|
||||||
).not.toHaveBeenWarned()
|
).not.toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should pause/resume effect', () => {
|
||||||
|
const obj = reactive({ foo: 1 })
|
||||||
|
const fnSpy = vi.fn(() => obj.foo)
|
||||||
|
const runner = effect(fnSpy)
|
||||||
|
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(obj.foo).toBe(1)
|
||||||
|
|
||||||
|
runner.effect.pause()
|
||||||
|
obj.foo++
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(obj.foo).toBe(2)
|
||||||
|
|
||||||
|
runner.effect.resume()
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(2)
|
||||||
|
expect(obj.foo).toBe(2)
|
||||||
|
|
||||||
|
obj.foo++
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(3)
|
||||||
|
expect(obj.foo).toBe(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should be executed once immediately when resume is called', () => {
|
||||||
|
const obj = reactive({ foo: 1 })
|
||||||
|
const fnSpy = vi.fn(() => obj.foo)
|
||||||
|
const runner = effect(fnSpy)
|
||||||
|
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(obj.foo).toBe(1)
|
||||||
|
|
||||||
|
runner.effect.pause()
|
||||||
|
obj.foo++
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(obj.foo).toBe(2)
|
||||||
|
|
||||||
|
obj.foo++
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(obj.foo).toBe(3)
|
||||||
|
|
||||||
|
runner.effect.resume()
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(2)
|
||||||
|
expect(obj.foo).toBe(3)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -295,4 +295,31 @@ describe('reactivity/effect/scope', () => {
|
||||||
expect(getCurrentScope()).toBe(parentScope)
|
expect(getCurrentScope()).toBe(parentScope)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should pause/resume EffectScope', async () => {
|
||||||
|
const counter = reactive({ num: 0 })
|
||||||
|
const fnSpy = vi.fn(() => counter.num)
|
||||||
|
const scope = new EffectScope()
|
||||||
|
scope.run(() => {
|
||||||
|
effect(fnSpy)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
counter.num++
|
||||||
|
await nextTick()
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
scope.pause()
|
||||||
|
counter.num++
|
||||||
|
await nextTick()
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
counter.num++
|
||||||
|
await nextTick()
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
scope.resume()
|
||||||
|
expect(fnSpy).toHaveBeenCalledTimes(3)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -478,4 +478,38 @@ describe('reactivity/ref', () => {
|
||||||
expect(toValue(c)).toBe(3)
|
expect(toValue(c)).toBe(3)
|
||||||
expect(toValue(d)).toBe(4)
|
expect(toValue(d)).toBe(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('ref w/ customRef w/ getterRef w/ objectRef should store value cache', () => {
|
||||||
|
const refValue = ref(1)
|
||||||
|
// @ts-expect-error private field
|
||||||
|
expect(refValue._value).toBe(1)
|
||||||
|
|
||||||
|
let customRefValueCache = 0
|
||||||
|
const customRefValue = customRef((track, trigger) => {
|
||||||
|
return {
|
||||||
|
get() {
|
||||||
|
track()
|
||||||
|
return customRefValueCache
|
||||||
|
},
|
||||||
|
set(value: number) {
|
||||||
|
customRefValueCache = value
|
||||||
|
trigger()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
customRefValue.value
|
||||||
|
|
||||||
|
// @ts-expect-error internal field
|
||||||
|
expect(customRefValue._value).toBe(0)
|
||||||
|
|
||||||
|
const getterRefValue = toRef(() => 1)
|
||||||
|
getterRefValue.value
|
||||||
|
// @ts-expect-error internal field
|
||||||
|
expect(getterRefValue._value).toBe(1)
|
||||||
|
|
||||||
|
const objectRefValue = toRef({ value: 1 }, 'value')
|
||||||
|
objectRefValue.value
|
||||||
|
// @ts-expect-error internal field
|
||||||
|
expect(objectRefValue._value).toBe(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/reactivity",
|
"name": "@vue/reactivity",
|
||||||
"version": "3.5.0-alpha.5",
|
"version": "3.5.0-beta.1",
|
||||||
"description": "@vue/reactivity",
|
"description": "@vue/reactivity",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "dist/reactivity.esm-bundler.js",
|
"module": "dist/reactivity.esm-bundler.js",
|
||||||
|
|
|
@ -52,7 +52,7 @@ class BaseReactiveHandler implements ProxyHandler<Target> {
|
||||||
protected readonly _isShallow = false,
|
protected readonly _isShallow = false,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get(target: Target, key: string | symbol, receiver: object) {
|
get(target: Target, key: string | symbol, receiver: object): any {
|
||||||
const isReadonly = this._isReadonly,
|
const isReadonly = this._isReadonly,
|
||||||
isShallow = this._isShallow
|
isShallow = this._isShallow
|
||||||
if (key === ReactiveFlags.IS_REACTIVE) {
|
if (key === ReactiveFlags.IS_REACTIVE) {
|
||||||
|
@ -73,7 +73,7 @@ class BaseReactiveHandler implements ProxyHandler<Target> {
|
||||||
: reactiveMap
|
: reactiveMap
|
||||||
).get(target) ||
|
).get(target) ||
|
||||||
// receiver is not the reactive proxy, but has the same prototype
|
// receiver is not the reactive proxy, but has the same prototype
|
||||||
// this means the reciever is a user proxy of the reactive proxy
|
// this means the receiver is a user proxy of the reactive proxy
|
||||||
Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)
|
Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)
|
||||||
) {
|
) {
|
||||||
return target
|
return target
|
||||||
|
@ -240,12 +240,11 @@ export const mutableHandlers: ProxyHandler<object> =
|
||||||
export const readonlyHandlers: ProxyHandler<object> =
|
export const readonlyHandlers: ProxyHandler<object> =
|
||||||
/*#__PURE__*/ new ReadonlyReactiveHandler()
|
/*#__PURE__*/ new ReadonlyReactiveHandler()
|
||||||
|
|
||||||
export const shallowReactiveHandlers = /*#__PURE__*/ new MutableReactiveHandler(
|
export const shallowReactiveHandlers: MutableReactiveHandler =
|
||||||
true,
|
/*#__PURE__*/ new MutableReactiveHandler(true)
|
||||||
)
|
|
||||||
|
|
||||||
// Props handlers are special in the sense that it should not unwrap top-level
|
// Props handlers are special in the sense that it should not unwrap top-level
|
||||||
// refs (in order to allow refs to be explicitly passed down), but should
|
// refs (in order to allow refs to be explicitly passed down), but should
|
||||||
// retain the reactivity of the normal readonly object.
|
// retain the reactivity of the normal readonly object.
|
||||||
export const shallowReadonlyHandlers =
|
export const shallowReadonlyHandlers: ReadonlyReactiveHandler =
|
||||||
/*#__PURE__*/ new ReadonlyReactiveHandler(true)
|
/*#__PURE__*/ new ReadonlyReactiveHandler(true)
|
||||||
|
|
|
@ -85,7 +85,7 @@ let activeWatcher: ReactiveEffect | undefined = undefined
|
||||||
/**
|
/**
|
||||||
* Returns the current active effect if there is one.
|
* Returns the current active effect if there is one.
|
||||||
*/
|
*/
|
||||||
export function getCurrentWatcher() {
|
export function getCurrentWatcher(): ReactiveEffect<any> | undefined {
|
||||||
return activeWatcher
|
return activeWatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,10 @@ export function getCurrentWatcher() {
|
||||||
*
|
*
|
||||||
* @param cleanupFn - The callback function to attach to the effect's cleanup.
|
* @param cleanupFn - The callback function to attach to the effect's cleanup.
|
||||||
*/
|
*/
|
||||||
export function onWatcherCleanup(cleanupFn: () => void, failSilently = false) {
|
export function onWatcherCleanup(
|
||||||
|
cleanupFn: () => void,
|
||||||
|
failSilently = false,
|
||||||
|
): void {
|
||||||
if (activeWatcher) {
|
if (activeWatcher) {
|
||||||
const cleanups =
|
const cleanups =
|
||||||
cleanupMap.get(activeWatcher) ||
|
cleanupMap.get(activeWatcher) ||
|
||||||
|
@ -133,11 +136,15 @@ export function baseWatch(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const reactiveGetter = (source: object) =>
|
const reactiveGetter = (source: object) => {
|
||||||
deep === true
|
// traverse will happen in wrapped getter below
|
||||||
? source // traverse will happen in wrapped getter below
|
if (deep) return source
|
||||||
: // for deep: false, only traverse root-level properties
|
// for `deep: false | 0` or shallow reactive, only traverse root-level properties
|
||||||
traverse(source, deep === false ? 1 : undefined)
|
if (isShallow(source) || deep === false || deep === 0)
|
||||||
|
return traverse(source, 1)
|
||||||
|
// for `deep: undefined` on a reactive object, deeply traverse all properties
|
||||||
|
return traverse(source)
|
||||||
|
}
|
||||||
|
|
||||||
let effect: ReactiveEffect
|
let effect: ReactiveEffect
|
||||||
let getter: () => any
|
let getter: () => any
|
||||||
|
@ -207,7 +214,8 @@ export function baseWatch(
|
||||||
|
|
||||||
if (cb && deep) {
|
if (cb && deep) {
|
||||||
const baseGetter = getter
|
const baseGetter = getter
|
||||||
getter = () => traverse(baseGetter())
|
const depth = deep === true ? Infinity : deep
|
||||||
|
getter = () => traverse(baseGetter(), depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (once) {
|
if (once) {
|
||||||
|
@ -322,9 +330,9 @@ export function baseWatch(
|
||||||
|
|
||||||
export function traverse(
|
export function traverse(
|
||||||
value: unknown,
|
value: unknown,
|
||||||
depth = Infinity,
|
depth: number = Infinity,
|
||||||
seen?: Set<unknown>,
|
seen?: Set<unknown>,
|
||||||
) {
|
): unknown {
|
||||||
if (depth <= 0 || !isObject(value) || (value as any)[ReactiveFlags.SKIP]) {
|
if (depth <= 0 || !isObject(value) || (value as any)[ReactiveFlags.SKIP]) {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ export interface ComputedRef<T = any> extends WritableComputedRef<T> {
|
||||||
[ComputedRefSymbol]: true
|
[ComputedRefSymbol]: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WritableComputedRef<T> extends Ref<T> {
|
export interface WritableComputedRef<T, S = T> extends Ref<T, S> {
|
||||||
/**
|
/**
|
||||||
* @deprecated computed no longer uses effect
|
* @deprecated computed no longer uses effect
|
||||||
*/
|
*/
|
||||||
|
@ -30,9 +30,9 @@ export interface WritableComputedRef<T> extends Ref<T> {
|
||||||
export type ComputedGetter<T> = (oldValue?: T) => T
|
export type ComputedGetter<T> = (oldValue?: T) => T
|
||||||
export type ComputedSetter<T> = (newValue: T) => void
|
export type ComputedSetter<T> = (newValue: T) => void
|
||||||
|
|
||||||
export interface WritableComputedOptions<T> {
|
export interface WritableComputedOptions<T, S = T> {
|
||||||
get: ComputedGetter<T>
|
get: ComputedGetter<T>
|
||||||
set: ComputedSetter<T>
|
set: ComputedSetter<S>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,15 +47,17 @@ export class ComputedRefImpl<T = any> implements Subscriber {
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
readonly dep = new Dep(this)
|
readonly dep: Dep = new Dep(this)
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
readonly [ReactiveFlags.IS_REF] = true
|
readonly __v_isRef = true
|
||||||
|
// TODO isolatedDeclarations ReactiveFlags.IS_REF
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
readonly [ReactiveFlags.IS_READONLY]: boolean
|
readonly __v_isReadonly: boolean
|
||||||
|
// TODO isolatedDeclarations ReactiveFlags.IS_READONLY
|
||||||
// A computed is also a subscriber that tracks other deps
|
// A computed is also a subscriber that tracks other deps
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -68,17 +70,17 @@ export class ComputedRefImpl<T = any> implements Subscriber {
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
flags = EffectFlags.DIRTY
|
flags: EffectFlags = EffectFlags.DIRTY
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
globalVersion = globalVersion - 1
|
globalVersion: number = globalVersion - 1
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
isSSR: boolean
|
isSSR: boolean
|
||||||
// for backwards compat
|
// for backwards compat
|
||||||
effect = this
|
effect: this = this
|
||||||
|
|
||||||
// dev only
|
// dev only
|
||||||
onTrack?: (event: DebuggerEvent) => void
|
onTrack?: (event: DebuggerEvent) => void
|
||||||
|
@ -103,7 +105,7 @@ export class ComputedRefImpl<T = any> implements Subscriber {
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
notify() {
|
notify(): void {
|
||||||
// avoid infinite self recursion
|
// avoid infinite self recursion
|
||||||
if (activeSub !== this) {
|
if (activeSub !== this) {
|
||||||
this.flags |= EffectFlags.DIRTY
|
this.flags |= EffectFlags.DIRTY
|
||||||
|
@ -113,7 +115,7 @@ export class ComputedRefImpl<T = any> implements Subscriber {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value(): T {
|
||||||
const link = __DEV__
|
const link = __DEV__
|
||||||
? this.dep.track({
|
? this.dep.track({
|
||||||
target: this,
|
target: this,
|
||||||
|
@ -175,10 +177,10 @@ export function computed<T>(
|
||||||
getter: ComputedGetter<T>,
|
getter: ComputedGetter<T>,
|
||||||
debugOptions?: DebuggerOptions,
|
debugOptions?: DebuggerOptions,
|
||||||
): ComputedRef<T>
|
): ComputedRef<T>
|
||||||
export function computed<T>(
|
export function computed<T, S = T>(
|
||||||
options: WritableComputedOptions<T>,
|
options: WritableComputedOptions<T, S>,
|
||||||
debugOptions?: DebuggerOptions,
|
debugOptions?: DebuggerOptions,
|
||||||
): WritableComputedRef<T>
|
): WritableComputedRef<T, S>
|
||||||
export function computed<T>(
|
export function computed<T>(
|
||||||
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
|
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
|
||||||
debugOptions?: DebuggerOptions,
|
debugOptions?: DebuggerOptions,
|
||||||
|
|
|
@ -39,7 +39,7 @@ export class Dep {
|
||||||
*/
|
*/
|
||||||
subsHead?: Link
|
subsHead?: Link
|
||||||
|
|
||||||
constructor(public computed?: ComputedRefImpl) {
|
constructor(public computed?: ComputedRefImpl | undefined) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
this.subsHead = undefined
|
this.subsHead = undefined
|
||||||
}
|
}
|
||||||
|
@ -115,13 +115,13 @@ export class Dep {
|
||||||
return link
|
return link
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger(debugInfo?: DebuggerEventExtraInfo) {
|
trigger(debugInfo?: DebuggerEventExtraInfo): void {
|
||||||
this.version++
|
this.version++
|
||||||
globalVersion++
|
globalVersion++
|
||||||
this.notify(debugInfo)
|
this.notify(debugInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
notify(debugInfo?: DebuggerEventExtraInfo) {
|
notify(debugInfo?: DebuggerEventExtraInfo): void {
|
||||||
startBatch()
|
startBatch()
|
||||||
try {
|
try {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
|
@ -185,9 +185,15 @@ function addSub(link: Link) {
|
||||||
type KeyToDepMap = Map<any, Dep>
|
type KeyToDepMap = Map<any, Dep>
|
||||||
const targetMap = new WeakMap<object, KeyToDepMap>()
|
const targetMap = new WeakMap<object, KeyToDepMap>()
|
||||||
|
|
||||||
export const ITERATE_KEY = Symbol(__DEV__ ? 'Object iterate' : '')
|
export const ITERATE_KEY: unique symbol = Symbol(
|
||||||
export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map keys iterate' : '')
|
__DEV__ ? 'Object iterate' : '',
|
||||||
export const ARRAY_ITERATE_KEY = Symbol(__DEV__ ? 'Array iterate' : '')
|
)
|
||||||
|
export const MAP_KEY_ITERATE_KEY: unique symbol = Symbol(
|
||||||
|
__DEV__ ? 'Map keys iterate' : '',
|
||||||
|
)
|
||||||
|
export const ARRAY_ITERATE_KEY: unique symbol = Symbol(
|
||||||
|
__DEV__ ? 'Array iterate' : '',
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks access to a reactive property.
|
* Tracks access to a reactive property.
|
||||||
|
@ -199,7 +205,7 @@ export const ARRAY_ITERATE_KEY = Symbol(__DEV__ ? 'Array iterate' : '')
|
||||||
* @param type - Defines the type of access to the reactive property.
|
* @param type - Defines the type of access to the reactive property.
|
||||||
* @param key - Identifier of the reactive property to track.
|
* @param key - Identifier of the reactive property to track.
|
||||||
*/
|
*/
|
||||||
export function track(target: object, type: TrackOpTypes, key: unknown) {
|
export function track(target: object, type: TrackOpTypes, key: unknown): void {
|
||||||
if (shouldTrack && activeSub) {
|
if (shouldTrack && activeSub) {
|
||||||
let depsMap = targetMap.get(target)
|
let depsMap = targetMap.get(target)
|
||||||
if (!depsMap) {
|
if (!depsMap) {
|
||||||
|
@ -236,7 +242,7 @@ export function trigger(
|
||||||
newValue?: unknown,
|
newValue?: unknown,
|
||||||
oldValue?: unknown,
|
oldValue?: unknown,
|
||||||
oldTarget?: Map<unknown, unknown> | Set<unknown>,
|
oldTarget?: Map<unknown, unknown> | Set<unknown>,
|
||||||
) {
|
): void {
|
||||||
const depsMap = targetMap.get(target)
|
const depsMap = targetMap.get(target)
|
||||||
if (!depsMap) {
|
if (!depsMap) {
|
||||||
// never been tracked
|
// never been tracked
|
||||||
|
@ -328,7 +334,10 @@ export function trigger(
|
||||||
/**
|
/**
|
||||||
* Test only
|
* Test only
|
||||||
*/
|
*/
|
||||||
export function getDepFromReactive(object: any, key: string | number | symbol) {
|
export function getDepFromReactive(
|
||||||
|
object: any,
|
||||||
|
key: string | number | symbol,
|
||||||
|
): Dep | undefined {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
return targetMap.get(object)?.get(key)
|
return targetMap.get(object)?.get(key)
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ export enum EffectFlags {
|
||||||
DIRTY = 1 << 4,
|
DIRTY = 1 << 4,
|
||||||
ALLOW_RECURSE = 1 << 5,
|
ALLOW_RECURSE = 1 << 5,
|
||||||
NO_BATCH = 1 << 6,
|
NO_BATCH = 1 << 6,
|
||||||
|
PAUSED = 1 << 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,6 +108,8 @@ export interface Link {
|
||||||
prevActiveLink?: Link
|
prevActiveLink?: Link
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pausedQueueEffects = new WeakSet<ReactiveEffect>()
|
||||||
|
|
||||||
export class ReactiveEffect<T = any>
|
export class ReactiveEffect<T = any>
|
||||||
implements Subscriber, ReactiveEffectOptions
|
implements Subscriber, ReactiveEffectOptions
|
||||||
{
|
{
|
||||||
|
@ -142,10 +145,24 @@ export class ReactiveEffect<T = any>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pause(): void {
|
||||||
|
this.flags |= EffectFlags.PAUSED
|
||||||
|
}
|
||||||
|
|
||||||
|
resume(): void {
|
||||||
|
if (this.flags & EffectFlags.PAUSED) {
|
||||||
|
this.flags &= ~EffectFlags.PAUSED
|
||||||
|
if (pausedQueueEffects.has(this)) {
|
||||||
|
pausedQueueEffects.delete(this)
|
||||||
|
this.trigger()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
notify() {
|
notify(): void {
|
||||||
if (
|
if (
|
||||||
this.flags & EffectFlags.RUNNING &&
|
this.flags & EffectFlags.RUNNING &&
|
||||||
!(this.flags & EffectFlags.ALLOW_RECURSE)
|
!(this.flags & EffectFlags.ALLOW_RECURSE)
|
||||||
|
@ -162,7 +179,7 @@ export class ReactiveEffect<T = any>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run() {
|
run(): T {
|
||||||
// TODO cleanupEffect
|
// TODO cleanupEffect
|
||||||
|
|
||||||
if (!(this.flags & EffectFlags.ACTIVE)) {
|
if (!(this.flags & EffectFlags.ACTIVE)) {
|
||||||
|
@ -194,7 +211,7 @@ export class ReactiveEffect<T = any>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop(): void {
|
||||||
if (this.flags & EffectFlags.ACTIVE) {
|
if (this.flags & EffectFlags.ACTIVE) {
|
||||||
for (let link = this.deps; link; link = link.nextDep) {
|
for (let link = this.deps; link; link = link.nextDep) {
|
||||||
removeSub(link)
|
removeSub(link)
|
||||||
|
@ -206,8 +223,10 @@ export class ReactiveEffect<T = any>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger() {
|
trigger(): void {
|
||||||
if (this.scheduler) {
|
if (this.flags & EffectFlags.PAUSED) {
|
||||||
|
pausedQueueEffects.add(this)
|
||||||
|
} else if (this.scheduler) {
|
||||||
this.scheduler()
|
this.scheduler()
|
||||||
} else {
|
} else {
|
||||||
this.runIfDirty()
|
this.runIfDirty()
|
||||||
|
@ -217,13 +236,13 @@ export class ReactiveEffect<T = any>
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
runIfDirty() {
|
runIfDirty(): void {
|
||||||
if (isDirty(this)) {
|
if (isDirty(this)) {
|
||||||
this.run()
|
this.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get dirty() {
|
get dirty(): boolean {
|
||||||
return isDirty(this)
|
return isDirty(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,7 +253,7 @@ let batchedEffect: ReactiveEffect | undefined
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export function startBatch() {
|
export function startBatch(): void {
|
||||||
batchDepth++
|
batchDepth++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +261,7 @@ export function startBatch() {
|
||||||
* Run batched effects when all batches have ended
|
* Run batched effects when all batches have ended
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export function endBatch() {
|
export function endBatch(): void {
|
||||||
if (batchDepth > 1) {
|
if (batchDepth > 1) {
|
||||||
batchDepth--
|
batchDepth--
|
||||||
return
|
return
|
||||||
|
@ -331,7 +350,7 @@ function isDirty(sub: Subscriber): boolean {
|
||||||
* Returning false indicates the refresh failed
|
* Returning false indicates the refresh failed
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export function refreshComputed(computed: ComputedRefImpl) {
|
export function refreshComputed(computed: ComputedRefImpl): false | undefined {
|
||||||
if (computed.flags & EffectFlags.RUNNING) {
|
if (computed.flags & EffectFlags.RUNNING) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -455,7 +474,7 @@ export function effect<T = any>(
|
||||||
*
|
*
|
||||||
* @param runner - Association with the effect to stop tracking.
|
* @param runner - Association with the effect to stop tracking.
|
||||||
*/
|
*/
|
||||||
export function stop(runner: ReactiveEffectRunner) {
|
export function stop(runner: ReactiveEffectRunner): void {
|
||||||
runner.effect.stop()
|
runner.effect.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,7 +487,7 @@ const trackStack: boolean[] = []
|
||||||
/**
|
/**
|
||||||
* Temporarily pauses tracking.
|
* Temporarily pauses tracking.
|
||||||
*/
|
*/
|
||||||
export function pauseTracking() {
|
export function pauseTracking(): void {
|
||||||
trackStack.push(shouldTrack)
|
trackStack.push(shouldTrack)
|
||||||
shouldTrack = false
|
shouldTrack = false
|
||||||
}
|
}
|
||||||
|
@ -476,7 +495,7 @@ export function pauseTracking() {
|
||||||
/**
|
/**
|
||||||
* Re-enables effect tracking (if it was paused).
|
* Re-enables effect tracking (if it was paused).
|
||||||
*/
|
*/
|
||||||
export function enableTracking() {
|
export function enableTracking(): void {
|
||||||
trackStack.push(shouldTrack)
|
trackStack.push(shouldTrack)
|
||||||
shouldTrack = true
|
shouldTrack = true
|
||||||
}
|
}
|
||||||
|
@ -484,7 +503,7 @@ export function enableTracking() {
|
||||||
/**
|
/**
|
||||||
* Resets the previous global effect tracking state.
|
* Resets the previous global effect tracking state.
|
||||||
*/
|
*/
|
||||||
export function resetTracking() {
|
export function resetTracking(): void {
|
||||||
const last = trackStack.pop()
|
const last = trackStack.pop()
|
||||||
shouldTrack = last === undefined ? true : last
|
shouldTrack = last === undefined ? true : last
|
||||||
}
|
}
|
||||||
|
@ -501,7 +520,7 @@ export function resetTracking() {
|
||||||
* @param failSilently - if `true`, will not throw warning when called without
|
* @param failSilently - if `true`, will not throw warning when called without
|
||||||
* an active effect.
|
* an active effect.
|
||||||
*/
|
*/
|
||||||
export function onEffectCleanup(fn: () => void, failSilently = false) {
|
export function onEffectCleanup(fn: () => void, failSilently = false): void {
|
||||||
if (activeSub instanceof ReactiveEffect) {
|
if (activeSub instanceof ReactiveEffect) {
|
||||||
activeSub.cleanup = fn
|
activeSub.cleanup = fn
|
||||||
} else if (__DEV__ && !failSilently) {
|
} else if (__DEV__ && !failSilently) {
|
||||||
|
|
|
@ -17,6 +17,8 @@ export class EffectScope {
|
||||||
*/
|
*/
|
||||||
cleanups: (() => void)[] = []
|
cleanups: (() => void)[] = []
|
||||||
|
|
||||||
|
private _isPaused = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* only assigned by undetached scope
|
* only assigned by undetached scope
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -44,10 +46,43 @@ export class EffectScope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get active() {
|
get active(): boolean {
|
||||||
return this._active
|
return this._active
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pause(): void {
|
||||||
|
if (this._active) {
|
||||||
|
this._isPaused = true
|
||||||
|
if (this.scopes) {
|
||||||
|
for (let i = 0, l = this.scopes.length; i < l; i++) {
|
||||||
|
this.scopes[i].pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0, l = this.effects.length; i < l; i++) {
|
||||||
|
this.effects[i].pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resumes the effect scope, including all child scopes and effects.
|
||||||
|
*/
|
||||||
|
resume(): void {
|
||||||
|
if (this._active) {
|
||||||
|
if (this._isPaused) {
|
||||||
|
this._isPaused = false
|
||||||
|
if (this.scopes) {
|
||||||
|
for (let i = 0, l = this.scopes.length; i < l; i++) {
|
||||||
|
this.scopes[i].resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0, l = this.effects.length; i < l; i++) {
|
||||||
|
this.effects[i].resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
run<T>(fn: () => T): T | undefined {
|
run<T>(fn: () => T): T | undefined {
|
||||||
if (this._active) {
|
if (this._active) {
|
||||||
const currentEffectScope = activeEffectScope
|
const currentEffectScope = activeEffectScope
|
||||||
|
@ -67,7 +102,7 @@ export class EffectScope {
|
||||||
* This should only be called on non-detached scopes
|
* This should only be called on non-detached scopes
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
on() {
|
on(): void {
|
||||||
this.prevScope = activeEffectScope
|
this.prevScope = activeEffectScope
|
||||||
activeEffectScope = this
|
activeEffectScope = this
|
||||||
}
|
}
|
||||||
|
@ -76,11 +111,11 @@ export class EffectScope {
|
||||||
* This should only be called on non-detached scopes
|
* This should only be called on non-detached scopes
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
off() {
|
off(): void {
|
||||||
activeEffectScope = this.prevScope
|
activeEffectScope = this.prevScope
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(fromParent?: boolean) {
|
stop(fromParent?: boolean): void {
|
||||||
if (this._active) {
|
if (this._active) {
|
||||||
let i, l
|
let i, l
|
||||||
for (i = 0, l = this.effects.length; i < l; i++) {
|
for (i = 0, l = this.effects.length; i < l; i++) {
|
||||||
|
@ -118,7 +153,7 @@ export class EffectScope {
|
||||||
* @param detached - Can be used to create a "detached" effect scope.
|
* @param detached - Can be used to create a "detached" effect scope.
|
||||||
* @see {@link https://vuejs.org/api/reactivity-advanced.html#effectscope}
|
* @see {@link https://vuejs.org/api/reactivity-advanced.html#effectscope}
|
||||||
*/
|
*/
|
||||||
export function effectScope(detached?: boolean) {
|
export function effectScope(detached?: boolean): EffectScope {
|
||||||
return new EffectScope(detached)
|
return new EffectScope(detached)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +162,7 @@ export function effectScope(detached?: boolean) {
|
||||||
*
|
*
|
||||||
* @see {@link https://vuejs.org/api/reactivity-advanced.html#getcurrentscope}
|
* @see {@link https://vuejs.org/api/reactivity-advanced.html#getcurrentscope}
|
||||||
*/
|
*/
|
||||||
export function getCurrentScope() {
|
export function getCurrentScope(): EffectScope | undefined {
|
||||||
return activeEffectScope
|
return activeEffectScope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +173,7 @@ export function getCurrentScope() {
|
||||||
* @param fn - The callback function to attach to the scope's cleanup.
|
* @param fn - The callback function to attach to the scope's cleanup.
|
||||||
* @see {@link https://vuejs.org/api/reactivity-advanced.html#onscopedispose}
|
* @see {@link https://vuejs.org/api/reactivity-advanced.html#onscopedispose}
|
||||||
*/
|
*/
|
||||||
export function onScopeDispose(fn: () => void, failSilently = false) {
|
export function onScopeDispose(fn: () => void, failSilently = false): void {
|
||||||
if (activeEffectScope) {
|
if (activeEffectScope) {
|
||||||
activeEffectScope.cleanups.push(fn)
|
activeEffectScope.cleanups.push(fn)
|
||||||
} else if (__DEV__ && !failSilently) {
|
} else if (__DEV__ && !failSilently) {
|
||||||
|
|
|
@ -23,10 +23,16 @@ export interface Target {
|
||||||
[ReactiveFlags.RAW]?: any
|
[ReactiveFlags.RAW]?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reactiveMap = new WeakMap<Target, any>()
|
export const reactiveMap: WeakMap<Target, any> = new WeakMap<Target, any>()
|
||||||
export const shallowReactiveMap = new WeakMap<Target, any>()
|
export const shallowReactiveMap: WeakMap<Target, any> = new WeakMap<
|
||||||
export const readonlyMap = new WeakMap<Target, any>()
|
Target,
|
||||||
export const shallowReadonlyMap = new WeakMap<Target, any>()
|
any
|
||||||
|
>()
|
||||||
|
export const readonlyMap: WeakMap<Target, any> = new WeakMap<Target, any>()
|
||||||
|
export const shallowReadonlyMap: WeakMap<Target, any> = new WeakMap<
|
||||||
|
Target,
|
||||||
|
any
|
||||||
|
>()
|
||||||
|
|
||||||
enum TargetType {
|
enum TargetType {
|
||||||
INVALID = 0,
|
INVALID = 0,
|
||||||
|
@ -60,8 +66,8 @@ export type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRefSimple<T>
|
||||||
|
|
||||||
declare const ReactiveMarkerSymbol: unique symbol
|
declare const ReactiveMarkerSymbol: unique symbol
|
||||||
|
|
||||||
export declare class ReactiveMarker {
|
export interface ReactiveMarker {
|
||||||
private [ReactiveMarkerSymbol]?: void
|
[ReactiveMarkerSymbol]?: void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Reactive<T> = UnwrapNestedRefs<T> &
|
export type Reactive<T> = UnwrapNestedRefs<T> &
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
toRaw,
|
toRaw,
|
||||||
toReactive,
|
toReactive,
|
||||||
} from './reactive'
|
} from './reactive'
|
||||||
import type { ComputedRef } from './computed'
|
import type { ComputedRef, WritableComputedRef } from './computed'
|
||||||
import { ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './constants'
|
import { ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './constants'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
|
|
||||||
|
@ -52,7 +52,9 @@ export function isRef(r: any): r is Ref {
|
||||||
* @param value - The object to wrap in the ref.
|
* @param value - The object to wrap in the ref.
|
||||||
* @see {@link https://vuejs.org/api/reactivity-core.html#ref}
|
* @see {@link https://vuejs.org/api/reactivity-core.html#ref}
|
||||||
*/
|
*/
|
||||||
export function ref<T>(value: T): Ref<UnwrapRef<T>, UnwrapRef<T> | T>
|
export function ref<T>(
|
||||||
|
value: T,
|
||||||
|
): [T] extends [Ref] ? IfAny<T, Ref<T>, T> : Ref<UnwrapRef<T>, UnwrapRef<T> | T>
|
||||||
export function ref<T = any>(): Ref<T | undefined>
|
export function ref<T = any>(): Ref<T | undefined>
|
||||||
export function ref(value?: unknown) {
|
export function ref(value?: unknown) {
|
||||||
return createRef(value, false)
|
return createRef(value, false)
|
||||||
|
@ -179,7 +181,7 @@ class RefImpl<T = any> {
|
||||||
* @param ref - The ref whose tied effects shall be executed.
|
* @param ref - The ref whose tied effects shall be executed.
|
||||||
* @see {@link https://vuejs.org/api/reactivity-advanced.html#triggerref}
|
* @see {@link https://vuejs.org/api/reactivity-advanced.html#triggerref}
|
||||||
*/
|
*/
|
||||||
export function triggerRef(ref: Ref) {
|
export function triggerRef(ref: Ref): void {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
;(ref as unknown as RefImpl).dep.trigger({
|
;(ref as unknown as RefImpl).dep.trigger({
|
||||||
target: ref,
|
target: ref,
|
||||||
|
@ -192,8 +194,13 @@ export function triggerRef(ref: Ref) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MaybeRef<T = any> = T | Ref<T>
|
export type MaybeRef<T = any> =
|
||||||
export type MaybeRefOrGetter<T = any> = MaybeRef<T> | (() => T)
|
| T
|
||||||
|
| Ref<T>
|
||||||
|
| ShallowRef<T>
|
||||||
|
| WritableComputedRef<T>
|
||||||
|
|
||||||
|
export type MaybeRefOrGetter<T = any> = MaybeRef<T> | ComputedRef<T> | (() => T)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the inner value if the argument is a ref, otherwise return the
|
* Returns the inner value if the argument is a ref, otherwise return the
|
||||||
|
@ -211,7 +218,7 @@ export type MaybeRefOrGetter<T = any> = MaybeRef<T> | (() => T)
|
||||||
* @param ref - Ref or plain value to be converted into the plain value.
|
* @param ref - Ref or plain value to be converted into the plain value.
|
||||||
* @see {@link https://vuejs.org/api/reactivity-utilities.html#unref}
|
* @see {@link https://vuejs.org/api/reactivity-utilities.html#unref}
|
||||||
*/
|
*/
|
||||||
export function unref<T>(ref: MaybeRef<T> | ComputedRef<T> | ShallowRef<T>): T {
|
export function unref<T>(ref: MaybeRef<T> | ComputedRef<T>): T {
|
||||||
return isRef(ref) ? ref.value : ref
|
return isRef(ref) ? ref.value : ref
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,9 +238,7 @@ export function unref<T>(ref: MaybeRef<T> | ComputedRef<T> | ShallowRef<T>): T {
|
||||||
* @param source - A getter, an existing ref, or a non-function value.
|
* @param source - A getter, an existing ref, or a non-function value.
|
||||||
* @see {@link https://vuejs.org/api/reactivity-utilities.html#tovalue}
|
* @see {@link https://vuejs.org/api/reactivity-utilities.html#tovalue}
|
||||||
*/
|
*/
|
||||||
export function toValue<T>(
|
export function toValue<T>(source: MaybeRefOrGetter<T>): T {
|
||||||
source: MaybeRefOrGetter<T> | ComputedRef<T> | ShallowRef<T>,
|
|
||||||
): T {
|
|
||||||
return isFunction(source) ? source() : unref(source)
|
return isFunction(source) ? source() : unref(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +287,8 @@ class CustomRefImpl<T> {
|
||||||
|
|
||||||
public readonly [ReactiveFlags.IS_REF] = true
|
public readonly [ReactiveFlags.IS_REF] = true
|
||||||
|
|
||||||
|
public _value: T = undefined!
|
||||||
|
|
||||||
constructor(factory: CustomRefFactory<T>) {
|
constructor(factory: CustomRefFactory<T>) {
|
||||||
const dep = (this.dep = new Dep())
|
const dep = (this.dep = new Dep())
|
||||||
const { get, set } = factory(dep.track.bind(dep), dep.trigger.bind(dep))
|
const { get, set } = factory(dep.track.bind(dep), dep.trigger.bind(dep))
|
||||||
|
@ -290,7 +297,7 @@ class CustomRefImpl<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return this._get()
|
return (this._value = this._get())
|
||||||
}
|
}
|
||||||
|
|
||||||
set value(newVal) {
|
set value(newVal) {
|
||||||
|
@ -334,6 +341,7 @@ export function toRefs<T extends object>(object: T): ToRefs<T> {
|
||||||
|
|
||||||
class ObjectRefImpl<T extends object, K extends keyof T> {
|
class ObjectRefImpl<T extends object, K extends keyof T> {
|
||||||
public readonly [ReactiveFlags.IS_REF] = true
|
public readonly [ReactiveFlags.IS_REF] = true
|
||||||
|
public _value: T[K] = undefined!
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _object: T,
|
private readonly _object: T,
|
||||||
|
@ -343,7 +351,7 @@ class ObjectRefImpl<T extends object, K extends keyof T> {
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
const val = this._object[this._key]
|
const val = this._object[this._key]
|
||||||
return val === undefined ? this._defaultValue! : val
|
return (this._value = val === undefined ? this._defaultValue! : val)
|
||||||
}
|
}
|
||||||
|
|
||||||
set value(newVal) {
|
set value(newVal) {
|
||||||
|
@ -358,9 +366,11 @@ class ObjectRefImpl<T extends object, K extends keyof T> {
|
||||||
class GetterRefImpl<T> {
|
class GetterRefImpl<T> {
|
||||||
public readonly [ReactiveFlags.IS_REF] = true
|
public readonly [ReactiveFlags.IS_REF] = true
|
||||||
public readonly [ReactiveFlags.IS_READONLY] = true
|
public readonly [ReactiveFlags.IS_READONLY] = true
|
||||||
|
public _value: T = undefined!
|
||||||
|
|
||||||
constructor(private readonly _getter: () => T) {}
|
constructor(private readonly _getter: () => T) {}
|
||||||
get value() {
|
get value() {
|
||||||
return this._getter()
|
return (this._value = this._getter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export function warn(msg: string, ...args: any[]) {
|
export function warn(msg: string, ...args: any[]): void {
|
||||||
console.warn(`[Vue warn] ${msg}`, ...args)
|
console.warn(`[Vue warn] ${msg}`, ...args)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ const { render, createApp } = createRenderer({
|
||||||
patchProp,
|
patchProp,
|
||||||
insert,
|
insert,
|
||||||
remove,
|
remove,
|
||||||
createElement
|
createElement,
|
||||||
// ...
|
// ...
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -116,12 +116,25 @@ describe('api: createApp', () => {
|
||||||
const app = createApp({
|
const app = createApp({
|
||||||
setup() {
|
setup() {
|
||||||
provide('foo', 'should not be seen')
|
provide('foo', 'should not be seen')
|
||||||
|
|
||||||
|
// nested createApp
|
||||||
|
const childApp = createApp({
|
||||||
|
setup() {
|
||||||
|
provide('foo', 'foo from child')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
childApp.provide('foo', 2)
|
||||||
|
expect(childApp.runWithContext(() => inject('foo'))).toBe(2)
|
||||||
|
|
||||||
return () => h('div')
|
return () => h('div')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
app.provide('foo', 1)
|
app.provide('foo', 1)
|
||||||
|
|
||||||
expect(app.runWithContext(() => inject('foo'))).toBe(1)
|
expect(app.runWithContext(() => inject('foo'))).toBe(1)
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
app.mount(root)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
app.runWithContext(() => {
|
app.runWithContext(() => {
|
||||||
|
|
|
@ -1562,6 +1562,186 @@ describe('api: watch', () => {
|
||||||
expect(spy2).toHaveBeenCalledTimes(1)
|
expect(spy2).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('watching reactive depth', async () => {
|
||||||
|
const state = reactive({
|
||||||
|
a: {
|
||||||
|
b: {
|
||||||
|
c: {
|
||||||
|
d: {
|
||||||
|
e: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const cb = vi.fn()
|
||||||
|
|
||||||
|
watch(state, cb, { deep: 2 })
|
||||||
|
|
||||||
|
state.a.b = { c: { d: { e: 2 } } }
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
state.a.b.c = { d: { e: 3 } }
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
state.a.b = { c: { d: { e: 4 } } }
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('watching ref depth', async () => {
|
||||||
|
const state = ref({
|
||||||
|
a: {
|
||||||
|
b: 2,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const cb = vi.fn()
|
||||||
|
|
||||||
|
watch(state, cb, { deep: 1 })
|
||||||
|
|
||||||
|
state.value.a.b = 3
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
|
state.value.a = { b: 3 }
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('watching array depth', async () => {
|
||||||
|
const arr = ref([
|
||||||
|
{
|
||||||
|
a: {
|
||||||
|
b: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: {
|
||||||
|
b: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
const cb = vi.fn()
|
||||||
|
watch(arr, cb, { deep: 2 })
|
||||||
|
|
||||||
|
arr.value[0].a.b = 3
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
|
arr.value[0].a = { b: 3 }
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
arr.value[1].a = { b: 4 }
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
arr.value.push({ a: { b: 5 } })
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(3)
|
||||||
|
|
||||||
|
arr.value.pop()
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(4)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('pause / resume', async () => {
|
||||||
|
const count = ref(0)
|
||||||
|
const cb = vi.fn()
|
||||||
|
const { pause, resume } = watch(count, cb)
|
||||||
|
|
||||||
|
count.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(1)
|
||||||
|
expect(cb).toHaveBeenLastCalledWith(1, 0, expect.any(Function))
|
||||||
|
|
||||||
|
pause()
|
||||||
|
count.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(1)
|
||||||
|
expect(cb).toHaveBeenLastCalledWith(1, 0, expect.any(Function))
|
||||||
|
|
||||||
|
resume()
|
||||||
|
count.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(2)
|
||||||
|
expect(cb).toHaveBeenLastCalledWith(3, 1, expect.any(Function))
|
||||||
|
|
||||||
|
count.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(3)
|
||||||
|
expect(cb).toHaveBeenLastCalledWith(4, 3, expect.any(Function))
|
||||||
|
|
||||||
|
pause()
|
||||||
|
count.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(3)
|
||||||
|
expect(cb).toHaveBeenLastCalledWith(4, 3, expect.any(Function))
|
||||||
|
|
||||||
|
resume()
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(4)
|
||||||
|
expect(cb).toHaveBeenLastCalledWith(5, 4, expect.any(Function))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shallowReactive', async () => {
|
||||||
|
const state = shallowReactive({
|
||||||
|
msg: ref('hello'),
|
||||||
|
foo: {
|
||||||
|
a: ref(1),
|
||||||
|
b: 2,
|
||||||
|
},
|
||||||
|
bar: 'bar',
|
||||||
|
})
|
||||||
|
|
||||||
|
const spy = vi.fn()
|
||||||
|
|
||||||
|
watch(state, spy)
|
||||||
|
|
||||||
|
state.msg.value = 'hi'
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
state.bar = 'bar2'
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
state.foo.a.value++
|
||||||
|
state.foo.b++
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
state.bar = 'bar3'
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(2)
|
||||||
|
})
|
||||||
|
it('watching reactive with deep: false', async () => {
|
||||||
|
const state = reactive({
|
||||||
|
foo: {
|
||||||
|
a: 2,
|
||||||
|
},
|
||||||
|
bar: 'bar',
|
||||||
|
})
|
||||||
|
|
||||||
|
const spy = vi.fn()
|
||||||
|
|
||||||
|
watch(state, spy, { deep: false })
|
||||||
|
|
||||||
|
state.foo.a++
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
|
state.bar = 'bar2'
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
test("effect should be removed from scope's effects after it is stopped", () => {
|
test("effect should be removed from scope's effects after it is stopped", () => {
|
||||||
const num = ref(0)
|
const num = ref(0)
|
||||||
let unwatch: () => void
|
let unwatch: () => void
|
||||||
|
|
|
@ -155,12 +155,12 @@ describe('component: emit', () => {
|
||||||
render() {},
|
render() {},
|
||||||
created() {
|
created() {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
this.$emit('bar')
|
this.$emit('bar-baz')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
render(h(Foo), nodeOps.createElement('div'))
|
render(h(Foo), nodeOps.createElement('div'))
|
||||||
expect(
|
expect(
|
||||||
`Component emitted event "bar" but it is neither declared`,
|
`Component emitted event "bar-baz" but it is neither declared in the emits option nor as an "onBarBaz" prop`,
|
||||||
).toHaveBeenWarned()
|
).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -172,12 +172,12 @@ describe('component: emit', () => {
|
||||||
render() {},
|
render() {},
|
||||||
created() {
|
created() {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
this.$emit('bar')
|
this.$emit('bar-baz')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
render(h(Foo), nodeOps.createElement('div'))
|
render(h(Foo), nodeOps.createElement('div'))
|
||||||
expect(
|
expect(
|
||||||
`Component emitted event "bar" but it is neither declared`,
|
`Component emitted event "bar-baz" but it is neither declared in the emits option nor as an "onBarBaz" prop`,
|
||||||
).toHaveBeenWarned()
|
).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -197,6 +197,22 @@ describe('component: emit', () => {
|
||||||
).not.toHaveBeenWarned()
|
).not.toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should not warn if has equivalent onXXX prop with kebab-cased event', () => {
|
||||||
|
const Foo = defineComponent({
|
||||||
|
props: ['onFooBar'],
|
||||||
|
emits: [],
|
||||||
|
render() {},
|
||||||
|
created() {
|
||||||
|
// @ts-expect-error
|
||||||
|
this.$emit('foo-bar')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
render(h(Foo), nodeOps.createElement('div'))
|
||||||
|
expect(
|
||||||
|
`Component emitted event "foo-bar" but it is neither declared`,
|
||||||
|
).not.toHaveBeenWarned()
|
||||||
|
})
|
||||||
|
|
||||||
test('validator warning', () => {
|
test('validator warning', () => {
|
||||||
const Foo = defineComponent({
|
const Foo = defineComponent({
|
||||||
emits: {
|
emits: {
|
||||||
|
|
|
@ -213,7 +213,7 @@ describe('component: slots', () => {
|
||||||
expect(instance.slots.default()).toMatchObject([normalizeVNode('footer')])
|
expect(instance.slots.default()).toMatchObject([normalizeVNode('footer')])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should respect $stable flag', async () => {
|
test('should respect $stable flag with a value of true', async () => {
|
||||||
const flag1 = ref(1)
|
const flag1 = ref(1)
|
||||||
const flag2 = ref(2)
|
const flag2 = ref(2)
|
||||||
const spy = vi.fn()
|
const spy = vi.fn()
|
||||||
|
@ -255,6 +255,48 @@ describe('component: slots', () => {
|
||||||
expect(spy).toHaveBeenCalledTimes(2)
|
expect(spy).toHaveBeenCalledTimes(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should respect $stable flag with a value of false', async () => {
|
||||||
|
const flag1 = ref(1)
|
||||||
|
const flag2 = ref(2)
|
||||||
|
const spy = vi.fn()
|
||||||
|
|
||||||
|
const Child = () => {
|
||||||
|
spy()
|
||||||
|
return 'child'
|
||||||
|
}
|
||||||
|
|
||||||
|
const App = {
|
||||||
|
setup() {
|
||||||
|
return () => [
|
||||||
|
flag1.value,
|
||||||
|
h(
|
||||||
|
Child,
|
||||||
|
{ n: flag2.value },
|
||||||
|
{
|
||||||
|
foo: () => 'foo',
|
||||||
|
$stable: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
render(h(App), nodeOps.createElement('div'))
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
// parent re-render, props didn't change, slots are not stable
|
||||||
|
// -> child should update
|
||||||
|
flag1.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
// parent re-render, props changed
|
||||||
|
// -> child should update
|
||||||
|
flag2.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(3)
|
||||||
|
})
|
||||||
|
|
||||||
test('should not warn when mounting another app in setup', () => {
|
test('should not warn when mounting another app in setup', () => {
|
||||||
const Comp = {
|
const Comp = {
|
||||||
setup(_: any, { slots }: any) {
|
setup(_: any, { slots }: any) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
KeepAlive,
|
KeepAlive,
|
||||||
Suspense,
|
Suspense,
|
||||||
type SuspenseProps,
|
type SuspenseProps,
|
||||||
|
createCommentVNode,
|
||||||
h,
|
h,
|
||||||
nextTick,
|
nextTick,
|
||||||
nodeOps,
|
nodeOps,
|
||||||
|
@ -2035,7 +2036,7 @@ describe('Suspense', () => {
|
||||||
expect(serializeInner(root)).toBe(`<div>sync</div>`)
|
expect(serializeInner(root)).toBe(`<div>sync</div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
// #10899
|
// #10899 / #11427
|
||||||
test('KeepAlive + Suspense switch before branch resolves', async () => {
|
test('KeepAlive + Suspense switch before branch resolves', async () => {
|
||||||
const Async1 = defineAsyncComponent({
|
const Async1 = defineAsyncComponent({
|
||||||
render() {
|
render() {
|
||||||
|
@ -2052,14 +2053,20 @@ describe('Suspense', () => {
|
||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
const App = {
|
const App = {
|
||||||
render() {
|
render() {
|
||||||
return h(KeepAlive, null, {
|
return h(
|
||||||
default: () => {
|
KeepAlive,
|
||||||
return h(Suspense, null, {
|
{
|
||||||
default: h(components[viewRef.value]),
|
max: 1,
|
||||||
fallback: h('div', 'loading'),
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
})
|
{
|
||||||
|
default: () => {
|
||||||
|
return h(Suspense, null, {
|
||||||
|
default: h(components[viewRef.value]),
|
||||||
|
fallback: h('div', 'loading'),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
render(h(App), root)
|
render(h(App), root)
|
||||||
|
@ -2085,6 +2092,35 @@ describe('Suspense', () => {
|
||||||
expect(serializeInner(root)).toBe(`<div>async2</div>`)
|
expect(serializeInner(root)).toBe(`<div>async2</div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('KeepAlive + Suspense + comment slot', async () => {
|
||||||
|
const toggle = ref(false)
|
||||||
|
const Async = defineAsyncComponent({
|
||||||
|
render() {
|
||||||
|
return h('div', 'async1')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const App = {
|
||||||
|
render() {
|
||||||
|
return h(KeepAlive, null, {
|
||||||
|
default: () => {
|
||||||
|
return h(Suspense, null, {
|
||||||
|
default: toggle.value ? h(Async) : createCommentVNode('v-if'),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
render(h(App), root)
|
||||||
|
expect(serializeInner(root)).toBe(`<!--v-if-->`)
|
||||||
|
|
||||||
|
toggle.value = true
|
||||||
|
await nextTick()
|
||||||
|
await Promise.all(deps)
|
||||||
|
expect(serializeInner(root)).toBe(`<div>async1</div>`)
|
||||||
|
})
|
||||||
|
|
||||||
// #6416 follow up / #10017
|
// #6416 follow up / #10017
|
||||||
test('Suspense patched during HOC async component re-mount', async () => {
|
test('Suspense patched during HOC async component re-mount', async () => {
|
||||||
const key = ref('k')
|
const key = ref('k')
|
||||||
|
|
|
@ -657,4 +657,96 @@ describe('useModel', () => {
|
||||||
expect(setValue).toBeCalledTimes(2)
|
expect(setValue).toBeCalledTimes(2)
|
||||||
expect(msg.value).toBe(defaultVal)
|
expect(msg.value).toBe(defaultVal)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #11526
|
||||||
|
test('custom getter', () => {
|
||||||
|
let changeChildMsg!: (val: boolean) => void
|
||||||
|
const getter = (value: boolean) => !value
|
||||||
|
|
||||||
|
const Comp = defineComponent({
|
||||||
|
props: ['msg'],
|
||||||
|
emits: ['update:msg'],
|
||||||
|
setup(props) {
|
||||||
|
const childMsg = useModel(props, 'msg', {
|
||||||
|
get: getter,
|
||||||
|
set: value => !value,
|
||||||
|
})
|
||||||
|
changeChildMsg = (val: boolean) => (childMsg.value = val)
|
||||||
|
return () => {
|
||||||
|
return childMsg.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const defaultVal = false
|
||||||
|
const msg = ref(defaultVal)
|
||||||
|
const Parent = defineComponent({
|
||||||
|
setup() {
|
||||||
|
return () =>
|
||||||
|
h(Comp, {
|
||||||
|
msg: msg.value,
|
||||||
|
'onUpdate:msg': val => {
|
||||||
|
msg.value = val
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
render(h(Parent), root)
|
||||||
|
|
||||||
|
changeChildMsg(!getter(msg.value))
|
||||||
|
expect(msg.value).toBe(true)
|
||||||
|
|
||||||
|
changeChildMsg(!getter(msg.value))
|
||||||
|
expect(msg.value).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
// #11541
|
||||||
|
test('custom setter', () => {
|
||||||
|
let changeChildMsg!: (val: boolean) => void
|
||||||
|
|
||||||
|
const Comp = defineComponent({
|
||||||
|
props: ['msg'],
|
||||||
|
emits: ['update:msg'],
|
||||||
|
setup(props) {
|
||||||
|
const childMsg = useModel(props, 'msg', {
|
||||||
|
set: value => {
|
||||||
|
if (value === msg.value) {
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
changeChildMsg = (val: boolean) => (childMsg.value = val)
|
||||||
|
return () => {
|
||||||
|
return childMsg.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const defaultVal = false
|
||||||
|
const msg = ref(defaultVal)
|
||||||
|
const Parent = defineComponent({
|
||||||
|
setup() {
|
||||||
|
return () =>
|
||||||
|
h(Comp, {
|
||||||
|
msg: msg.value,
|
||||||
|
'onUpdate:msg': val => {
|
||||||
|
msg.value = val
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
render(h(Parent), root)
|
||||||
|
|
||||||
|
changeChildMsg(true)
|
||||||
|
expect(msg.value).toBe(true)
|
||||||
|
|
||||||
|
changeChildMsg(true)
|
||||||
|
expect(msg.value).toBe(null)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -68,4 +68,18 @@ describe('useTemplateRef', () => {
|
||||||
expect(t2!.value).toBe(root.children[0])
|
expect(t2!.value).toBe(root.children[0])
|
||||||
expect(t1!.value).toBe(null)
|
expect(t1!.value).toBe(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should warn on duplicate useTemplateRef', () => {
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
render(
|
||||||
|
h(() => {
|
||||||
|
useTemplateRef('foo')
|
||||||
|
useTemplateRef('foo')
|
||||||
|
return ''
|
||||||
|
}),
|
||||||
|
root,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(`useTemplateRef('foo') already exists.`).toHaveBeenWarned()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1388,6 +1388,26 @@ describe('SSR hydration', () => {
|
||||||
expect((container.firstChild!.firstChild as any)._value).toBe(true)
|
expect((container.firstChild!.firstChild as any)._value).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #7203
|
||||||
|
test('force hydrate custom element with dynamic props', () => {
|
||||||
|
class MyElement extends HTMLElement {
|
||||||
|
foo = ''
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define('my-element-7203', MyElement)
|
||||||
|
|
||||||
|
const msg = ref('bar')
|
||||||
|
const container = document.createElement('div')
|
||||||
|
container.innerHTML = '<my-element-7203></my-element-7203>'
|
||||||
|
const app = createSSRApp({
|
||||||
|
render: () => h('my-element-7203', { foo: msg.value }),
|
||||||
|
})
|
||||||
|
app.mount(container)
|
||||||
|
expect((container.firstChild as any).foo).toBe(msg.value)
|
||||||
|
})
|
||||||
|
|
||||||
// #5728
|
// #5728
|
||||||
test('empty text node in slot', () => {
|
test('empty text node in slot', () => {
|
||||||
const Comp = {
|
const Comp = {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/runtime-core",
|
"name": "@vue/runtime-core",
|
||||||
"version": "3.5.0-alpha.5",
|
"version": "3.5.0-beta.1",
|
||||||
"description": "@vue/runtime-core",
|
"description": "@vue/runtime-core",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "dist/runtime-core.esm-bundler.js",
|
"module": "dist/runtime-core.esm-bundler.js",
|
||||||
|
|
|
@ -50,8 +50,18 @@ export interface App<HostElement = any> {
|
||||||
directive<T = any, V = any>(name: string, directive: Directive<T, V>): this
|
directive<T = any, V = any>(name: string, directive: Directive<T, V>): this
|
||||||
mount(
|
mount(
|
||||||
rootContainer: HostElement | string,
|
rootContainer: HostElement | string,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
isHydrate?: boolean,
|
isHydrate?: boolean,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
namespace?: boolean | ElementNamespace,
|
namespace?: boolean | ElementNamespace,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
vnode?: VNode,
|
||||||
): ComponentPublicInstance
|
): ComponentPublicInstance
|
||||||
unmount(): void
|
unmount(): void
|
||||||
onUnmount(cb: () => void): void
|
onUnmount(cb: () => void): void
|
||||||
|
@ -76,6 +86,11 @@ export interface App<HostElement = any> {
|
||||||
_context: AppContext
|
_context: AppContext
|
||||||
_instance: ComponentInternalInstance | null
|
_instance: ComponentInternalInstance | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal custom element vnode
|
||||||
|
*/
|
||||||
|
_ceVNode?: VNode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* v2 compat only
|
* v2 compat only
|
||||||
*/
|
*/
|
||||||
|
@ -337,7 +352,7 @@ export function createAppAPI<HostElement>(
|
||||||
` you need to unmount the previous app by calling \`app.unmount()\` first.`,
|
` you need to unmount the previous app by calling \`app.unmount()\` first.`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const vnode = createVNode(rootComponent, rootProps)
|
const vnode = app._ceVNode || createVNode(rootComponent, rootProps)
|
||||||
// store app context on the root VNode.
|
// store app context on the root VNode.
|
||||||
// this will be set on the root instance on initial mount.
|
// this will be set on the root instance on initial mount.
|
||||||
vnode.appContext = context
|
vnode.appContext = context
|
||||||
|
|
|
@ -67,6 +67,7 @@ export type DefineComponent<
|
||||||
Exposed extends string = string,
|
Exposed extends string = string,
|
||||||
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||||
MakeDefaultsOptional extends boolean = true,
|
MakeDefaultsOptional extends boolean = true,
|
||||||
|
TypeRefs extends Record<string, unknown> = {},
|
||||||
> = ComponentPublicInstanceConstructor<
|
> = ComponentPublicInstanceConstructor<
|
||||||
CreateComponentPublicInstanceWithMixins<
|
CreateComponentPublicInstanceWithMixins<
|
||||||
Props,
|
Props,
|
||||||
|
@ -84,7 +85,8 @@ export type DefineComponent<
|
||||||
S,
|
S,
|
||||||
LC & GlobalComponents,
|
LC & GlobalComponents,
|
||||||
Directives & GlobalDirectives,
|
Directives & GlobalDirectives,
|
||||||
Exposed
|
Exposed,
|
||||||
|
TypeRefs
|
||||||
>
|
>
|
||||||
> &
|
> &
|
||||||
ComponentOptionsBase<
|
ComponentOptionsBase<
|
||||||
|
@ -209,6 +211,7 @@ export function defineComponent<
|
||||||
: { [key in RuntimePropsKeys]?: any }
|
: { [key in RuntimePropsKeys]?: any }
|
||||||
: TypeProps,
|
: TypeProps,
|
||||||
ResolvedProps = Readonly<InferredProps & EmitsToProps<ResolvedEmits>>,
|
ResolvedProps = Readonly<InferredProps & EmitsToProps<ResolvedEmits>>,
|
||||||
|
TypeRefs extends Record<string, unknown> = {},
|
||||||
>(
|
>(
|
||||||
options: {
|
options: {
|
||||||
props?: (RuntimePropsOptions & ThisType<void>) | RuntimePropsKeys[]
|
props?: (RuntimePropsOptions & ThisType<void>) | RuntimePropsKeys[]
|
||||||
|
@ -220,6 +223,10 @@ export function defineComponent<
|
||||||
* @private for language-tools use only
|
* @private for language-tools use only
|
||||||
*/
|
*/
|
||||||
__typeEmits?: TypeEmits
|
__typeEmits?: TypeEmits
|
||||||
|
/**
|
||||||
|
* @private for language-tools use only
|
||||||
|
*/
|
||||||
|
__typeRefs?: TypeRefs
|
||||||
} & ComponentOptionsBase<
|
} & ComponentOptionsBase<
|
||||||
ResolvedProps,
|
ResolvedProps,
|
||||||
SetupBindings,
|
SetupBindings,
|
||||||
|
@ -279,7 +286,8 @@ export function defineComponent<
|
||||||
Provide,
|
Provide,
|
||||||
// MakeDefaultsOptional - if TypeProps is provided, set to false to use
|
// MakeDefaultsOptional - if TypeProps is provided, set to false to use
|
||||||
// user props types verbatim
|
// user props types verbatim
|
||||||
unknown extends TypeProps ? true : false
|
unknown extends TypeProps ? true : false,
|
||||||
|
TypeRefs
|
||||||
>
|
>
|
||||||
|
|
||||||
// implementation, close to no-op
|
// implementation, close to no-op
|
||||||
|
|
|
@ -4,12 +4,14 @@ import { currentRenderingInstance } from './componentRenderContext'
|
||||||
import { currentApp } from './apiCreateApp'
|
import { currentApp } from './apiCreateApp'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
|
|
||||||
export interface InjectionKey<T> extends Symbol {}
|
interface InjectionConstraint<T> {}
|
||||||
|
|
||||||
|
export type InjectionKey<T> = symbol & InjectionConstraint<T>
|
||||||
|
|
||||||
export function provide<T, K = InjectionKey<T> | string | number>(
|
export function provide<T, K = InjectionKey<T> | string | number>(
|
||||||
key: K,
|
key: K,
|
||||||
value: K extends InjectionKey<infer V> ? V : T,
|
value: K extends InjectionKey<infer V> ? V : T,
|
||||||
) {
|
): void {
|
||||||
if (!currentInstance) {
|
if (!currentInstance) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
warn(`provide() can only be used inside setup().`)
|
warn(`provide() can only be used inside setup().`)
|
||||||
|
@ -56,11 +58,14 @@ export function inject(
|
||||||
// #2400
|
// #2400
|
||||||
// to support `app.use` plugins,
|
// to support `app.use` plugins,
|
||||||
// fallback to appContext's `provides` if the instance is at root
|
// fallback to appContext's `provides` if the instance is at root
|
||||||
const provides = instance
|
// #11488, in a nested createApp, prioritize using the provides from currentApp
|
||||||
? instance.parent == null
|
const provides = currentApp
|
||||||
? instance.vnode.appContext && instance.vnode.appContext.provides
|
? currentApp._context.provides
|
||||||
: instance.parent.provides
|
: instance
|
||||||
: currentApp!._context.provides
|
? instance.parent == null
|
||||||
|
? instance.vnode.appContext && instance.vnode.appContext.provides
|
||||||
|
: instance.parent.provides
|
||||||
|
: undefined
|
||||||
|
|
||||||
if (provides && (key as string | symbol) in provides) {
|
if (provides && (key as string | symbol) in provides) {
|
||||||
// TS doesn't allow symbol as index type
|
// TS doesn't allow symbol as index type
|
||||||
|
|
|
@ -63,9 +63,12 @@ export function injectHook(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createHook =
|
const createHook =
|
||||||
<T extends Function = () => any>(lifecycle: LifecycleHooks) =>
|
<T extends Function = () => any>(lifecycle: LifecycleHooks) =>
|
||||||
(hook: T, target: ComponentInternalInstance | null = currentInstance) => {
|
(
|
||||||
|
hook: T,
|
||||||
|
target: ComponentInternalInstance | null = currentInstance,
|
||||||
|
): void => {
|
||||||
// post-create lifecycle registrations are noops during SSR (except for serverPrefetch)
|
// post-create lifecycle registrations are noops during SSR (except for serverPrefetch)
|
||||||
if (
|
if (
|
||||||
!isInSSRComponentSetup ||
|
!isInSSRComponentSetup ||
|
||||||
|
@ -74,22 +77,30 @@ export const createHook =
|
||||||
injectHook(lifecycle, (...args: unknown[]) => hook(...args), target)
|
injectHook(lifecycle, (...args: unknown[]) => hook(...args), target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
type CreateHook<T = any> = (
|
||||||
|
hook: T,
|
||||||
|
target?: ComponentInternalInstance | null,
|
||||||
|
) => void
|
||||||
|
|
||||||
export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
|
export const onBeforeMount: CreateHook = createHook(LifecycleHooks.BEFORE_MOUNT)
|
||||||
export const onMounted = createHook(LifecycleHooks.MOUNTED)
|
export const onMounted: CreateHook = createHook(LifecycleHooks.MOUNTED)
|
||||||
export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
|
export const onBeforeUpdate: CreateHook = createHook(
|
||||||
export const onUpdated = createHook(LifecycleHooks.UPDATED)
|
LifecycleHooks.BEFORE_UPDATE,
|
||||||
export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
|
)
|
||||||
export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED)
|
export const onUpdated: CreateHook = createHook(LifecycleHooks.UPDATED)
|
||||||
export const onServerPrefetch = createHook(LifecycleHooks.SERVER_PREFETCH)
|
export const onBeforeUnmount: CreateHook = createHook(
|
||||||
|
LifecycleHooks.BEFORE_UNMOUNT,
|
||||||
|
)
|
||||||
|
export const onUnmounted: CreateHook = createHook(LifecycleHooks.UNMOUNTED)
|
||||||
|
export const onServerPrefetch: CreateHook = createHook(
|
||||||
|
LifecycleHooks.SERVER_PREFETCH,
|
||||||
|
)
|
||||||
|
|
||||||
export type DebuggerHook = (e: DebuggerEvent) => void
|
export type DebuggerHook = (e: DebuggerEvent) => void
|
||||||
export const onRenderTriggered = createHook<DebuggerHook>(
|
export const onRenderTriggered: CreateHook<DebuggerHook> =
|
||||||
LifecycleHooks.RENDER_TRIGGERED,
|
createHook<DebuggerHook>(LifecycleHooks.RENDER_TRIGGERED)
|
||||||
)
|
export const onRenderTracked: CreateHook<DebuggerHook> =
|
||||||
export const onRenderTracked = createHook<DebuggerHook>(
|
createHook<DebuggerHook>(LifecycleHooks.RENDER_TRACKED)
|
||||||
LifecycleHooks.RENDER_TRACKED,
|
|
||||||
)
|
|
||||||
|
|
||||||
export type ErrorCapturedHook<TError = unknown> = (
|
export type ErrorCapturedHook<TError = unknown> = (
|
||||||
err: TError,
|
err: TError,
|
||||||
|
@ -100,6 +111,6 @@ export type ErrorCapturedHook<TError = unknown> = (
|
||||||
export function onErrorCaptured<TError = Error>(
|
export function onErrorCaptured<TError = Error>(
|
||||||
hook: ErrorCapturedHook<TError>,
|
hook: ErrorCapturedHook<TError>,
|
||||||
target: ComponentInternalInstance | null = currentInstance,
|
target: ComponentInternalInstance | null = currentInstance,
|
||||||
) {
|
): void {
|
||||||
injectHook(LifecycleHooks.ERROR_CAPTURED, hook, target)
|
injectHook(LifecycleHooks.ERROR_CAPTURED, hook, target)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
type IfAny,
|
||||||
type LooseRequired,
|
type LooseRequired,
|
||||||
type Prettify,
|
type Prettify,
|
||||||
type UnionToIntersection,
|
type UnionToIntersection,
|
||||||
|
@ -176,7 +177,7 @@ type ShortEmits<T extends Record<string, any>> = UnionToIntersection<
|
||||||
*/
|
*/
|
||||||
export function defineExpose<
|
export function defineExpose<
|
||||||
Exposed extends Record<string, any> = Record<string, any>,
|
Exposed extends Record<string, any> = Record<string, any>,
|
||||||
>(exposed?: Exposed) {
|
>(exposed?: Exposed): void {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
warnRuntimeUsage(`defineExpose`)
|
warnRuntimeUsage(`defineExpose`)
|
||||||
}
|
}
|
||||||
|
@ -329,7 +330,7 @@ type PropsWithDefaults<
|
||||||
> = Readonly<MappedOmit<T, keyof Defaults>> & {
|
> = Readonly<MappedOmit<T, keyof Defaults>> & {
|
||||||
readonly [K in keyof Defaults]-?: K extends keyof T
|
readonly [K in keyof Defaults]-?: K extends keyof T
|
||||||
? Defaults[K] extends undefined
|
? Defaults[K] extends undefined
|
||||||
? T[K]
|
? IfAny<Defaults[K], NotUndefined<T[K]>, T[K]>
|
||||||
: NotUndefined<T[K]>
|
: NotUndefined<T[K]>
|
||||||
: never
|
: never
|
||||||
} & {
|
} & {
|
||||||
|
@ -395,7 +396,7 @@ function getContext(): SetupContext {
|
||||||
*/
|
*/
|
||||||
export function normalizePropsOrEmits(
|
export function normalizePropsOrEmits(
|
||||||
props: ComponentPropsOptions | EmitsOptions,
|
props: ComponentPropsOptions | EmitsOptions,
|
||||||
) {
|
): ComponentObjectPropsOptions | ObjectEmitsOptions {
|
||||||
return isArray(props)
|
return isArray(props)
|
||||||
? props.reduce(
|
? props.reduce(
|
||||||
(normalized, p) => ((normalized[p] = null), normalized),
|
(normalized, p) => ((normalized[p] = null), normalized),
|
||||||
|
@ -443,7 +444,7 @@ export function mergeDefaults(
|
||||||
export function mergeModels(
|
export function mergeModels(
|
||||||
a: ComponentPropsOptions | EmitsOptions,
|
a: ComponentPropsOptions | EmitsOptions,
|
||||||
b: ComponentPropsOptions | EmitsOptions,
|
b: ComponentPropsOptions | EmitsOptions,
|
||||||
) {
|
): ComponentPropsOptions | EmitsOptions {
|
||||||
if (!a || !b) return a || b
|
if (!a || !b) return a || b
|
||||||
if (isArray(a) && isArray(b)) return a.concat(b)
|
if (isArray(a) && isArray(b)) return a.concat(b)
|
||||||
return extend({}, normalizePropsOrEmits(a), normalizePropsOrEmits(b))
|
return extend({}, normalizePropsOrEmits(a), normalizePropsOrEmits(b))
|
||||||
|
@ -488,7 +489,7 @@ export function createPropsRestProxy(
|
||||||
* ```
|
* ```
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export function withAsyncContext(getAwaitable: () => any) {
|
export function withAsyncContext(getAwaitable: () => any): [any, () => void] {
|
||||||
const ctx = getCurrentInstance()!
|
const ctx = getCurrentInstance()!
|
||||||
if (__DEV__ && !ctx) {
|
if (__DEV__ && !ctx) {
|
||||||
warn(
|
warn(
|
||||||
|
|
|
@ -61,24 +61,30 @@ export interface WatchOptionsBase extends DebuggerOptions {
|
||||||
|
|
||||||
export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
|
export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
|
||||||
immediate?: Immediate
|
immediate?: Immediate
|
||||||
deep?: boolean
|
deep?: boolean | number
|
||||||
once?: boolean
|
once?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WatchStopHandle = () => void
|
export type WatchStopHandle = () => void
|
||||||
|
|
||||||
|
export interface WatchHandle extends WatchStopHandle {
|
||||||
|
pause: () => void
|
||||||
|
resume: () => void
|
||||||
|
stop: () => void
|
||||||
|
}
|
||||||
|
|
||||||
// Simple effect.
|
// Simple effect.
|
||||||
export function watchEffect(
|
export function watchEffect(
|
||||||
effect: WatchEffect,
|
effect: WatchEffect,
|
||||||
options?: WatchOptionsBase,
|
options?: WatchOptionsBase,
|
||||||
): WatchStopHandle {
|
): WatchHandle {
|
||||||
return doWatch(effect, null, options)
|
return doWatch(effect, null, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function watchPostEffect(
|
export function watchPostEffect(
|
||||||
effect: WatchEffect,
|
effect: WatchEffect,
|
||||||
options?: DebuggerOptions,
|
options?: DebuggerOptions,
|
||||||
) {
|
): WatchStopHandle {
|
||||||
return doWatch(
|
return doWatch(
|
||||||
effect,
|
effect,
|
||||||
null,
|
null,
|
||||||
|
@ -89,7 +95,7 @@ export function watchPostEffect(
|
||||||
export function watchSyncEffect(
|
export function watchSyncEffect(
|
||||||
effect: WatchEffect,
|
effect: WatchEffect,
|
||||||
options?: DebuggerOptions,
|
options?: DebuggerOptions,
|
||||||
) {
|
): WatchStopHandle {
|
||||||
return doWatch(
|
return doWatch(
|
||||||
effect,
|
effect,
|
||||||
null,
|
null,
|
||||||
|
@ -97,14 +103,14 @@ export function watchSyncEffect(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type MultiWatchSources = (WatchSource<unknown> | object)[]
|
export type MultiWatchSources = (WatchSource<unknown> | object)[]
|
||||||
|
|
||||||
// overload: single source + cb
|
// overload: single source + cb
|
||||||
export function watch<T, Immediate extends Readonly<boolean> = false>(
|
export function watch<T, Immediate extends Readonly<boolean> = false>(
|
||||||
source: WatchSource<T>,
|
source: WatchSource<T>,
|
||||||
cb: WatchCallback<T, MaybeUndefined<T, Immediate>>,
|
cb: WatchCallback<T, MaybeUndefined<T, Immediate>>,
|
||||||
options?: WatchOptions<Immediate>,
|
options?: WatchOptions<Immediate>,
|
||||||
): WatchStopHandle
|
): WatchHandle
|
||||||
|
|
||||||
// overload: reactive array or tuple of multiple sources + cb
|
// overload: reactive array or tuple of multiple sources + cb
|
||||||
export function watch<
|
export function watch<
|
||||||
|
@ -116,7 +122,7 @@ export function watch<
|
||||||
? WatchCallback<T, MaybeUndefined<T, Immediate>>
|
? WatchCallback<T, MaybeUndefined<T, Immediate>>
|
||||||
: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
|
: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
|
||||||
options?: WatchOptions<Immediate>,
|
options?: WatchOptions<Immediate>,
|
||||||
): WatchStopHandle
|
): WatchHandle
|
||||||
|
|
||||||
// overload: array of multiple sources + cb
|
// overload: array of multiple sources + cb
|
||||||
export function watch<
|
export function watch<
|
||||||
|
@ -126,7 +132,7 @@ export function watch<
|
||||||
sources: [...T],
|
sources: [...T],
|
||||||
cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
|
cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
|
||||||
options?: WatchOptions<Immediate>,
|
options?: WatchOptions<Immediate>,
|
||||||
): WatchStopHandle
|
): WatchHandle
|
||||||
|
|
||||||
// overload: watching reactive object w/ cb
|
// overload: watching reactive object w/ cb
|
||||||
export function watch<
|
export function watch<
|
||||||
|
@ -136,14 +142,14 @@ export function watch<
|
||||||
source: T,
|
source: T,
|
||||||
cb: WatchCallback<T, MaybeUndefined<T, Immediate>>,
|
cb: WatchCallback<T, MaybeUndefined<T, Immediate>>,
|
||||||
options?: WatchOptions<Immediate>,
|
options?: WatchOptions<Immediate>,
|
||||||
): WatchStopHandle
|
): WatchHandle
|
||||||
|
|
||||||
// implementation
|
// implementation
|
||||||
export function watch<T = any, Immediate extends Readonly<boolean> = false>(
|
export function watch<T = any, Immediate extends Readonly<boolean> = false>(
|
||||||
source: T | WatchSource<T>,
|
source: T | WatchSource<T>,
|
||||||
cb: any,
|
cb: any,
|
||||||
options?: WatchOptions<Immediate>,
|
options?: WatchOptions<Immediate>,
|
||||||
): WatchStopHandle {
|
): WatchHandle {
|
||||||
if (__DEV__ && !isFunction(cb)) {
|
if (__DEV__ && !isFunction(cb)) {
|
||||||
warn(
|
warn(
|
||||||
`\`watch(fn, options?)\` signature has been moved to a separate API. ` +
|
`\`watch(fn, options?)\` signature has been moved to a separate API. ` +
|
||||||
|
@ -169,17 +175,9 @@ function doWatch(
|
||||||
source: WatchSource | WatchSource[] | WatchEffect | object,
|
source: WatchSource | WatchSource[] | WatchEffect | object,
|
||||||
cb: WatchCallback | null,
|
cb: WatchCallback | null,
|
||||||
options: WatchOptions = EMPTY_OBJ,
|
options: WatchOptions = EMPTY_OBJ,
|
||||||
): WatchStopHandle {
|
): WatchHandle {
|
||||||
const { immediate, deep, flush, once } = options
|
const { immediate, deep, flush, once } = options
|
||||||
|
|
||||||
// TODO remove in 3.5
|
|
||||||
if (__DEV__ && deep !== void 0 && typeof deep === 'number') {
|
|
||||||
warn(
|
|
||||||
`watch() "deep" option with number value will be used as watch depth in future versions. ` +
|
|
||||||
`Please use a boolean instead to avoid potential breakage.`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__DEV__ && !cb) {
|
if (__DEV__ && !cb) {
|
||||||
if (immediate !== undefined) {
|
if (immediate !== undefined) {
|
||||||
warn(
|
warn(
|
||||||
|
@ -214,8 +212,11 @@ function doWatch(
|
||||||
// immediately watch or watchEffect
|
// immediately watch or watchEffect
|
||||||
extendOptions.once = true
|
extendOptions.once = true
|
||||||
} else {
|
} else {
|
||||||
// watch(source, cb)
|
const watchHandle: WatchHandle = () => {}
|
||||||
return NOOP
|
watchHandle.stop = NOOP
|
||||||
|
watchHandle.resume = NOOP
|
||||||
|
watchHandle.pause = NOOP
|
||||||
|
return watchHandle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,15 +227,19 @@ function doWatch(
|
||||||
|
|
||||||
const effect = baseWatch(source, cb, extend({}, options, extendOptions))
|
const effect = baseWatch(source, cb, extend({}, options, extendOptions))
|
||||||
const scope = getCurrentScope()
|
const scope = getCurrentScope()
|
||||||
const unwatch = () => {
|
const watchHandle: WatchHandle = () => {
|
||||||
effect!.stop()
|
effect.stop()
|
||||||
if (scope) {
|
if (scope) {
|
||||||
remove(scope.effects, effect)
|
remove(scope.effects, effect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__SSR__ && ssrCleanup) ssrCleanup.push(unwatch)
|
watchHandle.pause = effect.pause.bind(effect)
|
||||||
return unwatch
|
watchHandle.resume = effect.resume.bind(effect)
|
||||||
|
watchHandle.stop = watchHandle
|
||||||
|
|
||||||
|
if (__SSR__ && ssrCleanup) ssrCleanup.push(watchHandle)
|
||||||
|
return watchHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.$watch
|
// this.$watch
|
||||||
|
@ -243,7 +248,7 @@ export function instanceWatch(
|
||||||
source: string | Function,
|
source: string | Function,
|
||||||
value: WatchCallback | ObjectWatchOptionItem,
|
value: WatchCallback | ObjectWatchOptionItem,
|
||||||
options?: WatchOptions,
|
options?: WatchOptions,
|
||||||
): WatchStopHandle {
|
): WatchHandle {
|
||||||
const publicThis = this.proxy as any
|
const publicThis = this.proxy as any
|
||||||
const getter = isString(source)
|
const getter = isString(source)
|
||||||
? source.includes('.')
|
? source.includes('.')
|
||||||
|
@ -265,7 +270,7 @@ export function instanceWatch(
|
||||||
|
|
||||||
export function createPathGetter(ctx: any, path: string) {
|
export function createPathGetter(ctx: any, path: string) {
|
||||||
const segments = path.split('.')
|
const segments = path.split('.')
|
||||||
return () => {
|
return (): any => {
|
||||||
let cur = ctx
|
let cur = ctx
|
||||||
for (let i = 0; i < segments.length && cur; i++) {
|
for (let i = 0; i < segments.length && cur; i++) {
|
||||||
cur = cur[segments[i]]
|
cur = cur[segments[i]]
|
||||||
|
|
|
@ -431,7 +431,7 @@ const warnCount: Record<string, number> = Object.create(null)
|
||||||
// test only
|
// test only
|
||||||
let warningEnabled = true
|
let warningEnabled = true
|
||||||
|
|
||||||
export function toggleDeprecationWarning(flag: boolean) {
|
export function toggleDeprecationWarning(flag: boolean): void {
|
||||||
warningEnabled = flag
|
warningEnabled = flag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,7 +439,7 @@ export function warnDeprecation(
|
||||||
key: DeprecationTypes,
|
key: DeprecationTypes,
|
||||||
instance: ComponentInternalInstance | null,
|
instance: ComponentInternalInstance | null,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
) {
|
): void {
|
||||||
if (!__DEV__) {
|
if (!__DEV__) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -502,7 +502,7 @@ export const globalCompatConfig: CompatConfig = {
|
||||||
MODE: 2,
|
MODE: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function configureCompat(config: CompatConfig) {
|
export function configureCompat(config: CompatConfig): void {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
validateCompatConfig(config)
|
validateCompatConfig(config)
|
||||||
}
|
}
|
||||||
|
@ -516,7 +516,7 @@ const warnedInvalidKeys: Record<string, boolean> = {}
|
||||||
export function validateCompatConfig(
|
export function validateCompatConfig(
|
||||||
config: CompatConfig,
|
config: CompatConfig,
|
||||||
instance?: ComponentInternalInstance,
|
instance?: ComponentInternalInstance,
|
||||||
) {
|
): void {
|
||||||
if (seenConfigObjects.has(config)) {
|
if (seenConfigObjects.has(config)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -554,7 +554,7 @@ export function validateCompatConfig(
|
||||||
export function getCompatConfigForKey(
|
export function getCompatConfigForKey(
|
||||||
key: DeprecationTypes | 'MODE',
|
key: DeprecationTypes | 'MODE',
|
||||||
instance: ComponentInternalInstance | null,
|
instance: ComponentInternalInstance | null,
|
||||||
) {
|
): CompatConfig[DeprecationTypes | 'MODE'] {
|
||||||
const instanceConfig =
|
const instanceConfig =
|
||||||
instance && (instance.type as ComponentOptions).compatConfig
|
instance && (instance.type as ComponentOptions).compatConfig
|
||||||
if (instanceConfig && key in instanceConfig) {
|
if (instanceConfig && key in instanceConfig) {
|
||||||
|
@ -594,7 +594,7 @@ export function assertCompatEnabled(
|
||||||
key: DeprecationTypes,
|
key: DeprecationTypes,
|
||||||
instance: ComponentInternalInstance | null,
|
instance: ComponentInternalInstance | null,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
) {
|
): void {
|
||||||
if (!isCompatEnabled(key, instance)) {
|
if (!isCompatEnabled(key, instance)) {
|
||||||
throw new Error(`${key} compat has been disabled.`)
|
throw new Error(`${key} compat has been disabled.`)
|
||||||
} else if (__DEV__) {
|
} else if (__DEV__) {
|
||||||
|
@ -610,7 +610,7 @@ export function softAssertCompatEnabled(
|
||||||
key: DeprecationTypes,
|
key: DeprecationTypes,
|
||||||
instance: ComponentInternalInstance | null,
|
instance: ComponentInternalInstance | null,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
) {
|
): boolean {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
warnDeprecation(key, instance, ...args)
|
warnDeprecation(key, instance, ...args)
|
||||||
}
|
}
|
||||||
|
@ -626,7 +626,7 @@ export function checkCompatEnabled(
|
||||||
key: DeprecationTypes,
|
key: DeprecationTypes,
|
||||||
instance: ComponentInternalInstance | null,
|
instance: ComponentInternalInstance | null,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
) {
|
): boolean {
|
||||||
const enabled = isCompatEnabled(key, instance)
|
const enabled = isCompatEnabled(key, instance)
|
||||||
if (__DEV__ && enabled) {
|
if (__DEV__ && enabled) {
|
||||||
warnDeprecation(key, instance, ...args)
|
warnDeprecation(key, instance, ...args)
|
||||||
|
|
|
@ -23,7 +23,9 @@ const normalizedAsyncComponentMap = new WeakMap<
|
||||||
Component
|
Component
|
||||||
>()
|
>()
|
||||||
|
|
||||||
export function convertLegacyAsyncComponent(comp: LegacyAsyncComponent) {
|
export function convertLegacyAsyncComponent(
|
||||||
|
comp: LegacyAsyncComponent,
|
||||||
|
): Component {
|
||||||
if (normalizedAsyncComponentMap.has(comp)) {
|
if (normalizedAsyncComponentMap.has(comp)) {
|
||||||
return normalizedAsyncComponentMap.get(comp)!
|
return normalizedAsyncComponentMap.get(comp)!
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,9 @@ export const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertLegacyFunctionalComponent(comp: ComponentOptions) {
|
export function convertLegacyFunctionalComponent(
|
||||||
|
comp: ComponentOptions,
|
||||||
|
): FunctionalComponent {
|
||||||
if (normalizedFunctionalComponentMap.has(comp)) {
|
if (normalizedFunctionalComponentMap.has(comp)) {
|
||||||
return normalizedFunctionalComponentMap.get(comp)!
|
return normalizedFunctionalComponentMap.get(comp)!
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export const compatModelEventPrefix = `onModelCompat:`
|
||||||
|
|
||||||
const warnedTypes = new WeakSet()
|
const warnedTypes = new WeakSet()
|
||||||
|
|
||||||
export function convertLegacyVModelProps(vnode: VNode) {
|
export function convertLegacyVModelProps(vnode: VNode): void {
|
||||||
const { type, shapeFlag, props, dynamicProps } = vnode
|
const { type, shapeFlag, props, dynamicProps } = vnode
|
||||||
const comp = type as ComponentOptions
|
const comp = type as ComponentOptions
|
||||||
if (shapeFlag & ShapeFlags.COMPONENT && props && 'modelValue' in props) {
|
if (shapeFlag & ShapeFlags.COMPONENT && props && 'modelValue' in props) {
|
||||||
|
@ -68,7 +68,7 @@ export function compatModelEmit(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
event: string,
|
event: string,
|
||||||
args: any[],
|
args: any[],
|
||||||
) {
|
): void {
|
||||||
if (!isCompatEnabled(DeprecationTypes.COMPONENT_V_MODEL, instance)) {
|
if (!isCompatEnabled(DeprecationTypes.COMPONENT_V_MODEL, instance)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { isPlainObject } from '@vue/shared'
|
import { isPlainObject } from '@vue/shared'
|
||||||
import { DeprecationTypes, warnDeprecation } from './compatConfig'
|
import { DeprecationTypes, warnDeprecation } from './compatConfig'
|
||||||
|
|
||||||
export function deepMergeData(to: any, from: any) {
|
export function deepMergeData(to: any, from: any): any {
|
||||||
for (const key in from) {
|
for (const key in from) {
|
||||||
const toVal = to[key]
|
const toVal = to[key]
|
||||||
const fromVal = from[key]
|
const fromVal = from[key]
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue