diff --git a/packages/@ember/-internals/deprecations/index.ts b/packages/@ember/-internals/deprecations/index.ts index 041b533468d..bab0bde9d4e 100644 --- a/packages/@ember/-internals/deprecations/index.ts +++ b/packages/@ember/-internals/deprecations/index.ts @@ -90,6 +90,16 @@ function deprecation(options: DeprecationOptions) { test the behavior without encountering the deprecated feature, just as users would. */ export const DEPRECATIONS = { + DEPRECATE_ATTRS_ARG_ACCESS: deprecation({ + id: 'attrs-arg-access', + url: 'https://deprecations.emberjs.com/id/attrs-arg-access', + until: '6.0.0', + for: 'ember-source', + since: { + available: '3.26.0', + enabled: '3.26.0', + }, + }), DEPRECATE_IMPLICIT_ROUTE_MODEL: deprecation({ id: 'deprecate-implicit-route-model', for: 'ember-source', diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js index 3dd188abc3f..386e7c6bb2c 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js @@ -1270,10 +1270,13 @@ moduleFor( }, "Using {{attrs}} to reference named arguments is not supported. {{attrs.someProp}} should be updated to {{@someProp}}. ('my-app/templates/components/non-block.hbs' @ L1:C24) "); } + // Perhaps change this test to `{{this.attrs.someProp.value}}` when removing the deprecation? ['@test non-block with properties on this.attrs']() { - this.registerComponent('non-block', { - template: 'In layout - someProp: {{this.attrs.someProp}}', - }); + expectDeprecation(() => { + this.registerComponent('non-block', { + template: 'In layout - someProp: {{this.attrs.someProp}}', + }); + }, /Using {{this.attrs}} to reference named arguments has been deprecated. {{this.attrs.someProp}} should be updated to {{@someProp}}./); this.render('{{non-block someProp=this.prop}}', { prop: 'something here', @@ -1477,21 +1480,24 @@ moduleFor( ); } + // Perhaps change this test to `{{this.attrs.foo.value}}` when removing the deprecation? ['@test this.attrs.foo === @foo === foo']() { - this.registerComponent('foo-bar', { - template: strip` - Args: {{this.attrs.value}} | {{@value}} | {{this.value}} - {{#each this.attrs.items as |item|}} - {{item}} - {{/each}} - {{#each @items as |item|}} - {{item}} - {{/each}} - {{#each this.items as |item|}} - {{item}} - {{/each}} - `, - }); + expectDeprecation(() => { + this.registerComponent('foo-bar', { + template: strip` + Args: {{this.attrs.value}} | {{@value}} | {{this.value}} + {{#each this.attrs.items as |item|}} + {{item}} + {{/each}} + {{#each @items as |item|}} + {{item}} + {{/each}} + {{#each this.items as |item|}} + {{item}} + {{/each}} + `, + }); + }, /Using {{this.attrs}} to reference named arguments has been deprecated. {{this.attrs..+}} should be updated to {{@.+}}./); this.render('{{foo-bar value=this.model.value items=this.model.items}}', { model: { @@ -1576,10 +1582,13 @@ moduleFor( }, "Using {{attrs}} to reference named arguments is not supported. {{attrs.someProp}} should be updated to {{@someProp}}. ('my-app/templates/components/with-block.hbs' @ L1:C24) "); } + // Perhaps change this test to `{{this.attrs.someProp.value}}` when removing the deprecation? ['@test block with properties on this.attrs']() { - this.registerComponent('with-block', { - template: 'In layout - someProp: {{this.attrs.someProp}} - {{yield}}', - }); + expectDeprecation(() => { + this.registerComponent('with-block', { + template: 'In layout - someProp: {{this.attrs.someProp}} - {{yield}}', + }); + }, /Using {{this.attrs}} to reference named arguments has been deprecated. {{this.attrs.someProp}} should be updated to {{@someProp}}./); this.render( strip` @@ -3319,16 +3328,19 @@ moduleFor( }, "Using {{attrs}} to reference named arguments is not supported. {{attrs.myVar}} should be updated to {{@myVar}}. ('my-app/templates/components/foo-bar.hbs' @ L1:C10) "); } + // Perhaps change this test to `{{this.attrs.myVar.value}}` when removing the deprecation? ['@test using this.attrs for positional params']() { let MyComponent = Component.extend(); - this.registerComponent('foo-bar', { - ComponentClass: MyComponent.reopenClass({ - positionalParams: ['myVar'], - }), - template: - 'MyVar1: {{this.attrs.myVar}} {{this.myVar}} MyVar2: {{this.myVar2}} {{this.attrs.myVar2}}', - }); + expectDeprecation(() => { + this.registerComponent('foo-bar', { + ComponentClass: MyComponent.reopenClass({ + positionalParams: ['myVar'], + }), + template: + 'MyVar1: {{this.attrs.myVar}} {{this.myVar}} MyVar2: {{this.myVar2}} {{this.attrs.myVar2}}', + }); + }, /Using {{this.attrs}} to reference named arguments has been deprecated. {{this.attrs.myVar2?}} should be updated to {{@myVar2?}}./); this.render('{{foo-bar 1 myVar2=2}}'); diff --git a/packages/ember-template-compiler/lib/plugins/assert-against-attrs.ts b/packages/ember-template-compiler/lib/plugins/assert-against-attrs.ts index 9a81ab58736..064968b7ff8 100644 --- a/packages/ember-template-compiler/lib/plugins/assert-against-attrs.ts +++ b/packages/ember-template-compiler/lib/plugins/assert-against-attrs.ts @@ -1,4 +1,5 @@ import { assert } from '@ember/debug'; +import { DEPRECATIONS, deprecateUntil } from '@ember/-internals/deprecations'; import type { AST, ASTPlugin } from '@glimmer/syntax'; import calculateLocationDisplay from '../system/calculate-location-display'; import type { EmberASTPluginEnvironment } from '../types'; @@ -56,17 +57,31 @@ export default function assertAgainstAttrs(env: EmberASTPluginEnvironment): ASTP PathExpression(node: AST.PathExpression): AST.Node | void { if (isAttrs(node, stack[stack.length - 1]!)) { - let path = b.path(node.original.substring(6)); - assert( - `Using {{attrs}} to reference named arguments is not supported. {{attrs.${ - path.original - }}} should be updated to {{@${path.original}}}. ${calculateLocationDisplay( + `Using {{attrs}} to reference named arguments is not supported. {{${ + node.original + }}} should be updated to {{@${node.original.slice(6)}}}. ${calculateLocationDisplay( + moduleName, + node.loc + )}` + ); + } else if (isThisDotAttrs(node)) { + // When removing this, ensure `{{this.attrs.foo}}` is left as-is, without triggering + // any assertions/deprecations. It's perfectly legal to reference `{{this.attrs.foo}}` + // in the template since it is a real property on the backing class – it will give you + // a `MutableCell` wrapper object, but maybe that's what you want. And in any case, + // there is no compelling to special case that property access. + deprecateUntil( + `Using {{this.attrs}} to reference named arguments has been deprecated. {{${ + node.original + }}} should be updated to {{@${node.original.slice(11)}}}. ${calculateLocationDisplay( moduleName, node.loc )}`, - node.this !== false + DEPRECATIONS.DEPRECATE_ATTRS_ARG_ACCESS ); + + return b.path(`@${node.original.slice(11)}`, node.loc); } }, }, @@ -75,19 +90,10 @@ export default function assertAgainstAttrs(env: EmberASTPluginEnvironment): ASTP function isAttrs(node: AST.PathExpression, symbols: string[]) { let name = node.parts[0]; + return node.head.type === 'VarHead' && name === 'attrs' && symbols.indexOf(name) === -1; +} - if (name && symbols.indexOf(name) !== -1) { - return false; - } - - if (name === 'attrs') { - if (node.this === true) { - node.parts.shift(); - node.original = node.original.slice(5); - } - - return true; - } - - return false; +function isThisDotAttrs(node: AST.PathExpression) { + let name = node.parts[0]; + return node.head.type === 'ThisHead' && name === 'attrs'; } diff --git a/packages/ember-template-compiler/tests/plugins/assert-against-attrs-test.js b/packages/ember-template-compiler/tests/plugins/assert-against-attrs-test.js index 30f5c32fbd9..ea5b584fa42 100644 --- a/packages/ember-template-compiler/tests/plugins/assert-against-attrs-test.js +++ b/packages/ember-template-compiler/tests/plugins/assert-against-attrs-test.js @@ -29,14 +29,26 @@ moduleFor( }, /Using {{attrs}} to reference named arguments is not supported. {{attrs.foo.bar}} should be updated to {{@foo.bar}}./); } - ['@test it does not assert against this.attrs']() { - this.assertTransformed(`{{this.attrs.foo}}`, `{{this.attrs.foo}}`); - this.assertTransformed(`{{if this.attrs.foo "foo"}}`, `{{if this.attrs.foo "foo"}}`); - this.assertTransformed(`{{#if this.attrs.foo}}{{/if}}`, `{{#if this.attrs.foo}}{{/if}}`); - this.assertTransformed( - `{{deeply (nested this.attrs.foo.bar)}}`, - `{{deeply (nested this.attrs.foo.bar)}}` - ); + // When removing the deprecation, ensure `{{this.attrs.foo}}` isn't rewritten and does NOT trigger any assertions/deprecations + ['@test this.attrs is deprecated']() { + expectDeprecation(() => { + this.assertTransformed(`{{this.attrs.foo}}`, `{{@foo}}`); + }, /Using {{this.attrs}} to reference named arguments has been deprecated. {{this.attrs.foo}} should be updated to {{@foo}}./); + + expectDeprecation(() => { + this.assertTransformed(`{{if this.attrs.foo "foo"}}`, `{{if @foo "foo"}}`); + }, /Using {{this.attrs}} to reference named arguments has been deprecated. {{this.attrs.foo}} should be updated to {{@foo}}./); + + expectDeprecation(() => { + this.assertTransformed(`{{#if this.attrs.foo}}{{/if}}`, `{{#if @foo}}{{/if}}`); + }, /Using {{this.attrs}} to reference named arguments has been deprecated. {{this.attrs.foo}} should be updated to {{@foo}}./); + + expectDeprecation(() => { + this.assertTransformed( + `{{deeply (nested this.attrs.foo.bar)}}`, + `{{deeply (nested @foo.bar)}}` + ); + }, /Using {{this.attrs}} to reference named arguments has been deprecated. {{this.attrs.foo.bar}} should be updated to {{@foo.bar}}./); } } );