@@ -271,11 +271,31 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
271271 /** Tracks imports from 'solid-js', handling aliases. */
272272 const { matchImport, handleImportDeclaration } = trackImports ( ) ;
273273
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+
274291 /** Populates the function stack. */
275292 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+ }
279299 }
280300 scopeStack . push ( new ScopeStackItem ( node ) ) ;
281301 } ;
@@ -381,34 +401,26 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
381401
382402 /** Performs all analysis and reporting. */
383403 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+
384417 // Ignore sync callbacks like Array#forEach and certain Solid primitives.
385418 // In this case only, currentScopeNode !== currentScope().node, but we're
386419 // returning early so it doesn't matter.
387420 if ( isFunctionNode ( currentScopeNode ) && scopeStack . syncCallbacks . has ( currentScopeNode ) ) {
388421 return ;
389422 }
390423
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-
412424 // Iterate through all usages of (derived) signals in the current scope
413425 for ( const { reference, declarationScope } of scopeStack . consumeSignalReferencesInScope ( ) ) {
414426 const identifier = reference . identifier ;
0 commit comments