"use strict"; exports.__esModule = true; exports.default = void 0; /** * Converts destructured parameters with default values to non-shorthand syntax. * This fixes the only Tagged Templates-related bug in ES Modules-supporting browsers (Safari 10 & 11). * Use this plugin instead of `@babel/plugin-transform-template-literals` when targeting ES Modules. * * @example * // Bug 1: Safari 10/11 doesn't reliably return the same Strings value. * // The value changes depending on invocation and function optimization state. * function f() { return Object`` } * f() === new f() // false, should be true. * * @example * // Bug 2: Safari 10/11 use the same cached strings value when the string parts are the same. * // This behavior comes from an earlier version of the spec, and can cause tricky bugs. * Object``===Object`` // true, should be false. * * Benchmarks: https://jsperf.com/compiled-tagged-template-performance */ var _default = ({ types: t }) => ({ name: "transform-tagged-template-caching", visitor: { TaggedTemplateExpression(path, state) { // tagged templates we've already dealt with let processed = state.get("processed"); if (!processed) { processed = new WeakSet(); state.set("processed", processed); } if (processed.has(path.node)) return path.skip(); // Grab the expressions from the original tag. // tag`a${'hello'}` // ['hello'] const expressions = path.node.quasi.expressions; // Create an identity function helper: // identity = t => t let identity = state.get("identity"); if (!identity) { identity = path.scope.getProgramParent().generateDeclaredUidIdentifier("_"); state.set("identity", identity); const binding = path.scope.getBinding(identity.name); binding.path.get("init").replaceWith(t.arrowFunctionExpression( // re-use the helper identifier for compressability [t.identifier("t")], t.identifier("t"))); } // Use the identity function helper to get a reference to the template's Strings. // We replace all expressions with `0` ensure Strings has the same shape. // identity`a${0}` const template = t.taggedTemplateExpression(t.cloneNode(identity), t.templateLiteral(path.node.quasi.quasis, expressions.map(() => t.numericLiteral(0)))); processed.add(template); // Install an inline cache at the callsite using the global variable: // _t || (_t = identity`a${0}`) const ident = path.scope.getProgramParent().generateDeclaredUidIdentifier("t"); path.scope.getBinding(ident.name).path.parent.kind = "let"; const inlineCache = t.logicalExpression("||", ident, t.assignmentExpression("=", t.cloneNode(ident), template)); // The original tag function becomes a plain function call. // The expressions omitted from the cached Strings tag are directly applied as arguments. // tag(_t || (_t = Object`a${0}`), 'hello') const node = t.callExpression(path.node.tag, [inlineCache, ...expressions]); path.replaceWith(node); } } }); exports.default = _default; module.exports = exports.default;