Skip to content

Commit

Permalink
feat(build): output rules for flat config in dts file (#182)
Browse files Browse the repository at this point in the history
~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
  • Loading branch information
Sysix authored Oct 15, 2024
1 parent 13cc44f commit ce55ede
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 56 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
},
"license": "MIT",
"scripts": {
"generate": "node --import @oxc-node/core/register ./scripts/generate.ts && pnpm format",
"generate": "node --import @oxc-node/core/register ./scripts/generate.ts",
"clone": "node --import @oxc-node/core/register ./scripts/sparse-clone.ts",
"build": "vite build",
"lint": "npx oxlint && npx eslint --flag unstable_ts_config",
Expand Down
8 changes: 4 additions & 4 deletions scripts/__snapshots__/rules-generator.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ exports[`RulesGenerator > RulesGenerator generates rules correctly > byCategory
const styleRules = {
'rulename-with-mod': "off"
} as const
} as const;
const correctnessRules = {
'@typescript-eslint/rulename-without-mod': "off"
} as const
} as const;
export {
styleRules,
Expand All @@ -22,11 +22,11 @@ exports[`RulesGenerator > RulesGenerator generates rules correctly > byScope 1`]
const eslintRules = {
'rulename-with-mod': "off"
} as const
} as const;
const typescriptRules = {
'@typescript-eslint/rulename-without-mod': "off"
} as const
} as const;
export {
eslintRules,
Expand Down
95 changes: 95 additions & 0 deletions scripts/config-generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { writeFileSync } from 'node:fs';
import path from 'node:path';
import type { Rule } from './traverse-rules.js';
import { camelCase, kebabCase, pascalCase } from 'scule';

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

export enum RulesGrouping {
CATEGORY = 'category',
SCOPE = 'scope',
}

export type ResultMap = Map<string, string[]>;

export class ConfigGenerator {
private oxlintVersion: string;
private rulesGrouping: RulesGrouping;
private rulesArray: Rule[];
constructor(
oxlintVersion: string,
rulesArray: Rule[] = [],
rulesGrouping: RulesGrouping = RulesGrouping.SCOPE
) {
this.oxlintVersion = oxlintVersion;
this.rulesArray = rulesArray;
this.rulesGrouping = rulesGrouping;
}

public setRulesGrouping(rulesGrouping: RulesGrouping) {
this.rulesGrouping = rulesGrouping;
}

private groupItemsBy(
rules: Rule[],
rulesGrouping: RulesGrouping
): Map<string, string[]> {
const map = new Map<string, string[]>();
for (const item of rules) {
const key = item[rulesGrouping];
const group = map.get(key) || [];
group.push(item.value);
map.set(key, group);
}

return map;
}

public async generateRulesCode() {
console.log(
`Generating config for ${this.oxlintVersion}, grouped by ${this.rulesGrouping}`
);

const rulesGrouping = this.rulesGrouping;
const rulesArray = this.rulesArray;

const rulesMap = this.groupItemsBy(rulesArray, rulesGrouping);
const exportName = pascalCase(this.rulesGrouping);

const exportGrouping: string[] = [];
let code =
'// These rules are automatically generated by scripts/generate-rules.ts\n\n';

code += `import * as rules from "./rules-by-${this.rulesGrouping}.js";\n\n`;

for (const grouping of rulesMap.keys()) {
exportGrouping.push(grouping);

code += `const ${camelCase(grouping)}Config = {\n`;

code += ` name: 'oxlint/${kebabCase(grouping)}',\n`;
code += ` rules: rules.${camelCase(grouping)}Rules,`;
code += '\n};\n\n';
}

code += `const configBy${exportName} = {\n`;
code += exportGrouping
.map((grouping) => {
return ` 'flat/${kebabCase(grouping)}': ${camelCase(grouping)}Config`;
})
.join(',\n');
code += '\n}\n\n';

code += `export default configBy${exportName}`;

return code;
}

public async generateRules() {
const output = await this.generateRulesCode();
writeFileSync(
path.resolve(__dirname, '..', `src/configs-by-${this.rulesGrouping}.ts`),
output
);
}
}
15 changes: 9 additions & 6 deletions scripts/generate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { writeFileSync } from 'node:fs';
import { RulesGenerator, RulesGrouping } from './rules-generator.js';
import { ConfigGenerator } from './config-generator.js';
import { traverseRules } from './traverse-rules.js';
import { getLatestVersionFromClonedRepo } from './oxlint-version.js';
import { TARGET_DIRECTORY, VERSION_PREFIX } from './constants.js';
Expand All @@ -24,13 +25,15 @@ if (!oxlintVersion) {
);
}

const generator = new RulesGenerator(oxlintVersion, successResultArray);

generator.setRulesGrouping(RulesGrouping.SCOPE);
await generator.generateRules();
generator.setRulesGrouping(RulesGrouping.CATEGORY);
await generator.generateRules();
const rulesGenerator = new RulesGenerator(oxlintVersion, successResultArray);
const configGenerator = new ConfigGenerator(oxlintVersion, successResultArray);

[rulesGenerator, configGenerator].forEach(async (generator) => {
generator.setRulesGrouping(RulesGrouping.SCOPE);
await generator.generateRules();
generator.setRulesGrouping(RulesGrouping.CATEGORY);
await generator.generateRules();
});
// Update package.json version
writeFileSync(
'../package.json',
Expand Down
7 changes: 3 additions & 4 deletions scripts/rules-generator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { writeFileSync } from 'node:fs';
import path from 'node:path';
import type { Rule } from './traverse-rules.js';
import { camelCase } from 'scule';

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

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

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

code += rules
?.map((rule) => {
return ` '${rule.replace(/_/g, '-')}': "off"`;
})
.join(',\n');
code += '\n} as const\n\n';
code += '\n} as const;\n\n';
}

code += 'export {\n';
Expand Down
110 changes: 110 additions & 0 deletions src/configs-by-category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// These rules are automatically generated by scripts/generate-rules.ts

import * as rules from './rules-by-category.js';

const pedanticConfig = {
name: 'oxlint/pedantic',
rules: rules.pedanticRules,
};

const nurseryConfig = {
name: 'oxlint/nursery',
rules: rules.nurseryRules,
};

const restrictionConfig = {
name: 'oxlint/restriction',
rules: rules.restrictionRules,
};

const styleConfig = {
name: 'oxlint/style',
rules: rules.styleRules,
};

const conditionalFixConfig = {
name: 'oxlint/conditional-fix',
rules: rules.conditionalFixRules,
};

const dangerousFixConfig = {
name: 'oxlint/dangerous-fix',
rules: rules.dangerousFixRules,
};

const conditionalFixSuggestionConfig = {
name: 'oxlint/conditional-fix-suggestion',
rules: rules.conditionalFixSuggestionRules,
};

const pendingConfig = {
name: 'oxlint/pending',
rules: rules.pendingRules,
};

const correctnessConfig = {
name: 'oxlint/correctness',
rules: rules.correctnessRules,
};

const perfConfig = {
name: 'oxlint/perf',
rules: rules.perfRules,
};

const conditionalSuggestionFixConfig = {
name: 'oxlint/conditional-suggestion-fix',
rules: rules.conditionalSuggestionFixRules,
};

const fixConfig = {
name: 'oxlint/fix',
rules: rules.fixRules,
};

const suggestionConfig = {
name: 'oxlint/suggestion',
rules: rules.suggestionRules,
};

const fixDangerousConfig = {
name: 'oxlint/fix-dangerous',
rules: rules.fixDangerousRules,
};

const suspiciousConfig = {
name: 'oxlint/suspicious',
rules: rules.suspiciousRules,
};

const conditionalSuggestionConfig = {
name: 'oxlint/conditional-suggestion',
rules: rules.conditionalSuggestionRules,
};

const dangerousSuggestionConfig = {
name: 'oxlint/dangerous-suggestion',
rules: rules.dangerousSuggestionRules,
};

const configByCategory = {
'flat/pedantic': pedanticConfig,
'flat/nursery': nurseryConfig,
'flat/restriction': restrictionConfig,
'flat/style': styleConfig,
'flat/conditional-fix': conditionalFixConfig,
'flat/dangerous-fix': dangerousFixConfig,
'flat/conditional-fix-suggestion': conditionalFixSuggestionConfig,
'flat/pending': pendingConfig,
'flat/correctness': correctnessConfig,
'flat/perf': perfConfig,
'flat/conditional-suggestion-fix': conditionalSuggestionFixConfig,
'flat/fix': fixConfig,
'flat/suggestion': suggestionConfig,
'flat/fix-dangerous': fixDangerousConfig,
'flat/suspicious': suspiciousConfig,
'flat/conditional-suggestion': conditionalSuggestionConfig,
'flat/dangerous-suggestion': dangerousSuggestionConfig,
};

export default configByCategory;
98 changes: 98 additions & 0 deletions src/configs-by-scope.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// These rules are automatically generated by scripts/generate-rules.ts

import * as rules from './rules-by-scope.js';

const eslintConfig = {
name: 'oxlint/eslint',
rules: rules.eslintRules,
};

const typescriptConfig = {
name: 'oxlint/typescript',
rules: rules.typescriptRules,
};

const importConfig = {
name: 'oxlint/import',
rules: rules.importRules,
};

const jestConfig = {
name: 'oxlint/jest',
rules: rules.jestRules,
};

const jsdocConfig = {
name: 'oxlint/jsdoc',
rules: rules.jsdocRules,
};

const jsxA11yConfig = {
name: 'oxlint/jsx-a11y',
rules: rules.jsxA11yRules,
};

const nextjsConfig = {
name: 'oxlint/nextjs',
rules: rules.nextjsRules,
};

const nodeConfig = {
name: 'oxlint/node',
rules: rules.nodeRules,
};

const promiseConfig = {
name: 'oxlint/promise',
rules: rules.promiseRules,
};

const reactConfig = {
name: 'oxlint/react',
rules: rules.reactRules,
};

const reactPerfConfig = {
name: 'oxlint/react-perf',
rules: rules.reactPerfRules,
};

const securityConfig = {
name: 'oxlint/security',
rules: rules.securityRules,
};

const treeShakingConfig = {
name: 'oxlint/tree-shaking',
rules: rules.treeShakingRules,
};

const unicornConfig = {
name: 'oxlint/unicorn',
rules: rules.unicornRules,
};

const vitestConfig = {
name: 'oxlint/vitest',
rules: rules.vitestRules,
};

const configByScope = {
'flat/eslint': eslintConfig,
'flat/typescript': typescriptConfig,
'flat/import': importConfig,
'flat/jest': jestConfig,
'flat/jsdoc': jsdocConfig,
'flat/jsx-a11y': jsxA11yConfig,
'flat/nextjs': nextjsConfig,
'flat/node': nodeConfig,
'flat/promise': promiseConfig,
'flat/react': reactConfig,
'flat/react-perf': reactPerfConfig,
'flat/security': securityConfig,
'flat/tree-shaking': treeShakingConfig,
'flat/unicorn': unicornConfig,
'flat/vitest': vitestConfig,
};

export default configByScope;
Loading

0 comments on commit ce55ede

Please sign in to comment.