mapExpression.spec.js (23677B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 import mapExpression from "../mapExpression"; 6 import { format } from "prettier"; 7 import cases from "jest-in-case"; 8 9 function test({ 10 expression, 11 newExpression, 12 bindings, 13 mappings, 14 shouldMapBindings, 15 expectedMapped, 16 parseExpression = true, 17 }) { 18 const res = mapExpression(expression, mappings, bindings, shouldMapBindings); 19 20 if (parseExpression) { 21 expect( 22 format(res.expression, { 23 parser: "babel", 24 }) 25 ).toEqual(format(newExpression, { parser: "babel" })); 26 } else { 27 expect(res.expression).toEqual(newExpression); 28 } 29 30 expect(res.mapped).toEqual(expectedMapped); 31 } 32 33 function formatAwait(body) { 34 return `(async () => { ${body} })();`; 35 } 36 37 describe("mapExpression", () => { 38 cases("mapExpressions", test, [ 39 { 40 name: "await", 41 expression: "await a()", 42 newExpression: formatAwait("return a()"), 43 bindings: [], 44 mappings: {}, 45 shouldMapBindings: true, 46 expectedMapped: { 47 await: true, 48 bindings: false, 49 originalExpression: false, 50 }, 51 }, 52 { 53 name: "await (multiple statements)", 54 expression: "const x = await a(); x + x", 55 newExpression: formatAwait("self.x = await a(); return x + x;"), 56 bindings: [], 57 mappings: {}, 58 shouldMapBindings: true, 59 expectedMapped: { 60 await: true, 61 bindings: true, 62 originalExpression: false, 63 }, 64 }, 65 { 66 name: "await (inner)", 67 expression: "async () => await a();", 68 newExpression: "async () => await a();", 69 bindings: [], 70 mappings: {}, 71 shouldMapBindings: true, 72 expectedMapped: { 73 await: false, 74 bindings: false, 75 originalExpression: false, 76 }, 77 }, 78 { 79 name: "await (multiple awaits)", 80 expression: "const x = await a(); await b(x)", 81 newExpression: formatAwait("self.x = await a(); return b(x);"), 82 bindings: [], 83 mappings: {}, 84 shouldMapBindings: true, 85 expectedMapped: { 86 await: true, 87 bindings: true, 88 originalExpression: false, 89 }, 90 }, 91 { 92 name: "await (assignment)", 93 expression: "let x = await sleep(100, 2)", 94 newExpression: formatAwait("return (self.x = await sleep(100, 2))"), 95 bindings: [], 96 mappings: {}, 97 shouldMapBindings: true, 98 expectedMapped: { 99 await: true, 100 bindings: true, 101 originalExpression: false, 102 }, 103 }, 104 { 105 name: "await (destructuring)", 106 expression: "const { a, c: y } = await b()", 107 newExpression: formatAwait( 108 "return ({ a: self.a, c: self.y } = await b())" 109 ), 110 bindings: [], 111 mappings: {}, 112 shouldMapBindings: true, 113 expectedMapped: { 114 await: true, 115 bindings: true, 116 originalExpression: false, 117 }, 118 }, 119 { 120 name: "await (array destructuring)", 121 expression: "const [a, y] = await b();", 122 newExpression: formatAwait("return ([self.a, self.y] = await b())"), 123 bindings: [], 124 mappings: {}, 125 shouldMapBindings: true, 126 expectedMapped: { 127 await: true, 128 bindings: true, 129 originalExpression: false, 130 }, 131 }, 132 { 133 name: "await (mixed destructuring)", 134 expression: "const [{ a }] = await b();", 135 newExpression: formatAwait("return ([{ a: self.a }] = await b())"), 136 bindings: [], 137 mappings: {}, 138 shouldMapBindings: true, 139 expectedMapped: { 140 await: true, 141 bindings: true, 142 originalExpression: false, 143 }, 144 }, 145 { 146 name: "await (destructuring, multiple statements)", 147 expression: "const { a, c: y } = await b(), { x } = await y()", 148 newExpression: formatAwait(` 149 ({ a: self.a, c: self.y } = await b()) 150 return ({ x: self.x } = await y()); 151 `), 152 bindings: [], 153 mappings: {}, 154 shouldMapBindings: true, 155 expectedMapped: { 156 await: true, 157 bindings: true, 158 originalExpression: false, 159 }, 160 }, 161 { 162 name: "await (destructuring, bindings)", 163 expression: "const { a, c: y } = await b();", 164 newExpression: formatAwait("return ({ a, c: y } = await b())"), 165 bindings: ["a", "y"], 166 mappings: {}, 167 shouldMapBindings: true, 168 expectedMapped: { 169 await: true, 170 bindings: true, 171 originalExpression: false, 172 }, 173 }, 174 { 175 name: "await (array destructuring, bindings)", 176 expression: "const [a, y] = await b();", 177 newExpression: formatAwait("return ([a, y] = await b())"), 178 bindings: ["a", "y"], 179 mappings: {}, 180 shouldMapBindings: true, 181 expectedMapped: { 182 await: true, 183 bindings: true, 184 originalExpression: false, 185 }, 186 }, 187 { 188 name: "await (mixed destructuring, bindings)", 189 expression: "const [{ a }] = await b();", 190 newExpression: formatAwait("return ([{ a }] = await b())"), 191 bindings: ["a"], 192 mappings: {}, 193 shouldMapBindings: true, 194 expectedMapped: { 195 await: true, 196 bindings: true, 197 originalExpression: false, 198 }, 199 }, 200 { 201 name: "await (destructuring with defaults, bindings)", 202 expression: "const { c, a = 5 } = await b();", 203 newExpression: formatAwait("return ({ c: self.c, a = 5 } = await b())"), 204 bindings: ["a", "y"], 205 mappings: {}, 206 shouldMapBindings: true, 207 expectedMapped: { 208 await: true, 209 bindings: true, 210 originalExpression: false, 211 }, 212 }, 213 { 214 name: "await (array destructuring with defaults, bindings)", 215 expression: "const [a, y = 10] = await b();", 216 newExpression: formatAwait("return ([a, y = 10] = await b())"), 217 bindings: ["a", "y"], 218 mappings: {}, 219 shouldMapBindings: true, 220 expectedMapped: { 221 await: true, 222 bindings: true, 223 originalExpression: false, 224 }, 225 }, 226 { 227 name: "await (mixed destructuring with defaults, bindings)", 228 expression: "const [{ c = 5 }, a = 5] = await b();", 229 newExpression: formatAwait( 230 "return ([ { c: self.c = 5 }, a = 5] = await b())" 231 ), 232 bindings: ["a"], 233 mappings: {}, 234 shouldMapBindings: true, 235 expectedMapped: { 236 await: true, 237 bindings: true, 238 originalExpression: false, 239 }, 240 }, 241 { 242 name: "await (nested destructuring, bindings)", 243 expression: "const { a, c: { y } } = await b();", 244 newExpression: formatAwait(` 245 return ({ 246 a, 247 c: { y } 248 } = await b()); 249 `), 250 bindings: ["a", "y"], 251 mappings: {}, 252 shouldMapBindings: true, 253 expectedMapped: { 254 await: true, 255 bindings: true, 256 originalExpression: false, 257 }, 258 }, 259 { 260 name: "await (nested destructuring with defaults)", 261 expression: "const { a, c: { y = 5 } = {} } = await b();", 262 newExpression: formatAwait(`return ({ 263 a: self.a, 264 c: { y: self.y = 5 } = {}, 265 } = await b()); 266 `), 267 bindings: [], 268 mappings: {}, 269 shouldMapBindings: true, 270 expectedMapped: { 271 await: true, 272 bindings: true, 273 originalExpression: false, 274 }, 275 }, 276 { 277 name: "await (very nested destructuring with defaults)", 278 expression: 279 "const { a, c: { y: { z = 10, b } = { b: 5 } } } = await b();", 280 newExpression: formatAwait(` 281 return ({ 282 a: self.a, 283 c: { 284 y: { z: self.z = 10, b: self.b } = { 285 b: 5 286 } 287 } 288 } = await b()); 289 `), 290 bindings: [], 291 mappings: {}, 292 shouldMapBindings: true, 293 expectedMapped: { 294 await: true, 295 bindings: true, 296 originalExpression: false, 297 }, 298 }, 299 { 300 name: "await (with SyntaxError)", 301 expression: "await new Promise())", 302 newExpression: formatAwait("await new Promise())"), 303 parseExpression: false, 304 bindings: [], 305 mappings: {}, 306 shouldMapBindings: true, 307 expectedMapped: { 308 await: true, 309 bindings: false, 310 originalExpression: false, 311 }, 312 }, 313 { 314 name: "await (no bindings, let assignment)", 315 expression: "let a = await 123;", 316 newExpression: `let a; 317 (async () => { 318 return a = await 123; 319 })()`, 320 shouldMapBindings: false, 321 expectedMapped: { 322 await: true, 323 bindings: false, 324 originalExpression: false, 325 }, 326 }, 327 { 328 name: "await (no bindings, var assignment)", 329 expression: "var a = await 123;", 330 newExpression: `var a; 331 (async () => { 332 return a = await 123; 333 })()`, 334 shouldMapBindings: false, 335 expectedMapped: { 336 await: true, 337 bindings: false, 338 originalExpression: false, 339 }, 340 }, 341 { 342 name: "await (no bindings, const assignment)", 343 expression: "const a = await 123;", 344 newExpression: `let a; 345 (async () => { 346 return a = await 123; 347 })()`, 348 shouldMapBindings: false, 349 expectedMapped: { 350 await: true, 351 bindings: false, 352 originalExpression: false, 353 }, 354 }, 355 { 356 name: "await (no bindings, multiple assignments)", 357 expression: "let a = 1, b, c = 3; b = await 123; a + b + c", 358 newExpression: `let a, b, c; 359 (async () => { 360 a = 1; 361 c = 3; 362 b = await 123; 363 return a + b + c; 364 })()`, 365 shouldMapBindings: false, 366 expectedMapped: { 367 await: true, 368 bindings: false, 369 originalExpression: false, 370 }, 371 }, 372 { 373 name: "await (no bindings, object destructuring)", 374 expression: "let {a, b, c} = await x;", 375 newExpression: `let a, b, c; 376 (async () => { 377 return ({a, b, c} = await x); 378 })()`, 379 shouldMapBindings: false, 380 expectedMapped: { 381 await: true, 382 bindings: false, 383 originalExpression: false, 384 }, 385 }, 386 { 387 name: "await (no bindings, object destructuring with rest)", 388 expression: "let {a, ...rest} = await x;", 389 newExpression: `let a, rest; 390 (async () => { 391 return ({a, ...rest} = await x); 392 })()`, 393 shouldMapBindings: false, 394 expectedMapped: { 395 await: true, 396 bindings: false, 397 originalExpression: false, 398 }, 399 }, 400 { 401 name: "await (no bindings, object destructuring with renaming and default)", 402 expression: "let {a: hello, b, c: world, d: $ = 4} = await x;", 403 newExpression: `let hello, b, world, $; 404 (async () => { 405 return ({a: hello, b, c: world, d: $ = 4} = await x); 406 })()`, 407 shouldMapBindings: false, 408 expectedMapped: { 409 await: true, 410 bindings: false, 411 originalExpression: false, 412 }, 413 }, 414 { 415 name: "await (no bindings, nested object destructuring + renaming + default)", 416 expression: `let { 417 a: hello, c: { y: { z = 10, b: bill, d: [e, f = 20] }} 418 } = await x; z;`, 419 newExpression: `let hello, z, bill, e, f; 420 (async () => { 421 ({ a: hello, c: { y: { z = 10, b: bill, d: [e, f = 20] }}} = await x); 422 return z; 423 })()`, 424 shouldMapBindings: false, 425 expectedMapped: { 426 await: true, 427 bindings: false, 428 originalExpression: false, 429 }, 430 }, 431 { 432 name: "await (no bindings, array destructuring)", 433 expression: "let [a, b, c] = await x; c;", 434 newExpression: `let a, b, c; 435 (async () => { 436 [a, b, c] = await x; 437 return c; 438 })()`, 439 shouldMapBindings: false, 440 expectedMapped: { 441 await: true, 442 bindings: false, 443 originalExpression: false, 444 }, 445 }, 446 { 447 name: "await (no bindings, array destructuring with default)", 448 expression: "let [a, b = 1, c = 2] = await x; c;", 449 newExpression: `let a, b, c; 450 (async () => { 451 [a, b = 1, c = 2] = await x; 452 return c; 453 })()`, 454 shouldMapBindings: false, 455 expectedMapped: { 456 await: true, 457 bindings: false, 458 originalExpression: false, 459 }, 460 }, 461 { 462 name: "await (no bindings, array destructuring with default and rest)", 463 expression: "let [a, b = 1, c = 2, ...rest] = await x; rest;", 464 newExpression: `let a, b, c, rest; 465 (async () => { 466 [a, b = 1, c = 2, ...rest] = await x; 467 return rest; 468 })()`, 469 shouldMapBindings: false, 470 expectedMapped: { 471 await: true, 472 bindings: false, 473 originalExpression: false, 474 }, 475 }, 476 { 477 name: "await (no bindings, nested array destructuring with default)", 478 expression: "let [a, b = 1, [c = 2, [d = 3, e = 4]]] = await x; c;", 479 newExpression: `let a, b, c, d, e; 480 (async () => { 481 [a, b = 1, [c = 2, [d = 3, e = 4]]] = await x; 482 return c; 483 })()`, 484 shouldMapBindings: false, 485 expectedMapped: { 486 await: true, 487 bindings: false, 488 originalExpression: false, 489 }, 490 }, 491 { 492 name: "await (no bindings, dynamic import)", 493 expression: ` 494 var {rainbowLog} = await import("./cool-module.js"); 495 rainbowLog("dynamic");`, 496 newExpression: `var rainbowLog; 497 (async () => { 498 ({rainbowLog} = await import("./cool-module.js")); 499 return rainbowLog("dynamic"); 500 })()`, 501 shouldMapBindings: false, 502 expectedMapped: { 503 await: true, 504 bindings: false, 505 originalExpression: false, 506 }, 507 }, 508 { 509 name: "await (nullish coalesce operator)", 510 expression: "await x; true ?? false", 511 newExpression: `(async () => { 512 await x; 513 return true ?? false; 514 })()`, 515 shouldMapBindings: false, 516 expectedMapped: { 517 await: true, 518 bindings: false, 519 originalExpression: false, 520 }, 521 }, 522 { 523 name: "await (optional chaining operator)", 524 expression: "await x; x?.y?.z", 525 newExpression: `(async () => { 526 await x; 527 return x?.y?.z; 528 })()`, 529 shouldMapBindings: false, 530 expectedMapped: { 531 await: true, 532 bindings: false, 533 originalExpression: false, 534 }, 535 }, 536 { 537 name: "await (async function declaration with nullish coalesce operator)", 538 expression: "async function coalesce(x) { await x; return x ?? false; }", 539 newExpression: 540 "async function coalesce(x) { await x; return x ?? false; }", 541 shouldMapBindings: false, 542 expectedMapped: { 543 await: false, 544 bindings: false, 545 originalExpression: false, 546 }, 547 }, 548 { 549 name: "await (async function declaration with optional chaining operator)", 550 expression: "async function chain(x) { await x; return x?.y?.z; }", 551 newExpression: "async function chain(x) { await x; return x?.y?.z; }", 552 shouldMapBindings: false, 553 expectedMapped: { 554 await: false, 555 bindings: false, 556 originalExpression: false, 557 }, 558 }, 559 { 560 // check that variable declaration in for loop is not put outside of the async iife 561 name: "await (for loop)", 562 expression: "for (let i=0;i<2;i++) {}; var b = await 1;", 563 newExpression: `var b; 564 (async () => { 565 for (let i=0;i<2;i++) {} 566 return (b = await 1); 567 })()`, 568 shouldMapBindings: false, 569 expectedMapped: { 570 await: true, 571 bindings: false, 572 originalExpression: false, 573 }, 574 }, 575 { 576 // check that variable declaration in for-in loop is not put outside of the async iife 577 name: "await (for..in loop)", 578 expression: "for (let i in {}) {}; var b = await 1;", 579 newExpression: `var b; 580 (async () => { 581 for (let i in {}) {} 582 return (b = await 1); 583 })()`, 584 shouldMapBindings: false, 585 expectedMapped: { 586 await: true, 587 bindings: false, 588 originalExpression: false, 589 }, 590 }, 591 { 592 // check that variable declaration in for-of loop is not put outside of the async iife 593 name: "await (for..of loop)", 594 expression: "for (let i of []) {}; var b = await 1;", 595 newExpression: `var b; 596 (async () => { 597 for (let i of []) {} 598 return (b = await 1); 599 })()`, 600 shouldMapBindings: false, 601 expectedMapped: { 602 await: true, 603 bindings: false, 604 originalExpression: false, 605 }, 606 }, 607 { 608 name: "await (if condition)", 609 expression: "if (await true) console.log(1);", 610 newExpression: formatAwait("if (await true) console.log(1);"), 611 shouldMapBindings: false, 612 expectedMapped: { 613 await: true, 614 bindings: false, 615 originalExpression: false, 616 }, 617 }, 618 { 619 name: "await (non-expression final statement: bug 1851759)", 620 expression: `j = { "foo": 1, "bar": 2 }; await 42; for (var k in j) { console.log(k); }`, 621 newExpression: formatAwait(` 622 j = { 623 foo: 1, 624 bar: 2, 625 }; 626 await 42; 627 for (var k in j) { 628 console.log(k); 629 }`), 630 shouldMapBindings: false, 631 expectedMapped: { 632 await: true, 633 bindings: false, 634 originalExpression: false, 635 }, 636 }, 637 { 638 name: "simple", 639 expression: "a", 640 newExpression: "a", 641 bindings: [], 642 mappings: {}, 643 shouldMapBindings: true, 644 expectedMapped: { 645 await: false, 646 bindings: false, 647 originalExpression: false, 648 }, 649 }, 650 { 651 name: "mappings", 652 expression: "a", 653 newExpression: "_a", 654 bindings: [], 655 mappings: { 656 a: "_a", 657 }, 658 shouldMapBindings: true, 659 expectedMapped: { 660 await: false, 661 bindings: false, 662 originalExpression: true, 663 }, 664 }, 665 { 666 name: "declaration", 667 expression: "var a = 3;", 668 newExpression: "self.a = 3", 669 bindings: [], 670 mappings: {}, 671 shouldMapBindings: true, 672 expectedMapped: { 673 await: false, 674 bindings: true, 675 originalExpression: false, 676 }, 677 }, 678 { 679 name: "declaration + destructuring", 680 expression: "var { a } = { a: 3 };", 681 newExpression: "({ a: self.a } = {\n a: 3 \n})", 682 bindings: [], 683 mappings: {}, 684 shouldMapBindings: true, 685 expectedMapped: { 686 await: false, 687 bindings: true, 688 originalExpression: false, 689 }, 690 }, 691 { 692 name: "bindings", 693 expression: "var a = 3;", 694 newExpression: "a = 3", 695 bindings: ["a"], 696 mappings: {}, 697 shouldMapBindings: true, 698 expectedMapped: { 699 await: false, 700 bindings: true, 701 originalExpression: false, 702 }, 703 }, 704 { 705 name: "bindings + destructuring", 706 expression: "var { a } = { a: 3 };", 707 newExpression: "({ a } = { \n a: 3 \n })", 708 bindings: ["a"], 709 mappings: {}, 710 shouldMapBindings: true, 711 expectedMapped: { 712 await: false, 713 bindings: true, 714 originalExpression: false, 715 }, 716 }, 717 { 718 name: "bindings + destructuring + rest", 719 expression: "var { a, ...foo } = {}", 720 newExpression: "({ a, ...self.foo } = {})", 721 bindings: ["a"], 722 mappings: {}, 723 shouldMapBindings: true, 724 expectedMapped: { 725 await: false, 726 bindings: true, 727 originalExpression: false, 728 }, 729 }, 730 { 731 name: "bindings + array destructuring + rest", 732 expression: "var [a, ...foo] = []", 733 newExpression: "([a, ...self.foo] = [])", 734 bindings: ["a"], 735 mappings: {}, 736 shouldMapBindings: true, 737 expectedMapped: { 738 await: false, 739 bindings: true, 740 originalExpression: false, 741 }, 742 }, 743 { 744 name: "bindings + mappings", 745 expression: "a = 3;", 746 newExpression: "self.a = 3", 747 bindings: ["_a"], 748 mappings: { a: "_a" }, 749 shouldMapBindings: true, 750 expectedMapped: { 751 await: false, 752 bindings: true, 753 originalExpression: false, 754 }, 755 }, 756 { 757 name: "bindings + mappings + destructuring", 758 expression: "var { a } = { a: 4 }", 759 newExpression: "({ a: self.a } = {\n a: 4 \n})", 760 bindings: ["_a"], 761 mappings: { a: "_a" }, 762 shouldMapBindings: true, 763 expectedMapped: { 764 await: false, 765 bindings: true, 766 originalExpression: false, 767 }, 768 }, 769 { 770 name: "bindings without mappings", 771 expression: "a = 3;", 772 newExpression: "a = 3", 773 bindings: [], 774 mappings: { a: "_a" }, 775 shouldMapBindings: false, 776 expectedMapped: { 777 await: false, 778 bindings: false, 779 originalExpression: false, 780 }, 781 }, 782 { 783 name: "object destructuring + bindings without mappings", 784 expression: "({ a } = {});", 785 newExpression: "({ a: _a } = {})", 786 bindings: [], 787 mappings: { a: "_a" }, 788 shouldMapBindings: false, 789 expectedMapped: { 790 await: false, 791 bindings: false, 792 originalExpression: true, 793 }, 794 }, 795 { 796 name: "await (inside top-level block)", 797 expression: "{ await 1 }", 798 newExpression: formatAwait("{ await 1 }"), 799 bindings: [], 800 mappings: {}, 801 shouldMapBindings: false, 802 expectedMapped: { 803 await: true, 804 bindings: false, 805 originalExpression: false, 806 }, 807 }, 808 { 809 name: "await (inside top-level try block)", 810 expression: "try { await 1 } catch (e) {}", 811 newExpression: formatAwait("try { await 1; } catch (e) {}"), 812 bindings: [], 813 mappings: {}, 814 shouldMapBindings: false, 815 expectedMapped: { 816 await: true, 817 bindings: false, 818 originalExpression: false, 819 }, 820 }, 821 { 822 name: "await (inside top-level catch block)", 823 expression: "try { throw 'Error' } catch (e) { await 2; }", 824 newExpression: formatAwait( 825 "try { throw 'Error' } catch (e) { await 2; }" 826 ), 827 bindings: [], 828 mappings: {}, 829 shouldMapBindings: false, 830 expectedMapped: { 831 await: true, 832 bindings: false, 833 originalExpression: false, 834 }, 835 }, 836 { 837 name: "await (inside top-level if-else block)", 838 expression: "if (false) { await 1; } else { await 2; }", 839 newExpression: formatAwait(`if (false) { await 1; } else { await 2; }`), 840 bindings: [], 841 mappings: {}, 842 shouldMapBindings: false, 843 expectedMapped: { 844 await: true, 845 bindings: false, 846 originalExpression: false, 847 }, 848 }, 849 { 850 name: "await (in method name of a class)", 851 expression: "class A { [await 0]() {} }", 852 newExpression: formatAwait(`class A { [await 0]() {} }`), 853 bindings: [], 854 mappings: {}, 855 shouldMapBindings: false, 856 expectedMapped: { 857 await: true, 858 bindings: false, 859 originalExpression: false, 860 }, 861 }, 862 { 863 name: "await (for await)", 864 expression: "for await (const num of [1,2,3]);", 865 newExpression: formatAwait(`for await (const num of [1,2,3]);`), 866 bindings: [], 867 mappings: {}, 868 shouldMapBindings: false, 869 expectedMapped: { 870 await: true, 871 bindings: false, 872 originalExpression: false, 873 }, 874 }, 875 ]); 876 });