@@ -70,6 +70,59 @@ function getTypes(targetNode) {
70
70
return [ ]
71
71
}
72
72
73
+ /**
74
+ * Extracts default definitions using assignment patterns.
75
+ * @param {CallExpression } node The node of defineProps
76
+ * @returns { { [key: string]: Expression | undefined } }
77
+ */
78
+ function getDefaultsPropExpressionsForAssignmentPatterns ( node ) {
79
+ const left = getLeftOfDefineProps ( node )
80
+ if ( ! left || left . type !== 'ObjectPattern' ) {
81
+ return { }
82
+ }
83
+ /** @type { { [key: string]: Expression | undefined } } */
84
+ const result = Object . create ( null )
85
+ for ( const prop of left . properties ) {
86
+ if ( prop . type !== 'Property' ) continue
87
+ const value = prop . value
88
+ if ( value . type !== 'AssignmentPattern' ) continue
89
+ const defaultNode = value . right
90
+ const name = utils . getStaticPropertyName ( prop )
91
+ if ( name != null ) {
92
+ result [ name ] = defaultNode
93
+ }
94
+ }
95
+ return result
96
+ }
97
+
98
+ /**
99
+ * Gets the pattern of the left operand of defineProps.
100
+ * @param {CallExpression } node The node of defineProps
101
+ * @returns {Pattern | null } The pattern of the left operand of defineProps
102
+ */
103
+ function getLeftOfDefineProps ( node ) {
104
+ let target = node
105
+ if (
106
+ target . parent &&
107
+ target . parent . type === 'CallExpression' &&
108
+ target . parent . arguments [ 0 ] === target &&
109
+ target . parent . callee . type === 'Identifier' &&
110
+ target . parent . callee . name === 'withDefaults'
111
+ ) {
112
+ target = target . parent
113
+ }
114
+ if ( ! target . parent ) {
115
+ return null
116
+ }
117
+ if (
118
+ target . parent . type === 'VariableDeclarator' &&
119
+ target . parent . init === target
120
+ ) {
121
+ return target . parent . id
122
+ }
123
+ return null
124
+ }
125
+
73
126
module . exports = {
74
127
meta : {
75
128
type : 'suggestion' ,
@@ -250,71 +303,81 @@ module.exports = {
250
303
}
251
304
252
305
/**
253
- * @param {(ComponentObjectDefineProp | ComponentTypeProp | ComponentInferTypeProp)[] } props
254
- * @param { { [key : string]: Expression | undefined } } withDefaults
306
+ * @param {(ComponentObjectProp | ComponentTypeProp | ComponentInferTypeProp)[] } props
307
+ * @param {(propName : string) => Expression[] } otherDefaultProvider
255
308
*/
256
- function processPropDefs ( props , withDefaults ) {
309
+ function processPropDefs ( props , otherDefaultProvider ) {
257
310
/** @type {PropDefaultFunctionContext[] } */
258
311
const propContexts = [ ]
259
312
for ( const prop of props ) {
260
313
let typeList
261
- let defExpr
314
+ /** @type {Expression[] } */
315
+ const defExprList = [ ]
262
316
if ( prop . type === 'object' ) {
263
- const type = getPropertyNode ( prop . value , 'type' )
264
- if ( ! type ) continue
317
+ if ( prop . value . type === 'ObjectExpression' ) {
318
+ const type = getPropertyNode ( prop . value , 'type' )
319
+ if ( ! type ) continue
265
320
266
- typeList = getTypes ( type . value )
321
+ typeList = getTypes ( type . value )
267
322
268
- const def = getPropertyNode ( prop . value , 'default' )
269
- if ( ! def ) continue
323
+ const def = getPropertyNode ( prop . value , 'default' )
324
+ if ( ! def ) continue
270
325
271
- defExpr = def . value
326
+ defExprList . push ( def . value )
327
+ } else {
328
+ typeList = getTypes ( prop . value )
329
+ }
272
330
} else {
273
331
typeList = prop . types
274
- defExpr = withDefaults [ prop . propName ]
275
332
}
276
- if ( ! defExpr ) continue
333
+ if ( prop . propName != null ) {
334
+ defExprList . push ( ...otherDefaultProvider ( prop . propName ) )
335
+ }
336
+
337
+ if ( defExprList . length === 0 ) continue
277
338
278
339
const typeNames = new Set (
279
340
typeList . filter ( ( item ) => NATIVE_TYPES . has ( item ) )
280
341
)
281
342
// There is no native types detected
282
343
if ( typeNames . size === 0 ) continue
283
344
284
- const defType = getValueType ( defExpr )
345
+ for ( const defExpr of defExprList ) {
346
+ const defType = getValueType ( defExpr )
285
347
286
- if ( ! defType ) continue
348
+ if ( ! defType ) continue
287
349
288
- if ( defType . function ) {
289
- if ( typeNames . has ( 'Function' ) ) {
290
- continue
291
- }
292
- if ( defType . expression ) {
293
- if ( ! defType . returnType || typeNames . has ( defType . returnType ) ) {
350
+ if ( defType . function ) {
351
+ if ( typeNames . has ( 'Function' ) ) {
294
352
continue
295
353
}
296
- report ( defType . functionBody , prop , typeNames )
354
+ if ( defType . expression ) {
355
+ if ( ! defType . returnType || typeNames . has ( defType . returnType ) ) {
356
+ continue
357
+ }
358
+ report ( defType . functionBody , prop , typeNames )
359
+ } else {
360
+ propContexts . push ( {
361
+ prop,
362
+ types : typeNames ,
363
+ default : defType
364
+ } )
365
+ }
297
366
} else {
298
- propContexts . push ( {
367
+ if (
368
+ typeNames . has ( defType . type ) &&
369
+ ! FUNCTION_VALUE_TYPES . has ( defType . type )
370
+ ) {
371
+ continue
372
+ }
373
+ report (
374
+ defExpr ,
299
375
prop ,
300
- types : typeNames ,
301
- default : defType
302
- } )
303
- }
304
- } else {
305
- if (
306
- typeNames . has ( defType . type ) &&
307
- ! FUNCTION_VALUE_TYPES . has ( defType . type )
308
- ) {
309
- continue
310
- }
311
- report (
312
- defExpr ,
313
- prop ,
314
- [ ...typeNames ] . map ( ( type ) =>
315
- FUNCTION_VALUE_TYPES . has ( type ) ? 'Function' : type
376
+ [ ...typeNames ] . map ( ( type ) =>
377
+ FUNCTION_VALUE_TYPES . has ( type ) ? 'Function' : type
378
+ )
316
379
)
317
- )
380
+ }
318
381
}
319
382
}
320
383
return propContexts
@@ -364,7 +427,7 @@ module.exports = {
364
427
prop . type === 'object' && prop . value . type === 'ObjectExpression'
365
428
)
366
429
)
367
- const propContexts = processPropDefs ( props , { } )
430
+ const propContexts = processPropDefs ( props , ( ) => [ ] )
368
431
vueObjectPropsContexts . set ( obj , propContexts )
369
432
} ,
370
433
/**
@@ -402,18 +465,25 @@ module.exports = {
402
465
const props = baseProps . filter (
403
466
/**
404
467
* @param {ComponentProp } prop
405
- * @returns {prop is ComponentObjectDefineProp | ComponentInferTypeProp | ComponentTypeProp }
468
+ * @returns {prop is ComponentObjectProp | ComponentInferTypeProp | ComponentTypeProp }
406
469
*/
407
470
( prop ) =>
408
471
Boolean (
409
472
prop . type === 'type' ||
410
473
prop . type === 'infer-type' ||
411
- ( prop . type === 'object' &&
412
- prop . value . type === 'ObjectExpression' )
474
+ prop . type === 'object'
413
475
)
414
476
)
415
- const defaults = utils . getWithDefaultsPropExpressions ( node )
416
- const propContexts = processPropDefs ( props , defaults )
477
+ const defaultsByWithDefaults =
478
+ utils . getWithDefaultsPropExpressions ( node )
479
+ const defaultsByAssignmentPatterns =
480
+ getDefaultsPropExpressionsForAssignmentPatterns ( node )
481
+ const propContexts = processPropDefs ( props , ( propName ) =>
482
+ [
483
+ defaultsByWithDefaults [ propName ] ,
484
+ defaultsByAssignmentPatterns [ propName ]
485
+ ] . filter ( utils . isDef )
486
+ )
417
487
scriptSetupPropsContexts . push ( { node, props : propContexts } )
418
488
} ,
419
489
/**
0 commit comments