• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ota-meshi / svelte-eslint-parser / 4581268530

01 Apr 2023 04:18AM UTC coverage: 90.693%. Remained the same
4581268530

push

github

GitHub
chore: release svelte-eslint-parser (#308)

880 of 1041 branches covered (84.53%)

Branch coverage included in aggregate %.

2053 of 2193 relevant lines covered (93.62%)

27060.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.86
/src/parser/analyze-scope.ts
1
import type ESTree from "estree";
22,396✔
2
import type { Scope, ScopeManager } from "eslint-scope";
3
import { Variable, Reference, analyze } from "eslint-scope";
1✔
4
import { getFallbackKeys } from "../traverse";
1✔
5
import type { SvelteReactiveStatement, SvelteScriptElement } from "../ast";
6
import { addReference, addVariable } from "../scope";
1✔
7
import { addElementToSortedArray } from "../utils";
1✔
8
/**
9
 * Analyze scope
10
 */
11
export function analyzeScope(
12
  node: ESTree.Node,
13
  parserOptions: any = {}
×
14
): ScopeManager {
15
  const ecmaVersion = parserOptions.ecmaVersion || 2020;
4,523!
16
  const ecmaFeatures = parserOptions.ecmaFeatures || {};
4,523✔
17
  const sourceType = parserOptions.sourceType || "module";
4,523!
18

19
  const root: ESTree.Program =
20
    node.type === "Program"
4,523!
21
      ? node
22
      : {
23
          type: "Program",
24
          body: [node as ESTree.Statement],
25
          sourceType,
26
        };
27

28
  return analyze(root, {
4,523✔
29
    ignoreEval: true,
30
    nodejsScope: false,
31
    impliedStrict: ecmaFeatures.impliedStrict,
32
    ecmaVersion: typeof ecmaVersion === "number" ? ecmaVersion : 2022,
4,523✔
33
    sourceType,
34
    fallback: getFallbackKeys,
35
  });
36
}
37

38
/** Analyze reactive scope */
39
export function analyzeReactiveScope(scopeManager: ScopeManager): void {
40
  for (const reference of [...scopeManager.globalScope.through]) {
4,916✔
41
    const parent = reference.writeExpr && getParent(reference.writeExpr);
5,790✔
42
    if (parent?.type === "AssignmentExpression") {
5,790✔
43
      const pp = getParent(parent);
663✔
44
      if (pp?.type === "ExpressionStatement") {
663✔
45
        const ppp = getParent(pp) as ESTree.Node | SvelteReactiveStatement;
593✔
46
        if (ppp?.type === "SvelteReactiveStatement" && ppp.label.name === "$") {
593✔
47
          const referenceScope: Scope = reference.from;
576✔
48
          if (referenceScope.type === "module") {
576!
49
            // It is computed
50
            transformComputedVariable(parent, ppp, reference);
576✔
51
            continue;
576✔
52
          }
53
        }
54
      }
55
    }
56
  }
57

58
  /** Transform ref to ComputedVariable */
59
  function transformComputedVariable(
60
    node: ESTree.AssignmentExpression,
61
    parent: SvelteReactiveStatement,
62
    reference: Reference
63
  ) {
64
    const referenceScope: Scope = reference.from;
576✔
65
    const name = reference.identifier.name;
576✔
66
    let variable = referenceScope.set.get(name);
576✔
67
    if (!variable) {
576!
68
      variable = new Variable();
576✔
69
      (variable as any).scope = referenceScope;
576✔
70
      variable.name = name;
576✔
71
      addElementToSortedArray(
576✔
72
        variable.defs,
73
        {
74
          type: "ComputedVariable" as "Variable",
75
          node: node as any,
76
          parent: parent as any,
77
          name: reference.identifier,
78
        },
79
        (a, b) => a.node.range[0] - b.node.range[0]
×
80
      );
81
      addVariable(referenceScope.variables, variable);
576✔
82
      referenceScope.set.set(name, variable);
576✔
83
    }
84
    addElementToSortedArray(
576✔
85
      variable.identifiers,
86
      reference.identifier,
87
      (a, b) => a.range![0] - b.range![0]
×
88
    );
89
    reference.resolved = variable;
576✔
90
    removeReferenceFromThrough(reference, referenceScope);
576✔
91
  }
92
}
93

94
/**
95
 * Analyze store scope. e.g. $count
96
 */
97
export function analyzeStoreScope(scopeManager: ScopeManager): void {
98
  const moduleScope = scopeManager.scopes.find(
9,832✔
99
    (scope) => scope.type === "module"
19,664✔
100
  );
101
  if (!moduleScope) {
9,832!
102
    return;
×
103
  }
104
  const toBeMarkAsUsedReferences: Reference[] = [];
9,832✔
105

106
  for (const reference of [...scopeManager.globalScope.through]) {
9,832✔
107
    if (reference.identifier.name.startsWith("$")) {
11,246✔
108
      const realName = reference.identifier.name.slice(1);
1,039✔
109
      const variable = moduleScope.set.get(realName);
1,039✔
110
      if (variable) {
1,039✔
111
        if (reference.isWriteOnly()) {
815✔
112
          // Need mark as used
113
          toBeMarkAsUsedReferences.push(reference);
112✔
114
        }
115

116
        // It does not write directly to the original variable.
117
        // Therefore, this variable is always a reference.
118
        reference.isWrite = () => false;
815✔
119
        reference.isWriteOnly = () => false;
815✔
120
        reference.isReadWrite = () => false;
815✔
121
        reference.isReadOnly = () => true;
815✔
122
        reference.isRead = () => true;
815✔
123

124
        addReference(variable.references, reference);
815✔
125
        reference.resolved = variable;
815✔
126
        removeReferenceFromThrough(reference, moduleScope);
815✔
127
      }
128
    }
129
  }
130

131
  for (const variable of new Set(
9,832✔
132
    toBeMarkAsUsedReferences.map((ref) => ref.resolved!)
112✔
133
  )) {
134
    if (
98✔
135
      variable.references.some(
136
        (ref) =>
137
          !toBeMarkAsUsedReferences.includes(ref) &&
224✔
138
          ref.identifier !== variable.identifiers[0]
139
      )
140
    ) {
141
      // It is already used.
142
      continue;
56✔
143
    }
144

145
    // Add the virtual reference for reading.
146
    (
147
      addVirtualReference(variable.identifiers[0], variable, moduleScope, {
42✔
148
        read: true,
149
      }) as any
150
    ).svelteMarkAsUsed = true;
151
  }
152
}
153

154
/** Transform props exports */
155
export function analyzePropsScope(
156
  body: SvelteScriptElement,
157
  scopeManager: ScopeManager
158
): void {
159
  const moduleScope = scopeManager.scopes.find(
3,122✔
160
    (scope) => scope.type === "module"
6,244✔
161
  );
162
  if (!moduleScope) {
3,122!
163
    return;
×
164
  }
165

166
  for (const node of body.body) {
3,122✔
167
    if (node.type !== "ExportNamedDeclaration") {
8,016✔
168
      continue;
7,170✔
169
    }
170
    if (node.declaration) {
846✔
171
      if (node.declaration.type === "VariableDeclaration") {
790✔
172
        for (const decl of node.declaration.declarations) {
762✔
173
          if (decl.id.type === "Identifier") {
762!
174
            addPropsReference(decl.id, moduleScope);
762✔
175
          }
176
        }
177
      }
178
    } else {
179
      for (const spec of node.specifiers) {
56✔
180
        addPropsReference(spec.local, moduleScope);
56✔
181
      }
182
    }
183
  }
184

185
  /** Add virtual props reference */
186
  function addPropsReference(node: ESTree.Identifier, scope: Scope) {
187
    for (const variable of scope.variables) {
818✔
188
      if (variable.name !== node.name) {
3,404✔
189
        continue;
2,586✔
190
      }
191

192
      if (variable.references.some((ref) => (ref as any).sveltePropReference)) {
1,331!
193
        continue;
×
194
      }
195

196
      // Add the virtual reference for writing.
197
      const reference = addVirtualReference(
818✔
198
        {
199
          ...node,
200
          // @ts-expect-error -- ignore
201
          parent: body,
202
          loc: {
203
            start: { ...node.loc!.start },
204
            end: { ...node.loc!.end },
205
          },
206
          range: [...node.range!],
207
        },
208
        variable,
209
        scope,
210
        {
211
          write: true,
212
          read: true,
213
        }
214
      );
215
      (reference as any).sveltePropReference = true;
818✔
216
    }
217
  }
218
}
219

220
/** Remove reference from through */
221
function removeReferenceFromThrough(reference: Reference, baseScope: Scope) {
222
  const variable = reference.resolved!;
1,391✔
223
  const name = reference.identifier.name;
1,391✔
224
  let scope: Scope | null = baseScope;
1,391✔
225
  while (scope) {
1,391✔
226
    scope.through = scope.through.filter((ref) => {
2,782✔
227
      if (reference === ref) {
8,265✔
228
        return false;
2,082✔
229
      } else if (ref.identifier.name === name) {
6,183✔
230
        ref.resolved = variable;
1,790✔
231
        if (!variable.references.includes(ref)) {
1,790✔
232
          addReference(variable.references, ref);
895✔
233
        }
234
        return false;
1,790✔
235
      }
236
      return true;
4,393✔
237
    });
238
    scope = scope.upper;
2,782✔
239
  }
240
}
241

242
/**
243
 * Add the virtual reference.
244
 */
245
function addVirtualReference(
246
  node: ESTree.Identifier,
247
  variable: Variable,
248
  scope: Scope,
249
  readWrite: { read?: boolean; write?: boolean }
250
) {
251
  const reference = new Reference();
860✔
252
  (reference as any).svelteVirtualReference = true;
860✔
253
  reference.from = scope;
860✔
254
  reference.identifier = node;
860✔
255
  reference.isWrite = () => Boolean(readWrite.write);
860✔
256
  reference.isWriteOnly = () => Boolean(readWrite.write) && !readWrite.read;
860!
257
  reference.isRead = () => Boolean(readWrite.read);
860✔
258
  reference.isReadOnly = () => Boolean(readWrite.read) && !readWrite.write;
860!
259
  reference.isReadWrite = () => Boolean(readWrite.read && readWrite.write);
860!
260

261
  addReference(variable.references, reference);
860✔
262
  reference.resolved = variable;
860✔
263

264
  return reference;
860✔
265
}
266

267
/** Get parent node */
268
function getParent(node: ESTree.Node): ESTree.Node | null {
269
  return (node as any).parent;
1,947✔
270
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc