1
- import { TSESLint , TSESTree as T , ASTUtils } from "@typescript-eslint/utils" ;
2
- import invariant from ' tiny-invariant'
1
+ import { TSESLint , TSESTree as T , ESLintUtils , ASTUtils } from "@typescript-eslint/utils" ;
2
+ import invariant from " tiny-invariant" ;
3
3
import {
4
4
ProgramOrFunctionNode ,
5
5
FunctionNode ,
@@ -10,6 +10,7 @@ import {
10
10
import { ReactivityScope , VirtualReference } from "./analyze" ;
11
11
import type { ReactivityPlugin , ReactivityPluginApi } from "./pluginApi" ;
12
12
13
+ const createRule = ESLintUtils . RuleCreator . withoutDocs ;
13
14
const { findVariable } = ASTUtils ;
14
15
15
16
function parsePath ( path : string ) : Array < string > | null {
@@ -24,18 +25,7 @@ function parsePath(path: string): Array<string> | null {
24
25
return null ;
25
26
}
26
27
27
- type MessageIds =
28
- | "noWrite"
29
- | "untrackedReactive"
30
- | "expectedFunctionGotExpression"
31
- | "badSignal"
32
- | "badUnnamedDerivedSignal"
33
- | "shouldDestructure"
34
- | "shouldAssign"
35
- | "noAsyncTrackedScope"
36
- | "jsxReactiveVariable" ;
37
-
38
- const rule : TSESLint . RuleModule < MessageIds , [ ] > = {
28
+ export default createRule ( {
39
29
meta : {
40
30
type : "problem" ,
41
31
docs : {
@@ -64,10 +54,11 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
64
54
jsxReactiveVariable : "This variable should not be used as a JSX element." ,
65
55
} ,
66
56
} ,
57
+ defaultOptions : [ ] ,
67
58
create ( context ) {
68
59
const sourceCode = context . getSourceCode ( ) ;
69
60
70
- const { handleImportDeclaration , matchImport, matchLocalToModule } = trackImports ( ) ;
61
+ const { matchImport, matchLocalToModule } = trackImports ( sourceCode . ast ) ;
71
62
72
63
const root = new ReactivityScope ( sourceCode . ast , null ) ;
73
64
const syncCallbacks = new Set < FunctionNode > ( ) ;
@@ -116,20 +107,40 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
116
107
}
117
108
118
109
function VirtualReference ( node : T . Node ) : VirtualReference {
119
- return { node, declarationScope : }
110
+ return { node, declarationScope : null } ;
120
111
}
121
112
122
113
/**
123
114
* Given what's usually a CallExpression and a description of how the expression must be used
124
115
* in order to be accessed reactively, return a list of virtual references for each place where
125
116
* a reactive expression is accessed.
126
- * `path` is a string formatted according to `pluginApi`.
117
+ * `path` is a array of segments parsed by `parsePath` according to `pluginApi`.
127
118
*/
128
- function * getReferences ( node : T . Expression , path : string , allowMutable = false ) : Generator < VirtualReference > {
119
+ function getReferences (
120
+ node : T . Node ,
121
+ path : string | null ,
122
+ allowMutable = false
123
+ ) : Array < VirtualReference > {
129
124
node = ignoreTransparentWrappers ( node , "up" ) ;
130
- if ( ! path ) {
125
+ const parsedPathOuter = path != null ? parsePath ( path ) : null ;
126
+ const eqCount = parsedPathOuter ?. reduce ( ( c , segment ) => c + + ( segment === '=' ) , 0 ) ?? 0 ;
127
+ if ( eqCount > 1 ) {
128
+ throw new Error ( `'${ path } ' must have 0 or 1 '=' characters, has ${ eqCount } ` )
129
+ }
130
+ const hasEq = eqCount === 1 ;
131
+
132
+ let declarationScope = hasEq ? null : context . getScope ( ) ;
133
+
134
+ function * recursiveGenerator ( node : T . Node , parsedPath : Array < string > | null ) {
135
+
136
+
137
+ if ( ! parsedPath ) {
131
138
yield VirtualReference ( node ) ;
132
139
} else if ( node . parent ?. type === "VariableDeclarator" && node . parent . init === node ) {
140
+ yield getReferences ( node . parent . id ) ;
141
+ } else if ( node . type === "Identifier" ) {
142
+
143
+ }
133
144
const { id } = node . parent ;
134
145
if ( id . type === "Identifier" ) {
135
146
const variable = findVariable ( context . getScope ( ) , id ) ;
@@ -144,31 +155,34 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
144
155
context . report ( { node : reference . identifier , messageId : "noWrite" } ) ;
145
156
}
146
157
} else {
147
- yield * getReferences ( reference . identifier , path ) ;
158
+ yield * getReferences ( reference . identifier , parsedPath , allowMutable ) ;
148
159
}
149
160
}
150
161
}
151
162
} else if ( id . type === "ArrayPattern" ) {
152
- const parsedPath = parsePath ( path )
153
- if ( parsedPath ) {
154
- const newPath = path . substring ( match [ 0 ] . length ) ;
155
- const index = match [ 1 ]
156
- if ( index === '*' ) {
157
-
158
- } else {
159
-
163
+ if ( parsedPath [ 0 ] === "[]" ) {
164
+ for ( const el of id . elements ) {
165
+ if ( ! el ) {
166
+ // ignore
167
+ } else if ( el . type === "Identifier" ) {
168
+ yield * getReferences ( el , parsedPath . slice ( 1 ) , allowMutable ) ;
169
+ } else if ( el . type === "RestElement" ) {
170
+ yield * getReferences ( el . argument , parsedPath , allowMutable ) ;
171
+ }
160
172
}
173
+ } else {
161
174
}
162
-
163
175
}
164
- }
176
+
177
+
178
+ return Array . from ( recursiveGenerator ( node , parsePath ( path ) ) ) ;
165
179
}
166
180
167
181
function distributeReferences ( root : ReactivityScope , references : Array < VirtualReference > ) {
168
182
references . forEach ( ( ref ) => {
169
183
const range = ref . node . range ;
170
184
const scope = root . deepestScopeContaining ( range ) ;
171
- invariant ( scope != null )
185
+ invariant ( scope != null ) ;
172
186
scope . references . push ( ref ) ;
173
187
} ) ;
174
188
}
@@ -238,4 +252,4 @@ const rule: TSESLint.RuleModule<MessageIds, []> = {
238
252
} ,
239
253
} ;
240
254
} ,
241
- } ;
255
+ } ) ;
0 commit comments