Skip to content

Commit f007b46

Browse files
authored
feat!: Resolve references to global var/function, add addGlobals() (#682)
* feat!: Close `var` and `function` references on global scope * add `ScopeManager#addGlobals` * refactor * add more tests * rename add-globals.js to add-globals.test.js * update docs
1 parent 68bbb74 commit f007b46

File tree

7 files changed

+477
-55
lines changed

7 files changed

+477
-55
lines changed

packages/eslint-scope/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ The `ScopeManager` class is at the core of eslint-scope and is returned when you
8989

9090
#### Methods
9191

92+
- **`addGlobals(names)`**
93+
Adds variables to the global scope and resolves references to them.
94+
- `names` - An array of strings, the names of variables to add to the global scope.
95+
- Returns: `undefined`.
96+
9297
- **`acquire(node, inner)`**
9398
Acquires the appropriate scope for a given node.
9499
- `node` - The AST node to acquire the scope from.

packages/eslint-scope/lib/scope-manager.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,16 @@ class ScopeManager {
182182
return null;
183183
}
184184

185+
/**
186+
* Add global variables and resolve their references.
187+
* @function ScopeManager#addGlobals
188+
* @param {string[]} names Names of global variables to add.
189+
* @returns {void}
190+
*/
191+
addGlobals(names) {
192+
this.globalScope.__addVariables(names);
193+
}
194+
185195
attach() { } // eslint-disable-line class-methods-use-this -- Desired as instance method
186196

187197
detach() { } // eslint-disable-line class-methods-use-this -- Desired as instance method

packages/eslint-scope/lib/scope.js

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -122,18 +122,6 @@ function registerScope(scopeManager, scope) {
122122
}
123123
}
124124

125-
/**
126-
* Should be statically
127-
* @param {Object} def def
128-
* @returns {boolean} should be statically
129-
*/
130-
function shouldBeStatically(def) {
131-
return (
132-
(def.type === Variable.ClassName) ||
133-
(def.type === Variable.Variable && def.parent.kind !== "var")
134-
);
135-
}
136-
137125
/**
138126
* @constructor Scope
139127
*/
@@ -267,22 +255,7 @@ class Scope {
267255
}
268256

269257
__shouldStaticallyClose(scopeManager) {
270-
return (!this.dynamic || scopeManager.__isOptimistic());
271-
}
272-
273-
__shouldStaticallyCloseForGlobal(ref) {
274-
275-
// On global scope, let/const/class declarations should be resolved statically.
276-
const name = ref.identifier.name;
277-
278-
if (!this.set.has(name)) {
279-
return false;
280-
}
281-
282-
const variable = this.set.get(name);
283-
const defs = variable.defs;
284-
285-
return defs.length > 0 && defs.every(shouldBeStatically);
258+
return (!this.dynamic || scopeManager.__isOptimistic() || this.type === "global");
286259
}
287260

288261
__staticCloseRef(ref) {
@@ -302,26 +275,13 @@ class Scope {
302275
} while (current);
303276
}
304277

305-
__globalCloseRef(ref) {
306-
307-
// let/const/class declarations should be resolved statically.
308-
// others should be resolved dynamically.
309-
if (this.__shouldStaticallyCloseForGlobal(ref)) {
310-
this.__staticCloseRef(ref);
311-
} else {
312-
this.__dynamicCloseRef(ref);
313-
}
314-
}
315-
316278
__close(scopeManager) {
317279
let closeRef;
318280

319281
if (this.__shouldStaticallyClose(scopeManager)) {
320282
closeRef = this.__staticCloseRef;
321-
} else if (this.type !== "global") {
322-
closeRef = this.__dynamicCloseRef;
323283
} else {
324-
closeRef = this.__globalCloseRef;
284+
closeRef = this.__dynamicCloseRef;
325285
}
326286

327287
// Try Resolving all references in this scope.
@@ -560,9 +520,11 @@ class GlobalScope extends Scope {
560520

561521
}
562522

563-
this.implicit.left = this.__left;
523+
super.__close(scopeManager);
564524

565-
return super.__close(scopeManager);
525+
this.implicit.left = [...this.through];
526+
527+
return null;
566528
}
567529

568530
__defineImplicit(node, def) {
@@ -576,6 +538,51 @@ class GlobalScope extends Scope {
576538
);
577539
}
578540
}
541+
542+
__addVariables(names) {
543+
for (const name of names) {
544+
this.__defineGeneric(
545+
name,
546+
this.set,
547+
this.variables,
548+
null,
549+
null
550+
);
551+
}
552+
553+
const namesSet = new Set(names);
554+
555+
this.through = this.through.filter(reference => {
556+
const name = reference.identifier.name;
557+
558+
if (namesSet.has(name)) {
559+
const variable = this.set.get(name);
560+
561+
reference.resolved = variable;
562+
variable.references.push(reference);
563+
564+
return false;
565+
}
566+
567+
return true;
568+
});
569+
570+
this.implicit.variables = this.implicit.variables.filter(variable => {
571+
const name = variable.name;
572+
573+
if (namesSet.has(name)) {
574+
this.implicit.set.delete(name);
575+
576+
return false;
577+
}
578+
579+
return true;
580+
});
581+
582+
this.implicit.left = this.implicit.left.filter(
583+
reference => !namesSet.has(reference.identifier.name)
584+
);
585+
}
579586
}
580587

581588
/**

0 commit comments

Comments
 (0)