Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle this-fallback for @arg Attr Node values within Element Nodes #13

Merged
merged 1 commit into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,29 @@ For each `PathExpression` with a `VarHead` that is NOT in the local template sco

- If there is NO `tail`:

- If the `MustacheStatement` is the child of an `AttrNode` with a name NOT starting with `@`:
- If the `MustacheStatement` is the child of an `AttrNode`

Wrap the invocation with the `tryLookupHelper` helper to determine if it is a helper at runtime and fall back to the `this` property if not ("ambiguous attribute fallback").
- And the `AttrNode` represents a component argument (the name starts with `'@'`):

```hbs
{{! before }}
<Parent id={{property}} />
Prefix the `head` with `this`, making it a `ThisHead` ("expression fallback"), as shown above.

{{! after }}
{{#let (hash property=(tryLookupHelper "property")) as |maybeHelpers|}}
<Parent
id={{(if
maybeHelpers.property (maybeHelpers.property) this.property
)}}
/>
{{/let}}
```
- And the `AttrNode` represents an attribute (the name does not start with `'@'`):

Wrap the invocation with the `tryLookupHelper` helper to determine if it is a helper at runtime and fall back to the `this` property if not ("ambiguous attribute fallback").

```hbs
{{! before }}
<Parent id={{property}} />

{{! after }}
{{#let (hash property=(tryLookupHelper "property")) as |maybeHelpers|}}
<Parent
id={{(if
maybeHelpers.property (maybeHelpers.property) this.property
)}}
/>
{{/let}}
```

- Otherwise:

Expand Down
18 changes: 10 additions & 8 deletions lib/this-fallback-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ class ThisFallbackPlugin implements ASTPlugin {
for (const attrNode of elementNode.attributes) {
const value = attrNode.value;
if (
!attrNode.name.startsWith('@') &&
isNode(value, 'MustacheStatement') &&
mustacheNeedsFallback(value, this.scopeStack)
) {
Expand All @@ -101,11 +100,15 @@ class ThisFallbackPlugin implements ASTPlugin {
'attrNode.value is not a MustacheStatement',
isNode(attrNode.value, 'MustacheStatement')
);
ambiguousHeads.set(value.path.head.name, value.loc);
attrNode.value.path = helperOrExpressionFallback(
blockParamName,
value
);
if (attrNode.name.startsWith('@')) {
attrNode.value.path = expressionFallback(value.path);
} else {
ambiguousHeads.set(value.path.head.name, value.loc);
attrNode.value.path = helperOrExpressionFallback(
blockParamName,
value
);
}
} else if (isNode(value, 'ConcatStatement')) {
for (const part of value.parts) {
const p = part;
Expand Down Expand Up @@ -182,8 +185,7 @@ class ThisFallbackPlugin implements ASTPlugin {
if (mustacheNeedsFallback(n, this.scopeStack)) {
assert(
'unexpected AmbiguousMustacheExpression in attribute value',
path.parentNode?.type !== 'AttrNode' ||
path.parentNode.name.startsWith('@')
path.parentNode?.type !== 'AttrNode'
);
if (n.path.tail.length > 0) {
node.path = expressionFallback(n.path);
Expand Down
16 changes: 16 additions & 0 deletions tests/integration/plugin/mustache-statement-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,22 @@ module('Integration | Plugin | MustacheStatement', function (hooks) {
});

module('for an @arg', function () {
test('has this-fallback', async function (assert) {
this.set('property', 'property-on-this');
await render<{ property: string }>(hbs`
{{!--
@glint-expect-error:
Unknown name 'property' (Glint knows better than to let us do this)
--}}
<GlobalComponent @arg={{property}} as |yielded|>
{{yielded}}
</GlobalComponent>
`);
assert
.dom()
.hasText('global-component-contents property-on-this');
});

test('does nothing to ThisHead PathExpression', async function (assert) {
this.set('property', 'property-on-this');
await render<{ property: string }>(hbs`
Expand Down