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();
|
||||
|
||||
const modelValue = _useModel(__props, "modelValue")
|
||||
const c = _useModel(__props, "count")
|
||||
const toString = _useModel(__props, "toString")
|
||||
const c = _useModel(__props, 'count')
|
||||
const toString = _useModel(__props, 'toString')
|
||||
|
||||
return { modelValue, c, toString }
|
||||
}
|
||||
|
@ -40,7 +40,10 @@ export default /*#__PURE__*/_defineComponent({
|
|||
setup(__props, { 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 }
|
||||
}
|
||||
|
@ -63,7 +66,36 @@ export default /*#__PURE__*/_defineComponent({
|
|||
setup(__props, { 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 }
|
||||
}
|
||||
|
@ -84,7 +116,7 @@ export default {
|
|||
__expose();
|
||||
|
||||
|
||||
const count = _useModel(__props, "count")
|
||||
const count = _useModel(__props, 'count')
|
||||
|
||||
return { count }
|
||||
}
|
||||
|
@ -132,10 +164,10 @@ export default /*#__PURE__*/_defineComponent({
|
|||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
const modelValue = _useModel(__props, "modelValue")
|
||||
const count = _useModel(__props, "count")
|
||||
const disabled = _useModel(__props, "disabled")
|
||||
const any = _useModel(__props, "any")
|
||||
const modelValue = _useModel<boolean | string>(__props, "modelValue")
|
||||
const count = _useModel<number>(__props, 'count')
|
||||
const disabled = _useModel<number>(__props, 'disabled')
|
||||
const any = _useModel<any | boolean>(__props, 'any')
|
||||
|
||||
return { modelValue, count, disabled, any }
|
||||
}
|
||||
|
@ -163,11 +195,11 @@ export default /*#__PURE__*/_defineComponent({
|
|||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
const modelValue = _useModel(__props, "modelValue")
|
||||
const fn = _useModel(__props, "fn")
|
||||
const fnWithDefault = _useModel(__props, "fnWithDefault")
|
||||
const str = _useModel(__props, "str")
|
||||
const optional = _useModel(__props, "optional")
|
||||
const modelValue = _useModel<boolean>(__props, "modelValue")
|
||||
const fn = _useModel<() => void>(__props, 'fn')
|
||||
const fnWithDefault = _useModel<() => void>(__props, 'fnWithDefault')
|
||||
const str = _useModel<string>(__props, 'str')
|
||||
const optional = _useModel<string>(__props, 'optional')
|
||||
|
||||
return { modelValue, fn, fnWithDefault, str, optional }
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ describe('defineModel()', () => {
|
|||
expect(content).toMatch(
|
||||
`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).not.toMatch('defineModel')
|
||||
|
||||
|
@ -71,7 +72,7 @@ describe('defineModel()', () => {
|
|||
"count": {},
|
||||
"countModifiers": {},
|
||||
})`)
|
||||
expect(content).toMatch(`const count = _useModel(__props, "count")`)
|
||||
expect(content).toMatch(`const count = _useModel(__props, 'count')`)
|
||||
expect(content).not.toMatch('defineModel')
|
||||
expect(bindings).toStrictEqual({
|
||||
foo: BindingTypes.PROPS,
|
||||
|
@ -104,11 +105,15 @@ describe('defineModel()', () => {
|
|||
)
|
||||
|
||||
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({
|
||||
modelValue: BindingTypes.SETUP_REF,
|
||||
|
@ -143,10 +148,10 @@ describe('defineModel()', () => {
|
|||
'emits: ["update:modelValue", "update:fn", "update:fnWithDefault", "update:str", "update:optional"]',
|
||||
)
|
||||
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 str = _useModel(__props, "str")`)
|
||||
expect(content).toMatch(`const fn = _useModel<() => void>(__props, 'fn')`)
|
||||
expect(content).toMatch(`const str = _useModel<string>(__props, 'str')`)
|
||||
expect(bindings).toStrictEqual({
|
||||
modelValue: BindingTypes.SETUP_REF,
|
||||
fn: BindingTypes.SETUP_REF,
|
||||
|
@ -171,7 +176,10 @@ describe('defineModel()', () => {
|
|||
assertCode(content)
|
||||
expect(content).toMatch(/"modelValue": {\s+required: true,?\s+}/m)
|
||||
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(
|
||||
|
@ -191,7 +199,26 @@ describe('defineModel()', () => {
|
|||
/"modelValue": {\s+default: 0,\s+required: true,?\s+}/m,
|
||||
)
|
||||
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 options: Node | undefined
|
||||
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
|
||||
options = node.arguments[1]
|
||||
} else {
|
||||
|
@ -46,39 +47,42 @@ export function processDefineModel(
|
|||
}
|
||||
|
||||
let optionsString = options && ctx.getString(options)
|
||||
let runtimeOptions = ''
|
||||
let transformOptions = ''
|
||||
let optionsRemoved = !options
|
||||
|
||||
if (options) {
|
||||
if (options.type === 'ObjectExpression') {
|
||||
for (let i = options.properties.length - 1; i >= 0; i--) {
|
||||
const p = options.properties[i]
|
||||
if (p.type === 'SpreadElement' || p.computed) {
|
||||
runtimeOptions = optionsString!
|
||||
break
|
||||
}
|
||||
if (
|
||||
(p.type === 'ObjectProperty' || p.type === 'ObjectMethod') &&
|
||||
((p.key.type === 'Identifier' &&
|
||||
(p.key.name === 'get' || p.key.name === 'set')) ||
|
||||
(p.key.type === 'StringLiteral' &&
|
||||
(p.key.value === 'get' || p.key.value === 'set')))
|
||||
) {
|
||||
transformOptions = ctx.getString(p) + ', ' + transformOptions
|
||||
|
||||
// remove transform option from prop options to avoid duplicates
|
||||
const offset = p.start! - options.start!
|
||||
const next = options.properties[i + 1]
|
||||
const end = (next ? next.start! : options.end! - 1) - options.start!
|
||||
optionsString =
|
||||
optionsString.slice(0, offset) + optionsString.slice(end)
|
||||
}
|
||||
if (
|
||||
options &&
|
||||
options.type === 'ObjectExpression' &&
|
||||
!options.properties.some(p => p.type === 'SpreadElement' || p.computed)
|
||||
) {
|
||||
let removed = 0
|
||||
for (let i = options.properties.length - 1; i >= 0; i--) {
|
||||
const p = options.properties[i]
|
||||
const next = options.properties[i + 1]
|
||||
const start = p.start!
|
||||
const end = next ? next.start! : options.end! - 1
|
||||
if (
|
||||
(p.type === 'ObjectProperty' || p.type === 'ObjectMethod') &&
|
||||
((p.key.type === 'Identifier' &&
|
||||
(p.key.name === 'get' || p.key.name === 'set')) ||
|
||||
(p.key.type === 'StringLiteral' &&
|
||||
(p.key.value === 'get' || p.key.value === 'set')))
|
||||
) {
|
||||
// remove runtime-only options from prop options to avoid duplicates
|
||||
optionsString =
|
||||
optionsString.slice(0, start - options.start!) +
|
||||
optionsString.slice(end - options.start!)
|
||||
} else {
|
||||
// remove prop options from runtime options
|
||||
removed++
|
||||
ctx.s.remove(ctx.startOffset! + start, ctx.startOffset! + end)
|
||||
}
|
||||
if (!runtimeOptions && transformOptions) {
|
||||
runtimeOptions = `{ ${transformOptions} }`
|
||||
}
|
||||
} else {
|
||||
runtimeOptions = optionsString!
|
||||
}
|
||||
if (removed === options.properties.length) {
|
||||
optionsRemoved = true
|
||||
ctx.s.remove(
|
||||
ctx.startOffset! + (hasName ? arg0.end! : options.start!),
|
||||
ctx.startOffset! + options.end!,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,12 +95,20 @@ export function processDefineModel(
|
|||
// register binding type
|
||||
ctx.bindingMetadata[modelName] = BindingTypes.PROPS
|
||||
|
||||
// defineModel -> useModel
|
||||
ctx.s.overwrite(
|
||||
ctx.startOffset! + node.start!,
|
||||
ctx.startOffset! + node.end!,
|
||||
`${ctx.helper('useModel')}(__props, ${JSON.stringify(modelName)}${
|
||||
runtimeOptions ? `, ${runtimeOptions}` : ``
|
||||
})`,
|
||||
ctx.startOffset! + node.callee.start!,
|
||||
ctx.startOffset! + node.callee.end!,
|
||||
ctx.helper('useModel'),
|
||||
)
|
||||
// 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
|
||||
|
|
Loading…
Reference in New Issue