Skip to content

Commit 0412e91

Browse files
authored
Add virtual reference to a used store (#142)
1 parent 2442067 commit 0412e91

5 files changed

+215
-53
lines changed

src/parser/analyze-scope.ts

Lines changed: 95 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -90,30 +90,65 @@ export function analyzeReactiveScope(scopeManager: ScopeManager): void {
9090
* Analyze store scope. e.g. $count
9191
*/
9292
export function analyzeStoreScope(scopeManager: ScopeManager): void {
93+
const moduleScope = scopeManager.scopes.find(
94+
(scope) => scope.type === "module",
95+
)
96+
if (!moduleScope) {
97+
return
98+
}
99+
const toBeMarkAsUsedReferences: Reference[] = []
100+
93101
for (const reference of [...scopeManager.globalScope.through]) {
94102
if (reference.identifier.name.startsWith("$")) {
95103
const realName = reference.identifier.name.slice(1)
96-
const moduleScope = scopeManager.scopes.find(
97-
(scope) => scope.type === "module",
98-
)
99-
if (moduleScope) {
100-
const variable = moduleScope?.set.get(realName)
101-
if (variable) {
102-
// It does not write directly to the original variable.
103-
// Therefore, this variable is always a reference.
104-
reference.isWrite = () => false
105-
reference.isWriteOnly = () => false
106-
reference.isReadWrite = () => false
107-
reference.isReadOnly = () => true
108-
reference.isRead = () => true
109-
110-
variable.references.push(reference)
111-
reference.resolved = variable
112-
removeReferenceFromThrough(reference, moduleScope)
104+
const variable = moduleScope.set.get(realName)
105+
if (variable) {
106+
if (reference.isWriteOnly()) {
107+
// Need mark as used
108+
toBeMarkAsUsedReferences.push(reference)
113109
}
110+
111+
// It does not write directly to the original variable.
112+
// Therefore, this variable is always a reference.
113+
reference.isWrite = () => false
114+
reference.isWriteOnly = () => false
115+
reference.isReadWrite = () => false
116+
reference.isReadOnly = () => true
117+
reference.isRead = () => true
118+
119+
variable.references.push(reference)
120+
reference.resolved = variable
121+
removeReferenceFromThrough(reference, moduleScope)
114122
}
115123
}
116124
}
125+
126+
for (const variable of new Set(
127+
toBeMarkAsUsedReferences.map((ref) => ref.resolved!),
128+
)) {
129+
if (
130+
variable.references.some(
131+
(ref) =>
132+
!toBeMarkAsUsedReferences.includes(ref) &&
133+
ref.identifier !== variable.identifiers[0],
134+
)
135+
) {
136+
// It is already used.
137+
continue
138+
}
139+
140+
// Add the virtual reference for reading.
141+
;(
142+
addVirtualReference(
143+
variable.identifiers[0],
144+
variable,
145+
moduleScope,
146+
{
147+
read: true,
148+
},
149+
) as any
150+
).svelteMarkAsUsed = true
151+
}
117152
}
118153

119154
/** Transform props exports */
@@ -163,27 +198,25 @@ export function analyzePropsScope(
163198
}
164199

165200
// Add the virtual reference for writing.
166-
const reference = new Reference()
167-
;(reference as any).sveltePropReference = true
168-
reference.from = scope
169-
reference.identifier = {
170-
...node,
171-
// @ts-expect-error -- ignore
172-
parent: body,
173-
loc: {
174-
start: { ...node.loc!.start },
175-
end: { ...node.loc!.end },
201+
const reference = addVirtualReference(
202+
{
203+
...node,
204+
// @ts-expect-error -- ignore
205+
parent: body,
206+
loc: {
207+
start: { ...node.loc!.start },
208+
end: { ...node.loc!.end },
209+
},
210+
range: [...node.range!],
176211
},
177-
range: [...node.range!],
178-
}
179-
reference.isWrite = () => true
180-
reference.isWriteOnly = () => false
181-
reference.isRead = () => true
182-
reference.isReadOnly = () => false
183-
reference.isReadWrite = () => true
184-
185-
variable.references.push(reference)
186-
reference.resolved = variable
212+
variable,
213+
scope,
214+
{
215+
write: true,
216+
read: true,
217+
},
218+
)
219+
;(reference as any).sveltePropReference = true
187220
}
188221
}
189222
}
@@ -210,6 +243,31 @@ function removeReferenceFromThrough(reference: Reference, baseScope: Scope) {
210243
}
211244
}
212245

246+
/**
247+
* Add the virtual reference.
248+
*/
249+
function addVirtualReference(
250+
node: ESTree.Identifier,
251+
variable: Variable,
252+
scope: Scope,
253+
readWrite: { read?: boolean; write?: boolean },
254+
) {
255+
const reference = new Reference()
256+
;(reference as any).svelteVirtualReference = true
257+
reference.from = scope
258+
reference.identifier = node
259+
reference.isWrite = () => Boolean(readWrite.write)
260+
reference.isWriteOnly = () => Boolean(readWrite.write) && !readWrite.read
261+
reference.isRead = () => Boolean(readWrite.read)
262+
reference.isReadOnly = () => Boolean(readWrite.read) && !readWrite.write
263+
reference.isReadWrite = () => Boolean(readWrite.read && readWrite.write)
264+
265+
variable.references.push(reference)
266+
reference.resolved = variable
267+
268+
return reference
269+
}
270+
213271
/** Get parent node */
214272
function getParent(node: ESTree.Node): ESTree.Node | null {
215273
return (node as any).parent

tests/fixtures/parser/ast/$var-no-unused-vars-result.json

Lines changed: 0 additions & 8 deletions
This file was deleted.

tests/fixtures/parser/ast/$var-scope-output.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,46 @@
163163
}
164164
}
165165
}
166+
},
167+
{
168+
"identifier": {
169+
"type": "Identifier",
170+
"name": "a",
171+
"range": [
172+
20,
173+
21
174+
],
175+
"loc": {
176+
"start": {
177+
"line": 2,
178+
"column": 11
179+
},
180+
"end": {
181+
"line": 2,
182+
"column": 12
183+
}
184+
}
185+
},
186+
"from": "module",
187+
"init": null,
188+
"resolved": {
189+
"type": "Identifier",
190+
"name": "a",
191+
"range": [
192+
20,
193+
21
194+
],
195+
"loc": {
196+
"start": {
197+
"line": 2,
198+
"column": 11
199+
},
200+
"end": {
201+
"line": 2,
202+
"column": 12
203+
}
204+
}
205+
}
166206
}
167207
]
168208
}

tests/fixtures/parser/ast/unused-write-only-store-no-unused-vars-result.json

Lines changed: 0 additions & 8 deletions
This file was deleted.

tests/fixtures/parser/ast/unused-write-only-store-scope-output.json

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,46 @@
243243
}
244244
}
245245
}
246+
},
247+
{
248+
"identifier": {
249+
"type": "Identifier",
250+
"name": "imported",
251+
"range": [
252+
19,
253+
27
254+
],
255+
"loc": {
256+
"start": {
257+
"line": 2,
258+
"column": 10
259+
},
260+
"end": {
261+
"line": 2,
262+
"column": 18
263+
}
264+
}
265+
},
266+
"from": "module",
267+
"init": null,
268+
"resolved": {
269+
"type": "Identifier",
270+
"name": "imported",
271+
"range": [
272+
19,
273+
27
274+
],
275+
"loc": {
276+
"start": {
277+
"line": 2,
278+
"column": 10
279+
},
280+
"end": {
281+
"line": 2,
282+
"column": 18
283+
}
284+
}
285+
}
246286
}
247287
]
248288
},
@@ -624,6 +664,46 @@
624664
}
625665
}
626666
}
667+
},
668+
{
669+
"identifier": {
670+
"type": "Identifier",
671+
"name": "writeOnly",
672+
"range": [
673+
69,
674+
78
675+
],
676+
"loc": {
677+
"start": {
678+
"line": 3,
679+
"column": 7
680+
},
681+
"end": {
682+
"line": 3,
683+
"column": 16
684+
}
685+
}
686+
},
687+
"from": "module",
688+
"init": null,
689+
"resolved": {
690+
"type": "Identifier",
691+
"name": "writeOnly",
692+
"range": [
693+
69,
694+
78
695+
],
696+
"loc": {
697+
"start": {
698+
"line": 3,
699+
"column": 7
700+
},
701+
"end": {
702+
"line": 3,
703+
"column": 16
704+
}
705+
}
706+
}
627707
}
628708
]
629709
},

0 commit comments

Comments
 (0)