@@ -12,6 +12,7 @@ import {
1212 isFunctionNode ,
1313 ProgramOrFunctionNode ,
1414 isProgramOrFunctionNode ,
15+ trackImports ,
1516} from "../utils" ;
1617
1718const { findVariable, getFunctionHeadLocation } = ASTUtils ;
@@ -258,6 +259,9 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
258259 const scopeStack = new ScopeStack ( ) ;
259260 const { currentScope } = scopeStack ;
260261
262+ /** Tracks imports from 'solid-js', handling aliases. */
263+ const { matchImport, handleImportDeclaration } = trackImports ( ) ;
264+
261265 /** Populates the function stack. */
262266 const onFunctionEnter = ( node : ProgramOrFunctionNode ) => {
263267 if ( isFunctionNode ( node ) && scopeStack . syncCallbacks . has ( node ) ) {
@@ -512,7 +516,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
512516 ) {
513517 if (
514518 node . callee . type === "Identifier" &&
515- [ "untrack" , "batch" , "onCleanup" , "onError" , "produce" ] . includes ( node . callee . name )
519+ matchImport ( [ "untrack" , "batch" , "onCleanup" , "onError" , "produce" ] , node . callee . name )
516520 ) {
517521 // These Solid APIs take callbacks that run in the current scope
518522 scopeStack . syncCallbacks . add ( node . arguments [ 0 ] ) ;
@@ -530,7 +534,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
530534 }
531535 if (
532536 node . callee . type === "Identifier" &&
533- [ "createSignal" , "createStore" ] . includes ( node . callee . name ) &&
537+ matchImport ( [ "createSignal" , "createStore" ] , node . callee . name ) &&
534538 node . parent ?. type === "VariableDeclarator"
535539 ) {
536540 // Allow using reactive variables in state setter if the current scope is tracked.
@@ -564,37 +568,37 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
564568 // Mark return values of certain functions as reactive
565569 if ( init . type === "CallExpression" && init . callee . type === "Identifier" ) {
566570 const { callee } = init ;
567- if ( callee . name === "createSignal" || callee . name === "useTransition" ) {
571+ if ( matchImport ( [ "createSignal" , "useTransition" ] , callee . name ) ) {
568572 const signal = id && getNthDestructuredVar ( id , 0 , context . getScope ( ) ) ;
569573 if ( signal ) {
570574 scopeStack . pushSignal ( signal , currentScope ( ) . node ) ;
571575 } else {
572576 warnShouldDestructure ( id ?? init , "first" ) ;
573577 }
574- } else if ( callee . name === "createMemo" || callee . name === "createSelector" ) {
578+ } else if ( matchImport ( [ "createMemo" , "createSelector" ] , callee . name ) ) {
575579 const memo = id && getReturnedVar ( id , context . getScope ( ) ) ;
576580 // memos act like signals
577581 if ( memo ) {
578582 scopeStack . pushSignal ( memo , currentScope ( ) . node ) ;
579583 } else {
580584 warnShouldAssign ( id ?? init ) ;
581585 }
582- } else if ( callee . name === "createStore" ) {
586+ } else if ( matchImport ( "createStore" , callee . name ) ) {
583587 const store = id && getNthDestructuredVar ( id , 0 , context . getScope ( ) ) ;
584588 // stores act like props
585589 if ( store ) {
586590 scopeStack . pushProps ( store , currentScope ( ) . node ) ;
587591 } else {
588592 warnShouldDestructure ( id ?? init , "first" ) ;
589593 }
590- } else if ( callee . name === "mergeProps" ) {
594+ } else if ( matchImport ( "mergeProps" , callee . name ) ) {
591595 const merged = id && getReturnedVar ( id , context . getScope ( ) ) ;
592596 if ( merged ) {
593597 scopeStack . pushProps ( merged , currentScope ( ) . node ) ;
594598 } else {
595599 warnShouldAssign ( id ?? init ) ;
596600 }
597- } else if ( callee . name === "splitProps" ) {
601+ } else if ( matchImport ( "splitProps" , callee . name ) ) {
598602 // splitProps can return an unbounded array of props variables, though it's most often two
599603 if ( id ?. type === "ArrayPattern" ) {
600604 const vars = id . elements
@@ -614,13 +618,13 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
614618 scopeStack . pushProps ( vars , currentScope ( ) . node ) ;
615619 }
616620 }
617- } else if ( callee . name === "createResource" ) {
621+ } else if ( matchImport ( "createResource" , callee . name ) ) {
618622 // createResource return value has reactive .loading and .error
619623 const resourceReturn = id && getNthDestructuredVar ( id , 0 , context . getScope ( ) ) ;
620624 if ( resourceReturn ) {
621625 scopeStack . pushProps ( resourceReturn , currentScope ( ) . node ) ;
622626 }
623- } else if ( callee . name === "createMutable" ) {
627+ } else if ( matchImport ( "createMutable" , callee . name ) ) {
624628 const mutable = id && getReturnedVar ( id , context . getScope ( ) ) ;
625629 if ( mutable ) {
626630 scopeStack . pushProps ( mutable , currentScope ( ) . node ) ;
@@ -669,23 +673,26 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
669673 const callee = node . callee ;
670674 const arg0 = node . arguments [ 0 ] ;
671675 if (
672- [
673- "createMemo" ,
674- "children" ,
675- "createEffect" ,
676- "createRenderEffect" ,
677- "createDeferred" ,
678- "createComputed" ,
679- "createSelector" ,
680- ] . includes ( callee . name ) ||
681- ( callee . name === "createResource" && node . arguments . length >= 2 )
676+ matchImport (
677+ [
678+ "createMemo" ,
679+ "children" ,
680+ "createEffect" ,
681+ "createRenderEffect" ,
682+ "createDeferred" ,
683+ "createComputed" ,
684+ "createSelector" ,
685+ ] ,
686+ callee . name
687+ ) ||
688+ ( matchImport ( "createResource" , callee . name ) && node . arguments . length >= 2 )
682689 ) {
683690 // createEffect, createMemo, etc. fn arg, and createResource optional
684691 // `source` first argument may be a signal
685692 pushTrackedScope ( arg0 , "function" ) ;
686693 } else if (
694+ matchImport ( "onMount" , callee . name ) ||
687695 [
688- "onMount" ,
689696 "setInterval" ,
690697 "setTimeout" ,
691698 "setImmediate" ,
@@ -698,9 +705,9 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
698705 // to updates to reactive variables; it's okay to poll the current
699706 // value. Consider them event-handler tracked scopes for our purposes.
700707 pushTrackedScope ( arg0 , "called-function" ) ;
701- } else if ( callee . name === "createMutable" && arg0 ) {
708+ } else if ( matchImport ( "createMutable" , callee . name ) && arg0 ) {
702709 pushTrackedScope ( arg0 , "expression" ) ;
703- } else if ( callee . name === "on" ) {
710+ } else if ( matchImport ( "on" , callee . name ) ) {
704711 // on accepts a signal or an array of signals as its first argument,
705712 // and a tracking function as its second
706713 if ( arg0 ) {
@@ -716,7 +723,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
716723 // Since dependencies are known, function can be async
717724 pushTrackedScope ( node . arguments [ 1 ] , "called-function" ) ;
718725 }
719- } else if ( callee . name === "runWithOwner" ) {
726+ } else if ( matchImport ( "runWithOwner" , callee . name ) ) {
720727 // runWithOwner(owner, fn) only creates a tracked scope if `owner =
721728 // getOwner()` runs in a tracked scope. If owner is a variable,
722729 // attempt to detect if it's a tracked scope or not, but if this
@@ -733,7 +740,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
733740 decl . node . type === "VariableDeclarator" &&
734741 decl . node . init ?. type === "CallExpression" &&
735742 decl . node . init . callee . type === "Identifier" &&
736- decl . node . init . callee . name === "getOwner"
743+ matchImport ( "getOwner" , decl . node . init . callee . name )
737744 ) {
738745 // Check if the function in which getOwner() is called is a tracked scope. If the scopeStack
739746 // has moved on from that scope already, assume it's tracked, since that's less intrusive.
@@ -770,7 +777,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
770777 // function, a tracked scope expecting a reactive function. All of the
771778 // track function's references where it's called push a tracked scope.
772779 if ( node . init ?. type === "CallExpression" && node . init . callee . type === "Identifier" ) {
773- if ( [ "createReactive" , "createReaction" ] . includes ( node . init . callee . name ) ) {
780+ if ( matchImport ( [ "createReactive" , "createReaction" ] , node . init . callee . name ) ) {
774781 const track = getReturnedVar ( node . id , context . getScope ( ) ) ;
775782 if ( track ) {
776783 for ( const reference of track . references ) {
@@ -808,6 +815,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
808815 } ;
809816
810817 return {
818+ ImportDeclaration : handleImportDeclaration ,
811819 JSXExpressionContainer ( node : T . JSXExpressionContainer ) {
812820 checkForTrackedScopes ( node ) ;
813821 } ,
@@ -843,7 +851,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
843851 if ( element . openingElement . name . type === "JSXIdentifier" ) {
844852 const tagName = element . openingElement . name . name ;
845853 if (
846- tagName === "For" &&
854+ matchImport ( "For" , tagName ) &&
847855 node . params . length === 2 &&
848856 node . params [ 1 ] . type === "Identifier"
849857 ) {
@@ -852,7 +860,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
852860 scopeStack . pushSignal ( index , currentScope ( ) . node ) ;
853861 }
854862 } else if (
855- tagName === "Index" &&
863+ matchImport ( "Index" , tagName ) &&
856864 node . params . length >= 1 &&
857865 node . params [ 0 ] . type === "Identifier"
858866 ) {
0 commit comments