Removing $placeholder changes how an unquoted template attribute value, attribute name, or tag name parses (13 languages)
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 13k
- Forks
- 1.4k
- Avg merge
- 15h 36m
- Merged PRs (30d)
- 3
Description
#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 <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.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the createInstance reproduction in tests/helper/prism-loader.js and compare the current output with v2 for the 13 templating languages and three positions listed. Inspect the changes from #4110 and the affected fixtures; the issue documents the behavior and trade-off but does not define a target fix or acceptance criteria.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100