diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 9816f7f5257..95bc65f357b 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -31,4 +31,4 @@ jobs: - name: Run prettier run: pnpm run format - - uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef + - uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 25c2556091c..1122eb35573 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -104,5 +104,8 @@ jobs: - name: Run prettier run: pnpm run format-check + - name: Run tsc + run: pnpm run check + - name: Run type declaration tests run: pnpm run test-dts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dbae4c79d5..2a0b96332b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,82 @@ +## [3.5.18](https://github.com/vuejs/core/compare/v3.5.17...v3.5.18) (2025-07-23) + + +### Bug Fixes + +* **compiler-core:** avoid cached text vnodes retaining detached DOM nodes ([#13662](https://github.com/vuejs/core/issues/13662)) ([00695a5](https://github.com/vuejs/core/commit/00695a5b41b2d032deaeada83831ff83aa6bfd4e)), closes [#13661](https://github.com/vuejs/core/issues/13661) +* **compiler-core:** avoid self updates of `v-pre` ([#12556](https://github.com/vuejs/core/issues/12556)) ([21b685a](https://github.com/vuejs/core/commit/21b685ad9d9d0e6060fc7d07b719bf35f2d9ae1f)) +* **compiler-core:** identifiers in function parameters should not be inferred as references ([#13548](https://github.com/vuejs/core/issues/13548)) ([9b02923](https://github.com/vuejs/core/commit/9b029239edf88558465b941e1e4c085f92b1ebff)) +* **compiler-core:** recognize empty string as non-identifier ([#12553](https://github.com/vuejs/core/issues/12553)) ([ce93339](https://github.com/vuejs/core/commit/ce933390ad1c72bed258f7ad959a78f0e8acdf57)) +* **compiler-core:** transform empty `v-bind` dynamic argument content correctly ([#12554](https://github.com/vuejs/core/issues/12554)) ([d3af67e](https://github.com/vuejs/core/commit/d3af67e878790892f9d34cfea15d13625aabe733)) +* **compiler-sfc:** transform empty srcset w/ includeAbsolute: true ([#13639](https://github.com/vuejs/core/issues/13639)) ([d8e40ef](https://github.com/vuejs/core/commit/d8e40ef7e1c20ee86b294e7cf78e2de60d12830e)), closes [vitejs/vite-plugin-vue#631](https://github.com/vitejs/vite-plugin-vue/issues/631) +* **css-vars:** nullish v-bind in style should not lead to unexpected inheritance ([#12461](https://github.com/vuejs/core/issues/12461)) ([c85f1b5](https://github.com/vuejs/core/commit/c85f1b5a132eb8ec25f71b250e25e65a5c20964f)), closes [#12434](https://github.com/vuejs/core/issues/12434) [#12439](https://github.com/vuejs/core/issues/12439) [#7474](https://github.com/vuejs/core/issues/7474) [#7475](https://github.com/vuejs/core/issues/7475) +* **custom-element:** ensure exposed methods are accessible from custom elements by making them enumerable ([#13634](https://github.com/vuejs/core/issues/13634)) ([90573b0](https://github.com/vuejs/core/commit/90573b06bf6fb6c14c6bbff6c4e34e0ab108953a)), closes [#13632](https://github.com/vuejs/core/issues/13632) +* **hydration:** prevent lazy hydration for updated components ([#13511](https://github.com/vuejs/core/issues/13511)) ([a9269c6](https://github.com/vuejs/core/commit/a9269c642bf944560bc29adb5dae471c11cd9ee8)), closes [#13510](https://github.com/vuejs/core/issues/13510) +* **runtime-core:** ensure correct anchor el for unresolved async components ([#13560](https://github.com/vuejs/core/issues/13560)) ([7f29943](https://github.com/vuejs/core/commit/7f2994393dcdb82cacbf62e02b5ba5565f32588b)), closes [#13559](https://github.com/vuejs/core/issues/13559) +* **slots:** refine internal key checking to support slot names starting with an underscore ([#13612](https://github.com/vuejs/core/issues/13612)) ([c5f7db1](https://github.com/vuejs/core/commit/c5f7db11542bb2246363aef78c88a8e6cef0ee93)), closes [#13611](https://github.com/vuejs/core/issues/13611) +* **ssr:** ensure empty slots render as a comment node in Transition ([#13396](https://github.com/vuejs/core/issues/13396)) ([8cfc10a](https://github.com/vuejs/core/commit/8cfc10a80b9cbf5d801ab149e49b8506d192e7e1)), closes [#13394](https://github.com/vuejs/core/issues/13394) + + + +## [3.5.17](https://github.com/vuejs/core/compare/v3.5.16...v3.5.17) (2025-06-18) + + +### Bug Fixes + +* **compat:** allow v-model built in modifiers on component ([#12654](https://github.com/vuejs/core/issues/12654)) ([cb14b86](https://github.com/vuejs/core/commit/cb14b860f150c4a83bcd52cd26096b7a5aa3a2bf)), closes [#12652](https://github.com/vuejs/core/issues/12652) +* **compile-sfc:** handle mapped types work with omit and pick ([#12648](https://github.com/vuejs/core/issues/12648)) ([4eb46e4](https://github.com/vuejs/core/commit/4eb46e443f1878199755cb73d481d318a9714392)), closes [#12647](https://github.com/vuejs/core/issues/12647) +* **compiler-core:** do not increase newlines in `InEntity` state ([#13362](https://github.com/vuejs/core/issues/13362)) ([f05a8d6](https://github.com/vuejs/core/commit/f05a8d613bd873b811cfdb9979ccac8382dba322)) +* **compiler-core:** ignore whitespace when matching adjacent v-if ([#12321](https://github.com/vuejs/core/issues/12321)) ([10ebcef](https://github.com/vuejs/core/commit/10ebcef8c870dbc042b0ea49b1424b2e8f692145)), closes [#9173](https://github.com/vuejs/core/issues/9173) +* **compiler-core:** prevent comments from blocking static node hoisting ([#13345](https://github.com/vuejs/core/issues/13345)) ([55dad62](https://github.com/vuejs/core/commit/55dad625acd9e9ddd5a933d5e323ecfdec1a612f)), closes [#13344](https://github.com/vuejs/core/issues/13344) +* **compiler-sfc:** improved type resolution for function type aliases ([#13452](https://github.com/vuejs/core/issues/13452)) ([f3479aa](https://github.com/vuejs/core/commit/f3479aac9625f4459e650d1c0a70e73863147903)), closes [#13444](https://github.com/vuejs/core/issues/13444) +* **custom-element:** ensure configureApp is applied to async component ([#12607](https://github.com/vuejs/core/issues/12607)) ([5ba1afb](https://github.com/vuejs/core/commit/5ba1afba09c3ea56c1c17484f5d8aeae210ce52a)), closes [#12448](https://github.com/vuejs/core/issues/12448) +* **custom-element:** prevent injecting child styles if shadowRoot is false ([#12769](https://github.com/vuejs/core/issues/12769)) ([73055d8](https://github.com/vuejs/core/commit/73055d8d9578d485e3fe846726b50666e1aa56f5)), closes [#12630](https://github.com/vuejs/core/issues/12630) +* **reactivity:** add `__v_skip` flag to `Dep` to prevent reactive conversion ([#12804](https://github.com/vuejs/core/issues/12804)) ([e8d8f5f](https://github.com/vuejs/core/commit/e8d8f5f604e821acc46b4200d5b06979c05af1c2)), closes [#12803](https://github.com/vuejs/core/issues/12803) +* **runtime-core:** unset old ref during patching when new ref is absent ([#12900](https://github.com/vuejs/core/issues/12900)) ([47ddf98](https://github.com/vuejs/core/commit/47ddf986021dff8de68b0da72787e53a6c19de4c)), closes [#12898](https://github.com/vuejs/core/issues/12898) +* **slots:** make cache indexes marker non-enumerable ([#13469](https://github.com/vuejs/core/issues/13469)) ([919c447](https://github.com/vuejs/core/commit/919c44744bba1f0c661c87d2059c3b429611aa7e)), closes [#13468](https://github.com/vuejs/core/issues/13468) +* **ssr:** handle initial selected state for select with v-model + v-for/v-if option ([#13487](https://github.com/vuejs/core/issues/13487)) ([1552095](https://github.com/vuejs/core/commit/15520954f9f1c7f834175938a50dba5d4be0e6c4)), closes [#13486](https://github.com/vuejs/core/issues/13486) +* **types:** typo of `vOnce` and `vSlot` ([#13343](https://github.com/vuejs/core/issues/13343)) ([762fae4](https://github.com/vuejs/core/commit/762fae4b57ad60602e5c84465a3bff562785b314)) + + + +## [3.5.16](https://github.com/vuejs/core/compare/v3.5.15...v3.5.16) (2025-05-29) + + +### Reverts + +* Revert "fix(compiler-sfc): add scoping tag to trailing universal selector" (#13406) ([19f23b1](https://github.com/vuejs/core/commit/19f23b180bb679e38db95d6a10a420abeedc8e1c)), closes [#13406](https://github.com/vuejs/core/issues/13406) +* Revert "fix(compiler-sfc): add error handling for defineModel() without variable" (#13390) ([42f879f](https://github.com/vuejs/core/commit/42f879fcab48e0e1011967a771b4ad9e8838d760)), closes [#13390](https://github.com/vuejs/core/issues/13390) + + + +## [3.5.15](https://github.com/vuejs/core/compare/v3.5.14...v3.5.15) (2025-05-26) + + +### Bug Fixes + +* **compat:** ensure false value on input retains value attribute ([#13216](https://github.com/vuejs/core/issues/13216)) ([1a66474](https://github.com/vuejs/core/commit/1a664749d4d65a345589a6d78106ede7574cb2e1)), closes [#13205](https://github.com/vuejs/core/issues/13205) +* **compat:** should not warn COMPILER_V_BIND_OBJECT_ORDER when using v-bind together with v-for ([#12993](https://github.com/vuejs/core/issues/12993)) ([93949e6](https://github.com/vuejs/core/commit/93949e6587ee019bccd5b8b9d76f0e1ed6ea16fc)), closes [#12992](https://github.com/vuejs/core/issues/12992) +* **compile-sfc:** handle inline template source map in prod build ([#12701](https://github.com/vuejs/core/issues/12701)) ([89edc6c](https://github.com/vuejs/core/commit/89edc6cdcbd34ea6394927ecbfaa61dc4f871de7)), closes [#12682](https://github.com/vuejs/core/issues/12682) [vitejs/vite-plugin-vue#500](https://github.com/vitejs/vite-plugin-vue/issues/500) +* **compiler-core:** ensure mapping is added only if node source is available ([#13285](https://github.com/vuejs/core/issues/13285)) ([d37a2ac](https://github.com/vuejs/core/commit/d37a2ac59d904ac0e3257ba552b6c04920a363f0)), closes [#13261](https://github.com/vuejs/core/issues/13261) [vitejs/vite-plugin-vue#368](https://github.com/vitejs/vite-plugin-vue/issues/368) +* **compiler-dom:** improve HTML nesting validation to allow any child element within template tag ([#13320](https://github.com/vuejs/core/issues/13320)) ([163b365](https://github.com/vuejs/core/commit/163b3651d174321911648a164052effa9249a2aa)), closes [#13318](https://github.com/vuejs/core/issues/13318) +* **compiler-sfc:** add error handling for defineModel() without variable assignment ([#13352](https://github.com/vuejs/core/issues/13352)) ([00734af](https://github.com/vuejs/core/commit/00734afef5f7bddbdaee52aa5359a6ef989f32d3)), closes [#13280](https://github.com/vuejs/core/issues/13280) +* **compiler-sfc:** add scoping tag to trailing universal selector ([#12918](https://github.com/vuejs/core/issues/12918)) ([949df80](https://github.com/vuejs/core/commit/949df808809fd7cccf7718797beab0654aa68302)), closes [#12906](https://github.com/vuejs/core/issues/12906) +* **compiler-sfc:** improve type inference for TSTypeAliasDeclaration with better runtime type detection ([#13245](https://github.com/vuejs/core/issues/13245)) ([cf5a5e0](https://github.com/vuejs/core/commit/cf5a5e0edf0efcab25c27aa2d13eba91f7372d39)), closes [#13240](https://github.com/vuejs/core/issues/13240) +* **compiler-sfc:** simulate `allowArbitraryExtensions` on resolving type ([#13301](https://github.com/vuejs/core/issues/13301)) ([f7ce5ae](https://github.com/vuejs/core/commit/f7ce5ae666129339c006b339437c2dff6bceffe0)), closes [#13295](https://github.com/vuejs/core/issues/13295) +* **custom-element:** allow injecting values ​​from app context in nested elements ([#13219](https://github.com/vuejs/core/issues/13219)) ([b991075](https://github.com/vuejs/core/commit/b9910755a50c7d6c52b28c3aef20cf97810295c9)), closes [#13212](https://github.com/vuejs/core/issues/13212) +* **custom-element:** ensure proper remount and prevent redundant slot parsing with shadowRoot false ([#13201](https://github.com/vuejs/core/issues/13201)) ([1d41d4d](https://github.com/vuejs/core/commit/1d41d4de7f64a37160c8171d0137fd8d35c346c9)), closes [#13199](https://github.com/vuejs/core/issues/13199) +* **custom-element:** preserve appContext during update ([#12455](https://github.com/vuejs/core/issues/12455)) ([013749e](https://github.com/vuejs/core/commit/013749e75ef3b51762a86da379ea4ba4501b54ae)), closes [#12453](https://github.com/vuejs/core/issues/12453) +* **custom-element:** properly resolve props for sync component defs ([#12855](https://github.com/vuejs/core/issues/12855)) ([a683c80](https://github.com/vuejs/core/commit/a683c80cf44ecc482f8ac9c76bf2381443c1b0bb)), closes [#12854](https://github.com/vuejs/core/issues/12854) +* **hydration:** handle transition appear hydration edge case ([#13339](https://github.com/vuejs/core/issues/13339)) ([35aeae7](https://github.com/vuejs/core/commit/35aeae7fa3168adcf9ed95fd35495d17c8b93eeb)), closes [#13335](https://github.com/vuejs/core/issues/13335) +* **hydration:** skip lazy hydration for patched components ([#13283](https://github.com/vuejs/core/issues/13283)) ([80055fd](https://github.com/vuejs/core/commit/80055fddfb3ca1e2a44f19c7f0ffaeba00de5140)), closes [#13255](https://github.com/vuejs/core/issues/13255) +* **suspense:** handle edge case in patching list nodes within Suspense ([#13306](https://github.com/vuejs/core/issues/13306)) ([772b008](https://github.com/vuejs/core/commit/772b0087cb7be151c514a1d30365fb0f61a652ba)), closes [#13305](https://github.com/vuejs/core/issues/13305) +* **teleport:** handle deferred teleport updates before and after mount ([#13350](https://github.com/vuejs/core/issues/13350)) ([d15dce3](https://github.com/vuejs/core/commit/d15dce3142474f2ef9fffed38383acdadcb26c4c)), closes [#13349](https://github.com/vuejs/core/issues/13349) +* **types:** avoid merging component instance into `$props` in `ComponentInstance` ([#12870](https://github.com/vuejs/core/issues/12870)) ([f44feed](https://github.com/vuejs/core/commit/f44feed6fa461a9c4c724e9631c19e9e214c0a20)), closes [#12751](https://github.com/vuejs/core/issues/12751) +* **types:** exclude `undefined` from inferred prop types with default values ([#13007](https://github.com/vuejs/core/issues/13007)) ([5179d32](https://github.com/vuejs/core/commit/5179d328d950015e7fb2a74fe1a8518fd8d2c94e)), closes [#13006](https://github.com/vuejs/core/issues/13006) +* **watch:** update `oldValue` before running `cb` to prevent stale value ([#12296](https://github.com/vuejs/core/issues/12296)) ([c69c4bb](https://github.com/vuejs/core/commit/c69c4bb59c114f2b5e03733b55ef9ace3087b5c3)), closes [#12294](https://github.com/vuejs/core/issues/12294) + + + ## [3.5.14](https://github.com/vuejs/core/compare/v3.5.13...v3.5.14) (2025-05-15) diff --git a/changelogs/CHANGELOG-3.0.md b/changelogs/CHANGELOG-3.0.md index 16483767fe4..c19ef26890b 100644 --- a/changelogs/CHANGELOG-3.0.md +++ b/changelogs/CHANGELOG-3.0.md @@ -56,13 +56,13 @@ - **hydration:** handle camel-case tag name when performing match assertion ([#3247](https://github.com/vuejs/core/issues/3247)) ([9036f88](https://github.com/vuejs/core/commit/9036f88d8304a3455265f1ecd86ec8f4a5ea4715)), closes [#3243](https://github.com/vuejs/core/issues/3243) - **KeepAlive:** adapt keepalive for ssr ([#3259](https://github.com/vuejs/core/issues/3259)) ([e8e9b00](https://github.com/vuejs/core/commit/e8e9b00f81ed42434afd92f84101e7a14d70a23c)), closes [#3255](https://github.com/vuejs/core/issues/3255) - **reactivity:** ensure computed can be wrapped by readonly ([41e02f0](https://github.com/vuejs/core/commit/41e02f0fac069c93c94438741517e713f3c94215)), closes [#3376](https://github.com/vuejs/core/issues/3376) -- **reactivity:** ensure that shallow and normal proxies are tracked seperately (close [#2843](https://github.com/vuejs/core/issues/2843)) ([#2851](https://github.com/vuejs/core/issues/2851)) ([22cc4a7](https://github.com/vuejs/core/commit/22cc4a76592cfe336e75e2fa0c05232ae1f0f149)) +- **reactivity:** ensure that shallow and normal proxies are tracked separately (close [#2843](https://github.com/vuejs/core/issues/2843)) ([#2851](https://github.com/vuejs/core/issues/2851)) ([22cc4a7](https://github.com/vuejs/core/commit/22cc4a76592cfe336e75e2fa0c05232ae1f0f149)) - **reactivity:** fix shallow readonly behavior for collections ([#3003](https://github.com/vuejs/core/issues/3003)) ([68de9f4](https://github.com/vuejs/core/commit/68de9f408a2e61a5726a4a0d03b026cba451c5bd)), closes [#3007](https://github.com/vuejs/core/issues/3007) - **rumtime-core:** custom dom props should be cloned when cloning a hoisted DOM ([#3080](https://github.com/vuejs/core/issues/3080)) ([5dbe834](https://github.com/vuejs/core/commit/5dbe8348581dacd7a3594a9b0055ce350ce8e5bf)), closes [#3072](https://github.com/vuejs/core/issues/3072) - **runtime-core:** cache props default values to avoid unnecessary watcher trigger ([#3474](https://github.com/vuejs/core/issues/3474)) ([44166b4](https://github.com/vuejs/core/commit/44166b43d9be1062f79612880f71284049bcab0b)), closes [#3471](https://github.com/vuejs/core/issues/3471) - **runtime-core:** ensure only skip unflushed job ([#3406](https://github.com/vuejs/core/issues/3406)) ([bf34e33](https://github.com/vuejs/core/commit/bf34e33c909da89681b9c5004cdf04ab198ec5a7)) - **runtime-core:** fix async component ref handling ([#3191](https://github.com/vuejs/core/issues/3191)) ([7562e72](https://github.com/vuejs/core/commit/7562e72c2b58a5646bd4fbd9adea11eb884fe140)), closes [#3188](https://github.com/vuejs/core/issues/3188) -- **runtime-core:** fix erraneous emits warnings w/ mixins ([60d777d](https://github.com/vuejs/core/commit/60d777d228414515cc32526ad72a53ef070501be)), closes [#2651](https://github.com/vuejs/core/issues/2651) +- **runtime-core:** fix erroneous emits warnings w/ mixins ([60d777d](https://github.com/vuejs/core/commit/60d777d228414515cc32526ad72a53ef070501be)), closes [#2651](https://github.com/vuejs/core/issues/2651) - **runtime-core:** fix warning for absent props ([#3363](https://github.com/vuejs/core/issues/3363)) ([86ceef4](https://github.com/vuejs/core/commit/86ceef43523bfbbb0a24731d3802ca6849cbefd6)), closes [#3362](https://github.com/vuejs/core/issues/3362) - **runtime-core:** handle error in async setup ([#2881](https://github.com/vuejs/core/issues/2881)) ([d668d48](https://github.com/vuejs/core/commit/d668d48e9e5211a49ee53361ea5b4d67ba16e0a3)) - **runtime-core:** handle error in async watchEffect ([#3129](https://github.com/vuejs/core/issues/3129)) ([eb1fae6](https://github.com/vuejs/core/commit/eb1fae63f926435fb0eef890663d24e09d4c79e1)) @@ -202,7 +202,7 @@ may cause build issues in projects still using TS 3.x. - **script-setup:** ensure useContext() return valid context ([73cdb9d](https://github.com/vuejs/core/commit/73cdb9d4208f887fe08349657122e39175d7166c)) - **slots:** dynamically named slots should be keyed by name ([2ab8c41](https://github.com/vuejs/core/commit/2ab8c41a1a43952fb229587a9da48d9a1214ab9e)), closes [#2535](https://github.com/vuejs/core/issues/2535) - **slots:** should render fallback content when slot content contains no valid nodes ([#2485](https://github.com/vuejs/core/issues/2485)) ([ce4915d](https://github.com/vuejs/core/commit/ce4915d8bed12f4cdb5fa8ca39bda98d0d3aabb7)), closes [#2347](https://github.com/vuejs/core/issues/2347) [#2461](https://github.com/vuejs/core/issues/2461) -- **suspense:** fix nested async child toggle inside already resovled suspense ([cf7f1db](https://github.com/vuejs/core/commit/cf7f1dbc9be8d50ad220e3630c38f5a9a217d693)), closes [#2215](https://github.com/vuejs/core/issues/2215) +- **suspense:** fix nested async child toggle inside already resolved suspense ([cf7f1db](https://github.com/vuejs/core/commit/cf7f1dbc9be8d50ad220e3630c38f5a9a217d693)), closes [#2215](https://github.com/vuejs/core/issues/2215) - **teleport:** Teleport into SVG elements ([#2648](https://github.com/vuejs/core/issues/2648)) ([cd92836](https://github.com/vuejs/core/commit/cd928362232747a51d1fd4790bb20adcdd59d187)), closes [#2652](https://github.com/vuejs/core/issues/2652) - **transition:** avoid invoking stale transition end callbacks ([eaf8a67](https://github.com/vuejs/core/commit/eaf8a67c7219e1b79d6abca44a1d7f1b341b58b0)), closes [#2482](https://github.com/vuejs/core/issues/2482) - **transition:** respect rules in \*-leave-from transition class ([#2597](https://github.com/vuejs/core/issues/2597)) ([e2618a6](https://github.com/vuejs/core/commit/e2618a632d4add2819ffb8b575af0da189dc3204)), closes [#2593](https://github.com/vuejs/core/issues/2593) @@ -236,7 +236,7 @@ may cause build issues in projects still using TS 3.x. - **compiler-sfc:** compileScript inline render function mode ([886ed76](https://github.com/vuejs/core/commit/886ed7681dd203c07ff3b504538328f43e14d9b0)) - **compiler-sfc:** new script setup implementation ([556560f](https://github.com/vuejs/core/commit/556560fae31d9e406cfae656089657b6332686c1)) -- **compiler-sfc:** new SFC css varaible injection implementation ([41bb7fa](https://github.com/vuejs/core/commit/41bb7fa330e78c4a354a2e67742bd13bee2f4293)) +- **compiler-sfc:** new SFC css variable injection implementation ([41bb7fa](https://github.com/vuejs/core/commit/41bb7fa330e78c4a354a2e67742bd13bee2f4293)) - **compiler-sfc:** support kebab-case components in ` diff --git a/packages/compiler-core/__tests__/parse.spec.ts b/packages/compiler-core/__tests__/parse.spec.ts index 4e5a9616511..cdc2b09fd48 100644 --- a/packages/compiler-core/__tests__/parse.spec.ts +++ b/packages/compiler-core/__tests__/parse.spec.ts @@ -2271,6 +2271,11 @@ describe('compiler: parse', () => { expect(span.loc.start.offset).toBe(0) expect(span.loc.end.offset).toBe(27) }) + + test('correct loc when a line in attribute value ends with &', () => { + const [span] = baseParse(``).children + expect(span.loc.end.line).toBe(2) + }) }) describe('decodeEntities option', () => { diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/cacheStatic.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/cacheStatic.spec.ts.snap index 375a0c8674a..91a82db5bba 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/cacheStatic.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/cacheStatic.spec.ts.snap @@ -8,7 +8,7 @@ return function render(_ctx, _cache) { const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ - _createElementVNode("div", { key: "foo" }, null, -1 /* HOISTED */) + _createElementVNode("div", { key: "foo" }, null, -1 /* CACHED */) ]))) } }" @@ -25,11 +25,11 @@ return function render(_ctx, _cache) { _createElementVNode("p", null, [ _createElementVNode("span"), _createElementVNode("span") - ], -1 /* HOISTED */), + ], -1 /* CACHED */), _createElementVNode("p", null, [ _createElementVNode("span"), _createElementVNode("span") - ], -1 /* HOISTED */) + ], -1 /* CACHED */) ]))) } }" @@ -45,7 +45,7 @@ return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ _createElementVNode("div", null, [ _createCommentVNode("comment") - ], -1 /* HOISTED */) + ], -1 /* CACHED */) ]))) } }" @@ -59,9 +59,9 @@ return function render(_ctx, _cache) { const { createElementVNode: _createElementVNode, createTextVNode: _createTextVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ - _createElementVNode("span", null, null, -1 /* HOISTED */), - _createTextVNode("foo"), - _createElementVNode("div", null, null, -1 /* HOISTED */) + _createElementVNode("span", null, null, -1 /* CACHED */), + _createTextVNode("foo", -1 /* CACHED */), + _createElementVNode("div", null, null, -1 /* CACHED */) ]))) } }" @@ -75,7 +75,7 @@ return function render(_ctx, _cache) { const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ - _createElementVNode("span", { class: "inline" }, "hello", -1 /* HOISTED */) + _createElementVNode("span", { class: "inline" }, "hello", -1 /* CACHED */) ]))) } }" @@ -148,7 +148,7 @@ return function render(_ctx, _cache) { const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ - _createElementVNode("span", null, "foo " + _toDisplayString(1) + " " + _toDisplayString(true), -1 /* HOISTED */) + _createElementVNode("span", null, "foo " + _toDisplayString(1) + " " + _toDisplayString(true), -1 /* CACHED */) ]))) } }" @@ -162,7 +162,7 @@ return function render(_ctx, _cache) { const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ - _createElementVNode("span", { foo: 0 }, _toDisplayString(1), -1 /* HOISTED */) + _createElementVNode("span", { foo: 0 }, _toDisplayString(1), -1 /* CACHED */) ]))) } }" @@ -178,7 +178,7 @@ return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("div", null, [ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(1, (i) => { return (_openBlock(), _createElementBlock("div", null, [...(_cache[0] || (_cache[0] = [ - _createElementVNode("span", { class: "hi" }, null, -1 /* HOISTED */) + _createElementVNode("span", { class: "hi" }, null, -1 /* CACHED */) ]))])) }), 256 /* UNKEYED_FRAGMENT */)) ])) @@ -216,7 +216,7 @@ return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("div", null, [ _withDirectives((_openBlock(), _createElementBlock("svg", null, _cache[0] || (_cache[0] = [ - _createElementVNode("path", { d: "M2,3H5.5L12" }, null, -1 /* HOISTED */) + _createElementVNode("path", { d: "M2,3H5.5L12" }, null, -1 /* CACHED */) ]))), [ [_directive_foo] ]) @@ -402,7 +402,7 @@ return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("div", null, [ ok ? (_openBlock(), _createElementBlock("div", _hoisted_1, _cache[0] || (_cache[0] = [ - _createElementVNode("span", null, null, -1 /* HOISTED */) + _createElementVNode("span", null, null, -1 /* CACHED */) ]))) : _createCommentVNode("v-if", true) ])) @@ -410,6 +410,32 @@ return function render(_ctx, _cache) { }" `; +exports[`compiler: cacheStatic transform > should hoist props for root with single element excluding comments 1`] = ` +"const _Vue = Vue +const { createElementVNode: _createElementVNode, createCommentVNode: _createCommentVNode } = _Vue + +const _hoisted_1 = { id: "a" } + +return function render(_ctx, _cache) { + with (_ctx) { + const { createCommentVNode: _createCommentVNode, createElementVNode: _createElementVNode, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue + + return (_openBlock(), _createElementBlock(_Fragment, null, [ + _createCommentVNode("comment"), + _createElementVNode("div", _hoisted_1, _cache[0] || (_cache[0] = [ + _createElementVNode("div", { id: "b" }, [ + _createElementVNode("div", { id: "c" }, [ + _createElementVNode("div", { id: "d" }, [ + _createElementVNode("div", { id: "e" }, "hello") + ]) + ]) + ], -1 /* CACHED */) + ])) + ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)) + } +}" +`; + exports[`compiler: cacheStatic transform > should hoist v-for children if static 1`] = ` "const _Vue = Vue const { createElementVNode: _createElementVNode } = _Vue @@ -423,7 +449,7 @@ return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("div", null, [ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(list, (i) => { return (_openBlock(), _createElementBlock("div", _hoisted_1, _cache[0] || (_cache[0] = [ - _createElementVNode("span", null, null, -1 /* HOISTED */) + _createElementVNode("span", null, null, -1 /* CACHED */) ]))) }), 256 /* UNKEYED_FRAGMENT */)) ])) diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap index 0d9c0e743de..2cd13bab036 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap @@ -246,6 +246,28 @@ return function render(_ctx, _cache) { }" `; +exports[`compiler: transform component slots > with whitespace: 'preserve' > named slot with v-if + v-else 1`] = ` +"const { resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, openBlock: _openBlock, createBlock: _createBlock } = Vue + +return function render(_ctx, _cache) { + const _component_Comp = _resolveComponent("Comp") + + return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 2 /* DYNAMIC */ }, [ + ok + ? { + name: "one", + fn: _withCtx(() => ["foo"]), + key: "0" + } + : { + name: "two", + fn: _withCtx(() => ["baz"]), + key: "1" + } + ]), 1024 /* DYNAMIC_SLOTS */)) +}" +`; + exports[`compiler: transform component slots > with whitespace: 'preserve' > should not generate whitespace only default slot 1`] = ` "const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue diff --git a/packages/compiler-core/__tests__/transforms/cacheStatic.spec.ts b/packages/compiler-core/__tests__/transforms/cacheStatic.spec.ts index 358c0e31c3d..74f6caca328 100644 --- a/packages/compiler-core/__tests__/transforms/cacheStatic.spec.ts +++ b/packages/compiler-core/__tests__/transforms/cacheStatic.spec.ts @@ -543,6 +543,32 @@ describe('compiler: cacheStatic transform', () => { expect(generate(root).code).toMatchSnapshot() }) + test('should hoist props for root with single element excluding comments', () => { + // deeply nested div to trigger stringification condition + const root = transformWithCache( + `
hello
`, + ) + expect(root.cached.length).toBe(1) + expect(root.hoists).toMatchObject([createObjectMatcher({ id: 'a' })]) + + expect((root.codegenNode as VNodeCall).children).toMatchObject([ + { + type: NodeTypes.COMMENT, + content: 'comment', + }, + { + type: NodeTypes.ELEMENT, + codegenNode: { + type: NodeTypes.VNODE_CALL, + tag: `"div"`, + props: { content: `_hoisted_1` }, + children: { type: NodeTypes.JS_CACHE_EXPRESSION }, + }, + }, + ]) + expect(generate(root).code).toMatchSnapshot() + }) + describe('prefixIdentifiers', () => { test('cache nested static tree with static interpolation', () => { const root = transformWithCache( diff --git a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts index 4766c2ca9d8..e0f44a064fb 100644 --- a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts @@ -988,5 +988,19 @@ describe('compiler: transform component slots', () => { expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot() }) + + test('named slot with v-if + v-else', () => { + const source = ` + + + + + ` + const { root } = parseWithSlots(source, { + whitespace: 'preserve', + }) + + expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot() + }) }) }) diff --git a/packages/compiler-core/__tests__/utils.spec.ts b/packages/compiler-core/__tests__/utils.spec.ts index 2d377a271ac..000b10e11bd 100644 --- a/packages/compiler-core/__tests__/utils.spec.ts +++ b/packages/compiler-core/__tests__/utils.spec.ts @@ -1,4 +1,9 @@ -import type { ExpressionNode, TransformContext } from '../src' +import { babelParse, walkIdentifiers } from '@vue/compiler-sfc' +import { + type ExpressionNode, + type TransformContext, + isReferencedIdentifier, +} from '../src' import { type Position, createSimpleExpression } from '../src/ast' import { advancePositionWithClone, @@ -115,3 +120,18 @@ test('toValidAssetId', () => { '_component_test_2797935797_1', ) }) + +describe('isReferencedIdentifier', () => { + test('identifiers in function parameters should not be inferred as references', () => { + expect.assertions(4) + const ast = babelParse(`(({ title }) => [])`) + walkIdentifiers( + ast.program.body[0], + (node, parent, parentStack, isReference) => { + expect(isReference).toBe(false) + expect(isReferencedIdentifier(node, parent, parentStack)).toBe(false) + }, + true, + ) + }) +}) diff --git a/packages/compiler-core/package.json b/packages/compiler-core/package.json index 7311f46935f..a59342aeb93 100644 --- a/packages/compiler-core/package.json +++ b/packages/compiler-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-core", - "version": "3.5.14", + "version": "3.5.18", "description": "@vue/compiler-core", "main": "index.js", "module": "dist/compiler-core.esm-bundler.js", diff --git a/packages/compiler-core/src/babelUtils.ts b/packages/compiler-core/src/babelUtils.ts index 52fabeea896..51614612b10 100644 --- a/packages/compiler-core/src/babelUtils.ts +++ b/packages/compiler-core/src/babelUtils.ts @@ -122,7 +122,7 @@ export function isReferencedIdentifier( return false } - if (isReferenced(id, parent)) { + if (isReferenced(id, parent, parentStack[parentStack.length - 2])) { return true } @@ -132,7 +132,8 @@ export function isReferencedIdentifier( case 'AssignmentExpression': case 'AssignmentPattern': return true - case 'ObjectPattern': + case 'ObjectProperty': + return parent.key !== id && isInDestructureAssignment(parent, parentStack) case 'ArrayPattern': return isInDestructureAssignment(parent, parentStack) } diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 70116cfb61a..6b4559fabb2 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -188,7 +188,9 @@ function createCodegenContext( name = content } } - addMapping(node.loc.start, name) + if (node.loc.source) { + addMapping(node.loc.start, name) + } } if (newlineIndex === NewlineType.Unknown) { // multiple newlines, full iteration @@ -225,7 +227,7 @@ function createCodegenContext( context.column = code.length - newlineIndex } } - if (node && node.loc !== locStub) { + if (node && node.loc !== locStub && node.loc.source) { addMapping(node.loc.end) } } diff --git a/packages/compiler-core/src/parser.ts b/packages/compiler-core/src/parser.ts index 7d1b01360c4..a6e25681d75 100644 --- a/packages/compiler-core/src/parser.ts +++ b/packages/compiler-core/src/parser.ts @@ -43,6 +43,7 @@ import { isCoreComponent, isSimpleIdentifier, isStaticArgOf, + isVPre, } from './utils' import { decodeHTML } from 'entities/lib/decode.js' import { @@ -246,7 +247,7 @@ const tokenizer = new Tokenizer(stack, { ondirarg(start, end) { if (start === end) return const arg = getSlice(start, end) - if (inVPre) { + if (inVPre && !isVPre(currentProp!)) { ;(currentProp as AttributeNode).name += arg setLocEnd((currentProp as AttributeNode).nameLoc, end) } else { @@ -262,7 +263,7 @@ const tokenizer = new Tokenizer(stack, { ondirmodifier(start, end) { const mod = getSlice(start, end) - if (inVPre) { + if (inVPre && !isVPre(currentProp!)) { ;(currentProp as AttributeNode).name += '.' + mod setLocEnd((currentProp as AttributeNode).nameLoc, end) } else if ((currentProp as DirectiveNode).name === 'slot') { @@ -647,7 +648,7 @@ function onCloseTag(el: ElementNode, end: number, isImplied = false) { // whitespace management if (!tokenizer.inRCDATA) { - el.children = condenseWhitespace(children, tag) + el.children = condenseWhitespace(children) } if (ns === Namespaces.HTML && currentOptions.isIgnoreNewlineTag(tag)) { @@ -832,10 +833,7 @@ function isUpperCase(c: number) { } const windowsNewlineRE = /\r\n/g -function condenseWhitespace( - nodes: TemplateChildNode[], - tag?: string, -): TemplateChildNode[] { +function condenseWhitespace(nodes: TemplateChildNode[]): TemplateChildNode[] { const shouldCondense = currentOptions.whitespace !== 'preserve' let removedWhitespace = false for (let i = 0; i < nodes.length; i++) { diff --git a/packages/compiler-core/src/tokenizer.ts b/packages/compiler-core/src/tokenizer.ts index 329e8b48181..b8a74790259 100644 --- a/packages/compiler-core/src/tokenizer.ts +++ b/packages/compiler-core/src/tokenizer.ts @@ -929,7 +929,7 @@ export default class Tokenizer { this.buffer = input while (this.index < this.buffer.length) { const c = this.buffer.charCodeAt(this.index) - if (c === CharCodes.NewLine) { + if (c === CharCodes.NewLine && this.state !== State.InEntity) { this.newlines.push(this.index) } switch (this.state) { diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index aeb96cc2b4a..9d8fd842935 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -37,7 +37,7 @@ import { helperNameMap, } from './runtimeHelpers' import { isVSlot } from './utils' -import { cacheStatic, isSingleElementRoot } from './transforms/cacheStatic' +import { cacheStatic, getSingleElementRoot } from './transforms/cacheStatic' import type { CompilerCompatOptions } from './compat/compatConfig' // There are two types of transforms: @@ -356,12 +356,12 @@ function createRootCodegen(root: RootNode, context: TransformContext) { const { helper } = context const { children } = root if (children.length === 1) { - const child = children[0] + const singleElementRootChild = getSingleElementRoot(root) // if the single child is an element, turn it into a block. - if (isSingleElementRoot(root, child) && child.codegenNode) { + if (singleElementRootChild && singleElementRootChild.codegenNode) { // single element root is never hoisted so codegenNode will never be // SimpleExpressionNode - const codegenNode = child.codegenNode + const codegenNode = singleElementRootChild.codegenNode if (codegenNode.type === NodeTypes.VNODE_CALL) { convertToBlock(codegenNode, context) } @@ -370,7 +370,7 @@ function createRootCodegen(root: RootNode, context: TransformContext) { // - single , IfNode, ForNode: already blocks. // - single text node: always patched. // root codegen falls through via genNode() - root.codegenNode = child + root.codegenNode = children[0] } } else if (children.length > 1) { // root has multiple nodes - return a fragment block. diff --git a/packages/compiler-core/src/transforms/cacheStatic.ts b/packages/compiler-core/src/transforms/cacheStatic.ts index e5d67380640..0f112e19cad 100644 --- a/packages/compiler-core/src/transforms/cacheStatic.ts +++ b/packages/compiler-core/src/transforms/cacheStatic.ts @@ -24,7 +24,13 @@ import { getVNodeHelper, } from '../ast' import type { TransformContext } from '../transform' -import { PatchFlags, isArray, isString, isSymbol } from '@vue/shared' +import { + PatchFlagNames, + PatchFlags, + isArray, + isString, + isSymbol, +} from '@vue/shared' import { findDir, isSlotOutlet } from '../utils' import { GUARD_REACTIVE_PROPS, @@ -41,20 +47,19 @@ export function cacheStatic(root: RootNode, context: TransformContext): void { context, // Root node is unfortunately non-hoistable due to potential parent // fallthrough attributes. - isSingleElementRoot(root, root.children[0]), + !!getSingleElementRoot(root), ) } -export function isSingleElementRoot( +export function getSingleElementRoot( root: RootNode, - child: TemplateChildNode, -): child is PlainElementNode | ComponentNode | TemplateNode { - const { children } = root - return ( - children.length === 1 && - child.type === NodeTypes.ELEMENT && - !isSlotOutlet(child) - ) +): PlainElementNode | ComponentNode | TemplateNode | null { + const children = root.children.filter(x => x.type !== NodeTypes.COMMENT) + return children.length === 1 && + children[0].type === NodeTypes.ELEMENT && + !isSlotOutlet(children[0]) + ? children[0] + : null } function walk( @@ -110,6 +115,15 @@ function walk( ? ConstantTypes.NOT_CONSTANT : getConstantType(child, context) if (constantType >= ConstantTypes.CAN_CACHE) { + if ( + child.codegenNode.type === NodeTypes.JS_CALL_EXPRESSION && + child.codegenNode.arguments.length > 0 + ) { + child.codegenNode.arguments.push( + PatchFlags.CACHED + + (__DEV__ ? ` /* ${PatchFlagNames[PatchFlags.CACHED]} */` : ``), + ) + } toCache.push(child) continue } diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index 76ca1d44353..1dca0c514c1 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -594,11 +594,9 @@ export function buildProps( hasDynamicKeys = true if (exp) { if (isVBind) { - // #10696 in case a v-bind object contains ref - pushRefVForMarker() - // have to merge early for compat build check - pushMergeArg() if (__COMPAT__) { + // have to merge early for compat build check + pushMergeArg() // 2.x v-bind object order compat if (__DEV__) { const hasOverridableKeys = mergeArgs.some(arg => { @@ -641,6 +639,9 @@ export function buildProps( } } + // #10696 in case a v-bind object contains ref + pushRefVForMarker() + pushMergeArg() mergeArgs.push(exp) } else { // v-on="obj" -> toHandlers(obj) diff --git a/packages/compiler-core/src/transforms/vBind.ts b/packages/compiler-core/src/transforms/vBind.ts index 1e5e371418b..c82706c10c7 100644 --- a/packages/compiler-core/src/transforms/vBind.ts +++ b/packages/compiler-core/src/transforms/vBind.ts @@ -65,7 +65,7 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => { arg.children.unshift(`(`) arg.children.push(`) || ""`) } else if (!arg.isStatic) { - arg.content = `${arg.content} || ""` + arg.content = arg.content ? `${arg.content} || ""` : `""` } // .sync is replaced by v-model:arg diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index 0dca0ba9ab4..a639caf2cae 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -263,7 +263,7 @@ export function processFor( dir: DirectiveNode, context: TransformContext, processCodegen?: (forNode: ForNode) => (() => void) | undefined, -) { +): (() => void) | undefined { if (!dir.exp) { context.onError( createCompilerError(ErrorCodes.X_V_FOR_NO_EXPRESSION, dir.loc), diff --git a/packages/compiler-core/src/transforms/vSlot.ts b/packages/compiler-core/src/transforms/vSlot.ts index 28625439a47..43296dcc9b6 100644 --- a/packages/compiler-core/src/transforms/vSlot.ts +++ b/packages/compiler-core/src/transforms/vSlot.ts @@ -222,7 +222,7 @@ export function buildSlots( let prev while (j--) { prev = children[j] - if (prev.type !== NodeTypes.COMMENT) { + if (prev.type !== NodeTypes.COMMENT && isNonWhitespaceContent(prev)) { break } } diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts index b49d70bb2fb..ab851ed6f69 100644 --- a/packages/compiler-core/src/utils.ts +++ b/packages/compiler-core/src/utils.ts @@ -63,7 +63,7 @@ export function isCoreComponent(tag: string): symbol | void { } } -const nonIdentifierRE = /^\d|[^\$\w\xA0-\uFFFF]/ +const nonIdentifierRE = /^$|^\d|[^\$\w\xA0-\uFFFF]/ export const isSimpleIdentifier = (name: string): boolean => !nonIdentifierRE.test(name) @@ -343,6 +343,10 @@ export function isText( return node.type === NodeTypes.INTERPOLATION || node.type === NodeTypes.TEXT } +export function isVPre(p: ElementNode['props'][0]): p is DirectiveNode { + return p.type === NodeTypes.DIRECTIVE && p.name === 'pre' +} + export function isVSlot(p: ElementNode['props'][0]): p is DirectiveNode { return p.type === NodeTypes.DIRECTIVE && p.name === 'slot' } diff --git a/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap b/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap index 2ed15ef5e62..5bc40d3fab5 100644 --- a/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap +++ b/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap @@ -6,7 +6,7 @@ exports[`stringify static html > eligible content (elements > 20) + non-eligible return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ _createStaticVNode("", 20), - _createElementVNode("div", { key: "1" }, "1", -1 /* HOISTED */), + _createElementVNode("div", { key: "1" }, "1", -1 /* CACHED */), _createStaticVNode("", 20) ]))) }" @@ -54,7 +54,7 @@ return function render(_ctx, _cache) { _createElementVNode("option", { value: "1" }), _createElementVNode("option", { value: "1" }), _createElementVNode("option", { value: "1" }) - ], -1 /* HOISTED */) + ], -1 /* CACHED */) ]))) }" `; @@ -70,11 +70,27 @@ return function render(_ctx, _cache) { _createElementVNode("option", { value: 1 }), _createElementVNode("option", { value: 1 }), _createElementVNode("option", { value: 1 }) - ], -1 /* HOISTED */) + ], -1 /* CACHED */) ]))) }" `; +exports[`stringify static html > should bail for comments 1`] = ` +"const { createCommentVNode: _createCommentVNode, createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue + +const _hoisted_1 = { class: "a" } + +return function render(_ctx, _cache) { + return (_openBlock(), _createElementBlock(_Fragment, null, [ + _createCommentVNode(" Comment 1 "), + _createElementVNode("div", _hoisted_1, [ + _createCommentVNode(" Comment 2 "), + _cache[0] || (_cache[0] = _createStaticVNode("", 5)) + ]) + ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)) +}" +`; + exports[`stringify static html > should bail on bindings that are cached but not stringifiable 1`] = ` "const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue @@ -87,7 +103,7 @@ return function render(_ctx, _cache) { _createElementVNode("span", { class: "foo" }, "foo"), _createElementVNode("span", { class: "foo" }, "foo"), _createElementVNode("img", { src: _imports_0_ }) - ], -1 /* HOISTED */) + ], -1 /* CACHED */) ]))) }" `; diff --git a/packages/compiler-dom/__tests__/transforms/stringifyStatic.spec.ts b/packages/compiler-dom/__tests__/transforms/stringifyStatic.spec.ts index 79e6fc9c6e8..f58e207d6cf 100644 --- a/packages/compiler-dom/__tests__/transforms/stringifyStatic.spec.ts +++ b/packages/compiler-dom/__tests__/transforms/stringifyStatic.spec.ts @@ -491,6 +491,16 @@ describe('stringify static html', () => { expect(code).toMatchSnapshot() }) + test('should bail for comments', () => { + const { code } = compileWithStringify( + `
${repeat( + ``, + StringifyThresholds.ELEMENT_WITH_BINDING_COUNT, + )}
`, + ) + expect(code).toMatchSnapshot() + }) + test('should bail for