From f786b83688c87222d4ae51e04338ad9638c0b52e Mon Sep 17 00:00:00 2001 From: Sysix Date: Fri, 29 Nov 2024 21:08:14 +0100 Subject: [PATCH] feat: support overrides in buildFromOxlintConfig(File) --- src/build-from-oxlint-config/index.ts | 21 +++++++--- src/build-from-oxlint-config/overrides.ts | 48 +++++++++++++++++++++++ src/build-from-oxlint-config/plugins.ts | 8 +++- src/build-from-oxlint-config/rules.ts | 19 +++++++-- src/build-from-oxlint-config/types.d.ts | 12 ++++++ 5 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 src/build-from-oxlint-config/overrides.ts diff --git a/src/build-from-oxlint-config/index.ts b/src/build-from-oxlint-config/index.ts index c58e58a..4ed9643 100644 --- a/src/build-from-oxlint-config/index.ts +++ b/src/build-from-oxlint-config/index.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; -import type { Linter } from 'eslint'; import JSONCParser from 'jsonc-parser'; -import { +import type { + EslintPluginOxLintConfig, OxlintConfig, OxlintConfigCategories, OxlintConfigPlugins, @@ -13,6 +13,7 @@ import { readCategoriesFromConfig, } from './categories.js'; import { readPluginsFromConfig } from './plugins.js'; +import { handleOverridesScope, readOverridesFromConfig } from './overrides.js'; // default plugins, see const defaultPlugins: OxlintConfigPlugins = ['react', 'unicorn', 'typescript']; @@ -59,9 +60,10 @@ const getConfigContent = ( */ export const buildFromOxlintConfig = ( config: OxlintConfig -): Linter.Config>[] => { +): EslintPluginOxLintConfig[] => { const rules: Record = {}; const plugins = readPluginsFromConfig(config) ?? defaultPlugins; + const categories = readCategoriesFromConfig(config) ?? defaultCategories; // it is not a plugin but it is activated by default plugins.push('eslint'); @@ -81,15 +83,22 @@ export const buildFromOxlintConfig = ( const configRules = readRulesFromConfig(config); if (configRules !== undefined) { - handleRulesScope(configRules, rules); + handleRulesScope(configRules, rules, true); } - return [ + const overrides = readOverridesFromConfig(config); + const configs: EslintPluginOxLintConfig[] = [ { name: 'oxlint/from-oxlint-config', rules, }, ]; + + if (overrides !== undefined) { + handleOverridesScope(overrides, configs, categories); + } + + return configs; }; /** @@ -101,7 +110,7 @@ export const buildFromOxlintConfig = ( */ export const buildFromOxlintConfigFile = ( oxlintConfigFile: string -): Linter.Config>[] => { +): EslintPluginOxLintConfig[] => { const config = getConfigContent(oxlintConfigFile); // we could not parse form the file, do not build with default values diff --git a/src/build-from-oxlint-config/overrides.ts b/src/build-from-oxlint-config/overrides.ts new file mode 100644 index 0000000..ea65fa5 --- /dev/null +++ b/src/build-from-oxlint-config/overrides.ts @@ -0,0 +1,48 @@ +import { handleCategoriesScope } from './categories.js'; +import { readPluginsFromConfig } from './plugins.js'; +import { handleRulesScope, readRulesFromConfig } from './rules.js'; +import { + EslintPluginOxLintConfig, + OxlintConfig, + OxlintConfigCategories, + OxlintConfigOverride, +} from './types.js'; + +export const handleOverridesScope = ( + overrides: OxlintConfigOverride[], + configs: EslintPluginOxLintConfig[], + baseCategories?: OxlintConfigCategories +): void => { + for (const overrideIndex in overrides) { + const override = overrides[overrideIndex]; + const eslintRules: Record = {}; + const eslintConfig: EslintPluginOxLintConfig = { + name: `oxlint/from-oxlint-config-override-${overrideIndex}`, + }; + + // expect that oxlint `files` syntax is the same as eslint + eslintConfig.files = override.files; + + const plugins = readPluginsFromConfig(override); + if (baseCategories !== undefined && plugins !== undefined) { + handleCategoriesScope(plugins, baseCategories, eslintRules); + } + + const rules = readRulesFromConfig(override); + if (rules !== undefined) { + // ToDo -- when off, we should enable the default settings + handleRulesScope(rules, eslintRules, false); + } + + eslintConfig.rules = eslintRules; + configs.push(eslintConfig); + } +}; + +export const readOverridesFromConfig = ( + config: OxlintConfig +): OxlintConfigOverride[] | undefined => { + return 'overrides' in config && Array.isArray(config.overrides) + ? (config.overrides as OxlintConfigOverride[]) + : undefined; +}; diff --git a/src/build-from-oxlint-config/plugins.ts b/src/build-from-oxlint-config/plugins.ts index db18287..1181a1d 100644 --- a/src/build-from-oxlint-config/plugins.ts +++ b/src/build-from-oxlint-config/plugins.ts @@ -1,11 +1,15 @@ -import { OxlintConfig, OxlintConfigPlugins } from './types.js'; +import { + OxlintConfig, + OxlintConfigOverride, + OxlintConfigPlugins, +} from './types.js'; /** * tries to return the "plugins" section from the config. * it returns `undefined` when not found or invalid. */ export const readPluginsFromConfig = ( - config: OxlintConfig + config: OxlintConfig | OxlintConfigOverride ): OxlintConfigPlugins | undefined => { return 'plugins' in config && Array.isArray(config.plugins) ? (config.plugins as OxlintConfigPlugins) diff --git a/src/build-from-oxlint-config/rules.ts b/src/build-from-oxlint-config/rules.ts index 971534b..1041347 100644 --- a/src/build-from-oxlint-config/rules.ts +++ b/src/build-from-oxlint-config/rules.ts @@ -2,7 +2,11 @@ import { aliasPluginNames, reactHookRulesInsideReactScope, } from '../constants.js'; -import { OxlintConfig, OxlintConfigRules } from './types.js'; +import { + OxlintConfig, + OxlintConfigOverride, + OxlintConfigRules, +} from './types.js'; import configByCategory from '../generated/configs-by-category.js'; import { isObject } from './utils.js'; @@ -73,7 +77,8 @@ const isActiveValue = (value: unknown) => */ export const handleRulesScope = ( oxlintRules: OxlintConfigRules, - rules: Record + rules: Record, + disableRule: boolean ): void => { for (const rule in oxlintRules) { const eslintName = getEsLintRuleName(rule); @@ -90,7 +95,13 @@ export const handleRulesScope = ( rules[eslintName] = 'off'; } else if (rule in rules && isDeactivateValue(oxlintRules[rule])) { // rules extended by categories or plugins can be disabled manually - delete rules[eslintName]; + if (disableRule) { + delete rules[eslintName]; + } + // inside overrides we need to enable the rule again + else { + rules[eslintName] = 'warn'; + } } } }; @@ -100,7 +111,7 @@ export const handleRulesScope = ( * it returns `undefined` when not found or invalid. */ export const readRulesFromConfig = ( - config: OxlintConfig + config: OxlintConfig | OxlintConfigOverride ): OxlintConfigRules | undefined => { return 'rules' in config && isObject(config.rules) ? (config.rules as OxlintConfigRules) diff --git a/src/build-from-oxlint-config/types.d.ts b/src/build-from-oxlint-config/types.d.ts index e14fe6f..74083bb 100644 --- a/src/build-from-oxlint-config/types.d.ts +++ b/src/build-from-oxlint-config/types.d.ts @@ -1,9 +1,21 @@ +import type { Linter } from 'eslint'; + export type OxlintConfigPlugins = string[]; export type OxlintConfigCategories = Record; export type OxlintConfigRules = Record; +export type OxlintConfigOverride = { + files: string[]; + plugins?: OxlintConfigPlugins; + rules?: OxlintConfigRules; +}; + +export type EslintPluginOxLintConfig = Linter.Config< + Record +>; + export type OxlintConfig = { [key: string]: unknown; plugins?: OxlintConfigPlugins;