forked from lightscript/lightscript-eslint
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use new scopeManager/visitorKeys APIs (babel#542)
(fixes eslint/eslint#9762)
- Loading branch information
1 parent
1f220c2
commit dbc6546
Showing
16 changed files
with
592 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,330 @@ | ||
"use strict"; | ||
|
||
const t = require("@babel/types"); | ||
const escope = require("eslint-scope"); | ||
const Definition = require("eslint-scope/lib/definition").Definition; | ||
const OriginalPatternVisitor = require("eslint-scope/lib/pattern-visitor"); | ||
const OriginalReferencer = require("eslint-scope/lib/referencer"); | ||
const fallback = require("eslint-visitor-keys").getKeys; | ||
const childVisitorKeys = require("./visitor-keys"); | ||
|
||
const flowFlippedAliasKeys = t.FLIPPED_ALIAS_KEYS.Flow.concat([ | ||
"ArrayPattern", | ||
"ClassDeclaration", | ||
"ClassExpression", | ||
"FunctionDeclaration", | ||
"FunctionExpression", | ||
"Identifier", | ||
"ObjectPattern", | ||
"RestElement", | ||
]); | ||
const visitorKeysMap = Object.keys(t.VISITOR_KEYS).reduce(function(acc, key) { | ||
const value = t.VISITOR_KEYS[key]; | ||
if (flowFlippedAliasKeys.indexOf(value) === -1) { | ||
acc[key] = value; | ||
} | ||
return acc; | ||
}, {}); | ||
|
||
const propertyTypes = { | ||
// loops | ||
callProperties: { type: "loop", values: ["value"] }, | ||
indexers: { type: "loop", values: ["key", "value"] }, | ||
properties: { type: "loop", values: ["argument", "value"] }, | ||
types: { type: "loop" }, | ||
params: { type: "loop" }, | ||
// single property | ||
argument: { type: "single" }, | ||
elementType: { type: "single" }, | ||
qualification: { type: "single" }, | ||
rest: { type: "single" }, | ||
returnType: { type: "single" }, | ||
// others | ||
typeAnnotation: { type: "typeAnnotation" }, | ||
typeParameters: { type: "typeParameters" }, | ||
id: { type: "id" }, | ||
}; | ||
|
||
class PatternVisitor extends OriginalPatternVisitor { | ||
ArrayPattern(node) { | ||
node.elements.forEach(this.visit, this); | ||
} | ||
|
||
ObjectPattern(node) { | ||
node.properties.forEach(this.visit, this); | ||
} | ||
} | ||
|
||
class Referencer extends OriginalReferencer { | ||
// inherits. | ||
visitPattern(node, options, callback) { | ||
if (!node) { | ||
return; | ||
} | ||
|
||
// Visit type annotations. | ||
this._checkIdentifierOrVisit(node.typeAnnotation); | ||
if (t.isAssignmentPattern(node)) { | ||
this._checkIdentifierOrVisit(node.left.typeAnnotation); | ||
} | ||
|
||
// Overwrite `super.visitPattern(node, options, callback)` in order to not visit `ArrayPattern#typeAnnotation` and `ObjectPattern#typeAnnotation`. | ||
if (typeof options === "function") { | ||
callback = options; | ||
options = { processRightHandNodes: false }; | ||
} | ||
|
||
const visitor = new PatternVisitor(this.options, node, callback); | ||
visitor.visit(node); | ||
|
||
// Process the right hand nodes recursively. | ||
if (options.processRightHandNodes) { | ||
visitor.rightHandNodes.forEach(this.visit, this); | ||
} | ||
} | ||
|
||
// inherits. | ||
visitClass(node) { | ||
// Decorators. | ||
this._visitArray(node.decorators); | ||
|
||
// Flow type parameters. | ||
const typeParamScope = this._nestTypeParamScope(node); | ||
|
||
// Flow super types. | ||
this._visitTypeAnnotation(node.implements); | ||
this._visitTypeAnnotation( | ||
node.superTypeParameters && node.superTypeParameters.params | ||
); | ||
|
||
// Basic. | ||
super.visitClass(node); | ||
|
||
// Close the type parameter scope. | ||
if (typeParamScope) { | ||
this.close(node); | ||
} | ||
} | ||
|
||
// inherits. | ||
visitFunction(node) { | ||
const typeParamScope = this._nestTypeParamScope(node); | ||
|
||
// Flow return types. | ||
this._checkIdentifierOrVisit(node.returnType); | ||
|
||
// Basic. | ||
super.visitFunction(node); | ||
|
||
// Close the type parameter scope. | ||
if (typeParamScope) { | ||
this.close(node); | ||
} | ||
} | ||
|
||
// inherits. | ||
visitProperty(node) { | ||
if (node.value && node.value.type === "TypeCastExpression") { | ||
this._visitTypeAnnotation(node.value); | ||
} | ||
this._visitArray(node.decorators); | ||
super.visitProperty(node); | ||
} | ||
|
||
InterfaceDeclaration(node) { | ||
this._createScopeVariable(node, node.id); | ||
|
||
const typeParamScope = this._nestTypeParamScope(node); | ||
|
||
// TODO: Handle mixins | ||
this._visitArray(node.extends); | ||
this.visit(node.body); | ||
|
||
if (typeParamScope) { | ||
this.close(node); | ||
} | ||
} | ||
|
||
TypeAlias(node) { | ||
this._createScopeVariable(node, node.id); | ||
|
||
const typeParamScope = this._nestTypeParamScope(node); | ||
|
||
this.visit(node.right); | ||
|
||
if (typeParamScope) { | ||
this.close(node); | ||
} | ||
} | ||
|
||
ClassProperty(node) { | ||
this._visitClassProperty(node); | ||
} | ||
|
||
ClassPrivateProperty(node) { | ||
this._visitClassProperty(node); | ||
} | ||
|
||
DeclareModule(node) { | ||
this._visitDeclareX(node); | ||
} | ||
|
||
DeclareFunction(node) { | ||
this._visitDeclareX(node); | ||
} | ||
|
||
DeclareVariable(node) { | ||
this._visitDeclareX(node); | ||
} | ||
|
||
DeclareClass(node) { | ||
this._visitDeclareX(node); | ||
} | ||
|
||
_visitClassProperty(node) { | ||
this._visitTypeAnnotation(node.typeAnnotation); | ||
this.visitProperty(node); | ||
} | ||
|
||
_visitDeclareX(node) { | ||
if (node.id) { | ||
this._createScopeVariable(node, node.id); | ||
} | ||
|
||
const typeParamScope = this._nestTypeParamScope(node); | ||
if (typeParamScope) { | ||
this.close(node); | ||
} | ||
} | ||
|
||
_createScopeVariable(node, name) { | ||
this.currentScope().variableScope.__define( | ||
name, | ||
new Definition("Variable", name, node, null, null, null) | ||
); | ||
} | ||
|
||
_nestTypeParamScope(node) { | ||
if (!node.typeParameters) { | ||
return null; | ||
} | ||
|
||
const parentScope = this.scopeManager.__currentScope; | ||
const scope = new escope.Scope( | ||
this.scopeManager, | ||
"type-parameters", | ||
parentScope, | ||
node, | ||
false | ||
); | ||
|
||
this.scopeManager.__nestScope(scope); | ||
for (let j = 0; j < node.typeParameters.params.length; j++) { | ||
const name = node.typeParameters.params[j]; | ||
scope.__define(name, new Definition("TypeParameter", name, name)); | ||
if (name.typeAnnotation) { | ||
this._checkIdentifierOrVisit(name); | ||
} | ||
} | ||
scope.__define = function() { | ||
return parentScope.__define.apply(parentScope, arguments); | ||
}; | ||
|
||
return scope; | ||
} | ||
|
||
_visitTypeAnnotation(node) { | ||
if (!node) { | ||
return; | ||
} | ||
if (Array.isArray(node)) { | ||
node.forEach(this._visitTypeAnnotation, this); | ||
return; | ||
} | ||
|
||
// get property to check (params, id, etc...) | ||
const visitorValues = visitorKeysMap[node.type]; | ||
if (!visitorValues) { | ||
return; | ||
} | ||
|
||
// can have multiple properties | ||
for (let i = 0; i < visitorValues.length; i++) { | ||
const visitorValue = visitorValues[i]; | ||
const propertyType = propertyTypes[visitorValue]; | ||
const nodeProperty = node[visitorValue]; | ||
// check if property or type is defined | ||
if (propertyType == null || nodeProperty == null) { | ||
continue; | ||
} | ||
if (propertyType.type === "loop") { | ||
for (let j = 0; j < nodeProperty.length; j++) { | ||
if (Array.isArray(propertyType.values)) { | ||
for (let k = 0; k < propertyType.values.length; k++) { | ||
const loopPropertyNode = nodeProperty[j][propertyType.values[k]]; | ||
if (loopPropertyNode) { | ||
this._checkIdentifierOrVisit(loopPropertyNode); | ||
} | ||
} | ||
} else { | ||
this._checkIdentifierOrVisit(nodeProperty[j]); | ||
} | ||
} | ||
} else if (propertyType.type === "single") { | ||
this._checkIdentifierOrVisit(nodeProperty); | ||
} else if (propertyType.type === "typeAnnotation") { | ||
this._visitTypeAnnotation(node.typeAnnotation); | ||
} else if (propertyType.type === "typeParameters") { | ||
for (let l = 0; l < node.typeParameters.params.length; l++) { | ||
this._checkIdentifierOrVisit(node.typeParameters.params[l]); | ||
} | ||
} else if (propertyType.type === "id") { | ||
if (node.id.type === "Identifier") { | ||
this._checkIdentifierOrVisit(node.id); | ||
} else { | ||
this._visitTypeAnnotation(node.id); | ||
} | ||
} | ||
} | ||
} | ||
|
||
_checkIdentifierOrVisit(node) { | ||
if (node && node.typeAnnotation) { | ||
this._visitTypeAnnotation(node.typeAnnotation); | ||
} else if (node && node.type === "Identifier") { | ||
this.visit(node); | ||
} else { | ||
this._visitTypeAnnotation(node); | ||
} | ||
} | ||
|
||
_visitArray(nodeList) { | ||
if (nodeList) { | ||
for (const node of nodeList) { | ||
this.visit(node); | ||
} | ||
} | ||
} | ||
} | ||
|
||
module.exports = function(ast, parserOptions) { | ||
const options = { | ||
optimistic: false, | ||
directive: false, | ||
nodejsScope: | ||
ast.sourceType === "script" && | ||
(parserOptions.ecmaFeatures && | ||
parserOptions.ecmaFeatures.globalReturn) === true, | ||
impliedStrict: false, | ||
sourceType: ast.sourceType, | ||
ecmaVersion: parserOptions.ecmaVersion || 6, | ||
childVisitorKeys, | ||
fallback, | ||
}; | ||
const scopeManager = new escope.ScopeManager(options); | ||
const referencer = new Referencer(options, scopeManager); | ||
|
||
referencer.visit(ast); | ||
|
||
return scopeManager; | ||
}; |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
"use strict"; | ||
|
||
let patched = false; | ||
|
||
exports.parse = function(code, options) { | ||
patched = true; | ||
return require("./parse-with-patch")(code, options); | ||
}; | ||
|
||
exports.parseForESLint = function(code, options) { | ||
if (!patched && options.eslintVisitorKeys && options.eslintScopeManager) { | ||
return require("./parse-with-scope")(code, options); | ||
} | ||
|
||
patched = true; | ||
return { ast: require("./parse-with-patch")(code, options) }; | ||
}; |
Oops, something went wrong.