@oxlint/migrate 1.25.0 → 1.26.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
@@ -21,6 +21,7 @@ When no config file provided, the script searches for the default eslint config
21
21
  | `--merge` | \* merge eslint configuration with an existing .oxlintrc.json configuration |
22
22
  | `--type-aware` | Include type aware rules, which are supported with `oxlint --type-aware` |
23
23
  | `--with-nursery` | Include oxlint rules which are currently under development |
24
+ | `--js-plugins` | \*\* Include ESLint plugins via `jsPlugins` key. |
24
25
  | `--output-file <file>` | The oxlint configuration file where to eslint v9 rules will be written to, default: `.oxlintrc.json` |
25
26
  | `--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. |
26
27
 
@@ -28,6 +29,10 @@ When no config file provided, the script searches for the default eslint config
28
29
  Else we need to disable each rule `plugin/categories` combination, which is not covered by your eslint configuration.
29
30
  This behavior can change in the future.
30
31
 
32
+ \*\* WARNING: Tries to guess the plugin name. Should work with most of the plugin names.
33
+ Not every ESLint API is integrated with `oxlint`.
34
+ Tested ESLint Plugins with `oxlint` can be found in this [Oxc Discussion](https://github.com/oxc-project/oxc/discussions/14862).
35
+
31
36
  ### User Flow
32
37
 
33
38
  - Upgrade `oxlint` and `@oxlint/migrate` to the same version.
@@ -36,6 +36,9 @@ program.name("oxlint-migrate").version(packageJson.version).argument("[eslint-co
36
36
  ).option(
37
37
  "--type-aware",
38
38
  "Includes supported type-aware rules. Needs the same flag in `oxlint` to enable it."
39
+ ).option(
40
+ "--js-plugins",
41
+ "Tries to convert unsupported oxlint plugins with `jsPlugins`."
39
42
  ).action(async (filePath) => {
40
43
  const cliOptions = program.opts();
41
44
  const oxlintFilePath = path.join(cwd, cliOptions.outputFile);
@@ -44,7 +47,8 @@ program.name("oxlint-migrate").version(packageJson.version).argument("[eslint-co
44
47
  reporter,
45
48
  merge: !!cliOptions.merge,
46
49
  withNursery: !!cliOptions.withNursery,
47
- typeAware: !!cliOptions.typeAware
50
+ typeAware: !!cliOptions.typeAware,
51
+ jsPlugins: !!cliOptions.jsPlugins
48
52
  };
49
53
  if (cliOptions.replaceEslintComments) {
50
54
  await walkAndReplaceProjectFiles(
@@ -1,4 +1,4 @@
1
- const version = "1.25.0";
1
+ const version = "1.26.0";
2
2
  const packageJson = {
3
3
  version
4
4
  };
@@ -35,6 +35,7 @@ const pedanticRules = [
35
35
  "jsdoc/require-returns-description",
36
36
  "jsdoc/require-returns-type",
37
37
  "react/checked-requires-onchange-or-readonly",
38
+ "react/jsx-no-target-blank",
38
39
  "react/jsx-no-useless-fragment",
39
40
  "react/no-unescaped-entities",
40
41
  "react-hooks/rules-of-hooks",
@@ -42,6 +43,7 @@ const pedanticRules = [
42
43
  "@typescript-eslint/ban-types",
43
44
  "@typescript-eslint/no-misused-promises",
44
45
  "@typescript-eslint/no-confusing-void-expression",
46
+ "@typescript-eslint/no-deprecated",
45
47
  "@typescript-eslint/no-mixed-enums",
46
48
  "@typescript-eslint/no-unsafe-argument",
47
49
  "@typescript-eslint/no-unsafe-assignment",
@@ -216,6 +218,7 @@ const styleRules = [
216
218
  "react/no-set-state",
217
219
  "react/prefer-es6-class",
218
220
  "react/self-closing-comp",
221
+ "react/state-in-constructor",
219
222
  "@typescript-eslint/adjacent-overload-signatures",
220
223
  "@typescript-eslint/array-type",
221
224
  "@typescript-eslint/ban-tslint-comment",
@@ -595,7 +598,6 @@ const correctnessRules = [
595
598
  "react/forward-ref-uses-ref",
596
599
  "react/jsx-key",
597
600
  "react/jsx-no-duplicate-props",
598
- "react/jsx-no-target-blank",
599
601
  "react/jsx-no-undef",
600
602
  "react/jsx-props-no-spread-multi",
601
603
  "react/no-children-prop",
@@ -59,13 +59,13 @@ const buildConfig = (configs, oxlintConfig, options) => {
59
59
  transformRuleEntry(config, targetConfig, options);
60
60
  transformEnvAndGlobals(config, targetConfig, options);
61
61
  if ("files" in targetConfig) {
62
- detectNeededRulesPlugins(targetConfig, options);
62
+ detectNeededRulesPlugins(targetConfig);
63
63
  detectEnvironmentByGlobals(targetConfig);
64
64
  cleanUpOxlintConfig(targetConfig);
65
65
  }
66
66
  }
67
67
  oxlintConfig.overrides = overrides;
68
- detectNeededRulesPlugins(oxlintConfig, options);
68
+ detectNeededRulesPlugins(oxlintConfig);
69
69
  detectEnvironmentByGlobals(oxlintConfig);
70
70
  cleanUpOxlintConfig(oxlintConfig);
71
71
  return oxlintConfig;
@@ -0,0 +1,3 @@
1
+ import { Linter } from 'eslint';
2
+ import { OxlintConfigOrOverride } from './types.js';
3
+ export declare const enableJsPluginRule: (targetConfig: OxlintConfigOrOverride, rule: string, ruleEntry: Linter.RuleEntry | undefined) => boolean;
@@ -0,0 +1,52 @@
1
+ import { rulesPrefixesForPlugins } from "./constants.mjs";
2
+ const ignorePlugins = /* @__PURE__ */ new Set([
3
+ ...Object.keys(rulesPrefixesForPlugins),
4
+ ...Object.values(rulesPrefixesForPlugins),
5
+ "local"
6
+ // ToDo: handle local plugin rules
7
+ ]);
8
+ const guessEslintPluginName = (pluginName) => {
9
+ if (pluginName.startsWith("@")) {
10
+ const [scope, maybeSub] = pluginName.split("/");
11
+ if (maybeSub) {
12
+ return `${scope}/eslint-plugin-${maybeSub}`;
13
+ }
14
+ return `${scope}/eslint-plugin`;
15
+ }
16
+ return `eslint-plugin-${pluginName}`;
17
+ };
18
+ const extractPluginId = (ruleId) => {
19
+ const firstSlash = ruleId.indexOf("/");
20
+ if (firstSlash === -1) {
21
+ return;
22
+ }
23
+ if (ruleId.startsWith("@")) {
24
+ const secondSlash = ruleId.indexOf("/", firstSlash + 1);
25
+ if (secondSlash !== -1) {
26
+ return ruleId.substring(0, secondSlash);
27
+ }
28
+ }
29
+ return ruleId.substring(0, firstSlash);
30
+ };
31
+ const enableJsPluginRule = (targetConfig, rule, ruleEntry) => {
32
+ const pluginName = extractPluginId(rule);
33
+ if (pluginName === void 0) {
34
+ return false;
35
+ }
36
+ if (ignorePlugins.has(pluginName)) {
37
+ return false;
38
+ }
39
+ if (targetConfig.jsPlugins === void 0) {
40
+ targetConfig.jsPlugins = [];
41
+ }
42
+ const eslintPluginName = guessEslintPluginName(pluginName);
43
+ if (!targetConfig.jsPlugins.includes(eslintPluginName)) {
44
+ targetConfig.jsPlugins.push(eslintPluginName);
45
+ }
46
+ targetConfig.rules = targetConfig.rules || {};
47
+ targetConfig.rules[rule] = ruleEntry;
48
+ return true;
49
+ };
50
+ export {
51
+ enableJsPluginRule
52
+ };
@@ -3,7 +3,8 @@ const fixForAntfuEslintConfig = (config) => {
3
3
  return config.renamePlugins({
4
4
  ts: "@typescript-eslint",
5
5
  test: "vitest",
6
- next: "@next/next"
6
+ next: "@next/next",
7
+ style: "@stylistic"
7
8
  });
8
9
  }
9
10
  return config;
@@ -1,7 +1,7 @@
1
1
  import { Linter } from 'eslint';
2
2
  import { Options, OxlintConfig, OxlintConfigOrOverride } from './types.js';
3
3
  export declare const transformRuleEntry: (eslintConfig: Linter.Config, targetConfig: OxlintConfigOrOverride, options?: Options) => void;
4
- export declare const detectNeededRulesPlugins: (targetConfig: OxlintConfigOrOverride, options?: Options) => void;
4
+ export declare const detectNeededRulesPlugins: (targetConfig: OxlintConfigOrOverride) => void;
5
5
  export declare const cleanUpUselessOverridesPlugins: (config: OxlintConfig) => void;
6
6
  export declare const cleanUpUselessOverridesRules: (config: OxlintConfig) => void;
7
7
  export declare const cleanUpRulesWhichAreCoveredByCategory: (config: OxlintConfigOrOverride) => void;
@@ -1,6 +1,7 @@
1
1
  import * as rules from "./generated/rules.mjs";
2
2
  import { nurseryRules } from "./generated/rules.mjs";
3
3
  import { typescriptTypeAwareRules, rulesPrefixesForPlugins, typescriptRulesExtendEslintRules } from "./constants.mjs";
4
+ import { enableJsPluginRule } from "./jsPlugins.mjs";
4
5
  const allRules = Object.values(rules).flat();
5
6
  const isValueInSet = (value, validSet) => validSet.includes(value) || Array.isArray(value) && validSet.includes(value[0]);
6
7
  const isActiveValue = (value) => isValueInSet(value, ["error", "warn", 1, 2]);
@@ -62,13 +63,21 @@ const transformRuleEntry = (eslintConfig, targetConfig, options) => {
62
63
  targetConfig.rules[rule] = normalizeSeverityValue(config);
63
64
  }
64
65
  } else {
65
- if (isActiveValue(config)) {
66
+ if (options?.jsPlugins) {
67
+ if (isActiveValue(config) && !enableJsPluginRule(
68
+ targetConfig,
69
+ rule,
70
+ normalizeSeverityValue(config)
71
+ )) {
72
+ options?.reporter?.report(`unsupported rule: ${rule}`);
73
+ }
74
+ } else if (isActiveValue(config)) {
66
75
  options?.reporter?.report(`unsupported rule: ${rule}`);
67
76
  }
68
77
  }
69
78
  }
70
79
  };
71
- const detectNeededRulesPlugins = (targetConfig, options) => {
80
+ const detectNeededRulesPlugins = (targetConfig) => {
72
81
  if (targetConfig.rules === void 0) {
73
82
  return;
74
83
  }
@@ -79,18 +88,11 @@ const detectNeededRulesPlugins = (targetConfig, options) => {
79
88
  if (!rule.includes("/")) {
80
89
  continue;
81
90
  }
82
- let found = false;
83
91
  for (const [prefix, plugin] of Object.entries(rulesPrefixesForPlugins)) {
84
- if (rule.startsWith(`${prefix}/`)) {
85
- if (!targetConfig.plugins.includes(plugin)) {
86
- targetConfig.plugins.push(plugin);
87
- }
88
- found = true;
92
+ if (rule.startsWith(`${prefix}/`) && !targetConfig.plugins.includes(plugin)) {
93
+ targetConfig.plugins.push(plugin);
89
94
  }
90
95
  }
91
- if (!found) {
92
- options?.reporter?.report(`unsupported plugin for rule: ${rule}`);
93
- }
94
96
  }
95
97
  if ("files" in targetConfig && targetConfig.plugins.length === 0) {
96
98
  delete targetConfig.plugins;
@@ -1,5 +1,6 @@
1
1
  import { Linter } from 'eslint';
2
2
  type OxlintConfigPlugins = string[];
3
+ type OxlintConfigJsPlugins = string[];
3
4
  type OxlintConfigCategories = Record<string, unknown>;
4
5
  type OxlintConfigEnv = Record<string, boolean>;
5
6
  type OxlintConfigIgnorePatterns = string[];
@@ -8,6 +9,7 @@ export type OxlintConfigOverride = {
8
9
  env?: OxlintConfigEnv;
9
10
  globals?: Linter.Globals;
10
11
  plugins?: OxlintConfigPlugins;
12
+ jsPlugins?: OxlintConfigJsPlugins;
11
13
  categories?: OxlintConfigCategories;
12
14
  rules?: Partial<Linter.RulesRecord>;
13
15
  };
@@ -16,6 +18,7 @@ export type OxlintConfig = {
16
18
  env?: OxlintConfigEnv;
17
19
  globals?: Linter.Globals;
18
20
  plugins?: OxlintConfigPlugins;
21
+ jsPlugins?: OxlintConfigJsPlugins;
19
22
  categories?: OxlintConfigCategories;
20
23
  rules?: Partial<Linter.RulesRecord>;
21
24
  overrides?: OxlintConfigOverride[];
@@ -31,5 +34,6 @@ export type Options = {
31
34
  merge?: boolean;
32
35
  withNursery?: boolean;
33
36
  typeAware?: boolean;
37
+ jsPlugins?: boolean;
34
38
  };
35
39
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxlint/migrate",
3
- "version": "1.25.0",
3
+ "version": "1.26.0",
4
4
  "description": "Generates a `.oxlintrc.json` from a existing eslint flat config",
5
5
  "type": "module",
6
6
  "bin": {
@@ -69,7 +69,7 @@
69
69
  "jiti": "^2.4.2",
70
70
  "lint-staged": "^16.1.2",
71
71
  "next": "^16.0.0",
72
- "oxlint": "^1.25.0",
72
+ "oxlint": "^1.26.0",
73
73
  "oxlint-tsgolint": "^0.2.0",
74
74
  "prettier": "^3.6.1",
75
75
  "typescript": "^5.8.3",
@@ -84,11 +84,11 @@
84
84
  "dependencies": {
85
85
  "commander": "^14.0.0",
86
86
  "globals": "^16.3.0",
87
- "oxc-parser": "^0.95.0",
87
+ "oxc-parser": "^0.96.0",
88
88
  "tinyglobby": "^0.2.14"
89
89
  },
90
90
  "peerDependencies": {
91
91
  "jiti": "*"
92
92
  },
93
- "packageManager": "pnpm@10.19.0"
93
+ "packageManager": "pnpm@10.20.0"
94
94
  }