Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/eslint-scope/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ The `ScopeManager` class is at the core of eslint-scope and is returned when you

#### Methods

- **`addGlobals(names)`**
Adds variables to the global scope and resolves references to them.
- `names` - An array of strings, the names of variables to add to the global scope.
- Returns: `undefined`.

- **`acquire(node, inner)`**
Acquires the appropriate scope for a given node.
- `node` - The AST node to acquire the scope from.
Expand Down
10 changes: 10 additions & 0 deletions packages/eslint-scope/lib/scope-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ class ScopeManager {
return null;
}

/**
* Add global variables and resolve their references.
* @function ScopeManager#addGlobals
* @param {string[]} names Names of global variables to add.
* @returns {void}
*/
addGlobals(names) {
this.globalScope.__addVariables(names);
}

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

detach() { } // eslint-disable-line class-methods-use-this -- Desired as instance method
Expand Down
95 changes: 51 additions & 44 deletions packages/eslint-scope/lib/scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,6 @@ function registerScope(scopeManager, scope) {
}
}

/**
* Should be statically
* @param {Object} def def
* @returns {boolean} should be statically
*/
function shouldBeStatically(def) {
return (
(def.type === Variable.ClassName) ||
(def.type === Variable.Variable && def.parent.kind !== "var")
);
}

/**
* @constructor Scope
*/
Expand Down Expand Up @@ -267,22 +255,7 @@ class Scope {
}

__shouldStaticallyClose(scopeManager) {
return (!this.dynamic || scopeManager.__isOptimistic());
}

__shouldStaticallyCloseForGlobal(ref) {

// On global scope, let/const/class declarations should be resolved statically.
const name = ref.identifier.name;

if (!this.set.has(name)) {
return false;
}

const variable = this.set.get(name);
const defs = variable.defs;

return defs.length > 0 && defs.every(shouldBeStatically);
return (!this.dynamic || scopeManager.__isOptimistic() || this.type === "global");
}

__staticCloseRef(ref) {
Expand All @@ -302,26 +275,13 @@ class Scope {
} while (current);
}

__globalCloseRef(ref) {

// let/const/class declarations should be resolved statically.
// others should be resolved dynamically.
if (this.__shouldStaticallyCloseForGlobal(ref)) {
this.__staticCloseRef(ref);
} else {
this.__dynamicCloseRef(ref);
}
}

__close(scopeManager) {
let closeRef;

if (this.__shouldStaticallyClose(scopeManager)) {
closeRef = this.__staticCloseRef;
} else if (this.type !== "global") {
closeRef = this.__dynamicCloseRef;
} else {
closeRef = this.__globalCloseRef;
closeRef = this.__dynamicCloseRef;
}

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

}

this.implicit.left = this.__left;
super.__close(scopeManager);

return super.__close(scopeManager);
this.implicit.left = [...this.through];

return null;
}

__defineImplicit(node, def) {
Expand All @@ -576,6 +538,51 @@ class GlobalScope extends Scope {
);
}
}

__addVariables(names) {
for (const name of names) {
this.__defineGeneric(
name,
this.set,
this.variables,
null,
null
);
}

const namesSet = new Set(names);

this.through = this.through.filter(reference => {
const name = reference.identifier.name;

if (namesSet.has(name)) {
const variable = this.set.get(name);

reference.resolved = variable;
variable.references.push(reference);

return false;
}

return true;
});

this.implicit.variables = this.implicit.variables.filter(variable => {
const name = variable.name;

if (namesSet.has(name)) {
this.implicit.set.delete(name);

return false;
}

return true;
});

this.implicit.left = this.implicit.left.filter(
reference => !namesSet.has(reference.identifier.name)
);
}
}

/**
Expand Down
Loading
Loading