Skip to content

Removing $placeholder changes how an unquoted template attribute value, attribute name, or tag name parses (13 languages) #4119

Description

@DmitrySharabin

#4110 removed $placeholder: template tokens no longer leave an identifier-like stand-in in the code handed to the host grammar, they simply vanish. Its description records the consequence for three fixtures; this issue is to track it, since nothing else does.

Where a template expression is an entire unquoted value, the host grammar no longer sees a value there and stops parsing the construct around it. Quoted values are unaffected.

Repro

import { createInstance } from './tests/helper/prism-loader.js';

const prism = await createInstance(['php', 'markup', 'javascript', 'clike', 'css']);

console.log(prism.highlight('<span data-x=<?= $x ?>>a</span>', 'php'));
v2   [tag [punctuation <][tag span] [attr-name data-x][attr-value [punctuation =][php …]][punctuation >]]
new  &lt;span data-x=[php …]>a…                                  ← the whole opening tag is no longer a tag

Scope, measured

All 13 markup-templating languages — django, twig, handlebars, liquid, ejs, erb, etlua, tt2, smarty, ftl, latte, soy, php — in three positions:

position example severity
unquoted attribute value <span data-x={{x}}> the whole opening tag stops being a tag
tag name <h{$level}> tag survives, name is just h
unquoted attribute name <div {{on "click"}}> tag survives, loses the attr-name wrapper

Quoted values (href="{{url}}") are identical to v2.

Of these, only the first loses a whole construct. latte and soy's fixtures changed in #4110 because their stripped text still parses; handlebars' fixture shows the tag being lost.

Not worth chasing

A template expression between JS regex delimiters — <script>var re = /{{p}}/;</script> — collapses to // and comments out the rest of that line. Same root cause, but nobody writes that: interpolating into a regex literal breaks on the first / or backslash in the value, so real code uses new RegExp('{{p}}'). Checked against a corpus of realistic template-in-script snippets (JSON blob, quoted string, bare number, condition, CSS value) — all byte-identical to v2.

Why it is a reasonable trade

Placeholders caused their own class of bugs, found in #4107: the stand-in being parsed as markdown emphasis, as a JS constant, or split in two by the host grammar. This is recorded for tracking, not to argue #4110 should be reverted.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions