diff --git a/packages/migrate-config/src/compat-configs.js b/packages/migrate-config/src/compat-configs.js new file mode 100644 index 0000000..cbf01ce --- /dev/null +++ b/packages/migrate-config/src/compat-configs.js @@ -0,0 +1,6 @@ +/** + * @fileoverview A list of shareable configs that need the compat utility. + * @author Nicholas C. Zakas + */ + +export default ["eslint-config-react-app"]; diff --git a/packages/migrate-config/src/migrate-config.js b/packages/migrate-config/src/migrate-config.js index b1dbc87..674cb54 100644 --- a/packages/migrate-config/src/migrate-config.js +++ b/packages/migrate-config/src/migrate-config.js @@ -11,6 +11,7 @@ import * as recast from "recast"; import { Legacy } from "@eslint/eslintrc"; import camelCase from "camelcase"; import pluginsNeedingCompat from "./compat-plugins.js"; +import configsNeedingCompat from "./compat-configs.js"; import { convertIgnorePatternToMinimatch } from "@eslint/compat"; //----------------------------------------------------------------------------- @@ -147,6 +148,23 @@ function pluginNeedsCompat(pluginName) { ); } +/** + * Determines if a shareable config needs the compat utility. + * @param {string} configName The name of the config. + * @returns {boolean} `true` if the config needs the compat utility. + */ +function configNeedsCompat(configName) { + const configNameToTest = configName.includes("/") + ? configName.slice(0, configName.indexOf("/")) + : configName; + + const fullConfigName = naming.normalizePackageName( + configNameToTest, + "eslint-config", + ); + return configsNeedingCompat.includes(fullConfigName); +} + /** * Gets the name of the variable to use for the plugin. If the plugin name * contains slashes or an @ symbol, it will be normalized to a camelcase name. @@ -165,6 +183,9 @@ function getPluginVariableName(pluginName) { name = name.slice(1); } + // replace slash with uppercase of following letter + name = name.replace(/\/(.)/gu, (_, letter) => letter.toUpperCase()); + return camelCase(name); } @@ -682,11 +703,19 @@ function migrateConfigObject(migration, config) { // Check if any of the extends are plugins that need the compat utility const needsCompat = extendsArray.some(extend => { - if (!extend.startsWith("plugin:")) { + if ( + extend.startsWith("eslint:") || + extend.startsWith(".") || + extend.startsWith("/") + ) { return false; } - return pluginNeedsCompat(extend.slice(7)); + if (extend.startsWith("plugin:")) { + return pluginNeedsCompat(extend.slice(7)); + } + + return configNeedsCompat(extend); }); if (needsCompat) { diff --git a/packages/migrate-config/tests/fixtures/slash-package/.eslintrc.json b/packages/migrate-config/tests/fixtures/slash-package/.eslintrc.json new file mode 100644 index 0000000..70ba5e4 --- /dev/null +++ b/packages/migrate-config/tests/fixtures/slash-package/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "extends": [ + "react-app", + "prettier", + "plugin:jsx-a11y/recommended", + "plugin:@tanstack/eslint-plugin-query/recommended" + ], + "plugins": [ + "jsx-a11y", + "@tanstack/query" + ] +} diff --git a/packages/migrate-config/tests/fixtures/slash-package/expected.cjs b/packages/migrate-config/tests/fixtures/slash-package/expected.cjs new file mode 100644 index 0000000..86c9a70 --- /dev/null +++ b/packages/migrate-config/tests/fixtures/slash-package/expected.cjs @@ -0,0 +1,30 @@ +const { + fixupConfigRules, + fixupPluginRules, +} = require("@eslint/compat"); + +const jsxA11Y = require("eslint-plugin-jsx-a11y"); +const tanstackQuery = require("@tanstack/eslint-plugin-query"); +const js = require("@eslint/js"); + +const { + FlatCompat, +} = require("@eslint/eslintrc"); + +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all +}); + +module.exports = [...fixupConfigRules(compat.extends( + "react-app", + "prettier", + "plugin:jsx-a11y/recommended", + "plugin:@tanstack/eslint-plugin-query/recommended", +)), { + plugins: { + "jsx-a11y": fixupPluginRules(jsxA11Y), + "@tanstack/query": fixupPluginRules(tanstackQuery), + }, +}]; \ No newline at end of file diff --git a/packages/migrate-config/tests/fixtures/slash-package/expected.mjs b/packages/migrate-config/tests/fixtures/slash-package/expected.mjs new file mode 100644 index 0000000..8935ab7 --- /dev/null +++ b/packages/migrate-config/tests/fixtures/slash-package/expected.mjs @@ -0,0 +1,27 @@ +import { fixupConfigRules, fixupPluginRules } from "@eslint/compat"; +import jsxA11Y from "eslint-plugin-jsx-a11y"; +import tanstackQuery from "@tanstack/eslint-plugin-query"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import js from "@eslint/js"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all +}); + +export default [...fixupConfigRules(compat.extends( + "react-app", + "prettier", + "plugin:jsx-a11y/recommended", + "plugin:@tanstack/eslint-plugin-query/recommended", +)), { + plugins: { + "jsx-a11y": fixupPluginRules(jsxA11Y), + "@tanstack/query": fixupPluginRules(tanstackQuery), + }, +}]; \ No newline at end of file diff --git a/packages/migrate-config/tests/migrate-config-cli.test.js b/packages/migrate-config/tests/migrate-config-cli.test.js index 27db2d1..6c5c962 100644 --- a/packages/migrate-config/tests/migrate-config-cli.test.js +++ b/packages/migrate-config/tests/migrate-config-cli.test.js @@ -26,6 +26,7 @@ const filePaths = [ "gitignore-simple/.eslintrc.json", "gitignore-complex/.eslintrc.json", "import-duplicate/.eslintrc.cjs", + "slash-package/.eslintrc.json", ].map(file => `tests/fixtures/${file}`); //----------------------------------------------------------------------------- @@ -83,6 +84,7 @@ describe("@eslint/migrate-config", async () => { // run the migration for mjs execSync( `node src/migrate-config-cli.js ${filePath} ${gitignoreFlag}`, + { stdio: "inherit" }, ); // run the migration for cjs