converters.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. import * as t from "../lib";
  2. import { parse } from "@babel/parser";
  3. import generate from "@babel/generator";
  4. function parseCode(string) {
  5. return parse(string, {
  6. allowReturnOutsideFunction: true,
  7. }).program.body[0];
  8. }
  9. function generateCode(node) {
  10. return generate(node).code;
  11. }
  12. describe("converters", function() {
  13. it("toIdentifier", function() {
  14. expect(t.toIdentifier("swag-lord")).toBe("swagLord");
  15. });
  16. describe("valueToNode", function() {
  17. it("number", function() {
  18. expect(t.valueToNode(Math.PI)).toEqual(t.numericLiteral(Math.PI));
  19. expect(t.valueToNode(-Math.PI)).toEqual(
  20. t.unaryExpression("-", t.numericLiteral(Math.PI)),
  21. );
  22. expect(t.valueToNode(0)).toEqual(t.numericLiteral(0));
  23. expect(t.valueToNode(-0)).toEqual(
  24. t.unaryExpression("-", t.numericLiteral(0)),
  25. );
  26. expect(t.valueToNode(NaN)).toEqual(
  27. t.binaryExpression("/", t.numericLiteral(0), t.numericLiteral(0)),
  28. );
  29. expect(t.valueToNode(-NaN)).toEqual(
  30. t.binaryExpression("/", t.numericLiteral(0), t.numericLiteral(0)),
  31. );
  32. expect(t.valueToNode(Infinity)).toEqual(
  33. t.binaryExpression("/", t.numericLiteral(1), t.numericLiteral(0)),
  34. );
  35. expect(t.valueToNode(-Infinity)).toEqual(
  36. t.unaryExpression(
  37. "-",
  38. t.binaryExpression("/", t.numericLiteral(1), t.numericLiteral(0)),
  39. ),
  40. );
  41. });
  42. it("string", function() {
  43. expect(t.valueToNode('This is a "string"')).toEqual(
  44. t.stringLiteral('This is a "string"'),
  45. );
  46. });
  47. it("boolean", function() {
  48. expect(t.valueToNode(true)).toEqual(t.booleanLiteral(true));
  49. expect(t.valueToNode(false)).toEqual(t.booleanLiteral(false));
  50. });
  51. it("null", function() {
  52. expect(t.valueToNode(null)).toEqual(t.nullLiteral());
  53. });
  54. it("undefined", function() {
  55. expect(t.valueToNode(undefined)).toEqual(t.identifier("undefined"));
  56. });
  57. it("RegExp", function() {
  58. expect(t.valueToNode(/abc.+/gm)).toEqual(t.regExpLiteral("abc.+", "gm"));
  59. });
  60. it("array", function() {
  61. expect(t.valueToNode([1, "a"])).toEqual(
  62. t.arrayExpression([t.numericLiteral(1), t.stringLiteral("a")]),
  63. );
  64. });
  65. it("object", function() {
  66. expect(
  67. t.valueToNode({
  68. a: 1,
  69. "b c": 2,
  70. }),
  71. ).toEqual(
  72. t.objectExpression([
  73. t.objectProperty(t.identifier("a"), t.numericLiteral(1)),
  74. t.objectProperty(t.stringLiteral("b c"), t.numericLiteral(2)),
  75. ]),
  76. );
  77. });
  78. it("throws if cannot convert", function() {
  79. expect(function() {
  80. t.valueToNode(Object);
  81. }).toThrow();
  82. expect(function() {
  83. t.valueToNode(Symbol());
  84. }).toThrow();
  85. });
  86. });
  87. describe("toKeyAlias", function() {
  88. beforeEach(function() {
  89. // make tests deterministic
  90. t.toKeyAlias.uid = 0;
  91. });
  92. it("doesn't change string literals", function() {
  93. expect(
  94. t.toKeyAlias(t.objectProperty(t.stringLiteral("a"), t.nullLiteral())),
  95. ).toBe('"a"');
  96. });
  97. it("wraps around at Number.MAX_SAFE_INTEGER", function() {
  98. expect(
  99. t.toKeyAlias(
  100. t.objectMethod("method", t.identifier("a"), [], t.blockStatement([])),
  101. ),
  102. ).toBe("0");
  103. });
  104. });
  105. describe("toStatement", function() {
  106. it("noop on statements", function() {
  107. const node = t.emptyStatement();
  108. expect(t.toStatement(node)).toEqual(node);
  109. t.assertEmptyStatement(node);
  110. });
  111. it("mutate class expression to declaration", function() {
  112. const node = t.classExpression(
  113. t.identifier("A"),
  114. null,
  115. t.classBody([]),
  116. [],
  117. );
  118. t.toStatement(node);
  119. t.assertClassDeclaration(node);
  120. });
  121. it("fail if class expression has no id", function() {
  122. const node = t.classExpression(null, null, t.classBody([]), []);
  123. expect(function() {
  124. t.toStatement(node);
  125. }).toThrow(Error);
  126. expect(t.toStatement(node, /* ignore = */ true)).toBe(false);
  127. t.assertClassExpression(node);
  128. });
  129. it("mutate function expression to declaration", function() {
  130. const node = t.functionExpression(
  131. t.identifier("A"),
  132. [],
  133. t.blockStatement([]),
  134. );
  135. t.toStatement(node);
  136. t.assertFunctionDeclaration(node);
  137. });
  138. it("fail if function expression has no id", function() {
  139. const node = t.functionExpression(null, [], t.blockStatement([]));
  140. expect(function() {
  141. t.toStatement(node);
  142. }).toThrow(Error);
  143. expect(t.toStatement(node, /* ignore = */ true)).toBe(false);
  144. t.assertFunctionExpression(node);
  145. });
  146. it("assignment expression", function() {
  147. const node = t.assignmentExpression(
  148. "+=",
  149. t.identifier("x"),
  150. t.numericLiteral(1),
  151. );
  152. t.assertExpressionStatement(t.toStatement(node));
  153. t.assertAssignmentExpression(node);
  154. });
  155. it("fail if cannot convert node type", function() {
  156. const node = t.yieldExpression(t.identifier("foo"));
  157. expect(function() {
  158. t.toStatement(node);
  159. }).toThrow(Error);
  160. expect(t.toStatement(node, /* ignore = */ true)).toBe(false);
  161. t.assertYieldExpression(node);
  162. });
  163. });
  164. describe("toExpression", function() {
  165. it("noop on expressions", function() {
  166. const node = t.identifier("a");
  167. expect(t.toExpression(node)).toEqual(node);
  168. t.assertIdentifier(node);
  169. });
  170. it("mutate class declaration to expression", function() {
  171. const node = t.classDeclaration(
  172. t.identifier("A"),
  173. null,
  174. t.classBody([]),
  175. [],
  176. );
  177. t.toExpression(node);
  178. t.assertClassExpression(node);
  179. });
  180. it("mutate function declaration to expression", function() {
  181. const node = t.functionDeclaration(
  182. t.identifier("A"),
  183. [],
  184. t.blockStatement([]),
  185. );
  186. t.toExpression(node);
  187. t.assertFunctionExpression(node);
  188. });
  189. it("mutate object method to expression", function() {
  190. const node = t.objectMethod(
  191. "method",
  192. t.identifier("A"),
  193. [],
  194. t.blockStatement([]),
  195. );
  196. t.toExpression(node);
  197. t.assertFunctionExpression(node);
  198. });
  199. it("mutate class method to expression", function() {
  200. const node = t.classMethod(
  201. "constructor",
  202. t.identifier("A"),
  203. [],
  204. t.blockStatement([]),
  205. );
  206. t.toExpression(node);
  207. t.assertFunctionExpression(node);
  208. });
  209. it("expression statement", function() {
  210. const inner = t.yieldExpression(t.identifier("foo"));
  211. const node = t.expressionStatement(inner);
  212. t.assertYieldExpression(t.toExpression(node));
  213. expect(t.toExpression(node)).toEqual(inner);
  214. t.assertExpressionStatement(node);
  215. });
  216. it("fail if cannot convert node type", function() {
  217. const node = t.program([]);
  218. expect(function() {
  219. t.toExpression(node);
  220. }).toThrow(Error);
  221. t.assertProgram(node);
  222. });
  223. });
  224. describe("toSequenceExpression", function() {
  225. let scope;
  226. const undefinedNode = t.identifier("undefined");
  227. beforeEach(function() {
  228. scope = [];
  229. scope.buildUndefinedNode = function() {
  230. return undefinedNode;
  231. };
  232. });
  233. it("gathers nodes into sequence", function() {
  234. const node = t.identifier("a");
  235. const sequence = t.toSequenceExpression([undefinedNode, node], scope);
  236. t.assertSequenceExpression(sequence);
  237. expect(sequence.expressions[0]).toBe(undefinedNode);
  238. expect(sequence.expressions[1]).toBe(node);
  239. t.assertIdentifier(node);
  240. });
  241. it("avoids sequence for single node", function() {
  242. const node = t.identifier("a");
  243. let sequence = t.toSequenceExpression([node], scope);
  244. expect(sequence).toBe(node);
  245. const block = t.blockStatement([t.expressionStatement(node)]);
  246. sequence = t.toSequenceExpression([block], scope);
  247. expect(sequence).toBe(node);
  248. });
  249. it("gathers expression", function() {
  250. const node = t.identifier("a");
  251. const sequence = t.toSequenceExpression([undefinedNode, node], scope);
  252. expect(sequence.expressions[1]).toBe(node);
  253. });
  254. it("gathers expression statement", function() {
  255. const node = t.expressionStatement(t.identifier("a"));
  256. const sequence = t.toSequenceExpression([undefinedNode, node], scope);
  257. expect(sequence.expressions[1]).toBe(node.expression);
  258. });
  259. it("gathers var declarations", function() {
  260. const node = parseCode("var a, b = 1;");
  261. const sequence = t.toSequenceExpression([undefinedNode, node], scope);
  262. t.assertIdentifier(scope[0].id, { name: "a" });
  263. t.assertIdentifier(scope[1].id, { name: "b" });
  264. expect(generateCode(sequence.expressions[1])).toBe("b = 1");
  265. expect(generateCode(sequence.expressions[2])).toBe("undefined");
  266. });
  267. it("skips undefined if expression after var declaration", function() {
  268. const node = parseCode("{ var a, b = 1; true }");
  269. const sequence = t.toSequenceExpression([undefinedNode, node], scope);
  270. expect(generateCode(sequence.expressions[1])).toBe("b = 1, true");
  271. });
  272. it("bails on let and const declarations", function() {
  273. let node = parseCode("let a, b = 1;");
  274. let sequence = t.toSequenceExpression([undefinedNode, node], scope);
  275. expect(sequence).toBeUndefined();
  276. node = parseCode("const b = 1;");
  277. sequence = t.toSequenceExpression([undefinedNode, node], scope);
  278. expect(sequence).toBeUndefined();
  279. });
  280. it("gathers if statements", function() {
  281. let node = parseCode("if (true) { true }");
  282. let sequence = t.toSequenceExpression([undefinedNode, node], scope);
  283. expect(generateCode(sequence.expressions[1])).toBe(
  284. "true ? true : undefined",
  285. );
  286. node = parseCode("if (true) { true } else { b }");
  287. sequence = t.toSequenceExpression([undefinedNode, node], scope);
  288. expect(generateCode(sequence.expressions[1])).toBe("true ? true : b");
  289. });
  290. it("bails in if statements if recurse bails", function() {
  291. let node = parseCode("if (true) { return }");
  292. let sequence = t.toSequenceExpression([undefinedNode, node], scope);
  293. expect(sequence).toBeUndefined();
  294. node = parseCode("if (true) { true } else { return }");
  295. sequence = t.toSequenceExpression([undefinedNode, node], scope);
  296. expect(sequence).toBeUndefined();
  297. });
  298. it("gathers block statements", function() {
  299. let node = parseCode("{ a }");
  300. let sequence = t.toSequenceExpression([undefinedNode, node], scope);
  301. expect(generateCode(sequence.expressions[1])).toBe("a");
  302. node = parseCode("{ a; b; }");
  303. sequence = t.toSequenceExpression([undefinedNode, node], scope);
  304. expect(generateCode(sequence.expressions[1])).toBe("a, b");
  305. });
  306. it("bails in block statements if recurse bails", function() {
  307. const node = parseCode("{ return }");
  308. const sequence = t.toSequenceExpression([undefinedNode, node], scope);
  309. expect(sequence).toBeUndefined();
  310. });
  311. it("gathers empty statements", function() {
  312. const node = parseCode(";");
  313. const sequence = t.toSequenceExpression([undefinedNode, node], scope);
  314. expect(generateCode(sequence.expressions[1])).toBe("undefined");
  315. });
  316. it("skips empty statement if expression afterwards", function() {
  317. const node = parseCode("{ ; true }");
  318. const sequence = t.toSequenceExpression([undefinedNode, node], scope);
  319. expect(generateCode(sequence.expressions[1])).toBe("true");
  320. });
  321. });
  322. });