Skip to content
This repository was archived by the owner on Mar 7, 2025. It is now read-only.

Commit b07d842

Browse files
DimavaShinigami92
andauthored
infra: async plugin importing (#182)
Co-authored-by: Shinigami <[email protected]>
1 parent 90acd74 commit b07d842

File tree

5 files changed

+80
-64
lines changed

5 files changed

+80
-64
lines changed

scripts/generate-rule-files/contracts/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ import type { Rule } from 'eslint';
22

33
export type MaybeArray<T> = T | T[];
44

5+
export type PluginRules = Record<string, Rule.RuleModule>;
6+
57
export interface Plugin {
68
name: string;
79
prefix?: string;
8-
rules: Record<string, Rule.RuleModule>;
10+
module: string;
11+
rules?: PluginRules;
912
}

scripts/generate-rule-files/index.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import type { Rule } from 'eslint';
44
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
55
import { join } from 'node:path';
66
import dedent from 'ts-dedent';
7-
import type { Plugin } from './contracts';
7+
import type { Plugin, PluginRules } from './contracts';
88
import { format } from './src/format';
99
import { JsDocBuilder } from './src/js-doc-builder';
10-
import { PLUGIN_REGISTRY } from './src/plugins-map';
10+
import { PLUGIN_REGISTRY, loadPlugin } from './src/plugins-map';
1111
import { RuleFile } from './src/rule-file';
1212

1313
/**
@@ -18,6 +18,12 @@ function generateRuleIndexFile(
1818
{ rules, name }: Plugin,
1919
failedRules: string[],
2020
): void {
21+
if (!rules) {
22+
throw new Error(
23+
`Plugin ${name} doesn't have any rules. Did you forget to load them?`,
24+
);
25+
}
26+
2127
const generatedRules: string[] = Object.keys(rules).filter(
2228
(ruleName) => !failedRules.includes(ruleName),
2329
);
@@ -82,7 +88,14 @@ async function generateRulesFiles(
8288
): Promise<{ failedRules: string[] }> {
8389
const failedRules: string[] = [];
8490

85-
const rules: Array<[string, Rule.RuleModule]> = Object.entries(plugin.rules);
91+
const pluginRules: PluginRules | undefined = plugin.rules;
92+
if (!pluginRules) {
93+
throw new Error(
94+
`Plugin ${plugin.name} doesn't have any rules. Did you forget to load them?`,
95+
);
96+
}
97+
98+
const rules: Array<[string, Rule.RuleModule]> = Object.entries(pluginRules);
8699
for (const [ruleName, rule] of rules) {
87100
logger.logUpdate(logger.colors.yellow(` Generating > ${ruleName}`));
88101

@@ -138,20 +151,23 @@ export async function run(options: RunOptions = {}): Promise<void> {
138151
const wantedPlugins: string[] = plugins ?? Object.keys(PLUGIN_REGISTRY);
139152

140153
for (const pluginName of wantedPlugins) {
141-
const plugin: Plugin = PLUGIN_REGISTRY[pluginName]!;
142-
154+
const plugin: Plugin | undefined = PLUGIN_REGISTRY[pluginName];
143155
if (!plugin) {
144156
throw new Error(`Plugin ${pluginName} doesn't exist.`);
145157
}
146158

147159
logger.info(`Generating ${plugin.name} rules.`);
160+
logger.logUpdate(
161+
logger.colors.yellow(` Loading plugin > ${plugin.module}`),
162+
);
163+
const loadedPlugin: Plugin = await loadPlugin(plugin);
148164

149165
const pluginDir: string = createPluginDirectory(
150166
pluginName,
151167
targetDirectory,
152168
);
153-
const { failedRules } = await generateRulesFiles(plugin, pluginDir);
169+
const { failedRules } = await generateRulesFiles(loadedPlugin, pluginDir);
154170

155-
generateRuleIndexFile(pluginDir, plugin, failedRules);
171+
generateRuleIndexFile(pluginDir, loadedPlugin, failedRules);
156172
}
157173
}
Lines changed: 32 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,97 @@
1-
import * as eslintPluginGraphQl from '@graphql-eslint/eslint-plugin';
2-
import * as eslint from 'eslint';
3-
import * as eslintPluginDeprecation from 'eslint-plugin-deprecation';
4-
// @ts-expect-error
5-
import eslintPluginEslintComments from 'eslint-plugin-eslint-comments';
6-
// @ts-expect-error
7-
import * as eslintPluginImport from 'eslint-plugin-import';
8-
// @ts-expect-error
9-
import eslintPluginJSDoc from 'eslint-plugin-jsdoc';
10-
import eslintPluginJsonc from 'eslint-plugin-jsonc';
11-
import * as eslintPluginMdx from 'eslint-plugin-mdx';
12-
// @ts-expect-error
13-
import eslintPluginPromise from 'eslint-plugin-promise';
14-
// @ts-expect-error
15-
import eslintPluginNode from 'eslint-plugin-node';
16-
// @ts-expect-error
17-
import eslintPluginN from 'eslint-plugin-n';
18-
import * as eslintPluginSonarJS from 'eslint-plugin-sonarjs';
19-
// @ts-expect-error
20-
import eslintPluginSpellcheck from 'eslint-plugin-spellcheck';
21-
// @ts-expect-error
22-
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
23-
// @ts-expect-error
24-
import eslintPluginVue from 'eslint-plugin-vue';
25-
// @ts-expect-error
26-
import eslintPluginVuePug from 'eslint-plugin-vue-pug';
27-
// @ts-expect-error
28-
import eslintPluginVueI18n from '@intlify/eslint-plugin-vue-i18n';
29-
import eslintPluginTypeScript from '@typescript-eslint/eslint-plugin';
30-
import type { Plugin } from '../contracts';
1+
import type { Plugin, PluginRules } from '../contracts';
312

323
/**
334
* Map of plugins for which the script will generate rule files.
345
*/
356
export const PLUGIN_REGISTRY: Readonly<Record<string, Plugin>> = {
367
deprecation: {
378
name: 'Deprecation',
38-
rules:
39-
// @ts-expect-error: throw error when plugin successfully updated their type defs
40-
eslintPluginDeprecation.rules as Plugin['rules'],
9+
module: 'eslint-plugin-deprecation',
4110
},
4211
eslint: {
4312
name: 'Eslint',
44-
rules: Object.fromEntries(new eslint.Linter().getRules().entries()),
13+
module: 'eslint',
4514
},
4615
'typescript-eslint': {
4716
name: 'TypeScript',
4817
prefix: '@typescript-eslint',
49-
rules: (eslintPluginTypeScript as unknown as Plugin).rules,
18+
module: '@typescript-eslint/eslint-plugin',
5019
},
5120
import: {
5221
name: 'Import',
53-
rules: (eslintPluginImport as Plugin).rules,
22+
module: 'eslint-plugin-import',
5423
},
5524
'eslint-comments': {
5625
name: 'EslintComments',
57-
rules: (eslintPluginEslintComments as Plugin).rules,
26+
module: 'eslint-plugin-eslint-comments',
5827
},
5928
'graphql-eslint': {
6029
name: 'GraphQL',
6130
prefix: '@graphql-eslint',
62-
rules: eslintPluginGraphQl.rules as Plugin['rules'],
31+
module: '@graphql-eslint/eslint-plugin',
6332
},
6433
jsdoc: {
6534
name: 'JSDoc',
6635
prefix: 'jsdoc',
67-
rules: (eslintPluginJSDoc as Plugin).rules,
36+
module: 'eslint-plugin-jsdoc',
6837
},
6938
jsonc: {
7039
name: 'Jsonc',
71-
rules:
72-
// @ts-expect-error: throw error when plugin successfully updated their type defs
73-
eslintPluginJsonc.rules as Plugin['rules'],
40+
module: 'eslint-plugin-jsonc',
7441
},
7542
mdx: {
7643
name: 'Mdx',
77-
rules: eslintPluginMdx.rules,
44+
module: 'eslint-plugin-mdx',
7845
},
7946
n: {
8047
name: 'N',
81-
rules: (eslintPluginN as Plugin).rules,
48+
module: 'eslint-plugin-n',
8249
},
8350
node: {
8451
name: 'Node',
85-
rules: (eslintPluginNode as Plugin).rules,
52+
module: 'eslint-plugin-node',
8653
},
8754
promise: {
8855
name: 'Promise',
89-
rules: (eslintPluginPromise as Plugin).rules,
56+
module: 'eslint-plugin-promise',
9057
},
9158
sonarjs: {
9259
name: 'SonarJS',
9360
prefix: 'sonarjs',
94-
rules: eslintPluginSonarJS.rules,
61+
module: 'eslint-plugin-sonarjs',
9562
},
9663
spellcheck: {
9764
name: 'Spellcheck',
98-
rules: (eslintPluginSpellcheck as Plugin).rules,
65+
module: 'eslint-plugin-spellcheck',
9966
},
10067
unicorn: {
10168
name: 'Unicorn',
102-
rules: (eslintPluginUnicorn as Plugin).rules,
69+
module: 'eslint-plugin-unicorn',
10370
},
10471
vue: {
10572
name: 'Vue',
106-
rules: (eslintPluginVue as Plugin).rules,
73+
module: 'eslint-plugin-vue',
10774
},
10875
'vue-i18n': {
10976
name: 'VueI18n',
11077
prefix: '@intlify/vue-i18n',
111-
rules: (eslintPluginVueI18n as Plugin).rules,
78+
module: '@intlify/eslint-plugin-vue-i18n',
11279
},
11380
'vue-pug': {
11481
name: 'VuePug',
115-
rules: (eslintPluginVuePug as Plugin).rules,
82+
module: 'eslint-plugin-vue-pug',
11683
},
11784
} as const;
85+
86+
export async function loadPlugin(plugin: Plugin): Promise<Plugin> {
87+
const mod: any = await import(plugin.module);
88+
const rules: PluginRules =
89+
plugin.module === 'eslint'
90+
? Object.fromEntries(
91+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
92+
new mod.Linter().getRules().entries(),
93+
)
94+
: // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
95+
mod.rules ?? mod.default.rules;
96+
return { ...plugin, rules };
97+
}

tests/generate-rule-files.test.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ describe('Rule File', () => {
4242
} as Rule.RuleModule;
4343

4444
const ruleFile: RuleFile = new RuleFile(
45-
{ name: 'my-plugin', rules: {} },
45+
{
46+
name: 'my-plugin',
47+
module: 'my-module/my-plugin',
48+
rules: {},
49+
},
4650
'my-plugin',
4751
'my-rule',
4852
rule,
@@ -67,7 +71,11 @@ describe('Rule File', () => {
6771
} as Rule.RuleModule;
6872

6973
const ruleFile: RuleFile = new RuleFile(
70-
{ name: 'my-plugin', rules: {} },
74+
{
75+
name: 'my-plugin',
76+
module: 'my-module/my-plugin',
77+
rules: {},
78+
},
7179
'my-plugin',
7280
'my-rule',
7381
rule,
@@ -93,7 +101,11 @@ describe('Rule File', () => {
93101
} as Rule.RuleModule;
94102

95103
const ruleFile: RuleFile = new RuleFile(
96-
{ name: 'my-plugin', rules: {} },
104+
{
105+
name: 'my-plugin',
106+
module: 'my-module/my-plugin',
107+
rules: {},
108+
},
97109
'my-plugin',
98110
'my-rule',
99111
rule,
@@ -117,7 +129,11 @@ describe('Rule File', () => {
117129
} as Rule.RuleModule;
118130

119131
const ruleFile: RuleFile = new RuleFile(
120-
{ name: 'my-plugin', rules: {} },
132+
{
133+
name: 'my-plugin',
134+
module: 'my-module/my-plugin',
135+
rules: {},
136+
},
121137
'my-plugin',
122138
'my-rule',
123139
rule,

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"compilerOptions": {
33
"target": "ES2020",
4+
"module": "ES2020",
45
"moduleResolution": "Node",
56
"rootDir": ".",
67
"strict": true,

0 commit comments

Comments
 (0)