patch-eslint-scope.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. "use strict";
  2. var Module = require("module");
  3. var path = require("path");
  4. var t = require("@babel/types");
  5. function getModules() {
  6. try {
  7. // avoid importing a local copy of eslint, try to find a peer dependency
  8. var eslintLoc = Module._resolveFilename("eslint", module.parent);
  9. } catch (err) {
  10. try {
  11. // avoids breaking in jest where module.parent is undefined
  12. eslintLoc = require.resolve("eslint");
  13. } catch (err) {
  14. throw new ReferenceError("couldn't resolve eslint");
  15. }
  16. }
  17. // get modules relative to what eslint will load
  18. var eslintMod = new Module(eslintLoc);
  19. eslintMod.filename = eslintLoc;
  20. eslintMod.paths = Module._nodeModulePaths(path.dirname(eslintLoc));
  21. try {
  22. var escope = eslintMod.require("eslint-scope");
  23. var Definition = eslintMod.require("eslint-scope/lib/definition")
  24. .Definition;
  25. var referencer = eslintMod.require("eslint-scope/lib/referencer");
  26. } catch (err) {
  27. escope = eslintMod.require("escope");
  28. Definition = eslintMod.require("escope/lib/definition").Definition;
  29. referencer = eslintMod.require("escope/lib/referencer");
  30. }
  31. var estraverse = eslintMod.require("estraverse");
  32. if (referencer.__esModule) referencer = referencer.default;
  33. return {
  34. Definition,
  35. escope,
  36. estraverse,
  37. referencer,
  38. };
  39. }
  40. function monkeypatch(modules) {
  41. var Definition = modules.Definition;
  42. var escope = modules.escope;
  43. var estraverse = modules.estraverse;
  44. var referencer = modules.referencer;
  45. Object.assign(estraverse.VisitorKeys, t.VISITOR_KEYS);
  46. estraverse.VisitorKeys.MethodDefinition.push("decorators");
  47. estraverse.VisitorKeys.Property.push("decorators");
  48. // if there are decorators, then visit each
  49. function visitDecorators(node) {
  50. if (!node.decorators) {
  51. return;
  52. }
  53. for (var i = 0; i < node.decorators.length; i++) {
  54. if (node.decorators[i].expression) {
  55. this.visit(node.decorators[i]);
  56. }
  57. }
  58. }
  59. // iterate through part of t.VISITOR_KEYS
  60. var flowFlippedAliasKeys = t.FLIPPED_ALIAS_KEYS.Flow.concat([
  61. "ArrayPattern",
  62. "ClassDeclaration",
  63. "ClassExpression",
  64. "FunctionDeclaration",
  65. "FunctionExpression",
  66. "Identifier",
  67. "ObjectPattern",
  68. "RestElement",
  69. ]);
  70. var visitorKeysMap = Object.keys(t.VISITOR_KEYS).reduce(function(acc, key) {
  71. var value = t.VISITOR_KEYS[key];
  72. if (flowFlippedAliasKeys.indexOf(value) === -1) {
  73. acc[key] = value;
  74. }
  75. return acc;
  76. }, {});
  77. var propertyTypes = {
  78. // loops
  79. callProperties: { type: "loop", values: ["value"] },
  80. indexers: { type: "loop", values: ["key", "value"] },
  81. properties: { type: "loop", values: ["argument", "value"] },
  82. types: { type: "loop" },
  83. params: { type: "loop" },
  84. // single property
  85. argument: { type: "single" },
  86. elementType: { type: "single" },
  87. qualification: { type: "single" },
  88. rest: { type: "single" },
  89. returnType: { type: "single" },
  90. // others
  91. typeAnnotation: { type: "typeAnnotation" },
  92. typeParameters: { type: "typeParameters" },
  93. id: { type: "id" },
  94. };
  95. function visitTypeAnnotation(node) {
  96. // get property to check (params, id, etc...)
  97. var visitorValues = visitorKeysMap[node.type];
  98. if (!visitorValues) {
  99. return;
  100. }
  101. // can have multiple properties
  102. for (var i = 0; i < visitorValues.length; i++) {
  103. var visitorValue = visitorValues[i];
  104. var propertyType = propertyTypes[visitorValue];
  105. var nodeProperty = node[visitorValue];
  106. // check if property or type is defined
  107. if (propertyType == null || nodeProperty == null) {
  108. continue;
  109. }
  110. if (propertyType.type === "loop") {
  111. for (var j = 0; j < nodeProperty.length; j++) {
  112. if (Array.isArray(propertyType.values)) {
  113. for (var k = 0; k < propertyType.values.length; k++) {
  114. var loopPropertyNode = nodeProperty[j][propertyType.values[k]];
  115. if (loopPropertyNode) {
  116. checkIdentifierOrVisit.call(this, loopPropertyNode);
  117. }
  118. }
  119. } else {
  120. checkIdentifierOrVisit.call(this, nodeProperty[j]);
  121. }
  122. }
  123. } else if (propertyType.type === "single") {
  124. checkIdentifierOrVisit.call(this, nodeProperty);
  125. } else if (propertyType.type === "typeAnnotation") {
  126. visitTypeAnnotation.call(this, node.typeAnnotation);
  127. } else if (propertyType.type === "typeParameters") {
  128. for (var l = 0; l < node.typeParameters.params.length; l++) {
  129. checkIdentifierOrVisit.call(this, node.typeParameters.params[l]);
  130. }
  131. } else if (propertyType.type === "id") {
  132. if (node.id.type === "Identifier") {
  133. checkIdentifierOrVisit.call(this, node.id);
  134. } else {
  135. visitTypeAnnotation.call(this, node.id);
  136. }
  137. }
  138. }
  139. }
  140. function checkIdentifierOrVisit(node) {
  141. if (node.typeAnnotation) {
  142. visitTypeAnnotation.call(this, node.typeAnnotation);
  143. } else if (node.type === "Identifier") {
  144. this.visit(node);
  145. } else {
  146. visitTypeAnnotation.call(this, node);
  147. }
  148. }
  149. function nestTypeParamScope(manager, node) {
  150. var parentScope = manager.__currentScope;
  151. var scope = new escope.Scope(
  152. manager,
  153. "type-parameters",
  154. parentScope,
  155. node,
  156. false
  157. );
  158. manager.__nestScope(scope);
  159. for (var j = 0; j < node.typeParameters.params.length; j++) {
  160. var name = node.typeParameters.params[j];
  161. scope.__define(name, new Definition("TypeParameter", name, name));
  162. if (name.typeAnnotation) {
  163. checkIdentifierOrVisit.call(this, name);
  164. }
  165. }
  166. scope.__define = function() {
  167. return parentScope.__define.apply(parentScope, arguments);
  168. };
  169. return scope;
  170. }
  171. // visit decorators that are in: ClassDeclaration / ClassExpression
  172. var visitClass = referencer.prototype.visitClass;
  173. referencer.prototype.visitClass = function(node) {
  174. visitDecorators.call(this, node);
  175. var typeParamScope;
  176. if (node.typeParameters) {
  177. typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
  178. }
  179. // visit flow type: ClassImplements
  180. if (node.implements) {
  181. for (var i = 0; i < node.implements.length; i++) {
  182. checkIdentifierOrVisit.call(this, node.implements[i]);
  183. }
  184. }
  185. if (node.superTypeParameters) {
  186. for (var k = 0; k < node.superTypeParameters.params.length; k++) {
  187. checkIdentifierOrVisit.call(this, node.superTypeParameters.params[k]);
  188. }
  189. }
  190. visitClass.call(this, node);
  191. if (typeParamScope) {
  192. this.close(node);
  193. }
  194. };
  195. // visit decorators that are in: Property / MethodDefinition
  196. var visitProperty = referencer.prototype.visitProperty;
  197. referencer.prototype.visitProperty = function(node) {
  198. if (node.value && node.value.type === "TypeCastExpression") {
  199. visitTypeAnnotation.call(this, node.value);
  200. }
  201. visitDecorators.call(this, node);
  202. visitProperty.call(this, node);
  203. };
  204. function visitClassProperty(node) {
  205. if (node.typeAnnotation) {
  206. visitTypeAnnotation.call(this, node.typeAnnotation);
  207. }
  208. this.visitProperty(node);
  209. }
  210. // visit ClassProperty as a Property.
  211. referencer.prototype.ClassProperty = visitClassProperty;
  212. // visit ClassPrivateProperty as a Property.
  213. referencer.prototype.ClassPrivateProperty = visitClassProperty;
  214. // visit OptionalMemberExpression as a MemberExpression.
  215. referencer.prototype.OptionalMemberExpression =
  216. referencer.prototype.MemberExpression;
  217. // visit flow type in FunctionDeclaration, FunctionExpression, ArrowFunctionExpression
  218. var visitFunction = referencer.prototype.visitFunction;
  219. referencer.prototype.visitFunction = function(node) {
  220. var typeParamScope;
  221. if (node.typeParameters) {
  222. typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
  223. }
  224. if (node.returnType) {
  225. checkIdentifierOrVisit.call(this, node.returnType);
  226. }
  227. // only visit if function parameters have types
  228. if (node.params) {
  229. for (var i = 0; i < node.params.length; i++) {
  230. var param = node.params[i];
  231. if (param.typeAnnotation) {
  232. checkIdentifierOrVisit.call(this, param);
  233. } else if (t.isAssignmentPattern(param)) {
  234. if (param.left.typeAnnotation) {
  235. checkIdentifierOrVisit.call(this, param.left);
  236. }
  237. }
  238. }
  239. }
  240. // set ArrayPattern/ObjectPattern visitor keys back to their original. otherwise
  241. // escope will traverse into them and include the identifiers within as declarations
  242. estraverse.VisitorKeys.ObjectPattern = ["properties"];
  243. estraverse.VisitorKeys.ArrayPattern = ["elements"];
  244. visitFunction.call(this, node);
  245. // set them back to normal...
  246. estraverse.VisitorKeys.ObjectPattern = t.VISITOR_KEYS.ObjectPattern;
  247. estraverse.VisitorKeys.ArrayPattern = t.VISITOR_KEYS.ArrayPattern;
  248. if (typeParamScope) {
  249. this.close(node);
  250. }
  251. };
  252. // visit flow type in VariableDeclaration
  253. var variableDeclaration = referencer.prototype.VariableDeclaration;
  254. referencer.prototype.VariableDeclaration = function(node) {
  255. if (node.declarations) {
  256. for (var i = 0; i < node.declarations.length; i++) {
  257. var id = node.declarations[i].id;
  258. var typeAnnotation = id.typeAnnotation;
  259. if (typeAnnotation) {
  260. checkIdentifierOrVisit.call(this, typeAnnotation);
  261. }
  262. }
  263. }
  264. variableDeclaration.call(this, node);
  265. };
  266. function createScopeVariable(node, name) {
  267. this.currentScope().variableScope.__define(
  268. name,
  269. new Definition("Variable", name, node, null, null, null)
  270. );
  271. }
  272. referencer.prototype.InterfaceDeclaration = function(node) {
  273. createScopeVariable.call(this, node, node.id);
  274. var typeParamScope;
  275. if (node.typeParameters) {
  276. typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
  277. }
  278. // TODO: Handle mixins
  279. for (var i = 0; i < node.extends.length; i++) {
  280. visitTypeAnnotation.call(this, node.extends[i]);
  281. }
  282. visitTypeAnnotation.call(this, node.body);
  283. if (typeParamScope) {
  284. this.close(node);
  285. }
  286. };
  287. referencer.prototype.TypeAlias = function(node) {
  288. createScopeVariable.call(this, node, node.id);
  289. var typeParamScope;
  290. if (node.typeParameters) {
  291. typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
  292. }
  293. if (node.right) {
  294. visitTypeAnnotation.call(this, node.right);
  295. }
  296. if (typeParamScope) {
  297. this.close(node);
  298. }
  299. };
  300. referencer.prototype.DeclareModule = referencer.prototype.DeclareFunction = referencer.prototype.DeclareVariable = referencer.prototype.DeclareClass = function(
  301. node
  302. ) {
  303. if (node.id) {
  304. createScopeVariable.call(this, node, node.id);
  305. }
  306. var typeParamScope;
  307. if (node.typeParameters) {
  308. typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
  309. }
  310. if (typeParamScope) {
  311. this.close(node);
  312. }
  313. };
  314. referencer._babelEslintPatched = true;
  315. }
  316. // To patch for each call.
  317. var escope = null;
  318. var escopeAnalyze = null;
  319. module.exports = function(parserOptions) {
  320. // Patch `Referencer.prototype` once.
  321. if (!escope) {
  322. const modules = getModules();
  323. monkeypatch(modules);
  324. // Store to patch for each call.
  325. escope = modules.escope;
  326. escopeAnalyze = modules.escope.analyze;
  327. }
  328. // Patch `escope.analyze` based on the current parserOptions.
  329. escope.analyze = function(ast, opts) {
  330. opts = opts || {};
  331. opts.ecmaVersion = parserOptions.ecmaVersion;
  332. opts.sourceType = parserOptions.sourceType;
  333. opts.nodejsScope =
  334. ast.sourceType === "script" &&
  335. (parserOptions.ecmaFeatures &&
  336. parserOptions.ecmaFeatures.globalReturn) === true;
  337. return escopeAnalyze.call(this, ast, opts);
  338. };
  339. };