@@ -12,6 +12,7 @@ import {
12
12
isFunctionNode ,
13
13
ProgramOrFunctionNode ,
14
14
isProgramOrFunctionNode ,
15
+ trackImports ,
15
16
} from "../utils" ;
16
17
17
18
const { findVariable, getFunctionHeadLocation } = ASTUtils ;
@@ -258,6 +259,9 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
258
259
const scopeStack = new ScopeStack ( ) ;
259
260
const { currentScope } = scopeStack ;
260
261
262
+ /** Tracks imports from 'solid-js', handling aliases. */
263
+ const { matchImport, handleImportDeclaration } = trackImports ( ) ;
264
+
261
265
/** Populates the function stack. */
262
266
const onFunctionEnter = ( node : ProgramOrFunctionNode ) => {
263
267
if ( isFunctionNode ( node ) && scopeStack . syncCallbacks . has ( node ) ) {
@@ -512,7 +516,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
512
516
) {
513
517
if (
514
518
node . callee . type === "Identifier" &&
515
- [ "untrack" , "batch" , "onCleanup" , "onError" , "produce" ] . includes ( node . callee . name )
519
+ matchImport ( [ "untrack" , "batch" , "onCleanup" , "onError" , "produce" ] , node . callee . name )
516
520
) {
517
521
// These Solid APIs take callbacks that run in the current scope
518
522
scopeStack . syncCallbacks . add ( node . arguments [ 0 ] ) ;
@@ -530,7 +534,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
530
534
}
531
535
if (
532
536
node . callee . type === "Identifier" &&
533
- [ "createSignal" , "createStore" ] . includes ( node . callee . name ) &&
537
+ matchImport ( [ "createSignal" , "createStore" ] , node . callee . name ) &&
534
538
node . parent ?. type === "VariableDeclarator"
535
539
) {
536
540
// Allow using reactive variables in state setter if the current scope is tracked.
@@ -564,37 +568,37 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
564
568
// Mark return values of certain functions as reactive
565
569
if ( init . type === "CallExpression" && init . callee . type === "Identifier" ) {
566
570
const { callee } = init ;
567
- if ( callee . name === "createSignal" || callee . name === "useTransition" ) {
571
+ if ( matchImport ( [ "createSignal" , "useTransition" ] , callee . name ) ) {
568
572
const signal = id && getNthDestructuredVar ( id , 0 , context . getScope ( ) ) ;
569
573
if ( signal ) {
570
574
scopeStack . pushSignal ( signal , currentScope ( ) . node ) ;
571
575
} else {
572
576
warnShouldDestructure ( id ?? init , "first" ) ;
573
577
}
574
- } else if ( callee . name === "createMemo" || callee . name === "createSelector" ) {
578
+ } else if ( matchImport ( [ "createMemo" , "createSelector" ] , callee . name ) ) {
575
579
const memo = id && getReturnedVar ( id , context . getScope ( ) ) ;
576
580
// memos act like signals
577
581
if ( memo ) {
578
582
scopeStack . pushSignal ( memo , currentScope ( ) . node ) ;
579
583
} else {
580
584
warnShouldAssign ( id ?? init ) ;
581
585
}
582
- } else if ( callee . name === "createStore" ) {
586
+ } else if ( matchImport ( "createStore" , callee . name ) ) {
583
587
const store = id && getNthDestructuredVar ( id , 0 , context . getScope ( ) ) ;
584
588
// stores act like props
585
589
if ( store ) {
586
590
scopeStack . pushProps ( store , currentScope ( ) . node ) ;
587
591
} else {
588
592
warnShouldDestructure ( id ?? init , "first" ) ;
589
593
}
590
- } else if ( callee . name === "mergeProps" ) {
594
+ } else if ( matchImport ( "mergeProps" , callee . name ) ) {
591
595
const merged = id && getReturnedVar ( id , context . getScope ( ) ) ;
592
596
if ( merged ) {
593
597
scopeStack . pushProps ( merged , currentScope ( ) . node ) ;
594
598
} else {
595
599
warnShouldAssign ( id ?? init ) ;
596
600
}
597
- } else if ( callee . name === "splitProps" ) {
601
+ } else if ( matchImport ( "splitProps" , callee . name ) ) {
598
602
// splitProps can return an unbounded array of props variables, though it's most often two
599
603
if ( id ?. type === "ArrayPattern" ) {
600
604
const vars = id . elements
@@ -614,13 +618,13 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
614
618
scopeStack . pushProps ( vars , currentScope ( ) . node ) ;
615
619
}
616
620
}
617
- } else if ( callee . name === "createResource" ) {
621
+ } else if ( matchImport ( "createResource" , callee . name ) ) {
618
622
// createResource return value has reactive .loading and .error
619
623
const resourceReturn = id && getNthDestructuredVar ( id , 0 , context . getScope ( ) ) ;
620
624
if ( resourceReturn ) {
621
625
scopeStack . pushProps ( resourceReturn , currentScope ( ) . node ) ;
622
626
}
623
- } else if ( callee . name === "createMutable" ) {
627
+ } else if ( matchImport ( "createMutable" , callee . name ) ) {
624
628
const mutable = id && getReturnedVar ( id , context . getScope ( ) ) ;
625
629
if ( mutable ) {
626
630
scopeStack . pushProps ( mutable , currentScope ( ) . node ) ;
@@ -669,23 +673,26 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
669
673
const callee = node . callee ;
670
674
const arg0 = node . arguments [ 0 ] ;
671
675
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 )
682
689
) {
683
690
// createEffect, createMemo, etc. fn arg, and createResource optional
684
691
// `source` first argument may be a signal
685
692
pushTrackedScope ( arg0 , "function" ) ;
686
693
} else if (
694
+ matchImport ( "onMount" , callee . name ) ||
687
695
[
688
- "onMount" ,
689
696
"setInterval" ,
690
697
"setTimeout" ,
691
698
"setImmediate" ,
@@ -698,9 +705,9 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
698
705
// to updates to reactive variables; it's okay to poll the current
699
706
// value. Consider them event-handler tracked scopes for our purposes.
700
707
pushTrackedScope ( arg0 , "called-function" ) ;
701
- } else if ( callee . name === "createMutable" && arg0 ) {
708
+ } else if ( matchImport ( "createMutable" , callee . name ) && arg0 ) {
702
709
pushTrackedScope ( arg0 , "expression" ) ;
703
- } else if ( callee . name === "on" ) {
710
+ } else if ( matchImport ( "on" , callee . name ) ) {
704
711
// on accepts a signal or an array of signals as its first argument,
705
712
// and a tracking function as its second
706
713
if ( arg0 ) {
@@ -716,7 +723,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
716
723
// Since dependencies are known, function can be async
717
724
pushTrackedScope ( node . arguments [ 1 ] , "called-function" ) ;
718
725
}
719
- } else if ( callee . name === "runWithOwner" ) {
726
+ } else if ( matchImport ( "runWithOwner" , callee . name ) ) {
720
727
// runWithOwner(owner, fn) only creates a tracked scope if `owner =
721
728
// getOwner()` runs in a tracked scope. If owner is a variable,
722
729
// attempt to detect if it's a tracked scope or not, but if this
@@ -733,7 +740,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
733
740
decl . node . type === "VariableDeclarator" &&
734
741
decl . node . init ?. type === "CallExpression" &&
735
742
decl . node . init . callee . type === "Identifier" &&
736
- decl . node . init . callee . name === "getOwner"
743
+ matchImport ( "getOwner" , decl . node . init . callee . name )
737
744
) {
738
745
// Check if the function in which getOwner() is called is a tracked scope. If the scopeStack
739
746
// 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, []> = {
770
777
// function, a tracked scope expecting a reactive function. All of the
771
778
// track function's references where it's called push a tracked scope.
772
779
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 ) ) {
774
781
const track = getReturnedVar ( node . id , context . getScope ( ) ) ;
775
782
if ( track ) {
776
783
for ( const reference of track . references ) {
@@ -808,6 +815,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
808
815
} ;
809
816
810
817
return {
818
+ ImportDeclaration : handleImportDeclaration ,
811
819
JSXExpressionContainer ( node : T . JSXExpressionContainer ) {
812
820
checkForTrackedScopes ( node ) ;
813
821
} ,
@@ -843,7 +851,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
843
851
if ( element . openingElement . name . type === "JSXIdentifier" ) {
844
852
const tagName = element . openingElement . name . name ;
845
853
if (
846
- tagName === "For" &&
854
+ matchImport ( "For" , tagName ) &&
847
855
node . params . length === 2 &&
848
856
node . params [ 1 ] . type === "Identifier"
849
857
) {
@@ -852,7 +860,7 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
852
860
scopeStack . pushSignal ( index , currentScope ( ) . node ) ;
853
861
}
854
862
} else if (
855
- tagName === "Index" &&
863
+ matchImport ( "Index" , tagName ) &&
856
864
node . params . length >= 1 &&
857
865
node . params [ 0 ] . type === "Identifier"
858
866
) {
0 commit comments