Repository: ember-cli/eslint-plugin-ember
Pull request #1513 (2022-07-14 21:18:50)

Stop exporting non-Ember utils
Based on this [github search](https://github.com/search?p=1&q=require%28%27eslint-plugin-ember%27%29&type=Code), it doesn't look like anyone is importing anything of substance from our utils/utils.js utility file. These are all generic, non-Ember specific utils that are subject to change at any time and so it's best not to have anyone import/rely/depend on them.

If we hear that there's a need for any/some of these utils, we can consider individually-exporting them instead of exporting the entire file. Please file an issue if that is the case. For simple/trivial utils, it's probably best for anyone who uses them to just copy them into their own project.

Note that based on the above search, I did see a few more usages of our Ember utils, and those utils are specifically related to the purpose of eslint-plugin-ember, so we'll continue exporting those, but perhaps we should audit them at some point.

Related change:
* https://github.com/ember-cli/eslint-plugin-ember/pull/1514

Part of v11 release (https://github.com/ember-cli/eslint-plugin-ember/issues/1169).


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c47a8624..fe724ac2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,7 +17,7 @@ jobs:
     strategy:
       matrix:
         os: [ ubuntu, windows ]
-        node-version: [10.x, 12.x, 14.x, 16.x, 18.x]
+        node-version: [14.x, 16.x, 18.x]
 
     steps:
     - uses: actions/checkout@v3
diff --git a/README.md b/README.md
index 1108cf8b..d1ec481a 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 ## ❗️Requirements
 
 - [ESLint](https://eslint.org/) `>= 6`
-- [Node.js](https://nodejs.org/) `10.* || 12.* || >= 14`
+- [Node.js](https://nodejs.org/) `14.* || 16.* || >= 18`
 
 ## 🚀 Usage
 
diff --git a/docs/rules/avoid-leaking-state-in-ember-objects.md b/docs/rules/avoid-leaking-state-in-ember-objects.md
index 784ebf64..ccbd45aa 100644
--- a/docs/rules/avoid-leaking-state-in-ember-objects.md
+++ b/docs/rules/avoid-leaking-state-in-ember-objects.md
@@ -62,22 +62,14 @@ export default class FooComponent extends Component {
 
 ## Configuration
 
-If you have properties where you know that the shared state won't be a problem (for example, read-only configuration values),
-you can configure this rule to ignore specific properties. Ember already does this internally for properties such as `actions`.
-
-The configuration of this rule takes a second argument that is a list of property names to ignore. This plugin makes the default
-list of ignored properties available for you to extend from, as follows:
+If you have custom properties where you know that the shared state won't be a problem (for example, read-only configuration values), you can configure this rule to ignore them by passing the property names to the rule as follows. Note that this rule will always automatically ignore known-safe Ember properties such as `actions`.
 
 ```js
-const {
-  DEFAULT_IGNORED_PROPERTIES
-} = require('eslint-plugin-ember/lib/rules/avoid-leaking-state-in-ember-objects');
-
 module.exports = {
   rules: {
     'ember/avoid-leaking-state-in-ember-objects': [
       'error',
-      [...DEFAULT_IGNORED_PROPERTIES, 'array', 'of', 'ignored', 'properties']
+      ['array', 'of', 'ignored', 'properties']
     ]
   }
 };
diff --git a/docs/rules/no-get.md b/docs/rules/no-get.md
index 3e4592b2..fb89757f 100644
--- a/docs/rules/no-get.md
+++ b/docs/rules/no-get.md
@@ -92,8 +92,8 @@ export default EmberObject.extend({
 This rule takes an optional object containing:
 
 * `boolean` -- `ignoreGetProperties` -- whether the rule should ignore `getProperties` (default `false`)
-* `boolean` -- `ignoreNestedPaths` -- whether the rule should ignore `this.get('some.nested.property')` (default `false`)
-* `boolean` -- `useOptionalChaining` -- whether the rule should use the [optional chaining operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) `?.` to autofix nested paths such as `this.get('some.nested.property')` to `this.some?.nested?.property` (when this option is off, these nested paths won't be autofixed at all) (default `false`)
+* `boolean` -- `ignoreNestedPaths` -- whether the rule should ignore `this.get('some.nested.property')` (can't be enabled at the same time as `useOptionalChaining`) (default `false`)
+* `boolean` -- `useOptionalChaining` -- whether the rule should use the [optional chaining operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) `?.` to autofix nested paths such as `this.get('some.nested.property')` to `this.some?.nested?.property` (when this option is off, these nested paths won't be autofixed at all) (default `true`)
 * `boolean` -- `catchSafeObjects` -- whether the rule should catch non-`this` imported usages like `get(foo, 'bar')` (default `true`)
 * `boolean` -- `catchUnsafeObjects` -- whether the rule should catch non-`this` usages like `foo.get('bar')` even though we don't know for sure if `foo` is an Ember object (default `false`)
 
diff --git a/lib/index.js b/lib/index.js
index 7601dfa0..2113ec9d 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -7,6 +7,5 @@ module.exports = {
   configs: requireIndex(`${__dirname}/config`),
   utils: {
     ember: require('./utils/ember'),
-    utils: require('./utils/utils'),
   },
 };
diff --git a/lib/rules/avoid-leaking-state-in-ember-objects.js b/lib/rules/avoid-leaking-state-in-ember-objects.js
index fd8ea8c7..f77b2599 100644
--- a/lib/rules/avoid-leaking-state-in-ember-objects.js
+++ b/lib/rules/avoid-leaking-state-in-ember-objects.js
@@ -69,10 +69,10 @@ module.exports = {
     ],
   },
 
-  DEFAULT_IGNORED_PROPERTIES,
-
   create(context) {
-    const ignoredProperties = context.options[0] || DEFAULT_IGNORED_PROPERTIES;
+    const ignoredProperties = context.options[0]
+      ? [...DEFAULT_IGNORED_PROPERTIES, ...context.options[0]]
+      : DEFAULT_IGNORED_PROPERTIES;
 
     const report = function (node) {
       const message =
diff --git a/lib/rules/no-duplicate-dependent-keys.js b/lib/rules/no-duplicate-dependent-keys.js
index c6bb4609..c6a78338 100644
--- a/lib/rules/no-duplicate-dependent-keys.js
+++ b/lib/rules/no-duplicate-dependent-keys.js
@@ -3,7 +3,6 @@
 const emberUtils = require('../utils/ember');
 const types = require('../utils/types');
 const fixerUtils = require('../utils/fixer');
-const javascriptUtils = require('../utils/javascript');
 const { getImportIdentifier } = require('../utils/import');
 
 const ERROR_MESSAGE = 'Dependent keys should not be repeated';
@@ -57,11 +56,11 @@ module.exports = {
                 return null;
               }
 
-              return javascriptUtils.flat(
-                duplicateNodes.map((duplicateNode) =>
+              return duplicateNodes
+                .map((duplicateNode) =>
                   fixerUtils.removeCommaSeparatedNode(duplicateNode, sourceCode, fixer)
                 )
-              );
+                .flat();
             },
           });
         }
diff --git a/lib/rules/no-get.js b/lib/rules/no-get.js
index 33b2b6b1..9882d106 100644
--- a/lib/rules/no-get.js
+++ b/lib/rules/no-get.js
@@ -107,7 +107,7 @@ module.exports = {
           },
           useOptionalChaining: {
             type: 'boolean',
-            default: false,
+            default: true,
           },
           catchSafeObjects: {
             type: 'boolean',
diff --git a/lib/rules/require-computed-property-dependencies.js b/lib/rules/require-computed-property-dependencies.js
index eea05e92..55cb28b3 100644
--- a/lib/rules/require-computed-property-dependencies.js
+++ b/lib/rules/require-computed-property-dependencies.js
@@ -3,7 +3,6 @@
 const estraverse = require('estraverse');
 const emberUtils = require('../utils/ember');
 const computedPropertyUtils = require('../utils/computed-properties');
-const javascriptUtils = require('../utils/javascript');
 const types = require('../utils/types');
 const utils = require('../utils/utils');
 const propertyGetterUtils = require('../utils/property-getter');
@@ -303,13 +302,12 @@ module.exports = {
       const computedPropertyFunctionBody =
         computedPropertyUtils.getComputedPropertyFunctionBody(node);
 
-      const usedKeys1 = javascriptUtils.flatMap(
-        findEmberGetCalls(computedPropertyFunctionBody, importedNames),
+      const usedKeys1 = findEmberGetCalls(computedPropertyFunctionBody, importedNames).flatMap(
         (node) => extractEmberGetDependencies(node, importedNames)
       );
-      const usedKeys2 = javascriptUtils.flatMap(
-        findThisGetCalls(computedPropertyFunctionBody),
-        (node) => extractThisGetDependencies(node, context)
+
+      const usedKeys2 = findThisGetCalls(computedPropertyFunctionBody).flatMap((node) =>
+        extractThisGetDependencies(node, context)
       );
       const usedKeys = [...usedKeys1, ...usedKeys2];
 
diff --git a/lib/utils/computed-property-dependent-keys.js b/lib/utils/computed-property-dependent-keys.js
index 8903d1e5..2028c5f4 100644
--- a/lib/utils/computed-property-dependent-keys.js
+++ b/lib/utils/computed-property-dependent-keys.js
@@ -64,7 +64,7 @@ function collapseKeys(keys) {
  * @returns {Array<string>}
  */
 function expandKeys(keys) {
-  return javascriptUtils.flatMap(keys, expandKey);
+  return keys.flatMap(expandKey);
 }
 
 /**
@@ -199,7 +199,7 @@ function findComputedPropertyDependentKeys(
   if (types.isClassDeclaration(nodeClass)) {
     // Native JS class.
     return new Set(
-      javascriptUtils.flatMap(nodeClass.body.body, (node) => {
+      nodeClass.body.body.flatMap((node) => {
         // Check for `computed` itself.
         const computedDecorator = decoratorUtils.findDecorator(node, computedImportName);
         if (computedDecorator) {
@@ -224,31 +224,28 @@ function findComputedPropertyDependentKeys(
   } else if (types.isCallExpression(nodeClass)) {
     // Classic class.
     return new Set(
-      javascriptUtils.flatMap(
-        nodeClass.arguments.filter(types.isObjectExpression),
-        (classObject) => {
-          return javascriptUtils.flatMap(classObject.properties, (node) => {
-            if (types.isProperty(node)) {
-              const name = getName(node.value);
-              if (!name) {
-                return [];
-              }
-
-              // Check for `computed` itself.
-              if (name === computedImportName) {
-                return getComputedPropertyDependentKeys(node.value);
-              }
-
-              // Check for a computed macro.
-              const macroConfigsByName = getMacrosFromImports(macrosByName, macrosByIndexName);
-              if (macroConfigsByName.has(name)) {
-                return getComputedPropertyDependentKeys(node.value, macroConfigsByName.get(name));
-              }
+      nodeClass.arguments.filter(types.isObjectExpression).flatMap((classObject) => {
+        return classObject.properties.flatMap((node) => {
+          if (types.isProperty(node)) {
+            const name = getName(node.value);
+            if (!name) {
+              return [];
             }
-            return [];
-          });
-        }
-      )
+
+            // Check for `computed` itself.
+            if (name === computedImportName) {
+              return getComputedPropertyDependentKeys(node.value);
+            }
+
+            // Check for a computed macro.
+            const macroConfigsByName = getMacrosFromImports(macrosByName, macrosByIndexName);
+            if (macroConfigsByName.has(name)) {
+              return getComputedPropertyDependentKeys(node.value, macroConfigsByName.get(name));
+            }
+          }
+          return [];
+        });
+      })
     );
   } else {
     assert(false, 'Unexpected node type for a class.');
@@ -324,19 +321,16 @@ function getComputedPropertyDependentKeys(node, computedDependenciesConfig = nul
     return [];
   }
 
-  const baseKeys = javascriptUtils.flatMap(node.arguments, (argument, index) => {
+  const baseKeys = node.arguments.flatMap((argument, index) => {
     if (!computedDependenciesConfig) {
       return argument.type === 'Literal' && typeof argument.value === 'string'
         ? [argument.value]
         : [];
     }
 
-    return javascriptUtils.flatMap(
-      computedDependenciesConfig.argumentFormat,
-      ({ strings, objects }) => {
-        return getComputedPropertyDependentKeysForConfig(argument, index, { strings, objects });
-      }
-    );
+    return computedDependenciesConfig.argumentFormat.flatMap(({ strings, objects }) => {
+      return getComputedPropertyDependentKeysForConfig(argument, index, { strings, objects });
+    });
   });
 
   return expandKeys(baseKeys);
diff --git a/lib/utils/computed-property-macros.js b/lib/utils/computed-property-macros.js
index 48acbee2..a2fc688d 100644
--- a/lib/utils/computed-property-macros.js
+++ b/lib/utils/computed-property-macros.js
@@ -1,5 +1,4 @@
 const assert = require('assert');
-const { flatMap } = require('./javascript');
 
 /**
  * Example macros:
@@ -77,7 +76,7 @@ function getMacros() {
 function getMacrosFromImports(macrosByImport, macrosByIndexImport) {
   return new Map([
     ...macrosByImport,
-    ...flatMap([...macrosByIndexImport], ([indexName, propertyConfigs]) => {
+    ...[...macrosByIndexImport].flatMap(([indexName, propertyConfigs]) => {
       return [...propertyConfigs].map(([name, config]) => [`${indexName}.${name}`, config]);
     }),
   ]);
diff --git a/lib/utils/ember.js b/lib/utils/ember.js
index ccd2e86c..00dcc50c 100644
--- a/lib/utils/ember.js
+++ b/lib/utils/ember.js
@@ -7,7 +7,6 @@ const types = require('./types');
 const assert = require('assert');
 const decoratorUtils = require('../utils/decorators');
 const { getNodeOrNodeFromVariable } = require('../utils/utils');
-const { flatMap } = require('../utils/javascript');
 const kebabCase = require('lodash.kebabcase');
 
 module.exports = {
@@ -604,7 +603,7 @@ function isControllerDefaultProp(property) {
 }
 
 function getModuleProperties(moduleNode, scopeManager) {
-  return flatMap(moduleNode.arguments, (arg) => {
+  return moduleNode.arguments.flatMap((arg) => {
     const resultingNode = getNodeOrNodeFromVariable(arg, scopeManager);
     return resultingNode && resultingNode.type === 'ObjectExpression'
       ? resultingNode.properties
diff --git a/lib/utils/javascript.js b/lib/utils/javascript.js
index 1d44fd7e..8ba4a834 100644
--- a/lib/utils/javascript.js
+++ b/lib/utils/javascript.js
@@ -2,8 +2,6 @@
 
 module.exports = {
   duplicateArrays,
-  flat,
-  flatMap,
   removeWhitespace,
 };
 
@@ -21,37 +19,6 @@ function duplicateArrays(arr, times) {
   return result;
 }
 
-/**
- * Flattens an array.
- * WARNING: can only handle one level of nested arrays.
- * TODO: switch to native JavaScript `flat` once Node 12 is the minimum version we need to support.
- * Input: [[1], [2]]
- * Output: [1, 2]
- *
- * @param  {Object[]} arr Array to flatten.
- * @return {Object[]}     Flattened array.
- */
-function flat(arr) {
-  if (!arr || arr.length === 0) {
-    return arr;
-  }
-
-  return arr.reduce((acc, val) => acc.concat(val)); // eslint-disable-line unicorn/prefer-spread
-}
-
-/**
- * Builds an array by concatenating the results of a map.
- * TODO: switch to native JavaScript `flatMap` once Node 12 is the minimum version we need to support.
- *
- * @template T, U
- * @param {Array<T>} array
- * @param {function(T): Array<U>} callback
- * @returns {Array<U>}
- */
-function flatMap(array, callback) {
-  return array.reduce((result, item, index) => result.concat(callback(item, index)), []); // eslint-disable-line unicorn/prefer-spread
-}
-
 function removeWhitespace(str) {
   // Removes whitespace anywhere inside string.
   return str.replace(/\s/g, '');
diff --git a/package.json b/package.json
index d512fb12..024f5986 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,8 @@
     "url": "git+https://github.com/ember-cli/eslint-plugin-ember.git"
   },
   "license": "MIT",
-  "main": "lib/index.js",
+  "exports": "./lib/index.js",
+  "main": "./lib/index.js",
   "directories": {
     "test": "test",
     "rules": "rules"
@@ -100,7 +101,7 @@
     "eslint": ">= 6"
   },
   "engines": {
-    "node": "10.* || 12.* || >= 14"
+    "node": "14.* || 16.* || >= 18"
   },
   "publishConfig": {
     "registry": "https://registry.npmjs.org"


Test patch:
diff --git a/tests/lib/rules/avoid-leaking-state-in-ember-objects.js b/tests/lib/rules/avoid-leaking-state-in-ember-objects.js
index 1e9b10ce..b093ddb3 100644
--- a/tests/lib/rules/avoid-leaking-state-in-ember-objects.js
+++ b/tests/lib/rules/avoid-leaking-state-in-ember-objects.js
@@ -9,22 +9,6 @@ const RuleTester = require('eslint').RuleTester;
 // Tests
 // ------------------------------------------------------------------------------
 
-describe('imports', () => {
-  it('should expose the default ignored properties', () => {
-    expect(rule.DEFAULT_IGNORED_PROPERTIES).toStrictEqual([
-      'classNames',
-      'classNameBindings',
-      'actions',
-      'concatenatedProperties',
-      'mergedProperties',
-      'positionalParams',
-      'attributeBindings',
-      'queryParams',
-      'attrs',
-    ]);
-  });
-});
-
 const eslintTester = new RuleTester({
   parser: require.resolve('@babel/eslint-parser'),
   parserOptions: { ecmaVersion: 2020, sourceType: 'module' },
@@ -71,6 +55,8 @@ eslintTester.run('avoid-leaking-state-in-ember-objects', rule, {
     'import Component from "@ember/component"; export default class MyNativeClassComponent extends Component { someArrayField = []; }',
     'import Component from "@ember/component"; export default class MyNativeClassComponent extends Component { someObjectField = {}; }',
     'import EmberObject from "@ember/object"; export default class MyNativeClassComponentWithAMixin extends EmberObject.extend(MyMixin) { someArrayField = []; }',
+    { code: 'export default Foo.extend({ someProp: [] });', options: [['someProp']] }, // With options.
+    { code: 'export default Foo.extend({ someProp: [], actions: {} });', options: [['someProp']] }, // With options and known Ember property.
   ],
   invalid: [
     {
@@ -83,6 +69,18 @@ eslintTester.run('avoid-leaking-state-in-ember-objects', rule, {
         },
       ],
     },
+    {
+      // With options.
+      code: 'export default Foo.extend({someProp: [], someProp2: [], actions: {} });',
+      output: null,
+      options: [['someProp']],
+      errors: [
+        {
+          message:
+            'Only string, number, symbol, boolean, null, undefined, and function are allowed as default properties',
+        },
+      ],
+    },
     {
       // With object variable.
       code: 'const body = {someProp: []}; export default Foo.extend(body);',
diff --git a/tests/lib/rules/no-get.js b/tests/lib/rules/no-get.js
index cb8b39fb..84d2bea3 100644
--- a/tests/lib/rules/no-get.js
+++ b/tests/lib/rules/no-get.js
@@ -19,10 +19,13 @@ ruleTester.run('no-get', rule, {
     // **************************
 
     // Nested property path.
-    { code: "this.get('foo.bar');", options: [{ ignoreNestedPaths: true }] },
+    {
+      code: "this.get('foo.bar');",
+      options: [{ ignoreNestedPaths: true, useOptionalChaining: false }],
+    },
     {
       code: "import { get } from '@ember/object'; get(this, 'foo.bar');",
-      options: [{ ignoreNestedPaths: true }],
+      options: [{ ignoreNestedPaths: true, useOptionalChaining: false }],
     },
 
     // Template literals.
@@ -74,15 +77,21 @@ ruleTester.run('no-get', rule, {
     // **************************
 
     // Nested property path.
-    { code: "this.getProperties('foo', 'bar.baz');", options: [{ ignoreNestedPaths: true }] },
-    { code: "this.getProperties(['foo', 'bar.baz']);", options: [{ ignoreNestedPaths: true }] }, // With parameters in array.
+    {
+      code: "this.getProperties('foo', 'bar.baz');",
+      options: [{ ignoreNestedPaths: true, useOptionalChaining: false }],
+    },
+    {
+      code: "this.getProperties(['foo', 'bar.baz']);",
+      options: [{ ignoreNestedPaths: true, useOptionalChaining: false }],
+    }, // With parameters in array.
     {
       code: "import { getProperties } from '@ember/object'; getProperties(this, 'foo', 'bar.baz');",
-      options: [{ ignoreNestedPaths: true }],
+      options: [{ ignoreNestedPaths: true, useOptionalChaining: false }],
     },
     {
       code: "import { getProperties } from '@ember/object'; getProperties(this, ['foo', 'bar.baz']);",
-      options: [{ ignoreNestedPaths: true }],
+      options: [{ ignoreNestedPaths: true, useOptionalChaining: false }],
     }, // With parameters in array.
 
     // Template literals.
@@ -216,12 +225,21 @@ ruleTester.run('no-get', rule, {
       errors: [{ message: ERROR_MESSAGE_GET, type: 'CallExpression' }],
     },
     {
+      // useOptionalChaining = false
       code: "foo1.foo2.get('bar.bar').baz;",
       output: null,
+      options: [{ catchUnsafeObjects: true, useOptionalChaining: false }],
+      errors: [{ message: ERROR_MESSAGE_GET, type: 'CallExpression' }],
+    },
+    {
+      // useOptionalChaining = true (implicit)
+      code: "foo1.foo2.get('bar.bar').baz;",
+      output: 'foo1.foo2.bar.bar.baz;',
       options: [{ catchUnsafeObjects: true }],
       errors: [{ message: ERROR_MESSAGE_GET, type: 'CallExpression' }],
     },
     {
+      // useOptionalChaining = true (explicit)
       code: "foo1.foo2.get('bar.bar').baz;",
       output: 'foo1.foo2.bar.bar.baz;',
       options: [{ catchUnsafeObjects: true, useOptionalChaining: true }],
diff --git a/tests/lib/rules/no-invalid-debug-function-arguments.js b/tests/lib/rules/no-invalid-debug-function-arguments.js
index d6ae3669..26a31cbe 100644
--- a/tests/lib/rules/no-invalid-debug-function-arguments.js
+++ b/tests/lib/rules/no-invalid-debug-function-arguments.js
@@ -4,7 +4,6 @@
 
 const rule = require('../../../lib/rules/no-invalid-debug-function-arguments');
 const RuleTester = require('eslint').RuleTester;
-const javascriptUtils = require('../../../lib/utils/javascript');
 
 const { DEBUG_FUNCTIONS, ERROR_MESSAGE } = rule;
 
@@ -21,102 +20,98 @@ const VALID_USAGES_BASIC = [
   },
 ];
 
-const VALID_USAGES_FOR_EACH_DEBUG_FUNCTION = javascriptUtils.flat(
-  DEBUG_FUNCTIONS.map((debugFunction) => [
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; OtherClass.${debugFunction}(true, 'My string.');`,
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}();`,
-    },
-    {
-      code: `Ember.${debugFunction}();`,
-    },
-    {
-      // Missing import:
-      code: `${debugFunction}(true, 'My string.');`,
-    },
-    {
-      // Wrong import:
-      code: `import { ${debugFunction} } from 'something-else'; ${debugFunction}(true, 'My string.');`,
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.');`,
-    },
-    {
-      code: `Ember.${debugFunction}('My description.');`,
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.', true);`,
-    },
-    {
-      code: `Ember.${debugFunction}('My description.', true);`,
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.', true, { id: 'some-id' });`,
-    },
-    {
-      code: `Ember.${debugFunction}('My description.', true, { id: 'some-id' });`,
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.', CONDITION);`,
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(DESCRIPTION, true);`,
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(DESCRIPTION, CONDITION);`,
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}.unrelatedFunction(true, 'My description.');`,
-    },
-    {
-      code: `Ember.${debugFunction}.unrelatedFunction(true, 'My description.');`,
-    },
-  ])
-);
+const VALID_USAGES_FOR_EACH_DEBUG_FUNCTION = DEBUG_FUNCTIONS.map((debugFunction) => [
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; OtherClass.${debugFunction}(true, 'My string.');`,
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}();`,
+  },
+  {
+    code: `Ember.${debugFunction}();`,
+  },
+  {
+    // Missing import:
+    code: `${debugFunction}(true, 'My string.');`,
+  },
+  {
+    // Wrong import:
+    code: `import { ${debugFunction} } from 'something-else'; ${debugFunction}(true, 'My string.');`,
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.');`,
+  },
+  {
+    code: `Ember.${debugFunction}('My description.');`,
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.', true);`,
+  },
+  {
+    code: `Ember.${debugFunction}('My description.', true);`,
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.', true, { id: 'some-id' });`,
+  },
+  {
+    code: `Ember.${debugFunction}('My description.', true, { id: 'some-id' });`,
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.', CONDITION);`,
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(DESCRIPTION, true);`,
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(DESCRIPTION, CONDITION);`,
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}.unrelatedFunction(true, 'My description.');`,
+  },
+  {
+    code: `Ember.${debugFunction}.unrelatedFunction(true, 'My description.');`,
+  },
+]).flat();
 
 const VALID_USAGES = [...VALID_USAGES_BASIC, ...VALID_USAGES_FOR_EACH_DEBUG_FUNCTION];
 
-const INVALID_USAGES = javascriptUtils.flat(
-  DEBUG_FUNCTIONS.map((debugFunction) => [
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(true, 'My description.');`,
-      output: null,
-      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
-    },
-    {
-      code: `Ember.${debugFunction}(true, 'My description.');`,
-      output: null,
-      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(true, 'My description.', { id: 'some-id' });`,
-      output: null,
-      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
-    },
-    {
-      code: `Ember.${debugFunction}(true, 'My description.', { id: 'some-id' });`,
-      output: null,
-      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(true, \`My \${123} description.\`);`,
-      output: null,
-      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(CONDITION, 'My description.');`,
-      output: null,
-      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
-    },
-    {
-      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(CONDITION, \`My \${123} description.\`);`,
-      output: null,
-      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
-    },
-  ])
-);
+const INVALID_USAGES = DEBUG_FUNCTIONS.map((debugFunction) => [
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(true, 'My description.');`,
+    output: null,
+    errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
+  },
+  {
+    code: `Ember.${debugFunction}(true, 'My description.');`,
+    output: null,
+    errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(true, 'My description.', { id: 'some-id' });`,
+    output: null,
+    errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
+  },
+  {
+    code: `Ember.${debugFunction}(true, 'My description.', { id: 'some-id' });`,
+    output: null,
+    errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(true, \`My \${123} description.\`);`,
+    output: null,
+    errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(CONDITION, 'My description.');`,
+    output: null,
+    errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
+  },
+  {
+    code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(CONDITION, \`My \${123} description.\`);`,
+    output: null,
+    errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
+  },
+]).flat();
 
 const ruleTester = new RuleTester({
   parserOptions: {
diff --git a/tests/lib/utils/javascript-test.js b/tests/lib/utils/javascript-test.js
index 758f6f7f..421184b1 100644
--- a/tests/lib/utils/javascript-test.js
+++ b/tests/lib/utils/javascript-test.js
@@ -17,23 +17,6 @@ describe('duplicateArrays', () => {
   });
 });
 
-describe('flat', function () {
-  it('correctly flattens arrays', function () {
-    expect(javascriptUtils.flat([])).toStrictEqual([]);
-    expect(javascriptUtils.flat(null)).toBeNull();
-    expect(javascriptUtils.flat([[1], [2]])).toStrictEqual([1, 2]);
-    expect(
-      javascriptUtils.flat([
-        [1, 2],
-        [3, 4],
-      ])
-    ).toStrictEqual([1, 2, 3, 4]);
-    expect(javascriptUtils.flat([[1], [2, 3], [4, 5, 6]])).toStrictEqual([1, 2, 3, 4, 5, 6]); // different lengths forward
-    expect(javascriptUtils.flat([[6, 5, 4], [3, 2], [1]])).toStrictEqual([6, 5, 4, 3, 2, 1]); // different lengths reverse
-    expect(javascriptUtils.flat([[1], null, [2]])).toStrictEqual([1, null, 2]); // with null array
-  });
-});
-
 describe('removeWhitespace', () => {
   it('returns the right result', () => {
     expect(javascriptUtils.removeWhitespace('abcdef')).toStrictEqual('abcdef');
diff --git a/tests/plugin-exports.js b/tests/plugin-exports.js
index 2da2f975..a431b345 100644
--- a/tests/plugin-exports.js
+++ b/tests/plugin-exports.js
@@ -2,14 +2,13 @@
 
 const plugin = require('../lib');
 const ember = require('../lib/utils/ember');
-const utils = require('../lib/utils/utils');
 const base = require('../lib/config/base');
 const recommended = require('../lib/config/recommended');
 
 describe('plugin exports', () => {
   describe('utils', () => {
     it('has the right util functions', () => {
-      expect(plugin.utils).toStrictEqual({ ember, utils });
+      expect(plugin.utils).toStrictEqual({ ember });
     });
   });
 
diff --git a/tests/rule-setup.js b/tests/rule-setup.js
index 53885073..f6426cd7 100644
--- a/tests/rule-setup.js
+++ b/tests/rule-setup.js
@@ -4,7 +4,6 @@ const { readdirSync, readFileSync } = require('fs');
 const path = require('path');
 const rules = require('../lib').rules;
 const recommendedRules = require('../lib/recommended-rules');
-const { flat } = require('../lib/utils/javascript');
 
 const RULE_NAMES = Object.keys(rules);
 const RECOMMENDED_RULE_NAMES = Object.keys(recommendedRules).map((name) =>
@@ -17,7 +16,7 @@ function getAllNamedOptions(jsonSchema) {
   }
 
   if (Array.isArray(jsonSchema)) {
-    return flat(jsonSchema.map(getAllNamedOptions));
+    return jsonSchema.map(getAllNamedOptions).flat();
   }
 
   if (jsonSchema.items) {