@@ -271,11 +271,31 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
271
271
/** Tracks imports from 'solid-js', handling aliases. */
272
272
const { matchImport, handleImportDeclaration } = trackImports ( ) ;
273
273
274
+ /** Workaround for #61 */
275
+ const markPropsOnCondition = ( node : FunctionNode , cb : ( props : T . Identifier ) => boolean ) => {
276
+ if (
277
+ node . params . length === 1 &&
278
+ node . params [ 0 ] . type === "Identifier" &&
279
+ node . parent ?. type !== "JSXExpressionContainer" && // "render props" aren't components
280
+ node . parent ?. type !== "TemplateLiteral" && // inline functions in tagged template literals aren't components
281
+ cb ( node . params [ 0 ] )
282
+ ) {
283
+ // This function is a component, consider its parameter a props
284
+ const propsParam = findVariable ( context . getScope ( ) , node . params [ 0 ] ) ;
285
+ if ( propsParam ) {
286
+ scopeStack . pushProps ( propsParam , node ) ;
287
+ }
288
+ }
289
+ } ;
290
+
274
291
/** Populates the function stack. */
275
292
const onFunctionEnter = ( node : ProgramOrFunctionNode ) => {
276
- if ( isFunctionNode ( node ) && scopeStack . syncCallbacks . has ( node ) ) {
277
- // Ignore sync callbacks like Array#forEach and certain Solid primitives
278
- return ;
293
+ if ( isFunctionNode ( node ) ) {
294
+ markPropsOnCondition ( node , ( props ) => isPropsByName ( props . name ) ) ;
295
+ if ( scopeStack . syncCallbacks . has ( node ) ) {
296
+ // Ignore sync callbacks like Array#forEach and certain Solid primitives
297
+ return ;
298
+ }
279
299
}
280
300
scopeStack . push ( new ScopeStackItem ( node ) ) ;
281
301
} ;
@@ -381,34 +401,26 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
381
401
382
402
/** Performs all analysis and reporting. */
383
403
const onFunctionExit = ( currentScopeNode : ProgramOrFunctionNode ) => {
404
+ // If this function is a component, add its props as a reactive variable
405
+ if ( isFunctionNode ( currentScopeNode ) ) {
406
+ markPropsOnCondition (
407
+ currentScopeNode ,
408
+ ( props ) =>
409
+ ! isPropsByName ( props . name ) && // already added in markPropsOnEnter
410
+ currentScope ( ) . hasJSX &&
411
+ // begins with lowercase === not component
412
+ ( currentScopeNode . type !== "FunctionDeclaration" ||
413
+ ! currentScopeNode . id ?. name ?. match ( / ^ [ a - z ] / ) )
414
+ ) ;
415
+ }
416
+
384
417
// Ignore sync callbacks like Array#forEach and certain Solid primitives.
385
418
// In this case only, currentScopeNode !== currentScope().node, but we're
386
419
// returning early so it doesn't matter.
387
420
if ( isFunctionNode ( currentScopeNode ) && scopeStack . syncCallbacks . has ( currentScopeNode ) ) {
388
421
return ;
389
422
}
390
423
391
- // If this function is a component, add its props as a reactive variable
392
- if ( isFunctionNode ( currentScopeNode ) && currentScopeNode . params . length === 1 ) {
393
- const paramsNode = currentScopeNode . params [ 0 ] ;
394
- if (
395
- paramsNode ?. type === "Identifier" &&
396
- ( ( currentScope ( ) . hasJSX &&
397
- ( currentScopeNode . type !== "FunctionDeclaration" ||
398
- ! currentScopeNode . id ?. name ?. match ( / ^ [ a - z ] / ) ) ) ||
399
- // begins with lowercase === not component
400
- isPropsByName ( paramsNode . name ) ) &&
401
- currentScopeNode . parent ?. type !== "JSXExpressionContainer" && // "render props" aren't components
402
- currentScopeNode . parent ?. type !== "TemplateLiteral" // inline functions in tagged template literals aren't components
403
- ) {
404
- // This function is a component, consider its parameter a props
405
- const propsParam = findVariable ( context . getScope ( ) , paramsNode ) ;
406
- if ( propsParam ) {
407
- scopeStack . pushProps ( propsParam ) ;
408
- }
409
- }
410
- }
411
-
412
424
// Iterate through all usages of (derived) signals in the current scope
413
425
for ( const { reference, declarationScope } of scopeStack . consumeSignalReferencesInScope ( ) ) {
414
426
const identifier = reference . identifier ;
0 commit comments