Skip to content

Commit 40ac8cd

Browse files
committed
chore: allow automated rule creation script
1 parent a0ee0a6 commit 40ac8cd

File tree

4 files changed

+183
-46
lines changed

4 files changed

+183
-46
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"build": "rimraf ./dist && cross-env NODE_ENV=production babel ./src --out-dir ./dist --copy-files --source-maps --ignore ./src/bin/*.js --no-copy-ignored",
8585
"check-readme": "babel-node ./src/bin/generateReadme.js --check",
8686
"create-readme": "babel-node ./src/bin/generateReadme.js",
87+
"create-rule": "babel-node ./src/bin/generateRule.js",
8788
"install-offline": "npm install --prefer-offline --no-audit",
8889
"lint-fix": "eslint --report-unused-disable-directives --fix ./src ./test",
8990
"lint": "eslint --report-unused-disable-directives --ignore-pattern '!.ncurc.js' ./src ./test .ncurc.js",

src/bin/generateRule.js

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/* eslint-disable no-console -- CLI */
2+
import fs from 'fs/promises';
3+
import _ from 'lodash';
4+
5+
// Todo: Would ideally have prompts, e.g., to ask for whether type was problem/layout, etc.
6+
7+
const [, , ruleName] = process.argv;
8+
9+
(async () => {
10+
if (!ruleName) {
11+
console.error('Please supply a rule name');
12+
13+
return;
14+
}
15+
if ((/[A-Z]/u).test(ruleName)) {
16+
console.error('Please ensure the rule has no capital letters');
17+
18+
return;
19+
}
20+
21+
const ruleNamesPath = './test/rules/ruleNames.json';
22+
const ruleNames = JSON.parse(await fs.readFile(
23+
ruleNamesPath, 'utf8',
24+
));
25+
if (!ruleNames.includes(ruleName)) {
26+
ruleNames.push(ruleName);
27+
ruleNames.sort();
28+
}
29+
30+
await fs.writeFile(ruleNamesPath, JSON.stringify(ruleNames, null, 2) + '\n');
31+
console.log('ruleNames', ruleNames);
32+
33+
const ruleTemplate = `import iterateJsdoc from '../iterateJsdoc';
34+
35+
export default iterateJsdoc(({
36+
report,
37+
utils,
38+
}) => {
39+
// Rule here
40+
}, {
41+
iterateAllJsdocs: true,
42+
meta: {
43+
docs: {
44+
description: '',
45+
url: 'https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-rules-${ruleName}',
46+
},
47+
schema: [
48+
{
49+
additionalProperies: false,
50+
properties: {
51+
// Option properties here (or remove the object)
52+
},
53+
type: 'object',
54+
},
55+
],
56+
type: 'suggestion',
57+
},
58+
});
59+
`;
60+
61+
const camelCasedRuleName = _.camelCase(ruleName);
62+
63+
await fs.writeFile(`./src/rules/${camelCasedRuleName}.js`, ruleTemplate);
64+
65+
const ruleTestTemplate = `export default {
66+
invalid: [
67+
{
68+
code: \`\`,
69+
errors: [{
70+
message: '',
71+
}],
72+
},
73+
],
74+
valid: [
75+
{
76+
code: \`\`,
77+
},
78+
],
79+
};
80+
`;
81+
82+
await fs.writeFile(`./test/rules/assertions/${camelCasedRuleName}.js`, ruleTestTemplate);
83+
84+
const ruleReadmeTemplate = `### \`${ruleName}\`
85+
86+
|||
87+
|---|---|
88+
|Context|everywhere|
89+
|Tags|\`\`|
90+
|Recommended|false|
91+
|Settings||
92+
|Options||
93+
94+
<!-- assertions ${camelCasedRuleName} -->
95+
`;
96+
97+
await fs.writeFile(`./.README/rules/${ruleName}.md`, ruleReadmeTemplate);
98+
99+
const readmePath = './.README/README.md';
100+
101+
const offsets = [];
102+
let readme = await fs.readFile(readmePath, 'utf8');
103+
104+
const readmeNewRuleLine = `{"gitdown": "include", "file": "./rules/${ruleName}.md"}`;
105+
106+
readme.replace(
107+
/\{"gitdown": "include", "file": ".\/rules\/(?<oldRule>[^.]*).md"\}/gu,
108+
(__, n1, offset, str, {oldRule}) => {
109+
offsets.push({
110+
offset,
111+
oldRule,
112+
});
113+
},
114+
);
115+
116+
offsets.sort(({oldRule}, {oldRule: oldRuleB}) => {
117+
// eslint-disable-next-line no-extra-parens
118+
return oldRule < oldRuleB ? -1 : (oldRule > oldRuleB ? 1 : 0);
119+
});
120+
const item = offsets.find(({oldRule}) => {
121+
return ruleName < oldRule;
122+
});
123+
if (item) {
124+
readme = readme.slice(0, item.offset) + readmeNewRuleLine + '\n' + readme.slice(item.offset);
125+
} else {
126+
readme += `\n${readmeNewRuleLine}`;
127+
}
128+
129+
await fs.writeFile(readmePath, readme);
130+
131+
await import('./generateReadme.js');
132+
})();

test/rules/index.js

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,54 +4,11 @@ import {
44
import _ from 'lodash';
55
import config from '../../src';
66

7+
import ruleNames from './ruleNames.json';
8+
79
const ruleTester = new RuleTester();
810

9-
(process.env.npm_config_rule ? process.env.npm_config_rule.split(',') : [
10-
'check-access',
11-
'check-alignment',
12-
'check-line-alignment',
13-
'check-examples',
14-
'check-indentation',
15-
'check-param-names',
16-
'check-property-names',
17-
'check-syntax',
18-
'check-tag-names',
19-
'check-types',
20-
'check-values',
21-
'empty-tags',
22-
'implements-on-classes',
23-
'match-description',
24-
'newline-after-description',
25-
'no-bad-blocks',
26-
'no-defaults',
27-
'no-missing-syntax',
28-
'no-restricted-syntax',
29-
'no-types',
30-
'no-undefined-types',
31-
'require-asterisk-prefix',
32-
'require-description',
33-
'require-description-complete-sentence',
34-
'require-example',
35-
'require-file-overview',
36-
'require-hyphen-before-param-description',
37-
'require-jsdoc',
38-
'require-param',
39-
'require-param-description',
40-
'require-param-name',
41-
'require-param-type',
42-
'require-property',
43-
'require-property-description',
44-
'require-property-name',
45-
'require-property-type',
46-
'require-returns',
47-
'require-returns-check',
48-
'require-returns-description',
49-
'require-returns-type',
50-
'require-throws',
51-
'require-yields',
52-
'require-yields-check',
53-
'valid-types',
54-
]).forEach((ruleName) => {
11+
(process.env.npm_config_rule ? process.env.npm_config_rule.split(',') : ruleNames).forEach((ruleName) => {
5512
const rule = config.rules[ruleName];
5613

5714
const parserOptions = {

test/rules/ruleNames.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
[
2+
"check-access",
3+
"check-alignment",
4+
"check-examples",
5+
"check-indentation",
6+
"check-line-alignment",
7+
"check-param-names",
8+
"check-property-names",
9+
"check-syntax",
10+
"check-tag-names",
11+
"check-types",
12+
"check-values",
13+
"empty-tags",
14+
"implements-on-classes",
15+
"match-description",
16+
"newline-after-description",
17+
"no-bad-blocks",
18+
"no-defaults",
19+
"no-missing-syntax",
20+
"no-restricted-syntax",
21+
"no-types",
22+
"no-undefined-types",
23+
"require-asterisk-prefix",
24+
"require-description",
25+
"require-description-complete-sentence",
26+
"require-example",
27+
"require-file-overview",
28+
"require-hyphen-before-param-description",
29+
"require-jsdoc",
30+
"require-param",
31+
"require-param-description",
32+
"require-param-name",
33+
"require-param-type",
34+
"require-property",
35+
"require-property-description",
36+
"require-property-name",
37+
"require-property-type",
38+
"require-returns",
39+
"require-returns-check",
40+
"require-returns-description",
41+
"require-returns-type",
42+
"require-throws",
43+
"require-yields",
44+
"require-yields-check",
45+
"tag-lines",
46+
"valid-types"
47+
]

0 commit comments

Comments
 (0)