Skip to content

Commit 73317ae

Browse files
committed
chore: Merge branch 'main' into minor
1 parent b883488 commit 73317ae

40 files changed

Lines changed: 1723 additions & 656 deletions

File tree

.github/renovate.json5

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
// ESM only
5353
'estree-walker',
5454

55+
// v8 is ESM-only and breaks compiler-core's CJS path
56+
'entities',
57+
5558
// pinned
5659
// https://github.com/vuejs/core/issues/10300#issuecomment-1940855364
5760
'lru-cache',

.github/workflows/size-report.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
cache: true
3232

3333
- name: Download Size Data
34-
uses: dawidd6/action-download-artifact@v14
34+
uses: dawidd6/action-download-artifact@v19
3535
with:
3636
name: size-data
3737
run_id: ${{ github.event.workflow_run.id }}
@@ -50,7 +50,7 @@ jobs:
5050
path: temp/size/base.txt
5151

5252
- name: Download Previous Size Data
53-
uses: dawidd6/action-download-artifact@v14
53+
uses: dawidd6/action-download-artifact@v19
5454
with:
5555
branch: ${{ steps.pr-base.outputs.content }}
5656
workflow: size-data.yml

changelogs/CHANGELOG-3.5.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
## [3.5.31](https://github.com/vuejs/core/compare/v3.5.30...v3.5.31) (2026-03-25)
2+
3+
4+
### Bug Fixes
5+
6+
* **compiler-sfc:** allow Node.js subpath imports patterns in asset urls ([#13045](https://github.com/vuejs/core/issues/13045)) ([95c3356](https://github.com/vuejs/core/commit/95c33560c9af369d44a7670d0c3b93fb62323be2)), closes [#9919](https://github.com/vuejs/core/issues/9919)
7+
* **compiler-sfc:** support template literal as defineModel name ([#14622](https://github.com/vuejs/core/issues/14622)) ([bd7eef0](https://github.com/vuejs/core/commit/bd7eef0161d69bccd48ac303bc6a56ba8d718e2d)), closes [#14621](https://github.com/vuejs/core/issues/14621)
8+
* **reactivity:** normalize toRef property keys before dep lookup + improve types ([#14625](https://github.com/vuejs/core/issues/14625)) ([1bb28d0](https://github.com/vuejs/core/commit/1bb28d011b31bc75c80d2860bc6484cceec1ff20)), closes [#12427](https://github.com/vuejs/core/issues/12427) [#12431](https://github.com/vuejs/core/issues/12431)
9+
* **runtime-core:** invalidate detached v-for memo vnodes after unmount ([#14624](https://github.com/vuejs/core/issues/14624)) ([560def4](https://github.com/vuejs/core/commit/560def426fc38c1710fda7ddd1ac634d67897960)), closes [#12708](https://github.com/vuejs/core/issues/12708) [#12710](https://github.com/vuejs/core/issues/12710)
10+
* **runtime-core:** preserve nullish event handlers in mergeProps ([#14550](https://github.com/vuejs/core/issues/14550)) ([5725222](https://github.com/vuejs/core/commit/5725222a6bc5d1dd032318349ce0c540b1d63a49))
11+
* **runtime-core:** prevent merging model listener when value is null or undefined ([#14629](https://github.com/vuejs/core/issues/14629)) ([b39e032](https://github.com/vuejs/core/commit/b39e0329f67354702f4e417e55c15c61d2439657))
12+
* **runtime-dom:** defer teleport mount/update until suspense resolves ([#8619](https://github.com/vuejs/core/issues/8619)) ([88ed045](https://github.com/vuejs/core/commit/88ed04501555b9257df8d7ad86d844c2c2136e50)), closes [#8603](https://github.com/vuejs/core/issues/8603)
13+
* **runtime-dom:** handle activeElement check in Shadow DOM for v-model ([#14196](https://github.com/vuejs/core/issues/14196)) ([959ded2](https://github.com/vuejs/core/commit/959ded22ab7ea1453f607e0964e1fb6748ece6c7))
14+
* **server-renderer:** cleanup component effect scopes after SSR render ([#14548](https://github.com/vuejs/core/issues/14548)) ([862f11e](https://github.com/vuejs/core/commit/862f11ee017d51cb9573a8c0642055b3b17cace8))
15+
* **suspense:** avoid unmount activeBranch twice if wrapped in transition ([#9392](https://github.com/vuejs/core/issues/9392)) ([908c6ad](https://github.com/vuejs/core/commit/908c6ad05e1c76ae690d2e50f3bd28278af07e22)), closes [#7966](https://github.com/vuejs/core/issues/7966)
16+
* **suspense:** update suspense vnode's el during branch self-update ([#12922](https://github.com/vuejs/core/issues/12922)) ([a2c1700](https://github.com/vuejs/core/commit/a2c17004c84e5ce3c4e82e35b806ba381144eed3)), closes [#12920](https://github.com/vuejs/core/issues/12920)
17+
* **transition:** skip enter guard while hmr updating ([#14611](https://github.com/vuejs/core/issues/14611)) ([be0a2f1](https://github.com/vuejs/core/commit/be0a2f1a7fc3d81d05638798cc628848cfa62cef)), closes [#14608](https://github.com/vuejs/core/issues/14608)
18+
* **types:** prevent shallowReactive marker from leaking into value unions ([#14493](https://github.com/vuejs/core/issues/14493)) ([3b561db](https://github.com/vuejs/core/commit/3b561db4ab42d06166b002f13c0e97cb2bd4a061)), closes [#14490](https://github.com/vuejs/core/issues/14490)
19+
20+
21+
122
## [3.5.30](https://github.com/vuejs/core/compare/v3.5.29...v3.5.30) (2026-03-09)
223

324

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,14 @@
5656
"@types/node": "^24.12.0",
5757
"@types/semver": "^7.7.1",
5858
"@types/serve-handler": "^6.1.4",
59-
"@vitest/coverage-v8": "^4.0.18",
60-
"@vitest/ui": "^4.0.18",
59+
"@vitest/coverage-v8": "^4.1.1",
60+
"@vitest/ui": "^4.1.1",
6161
"@vue/consolidate": "1.0.0",
6262
"conventional-changelog-cli": "^5.0.0",
6363
"enquirer": "^2.4.1",
6464
"estree-walker": "catalog:",
6565
"fast-glob": "^3.3.2",
66-
"jsdom": "^27.3.0",
66+
"jsdom": "^29.0.1",
6767
"lodash": "^4.17.23",
6868
"magic-string": "^0.30.21",
6969
"markdown-table": "^3.0.4",
@@ -75,14 +75,14 @@
7575
"picocolors": "^1.1.1",
7676
"playwright": "^1.56.1",
7777
"pretty-bytes": "^7.1.0",
78-
"pug": "^3.0.3",
79-
"puppeteer": "~24.38.0",
78+
"pug": "^3.0.4",
79+
"puppeteer": "~24.40.0",
8080
"rimraf": "^6.1.3",
8181
"rolldown": "^1.0.0-rc.6",
8282
"rolldown-plugin-dts": "^0.22.3",
83-
"semver": "^7.7.3",
84-
"serve": "^14.2.5",
85-
"serve-handler": "^6.1.6",
83+
"semver": "^7.7.4",
84+
"serve": "^14.2.6",
85+
"serve-handler": "^6.1.7",
8686
"todomvc-app-css": "^2.4.3",
8787
"tslib": "^2.8.1",
8888
"typescript": "~5.6.2",

packages-private/dts-test/reactivity.test-d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
reactive,
55
readonly,
66
ref,
7+
shallowReactive,
78
shallowReadonly,
89
} from 'vue'
910
import { describe, expectType } from './utils'
@@ -130,3 +131,12 @@ describe('should not error when assignment', () => {
130131
record2 = arr
131132
expectType<string>(record2[0])
132133
})
134+
135+
describe('shallowReactive marker should not leak into value unions', () => {
136+
const state = shallowReactive({
137+
a: { title: 'A' },
138+
b: { title: 'B' },
139+
})
140+
const value = {} as (typeof state)[keyof typeof state]
141+
expectType<string>(value.title)
142+
})

packages-private/dts-test/ref.test-d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,14 @@ expectType<{ name: string } | null>(p2.union)
338338
// Should not distribute Refs over union
339339
expectType<Ref<number | string>>(toRef(obj, 'c'))
340340

341+
const array = reactive(['a', 'b'])
342+
expectType<Ref<string>>(toRef(array, '1'))
343+
expectType<Ref<string>>(toRef(array, '1', 'fallback'))
344+
345+
const tuple: [string, number] = ['a', 1]
346+
expectType<Ref<string>>(toRef(tuple, '0'))
347+
expectType<Ref<number>>(toRef(tuple, '1'))
348+
341349
expectType<Ref<number>>(toRef(() => 123))
342350
expectType<Ref<number | string>>(toRef(() => obj.c))
343351

packages/compiler-core/__tests__/transforms/__snapshots__/vMemo.spec.ts.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export function render(_ctx, _cache) {
77
return (_openBlock(), _createElementBlock("div", null, [
88
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.tableData, (data, __, ___, _cached) => {
99
const _memo = (_ctx.getLetter(data))
10-
if (_cached && _cached.key === _ctx.getId(data) && _isMemoSame(_cached, _memo)) return _cached
10+
if (_cached && _cached.el && _cached.key === _ctx.getId(data) && _isMemoSame(_cached, _memo)) return _cached
1111
const _item = (_openBlock(), _createElementBlock("span", {
1212
key: _ctx.getId(data)
1313
}))
@@ -55,7 +55,7 @@ export function render(_ctx, _cache) {
5555
return (_openBlock(), _createElementBlock("div", null, [
5656
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, ({ x, y }, __, ___, _cached) => {
5757
const _memo = ([x, y === _ctx.z])
58-
if (_cached && _cached.key === x && _isMemoSame(_cached, _memo)) return _cached
58+
if (_cached && _cached.el && _cached.key === x && _isMemoSame(_cached, _memo)) return _cached
5959
const _item = (_openBlock(), _createElementBlock("span", { key: x }, "foobar"))
6060
_item.memo = _memo
6161
return _item
@@ -71,7 +71,7 @@ export function render(_ctx, _cache) {
7171
return (_openBlock(), _createElementBlock("div", null, [
7272
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, ({ x, y }, __, ___, _cached) => {
7373
const _memo = ([x, y === _ctx.z])
74-
if (_cached && _cached.key === x && _isMemoSame(_cached, _memo)) return _cached
74+
if (_cached && _cached.el && _cached.key === x && _isMemoSame(_cached, _memo)) return _cached
7575
const _item = (_openBlock(), _createElementBlock("div", { key: x }, [
7676
_createElementVNode("span", null, "foobar")
7777
]))

packages/compiler-core/src/transforms/vFor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ export const transformFor: NodeTransform = createStructuralDirectiveTransform(
221221
loop.body = createBlockStatement([
222222
createCompoundExpression([`const _memo = (`, memo.exp!, `)`]),
223223
createCompoundExpression([
224-
`if (_cached`,
224+
`if (_cached && _cached.el`,
225225
...(keyExp ? [` && _cached.key === `, keyExp] : []),
226226
` && ${context.helperString(
227227
IS_MEMO_SAME,

packages/compiler-sfc/__tests__/compileScript/__snapshots__/defineModel.spec.ts.snap

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,50 @@ return { count }
166166
}"
167167
`;
168168

169+
exports[`defineModel() > w/ template literal name 1`] = `
170+
"import { useModel as _useModel } from 'vue'
171+
172+
export default {
173+
props: {
174+
"x": { default: 100 },
175+
"xModifiers": {},
176+
"y": { default: 200 },
177+
"yModifiers": {},
178+
},
179+
emits: ["update:x", "update:y"],
180+
setup(__props, { expose: __expose }) {
181+
__expose();
182+
183+
const x = _useModel(__props, \`x\`)
184+
const y = _useModel(__props, \`y\`)
185+
186+
return { x, y }
187+
}
188+
189+
}"
190+
`;
191+
192+
exports[`defineModel() > w/ template literal name with expressions falls back to modelValue 1`] = `
193+
"import { useModel as _useModel } from 'vue'
194+
const name = 'x'
195+
196+
export default {
197+
props: {
198+
"modelValue": \`\${name}\`,
199+
"modelModifiers": {},
200+
},
201+
emits: ["update:modelValue"],
202+
setup(__props, { expose: __expose }) {
203+
__expose();
204+
205+
const m = _useModel(__props, "modelValue", \`\${name}\`)
206+
207+
return { name, m }
208+
}
209+
210+
}"
211+
`;
212+
169213
exports[`defineModel() > w/ types, basic usage 1`] = `
170214
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
171215

packages/compiler-sfc/__tests__/compileScript/defineModel.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,43 @@ describe('defineModel()', () => {
3636
})
3737
})
3838

39+
test('w/ template literal name', () => {
40+
const { content, bindings } = compile(
41+
`
42+
<script setup>
43+
const x = defineModel(\`x\`, { default: 100 })
44+
const y = defineModel(\`y\`, { default: 200 })
45+
</script>
46+
`,
47+
)
48+
assertCode(content)
49+
expect(content).toMatch('"x": { default: 100 },')
50+
expect(content).toMatch('"y": { default: 200 },')
51+
expect(content).toMatch('emits: ["update:x", "update:y"],')
52+
expect(content).toMatch('const x = _useModel(__props, `x`)')
53+
expect(content).toMatch('const y = _useModel(__props, `y`)')
54+
expect(content).not.toMatch('defineModel')
55+
56+
expect(bindings).toStrictEqual({
57+
x: BindingTypes.SETUP_REF,
58+
y: BindingTypes.SETUP_REF,
59+
})
60+
})
61+
62+
test('w/ template literal name with expressions falls back to modelValue', () => {
63+
const { content } = compile(
64+
`
65+
<script setup>
66+
const name = 'x'
67+
const m = defineModel(\`\${name}\`)
68+
</script>
69+
`,
70+
)
71+
assertCode(content)
72+
expect(content).toMatch('"modelValue":')
73+
expect(content).toMatch('_useModel(__props, "modelValue",')
74+
})
75+
3976
test('w/ defineProps and defineEmits', () => {
4077
const { content, bindings } = compile(
4178
`

0 commit comments

Comments
 (0)