This commit is contained in:
Alex
2024-04-17 00:43:45 -04:00
parent 51455e3c21
commit fb50ede688
12 changed files with 401 additions and 297 deletions

View File

@ -1,18 +1,13 @@
import type { ESLint } from 'eslint';
import * as fs from 'node:fs';
import { resolve, basename, extname } from 'node:path';
function tryRequire(candidates: string[]) {
for (const candidate of candidates) {
try {
require(candidate);
return;
} catch {}
}
}
import { glob } from 'fast-glob';
import { parseModule } from 'esprima';
import query from 'esquery';
import type { Node, Property } from 'estree';
// https://github.com/gulpjs/interpret
tryRequire([
const transpilers = [
'esbin/register',
'esbuild-register',
'ts-node/register/transpile-only',
@ -20,20 +15,63 @@ tryRequire([
'sucrase/register',
'@babel/register',
'coffeescript/register',
]);
];
const folders = resolve(process.cwd(), 'eslint-local-rules');
const files = fs.readdirSync(folders);
function tryRequire() {
for (const candidate of transpilers) {
try {
require(candidate);
return;
} catch {}
}
}
const unwrapDefault = <T = any>(module: any): T => module.default ?? module;
const plugin: ESLint.Plugin = {
rules: {},
};
for (const file of files) {
const module = require(resolve(folders, file));
const unwrap = module.default ?? module;
const name = unwrap.name ?? basename(file, extname(file));
plugin.rules![name] = unwrap;
function hydrateESTreeNode(n: Node): any {
switch (n.type) {
case 'Literal':
return n.value;
case 'ArrayExpression':
return n.elements.filter(Boolean).map(hydrateESTreeNode);
default:
throw new Error(`Unsupported node type: ${n.type}`);
}
}
function parseConfigFile(js: string) {
const [node] = query(
parseModule(js),
'CallExpression[callee.name="extendConfig"] > ObjectExpression > Property[key.name="customRuleFiles"]',
);
return hydrateESTreeNode((node as Property).value);
}
function main() {
const rootDir = process.cwd();
const eslintConfigFile = ['.eslintrc.js', '.eslintrc.cjs']
.map(file => resolve(rootDir, file))
.find(file => fs.existsSync(file));
if (!eslintConfigFile) return;
const eslintConfig = fs.readFileSync(eslintConfigFile, 'utf8');
const customRuleFiles = parseConfigFile(eslintConfig);
if (!customRuleFiles?.length) return;
tryRequire();
for (const file of glob.sync(customRuleFiles)) {
const module = unwrapDefault(require(file));
const name = module.name ?? basename(file, extname(file));
plugin.rules![name] = module;
}
}
main();
export = plugin;