diff --git a/benches/js/CLAUDE.md b/benches/js/CLAUDE.md
index 2719b2ff4..03fbb5940 100644
--- a/benches/js/CLAUDE.md
+++ b/benches/js/CLAUDE.md
@@ -1619,7 +1619,10 @@ CWD-relative).
`loc` from `start`/`end` (UTF-16 offsets) + source via the ECMAScript (TS) / LF-only
(Svelte) line rules, and assert equality. TS is 100% exact; the two Svelte non-derivable
cases (the `');
Three formatters (`format_svelte`, `format_typescript`, `format_css`) take a source `string` and return the formatted `string`. Three parsers (`parse_svelte`, `parse_typescript`, `parse_css`) return a Svelte-compatible JSON AST; the `parse_*_json` variants return the AST as a compact JSON string instead (faster when writing to disk or the wire). For TypeScript and Svelte, `parse_{typescript,svelte}_no_locations` (and `_json_no_locations`) emit a **span-only** wire — `start`/`end` offsets, no per-node `loc` (Svelte also no `name_loc`) — ~46% smaller and faster to materialize, with line/column derivable from offsets + source. All throw on a parse error.
-To turn a span-only wire back into a loc-bearing one, `reconstruct_locations(ast, source)` adds `loc` to every node (mutating in place; `structuredClone` first to keep the input) — **exact for TypeScript** (each node's `loc` value equals acorn's; the key is appended last, so an object consumer matches but a re-serialized tree won't byte-match the wire's key order), **approximate for Svelte** (no `name_loc`, and it skips Svelte's `\n\n
{x}
';
+ // Covers each `name_loc` shape (tag name, attribute, shorthand padded and
+ // not, a directive head with modifiers) and each identifier Svelte gives the
+ // name-shaped `loc` (shorthand expansion, snippet name, block patterns).
+ const sv =
+ '\n\n\n\tt\n\t{@const y = x}\n\t{#each [x] as item}{item}{/each}\n\t{#await x}p{:then value}{value}{:catch err}{err}{/await}\n
\n{#snippet row(a)}{a}{/snippet}';
const full = node_entry.parse_svelte(sv);
const recon = node_entry.reconstruct_locations(node_entry.parse_svelte_no_locations(sv), sv);
let checked = 0;
+ let name_locs_checked = 0;
const walk = (r: any, f: any): void => {
if (Array.isArray(f)) {
f.forEach((x, i) => walk(r[i], x));
@@ -242,6 +247,12 @@ describe(`locations helper (index.js): ${pkg_dir}`, { skip: !has_parse }, () =>
assert.deepEqual(r.loc, f.loc, `loc mismatch at ${f.type}@${f.start}`);
checked++;
}
+ // `name_loc` (elements, attributes, directives) is exact — its span is a
+ // function of the node's own start/end + type.
+ if (f.name_loc) {
+ assert.deepEqual(r.name_loc, f.name_loc, `name_loc mismatch at ${f.type}@${f.start}`);
+ name_locs_checked++;
+ }
for (const k of Object.keys(f)) {
if (k === 'loc' || k === 'name_loc') continue;
walk(r[k], f[k]);
@@ -250,6 +261,7 @@ describe(`locations helper (index.js): ${pkg_dir}`, { skip: !has_parse }, () =>
};
walk(recon, full);
assert.ok(checked > 0, 'expected at least one acorn node to compare');
+ assert.ok(name_locs_checked > 0, 'expected at least one name_loc to compare');
// The walk is a superset: it adds `loc` to template nodes Svelte's wire omits.
assert.ok(recon.loc, 'reconstruct added loc to the Root (template node)');
});