Skip to content

Commit

Permalink
feat: support overrides in buildFromOxlintConfig(File) (#265)
Browse files Browse the repository at this point in the history
closes #247
  • Loading branch information
Sysix authored Dec 25, 2024
1 parent 15c8143 commit 25d1a67
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 11 deletions.
76 changes: 75 additions & 1 deletion src/build-from-oxlint-config.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest';
import { assert, describe, expect, it } from 'vitest';
import {
buildFromOxlintConfig,
buildFromOxlintConfigFile,
Expand Down Expand Up @@ -214,6 +214,80 @@ describe('buildFromOxlintConfig', () => {
expect(configs[0].ignores).toStrictEqual(['./tests/.*ts']);
});
});

describe('overrides', () => {
it('supports simple files + rules overrides', () => {
const configs = buildFromOxlintConfig({
rules: {
eqeqeq: 'warn',
},
overrides: [
{
files: ['./*.ts'],
rules: {
'no-alert': 'error',
},
},
],
});

expect(configs.length).toBe(2);
assert(configs[0].rules !== undefined);
expect('eqeqeq' in configs[0].rules).toBe(true);
expect('no-alert' in configs[0].rules).toBe(false);

assert(configs[1].rules !== undefined);
expect('eqeqeq' in configs[1].rules).toBe(false);
expect('no-alert' in configs[1].rules).toBe(true);
});

it('supports simple files + plugins overrides', () => {
const configs = buildFromOxlintConfig({
rules: {
eqeqeq: 'warn',
},
overrides: [
{
files: ['./*.test.ts'],
plugins: ['vitest'],
},
],
});

expect(configs.length).toBe(2);
assert(configs[0].rules !== undefined);
expect('eqeqeq' in configs[0].rules).toBe(true);
expect('vitest/no-conditional-tests' in configs[0].rules).toBe(false);

assert(configs[1].rules !== undefined);
expect('eqeqeq' in configs[1].rules).toBe(false);
expect('vitest/no-conditional-tests' in configs[1].rules).toBe(true);
});

it(' rule in overrides', () => {
const configs = buildFromOxlintConfig({
rules: {
'no-debugger': 'warn',
},
overrides: [
{
files: ['./*.test.ts'],
rules: {
'no-debugger': 'off',
},
},
],
});

expect(configs.length).toBe(2);
assert(configs[0].rules !== undefined);
expect('no-debugger' in configs[0].rules).toBe(true);

console.log(configs[1].rules);
assert(configs[1].rules !== undefined);
expect('no-debugger' in configs[1].rules).toBe(false);
});
});
});

const createConfigFileAndBuildFromIt = (
Expand Down
17 changes: 11 additions & 6 deletions src/build-from-oxlint-config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
handleIgnorePatternsScope,
readIgnorePatternsFromConfig,
} from './ignore-patterns.js';
import { handleOverridesScope, readOverridesFromConfig } from './overrides.js';

// default plugins, see <https://oxc.rs/docs/guide/usage/linter/config#plugins>
const defaultPlugins: OxlintConfigPlugins = ['react', 'unicorn', 'typescript'];
Expand Down Expand Up @@ -66,6 +67,7 @@ export const buildFromOxlintConfig = (
): EslintPluginOxlintConfig[] => {
const rules: Record<string, 'off'> = {};
const plugins = readPluginsFromConfig(config) ?? defaultPlugins;
const categories = readCategoriesFromConfig(config) ?? defaultCategories;

// it is not a plugin but it is activated by default
plugins.push('eslint');
Expand All @@ -76,11 +78,7 @@ export const buildFromOxlintConfig = (
plugins.push('react-hooks');
}

handleCategoriesScope(
plugins,
readCategoriesFromConfig(config) ?? defaultCategories,
rules
);
handleCategoriesScope(plugins, categories, rules);

const configRules = readRulesFromConfig(config);

Expand All @@ -99,7 +97,14 @@ export const buildFromOxlintConfig = (
handleIgnorePatternsScope(ignorePatterns, baseConfig);
}

return [baseConfig];
const overrides = readOverridesFromConfig(config);
const configs = [baseConfig];

if (overrides !== undefined) {
handleOverridesScope(overrides, configs, categories);
}

return configs;
};

/**
Expand Down
47 changes: 47 additions & 0 deletions src/build-from-oxlint-config/overrides.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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<string, 'off'> = {};
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) {
handleRulesScope(rules, eslintRules);
}

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;
};
8 changes: 6 additions & 2 deletions src/build-from-oxlint-config/plugins.ts
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
8 changes: 6 additions & 2 deletions src/build-from-oxlint-config/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -100,7 +104,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)
Expand Down
6 changes: 6 additions & 0 deletions src/build-from-oxlint-config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ export type OxlintConfigIgnorePatterns = string[];

export type EslintPluginOxlintConfig = Linter.Config<Record<string, 'off'>>;

export type OxlintConfigOverride = {
files: string[];
plugins?: OxlintConfigPlugins;
rules?: OxlintConfigRules;
};

export type OxlintConfig = {
[key: string]: unknown;
plugins?: OxlintConfigPlugins;
Expand Down

0 comments on commit 25d1a67

Please sign in to comment.