mirror of https://github.com/vuejs/core.git
fix(compiler-sfc): fix co-usage of defineModel transform options and props destructure
close #9972
This commit is contained in:
parent
ae60a91cc2
commit
b20350ded5
|
@ -17,8 +17,8 @@ export default {
|
||||||
__expose();
|
__expose();
|
||||||
|
|
||||||
const modelValue = _useModel(__props, "modelValue")
|
const modelValue = _useModel(__props, "modelValue")
|
||||||
const c = _useModel(__props, "count")
|
const c = _useModel(__props, 'count')
|
||||||
const toString = _useModel(__props, "toString")
|
const toString = _useModel(__props, 'toString')
|
||||||
|
|
||||||
return { modelValue, c, toString }
|
return { modelValue, c, toString }
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,10 @@ export default /*#__PURE__*/_defineComponent({
|
||||||
setup(__props, { expose: __expose }) {
|
setup(__props, { expose: __expose }) {
|
||||||
__expose();
|
__expose();
|
||||||
|
|
||||||
const modelValue = _useModel(__props, "modelValue", { get(v) { return v - 1 }, set: (v) => { return v + 1 }, })
|
const modelValue = _useModel(__props, "modelValue", {
|
||||||
|
get(v) { return v - 1 },
|
||||||
|
set: (v) => { return v + 1 },
|
||||||
|
})
|
||||||
|
|
||||||
return { modelValue }
|
return { modelValue }
|
||||||
}
|
}
|
||||||
|
@ -63,7 +66,36 @@ export default /*#__PURE__*/_defineComponent({
|
||||||
setup(__props, { expose: __expose }) {
|
setup(__props, { expose: __expose }) {
|
||||||
__expose();
|
__expose();
|
||||||
|
|
||||||
const modelValue = _useModel(__props, "modelValue", { get(v) { return v - 1 }, set: (v) => { return v + 1 }, })
|
const modelValue = _useModel(__props, "modelValue", {
|
||||||
|
get(v) { return v - 1 },
|
||||||
|
set: (v) => { return v + 1 },
|
||||||
|
})
|
||||||
|
|
||||||
|
return { modelValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`defineModel() > usage w/ props destructure 1`] = `
|
||||||
|
"import { useModel as _useModel, mergeModels as _mergeModels, defineComponent as _defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default /*#__PURE__*/_defineComponent({
|
||||||
|
props: /*#__PURE__*/_mergeModels({
|
||||||
|
x: { type: Number, required: true }
|
||||||
|
}, {
|
||||||
|
"modelValue": {
|
||||||
|
},
|
||||||
|
"modelModifiers": {},
|
||||||
|
}),
|
||||||
|
emits: ["update:modelValue"],
|
||||||
|
setup(__props: any, { expose: __expose }) {
|
||||||
|
__expose();
|
||||||
|
|
||||||
|
|
||||||
|
const modelValue = _useModel(__props, "modelValue", {
|
||||||
|
set: (v) => { return v + __props.x }
|
||||||
|
})
|
||||||
|
|
||||||
return { modelValue }
|
return { modelValue }
|
||||||
}
|
}
|
||||||
|
@ -84,7 +116,7 @@ export default {
|
||||||
__expose();
|
__expose();
|
||||||
|
|
||||||
|
|
||||||
const count = _useModel(__props, "count")
|
const count = _useModel(__props, 'count')
|
||||||
|
|
||||||
return { count }
|
return { count }
|
||||||
}
|
}
|
||||||
|
@ -132,10 +164,10 @@ export default /*#__PURE__*/_defineComponent({
|
||||||
setup(__props, { expose: __expose }) {
|
setup(__props, { expose: __expose }) {
|
||||||
__expose();
|
__expose();
|
||||||
|
|
||||||
const modelValue = _useModel(__props, "modelValue")
|
const modelValue = _useModel<boolean | string>(__props, "modelValue")
|
||||||
const count = _useModel(__props, "count")
|
const count = _useModel<number>(__props, 'count')
|
||||||
const disabled = _useModel(__props, "disabled")
|
const disabled = _useModel<number>(__props, 'disabled')
|
||||||
const any = _useModel(__props, "any")
|
const any = _useModel<any | boolean>(__props, 'any')
|
||||||
|
|
||||||
return { modelValue, count, disabled, any }
|
return { modelValue, count, disabled, any }
|
||||||
}
|
}
|
||||||
|
@ -163,11 +195,11 @@ export default /*#__PURE__*/_defineComponent({
|
||||||
setup(__props, { expose: __expose }) {
|
setup(__props, { expose: __expose }) {
|
||||||
__expose();
|
__expose();
|
||||||
|
|
||||||
const modelValue = _useModel(__props, "modelValue")
|
const modelValue = _useModel<boolean>(__props, "modelValue")
|
||||||
const fn = _useModel(__props, "fn")
|
const fn = _useModel<() => void>(__props, 'fn')
|
||||||
const fnWithDefault = _useModel(__props, "fnWithDefault")
|
const fnWithDefault = _useModel<() => void>(__props, 'fnWithDefault')
|
||||||
const str = _useModel(__props, "str")
|
const str = _useModel<string>(__props, 'str')
|
||||||
const optional = _useModel(__props, "optional")
|
const optional = _useModel<string>(__props, 'optional')
|
||||||
|
|
||||||
return { modelValue, fn, fnWithDefault, str, optional }
|
return { modelValue, fn, fnWithDefault, str, optional }
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,8 @@ describe('defineModel()', () => {
|
||||||
expect(content).toMatch(
|
expect(content).toMatch(
|
||||||
`const modelValue = _useModel(__props, "modelValue")`,
|
`const modelValue = _useModel(__props, "modelValue")`,
|
||||||
)
|
)
|
||||||
expect(content).toMatch(`const c = _useModel(__props, "count")`)
|
expect(content).toMatch(`const c = _useModel(__props, 'count')`)
|
||||||
|
expect(content).toMatch(`const toString = _useModel(__props, 'toString')`)
|
||||||
expect(content).toMatch(`return { modelValue, c, toString }`)
|
expect(content).toMatch(`return { modelValue, c, toString }`)
|
||||||
expect(content).not.toMatch('defineModel')
|
expect(content).not.toMatch('defineModel')
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ describe('defineModel()', () => {
|
||||||
"count": {},
|
"count": {},
|
||||||
"countModifiers": {},
|
"countModifiers": {},
|
||||||
})`)
|
})`)
|
||||||
expect(content).toMatch(`const count = _useModel(__props, "count")`)
|
expect(content).toMatch(`const count = _useModel(__props, 'count')`)
|
||||||
expect(content).not.toMatch('defineModel')
|
expect(content).not.toMatch('defineModel')
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
foo: BindingTypes.PROPS,
|
foo: BindingTypes.PROPS,
|
||||||
|
@ -104,11 +105,15 @@ describe('defineModel()', () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(content).toMatch(
|
expect(content).toMatch(
|
||||||
`const modelValue = _useModel(__props, "modelValue")`,
|
`const modelValue = _useModel<boolean | string>(__props, "modelValue")`,
|
||||||
|
)
|
||||||
|
expect(content).toMatch(`const count = _useModel<number>(__props, 'count')`)
|
||||||
|
expect(content).toMatch(
|
||||||
|
`const disabled = _useModel<number>(__props, 'disabled')`,
|
||||||
|
)
|
||||||
|
expect(content).toMatch(
|
||||||
|
`const any = _useModel<any | boolean>(__props, 'any')`,
|
||||||
)
|
)
|
||||||
expect(content).toMatch(`const count = _useModel(__props, "count")`)
|
|
||||||
expect(content).toMatch(`const disabled = _useModel(__props, "disabled")`)
|
|
||||||
expect(content).toMatch(`const any = _useModel(__props, "any")`)
|
|
||||||
|
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
modelValue: BindingTypes.SETUP_REF,
|
modelValue: BindingTypes.SETUP_REF,
|
||||||
|
@ -143,10 +148,10 @@ describe('defineModel()', () => {
|
||||||
'emits: ["update:modelValue", "update:fn", "update:fnWithDefault", "update:str", "update:optional"]',
|
'emits: ["update:modelValue", "update:fn", "update:fnWithDefault", "update:str", "update:optional"]',
|
||||||
)
|
)
|
||||||
expect(content).toMatch(
|
expect(content).toMatch(
|
||||||
`const modelValue = _useModel(__props, "modelValue")`,
|
`const modelValue = _useModel<boolean>(__props, "modelValue")`,
|
||||||
)
|
)
|
||||||
expect(content).toMatch(`const fn = _useModel(__props, "fn")`)
|
expect(content).toMatch(`const fn = _useModel<() => void>(__props, 'fn')`)
|
||||||
expect(content).toMatch(`const str = _useModel(__props, "str")`)
|
expect(content).toMatch(`const str = _useModel<string>(__props, 'str')`)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
modelValue: BindingTypes.SETUP_REF,
|
modelValue: BindingTypes.SETUP_REF,
|
||||||
fn: BindingTypes.SETUP_REF,
|
fn: BindingTypes.SETUP_REF,
|
||||||
|
@ -171,7 +176,10 @@ describe('defineModel()', () => {
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
expect(content).toMatch(/"modelValue": {\s+required: true,?\s+}/m)
|
expect(content).toMatch(/"modelValue": {\s+required: true,?\s+}/m)
|
||||||
expect(content).toMatch(
|
expect(content).toMatch(
|
||||||
`_useModel(__props, "modelValue", { get(v) { return v - 1 }, set: (v) => { return v + 1 }, })`,
|
`_useModel(__props, "modelValue", {
|
||||||
|
get(v) { return v - 1 },
|
||||||
|
set: (v) => { return v + 1 },
|
||||||
|
})`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const { content: content2 } = compile(
|
const { content: content2 } = compile(
|
||||||
|
@ -191,7 +199,26 @@ describe('defineModel()', () => {
|
||||||
/"modelValue": {\s+default: 0,\s+required: true,?\s+}/m,
|
/"modelValue": {\s+default: 0,\s+required: true,?\s+}/m,
|
||||||
)
|
)
|
||||||
expect(content2).toMatch(
|
expect(content2).toMatch(
|
||||||
`_useModel(__props, "modelValue", { get(v) { return v - 1 }, set: (v) => { return v + 1 }, })`,
|
`_useModel(__props, "modelValue", {
|
||||||
|
get(v) { return v - 1 },
|
||||||
|
set: (v) => { return v + 1 },
|
||||||
|
})`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('usage w/ props destructure', () => {
|
||||||
|
const { content } = compile(
|
||||||
|
`
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { x } = defineProps<{ x: number }>()
|
||||||
|
const modelValue = defineModel({
|
||||||
|
set: (v) => { return v + x }
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
`,
|
||||||
|
{ propsDestructure: true },
|
||||||
|
)
|
||||||
|
assertCode(content)
|
||||||
|
expect(content).toMatch(`set: (v) => { return v + __props.x }`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -33,7 +33,8 @@ export function processDefineModel(
|
||||||
let modelName: string
|
let modelName: string
|
||||||
let options: Node | undefined
|
let options: Node | undefined
|
||||||
const arg0 = node.arguments[0] && unwrapTSNode(node.arguments[0])
|
const arg0 = node.arguments[0] && unwrapTSNode(node.arguments[0])
|
||||||
if (arg0 && arg0.type === 'StringLiteral') {
|
const hasName = arg0 && arg0.type === 'StringLiteral'
|
||||||
|
if (hasName) {
|
||||||
modelName = arg0.value
|
modelName = arg0.value
|
||||||
options = node.arguments[1]
|
options = node.arguments[1]
|
||||||
} else {
|
} else {
|
||||||
|
@ -46,39 +47,42 @@ export function processDefineModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
let optionsString = options && ctx.getString(options)
|
let optionsString = options && ctx.getString(options)
|
||||||
let runtimeOptions = ''
|
let optionsRemoved = !options
|
||||||
let transformOptions = ''
|
|
||||||
|
|
||||||
if (options) {
|
if (
|
||||||
if (options.type === 'ObjectExpression') {
|
options &&
|
||||||
for (let i = options.properties.length - 1; i >= 0; i--) {
|
options.type === 'ObjectExpression' &&
|
||||||
const p = options.properties[i]
|
!options.properties.some(p => p.type === 'SpreadElement' || p.computed)
|
||||||
if (p.type === 'SpreadElement' || p.computed) {
|
) {
|
||||||
runtimeOptions = optionsString!
|
let removed = 0
|
||||||
break
|
for (let i = options.properties.length - 1; i >= 0; i--) {
|
||||||
}
|
const p = options.properties[i]
|
||||||
if (
|
const next = options.properties[i + 1]
|
||||||
(p.type === 'ObjectProperty' || p.type === 'ObjectMethod') &&
|
const start = p.start!
|
||||||
((p.key.type === 'Identifier' &&
|
const end = next ? next.start! : options.end! - 1
|
||||||
(p.key.name === 'get' || p.key.name === 'set')) ||
|
if (
|
||||||
(p.key.type === 'StringLiteral' &&
|
(p.type === 'ObjectProperty' || p.type === 'ObjectMethod') &&
|
||||||
(p.key.value === 'get' || p.key.value === 'set')))
|
((p.key.type === 'Identifier' &&
|
||||||
) {
|
(p.key.name === 'get' || p.key.name === 'set')) ||
|
||||||
transformOptions = ctx.getString(p) + ', ' + transformOptions
|
(p.key.type === 'StringLiteral' &&
|
||||||
|
(p.key.value === 'get' || p.key.value === 'set')))
|
||||||
// remove transform option from prop options to avoid duplicates
|
) {
|
||||||
const offset = p.start! - options.start!
|
// remove runtime-only options from prop options to avoid duplicates
|
||||||
const next = options.properties[i + 1]
|
optionsString =
|
||||||
const end = (next ? next.start! : options.end! - 1) - options.start!
|
optionsString.slice(0, start - options.start!) +
|
||||||
optionsString =
|
optionsString.slice(end - options.start!)
|
||||||
optionsString.slice(0, offset) + optionsString.slice(end)
|
} else {
|
||||||
}
|
// remove prop options from runtime options
|
||||||
|
removed++
|
||||||
|
ctx.s.remove(ctx.startOffset! + start, ctx.startOffset! + end)
|
||||||
}
|
}
|
||||||
if (!runtimeOptions && transformOptions) {
|
}
|
||||||
runtimeOptions = `{ ${transformOptions} }`
|
if (removed === options.properties.length) {
|
||||||
}
|
optionsRemoved = true
|
||||||
} else {
|
ctx.s.remove(
|
||||||
runtimeOptions = optionsString!
|
ctx.startOffset! + (hasName ? arg0.end! : options.start!),
|
||||||
|
ctx.startOffset! + options.end!,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,12 +95,20 @@ export function processDefineModel(
|
||||||
// register binding type
|
// register binding type
|
||||||
ctx.bindingMetadata[modelName] = BindingTypes.PROPS
|
ctx.bindingMetadata[modelName] = BindingTypes.PROPS
|
||||||
|
|
||||||
|
// defineModel -> useModel
|
||||||
ctx.s.overwrite(
|
ctx.s.overwrite(
|
||||||
ctx.startOffset! + node.start!,
|
ctx.startOffset! + node.callee.start!,
|
||||||
ctx.startOffset! + node.end!,
|
ctx.startOffset! + node.callee.end!,
|
||||||
`${ctx.helper('useModel')}(__props, ${JSON.stringify(modelName)}${
|
ctx.helper('useModel'),
|
||||||
runtimeOptions ? `, ${runtimeOptions}` : ``
|
)
|
||||||
})`,
|
// inject arguments
|
||||||
|
ctx.s.appendLeft(
|
||||||
|
ctx.startOffset! +
|
||||||
|
(node.arguments.length ? node.arguments[0].start! : node.end! - 1),
|
||||||
|
`__props, ` +
|
||||||
|
(hasName
|
||||||
|
? ``
|
||||||
|
: `${JSON.stringify(modelName)}${optionsRemoved ? `` : `, `}`),
|
||||||
)
|
)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
Loading…
Reference in New Issue