@oxlint/migrate 1.50.0 → 1.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @oxlint/migrate
2
2
 
3
3
  ![test](https://github.com/oxc-project/oxlint-migrate/actions/workflows/test.yml/badge.svg)
4
- [![NPM Version](https://img.shields.io/npm/v/%40oxlint%2Fmigrate)](https://www.npmjs.com/package/@oxlint/migrate)
5
- [![NPM Downloads](https://img.shields.io/npm/dm/%40oxlint%2Fmigrate)](https://www.npmjs.com/package/@oxlint/migrate)
4
+ [![NPM Version](https://img.shields.io/npm/v/%40oxlint%2Fmigrate)](https://npmx.dev/package/@oxlint/migrate)
5
+ [![NPM Downloads](https://img.shields.io/npm/dm/%40oxlint%2Fmigrate)](https://npmx.dev/package/@oxlint/migrate)
6
6
 
7
7
  Generates a `.oxlintrc.json` from an existing ESLint flat config.
8
8
 
@@ -18,15 +18,15 @@ When no config file is provided, the script searches for the default ESLint conf
18
18
 
19
19
  ### Options
20
20
 
21
- | Options | Description |
22
- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
23
- | `--merge` | \* merge ESLint configuration with an existing .oxlintrc.json configuration |
24
- | `--type-aware` | Include type aware rules, which are supported with `oxlint --type-aware` and [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) |
25
- | `--with-nursery` | Include oxlint rules which are currently under development |
26
- | `--js-plugins` | \*\* Include ESLint plugins via `jsPlugins` key. |
27
- | `--details` | List rules that could not be migrated to oxlint |
28
- | `--output-file <file>` | The oxlint configuration file where ESLint v9 rules will be written to, default: `.oxlintrc.json` |
29
- | `--replace-eslint-comments` | Search in the project files for ESLint comments and replaces them with oxlint. Some ESLint comments are not supported and will be reported. |
21
+ | Options | Description |
22
+ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
+ | `--merge` | \* merge ESLint configuration with an existing .oxlintrc.json configuration |
24
+ | `--type-aware` | Include type aware rules. These rules are supported with `oxlint --type-aware` and [oxlint-tsgolint](https://github.com/oxc-project/tsgolint). This will also enable the `typeAware` option in the generated configuration. |
25
+ | `--with-nursery` | Include oxlint rules which are currently under development |
26
+ | `--js-plugins [bool]` | \*\* Include ESLint plugins via `jsPlugins` key (enabled by default). Use `--js-plugins=false` to disable. |
27
+ | `--details` | List rules that could not be migrated to oxlint |
28
+ | `--output-file <file>` | The oxlint configuration file where ESLint v9 rules will be written to, default: `.oxlintrc.json` |
29
+ | `--replace-eslint-comments` | Search in the project files for ESLint comments and replaces them with oxlint. Some ESLint comments are not supported and will be reported. |
30
30
 
31
31
  \* WARNING: When some `categories` are enabled, this tools will enable more rules with the combination of `plugins`.
32
32
  Else we need to disable each rule `plugin/categories` combination, which is not covered by your ESLint configuration.
@@ -49,7 +49,7 @@ TypeScript configuration files, like `eslint.config.mts`, are supported in the f
49
49
  - **Deno and Bun**: TypeScript configuration files are natively supported.
50
50
  - **Node.js >=22.18.0**: TypeScript configuration files are supported natively with built-in type-stripping enabled by default.
51
51
  - **Node.js >=22.6.0**: TypeScript configuration files can be used by setting `NODE_OPTIONS=--experimental-strip-types`.
52
- - **Node.js <22.6.0**: TypeScript configuration files can be used by setting `NODE_OPTIONS=--import @oxc-node/core/register` and installing [@oxc-node/core](https://www.npmjs.com/package/@oxc-node/core) as a dev dependency.
52
+ - **Node.js <22.6.0**: TypeScript configuration files can be used by setting `NODE_OPTIONS=--import @oxc-node/core/register` and installing [@oxc-node/core](https://npmx.dev/package/@oxc-node/core) as a dev dependency.
53
53
 
54
54
  If you attempt to use a TypeScript configuration file without the proper setup for your Node.js version, Node.js will throw an error when trying to import the file.
55
55
 
@@ -65,7 +65,7 @@ Here are some known caveats to be aware of:
65
65
 
66
66
  **`settings` field migration**
67
67
 
68
- The `settings` field (e.g. for setting the React version) is migrated for known oxlint-supported plugins: `jsx-a11y`, `next`, `react`, `jsdoc`, and `vitest`. By default, other settings keys are skipped since they aren't supported by oxlint. If using the `--js-plugins` flag, other settings keys will also be migrated in order to support JS Plugins.
68
+ The `settings` field (e.g. for setting the React version) is migrated for known oxlint-supported plugins: `jsx-a11y`, `next`, `react`, `jsdoc`, and `vitest`. By default, other settings keys are also migrated to support JS Plugins. Use `--js-plugins=false` to skip migrating unknown settings keys.
69
69
 
70
70
  Note: Oxlint does not support `settings` in override configs. If your ESLint config has settings in configs with `files` patterns, those settings will be skipped and a warning will be shown.
71
71
 
@@ -73,7 +73,7 @@ Not all `settings` options are supported by oxlint, and so rule behavior in cert
73
73
 
74
74
  **Local ESLint Plugins imported via path are not migrated**
75
75
 
76
- The `--js-plugins` flag cannot migrate ESLint plugins from file paths in the same repo currently (e.g. if you have `../eslint-plugin-myplugin` in your `eslint.config.mjs`). You will need to copy them over into the `jsPlugins` manually. See [the JS Plugins docs]() for more info.
76
+ JS plugin migration cannot migrate ESLint plugins from file paths in the same repo currently (e.g. if you have `../eslint-plugin-myplugin` in your `eslint.config.mjs`). You will need to copy them over into the `jsPlugins` manually. See [the JS Plugins docs]() for more info.
77
77
 
78
78
  **`globals` field with large number of values**
79
79
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as preFixForJsPlugins, f as buildUnsupportedRuleExplanations, m as rules_exports, p as nurseryRules, u as isOffValue } from "../settings-D8R7axmT.mjs";
2
+ import { a as preFixForJsPlugins, f as buildUnsupportedRuleExplanations, m as rules_exports, p as nurseryRules, u as isOffValue } from "../settings-R7dCJ7Y3.mjs";
3
3
  import main from "../src/index.mjs";
4
4
  import { program } from "commander";
5
5
  import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
@@ -33,7 +33,7 @@ const loadESLintConfig = async (filePath) => {
33
33
 
34
34
  //#endregion
35
35
  //#region package.json
36
- var version = "1.50.0";
36
+ var version = "1.51.0";
37
37
 
38
38
  //#endregion
39
39
  //#region src/walker/comments/replaceRuleDirectiveComment.ts
@@ -357,7 +357,7 @@ function detectMissingFlags(byCategory, cliOptions) {
357
357
  const missingFlags = [];
358
358
  if (byCategory.nursery.length > 0 && !cliOptions.withNursery) missingFlags.push("--with-nursery");
359
359
  if (byCategory["type-aware"].length > 0 && !cliOptions.typeAware) missingFlags.push("--type-aware");
360
- if (byCategory["js-plugins"].length > 0 && !cliOptions.jsPlugins) missingFlags.push("--js-plugins");
360
+ if (byCategory["js-plugins"].length > 0 && !cliOptions.jsPlugins) missingFlags.push("--js-plugins=true");
361
361
  return missingFlags;
362
362
  }
363
363
  /**
@@ -399,14 +399,25 @@ function formatMigrationOutput(data) {
399
399
  }
400
400
  return output;
401
401
  }
402
+ function formatWarningsOutput(warnings) {
403
+ if (warnings.length === 0) return "";
404
+ let output = `⚠️ Warnings (${warnings.length}):\n`;
405
+ for (const warning of warnings) {
406
+ const [message, ...details] = warning.split("\n");
407
+ output += ` * ${message}\n`;
408
+ for (const detail of details.filter((line) => line.trim().length)) output += ` * ${detail}\n`;
409
+ }
410
+ return output.trimEnd();
411
+ }
402
412
  function displayMigrationResult(outputMessage, warnings) {
403
413
  console.log(outputMessage);
404
- for (const warning of warnings) console.warn(warning);
414
+ if (warnings.length > 0) console.warn(formatWarningsOutput(warnings));
405
415
  }
406
416
 
407
417
  //#endregion
408
418
  //#region bin/oxlint-migrate.ts
409
419
  const cwd = process.cwd();
420
+ const parseCliBoolean = (value) => value === "false" ? false : !!value;
410
421
  const getFileContent = (absoluteFilePath) => {
411
422
  try {
412
423
  return readFileSync(absoluteFilePath, "utf-8");
@@ -429,8 +440,9 @@ const countEnabledRules = (config) => {
429
440
  }
430
441
  return enabledRules.size;
431
442
  };
432
- program.name("oxlint-migrate").version(version).argument("[eslint-config]", "The path to the eslint v9 config file").option("--output-file <file>", "The oxlint configuration file where to eslint v9 rules will be written to", ".oxlintrc.json").option("--merge", "Merge eslint configuration with an existing .oxlintrc.json configuration", false).option("--with-nursery", "Include oxlint rules which are currently under development", false).option("--replace-eslint-comments", "Search in the project files for eslint comments and replaces them with oxlint. Some eslint comments are not supported and will be reported.").option("--type-aware", "Includes supported type-aware rules. Needs the same flag in `oxlint` to enable it.").option("--js-plugins", "Tries to convert unsupported oxlint plugins with `jsPlugins`.").option("--details", "List rules that could not be migrated to oxlint.", false).action(async (filePath) => {
443
+ program.name("oxlint-migrate").version(version).argument("[eslint-config]", "The path to the eslint v9 config file").option("--output-file <file>", "The oxlint configuration file where to eslint v9 rules will be written to", ".oxlintrc.json").option("--merge", "Merge eslint configuration with an existing .oxlintrc.json configuration", false).option("--with-nursery", "Include oxlint rules which are currently under development", false).option("--replace-eslint-comments", "Search in the project files for eslint comments and replaces them with oxlint. Some eslint comments are not supported and will be reported.").option("--type-aware", "Includes supported type-aware rules. Needs the same flag in `oxlint` or the `typeAware` config option to enable it.").option("--js-plugins [bool]", "Tries to convert unsupported oxlint plugins with `jsPlugins`. Enabled by default; pass `--js-plugins=false` to disable.", true).option("--details", "List rules that could not be migrated to oxlint.", false).action(async (filePath) => {
433
444
  const cliOptions = program.opts();
445
+ const jsPlugins = parseCliBoolean(cliOptions.jsPlugins);
434
446
  const oxlintFilePath = path.join(cwd, cliOptions.outputFile);
435
447
  const reporter = new DefaultReporter();
436
448
  const options = {
@@ -438,7 +450,7 @@ program.name("oxlint-migrate").version(version).argument("[eslint-config]", "The
438
450
  merge: !!cliOptions.merge,
439
451
  withNursery: !!cliOptions.withNursery,
440
452
  typeAware: !!cliOptions.typeAware,
441
- jsPlugins: !!cliOptions.jsPlugins
453
+ jsPlugins
442
454
  };
443
455
  if (cliOptions.replaceEslintComments) {
444
456
  await walkAndReplaceProjectFiles(await getAllProjectFiles(), (filePath) => getFileContent(filePath), (filePath, content) => writeFile(filePath, content, "utf-8"), options);
@@ -467,7 +479,7 @@ program.name("oxlint-migrate").version(version).argument("[eslint-config]", "The
467
479
  withNursery: !!cliOptions.withNursery,
468
480
  typeAware: !!cliOptions.typeAware,
469
481
  details: !!cliOptions.details,
470
- jsPlugins: !!cliOptions.jsPlugins
482
+ jsPlugins
471
483
  },
472
484
  eslintConfigPath: filePath
473
485
  }), reporter.getWarnings());
@@ -1071,13 +1071,47 @@ const ignorePlugins = new Set([
1071
1071
  ...Object.values(rulesPrefixesForPlugins),
1072
1072
  "local"
1073
1073
  ]);
1074
- const guessEslintPluginName = (pluginName) => {
1074
+ const tryResolvePackage = (packageName) => {
1075
+ try {
1076
+ import.meta.resolve(packageName);
1077
+ return true;
1078
+ } catch {
1079
+ return false;
1080
+ }
1081
+ };
1082
+ const pluginNameCache = /* @__PURE__ */ new Map();
1083
+ /**
1084
+ * Resolves the npm package name for an ESLint plugin given its scope name.
1085
+ *
1086
+ * For scoped plugin names (starting with `@`), the mapping is unambiguous:
1087
+ * - `@scope` -> `@scope/eslint-plugin`
1088
+ * - `@scope/sub` -> `@scope/eslint-plugin-sub`
1089
+ *
1090
+ * For non-scoped names, the npm package could follow either convention:
1091
+ * - `eslint-plugin-{name}` (e.g. `eslint-plugin-mocha`)
1092
+ * - `@{name}/eslint-plugin` (e.g. `@e18e/eslint-plugin`)
1093
+ *
1094
+ * We try to resolve both candidates against the installed packages and
1095
+ * use the one that is actually present, falling back to the standard
1096
+ * `eslint-plugin-{name}` convention when neither can be resolved.
1097
+ */
1098
+ const resolveEslintPluginName = (pluginName) => {
1099
+ const cached = pluginNameCache.get(pluginName);
1100
+ if (cached !== void 0) return cached;
1101
+ let result;
1075
1102
  if (pluginName.startsWith("@")) {
1076
1103
  const [scope, maybeSub] = pluginName.split("/");
1077
- if (maybeSub) return `${scope}/eslint-plugin-${maybeSub}`;
1078
- return `${scope}/eslint-plugin`;
1104
+ if (maybeSub) result = `${scope}/eslint-plugin-${maybeSub}`;
1105
+ else result = `${scope}/eslint-plugin`;
1106
+ } else {
1107
+ const standardName = `eslint-plugin-${pluginName}`;
1108
+ const scopedName = `@${pluginName}/eslint-plugin`;
1109
+ if (tryResolvePackage(standardName)) result = standardName;
1110
+ else if (tryResolvePackage(scopedName)) result = scopedName;
1111
+ else result = standardName;
1079
1112
  }
1080
- return `eslint-plugin-${pluginName}`;
1113
+ pluginNameCache.set(pluginName, result);
1114
+ return result;
1081
1115
  };
1082
1116
  const extractPluginId = (ruleId) => {
1083
1117
  const firstSlash = ruleId.indexOf("/");
@@ -1093,15 +1127,68 @@ const isIgnoredPluginRule = (ruleId) => {
1093
1127
  if (pluginName === void 0) return true;
1094
1128
  return ignorePlugins.has(pluginName);
1095
1129
  };
1096
- const enableJsPluginRule = (targetConfig, rule, ruleEntry) => {
1130
+ /**
1131
+ * Derives the npm package name for a plugin from its `meta.name` field.
1132
+ *
1133
+ * If `meta.name` already looks like a full npm package name (contains
1134
+ * "eslint-plugin"), it is returned as-is. Otherwise it is fed through
1135
+ * {@link resolveEslintPluginName} for the usual heuristic resolution.
1136
+ */
1137
+ const resolveFromMetaName = (metaName) => {
1138
+ if (metaName.includes("eslint-plugin")) return metaName;
1139
+ return resolveEslintPluginName(metaName);
1140
+ };
1141
+ /**
1142
+ * Derives the rule-ID prefix that an npm package exposes.
1143
+ *
1144
+ * Examples:
1145
+ * `eslint-plugin-react-dom` -> `react-dom`
1146
+ * `eslint-plugin-mocha` -> `mocha`
1147
+ * `@stylistic/eslint-plugin` -> `@stylistic`
1148
+ * `@stylistic/eslint-plugin-ts` -> `@stylistic/ts`
1149
+ */
1150
+ const deriveRulePrefix = (packageName) => {
1151
+ if (packageName.startsWith("@")) {
1152
+ const slashIdx = packageName.indexOf("/");
1153
+ const scope = packageName.substring(0, slashIdx);
1154
+ const rest = packageName.substring(slashIdx + 1);
1155
+ if (rest === "eslint-plugin") return scope;
1156
+ if (rest.startsWith("eslint-plugin-")) return `${scope}/${rest.substring(14)}`;
1157
+ return packageName;
1158
+ }
1159
+ if (packageName.startsWith("eslint-plugin-")) return packageName.substring(14);
1160
+ return packageName;
1161
+ };
1162
+ /**
1163
+ * Resolves the canonical rule name for a jsPlugin rule.
1164
+ *
1165
+ * When a plugin is registered under an alias (e.g. `@eslint-react/dom`) but
1166
+ * its `meta.name` reveals a different canonical package (`eslint-plugin-react-dom`),
1167
+ * the rule must be rewritten so that oxlint can match it to the loaded plugin.
1168
+ *
1169
+ * For example:
1170
+ * `@eslint-react/dom/no-find-dom-node` -> `react-dom/no-find-dom-node`
1171
+ */
1172
+ const resolveJsPluginRuleName = (rule, plugins) => {
1173
+ const pluginName = extractPluginId(rule);
1174
+ if (pluginName === void 0) return rule;
1175
+ const metaName = plugins?.[pluginName]?.meta?.name;
1176
+ if (!metaName || !metaName.includes("eslint-plugin")) return rule;
1177
+ const canonicalPrefix = deriveRulePrefix(metaName);
1178
+ if (canonicalPrefix === pluginName) return rule;
1179
+ return `${canonicalPrefix}/${rule.substring(pluginName.length + 1)}`;
1180
+ };
1181
+ const enableJsPluginRule = (targetConfig, rule, ruleEntry, plugins) => {
1097
1182
  const pluginName = extractPluginId(rule);
1098
1183
  if (pluginName === void 0) return false;
1099
1184
  if (ignorePlugins.has(pluginName)) return false;
1100
1185
  if (targetConfig.jsPlugins === void 0) targetConfig.jsPlugins = [];
1101
- const eslintPluginName = guessEslintPluginName(pluginName);
1186
+ const metaName = plugins?.[pluginName]?.meta?.name;
1187
+ const eslintPluginName = metaName ? resolveFromMetaName(metaName) : resolveEslintPluginName(pluginName);
1102
1188
  if (!targetConfig.jsPlugins.includes(eslintPluginName)) targetConfig.jsPlugins.push(eslintPluginName);
1189
+ const resolvedRule = resolveJsPluginRuleName(rule, plugins);
1103
1190
  targetConfig.rules = targetConfig.rules || {};
1104
- targetConfig.rules[rule] = ruleEntry;
1191
+ targetConfig.rules[resolvedRule] = ruleEntry;
1105
1192
  return true;
1106
1193
  };
1107
1194
 
@@ -1146,6 +1233,7 @@ var unsupportedRules = {
1146
1233
  "import/no-deprecated": "No need to implement, already implemented by `typescript/no-deprecated` via tsgolint.",
1147
1234
  "n/no-restricted-import": "No need to implement, already implemented by `no-restricted-imports` rule.",
1148
1235
  "n/no-restricted-require": "No need to implement, already implemented by `no-restricted-imports` rule.",
1236
+ "import/order": "Not implementing this in Oxlint as its behavior is covered very well by [Oxfmt's import sorting](https://oxc.rs/docs/guide/usage/formatter/sorting.html).",
1149
1237
  "jsdoc/type-formatting": "Experimental rule in the original plugin, may reconsider once stable.",
1150
1238
  "jsdoc/convert-to-jsdoc-comments": "Experimental rule in the original plugin, may reconsider once stable.",
1151
1239
  "jsdoc/check-examples": "Deprecated.",
@@ -1571,12 +1659,22 @@ const mergeRuleConfig = (existingConfig, newConfig) => {
1571
1659
  if (Array.isArray(newConfig) && newConfig.length === 1 && existingIsArray && existingConfig.length > 1) return [newConfig[0], ...existingConfig.slice(1)];
1572
1660
  return newConfig;
1573
1661
  };
1574
- const transformRuleEntry = (eslintConfig, targetConfig, baseConfig, options, overrides) => {
1662
+ const transformRuleEntry = (eslintConfig, targetConfig, baseConfig, options, overrides, globalPlugins) => {
1575
1663
  if (eslintConfig.rules === void 0) return;
1576
1664
  if (targetConfig.rules === void 0) targetConfig.rules = {};
1665
+ const effectivePlugins = globalPlugins ? {
1666
+ ...globalPlugins,
1667
+ ...eslintConfig.plugins
1668
+ } : eslintConfig.plugins;
1577
1669
  for (const [rule, config] of Object.entries(eslintConfig.rules)) {
1578
1670
  const normalizedConfig = normalizeSeverityValue(config);
1579
- if (!options?.merge) removePreviousOverrideRule(rule, eslintConfig, overrides);
1671
+ if (!options?.merge) {
1672
+ removePreviousOverrideRule(rule, eslintConfig, overrides);
1673
+ if (options?.jsPlugins) {
1674
+ const resolved = resolveJsPluginRuleName(rule, effectivePlugins);
1675
+ if (resolved !== rule) removePreviousOverrideRule(resolved, eslintConfig, overrides);
1676
+ }
1677
+ }
1580
1678
  if (allRules.includes(rule)) {
1581
1679
  if (!options?.withNursery && nurseryRules.includes(rule)) {
1582
1680
  options?.reporter?.markSkipped(rule, "nursery");
@@ -1595,8 +1693,9 @@ const transformRuleEntry = (eslintConfig, targetConfig, baseConfig, options, ove
1595
1693
  } else {
1596
1694
  if (options?.jsPlugins) {
1597
1695
  if (isOffValue(normalizedConfig)) {
1598
- if (eslintConfig.files === void 0) delete targetConfig.rules[rule];
1599
- else if (!isIgnoredPluginRule(rule)) targetConfig.rules[rule] = normalizedConfig;
1696
+ const resolvedRule = resolveJsPluginRuleName(rule, effectivePlugins);
1697
+ if (eslintConfig.files === void 0) delete targetConfig.rules[resolvedRule];
1698
+ else if (!isIgnoredPluginRule(rule)) targetConfig.rules[resolvedRule] = normalizedConfig;
1600
1699
  if (eslintConfig.files === void 0) {
1601
1700
  options?.reporter?.removeSkipped(rule, "js-plugins");
1602
1701
  options?.reporter?.removeSkipped(rule, "not-implemented");
@@ -1604,7 +1703,7 @@ const transformRuleEntry = (eslintConfig, targetConfig, baseConfig, options, ove
1604
1703
  }
1605
1704
  continue;
1606
1705
  }
1607
- if (!enableJsPluginRule(targetConfig, rule, normalizedConfig)) {
1706
+ if (!enableJsPluginRule(targetConfig, rule, normalizedConfig, effectivePlugins)) {
1608
1707
  const category = unsupportedRuleExplanations[rule] ? "unsupported" : "not-implemented";
1609
1708
  options?.reporter?.markSkipped(rule, category);
1610
1709
  }
@@ -1991,6 +2090,13 @@ const isSupportedSettingsKey = (key) => {
1991
2090
  return OXLINT_SUPPORTED_SETTINGS_KEYS.includes(key);
1992
2091
  };
1993
2092
  /**
2093
+ * Format a list of settings keys into a newline-separated, backtick-quoted
2094
+ * string for warning output.
2095
+ */
2096
+ const formatSettingsKeyList = (keys) => {
2097
+ return keys.map((key) => `\`${key}\``).join("\n");
2098
+ };
2099
+ /**
1994
2100
  * Transform ESLint settings to oxlint settings.
1995
2101
  *
1996
2102
  * Only processes settings for the base config (not overrides) since oxlint
@@ -2027,24 +2133,22 @@ const transformSettings = (eslintConfig, targetConfig, options) => {
2027
2133
  }
2028
2134
  let settingsValue = value;
2029
2135
  if (key === "react" && settingsValue.version === "detect") {
2030
- options?.reporter?.addWarning("react.version \"detect\" is not supported by oxlint. Please specify an explicit version (e.g., \"18.2.0\") in your oxlint config.");
2136
+ options?.reporter?.addWarning("react.version \"detect\" is not supported. Specify an explicit version (e.g., \"18.2.0\") in your oxlint config.");
2031
2137
  const { version: _, ...restReactSettings } = settingsValue;
2032
2138
  settingsValue = restReactSettings;
2033
2139
  }
2034
2140
  const unsupportedSubKeys = UNSUPPORTED_SETTINGS_SUB_KEYS[key];
2035
2141
  if (unsupportedSubKeys !== void 0) {
2036
- const strippedKeys = [];
2037
2142
  for (const subKey of unsupportedSubKeys) if (subKey in settingsValue) {
2038
- strippedKeys.push(`${key}.${subKey}`);
2143
+ skippedKeys.push(`${key}.${subKey}`);
2039
2144
  const { [subKey]: _, ...rest } = settingsValue;
2040
2145
  settingsValue = rest;
2041
2146
  }
2042
- if (strippedKeys.length > 0) options?.reporter?.addWarning(`Settings not migrated (not supported by oxlint): ${strippedKeys.join(", ")}.`);
2043
2147
  }
2044
2148
  if (Object.keys(settingsValue).length === 0) continue;
2045
2149
  filteredSettings[key] = settingsValue;
2046
2150
  }
2047
- if (skippedKeys.length > 0) options?.reporter?.addWarning(`Settings not migrated (not supported by oxlint): ${skippedKeys.join(", ")}.`);
2151
+ if (skippedKeys.length > 0) options?.reporter?.addWarning(`Settings not migrated (not supported by oxlint):\n${formatSettingsKeyList(skippedKeys)}`);
2048
2152
  if (Object.keys(filteredSettings).length === 0) return;
2049
2153
  if (targetConfig.settings === void 0) targetConfig.settings = {};
2050
2154
  if (options?.merge) deepMerge(targetConfig.settings, filteredSettings);
@@ -2059,10 +2163,10 @@ const transformSettings = (eslintConfig, targetConfig, options) => {
2059
2163
  */
2060
2164
  const warnSettingsInOverride = (eslintConfig, options) => {
2061
2165
  if (eslintConfig.settings !== void 0 && eslintConfig.settings !== null && Object.keys(eslintConfig.settings).length > 0) {
2062
- const settingsKeys = Object.keys(eslintConfig.settings).join(", ");
2063
- options?.reporter?.addWarning(`Settings found in config with 'files' pattern (${settingsKeys}). Oxlint does not support settings in overrides, these settings will be skipped.`);
2166
+ const settingsKeys = Object.keys(eslintConfig.settings);
2167
+ options?.reporter?.addWarning("Settings found under a 'files' pattern oxlint does not support settings in overrides and they will be skipped:\n" + formatSettingsKeyList(settingsKeys));
2064
2168
  }
2065
2169
  };
2066
2170
 
2067
2171
  //#endregion
2068
- export { preFixForJsPlugins as a, cleanUpOxlintConfig as c, transformRuleEntry as d, buildUnsupportedRuleExplanations as f, transformEnvAndGlobals as g, detectEnvironmentByGlobals as h, fixForJsPlugins as i, detectNeededRulesPlugins as l, rules_exports as m, warnSettingsInOverride as n, detectSameOverride as o, nurseryRules as p, processConfigFiles as r, transformIgnorePatterns as s, transformSettings as t, isOffValue as u };
2172
+ export { transformEnvAndGlobals as _, preFixForJsPlugins as a, cleanUpOxlintConfig as c, transformRuleEntry as d, buildUnsupportedRuleExplanations as f, detectEnvironmentByGlobals as g, typeAwareRules as h, fixForJsPlugins as i, detectNeededRulesPlugins as l, rules_exports as m, warnSettingsInOverride as n, detectSameOverride as o, nurseryRules as p, processConfigFiles as r, transformIgnorePatterns as s, transformSettings as t, isOffValue as u };