diff --git a/README.md b/README.md index f46d0a76..d14cb822 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,15 @@ When no config file is provided, the script searches for the default ESLint conf ### Options -| Options | Description | -| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| `--merge` | \* merge ESLint configuration with an existing .oxlintrc.json configuration | -| `--type-aware` | Include type aware rules. These rules are supported with `oxlint --type-aware` and [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) | -| `--with-nursery` | Include oxlint rules which are currently under development | -| `--js-plugins [bool]` | \*\* Include ESLint plugins via `jsPlugins` key (enabled by default). Use `--js-plugins=false` to disable. | -| `--details` | List rules that could not be migrated to oxlint | -| `--output-file ` | The oxlint configuration file where ESLint v9 rules will be written to, default: `.oxlintrc.json` | -| `--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. | +| Options | Description | +| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--merge` | \* merge ESLint configuration with an existing .oxlintrc.json configuration | +| `--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. | +| `--with-nursery` | Include oxlint rules which are currently under development | +| `--js-plugins [bool]` | \*\* Include ESLint plugins via `jsPlugins` key (enabled by default). Use `--js-plugins=false` to disable. | +| `--details` | List rules that could not be migrated to oxlint | +| `--output-file ` | The oxlint configuration file where ESLint v9 rules will be written to, default: `.oxlintrc.json` | +| `--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. | \* WARNING: When some `categories` are enabled, this tools will enable more rules with the combination of `plugins`. Else we need to disable each rule `plugin/categories` combination, which is not covered by your ESLint configuration. diff --git a/bin/oxlint-migrate.ts b/bin/oxlint-migrate.ts index 2b08be2e..d572490a 100755 --- a/bin/oxlint-migrate.ts +++ b/bin/oxlint-migrate.ts @@ -88,7 +88,7 @@ program ) .option( '--type-aware', - 'Includes supported type-aware rules. Needs the same flag in `oxlint` to enable it.' + 'Includes supported type-aware rules. Needs the same flag in `oxlint` or the `typeAware` config option to enable it.' ) .option( '--js-plugins [bool]', diff --git a/integration_test/__snapshots__/many-extends.spec.ts.snap b/integration_test/__snapshots__/many-extends.spec.ts.snap index 86ab3b05..2466b84e 100644 --- a/integration_test/__snapshots__/many-extends.spec.ts.snap +++ b/integration_test/__snapshots__/many-extends.spec.ts.snap @@ -689,6 +689,9 @@ exports[`many-extends.spec.ts --type-aware > many-extends.spec.ts--type-aware 1` "builtin": true, "es2018": true, }, + "options": { + "typeAware": true, + }, "overrides": [ { "env": { diff --git a/integration_test/__snapshots__/pupeeteer.spec.ts.snap b/integration_test/__snapshots__/pupeeteer.spec.ts.snap index ffccb408..d364ee06 100644 --- a/integration_test/__snapshots__/pupeeteer.spec.ts.snap +++ b/integration_test/__snapshots__/pupeeteer.spec.ts.snap @@ -650,6 +650,9 @@ exports[`puppeteer --type-aware > puppeteer--type-aware 1`] = ` "examples/puppeteer-in-extension/out/**/*", "examples/puppeteer-in-extension/node_modules/**/*", ], + "options": { + "typeAware": true, + }, "overrides": [ { "files": [ diff --git a/integration_test/__snapshots__/typescript-simple.spec.ts.snap b/integration_test/__snapshots__/typescript-simple.spec.ts.snap index 266ab6ee..d60523a6 100644 --- a/integration_test/__snapshots__/typescript-simple.spec.ts.snap +++ b/integration_test/__snapshots__/typescript-simple.spec.ts.snap @@ -288,6 +288,9 @@ exports[`typescript-simple --type-aware > typescript-simple--type-aware 1`] = ` "env": { "builtin": true, }, + "options": { + "typeAware": true, + }, "overrides": [ { "files": [ diff --git a/integration_test/__snapshots__/typescript.spec.ts.snap b/integration_test/__snapshots__/typescript.spec.ts.snap index 78c6a55f..15c80c7f 100644 --- a/integration_test/__snapshots__/typescript.spec.ts.snap +++ b/integration_test/__snapshots__/typescript.spec.ts.snap @@ -827,6 +827,9 @@ exports[`typescript --type-aware > typescript--type-aware 1`] = ` "internal/**", "coverage/**", ], + "options": { + "typeAware": true, + }, "overrides": [ { "files": [ diff --git a/src/index.spec.ts b/src/index.spec.ts index 53c0ec45..718043c0 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -186,4 +186,134 @@ describe('main', () => { plugins: [], }); }); + + test('adds options.typeAware when type-aware rules are detected', async () => { + const result = await main( + [ + { + rules: { + '@typescript-eslint/no-floating-promises': 'error', + }, + }, + ], + undefined, + { typeAware: true } + ); + + expect(result).toStrictEqual({ + $schema: './node_modules/oxlint/configuration_schema.json', + categories: { + correctness: 'off', + }, + env: { + builtin: true, + }, + options: { + typeAware: true, + }, + plugins: ['typescript'], + rules: { + '@typescript-eslint/no-floating-promises': 'error', + }, + }); + }); + + test('does not add options.typeAware when type-aware flag is false', async () => { + const result = await main( + [ + { + rules: { + '@typescript-eslint/no-floating-promises': 'error', + }, + }, + ], + undefined, + { typeAware: false } + ); + + expect(result).toStrictEqual({ + $schema: './node_modules/oxlint/configuration_schema.json', + categories: { + correctness: 'off', + }, + env: { + builtin: true, + }, + plugins: [], + }); + }); + + test('does not add options.typeAware when no type-aware rules are detected', async () => { + const result = await main( + [ + { + rules: { + 'no-magic-numbers': 'error', + }, + }, + ], + undefined, + { typeAware: true } + ); + + expect(result).toStrictEqual({ + $schema: './node_modules/oxlint/configuration_schema.json', + categories: { + correctness: 'off', + }, + env: { + builtin: true, + }, + plugins: [], + rules: { + 'no-magic-numbers': 'error', + }, + }); + }); + + test('enables options.typeAware even if the type-aware rules are only in overrides', async () => { + const result = await main( + [ + { + rules: { + 'no-magic-numbers': 'error', + }, + }, + { + files: ['*.ts'], + rules: { + '@typescript-eslint/no-floating-promises': 'error', + }, + }, + ], + undefined, + { typeAware: true } + ); + + expect(result).toStrictEqual({ + $schema: './node_modules/oxlint/configuration_schema.json', + categories: { + correctness: 'off', + }, + env: { + builtin: true, + }, + options: { + typeAware: true, + }, + rules: { + 'no-magic-numbers': 'error', + }, + overrides: [ + { + files: ['*.ts'], + plugins: ['typescript'], + rules: { + '@typescript-eslint/no-floating-promises': 'error', + }, + }, + ], + plugins: [], + }); + }); }); diff --git a/src/index.ts b/src/index.ts index 8c0972b0..60fb24cc 100755 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import { detectNeededRulesPlugins, transformRuleEntry, } from './plugins_rules.js'; +import { typeAwareRules } from './generated/rules.js'; import { detectSameOverride } from './overrides.js'; import fixForJsPlugins from './js_plugin_fixes.js'; import { processConfigFiles } from './files.js'; @@ -131,6 +132,20 @@ const buildConfig = ( detectEnvironmentByGlobals(oxlintConfig); cleanUpOxlintConfig(oxlintConfig); + // If the --type-aware flag is used and the output config contains any + // type-aware rules, set the `typeAware` option to true. + if (options?.typeAware) { + const allOutputRules = [ + ...Object.keys(oxlintConfig.rules ?? {}), + ...(oxlintConfig.overrides ?? []).flatMap((o) => + Object.keys(o.rules ?? {}) + ), + ]; + if (allOutputRules.some((rule) => typeAwareRules.includes(rule))) { + oxlintConfig.options = { ...oxlintConfig.options, typeAware: true }; + } + } + return oxlintConfig; }; diff --git a/src/plugins_rules.ts b/src/plugins_rules.ts index f1d7f4ae..2340aad4 100644 --- a/src/plugins_rules.ts +++ b/src/plugins_rules.ts @@ -280,7 +280,6 @@ export const detectNeededRulesPlugins = ( for (const rule of Object.keys(targetConfig.rules)) { // eslint rule has no / prefix and is supported by oxlint out of the box - // ToDo: not every rule if (!rule.includes('/')) { continue; } diff --git a/src/types.ts b/src/types.ts index 9cbfc788..b699bf75 100644 --- a/src/types.ts +++ b/src/types.ts @@ -100,7 +100,6 @@ export namespace ESLint { /** Legacy overrides configuration */ overrides?: unknown[]; /** Index signature to accept any additional properties */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any; } } @@ -122,6 +121,11 @@ export type OxlintSettings = { [K in OxlintSupportedSettingsKey]?: Record; } & Record | undefined>; +export type OxlintOptions = { + typeAware?: boolean; + typeCheck?: boolean; +}; + export type OxlintConfigOverride = { files: string[]; env?: OxlintConfigEnv; @@ -143,8 +147,8 @@ export type OxlintConfig = { overrides?: OxlintConfigOverride[]; ignorePatterns?: OxlintConfigIgnorePatterns; settings?: OxlintSettings; + options?: OxlintOptions; }; - export type OxlintConfigOrOverride = OxlintConfig | OxlintConfigOverride; export type RuleSkippedCategory =