ast.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /**
  2. * @fileoverview Utility functions for AST
  3. */
  4. 'use strict';
  5. /**
  6. * Find a return statment in the current node
  7. *
  8. * @param {ASTNode} node The AST node being checked
  9. * @returns {ASTNode | false}
  10. */
  11. function findReturnStatement(node) {
  12. if (
  13. (!node.value || !node.value.body || !node.value.body.body)
  14. && (!node.body || !node.body.body)
  15. ) {
  16. return false;
  17. }
  18. const bodyNodes = (node.value ? node.value.body.body : node.body.body);
  19. return (function loopNodes(nodes) {
  20. let i = nodes.length - 1;
  21. for (; i >= 0; i--) {
  22. if (nodes[i].type === 'ReturnStatement') {
  23. return nodes[i];
  24. }
  25. if (nodes[i].type === 'SwitchStatement') {
  26. let j = nodes[i].cases.length - 1;
  27. for (; j >= 0; j--) {
  28. return loopNodes(nodes[i].cases[j].consequent);
  29. }
  30. }
  31. }
  32. return false;
  33. }(bodyNodes));
  34. }
  35. /**
  36. * Get node with property's name
  37. * @param {Object} node - Property.
  38. * @returns {Object} Property name node.
  39. */
  40. function getPropertyNameNode(node) {
  41. if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) {
  42. return node.key;
  43. }
  44. if (node.type === 'MemberExpression') {
  45. return node.property;
  46. }
  47. return null;
  48. }
  49. /**
  50. * Get properties name
  51. * @param {Object} node - Property.
  52. * @returns {String} Property name.
  53. */
  54. function getPropertyName(node) {
  55. const nameNode = getPropertyNameNode(node);
  56. return nameNode ? nameNode.name : '';
  57. }
  58. /**
  59. * Get properties for a given AST node
  60. * @param {ASTNode} node The AST node being checked.
  61. * @returns {Array} Properties array.
  62. */
  63. function getComponentProperties(node) {
  64. switch (node.type) {
  65. case 'ClassDeclaration':
  66. case 'ClassExpression':
  67. return node.body.body;
  68. case 'ObjectExpression':
  69. return node.properties;
  70. default:
  71. return [];
  72. }
  73. }
  74. /**
  75. * Gets the first node in a line from the initial node, excluding whitespace.
  76. * @param {Object} context The node to check
  77. * @param {ASTNode} node The node to check
  78. * @return {ASTNode} the first node in the line
  79. */
  80. function getFirstNodeInLine(context, node) {
  81. const sourceCode = context.getSourceCode();
  82. let token = node;
  83. let lines;
  84. do {
  85. token = sourceCode.getTokenBefore(token);
  86. lines = token.type === 'JSXText'
  87. ? token.value.split('\n')
  88. : null;
  89. } while (
  90. token.type === 'JSXText'
  91. && /^\s*$/.test(lines[lines.length - 1])
  92. );
  93. return token;
  94. }
  95. /**
  96. * Checks if the node is the first in its line, excluding whitespace.
  97. * @param {Object} context The node to check
  98. * @param {ASTNode} node The node to check
  99. * @return {Boolean} true if it's the first node in its line
  100. */
  101. function isNodeFirstInLine(context, node) {
  102. const token = getFirstNodeInLine(context, node);
  103. const startLine = node.loc.start.line;
  104. const endLine = token ? token.loc.end.line : -1;
  105. return startLine !== endLine;
  106. }
  107. /**
  108. * Checks if the node is a function or arrow function expression.
  109. * @param {ASTNode} node The node to check
  110. * @return {Boolean} true if it's a function-like expression
  111. */
  112. function isFunctionLikeExpression(node) {
  113. return node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression';
  114. }
  115. /**
  116. * Checks if the node is a function.
  117. * @param {ASTNode} node The node to check
  118. * @return {Boolean} true if it's a function
  119. */
  120. function isFunction(node) {
  121. return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration';
  122. }
  123. /**
  124. * Checks if the node is a class.
  125. * @param {ASTNode} node The node to check
  126. * @return {Boolean} true if it's a class
  127. */
  128. function isClass(node) {
  129. return node.type === 'ClassDeclaration' || node.type === 'ClassExpression';
  130. }
  131. /**
  132. * Removes quotes from around an identifier.
  133. * @param {string} string the identifier to strip
  134. * @returns {string}
  135. */
  136. function stripQuotes(string) {
  137. return string.replace(/^'|'$/g, '');
  138. }
  139. /**
  140. * Retrieve the name of a key node
  141. * @param {Context} context The AST node with the key.
  142. * @param {ASTNode} node The AST node with the key.
  143. * @return {string | undefined} the name of the key
  144. */
  145. function getKeyValue(context, node) {
  146. if (node.type === 'ObjectTypeProperty') {
  147. const tokens = context.getFirstTokens(node, 2);
  148. return (tokens[0].value === '+' || tokens[0].value === '-'
  149. ? tokens[1].value
  150. : stripQuotes(tokens[0].value)
  151. );
  152. }
  153. if (node.type === 'GenericTypeAnnotation') {
  154. return node.id.name;
  155. }
  156. if (node.type === 'ObjectTypeAnnotation') {
  157. return;
  158. }
  159. const key = node.key || node.argument;
  160. if (!key) {
  161. return;
  162. }
  163. return key.type === 'Identifier' ? key.name : key.value;
  164. }
  165. /**
  166. * Checks if a node is being assigned a value: props.bar = 'bar'
  167. * @param {ASTNode} node The AST node being checked.
  168. * @returns {Boolean}
  169. */
  170. function isAssignmentLHS(node) {
  171. return (
  172. node.parent
  173. && node.parent.type === 'AssignmentExpression'
  174. && node.parent.left === node
  175. );
  176. }
  177. /**
  178. * Extracts the expression node that is wrapped inside a TS type assertion
  179. *
  180. * @param {ASTNode} node - potential TS node
  181. * @returns {ASTNode} - unwrapped expression node
  182. */
  183. function unwrapTSAsExpression(node) {
  184. if (node && node.type === 'TSAsExpression') return node.expression;
  185. return node;
  186. }
  187. function isTSTypeReference(node) {
  188. if (!node) return false;
  189. const nodeType = node.type;
  190. return nodeType === 'TSTypeReference';
  191. }
  192. function isTSTypeAnnotation(node) {
  193. if (!node) return false;
  194. const nodeType = node.type;
  195. return nodeType === 'TSTypeAnnotation';
  196. }
  197. function isTSTypeLiteral(node) {
  198. if (!node) return false;
  199. const nodeType = node.type;
  200. return nodeType === 'TSTypeLiteral';
  201. }
  202. function isTSIntersectionType(node) {
  203. if (!node) return false;
  204. const nodeType = node.type;
  205. return nodeType === 'TSIntersectionType';
  206. }
  207. function isTSInterfaceHeritage(node) {
  208. if (!node) return false;
  209. const nodeType = node.type;
  210. return nodeType === 'TSInterfaceHeritage';
  211. }
  212. function isTSInterfaceDeclaration(node) {
  213. if (!node) return false;
  214. const nodeType = node.type;
  215. return nodeType === 'TSInterfaceDeclaration';
  216. }
  217. function isTSTypeAliasDeclaration(node) {
  218. if (!node) return false;
  219. const nodeType = node.type;
  220. return nodeType === 'TSTypeAliasDeclaration';
  221. }
  222. function isTSParenthesizedType(node) {
  223. if (!node) return false;
  224. const nodeType = node.type;
  225. return nodeType === 'TSTypeAliasDeclaration';
  226. }
  227. function isTSFunctionType(node) {
  228. if (!node) return false;
  229. const nodeType = node.type;
  230. return nodeType === 'TSFunctionType';
  231. }
  232. function isTSTypeQuery(node) {
  233. if (!node) return false;
  234. const nodeType = node.type;
  235. return nodeType === 'TSTypeQuery';
  236. }
  237. function isTSTypeParameterInstantiation(node) {
  238. if (!node) return false;
  239. const nodeType = node.type;
  240. return nodeType === 'TSTypeParameterInstantiation';
  241. }
  242. module.exports = {
  243. findReturnStatement,
  244. getFirstNodeInLine,
  245. getPropertyName,
  246. getPropertyNameNode,
  247. getComponentProperties,
  248. getKeyValue,
  249. isAssignmentLHS,
  250. isClass,
  251. isFunction,
  252. isFunctionLikeExpression,
  253. isNodeFirstInLine,
  254. unwrapTSAsExpression,
  255. isTSTypeReference,
  256. isTSTypeAnnotation,
  257. isTSTypeLiteral,
  258. isTSIntersectionType,
  259. isTSInterfaceHeritage,
  260. isTSInterfaceDeclaration,
  261. isTSTypeAliasDeclaration,
  262. isTSParenthesizedType,
  263. isTSFunctionType,
  264. isTSTypeQuery,
  265. isTSTypeParameterInstantiation
  266. };