Merge branch 'main' into edison/fix/hmrRootReload
ci / test (push) Has been cancelled Details
ci / continuous-release (push) Has been cancelled Details

This commit is contained in:
edison 2025-05-13 11:54:19 +08:00 committed by GitHub
commit c5450a3aed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 2247 additions and 1607 deletions

View File

@ -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
@ -31,4 +31,4 @@ jobs:
- name: Run prettier - name: Run prettier
run: pnpm run format run: pnpm run format
- uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c - uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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@v6 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@v6 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

View File

@ -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
@ -88,7 +88,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

View File

@ -1 +1 @@
20 22.14.0

View File

@ -8,7 +8,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))

View File

@ -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

View File

@ -741,17 +741,6 @@ Note that this is a type-only breaking change in a minor release, which adheres
## [3.3.13](https://github.com/vuejs/core/compare/v3.3.12...v3.3.13) (2023-12-19)
### Bug Fixes
* **compiler-core:** fix v-on with modifiers on inline expression of undefined ([#9866](https://github.com/vuejs/core/issues/9866)) ([bae79dd](https://github.com/vuejs/core/commit/bae79ddf8564a2da4a5365cfeb8d811990f42335)), closes [#9865](https://github.com/vuejs/core/issues/9865)
* **runtime-dom:** cache event handlers by key/modifiers ([#9851](https://github.com/vuejs/core/issues/9851)) ([04d2c05](https://github.com/vuejs/core/commit/04d2c05054c26b02fbc1d84839b0ed5cd36455b6)), closes [#9849](https://github.com/vuejs/core/issues/9849)
* **types:** extract properties from extended collections ([#9854](https://github.com/vuejs/core/issues/9854)) ([24b1c1d](https://github.com/vuejs/core/commit/24b1c1dd57fd55d998aa231a147500e010b10219)), closes [#9852](https://github.com/vuejs/core/issues/9852)
# [3.4.0-beta.3](https://github.com/vuejs/core/compare/v3.3.12...v3.4.0-beta.3) (2023-12-16) # [3.4.0-beta.3](https://github.com/vuejs/core/compare/v3.3.12...v3.4.0-beta.3) (2023-12-16)
@ -764,19 +753,6 @@ Note that this is a type-only breaking change in a minor release, which adheres
## [3.3.12](https://github.com/vuejs/core/compare/v3.3.11...v3.3.12) (2023-12-16)
### Bug Fixes
* **hydration:** handle appear transition before patch props ([#9837](https://github.com/vuejs/core/issues/9837)) ([e70f4c4](https://github.com/vuejs/core/commit/e70f4c47c553b6e16d8fad70743271ca23802fe7)), closes [#9832](https://github.com/vuejs/core/issues/9832)
* **sfc/cssVars:** fix loss of CSS v-bind variables when setting inline style with string value ([#9824](https://github.com/vuejs/core/issues/9824)) ([0a387df](https://github.com/vuejs/core/commit/0a387dfb1d04afb6eae4296b6da76dfdaca77af4)), closes [#9821](https://github.com/vuejs/core/issues/9821)
* **ssr:** fix suspense hydration of fallback content ([#7188](https://github.com/vuejs/core/issues/7188)) ([60415b5](https://github.com/vuejs/core/commit/60415b5d67df55f1fd6b176615299c08640fa142))
* **types:** add `xmlns:xlink` to `SVGAttributes` ([#9300](https://github.com/vuejs/core/issues/9300)) ([0d61b42](https://github.com/vuejs/core/commit/0d61b429ecf63591d31e09702058fa4c7132e1a7)), closes [#9299](https://github.com/vuejs/core/issues/9299)
* **types:** fix `shallowRef` type error ([#9839](https://github.com/vuejs/core/issues/9839)) ([9a57158](https://github.com/vuejs/core/commit/9a571582b53220270e498d8712ea59312c0bef3a))
* **types:** support for generic keyof slots ([#8374](https://github.com/vuejs/core/issues/8374)) ([213eba4](https://github.com/vuejs/core/commit/213eba479ce080efc1053fe636f6be4a4c889b44))
# [3.4.0-beta.2](https://github.com/vuejs/core/compare/v3.4.0-beta.1...v3.4.0-beta.2) (2023-12-14) # [3.4.0-beta.2](https://github.com/vuejs/core/compare/v3.4.0-beta.1...v3.4.0-beta.2) (2023-12-14)
@ -836,22 +812,6 @@ default.
## [3.3.11](https://github.com/vuejs/core/compare/v3.3.10...v3.3.11) (2023-12-08)
### Bug Fixes
* **custom-element:** correctly handle number type props in prod ([#8989](https://github.com/vuejs/core/issues/8989)) ([d74d364](https://github.com/vuejs/core/commit/d74d364d62db8e48881af6b5a75ce4fb5f36cc35))
* **reactivity:** fix mutation on user proxy of reactive Array ([6ecbd5c](https://github.com/vuejs/core/commit/6ecbd5ce2a7f59314a8326a1d193874b87f4d8c8)), closes [#9742](https://github.com/vuejs/core/issues/9742) [#9751](https://github.com/vuejs/core/issues/9751) [#9750](https://github.com/vuejs/core/issues/9750)
* **runtime-dom:** fix width and height prop check condition ([5b00286](https://github.com/vuejs/core/commit/5b002869c533220706f9788b496b8ca8d8e98609)), closes [#9762](https://github.com/vuejs/core/issues/9762)
* **shared:** handle Map with symbol keys in toDisplayString ([#9731](https://github.com/vuejs/core/issues/9731)) ([364821d](https://github.com/vuejs/core/commit/364821d6bdb1775e2f55a69bcfb9f40f7acf1506)), closes [#9727](https://github.com/vuejs/core/issues/9727)
* **shared:** handle more Symbol cases in toDisplayString ([983d45d](https://github.com/vuejs/core/commit/983d45d4f8eb766b5a16b7ea93b86d3c51618fa6))
* **Suspense:** properly get anchor when mount fallback vnode ([#9770](https://github.com/vuejs/core/issues/9770)) ([b700328](https://github.com/vuejs/core/commit/b700328342e17dc16b19316c2e134a26107139d2)), closes [#9769](https://github.com/vuejs/core/issues/9769)
* **types:** ref() return type should not be any when initial value is any ([#9768](https://github.com/vuejs/core/issues/9768)) ([cdac121](https://github.com/vuejs/core/commit/cdac12161ec27b45ded48854c3d749664b6d4a6d))
* **watch:** should not fire pre watcher on child component unmount ([#7181](https://github.com/vuejs/core/issues/7181)) ([6784f0b](https://github.com/vuejs/core/commit/6784f0b1f8501746ea70d87d18ed63a62cf6b76d)), closes [#7030](https://github.com/vuejs/core/issues/7030)
# [3.4.0-alpha.4](https://github.com/vuejs/core/compare/v3.3.10...v3.4.0-alpha.4) (2023-12-04) # [3.4.0-alpha.4](https://github.com/vuejs/core/compare/v3.3.10...v3.4.0-alpha.4) (2023-12-04)
@ -873,37 +833,6 @@ default.
## [3.3.10](https://github.com/vuejs/core/compare/v3.3.9...v3.3.10) (2023-12-04)
### Bug Fixes
* **app:** prevent template from being cached between apps with different options ([#9724](https://github.com/vuejs/core/issues/9724)) ([ec71585](https://github.com/vuejs/core/commit/ec715854ca12520b2afc9e9b3981cbae05ae5206)), closes [#9618](https://github.com/vuejs/core/issues/9618)
* **compiler-sfc:** avoid passing forEach index to genMap ([f12db7f](https://github.com/vuejs/core/commit/f12db7fb564a534cef2e5805cc9f54afe5d72fbf))
* **compiler-sfc:** deindent pug/jade templates ([6345197](https://github.com/vuejs/core/commit/634519720a21fb5a6871454e1cadad7053a568b8)), closes [#3231](https://github.com/vuejs/core/issues/3231) [#3842](https://github.com/vuejs/core/issues/3842) [#7723](https://github.com/vuejs/core/issues/7723)
* **compiler-sfc:** fix :where and :is selector in scoped mode with multiple selectors ([#9735](https://github.com/vuejs/core/issues/9735)) ([c3e2c55](https://github.com/vuejs/core/commit/c3e2c556b532656b50b8ab5cd2d9eabc26622d63)), closes [#9707](https://github.com/vuejs/core/issues/9707)
* **compiler-sfc:** generate more treeshaking friendly code ([#9507](https://github.com/vuejs/core/issues/9507)) ([8d74ca0](https://github.com/vuejs/core/commit/8d74ca0e6fa2738ca6854b7e879ff59419f948c7)), closes [#9500](https://github.com/vuejs/core/issues/9500)
* **compiler-sfc:** support inferring generic types ([#8511](https://github.com/vuejs/core/issues/8511)) ([eb5e307](https://github.com/vuejs/core/commit/eb5e307c0be62002e62c4c800d0dfacb39b0d4ca)), closes [#8482](https://github.com/vuejs/core/issues/8482)
* **compiler-sfc:** support resolving components from props ([#8785](https://github.com/vuejs/core/issues/8785)) ([7cbcee3](https://github.com/vuejs/core/commit/7cbcee3d831241a8bd3588ae92d3f27e3641e25f))
* **compiler-sfc:** throw error when failing to load TS during type resolution ([#8883](https://github.com/vuejs/core/issues/8883)) ([4936d2e](https://github.com/vuejs/core/commit/4936d2e11a8d0ca3704bfe408548cb26bb3fd5e9))
* **cssVars:** cssVar names should be double-escaped when generating code for ssr ([#8824](https://github.com/vuejs/core/issues/8824)) ([5199a12](https://github.com/vuejs/core/commit/5199a12f8855cd06f24bf355708b5a2134f63176)), closes [#7823](https://github.com/vuejs/core/issues/7823)
* **deps:** update compiler to ^7.23.4 ([#9681](https://github.com/vuejs/core/issues/9681)) ([31f6ebc](https://github.com/vuejs/core/commit/31f6ebc4df84490ed29fb75e7bf4259200eb51f0))
* **runtime-core:** Suspense get anchor properly in Transition ([#9309](https://github.com/vuejs/core/issues/9309)) ([65f3fe2](https://github.com/vuejs/core/commit/65f3fe273127a8b68e1222fbb306d28d85f01757)), closes [#8105](https://github.com/vuejs/core/issues/8105)
* **runtime-dom:** set width/height with units as attribute ([#8781](https://github.com/vuejs/core/issues/8781)) ([bfc1838](https://github.com/vuejs/core/commit/bfc1838f31199de3f189198a3c234fa7bae91386))
* **ssr:** avoid computed being accidentally cached before server render ([#9688](https://github.com/vuejs/core/issues/9688)) ([30d5d93](https://github.com/vuejs/core/commit/30d5d93a92b2154406ec04f8aca6b217fa01177c)), closes [#5300](https://github.com/vuejs/core/issues/5300)
* **types:** expose emits as props in functional components ([#9234](https://github.com/vuejs/core/issues/9234)) ([887e54c](https://github.com/vuejs/core/commit/887e54c347ea9eac4c721b5e2288f054873d1d30))
* **types:** fix reactive collection types ([#8960](https://github.com/vuejs/core/issues/8960)) ([ad27473](https://github.com/vuejs/core/commit/ad274737015c36906d76f3189203093fa3a2e4e7)), closes [#8904](https://github.com/vuejs/core/issues/8904)
* **types:** improve return type withKeys and withModifiers ([#9734](https://github.com/vuejs/core/issues/9734)) ([43c3cfd](https://github.com/vuejs/core/commit/43c3cfdec5ae5d70fa2a21e857abc2d73f1a0d07))
### Performance Improvements
* optimize on* prop check ([38aaa8c](https://github.com/vuejs/core/commit/38aaa8c88648c54fe2616ad9c0961288092fcb44))
* **runtime-dom:** cache modifier wrapper functions ([da4a4fb](https://github.com/vuejs/core/commit/da4a4fb5e8eee3c6d31f24ebd79a9d0feca56cb2)), closes [#8882](https://github.com/vuejs/core/issues/8882)
* **v-on:** constant handlers with modifiers should not be treated as dynamic ([4d94ebf](https://github.com/vuejs/core/commit/4d94ebfe75174b340d2b794e699cad1add3600a9))
# [3.4.0-alpha.3](https://github.com/vuejs/core/compare/v3.4.0-alpha.2...v3.4.0-alpha.3) (2023-11-28) # [3.4.0-alpha.3](https://github.com/vuejs/core/compare/v3.4.0-alpha.2...v3.4.0-alpha.3) (2023-11-28)
@ -960,55 +889,6 @@ default.
## [3.3.9](https://github.com/vuejs/core/compare/v3.3.8...v3.3.9) (2023-11-25)
### Bug Fixes
* **compiler-core:** avoid rewriting scope variables in inline for loops ([#7245](https://github.com/vuejs/core/issues/7245)) ([a2d810e](https://github.com/vuejs/core/commit/a2d810eb40cef631f61991ca68b426ee9546aba0)), closes [#7238](https://github.com/vuejs/core/issues/7238)
* **compiler-core:** fix `resolveParserPlugins` decorators check ([#9566](https://github.com/vuejs/core/issues/9566)) ([9d0eba9](https://github.com/vuejs/core/commit/9d0eba916f3bf6fb5c03222400edae1a2db7444f)), closes [#9560](https://github.com/vuejs/core/issues/9560)
* **compiler-sfc:** consistently escape type-only prop names ([#8654](https://github.com/vuejs/core/issues/8654)) ([3e08d24](https://github.com/vuejs/core/commit/3e08d246dfd8523c54fb8e7a4a6fd5506ffb1bcc)), closes [#8635](https://github.com/vuejs/core/issues/8635) [#8910](https://github.com/vuejs/core/issues/8910) [vitejs/vite-plugin-vue#184](https://github.com/vitejs/vite-plugin-vue/issues/184)
* **compiler-sfc:** malformed filename on windows using path.posix.join() ([#9478](https://github.com/vuejs/core/issues/9478)) ([f18a174](https://github.com/vuejs/core/commit/f18a174979626b3429db93c5d5b7ae5448917c70)), closes [#8671](https://github.com/vuejs/core/issues/8671) [#9583](https://github.com/vuejs/core/issues/9583) [#9446](https://github.com/vuejs/core/issues/9446) [#9473](https://github.com/vuejs/core/issues/9473)
* **compiler-sfc:** support `:is` and `:where` selector in scoped css rewrite ([#8929](https://github.com/vuejs/core/issues/8929)) ([3227e50](https://github.com/vuejs/core/commit/3227e50b32105f8893f7dff2f29278c5b3a9f621))
* **compiler-sfc:** support resolve extends interface for defineEmits ([#8470](https://github.com/vuejs/core/issues/8470)) ([9e1b74b](https://github.com/vuejs/core/commit/9e1b74bcd5fa4151f5d1bc02c69fbbfa4762f577)), closes [#8465](https://github.com/vuejs/core/issues/8465)
* **hmr/transition:** fix kept-alive component inside transition disappearing after hmr ([#7126](https://github.com/vuejs/core/issues/7126)) ([d11e978](https://github.com/vuejs/core/commit/d11e978fc98dcc83526c167e603b8308f317f786)), closes [#7121](https://github.com/vuejs/core/issues/7121)
* **hydration:** force hydration for v-bind with .prop modifier ([364f319](https://github.com/vuejs/core/commit/364f319d214226770d97c98d8fcada80c9e8dde3)), closes [#7490](https://github.com/vuejs/core/issues/7490)
* **hydration:** properly hydrate indeterminate prop ([34b5a5d](https://github.com/vuejs/core/commit/34b5a5da4ae9c9faccac237acd7acc8e7e017571)), closes [#7476](https://github.com/vuejs/core/issues/7476)
* **reactivity:** clear method on readonly collections should return undefined ([#7316](https://github.com/vuejs/core/issues/7316)) ([657476d](https://github.com/vuejs/core/commit/657476dcdb964be4fbb1277c215c073f3275728e))
* **reactivity:** onCleanup also needs to be cleaned ([#8655](https://github.com/vuejs/core/issues/8655)) ([73fd810](https://github.com/vuejs/core/commit/73fd810eebdd383a2b4629f67736c4db1f428abd)), closes [#5151](https://github.com/vuejs/core/issues/5151) [#7695](https://github.com/vuejs/core/issues/7695)
* **ssr:** hydration `__vnode` missing for devtools ([#9328](https://github.com/vuejs/core/issues/9328)) ([5156ac5](https://github.com/vuejs/core/commit/5156ac5b38cfa80d3db26f2c9bf40cb22a7521cb))
* **types:** allow falsy value types in `StyleValue` ([#7954](https://github.com/vuejs/core/issues/7954)) ([17aa92b](https://github.com/vuejs/core/commit/17aa92b79b31d8bb8b5873ddc599420cb9806db8)), closes [#7955](https://github.com/vuejs/core/issues/7955)
* **types:** defineCustomElement using defineComponent return type with emits ([#7937](https://github.com/vuejs/core/issues/7937)) ([5d932a8](https://github.com/vuejs/core/commit/5d932a8e6d14343c9d7fc7c2ecb58ac618b2f938)), closes [#7782](https://github.com/vuejs/core/issues/7782)
* **types:** fix `unref` and `toValue` when input union type contains ComputedRef ([#8748](https://github.com/vuejs/core/issues/8748)) ([176d476](https://github.com/vuejs/core/commit/176d47671271b1abc21b1508e9a493c7efca6451)), closes [#8747](https://github.com/vuejs/core/issues/8747) [#8857](https://github.com/vuejs/core/issues/8857)
* **types:** fix instance type when props type is incompatible with setup returned type ([#7338](https://github.com/vuejs/core/issues/7338)) ([0e1e8f9](https://github.com/vuejs/core/commit/0e1e8f919e5a74cdaadf9c80ee135088b25e7fa3)), closes [#5885](https://github.com/vuejs/core/issues/5885)
* **types:** fix shallowRef return type with union value type ([#7853](https://github.com/vuejs/core/issues/7853)) ([7c44800](https://github.com/vuejs/core/commit/7c448000b0def910c2cfabfdf7ff20a3d6bc844f)), closes [#7852](https://github.com/vuejs/core/issues/7852)
* **types:** more precise types for class bindings ([#8012](https://github.com/vuejs/core/issues/8012)) ([46e3374](https://github.com/vuejs/core/commit/46e33744c890bd49482c5e5c5cdea44e00ec84d5))
* **types:** remove optional properties from defineProps return type ([#6421](https://github.com/vuejs/core/issues/6421)) ([94c049d](https://github.com/vuejs/core/commit/94c049d930d922069e38ea8700d7ff0970f71e61)), closes [#6420](https://github.com/vuejs/core/issues/6420)
* **types:** return type of withDefaults should be readonly ([#8601](https://github.com/vuejs/core/issues/8601)) ([f15debc](https://github.com/vuejs/core/commit/f15debc01acb22d23f5acee97e6f02db88cef11a))
* **types:** revert class type restrictions ([5d077c8](https://github.com/vuejs/core/commit/5d077c8754cc14f85d2d6d386df70cf8c0d93842)), closes [#8012](https://github.com/vuejs/core/issues/8012)
* **types:** update jsx type definitions ([#8607](https://github.com/vuejs/core/issues/8607)) ([58e2a94](https://github.com/vuejs/core/commit/58e2a94871ae06a909c5f8bad07fb401193e6a38))
* **types:** widen ClassValue type ([2424013](https://github.com/vuejs/core/commit/242401305944422d0c361b16101a4d18908927af))
* **v-model:** avoid overwriting number input with same value ([#7004](https://github.com/vuejs/core/issues/7004)) ([40f4b77](https://github.com/vuejs/core/commit/40f4b77bb570868cb6e47791078767797e465989)), closes [#7003](https://github.com/vuejs/core/issues/7003)
* **v-model:** unnecessary value binding error should apply to dynamic instead of static binding ([2859b65](https://github.com/vuejs/core/commit/2859b653c9a22460e60233cac10fe139e359b046)), closes [#3596](https://github.com/vuejs/core/issues/3596)
## [3.3.8](https://github.com/vuejs/core/compare/v3.3.7...v3.3.8) (2023-11-06)
### Bug Fixes
* **compile-sfc:** support `Error` type in `defineProps` ([#5955](https://github.com/vuejs/core/issues/5955)) ([a989345](https://github.com/vuejs/core/commit/a9893458ec519aae442e1b99e64e6d74685cd22c))
* **compiler-core:** known global should be shadowed by local variables in expression rewrite ([#9492](https://github.com/vuejs/core/issues/9492)) ([a75d1c5](https://github.com/vuejs/core/commit/a75d1c5c6242e91a73cc5ba01e6da620dea0b3d9)), closes [#9482](https://github.com/vuejs/core/issues/9482)
* **compiler-sfc:** fix dynamic directive arguments usage check for slots ([#9495](https://github.com/vuejs/core/issues/9495)) ([b39fa1f](https://github.com/vuejs/core/commit/b39fa1f8157647859331ce439c42ae016a49b415)), closes [#9493](https://github.com/vuejs/core/issues/9493)
* **deps:** update dependency @vue/repl to ^2.6.2 ([#9536](https://github.com/vuejs/core/issues/9536)) ([5cef325](https://github.com/vuejs/core/commit/5cef325f41e3b38657c72fa1a38dedeee1c7a60a))
* **deps:** update dependency @vue/repl to ^2.6.3 ([#9540](https://github.com/vuejs/core/issues/9540)) ([176d590](https://github.com/vuejs/core/commit/176d59058c9aecffe9da4d4311e98496684f06d4))
* **hydration:** fix tagName access error on comment/text node hydration mismatch ([dd8a0cf](https://github.com/vuejs/core/commit/dd8a0cf5dcde13d2cbd899262a0e07f16e14e489)), closes [#9531](https://github.com/vuejs/core/issues/9531)
* **types:** avoid exposing lru-cache types in generated dts ([462aeb3](https://github.com/vuejs/core/commit/462aeb3b600765e219ded2ee9a0ed1e74df61de0)), closes [#9521](https://github.com/vuejs/core/issues/9521)
* **warn:** avoid warning on empty children with Suspense ([#3962](https://github.com/vuejs/core/issues/3962)) ([405f345](https://github.com/vuejs/core/commit/405f34587a63a5f1e3d147b9848219ea98acc22d))
# [3.4.0-alpha.1](https://github.com/vuejs/core/compare/v3.3.7...v3.4.0-alpha.1) (2023-10-28) # [3.4.0-alpha.1](https://github.com/vuejs/core/compare/v3.3.7...v3.4.0-alpha.1) (2023-10-28)

View File

@ -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

View File

@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"version": "3.5.13", "version": "3.5.13",
"packageManager": "pnpm@9.13.2", "packageManager": "pnpm@10.9.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "node scripts/dev.js", "dev": "node scripts/dev.js",
@ -65,62 +65,51 @@
"@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.1", "@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-json": "^6.1.0", "@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-replace": "5.0.4", "@rollup/plugin-replace": "5.0.4",
"@swc/core": "^1.9.2", "@swc/core": "^1.11.24",
"@types/hash-sum": "^1.0.2", "@types/hash-sum": "^1.0.2",
"@types/node": "^22.9.0", "@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": "^2.1.1", "@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.0", "esbuild": "^0.25.4",
"esbuild-plugin-polyfill-node": "^0.3.0", "esbuild-plugin-polyfill-node": "^0.3.0",
"eslint": "^9.15.0", "eslint": "^9.25.1",
"eslint-plugin-import-x": "^4.4.2", "eslint-plugin-import-x": "^4.11.0",
"@vitest/eslint-plugin": "^1.0.1",
"estree-walker": "catalog:", "estree-walker": "catalog:",
"jsdom": "^25.0.0", "jsdom": "^26.1.0",
"lint-staged": "^15.2.10", "lint-staged": "^15.5.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"magic-string": "^0.30.12", "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.1", "npm-run-all2": "^7.0.2",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"prettier": "^3.3.3", "prettier": "^3.5.3",
"pretty-bytes": "^6.1.1", "pretty-bytes": "^6.1.1",
"pug": "^3.0.3", "pug": "^3.0.3",
"puppeteer": "~23.3.0", "puppeteer": "~24.8.2",
"rimraf": "^6.0.1", "rimraf": "^6.0.1",
"rollup": "^4.27.2", "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.14.0", "typescript-eslint": "^8.31.1",
"vite": "catalog:", "vite": "catalog:",
"vitest": "^2.1.1" "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"
}
}
} }
} }

View File

@ -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

View File

@ -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 = ''

View File

@ -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<{

View File

@ -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)

View File

@ -13,7 +13,7 @@
"vite": "catalog:" "vite": "catalog:"
}, },
"dependencies": { "dependencies": {
"@vue/repl": "^4.4.2", "@vue/repl": "^4.5.1",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"vue": "workspace:*" "vue": "workspace:*"

View File

@ -165,8 +165,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;

View File

@ -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)

View File

@ -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.0", "@vitejs/plugin-vue": "^5.2.4",
"vite": "^5.4.11" "vite": "^6.3.5"
} }
} }

View File

@ -11,7 +11,7 @@
"enableNonBrowserBranches": true "enableNonBrowserBranches": true
}, },
"dependencies": { "dependencies": {
"monaco-editor": "^0.52.0", "monaco-editor": "^0.52.2",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
} }
} }

View File

@ -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;
} }

View File

@ -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]' },
},
], ],
}) })
}) })

View File

@ -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'

View File

@ -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

View File

@ -12,7 +12,7 @@ import { camelize } from '@vue/shared'
import { CAMELIZE } from '../runtimeHelpers' import { CAMELIZE } from '../runtimeHelpers'
import { processExpression } from './transformExpression' import { processExpression } from './transformExpression'
// v-bind without arg is handled directly in ./transformElements.ts due to it affecting // v-bind without arg is handled directly in ./transformElement.ts due to its affecting
// codegen for the entire props object. This transform here is only for v-bind // codegen for the entire props object. This transform here is only for v-bind
// *with* args. // *with* args.
export const transformBind: DirectiveTransform = (dir, _node, context) => { export const transformBind: DirectiveTransform = (dir, _node, context) => {

View File

@ -17,7 +17,7 @@ import { hasScopeRef, isFnExpression, isMemberExpression } from '../utils'
import { TO_HANDLER_KEY } from '../runtimeHelpers' import { TO_HANDLER_KEY } from '../runtimeHelpers'
export interface VOnDirectiveNode extends DirectiveNode { export interface VOnDirectiveNode extends DirectiveNode {
// v-on without arg is handled directly in ./transformElements.ts due to it affecting // v-on without arg is handled directly in ./transformElement.ts due to its affecting
// codegen for the entire props object. This transform here is only for v-on // codegen for the entire props object. This transform here is only for v-on
// *with* args. // *with* args.
arg: ExpressionNode arg: ExpressionNode

View File

@ -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(

View File

@ -214,7 +214,8 @@ color: red
".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; }`),
@ -222,7 +223,8 @@ color: red
".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(
@ -232,7 +234,8 @@ color: red
".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(
@ -242,7 +245,8 @@ color: red
".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', () => {

View File

@ -81,7 +81,7 @@ font-weight: bold;
const consumer = new SourceMapConsumer(script!.map!) const consumer = new SourceMapConsumer(script!.map!)
consumer.eachMapping(mapping => { consumer.eachMapping(mapping => {
expect(mapping.originalLine - mapping.generatedLine).toBe(padding) expect(mapping.originalLine! - mapping.generatedLine).toBe(padding)
}) })
}) })
@ -100,8 +100,8 @@ font-weight: bold;
const consumer = new SourceMapConsumer(template.map!) const consumer = new SourceMapConsumer(template.map!)
consumer.eachMapping(mapping => { consumer.eachMapping(mapping => {
expect(mapping.originalLine - mapping.generatedLine).toBe(padding) expect(mapping.originalLine! - mapping.generatedLine).toBe(padding)
expect(mapping.originalColumn - mapping.generatedColumn).toBe(2) expect(mapping.originalColumn! - mapping.generatedColumn).toBe(2)
}) })
}) })
@ -115,7 +115,7 @@ font-weight: bold;
const consumer = new SourceMapConsumer(custom!.map!) const consumer = new SourceMapConsumer(custom!.map!)
consumer.eachMapping(mapping => { consumer.eachMapping(mapping => {
expect(mapping.originalLine - mapping.generatedLine).toBe(padding) expect(mapping.originalLine! - mapping.generatedLine).toBe(padding)
}) })
}) })
}) })

View File

@ -49,7 +49,7 @@
"@vue/shared": "workspace:*", "@vue/shared": "workspace:*",
"estree-walker": "catalog:", "estree-walker": "catalog:",
"magic-string": "catalog:", "magic-string": "catalog:",
"postcss": "^8.4.49", "postcss": "^8.5.3",
"source-map-js": "catalog:" "source-map-js": "catalog:"
}, },
"devDependencies": { "devDependencies": {
@ -58,10 +58,10 @@
"hash-sum": "^2.0.0", "hash-sum": "^2.0.0",
"lru-cache": "10.1.0", "lru-cache": "10.1.0",
"merge-source-map": "^1.1.0", "merge-source-map": "^1.1.0",
"minimatch": "~9.0.5", "minimatch": "~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.81.0" "sass": "^1.86.3"
} }
} }

View File

@ -170,8 +170,6 @@ export function compileScript(
const scriptLang = script && script.lang const scriptLang = script && script.lang
const scriptSetupLang = scriptSetup && scriptSetup.lang const scriptSetupLang = scriptSetup && scriptSetup.lang
let refBindings: string[] | undefined
if (!scriptSetup) { if (!scriptSetup) {
if (!script) { if (!script) {
throw new Error(`[@vue/compiler-sfc] SFC contains no <script> tags.`) throw new Error(`[@vue/compiler-sfc] SFC contains no <script> tags.`)
@ -740,12 +738,6 @@ export function compileScript(
for (const key in setupBindings) { for (const key in setupBindings) {
ctx.bindingMetadata[key] = setupBindings[key] ctx.bindingMetadata[key] = setupBindings[key]
} }
// known ref bindings
if (refBindings) {
for (const key of refBindings) {
ctx.bindingMetadata[key] = BindingTypes.SETUP_REF
}
}
// 7. inject `useCssVars` calls // 7. inject `useCssVars` calls
if ( if (
@ -1112,6 +1104,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,
) )
) { ) {

View File

@ -289,7 +289,7 @@ function mapLines(oldMap: RawSourceMap, newMap: RawSourceMap): RawSourceMap {
const origPosInOldMap = oldMapConsumer.originalPositionFor({ const origPosInOldMap = oldMapConsumer.originalPositionFor({
line: m.originalLine, line: m.originalLine,
column: m.originalColumn, column: m.originalColumn!,
}) })
if (origPosInOldMap.source == null) { if (origPosInOldMap.source == null) {
@ -305,7 +305,7 @@ function mapLines(oldMap: RawSourceMap, newMap: RawSourceMap): RawSourceMap {
line: origPosInOldMap.line, // map line line: origPosInOldMap.line, // map line
// use current column, since the oldMap produced by @vue/compiler-sfc // use current column, since the oldMap produced by @vue/compiler-sfc
// does not // does not
column: m.originalColumn, column: m.originalColumn!,
}, },
source: origPosInOldMap.source, source: origPosInOldMap.source,
name: origPosInOldMap.name, name: origPosInOldMap.name,

View File

@ -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

View File

@ -1012,6 +1012,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)

View File

@ -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)
})
}) })

View File

@ -49,6 +49,7 @@ export enum EffectFlags {
DIRTY = 1 << 4, DIRTY = 1 << 4,
ALLOW_RECURSE = 1 << 5, ALLOW_RECURSE = 1 << 5,
PAUSED = 1 << 6, PAUSED = 1 << 6,
EVALUATED = 1 << 7,
} }
/** /**
@ -377,22 +378,22 @@ export function refreshComputed(computed: ComputedRefImpl): undefined {
} }
computed.globalVersion = globalVersion computed.globalVersion = globalVersion
const dep = computed.dep
computed.flags |= EffectFlags.RUNNING
// In SSR there will be no render effect, so the computed has no subscriber // In SSR there will be no render effect, so the computed has no subscriber
// and therefore tracks no deps, thus we cannot rely on the dirty check. // and therefore tracks no deps, thus we cannot rely on the dirty check.
// Instead, computed always re-evaluate and relies on the globalVersion // Instead, computed always re-evaluate and relies on the globalVersion
// fast path above for caching. // fast path above for caching.
// #12337 if computed has no deps (does not rely on any reactive data) and evaluated,
// there is no need to re-evaluate.
if ( if (
dep.version > 0 &&
!computed.isSSR && !computed.isSSR &&
computed.deps && computed.flags & EffectFlags.EVALUATED &&
!isDirty(computed) ((!computed.deps && !(computed as any)._dirty) || !isDirty(computed))
) { ) {
computed.flags &= ~EffectFlags.RUNNING
return return
} }
computed.flags |= EffectFlags.RUNNING
const dep = computed.dep
const prevSub = activeSub const prevSub = activeSub
const prevShouldTrack = shouldTrack const prevShouldTrack = shouldTrack
activeSub = computed activeSub = computed
@ -402,6 +403,7 @@ export function refreshComputed(computed: ComputedRefImpl): undefined {
prepareDeps(computed) prepareDeps(computed)
const value = computed.fn(computed._value) const value = computed.fn(computed._value)
if (dep.version === 0 || hasChanged(value, computed._value)) { if (dep.version === 0 || hasChanged(value, computed._value)) {
computed.flags |= EffectFlags.EVALUATED
computed._value = value computed._value = value
dep.version++ dep.version++
} }

View File

@ -8,6 +8,10 @@ export class EffectScope {
* @internal * @internal
*/ */
private _active = true private _active = true
/**
* @internal track `on` calls, allow `on` call multiple times
*/
private _on = 0
/** /**
* @internal * @internal
*/ */
@ -99,20 +103,27 @@ export class EffectScope {
} }
} }
prevScope: EffectScope | undefined
/** /**
* This should only be called on non-detached scopes * This should only be called on non-detached scopes
* @internal * @internal
*/ */
on(): void { on(): void {
if (++this._on === 1) {
this.prevScope = activeEffectScope
activeEffectScope = this activeEffectScope = this
} }
}
/** /**
* This should only be called on non-detached scopes * This should only be called on non-detached scopes
* @internal * @internal
*/ */
off(): void { off(): void {
activeEffectScope = this.parent if (this._on > 0 && --this._on === 0) {
activeEffectScope = this.prevScope
this.prevScope = undefined
}
} }
stop(fromParent?: boolean): void { stop(fromParent?: boolean): void {

View File

@ -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.
* *

View File

@ -67,7 +67,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
@ -229,7 +229,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.
* *
@ -331,7 +331,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}

View File

@ -31,6 +31,7 @@ import {
TrackOpTypes, TrackOpTypes,
TriggerOpTypes, TriggerOpTypes,
effectScope, effectScope,
onScopeDispose,
shallowReactive, shallowReactive,
shallowRef, shallowRef,
toRef, toRef,
@ -1982,4 +1983,31 @@ describe('api: watch', () => {
expect(spy1).toHaveBeenCalled() expect(spy1).toHaveBeenCalled()
expect(spy2).toHaveBeenCalled() expect(spy2).toHaveBeenCalled()
}) })
// #12631
test('this.$watch w/ onScopeDispose', () => {
const onCleanup = vi.fn()
const toggle = ref(true)
const Comp = defineComponent({
render() {},
created(this: any) {
this.$watch(
() => 1,
function () {},
)
onScopeDispose(onCleanup)
},
})
const App = defineComponent({
render() {
return toggle.value ? h(Comp) : null
},
})
const root = nodeOps.createElement('div')
createApp(App).mount(root)
expect(onCleanup).toBeCalledTimes(0)
})
}) })

View File

@ -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()
})
}) })

View File

@ -10,6 +10,7 @@ import {
markRaw, markRaw,
nextTick, nextTick,
nodeOps, nodeOps,
onMounted,
h as originalH, h as originalH,
ref, ref,
render, render,
@ -18,6 +19,10 @@ import {
} from '@vue/runtime-test' } from '@vue/runtime-test'
import { Fragment, createCommentVNode, createVNode } from '../../src/vnode' import { Fragment, createCommentVNode, createVNode } from '../../src/vnode'
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', () => {
@ -741,4 +746,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>',
)
})
}) })

View File

@ -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])
})
}) })

View File

@ -36,9 +36,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
@ -215,9 +215,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 {

View File

@ -806,7 +806,7 @@ export function setupComponent(
const { props, children } = instance.vnode const { props, children } = instance.vnode
const isStateful = isStatefulComponent(instance) const isStateful = isStatefulComponent(instance)
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
? setupStatefulComponent(instance, isSSR) ? setupStatefulComponent(instance, isSSR)

View File

@ -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'
@ -97,7 +106,8 @@ const normalizeSlot = (
if ( if (
__DEV__ && __DEV__ &&
currentInstance && currentInstance &&
(!ctx || ctx.root === currentInstance.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: ` +
@ -170,7 +180,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]
} }
} }

View File

@ -187,6 +187,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) {

View File

@ -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,

View File

@ -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)) {

View File

@ -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)) {

View File

@ -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],

View File

@ -5,9 +5,11 @@ 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>> { ): TemplateRef<T> {
const i = getCurrentInstance() const i = getCurrentInstance()
const r = shallowRef(null) const r = shallowRef(null)
if (i) { if (i) {

View File

@ -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,

View File

@ -2049,7 +2049,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()
@ -2092,7 +2098,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
@ -2256,7 +2264,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)
@ -2265,6 +2283,13 @@ function baseCreateRenderer(
invokeArrayFns(bum) invokeArrayFns(bum)
} }
// remove slots content from parent renderCache
if (parent && isArray(slotCacheKeys)) {
slotCacheKeys.forEach(v => {
parent.renderCache[v] = undefined
})
}
if ( if (
__COMPAT__ && __COMPAT__ &&
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance) isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
@ -2478,11 +2503,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 if (__DEV__) {
// #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
} }
c2.el && (c2.el.__vnode = c2)
}
} }
} }
} }

View File

@ -81,6 +81,7 @@ const TransitionGroupImpl: ComponentOptions = /*@__PURE__*/ decorate({
moveClass, moveClass,
) )
) { ) {
prevChildren = []
return return
} }
@ -110,6 +111,7 @@ const TransitionGroupImpl: ComponentOptions = /*@__PURE__*/ decorate({
}) })
el.addEventListener('transitionend', cb) el.addEventListener('transitionend', cb)
}) })
prevChildren = []
}) })
return () => { return () => {

View File

@ -102,7 +102,12 @@ function shouldSetAsProp(
// them as attributes. // them as attributes.
// Note that `contentEditable` doesn't have this problem: its DOM // Note that `contentEditable` doesn't have this problem: its DOM
// property is also enumerated string values. // property is also enumerated string values.
if (key === 'spellcheck' || key === 'draggable' || key === 'translate') { if (
key === 'spellcheck' ||
key === 'draggable' ||
key === 'translate' ||
key === 'autocorrect'
) {
return false return false
} }

View File

@ -1,4 +1,4 @@
import { createApp } from 'vue' import { createApp, defineAsyncComponent, h } from 'vue'
import { renderToString } from '../src/renderToString' import { renderToString } from '../src/renderToString'
const components = { const components = {
@ -154,6 +154,38 @@ describe('ssr: slot', () => {
).toBe(`<div><p>1</p><p>2</p></div>`) ).toBe(`<div><p>1</p><p>2</p></div>`)
}) })
// #12438
test('async component slot with v-if true', async () => {
const Layout = defineAsyncComponent(() =>
Promise.resolve({
template: `<div><slot name="header">default header</slot></div>`,
}),
)
const LayoutLoader = {
setup(_: any, context: any) {
return () => h(Layout, {}, context.slots)
},
}
expect(
await renderToString(
createApp({
components: {
LayoutLoader,
},
template: `
<Suspense>
<LayoutLoader>
<template v-if="true" #header>
new header
</template>
</LayoutLoader>
</Suspense>
`,
}),
),
).toBe(`<div><!--[--> new header <!--]--></div>`)
})
// #11326 // #11326
test('dynamic component slot', async () => { test('dynamic component slot', async () => {
expect( expect(

View File

@ -1,3 +1,4 @@
import type { ElementHandle } from 'puppeteer'
import { E2E_TIMEOUT, setupPuppeteer } from './e2eUtils' import { E2E_TIMEOUT, setupPuppeteer } from './e2eUtils'
import path from 'node:path' import path from 'node:path'
import { Transition, createApp, h, nextTick, ref } from 'vue' import { Transition, createApp, h, nextTick, ref } from 'vue'
@ -1653,6 +1654,74 @@ describe('e2e: Transition', () => {
}, },
E2E_TIMEOUT, E2E_TIMEOUT,
) )
// #12860
test(
'unmount children',
async () => {
const unmountSpy = vi.fn()
let storageContainer: ElementHandle<HTMLDivElement>
const setStorageContainer = (container: any) =>
(storageContainer = container)
await page().exposeFunction('unmountSpy', unmountSpy)
await page().exposeFunction('setStorageContainer', setStorageContainer)
await page().evaluate(() => {
const { unmountSpy, setStorageContainer } = window as any
const { createApp, ref, h, onUnmounted, getCurrentInstance } = (
window as any
).Vue
createApp({
template: `
<div id="container">
<transition>
<KeepAlive :include="includeRef">
<TrueBranch v-if="toggle"></TrueBranch>
</KeepAlive>
</transition>
</div>
<button id="toggleBtn" @click="click">button</button>
`,
components: {
TrueBranch: {
name: 'TrueBranch',
setup() {
const instance = getCurrentInstance()
onUnmounted(() => {
unmountSpy()
setStorageContainer(instance.__keepAliveStorageContainer)
})
const count = ref(0)
return () => h('div', count.value)
},
},
},
setup: () => {
const includeRef = ref(['TrueBranch'])
const toggle = ref(true)
const click = () => {
toggle.value = !toggle.value
if (toggle.value) {
includeRef.value = ['TrueBranch']
} else {
includeRef.value = []
}
}
return { toggle, click, unmountSpy, includeRef }
},
}).mount('#app')
})
await transitionFinish()
expect(await html('#container')).toBe('<div>0</div>')
await click('#toggleBtn')
await transitionFinish()
expect(await html('#container')).toBe('<!--v-if-->')
expect(unmountSpy).toBeCalledTimes(1)
expect(await storageContainer!.evaluate(x => x.innerHTML)).toBe(``)
},
E2E_TIMEOUT,
)
}) })
describe('transition with Suspense', () => { describe('transition with Suspense', () => {

View File

@ -645,4 +645,55 @@ describe('e2e: TransitionGroup', () => {
}, },
E2E_TIMEOUT, E2E_TIMEOUT,
) )
test(
'not leaking after children unmounted',
async () => {
const client = await page().createCDPSession()
await page().evaluate(async () => {
const { createApp, ref, nextTick } = (window as any).Vue
const show = ref(true)
createApp({
components: {
Child: {
setup: () => {
// Big arrays kick GC earlier
const test = ref([...Array(3000)].map((_, i) => ({ i })))
// @ts-expect-error - Custom property and same lib as runtime is used
window.__REF__ = new WeakRef(test)
return { test }
},
template: `
<p>{{ test.length }}</p>
`,
},
},
template: `
<transition-group>
<Child v-if="show" />
</transition-group>
`,
setup() {
return { show }
},
}).mount('#app')
show.value = false
await nextTick()
})
const isCollected = async () =>
// @ts-expect-error - Custom property
await page().evaluate(() => window.__REF__.deref() === undefined)
while ((await isCollected()) === false) {
await client.send('HeapProfiler.collectGarbage')
}
expect(await isCollected()).toBe(true)
},
E2E_TIMEOUT,
)
}) })

View File

@ -1,13 +1,13 @@
import puppeteer, { import puppeteer, {
type Browser, type Browser,
type ClickOptions, type ClickOptions,
type LaunchOptions,
type Page, type Page,
type PuppeteerLaunchOptions,
} from 'puppeteer' } from 'puppeteer'
export const E2E_TIMEOUT: number = 30 * 1000 export const E2E_TIMEOUT: number = 30 * 1000
const puppeteerOptions: PuppeteerLaunchOptions = { const puppeteerOptions: LaunchOptions = {
args: process.env.CI ? ['--no-sandbox', '--disable-setuid-sandbox'] : [], args: process.env.CI ? ['--no-sandbox', '--disable-setuid-sandbox'] : [],
headless: true, headless: true,
} }

View File

@ -14,6 +14,7 @@ describe('e2e: todomvc', () => {
classList, classList,
enterValue, enterValue,
clearValue, clearValue,
timeout,
} = setupPuppeteer() } = setupPuppeteer()
async function removeItemAt(n: number) { async function removeItemAt(n: number) {
@ -101,6 +102,7 @@ describe('e2e: todomvc', () => {
// active filter // active filter
await click('.filters li:nth-child(2) a') await click('.filters li:nth-child(2) a')
await timeout(1)
expect(await count('.todo')).toBe(1) expect(await count('.todo')).toBe(1)
expect(await count('.todo.completed')).toBe(0) expect(await count('.todo.completed')).toBe(0)
// add item with filter active // add item with filter active
@ -109,6 +111,7 @@ describe('e2e: todomvc', () => {
// completed filter // completed filter
await click('.filters li:nth-child(3) a') await click('.filters li:nth-child(3) a')
await timeout(1)
expect(await count('.todo')).toBe(2) expect(await count('.todo')).toBe(2)
expect(await count('.todo.completed')).toBe(2) expect(await count('.todo.completed')).toBe(2)
@ -128,13 +131,15 @@ describe('e2e: todomvc', () => {
await click('.todo .toggle') await click('.todo .toggle')
expect(await count('.todo')).toBe(1) expect(await count('.todo')).toBe(1)
await click('.filters li:nth-child(2) a') await click('.filters li:nth-child(2) a')
await timeout(1)
expect(await count('.todo')).toBe(3) expect(await count('.todo')).toBe(3)
await click('.todo .toggle') await click('.todo .toggle')
expect(await count('.todo')).toBe(2) expect(await count('.todo')).toBe(2)
// editing triggered by blur // editing triggered by blur
await click('.filters li:nth-child(1) a') await click('.filters li:nth-child(1) a')
await click('.todo:nth-child(1) label', { clickCount: 2 }) await timeout(1)
await click('.todo:nth-child(1) label', { count: 2 })
expect(await count('.todo.editing')).toBe(1) expect(await count('.todo.editing')).toBe(1)
expect(await isFocused('.todo:nth-child(1) .edit')).toBe(true) expect(await isFocused('.todo:nth-child(1) .edit')).toBe(true)
await clearValue('.todo:nth-child(1) .edit') await clearValue('.todo:nth-child(1) .edit')
@ -144,13 +149,13 @@ describe('e2e: todomvc', () => {
expect(await text('.todo:nth-child(1) label')).toBe('edited!') expect(await text('.todo:nth-child(1) label')).toBe('edited!')
// editing triggered by enter // editing triggered by enter
await click('.todo label', { clickCount: 2 }) await click('.todo label', { count: 2 })
await enterValue('.todo:nth-child(1) .edit', 'edited again!') await enterValue('.todo:nth-child(1) .edit', 'edited again!')
expect(await count('.todo.editing')).toBe(0) expect(await count('.todo.editing')).toBe(0)
expect(await text('.todo:nth-child(1) label')).toBe('edited again!') expect(await text('.todo:nth-child(1) label')).toBe('edited again!')
// cancel // cancel
await click('.todo label', { clickCount: 2 }) await click('.todo label', { count: 2 })
await clearValue('.todo:nth-child(1) .edit') await clearValue('.todo:nth-child(1) .edit')
await page().type('.todo:nth-child(1) .edit', 'edited!') await page().type('.todo:nth-child(1) .edit', 'edited!')
await page().keyboard.press('Escape') await page().keyboard.press('Escape')
@ -158,7 +163,7 @@ describe('e2e: todomvc', () => {
expect(await text('.todo:nth-child(1) label')).toBe('edited again!') expect(await text('.todo:nth-child(1) label')).toBe('edited again!')
// empty value should remove // empty value should remove
await click('.todo label', { clickCount: 2 }) await click('.todo label', { count: 2 })
await enterValue('.todo:nth-child(1) .edit', ' ') await enterValue('.todo:nth-child(1) .edit', ' ')
expect(await count('.todo')).toBe(3) expect(await count('.todo')).toBe(3)

View File

@ -88,7 +88,7 @@ describe('e2e: tree', () => {
expect(await isVisible('#demo ul')).toBe(true) expect(await isVisible('#demo ul')).toBe(true)
expect(await text('#demo li div span')).toContain('[-]') expect(await text('#demo li div span')).toContain('[-]')
await click('#demo ul > .item div', { clickCount: 2 }) await click('#demo ul > .item div', { count: 2 })
expect(await count('.item')).toBe(15) expect(await count('.item')).toBe(15)
expect(await count('.item > ul')).toBe(5) expect(await count('.item > ul')).toBe(5)
expect(await text('#demo ul > .item:nth-child(1)')).toContain('[-]') expect(await text('#demo ul > .item:nth-child(1)')).toContain('[-]')

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,25 @@ packages:
- 'packages-private/*' - 'packages-private/*'
catalog: catalog:
'@babel/parser': ^7.25.3 '@babel/parser': ^7.27.1
'@babel/types': ^7.25.2 '@babel/types': ^7.27.1
'estree-walker': ^2.0.2 'estree-walker': ^2.0.2
'magic-string': ^0.30.11 'magic-string': ^0.30.17
'source-map-js': ^1.2.0 'source-map-js': ^1.2.1
'vite': ^5.4.0 'vite': ^5.4.15
'@vitejs/plugin-vue': ^5.1.2 '@vitejs/plugin-vue': ^5.2.4
onlyBuiltDependencies:
- '@swc/core'
- 'esbuild'
- 'puppeteer'
- 'simple-git-hooks'
- 'unrs-resolver'
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'

View File

@ -111,7 +111,7 @@ async function renderUsages() {
*/ */
async function importJSON(filePath) { async function importJSON(filePath) {
if (!existsSync(filePath)) return undefined if (!existsSync(filePath)) return undefined
return (await import(filePath, { assert: { type: 'json' } })).default return (await import(filePath, { with: { type: 'json' } })).default
} }
/** /**

View File

@ -36,7 +36,7 @@ exec('pnpm', ['build', 'vue', '-f', 'global-runtime']).then(() => {
prodBuild.includes('annotation,annotation-xml,maction') prodBuild.includes('annotation,annotation-xml,maction')
) { ) {
errors.push( errors.push(
'prod build contains unexpected domTagConifg lists.\n' + 'prod build contains unexpected domTagConfig lists.\n' +
'This means helpers like isHTMLTag() is used in runtime code paths when it should be compiler-only.', 'This means helpers like isHTMLTag() is used in runtime code paths when it should be compiler-only.',
) )
} }

View File

@ -17,7 +17,6 @@
"packages/runtime-dom/src", "packages/runtime-dom/src",
"packages/reactivity/src", "packages/reactivity/src",
"packages/shared/src", "packages/shared/src",
"packages/global.d.ts",
"packages/compiler-sfc/src", "packages/compiler-sfc/src",
"packages/compiler-ssr/src", "packages/compiler-ssr/src",
"packages/server-renderer/src" "packages/server-renderer/src"

View File

@ -1,4 +1,4 @@
import { configDefaults, defineConfig } from 'vitest/config' import { defineConfig } from 'vitest/config'
import { entries } from './scripts/aliases.js' import { entries } from './scripts/aliases.js'
export default defineConfig({ export default defineConfig({