@@ -9,6 +9,7 @@ const { capitalize } = require('../utils/casing')
9
9
/**
10
10
* @typedef {import('../utils').ComponentObjectProp } ComponentObjectProp
11
11
* @typedef {import('../utils').ComponentArrayProp } ComponentArrayProp
12
+ * @typedef {import('../utils').ComponentTypeProp } ComponentTypeProp
12
13
* @typedef {import('../utils').VueObjectData } VueObjectData
13
14
*/
14
15
@@ -88,18 +89,45 @@ module.exports = {
88
89
/** @param {RuleContext } context */
89
90
create ( context ) {
90
91
/**
91
- * @typedef { { type: string, function: false } } StandardValueType
92
- * @typedef { { type: 'Function', function: true, expression: true, functionBody: Expression, returnType: string | null } } FunctionExprValueType
93
- * @typedef { { type: 'Function', function: true, expression: false, functionBody: BlockStatement, returnTypes: ReturnType[] } } FunctionValueType
92
+ * @typedef {object } StandardValueType
93
+ * @property {string } type
94
+ * @property {false } function
95
+ */
96
+ /**
97
+ * @typedef {object } FunctionExprValueType
98
+ * @property {'Function' } type
99
+ * @property {true } function
100
+ * @property {true } expression
101
+ * @property {Expression } functionBody
102
+ * @property {string | null } returnType
103
+ */
104
+ /**
105
+ * @typedef {object } FunctionValueType
106
+ * @property {'Function' } type
107
+ * @property {true } function
108
+ * @property {false } expression
109
+ * @property {BlockStatement } functionBody
110
+ * @property {ReturnType[] } returnTypes
111
+ */
112
+ /**
94
113
* @typedef { ComponentObjectProp & { value: ObjectExpression } } ComponentObjectDefineProp
95
- * @typedef { { prop: ComponentObjectDefineProp, type: Set<string>, default: FunctionValueType } } PropDefaultFunctionContext
96
114
* @typedef { { type: string, node: Expression } } ReturnType
97
115
*/
116
+ /**
117
+ * @typedef {object } PropDefaultFunctionContext
118
+ * @property {ComponentObjectProp | ComponentTypeProp } prop
119
+ * @property {Set<string> } types
120
+ * @property {FunctionValueType } default
121
+ */
98
122
99
123
/**
100
124
* @type {Map<ObjectExpression, PropDefaultFunctionContext[]> }
101
125
*/
102
126
const vueObjectPropsContexts = new Map ( )
127
+ /**
128
+ * @type { {node: CallExpression, props:PropDefaultFunctionContext[]}[] }
129
+ */
130
+ const scriptSetupPropsContexts = [ ]
103
131
104
132
/**
105
133
* @typedef {object } ScopeStack
@@ -194,7 +222,7 @@ module.exports = {
194
222
195
223
/**
196
224
* @param {* } node
197
- * @param {ComponentObjectProp } prop
225
+ * @param {ComponentObjectProp | ComponentTypeProp } prop
198
226
* @param {Iterable<string> } expectedTypeNames
199
227
*/
200
228
function report ( node , prop , expectedTypeNames ) {
@@ -213,127 +241,196 @@ module.exports = {
213
241
} )
214
242
}
215
243
216
- // ----------------------------------------------------------------------
217
- // Public
218
- // ----------------------------------------------------------------------
219
-
220
- return utils . defineVueVisitor ( context , {
221
- onVueObjectEnter ( obj ) {
222
- /** @type {ComponentObjectDefineProp[] } */
223
- const props = utils . getComponentProps ( obj ) . filter (
224
- /**
225
- * @param {ComponentObjectProp | ComponentArrayProp } prop
226
- * @returns {prop is ComponentObjectDefineProp }
227
- */
228
- ( prop ) =>
229
- Boolean ( prop . value && prop . value . type === 'ObjectExpression' )
230
- )
231
- /** @type {PropDefaultFunctionContext[] } */
232
- const propContexts = [ ]
233
- for ( const prop of props ) {
244
+ /**
245
+ * @param {(ComponentObjectDefineProp | ComponentTypeProp)[] } props
246
+ * @param { { [key: string]: Expression | undefined } } withDefaults
247
+ */
248
+ function processPropDefs ( props , withDefaults ) {
249
+ /** @type {PropDefaultFunctionContext[] } */
250
+ const propContexts = [ ]
251
+ for ( const prop of props ) {
252
+ let typeList
253
+ let defExpr
254
+ if ( prop . type === 'object' ) {
234
255
const type = getPropertyNode ( prop . value , 'type' )
235
256
if ( ! type ) continue
236
257
237
- const typeNames = new Set (
238
- getTypes ( type . value ) . filter ( ( item ) => NATIVE_TYPES . has ( item ) )
239
- )
240
-
241
- // There is no native types detected
242
- if ( typeNames . size === 0 ) continue
258
+ typeList = getTypes ( type . value )
243
259
244
260
const def = getPropertyNode ( prop . value , 'default' )
245
261
if ( ! def ) continue
246
262
247
- const defType = getValueType ( def . value )
263
+ defExpr = def . value
264
+ } else {
265
+ typeList = prop . types
266
+ defExpr = withDefaults [ prop . propName ]
267
+ }
268
+ if ( ! defExpr ) continue
269
+
270
+ const typeNames = new Set (
271
+ typeList . filter ( ( item ) => NATIVE_TYPES . has ( item ) )
272
+ )
273
+ // There is no native types detected
274
+ if ( typeNames . size === 0 ) continue
275
+
276
+ const defType = getValueType ( defExpr )
248
277
249
- if ( ! defType ) continue
278
+ if ( ! defType ) continue
250
279
251
- if ( ! defType . function ) {
252
- if ( typeNames . has ( defType . type ) ) {
253
- if ( ! FUNCTION_VALUE_TYPES . has ( defType . type ) ) {
254
- continue
255
- }
280
+ if ( ! defType . function ) {
281
+ if ( typeNames . has ( defType . type ) ) {
282
+ if ( ! FUNCTION_VALUE_TYPES . has ( defType . type ) ) {
283
+ continue
256
284
}
257
- report (
258
- def . value ,
259
- prop ,
260
- Array . from ( typeNames ) . map ( ( type ) =>
261
- FUNCTION_VALUE_TYPES . has ( type ) ? 'Function' : type
262
- )
285
+ }
286
+ report (
287
+ defExpr ,
288
+ prop ,
289
+ Array . from ( typeNames ) . map ( ( type ) =>
290
+ FUNCTION_VALUE_TYPES . has ( type ) ? 'Function' : type
263
291
)
264
- } else {
265
- if ( typeNames . has ( 'Function' ) ) {
292
+ )
293
+ } else {
294
+ if ( typeNames . has ( 'Function' ) ) {
295
+ continue
296
+ }
297
+ if ( defType . expression ) {
298
+ if ( ! defType . returnType || typeNames . has ( defType . returnType ) ) {
266
299
continue
267
300
}
268
- if ( defType . expression ) {
269
- if ( ! defType . returnType || typeNames . has ( defType . returnType ) ) {
270
- continue
271
- }
272
- report ( defType . functionBody , prop , typeNames )
273
- } else {
274
- propContexts . push ( {
275
- prop,
276
- type : typeNames ,
277
- default : defType
301
+ report ( defType . functionBody , prop , typeNames )
302
+ } else {
303
+ propContexts . push ( {
304
+ prop,
305
+ types : typeNames ,
306
+ default : defType
307
+ } )
308
+ }
309
+ }
310
+ }
311
+ return propContexts
312
+ }
313
+
314
+ // ----------------------------------------------------------------------
315
+ // Public
316
+ // ----------------------------------------------------------------------
317
+
318
+ return utils . compositingVisitors (
319
+ {
320
+ /**
321
+ * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression } node
322
+ */
323
+ ':function' ( node ) {
324
+ scopeStack = {
325
+ upper : scopeStack ,
326
+ body : node . body ,
327
+ returnTypes : null
328
+ }
329
+ } ,
330
+ /**
331
+ * @param {ReturnStatement } node
332
+ */
333
+ ReturnStatement ( node ) {
334
+ if ( ! scopeStack ) {
335
+ return
336
+ }
337
+ if ( scopeStack . returnTypes && node . argument ) {
338
+ const type = getValueType ( node . argument )
339
+ if ( type ) {
340
+ scopeStack . returnTypes . push ( {
341
+ type : type . type ,
342
+ node : node . argument
278
343
} )
279
344
}
280
345
}
281
- }
282
- vueObjectPropsContexts . set ( obj , propContexts )
346
+ } ,
347
+ ':function:exit' : onFunctionExit
283
348
} ,
284
- /**
285
- * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression } node
286
- * @param {VueObjectData } data
287
- */
288
- ':function' ( node , { node : vueNode } ) {
289
- scopeStack = {
290
- upper : scopeStack ,
291
- body : node . body ,
292
- returnTypes : null
293
- }
349
+ utils . defineVueVisitor ( context , {
350
+ onVueObjectEnter ( obj ) {
351
+ /** @type {ComponentObjectDefineProp[] } */
352
+ const props = utils . getComponentProps ( obj ) . filter (
353
+ /**
354
+ * @param {ComponentObjectProp | ComponentArrayProp } prop
355
+ * @returns {prop is ComponentObjectDefineProp }
356
+ */
357
+ ( prop ) =>
358
+ Boolean (
359
+ prop . type === 'object' && prop . value . type === 'ObjectExpression'
360
+ )
361
+ )
362
+ const propContexts = processPropDefs ( props , { } )
363
+ vueObjectPropsContexts . set ( obj , propContexts )
364
+ } ,
365
+ /**
366
+ * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression } node
367
+ * @param {VueObjectData } data
368
+ */
369
+ ':function' ( node , { node : vueNode } ) {
370
+ const data = vueObjectPropsContexts . get ( vueNode )
371
+ if ( ! data || ! scopeStack ) {
372
+ return
373
+ }
294
374
295
- const data = vueObjectPropsContexts . get ( vueNode )
296
- if ( ! data ) {
297
- return
298
- }
375
+ for ( const { default : defType } of data ) {
376
+ if ( node . body === defType . functionBody ) {
377
+ scopeStack . returnTypes = defType . returnTypes
378
+ }
379
+ }
380
+ } ,
381
+ onVueObjectExit ( obj ) {
382
+ const data = vueObjectPropsContexts . get ( obj )
383
+ if ( ! data ) {
384
+ return
385
+ }
386
+ for ( const { prop, types : typeNames , default : defType } of data ) {
387
+ for ( const returnType of defType . returnTypes ) {
388
+ if ( typeNames . has ( returnType . type ) ) continue
299
389
300
- for ( const { default : defType } of data ) {
301
- if ( node . body === defType . functionBody ) {
302
- scopeStack . returnTypes = defType . returnTypes
390
+ report ( returnType . node , prop , typeNames )
391
+ }
303
392
}
304
393
}
305
- } ,
306
- /**
307
- * @param {ReturnStatement } node
308
- */
309
- ReturnStatement ( node ) {
310
- if ( ! scopeStack ) {
311
- return
312
- }
313
- if ( scopeStack . returnTypes && node . argument ) {
314
- const type = getValueType ( node . argument )
315
- if ( type ) {
316
- scopeStack . returnTypes . push ( {
317
- type : type . type ,
318
- node : node . argument
319
- } )
394
+ } ) ,
395
+ utils . defineScriptSetupVisitor ( context , {
396
+ onDefinePropsEnter ( node , baseProps ) {
397
+ /** @type {(ComponentObjectDefineProp | ComponentTypeProp)[] } */
398
+ const props = baseProps . filter (
399
+ /**
400
+ * @param {ComponentObjectProp | ComponentArrayProp | ComponentTypeProp } prop
401
+ * @returns {prop is ComponentObjectDefineProp | ComponentTypeProp }
402
+ */
403
+ ( prop ) =>
404
+ Boolean (
405
+ prop . type === 'type' ||
406
+ ( prop . type === 'object' &&
407
+ prop . value . type === 'ObjectExpression' )
408
+ )
409
+ )
410
+ const defaults = utils . getWithDefaultsPropExpressions ( node )
411
+ const propContexts = processPropDefs ( props , defaults )
412
+ scriptSetupPropsContexts . push ( { node, props : propContexts } )
413
+ } ,
414
+ /**
415
+ * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression } node
416
+ */
417
+ ':function' ( node ) {
418
+ const data =
419
+ scriptSetupPropsContexts [ scriptSetupPropsContexts . length - 1 ]
420
+ if ( ! data || ! scopeStack ) {
421
+ return
320
422
}
321
- }
322
- } ,
323
- ':function:exit' : onFunctionExit ,
324
- onVueObjectExit ( obj ) {
325
- const data = vueObjectPropsContexts . get ( obj )
326
- if ( ! data ) {
327
- return
328
- }
329
- for ( const { prop, type : typeNames , default : defType } of data ) {
330
- for ( const returnType of defType . returnTypes ) {
331
- if ( typeNames . has ( returnType . type ) ) continue
332
423
333
- report ( returnType . node , prop , typeNames )
424
+ for ( const { default : defType } of data . props ) {
425
+ if ( node . body === defType . functionBody ) {
426
+ scopeStack . returnTypes = defType . returnTypes
427
+ }
334
428
}
429
+ } ,
430
+ onDefinePropsExit ( node ) {
431
+ scriptSetupPropsContexts . pop ( )
335
432
}
336
- }
337
- } )
433
+ } )
434
+ )
338
435
}
339
436
}
0 commit comments