diff --git a/packages/@ember/-internals/glimmer/tests/integration/application/engine-test.js b/packages/@ember/-internals/glimmer/tests/integration/application/engine-test.js index 8c719b1878b..bed3b718032 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/application/engine-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/application/engine-test.js @@ -272,6 +272,9 @@ moduleFor( expectDeprecation( `The use of \`{{partial}}\` is deprecated, please refactor the "troll" partial to a component` ); + expectDeprecation( + 'Using {{attrs}} to reference named arguments has been deprecated. {{attrs.wat}} should be updated to {{@wat}}. (L1:C2) ' + ); this.setupEngineWithAttrs([]); diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/class-bindings-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/class-bindings-test.js index a2388f668f6..8ea37af3657 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/class-bindings-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/class-bindings-test.js @@ -126,6 +126,10 @@ moduleFor( } ['@test it can have class name bindings in the template']() { + expectDeprecation( + "Passing the `classNameBindings` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('-top-level' @ L1:C0) " + ); + this.registerComponent('foo-bar', { template: 'hello' }); this.render( @@ -343,6 +347,10 @@ moduleFor( } ['@test const bindings can be set as attrs']() { + expectDeprecation( + "Passing the `classNameBindings` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('-top-level' @ L1:C0) " + ); + this.registerComponent('foo-bar', { template: 'hello' }); this.render('{{foo-bar classNameBindings="foo:enabled:disabled"}}', { foo: true, @@ -675,6 +683,10 @@ moduleFor( 'ClassBinding integration', class extends RenderingTestCase { ['@test it should apply classBinding without condition always']() { + expectDeprecation( + "Passing the `classBinding` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('-top-level' @ L1:C0) " + ); + this.registerComponent('foo-bar', { template: 'hello' }); this.render('{{foo-bar classBinding=":foo"}}'); @@ -695,6 +707,10 @@ moduleFor( } ['@test it should merge classBinding with class']() { + expectDeprecation( + "Passing the `classBinding` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('-top-level' @ L1:C0) " + ); + this.registerComponent('foo-bar', { template: 'hello' }); this.render('{{foo-bar classBinding="birdman:respeck" class="myName"}}', { @@ -717,6 +733,10 @@ moduleFor( } ['@test it should apply classBinding with only truthy condition']() { + expectDeprecation( + "Passing the `classBinding` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('-top-level' @ L1:C0) " + ); + this.registerComponent('foo-bar', { template: 'hello' }); this.render('{{foo-bar classBinding="myName:respeck"}}', { @@ -739,6 +759,10 @@ moduleFor( } ['@test it should apply classBinding with only falsy condition']() { + expectDeprecation( + "Passing the `classBinding` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('-top-level' @ L1:C0) " + ); + this.registerComponent('foo-bar', { template: 'hello' }); this.render('{{foo-bar classBinding="myName::shade"}}', { @@ -761,6 +785,10 @@ moduleFor( } ['@test it should apply nothing when classBinding is falsy but only supplies truthy class']() { + expectDeprecation( + "Passing the `classBinding` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('-top-level' @ L1:C0) " + ); + this.registerComponent('foo-bar', { template: 'hello' }); this.render('{{foo-bar classBinding="myName:respeck"}}', { @@ -783,6 +811,10 @@ moduleFor( } ['@test it should apply nothing when classBinding is truthy but only supplies falsy class']() { + expectDeprecation( + "Passing the `classBinding` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('-top-level' @ L1:C0) " + ); + this.registerComponent('foo-bar', { template: 'hello' }); this.render('{{foo-bar classBinding="myName::shade"}}', { myName: true }); @@ -803,6 +835,10 @@ moduleFor( } ['@test it should apply classBinding with falsy condition']() { + expectDeprecation( + "Passing the `classBinding` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('-top-level' @ L1:C0) " + ); + this.registerComponent('foo-bar', { template: 'hello' }); this.render('{{foo-bar classBinding="swag:fresh:scrub"}}', { @@ -825,6 +861,10 @@ moduleFor( } ['@test it should apply classBinding with truthy condition']() { + expectDeprecation( + "Passing the `classBinding` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('-top-level' @ L1:C0) " + ); + this.registerComponent('foo-bar', { template: 'hello' }); this.render('{{foo-bar classBinding="swag:fresh:scrub"}}', { 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 c498dfd4495..23f8fafe780 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 @@ -1303,6 +1303,10 @@ moduleFor( } ['@test non-block with properties on attrs']() { + expectDeprecation( + "Using {{attrs}} to reference named arguments has been deprecated. {{attrs.someProp}} should be updated to {{@someProp}}. ('my-app/templates/components/non-block.hbs' @ L1:C24) " + ); + this.registerComponent('non-block', { template: 'In layout - someProp: {{attrs.someProp}}', }); @@ -1512,6 +1516,22 @@ moduleFor( } ['@test this.attrs.foo === attrs.foo === @foo === foo']() { + expectDeprecation( + "Using {{attrs}} to reference named arguments has been deprecated. {{attrs.value}} should be updated to {{@value}}. ('my-app/templates/components/foo-bar.hbs' @ L1:C8) " + ); + + expectDeprecation( + "Using {{attrs}} to reference named arguments has been deprecated. {{attrs.value}} should be updated to {{@value}}. ('my-app/templates/components/foo-bar.hbs' @ L1:C31) " + ); + + expectDeprecation( + "Using {{attrs}} to reference named arguments has been deprecated. {{attrs.items}} should be updated to {{@items}}. ('my-app/templates/components/foo-bar.hbs' @ L1:C82) " + ); + + expectDeprecation( + "Using {{attrs}} to reference named arguments has been deprecated. {{attrs.items}} should be updated to {{@items}}. ('my-app/templates/components/foo-bar.hbs' @ L1:C135) " + ); + this.registerComponent('foo-bar', { template: strip` Args: {{this.attrs.value}} | {{attrs.value}} | {{@value}} | {{this.value}} @@ -1606,6 +1626,10 @@ moduleFor( } ['@test block with properties on attrs']() { + expectDeprecation( + "Using {{attrs}} to reference named arguments has been deprecated. {{attrs.someProp}} should be updated to {{@someProp}}. ('my-app/templates/components/with-block.hbs' @ L1:C24) " + ); + this.registerComponent('with-block', { template: 'In layout - someProp: {{attrs.someProp}} - {{yield}}', }); @@ -3521,6 +3545,13 @@ moduleFor( } ['@test using attrs for positional params']() { + expectDeprecation( + "Using {{attrs}} to reference named arguments has been deprecated. {{attrs.myVar}} should be updated to {{@myVar}}. ('my-app/templates/components/foo-bar.hbs' @ L1:C10) " + ); + expectDeprecation( + "Using {{attrs}} to reference named arguments has been deprecated. {{attrs.myVar2}} should be updated to {{@myVar2}}. ('my-app/templates/components/foo-bar.hbs' @ L1:C65) " + ); + let MyComponent = Component.extend(); this.registerComponent('foo-bar', { diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/dynamic-components-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/dynamic-components-test.js index 6b98567099c..6fbcb798bf8 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/dynamic-components-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/dynamic-components-test.js @@ -478,13 +478,13 @@ moduleFor( ['@test nested component helpers']() { this.registerComponent('foo-bar', { - template: 'yippie! {{attrs.location}} {{yield}}', + template: 'yippie! {{@location}} {{yield}}', }); this.registerComponent('baz-qux', { - template: 'yummy {{attrs.location}} {{yield}}', + template: 'yummy {{@location}} {{yield}}', }); this.registerComponent('corge-grault', { - template: 'delicious {{attrs.location}} {{yield}}', + template: 'delicious {{@location}} {{yield}}', }); this.render( diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-angle-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-angle-test.js index 9ef1407a36f..829ef19c71a 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-angle-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-angle-test.js @@ -320,43 +320,6 @@ moduleFor( }); } - // See https://github.com/emberjs/ember.js/issues/17771 - [`@skip The component supports 'classNameBindings' with custom values [GH #11699]`]( - assert - ) { - this.addTemplate( - 'index', - ` -

Home

- About - ` - ); - - this.add( - 'controller:index', - Controller.extend({ - foo: false, - }) - ); - - return this.visit('/').then(() => { - assert.equal( - this.$('#about-link.foo-is-false').length, - 1, - 'The about-link was rendered with the falsy class' - ); - - let controller = this.applicationInstance.lookup('controller:index'); - runTask(() => controller.set('foo', true)); - - assert.equal( - this.$('#about-link.foo-is-true').length, - 1, - 'The about-link was rendered with the truthy class after toggling the property' - ); - }); - } - async ['@test Using inside a non-routable engine errors'](assert) { this.add( 'engine:not-routable', diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-curly-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-curly-test.js index 06f162c2917..3e96ddf5e6a 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-curly-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-curly-test.js @@ -325,6 +325,10 @@ moduleFor( [`@test The {{link-to}} component supports 'classNameBindings' with custom values [GH #11699]`]( assert ) { + expectDeprecation( + "Passing the `classNameBindings` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ('my-app/templates/index.hbs' @ L3:C8) " + ); + this.addTemplate( 'index', ` diff --git a/packages/@ember/-internals/glimmer/tests/integration/helpers/closure-action-test.js b/packages/@ember/-internals/glimmer/tests/integration/helpers/closure-action-test.js index af72049cccd..b08c701407f 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/helpers/closure-action-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/helpers/closure-action-test.js @@ -251,6 +251,10 @@ moduleFor( } ['@test [#12718] a nice error is shown when a bound action function is undefined and it is passed as attrs.foo']() { + expectDeprecation( + "Using {{attrs}} to reference named arguments has been deprecated. {{attrs.external-action}} should be updated to {{@external-action}}. ('my-app/templates/components/inner-component.hbs' @ L1:C43) " + ); + this.registerComponent('inner-component', { template: '', diff --git a/packages/ember-template-compiler/lib/plugins/transform-attrs-into-args.ts b/packages/ember-template-compiler/lib/plugins/transform-attrs-into-args.ts index 2981c52e2e7..8f7ea8c50ad 100644 --- a/packages/ember-template-compiler/lib/plugins/transform-attrs-into-args.ts +++ b/packages/ember-template-compiler/lib/plugins/transform-attrs-into-args.ts @@ -1,4 +1,6 @@ +import { deprecate } from '@ember/debug'; import { AST, ASTPlugin } from '@glimmer/syntax'; +import calculateLocationDisplay from '../system/calculate-location-display'; import { EmberASTPluginEnvironment } from '../types'; /** @@ -27,6 +29,7 @@ import { EmberASTPluginEnvironment } from '../types'; export default function transformAttrsIntoArgs(env: EmberASTPluginEnvironment): ASTPlugin { let { builders: b } = env.syntax; + let { moduleName } = env.meta; let stack: string[][] = [[]]; @@ -47,6 +50,26 @@ export default function transformAttrsIntoArgs(env: EmberASTPluginEnvironment): PathExpression(node: AST.PathExpression): AST.Node | void { if (isAttrs(node, stack[stack.length - 1])) { let path = b.path(node.original.substr(6)) as AST.PathExpression; + + deprecate( + `Using {{attrs}} to reference named arguments has been deprecated. {{attrs.${ + path.original + }}} should be updated to {{@${path.original}}}. ${calculateLocationDisplay( + moduleName, + node.loc + )}`, + false, + { + id: 'attrs-arg-access', + url: 'https://deprecations.emberjs.com/v3.x/#toc_attrs-arg-access', + until: '4.0.0', + for: 'ember-source', + since: { + enabled: '3.26.0', + }, + } + ); + path.original = `@${path.original}`; path.data = true; return path; diff --git a/packages/ember-template-compiler/lib/plugins/transform-old-class-binding-syntax.ts b/packages/ember-template-compiler/lib/plugins/transform-old-class-binding-syntax.ts index b18447583fc..805b0cecfad 100644 --- a/packages/ember-template-compiler/lib/plugins/transform-old-class-binding-syntax.ts +++ b/packages/ember-template-compiler/lib/plugins/transform-old-class-binding-syntax.ts @@ -1,25 +1,32 @@ +import { deprecate } from '@ember/debug'; import { AST, ASTPlugin } from '@glimmer/syntax'; +import calculateLocationDisplay from '../system/calculate-location-display'; import { Builders, EmberASTPluginEnvironment } from '../types'; export default function transformOldClassBindingSyntax(env: EmberASTPluginEnvironment): ASTPlugin { let b = env.syntax.builders; + let { moduleName } = env.meta; return { name: 'transform-old-class-binding-syntax', visitor: { MustacheStatement(node: AST.MustacheStatement) { - process(b, node); + process(b, node, moduleName); }, BlockStatement(node: AST.BlockStatement) { - process(b, node); + process(b, node, moduleName); }, }, }; } -function process(b: Builders, node: AST.BlockStatement | AST.MustacheStatement) { +function process( + b: Builders, + node: AST.BlockStatement | AST.MustacheStatement, + moduleName: string +) { let allOfTheMicrosyntaxes: AST.HashPair[] = []; let allOfTheMicrosyntaxIndexes: number[] = []; let classPair: AST.HashPair | undefined; @@ -28,6 +35,24 @@ function process(b: Builders, node: AST.BlockStatement | AST.MustacheStatement) let { key } = pair; if (key === 'classBinding' || key === 'classNameBindings') { + deprecate( + `Passing the \`${key}\` property as an argument within templates has been deprecated. Instead, you can pass the class argument and use concatenation to produce the class value dynamically. ${calculateLocationDisplay( + moduleName, + node.loc + )}`, + false, + { + id: 'class-binding-and-class-name-bindings-in-templates', + url: + 'https://deprecations.emberjs.com/v3.x/#toc_class-binding-and-class-name-bindings-in-templates', + until: '4.0.0', + for: 'ember-source', + since: { + enabled: '3.26.0', + }, + } + ); + allOfTheMicrosyntaxIndexes.push(index); allOfTheMicrosyntaxes.push(pair); } else if (key === 'class') {