Skip to content

Commit 5557b11

Browse files
authored
Merge pull request #20782 from emberjs/bugfix/keyword-shadowing
Fixes Ember keyword shadowing
2 parents e1abff5 + 9d0a18a commit 5557b11

17 files changed

+221
-78
lines changed

packages/@ember/-internals/glimmer/tests/integration/helpers/array-test.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { RenderingTestCase, moduleFor, strip, runTask } from 'internal-test-helpers';
1+
import {
2+
RenderingTestCase,
3+
defineComponent,
4+
moduleFor,
5+
runTask,
6+
strip,
7+
} from 'internal-test-helpers';
28

39
import { set } from '@ember/object';
410

@@ -20,6 +26,21 @@ moduleFor(
2026
this.assertStableRerender();
2127
}
2228

29+
['@test the array helper can be shadowed']() {
30+
function array(...list) {
31+
return list.map((n) => n * 2);
32+
}
33+
34+
let First = defineComponent({ array }, `{{#each (array 1 2 3) as |n|}}[{{n}}]{{/each}}`);
35+
36+
let Root = defineComponent(
37+
{ shadowArray: array, First },
38+
`{{#let shadowArray as |array|}}{{#each (array 5 10 15) as |n|}}[{{n}}]{{/each}}{{/let}}<First />`
39+
);
40+
41+
this.renderComponent(Root, { expect: '[10][20][30][2][4][6]' });
42+
}
43+
2344
['@test can have more than one value']() {
2445
this.render(strip`
2546
{{#let (array "Sergio" "Robert") as |people|}}

packages/@ember/-internals/glimmer/tests/integration/helpers/fn-test.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { set } from '@ember/object';
22
import { DEBUG } from '@glimmer/env';
3-
import { RenderingTestCase, moduleFor, runTask } from 'internal-test-helpers';
3+
import { RenderingTestCase, defineComponent, moduleFor, runTask } from 'internal-test-helpers';
44
import { Component } from '../../utils/helpers';
55

66
moduleFor(
@@ -22,6 +22,14 @@ moduleFor(
2222
});
2323
}
2424

25+
'@test fn can be shadowed'() {
26+
let First = defineComponent(
27+
{ fn: boundFn, boundFn, id, invoke },
28+
`[{{invoke (fn id 1)}}]{{#let boundFn as |fn|}}[{{invoke (fn id 2)}}{{/let}}]`
29+
);
30+
this.renderComponent(First, { expect: `[bound:1][bound:2]` });
31+
}
32+
2533
'@test updates when arguments change'() {
2634
this.render(`{{invoke (fn this.myFunc this.arg1 this.arg2)}}`, {
2735
myFunc(arg1, arg2) {
@@ -209,3 +217,13 @@ moduleFor(
209217
}
210218
}
211219
);
220+
221+
function invoke(fn) {
222+
return fn();
223+
}
224+
225+
function boundFn(fn, ...args) {
226+
return () => fn(...args.map((arg) => `bound:${arg}`));
227+
}
228+
229+
let id = (arg) => arg;

packages/@ember/-internals/glimmer/tests/integration/helpers/get-test.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { RenderingTestCase, moduleFor, runTask } from 'internal-test-helpers';
1+
import { RenderingTestCase, defineComponent, moduleFor, runTask } from 'internal-test-helpers';
22

33
import { set, get } from '@ember/object';
44

@@ -32,6 +32,17 @@ moduleFor(
3232
this.assertText('[red] [red]');
3333
}
3434

35+
['@test can be shadowed']() {
36+
let get = (obj, key) => `obj.${key}=${obj[key]}`;
37+
let obj = { apple: 'red', banana: 'yellow' };
38+
let Root = defineComponent(
39+
{ get, outerGet: get, obj },
40+
`[{{get obj 'apple'}}][{{#let outerGet as |get|}}{{get obj 'banana'}}{{/let}}]`
41+
);
42+
43+
this.renderComponent(Root, { expect: '[obj.apple=red][obj.banana=yellow]' });
44+
}
45+
3546
['@test should be able to get an object value with nested static key']() {
3647
this.render(
3748
`[{{get this.colors "apple.gala"}}] [{{if true (get this.colors "apple.gala")}}]`,

packages/@ember/-internals/glimmer/tests/integration/helpers/hash-test.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { RenderingTestCase, moduleFor, runTask } from 'internal-test-helpers';
1+
import { RenderingTestCase, defineComponent, moduleFor, runTask } from 'internal-test-helpers';
22

33
import { Component } from '../../utils/helpers';
44

@@ -17,6 +17,21 @@ moduleFor(
1717
this.assertText('Sergio');
1818
}
1919

20+
['@test can be shadowed']() {
21+
let hash = (obj) =>
22+
Object.entries(obj)
23+
.map(([key, value]) => `hash:${key}=${value}`)
24+
.join(',');
25+
let Root = defineComponent(
26+
{ hash, shadowHash: hash },
27+
`({{hash apple='red' banana='yellow'}}) ({{#let shadowHash as |hash|}}{{hash apple='green'}}{{/let}})`
28+
);
29+
30+
this.renderComponent(Root, {
31+
expect: '(hash:apple=red,hash:banana=yellow) (hash:apple=green)',
32+
});
33+
}
34+
2035
['@test can have more than one key-value']() {
2136
this.render(
2237
`{{#let (hash name="Sergio" lastName="Arbeo") as |person|}}{{person.name}} {{person.lastName}}{{/let}}`

packages/ember-template-compiler/lib/plugins/assert-against-attrs.ts

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { assert, deprecate } from '@ember/debug';
22
import type { AST, ASTPlugin } from '@glimmer/syntax';
33
import calculateLocationDisplay from '../system/calculate-location-display';
44
import type { EmberASTPluginEnvironment } from '../types';
5+
import { trackLocals } from './utils';
56

67
/**
78
@module ember
@@ -23,48 +24,16 @@ import type { EmberASTPluginEnvironment } from '../types';
2324
export default function assertAgainstAttrs(env: EmberASTPluginEnvironment): ASTPlugin {
2425
let { builders: b } = env.syntax;
2526
let moduleName = env.meta?.moduleName;
26-
27-
let stack: string[][] = [[]];
28-
29-
function updateBlockParamsStack(blockParams: string[]) {
30-
let parent = stack[stack.length - 1];
31-
assert('has parent', parent);
32-
stack.push(parent.concat(blockParams));
33-
}
27+
let { hasLocal, visitor } = trackLocals(env);
3428

3529
return {
3630
name: 'assert-against-attrs',
3731

3832
visitor: {
39-
Template: {
40-
enter(node: AST.Template) {
41-
updateBlockParamsStack(node.blockParams);
42-
},
43-
exit() {
44-
stack.pop();
45-
},
46-
},
47-
48-
Block: {
49-
enter(node: AST.Block) {
50-
updateBlockParamsStack(node.blockParams);
51-
},
52-
exit() {
53-
stack.pop();
54-
},
55-
},
56-
57-
ElementNode: {
58-
enter(node: AST.ElementNode) {
59-
updateBlockParamsStack(node.blockParams);
60-
},
61-
exit() {
62-
stack.pop();
63-
},
64-
},
33+
...visitor,
6534

6635
PathExpression(node: AST.PathExpression): AST.Node | void {
67-
if (isAttrs(node, stack[stack.length - 1]!)) {
36+
if (isAttrs(node, hasLocal)) {
6837
assert(
6938
`Using {{attrs}} to reference named arguments is not supported. {{${
7039
node.original
@@ -106,12 +75,8 @@ export default function assertAgainstAttrs(env: EmberASTPluginEnvironment): ASTP
10675
};
10776
}
10877

109-
function isAttrs(node: AST.PathExpression, symbols: string[]) {
110-
return (
111-
node.head.type === 'VarHead' &&
112-
node.head.name === 'attrs' &&
113-
symbols.indexOf(node.head.name) === -1
114-
);
78+
function isAttrs(node: AST.PathExpression, hasLocal: (k: string) => boolean) {
79+
return node.head.type === 'VarHead' && node.head.name === 'attrs' && !hasLocal(node.head.name);
11580
}
11681

11782
function isThisDotAttrs(node: AST.PathExpression) {

packages/ember-template-compiler/lib/plugins/assert-against-named-outlets.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { assert } from '@ember/debug';
22
import type { AST, ASTPlugin } from '@glimmer/syntax';
33
import calculateLocationDisplay from '../system/calculate-location-display';
44
import type { EmberASTPluginEnvironment } from '../types';
5+
import { trackLocals } from './utils';
56

67
/**
78
@module ember
@@ -15,16 +16,19 @@ import type { EmberASTPluginEnvironment } from '../types';
1516
*/
1617
export default function assertAgainstNamedOutlets(env: EmberASTPluginEnvironment): ASTPlugin {
1718
let moduleName = env.meta?.moduleName;
19+
let { hasLocal, visitor } = trackLocals(env);
1820

1921
return {
2022
name: 'assert-against-named-outlets',
2123

2224
visitor: {
25+
...visitor,
2326
MustacheStatement(node: AST.MustacheStatement) {
2427
if (
2528
node.path.type === 'PathExpression' &&
2629
node.path.original === 'outlet' &&
27-
node.params[0]
30+
node.params[0] &&
31+
!hasLocal('outlet')
2832
) {
2933
let sourceInformation = calculateLocationDisplay(moduleName, node.loc);
3034
assert(

packages/ember-template-compiler/lib/plugins/assert-input-helper-without-block.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@ import { assert } from '@ember/debug';
22
import type { AST, ASTPlugin } from '@glimmer/syntax';
33
import calculateLocationDisplay from '../system/calculate-location-display';
44
import type { EmberASTPluginEnvironment } from '../types';
5-
import { isPath } from './utils';
5+
import { isPath, trackLocals } from './utils';
66

77
export default function errorOnInputWithContent(env: EmberASTPluginEnvironment): ASTPlugin {
88
let moduleName = env.meta?.moduleName;
9+
let { hasLocal, visitor } = trackLocals(env);
910

1011
return {
1112
name: 'assert-input-helper-without-block',
1213

1314
visitor: {
15+
...visitor,
1416
BlockStatement(node: AST.BlockStatement) {
17+
if (hasLocal('input')) return;
18+
1519
if (isPath(node.path) && node.path.original === 'input') {
1620
assert(assertMessage(moduleName, node));
1721
}

packages/ember-template-compiler/lib/plugins/transform-action-syntax.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { AST, ASTPlugin } from '@glimmer/syntax';
22
import type { Builders, EmberASTPluginEnvironment } from '../types';
3-
import { isPath } from './utils';
3+
import { isPath, trackLocals } from './utils';
44

55
/**
66
@module ember
@@ -27,36 +27,41 @@ import { isPath } from './utils';
2727
@class TransformActionSyntax
2828
*/
2929

30-
export default function transformActionSyntax({ syntax }: EmberASTPluginEnvironment): ASTPlugin {
31-
let { builders: b } = syntax;
30+
export default function transformActionSyntax(env: EmberASTPluginEnvironment): ASTPlugin {
31+
let { builders: b } = env.syntax;
32+
let { hasLocal, visitor } = trackLocals(env);
3233

3334
return {
3435
name: 'transform-action-syntax',
3536

3637
visitor: {
38+
...visitor,
3739
ElementModifierStatement(node: AST.ElementModifierStatement) {
38-
if (isAction(node)) {
40+
if (isAction(node, hasLocal)) {
3941
insertThisAsFirstParam(node, b);
4042
}
4143
},
4244

4345
MustacheStatement(node: AST.MustacheStatement) {
44-
if (isAction(node)) {
46+
if (isAction(node, hasLocal)) {
4547
insertThisAsFirstParam(node, b);
4648
}
4749
},
4850

4951
SubExpression(node: AST.SubExpression) {
50-
if (isAction(node)) {
52+
if (isAction(node, hasLocal)) {
5153
insertThisAsFirstParam(node, b);
5254
}
5355
},
5456
},
5557
};
5658
}
5759

58-
function isAction(node: AST.ElementModifierStatement | AST.MustacheStatement | AST.SubExpression) {
59-
return isPath(node.path) && node.path.original === 'action';
60+
function isAction(
61+
node: AST.ElementModifierStatement | AST.MustacheStatement | AST.SubExpression,
62+
hasLocal: (k: string) => boolean
63+
) {
64+
return isPath(node.path) && node.path.original === 'action' && !hasLocal('action');
6065
}
6166

6267
function insertThisAsFirstParam(

packages/ember-template-compiler/lib/plugins/transform-each-track-array.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { AST, ASTPlugin } from '@glimmer/syntax';
22
import { assert } from '@ember/debug';
33
import type { EmberASTPluginEnvironment } from '../types';
4-
import { isPath } from './utils';
4+
import { isPath, trackLocals } from './utils';
55

66
/**
77
@module ember
@@ -25,13 +25,15 @@ import { isPath } from './utils';
2525
*/
2626
export default function transformEachTrackArray(env: EmberASTPluginEnvironment): ASTPlugin {
2727
let { builders: b } = env.syntax;
28+
let { hasLocal, visitor } = trackLocals(env);
2829

2930
return {
3031
name: 'transform-each-track-array',
3132

3233
visitor: {
34+
...visitor,
3335
BlockStatement(node: AST.BlockStatement): AST.Node | void {
34-
if (isPath(node.path) && node.path.original === 'each') {
36+
if (isPath(node.path) && node.path.original === 'each' && !hasLocal('each')) {
3537
let firstParam = node.params[0];
3638
assert('has firstParam', firstParam);
3739

packages/ember-template-compiler/lib/plugins/transform-resolutions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const TARGETS = Object.freeze(['helper', 'modifier']);
6767
export default function transformResolutions(env: EmberASTPluginEnvironment): ASTPlugin {
6868
let { builders: b } = env.syntax;
6969
let moduleName = env.meta?.moduleName;
70-
let { hasLocal, node: tracker } = trackLocals();
70+
let { hasLocal, node: tracker } = trackLocals(env);
7171
let seen: Set<AST.Node> | undefined;
7272

7373
return {

0 commit comments

Comments
 (0)