Skip to content

Commit 47c139e

Browse files
committed
feat(macros): automatically infer type from default value for defineModel
1 parent d7b31d6 commit 47c139e

File tree

3 files changed

+100
-61
lines changed

3 files changed

+100
-61
lines changed

packages/macros/src/core/define-component/index.ts

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,27 @@ export function transformDefineComponent(
4141
const defaultValue = getDefaultValue(prop.value.right)
4242
const isRequired = prop.value.right.type === 'TSNonNullExpression'
4343

44-
props[propName] = `{`
44+
const propOptions = []
4545
if (isRequired) {
46-
props[propName] += 'required: true,'
46+
propOptions.push('required: true')
4747
}
4848
if (defaultValue) {
4949
const { value, type, skipFactory } = getTypeAndValue(s, defaultValue)
5050
if (type) {
51-
props[propName] += `type: ${type},`
51+
propOptions.push(`type: ${type}`)
5252
}
5353
if (value) {
54-
props[propName] += `default: ${value},`
54+
propOptions.push(`default: ${value}`)
5555
}
5656
if (skipFactory) {
57-
props[propName] += 'skipFactory: true,'
57+
propOptions.push('skipFactory: true')
5858
}
5959
}
60-
props[propName] += `}`
60+
if (propOptions.length) {
61+
props[propName] = `{ ${propOptions.join(', ')} }`
62+
} else {
63+
props[propName] = null
64+
}
6165
}
6266

6367
restructure(s, root, {
@@ -73,36 +77,7 @@ export function transformDefineComponent(
7377
}
7478
}
7579

76-
for (const { expression, isRequired } of map.defineModel || []) {
77-
const modelOptions =
78-
expression.arguments[0]?.type === 'ObjectExpression'
79-
? expression.arguments[0]
80-
: expression.arguments[1]?.type === 'ObjectExpression'
81-
? expression.arguments[1]
82-
: undefined
83-
const options: any = {}
84-
if (isRequired) options.required = true
85-
for (const prop of modelOptions?.properties || []) {
86-
if (
87-
prop.type === 'ObjectProperty' &&
88-
prop.key.type === 'Identifier' &&
89-
['validator', 'type', 'required'].includes(prop.key.name)
90-
) {
91-
options[prop.key.name] = s.sliceNode(prop.value)
92-
}
93-
}
94-
const propName =
95-
expression.arguments[0]?.type === 'StringLiteral'
96-
? expression.arguments[0].value
97-
: 'modelValue'
98-
props[propName] = Object.keys(options).length
99-
? `{ ${Object.entries(options)
100-
.map(([key, value]) => `${key}: ${value}`)
101-
.join(', ')} }`
102-
: null
103-
props[`onUpdate:${propName}`] = null
104-
props[`${propName === 'modelValue' ? 'model' : propName}Modifiers`] = null
105-
}
80+
transformDefineModel(s, map.defineModel, props)
10681

10782
const propsString = Object.entries(props)
10883
.map(([key, value]) => `'${key}': ${value}`)
@@ -166,6 +141,59 @@ function getWalkedIds(root: FunctionalNode, propsName: string) {
166141
return walkedIds
167142
}
168143

144+
function transformDefineModel(
145+
s: MagicStringAST,
146+
defineModel: RootMapValue['defineModel'],
147+
props: Record<string, string | null>,
148+
) {
149+
for (const { expression, isRequired } of defineModel || []) {
150+
const modelOptions =
151+
expression.arguments[0]?.type === 'ObjectExpression'
152+
? expression.arguments[0]
153+
: expression.arguments[1]?.type === 'ObjectExpression'
154+
? expression.arguments[1]
155+
: undefined
156+
const options: any = {}
157+
if (isRequired) options.required = true
158+
let defaultValueNode: Node | undefined
159+
for (const prop of modelOptions?.properties || []) {
160+
if (
161+
prop.type === 'ObjectProperty' &&
162+
prop.key.type === 'Identifier' &&
163+
['validator', 'type', 'required', 'default'].includes(prop.key.name)
164+
) {
165+
if (prop.key.name === 'default') {
166+
defaultValueNode = prop.value
167+
}
168+
options[prop.key.name] = s.sliceNode(prop.value)
169+
}
170+
}
171+
if (defaultValueNode && !options.type) {
172+
const { value, type, skipFactory } = getTypeAndValue(s, defaultValueNode)
173+
if (type) {
174+
options.type = type
175+
}
176+
if (value) {
177+
options.default = value
178+
}
179+
if (skipFactory) {
180+
options.skipFactory = 'true'
181+
}
182+
}
183+
const propName =
184+
expression.arguments[0]?.type === 'StringLiteral'
185+
? expression.arguments[0].value
186+
: 'modelValue'
187+
props[propName] = Object.keys(options).length
188+
? `{ ${Object.entries(options)
189+
.map(([key, value]) => `${key}: ${value}`)
190+
.join(', ')} }`
191+
: null
192+
props[`onUpdate:${propName}`] = null
193+
props[`${propName === 'modelValue' ? 'model' : propName}Modifiers`] = null
194+
}
195+
}
196+
169197
function getTypeAndValue(s: MagicStringAST, node: Node) {
170198
let value = ''
171199
let type = ''

packages/macros/tests/__snapshots__/fixtures.spec.ts.snap

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const Comp = defineComponent(
2020
return () => <div>{[foo, __MACROS_props.bar, attrs.baz]}</div>
2121
},
2222
{props: {
23-
'bar': {required: true,type: String,default: 'bar',},
23+
'bar': { required: true, type: String, default: 'bar' },
2424
'modelValue': null,
2525
'onUpdate:modelValue': null,
2626
'modelModifiers': null,
@@ -78,15 +78,15 @@ defineComponent((__MACROS_props) => {
7878
</>
7979
)
8080
}, { props: {
81-
'a': {type: Number,default: 0,},
82-
'b': {type: String,default: 'b',},
83-
'c': {type: Boolean,default: true,},
84-
'd': {type: Function,default: () => {},},
85-
'e': {type: Object,default: () => ({}),},
86-
'f': {type: Array,default: () => ([]),},
87-
'g': {default: foo,skipFactory: true,},
88-
'h': {default: null,},
89-
'i': {required: true,default: undefined,}
81+
'a': { type: Number, default: 0 },
82+
'b': { type: String, default: 'b' },
83+
'c': { type: Boolean, default: true },
84+
'd': { type: Function, default: () => {} },
85+
'e': { type: Object, default: () => ({}) },
86+
'f': { type: Array, default: () => ([]) },
87+
'g': { default: foo, skipFactory: true },
88+
'h': { default: null },
89+
'i': { required: true, default: undefined }
9090
} })"
9191
`;
9292

@@ -110,7 +110,7 @@ const Comp = defineComponent(
110110
return <div>{[foo, __MACROS_props.bar, attrs.baz]}</div>
111111
},
112112
{props: {
113-
'bar': {required: true,type: String,default: 'bar',},
113+
'bar': { required: true, type: String, default: 'bar' },
114114
'modelValue': null,
115115
'onUpdate:modelValue': null,
116116
'modelModifiers': null,
@@ -168,15 +168,15 @@ defineComponent((__MACROS_props) => {
168168
</>
169169
)
170170
}, { props: {
171-
'a': {type: Number,default: 0,},
172-
'b': {type: String,default: 'b',},
173-
'c': {type: Boolean,default: true,},
174-
'd': {type: Function,default: () => {},},
175-
'e': {type: Object,default: () => ({}),},
176-
'f': {type: Array,default: () => ([]),},
177-
'g': {default: foo,skipFactory: true,},
178-
'h': {default: null,},
179-
'i': {required: true,default: undefined,}
171+
'a': { type: Number, default: 0 },
172+
'b': { type: String, default: 'b' },
173+
'c': { type: Boolean, default: true },
174+
'd': { type: Function, default: () => {} },
175+
'e': { type: Object, default: () => ({}) },
176+
'f': { type: Array, default: () => ([]) },
177+
'g': { default: foo, skipFactory: true },
178+
'h': { default: null },
179+
'i': { required: true, default: undefined }
180180
} })"
181181
`;
182182

@@ -207,10 +207,18 @@ export const Comp2 = ({ foo, ...__MACROS_props }: any) => {
207207

208208
exports[`fixtures > ./fixtures/define-model.tsx 1`] = `
209209
"
210-
import { useModel as __MACROS_useModel } from "vue-jsx-vapor/macros/use-model";export const Comp = ({ bar, ...__MACROS_props }: { bar: string }) => {
211-
const foo = __MACROS_useModel<string, 'm1' | 'm2'>(__MACROS_props, 'foo', { default: bar })
210+
import { useModel as __MACROS_useModel } from "vue-jsx-vapor/macros/use-model";
211+
import { defineComponent } from 'vue'
212+
213+
export const Comp = defineComponent((__MACROS_props) => {
214+
const foo = __MACROS_useModel<string, 'm1' | 'm2'>(__MACROS_props, 'foo', { default: __MACROS_props.bar })
212215
return <div>{foo.value}</div>
213-
}
216+
}, { props: {
217+
'bar': null,
218+
'foo': { default: __MACROS_props.bar, skipFactory: true },
219+
'onUpdate:foo': null,
220+
'fooModifiers': null
221+
} })
214222
215223
export default function (__MACROS_props) {
216224
const modelValue = $(__MACROS_useModel<string>(__MACROS_props, 'modelValue',)!)

packages/macros/tests/fixtures/define-model.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
export const Comp = ({ bar }: { bar: string }) => {
1+
2+
import { defineComponent } from 'vue'
3+
4+
export const Comp = defineComponent(({ bar }: { bar: string }) => {
25
const foo = defineModel<string, 'm1' | 'm2'>('foo', { default: bar })
36
return <div>{foo.value}</div>
4-
}
7+
})
58

69
export default function () {
710
const modelValue = $(defineModel<string>()!)

0 commit comments

Comments
 (0)