Repository: ember-cli/ember-cli
Pull request #10558 (2024-11-11 23:10:55)

[Bugfix] Fix typed eslint support
When linting ts, and that ts file imports a gts, we _must_ use ember-eslint-parser, rather than the default ts parser. This means it makes 0  sense to  have separate config override entries for ts and gts file extensions.


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index facc2da90..a1ff9b4bf 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -112,3 +112,15 @@ jobs:
       - run: pnpm test:all
         env:
           'EMBER_CLI_${{ matrix.feature-flag }}': true
+
+  deprecations-broken-test:
+    name: Deprecations "broken"
+    runs-on: ubuntu-latest
+    needs: [basic-tests, linting]
+    steps:
+      - uses: actions/checkout@v4
+      - uses: ./.github/actions/setup-environment
+      - name: Run Tests with Deprecations as Errors
+        env:
+          OVERRIDE_DEPRECATION_VERSION: '22.0.0'
+        run: pnpm test:all
diff --git a/blueprints/app/files/_ts_eslint.config.mjs b/blueprints/app/files/_ts_eslint.config.mjs
index c84c3aebe..ccde221f2 100644
--- a/blueprints/app/files/_ts_eslint.config.mjs
+++ b/blueprints/app/files/_ts_eslint.config.mjs
@@ -84,14 +84,7 @@ export default ts.config(
     },
   },
   {
-    files: ['**/*.ts'],
-    languageOptions: {
-      parserOptions: parserOptions.esm.ts,
-    },
-    extends: [ember.configs.base, ...ts.configs.recommendedTypeChecked],
-  },
-  {
-    files: ['**/*.gts'],
+    files: ['**/*.{ts,gts}'],
     languageOptions: {
       parser: ember.parser,
       parserOptions: parserOptions.esm.ts,
diff --git a/lib/broccoli/ember-app.js b/lib/broccoli/ember-app.js
index dae062e80..1e5c41384 100644
--- a/lib/broccoli/ember-app.js
+++ b/lib/broccoli/ember-app.js
@@ -9,7 +9,7 @@ const p = require('ember-cli-preprocess-registry/preprocessors');
 const chalk = require('chalk');
 const resolve = require('resolve');
 
-const { assert, deprecate } = require('../debug');
+const { assert, deprecate, DEPRECATIONS } = require('../debug');
 const Project = require('../models/project');
 
 const concat = require('broccoli-concat');
@@ -226,15 +226,7 @@ class EmberApp {
     deprecate(
       'Using the `outputPaths` build option is deprecated, as output paths will no longer be predetermined under Embroider.',
       typeof options.outputPaths === 'undefined',
-      {
-        for: 'ember-cli',
-        id: 'ember-cli.outputPaths-build-option',
-        since: {
-          available: '5.3.0',
-          enabled: '5.3.0',
-        },
-        until: '6.0.0',
-      }
+      DEPRECATIONS.DEPRECATE_OUTPUT_PATHS.options
     );
 
     let resolvePathFor = (defaultPath, specified) => {
diff --git a/lib/commands/init.js b/lib/commands/init.js
index 29ffdd73e..1e48720af 100644
--- a/lib/commands/init.js
+++ b/lib/commands/init.js
@@ -9,7 +9,7 @@ const normalizeBlueprint = require('../utilities/normalize-blueprint-option');
 const mergeBlueprintOptions = require('../utilities/merge-blueprint-options');
 const { isPnpmProject, isYarnProject } = require('../utilities/package-managers');
 const getLangArg = require('../../lib/utilities/get-lang-arg');
-const { deprecate } = require('../debug');
+const { deprecate, DEPRECATIONS } = require('../debug');
 
 module.exports = Command.extend({
   name: 'init',
@@ -126,16 +126,7 @@ module.exports = Command.extend({
       deprecate(
         'Support for generating a Travis CI config file is deprecated.\nYou can keep using Travis CI, or you could also consider switching to GitHub Actions instead.',
         false,
-        {
-          for: 'ember-cli',
-          id: 'travis-ci-support',
-          since: {
-            available: '5.5.0',
-            enabled: '5.5.0',
-          },
-          until: '6.0.0',
-          url: 'https://deprecations.emberjs.com/id/travis-ci-support',
-        }
+        DEPRECATIONS.DEPRECATE_TRAVIS_CI_SUPPORT.options
       );
     }
 
diff --git a/lib/debug/deprecate.js b/lib/debug/deprecate.js
index 4342c39ce..f03702800 100644
--- a/lib/debug/deprecate.js
+++ b/lib/debug/deprecate.js
@@ -3,6 +3,7 @@
 const chalk = require('chalk');
 const semver = require('semver');
 const assert = require('./assert');
+const emberCLIVersion = require('../../package').version;
 
 /**
  * Display a deprecation message.
@@ -71,6 +72,12 @@ function deprecate(description, condition, options) {
     return;
   }
 
+  if (options.for === 'ember-cli' && isDeprecationRemoved(options.until)) {
+    throw new Error(
+      `The API deprecated by ${options.id} was removed in ember-cli ${options.until}. The message was: ${description}. Please see ${options.url} for more details.`
+    );
+  }
+
   let message = formatMessage(description, options);
 
   warn(message);
@@ -117,4 +124,12 @@ function warn(message) {
   console.warn(chalk.yellow(message));
 }
 
+function isDeprecationRemoved(until) {
+  const currentEmberCLIVersion = parseFloat(process.env.OVERRIDE_DEPRECATION_VERSION ?? emberCLIVersion);
+
+  let significantUntil = until.replace(/(\.0+)/g, '');
+  return currentEmberCLIVersion >= parseFloat(significantUntil);
+}
+
 module.exports = deprecate;
+module.exports._isDeprecationRemoved = isDeprecationRemoved;
diff --git a/lib/debug/deprecations.js b/lib/debug/deprecations.js
new file mode 100644
index 000000000..9c99db35e
--- /dev/null
+++ b/lib/debug/deprecations.js
@@ -0,0 +1,34 @@
+'use strict';
+
+const { _isDeprecationRemoved } = require('./deprecate');
+
+function deprecation(options) {
+  return {
+    options,
+    isRemoved: _isDeprecationRemoved(options.until),
+  };
+}
+
+const DEPRECATIONS = {
+  DEPRECATE_OUTPUT_PATHS: deprecation({
+    for: 'ember-cli',
+    id: 'ember-cli.outputPaths-build-option',
+    since: {
+      available: '5.3.0',
+      enabled: '5.3.0',
+    },
+    until: '6.0.0',
+  }),
+  DEPRECATE_TRAVIS_CI_SUPPORT: deprecation({
+    for: 'ember-cli',
+    id: 'travis-ci-support',
+    since: {
+      available: '5.5.0',
+      enabled: '5.5.0',
+    },
+    until: '6.0.0',
+    url: 'https://deprecations.emberjs.com/id/travis-ci-support',
+  }),
+};
+
+module.exports = DEPRECATIONS;
diff --git a/lib/debug/index.js b/lib/debug/index.js
index 10b336ceb..d8b272011 100644
--- a/lib/debug/index.js
+++ b/lib/debug/index.js
@@ -3,4 +3,5 @@
 module.exports = {
   assert: require('./assert'),
   deprecate: require('./deprecate'),
+  DEPRECATIONS: require('./deprecations'),
 };


Test patch:
diff --git a/tests/acceptance/brocfile-smoke-test-slow.js b/tests/acceptance/brocfile-smoke-test-slow.js
index c87536c37..ad04130a5 100644
--- a/tests/acceptance/brocfile-smoke-test-slow.js
+++ b/tests/acceptance/brocfile-smoke-test-slow.js
@@ -15,6 +15,7 @@ let cleanupRun = acceptance.cleanupRun;
 
 const { expect } = require('chai');
 const { dir, file } = require('chai-files');
+const { DEPRECATIONS } = require('../../lib/debug');
 
 let appName = 'some-cool-app';
 let appRoot;
@@ -417,6 +418,10 @@ describe('Acceptance: brocfile-smoke-test', function () {
   // custom outputPaths are deprecated under embroider
   if (!isExperimentEnabled('EMBROIDER')) {
     it('multiple paths can be CSS preprocessed', async function () {
+      if (DEPRECATIONS.DEPRECATE_OUTPUT_PATHS.isRemoved) {
+        this.skip();
+      }
+
       await copyFixtureFiles('brocfile-tests/multiple-sass-files');
 
       let packageJsonPath = path.join(appRoot, 'package.json');
diff --git a/tests/acceptance/init-test.js b/tests/acceptance/init-test.js
index c34aadd4f..b7135193d 100644
--- a/tests/acceptance/init-test.js
+++ b/tests/acceptance/init-test.js
@@ -17,6 +17,7 @@ const lintFix = require('../../lib/utilities/lint-fix');
 
 const { expect } = require('chai');
 const { dir, file } = require('chai-files');
+const { DEPRECATIONS } = require('../../lib/debug');
 
 let defaultIgnoredFiles = Blueprint.ignoredFiles;
 
@@ -196,6 +197,10 @@ describe('Acceptance: ember init', function () {
   });
 
   it('configurable CI option', async function () {
+    if (DEPRECATIONS.DEPRECATE_TRAVIS_CI_SUPPORT.isRemoved) {
+      this.skip();
+    }
+
     await ember(['init', '--ci-provider=travis', '--skip-npm']);
 
     let fixturePath = 'app/npm-travis';
diff --git a/tests/acceptance/new-test.js b/tests/acceptance/new-test.js
index 2f722e0ae..d54aedf83 100644
--- a/tests/acceptance/new-test.js
+++ b/tests/acceptance/new-test.js
@@ -17,6 +17,7 @@ const { isExperimentEnabled } = require('../../lib/experiments');
 const { expect } = require('chai');
 const { dir, file } = require('chai-files');
 const assertVersionLock = require('../helpers/assert-version-lock');
+const { DEPRECATIONS } = require('../../lib/debug');
 
 let tmpDir = './tmp/new-test';
 
@@ -632,6 +633,10 @@ describe('Acceptance: ember new', function () {
     });
 
     it('configurable CI option', async function () {
+      if (DEPRECATIONS.DEPRECATE_TRAVIS_CI_SUPPORT.isRemoved) {
+        this.skip();
+      }
+
       await ember(['new', 'foo', '--ci-provider=travis', '--skip-npm', '--skip-git']);
 
       let fixturePath = 'app/npm-travis';
@@ -648,6 +653,9 @@ describe('Acceptance: ember new', function () {
     });
 
     it('configurable CI option with yarn', async function () {
+      if (DEPRECATIONS.DEPRECATE_TRAVIS_CI_SUPPORT.isRemoved) {
+        this.skip();
+      }
       await ember(['new', 'foo', '--ci-provider=travis', '--skip-npm', '--skip-git', '--yarn']);
 
       let fixturePath = 'app/yarn-travis';
@@ -663,6 +671,10 @@ describe('Acceptance: ember new', function () {
     });
 
     it('addon configurable CI option', async function () {
+      if (DEPRECATIONS.DEPRECATE_TRAVIS_CI_SUPPORT.isRemoved) {
+        this.skip();
+      }
+
       await ember(['addon', 'foo', '--ci-provider=travis', '--skip-npm', '--skip-git']);
 
       let namespace = 'addon';
diff --git a/tests/fixtures/addon/typescript/eslint.config.mjs b/tests/fixtures/addon/typescript/eslint.config.mjs
index c84c3aebe..ccde221f2 100644
--- a/tests/fixtures/addon/typescript/eslint.config.mjs
+++ b/tests/fixtures/addon/typescript/eslint.config.mjs
@@ -84,14 +84,7 @@ export default ts.config(
     },
   },
   {
-    files: ['**/*.ts'],
-    languageOptions: {
-      parserOptions: parserOptions.esm.ts,
-    },
-    extends: [ember.configs.base, ...ts.configs.recommendedTypeChecked],
-  },
-  {
-    files: ['**/*.gts'],
+    files: ['**/*.{ts,gts}'],
     languageOptions: {
       parser: ember.parser,
       parserOptions: parserOptions.esm.ts,
diff --git a/tests/fixtures/app/typescript-embroider/eslint.config.mjs b/tests/fixtures/app/typescript-embroider/eslint.config.mjs
index c84c3aebe..ccde221f2 100644
--- a/tests/fixtures/app/typescript-embroider/eslint.config.mjs
+++ b/tests/fixtures/app/typescript-embroider/eslint.config.mjs
@@ -84,14 +84,7 @@ export default ts.config(
     },
   },
   {
-    files: ['**/*.ts'],
-    languageOptions: {
-      parserOptions: parserOptions.esm.ts,
-    },
-    extends: [ember.configs.base, ...ts.configs.recommendedTypeChecked],
-  },
-  {
-    files: ['**/*.gts'],
+    files: ['**/*.{ts,gts}'],
     languageOptions: {
       parser: ember.parser,
       parserOptions: parserOptions.esm.ts,
diff --git a/tests/fixtures/app/typescript/eslint.config.mjs b/tests/fixtures/app/typescript/eslint.config.mjs
index c84c3aebe..ccde221f2 100644
--- a/tests/fixtures/app/typescript/eslint.config.mjs
+++ b/tests/fixtures/app/typescript/eslint.config.mjs
@@ -84,14 +84,7 @@ export default ts.config(
     },
   },
   {
-    files: ['**/*.ts'],
-    languageOptions: {
-      parserOptions: parserOptions.esm.ts,
-    },
-    extends: [ember.configs.base, ...ts.configs.recommendedTypeChecked],
-  },
-  {
-    files: ['**/*.gts'],
+    files: ['**/*.{ts,gts}'],
     languageOptions: {
       parser: ember.parser,
       parserOptions: parserOptions.esm.ts,
diff --git a/tests/unit/debug/deprecate-test.js b/tests/unit/debug/deprecate-test.js
index 56f14188e..7de75c3b0 100644
--- a/tests/unit/debug/deprecate-test.js
+++ b/tests/unit/debug/deprecate-test.js
@@ -182,4 +182,56 @@ ID     foo
 UNTIL  5.0.0
 URL    https://example.com`);
   });
+
+  it('throws an deprecation if the current ember-cli version is greater than the until version of deprecation', function () {
+    expect(() => {
+      deprecate('The `foo` method is deprecated', false, {
+        for: 'ember-cli',
+        id: 'ember-cli.foo-method',
+        since: {
+          available: '4.1.0',
+          enabled: '4.2.0',
+        },
+        until: '3.0.0', // This should be less than the current emberCLIVersion to trigger the error
+        url: 'https://example.com',
+      });
+    }).to.throw(
+      'The API deprecated by ember-cli.foo-method was removed in ember-cli 3.0.0. The message was: The `foo` method is deprecated. Please see https://example.com for more details.'
+    );
+  });
+
+  it('throws an deprecation if the pre-release ember-cli version is greater than the until version of deprecation', function () {
+    const OVERRIDE_VERSION = process.env.OVERRIDE_DEPRECATION_VERSION;
+    process.env.OVERRIDE_DEPRECATION_VERSION = '9.0.0-beta.1';
+    expect(() => {
+      deprecate('The `bar` method is deprecated', false, {
+        for: 'ember-cli',
+        id: 'ember-cli.bar-method',
+        since: {
+          available: '4.1.0',
+          enabled: '4.2.0',
+        },
+        until: '9.0.0', // This should be less than the current emberCLIVersion to trigger the error
+        url: 'https://example.com',
+      });
+    }).to.throw(
+      'The API deprecated by ember-cli.bar-method was removed in ember-cli 9.0.0. The message was: The `bar` method is deprecated. Please see https://example.com for more details.'
+    );
+    process.env.OVERRIDE_DEPRECATION_VERSION = OVERRIDE_VERSION;
+  });
+
+  it('does not throw a deprecation if "for" is not "ember-cli"', function () {
+    expect(() => {
+      deprecate('The `foo` method is deprecated.', false, {
+        for: 'ember-source',
+        id: 'not-ember-cli.foo-method',
+        since: {
+          available: '4.1.0',
+          enabled: '4.2.0',
+        },
+        until: '3.0.0',
+        url: 'https://example.com',
+      });
+    }).to.not.throw();
+  });
 });