mirror of https://github.com/vuejs/core.git
chore: Merge branch 'vapor' into edison/feat/vaporAsyncComponent
This commit is contained in:
commit
637b3cb9b3
|
@ -290,27 +290,39 @@ This is made possible via several configurations:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
|
vue["vue"]
|
||||||
compiler-sfc["@vue/compiler-sfc"]
|
compiler-sfc["@vue/compiler-sfc"]
|
||||||
compiler-dom["@vue/compiler-dom"]
|
compiler-dom["@vue/compiler-dom"]
|
||||||
|
compiler-vapor["@vue/compiler-vapor"]
|
||||||
compiler-core["@vue/compiler-core"]
|
compiler-core["@vue/compiler-core"]
|
||||||
vue["vue"]
|
|
||||||
runtime-dom["@vue/runtime-dom"]
|
runtime-dom["@vue/runtime-dom"]
|
||||||
|
runtime-vapor["@vue/runtime-vapor"]
|
||||||
runtime-core["@vue/runtime-core"]
|
runtime-core["@vue/runtime-core"]
|
||||||
reactivity["@vue/reactivity"]
|
reactivity["@vue/reactivity"]
|
||||||
|
|
||||||
subgraph "Runtime Packages"
|
subgraph "Runtime Packages"
|
||||||
runtime-dom --> runtime-core
|
runtime-dom --> runtime-core
|
||||||
|
runtime-vapor --> runtime-core
|
||||||
runtime-core --> reactivity
|
runtime-core --> reactivity
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph "Compiler Packages"
|
subgraph "Compiler Packages"
|
||||||
compiler-sfc --> compiler-core
|
compiler-sfc --> compiler-core
|
||||||
compiler-sfc --> compiler-dom
|
compiler-sfc --> compiler-dom
|
||||||
|
compiler-sfc --> compiler-vapor
|
||||||
compiler-dom --> compiler-core
|
compiler-dom --> compiler-core
|
||||||
|
compiler-vapor --> compiler-core
|
||||||
end
|
end
|
||||||
|
|
||||||
|
vue --> compiler-sfc
|
||||||
vue ---> compiler-dom
|
vue ---> compiler-dom
|
||||||
vue --> runtime-dom
|
vue --> runtime-dom
|
||||||
|
vue --> compiler-vapor
|
||||||
|
vue --> runtime-vapor
|
||||||
|
|
||||||
|
%% Highlight class
|
||||||
|
classDef highlight stroke:#35eb9a,stroke-width:3px;
|
||||||
|
class compiler-vapor,runtime-vapor highlight;
|
||||||
```
|
```
|
||||||
|
|
||||||
There are some rules to follow when importing across package boundaries:
|
There are some rules to follow when importing across package boundaries:
|
||||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
|
|
@ -17,7 +17,7 @@ jobs:
|
||||||
ref: minor
|
ref: minor
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
|
|
@ -15,7 +15,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
|
|
@ -25,7 +25,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
|
|
@ -25,7 +25,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
@ -37,7 +37,7 @@ jobs:
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Download Size Data
|
- name: Download Size Data
|
||||||
uses: dawidd6/action-download-artifact@v7
|
uses: dawidd6/action-download-artifact@v9
|
||||||
with:
|
with:
|
||||||
name: size-data
|
name: size-data
|
||||||
run_id: ${{ github.event.workflow_run.id }}
|
run_id: ${{ github.event.workflow_run.id }}
|
||||||
|
@ -56,7 +56,7 @@ jobs:
|
||||||
path: temp/size/base.txt
|
path: temp/size/base.txt
|
||||||
|
|
||||||
- name: Download Previous Size Data
|
- name: Download Previous Size Data
|
||||||
uses: dawidd6/action-download-artifact@v7
|
uses: dawidd6/action-download-artifact@v9
|
||||||
with:
|
with:
|
||||||
branch: ${{ steps.pr-base.outputs.content }}
|
branch: ${{ steps.pr-base.outputs.content }}
|
||||||
workflow: size-data.yml
|
workflow: size-data.yml
|
||||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
@ -35,7 +35,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
@ -63,7 +63,7 @@ jobs:
|
||||||
key: chromium-${{ hashFiles('pnpm-lock.yaml') }}
|
key: chromium-${{ hashFiles('pnpm-lock.yaml') }}
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
@ -114,7 +114,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
20
|
22.14.0
|
||||||
|
|
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -1,3 +1,41 @@
|
||||||
|
## [3.5.14](https://github.com/vuejs/core/compare/v3.5.13...v3.5.14) (2025-05-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compat:** correct deprecation message for v-bind.sync usage ([#13137](https://github.com/vuejs/core/issues/13137)) ([466b30f](https://github.com/vuejs/core/commit/466b30f4049ec89fb282624ec17d1a93472ab93f)), closes [#13133](https://github.com/vuejs/core/issues/13133)
|
||||||
|
* **compiler-core:** remove slot cache from parent renderCache during unmounting ([#13215](https://github.com/vuejs/core/issues/13215)) ([5d166f3](https://github.com/vuejs/core/commit/5d166f3796a03a497435fc079c6a83a4e9c6cf52))
|
||||||
|
* **compiler-sfc:** fix scope handling for props destructure in function parameters and catch clauses ([8e34357](https://github.com/vuejs/core/commit/8e3435779a667de485cf9efd78667d0ca14c5f84)), closes [#12790](https://github.com/vuejs/core/issues/12790)
|
||||||
|
* **compiler-sfc:** treat the return value of `useTemplateRef` as a definite ref ([#13197](https://github.com/vuejs/core/issues/13197)) ([8ae1122](https://github.com/vuejs/core/commit/8ae11226e8ee938615e17c7b81dc38ae3f7cefb9))
|
||||||
|
* **compiler:** fix spelling error in domTagConfig ([#13043](https://github.com/vuejs/core/issues/13043)) ([388295b](https://github.com/vuejs/core/commit/388295b27f3cc69eba25d325bbe60a36a3df831a))
|
||||||
|
* **customFormatter:** properly accessing ref value during debugger ([#12948](https://github.com/vuejs/core/issues/12948)) ([fdbd026](https://github.com/vuejs/core/commit/fdbd02658301dd794fe0c84f0018d080a07fca9f))
|
||||||
|
* **hmr/teleport:** adjust static children traversal for HMR in dev mode ([#12819](https://github.com/vuejs/core/issues/12819)) ([5e37dd0](https://github.com/vuejs/core/commit/5e37dd009562bcd8080a200c32abde2d6e4f0305)), closes [#12816](https://github.com/vuejs/core/issues/12816)
|
||||||
|
* **hmr:** avoid hydration for hmr root reload ([#12450](https://github.com/vuejs/core/issues/12450)) ([1f98a9c](https://github.com/vuejs/core/commit/1f98a9c493d01c21befa90107f0593bc92a58932)), closes [vitejs/vite-plugin-vue#146](https://github.com/vitejs/vite-plugin-vue/issues/146) [vitejs/vite-plugin-vue#477](https://github.com/vitejs/vite-plugin-vue/issues/477)
|
||||||
|
* **hmr:** avoid hydration for hmr updating ([#12262](https://github.com/vuejs/core/issues/12262)) ([9c4dbbc](https://github.com/vuejs/core/commit/9c4dbbc5185125835ad3e49baba303bd54676111)), closes [#7706](https://github.com/vuejs/core/issues/7706) [#8170](https://github.com/vuejs/core/issues/8170)
|
||||||
|
* **reactivity:** ensure markRaw objects are not reactive ([#12824](https://github.com/vuejs/core/issues/12824)) ([295b5ec](https://github.com/vuejs/core/commit/295b5ec19b6a52c4a56652cc4d6e93a4ea7c14ed)), closes [#12807](https://github.com/vuejs/core/issues/12807)
|
||||||
|
* **reactivity:** ensure multiple effectScope on() and off() calls maintains correct active scope ([22dcbf3](https://github.com/vuejs/core/commit/22dcbf3e20eb84f69c8952f6f70d9990136a4a68)), closes [#12631](https://github.com/vuejs/core/issues/12631) [#12632](https://github.com/vuejs/core/issues/12632) [#12641](https://github.com/vuejs/core/issues/12641)
|
||||||
|
* **reactivity:** should not recompute if computed does not track reactive data ([#12341](https://github.com/vuejs/core/issues/12341)) ([0b23fd2](https://github.com/vuejs/core/commit/0b23fd23833cf085e7e112bf4435cfc9b360d072)), closes [#12337](https://github.com/vuejs/core/issues/12337)
|
||||||
|
* **runtime-core:** stop tracking deps in setRef during unmount ([#13210](https://github.com/vuejs/core/issues/13210)) ([016c472](https://github.com/vuejs/core/commit/016c472bd2e7604b21c69dee1da8545ce26e4d2f))
|
||||||
|
* **runtime-core:** update __vnode of static nodes when patching along the optimized path ([#13223](https://github.com/vuejs/core/issues/13223)) ([b3ecee3](https://github.com/vuejs/core/commit/b3ecee3da8ed5c55dea89ce6b4b376b2b722b018))
|
||||||
|
* **runtime-core:** inherit comment nodes during block patch in production build ([#10748](https://github.com/vuejs/core/issues/10748)) ([6264505](https://github.com/vuejs/core/commit/626450590d81f79117b34d2a73073b1dc8f551bd)), closes [#10747](https://github.com/vuejs/core/issues/10747) [#12650](https://github.com/vuejs/core/issues/12650)
|
||||||
|
* **runtime-core:** prevent unmounted vnode from being inserted during transition leave ([#12862](https://github.com/vuejs/core/issues/12862)) ([d6a6ec1](https://github.com/vuejs/core/commit/d6a6ec13ce521683bfb2a22932778ef7b51f8600)), closes [#12860](https://github.com/vuejs/core/issues/12860)
|
||||||
|
* **runtime-core:** respect immutability for readonly reactive arrays in `v-for` ([#13091](https://github.com/vuejs/core/issues/13091)) ([3f27c58](https://github.com/vuejs/core/commit/3f27c58ffbd4309df369bc89493fdc284dc540bb)), closes [#13087](https://github.com/vuejs/core/issues/13087)
|
||||||
|
* **runtime-dom:** always treat autocorrect as attribute ([#13001](https://github.com/vuejs/core/issues/13001)) ([1499135](https://github.com/vuejs/core/commit/1499135c227236e037bb746beeb777941b0b58ff)), closes [#5705](https://github.com/vuejs/core/issues/5705)
|
||||||
|
* **slots:** properly warn if slot invoked in setup ([#12195](https://github.com/vuejs/core/issues/12195)) ([9196222](https://github.com/vuejs/core/commit/9196222ae1d63b52b35ac5fbf5e71494587ccf05)), closes [#12194](https://github.com/vuejs/core/issues/12194)
|
||||||
|
* **ssr:** properly init slots during ssr rendering ([#12441](https://github.com/vuejs/core/issues/12441)) ([2206cd2](https://github.com/vuejs/core/commit/2206cd235a1627c540e795e378b7564a55b47313)), closes [#12438](https://github.com/vuejs/core/issues/12438)
|
||||||
|
* **transition:** fix KeepAlive with transition out-in mode behavior in production ([#12468](https://github.com/vuejs/core/issues/12468)) ([343c891](https://github.com/vuejs/core/commit/343c89122448719bd6ed6bd9de986dfb2721d6bf)), closes [#12465](https://github.com/vuejs/core/issues/12465)
|
||||||
|
* **TransitionGroup:** reset prevChildren to prevent memory leak ([#13183](https://github.com/vuejs/core/issues/13183)) ([8b848cb](https://github.com/vuejs/core/commit/8b848cbbd2af337d23e19e202f9ab433f8580855)), closes [#13181](https://github.com/vuejs/core/issues/13181)
|
||||||
|
* **types:** allow return any for Options API lifecycle hooks ([#5914](https://github.com/vuejs/core/issues/5914)) ([06310e8](https://github.com/vuejs/core/commit/06310e82f5bed62d1b9733dcb18cd8d6edc988de))
|
||||||
|
* **types:** the directive's modifiers should be optional ([#12605](https://github.com/vuejs/core/issues/12605)) ([10e54dc](https://github.com/vuejs/core/commit/10e54dcc86a7967f3196d96200bcbd1d3d42082f))
|
||||||
|
* **typos:** fix comments referencing transformElement.ts ([#12551](https://github.com/vuejs/core/issues/12551))[ci-skip] ([11c053a](https://github.com/vuejs/core/commit/11c053a5429ad0d27a0e2c78b6b026ea00ace116))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **types:** add type TemplateRef ([#12645](https://github.com/vuejs/core/issues/12645)) ([636a861](https://github.com/vuejs/core/commit/636a8619f06c71dfd79f7f6412fd130c4f84226f))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [3.5.13](https://github.com/vuejs/core/compare/v3.5.12...v3.5.13) (2024-11-15)
|
## [3.5.13](https://github.com/vuejs/core/compare/v3.5.12...v3.5.13) (2024-11-15)
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +46,7 @@
|
||||||
* **custom-element:** avoid triggering mutationObserver when relecting props ([352bc88](https://github.com/vuejs/core/commit/352bc88c1bd2fda09c61ab17ea1a5967ffcd7bc0)), closes [#12214](https://github.com/vuejs/core/issues/12214) [#12215](https://github.com/vuejs/core/issues/12215)
|
* **custom-element:** avoid triggering mutationObserver when relecting props ([352bc88](https://github.com/vuejs/core/commit/352bc88c1bd2fda09c61ab17ea1a5967ffcd7bc0)), closes [#12214](https://github.com/vuejs/core/issues/12214) [#12215](https://github.com/vuejs/core/issues/12215)
|
||||||
* **deps:** update dependency postcss to ^8.4.48 ([#12356](https://github.com/vuejs/core/issues/12356)) ([b5ff930](https://github.com/vuejs/core/commit/b5ff930089985a58c3553977ef999cec2a6708a4))
|
* **deps:** update dependency postcss to ^8.4.48 ([#12356](https://github.com/vuejs/core/issues/12356)) ([b5ff930](https://github.com/vuejs/core/commit/b5ff930089985a58c3553977ef999cec2a6708a4))
|
||||||
* **hydration:** the component vnode's el should be updated when a mismatch occurs. ([#12255](https://github.com/vuejs/core/issues/12255)) ([a20a4cb](https://github.com/vuejs/core/commit/a20a4cb36a3e717d1f8f259d0d59f133f508ff0a)), closes [#12253](https://github.com/vuejs/core/issues/12253)
|
* **hydration:** the component vnode's el should be updated when a mismatch occurs. ([#12255](https://github.com/vuejs/core/issues/12255)) ([a20a4cb](https://github.com/vuejs/core/commit/a20a4cb36a3e717d1f8f259d0d59f133f508ff0a)), closes [#12253](https://github.com/vuejs/core/issues/12253)
|
||||||
* **reactiivty:** avoid unnecessary watcher effect removal from inactive scope ([2193284](https://github.com/vuejs/core/commit/21932840eae72ffcd357a62ec596aaecc7ec224a)), closes [#5783](https://github.com/vuejs/core/issues/5783) [#5806](https://github.com/vuejs/core/issues/5806)
|
* **reactivity:** avoid unnecessary watcher effect removal from inactive scope ([2193284](https://github.com/vuejs/core/commit/21932840eae72ffcd357a62ec596aaecc7ec224a)), closes [#5783](https://github.com/vuejs/core/issues/5783) [#5806](https://github.com/vuejs/core/issues/5806)
|
||||||
* **reactivity:** release nested effects/scopes on effect scope stop ([#12373](https://github.com/vuejs/core/issues/12373)) ([bee2f5e](https://github.com/vuejs/core/commit/bee2f5ee62dc0cd04123b737779550726374dd0a)), closes [#12370](https://github.com/vuejs/core/issues/12370)
|
* **reactivity:** release nested effects/scopes on effect scope stop ([#12373](https://github.com/vuejs/core/issues/12373)) ([bee2f5e](https://github.com/vuejs/core/commit/bee2f5ee62dc0cd04123b737779550726374dd0a)), closes [#12370](https://github.com/vuejs/core/issues/12370)
|
||||||
* **runtime-dom:** set css vars before user onMounted hooks ([2d5c5e2](https://github.com/vuejs/core/commit/2d5c5e25e9b7a56e883674fb434135ac514429b5)), closes [#11533](https://github.com/vuejs/core/issues/11533)
|
* **runtime-dom:** set css vars before user onMounted hooks ([2d5c5e2](https://github.com/vuejs/core/commit/2d5c5e25e9b7a56e883674fb434135ac514429b5)), closes [#11533](https://github.com/vuejs/core/issues/11533)
|
||||||
* **runtime-dom:** set css vars on update to handle child forcing reflow in onMount ([#11561](https://github.com/vuejs/core/issues/11561)) ([c4312f9](https://github.com/vuejs/core/commit/c4312f9c715c131a09e552ba46e9beb4b36d55e6))
|
* **runtime-dom:** set css vars on update to handle child forcing reflow in onMount ([#11561](https://github.com/vuejs/core/issues/11561)) ([c4312f9](https://github.com/vuejs/core/commit/c4312f9c715c131a09e552ba46e9beb4b36d55e6))
|
||||||
|
|
|
@ -34,7 +34,8 @@ Please make sure to respect issue requirements and use [the new issue helper](ht
|
||||||
|
|
||||||
## Stay In Touch
|
## Stay In Touch
|
||||||
|
|
||||||
- [Twitter](https://twitter.com/vuejs)
|
- [X](https://x.com/vuejs)
|
||||||
|
- [Bluesky](https://bsky.app/profile/vuejs.org)
|
||||||
- [Blog](https://blog.vuejs.org/)
|
- [Blog](https://blog.vuejs.org/)
|
||||||
- [Job Board](https://vuejobs.com/?ref=vuejs)
|
- [Job Board](https://vuejobs.com/?ref=vuejs)
|
||||||
|
|
||||||
|
@ -44,7 +45,9 @@ Please make sure to read the [Contributing Guide](https://github.com/vuejs/core/
|
||||||
|
|
||||||
Thank you to all the people who already contributed to Vue!
|
Thank you to all the people who already contributed to Vue!
|
||||||
|
|
||||||
<a href="https://github.com/vuejs/core/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890" /></a>
|
<a href="https://github.com/vuejs/core/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890&limit=500" /></a>
|
||||||
|
|
||||||
|
<sub>_Note: Showing the first 500 contributors only due to GitHub image size limitations_</sub>
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[build.environment]
|
[build.environment]
|
||||||
NODE_VERSION = "18"
|
NODE_VERSION = "22"
|
||||||
NPM_FLAGS = "--version" # prevent Netlify npm install
|
NPM_FLAGS = "--version" # prevent Netlify npm install
|
||||||
|
|
57
package.json
57
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"packageManager": "pnpm@9.15.4",
|
"packageManager": "pnpm@10.9.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node scripts/dev.js",
|
"dev": "node scripts/dev.js",
|
||||||
|
@ -65,63 +65,52 @@
|
||||||
"@babel/parser": "catalog:",
|
"@babel/parser": "catalog:",
|
||||||
"@babel/types": "catalog:",
|
"@babel/types": "catalog:",
|
||||||
"@rollup/plugin-alias": "^5.1.1",
|
"@rollup/plugin-alias": "^5.1.1",
|
||||||
"@rollup/plugin-commonjs": "^28.0.2",
|
"@rollup/plugin-commonjs": "^28.0.3",
|
||||||
"@rollup/plugin-json": "^6.1.0",
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^16.0.0",
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||||
"@rollup/plugin-replace": "5.0.4",
|
"@rollup/plugin-replace": "5.0.4",
|
||||||
"@swc/core": "^1.10.8",
|
"@swc/core": "^1.11.24",
|
||||||
"@types/hash-sum": "^1.0.2",
|
"@types/hash-sum": "^1.0.2",
|
||||||
"@types/node": "^22.10.7",
|
"@types/node": "^22.14.1",
|
||||||
"@types/semver": "^7.5.8",
|
"@types/semver": "^7.7.0",
|
||||||
"@types/serve-handler": "^6.1.4",
|
"@types/serve-handler": "^6.1.4",
|
||||||
"@vitest/coverage-v8": "^3.0.2",
|
|
||||||
"@vitest/ui": "^3.0.2",
|
"@vitest/ui": "^3.0.2",
|
||||||
|
"@vitest/coverage-v8": "^3.1.3",
|
||||||
|
"@vitest/eslint-plugin": "^1.1.44",
|
||||||
"@vue/consolidate": "1.0.0",
|
"@vue/consolidate": "1.0.0",
|
||||||
"conventional-changelog-cli": "^5.0.0",
|
"conventional-changelog-cli": "^5.0.0",
|
||||||
"enquirer": "^2.4.1",
|
"enquirer": "^2.4.1",
|
||||||
"esbuild": "^0.24.2",
|
"esbuild": "^0.25.4",
|
||||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||||
"eslint": "^9.18.0",
|
"eslint": "^9.25.1",
|
||||||
"eslint-plugin-import-x": "^4.6.1",
|
"eslint-plugin-import-x": "^4.11.0",
|
||||||
"@vitest/eslint-plugin": "^1.1.25",
|
|
||||||
"estree-walker": "catalog:",
|
"estree-walker": "catalog:",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.1.0",
|
||||||
"lint-staged": "^15.4.1",
|
"lint-staged": "^15.5.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"magic-string": "^0.30.17",
|
"magic-string": "^0.30.17",
|
||||||
"markdown-table": "^3.0.4",
|
"markdown-table": "^3.0.4",
|
||||||
"marked": "13.0.3",
|
"marked": "13.0.3",
|
||||||
"npm-run-all2": "^7.0.2",
|
"npm-run-all2": "^7.0.2",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.5.3",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3",
|
||||||
"puppeteer": "~24.1.0",
|
"puppeteer": "~24.8.2",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"rollup": "^4.31.0",
|
"rollup": "^4.40.2",
|
||||||
"rollup-plugin-dts": "^6.1.1",
|
"rollup-plugin-dts": "^6.2.1",
|
||||||
"rollup-plugin-esbuild": "^6.1.1",
|
"rollup-plugin-esbuild": "^6.2.1",
|
||||||
"rollup-plugin-polyfill-node": "^0.13.0",
|
"rollup-plugin-polyfill-node": "^0.13.0",
|
||||||
"semver": "^7.6.3",
|
"semver": "^7.7.1",
|
||||||
"serve": "^14.2.4",
|
"serve": "^14.2.4",
|
||||||
"serve-handler": "^6.1.6",
|
"serve-handler": "^6.1.6",
|
||||||
"simple-git-hooks": "^2.11.1",
|
"simple-git-hooks": "^2.13.0",
|
||||||
"todomvc-app-css": "^2.4.3",
|
"todomvc-app-css": "^2.4.3",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "~5.6.2",
|
"typescript": "~5.6.2",
|
||||||
"typescript-eslint": "^8.20.0",
|
"typescript-eslint": "^8.31.1",
|
||||||
"vite": "catalog:",
|
"vite": "catalog:",
|
||||||
"vitest": "^3.0.2"
|
"vitest": "^3.1.3"
|
||||||
},
|
|
||||||
"pnpm": {
|
|
||||||
"peerDependencyRules": {
|
|
||||||
"allowedVersions": {
|
|
||||||
"typescript-eslint>eslint": "^9.0.0",
|
|
||||||
"@typescript-eslint/eslint-plugin>eslint": "^9.0.0",
|
|
||||||
"@typescript-eslint/parser>eslint": "^9.0.0",
|
|
||||||
"@typescript-eslint/type-utils>eslint": "^9.0.0",
|
|
||||||
"@typescript-eslint/utils>eslint": "^9.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ app.directive<HTMLElement, string, 'prevent' | 'stop', 'arg1' | 'arg2'>(
|
||||||
mounted(el, binding) {
|
mounted(el, binding) {
|
||||||
expectType<HTMLElement>(el)
|
expectType<HTMLElement>(el)
|
||||||
expectType<string>(binding.value)
|
expectType<string>(binding.value)
|
||||||
expectType<{ prevent: boolean; stop: boolean }>(binding.modifiers)
|
expectType<{ prevent?: boolean; stop?: boolean }>(binding.modifiers)
|
||||||
expectType<'arg1' | 'arg2'>(binding.arg!)
|
expectType<'arg1' | 'arg2'>(binding.arg!)
|
||||||
|
|
||||||
// @ts-expect-error not any
|
// @ts-expect-error not any
|
||||||
|
|
|
@ -12,8 +12,11 @@ app.use(PluginWithoutType, 2)
|
||||||
app.use(PluginWithoutType, { anything: 'goes' }, true)
|
app.use(PluginWithoutType, { anything: 'goes' }, true)
|
||||||
|
|
||||||
type PluginOptions = {
|
type PluginOptions = {
|
||||||
|
/** option1 */
|
||||||
option1?: string
|
option1?: string
|
||||||
|
/** option2 */
|
||||||
option2: number
|
option2: number
|
||||||
|
/** option3 */
|
||||||
option3: boolean
|
option3: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +28,20 @@ const PluginWithObjectOptions = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const objectPluginOptional = {
|
||||||
|
install(app: App, options?: PluginOptions) {},
|
||||||
|
}
|
||||||
|
app.use(objectPluginOptional)
|
||||||
|
app.use(
|
||||||
|
objectPluginOptional,
|
||||||
|
// Test JSDoc and `go to definition` for options
|
||||||
|
{
|
||||||
|
option1: 'foo',
|
||||||
|
option2: 1,
|
||||||
|
option3: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
for (const Plugin of [
|
for (const Plugin of [
|
||||||
PluginWithObjectOptions,
|
PluginWithObjectOptions,
|
||||||
PluginWithObjectOptions.install,
|
PluginWithObjectOptions.install,
|
||||||
|
@ -92,7 +109,27 @@ const PluginTyped: Plugin<PluginOptions> = (app, options) => {}
|
||||||
|
|
||||||
// @ts-expect-error: needs options
|
// @ts-expect-error: needs options
|
||||||
app.use(PluginTyped)
|
app.use(PluginTyped)
|
||||||
app.use(PluginTyped, { option2: 2, option3: true })
|
app.use(
|
||||||
|
PluginTyped,
|
||||||
|
// Test autocomplete for options
|
||||||
|
{
|
||||||
|
option1: '',
|
||||||
|
option2: 2,
|
||||||
|
option3: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const functionPluginOptional = (app: App, options?: PluginOptions) => {}
|
||||||
|
app.use(functionPluginOptional)
|
||||||
|
app.use(functionPluginOptional, { option2: 2, option3: true })
|
||||||
|
|
||||||
|
// type optional params
|
||||||
|
const functionPluginOptional2: Plugin<[options?: PluginOptions]> = (
|
||||||
|
app,
|
||||||
|
options,
|
||||||
|
) => {}
|
||||||
|
app.use(functionPluginOptional2)
|
||||||
|
app.use(functionPluginOptional2, { option2: 2, option3: true })
|
||||||
|
|
||||||
// vuetify usage
|
// vuetify usage
|
||||||
const key: string = ''
|
const key: string = ''
|
||||||
|
|
|
@ -29,7 +29,7 @@ describe('custom', () => {
|
||||||
value: number
|
value: number
|
||||||
oldValue: number | null
|
oldValue: number | null
|
||||||
arg?: 'Arg'
|
arg?: 'Arg'
|
||||||
modifiers: Record<'a' | 'b', boolean>
|
modifiers: Partial<Record<'a' | 'b', boolean>>
|
||||||
}>(testDirective<number, 'a' | 'b', 'Arg'>())
|
}>(testDirective<number, 'a' | 'b', 'Arg'>())
|
||||||
|
|
||||||
expectType<{
|
expectType<{
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
type MaybeRefOrGetter,
|
type MaybeRefOrGetter,
|
||||||
type Ref,
|
type Ref,
|
||||||
type ShallowRef,
|
type ShallowRef,
|
||||||
|
type TemplateRef,
|
||||||
type ToRefs,
|
type ToRefs,
|
||||||
type WritableComputedRef,
|
type WritableComputedRef,
|
||||||
computed,
|
computed,
|
||||||
|
@ -535,7 +536,7 @@ expectType<string>(toValue(unref2))
|
||||||
|
|
||||||
// useTemplateRef
|
// useTemplateRef
|
||||||
const tRef = useTemplateRef('foo')
|
const tRef = useTemplateRef('foo')
|
||||||
expectType<Readonly<ShallowRef<unknown>>>(tRef)
|
expectType<TemplateRef>(tRef)
|
||||||
|
|
||||||
const tRef2 = useTemplateRef<HTMLElement>('bar')
|
const tRef2 = useTemplateRef<HTMLElement>('bar')
|
||||||
expectType<Readonly<ShallowRef<HTMLElement | null>>>(tRef2)
|
expectType<TemplateRef<HTMLElement>>(tRef2)
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"isolatedDeclarations": false,
|
"isolatedDeclarations": false,
|
||||||
"allowJs": true
|
"allowJs": true
|
||||||
},
|
},
|
||||||
"include": ["./**/*", "../packages/*/src"]
|
"include": ["./**/*", "../../packages/*/src"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,8 +182,9 @@ onMounted(() => {
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
font-family:
|
||||||
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||||
|
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
--base: #444;
|
--base: #444;
|
||||||
--nav-height: 50px;
|
--nav-height: 50px;
|
||||||
|
|
|
@ -17,7 +17,10 @@ export async function downloadProject(store: ReplStore) {
|
||||||
|
|
||||||
// basic structure
|
// basic structure
|
||||||
zip.file('index.html', index)
|
zip.file('index.html', index)
|
||||||
zip.file('package.json', pkg)
|
zip.file(
|
||||||
|
'package.json',
|
||||||
|
pkg.replace(`"vue": "latest"`, `"vue": "${store.vueVersion || 'latest'}"`),
|
||||||
|
)
|
||||||
zip.file('vite.config.js', config)
|
zip.file('vite.config.js', config)
|
||||||
zip.file('README.md', readme)
|
zip.file('README.md', readme)
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
"serve": "vite preview"
|
"serve": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.4.0"
|
"vue": "latest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.4",
|
||||||
"vite": "^6.0.7"
|
"vite": "^6.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
font-family:
|
||||||
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||||
|
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
--bg: #1d1f21;
|
--bg: #1d1f21;
|
||||||
--border: #333;
|
--border: #333;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,6 +170,11 @@ describe('compiler: cacheStatic transform', () => {
|
||||||
{
|
{
|
||||||
/* _ slot flag */
|
/* _ slot flag */
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_PROPERTY,
|
||||||
|
key: { content: '__' },
|
||||||
|
value: { content: '[0]' },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -197,6 +202,11 @@ describe('compiler: cacheStatic transform', () => {
|
||||||
{
|
{
|
||||||
/* _ slot flag */
|
/* _ slot flag */
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_PROPERTY,
|
||||||
|
key: { content: '__' },
|
||||||
|
value: { content: '[0]' },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/compiler-core",
|
"name": "@vue/compiler-core",
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"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",
|
||||||
|
|
|
@ -17,6 +17,7 @@ export {
|
||||||
createTransformContext,
|
createTransformContext,
|
||||||
traverseNode,
|
traverseNode,
|
||||||
createStructuralDirectiveTransform,
|
createStructuralDirectiveTransform,
|
||||||
|
getSelfName,
|
||||||
type NodeTransform,
|
type NodeTransform,
|
||||||
type StructuralDirectiveTransform,
|
type StructuralDirectiveTransform,
|
||||||
type DirectiveTransform,
|
type DirectiveTransform,
|
||||||
|
|
|
@ -388,7 +388,7 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
CompilerDeprecationTypes.COMPILER_V_BIND_SYNC,
|
CompilerDeprecationTypes.COMPILER_V_BIND_SYNC,
|
||||||
currentOptions,
|
currentOptions,
|
||||||
currentProp.loc,
|
currentProp.loc,
|
||||||
currentProp.rawName,
|
currentProp.arg!.loc.source,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
currentProp.name = 'model'
|
currentProp.name = 'model'
|
||||||
|
|
|
@ -123,6 +123,11 @@ export interface TransformContext
|
||||||
filters?: Set<string>
|
filters?: Set<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSelfName(filename: string): string | null {
|
||||||
|
const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
|
||||||
|
return nameMatch ? capitalize(camelize(nameMatch[1])) : null
|
||||||
|
}
|
||||||
|
|
||||||
export function createTransformContext(
|
export function createTransformContext(
|
||||||
root: RootNode,
|
root: RootNode,
|
||||||
{
|
{
|
||||||
|
@ -150,11 +155,10 @@ export function createTransformContext(
|
||||||
compatConfig,
|
compatConfig,
|
||||||
}: TransformOptions,
|
}: TransformOptions,
|
||||||
): TransformContext {
|
): TransformContext {
|
||||||
const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
|
|
||||||
const context: TransformContext = {
|
const context: TransformContext = {
|
||||||
// options
|
// options
|
||||||
filename,
|
filename,
|
||||||
selfName: nameMatch && capitalize(camelize(nameMatch[1])),
|
selfName: getSelfName(filename),
|
||||||
prefixIdentifiers,
|
prefixIdentifiers,
|
||||||
hoistStatic,
|
hoistStatic,
|
||||||
hmr,
|
hmr,
|
||||||
|
|
|
@ -12,11 +12,14 @@ import {
|
||||||
type RootNode,
|
type RootNode,
|
||||||
type SimpleExpressionNode,
|
type SimpleExpressionNode,
|
||||||
type SlotFunctionExpression,
|
type SlotFunctionExpression,
|
||||||
|
type SlotsObjectProperty,
|
||||||
type TemplateChildNode,
|
type TemplateChildNode,
|
||||||
type TemplateNode,
|
type TemplateNode,
|
||||||
type TextCallNode,
|
type TextCallNode,
|
||||||
type VNodeCall,
|
type VNodeCall,
|
||||||
createArrayExpression,
|
createArrayExpression,
|
||||||
|
createObjectProperty,
|
||||||
|
createSimpleExpression,
|
||||||
getVNodeBlockHelper,
|
getVNodeBlockHelper,
|
||||||
getVNodeHelper,
|
getVNodeHelper,
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
|
@ -140,6 +143,7 @@ function walk(
|
||||||
}
|
}
|
||||||
|
|
||||||
let cachedAsArray = false
|
let cachedAsArray = false
|
||||||
|
const slotCacheKeys = []
|
||||||
if (toCache.length === children.length && node.type === NodeTypes.ELEMENT) {
|
if (toCache.length === children.length && node.type === NodeTypes.ELEMENT) {
|
||||||
if (
|
if (
|
||||||
node.tagType === ElementTypes.ELEMENT &&
|
node.tagType === ElementTypes.ELEMENT &&
|
||||||
|
@ -163,6 +167,7 @@ function walk(
|
||||||
// default slot
|
// default slot
|
||||||
const slot = getSlotNode(node.codegenNode, 'default')
|
const slot = getSlotNode(node.codegenNode, 'default')
|
||||||
if (slot) {
|
if (slot) {
|
||||||
|
slotCacheKeys.push(context.cached.length)
|
||||||
slot.returns = getCacheExpression(
|
slot.returns = getCacheExpression(
|
||||||
createArrayExpression(slot.returns as TemplateChildNode[]),
|
createArrayExpression(slot.returns as TemplateChildNode[]),
|
||||||
)
|
)
|
||||||
|
@ -186,6 +191,7 @@ function walk(
|
||||||
slotName.arg &&
|
slotName.arg &&
|
||||||
getSlotNode(parent.codegenNode, slotName.arg)
|
getSlotNode(parent.codegenNode, slotName.arg)
|
||||||
if (slot) {
|
if (slot) {
|
||||||
|
slotCacheKeys.push(context.cached.length)
|
||||||
slot.returns = getCacheExpression(
|
slot.returns = getCacheExpression(
|
||||||
createArrayExpression(slot.returns as TemplateChildNode[]),
|
createArrayExpression(slot.returns as TemplateChildNode[]),
|
||||||
)
|
)
|
||||||
|
@ -196,10 +202,31 @@ function walk(
|
||||||
|
|
||||||
if (!cachedAsArray) {
|
if (!cachedAsArray) {
|
||||||
for (const child of toCache) {
|
for (const child of toCache) {
|
||||||
|
slotCacheKeys.push(context.cached.length)
|
||||||
child.codegenNode = context.cache(child.codegenNode!)
|
child.codegenNode = context.cache(child.codegenNode!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// put the slot cached keys on the slot object, so that the cache
|
||||||
|
// can be removed when component unmounting to prevent memory leaks
|
||||||
|
if (
|
||||||
|
slotCacheKeys.length &&
|
||||||
|
node.type === NodeTypes.ELEMENT &&
|
||||||
|
node.tagType === ElementTypes.COMPONENT &&
|
||||||
|
node.codegenNode &&
|
||||||
|
node.codegenNode.type === NodeTypes.VNODE_CALL &&
|
||||||
|
node.codegenNode.children &&
|
||||||
|
!isArray(node.codegenNode.children) &&
|
||||||
|
node.codegenNode.children.type === NodeTypes.JS_OBJECT_EXPRESSION
|
||||||
|
) {
|
||||||
|
node.codegenNode.children.properties.push(
|
||||||
|
createObjectProperty(
|
||||||
|
`__`,
|
||||||
|
createSimpleExpression(JSON.stringify(slotCacheKeys), false),
|
||||||
|
) as SlotsObjectProperty,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function getCacheExpression(value: JSChildNode): CacheExpression {
|
function getCacheExpression(value: JSChildNode): CacheExpression {
|
||||||
const exp = context.cache(value)
|
const exp = context.cache(value)
|
||||||
// #6978, #7138, #7114
|
// #6978, #7138, #7114
|
||||||
|
|
|
@ -342,7 +342,6 @@ export function buildSlots(
|
||||||
: hasForwardedSlots(node.children)
|
: hasForwardedSlots(node.children)
|
||||||
? SlotFlags.FORWARDED
|
? SlotFlags.FORWARDED
|
||||||
: SlotFlags.STABLE
|
: SlotFlags.STABLE
|
||||||
|
|
||||||
let slots = createObjectExpression(
|
let slots = createObjectExpression(
|
||||||
slotsProperties.concat(
|
slotsProperties.concat(
|
||||||
createObjectProperty(
|
createObjectProperty(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/compiler-dom",
|
"name": "@vue/compiler-dom",
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"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",
|
||||||
|
|
|
@ -196,6 +196,25 @@ return () => {}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`sfc reactive props destructure > handle function parameters with same name as destructured props 1`] = `
|
||||||
|
"
|
||||||
|
export default {
|
||||||
|
setup(__props) {
|
||||||
|
|
||||||
|
|
||||||
|
function test(value) {
|
||||||
|
try {
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(__props.value)
|
||||||
|
|
||||||
|
return () => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`sfc reactive props destructure > multi-variable declaration 1`] = `
|
exports[`sfc reactive props destructure > multi-variable declaration 1`] = `
|
||||||
"
|
"
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -360,6 +360,22 @@ describe('sfc reactive props destructure', () => {
|
||||||
expect(content).toMatch(`props: ['item'],`)
|
expect(content).toMatch(`props: ['item'],`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('handle function parameters with same name as destructured props', () => {
|
||||||
|
const { content } = compile(`
|
||||||
|
<script setup>
|
||||||
|
const { value } = defineProps()
|
||||||
|
function test(value) {
|
||||||
|
try {
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(value)
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
assertCode(content)
|
||||||
|
expect(content).toMatch(`console.log(__props.value)`)
|
||||||
|
})
|
||||||
|
|
||||||
test('defineProps/defineEmits in multi-variable declaration (full removal)', () => {
|
test('defineProps/defineEmits in multi-variable declaration (full removal)', () => {
|
||||||
const { content } = compile(`
|
const { content } = compile(`
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
|
@ -211,38 +211,42 @@ color: red
|
||||||
expect(
|
expect(
|
||||||
compileScoped(`.div { color: red; } .div:where(:hover) { color: blue; }`),
|
compileScoped(`.div { color: red; } .div:where(:hover) { color: blue; }`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
".div[data-v-test] { color: red;
|
".div[data-v-test] { color: red;
|
||||||
}
|
}
|
||||||
.div[data-v-test]:where(:hover) { color: blue;
|
.div[data-v-test]:where(:hover) { color: blue;
|
||||||
}"`)
|
}"
|
||||||
|
`)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
compileScoped(`.div { color: red; } .div:is(:hover) { color: blue; }`),
|
compileScoped(`.div { color: red; } .div:is(:hover) { color: blue; }`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
".div[data-v-test] { color: red;
|
".div[data-v-test] { color: red;
|
||||||
}
|
}
|
||||||
.div[data-v-test]:is(:hover) { color: blue;
|
.div[data-v-test]:is(:hover) { color: blue;
|
||||||
}"`)
|
}"
|
||||||
|
`)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
compileScoped(
|
compileScoped(
|
||||||
`.div { color: red; } .div:where(.foo:hover) { color: blue; }`,
|
`.div { color: red; } .div:where(.foo:hover) { color: blue; }`,
|
||||||
),
|
),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
".div[data-v-test] { color: red;
|
".div[data-v-test] { color: red;
|
||||||
}
|
}
|
||||||
.div[data-v-test]:where(.foo:hover) { color: blue;
|
.div[data-v-test]:where(.foo:hover) { color: blue;
|
||||||
}"`)
|
}"
|
||||||
|
`)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
compileScoped(
|
compileScoped(
|
||||||
`.div { color: red; } .div:is(.foo:hover) { color: blue; }`,
|
`.div { color: red; } .div:is(.foo:hover) { color: blue; }`,
|
||||||
),
|
),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
".div[data-v-test] { color: red;
|
".div[data-v-test] { color: red;
|
||||||
}
|
}
|
||||||
.div[data-v-test]:is(.foo:hover) { color: blue;
|
.div[data-v-test]:is(.foo:hover) { color: blue;
|
||||||
}"`)
|
}"
|
||||||
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('media query', () => {
|
test('media query', () => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/compiler-sfc",
|
"name": "@vue/compiler-sfc",
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"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",
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
"@vue/shared": "workspace:*",
|
"@vue/shared": "workspace:*",
|
||||||
"estree-walker": "catalog:",
|
"estree-walker": "catalog:",
|
||||||
"magic-string": "catalog:",
|
"magic-string": "catalog:",
|
||||||
"postcss": "^8.5.1",
|
"postcss": "^8.5.3",
|
||||||
"source-map-js": "catalog:"
|
"source-map-js": "catalog:"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -61,8 +61,8 @@
|
||||||
"merge-source-map": "^1.1.0",
|
"merge-source-map": "^1.1.0",
|
||||||
"minimatch": "~10.0.1",
|
"minimatch": "~10.0.1",
|
||||||
"postcss-modules": "^6.0.1",
|
"postcss-modules": "^6.0.1",
|
||||||
"postcss-selector-parser": "^7.0.0",
|
"postcss-selector-parser": "^7.1.0",
|
||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3",
|
||||||
"sass": "^1.83.4"
|
"sass": "^1.86.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1120,6 +1120,7 @@ function walkDeclaration(
|
||||||
m === userImportAliases['shallowRef'] ||
|
m === userImportAliases['shallowRef'] ||
|
||||||
m === userImportAliases['customRef'] ||
|
m === userImportAliases['customRef'] ||
|
||||||
m === userImportAliases['toRef'] ||
|
m === userImportAliases['toRef'] ||
|
||||||
|
m === userImportAliases['useTemplateRef'] ||
|
||||||
m === DEFINE_MODEL,
|
m === DEFINE_MODEL,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -39,7 +39,7 @@ export function rewriteDefaultAST(
|
||||||
ast.forEach(node => {
|
ast.forEach(node => {
|
||||||
if (node.type === 'ExportDefaultDeclaration') {
|
if (node.type === 'ExportDefaultDeclaration') {
|
||||||
if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
|
if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
|
||||||
let start: number =
|
const start: number =
|
||||||
node.declaration.decorators && node.declaration.decorators.length > 0
|
node.declaration.decorators && node.declaration.decorators.length > 0
|
||||||
? node.declaration.decorators[
|
? node.declaration.decorators[
|
||||||
node.declaration.decorators.length - 1
|
node.declaration.decorators.length - 1
|
||||||
|
|
|
@ -291,7 +291,8 @@ export function transformDestructuredProps(
|
||||||
parent && parentStack.pop()
|
parent && parentStack.pop()
|
||||||
if (
|
if (
|
||||||
(node.type === 'BlockStatement' && !isFunctionType(parent!)) ||
|
(node.type === 'BlockStatement' && !isFunctionType(parent!)) ||
|
||||||
isFunctionType(node)
|
isFunctionType(node) ||
|
||||||
|
node.type === 'CatchClause'
|
||||||
) {
|
) {
|
||||||
popScope()
|
popScope()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/compiler-ssr",
|
"name": "@vue/compiler-ssr",
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"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",
|
||||||
|
|
|
@ -149,7 +149,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
|
exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
|
||||||
"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
|
"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||||
const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>")
|
const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>")
|
||||||
const t1 = _template("<div> </div>")
|
const t1 = _template("<div> </div>")
|
||||||
|
|
||||||
|
@ -157,12 +157,12 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n0 = t0()
|
const n0 = t0()
|
||||||
const n3 = t1()
|
const n3 = t1()
|
||||||
|
const n2 = _child(n3)
|
||||||
_setInsertionState(n3, 0)
|
_setInsertionState(n3, 0)
|
||||||
const n1 = _createComponentWithFallback(_component_Comp)
|
const n1 = _createComponentWithFallback(_component_Comp)
|
||||||
const n2 = _child(n3)
|
|
||||||
_renderEffect(() => {
|
_renderEffect(() => {
|
||||||
_setText(n2, _toDisplayString(_ctx.bar))
|
|
||||||
_setProp(n3, "id", _ctx.foo)
|
_setProp(n3, "id", _ctx.foo)
|
||||||
|
_setText(n2, _toDisplayString(_ctx.bar))
|
||||||
})
|
})
|
||||||
return [n0, n3]
|
return [n0, n3]
|
||||||
}"
|
}"
|
||||||
|
@ -180,7 +180,7 @@ export function render(_ctx) {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compile > dynamic root nodes and interpolation 1`] = `
|
exports[`compile > dynamic root nodes and interpolation 1`] = `
|
||||||
"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue';
|
"import { child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue';
|
||||||
const t0 = _template("<button> </button>", true)
|
const t0 = _template("<button> </button>", true)
|
||||||
_delegateEvents("click")
|
_delegateEvents("click")
|
||||||
|
|
||||||
|
@ -190,13 +190,47 @@ export function render(_ctx) {
|
||||||
n0.$evtclick = e => _ctx.handleClick(e)
|
n0.$evtclick = e => _ctx.handleClick(e)
|
||||||
_renderEffect(() => {
|
_renderEffect(() => {
|
||||||
const _count = _ctx.count
|
const _count = _ctx.count
|
||||||
_setText(x0, _toDisplayString(_count) + "foo" + _toDisplayString(_count) + "foo" + _toDisplayString(_count))
|
|
||||||
_setProp(n0, "id", _count)
|
_setProp(n0, "id", _count)
|
||||||
|
_setText(x0, _toDisplayString(_count) + "foo" + _toDisplayString(_count) + "foo" + _toDisplayString(_count))
|
||||||
})
|
})
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compile > execution order > basic 1`] = `
|
||||||
|
"import { child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<div> </div>", true)
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const n0 = t0()
|
||||||
|
const x0 = _child(n0)
|
||||||
|
_renderEffect(() => {
|
||||||
|
_setProp(n0, "id", _ctx.foo)
|
||||||
|
_setText(x0, _toDisplayString(_ctx.bar))
|
||||||
|
})
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compile > execution order > with v-once 1`] = `
|
||||||
|
"import { child as _child, next as _next, nthChild as _nthChild, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<div><span> </span> <br> </div>", true)
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const n3 = t0()
|
||||||
|
const n0 = _child(n3)
|
||||||
|
const n1 = _next(n0)
|
||||||
|
const n2 = _nthChild(n3, 3)
|
||||||
|
const x0 = _child(n0)
|
||||||
|
_setText(x0, _toDisplayString(_ctx.foo))
|
||||||
|
_renderEffect(() => {
|
||||||
|
_setText(n1, " " + _toDisplayString(_ctx.bar))
|
||||||
|
_setText(n2, " " + _toDisplayString(_ctx.baz))
|
||||||
|
})
|
||||||
|
return n3
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compile > expression parsing > interpolation 1`] = `
|
exports[`compile > expression parsing > interpolation 1`] = `
|
||||||
"
|
"
|
||||||
const n0 = t0()
|
const n0 = t0()
|
||||||
|
@ -230,6 +264,30 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compile > setInsertionState > next, child and nthChild should be above the setInsertionState 1`] = `
|
||||||
|
"import { resolveComponent as _resolveComponent, child as _child, next as _next, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, nthChild as _nthChild, createIf as _createIf, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const t1 = _template("<div><div></div><!><div></div><!><div><button></button></div></div>", true)
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
|
const n6 = t1()
|
||||||
|
const n5 = _next(_child(n6))
|
||||||
|
const n7 = _nthChild(n6, 3)
|
||||||
|
const p0 = _next(n7)
|
||||||
|
const n4 = _child(p0)
|
||||||
|
_setInsertionState(n6, n5)
|
||||||
|
const n0 = _createComponentWithFallback(_component_Comp)
|
||||||
|
_setInsertionState(n6, n7)
|
||||||
|
const n1 = _createIf(() => (true), () => {
|
||||||
|
const n3 = t0()
|
||||||
|
return n3
|
||||||
|
})
|
||||||
|
_renderEffect(() => _setProp(n4, "disabled", _ctx.foo))
|
||||||
|
return n6
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compile > static + dynamic root 1`] = `
|
exports[`compile > static + dynamic root 1`] = `
|
||||||
"import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
|
"import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
|
||||||
const t0 = _template(" ")
|
const t0 = _template(" ")
|
||||||
|
|
|
@ -220,4 +220,46 @@ describe('compile', () => {
|
||||||
expect(code).matchSnapshot()
|
expect(code).matchSnapshot()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('setInsertionState', () => {
|
||||||
|
test('next, child and nthChild should be above the setInsertionState', () => {
|
||||||
|
const code = compile(`
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
<Comp />
|
||||||
|
<div />
|
||||||
|
<div v-if="true" />
|
||||||
|
<div>
|
||||||
|
<button :disabled="foo" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('execution order', () => {
|
||||||
|
test('basic', () => {
|
||||||
|
const code = compile(`<div :id="foo">{{ bar }}</div>`)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).contains(
|
||||||
|
`_setProp(n0, "id", _ctx.foo)
|
||||||
|
_setText(x0, _toDisplayString(_ctx.bar))`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
test('with v-once', () => {
|
||||||
|
const code = compile(
|
||||||
|
`<div>
|
||||||
|
<span v-once>{{ foo }}</span>
|
||||||
|
{{ bar }}<br>
|
||||||
|
{{ baz }}
|
||||||
|
</div>`,
|
||||||
|
)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).contains(
|
||||||
|
`_setText(n1, " " + _toDisplayString(_ctx.bar))
|
||||||
|
_setText(n2, " " + _toDisplayString(_ctx.baz))`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -32,3 +32,24 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: expression > update expression 1`] = `
|
||||||
|
"import { child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<div> </div>", true)
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const n1 = t0()
|
||||||
|
const n0 = _child(n1)
|
||||||
|
const x1 = _child(n1)
|
||||||
|
_renderEffect(() => {
|
||||||
|
const _String = String
|
||||||
|
const _foo = _ctx.foo
|
||||||
|
_setProp(n1, "id", _String(_foo.id++))
|
||||||
|
_setProp(n1, "foo", _foo)
|
||||||
|
_setProp(n1, "bar", _ctx.bar++)
|
||||||
|
_setText(n0, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar))
|
||||||
|
_setText(x1, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar))
|
||||||
|
})
|
||||||
|
return n1
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
|
@ -67,7 +67,6 @@ export function render(_ctx) {
|
||||||
const x2 = _child(n2)
|
const x2 = _child(n2)
|
||||||
_renderEffect(() => {
|
_renderEffect(() => {
|
||||||
const _msg = _ctx.msg
|
const _msg = _ctx.msg
|
||||||
|
|
||||||
_setText(x0, _toDisplayString(_msg))
|
_setText(x0, _toDisplayString(_msg))
|
||||||
_setText(x1, _toDisplayString(_msg))
|
_setText(x1, _toDisplayString(_msg))
|
||||||
_setText(x2, _toDisplayString(_msg))
|
_setText(x2, _toDisplayString(_msg))
|
||||||
|
|
|
@ -77,6 +77,16 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: element transform > component > resolve implicitly self-referencing component 1`] = `
|
||||||
|
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const _component_Example__self = _resolveComponent("Example", true)
|
||||||
|
const n0 = _createComponentWithFallback(_component_Example__self, null, null, true)
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: element transform > component > resolve namespaced component from props bindings (inline) 1`] = `
|
exports[`compiler: element transform > component > resolve namespaced component from props bindings (inline) 1`] = `
|
||||||
"
|
"
|
||||||
const n0 = _createComponent(Foo.Example, null, null, true)
|
const n0 = _createComponent(Foo.Example, null, null, true)
|
||||||
|
@ -178,6 +188,18 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: element transform > component > v-for on component should not mark as single root 1`] = `
|
||||||
|
"import { createComponent as _createComponent, createFor as _createFor } from 'vue';
|
||||||
|
|
||||||
|
export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||||
|
const n0 = _createFor(() => (_ctx.items), (_for_item0) => {
|
||||||
|
const n2 = _createComponent(_ctx.Comp)
|
||||||
|
return n2
|
||||||
|
}, (item) => (item), 2)
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: element transform > component > v-on expression is a function call 1`] = `
|
exports[`compiler: element transform > component > v-on expression is a function call 1`] = `
|
||||||
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
|
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,26 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: template ref transform > function ref 1`] = `
|
||||||
|
"import { createTemplateRefSetter as _createTemplateRefSetter, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<div></div>", true)
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const _setTemplateRef = _createTemplateRefSetter()
|
||||||
|
const n0 = t0()
|
||||||
|
let r0
|
||||||
|
_renderEffect(() => {
|
||||||
|
const _foo = _ctx.foo
|
||||||
|
r0 = _setTemplateRef(n0, bar => {
|
||||||
|
_foo.value = bar
|
||||||
|
;({ baz: _ctx.baz } = bar)
|
||||||
|
console.log(_foo.value, _ctx.baz)
|
||||||
|
}, r0)
|
||||||
|
})
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: template ref transform > ref + v-for 1`] = `
|
exports[`compiler: template ref transform > ref + v-for 1`] = `
|
||||||
"import { createTemplateRefSetter as _createTemplateRefSetter, createFor as _createFor, template as _template } from 'vue';
|
"import { createTemplateRefSetter as _createTemplateRefSetter, createFor as _createFor, template as _template } from 'vue';
|
||||||
const t0 = _template("<div></div>", true)
|
const t0 = _template("<div></div>", true)
|
||||||
|
|
|
@ -55,7 +55,6 @@ export function render(_ctx) {
|
||||||
const _foo = _ctx.foo
|
const _foo = _ctx.foo
|
||||||
const _bar = _ctx.bar
|
const _bar = _ctx.bar
|
||||||
const _foo_bar_baz = _foo[_bar(_ctx.baz)]
|
const _foo_bar_baz = _foo[_bar(_ctx.baz)]
|
||||||
|
|
||||||
_setProp(n0, "id", _foo_bar_baz)
|
_setProp(n0, "id", _foo_bar_baz)
|
||||||
_setProp(n1, "id", _foo_bar_baz)
|
_setProp(n1, "id", _foo_bar_baz)
|
||||||
_setProp(n2, "id", _bar() + _foo)
|
_setProp(n2, "id", _bar() + _foo)
|
||||||
|
@ -75,6 +74,17 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`cache multiple access > not cache variable in function expression 1`] = `
|
||||||
|
"import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<div></div>", true)
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const n0 = t0()
|
||||||
|
_renderEffect(() => _setDynamicProps(n0, [{ foo: bar => _ctx.foo = bar }], true))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`cache multiple access > not cache variable only used in property shorthand 1`] = `
|
exports[`cache multiple access > not cache variable only used in property shorthand 1`] = `
|
||||||
"import { setStyle as _setStyle, renderEffect as _renderEffect, template as _template } from 'vue';
|
"import { setStyle as _setStyle, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||||
const t0 = _template("<div></div>", true)
|
const t0 = _template("<div></div>", true)
|
||||||
|
@ -96,7 +106,6 @@ export function render(_ctx) {
|
||||||
_renderEffect(() => {
|
_renderEffect(() => {
|
||||||
const _obj = _ctx.obj
|
const _obj = _ctx.obj
|
||||||
const _obj_foo_baz_obj_bar = _obj['foo']['baz'] + _obj.bar
|
const _obj_foo_baz_obj_bar = _obj['foo']['baz'] + _obj.bar
|
||||||
|
|
||||||
_setProp(n0, "id", _obj_foo_baz_obj_bar)
|
_setProp(n0, "id", _obj_foo_baz_obj_bar)
|
||||||
_setProp(n1, "id", _obj_foo_baz_obj_bar)
|
_setProp(n1, "id", _obj_foo_baz_obj_bar)
|
||||||
})
|
})
|
||||||
|
@ -115,7 +124,6 @@ export function render(_ctx) {
|
||||||
_renderEffect(() => {
|
_renderEffect(() => {
|
||||||
const _foo = _ctx.foo
|
const _foo = _ctx.foo
|
||||||
const _foo_bar = _foo + _ctx.bar
|
const _foo_bar = _foo + _ctx.bar
|
||||||
|
|
||||||
_setProp(n0, "id", _foo_bar)
|
_setProp(n0, "id", _foo_bar)
|
||||||
_setProp(n1, "id", _foo_bar)
|
_setProp(n1, "id", _foo_bar)
|
||||||
_setProp(n2, "id", _foo + _foo_bar)
|
_setProp(n2, "id", _foo + _foo_bar)
|
||||||
|
@ -133,7 +141,6 @@ export function render(_ctx) {
|
||||||
const n1 = t0()
|
const n1 = t0()
|
||||||
_renderEffect(() => {
|
_renderEffect(() => {
|
||||||
const _foo_bar = _ctx.foo + _ctx.bar
|
const _foo_bar = _ctx.foo + _ctx.bar
|
||||||
|
|
||||||
_setProp(n0, "id", _foo_bar)
|
_setProp(n0, "id", _foo_bar)
|
||||||
_setProp(n1, "id", _foo_bar)
|
_setProp(n1, "id", _foo_bar)
|
||||||
})
|
})
|
||||||
|
@ -166,7 +173,6 @@ export function render(_ctx) {
|
||||||
const n1 = t0()
|
const n1 = t0()
|
||||||
_renderEffect(() => {
|
_renderEffect(() => {
|
||||||
const _foo = _ctx.foo
|
const _foo = _ctx.foo
|
||||||
|
|
||||||
_setClass(n0, _foo)
|
_setClass(n0, _foo)
|
||||||
_setClass(n1, _foo)
|
_setClass(n1, _foo)
|
||||||
})
|
})
|
||||||
|
@ -487,15 +493,13 @@ export function render(_ctx) {
|
||||||
_setAttr(n0, "form", _ctx.form)
|
_setAttr(n0, "form", _ctx.form)
|
||||||
_setAttr(n1, "list", _ctx.list)
|
_setAttr(n1, "list", _ctx.list)
|
||||||
_setAttr(n2, "type", _ctx.type)
|
_setAttr(n2, "type", _ctx.type)
|
||||||
|
|
||||||
_setAttr(n3, "width", _width)
|
_setAttr(n3, "width", _width)
|
||||||
_setAttr(n4, "width", _width)
|
|
||||||
_setAttr(n5, "width", _width)
|
|
||||||
_setAttr(n6, "width", _width)
|
|
||||||
|
|
||||||
_setAttr(n3, "height", _height)
|
_setAttr(n3, "height", _height)
|
||||||
|
_setAttr(n4, "width", _width)
|
||||||
_setAttr(n4, "height", _height)
|
_setAttr(n4, "height", _height)
|
||||||
|
_setAttr(n5, "width", _width)
|
||||||
_setAttr(n5, "height", _height)
|
_setAttr(n5, "height", _height)
|
||||||
|
_setAttr(n6, "width", _width)
|
||||||
_setAttr(n6, "height", _height)
|
_setAttr(n6, "height", _height)
|
||||||
})
|
})
|
||||||
return [n0, n1, n2, n3, n4, n5, n6]
|
return [n0, n1, n2, n3, n4, n5, n6]
|
||||||
|
|
|
@ -115,6 +115,21 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: v-for > object value, key and index 1`] = `
|
||||||
|
"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<div> </div>", true)
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0, _for_index0) => {
|
||||||
|
const n2 = t0()
|
||||||
|
const x2 = _child(n2)
|
||||||
|
_renderEffect(() => _setText(x2, _toDisplayString(_for_item0.value + _for_key0.value + _for_index0.value)))
|
||||||
|
return n2
|
||||||
|
}, (value, key, index) => (key))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: v-for > v-for aliases w/ complex expressions 1`] = `
|
exports[`compiler: v-for > v-for aliases w/ complex expressions 1`] = `
|
||||||
"import { getDefaultValue as _getDefaultValue, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
|
"import { getDefaultValue as _getDefaultValue, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
|
||||||
const t0 = _template("<div> </div>", true)
|
const t0 = _template("<div> </div>", true)
|
||||||
|
|
|
@ -134,3 +134,29 @@ export function render(_ctx) {
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: v-if > v-if + v-if / v-else[-if] 1`] = `
|
||||||
|
"import { setInsertionState as _setInsertionState, createIf as _createIf, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<span>foo</span>")
|
||||||
|
const t1 = _template("<span>bar</span>")
|
||||||
|
const t2 = _template("<span>baz</span>")
|
||||||
|
const t3 = _template("<div></div>", true)
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const n8 = t3()
|
||||||
|
_setInsertionState(n8)
|
||||||
|
const n0 = _createIf(() => (_ctx.foo), () => {
|
||||||
|
const n2 = t0()
|
||||||
|
return n2
|
||||||
|
})
|
||||||
|
_setInsertionState(n8)
|
||||||
|
const n3 = _createIf(() => (_ctx.bar), () => {
|
||||||
|
const n5 = t1()
|
||||||
|
return n5
|
||||||
|
}, () => {
|
||||||
|
const n7 = t2()
|
||||||
|
return n7
|
||||||
|
})
|
||||||
|
return n8
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
|
@ -123,6 +123,18 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > expression with type 1`] = `
|
||||||
|
"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<div></div>", true)
|
||||||
|
_delegateEvents("click")
|
||||||
|
|
||||||
|
export function render(_ctx, $props, $emit, $attrs, $slots) {
|
||||||
|
const n0 = t0()
|
||||||
|
n0.$evtclick = e => _ctx.handleClick(e)
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`v-on > function expression w/ prefixIdentifiers: true 1`] = `
|
exports[`v-on > function expression w/ prefixIdentifiers: true 1`] = `
|
||||||
"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
|
"import { delegateEvents as _delegateEvents, template as _template } from 'vue';
|
||||||
const t0 = _template("<div></div>", true)
|
const t0 = _template("<div></div>", true)
|
||||||
|
|
|
@ -252,3 +252,90 @@ export function render(_ctx) {
|
||||||
return n1
|
return n1
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform slot > slot + v-if / v-else[-if] should not cause error 1`] = `
|
||||||
|
"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createSlot as _createSlot, createComponentWithFallback as _createComponentWithFallback, createIf as _createIf, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<div></div>", true)
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const _component_Foo = _resolveComponent("Foo")
|
||||||
|
const _component_Bar = _resolveComponent("Bar")
|
||||||
|
const n6 = t0()
|
||||||
|
_setInsertionState(n6)
|
||||||
|
const n0 = _createSlot("foo", null)
|
||||||
|
_setInsertionState(n6)
|
||||||
|
const n1 = _createIf(() => (true), () => {
|
||||||
|
const n3 = _createComponentWithFallback(_component_Foo)
|
||||||
|
return n3
|
||||||
|
}, () => {
|
||||||
|
const n5 = _createComponentWithFallback(_component_Bar)
|
||||||
|
return n5
|
||||||
|
})
|
||||||
|
return n6
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform slot > with whitespace: 'preserve' > implicit default slot 1`] = `
|
||||||
|
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
|
||||||
|
const t0 = _template(" Header ")
|
||||||
|
const t1 = _template(" ")
|
||||||
|
const t2 = _template("<p></p>")
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
|
const n4 = _createComponentWithFallback(_component_Comp, null, {
|
||||||
|
"header": () => {
|
||||||
|
const n0 = t0()
|
||||||
|
return n0
|
||||||
|
},
|
||||||
|
"default": () => {
|
||||||
|
const n2 = t1()
|
||||||
|
const n3 = t2()
|
||||||
|
return [n2, n3]
|
||||||
|
}
|
||||||
|
}, true)
|
||||||
|
return n4
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform slot > with whitespace: 'preserve' > named default slot + implicit whitespace content 1`] = `
|
||||||
|
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
|
||||||
|
const t0 = _template(" Header ")
|
||||||
|
const t1 = _template(" Default ")
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
|
const n5 = _createComponentWithFallback(_component_Comp, null, {
|
||||||
|
"header": () => {
|
||||||
|
const n0 = t0()
|
||||||
|
return n0
|
||||||
|
},
|
||||||
|
"default": () => {
|
||||||
|
const n3 = t1()
|
||||||
|
return n3
|
||||||
|
}
|
||||||
|
}, true)
|
||||||
|
return n5
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform slot > with whitespace: 'preserve' > should not generate whitespace only default slot 1`] = `
|
||||||
|
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
|
||||||
|
const t0 = _template(" Header ")
|
||||||
|
const t1 = _template(" Footer ")
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
|
const n5 = _createComponentWithFallback(_component_Comp, null, {
|
||||||
|
"header": () => {
|
||||||
|
const n0 = t0()
|
||||||
|
return n0
|
||||||
|
},
|
||||||
|
"footer": () => {
|
||||||
|
const n3 = t1()
|
||||||
|
return n3
|
||||||
|
}
|
||||||
|
}, true)
|
||||||
|
return n5
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
import { BindingTypes } from '@vue/compiler-dom'
|
import { BindingTypes } from '@vue/compiler-dom'
|
||||||
import { transformChildren, transformText } from '../../src'
|
import {
|
||||||
|
transformChildren,
|
||||||
|
transformElement,
|
||||||
|
transformText,
|
||||||
|
transformVBind,
|
||||||
|
} from '../../src'
|
||||||
import { makeCompile } from './_utils'
|
import { makeCompile } from './_utils'
|
||||||
|
|
||||||
const compileWithExpression = makeCompile({
|
const compileWithExpression = makeCompile({
|
||||||
nodeTransforms: [transformChildren, transformText],
|
nodeTransforms: [transformElement, transformChildren, transformText],
|
||||||
|
directiveTransforms: { bind: transformVBind },
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('compiler: expression', () => {
|
describe('compiler: expression', () => {
|
||||||
|
@ -31,4 +37,14 @@ describe('compiler: expression', () => {
|
||||||
expect(code).toMatchSnapshot()
|
expect(code).toMatchSnapshot()
|
||||||
expect(code).contains(`$props['bar']`)
|
expect(code).contains(`$props['bar']`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('update expression', () => {
|
||||||
|
const { code } = compileWithExpression(`
|
||||||
|
<div :id="String(foo.id++)" :foo="foo" :bar="bar++">
|
||||||
|
{{ String(foo.id++) }} {{ foo }} {{ bar }}
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
expect(code).contains(`_String(_foo.id++)`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -69,8 +69,8 @@ describe('compiler: children transform', () => {
|
||||||
</div>`,
|
</div>`,
|
||||||
)
|
)
|
||||||
// ensure the insertion anchor is generated before the insertion statement
|
// ensure the insertion anchor is generated before the insertion statement
|
||||||
expect(code).toMatch(`const n3 = _next(_child(n4))
|
expect(code).toMatch(`const n3 = _next(_child(n4))`)
|
||||||
_setInsertionState(n4, n3)`)
|
expect(code).toMatch(`_setInsertionState(n4, n3)`)
|
||||||
expect(code).toMatchSnapshot()
|
expect(code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,16 +6,22 @@ import {
|
||||||
transformElement,
|
transformElement,
|
||||||
transformText,
|
transformText,
|
||||||
transformVBind,
|
transformVBind,
|
||||||
|
transformVFor,
|
||||||
transformVOn,
|
transformVOn,
|
||||||
} from '../../src'
|
} from '../../src'
|
||||||
import {
|
import {
|
||||||
type BindingMetadata,
|
type BindingMetadata,
|
||||||
BindingTypes,
|
BindingTypes,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-dom'
|
||||||
|
|
||||||
const compileWithElementTransform = makeCompile({
|
const compileWithElementTransform = makeCompile({
|
||||||
nodeTransforms: [transformElement, transformChildren, transformText],
|
nodeTransforms: [
|
||||||
|
transformVFor,
|
||||||
|
transformElement,
|
||||||
|
transformChildren,
|
||||||
|
transformText,
|
||||||
|
],
|
||||||
directiveTransforms: {
|
directiveTransforms: {
|
||||||
bind: transformVBind,
|
bind: transformVBind,
|
||||||
on: transformVOn,
|
on: transformVOn,
|
||||||
|
@ -39,11 +45,12 @@ describe('compiler: element transform', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.todo('resolve implicitly self-referencing component', () => {
|
test('resolve implicitly self-referencing component', () => {
|
||||||
const { code, helpers } = compileWithElementTransform(`<Example/>`, {
|
const { code, helpers } = compileWithElementTransform(`<Example/>`, {
|
||||||
filename: `/foo/bar/Example.vue?vue&type=template`,
|
filename: `/foo/bar/Example.vue?vue&type=template`,
|
||||||
})
|
})
|
||||||
expect(code).toMatchSnapshot()
|
expect(code).toMatchSnapshot()
|
||||||
|
expect(code).toContain('_resolveComponent("Example", true)')
|
||||||
expect(helpers).toContain('resolveComponent')
|
expect(helpers).toContain('resolveComponent')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -169,6 +176,17 @@ describe('compiler: element transform', () => {
|
||||||
expect(code).contains('_createComponent(_ctx.Comp)')
|
expect(code).contains('_createComponent(_ctx.Comp)')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('v-for on component should not mark as single root', () => {
|
||||||
|
const { code } = compileWithElementTransform(
|
||||||
|
`<Comp v-for="item in items" :key="item"/>`,
|
||||||
|
{
|
||||||
|
bindingMetadata: { Comp: BindingTypes.SETUP_CONST },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
expect(code).contains('_createComponent(_ctx.Comp)')
|
||||||
|
})
|
||||||
|
|
||||||
test('static props', () => {
|
test('static props', () => {
|
||||||
const { code, ir } = compileWithElementTransform(
|
const { code, ir } = compileWithElementTransform(
|
||||||
`<Foo id="foo" class="bar" />`,
|
`<Foo id="foo" class="bar" />`,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ErrorCodes, NodeTypes } from '@vue/compiler-core'
|
import { ErrorCodes, NodeTypes } from '@vue/compiler-dom'
|
||||||
import {
|
import {
|
||||||
IRNodeTypes,
|
IRNodeTypes,
|
||||||
transformChildren,
|
transformChildren,
|
||||||
|
|
|
@ -81,6 +81,47 @@ describe('compiler: template ref transform', () => {
|
||||||
expect(code).contains('_setTemplateRef(n0, _ctx.foo, r0)')
|
expect(code).contains('_setTemplateRef(n0, _ctx.foo, r0)')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('function ref', () => {
|
||||||
|
const { ir, code } = compileWithTransformRef(
|
||||||
|
`<div :ref="bar => {
|
||||||
|
foo.value = bar
|
||||||
|
;({ baz } = bar)
|
||||||
|
console.log(foo.value, baz)
|
||||||
|
}" />`,
|
||||||
|
)
|
||||||
|
expect(ir.block.dynamic.children[0]).toMatchObject({
|
||||||
|
id: 0,
|
||||||
|
flags: DynamicFlag.REFERENCED,
|
||||||
|
})
|
||||||
|
expect(ir.template).toEqual(['<div></div>'])
|
||||||
|
expect(ir.block.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.DECLARE_OLD_REF,
|
||||||
|
id: 0,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
expect(ir.block.effect).toMatchObject([
|
||||||
|
{
|
||||||
|
operations: [
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_TEMPLATE_REF,
|
||||||
|
element: 0,
|
||||||
|
value: {
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
expect(code).contains('const _setTemplateRef = _createTemplateRefSetter()')
|
||||||
|
expect(code).contains(`_setTemplateRef(n0, bar => {
|
||||||
|
_foo.value = bar
|
||||||
|
;({ baz: _ctx.baz } = bar)
|
||||||
|
console.log(_foo.value, _ctx.baz)
|
||||||
|
}, r0)`)
|
||||||
|
})
|
||||||
|
|
||||||
test('ref + v-if', () => {
|
test('ref + v-if', () => {
|
||||||
const { ir, code } = compileWithTransformRef(
|
const { ir, code } = compileWithTransformRef(
|
||||||
`<div ref="foo" v-if="true" />`,
|
`<div ref="foo" v-if="true" />`,
|
||||||
|
|
|
@ -809,4 +809,12 @@ describe('cache multiple access', () => {
|
||||||
expect(code).matchSnapshot()
|
expect(code).matchSnapshot()
|
||||||
expect(code).not.contains('const _bar = _ctx.bar')
|
expect(code).not.contains('const _bar = _ctx.bar')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('not cache variable in function expression', () => {
|
||||||
|
const { code } = compileWithVBind(`
|
||||||
|
<div v-bind="{ foo: bar => foo = bar }"></div>
|
||||||
|
`)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).not.contains('const _bar = _ctx.bar')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -119,6 +119,32 @@ describe('compiler: v-for', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('object value, key and index', () => {
|
||||||
|
const { code, ir } = compileWithVFor(
|
||||||
|
`<div v-for="(value, key, index) in list" :key="key">{{ value + key + index }}</div>`,
|
||||||
|
)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(ir.block.dynamic.children[0].operation).toMatchObject({
|
||||||
|
type: IRNodeTypes.FOR,
|
||||||
|
source: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'list',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'value',
|
||||||
|
},
|
||||||
|
key: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'key',
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'index',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('object de-structured value', () => {
|
test('object de-structured value', () => {
|
||||||
const { code, ir } = compileWithVFor(
|
const { code, ir } = compileWithVFor(
|
||||||
'<span v-for="({ id, value }) in items" :key="id">{{ id }}{{ value }}</span>',
|
'<span v-for="({ id, value }) in items" :key="id">{{ id }}{{ value }}</span>',
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
transformVOnce,
|
transformVOnce,
|
||||||
transformVText,
|
transformVText,
|
||||||
} from '../../src'
|
} from '../../src'
|
||||||
import { NodeTypes } from '@vue/compiler-core'
|
import { NodeTypes } from '@vue/compiler-dom'
|
||||||
|
|
||||||
const compileWithVIf = makeCompile({
|
const compileWithVIf = makeCompile({
|
||||||
nodeTransforms: [
|
nodeTransforms: [
|
||||||
|
@ -215,6 +215,17 @@ describe('compiler: v-if', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('v-if + v-if / v-else[-if]', () => {
|
||||||
|
const { code } = compileWithVIf(
|
||||||
|
`<div>
|
||||||
|
<span v-if="foo">foo</span>
|
||||||
|
<span v-if="bar">bar</span>
|
||||||
|
<span v-else>baz</span>
|
||||||
|
</div>`,
|
||||||
|
)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
test('comment between branches', () => {
|
test('comment between branches', () => {
|
||||||
const { code, ir } = compileWithVIf(`
|
const { code, ir } = compileWithVIf(`
|
||||||
<div v-if="ok"/>
|
<div v-if="ok"/>
|
||||||
|
|
|
@ -682,4 +682,17 @@ describe('v-on', () => {
|
||||||
'_delegate(n0, "click", _withModifiers(e => _ctx.test(e), ["stop"]))',
|
'_delegate(n0, "click", _withModifiers(e => _ctx.test(e), ["stop"]))',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('expression with type', () => {
|
||||||
|
const { code } = compileWithVOn(
|
||||||
|
`<div @click="(<number>handleClick as any)"></div>`,
|
||||||
|
{
|
||||||
|
bindingMetadata: {
|
||||||
|
handleClick: BindingTypes.SETUP_CONST,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).include('n0.$evtclick = e => _ctx.handleClick(e)')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ErrorCodes, NodeTypes } from '@vue/compiler-core'
|
import { ErrorCodes, NodeTypes } from '@vue/compiler-dom'
|
||||||
import {
|
import {
|
||||||
IRNodeTypes,
|
IRNodeTypes,
|
||||||
IRSlotType,
|
IRSlotType,
|
||||||
|
@ -371,6 +371,17 @@ describe('compiler: transform slot', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('slot + v-if / v-else[-if] should not cause error', () => {
|
||||||
|
const { code } = compileWithSlots(
|
||||||
|
`<div>
|
||||||
|
<slot name="foo"></slot>
|
||||||
|
<Foo v-if="true"></Foo>
|
||||||
|
<Bar v-else />
|
||||||
|
</div>`,
|
||||||
|
)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
test('quote slot name', () => {
|
test('quote slot name', () => {
|
||||||
const { code } = compileWithSlots(
|
const { code } = compileWithSlots(
|
||||||
`<Comp><template #nav-bar-title-before></template></Comp>`,
|
`<Comp><template #nav-bar-title-before></template></Comp>`,
|
||||||
|
@ -498,4 +509,60 @@ describe('compiler: transform slot', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe(`with whitespace: 'preserve'`, () => {
|
||||||
|
test('named default slot + implicit whitespace content', () => {
|
||||||
|
const source = `
|
||||||
|
<Comp>
|
||||||
|
<template #header> Header </template>
|
||||||
|
<template #default> Default </template>
|
||||||
|
</Comp>
|
||||||
|
`
|
||||||
|
const { code } = compileWithSlots(source, {
|
||||||
|
whitespace: 'preserve',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(
|
||||||
|
`Extraneous children found when component already has explicitly named default slot.`,
|
||||||
|
).not.toHaveBeenWarned()
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('implicit default slot', () => {
|
||||||
|
const source = `
|
||||||
|
<Comp>
|
||||||
|
<template #header> Header </template>
|
||||||
|
<p/>
|
||||||
|
</Comp>
|
||||||
|
`
|
||||||
|
const { code } = compileWithSlots(source, {
|
||||||
|
whitespace: 'preserve',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(
|
||||||
|
`Extraneous children found when component already has explicitly named default slot.`,
|
||||||
|
).not.toHaveBeenWarned()
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not generate whitespace only default slot', () => {
|
||||||
|
const source = `
|
||||||
|
<Comp>
|
||||||
|
<template #header> Header </template>
|
||||||
|
<template #footer> Footer </template>
|
||||||
|
</Comp>
|
||||||
|
`
|
||||||
|
const { code, ir } = compileWithSlots(source, {
|
||||||
|
whitespace: 'preserve',
|
||||||
|
})
|
||||||
|
|
||||||
|
const slots = (ir.block.dynamic.children[0].operation as any).slots[0]
|
||||||
|
.slots
|
||||||
|
// should be: header, footer (no default)
|
||||||
|
expect(Object.keys(slots).length).toBe(2)
|
||||||
|
expect(!!slots['default']).toBe(false)
|
||||||
|
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -81,8 +81,8 @@ export function getBaseTransformPreset(): TransformPreset {
|
||||||
transformVFor,
|
transformVFor,
|
||||||
transformSlotOutlet,
|
transformSlotOutlet,
|
||||||
transformTemplateRef,
|
transformTemplateRef,
|
||||||
transformText,
|
|
||||||
transformElement,
|
transformElement,
|
||||||
|
transformText,
|
||||||
transformVSlot,
|
transformVSlot,
|
||||||
transformComment,
|
transformComment,
|
||||||
transformChildren,
|
transformChildren,
|
||||||
|
|
|
@ -44,7 +44,21 @@ export function genBlockContent(
|
||||||
const resetBlock = context.enterBlock(block)
|
const resetBlock = context.enterBlock(block)
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
genResolveAssets('component', 'resolveComponent')
|
for (let name of context.ir.component) {
|
||||||
|
const id = toValidAssetId(name, 'component')
|
||||||
|
const maybeSelfReference = name.endsWith('__self')
|
||||||
|
if (maybeSelfReference) name = name.slice(0, -6)
|
||||||
|
push(
|
||||||
|
NEWLINE,
|
||||||
|
`const ${id} = `,
|
||||||
|
...genCall(
|
||||||
|
context.helper('resolveComponent'),
|
||||||
|
JSON.stringify(name),
|
||||||
|
// pass additional `maybeSelfReference` flag
|
||||||
|
maybeSelfReference ? 'true' : undefined,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
genResolveAssets('directive', 'resolveDirective')
|
genResolveAssets('directive', 'resolveDirective')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +66,7 @@ export function genBlockContent(
|
||||||
push(...genSelf(child, context))
|
push(...genSelf(child, context))
|
||||||
}
|
}
|
||||||
for (const child of dynamic.children) {
|
for (const child of dynamic.children) {
|
||||||
push(...genChildren(child, context, `n${child.id!}`))
|
push(...genChildren(child, context, push, `n${child.id!}`))
|
||||||
}
|
}
|
||||||
|
|
||||||
push(...genOperations(operation, context))
|
push(...genOperations(operation, context))
|
||||||
|
|
|
@ -34,7 +34,7 @@ import {
|
||||||
isMemberExpression,
|
isMemberExpression,
|
||||||
toValidAssetId,
|
toValidAssetId,
|
||||||
walkIdentifiers,
|
walkIdentifiers,
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-dom'
|
||||||
import { genEventHandler } from './event'
|
import { genEventHandler } from './event'
|
||||||
import { genDirectiveModifiers, genDirectivesForElement } from './directive'
|
import { genDirectiveModifiers, genDirectivesForElement } from './directive'
|
||||||
import { genBlock } from './block'
|
import { genBlock } from './block'
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
NewlineType,
|
NewlineType,
|
||||||
type SimpleExpressionNode,
|
type SimpleExpressionNode,
|
||||||
type SourceLocation,
|
type SourceLocation,
|
||||||
|
TS_NODE_TYPES,
|
||||||
advancePositionWithClone,
|
advancePositionWithClone,
|
||||||
createSimpleExpression,
|
createSimpleExpression,
|
||||||
isInDestructureAssignment,
|
isInDestructureAssignment,
|
||||||
|
@ -20,7 +21,6 @@ import type { Identifier, Node } from '@babel/types'
|
||||||
import type { CodegenContext } from '../generate'
|
import type { CodegenContext } from '../generate'
|
||||||
import { isConstantExpression } from '../utils'
|
import { isConstantExpression } from '../utils'
|
||||||
import { type CodeFragment, NEWLINE, buildCodeFragment } from './utils'
|
import { type CodeFragment, NEWLINE, buildCodeFragment } from './utils'
|
||||||
import { walk } from 'estree-walker'
|
|
||||||
import { type ParserOptions, parseExpression } from '@babel/parser'
|
import { type ParserOptions, parseExpression } from '@babel/parser'
|
||||||
|
|
||||||
export function genExpression(
|
export function genExpression(
|
||||||
|
@ -64,6 +64,7 @@ export function genExpression(
|
||||||
let hasMemberExpression = false
|
let hasMemberExpression = false
|
||||||
if (ids.length) {
|
if (ids.length) {
|
||||||
const [frag, push] = buildCodeFragment()
|
const [frag, push] = buildCodeFragment()
|
||||||
|
const isTSNode = ast && TS_NODE_TYPES.includes(ast.type)
|
||||||
ids
|
ids
|
||||||
.sort((a, b) => a.start! - b.start!)
|
.sort((a, b) => a.start! - b.start!)
|
||||||
.forEach((id, i) => {
|
.forEach((id, i) => {
|
||||||
|
@ -72,8 +73,10 @@ export function genExpression(
|
||||||
const end = id.end! - 1
|
const end = id.end! - 1
|
||||||
const last = ids[i - 1]
|
const last = ids[i - 1]
|
||||||
|
|
||||||
const leadingText = content.slice(last ? last.end! - 1 : 0, start)
|
if (!(isTSNode && i === 0)) {
|
||||||
if (leadingText.length) push([leadingText, NewlineType.Unknown])
|
const leadingText = content.slice(last ? last.end! - 1 : 0, start)
|
||||||
|
if (leadingText.length) push([leadingText, NewlineType.Unknown])
|
||||||
|
}
|
||||||
|
|
||||||
const source = content.slice(start, end)
|
const source = content.slice(start, end)
|
||||||
const parentStack = parentStackMap.get(id)!
|
const parentStack = parentStackMap.get(id)!
|
||||||
|
@ -100,7 +103,7 @@ export function genExpression(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (i === ids.length - 1 && end < content.length) {
|
if (i === ids.length - 1 && end < content.length && !isTSNode) {
|
||||||
push([content.slice(end), NewlineType.Unknown])
|
push([content.slice(end), NewlineType.Unknown])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -245,8 +248,13 @@ export function processExpressions(
|
||||||
expressions: SimpleExpressionNode[],
|
expressions: SimpleExpressionNode[],
|
||||||
): DeclarationResult {
|
): DeclarationResult {
|
||||||
// analyze variables
|
// analyze variables
|
||||||
const { seenVariable, variableToExpMap, expToVariableMap, seenIdentifier } =
|
const {
|
||||||
analyzeExpressions(expressions)
|
seenVariable,
|
||||||
|
variableToExpMap,
|
||||||
|
expToVariableMap,
|
||||||
|
seenIdentifier,
|
||||||
|
updatedVariable,
|
||||||
|
} = analyzeExpressions(expressions)
|
||||||
|
|
||||||
// process repeated identifiers and member expressions
|
// process repeated identifiers and member expressions
|
||||||
// e.g., `foo[baz]` will be transformed into `foo_baz`
|
// e.g., `foo[baz]` will be transformed into `foo_baz`
|
||||||
|
@ -256,6 +264,7 @@ export function processExpressions(
|
||||||
variableToExpMap,
|
variableToExpMap,
|
||||||
expToVariableMap,
|
expToVariableMap,
|
||||||
seenIdentifier,
|
seenIdentifier,
|
||||||
|
updatedVariable,
|
||||||
)
|
)
|
||||||
|
|
||||||
// process duplicate expressions after identifier and member expression handling.
|
// process duplicate expressions after identifier and member expression handling.
|
||||||
|
@ -264,6 +273,8 @@ export function processExpressions(
|
||||||
context,
|
context,
|
||||||
expressions,
|
expressions,
|
||||||
varDeclarations,
|
varDeclarations,
|
||||||
|
updatedVariable,
|
||||||
|
expToVariableMap,
|
||||||
)
|
)
|
||||||
|
|
||||||
return genDeclarations([...varDeclarations, ...expDeclarations], context)
|
return genDeclarations([...varDeclarations, ...expDeclarations], context)
|
||||||
|
@ -274,11 +285,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
|
||||||
const variableToExpMap = new Map<string, Set<SimpleExpressionNode>>()
|
const variableToExpMap = new Map<string, Set<SimpleExpressionNode>>()
|
||||||
const expToVariableMap = new Map<SimpleExpressionNode, string[]>()
|
const expToVariableMap = new Map<SimpleExpressionNode, string[]>()
|
||||||
const seenIdentifier = new Set<string>()
|
const seenIdentifier = new Set<string>()
|
||||||
|
const updatedVariable = new Set<string>()
|
||||||
|
|
||||||
const registerVariable = (
|
const registerVariable = (
|
||||||
name: string,
|
name: string,
|
||||||
exp: SimpleExpressionNode,
|
exp: SimpleExpressionNode,
|
||||||
isIdentifier: boolean,
|
isIdentifier: boolean,
|
||||||
|
parentStack: Node[] = [],
|
||||||
) => {
|
) => {
|
||||||
if (isIdentifier) seenIdentifier.add(name)
|
if (isIdentifier) seenIdentifier.add(name)
|
||||||
seenVariable[name] = (seenVariable[name] || 0) + 1
|
seenVariable[name] = (seenVariable[name] || 0) + 1
|
||||||
|
@ -287,6 +300,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
|
||||||
(variableToExpMap.get(name) || new Set()).add(exp),
|
(variableToExpMap.get(name) || new Set()).add(exp),
|
||||||
)
|
)
|
||||||
expToVariableMap.set(exp, (expToVariableMap.get(exp) || []).concat(name))
|
expToVariableMap.set(exp, (expToVariableMap.get(exp) || []).concat(name))
|
||||||
|
if (
|
||||||
|
parentStack.some(
|
||||||
|
p => p.type === 'UpdateExpression' || p.type === 'AssignmentExpression',
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
updatedVariable.add(name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const exp of expressions) {
|
for (const exp of expressions) {
|
||||||
|
@ -295,37 +315,25 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
walk(exp.ast, {
|
walkIdentifiers(exp.ast, (currentNode, parent, parentStack) => {
|
||||||
enter(currentNode: Node, parent: Node | null) {
|
if (parent && isMemberExpression(parent)) {
|
||||||
if (currentNode.type === 'MemberExpression') {
|
const memberExp = extractMemberExpression(parent, name => {
|
||||||
const memberExp = extractMemberExpression(
|
registerVariable(name, exp, true)
|
||||||
currentNode,
|
})
|
||||||
(name: string) => {
|
registerVariable(memberExp, exp, false, parentStack)
|
||||||
registerVariable(name, exp, true)
|
} else if (!parentStack.some(isMemberExpression)) {
|
||||||
},
|
registerVariable(currentNode.name, exp, true, parentStack)
|
||||||
)
|
}
|
||||||
registerVariable(memberExp, exp, false)
|
|
||||||
return this.skip()
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip shorthand or non-computed property keys
|
|
||||||
if (
|
|
||||||
parent &&
|
|
||||||
parent.type === 'ObjectProperty' &&
|
|
||||||
parent.key === currentNode &&
|
|
||||||
(parent.shorthand || !parent.computed)
|
|
||||||
) {
|
|
||||||
return this.skip()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentNode.type === 'Identifier') {
|
|
||||||
registerVariable(currentNode.name, exp, true)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return { seenVariable, seenIdentifier, variableToExpMap, expToVariableMap }
|
return {
|
||||||
|
seenVariable,
|
||||||
|
seenIdentifier,
|
||||||
|
variableToExpMap,
|
||||||
|
expToVariableMap,
|
||||||
|
updatedVariable,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processRepeatedVariables(
|
function processRepeatedVariables(
|
||||||
|
@ -334,9 +342,11 @@ function processRepeatedVariables(
|
||||||
variableToExpMap: Map<string, Set<SimpleExpressionNode>>,
|
variableToExpMap: Map<string, Set<SimpleExpressionNode>>,
|
||||||
expToVariableMap: Map<SimpleExpressionNode, string[]>,
|
expToVariableMap: Map<SimpleExpressionNode, string[]>,
|
||||||
seenIdentifier: Set<string>,
|
seenIdentifier: Set<string>,
|
||||||
|
updatedVariable: Set<string>,
|
||||||
): DeclarationValue[] {
|
): DeclarationValue[] {
|
||||||
const declarations: DeclarationValue[] = []
|
const declarations: DeclarationValue[] = []
|
||||||
for (const [name, exps] of variableToExpMap) {
|
for (const [name, exps] of variableToExpMap) {
|
||||||
|
if (updatedVariable.has(name)) continue
|
||||||
if (seenVariable[name] > 1 && exps.size > 0) {
|
if (seenVariable[name] > 1 && exps.size > 0) {
|
||||||
const isIdentifier = seenIdentifier.has(name)
|
const isIdentifier = seenIdentifier.has(name)
|
||||||
const varName = isIdentifier ? name : genVarName(name)
|
const varName = isIdentifier ? name : genVarName(name)
|
||||||
|
@ -428,12 +438,19 @@ function processRepeatedExpressions(
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
expressions: SimpleExpressionNode[],
|
expressions: SimpleExpressionNode[],
|
||||||
varDeclarations: DeclarationValue[],
|
varDeclarations: DeclarationValue[],
|
||||||
|
updatedVariable: Set<string>,
|
||||||
|
expToVariableMap: Map<SimpleExpressionNode, string[]>,
|
||||||
): DeclarationValue[] {
|
): DeclarationValue[] {
|
||||||
const declarations: DeclarationValue[] = []
|
const declarations: DeclarationValue[] = []
|
||||||
const seenExp = expressions.reduce(
|
const seenExp = expressions.reduce(
|
||||||
(acc, exp) => {
|
(acc, exp) => {
|
||||||
|
const variables = expToVariableMap.get(exp)
|
||||||
// only handle expressions that are not identifiers
|
// only handle expressions that are not identifiers
|
||||||
if (exp.ast && exp.ast.type !== 'Identifier') {
|
if (
|
||||||
|
exp.ast &&
|
||||||
|
exp.ast.type !== 'Identifier' &&
|
||||||
|
!(variables && variables.some(v => updatedVariable.has(v)))
|
||||||
|
) {
|
||||||
acc[exp.content] = (acc[exp.content] || 0) + 1
|
acc[exp.content] = (acc[exp.content] || 0) + 1
|
||||||
}
|
}
|
||||||
return acc
|
return acc
|
||||||
|
@ -580,3 +597,9 @@ function extractMemberExpression(
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isMemberExpression = (node: Node) => {
|
||||||
|
return (
|
||||||
|
node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ export function genOperationWithInsertionState(
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
const [frag, push] = buildCodeFragment()
|
const [frag, push] = buildCodeFragment()
|
||||||
if (isBlockOperation(oper) && oper.parent) {
|
if (isBlockOperation(oper) && oper.parent) {
|
||||||
push(...genInsertionstate(oper, context))
|
push(...genInsertionState(oper, context))
|
||||||
}
|
}
|
||||||
push(...genOperation(oper, context))
|
push(...genOperation(oper, context))
|
||||||
return frag
|
return frag
|
||||||
|
@ -99,10 +99,8 @@ export function genEffects(
|
||||||
effects: IREffect[],
|
effects: IREffect[],
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
const {
|
const { helper } = context
|
||||||
helper,
|
const expressions = effects.flatMap(effect => effect.expressions)
|
||||||
block: { expressions },
|
|
||||||
} = context
|
|
||||||
const [frag, push, unshift] = buildCodeFragment()
|
const [frag, push, unshift] = buildCodeFragment()
|
||||||
let operationsCount = 0
|
let operationsCount = 0
|
||||||
const { ids, frag: declarationFrags } = processExpressions(
|
const { ids, frag: declarationFrags } = processExpressions(
|
||||||
|
@ -152,7 +150,7 @@ export function genEffect(
|
||||||
return frag
|
return frag
|
||||||
}
|
}
|
||||||
|
|
||||||
function genInsertionstate(
|
function genInsertionState(
|
||||||
operation: InsertionStateTypes,
|
operation: InsertionStateTypes,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {
|
||||||
NewlineType,
|
NewlineType,
|
||||||
type SimpleExpressionNode,
|
type SimpleExpressionNode,
|
||||||
isSimpleIdentifier,
|
isSimpleIdentifier,
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-dom'
|
||||||
import type { CodegenContext } from '../generate'
|
import type { CodegenContext } from '../generate'
|
||||||
import {
|
import {
|
||||||
IRDynamicPropsKind,
|
IRDynamicPropsKind,
|
||||||
|
|
|
@ -41,6 +41,7 @@ export function genSelf(
|
||||||
export function genChildren(
|
export function genChildren(
|
||||||
dynamic: IRDynamicInfo,
|
dynamic: IRDynamicInfo,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
|
pushBlock: (...items: CodeFragment[]) => number,
|
||||||
from: string = `n${dynamic.id}`,
|
from: string = `n${dynamic.id}`,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
const { helper } = context
|
const { helper } = context
|
||||||
|
@ -72,17 +73,17 @@ export function genChildren(
|
||||||
// p for "placeholder" variables that are meant for possible reuse by
|
// p for "placeholder" variables that are meant for possible reuse by
|
||||||
// other access paths
|
// other access paths
|
||||||
const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
|
const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
|
||||||
push(NEWLINE, `const ${variable} = `)
|
pushBlock(NEWLINE, `const ${variable} = `)
|
||||||
|
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (elementIndex - prev[1] === 1) {
|
if (elementIndex - prev[1] === 1) {
|
||||||
push(...genCall(helper('next'), prev[0]))
|
pushBlock(...genCall(helper('next'), prev[0]))
|
||||||
} else {
|
} else {
|
||||||
push(...genCall(helper('nthChild'), from, String(elementIndex)))
|
pushBlock(...genCall(helper('nthChild'), from, String(elementIndex)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (elementIndex === 0) {
|
if (elementIndex === 0) {
|
||||||
push(...genCall(helper('child'), from))
|
pushBlock(...genCall(helper('child'), from))
|
||||||
} else {
|
} else {
|
||||||
// check if there's a node that we can reuse from
|
// check if there's a node that we can reuse from
|
||||||
let init = genCall(helper('child'), from)
|
let init = genCall(helper('child'), from)
|
||||||
|
@ -91,7 +92,7 @@ export function genChildren(
|
||||||
} else if (elementIndex > 1) {
|
} else if (elementIndex > 1) {
|
||||||
init = genCall(helper('nthChild'), from, String(elementIndex))
|
init = genCall(helper('nthChild'), from, String(elementIndex))
|
||||||
}
|
}
|
||||||
push(...init)
|
pushBlock(...init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +110,7 @@ export function genChildren(
|
||||||
|
|
||||||
if (childrenToGen.length) {
|
if (childrenToGen.length) {
|
||||||
for (const [child, from] of childrenToGen) {
|
for (const [child, from] of childrenToGen) {
|
||||||
push(...genChildren(child, context, from))
|
push(...genChildren(child, context, pushBlock, from))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,6 @@ export interface BlockIRNode extends BaseIRNode {
|
||||||
tempId: number
|
tempId: number
|
||||||
effect: IREffect[]
|
effect: IREffect[]
|
||||||
operation: OperationNode[]
|
operation: OperationNode[]
|
||||||
expressions: SimpleExpressionNode[]
|
|
||||||
returns: number[]
|
returns: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
type TemplateChildNode,
|
type TemplateChildNode,
|
||||||
defaultOnError,
|
defaultOnError,
|
||||||
defaultOnWarn,
|
defaultOnWarn,
|
||||||
|
getSelfName,
|
||||||
isVSlot,
|
isVSlot,
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
|
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
|
||||||
|
@ -61,6 +62,7 @@ export type StructuralDirectiveTransform = (
|
||||||
export type TransformOptions = HackOptions<BaseTransformOptions>
|
export type TransformOptions = HackOptions<BaseTransformOptions>
|
||||||
|
|
||||||
export class TransformContext<T extends AllNode = AllNode> {
|
export class TransformContext<T extends AllNode = AllNode> {
|
||||||
|
selfName: string | null = null
|
||||||
parent: TransformContext<RootNode | ElementNode> | null = null
|
parent: TransformContext<RootNode | ElementNode> | null = null
|
||||||
root: TransformContext<RootNode>
|
root: TransformContext<RootNode>
|
||||||
index: number = 0
|
index: number = 0
|
||||||
|
@ -92,6 +94,7 @@ export class TransformContext<T extends AllNode = AllNode> {
|
||||||
) {
|
) {
|
||||||
this.options = extend({}, defaultOptions, options)
|
this.options = extend({}, defaultOptions, options)
|
||||||
this.root = this as TransformContext<RootNode>
|
this.root = this as TransformContext<RootNode>
|
||||||
|
if (options.filename) this.selfName = getSelfName(options.filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
enterBlock(ir: BlockIRNode, isVFor: boolean = false): () => void {
|
enterBlock(ir: BlockIRNode, isVFor: boolean = false): () => void {
|
||||||
|
@ -137,8 +140,10 @@ export class TransformContext<T extends AllNode = AllNode> {
|
||||||
|
|
||||||
registerEffect(
|
registerEffect(
|
||||||
expressions: SimpleExpressionNode[],
|
expressions: SimpleExpressionNode[],
|
||||||
...operations: OperationNode[]
|
operation: OperationNode | OperationNode[],
|
||||||
|
getIndex = (): number => this.block.effect.length,
|
||||||
): void {
|
): void {
|
||||||
|
const operations = [operation].flat()
|
||||||
expressions = expressions.filter(exp => !isConstantExpression(exp))
|
expressions = expressions.filter(exp => !isConstantExpression(exp))
|
||||||
if (
|
if (
|
||||||
this.inVOnce ||
|
this.inVOnce ||
|
||||||
|
@ -150,26 +155,10 @@ export class TransformContext<T extends AllNode = AllNode> {
|
||||||
return this.registerOperation(...operations)
|
return this.registerOperation(...operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.block.expressions.push(...expressions)
|
this.block.effect.splice(getIndex(), 0, {
|
||||||
const existing = this.block.effect.find(e =>
|
expressions,
|
||||||
isSameExpression(e.expressions, expressions),
|
operations,
|
||||||
)
|
})
|
||||||
if (existing) {
|
|
||||||
existing.operations.push(...operations)
|
|
||||||
} else {
|
|
||||||
this.block.effect.push({
|
|
||||||
expressions,
|
|
||||||
operations,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSameExpression(
|
|
||||||
a: SimpleExpressionNode[],
|
|
||||||
b: SimpleExpressionNode[],
|
|
||||||
) {
|
|
||||||
if (a.length !== b.length) return false
|
|
||||||
return a.every((exp, i) => exp.content === b[i].content)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOperation(...node: OperationNode[]): void {
|
registerOperation(...node: OperationNode[]): void {
|
||||||
|
|
|
@ -44,6 +44,8 @@ export const isReservedProp: (key: string) => boolean = /*#__PURE__*/ makeMap(
|
||||||
)
|
)
|
||||||
|
|
||||||
export const transformElement: NodeTransform = (node, context) => {
|
export const transformElement: NodeTransform = (node, context) => {
|
||||||
|
let effectIndex = context.block.effect.length
|
||||||
|
const getEffectIndex = () => effectIndex++
|
||||||
return function postTransformElement() {
|
return function postTransformElement() {
|
||||||
;({ node } = context)
|
;({ node } = context)
|
||||||
if (
|
if (
|
||||||
|
@ -62,6 +64,7 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||||
context as TransformContext<ElementNode>,
|
context as TransformContext<ElementNode>,
|
||||||
isComponent,
|
isComponent,
|
||||||
isDynamicComponent,
|
isDynamicComponent,
|
||||||
|
getEffectIndex,
|
||||||
)
|
)
|
||||||
|
|
||||||
let { parent } = context
|
let { parent } = context
|
||||||
|
@ -78,13 +81,23 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||||
parent.node.children.filter(child => child.type !== NodeTypes.COMMENT)
|
parent.node.children.filter(child => child.type !== NodeTypes.COMMENT)
|
||||||
.length === 1
|
.length === 1
|
||||||
|
|
||||||
;(isComponent ? transformComponentElement : transformNativeElement)(
|
if (isComponent) {
|
||||||
node as any,
|
transformComponentElement(
|
||||||
propsResult,
|
node as ComponentNode,
|
||||||
singleRoot,
|
propsResult,
|
||||||
context as TransformContext<ElementNode>,
|
singleRoot,
|
||||||
isDynamicComponent,
|
context,
|
||||||
)
|
isDynamicComponent,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
transformNativeElement(
|
||||||
|
node as PlainElementNode,
|
||||||
|
propsResult,
|
||||||
|
singleRoot,
|
||||||
|
context,
|
||||||
|
getEffectIndex,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +132,13 @@ function transformComponentElement(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset) {
|
if (asset) {
|
||||||
|
// self referencing component (inferred from filename)
|
||||||
|
if (context.selfName && capitalize(camelize(tag)) === context.selfName) {
|
||||||
|
// generators/block.ts has special check for __self postfix when generating
|
||||||
|
// component imports, which will pass additional `maybeSelfReference` flag
|
||||||
|
// to `resolveComponent`.
|
||||||
|
tag += `__self`
|
||||||
|
}
|
||||||
context.component.add(tag)
|
context.component.add(tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +150,7 @@ function transformComponentElement(
|
||||||
tag,
|
tag,
|
||||||
props: propsResult[0] ? propsResult[1] : [propsResult[1]],
|
props: propsResult[0] ? propsResult[1] : [propsResult[1]],
|
||||||
asset,
|
asset,
|
||||||
root: singleRoot,
|
root: singleRoot && !context.inVFor,
|
||||||
slots: [...context.slots],
|
slots: [...context.slots],
|
||||||
once: context.inVOnce,
|
once: context.inVOnce,
|
||||||
dynamic: dynamicComponent,
|
dynamic: dynamicComponent,
|
||||||
|
@ -176,7 +196,8 @@ function transformNativeElement(
|
||||||
node: PlainElementNode,
|
node: PlainElementNode,
|
||||||
propsResult: PropsResult,
|
propsResult: PropsResult,
|
||||||
singleRoot: boolean,
|
singleRoot: boolean,
|
||||||
context: TransformContext<ElementNode>,
|
context: TransformContext,
|
||||||
|
getEffectIndex: () => number,
|
||||||
) {
|
) {
|
||||||
const { tag } = node
|
const { tag } = node
|
||||||
const { scopeId } = context.options
|
const { scopeId } = context.options
|
||||||
|
@ -189,12 +210,16 @@ function transformNativeElement(
|
||||||
const dynamicProps: string[] = []
|
const dynamicProps: string[] = []
|
||||||
if (propsResult[0] /* dynamic props */) {
|
if (propsResult[0] /* dynamic props */) {
|
||||||
const [, dynamicArgs, expressions] = propsResult
|
const [, dynamicArgs, expressions] = propsResult
|
||||||
context.registerEffect(expressions, {
|
context.registerEffect(
|
||||||
type: IRNodeTypes.SET_DYNAMIC_PROPS,
|
expressions,
|
||||||
element: context.reference(),
|
{
|
||||||
props: dynamicArgs,
|
type: IRNodeTypes.SET_DYNAMIC_PROPS,
|
||||||
root: singleRoot,
|
element: context.reference(),
|
||||||
})
|
props: dynamicArgs,
|
||||||
|
root: singleRoot,
|
||||||
|
},
|
||||||
|
getEffectIndex,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
for (const prop of propsResult[1]) {
|
for (const prop of propsResult[1]) {
|
||||||
const { key, values } = prop
|
const { key, values } = prop
|
||||||
|
@ -203,13 +228,17 @@ function transformNativeElement(
|
||||||
if (values[0].content) template += `="${values[0].content}"`
|
if (values[0].content) template += `="${values[0].content}"`
|
||||||
} else {
|
} else {
|
||||||
dynamicProps.push(key.content)
|
dynamicProps.push(key.content)
|
||||||
context.registerEffect(values, {
|
context.registerEffect(
|
||||||
type: IRNodeTypes.SET_PROP,
|
values,
|
||||||
element: context.reference(),
|
{
|
||||||
prop,
|
type: IRNodeTypes.SET_PROP,
|
||||||
root: singleRoot,
|
element: context.reference(),
|
||||||
tag,
|
prop,
|
||||||
})
|
root: singleRoot,
|
||||||
|
tag,
|
||||||
|
},
|
||||||
|
getEffectIndex,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,6 +275,7 @@ export function buildProps(
|
||||||
context: TransformContext<ElementNode>,
|
context: TransformContext<ElementNode>,
|
||||||
isComponent: boolean,
|
isComponent: boolean,
|
||||||
isDynamicComponent?: boolean,
|
isDynamicComponent?: boolean,
|
||||||
|
getEffectIndex?: () => number,
|
||||||
): PropsResult {
|
): PropsResult {
|
||||||
const props = node.props as (VaporDirectiveNode | AttributeNode)[]
|
const props = node.props as (VaporDirectiveNode | AttributeNode)[]
|
||||||
if (props.length === 0) return [false, []]
|
if (props.length === 0) return [false, []]
|
||||||
|
@ -292,12 +322,12 @@ export function buildProps(
|
||||||
} else {
|
} else {
|
||||||
context.registerEffect(
|
context.registerEffect(
|
||||||
[prop.exp],
|
[prop.exp],
|
||||||
|
|
||||||
{
|
{
|
||||||
type: IRNodeTypes.SET_DYNAMIC_EVENTS,
|
type: IRNodeTypes.SET_DYNAMIC_EVENTS,
|
||||||
element: context.reference(),
|
element: context.reference(),
|
||||||
event: prop.exp,
|
event: prop.exp,
|
||||||
},
|
},
|
||||||
|
getEffectIndex,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
createSimpleExpression,
|
createSimpleExpression,
|
||||||
isStaticArgOf,
|
isStaticArgOf,
|
||||||
isStaticExp,
|
isStaticExp,
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-dom'
|
||||||
import type { NodeTransform, TransformContext } from '../transform'
|
import type { NodeTransform, TransformContext } from '../transform'
|
||||||
import {
|
import {
|
||||||
type BlockIRNode,
|
type BlockIRNode,
|
||||||
|
|
|
@ -23,6 +23,13 @@ const seen = new WeakMap<
|
||||||
WeakSet<TemplateChildNode | RootNode>
|
WeakSet<TemplateChildNode | RootNode>
|
||||||
>()
|
>()
|
||||||
|
|
||||||
|
export function markNonTemplate(
|
||||||
|
node: TemplateChildNode,
|
||||||
|
context: TransformContext,
|
||||||
|
): void {
|
||||||
|
seen.get(context.root)!.add(node)
|
||||||
|
}
|
||||||
|
|
||||||
export const transformText: NodeTransform = (node, context) => {
|
export const transformText: NodeTransform = (node, context) => {
|
||||||
if (!seen.has(context.root)) seen.set(context.root, new WeakSet())
|
if (!seen.has(context.root)) seen.set(context.root, new WeakSet())
|
||||||
if (seen.get(context.root)!.has(node)) {
|
if (seen.get(context.root)!.has(node)) {
|
||||||
|
@ -68,7 +75,7 @@ export const transformText: NodeTransform = (node, context) => {
|
||||||
prev.type === NodeTypes.TEXT
|
prev.type === NodeTypes.TEXT
|
||||||
) {
|
) {
|
||||||
// mark leading text node for skipping
|
// mark leading text node for skipping
|
||||||
seen.get(context.root)!.add(prev)
|
markNonTemplate(prev, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,7 +150,7 @@ function processTextContainer(
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTextLikeExpression(node: TextLike, context: TransformContext) {
|
function createTextLikeExpression(node: TextLike, context: TransformContext) {
|
||||||
seen.get(context.root)!.add(node)
|
markNonTemplate(node, context)
|
||||||
if (node.type === NodeTypes.TEXT) {
|
if (node.type === NodeTypes.TEXT) {
|
||||||
return createSimpleExpression(node.content, true, node.loc)
|
return createSimpleExpression(node.content, true, node.loc)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -29,7 +29,6 @@ export const newBlock = (node: BlockIRNode['node']): BlockIRNode => ({
|
||||||
effect: [],
|
effect: [],
|
||||||
operation: [],
|
operation: [],
|
||||||
returns: [],
|
returns: [],
|
||||||
expressions: [],
|
|
||||||
tempId: 0,
|
tempId: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,13 @@ export function processIf(
|
||||||
if (siblings) {
|
if (siblings) {
|
||||||
let i = siblings.length
|
let i = siblings.length
|
||||||
while (i--) {
|
while (i--) {
|
||||||
if (siblings[i].operation) lastIfNode = siblings[i].operation
|
if (
|
||||||
|
siblings[i].operation &&
|
||||||
|
siblings[i].operation!.type === IRNodeTypes.IF
|
||||||
|
) {
|
||||||
|
lastIfNode = siblings[i].operation
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
createCompilerError,
|
createCompilerError,
|
||||||
isTemplateNode,
|
isTemplateNode,
|
||||||
isVSlot,
|
isVSlot,
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-dom'
|
||||||
import type { NodeTransform, TransformContext } from '../transform'
|
import type { NodeTransform, TransformContext } from '../transform'
|
||||||
import { newBlock } from './utils'
|
import { newBlock } from './utils'
|
||||||
import {
|
import {
|
||||||
|
@ -24,6 +24,7 @@ import {
|
||||||
type VaporDirectiveNode,
|
type VaporDirectiveNode,
|
||||||
} from '../ir'
|
} from '../ir'
|
||||||
import { findDir, resolveExpression } from '../utils'
|
import { findDir, resolveExpression } from '../utils'
|
||||||
|
import { markNonTemplate } from './transformText'
|
||||||
|
|
||||||
export const transformVSlot: NodeTransform = (node, context) => {
|
export const transformVSlot: NodeTransform = (node, context) => {
|
||||||
if (node.type !== NodeTypes.ELEMENT) return
|
if (node.type !== NodeTypes.ELEMENT) return
|
||||||
|
@ -66,11 +67,21 @@ function transformComponentSlot(
|
||||||
) {
|
) {
|
||||||
const { children } = node
|
const { children } = node
|
||||||
const arg = dir && dir.arg
|
const arg = dir && dir.arg
|
||||||
const nonSlotTemplateChildren = children.filter(
|
|
||||||
n =>
|
// whitespace: 'preserve'
|
||||||
isNonWhitespaceContent(node) &&
|
const emptyTextNodes: TemplateChildNode[] = []
|
||||||
!(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)),
|
const nonSlotTemplateChildren = children.filter(n => {
|
||||||
)
|
if (isNonWhitespaceContent(n)) {
|
||||||
|
return !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot))
|
||||||
|
} else {
|
||||||
|
emptyTextNodes.push(n)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!nonSlotTemplateChildren.length) {
|
||||||
|
emptyTextNodes.forEach(n => {
|
||||||
|
markNonTemplate(n, context)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const [block, onExit] = createSlotBlock(node, dir, context)
|
const [block, onExit] = createSlotBlock(node, dir, context)
|
||||||
|
|
||||||
|
|
|
@ -1017,6 +1017,17 @@ describe('reactivity/computed', () => {
|
||||||
expect(cValue.value).toBe(1)
|
expect(cValue.value).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should not recompute if computed does not track reactive data', async () => {
|
||||||
|
const spy = vi.fn()
|
||||||
|
const c1 = computed(() => spy())
|
||||||
|
|
||||||
|
c1.value
|
||||||
|
ref(0).value++ // update globalVersion
|
||||||
|
c1.value
|
||||||
|
|
||||||
|
expect(spy).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
test('computed should remain live after losing all subscribers', () => {
|
test('computed should remain live after losing all subscribers', () => {
|
||||||
const state = reactive({ a: 1 })
|
const state = reactive({ a: 1 })
|
||||||
const p = computed(() => state.a + 1)
|
const p = computed(() => state.a + 1)
|
||||||
|
|
|
@ -20,7 +20,7 @@ describe.skipIf(!global.gc)('reactivity/gc', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// #9233
|
// #9233
|
||||||
it.todo('should release computed cache', async () => {
|
it('should release computed cache', async () => {
|
||||||
const src = ref<{} | undefined>({})
|
const src = ref<{} | undefined>({})
|
||||||
// @ts-expect-error ES2021 API
|
// @ts-expect-error ES2021 API
|
||||||
const srcRef = new WeakRef(src.value!)
|
const srcRef = new WeakRef(src.value!)
|
||||||
|
@ -35,7 +35,7 @@ describe.skipIf(!global.gc)('reactivity/gc', () => {
|
||||||
expect(srcRef.deref()).toBeUndefined()
|
expect(srcRef.deref()).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it.todo('should release reactive property dep', async () => {
|
it('should release reactive property dep', async () => {
|
||||||
const src = reactive({ foo: 1 })
|
const src = reactive({ foo: 1 })
|
||||||
|
|
||||||
let c: ComputedRef | undefined = computed(() => src.foo)
|
let c: ComputedRef | undefined = computed(() => src.foo)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { isRef, ref } from '../src/ref'
|
import { isRef, ref, shallowRef } from '../src/ref'
|
||||||
import {
|
import {
|
||||||
isProxy,
|
isProxy,
|
||||||
isReactive,
|
isReactive,
|
||||||
|
@ -301,6 +301,13 @@ describe('reactivity/reactive', () => {
|
||||||
expect(() => markRaw(obj)).not.toThrowError()
|
expect(() => markRaw(obj)).not.toThrowError()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should not markRaw object as reactive', () => {
|
||||||
|
const a = reactive({ a: 1 })
|
||||||
|
const b = reactive({ b: 2 }) as any
|
||||||
|
b.a = markRaw(toRaw(a))
|
||||||
|
expect(b.a === a).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
test('should not observe non-extensible objects', () => {
|
test('should not observe non-extensible objects', () => {
|
||||||
const obj = reactive({
|
const obj = reactive({
|
||||||
foo: Object.preventExtensions({ a: 1 }),
|
foo: Object.preventExtensions({ a: 1 }),
|
||||||
|
@ -419,4 +426,17 @@ describe('reactivity/reactive', () => {
|
||||||
map.set(void 0, 1)
|
map.set(void 0, 1)
|
||||||
expect(c.value).toBe(1)
|
expect(c.value).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should return true for reactive objects', () => {
|
||||||
|
expect(isReactive(reactive({}))).toBe(true)
|
||||||
|
expect(isReactive(readonly(reactive({})))).toBe(true)
|
||||||
|
expect(isReactive(ref({}).value)).toBe(true)
|
||||||
|
expect(isReactive(readonly(ref({})).value)).toBe(true)
|
||||||
|
expect(isReactive(shallowReactive({}))).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return false for non-reactive objects', () => {
|
||||||
|
expect(isReactive(ref(true))).toBe(false)
|
||||||
|
expect(isReactive(shallowRef({}).value)).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/reactivity",
|
"name": "@vue/reactivity",
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"description": "@vue/reactivity",
|
"description": "@vue/reactivity",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "dist/reactivity.esm-bundler.js",
|
"module": "dist/reactivity.esm-bundler.js",
|
||||||
|
|
|
@ -87,7 +87,10 @@ export class ComputedRefImpl<T = any> implements Dependency, Subscriber {
|
||||||
get dep(): Dependency {
|
get dep(): Dependency {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
// for backwards compat
|
/**
|
||||||
|
* @internal
|
||||||
|
* for backwards compat
|
||||||
|
*/
|
||||||
get _dirty(): boolean {
|
get _dirty(): boolean {
|
||||||
const flags = this.flags
|
const flags = this.flags
|
||||||
if (
|
if (
|
||||||
|
@ -99,6 +102,10 @@ export class ComputedRefImpl<T = any> implements Dependency, Subscriber {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* for backwards compat
|
||||||
|
*/
|
||||||
set _dirty(v: boolean) {
|
set _dirty(v: boolean) {
|
||||||
if (v) {
|
if (v) {
|
||||||
this.flags |= SubscriberFlags.Dirty
|
this.flags |= SubscriberFlags.Dirty
|
||||||
|
@ -112,12 +119,6 @@ export class ComputedRefImpl<T = any> implements Dependency, Subscriber {
|
||||||
// dev only
|
// dev only
|
||||||
onTrigger?: (event: DebuggerEvent) => void
|
onTrigger?: (event: DebuggerEvent) => void
|
||||||
|
|
||||||
/**
|
|
||||||
* Dev only
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
_warnRecursive?: boolean
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public fn: ComputedGetter<T>,
|
public fn: ComputedGetter<T>,
|
||||||
private readonly setter: ComputedSetter<T> | undefined,
|
private readonly setter: ComputedSetter<T> | undefined,
|
||||||
|
|
|
@ -108,9 +108,9 @@ export declare const ShallowReactiveMarker: unique symbol
|
||||||
export type ShallowReactive<T> = T & { [ShallowReactiveMarker]?: true }
|
export type ShallowReactive<T> = T & { [ShallowReactiveMarker]?: true }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shallow version of {@link reactive()}.
|
* Shallow version of {@link reactive}.
|
||||||
*
|
*
|
||||||
* Unlike {@link reactive()}, there is no deep conversion: only root-level
|
* Unlike {@link reactive}, there is no deep conversion: only root-level
|
||||||
* properties are reactive for a shallow reactive object. Property values are
|
* properties are reactive for a shallow reactive object. Property values are
|
||||||
* stored and exposed as-is - this also means properties with ref values will
|
* stored and exposed as-is - this also means properties with ref values will
|
||||||
* not be automatically unwrapped.
|
* not be automatically unwrapped.
|
||||||
|
@ -178,7 +178,7 @@ export type DeepReadonly<T> = T extends Builtin
|
||||||
* the original.
|
* the original.
|
||||||
*
|
*
|
||||||
* A readonly proxy is deep: any nested property accessed will be readonly as
|
* A readonly proxy is deep: any nested property accessed will be readonly as
|
||||||
* well. It also has the same ref-unwrapping behavior as {@link reactive()},
|
* well. It also has the same ref-unwrapping behavior as {@link reactive},
|
||||||
* except the unwrapped values will also be made readonly.
|
* except the unwrapped values will also be made readonly.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
|
@ -215,9 +215,9 @@ export function readonly<T extends object>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shallow version of {@link readonly()}.
|
* Shallow version of {@link readonly}.
|
||||||
*
|
*
|
||||||
* Unlike {@link readonly()}, there is no deep conversion: only root-level
|
* Unlike {@link readonly}, there is no deep conversion: only root-level
|
||||||
* properties are made readonly. Property values are stored and exposed as-is -
|
* properties are made readonly. Property values are stored and exposed as-is -
|
||||||
* this also means properties with ref values will not be automatically
|
* this also means properties with ref values will not be automatically
|
||||||
* unwrapped.
|
* unwrapped.
|
||||||
|
@ -279,16 +279,16 @@ function createReactiveObject(
|
||||||
) {
|
) {
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
// target already has corresponding Proxy
|
|
||||||
const existingProxy = proxyMap.get(target)
|
|
||||||
if (existingProxy) {
|
|
||||||
return existingProxy
|
|
||||||
}
|
|
||||||
// only specific value types can be observed.
|
// only specific value types can be observed.
|
||||||
const targetType = getTargetType(target)
|
const targetType = getTargetType(target)
|
||||||
if (targetType === TargetType.INVALID) {
|
if (targetType === TargetType.INVALID) {
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
|
// target already has corresponding Proxy
|
||||||
|
const existingProxy = proxyMap.get(target)
|
||||||
|
if (existingProxy) {
|
||||||
|
return existingProxy
|
||||||
|
}
|
||||||
const proxy = new Proxy(
|
const proxy = new Proxy(
|
||||||
target,
|
target,
|
||||||
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
|
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
|
||||||
|
@ -298,8 +298,8 @@ function createReactiveObject(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an object is a proxy created by {@link reactive()} or
|
* Checks if an object is a proxy created by {@link reactive} or
|
||||||
* {@link shallowReactive()} (or {@link ref()} in some cases).
|
* {@link shallowReactive} (or {@link ref} in some cases).
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```js
|
* ```js
|
||||||
|
@ -327,7 +327,7 @@ export function isReactive(value: unknown): boolean {
|
||||||
* readonly object can change, but they can't be assigned directly via the
|
* readonly object can change, but they can't be assigned directly via the
|
||||||
* passed object.
|
* passed object.
|
||||||
*
|
*
|
||||||
* The proxies created by {@link readonly()} and {@link shallowReadonly()} are
|
* The proxies created by {@link readonly} and {@link shallowReadonly} are
|
||||||
* both considered readonly, as is a computed ref without a set function.
|
* both considered readonly, as is a computed ref without a set function.
|
||||||
*
|
*
|
||||||
* @param value - The value to check.
|
* @param value - The value to check.
|
||||||
|
@ -343,7 +343,7 @@ export function isShallow(value: unknown): boolean {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an object is a proxy created by {@link reactive},
|
* Checks if an object is a proxy created by {@link reactive},
|
||||||
* {@link readonly}, {@link shallowReactive} or {@link shallowReadonly()}.
|
* {@link readonly}, {@link shallowReactive} or {@link shallowReadonly}.
|
||||||
*
|
*
|
||||||
* @param value - The value to check.
|
* @param value - The value to check.
|
||||||
* @see {@link https://vuejs.org/api/reactivity-utilities.html#isproxy}
|
* @see {@link https://vuejs.org/api/reactivity-utilities.html#isproxy}
|
||||||
|
@ -356,8 +356,8 @@ export function isProxy(value: any): boolean {
|
||||||
* Returns the raw, original object of a Vue-created proxy.
|
* Returns the raw, original object of a Vue-created proxy.
|
||||||
*
|
*
|
||||||
* `toRaw()` can return the original object from proxies created by
|
* `toRaw()` can return the original object from proxies created by
|
||||||
* {@link reactive()}, {@link readonly()}, {@link shallowReactive()} or
|
* {@link reactive}, {@link readonly}, {@link shallowReactive} or
|
||||||
* {@link shallowReadonly()}.
|
* {@link shallowReadonly}.
|
||||||
*
|
*
|
||||||
* This is an escape hatch that can be used to temporarily read without
|
* This is an escape hatch that can be used to temporarily read without
|
||||||
* incurring proxy access / tracking overhead or write without triggering
|
* incurring proxy access / tracking overhead or write without triggering
|
||||||
|
@ -397,7 +397,7 @@ export type Raw<T> = T & { [RawSymbol]?: true }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* **Warning:** `markRaw()` together with the shallow APIs such as
|
* **Warning:** `markRaw()` together with the shallow APIs such as
|
||||||
* {@link shallowReactive()} allow you to selectively opt-out of the default
|
* {@link shallowReactive} allow you to selectively opt-out of the default
|
||||||
* deep reactive/readonly conversion and embed raw, non-proxied objects in your
|
* deep reactive/readonly conversion and embed raw, non-proxied objects in your
|
||||||
* state graph.
|
* state graph.
|
||||||
*
|
*
|
||||||
|
|
|
@ -68,7 +68,7 @@ export type ShallowRef<T = any, S = T> = Ref<T, S> & {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shallow version of {@link ref()}.
|
* Shallow version of {@link ref}.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```js
|
* ```js
|
||||||
|
@ -238,7 +238,7 @@ export function unref<T>(ref: MaybeRef<T> | ComputedRef<T>): T {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalizes values / refs / getters to values.
|
* Normalizes values / refs / getters to values.
|
||||||
* This is similar to {@link unref()}, except that it also normalizes getters.
|
* This is similar to {@link unref}, except that it also normalizes getters.
|
||||||
* If the argument is a getter, it will be invoked and its return value will
|
* If the argument is a getter, it will be invoked and its return value will
|
||||||
* be returned.
|
* be returned.
|
||||||
*
|
*
|
||||||
|
@ -348,7 +348,7 @@ export type ToRefs<T = any> = {
|
||||||
/**
|
/**
|
||||||
* Converts a reactive object to a plain object where each property of the
|
* Converts a reactive object to a plain object where each property of the
|
||||||
* resulting object is a ref pointing to the corresponding property of the
|
* resulting object is a ref pointing to the corresponding property of the
|
||||||
* original object. Each individual ref is created using {@link toRef()}.
|
* original object. Each individual ref is created using {@link toRef}.
|
||||||
*
|
*
|
||||||
* @param object - Reactive object to be made into an object of linked refs.
|
* @param object - Reactive object to be made into an object of linked refs.
|
||||||
* @see {@link https://vuejs.org/api/reactivity-utilities.html#torefs}
|
* @see {@link https://vuejs.org/api/reactivity-utilities.html#torefs}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// Ported from https://github.com/stackblitz/alien-signals/blob/v1.0.4/src/system.ts
|
// Ported from https://github.com/stackblitz/alien-signals/blob/v1.0.13/src/system.ts
|
||||||
import type { ComputedRefImpl as Computed } from './computed.js'
|
import type { ComputedRefImpl as Computed } from './computed.js'
|
||||||
import type { ReactiveEffect as Effect } from './effect.js'
|
import type { ReactiveEffect as Effect } from './effect.js'
|
||||||
|
|
||||||
|
@ -32,9 +32,16 @@ export const enum SubscriberFlags {
|
||||||
Propagated = Dirty | PendingComputed,
|
Propagated = Dirty | PendingComputed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface OneWayLink<T> {
|
||||||
|
target: T
|
||||||
|
linked: OneWayLink<T> | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const notifyBuffer: (Effect | undefined)[] = []
|
||||||
|
|
||||||
let batchDepth = 0
|
let batchDepth = 0
|
||||||
let queuedEffects: Effect | undefined
|
let notifyIndex = 0
|
||||||
let queuedEffectsTail: Effect | undefined
|
let notifyBufferLength = 0
|
||||||
|
|
||||||
export function startBatch(): void {
|
export function startBatch(): void {
|
||||||
++batchDepth
|
++batchDepth
|
||||||
|
@ -67,80 +74,81 @@ export function link(dep: Dependency, sub: Subscriber): Link | undefined {
|
||||||
return linkNewDep(dep, sub, nextDep, currentDep)
|
return linkNewDep(dep, sub, nextDep, currentDep)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function propagate(link: Link): void {
|
export function propagate(current: Link): void {
|
||||||
|
let next = current.nextSub
|
||||||
|
let branchs: OneWayLink<Link | undefined> | undefined
|
||||||
|
let branchDepth = 0
|
||||||
let targetFlag = SubscriberFlags.Dirty
|
let targetFlag = SubscriberFlags.Dirty
|
||||||
let subs = link
|
|
||||||
let stack = 0
|
|
||||||
|
|
||||||
top: do {
|
top: do {
|
||||||
const sub = link.sub
|
const sub = current.sub
|
||||||
const subFlags = sub.flags
|
const subFlags = sub.flags
|
||||||
|
|
||||||
|
let shouldNotify = false
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(!(
|
!(
|
||||||
subFlags &
|
subFlags &
|
||||||
(SubscriberFlags.Tracking |
|
(SubscriberFlags.Tracking |
|
||||||
SubscriberFlags.Recursed |
|
SubscriberFlags.Recursed |
|
||||||
SubscriberFlags.Propagated)
|
SubscriberFlags.Propagated)
|
||||||
) &&
|
)
|
||||||
((sub.flags = subFlags | targetFlag), true)) ||
|
|
||||||
(subFlags & SubscriberFlags.Recursed &&
|
|
||||||
!(subFlags & SubscriberFlags.Tracking) &&
|
|
||||||
((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag),
|
|
||||||
true)) ||
|
|
||||||
(!(subFlags & SubscriberFlags.Propagated) &&
|
|
||||||
isValidLink(link, sub) &&
|
|
||||||
((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag),
|
|
||||||
(sub as Dependency).subs !== undefined))
|
|
||||||
) {
|
) {
|
||||||
|
sub.flags = subFlags | targetFlag
|
||||||
|
shouldNotify = true
|
||||||
|
} else if (
|
||||||
|
subFlags & SubscriberFlags.Recursed &&
|
||||||
|
!(subFlags & SubscriberFlags.Tracking)
|
||||||
|
) {
|
||||||
|
sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag
|
||||||
|
shouldNotify = true
|
||||||
|
} else if (
|
||||||
|
!(subFlags & SubscriberFlags.Propagated) &&
|
||||||
|
isValidLink(current, sub)
|
||||||
|
) {
|
||||||
|
sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag
|
||||||
|
shouldNotify = (sub as Dependency).subs !== undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldNotify) {
|
||||||
const subSubs = (sub as Dependency).subs
|
const subSubs = (sub as Dependency).subs
|
||||||
if (subSubs !== undefined) {
|
if (subSubs !== undefined) {
|
||||||
|
current = subSubs
|
||||||
if (subSubs.nextSub !== undefined) {
|
if (subSubs.nextSub !== undefined) {
|
||||||
subSubs.prevSub = subs
|
branchs = { target: next, linked: branchs }
|
||||||
link = subs = subSubs
|
++branchDepth
|
||||||
targetFlag = SubscriberFlags.PendingComputed
|
next = current.nextSub
|
||||||
++stack
|
|
||||||
} else {
|
|
||||||
link = subSubs
|
|
||||||
targetFlag = SubscriberFlags.PendingComputed
|
|
||||||
}
|
}
|
||||||
|
targetFlag = SubscriberFlags.PendingComputed
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (subFlags & SubscriberFlags.Effect) {
|
if (subFlags & SubscriberFlags.Effect) {
|
||||||
if (queuedEffectsTail !== undefined) {
|
notifyBuffer[notifyBufferLength++] = sub as Effect
|
||||||
queuedEffectsTail.depsTail!.nextDep = sub.deps
|
|
||||||
} else {
|
|
||||||
queuedEffects = sub as Effect
|
|
||||||
}
|
|
||||||
queuedEffectsTail = sub as Effect
|
|
||||||
}
|
}
|
||||||
} else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) {
|
} else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) {
|
||||||
sub.flags = subFlags | targetFlag
|
sub.flags = subFlags | targetFlag
|
||||||
} else if (
|
} else if (
|
||||||
!(subFlags & targetFlag) &&
|
!(subFlags & targetFlag) &&
|
||||||
subFlags & SubscriberFlags.Propagated &&
|
subFlags & SubscriberFlags.Propagated &&
|
||||||
isValidLink(link, sub)
|
isValidLink(current, sub)
|
||||||
) {
|
) {
|
||||||
sub.flags = subFlags | targetFlag
|
sub.flags = subFlags | targetFlag
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((link = subs.nextSub!) !== undefined) {
|
if ((current = next!) !== undefined) {
|
||||||
subs = link
|
next = current.nextSub
|
||||||
targetFlag = stack
|
targetFlag = branchDepth
|
||||||
? SubscriberFlags.PendingComputed
|
? SubscriberFlags.PendingComputed
|
||||||
: SubscriberFlags.Dirty
|
: SubscriberFlags.Dirty
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
while (stack) {
|
while (branchDepth--) {
|
||||||
--stack
|
current = branchs!.target!
|
||||||
const dep = subs.dep
|
branchs = branchs!.linked
|
||||||
const depSubs = dep.subs!
|
if (current !== undefined) {
|
||||||
subs = depSubs.prevSub!
|
next = current.nextSub
|
||||||
depSubs.prevSub = undefined
|
targetFlag = branchDepth
|
||||||
if ((link = subs.nextSub!) !== undefined) {
|
|
||||||
subs = link
|
|
||||||
targetFlag = stack
|
|
||||||
? SubscriberFlags.PendingComputed
|
? SubscriberFlags.PendingComputed
|
||||||
: SubscriberFlags.Dirty
|
: SubscriberFlags.Dirty
|
||||||
continue top
|
continue top
|
||||||
|
@ -194,35 +202,26 @@ export function processComputedUpdate(
|
||||||
computed: Computed,
|
computed: Computed,
|
||||||
flags: SubscriberFlags,
|
flags: SubscriberFlags,
|
||||||
): void {
|
): void {
|
||||||
if (
|
if (flags & SubscriberFlags.Dirty || checkDirty(computed.deps!)) {
|
||||||
flags & SubscriberFlags.Dirty ||
|
|
||||||
(checkDirty(computed.deps!)
|
|
||||||
? true
|
|
||||||
: ((computed.flags = flags & ~SubscriberFlags.PendingComputed), false))
|
|
||||||
) {
|
|
||||||
if (computed.update()) {
|
if (computed.update()) {
|
||||||
const subs = computed.subs
|
const subs = computed.subs
|
||||||
if (subs !== undefined) {
|
if (subs !== undefined) {
|
||||||
shallowPropagate(subs)
|
shallowPropagate(subs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
computed.flags = flags & ~SubscriberFlags.PendingComputed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function processEffectNotifications(): void {
|
export function processEffectNotifications(): void {
|
||||||
while (queuedEffects !== undefined) {
|
while (notifyIndex < notifyBufferLength) {
|
||||||
const effect = queuedEffects
|
const effect = notifyBuffer[notifyIndex]!
|
||||||
const depsTail = effect.depsTail!
|
notifyBuffer[notifyIndex++] = undefined
|
||||||
const queuedNext = depsTail.nextDep
|
|
||||||
if (queuedNext !== undefined) {
|
|
||||||
depsTail.nextDep = undefined
|
|
||||||
queuedEffects = queuedNext.sub as Effect
|
|
||||||
} else {
|
|
||||||
queuedEffects = undefined
|
|
||||||
queuedEffectsTail = undefined
|
|
||||||
}
|
|
||||||
effect.notify()
|
effect.notify()
|
||||||
}
|
}
|
||||||
|
notifyIndex = 0
|
||||||
|
notifyBufferLength = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkNewDep(
|
function linkNewDep(
|
||||||
|
@ -259,15 +258,18 @@ function linkNewDep(
|
||||||
return newLink
|
return newLink
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkDirty(link: Link): boolean {
|
function checkDirty(current: Link): boolean {
|
||||||
let stack = 0
|
let prevLinks: OneWayLink<Link> | undefined
|
||||||
|
let checkDepth = 0
|
||||||
let dirty: boolean
|
let dirty: boolean
|
||||||
|
|
||||||
top: do {
|
top: do {
|
||||||
dirty = false
|
dirty = false
|
||||||
const dep = link.dep
|
const dep = current.dep
|
||||||
|
|
||||||
if ('flags' in dep) {
|
if (current.sub.flags & SubscriberFlags.Dirty) {
|
||||||
|
dirty = true
|
||||||
|
} else if ('flags' in dep) {
|
||||||
const depFlags = dep.flags
|
const depFlags = dep.flags
|
||||||
if (
|
if (
|
||||||
(depFlags & (SubscriberFlags.Computed | SubscriberFlags.Dirty)) ===
|
(depFlags & (SubscriberFlags.Computed | SubscriberFlags.Dirty)) ===
|
||||||
|
@ -285,58 +287,49 @@ function checkDirty(link: Link): boolean {
|
||||||
(SubscriberFlags.Computed | SubscriberFlags.PendingComputed)) ===
|
(SubscriberFlags.Computed | SubscriberFlags.PendingComputed)) ===
|
||||||
(SubscriberFlags.Computed | SubscriberFlags.PendingComputed)
|
(SubscriberFlags.Computed | SubscriberFlags.PendingComputed)
|
||||||
) {
|
) {
|
||||||
const depSubs = dep.subs!
|
if (current.nextSub !== undefined || current.prevSub !== undefined) {
|
||||||
if (depSubs.nextSub !== undefined) {
|
prevLinks = { target: current, linked: prevLinks }
|
||||||
depSubs.prevSub = link
|
|
||||||
}
|
}
|
||||||
link = dep.deps!
|
current = dep.deps!
|
||||||
++stack
|
++checkDepth
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dirty && link.nextDep !== undefined) {
|
if (!dirty && current.nextDep !== undefined) {
|
||||||
link = link.nextDep
|
current = current.nextDep
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stack) {
|
while (checkDepth) {
|
||||||
let sub = link.sub as Computed
|
--checkDepth
|
||||||
do {
|
const sub = current.sub as Computed
|
||||||
--stack
|
const firstSub = sub.subs!
|
||||||
const subSubs = sub.subs!
|
if (dirty) {
|
||||||
|
if (sub.update()) {
|
||||||
if (dirty) {
|
if (firstSub.nextSub !== undefined) {
|
||||||
if (sub.update()) {
|
current = prevLinks!.target
|
||||||
if ((link = subSubs.prevSub!) !== undefined) {
|
prevLinks = prevLinks!.linked
|
||||||
subSubs.prevSub = undefined
|
shallowPropagate(firstSub)
|
||||||
shallowPropagate(subSubs)
|
} else {
|
||||||
sub = link.sub as Computed
|
current = firstSub
|
||||||
} else {
|
|
||||||
sub = subSubs.sub as Computed
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
} else {
|
continue
|
||||||
sub.flags &= ~SubscriberFlags.PendingComputed
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if ((link = subSubs.prevSub!) !== undefined) {
|
sub.flags &= ~SubscriberFlags.PendingComputed
|
||||||
subSubs.prevSub = undefined
|
}
|
||||||
if (link.nextDep !== undefined) {
|
if (firstSub.nextSub !== undefined) {
|
||||||
link = link.nextDep
|
current = prevLinks!.target
|
||||||
continue top
|
prevLinks = prevLinks!.linked
|
||||||
}
|
} else {
|
||||||
sub = link.sub as Computed
|
current = firstSub
|
||||||
} else {
|
}
|
||||||
if ((link = subSubs.nextDep!) !== undefined) {
|
if (current.nextDep !== undefined) {
|
||||||
continue top
|
current = current.nextDep
|
||||||
}
|
continue top
|
||||||
sub = subSubs.sub as Computed
|
}
|
||||||
}
|
dirty = false
|
||||||
|
|
||||||
dirty = false
|
|
||||||
} while (stack)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return dirty
|
return dirty
|
||||||
|
|
|
@ -324,4 +324,98 @@ describe('component: slots', () => {
|
||||||
'Slot "default" invoked outside of the render function',
|
'Slot "default" invoked outside of the render function',
|
||||||
).not.toHaveBeenWarned()
|
).not.toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('basic warn', () => {
|
||||||
|
const Comp = {
|
||||||
|
setup(_: any, { slots }: any) {
|
||||||
|
slots.default && slots.default()
|
||||||
|
return () => null
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const App = {
|
||||||
|
setup() {
|
||||||
|
return () => h(Comp, () => h('div'))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
createApp(App).mount(nodeOps.createElement('div'))
|
||||||
|
expect(
|
||||||
|
'Slot "default" invoked outside of the render function',
|
||||||
|
).toHaveBeenWarned()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('basic warn when mounting another app in setup', () => {
|
||||||
|
const Comp = {
|
||||||
|
setup(_: any, { slots }: any) {
|
||||||
|
slots.default?.()
|
||||||
|
return () => null
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const mountComp = () => {
|
||||||
|
createApp({
|
||||||
|
setup() {
|
||||||
|
return () => h(Comp, () => 'msg')
|
||||||
|
},
|
||||||
|
}).mount(nodeOps.createElement('div'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const App = {
|
||||||
|
setup() {
|
||||||
|
mountComp()
|
||||||
|
return () => null
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
createApp(App).mount(nodeOps.createElement('div'))
|
||||||
|
expect(
|
||||||
|
'Slot "default" invoked outside of the render function',
|
||||||
|
).toHaveBeenWarned()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not warn when render in setup', () => {
|
||||||
|
const container = {
|
||||||
|
setup(_: any, { slots }: any) {
|
||||||
|
return () => slots.default && slots.default()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const comp = h(container, null, () => h('div'))
|
||||||
|
|
||||||
|
const App = {
|
||||||
|
setup() {
|
||||||
|
render(h(comp), nodeOps.createElement('div'))
|
||||||
|
return () => null
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
createApp(App).mount(nodeOps.createElement('div'))
|
||||||
|
expect(
|
||||||
|
'Slot "default" invoked outside of the render function',
|
||||||
|
).not.toHaveBeenWarned()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('basic warn when render in setup', () => {
|
||||||
|
const container = {
|
||||||
|
setup(_: any, { slots }: any) {
|
||||||
|
slots.default && slots.default()
|
||||||
|
return () => null
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const comp = h(container, null, () => h('div'))
|
||||||
|
|
||||||
|
const App = {
|
||||||
|
setup() {
|
||||||
|
render(h(comp), nodeOps.createElement('div'))
|
||||||
|
return () => null
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
createApp(App).mount(nodeOps.createElement('div'))
|
||||||
|
expect(
|
||||||
|
'Slot "default" invoked outside of the render function',
|
||||||
|
).toHaveBeenWarned()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1198,4 +1198,51 @@ describe('BaseTransition', () => {
|
||||||
test('should not error on KeepAlive w/ function children', () => {
|
test('should not error on KeepAlive w/ function children', () => {
|
||||||
expect(() => mount({}, () => () => h('div'), true)).not.toThrow()
|
expect(() => mount({}, () => () => h('div'), true)).not.toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #12465
|
||||||
|
test('mode: "out-in" w/ KeepAlive + fallthrough attrs (prod mode)', async () => {
|
||||||
|
__DEV__ = false
|
||||||
|
async function testOutIn({ trueBranch, falseBranch }: ToggleOptions) {
|
||||||
|
const toggle = ref(true)
|
||||||
|
const { props, cbs } = mockProps({ mode: 'out-in' }, true)
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
const App = {
|
||||||
|
render() {
|
||||||
|
return h(
|
||||||
|
BaseTransition,
|
||||||
|
{
|
||||||
|
...props,
|
||||||
|
class: 'test',
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
h(KeepAlive, null, toggle.value ? trueBranch() : falseBranch()),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
render(h(App), root)
|
||||||
|
|
||||||
|
expect(serializeInner(root)).toBe(`<div class="test">0</div>`)
|
||||||
|
|
||||||
|
// trigger toggle
|
||||||
|
toggle.value = false
|
||||||
|
await nextTick()
|
||||||
|
expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
|
||||||
|
expect(serialize((props.onBeforeLeave as any).mock.calls[0][0])).toBe(
|
||||||
|
`<div class="test">0</div>`,
|
||||||
|
)
|
||||||
|
expect(props.onLeave).toHaveBeenCalledTimes(1)
|
||||||
|
expect(serialize((props.onLeave as any).mock.calls[0][0])).toBe(
|
||||||
|
`<div class="test">0</div>`,
|
||||||
|
)
|
||||||
|
expect(props.onAfterLeave).not.toHaveBeenCalled()
|
||||||
|
// enter should not have started
|
||||||
|
expect(props.onBeforeEnter).not.toHaveBeenCalled()
|
||||||
|
expect(props.onEnter).not.toHaveBeenCalled()
|
||||||
|
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||||
|
cbs.doneLeave[`<div class="test">0</div>`]()
|
||||||
|
expect(serializeInner(root)).toBe(`<span class="test">0</span>`)
|
||||||
|
}
|
||||||
|
await runTestWithKeepAlive(testOutIn)
|
||||||
|
__DEV__ = true
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,14 +10,28 @@ import {
|
||||||
markRaw,
|
markRaw,
|
||||||
nextTick,
|
nextTick,
|
||||||
nodeOps,
|
nodeOps,
|
||||||
|
onMounted,
|
||||||
h as originalH,
|
h as originalH,
|
||||||
ref,
|
ref,
|
||||||
render,
|
render,
|
||||||
|
serialize,
|
||||||
serializeInner,
|
serializeInner,
|
||||||
withDirectives,
|
withDirectives,
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import { Fragment, createCommentVNode, createVNode } from '../../src/vnode'
|
import {
|
||||||
|
Fragment,
|
||||||
|
createBlock,
|
||||||
|
createCommentVNode,
|
||||||
|
createTextVNode,
|
||||||
|
createVNode,
|
||||||
|
openBlock,
|
||||||
|
} from '../../src/vnode'
|
||||||
|
import { toDisplayString } from '@vue/shared'
|
||||||
import { compile, createApp as createDOMApp, render as domRender } from 'vue'
|
import { compile, createApp as createDOMApp, render as domRender } from 'vue'
|
||||||
|
import type { HMRRuntime } from '../../src/hmr'
|
||||||
|
|
||||||
|
declare var __VUE_HMR_RUNTIME__: HMRRuntime
|
||||||
|
const { rerender, createRecord } = __VUE_HMR_RUNTIME__
|
||||||
|
|
||||||
describe('renderer: teleport', () => {
|
describe('renderer: teleport', () => {
|
||||||
describe('eager mode', () => {
|
describe('eager mode', () => {
|
||||||
|
@ -243,6 +257,39 @@ describe('renderer: teleport', () => {
|
||||||
expect(serializeInner(target)).toBe(`teleported`)
|
expect(serializeInner(target)).toBe(`teleported`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should traverse comment node after updating in optimize mode', async () => {
|
||||||
|
const target = nodeOps.createElement('div')
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
const count = ref(0)
|
||||||
|
let teleport
|
||||||
|
|
||||||
|
__DEV__ = false
|
||||||
|
render(
|
||||||
|
h(() => {
|
||||||
|
teleport =
|
||||||
|
(openBlock(),
|
||||||
|
createBlock(Teleport, { to: target }, [
|
||||||
|
createCommentVNode('comment in teleport'),
|
||||||
|
]))
|
||||||
|
return h('div', null, [
|
||||||
|
createTextVNode(toDisplayString(count.value)),
|
||||||
|
teleport,
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
root,
|
||||||
|
)
|
||||||
|
const commentNode = teleport!.children[0].el
|
||||||
|
expect(serializeInner(root)).toBe(`<div>0</div>`)
|
||||||
|
expect(serializeInner(target)).toBe(`<!--comment in teleport-->`)
|
||||||
|
expect(serialize(commentNode)).toBe(`<!--comment in teleport-->`)
|
||||||
|
|
||||||
|
count.value = 1
|
||||||
|
await nextTick()
|
||||||
|
__DEV__ = true
|
||||||
|
expect(serializeInner(root)).toBe(`<div>1</div>`)
|
||||||
|
expect(teleport!.children[0].el).toBe(commentNode)
|
||||||
|
})
|
||||||
|
|
||||||
test('should remove children when unmounted', () => {
|
test('should remove children when unmounted', () => {
|
||||||
const target = nodeOps.createElement('div')
|
const target = nodeOps.createElement('div')
|
||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
|
@ -269,6 +316,34 @@ describe('renderer: teleport', () => {
|
||||||
testUnmount({ to: null, disabled: true })
|
testUnmount({ to: null, disabled: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #10747
|
||||||
|
test('should unmount correctly when using top level comment in teleport', async () => {
|
||||||
|
const target = nodeOps.createElement('div')
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
const count = ref(0)
|
||||||
|
|
||||||
|
__DEV__ = false
|
||||||
|
render(
|
||||||
|
h(() => {
|
||||||
|
return h('div', null, [
|
||||||
|
createTextVNode(toDisplayString(count.value)),
|
||||||
|
(openBlock(),
|
||||||
|
createBlock(Teleport, { to: target }, [
|
||||||
|
createCommentVNode('comment in teleport'),
|
||||||
|
])),
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
root,
|
||||||
|
)
|
||||||
|
|
||||||
|
count.value = 1
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
__DEV__ = true
|
||||||
|
render(null, root)
|
||||||
|
expect(root.children.length).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
test('component with multi roots should be removed when unmounted', () => {
|
test('component with multi roots should be removed when unmounted', () => {
|
||||||
const target = nodeOps.createElement('div')
|
const target = nodeOps.createElement('div')
|
||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
|
@ -741,4 +816,56 @@ describe('renderer: teleport', () => {
|
||||||
expect(tRefInMounted).toBe(target.children[1])
|
expect(tRefInMounted).toBe(target.children[1])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test('handle update and hmr rerender', async () => {
|
||||||
|
const target = document.createElement('div')
|
||||||
|
const root = document.createElement('div')
|
||||||
|
|
||||||
|
const Comp = {
|
||||||
|
setup() {
|
||||||
|
const cls = ref('foo')
|
||||||
|
onMounted(() => {
|
||||||
|
// trigger update
|
||||||
|
cls.value = 'bar'
|
||||||
|
})
|
||||||
|
return { cls, target }
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<Teleport :to="target">
|
||||||
|
<div :class="cls">
|
||||||
|
<div>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
const appId = 'test-app-id'
|
||||||
|
const App = {
|
||||||
|
__hmrId: appId,
|
||||||
|
components: { Comp },
|
||||||
|
render() {
|
||||||
|
return originalH(Comp, null, { default: () => originalH('div', 'foo') })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
createRecord(appId, App)
|
||||||
|
|
||||||
|
domRender(originalH(App), root)
|
||||||
|
expect(target.innerHTML).toBe(
|
||||||
|
'<div class="foo"><div><div>foo</div></div></div>',
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
expect(target.innerHTML).toBe(
|
||||||
|
'<div class="bar"><div><div>foo</div></div></div>',
|
||||||
|
)
|
||||||
|
|
||||||
|
rerender(appId, () =>
|
||||||
|
originalH(Comp, null, { default: () => originalH('div', 'bar') }),
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
expect(target.innerHTML).toBe(
|
||||||
|
'<div class="bar"><div><div>bar</div></div></div>',
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
import { isReactive, reactive, shallowReactive } from '../../src/index'
|
import {
|
||||||
|
effect,
|
||||||
|
isReactive,
|
||||||
|
reactive,
|
||||||
|
readonly,
|
||||||
|
shallowReactive,
|
||||||
|
} from '../../src/index'
|
||||||
import { renderList } from '../../src/helpers/renderList'
|
import { renderList } from '../../src/helpers/renderList'
|
||||||
|
|
||||||
describe('renderList', () => {
|
describe('renderList', () => {
|
||||||
|
@ -65,4 +71,31 @@ describe('renderList', () => {
|
||||||
const shallowReactiveArray = shallowReactive([{ foo: 1 }])
|
const shallowReactiveArray = shallowReactive([{ foo: 1 }])
|
||||||
expect(renderList(shallowReactiveArray, isReactive)).toEqual([false])
|
expect(renderList(shallowReactiveArray, isReactive)).toEqual([false])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not allow mutation', () => {
|
||||||
|
const arr = readonly(reactive([{ foo: 1 }]))
|
||||||
|
expect(
|
||||||
|
renderList(arr, item => {
|
||||||
|
;(item as any).foo = 0
|
||||||
|
return item.foo
|
||||||
|
}),
|
||||||
|
).toEqual([1])
|
||||||
|
expect(
|
||||||
|
`Set operation on key "foo" failed: target is readonly.`,
|
||||||
|
).toHaveBeenWarned()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should trigger effect for deep mutations in readonly reactive arrays', () => {
|
||||||
|
const arr = reactive([{ foo: 1 }])
|
||||||
|
const readonlyArr = readonly(arr)
|
||||||
|
|
||||||
|
let dummy
|
||||||
|
effect(() => {
|
||||||
|
dummy = renderList(readonlyArr, item => item.foo)
|
||||||
|
})
|
||||||
|
expect(dummy).toEqual([1])
|
||||||
|
|
||||||
|
arr[0].foo = 2
|
||||||
|
expect(dummy).toEqual([2])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -32,10 +32,13 @@ import {
|
||||||
withCtx,
|
withCtx,
|
||||||
withDirectives,
|
withDirectives,
|
||||||
} from '@vue/runtime-dom'
|
} from '@vue/runtime-dom'
|
||||||
|
import type { HMRRuntime } from '../src/hmr'
|
||||||
import { type SSRContext, renderToString } from '@vue/server-renderer'
|
import { type SSRContext, renderToString } from '@vue/server-renderer'
|
||||||
import { PatchFlags, normalizeStyle } from '@vue/shared'
|
import { PatchFlags, normalizeStyle } from '@vue/shared'
|
||||||
import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow'
|
import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow'
|
||||||
import { expect } from 'vitest'
|
|
||||||
|
declare var __VUE_HMR_RUNTIME__: HMRRuntime
|
||||||
|
const { createRecord, reload } = __VUE_HMR_RUNTIME__
|
||||||
|
|
||||||
function mountWithHydration(html: string, render: () => any) {
|
function mountWithHydration(html: string, render: () => any) {
|
||||||
const container = document.createElement('div')
|
const container = document.createElement('div')
|
||||||
|
@ -1843,6 +1846,60 @@ describe('SSR hydration', () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('hmr reload child wrapped in KeepAlive', async () => {
|
||||||
|
const id = 'child-reload'
|
||||||
|
const Child = {
|
||||||
|
__hmrId: id,
|
||||||
|
template: `<div>foo</div>`,
|
||||||
|
}
|
||||||
|
createRecord(id, Child)
|
||||||
|
|
||||||
|
const appId = 'test-app-id'
|
||||||
|
const App = {
|
||||||
|
__hmrId: appId,
|
||||||
|
components: { Child },
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<KeepAlive>
|
||||||
|
<Child />
|
||||||
|
</KeepAlive>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = document.createElement('div')
|
||||||
|
root.innerHTML = await renderToString(h(App))
|
||||||
|
createSSRApp(App).mount(root)
|
||||||
|
expect(root.innerHTML).toBe('<div><div>foo</div></div>')
|
||||||
|
|
||||||
|
reload(id, {
|
||||||
|
__hmrId: id,
|
||||||
|
template: `<div>bar</div>`,
|
||||||
|
})
|
||||||
|
await nextTick()
|
||||||
|
expect(root.innerHTML).toBe('<div><div>bar</div></div>')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hmr root reload', async () => {
|
||||||
|
const appId = 'test-app-id'
|
||||||
|
const App = {
|
||||||
|
__hmrId: appId,
|
||||||
|
template: `<div>foo</div>`,
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = document.createElement('div')
|
||||||
|
root.innerHTML = await renderToString(h(App))
|
||||||
|
createSSRApp(App).mount(root)
|
||||||
|
expect(root.innerHTML).toBe('<div>foo</div>')
|
||||||
|
|
||||||
|
reload(appId, {
|
||||||
|
__hmrId: appId,
|
||||||
|
template: `<div>bar</div>`,
|
||||||
|
})
|
||||||
|
await nextTick()
|
||||||
|
expect(root.innerHTML).toBe('<div>bar</div>')
|
||||||
|
})
|
||||||
|
|
||||||
describe('mismatch handling', () => {
|
describe('mismatch handling', () => {
|
||||||
test('text node', () => {
|
test('text node', () => {
|
||||||
const { container } = mountWithHydration(`foo`, () => 'bar')
|
const { container } = mountWithHydration(`foo`, () => 'bar')
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vue/runtime-core",
|
"name": "@vue/runtime-core",
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"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",
|
||||||
|
|
|
@ -1,17 +1,10 @@
|
||||||
import { type ComputedRefImpl, computed as _computed } from '@vue/reactivity'
|
import { computed as _computed } from '@vue/reactivity'
|
||||||
import { getCurrentInstance, isInSSRComponentSetup } from './component'
|
import { isInSSRComponentSetup } from './component'
|
||||||
|
|
||||||
export const computed: typeof _computed = (
|
export const computed: typeof _computed = (
|
||||||
getterOrOptions: any,
|
getterOrOptions: any,
|
||||||
debugOptions?: any,
|
debugOptions?: any,
|
||||||
) => {
|
) => {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const c = _computed(getterOrOptions, debugOptions, isInSSRComponentSetup)
|
return _computed(getterOrOptions, debugOptions, isInSSRComponentSetup) as any
|
||||||
if (__DEV__) {
|
|
||||||
const i = getCurrentInstance()
|
|
||||||
if (i && i.appContext.config.warnRecursiveComputed) {
|
|
||||||
;(c as unknown as ComputedRefImpl<any>)._warnRecursive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c as any
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,9 @@ export interface App<HostElement = any> {
|
||||||
|
|
||||||
use<Options extends unknown[]>(
|
use<Options extends unknown[]>(
|
||||||
plugin: Plugin<Options>,
|
plugin: Plugin<Options>,
|
||||||
...options: Options
|
...options: NoInfer<Options>
|
||||||
): this
|
): this
|
||||||
use<Options>(plugin: Plugin<Options>, options: Options): this
|
use<Options>(plugin: Plugin<Options>, options: NoInfer<Options>): this
|
||||||
|
|
||||||
mixin(mixin: ComponentOptions): this
|
mixin(mixin: ComponentOptions): this
|
||||||
component(name: string): Component | undefined
|
component(name: string): Component | undefined
|
||||||
|
@ -140,12 +140,6 @@ export interface GenericAppConfig {
|
||||||
trace: string,
|
trace: string,
|
||||||
) => void
|
) => void
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO document for 3.5
|
|
||||||
* Enable warnings for computed getters that recursively trigger itself.
|
|
||||||
*/
|
|
||||||
warnRecursiveComputed?: boolean
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to throw unhandled errors in production.
|
* Whether to throw unhandled errors in production.
|
||||||
* Default is `false` to avoid crashing on any error (and only logs it)
|
* Default is `false` to avoid crashing on any error (and only logs it)
|
||||||
|
@ -266,9 +260,11 @@ export type ObjectPlugin<Options = any[]> = {
|
||||||
export type FunctionPlugin<Options = any[]> = PluginInstallFunction<Options> &
|
export type FunctionPlugin<Options = any[]> = PluginInstallFunction<Options> &
|
||||||
Partial<ObjectPlugin<Options>>
|
Partial<ObjectPlugin<Options>>
|
||||||
|
|
||||||
export type Plugin<Options = any[]> =
|
export type Plugin<
|
||||||
| FunctionPlugin<Options>
|
Options = any[],
|
||||||
| ObjectPlugin<Options>
|
// TODO: in next major Options extends unknown[] and remove P
|
||||||
|
P extends unknown[] = Options extends unknown[] ? Options : [Options],
|
||||||
|
> = FunctionPlugin<P> | ObjectPlugin<P>
|
||||||
|
|
||||||
export function createAppContext(): AppContext {
|
export function createAppContext(): AppContext {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -837,7 +837,7 @@ export function setupComponent(
|
||||||
vi(instance)
|
vi(instance)
|
||||||
} else {
|
} else {
|
||||||
initProps(instance, props, isStateful, isSSR)
|
initProps(instance, props, isStateful, isSSR)
|
||||||
initSlots(instance, children, optimized)
|
initSlots(instance, children, optimized || isSSR)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setupResult = isStateful
|
const setupResult = isStateful
|
||||||
|
|
|
@ -17,7 +17,11 @@ import {
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { isKeepAlive } from './components/KeepAlive'
|
import { isKeepAlive } from './components/KeepAlive'
|
||||||
import { type ContextualRenderFn, withCtx } from './componentRenderContext'
|
import {
|
||||||
|
type ContextualRenderFn,
|
||||||
|
currentRenderingInstance,
|
||||||
|
withCtx,
|
||||||
|
} from './componentRenderContext'
|
||||||
import { isHmrUpdating } from './hmr'
|
import { isHmrUpdating } from './hmr'
|
||||||
import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig'
|
import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig'
|
||||||
import { TriggerOpTypes, trigger } from '@vue/reactivity'
|
import { TriggerOpTypes, trigger } from '@vue/reactivity'
|
||||||
|
@ -75,6 +79,11 @@ export type RawSlots = {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
_?: SlotFlags
|
_?: SlotFlags
|
||||||
|
/**
|
||||||
|
* cache indexes for slot content
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
__?: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const isInternalKey = (key: string) => key[0] === '_' || key === '$stable'
|
const isInternalKey = (key: string) => key[0] === '_' || key === '$stable'
|
||||||
|
@ -98,7 +107,8 @@ const normalizeSlot = (
|
||||||
__DEV__ &&
|
__DEV__ &&
|
||||||
currentInstance &&
|
currentInstance &&
|
||||||
!currentInstance.vapor &&
|
!currentInstance.vapor &&
|
||||||
(!ctx || ctx.root === (currentInstance as ComponentInternalInstance).root)
|
!(ctx === null && currentRenderingInstance) &&
|
||||||
|
!(ctx && ctx.root !== currentInstance.root)
|
||||||
) {
|
) {
|
||||||
warn(
|
warn(
|
||||||
`Slot "${key}" invoked outside of the render function: ` +
|
`Slot "${key}" invoked outside of the render function: ` +
|
||||||
|
@ -171,7 +181,7 @@ const assignSlots = (
|
||||||
// when rendering the optimized slots by manually written render function,
|
// when rendering the optimized slots by manually written render function,
|
||||||
// do not copy the `slots._` compiler flag so that `renderSlot` creates
|
// do not copy the `slots._` compiler flag so that `renderSlot` creates
|
||||||
// slot Fragment with BAIL patchFlag to force full updates
|
// slot Fragment with BAIL patchFlag to force full updates
|
||||||
if (optimized || key !== '_') {
|
if (optimized || !isInternalKey(key)) {
|
||||||
slots[key] = children[key]
|
slots[key] = children[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -501,9 +501,8 @@ function getInnerChild(vnode: VNode): VNode | undefined {
|
||||||
|
|
||||||
return vnode
|
return vnode
|
||||||
}
|
}
|
||||||
// #7121 ensure get the child component subtree in case
|
// #7121,#12465 get the component subtree if it's been mounted
|
||||||
// it's been replaced during HMR
|
if (vnode.component) {
|
||||||
if (__DEV__ && vnode.component) {
|
|
||||||
return vnode.component.subTree
|
return vnode.component.subTree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,11 @@ const KeepAliveImpl: ComponentOptions = {
|
||||||
// Update components tree
|
// Update components tree
|
||||||
devtoolsComponentAdded(instance)
|
devtoolsComponentAdded(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for e2e test
|
||||||
|
if (__DEV__ && __BROWSER__) {
|
||||||
|
;(instance as any).__keepAliveStorageContainer = storageContainer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmount(vnode: VNode) {
|
function unmount(vnode: VNode) {
|
||||||
|
|
|
@ -220,7 +220,8 @@ export const TeleportImpl = {
|
||||||
// even in block tree mode we need to make sure all root-level nodes
|
// even in block tree mode we need to make sure all root-level nodes
|
||||||
// in the teleport inherit previous DOM references so that they can
|
// in the teleport inherit previous DOM references so that they can
|
||||||
// be moved in future patches.
|
// be moved in future patches.
|
||||||
traverseStaticChildren(n1, n2, true)
|
// in dev mode, deep traversal is necessary for HMR
|
||||||
|
traverseStaticChildren(n1, n2, !__DEV__)
|
||||||
} else if (!optimized) {
|
} else if (!optimized) {
|
||||||
patchChildren(
|
patchChildren(
|
||||||
n1,
|
n1,
|
||||||
|
|
|
@ -4,6 +4,8 @@ import {
|
||||||
isReadonly,
|
isReadonly,
|
||||||
isRef,
|
isRef,
|
||||||
isShallow,
|
isShallow,
|
||||||
|
pauseTracking,
|
||||||
|
resetTracking,
|
||||||
toRaw,
|
toRaw,
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
import { EMPTY_OBJ, extend, isArray, isFunction, isObject } from '@vue/shared'
|
import { EMPTY_OBJ, extend, isArray, isFunction, isObject } from '@vue/shared'
|
||||||
|
@ -34,13 +36,16 @@ export function initCustomFormatter(): void {
|
||||||
if (obj.__isVue) {
|
if (obj.__isVue) {
|
||||||
return ['div', vueStyle, `VueInstance`]
|
return ['div', vueStyle, `VueInstance`]
|
||||||
} else if (isRef(obj)) {
|
} else if (isRef(obj)) {
|
||||||
|
// avoid tracking during debugger accessing
|
||||||
|
pauseTracking()
|
||||||
|
const value = obj.value
|
||||||
|
resetTracking()
|
||||||
return [
|
return [
|
||||||
'div',
|
'div',
|
||||||
{},
|
{},
|
||||||
['span', vueStyle, genRefFlag(obj)],
|
['span', vueStyle, genRefFlag(obj)],
|
||||||
'<',
|
'<',
|
||||||
// avoid debugger accessing value affecting behavior
|
formatValue(value),
|
||||||
formatValue('_value' in obj ? obj._value : obj),
|
|
||||||
`>`,
|
`>`,
|
||||||
]
|
]
|
||||||
} else if (isReactive(obj)) {
|
} else if (isReactive(obj)) {
|
||||||
|
|
|
@ -111,7 +111,9 @@ export type Directive<
|
||||||
| ObjectDirective<HostElement, Value, Modifiers, Arg>
|
| ObjectDirective<HostElement, Value, Modifiers, Arg>
|
||||||
| FunctionDirective<HostElement, Value, Modifiers, Arg>
|
| FunctionDirective<HostElement, Value, Modifiers, Arg>
|
||||||
|
|
||||||
export type DirectiveModifiers<K extends string = string> = Record<K, boolean>
|
export type DirectiveModifiers<K extends string = string> = Partial<
|
||||||
|
Record<K, boolean>
|
||||||
|
>
|
||||||
|
|
||||||
export function validateDirectiveName(name: string): void {
|
export function validateDirectiveName(name: string): void {
|
||||||
if (isBuiltInDirective(name)) {
|
if (isBuiltInDirective(name)) {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import type { VNode, VNodeChild } from '../vnode'
|
import type { VNode, VNodeChild } from '../vnode'
|
||||||
import {
|
import {
|
||||||
isReactive,
|
isReactive,
|
||||||
|
isReadonly,
|
||||||
isShallow,
|
isShallow,
|
||||||
shallowReadArray,
|
shallowReadArray,
|
||||||
toReactive,
|
toReactive,
|
||||||
|
toReadonly,
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
import { isArray, isObject, isString } from '@vue/shared'
|
import { isArray, isObject, isString } from '@vue/shared'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
|
@ -69,14 +71,20 @@ export function renderList(
|
||||||
if (sourceIsArray || isString(source)) {
|
if (sourceIsArray || isString(source)) {
|
||||||
const sourceIsReactiveArray = sourceIsArray && isReactive(source)
|
const sourceIsReactiveArray = sourceIsArray && isReactive(source)
|
||||||
let needsWrap = false
|
let needsWrap = false
|
||||||
|
let isReadonlySource = false
|
||||||
if (sourceIsReactiveArray) {
|
if (sourceIsReactiveArray) {
|
||||||
needsWrap = !isShallow(source)
|
needsWrap = !isShallow(source)
|
||||||
|
isReadonlySource = isReadonly(source)
|
||||||
source = shallowReadArray(source)
|
source = shallowReadArray(source)
|
||||||
}
|
}
|
||||||
ret = new Array(source.length)
|
ret = new Array(source.length)
|
||||||
for (let i = 0, l = source.length; i < l; i++) {
|
for (let i = 0, l = source.length; i < l; i++) {
|
||||||
ret[i] = renderItem(
|
ret[i] = renderItem(
|
||||||
needsWrap ? toReactive(source[i]) : source[i],
|
needsWrap
|
||||||
|
? isReadonlySource
|
||||||
|
? toReadonly(toReactive(source[i]))
|
||||||
|
: toReactive(source[i])
|
||||||
|
: source[i],
|
||||||
i,
|
i,
|
||||||
undefined,
|
undefined,
|
||||||
cached && cached[i],
|
cached && cached[i],
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { EMPTY_OBJ } from '@vue/shared'
|
||||||
|
|
||||||
export const knownTemplateRefs: WeakSet<ShallowRef> = new WeakSet()
|
export const knownTemplateRefs: WeakSet<ShallowRef> = new WeakSet()
|
||||||
|
|
||||||
|
export type TemplateRef<T = unknown> = Readonly<ShallowRef<T | null>>
|
||||||
|
|
||||||
export function useTemplateRef<T = unknown, Keys extends string = string>(
|
export function useTemplateRef<T = unknown, Keys extends string = string>(
|
||||||
key: Keys,
|
key: Keys,
|
||||||
): Readonly<ShallowRef<T | null>> {
|
): Readonly<ShallowRef<T | null>> {
|
||||||
|
|
|
@ -64,7 +64,7 @@ export { defineComponent } from './apiDefineComponent'
|
||||||
export { defineAsyncComponent } from './apiAsyncComponent'
|
export { defineAsyncComponent } from './apiAsyncComponent'
|
||||||
export { useAttrs, useSlots } from './apiSetupHelpers'
|
export { useAttrs, useSlots } from './apiSetupHelpers'
|
||||||
export { useModel } from './helpers/useModel'
|
export { useModel } from './helpers/useModel'
|
||||||
export { useTemplateRef } from './helpers/useTemplateRef'
|
export { useTemplateRef, type TemplateRef } from './helpers/useTemplateRef'
|
||||||
export { useId } from './helpers/useId'
|
export { useId } from './helpers/useId'
|
||||||
export {
|
export {
|
||||||
hydrateOnIdle,
|
hydrateOnIdle,
|
||||||
|
@ -569,3 +569,7 @@ export {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export { markAsyncBoundary } from './helpers/useId'
|
export { markAsyncBoundary } from './helpers/useId'
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export { createInternalObject } from './internalObject'
|
||||||
|
|
|
@ -1248,12 +1248,12 @@ function baseCreateRenderer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// avoid hydration for hmr updating
|
||||||
|
if (__DEV__ && isHmrUpdating) initialVNode.el = null
|
||||||
|
|
||||||
// setup() is async. This component relies on async logic to be resolved
|
// setup() is async. This component relies on async logic to be resolved
|
||||||
// before proceeding
|
// before proceeding
|
||||||
if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
|
if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
|
||||||
// avoid hydration for hmr updating
|
|
||||||
if (__DEV__ && isHmrUpdating) initialVNode.el = null
|
|
||||||
|
|
||||||
parentSuspense &&
|
parentSuspense &&
|
||||||
parentSuspense.registerDep(instance, setupRenderEffect, optimized)
|
parentSuspense.registerDep(instance, setupRenderEffect, optimized)
|
||||||
|
|
||||||
|
@ -2120,7 +2120,13 @@ function baseCreateRenderer(
|
||||||
queuePostRenderEffect(() => transition!.enter(el!), parentSuspense)
|
queuePostRenderEffect(() => transition!.enter(el!), parentSuspense)
|
||||||
} else {
|
} else {
|
||||||
const { leave, delayLeave, afterLeave } = transition!
|
const { leave, delayLeave, afterLeave } = transition!
|
||||||
const remove = () => hostInsert(el!, container, anchor)
|
const remove = () => {
|
||||||
|
if (vnode.ctx!.isUnmounted) {
|
||||||
|
hostRemove(el!)
|
||||||
|
} else {
|
||||||
|
hostInsert(el!, container, anchor)
|
||||||
|
}
|
||||||
|
}
|
||||||
const performLeave = () => {
|
const performLeave = () => {
|
||||||
leave(el!, () => {
|
leave(el!, () => {
|
||||||
remove()
|
remove()
|
||||||
|
@ -2163,7 +2169,9 @@ function baseCreateRenderer(
|
||||||
|
|
||||||
// unset ref
|
// unset ref
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
|
pauseTracking()
|
||||||
setRef(ref, null, parentSuspense, vnode, true)
|
setRef(ref, null, parentSuspense, vnode, true)
|
||||||
|
resetTracking()
|
||||||
}
|
}
|
||||||
|
|
||||||
// #6593 should clean memo cache when unmount
|
// #6593 should clean memo cache when unmount
|
||||||
|
@ -2337,7 +2345,17 @@ function baseCreateRenderer(
|
||||||
unregisterHMR(instance)
|
unregisterHMR(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { bum, scope, job, subTree, um, m, a } = instance
|
const {
|
||||||
|
bum,
|
||||||
|
scope,
|
||||||
|
job,
|
||||||
|
subTree,
|
||||||
|
um,
|
||||||
|
m,
|
||||||
|
a,
|
||||||
|
parent,
|
||||||
|
slots: { __: slotCacheKeys },
|
||||||
|
} = instance
|
||||||
invalidateMount(m)
|
invalidateMount(m)
|
||||||
invalidateMount(a)
|
invalidateMount(a)
|
||||||
|
|
||||||
|
@ -2346,6 +2364,13 @@ function baseCreateRenderer(
|
||||||
invokeArrayFns(bum)
|
invokeArrayFns(bum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove slots content from parent renderCache
|
||||||
|
if (parent && isArray(slotCacheKeys)) {
|
||||||
|
slotCacheKeys.forEach(v => {
|
||||||
|
;(parent as ComponentInternalInstance).renderCache[v] = undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
__COMPAT__ &&
|
__COMPAT__ &&
|
||||||
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
|
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
|
||||||
|
@ -2496,9 +2521,12 @@ function baseCreateRenderer(
|
||||||
// HMR root reload
|
// HMR root reload
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
app._context.reload = () => {
|
app._context.reload = () => {
|
||||||
|
const cloned = cloneVNode(vnode)
|
||||||
|
// avoid hydration for hmr updating
|
||||||
|
cloned.el = null
|
||||||
// casting to ElementNamespace because TS doesn't guarantee type narrowing
|
// casting to ElementNamespace because TS doesn't guarantee type narrowing
|
||||||
// over function boundaries
|
// over function boundaries
|
||||||
render(cloneVNode(vnode), container, namespace as ElementNamespace)
|
render(cloned, container, namespace as ElementNamespace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2604,11 +2632,15 @@ export function traverseStaticChildren(
|
||||||
if (c2.type === Text) {
|
if (c2.type === Text) {
|
||||||
c2.el = c1.el
|
c2.el = c1.el
|
||||||
}
|
}
|
||||||
// also inherit for comment nodes, but not placeholders (e.g. v-if which
|
// #2324 also inherit for comment nodes, but not placeholders (e.g. v-if which
|
||||||
// would have received .el during block patch)
|
// would have received .el during block patch)
|
||||||
if (__DEV__ && c2.type === Comment && !c2.el) {
|
if (c2.type === Comment && !c2.el) {
|
||||||
c2.el = c1.el
|
c2.el = c1.el
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (__DEV__) {
|
||||||
|
c2.el && (c2.el.__vnode = c2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue