@@ -18,19 +18,18 @@ import type ESTree from "estree";
18
18
19
19
const RESERVED_NAMES = new Set < string > ( [ "$$props" , "$$restProps" , "$$slots" ] ) ;
20
20
/**
21
- * Analyze <script> block script
22
- * Modify the source code to provide correct type information for svelte special variables and scopes.
21
+ * Analyze TypeScript source code.
22
+ * Generate virtual code to provide correct type information for Svelte store reference namess and scopes.
23
23
* See https://github.com/ota-meshi/svelte-eslint-parser/blob/main/docs/internal-mechanism.md#scope-types
24
24
*/
25
- export function analyzeScript (
25
+ export function analyzeTypeScript (
26
26
code : { script : string ; render : string } ,
27
27
attrs : Record < string , string | undefined > ,
28
28
parserOptions : any
29
29
) : VirtualTypeScriptContext {
30
30
const ctx = new VirtualTypeScriptContext ( code . script + code . render ) ;
31
31
ctx . appendOriginal ( / ^ \s * / u. exec ( code . script ) ! [ 0 ] . length ) ;
32
32
33
- // We will need to parse TypeScript once to extract the reactive variables.
34
33
const result = parseScriptWithoutAnalyzeScope (
35
34
code . script + code . render ,
36
35
attrs ,
@@ -43,56 +42,34 @@ export function analyzeScript(
43
42
44
43
ctx . _beforeResult = result ;
45
44
46
- const throughIds = analyzeStoreReferenceNamesAndExtractThroughIdentifiers (
47
- result ,
48
- ctx
49
- ) ;
45
+ analyzeStoreReferenceNames ( result , ctx ) ;
50
46
51
- analyzeReactiveScopes ( result , throughIds , ctx ) ;
47
+ analyzeReactiveScopes ( result , ctx ) ;
52
48
53
- ctx . appendOriginal ( code . script . length ) ;
54
- const renderFunctionName = ctx . generateUniqueId ( "render" ) ;
55
- ctx . appendScript ( `function ${ renderFunctionName } (){` ) ;
56
- ctx . appendOriginalToEnd ( ) ;
57
- ctx . appendScript ( `}` ) ;
58
- ctx . restoreContext . addRestoreStatementProcess ( ( node , result ) => {
59
- if (
60
- node . type !== "FunctionDeclaration" ||
61
- node . id . name !== renderFunctionName
62
- ) {
63
- return false ;
64
- }
65
- const program = result . ast ;
66
- program . body . splice ( program . body . indexOf ( node ) , 1 , ...node . body . body ) ;
67
- for ( const body of node . body . body ) {
68
- body . parent = program ;
69
- }
70
-
71
- const scopeManager = result . scopeManager as ScopeManager ;
72
- removeFunctionScope ( node , scopeManager ) ;
73
- return true ;
74
- } ) ;
49
+ analyzeRenderScopes ( code , ctx ) ;
75
50
76
51
return ctx ;
77
52
}
78
53
79
54
/**
80
- * Analyze the store reference names and extract through identifiers.
55
+ * Analyze the store reference names.
56
+ * Insert type definitions code to provide correct type information for variables that begin with `$`.
81
57
*/
82
- function analyzeStoreReferenceNamesAndExtractThroughIdentifiers (
58
+ function analyzeStoreReferenceNames (
83
59
result : TSESParseForESLintResult ,
84
60
ctx : VirtualTypeScriptContext
85
61
) {
86
62
const scopeManager = result . scopeManager ;
87
63
const programScope = getProgramScope ( scopeManager as ScopeManager ) ;
88
- const throughIds : ( TSESTree . Identifier | TSESTree . JSXIdentifier ) [ ] = [ ] ;
89
64
const maybeStoreRefNames = new Set < string > ( ) ;
90
65
91
66
for ( const reference of scopeManager . globalScope ! . through ) {
92
- throughIds . push ( reference . identifier ) ;
93
67
if (
68
+ // Begin with `$`.
94
69
reference . identifier . name . startsWith ( "$" ) &&
70
+ // Ignore it is a reserved variable.
95
71
! RESERVED_NAMES . has ( reference . identifier . name ) &&
72
+ // Ignore if it is already defined.
96
73
! programScope . set . has ( reference . identifier . name )
97
74
) {
98
75
maybeStoreRefNames . add ( reference . identifier . name ) ;
@@ -159,18 +136,20 @@ function analyzeStoreReferenceNamesAndExtractThroughIdentifiers(
159
136
} ) ;
160
137
}
161
138
}
162
-
163
- return throughIds ;
164
139
}
165
140
166
141
/**
167
142
* Analyze the reactive scopes.
143
+ * Transform source code to provide the correct type information in the `$:` statements.
168
144
*/
169
145
function analyzeReactiveScopes (
170
146
result : TSESParseForESLintResult ,
171
- throughIds : ( TSESTree . Identifier | TSESTree . JSXIdentifier ) [ ] ,
172
147
ctx : VirtualTypeScriptContext
173
148
) {
149
+ const scopeManager = result . scopeManager ;
150
+ const throughIds = scopeManager . globalScope ! . through . map (
151
+ ( reference ) => reference . identifier
152
+ ) ;
174
153
for ( const statement of result . ast . body ) {
175
154
if ( statement . type === "LabeledStatement" && statement . label . name === "$" ) {
176
155
if (
@@ -204,6 +183,38 @@ function analyzeReactiveScopes(
204
183
}
205
184
}
206
185
186
+ /**
187
+ * Analyze the render scopes.
188
+ * Transform source code to provide the correct type information in the HTML templates.
189
+ */
190
+ function analyzeRenderScopes (
191
+ code : { script : string ; render : string } ,
192
+ ctx : VirtualTypeScriptContext
193
+ ) {
194
+ ctx . appendOriginal ( code . script . length ) ;
195
+ const renderFunctionName = ctx . generateUniqueId ( "render" ) ;
196
+ ctx . appendScript ( `function ${ renderFunctionName } (){` ) ;
197
+ ctx . appendOriginalToEnd ( ) ;
198
+ ctx . appendScript ( `}` ) ;
199
+ ctx . restoreContext . addRestoreStatementProcess ( ( node , result ) => {
200
+ if (
201
+ node . type !== "FunctionDeclaration" ||
202
+ node . id . name !== renderFunctionName
203
+ ) {
204
+ return false ;
205
+ }
206
+ const program = result . ast ;
207
+ program . body . splice ( program . body . indexOf ( node ) , 1 , ...node . body . body ) ;
208
+ for ( const body of node . body . body ) {
209
+ body . parent = program ;
210
+ }
211
+
212
+ const scopeManager = result . scopeManager as ScopeManager ;
213
+ removeFunctionScope ( node , scopeManager ) ;
214
+ return true ;
215
+ } ) ;
216
+ }
217
+
207
218
/**
208
219
* Transform for `$: id = ...` to `$: let id = ...`
209
220
*/
@@ -230,34 +241,58 @@ function transformForDeclareReactiveVar(
230
241
// $: let {id} = fn()
231
242
// function fn () { return foo; }
232
243
244
+ /**
245
+ * The opening paren tokens for
246
+ * `$: ({id} = foo);`
247
+ * ^
248
+ */
233
249
const openParens : TSESTree . Token [ ] = [ ] ;
250
+ /**
251
+ * The equal token for
252
+ * `$: ({id} = foo);`
253
+ * ^
254
+ */
234
255
let eq : TSESTree . Token | null = null ;
256
+ /**
257
+ * The closing paren tokens for
258
+ * `$: ({id} = foo);`
259
+ * ^
260
+ */
235
261
const closeParens : TSESTree . Token [ ] = [ ] ;
262
+ /**
263
+ * The closing paren token for
264
+ * `$: id = (foo);`
265
+ * ^
266
+ */
267
+ let expressionCloseParen : TSESTree . Token | null = null ;
236
268
const startIndex = sortedLastIndex (
237
269
tokens ,
238
270
( target ) => target . range [ 0 ] - statement . range [ 0 ]
239
271
) ;
240
272
for ( let index = startIndex ; index < tokens . length ; index ++ ) {
241
273
const token = tokens [ index ] ;
274
+ if ( statement . range [ 1 ] <= token . range [ 0 ] ) {
275
+ break ;
276
+ }
277
+ if ( token . range [ 1 ] <= statement . range [ 0 ] ) {
278
+ continue ;
279
+ }
280
+ if ( token . value === "(" && token . range [ 1 ] <= expression . range [ 0 ] ) {
281
+ openParens . push ( token ) ;
282
+ }
242
283
if (
243
- statement . range [ 0 ] <= token . range [ 0 ] &&
244
- token . range [ 1 ] <= statement . range [ 1 ]
284
+ token . value === "=" &&
285
+ expression . left . range [ 1 ] <= token . range [ 0 ] &&
286
+ token . range [ 1 ] <= expression . right . range [ 0 ]
245
287
) {
246
- if ( token . value === "(" && token . range [ 1 ] <= expression . range [ 0 ] ) {
247
- openParens . push ( token ) ;
248
- }
249
- if (
250
- token . value === "=" &&
251
- expression . left . range [ 1 ] <= token . range [ 0 ] &&
252
- token . range [ 1 ] <= expression . right . range [ 0 ]
253
- ) {
254
- eq = token ;
255
- }
256
- if ( token . value === ")" && expression . range [ 1 ] <= token . range [ 0 ] ) {
288
+ eq = token ;
289
+ }
290
+ if ( token . value === ")" ) {
291
+ if ( expression . range [ 1 ] <= token . range [ 0 ] ) {
257
292
closeParens . push ( token ) ;
293
+ } else if ( expression . right . range [ 1 ] <= token . range [ 0 ] ) {
294
+ expressionCloseParen = token ;
258
295
}
259
- } else if ( statement . range [ 1 ] <= token . range [ 0 ] ) {
260
- break ;
261
296
}
262
297
}
263
298
@@ -327,9 +362,16 @@ function transformForDeclareReactiveVar(
327
362
right : returnStatement . argument ,
328
363
loc : {
329
364
start : idDecl . id . loc . start ,
330
- end : returnStatement . argument . loc . end ,
365
+ end : expressionCloseParen
366
+ ? expressionCloseParen . loc . end
367
+ : returnStatement . argument . loc . end ,
331
368
} ,
332
- range : [ idDecl . id . range [ 0 ] , returnStatement . argument . range [ 1 ] ] ,
369
+ range : [
370
+ idDecl . id . range [ 0 ] ,
371
+ expressionCloseParen
372
+ ? expressionCloseParen . range [ 1 ]
373
+ : returnStatement . argument . range [ 1 ] ,
374
+ ] ,
333
375
} ;
334
376
idDecl . id . parent = newExpression ;
335
377
returnStatement . argument . parent = newExpression ;
0 commit comments