Skip to content

Commit

Permalink
Merge pull request #240 from JoshuaKGoldberg/init-rule-docs
Browse files Browse the repository at this point in the history
Fixes #79
  • Loading branch information
bmish authored Nov 18, 2022
2 parents b6af114 + 6067f1d commit 50167bc
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 16 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ There's also an optional path argument if you need to point the CLI to an ESLint
| `--config-emoji` | Custom emoji to use for a config. Format is `config-name,emoji`. Default emojis are provided for [common configs](./lib/emojis.ts). To remove a default emoji and rely on a [badge](#badge) instead, provide the config name without an emoji. Option can be repeated. |
| `--ignore-config` | Config to ignore from being displayed. Often used for an `all` config. Option can be repeated. |
| `--ignore-deprecated-rules` | Whether to ignore deprecated rules from being checked, displayed, or updated (default: `false`). |
| `--init-rule-docs` | Whether to create rule doc files if they don't yet exist (default: `false`). |
| `--path-rule-doc` | Path to markdown file for each rule doc. Use `{name}` placeholder for the rule name (default: `docs/rules/{name}.md`). |
| `--path-rule-list` | Path to markdown file with a rules section where the rules table list should live (default: `README.md`). |
| `--rule-doc-notices` | Ordered, comma-separated list of notices to display in rule doc. Non-applicable notices will be hidden. Choices: `configs`, `deprecated`, `fixable` (off by default), `fixableAndHasSuggestions`, `hasSuggestions` (off by default), `options` (off by default), `requiresTypeChecking`, `type` (off by default). Default: `deprecated,configs,fixableAndHasSuggestions,requiresTypeChecking`. |
Expand Down
8 changes: 8 additions & 0 deletions lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ async function loadConfigFileOptions() {
configEmoji: schemaStringArray,
ignoreConfig: schemaStringArray,
ignoreDeprecatedRules: { type: 'boolean' },
initRuleDocs: { type: 'boolean' },
pathRuleDoc: { type: 'string' },
pathRuleList: { type: 'string' },
ruleDocNotices: { type: 'string' },
Expand Down Expand Up @@ -133,6 +134,13 @@ export async function run(
})`,
parseBoolean
)
.option(
'--init-rule-docs [boolean]',
`(optional) Whether to create rule doc files if they don't yet exist. (default: ${
OPTION_DEFAULTS[OPTION_TYPE.INIT_RULE_DOCS]
})`,
parseBoolean
)
.option(
'--path-rule-doc <path>',
`(optional) Path to markdown file for each rule doc. Use \`{name}\` placeholder for the rule name. (default: ${
Expand Down
26 changes: 21 additions & 5 deletions lib/generator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { join, relative } from 'node:path';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, join, relative } from 'node:path';
import { getAllNamedOptions, hasOptions } from './rule-options.js';
import {
loadPlugin,
Expand Down Expand Up @@ -100,6 +100,8 @@ export async function generate(path: string, options?: GenerateOptions) {
const ignoreDeprecatedRules =
options?.ignoreDeprecatedRules ??
OPTION_DEFAULTS[OPTION_TYPE.IGNORE_DEPRECATED_RULES];
const initRuleDocs =
options?.initRuleDocs ?? OPTION_DEFAULTS[OPTION_TYPE.INIT_RULE_DOCS];
const pathRuleDoc =
options?.pathRuleDoc ?? OPTION_DEFAULTS[OPTION_TYPE.PATH_RULE_DOC];
const pathRuleList =
Expand Down Expand Up @@ -157,14 +159,22 @@ export async function generate(path: string, options?: GenerateOptions) {
(details) => !ignoreDeprecatedRules || !details.deprecated
);

let initializedRuleDoc = false;

// Update rule doc for each rule.
for (const { name, description, schema } of details) {
const pathToDoc = join(path, pathRuleDoc).replace(/{name}/g, name);

if (!existsSync(pathToDoc)) {
throw new Error(
`Could not find rule doc: ${relative(getPluginRoot(path), pathToDoc)}`
);
if (!initRuleDocs) {
throw new Error(
`Could not find rule doc: ${relative(getPluginRoot(path), pathToDoc)}`
);
}

mkdirSync(dirname(pathToDoc), { recursive: true });
writeFileSync(pathToDoc, '');
initializedRuleDoc = true;
}

// Regenerate the header (title/notices) of each rule doc.
Expand Down Expand Up @@ -267,4 +277,10 @@ export async function generate(path: string, options?: GenerateOptions) {
} else {
writeFileSync(pathToReadme, readmeContentsNew, 'utf8');
}

if (initRuleDocs && !initializedRuleDoc) {
throw new Error(
'--init-rule-docs was enabled, but no rule doc file needed to be created.'
);
}
}
3 changes: 3 additions & 0 deletions lib/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export enum OPTION_TYPE {
CONFIG_EMOJI = 'configEmoji',
IGNORE_CONFIG = 'ignoreConfig',
IGNORE_DEPRECATED_RULES = 'ignoreDeprecatedRules',
INIT_RULE_DOCS = 'initRuleDocs',
PATH_RULE_DOC = 'pathRuleDoc',
PATH_RULE_LIST = 'pathRuleList',
RULE_DOC_NOTICES = 'ruleDocNotices',
Expand All @@ -61,6 +62,7 @@ export const OPTION_DEFAULTS = {
[OPTION_TYPE.CONFIG_EMOJI]: [],
[OPTION_TYPE.IGNORE_CONFIG]: [],
[OPTION_TYPE.IGNORE_DEPRECATED_RULES]: false,
[OPTION_TYPE.INIT_RULE_DOCS]: false,
[OPTION_TYPE.PATH_RULE_DOC]: 'docs/rules/{name}.md',
[OPTION_TYPE.PATH_RULE_LIST]: 'README.md',
[OPTION_TYPE.RULE_DOC_NOTICES]: Object.entries(
Expand Down Expand Up @@ -88,6 +90,7 @@ export type GenerateOptions = {
configEmoji?: string[];
ignoreConfig?: string[];
ignoreDeprecatedRules?: boolean;
initRuleDocs?: boolean;
pathRuleDoc?: string;
pathRuleList?: string;
ruleDocNotices?: string;
Expand Down
3 changes: 3 additions & 0 deletions test/lib/__snapshots__/cli-test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ exports[`cli all CLI options and all config files options merges correctly, with
"ignoredConfigFromCli2",
],
"ignoreDeprecatedRules": true,
"initRuleDocs": false,
"pathRuleDoc": "www.example.com/rule-doc-from-cli",
"pathRuleList": "www.example.com/rule-list-from-cli",
"ruleDocNotices": "type",
Expand Down Expand Up @@ -53,6 +54,7 @@ exports[`cli all CLI options, no config file options is called correctly 1`] = `
"ignoredConfigFromCli2",
],
"ignoreDeprecatedRules": true,
"initRuleDocs": false,
"pathRuleDoc": "www.example.com/rule-doc-from-cli",
"pathRuleList": "www.example.com/rule-list-from-cli",
"ruleDocNotices": "type",
Expand Down Expand Up @@ -86,6 +88,7 @@ exports[`cli all config files options, no CLI options is called correctly 1`] =
"ignoredConfigFromConfigFile2",
],
"ignoreDeprecatedRules": true,
"initRuleDocs": true,
"pathRuleDoc": "www.example.com/rule-doc-from-config-file",
"pathRuleList": "www.example.com/rule-list-from-config-file",
"ruleDocNotices": "type",
Expand Down
3 changes: 3 additions & 0 deletions test/lib/cli-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const configFileOptionsAll: { [key in OPTION_TYPE]: unknown } = {
'ignoredConfigFromConfigFile2',
],
ignoreDeprecatedRules: true,
initRuleDocs: true,
pathRuleDoc: 'www.example.com/rule-doc-from-config-file',
pathRuleList: 'www.example.com/rule-list-from-config-file',
ruleDocNotices: 'type',
Expand Down Expand Up @@ -43,6 +44,8 @@ const cliOptionsAll: { [key in OPTION_TYPE]: string[] } = {

[OPTION_TYPE.IGNORE_DEPRECATED_RULES]: ['--ignore-deprecated-rules', 'true'],

[OPTION_TYPE.INIT_RULE_DOCS]: ['--init-rule-docs', 'false'],

[OPTION_TYPE.PATH_RULE_DOC]: [
'--path-rule-doc',
'www.example.com/rule-doc-from-cli',
Expand Down
7 changes: 7 additions & 0 deletions test/lib/generate/__snapshots__/file-paths-test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,10 @@ exports[`generate (file paths) lowercase README file generates the documentation
<!-- end auto-generated rules list -->"
`;

exports[`generate (file paths) missing rule doc when initRuleDocs is true creates the rule doc 1`] = `
"# test/no-foo
<!-- end auto-generated rule header -->
"
`;
72 changes: 61 additions & 11 deletions test/lib/generate/file-paths-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,65 @@ describe('generate (file paths)', function () {
}),

'index.js': `
export default {
rules: {
'no-foo': {
meta: { },
create(context) {}
},
},
};`,
export default {
rules: {
'no-foo': {
meta: { },
create(context) {}
},
},
};`,

'README.md':
'<!-- begin auto-generated rules list --><!-- end auto-generated rules list -->',

// Needed for some of the test infrastructure to work.
node_modules: mockFs.load(PATH_NODE_MODULES),
});
});

afterEach(function () {
mockFs.restore();
jest.resetModules();
});

describe('when initRuleDocs is false', () => {
it('throws an error', async function () {
// Use join to handle both Windows and Unix paths.
await expect(generate('.')).rejects.toThrow(
`Could not find rule doc: ${join('docs', 'rules', 'no-foo.md')}`
);
});
});

describe('when initRuleDocs is true', () => {
it('creates the rule doc', async function () {
await generate('.', { initRuleDocs: true });
expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot();
});
});
});

describe('no missing rule doc but --init-rule-docs enabled', function () {
beforeEach(function () {
mockFs({
'docs/rules/no-foo.md': '',

'package.json': JSON.stringify({
name: 'eslint-plugin-test',
exports: 'index.js',
type: 'module',
}),

'index.js': `
export default {
rules: {
'no-foo': {
meta: { },
create(context) {}
},
},
};`,

'README.md':
'<!-- begin auto-generated rules list --><!-- end auto-generated rules list -->',
Expand All @@ -43,9 +94,8 @@ describe('generate (file paths)', function () {
});

it('throws an error', async function () {
// Use join to handle both Windows and Unix paths.
await expect(generate('.')).rejects.toThrow(
`Could not find rule doc: ${join('docs', 'rules', 'no-foo.md')}`
await expect(generate('.', { initRuleDocs: true })).rejects.toThrow(
'--init-rule-docs was enabled, but no rule doc file needed to be created.'
);
});
});
Expand Down

0 comments on commit 50167bc

Please sign in to comment.