index.js 13 KB

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