Skip to content

Commit 4f897c0

Browse files
committed
fix: resolve to module scope for top level statements
1 parent 97a1a68 commit 4f897c0

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

src/scope/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type * as ESTree from "estree";
33
import type { TSESTree } from "@typescript-eslint/types";
44
import { traverseNodes } from "../traverse";
55
import { addElementsToSortedArray, addElementToSortedArray } from "../utils";
6+
import type { SvelteHTMLNode } from "../ast";
67

78
/** Remove all scope, variable, and reference */
89
export function removeAllScopeAndVariableAndReference(
@@ -58,9 +59,9 @@ export function removeAllScopeAndVariableAndReference(
5859
*/
5960
export function getScopeFromNode(
6061
scopeManager: ScopeManager,
61-
currentNode: ESTree.Node
62+
currentNode: ESTree.Node | SvelteHTMLNode
6263
): Scope {
63-
let node: ESTree.Node | null = currentNode;
64+
let node: ESTree.Node | SvelteHTMLNode | null = currentNode;
6465
for (; node; node = (node as any).parent || null) {
6566
const scope = scopeManager.acquire(node, false);
6667
if (scope) {
@@ -78,7 +79,10 @@ export function getScopeFromNode(
7879
}
7980
}
8081
const global = scopeManager.globalScope;
81-
return global;
82+
83+
return currentNode.type === "Program" || currentNode.type === "SvelteScriptElement" ?
84+
global :
85+
getProgramScope(scopeManager);
8286
}
8387
/**
8488
* Gets the scope for the Program node

tests/src/scope/scope.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import assert from "assert";
2+
import { parseForESLint } from "../../../src";
3+
import { getScopeFromNode } from "../../../src/scope";
4+
5+
describe('getScopeFromNode', () => {
6+
it('returns the global scope for the root node', () => {
7+
const { ast, scopeManager } = parseForESLint('');
8+
9+
assert.strictEqual(getScopeFromNode(scopeManager, ast), scopeManager.globalScope);
10+
});
11+
12+
it('returns the global scope for the script element', () => {
13+
const { ast, scopeManager } = parseForESLint('<script></script>');
14+
const script = ast.body[0];
15+
16+
assert.strictEqual(getScopeFromNode(scopeManager, script), scopeManager.globalScope);
17+
});
18+
19+
it('returns the module scope for nodes for top level nodes of script', () => {
20+
const { ast, scopeManager } = parseForESLint('<script>import mod from "mod";</script>');
21+
const importStatement = ast.body[0].body[0];
22+
23+
assert.strictEqual(getScopeFromNode(scopeManager, importStatement), scopeManager.globalScope.childScopes[0]);
24+
});
25+
26+
it('returns the module scope for nested nodes without their own scope', () => {
27+
const { ast, scopeManager } = parseForESLint('<script>a || b</script>');
28+
const importStatement = ast.body[0].body[0].expression.right;
29+
30+
assert.strictEqual(getScopeFromNode(scopeManager, importStatement), scopeManager.globalScope.childScopes[0]);
31+
});
32+
33+
it('returns the module scope for nested nodes for non-modules', () => {
34+
const { ast, scopeManager } = parseForESLint('<script>a || b</script>', { sourceType: 'script' });
35+
const importStatement = ast.body[0].body[0].expression.right;
36+
37+
assert.strictEqual(getScopeFromNode(scopeManager, importStatement), scopeManager.globalScope.childScopes[0]);
38+
});
39+
40+
it('returns the the child scope of top level nodes with their own scope', () => {
41+
const { ast, scopeManager } = parseForESLint('<script>function fn() {}</script>');
42+
const fnNode = ast.body[0].body[0];
43+
44+
assert.strictEqual(getScopeFromNode(scopeManager, fnNode), scopeManager.globalScope.childScopes[0].childScopes[0]);
45+
});
46+
47+
it('returns the own scope for nested nodes', () => {
48+
const { ast, scopeManager } = parseForESLint('<script>a || (() => {})</script>');
49+
const importStatement = ast.body[0].body[0].expression.right;
50+
51+
assert.strictEqual(getScopeFromNode(scopeManager, importStatement), scopeManager.globalScope.childScopes[0].childScopes[0]);
52+
});
53+
54+
it('returns the the nearest child scope for statements inside non-global scopes', () => {
55+
const { ast, scopeManager } = parseForESLint('<script>function fn() { nested; }</script>');
56+
const fnNode = ast.body[0].body[0];
57+
const nestedStatement = fnNode.body.body[0];
58+
59+
assert.strictEqual(getScopeFromNode(scopeManager, nestedStatement), scopeManager.globalScope.childScopes[0].childScopes[0]);
60+
});
61+
});

0 commit comments

Comments
 (0)