Skip to content

Commit 712417b

Browse files
authored
feat: receive plugin name via plugin meta data (#816)
1 parent 8fba5da commit 712417b

File tree

6 files changed

+229
-7
lines changed

6 files changed

+229
-7
lines changed

lib/generator.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ import { dirname, join, relative, resolve } from 'node:path';
33
import { getAllNamedOptions, hasOptions } from './rule-options.js';
44
import {
55
loadPlugin,
6-
getPluginPrefix,
6+
getPluginName,
77
getPluginRoot,
88
getPathWithExactFileNameCasing,
99
} from './package-json.js';
10+
import { getPluginPrefix } from './plugin-prefix.js';
1011
import { updateRulesList } from './rule-list.js';
1112
import { updateConfigsList } from './config-list.js';
1213
import { generateRuleHeaderLines } from './rule-doc-notices.js';
@@ -67,7 +68,9 @@ export async function generate(path: string, options?: GenerateOptions) {
6768
const { endOfLine } = context;
6869

6970
const plugin = await loadPlugin(path);
70-
const pluginPrefix = await getPluginPrefix(path);
71+
const pluginPrefix = getPluginPrefix(
72+
plugin.meta?.name ?? (await getPluginName(path)),
73+
);
7174
const configsToRules = await resolveConfigsToRules(plugin);
7275

7376
if (!plugin.rules) {

lib/package-json.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,17 @@ export async function loadPlugin(path: string): Promise<Plugin> {
119119
}
120120
}
121121

122-
export async function getPluginPrefix(path: string): Promise<string> {
122+
/**
123+
* Get the plugin name by reading the `name` field in the package.json file.
124+
*/
125+
export async function getPluginName(path: string): Promise<string> {
123126
const pluginPackageJson = await loadPackageJson(path);
124127
if (!pluginPackageJson.name) {
125128
throw new Error(
126129
"Could not find `name` field in ESLint plugin's package.json.",
127130
);
128131
}
129-
return pluginPackageJson.name.endsWith('/eslint-plugin')
130-
? pluginPackageJson.name.split('/')[0] // Scoped plugin name like @my-scope/eslint-plugin.
131-
: pluginPackageJson.name.replace('eslint-plugin-', ''); // Unscoped name like eslint-plugin-foo or scoped name like @my-scope/eslint-plugin-foo.
132+
return pluginPackageJson.name;
132133
}
133134

134135
/**

lib/plugin-prefix.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Construct the plugin prefix out of the plugin's name.
3+
*/
4+
export function getPluginPrefix(name: string): string {
5+
return name.endsWith('/eslint-plugin')
6+
? name.split('/')[0] // Scoped plugin name like @my-scope/eslint-plugin.
7+
: name.replace('eslint-plugin-', ''); // Unscoped name like eslint-plugin-foo or scoped name like @my-scope/eslint-plugin-foo.
8+
}

package-lock.json

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/lib/generate/__snapshots__/general-test.ts.snap

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,53 @@ exports[`generate (general) basic updates the documentation 4`] = `
5757
## Rule details
5858
details"
5959
`;
60+
61+
exports[`generate (general) plugin prefix uses \`plugin.meta.name\` as source for rule prefix 1`] = `
62+
"# eslint-plugin-test
63+
Description.
64+
## Rules
65+
<!-- begin auto-generated rules list -->
66+
67+
🔧 Automatically fixable by the [\`--fix\` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\\
68+
💡 Manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
69+
70+
| Name | Description | 🔧 | 💡 |
71+
| :----------------------------- | :--------------------- | :- | :- |
72+
| [no-bar](docs/rules/no-bar.md) | Description of no-bar. | 🔧 | |
73+
| [no-baz](docs/rules/no-baz.md) | Description of no-boz. | | |
74+
| [no-foo](docs/rules/no-foo.md) | Description of no-foo. | 🔧 | 💡 |
75+
76+
<!-- end auto-generated rules list -->
77+
more content."
78+
`;
79+
80+
exports[`generate (general) plugin prefix uses \`plugin.meta.name\` as source for rule prefix 2`] = `
81+
"# Description of no-foo (\`custom/no-foo\`)
82+
83+
🔧💡 This rule is automatically fixable by the [\`--fix\` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
84+
85+
<!-- end auto-generated rule header -->
86+
## Rule details
87+
details
88+
## Options
89+
optionToDoSomething1 - explanation
90+
optionToDoSomething2 - explanation"
91+
`;
92+
93+
exports[`generate (general) plugin prefix uses \`plugin.meta.name\` as source for rule prefix 3`] = `
94+
"# Description of no-bar (\`custom/no-bar\`)
95+
96+
🔧 This rule is automatically fixable by the [\`--fix\` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
97+
98+
<!-- end auto-generated rule header -->
99+
## Rule details
100+
details"
101+
`;
102+
103+
exports[`generate (general) plugin prefix uses \`plugin.meta.name\` as source for rule prefix 4`] = `
104+
"# Description of no-boz (\`custom/no-baz\`)
105+
106+
<!-- end auto-generated rule header -->
107+
## Rule details
108+
details"
109+
`;

test/lib/generate/general-test.ts

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,149 @@ describe('generate (general)', function () {
154154
expect(readFileSync('docs/rules/no-baz.md', 'utf8')).toMatchSnapshot();
155155
});
156156
});
157+
158+
describe('plugin prefix', function () {
159+
beforeEach(function () {
160+
mockFs({
161+
// package.json
162+
'package.json': JSON.stringify({
163+
name: 'eslint-plugin-test',
164+
exports: 'index.js',
165+
type: 'module',
166+
}),
167+
168+
// entry point
169+
'index.js': `
170+
export default {
171+
meta: {
172+
name: 'custom',
173+
},
174+
rules: {
175+
'no-foo': {
176+
meta: {
177+
docs: { description: 'Description of no-foo.' },
178+
fixable: 'code',
179+
hasSuggestions: true,
180+
schema: [
181+
{
182+
type: 'object',
183+
properties: {
184+
optionToDoSomething1: {
185+
type: 'boolean',
186+
default: false,
187+
},
188+
},
189+
additionalProperties: false,
190+
},
191+
{
192+
type: 'array',
193+
minItems: 1,
194+
maxItems: 1,
195+
items: [
196+
{
197+
type: 'object',
198+
properties: {
199+
optionToDoSomething2: {
200+
type: 'boolean',
201+
default: false,
202+
},
203+
},
204+
additionalProperties: false,
205+
},
206+
],
207+
},
208+
{
209+
type: 'array',
210+
},
211+
]
212+
},
213+
create(context) {}
214+
},
215+
'no-bar': {
216+
meta: {
217+
docs: { description: 'Description of no-bar.' },
218+
fixable: 'code',
219+
schema: [],
220+
},
221+
create(context) {},
222+
},
223+
'no-baz': {
224+
meta: { docs: { description: 'Description of no-boz.' }, },
225+
create(context) {}
226+
},
227+
},
228+
configs: {
229+
all: {
230+
rules: {
231+
'test/no-foo': 'error',
232+
'test/no-bar': 'error',
233+
// test/no-baz rule intentionally not in any config.
234+
}
235+
},
236+
recommended: {
237+
rules: {
238+
'test/no-foo': 'error',
239+
}
240+
},
241+
style: {
242+
rules: {
243+
'test/no-bar': 'error',
244+
}
245+
}
246+
}
247+
};`,
248+
249+
// README.md
250+
'README.md': outdent`
251+
# eslint-plugin-test
252+
Description.
253+
## Rules
254+
<!-- begin auto-generated rules list -->
255+
...
256+
<!-- end auto-generated rules list -->
257+
more content.
258+
`,
259+
260+
// RULE DOC FILES
261+
262+
'docs/rules/no-foo.md': outdent`
263+
# title (rule-name)
264+
description
265+
<!-- end auto-generated rule header -->
266+
## Rule details
267+
details
268+
## Options
269+
optionToDoSomething1 - explanation
270+
optionToDoSomething2 - explanation
271+
`, // rule doc with incorrect header content
272+
'docs/rules/no-bar.md': outdent`
273+
<!-- end auto-generated rule header -->
274+
## Rule details
275+
details
276+
`, // marker but no header content
277+
'docs/rules/no-baz.md': outdent`
278+
## Rule details
279+
details
280+
`, // content but missing marker
281+
282+
// Needed for some of the test infrastructure to work.
283+
node_modules: mockFs.load(PATH_NODE_MODULES),
284+
});
285+
});
286+
287+
afterEach(function () {
288+
mockFs.restore();
289+
jest.resetModules();
290+
});
291+
292+
it('uses `plugin.meta.name` as source for rule prefix', async function () {
293+
await generate('.');
294+
295+
expect(readFileSync('README.md', 'utf8')).toMatchSnapshot();
296+
297+
expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot();
298+
expect(readFileSync('docs/rules/no-bar.md', 'utf8')).toMatchSnapshot();
299+
expect(readFileSync('docs/rules/no-baz.md', 'utf8')).toMatchSnapshot();
300+
});
301+
});
157302
});

0 commit comments

Comments
 (0)