From 8efc2d0c92dab6099f34c1479cd80bdc5cd1b07b Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 27 Dec 2024 08:06:15 +0100 Subject: [PATCH] feat: unflag TypeScript config files (#19266) --- docs/src/use/configure/configuration-files.md | 14 +- lib/cli.js | 7 +- lib/config/config-loader.js | 31 +--- lib/eslint/eslint.js | 7 +- lib/shared/flags.js | 6 +- tests/fixtures/ts-config-files/helper.ts | 31 +--- tests/lib/eslint/eslint.js | 156 ++++++++---------- 7 files changed, 86 insertions(+), 166 deletions(-) diff --git a/docs/src/use/configure/configuration-files.md b/docs/src/use/configure/configuration-files.md index 05cc2bd8bbb1..d90c0fefec70 100644 --- a/docs/src/use/configure/configuration-files.md +++ b/docs/src/use/configure/configuration-files.md @@ -517,18 +517,6 @@ For more information about using feature flags, see [Feature Flags](../../flags/ ## TypeScript Configuration Files -::: warning -This feature is currently experimental and may change in future versions. -::: - -You need to enable this feature through the `unstable_ts_config` feature flag: - -```bash -npx eslint --flag unstable_ts_config -``` - -For more information about using feature flags, see [Feature Flags](../../flags/). - For Deno and Bun, TypeScript configuration files are natively supported; for Node.js, you must install the optional dev dependency [`jiti`](https://github.com/unjs/jiti) in version 2.0.0 or later in your project (this dependency is not automatically installed by ESLint): ```bash @@ -591,5 +579,5 @@ If you have multiple ESLint configuration files, ESLint prioritizes JavaScript f To override this behavior, use the `--config` or `-c` command line option to specify a different configuration file: ```bash -npx eslint --flag unstable_ts_config --config eslint.config.ts +npx eslint --config eslint.config.ts ``` diff --git a/lib/cli.js b/lib/cli.js index 3229d6a20056..3a508d76637b 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -341,16 +341,15 @@ const cli = { /** * Calculates the command string for the --inspect-config operation. * @param {string} configFile The path to the config file to inspect. - * @param {boolean} hasUnstableTSConfigFlag `true` if the `unstable_ts_config` flag is enabled, `false` if it's not. * @returns {Promise} The command string to execute. */ - async calculateInspectConfigFlags(configFile, hasUnstableTSConfigFlag) { + async calculateInspectConfigFlags(configFile) { // find the config file const { configFilePath, basePath - } = await locateConfigFileToUse({ cwd: process.cwd(), configFile }, hasUnstableTSConfigFlag); + } = await locateConfigFileToUse({ cwd: process.cwd(), configFile }); return ["--config", configFilePath, "--basePath", basePath]; }, @@ -451,7 +450,7 @@ const cli = { try { const flatOptions = await translateOptions(options, "flat"); const spawn = require("cross-spawn"); - const flags = await cli.calculateInspectConfigFlags(flatOptions.overrideConfigFile, flatOptions.flags ? flatOptions.flags.includes("unstable_ts_config") : false); + const flags = await cli.calculateInspectConfigFlags(flatOptions.overrideConfigFile); spawn.sync("npx", ["@eslint/config-inspector@latest", ...flags], { encoding: "utf8", stdio: "inherit" }); } catch (error) { diff --git a/lib/config/config-loader.js b/lib/config/config-loader.js index 845cd0c92861..85944672ac23 100644 --- a/lib/config/config-loader.js +++ b/lib/config/config-loader.js @@ -31,7 +31,6 @@ const { FlatConfigArray } = require("./flat-config-array"); * @property {Array} [defaultConfigs] The default configs to use. * @property {Array} [ignorePatterns] The ignore patterns to use. * @property {FlatConfigObject|Array} overrideConfig The override config to use. - * @property {boolean} allowTS Indicates if TypeScript configuration files are allowed. */ //------------------------------------------------------------------------------ @@ -41,10 +40,7 @@ const { FlatConfigArray } = require("./flat-config-array"); const FLAT_CONFIG_FILENAMES = [ "eslint.config.js", "eslint.config.mjs", - "eslint.config.cjs" -]; - -const TS_FLAT_CONFIG_FILENAMES = [ + "eslint.config.cjs", "eslint.config.ts", "eslint.config.mts", "eslint.config.cts" @@ -119,10 +115,9 @@ function isRunningInDeno() { /** * Load the config array from the given filename. * @param {string} filePath The filename to load from. - * @param {boolean} allowTS Indicates if TypeScript configuration files are allowed. * @returns {Promise} The config loaded from the config file. */ -async function loadConfigFile(filePath, allowTS) { +async function loadConfigFile(filePath) { debug(`Loading config from ${filePath}`); @@ -171,7 +166,7 @@ async function loadConfigFile(filePath, allowTS) { * * When Node.js supports native TypeScript imports, we can remove this check. */ - if (allowTS && isTS && !isDeno && !isBun) { + if (isTS && !isDeno && !isBun) { // eslint-disable-next-line no-use-before-define -- `ConfigLoader.loadJiti` can be overwritten for testing const { createJiti } = await ConfigLoader.loadJiti().catch(() => { @@ -261,8 +256,7 @@ class ConfigLoader { const resultPromise = ConfigLoader.locateConfigFileToUse({ useConfigFile: this.#options.configFile, cwd: this.#options.cwd, - fromDirectory, - allowTS: this.#options.allowTS + fromDirectory }); // ensure `ConfigLoader.locateConfigFileToUse` is called only once for `fromDirectory` @@ -443,15 +437,10 @@ class ConfigLoader { * @param {string|false|undefined} options.useConfigFile The path to the config file to use. * @param {string} options.cwd Path to a directory that should be considered as the current working directory. * @param {string} [options.fromDirectory] The directory from which to start searching. Defaults to `cwd`. - * @param {boolean} options.allowTS Indicates if TypeScript configuration files are allowed. * @returns {Promise<{configFilePath:string|undefined,basePath:string}>} Location information for * the config file. */ - static async locateConfigFileToUse({ useConfigFile, cwd, fromDirectory = cwd, allowTS }) { - - const configFilenames = allowTS - ? [...FLAT_CONFIG_FILENAMES, ...TS_FLAT_CONFIG_FILENAMES] - : FLAT_CONFIG_FILENAMES; + static async locateConfigFileToUse({ useConfigFile, cwd, fromDirectory = cwd }) { // determine where to load config file from let configFilePath; @@ -464,7 +453,7 @@ class ConfigLoader { } else if (useConfigFile !== false) { debug("Searching for eslint.config.js"); configFilePath = await findUp( - configFilenames, + FLAT_CONFIG_FILENAMES, { cwd: fromDirectory } ); @@ -497,8 +486,7 @@ class ConfigLoader { ignoreEnabled, ignorePatterns, overrideConfig, - defaultConfigs = [], - allowTS + defaultConfigs = [] } = options; debug(`Calculating config array from config file ${configFilePath} and base path ${basePath}`); @@ -509,7 +497,7 @@ class ConfigLoader { if (configFilePath) { debug(`Loading config file ${configFilePath}`); - const fileConfig = await loadConfigFile(configFilePath, allowTS); + const fileConfig = await loadConfigFile(configFilePath); if (Array.isArray(fileConfig)) { configs.push(...fileConfig); @@ -618,8 +606,7 @@ class LegacyConfigLoader extends ConfigLoader { if (!this.#configFilePath) { this.#configFilePath = ConfigLoader.locateConfigFileToUse({ useConfigFile: this.#options.configFile, - cwd: this.#options.cwd, - allowTS: this.#options.allowTS + cwd: this.#options.cwd }); } diff --git a/lib/eslint/eslint.js b/lib/eslint/eslint.js index c91a376fb518..89583d53f59d 100644 --- a/lib/eslint/eslint.js +++ b/lib/eslint/eslint.js @@ -266,15 +266,13 @@ function compareResultsByFilePath(a, b) { * This function is used primarily by the `--inspect-config` option. For now, * we will maintain the existing behavior, which is to search up from the cwd. * @param {ESLintOptions} options The ESLint instance options. - * @param {boolean} allowTS `true` if the `unstable_ts_config` flag is enabled, `false` if it's not. * @returns {Promise<{configFilePath:string|undefined;basePath:string}>} Location information for * the config file. */ -async function locateConfigFileToUse({ configFile, cwd }, allowTS) { +async function locateConfigFileToUse({ configFile, cwd }) { const configLoader = new ConfigLoader({ cwd, - allowTS, configFile }); @@ -469,8 +467,7 @@ class ESLint { configFile: processedOptions.configFile, ignoreEnabled: processedOptions.ignore, ignorePatterns: processedOptions.ignorePatterns, - defaultConfigs, - allowTS: processedOptions.flags.includes("unstable_ts_config") + defaultConfigs }; this.#configLoader = processedOptions.flags.includes("unstable_config_lookup_from_file") diff --git a/lib/shared/flags.js b/lib/shared/flags.js index 70ef2c9034a2..ba2ea2504106 100644 --- a/lib/shared/flags.js +++ b/lib/shared/flags.js @@ -10,8 +10,7 @@ */ const activeFlags = new Map([ ["test_only", "Used only for testing."], - ["unstable_config_lookup_from_file", "Look up `eslint.config.js` from the file being linted."], - ["unstable_ts_config", "Enable TypeScript configuration files."] + ["unstable_config_lookup_from_file", "Look up `eslint.config.js` from the file being linted."] ]); /** @@ -19,7 +18,8 @@ const activeFlags = new Map([ * @type {Map} */ const inactiveFlags = new Map([ - ["test_only_old", "Used only for testing."] + ["test_only_old", "Used only for testing."], + ["unstable_ts_config", "This flag is no longer required to enable TypeScript configuration files."] ]); module.exports = { diff --git a/tests/fixtures/ts-config-files/helper.ts b/tests/fixtures/ts-config-files/helper.ts index 4763889adcb6..c4728b4e6ed9 100644 --- a/tests/fixtures/ts-config-files/helper.ts +++ b/tests/fixtures/ts-config-files/helper.ts @@ -4,12 +4,7 @@ * and namespaces from other TypeScript files. */ -export type RuleLevelAndOptions = Prepend< - Partial, - RuleLevel ->; - -export type StringSeverity = "off" | "warn" | "error"; +import { Linter } from "eslint"; export const enum Severity { "Off" = 0, @@ -17,29 +12,7 @@ export const enum Severity { "Error" = 2, } -export type RuleLevel = Severity | StringSeverity; - -export type RuleEntry = - | RuleLevel - | RuleLevelAndOptions; - -export interface RulesRecord { - [rule: string]: RuleEntry; -} - -export interface FlatConfig { - name?: string; - files?: Array; - ignores?: string[]; - linterOptions?: { - noInlineConfig?: boolean; - reportUnusedDisableDirectives?: Severity | StringSeverity | boolean; - }; - processor?: string; - plugins?: Record; - rules?: Partial; - settings?: Record; -} +export type FlatConfig = Linter.Config; export namespace ESLintNameSpace { export const enum StringSeverity { diff --git a/tests/lib/eslint/eslint.js b/tests/lib/eslint/eslint.js index 1c851ffea4a3..96c270e62b07 100644 --- a/tests/lib/eslint/eslint.js +++ b/tests/lib/eslint/eslint.js @@ -335,6 +335,15 @@ describe("ESLint", () => { processStub.restore(); }); + + it("should throw an error if the flag 'unstable_ts_config' is used", () => { + assert.throws( + () => new ESLint({ + flags: [...flags, "unstable_ts_config"] + }), + { message: "The flag 'unstable_ts_config' is inactive: This flag is no longer required to enable TypeScript configuration files." } + ); + }); }); describe("hasFlag", () => { @@ -1107,15 +1116,13 @@ describe("ESLint", () => { describe("TypeScript config files", () => { - const tsFlags = ["unstable_ts_config", ...flags]; - it("should find and load eslint.config.ts when present", async () => { const cwd = getFixturePath("ts-config-files", "ts"); eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1133,7 +1140,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1151,7 +1158,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1169,7 +1176,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1187,7 +1194,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1205,7 +1212,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags, + flags, overrideConfigFile: getFixturePath("ts-config-files", "ts", "custom-config", "eslint.custom.config.ts") }); @@ -1224,7 +1231,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1242,7 +1249,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1260,7 +1267,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1278,7 +1285,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1296,7 +1303,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1314,7 +1321,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo"); @@ -1332,7 +1339,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); const results = await eslint.lintText("foo;"); @@ -1354,7 +1361,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); await assert.rejects( @@ -1373,7 +1380,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags + flags }); await assert.rejects( @@ -1388,7 +1395,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: tsFlags, + flags, overrideConfigFile: "eslint.undefined.config.ts" }); @@ -4878,15 +4885,13 @@ describe("ESLint", () => { const typeCommonJS = JSON.stringify({ type: "commonjs" }, null, 2); - const newFlags = flags.concat("unstable_ts_config"); - it("should find and load eslint.config.ts when present", async () => { const cwd = getFixturePath("ts-config-files", "ts"); eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -4905,7 +4910,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -4924,7 +4929,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -4959,7 +4964,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -4994,7 +4999,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5029,7 +5034,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5064,7 +5069,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5099,7 +5104,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5135,7 +5140,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5171,7 +5176,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5207,7 +5212,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5243,7 +5248,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5279,7 +5284,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5313,7 +5318,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5347,7 +5352,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5381,7 +5386,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5415,7 +5420,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5452,7 +5457,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5489,7 +5494,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5523,7 +5528,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5557,7 +5562,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5591,7 +5596,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5625,7 +5630,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5659,7 +5664,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5693,7 +5698,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo.js"]); @@ -5713,7 +5718,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -5732,7 +5737,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -5753,7 +5758,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags, + flags, overrideConfigFile }); @@ -5773,7 +5778,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -5792,7 +5797,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -5811,7 +5816,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -5830,7 +5835,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -5849,7 +5854,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -5868,7 +5873,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles("foo.js"); @@ -5904,7 +5909,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, overrideConfigFile: "eslint.config.mcts", - flags: newFlags + flags }); assert.strictEqual(await eslint.findConfigFile(), path.join(cwd, "eslint.config.mcts")); @@ -5912,42 +5917,13 @@ describe("ESLint", () => { }); - it("should not load TS config files when `\"unstable_ts_config\"` flag is not set", async () => { - - const cwd = getFixturePath("ts-config-files", "ts"); - - eslint = new ESLint({ - cwd, - flags, - overrideConfigFile: "eslint.config.ts" - }); - - assert.strictEqual(await eslint.findConfigFile(), path.join(cwd, "eslint.config.ts")); - await assert.rejects(() => eslint.lintFiles(["foo.js"])); - - }); - - it("should fallback to JS config files when `\"unstable_ts_config\"` flag is not set", async () => { - - const cwd = getFixturePath("ts-config-files", "ts"); - - eslint = new ESLint({ - cwd, - flags - }); - - assert.strictEqual(await eslint.findConfigFile(), path.join(cwd, "../../eslint.config.js")); - await assert.doesNotReject(() => eslint.lintFiles(["foo.js"])); - - }); - it("should successfully load a TS config file that exports a promise", async () => { const cwd = getFixturePath("ts-config-files", "ts", "exports-promise"); eslint = new ESLint({ cwd, - flags: newFlags + flags }); const results = await eslint.lintFiles(["foo*.js"]); @@ -5971,7 +5947,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); await assert.rejects( @@ -5990,7 +5966,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags + flags }); await assert.rejects( @@ -6005,7 +5981,7 @@ describe("ESLint", () => { eslint = new ESLint({ cwd, - flags: newFlags, + flags, overrideConfigFile: "eslint.undefined.config.ts" }); @@ -8503,7 +8479,7 @@ describe("ESLint", () => { await teardown.prepare(); - let eslint = new ESLint({ cwd, flags: ["unstable_ts_config"] }); + let eslint = new ESLint({ flags, cwd }); let [{ messages }] = await eslint.lintFiles(["a.js"]); assert.strictEqual(messages.length, 1); @@ -8514,7 +8490,7 @@ describe("ESLint", () => { await sleep(100); await fsp.writeFile(path.join(cwd, "eslint.config.ts"), configFileContent.replace("always", "never")); - eslint = new ESLint({ cwd, flags: ["unstable_ts_config"] }); + eslint = new ESLint({ flags, cwd }); [{ messages }] = await eslint.lintFiles(["a.js"]); assert.strictEqual(messages.length, 1);