e0b47ffbf47ccb875a63ae682d88b8f51ea49427b46d33a330ca0365efecbbccab3a6642002a7780d429ae0ce43e0d40df7cbbdf4b8a6f25ffd7696228d870 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. "use strict";
  2. exports.__esModule = true;
  3. exports.default = void 0;
  4. /**
  5. * Converts destructured parameters with default values to non-shorthand syntax.
  6. * This fixes the only Tagged Templates-related bug in ES Modules-supporting browsers (Safari 10 & 11).
  7. * Use this plugin instead of `@babel/plugin-transform-template-literals` when targeting ES Modules.
  8. *
  9. * @example
  10. * // Bug 1: Safari 10/11 doesn't reliably return the same Strings value.
  11. * // The value changes depending on invocation and function optimization state.
  12. * function f() { return Object`` }
  13. * f() === new f() // false, should be true.
  14. *
  15. * @example
  16. * // Bug 2: Safari 10/11 use the same cached strings value when the string parts are the same.
  17. * // This behavior comes from an earlier version of the spec, and can cause tricky bugs.
  18. * Object``===Object`` // true, should be false.
  19. *
  20. * Benchmarks: https://jsperf.com/compiled-tagged-template-performance
  21. */
  22. var _default = ({
  23. types: t
  24. }) => ({
  25. name: "transform-tagged-template-caching",
  26. visitor: {
  27. TaggedTemplateExpression(path, state) {
  28. // tagged templates we've already dealt with
  29. let processed = state.get("processed");
  30. if (!processed) {
  31. processed = new WeakSet();
  32. state.set("processed", processed);
  33. }
  34. if (processed.has(path.node)) return path.skip(); // Grab the expressions from the original tag.
  35. // tag`a${'hello'}` // ['hello']
  36. const expressions = path.node.quasi.expressions; // Create an identity function helper:
  37. // identity = t => t
  38. let identity = state.get("identity");
  39. if (!identity) {
  40. identity = path.scope.getProgramParent().generateDeclaredUidIdentifier("_");
  41. state.set("identity", identity);
  42. const binding = path.scope.getBinding(identity.name);
  43. binding.path.get("init").replaceWith(t.arrowFunctionExpression( // re-use the helper identifier for compressability
  44. [t.identifier("t")], t.identifier("t")));
  45. } // Use the identity function helper to get a reference to the template's Strings.
  46. // We replace all expressions with `0` ensure Strings has the same shape.
  47. // identity`a${0}`
  48. const template = t.taggedTemplateExpression(t.cloneNode(identity), t.templateLiteral(path.node.quasi.quasis, expressions.map(() => t.numericLiteral(0))));
  49. processed.add(template); // Install an inline cache at the callsite using the global variable:
  50. // _t || (_t = identity`a${0}`)
  51. const ident = path.scope.getProgramParent().generateDeclaredUidIdentifier("t");
  52. path.scope.getBinding(ident.name).path.parent.kind = "let";
  53. const inlineCache = t.logicalExpression("||", ident, t.assignmentExpression("=", t.cloneNode(ident), template)); // The original tag function becomes a plain function call.
  54. // The expressions omitted from the cached Strings tag are directly applied as arguments.
  55. // tag(_t || (_t = Object`a${0}`), 'hello')
  56. const node = t.callExpression(path.node.tag, [inlineCache, ...expressions]);
  57. path.replaceWith(node);
  58. }
  59. }
  60. });
  61. exports.default = _default;
  62. module.exports = exports.default;