overlookmotel/livepack

Tokens arrays of tagged template literals not linked to the tagged template

Opened this issue · 1 comments

From MDN:

For any particular tagged template literal expression, the tag function will always be called with the exact same literal array, no matter how many times the literal is evaluated.

Input:

function tag(tokens) {
  return tokens;
}

function runTag() {
  return tag`a${123}b${456}c`;
}

const tokens = runTag();

export default function compare() {
  return runTag() === tokens;
}

In the source code, compare() returns true. But in output it returns false because Livepack does not understand the link between tokens and the template literal in runTag().

Output:

const Object$0 = Object,
  ObjectFreeze = Object$0.freeze;
export default (
  (tokens, tag, runTag$0) => (
    runTag$0 = function runTag() {
      return tag`a${123}b${456}c`;
    },
    function compare() {
      return runTag$0() === tokens;
    }
  )
)(
  ObjectFreeze(
    Object$0.defineProperties(
      ["a", "b", "c"],
      { raw: { value: ObjectFreeze(["a", "b", "c"]) } }
    )
  ),
  function tag(tokens) {
    return tokens;
  }
);

This would be annoying to solve.

Instrumentation would need to replace the tag function with a wrapper which records:

  • the tokens array
  • the raw property of the tokens array
  • the function which the tagged template is in

If the tokens array/raw tokens array and the function are both included in output, it'd need to rewrite the runTag function as:

const tokens = (t => t)`a${0}b${0}c`;
function runTag() {
  // Was: tag`a${123}b${456}c`
  return tag(tokens, 123, 456);
}

NB: The tokens array is lexically bound to the tagged template.

const tag = tokens => tokens;
const makeRunTag = () => () => tag`a${1}b${2}c`;
const tokens1 = makeRunTag()();
const tokens2 = makeRunTag()();

console.log(tokens1 === tokens2); // true

i.e. A single tagged template in source code corresponds to a single tokens array, regardless of how many function instances it's in.