Skip to content

Commit 420295b

Browse files
lifeartclaude
andcommitted
fix(gxt-backend): detect SubExpression refs in strict-mode scope threading
The strict-mode scope() callback merge in precompileTemplate was gated by _templateMayNeedScopeThreading, which only fired when the template had a PascalCase tag or a free-identifier mustache head. Templates whose only mustache was `{{on "evt" (fn handler arg)}}` slipped through: - The mustache head `on` is skipped (the visitor short-circuits {{on}}) - There is no PascalCase tag - The SubExpression `(fn handler arg)` was never inspected Result: `handler` and `fn` were never merged into scopeValues, the GXT compiler emitted `$__fn(this.handler, ...)` (resolving against `this`, not the strict-mode scope), and the click handler was a no-op. Extend the gate to also scan for SubExpression heads `(ident ...)`. The existing `_scopeNameAppearsAsReference` filter still prunes scope entries the template never references, so loose / Input / Textarea templates stay unaffected. Fixes the two `Strict Mode - built ins: Can use on and fn` and `Strict Mode - renderComponent - built ins: Can use on and fn` tests. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
1 parent 98b7365 commit 420295b

1 file changed

Lines changed: 15 additions & 0 deletions

File tree

packages/@ember/-internals/gxt-backend/compile.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11714,6 +11714,21 @@ function _templateMayNeedScopeThreading(template: string): boolean {
1171411714
if (head === 'this' || head === 'on') continue;
1171511715
return true;
1171611716
}
11717+
// SubExpression `(ident ...)` syntax — e.g. `{{on "click" (fn handleClick 123)}}`.
11718+
// The mustache head may be `on` (skipped above) but the modifier args can
11719+
// reference a strict-mode scope binding via a SubExpression. Detect that
11720+
// pattern so the scope() callback is consulted for any free identifiers.
11721+
// Match `(ident` only when preceded by whitespace, `=`, `(`, `{` or `,`
11722+
// to avoid matching attribute literals like `style="(border)"`.
11723+
const subExprRe = /(?:^|[\s={(,])\(([A-Za-z_][A-Za-z0-9_-]*)/g;
11724+
while ((m = subExprRe.exec(template))) {
11725+
const head = m[1];
11726+
// `this` cannot appear as a SubExpression head; only `on` is worth
11727+
// skipping (a SubExpression `(on ...)` would reference the modifier
11728+
// helper, not a free user binding).
11729+
if (head === 'this' || head === 'on') continue;
11730+
return true;
11731+
}
1171711732
return false;
1171811733
}
1171911734

0 commit comments

Comments
 (0)