Summary
When JSON.parse fails during template compilation in src/template.ts, the thrown TemplateParseError currently hardcodes the offset to 0, losing actual position information.
Additionally, buildTree() in src/tree.ts always throws TemplateParseError(..., 0) when it detects a variable expression in an object key, even though the sentinel encodes the originating expression index — which maps directly to a real source offset.
Motivation
Accurate offsets in TemplateParseError would significantly improve the developer experience by pointing directly to the problematic location in the original template string, rather than always reporting offset 0.
Affected locations
1. compile() — JSON.parse catch block (src/template.ts)
- Parse the native
SyntaxError message from JSON.parse using a regex to extract numeric column/position info (e.g. "column X" or "position X" patterns present in V8/SpiderMonkey messages).
- Map that extracted position back to the original template string, accounting for the sentinel substitutions made during compilation.
- Fall back to
0 if no numeric position can be extracted from the error message.
- Consider also tracking line numbers (not just character offsets) in
TemplateParseError for multi-line templates.
2. buildTree() — object-key sentinel check (src/tree.ts)
buildTree() currently uses SENTINEL_RE.test(k) and hard-codes 0 as the offset:
if (SENTINEL_RE.test(k)) {
throw new TemplateParseError(
'Variable expressions in object keys are not supported',
0, // always 0 — real offset is knowable
);
}
The sentinel encodes the expression index (e.g. \uE000E2\uE000 → index 2), which can be looked up in the exprs array already available at compile time. The fix is to:
- Thread a
readonly number[] of expression offsets into buildTree() (e.g. exprs.map(e => e.offset) passed from compile()).
- Switch from
SENTINEL_RE.test(k) to SENTINEL_RE.exec(k) to capture the match group.
- Use
exprOffsets[Number(match[1])] ?? 0 as the TemplateParseError offset.
- Propagate
exprOffsets into recursive buildTree() calls for arrays and object values.
Context
Requested by
@amery
Summary
When
JSON.parsefails during template compilation insrc/template.ts, the thrownTemplateParseErrorcurrently hardcodes the offset to0, losing actual position information.Additionally,
buildTree()insrc/tree.tsalways throwsTemplateParseError(..., 0)when it detects a variable expression in an object key, even though the sentinel encodes the originating expression index — which maps directly to a real source offset.Motivation
Accurate offsets in
TemplateParseErrorwould significantly improve the developer experience by pointing directly to the problematic location in the original template string, rather than always reporting offset0.Affected locations
1.
compile()—JSON.parsecatch block (src/template.ts)SyntaxErrormessage fromJSON.parseusing a regex to extract numeric column/position info (e.g."column X"or"position X"patterns present in V8/SpiderMonkey messages).0if no numeric position can be extracted from the error message.TemplateParseErrorfor multi-line templates.2.
buildTree()— object-key sentinel check (src/tree.ts)buildTree()currently usesSENTINEL_RE.test(k)and hard-codes0as the offset:The sentinel encodes the expression index (e.g.
\uE000E2\uE000→ index2), which can be looked up in theexprsarray already available at compile time. The fix is to:readonly number[]of expression offsets intobuildTree()(e.g.exprs.map(e => e.offset)passed fromcompile()).SENTINEL_RE.test(k)toSENTINEL_RE.exec(k)to capture the match group.exprOffsets[Number(match[1])] ?? 0as theTemplateParseErroroffset.exprOffsetsinto recursivebuildTree()calls for arrays and object values.Context
JSON.parseoffset): feat: JSON template engine with shell-style variable substitution #1 (comment)buildTree()object-key offset): feat: JSON template engine with shell-style variable substitution #1 (comment)src/template.ts(catch block),src/tree.ts(buildTree()object-key branch).Requested by
@amery