NodeUtils.spec.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. import * as ESTree from 'estree';
  2. import { assert } from 'chai';
  3. import { TStatement } from '../../../../src/types/node/TStatement';
  4. import { Nodes } from '../../../../src/node/Nodes';
  5. import { NodeUtils } from '../../../../src/node/NodeUtils';
  6. describe('NodeUtils', () => {
  7. describe('addXVerbatimPropertyToLiterals (node: ESTree.Node): void', () => {
  8. let literalNode: any,
  9. expectedLiteralNode: any;
  10. before(() => {
  11. literalNode = Nodes.getLiteralNode('value');
  12. delete literalNode['x-verbatim-property'];
  13. expectedLiteralNode = Nodes.getLiteralNode('value');
  14. NodeUtils.addXVerbatimPropertyToLiterals(literalNode);
  15. });
  16. it('should add `x-verbatim-property` to `Literal` node', () => {
  17. assert.deepEqual(literalNode, expectedLiteralNode);
  18. });
  19. });
  20. describe('clone <T extends ESTree.Node> (astTree: T): T', () => {
  21. let programNode: ESTree.Program,
  22. expectedProgramNode: ESTree.Program;
  23. before(() => {
  24. // actual AST tree
  25. const expressionStatementNode1: ESTree.ExpressionStatement = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  26. const expressionStatementNode2: ESTree.ExpressionStatement = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  27. const ifStatementBlockStatementNode1: ESTree.BlockStatement = Nodes.getBlockStatementNode([
  28. expressionStatementNode1,
  29. expressionStatementNode2
  30. ]);
  31. const ifStatementNode1: ESTree.IfStatement = Nodes.getIfStatementNode(
  32. Nodes.getLiteralNode(true),
  33. ifStatementBlockStatementNode1
  34. );
  35. // expected AST tree
  36. const expressionStatementNode3: ESTree.ExpressionStatement = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  37. const expressionStatementNode4: ESTree.ExpressionStatement = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  38. const ifStatementBlockStatementNode2: ESTree.BlockStatement = Nodes.getBlockStatementNode([
  39. expressionStatementNode3,
  40. expressionStatementNode4
  41. ]);
  42. const ifStatementNode2: ESTree.IfStatement = Nodes.getIfStatementNode(
  43. Nodes.getLiteralNode(true),
  44. ifStatementBlockStatementNode2
  45. );
  46. programNode = NodeUtils.clone(
  47. Nodes.getProgramNode([
  48. ifStatementNode1
  49. ])
  50. );
  51. expectedProgramNode = NodeUtils.parentize(
  52. Nodes.getProgramNode([
  53. ifStatementNode2
  54. ])
  55. );
  56. });
  57. it('should clone given AST-tree', () => {
  58. assert.deepEqual(programNode, expectedProgramNode);
  59. });
  60. });
  61. describe('convertCodeToStructure (code: string): ESTree.Node[]', () => {
  62. let structure: TStatement[],
  63. expectedStructure: TStatement[];
  64. before(() => {
  65. const code: string = `
  66. var abc = 'cde';
  67. `;
  68. const identifierNode: ESTree.Identifier = Nodes.getIdentifierNode('abc');
  69. const literalNode: ESTree.Literal = Nodes.getLiteralNode('cde');
  70. const variableDeclaratorNode: ESTree.VariableDeclarator = Nodes.getVariableDeclaratorNode(identifierNode, literalNode);
  71. const variableDeclarationNode: ESTree.VariableDeclaration = Nodes.getVariableDeclarationNode([
  72. variableDeclaratorNode
  73. ]);
  74. const programNode: ESTree.Program = Nodes.getProgramNode([
  75. variableDeclarationNode
  76. ]);
  77. programNode.parentNode = programNode;
  78. variableDeclarationNode.parentNode = programNode;
  79. variableDeclaratorNode.parentNode = variableDeclarationNode;
  80. identifierNode.parentNode = variableDeclaratorNode;
  81. literalNode.parentNode = variableDeclaratorNode;
  82. structure = NodeUtils.convertCodeToStructure(code);
  83. expectedStructure = [variableDeclarationNode];
  84. });
  85. it('should convert code to `ESTree.Node[]` structure array', () => {
  86. assert.deepEqual(structure, expectedStructure);
  87. });
  88. });
  89. describe('convertStructureToCode (structure: ESTree.Node[]): string', () => {
  90. let structure: ESTree.Node[],
  91. expectedCode: string;
  92. before(() => {
  93. structure = [
  94. Nodes.getProgramNode([
  95. Nodes.getVariableDeclarationNode([
  96. Nodes.getVariableDeclaratorNode(
  97. Nodes.getIdentifierNode('abc'),
  98. Nodes.getLiteralNode('cde')
  99. )
  100. ])
  101. ])
  102. ];
  103. expectedCode = 'var abc = \'cde\';';
  104. });
  105. it('should convert `ESTree.Node[]` structure to source code', () => {
  106. assert.deepEqual(NodeUtils.convertStructureToCode(structure), expectedCode);
  107. });
  108. });
  109. describe('getBlockStatementNodeByIndex (node: ESTree.Node, index: number = 0): ESTree.Node', () => {
  110. let blockStatementNode: ESTree.BlockStatement,
  111. expressionStatementNode1: ESTree.ExpressionStatement,
  112. expressionStatementNode2: ESTree.ExpressionStatement;
  113. beforeEach(() => {
  114. expressionStatementNode1 = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  115. expressionStatementNode2 = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  116. blockStatementNode = Nodes.getBlockStatementNode([
  117. expressionStatementNode1,
  118. expressionStatementNode2
  119. ]);
  120. });
  121. it('should return block-statement child node of given node if that node has block-statement', () => {
  122. assert.deepEqual(NodeUtils.getBlockStatementNodeByIndex(blockStatementNode), expressionStatementNode1);
  123. });
  124. it('should return block-statement child node of given node with index `1` if that node has block-statement', () => {
  125. assert.deepEqual(NodeUtils.getBlockStatementNodeByIndex(blockStatementNode, 1), expressionStatementNode2);
  126. });
  127. it('should throw a `ReferenceError` if index is out of boundaries', () => {
  128. assert.throws(() => NodeUtils.getBlockStatementNodeByIndex(blockStatementNode, 2), ReferenceError);
  129. });
  130. it('should throw a `TypeError` if node have no a block-statement', () => {
  131. assert.throws(() => NodeUtils.getBlockStatementNodeByIndex(expressionStatementNode1, 1), TypeError);
  132. });
  133. });
  134. describe('getBlockScopesOfNode (node: ESTree.Node, blockScopes: TNodeWithBlockStatement[] = []): TNodeWithBlockStatement[]', () => {
  135. let functionDeclarationBlockStatementNode: ESTree.BlockStatement,
  136. ifStatementBlockStatementNode1: ESTree.BlockStatement,
  137. ifStatementBlockStatementNode2: ESTree.BlockStatement,
  138. ifStatementNode1: ESTree.IfStatement,
  139. ifStatementNode2: ESTree.IfStatement,
  140. expressionStatementNode3: ESTree.ExpressionStatement,
  141. expressionStatementNode2: ESTree.ExpressionStatement,
  142. expressionStatementNode1: ESTree.ExpressionStatement,
  143. functionDeclarationNode: ESTree.FunctionDeclaration,
  144. programNode: ESTree.Program;
  145. before(() => {
  146. expressionStatementNode1 = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  147. expressionStatementNode2 = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  148. expressionStatementNode3 = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  149. ifStatementBlockStatementNode2 = Nodes.getBlockStatementNode([
  150. expressionStatementNode2,
  151. expressionStatementNode3
  152. ]);
  153. ifStatementNode2 = Nodes.getIfStatementNode(
  154. Nodes.getLiteralNode(true),
  155. ifStatementBlockStatementNode2
  156. );
  157. ifStatementBlockStatementNode1 = Nodes.getBlockStatementNode([
  158. ifStatementNode2
  159. ]);
  160. ifStatementNode1 = Nodes.getIfStatementNode(
  161. Nodes.getLiteralNode(true),
  162. ifStatementBlockStatementNode1
  163. );
  164. functionDeclarationBlockStatementNode = Nodes.getBlockStatementNode([
  165. expressionStatementNode1,
  166. ifStatementNode1
  167. ]);
  168. functionDeclarationNode = Nodes.getFunctionDeclarationNode('test', [], functionDeclarationBlockStatementNode);
  169. programNode = Nodes.getProgramNode([
  170. functionDeclarationNode
  171. ]);
  172. programNode.parentNode = programNode;
  173. functionDeclarationNode.parentNode = programNode;
  174. functionDeclarationBlockStatementNode.parentNode = functionDeclarationNode;
  175. expressionStatementNode1.parentNode = functionDeclarationBlockStatementNode;
  176. ifStatementNode1.parentNode = functionDeclarationBlockStatementNode;
  177. ifStatementBlockStatementNode1.parentNode = ifStatementNode1;
  178. ifStatementNode2.parentNode = ifStatementBlockStatementNode1;
  179. ifStatementBlockStatementNode2.parentNode = ifStatementNode2;
  180. expressionStatementNode3.parentNode = ifStatementBlockStatementNode2;
  181. });
  182. it('should return block-scope node for `program` node child', () => {
  183. assert.deepEqual(NodeUtils.getBlockScopesOfNode(programNode)[0], programNode);
  184. });
  185. it('should return block-scope node for `functionDeclaration` node child node #1', () => {
  186. assert.deepEqual(NodeUtils.getBlockScopesOfNode(functionDeclarationNode)[0], programNode);
  187. });
  188. it('should return block-scope node for `functionDeclaration blockStatement` node child node #1', () => {
  189. assert.deepEqual(NodeUtils.getBlockScopesOfNode(functionDeclarationBlockStatementNode)[0], programNode);
  190. });
  191. it('should return block-scope node for `expressionStatement` node #1 child node #1', () => {
  192. assert.deepEqual(NodeUtils.getBlockScopesOfNode(expressionStatementNode1)[0], functionDeclarationBlockStatementNode);
  193. });
  194. it('should return block-scope node for `expressionStatement` node #1 child node #2', () => {
  195. assert.deepEqual(NodeUtils.getBlockScopesOfNode(expressionStatementNode1)[1], programNode);
  196. });
  197. it('should return block-scope node for `ifStatement` node child node #1', () => {
  198. assert.deepEqual(NodeUtils.getBlockScopesOfNode(ifStatementNode1)[0], functionDeclarationBlockStatementNode);
  199. });
  200. it('should return block-scope node for `ifStatement` node child node #2', () => {
  201. assert.deepEqual(NodeUtils.getBlockScopesOfNode(ifStatementNode1)[1], programNode);
  202. });
  203. it('should return block-scope node for `ifStatement blockStatement` node #1 child node #1', () => {
  204. assert.deepEqual(NodeUtils.getBlockScopesOfNode(ifStatementBlockStatementNode1)[0], functionDeclarationBlockStatementNode);
  205. });
  206. it('should return block-scope node for `ifStatement blockStatement` node #1 child node #2', () => {
  207. assert.deepEqual(NodeUtils.getBlockScopesOfNode(ifStatementBlockStatementNode1)[1], programNode);
  208. });
  209. it('should return block-scope node for `ifStatement blockStatement` node #2 child node #1', () => {
  210. assert.deepEqual(NodeUtils.getBlockScopesOfNode(ifStatementBlockStatementNode2)[0], functionDeclarationBlockStatementNode);
  211. });
  212. it('should return block-scope node for `ifStatement blockStatement` node #1 child node #2', () => {
  213. assert.deepEqual(NodeUtils.getBlockScopesOfNode(ifStatementBlockStatementNode2)[1], programNode);
  214. });
  215. it('should return block-scope node for `expressionStatement` node #3 child node #1', () => {
  216. assert.deepEqual(NodeUtils.getBlockScopesOfNode(expressionStatementNode3)[0], functionDeclarationBlockStatementNode);
  217. });
  218. it('should return block-scope node for `expressionStatement` node #3 child node #2', () => {
  219. assert.deepEqual(NodeUtils.getBlockScopesOfNode(expressionStatementNode3)[1], programNode);
  220. });
  221. it('should throw a `ReferenceError` if node has no `parentNode` property', () => {
  222. assert.throws(() => NodeUtils.getBlockScopesOfNode(expressionStatementNode2)[0], ReferenceError);
  223. });
  224. });
  225. describe('getNodeBlockScopeDepth (node: ESTree.Node, depth: number = 0): number', () => {
  226. let functionDeclarationBlockStatementNode1: ESTree.BlockStatement,
  227. functionDeclarationBlockStatementNode2: ESTree.BlockStatement,
  228. ifStatementBlockStatementNode1: ESTree.BlockStatement,
  229. ifStatementBlockStatementNode2: ESTree.BlockStatement,
  230. ifStatementNode1: ESTree.IfStatement,
  231. ifStatementNode2: ESTree.IfStatement,
  232. expressionStatementNode1: ESTree.ExpressionStatement,
  233. expressionStatementNode2: ESTree.ExpressionStatement,
  234. expressionStatementNode3: ESTree.ExpressionStatement,
  235. functionDeclarationNode1: ESTree.FunctionDeclaration,
  236. functionDeclarationNode2: ESTree.FunctionDeclaration,
  237. programNode: ESTree.Program;
  238. before(() => {
  239. expressionStatementNode1 = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  240. expressionStatementNode2 = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  241. expressionStatementNode3 = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  242. ifStatementBlockStatementNode2 = Nodes.getBlockStatementNode([
  243. expressionStatementNode3
  244. ]);
  245. ifStatementNode2 = Nodes.getIfStatementNode(
  246. Nodes.getLiteralNode(true),
  247. ifStatementBlockStatementNode2
  248. );
  249. functionDeclarationBlockStatementNode2 = Nodes.getBlockStatementNode([
  250. ifStatementNode2,
  251. expressionStatementNode2
  252. ]);
  253. functionDeclarationNode2 = Nodes.getFunctionDeclarationNode('test', [], functionDeclarationBlockStatementNode2);
  254. ifStatementBlockStatementNode1 = Nodes.getBlockStatementNode([
  255. functionDeclarationNode2
  256. ]);
  257. ifStatementNode1 = Nodes.getIfStatementNode(
  258. Nodes.getLiteralNode(true),
  259. ifStatementBlockStatementNode1
  260. );
  261. functionDeclarationBlockStatementNode1 = Nodes.getBlockStatementNode([
  262. expressionStatementNode1,
  263. ifStatementNode1
  264. ]);
  265. functionDeclarationNode1 = Nodes.getFunctionDeclarationNode('test', [], functionDeclarationBlockStatementNode1);
  266. programNode = Nodes.getProgramNode([
  267. functionDeclarationNode1
  268. ]);
  269. programNode.parentNode = programNode;
  270. functionDeclarationNode1.parentNode = programNode;
  271. functionDeclarationBlockStatementNode1.parentNode = functionDeclarationNode1;
  272. expressionStatementNode1.parentNode = functionDeclarationBlockStatementNode1;
  273. ifStatementNode1.parentNode = functionDeclarationBlockStatementNode1;
  274. ifStatementBlockStatementNode1.parentNode = ifStatementNode1;
  275. functionDeclarationNode2.parentNode = ifStatementBlockStatementNode1;
  276. functionDeclarationBlockStatementNode2.parentNode = functionDeclarationNode2;
  277. expressionStatementNode2.parentNode = functionDeclarationBlockStatementNode2;
  278. ifStatementNode2.parentNode = functionDeclarationBlockStatementNode2;
  279. ifStatementBlockStatementNode2.parentNode = ifStatementNode2;
  280. });
  281. it('should return block-scope depth for `program` node', () => {
  282. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(programNode), 0);
  283. });
  284. it('should return block-scope depth for `functionDeclaration` node #1', () => {
  285. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(functionDeclarationNode1), 0);
  286. });
  287. it('should return block-scope depth for `functionDeclaration blockStatement` node #1', () => {
  288. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(functionDeclarationBlockStatementNode1), 1);
  289. });
  290. it('should return block-scope depth for `expressionStatement` node #1', () => {
  291. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(expressionStatementNode1), 1);
  292. });
  293. it('should return block-scope depth for `ifStatement` node #1', () => {
  294. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(ifStatementNode1), 1);
  295. });
  296. it('should return block-scope depth for `ifStatement blockStatement` node #1', () => {
  297. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(ifStatementBlockStatementNode1), 1);
  298. });
  299. it('should return block-scope depth for `functionDeclaration` node #2', () => {
  300. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(functionDeclarationNode2), 1);
  301. });
  302. it('should return block-scope depth for `functionDeclaration blockStatement` node #2', () => {
  303. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(functionDeclarationBlockStatementNode2), 2);
  304. });
  305. it('should return block-scope depth for `expressionStatement` node #2', () => {
  306. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(expressionStatementNode2), 2);
  307. });
  308. it('should return block-scope depth for `ifStatement` node #2', () => {
  309. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(ifStatementNode2), 2);
  310. });
  311. it('should return block-scope depth for `ifStatement blockStatement` node #2', () => {
  312. assert.deepEqual(NodeUtils.getNodeBlockScopeDepth(ifStatementBlockStatementNode2), 2);
  313. });
  314. it('should throw a `ReferenceError` if node has no `parentNode` property', () => {
  315. assert.throws(() => NodeUtils.getNodeBlockScopeDepth(expressionStatementNode3), ReferenceError);
  316. });
  317. });
  318. describe('getUnaryExpressionArgumentNode (unaryExpressionNode: ESTree.UnaryExpression): ESTree.Node', () => {
  319. let expectedNode: ESTree.Literal,
  320. unaryExpressionArgumentNode: ESTree.Node;
  321. before(() => {
  322. const literalNode: ESTree.Literal = Nodes.getLiteralNode('test');
  323. const unaryExpressionNode2: ESTree.UnaryExpression = Nodes.getUnaryExpressionNode('!', literalNode);
  324. const unaryExpressionNode1: ESTree.UnaryExpression = Nodes.getUnaryExpressionNode('!', unaryExpressionNode2);
  325. const expressionStatementNode: ESTree.ExpressionStatement = Nodes.getExpressionStatementNode(unaryExpressionNode1);
  326. const programNode: ESTree.Program = Nodes.getProgramNode([
  327. expressionStatementNode
  328. ]);
  329. programNode.parentNode = programNode;
  330. expressionStatementNode.parentNode = programNode;
  331. unaryExpressionNode1.parentNode = expressionStatementNode;
  332. unaryExpressionNode2.parentNode = unaryExpressionNode1;
  333. literalNode.parentNode = unaryExpressionNode2;
  334. unaryExpressionArgumentNode = NodeUtils.getUnaryExpressionArgumentNode(unaryExpressionNode1);
  335. expectedNode = literalNode;
  336. });
  337. it('should return unary expression argument node', () => {
  338. assert.deepEqual(unaryExpressionArgumentNode, expectedNode);
  339. });
  340. });
  341. describe('parentize <T extends ESTree.Node> (astTree: T): T', () => {
  342. let ifStatementNode: ESTree.IfStatement,
  343. ifStatementBlockStatementNode: ESTree.BlockStatement,
  344. expressionStatementNode1: ESTree.ExpressionStatement,
  345. expressionStatementNode2: ESTree.ExpressionStatement,
  346. programNode: ESTree.Program;
  347. beforeEach(() => {
  348. expressionStatementNode1 = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  349. expressionStatementNode2 = Nodes.getExpressionStatementNode(Nodes.getIdentifierNode('identifier'));
  350. ifStatementBlockStatementNode = Nodes.getBlockStatementNode([
  351. expressionStatementNode1,
  352. expressionStatementNode2
  353. ]);
  354. ifStatementNode = Nodes.getIfStatementNode(
  355. Nodes.getLiteralNode(true),
  356. ifStatementBlockStatementNode
  357. );
  358. });
  359. describe('parentize AST-tree with `ProgramNode` as root node', () => {
  360. beforeEach(() => {
  361. programNode = Nodes.getProgramNode([
  362. ifStatementNode
  363. ]);
  364. programNode = NodeUtils.parentize(programNode);
  365. });
  366. it('should parentize `program` node with `ProgramNode` as root node', () => {
  367. assert.deepEqual(programNode.parentNode, programNode);
  368. });
  369. it('should parentize `ifStatement` node with `ProgramNode` as root node', () => {
  370. assert.deepEqual(ifStatementNode.parentNode, programNode);
  371. });
  372. it('should parentize `ifStatement blockStatement` node with `ProgramNode` as root node', () => {
  373. assert.deepEqual(ifStatementBlockStatementNode.parentNode, ifStatementNode);
  374. });
  375. it('should parentize `expressionStatement` node #1 with `ProgramNode` as root node', () => {
  376. assert.deepEqual(expressionStatementNode1.parentNode, ifStatementBlockStatementNode);
  377. });
  378. it('should parentize `expressionStatement` node #2 with `ProgramNode` as root node', () => {
  379. assert.deepEqual(expressionStatementNode2.parentNode, ifStatementBlockStatementNode);
  380. });
  381. });
  382. describe('parentize AST-tree', () => {
  383. beforeEach(() => {
  384. programNode = Nodes.getProgramNode([
  385. ifStatementNode
  386. ]);
  387. programNode.parentNode = programNode;
  388. ifStatementNode = NodeUtils.parentize(ifStatementNode);
  389. });
  390. it('should parentize `ifStatement` node', () => {
  391. assert.deepEqual(ifStatementNode.parentNode, programNode);
  392. });
  393. it('should parentize `ifStatement blockStatement` node', () => {
  394. assert.deepEqual(ifStatementBlockStatementNode.parentNode, ifStatementNode);
  395. });
  396. it('should parentize `expressionStatement` node #1', () => {
  397. assert.deepEqual(expressionStatementNode1.parentNode, ifStatementBlockStatementNode);
  398. });
  399. it('should parentize `expressionStatement` node #2', () => {
  400. assert.deepEqual(expressionStatementNode2.parentNode, ifStatementBlockStatementNode);
  401. });
  402. });
  403. });
  404. });