Skip to content

Commit ce55ede

Browse files
authored
feat(build): output rules for flat config in dts file (#182)
~On Hold at the moment.~ ~Waiting for a new prettier release, see prettier/prettier#1665 Solved my creating an object. ![grafik](https://github.com/user-attachments/assets/62189583-ea43-4fa7-9e9f-387018b47b94) Question: Should we export them too in a namespace? See: https://github.com/oxc-project/eslint-plugin-oxlint/blob/main/package.json#L7-L25
1 parent 13cc44f commit ce55ede

9 files changed

+324
-56
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
},
3737
"license": "MIT",
3838
"scripts": {
39-
"generate": "node --import @oxc-node/core/register ./scripts/generate.ts && pnpm format",
39+
"generate": "node --import @oxc-node/core/register ./scripts/generate.ts",
4040
"clone": "node --import @oxc-node/core/register ./scripts/sparse-clone.ts",
4141
"build": "vite build",
4242
"lint": "npx oxlint && npx eslint --flag unstable_ts_config",

scripts/__snapshots__/rules-generator.test.ts.snap

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ exports[`RulesGenerator > RulesGenerator generates rules correctly > byCategory
55
66
const styleRules = {
77
'rulename-with-mod': "off"
8-
} as const
8+
} as const;
99
1010
const correctnessRules = {
1111
'@typescript-eslint/rulename-without-mod': "off"
12-
} as const
12+
} as const;
1313
1414
export {
1515
styleRules,
@@ -22,11 +22,11 @@ exports[`RulesGenerator > RulesGenerator generates rules correctly > byScope 1`]
2222
2323
const eslintRules = {
2424
'rulename-with-mod': "off"
25-
} as const
25+
} as const;
2626
2727
const typescriptRules = {
2828
'@typescript-eslint/rulename-without-mod': "off"
29-
} as const
29+
} as const;
3030
3131
export {
3232
eslintRules,

scripts/config-generator.ts

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { writeFileSync } from 'node:fs';
2+
import path from 'node:path';
3+
import type { Rule } from './traverse-rules.js';
4+
import { camelCase, kebabCase, pascalCase } from 'scule';
5+
6+
const __dirname = new URL('.', import.meta.url).pathname;
7+
8+
export enum RulesGrouping {
9+
CATEGORY = 'category',
10+
SCOPE = 'scope',
11+
}
12+
13+
export type ResultMap = Map<string, string[]>;
14+
15+
export class ConfigGenerator {
16+
private oxlintVersion: string;
17+
private rulesGrouping: RulesGrouping;
18+
private rulesArray: Rule[];
19+
constructor(
20+
oxlintVersion: string,
21+
rulesArray: Rule[] = [],
22+
rulesGrouping: RulesGrouping = RulesGrouping.SCOPE
23+
) {
24+
this.oxlintVersion = oxlintVersion;
25+
this.rulesArray = rulesArray;
26+
this.rulesGrouping = rulesGrouping;
27+
}
28+
29+
public setRulesGrouping(rulesGrouping: RulesGrouping) {
30+
this.rulesGrouping = rulesGrouping;
31+
}
32+
33+
private groupItemsBy(
34+
rules: Rule[],
35+
rulesGrouping: RulesGrouping
36+
): Map<string, string[]> {
37+
const map = new Map<string, string[]>();
38+
for (const item of rules) {
39+
const key = item[rulesGrouping];
40+
const group = map.get(key) || [];
41+
group.push(item.value);
42+
map.set(key, group);
43+
}
44+
45+
return map;
46+
}
47+
48+
public async generateRulesCode() {
49+
console.log(
50+
`Generating config for ${this.oxlintVersion}, grouped by ${this.rulesGrouping}`
51+
);
52+
53+
const rulesGrouping = this.rulesGrouping;
54+
const rulesArray = this.rulesArray;
55+
56+
const rulesMap = this.groupItemsBy(rulesArray, rulesGrouping);
57+
const exportName = pascalCase(this.rulesGrouping);
58+
59+
const exportGrouping: string[] = [];
60+
let code =
61+
'// These rules are automatically generated by scripts/generate-rules.ts\n\n';
62+
63+
code += `import * as rules from "./rules-by-${this.rulesGrouping}.js";\n\n`;
64+
65+
for (const grouping of rulesMap.keys()) {
66+
exportGrouping.push(grouping);
67+
68+
code += `const ${camelCase(grouping)}Config = {\n`;
69+
70+
code += ` name: 'oxlint/${kebabCase(grouping)}',\n`;
71+
code += ` rules: rules.${camelCase(grouping)}Rules,`;
72+
code += '\n};\n\n';
73+
}
74+
75+
code += `const configBy${exportName} = {\n`;
76+
code += exportGrouping
77+
.map((grouping) => {
78+
return ` 'flat/${kebabCase(grouping)}': ${camelCase(grouping)}Config`;
79+
})
80+
.join(',\n');
81+
code += '\n}\n\n';
82+
83+
code += `export default configBy${exportName}`;
84+
85+
return code;
86+
}
87+
88+
public async generateRules() {
89+
const output = await this.generateRulesCode();
90+
writeFileSync(
91+
path.resolve(__dirname, '..', `src/configs-by-${this.rulesGrouping}.ts`),
92+
output
93+
);
94+
}
95+
}

scripts/generate.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { writeFileSync } from 'node:fs';
22
import { RulesGenerator, RulesGrouping } from './rules-generator.js';
3+
import { ConfigGenerator } from './config-generator.js';
34
import { traverseRules } from './traverse-rules.js';
45
import { getLatestVersionFromClonedRepo } from './oxlint-version.js';
56
import { TARGET_DIRECTORY, VERSION_PREFIX } from './constants.js';
@@ -24,13 +25,15 @@ if (!oxlintVersion) {
2425
);
2526
}
2627

27-
const generator = new RulesGenerator(oxlintVersion, successResultArray);
28-
29-
generator.setRulesGrouping(RulesGrouping.SCOPE);
30-
await generator.generateRules();
31-
generator.setRulesGrouping(RulesGrouping.CATEGORY);
32-
await generator.generateRules();
28+
const rulesGenerator = new RulesGenerator(oxlintVersion, successResultArray);
29+
const configGenerator = new ConfigGenerator(oxlintVersion, successResultArray);
3330

31+
[rulesGenerator, configGenerator].forEach(async (generator) => {
32+
generator.setRulesGrouping(RulesGrouping.SCOPE);
33+
await generator.generateRules();
34+
generator.setRulesGrouping(RulesGrouping.CATEGORY);
35+
await generator.generateRules();
36+
});
3437
// Update package.json version
3538
writeFileSync(
3639
'../package.json',

scripts/rules-generator.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { writeFileSync } from 'node:fs';
22
import path from 'node:path';
33
import type { Rule } from './traverse-rules.js';
4+
import { camelCase } from 'scule';
45

56
const __dirname = new URL('.', import.meta.url).pathname;
67

@@ -62,16 +63,14 @@ export class RulesGenerator {
6263
exportGrouping.push(grouping);
6364
const rules = rulesMap.get(grouping);
6465

65-
code += `const ${grouping.replace(/_(\w)/g, (_, c) =>
66-
c.toUpperCase()
67-
)}Rules = {\n`;
66+
code += `const ${camelCase(grouping)}Rules = {\n`;
6867

6968
code += rules
7069
?.map((rule) => {
7170
return ` '${rule.replace(/_/g, '-')}': "off"`;
7271
})
7372
.join(',\n');
74-
code += '\n} as const\n\n';
73+
code += '\n} as const;\n\n';
7574
}
7675

7776
code += 'export {\n';

src/configs-by-category.ts

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// These rules are automatically generated by scripts/generate-rules.ts
2+
3+
import * as rules from './rules-by-category.js';
4+
5+
const pedanticConfig = {
6+
name: 'oxlint/pedantic',
7+
rules: rules.pedanticRules,
8+
};
9+
10+
const nurseryConfig = {
11+
name: 'oxlint/nursery',
12+
rules: rules.nurseryRules,
13+
};
14+
15+
const restrictionConfig = {
16+
name: 'oxlint/restriction',
17+
rules: rules.restrictionRules,
18+
};
19+
20+
const styleConfig = {
21+
name: 'oxlint/style',
22+
rules: rules.styleRules,
23+
};
24+
25+
const conditionalFixConfig = {
26+
name: 'oxlint/conditional-fix',
27+
rules: rules.conditionalFixRules,
28+
};
29+
30+
const dangerousFixConfig = {
31+
name: 'oxlint/dangerous-fix',
32+
rules: rules.dangerousFixRules,
33+
};
34+
35+
const conditionalFixSuggestionConfig = {
36+
name: 'oxlint/conditional-fix-suggestion',
37+
rules: rules.conditionalFixSuggestionRules,
38+
};
39+
40+
const pendingConfig = {
41+
name: 'oxlint/pending',
42+
rules: rules.pendingRules,
43+
};
44+
45+
const correctnessConfig = {
46+
name: 'oxlint/correctness',
47+
rules: rules.correctnessRules,
48+
};
49+
50+
const perfConfig = {
51+
name: 'oxlint/perf',
52+
rules: rules.perfRules,
53+
};
54+
55+
const conditionalSuggestionFixConfig = {
56+
name: 'oxlint/conditional-suggestion-fix',
57+
rules: rules.conditionalSuggestionFixRules,
58+
};
59+
60+
const fixConfig = {
61+
name: 'oxlint/fix',
62+
rules: rules.fixRules,
63+
};
64+
65+
const suggestionConfig = {
66+
name: 'oxlint/suggestion',
67+
rules: rules.suggestionRules,
68+
};
69+
70+
const fixDangerousConfig = {
71+
name: 'oxlint/fix-dangerous',
72+
rules: rules.fixDangerousRules,
73+
};
74+
75+
const suspiciousConfig = {
76+
name: 'oxlint/suspicious',
77+
rules: rules.suspiciousRules,
78+
};
79+
80+
const conditionalSuggestionConfig = {
81+
name: 'oxlint/conditional-suggestion',
82+
rules: rules.conditionalSuggestionRules,
83+
};
84+
85+
const dangerousSuggestionConfig = {
86+
name: 'oxlint/dangerous-suggestion',
87+
rules: rules.dangerousSuggestionRules,
88+
};
89+
90+
const configByCategory = {
91+
'flat/pedantic': pedanticConfig,
92+
'flat/nursery': nurseryConfig,
93+
'flat/restriction': restrictionConfig,
94+
'flat/style': styleConfig,
95+
'flat/conditional-fix': conditionalFixConfig,
96+
'flat/dangerous-fix': dangerousFixConfig,
97+
'flat/conditional-fix-suggestion': conditionalFixSuggestionConfig,
98+
'flat/pending': pendingConfig,
99+
'flat/correctness': correctnessConfig,
100+
'flat/perf': perfConfig,
101+
'flat/conditional-suggestion-fix': conditionalSuggestionFixConfig,
102+
'flat/fix': fixConfig,
103+
'flat/suggestion': suggestionConfig,
104+
'flat/fix-dangerous': fixDangerousConfig,
105+
'flat/suspicious': suspiciousConfig,
106+
'flat/conditional-suggestion': conditionalSuggestionConfig,
107+
'flat/dangerous-suggestion': dangerousSuggestionConfig,
108+
};
109+
110+
export default configByCategory;

src/configs-by-scope.ts

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// These rules are automatically generated by scripts/generate-rules.ts
2+
3+
import * as rules from './rules-by-scope.js';
4+
5+
const eslintConfig = {
6+
name: 'oxlint/eslint',
7+
rules: rules.eslintRules,
8+
};
9+
10+
const typescriptConfig = {
11+
name: 'oxlint/typescript',
12+
rules: rules.typescriptRules,
13+
};
14+
15+
const importConfig = {
16+
name: 'oxlint/import',
17+
rules: rules.importRules,
18+
};
19+
20+
const jestConfig = {
21+
name: 'oxlint/jest',
22+
rules: rules.jestRules,
23+
};
24+
25+
const jsdocConfig = {
26+
name: 'oxlint/jsdoc',
27+
rules: rules.jsdocRules,
28+
};
29+
30+
const jsxA11yConfig = {
31+
name: 'oxlint/jsx-a11y',
32+
rules: rules.jsxA11yRules,
33+
};
34+
35+
const nextjsConfig = {
36+
name: 'oxlint/nextjs',
37+
rules: rules.nextjsRules,
38+
};
39+
40+
const nodeConfig = {
41+
name: 'oxlint/node',
42+
rules: rules.nodeRules,
43+
};
44+
45+
const promiseConfig = {
46+
name: 'oxlint/promise',
47+
rules: rules.promiseRules,
48+
};
49+
50+
const reactConfig = {
51+
name: 'oxlint/react',
52+
rules: rules.reactRules,
53+
};
54+
55+
const reactPerfConfig = {
56+
name: 'oxlint/react-perf',
57+
rules: rules.reactPerfRules,
58+
};
59+
60+
const securityConfig = {
61+
name: 'oxlint/security',
62+
rules: rules.securityRules,
63+
};
64+
65+
const treeShakingConfig = {
66+
name: 'oxlint/tree-shaking',
67+
rules: rules.treeShakingRules,
68+
};
69+
70+
const unicornConfig = {
71+
name: 'oxlint/unicorn',
72+
rules: rules.unicornRules,
73+
};
74+
75+
const vitestConfig = {
76+
name: 'oxlint/vitest',
77+
rules: rules.vitestRules,
78+
};
79+
80+
const configByScope = {
81+
'flat/eslint': eslintConfig,
82+
'flat/typescript': typescriptConfig,
83+
'flat/import': importConfig,
84+
'flat/jest': jestConfig,
85+
'flat/jsdoc': jsdocConfig,
86+
'flat/jsx-a11y': jsxA11yConfig,
87+
'flat/nextjs': nextjsConfig,
88+
'flat/node': nodeConfig,
89+
'flat/promise': promiseConfig,
90+
'flat/react': reactConfig,
91+
'flat/react-perf': reactPerfConfig,
92+
'flat/security': securityConfig,
93+
'flat/tree-shaking': treeShakingConfig,
94+
'flat/unicorn': unicornConfig,
95+
'flat/vitest': vitestConfig,
96+
};
97+
98+
export default configByScope;

0 commit comments

Comments
 (0)