Skip to content

Commit e56ffa8

Browse files
committed
feat: addRequiredToModels
1 parent 11149ec commit e56ffa8

File tree

7 files changed

+123
-95
lines changed

7 files changed

+123
-95
lines changed

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

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -654,13 +654,13 @@ return { }
654654
`;
655655

656656
exports[`SFC compile <script setup> > defineModel() > basic usage 1`] = `
657-
"import { useModel as _useModel } from 'vue'
657+
"import { useModel as _useModel, addRequiredToModels as _addRequiredToModels } from 'vue'
658658

659659
export default {
660-
props: {
661-
\\"modelValue\\": Object.assign({ required: true }, { required: true }),
662-
\\"count\\": { required: true },
663-
},
660+
props: _addRequiredToModels({
661+
\\"modelValue\\": { required: true },
662+
\\"count\\": {},
663+
}),
664664
emits: [\\"update:modelValue\\", \\"update:count\\"],
665665
setup(__props, { expose: __expose }) {
666666
__expose();
@@ -675,12 +675,12 @@ return { modelValue, c }
675675
`;
676676

677677
exports[`SFC compile <script setup> > defineModel() > w/ array props 1`] = `
678-
"import { useModel as _useModel, mergeModels as _mergeModels } from 'vue'
678+
"import { useModel as _useModel, addRequiredToModels as _addRequiredToModels, mergeModels as _mergeModels } from 'vue'
679679

680680
export default {
681-
props: _mergeModels(['foo', 'bar'], {
682-
\\"count\\": { required: true },
683-
}),
681+
props: _mergeModels(['foo', 'bar'], _addRequiredToModels({
682+
\\"count\\": {},
683+
})),
684684
emits: [\\"update:count\\"],
685685
setup(__props, { expose: __expose }) {
686686
__expose();
@@ -695,12 +695,12 @@ return { count }
695695
`;
696696

697697
exports[`SFC compile <script setup> > defineModel() > w/ defineProps and defineEmits 1`] = `
698-
"import { useModel as _useModel, mergeModels as _mergeModels } from 'vue'
698+
"import { useModel as _useModel, addRequiredToModels as _addRequiredToModels, mergeModels as _mergeModels } from 'vue'
699699

700700
export default {
701-
props: _mergeModels({ foo: String }, {
702-
\\"modelValue\\": Object.assign({ required: true }, { default: 0 }),
703-
}),
701+
props: _mergeModels({ foo: String }, _addRequiredToModels({
702+
\\"modelValue\\": { default: 0 },
703+
})),
704704
emits: _mergeModels(['change'], [\\"update:modelValue\\"]),
705705
setup(__props, { expose: __expose }) {
706706
__expose();
@@ -1643,14 +1643,14 @@ return { emit }
16431643
`;
16441644

16451645
exports[`SFC compile <script setup> > with TypeScript > defineModel() > basic usage 1`] = `
1646-
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
1646+
"import { useModel as _useModel, addRequiredToModels as _addRequiredToModels, defineComponent as _defineComponent } from 'vue'
16471647

16481648
export default /*#__PURE__*/_defineComponent({
1649-
props: {
1650-
\\"modelValue\\": { type: [Boolean, String], required: true },
1651-
\\"count\\": { type: Number, required: true },
1652-
\\"disabled\\": { type: Number, required: true, ...{ required: false } },
1653-
},
1649+
props: _addRequiredToModels({
1650+
\\"modelValue\\": { type: [Boolean, String] },
1651+
\\"count\\": { type: Number },
1652+
\\"disabled\\": { type: Number, ...{ required: false } },
1653+
}),
16541654
emits: [\\"update:modelValue\\", \\"update:count\\", \\"update:disabled\\"],
16551655
setup(__props, { expose: __expose }) {
16561656
__expose();
@@ -1666,16 +1666,16 @@ return { modelValue, count, disabled }
16661666
`;
16671667

16681668
exports[`SFC compile <script setup> > with TypeScript > defineModel() > w/ production mode 1`] = `
1669-
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
1669+
"import { useModel as _useModel, addRequiredToModels as _addRequiredToModels, defineComponent as _defineComponent } from 'vue'
16701670

16711671
export default /*#__PURE__*/_defineComponent({
1672-
props: {
1672+
props: _addRequiredToModels({
16731673
\\"modelValue\\": Boolean,
1674-
\\"fn\\": { },
1674+
\\"fn\\": {},
16751675
\\"fnWithDefault\\": { type: Function, ...{ default: () => null } },
1676-
\\"str\\": { },
1676+
\\"str\\": {},
16771677
\\"optional\\": { required: false },
1678-
},
1678+
}),
16791679
emits: [\\"update:modelValue\\", \\"update:fn\\", \\"update:fnWithDefault\\", \\"update:str\\", \\"update:optional\\"],
16801680
setup(__props, { expose: __expose }) {
16811681
__expose();

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

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -377,10 +377,9 @@ defineExpose({ foo: 123 })
377377
{ defineModel: true }
378378
)
379379
assertCode(content)
380-
expect(content).toMatch(
381-
'"modelValue": Object.assign({ required: true }, { required: true }),'
382-
)
383-
expect(content).toMatch('"count": { required: true },')
380+
expect(content).toMatch('props: _addRequiredToModels({')
381+
expect(content).toMatch('"modelValue": { required: true },')
382+
expect(content).toMatch('"count": {},')
384383
expect(content).toMatch('emits: ["update:modelValue", "update:count"],')
385384
expect(content).toMatch(`const modelValue = _useModel("modelValue")`)
386385
expect(content).toMatch(`const c = _useModel("count")`)
@@ -407,9 +406,7 @@ defineExpose({ foo: 123 })
407406
)
408407
assertCode(content)
409408
expect(content).toMatch(`props: _mergeModels({ foo: String }`)
410-
expect(content).toMatch(
411-
`"modelValue": Object.assign({ required: true }, { default: 0 })`
412-
)
409+
expect(content).toMatch(`"modelValue": { default: 0 }`)
413410
expect(content).toMatch(`const count = _useModel("modelValue")`)
414411
expect(content).not.toMatch('defineModel')
415412
expect(bindings).toStrictEqual({
@@ -430,8 +427,10 @@ defineExpose({ foo: 123 })
430427
{ defineModel: true }
431428
)
432429
assertCode(content)
433-
expect(content).toMatch(`_mergeModels(['foo', 'bar'], {`)
434-
expect(content).toMatch(`"count": { required: true }`)
430+
expect(content)
431+
.toMatch(`props: _mergeModels(['foo', 'bar'], _addRequiredToModels({
432+
"count": {},
433+
}))`)
435434
expect(content).toMatch(`const count = _useModel("count")`)
436435
expect(content).not.toMatch('defineModel')
437436
expect(bindings).toStrictEqual({
@@ -1800,12 +1799,10 @@ const emit = defineEmits(['a', 'b'])
18001799
{ defineModel: true }
18011800
)
18021801
assertCode(content)
1802+
expect(content).toMatch('"modelValue": { type: [Boolean, String] }')
1803+
expect(content).toMatch('"count": { type: Number }')
18031804
expect(content).toMatch(
1804-
'"modelValue": { type: [Boolean, String], required: true }'
1805-
)
1806-
expect(content).toMatch('"count": { type: Number, required: true }')
1807-
expect(content).toMatch(
1808-
'"disabled": { type: Number, required: true, ...{ required: false } },'
1805+
'"disabled": { type: Number, ...{ required: false } },'
18091806
)
18101807
expect(content).toMatch(
18111808
'emits: ["update:modelValue", "update:count", "update:disabled"]'
@@ -1838,11 +1835,11 @@ const emit = defineEmits(['a', 'b'])
18381835
)
18391836
assertCode(content)
18401837
expect(content).toMatch('"modelValue": Boolean')
1841-
expect(content).toMatch('"fn": { }')
1838+
expect(content).toMatch('"fn": {}')
18421839
expect(content).toMatch(
18431840
'"fnWithDefault": { type: Function, ...{ default: () => null } },'
18441841
)
1845-
expect(content).toMatch('"str": { }')
1842+
expect(content).toMatch('"str": {}')
18461843
expect(content).toMatch('"optional": { required: false }')
18471844
expect(content).toMatch(
18481845
'emits: ["update:modelValue", "update:fn", "update:fnWithDefault", "update:str", "update:optional"]'

packages/compiler-sfc/src/compileScript.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export interface SFCScriptCompileOptions {
127127
*/
128128
hoistStatic?: boolean
129129
/**
130-
* (Experimental) Enable macro `defineModel`
130+
* (**Experimental**) Enable macro `defineModel`
131131
*/
132132
defineModel?: boolean
133133
}
@@ -1032,34 +1032,28 @@ export function compileScript(
10321032
el => el === 'Boolean' || (el === 'Function' && options)
10331033
)
10341034
}
1035-
const runtimeType =
1035+
let runtimeType =
10361036
(runtimeTypes &&
10371037
runtimeTypes.length > 0 &&
10381038
toRuntimeTypeString(runtimeTypes)) ||
10391039
undefined
10401040

10411041
let decl: string
1042-
10431042
if (runtimeType && isProd && !options) {
10441043
decl = runtimeType
10451044
} else {
1046-
const pairs: string[] = []
1047-
if (runtimeType) pairs.push(`type: ${runtimeType}`)
1048-
if (!isProd) pairs.push('required: true')
1049-
decl = pairs.join(', ')
1050-
1051-
if (decl && options) {
1045+
if (runtimeType) runtimeType = `type: ${runtimeType}`
1046+
if (runtimeType && options) {
10521047
decl = isTS
1053-
? `{ ${decl}, ...${options} }`
1054-
: `Object.assign({ ${decl} }, ${options})`
1048+
? `{ ${runtimeType}, ...${options} }`
1049+
: `Object.assign({ ${runtimeType} }, ${options})`
10551050
} else {
1056-
decl = options || `{ ${decl} }`
1051+
decl = options || (runtimeType ? `{ ${runtimeType} }` : '{}')
10571052
}
10581053
}
1059-
10601054
modelPropsDecl += `\n ${JSON.stringify(name)}: ${decl},`
10611055
}
1062-
return `{${modelPropsDecl}\n }`
1056+
return `${helper('addRequiredToModels')}({${modelPropsDecl}\n })`
10631057
}
10641058

10651059
let propsDecls: undefined | string

packages/runtime-core/__tests__/apiSetupHelpers.spec.ts

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import {
2828
withAsyncContext,
2929
createPropsRestProxy,
3030
mergeModelsOptions,
31-
useModel
31+
useModel,
32+
addRequiredToModels
3233
} from '../src/apiSetupHelpers'
3334

3435
describe('SFC <script setup> helpers', () => {
@@ -178,24 +179,40 @@ describe('SFC <script setup> helpers', () => {
178179
})
179180
})
180181

182+
test('addRequiredToModels', () => {
183+
expect(
184+
addRequiredToModels({
185+
foo: {},
186+
bar: { required: false },
187+
baz: { default: 1 },
188+
qux: { required: false, default: 1 },
189+
quux: { type: String }
190+
})
191+
).toStrictEqual({
192+
foo: { required: true },
193+
bar: { required: false },
194+
baz: { default: 1 },
195+
qux: { required: false, default: 1 },
196+
quux: { type: String, required: true }
197+
})
198+
})
199+
181200
describe('useModel', () => {
182201
test('basic', async () => {
183-
let proxy: any
202+
let foo: any
203+
const update = () => {
204+
foo.value = 'bar'
205+
}
206+
184207
const Comp = defineComponent({
185208
props: {
186209
modelValue: { required: true }
187210
},
188211
emits: ['update:modelValue'],
189212
setup() {
190-
const foo = useModel<string>('modelValue')
191-
const update = () => {
192-
foo.value = 'bar'
193-
}
194-
return { foo, update }
213+
foo = useModel<string>('modelValue')
195214
},
196-
render() {
197-
proxy = this
198-
}
215+
render() {}
199216
})
200217

201218
const msg = ref('')
@@ -208,82 +225,86 @@ describe('SFC <script setup> helpers', () => {
208225
})
209226
).mount(root)
210227

211-
expect(proxy.foo).toBe('')
228+
// it's a ComputedRef, so not passive
229+
expect(foo.effect).not.toBeUndefined()
230+
231+
expect(foo.value).toBe('')
212232
expect(msg.value).toBe('')
213233
expect(setValue).not.toBeCalled()
214234

215235
// update from child
216-
proxy.update()
236+
update()
217237

218238
await nextTick()
219239
expect(msg.value).toBe('bar')
220-
expect(proxy.foo).toBe('bar')
240+
expect(foo.value).toBe('bar')
221241
expect(setValue).toBeCalledTimes(1)
222242

223243
// update from parent
224244
msg.value = 'qux'
225245

226246
await nextTick()
227247
expect(msg.value).toBe('qux')
228-
expect(proxy.foo).toBe('qux')
248+
expect(foo.value).toBe('qux')
229249
expect(setValue).toBeCalledTimes(1)
230250
})
231251

232252
test('optional', async () => {
233-
let proxy: any
253+
let foo: any
254+
const update = () => {
255+
foo.value = 'bar'
256+
}
257+
234258
const Comp = defineComponent({
235259
props: ['foo'],
236260
emits: ['update:foo'],
237261
setup() {
238-
const foo = useModel<string>('foo')
239-
const update = () => {
240-
foo.value = 'bar'
241-
}
242-
return { foo, update }
262+
foo = useModel<string>('foo')
243263
},
244-
render() {
245-
proxy = this
246-
}
264+
render() {}
247265
})
248266

249267
const root = nodeOps.createElement('div')
250268
const updateFoo = vi.fn()
251269
render(h(Comp, { 'onUpdate:foo': updateFoo }), root)
252270

253-
expect(proxy.foo).toBeUndefined()
254-
proxy.update()
271+
// it's a ComputedRef, so it's passive
272+
expect(foo.effect).toBeUndefined()
273+
274+
expect(foo.value).toBeUndefined()
275+
update()
255276

256-
expect(proxy.foo).toBe('bar')
277+
expect(foo.value).toBe('bar')
257278

258279
await nextTick()
259280
expect(updateFoo).toBeCalledTimes(1)
260281
})
261282

262283
test('default value', async () => {
263-
let proxy: any
284+
let count: any
285+
const inc = () => {
286+
count.value++
287+
}
264288
const Comp = defineComponent({
265289
props: { count: { default: 0 } },
266290
emits: ['update:count'],
267291
setup() {
268-
const count = useModel<number>('count')
269-
const inc = () => {
270-
count.value++
271-
}
272-
return { count, inc }
292+
count = useModel<number>('count')
273293
},
274-
render() {
275-
proxy = this
276-
}
294+
render() {}
277295
})
278296

279297
const root = nodeOps.createElement('div')
280298
const updateCount = vi.fn()
281299
render(h(Comp, { 'onUpdate:count': updateCount }), root)
282300

283-
expect(proxy.count).toBe(0)
301+
// it's a ComputedRef, so it's passive
302+
expect(count.effect).toBeUndefined()
303+
304+
expect(count.value).toBe(0)
284305

285-
proxy.inc()
286-
expect(proxy.count).toBe(1)
306+
inc()
307+
expect(count.value).toBe(1)
287308
await nextTick()
288309
expect(updateCount).toBeCalledTimes(1)
289310
})

0 commit comments

Comments
 (0)