@@ -9,6 +9,7 @@ const { capitalize } = require('../utils/casing')
99/**
1010 * @typedef {import('../utils').ComponentObjectProp } ComponentObjectProp
1111 * @typedef {import('../utils').ComponentArrayProp } ComponentArrayProp
12+ * @typedef {import('../utils').ComponentTypeProp } ComponentTypeProp
1213 * @typedef {import('../utils').VueObjectData } VueObjectData
1314 */
1415
@@ -88,18 +89,45 @@ module.exports = {
8889 /** @param {RuleContext } context */
8990 create ( context ) {
9091 /**
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+ /**
94113 * @typedef { ComponentObjectProp & { value: ObjectExpression } } ComponentObjectDefineProp
95- * @typedef { { prop: ComponentObjectDefineProp, type: Set<string>, default: FunctionValueType } } PropDefaultFunctionContext
96114 * @typedef { { type: string, node: Expression } } ReturnType
97115 */
116+ /**
117+ * @typedef {object } PropDefaultFunctionContext
118+ * @property {ComponentObjectProp | ComponentTypeProp } prop
119+ * @property {Set<string> } types
120+ * @property {FunctionValueType } default
121+ */
98122
99123 /**
100124 * @type {Map<ObjectExpression, PropDefaultFunctionContext[]> }
101125 */
102126 const vueObjectPropsContexts = new Map ( )
127+ /**
128+ * @type { {node: CallExpression, props:PropDefaultFunctionContext[]}[] }
129+ */
130+ const scriptSetupPropsContexts = [ ]
103131
104132 /**
105133 * @typedef {object } ScopeStack
@@ -194,7 +222,7 @@ module.exports = {
194222
195223 /**
196224 * @param {* } node
197- * @param {ComponentObjectProp } prop
225+ * @param {ComponentObjectProp | ComponentTypeProp } prop
198226 * @param {Iterable<string> } expectedTypeNames
199227 */
200228 function report ( node , prop , expectedTypeNames ) {
@@ -213,127 +241,196 @@ module.exports = {
213241 } )
214242 }
215243
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' ) {
234255 const type = getPropertyNode ( prop . value , 'type' )
235256 if ( ! type ) continue
236257
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 )
243259
244260 const def = getPropertyNode ( prop . value , 'default' )
245261 if ( ! def ) continue
246262
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 )
248277
249- if ( ! defType ) continue
278+ if ( ! defType ) continue
250279
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
256284 }
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
263291 )
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 ) ) {
266299 continue
267300 }
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
278343 } )
279344 }
280345 }
281- }
282- vueObjectPropsContexts . set ( obj , propContexts )
346+ } ,
347+ ':function:exit' : onFunctionExit
283348 } ,
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+ }
294374
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
299389
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+ }
303392 }
304393 }
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
320422 }
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
332423
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+ }
334428 }
429+ } ,
430+ onDefinePropsExit ( node ) {
431+ scriptSetupPropsContexts . pop ( )
335432 }
336- }
337- } )
433+ } )
434+ )
338435 }
339436}
0 commit comments