diff --git a/lib/rules/no-deprecated-router-transition-methods.js b/lib/rules/no-deprecated-router-transition-methods.js index 46b80c5e40..1b691c778e 100644 --- a/lib/rules/no-deprecated-router-transition-methods.js +++ b/lib/rules/no-deprecated-router-transition-methods.js @@ -76,26 +76,29 @@ module.exports = { if (emberUtils.isAnyEmberCoreModule(context, node)) { const classMembers = node.body.body; - for (const classMember of classMembers) { - if (emberUtils.isInjectedServiceProp(classMember, undefined, serviceInjectImportName)) { - const serviceExpression = decoratorUtils.findDecorator( - classMember, - serviceInjectImportName - ).expression; - - if (serviceExpression.type === 'CallExpression') { - if ( - (serviceExpression.arguments.length === 0 && classMember.key.name === 'router') || - (serviceExpression.arguments.length > 0 && - serviceExpression.arguments[0].value === 'router') - ) { + if (serviceInjectImportName) { + for (const classMember of classMembers) { + if (emberUtils.isInjectedServiceProp(classMember, undefined, serviceInjectImportName)) { + const serviceExpression = decoratorUtils.findDecorator( + classMember, + serviceInjectImportName + ).expression; + + if (serviceExpression.type === 'CallExpression') { + if ( + (serviceExpression.arguments.length === 0 && classMember.key.name === 'router') || + (serviceExpression.arguments.length > 0 && + serviceExpression.arguments[0].value === 'router') + ) { + routerServicePropertyName = classMember.key.name; + } + } else if (classMember.key.name === 'router') { routerServicePropertyName = classMember.key.name; } - } else if (classMember.key.name === 'router') { - routerServicePropertyName = classMember.key.name; } } } + const isRoute = emberUtils.isEmberRoute(context, node); const isController = emberUtils.isEmberController(context, node); isValidModule = isRoute || isController; @@ -135,33 +138,38 @@ module.exports = { ClassExpression: onClassEnter, CallExpression(node) { if (emberUtils.isAnyEmberCoreModule(context, node)) { - const classMembers = node.arguments[0].properties; - - for (const classMember of classMembers) { - if (emberUtils.isInjectedServiceProp(classMember, undefined, serviceInjectImportName)) { - const callExpression = classMember.value; + const lastExtendArg = node.arguments[node.arguments.length - 1]; + if (lastExtendArg && lastExtendArg.type === 'ObjectExpression') { + const classMembers = lastExtendArg.properties; + for (const classMember of classMembers) { if ( - (callExpression.arguments.length === 0 && classMember.key.name === 'router') || - (callExpression.arguments.length > 0 && - callExpression.arguments[0].value === 'router') + emberUtils.isInjectedServiceProp(classMember, undefined, serviceInjectImportName) ) { - routerServicePropertyName = classMember.key.name; + const callExpression = classMember.value; + + if ( + (callExpression.arguments.length === 0 && classMember.key.name === 'router') || + (callExpression.arguments.length > 0 && + callExpression.arguments[0].value === 'router') + ) { + routerServicePropertyName = classMember.key.name; + } } } + const isRoute = emberUtils.isEmberRoute(context, node); + const isController = emberUtils.isEmberController(context, node); + isValidModule = isRoute || isController; + + classStack.push({ + node, + serviceInjectImportName, + routerServicePropertyName, + isValidModule, + isRoute, + isController, + }); } - const isRoute = emberUtils.isEmberRoute(context, node); - const isController = emberUtils.isEmberController(context, node); - isValidModule = isRoute || isController; - - classStack.push({ - node, - serviceInjectImportName, - routerServicePropertyName, - isValidModule, - isRoute, - isController, - }); } }, diff --git a/lib/rules/no-implicit-injections.js b/lib/rules/no-implicit-injections.js index b9df3d138e..be749cd83c 100644 --- a/lib/rules/no-implicit-injections.js +++ b/lib/rules/no-implicit-injections.js @@ -143,7 +143,9 @@ module.exports = { }); const modulePropertyDeclarations = - node.type === 'CallExpression' ? node.arguments[0].properties : node.body.body; + node.type === 'CallExpression' + ? node.arguments[node.arguments.length - 1].properties + : node.body.body; // Get Services that don't have properties/service injections declared configToCheckFor = modulePropertyDeclarations.reduce((accum, n) => { @@ -189,8 +191,12 @@ module.exports = { ClassDeclaration: onClassEnter, ClassExpression: onClassEnter, CallExpression(node) { - if (emberUtils.isAnyEmberCoreModule(context, node)) { - onModuleFound(node); + if (emberUtils.isAnyEmberCoreModule(context, node) && emberUtils.isExtendObject(node)) { + const lastExtendArg = node.arguments[node.arguments.length - 1]; + + if (lastExtendArg && lastExtendArg.type === 'ObjectExpression') { + onModuleFound(node); + } } }, diff --git a/tests/lib/rules/no-deprecated-router-transition-methods.js b/tests/lib/rules/no-deprecated-router-transition-methods.js index 2851b8c22c..2ac605f7e8 100644 --- a/tests/lib/rules/no-deprecated-router-transition-methods.js +++ b/tests/lib/rules/no-deprecated-router-transition-methods.js @@ -305,6 +305,59 @@ ruleTester.run('no-deprecated-router-transition-methods', rule, { } }`, }, + + // Does not error on .create + { + filename: 'utils/loads-user-controller.js', + code: ` + import Controller from '@ember/controller'; + + const myObj = Controller.create();`, + }, + + // Does not error on empty .extend + { + filename: 'utils/loads-user-controller.js', + code: ` + import Controller from '@ember/controller'; + + const myObj = Controller.extend()`, + }, + + // Does not error when using mixin with native class (common for validations) + { + filename: 'controllers/index.js', + code: ` + import Controller from '@ember/controller'; + import SomeMixin from './my-mixin'; + + export default class FoobarTestError extends Controller.extend(SomeMixin) { + }`, + }, + + // Does not error when using Mixin + { + filename: 'controller/index.js', + code: ` + import Controller from '@ember/controller'; + import SomeMixin from './my-mixin'; + + export default Component.extend(SomeMixin, { + });`, + }, + + // Does not error when dot access decorator is used + { + filename: 'controllers/dot-access.js', + code: ` + import Controller from '@ember/controller'; + import SomeMixin from './my-mixin'; + import EmberObject, { computed } from '@ember/object'; + + export default class FoobarTestError extends Controller { + @computed.reads('model.actors') actors; + }`, + }, ], invalid: [ // Route Uses RouterService.transitionTo with different service injection types diff --git a/tests/lib/rules/no-implicit-injections.js b/tests/lib/rules/no-implicit-injections.js index e0e180b94a..5a9e1fc6bc 100644 --- a/tests/lib/rules/no-implicit-injections.js +++ b/tests/lib/rules/no-implicit-injections.js @@ -339,6 +339,77 @@ ruleTester.run('no-implicit-injections', rule, { createExtendUsage('flashMessages: service(),'), createExtendUsage("flashMessages: service('flashMessages'),"), createExtendUsage("flashMessages: service('flash-messages'),"), + + // Does not error on .create + { + filename: 'utils/loads-user-controller.js', + code: ` + import EmberObject from '@ember/object'; + + const myObj = EmberObject.create();`, + }, + + // Does not error on empty .extend + { + filename: 'utils/loads-user-controller.js', + code: ` + import EmberObject from '@ember/object'; + + const myObj = EmberObject.extend()`, + }, + + // Does not error when using mixin with native class (common for validations) + { + filename: 'controller-mixin/index.js', + code: ` + import { inject as service } from '@ember/service'; + import Component from '@ember/component'; + import SomeMixin from './my-mixin'; + + export default class FoobarTestError extends Component.extend(SomeMixin) { + @service flashMessages; + + @action + save() { + return this.flashMessages.warn('some message'); + } + }`, + options: [FLASH_MESSAGES_CONFIG], + }, + + // Does not error when using Mixin + { + filename: 'controller/index.js', + code: ` + import { inject as service } from '@ember/service'; + import Component from '@ember/component'; + import SomeMixin from './my-mixin'; + + export default Component.extend(SomeMixin, { + flashMessages: service(), + + actions: { + + save() { + return this.flashMessages.warn('some message'); + } + } + });`, + options: [FLASH_MESSAGES_CONFIG], + }, + + // Does not error when dot access decorator is used + { + filename: 'controllers/dot-access.js', + code: ` + import Controller from '@ember/controller'; + import SomeMixin from './my-mixin'; + import EmberObject, { computed } from '@ember/object'; + + export default class FoobarTestError extends Controller { + @computed.reads('model.actors') actors; + }`, + }, ], invalid: [ // Basic store lint error in routes/controllers