From 1c4a0fd9f973c46b34aaee842b5067d79b0c7b49 Mon Sep 17 00:00:00 2001 From: Ryan Tablada Date: Fri, 30 Dec 2022 14:31:33 -0600 Subject: [PATCH] fix: check for nested services and nested service config --- lib/rules/no-implicit-injections.js | 8 +++- tests/lib/rules/no-implicit-injections.js | 52 ++++++++++++++++++++++- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/lib/rules/no-implicit-injections.js b/lib/rules/no-implicit-injections.js index 0ed8674337..6d17eb00e7 100644 --- a/lib/rules/no-implicit-injections.js +++ b/lib/rules/no-implicit-injections.js @@ -5,7 +5,6 @@ const emberUtils = require('../utils/ember'); const { getImportIdentifier } = require('../utils/import'); const Stack = require('../utils/stack'); const types = require('../utils/types'); -const kebabCase = require('lodash.kebabcase'); const camelCase = require('lodash.camelcase'); const defaultServiceConfig = { service: 'store', moduleNames: ['Route', 'Controller'] }; @@ -110,6 +109,11 @@ module.exports = { config.service.toLowerCase() === config.service, 'Service name should be passed in kebab-case (all lower case)' ); + + assert( + !config.service.includes('/') || config.propertyName, + 'Nested services must declare a property name' + ); } // State being tracked for this file. @@ -211,7 +215,7 @@ module.exports = { node, messageId: 'main', data: { - serviceName: kebabCase(failedConfig.service), + serviceName: failedConfig.service, }, fix(fixer) { const sourceCode = context.getSourceCode(); diff --git a/tests/lib/rules/no-implicit-injections.js b/tests/lib/rules/no-implicit-injections.js index 58459cc171..e0e180b94a 100644 --- a/tests/lib/rules/no-implicit-injections.js +++ b/tests/lib/rules/no-implicit-injections.js @@ -29,6 +29,9 @@ const MEDIA_CONFIG = { const FEATURE_CHECKER_CONFIG = { denyList: [{ service: 'feature', propertyName: 'featureChecker' }], }; +const NESTED_SERVICE_CONFIG = { + denyList: [{ service: 'cart/checkout', propertyName: 'checkout' }], +}; function createClassUsage(serviceDefinition) { return { @@ -216,16 +219,33 @@ ruleTester.run('no-implicit-injections', rule, { { filename: 'routes/index.js', code: ` + import { inject as service } from '@ember/service'; import Route from '@ember/routing/route'; export default class IndexRoute extends Route { - featureChecker = null; + @service('feature') featureChecker; get canVisitCheckout() { return this.featureChecker.isEnabled('checkout'); } }`, - options: [MEDIA_CONFIG], + options: [FEATURE_CHECKER_CONFIG], + }, + // Can work with services with nested module paths + { + filename: 'routes/index.js', + code: ` + import Route from '@ember/routing/route'; + import { inject as service } from '@ember/service'; + + export default class IndexRoute extends Route { + @service('cart/checkout') checkout; + + model() { + return this.checkout.viewCart(); + } + }`, + options: [NESTED_SERVICE_CONFIG], }, // Ignores use outside of classes { @@ -729,6 +749,34 @@ get canVisitCheckout() { errors: [{ messageId: 'main', data: { serviceName: 'feature' }, type: 'MemberExpression' }], }, + // Can work with services with nested module paths + { + filename: 'routes/index.js', + code: ` + import Route from '@ember/routing/route'; + import { inject as service } from '@ember/service'; + + export default class IndexRoute extends Route { + model() { + return this.checkout.viewCart(); + } + }`, + output: ` + import Route from '@ember/routing/route'; + import { inject as service } from '@ember/service'; + + export default class IndexRoute extends Route { + @service('cart/checkout') checkout; +model() { + return this.checkout.viewCart(); + } + }`, + options: [NESTED_SERVICE_CONFIG], + errors: [ + { messageId: 'main', data: { serviceName: 'cart/checkout' }, type: 'MemberExpression' }, + ], + }, + // Check use and fix in legacy ember components { filename: 'pods/index.js',