Skip to content

Commit 468e729

Browse files
authored
Merge branch 'main' into GH-12964
2 parents 416c35f + 9fa787c commit 468e729

File tree

29 files changed

+957
-462
lines changed

29 files changed

+957
-462
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,8 @@ jobs:
104104
- name: Run prettier
105105
run: pnpm run format-check
106106

107+
- name: Run tsc
108+
run: pnpm run check
109+
107110
- name: Run type declaration tests
108111
run: pnpm run test-dts

package.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"version": "3.5.14",
4-
"packageManager": "pnpm@10.9.0",
4+
"packageManager": "pnpm@10.11.0",
55
"type": "module",
66
"scripts": {
77
"dev": "node scripts/dev.js",
@@ -71,21 +71,21 @@
7171
"@rollup/plugin-replace": "5.0.4",
7272
"@swc/core": "^1.11.24",
7373
"@types/hash-sum": "^1.0.2",
74-
"@types/node": "^22.14.1",
74+
"@types/node": "^22.15.21",
7575
"@types/semver": "^7.7.0",
7676
"@types/serve-handler": "^6.1.4",
77-
"@vitest/coverage-v8": "^3.1.3",
78-
"@vitest/eslint-plugin": "^1.1.44",
77+
"@vitest/coverage-v8": "^3.1.4",
78+
"@vitest/eslint-plugin": "^1.2.0",
7979
"@vue/consolidate": "1.0.0",
8080
"conventional-changelog-cli": "^5.0.0",
8181
"enquirer": "^2.4.1",
8282
"esbuild": "^0.25.4",
8383
"esbuild-plugin-polyfill-node": "^0.3.0",
84-
"eslint": "^9.25.1",
85-
"eslint-plugin-import-x": "^4.11.0",
84+
"eslint": "^9.27.0",
85+
"eslint-plugin-import-x": "^4.12.2",
8686
"estree-walker": "catalog:",
8787
"jsdom": "^26.1.0",
88-
"lint-staged": "^15.5.1",
88+
"lint-staged": "^15.5.2",
8989
"lodash": "^4.17.21",
9090
"magic-string": "^0.30.17",
9191
"markdown-table": "^3.0.4",
@@ -95,21 +95,21 @@
9595
"prettier": "^3.5.3",
9696
"pretty-bytes": "^6.1.1",
9797
"pug": "^3.0.3",
98-
"puppeteer": "~24.8.2",
98+
"puppeteer": "~24.9.0",
9999
"rimraf": "^6.0.1",
100-
"rollup": "^4.40.2",
100+
"rollup": "^4.41.0",
101101
"rollup-plugin-dts": "^6.2.1",
102102
"rollup-plugin-esbuild": "^6.2.1",
103103
"rollup-plugin-polyfill-node": "^0.13.0",
104-
"semver": "^7.7.1",
104+
"semver": "^7.7.2",
105105
"serve": "^14.2.4",
106106
"serve-handler": "^6.1.6",
107107
"simple-git-hooks": "^2.13.0",
108108
"todomvc-app-css": "^2.4.3",
109109
"tslib": "^2.8.1",
110110
"typescript": "~5.6.2",
111-
"typescript-eslint": "^8.31.1",
111+
"typescript-eslint": "^8.32.1",
112112
"vite": "catalog:",
113-
"vitest": "^3.1.3"
113+
"vitest": "^3.1.4"
114114
}
115115
}

packages-private/dts-test/componentInstance.test-d.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,18 @@ describe('Generic component', () => {
137137
expectType<string | number>(comp.msg)
138138
expectType<Array<string | number>>(comp.list)
139139
})
140+
141+
// #12751
142+
{
143+
const Comp = defineComponent({
144+
__typeEmits: {} as {
145+
'update:visible': [value?: boolean]
146+
},
147+
})
148+
const comp: ComponentInstance<typeof Comp> = {} as any
149+
150+
expectType<((value?: boolean) => any) | undefined>(comp['onUpdate:visible'])
151+
expectType<{ 'onUpdate:visible'?: (value?: boolean) => any }>(comp['$props'])
152+
// @ts-expect-error
153+
comp['$props']['$props']
154+
}

packages-private/dts-test/defineComponent.test-d.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import { type IsAny, type IsUnion, describe, expectType } from './utils'
2020
describe('with object props', () => {
2121
interface ExpectedProps {
2222
a?: number | undefined
23+
aa: number
24+
aaa: number | null
25+
aaaa: number | undefined
2326
b: string
2427
e?: Function
2528
h: boolean
@@ -53,6 +56,19 @@ describe('with object props', () => {
5356

5457
const props = {
5558
a: Number,
59+
aa: {
60+
type: Number as PropType<number | undefined>,
61+
default: 1,
62+
},
63+
aaa: {
64+
type: Number as PropType<number | null>,
65+
default: 1,
66+
},
67+
aaaa: {
68+
type: Number as PropType<number | undefined>,
69+
// `as const` prevents widening to `boolean` (keeps literal `true` type)
70+
required: true as const,
71+
},
5672
// required should make property non-void
5773
b: {
5874
type: String,
@@ -146,6 +162,13 @@ describe('with object props', () => {
146162
setup(props) {
147163
// type assertion. See https://github.com/SamVerschueren/tsd
148164
expectType<ExpectedProps['a']>(props.a)
165+
expectType<ExpectedProps['aa']>(props.aa)
166+
expectType<ExpectedProps['aaa']>(props.aaa)
167+
168+
// @ts-expect-error should included `undefined`
169+
expectType<number>(props.aaaa)
170+
expectType<ExpectedProps['aaaa']>(props.aaaa)
171+
149172
expectType<ExpectedProps['b']>(props.b)
150173
expectType<ExpectedProps['e']>(props.e)
151174
expectType<ExpectedProps['h']>(props.h)
@@ -198,6 +221,8 @@ describe('with object props', () => {
198221
render() {
199222
const props = this.$props
200223
expectType<ExpectedProps['a']>(props.a)
224+
expectType<ExpectedProps['aa']>(props.aa)
225+
expectType<ExpectedProps['aaa']>(props.aaa)
201226
expectType<ExpectedProps['b']>(props.b)
202227
expectType<ExpectedProps['e']>(props.e)
203228
expectType<ExpectedProps['h']>(props.h)
@@ -225,6 +250,8 @@ describe('with object props', () => {
225250

226251
// should also expose declared props on `this`
227252
expectType<ExpectedProps['a']>(this.a)
253+
expectType<ExpectedProps['aa']>(this.aa)
254+
expectType<ExpectedProps['aaa']>(this.aaa)
228255
expectType<ExpectedProps['b']>(this.b)
229256
expectType<ExpectedProps['e']>(this.e)
230257
expectType<ExpectedProps['h']>(this.h)
@@ -269,6 +296,7 @@ describe('with object props', () => {
269296
expectType<JSX.Element>(
270297
<MyComponent
271298
a={1}
299+
aaaa={1}
272300
b="b"
273301
bb="bb"
274302
e={() => {}}
@@ -295,6 +323,7 @@ describe('with object props', () => {
295323

296324
expectType<Component>(
297325
<MyComponent
326+
aaaa={1}
298327
b="b"
299328
dd={{ n: 1 }}
300329
ddd={['ddd']}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -594,11 +594,9 @@ export function buildProps(
594594
hasDynamicKeys = true
595595
if (exp) {
596596
if (isVBind) {
597-
// #10696 in case a v-bind object contains ref
598-
pushRefVForMarker()
599-
// have to merge early for compat build check
600-
pushMergeArg()
601597
if (__COMPAT__) {
598+
// have to merge early for compat build check
599+
pushMergeArg()
602600
// 2.x v-bind object order compat
603601
if (__DEV__) {
604602
const hasOverridableKeys = mergeArgs.some(arg => {
@@ -641,6 +639,9 @@ export function buildProps(
641639
}
642640
}
643641

642+
// #10696 in case a v-bind object contains ref
643+
pushRefVForMarker()
644+
pushMergeArg()
644645
mergeArgs.push(exp)
645646
} else {
646647
// v-on="obj" -> toHandlers(obj)

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

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { BindingTypes } from '@vue/compiler-core'
2-
import { assertCode, compileSFCScript as compile, mockId } from './utils'
2+
import {
3+
assertCode,
4+
compileSFCScript as compile,
5+
getPositionInCode,
6+
mockId,
7+
} from './utils'
8+
import { type RawSourceMap, SourceMapConsumer } from 'source-map-js'
39

410
describe('SFC compile <script setup>', () => {
511
test('should compile JS syntax', () => {
@@ -690,6 +696,27 @@ describe('SFC compile <script setup>', () => {
690696
expect(content).toMatch(`new (_unref(Foo)).Bar()`)
691697
assertCode(content)
692698
})
699+
700+
// #12682
701+
test('source map', () => {
702+
const source = `
703+
<script setup>
704+
const count = ref(0)
705+
</script>
706+
<template>
707+
<button @click="throw new Error(\`msg\`);"></button>
708+
</template>
709+
`
710+
const { content, map } = compile(source, { inlineTemplate: true })
711+
expect(map).not.toBeUndefined()
712+
const consumer = new SourceMapConsumer(map as RawSourceMap)
713+
expect(
714+
consumer.originalPositionFor(getPositionInCode(content, 'count')),
715+
).toMatchObject(getPositionInCode(source, `count`))
716+
expect(
717+
consumer.originalPositionFor(getPositionInCode(content, 'Error')),
718+
).toMatchObject(getPositionInCode(source, `Error`))
719+
})
693720
})
694721

695722
describe('with TypeScript', () => {
@@ -980,7 +1007,7 @@ describe('SFC compile <script setup>', () => {
9801007
expect(() =>
9811008
compile(`<script setup>
9821009
let bar = 1
983-
defineModel({
1010+
const model = defineModel({
9841011
default: () => bar
9851012
})
9861013
</script>`),
@@ -990,7 +1017,7 @@ describe('SFC compile <script setup>', () => {
9901017
expect(() =>
9911018
compile(`<script setup>
9921019
const bar = 1
993-
defineModel({
1020+
const model = defineModel({
9941021
default: () => bar
9951022
})
9961023
</script>`),
@@ -1000,7 +1027,7 @@ describe('SFC compile <script setup>', () => {
10001027
expect(() =>
10011028
compile(`<script setup>
10021029
let bar = 1
1003-
defineModel({
1030+
const model = defineModel({
10041031
get: () => bar,
10051032
set: () => bar
10061033
})

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,4 +269,16 @@ describe('defineModel()', () => {
269269
modelValue: BindingTypes.SETUP_REF,
270270
})
271271
})
272+
273+
test('error when defineModel is not assigned to a variable', () => {
274+
expect(() =>
275+
compile(`
276+
<script setup>
277+
defineModel()
278+
</script>
279+
`),
280+
).toThrow(
281+
'defineModel() must be assigned to a variable. For example: const model = defineModel()',
282+
)
283+
})
272284
})

packages/compiler-sfc/__tests__/compileStyle.spec.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,31 @@ describe('SFC style preprocessors', () => {
493493
}"
494494
`)
495495
expect(compileScoped(`.foo * { color: red; }`)).toMatchInlineSnapshot(`
496-
".foo[data-v-test] * { color: red;
496+
".foo[data-v-test] [data-v-test] { color: red;
497+
}"
498+
`)
499+
expect(compileScoped(`.foo :active { color: red; }`))
500+
.toMatchInlineSnapshot(`
501+
".foo[data-v-test] :active { color: red;
502+
}"
503+
`)
504+
expect(compileScoped(`.foo *:active { color: red; }`))
505+
.toMatchInlineSnapshot(`
506+
".foo[data-v-test] [data-v-test]:active { color: red;
507+
}"
508+
`)
509+
expect(compileScoped(`.foo * .bar { color: red; }`)).toMatchInlineSnapshot(`
510+
".foo * .bar[data-v-test] { color: red;
511+
}"
512+
`)
513+
expect(compileScoped(`:last-child * { color: red; }`))
514+
.toMatchInlineSnapshot(`
515+
"[data-v-test]:last-child [data-v-test] { color: red;
516+
}"
517+
`)
518+
expect(compileScoped(`:last-child *:active { color: red; }`))
519+
.toMatchInlineSnapshot(`
520+
"[data-v-test]:last-child [data-v-test]:active { color: red;
497521
}"
498522
`)
499523
})

packages/compiler-sfc/__tests__/compileTemplate.spec.ts

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
} from '../src/compileTemplate'
77
import { type SFCTemplateBlock, parse } from '../src/parse'
88
import { compileScript } from '../src'
9+
import { getPositionInCode } from './utils'
910

1011
function compile(opts: Omit<SFCTemplateCompileOptions, 'id'>) {
1112
return compileTemplate({
@@ -511,36 +512,3 @@ test('non-identifier expression in legacy filter syntax', () => {
511512
babelParse(compilationResult.code, { sourceType: 'module' })
512513
}).not.toThrow()
513514
})
514-
515-
interface Pos {
516-
line: number
517-
column: number
518-
name?: string
519-
}
520-
521-
function getPositionInCode(
522-
code: string,
523-
token: string,
524-
expectName: string | boolean = false,
525-
): Pos {
526-
const generatedOffset = code.indexOf(token)
527-
let line = 1
528-
let lastNewLinePos = -1
529-
for (let i = 0; i < generatedOffset; i++) {
530-
if (code.charCodeAt(i) === 10 /* newline char code */) {
531-
line++
532-
lastNewLinePos = i
533-
}
534-
}
535-
const res: Pos = {
536-
line,
537-
column:
538-
lastNewLinePos === -1
539-
? generatedOffset
540-
: generatedOffset - lastNewLinePos - 1,
541-
}
542-
if (expectName) {
543-
res.name = typeof expectName === 'string' ? expectName : token
544-
}
545-
return res
546-
}

packages/compiler-sfc/__tests__/utils.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,36 @@ export function assertCode(code: string): void {
4040
}
4141
expect(code).toMatchSnapshot()
4242
}
43+
44+
interface Pos {
45+
line: number
46+
column: number
47+
name?: string
48+
}
49+
50+
export function getPositionInCode(
51+
code: string,
52+
token: string,
53+
expectName: string | boolean = false,
54+
): Pos {
55+
const generatedOffset = code.indexOf(token)
56+
let line = 1
57+
let lastNewLinePos = -1
58+
for (let i = 0; i < generatedOffset; i++) {
59+
if (code.charCodeAt(i) === 10 /* newline char code */) {
60+
line++
61+
lastNewLinePos = i
62+
}
63+
}
64+
const res: Pos = {
65+
line,
66+
column:
67+
lastNewLinePos === -1
68+
? generatedOffset
69+
: generatedOffset - lastNewLinePos - 1,
70+
}
71+
if (expectName) {
72+
res.name = typeof expectName === 'string' ? expectName : token
73+
}
74+
return res
75+
}

0 commit comments

Comments
 (0)