diff --git a/lib/legend.ts b/lib/legend.ts index a77bf8c3..c534c540 100644 --- a/lib/legend.ts +++ b/lib/legend.ts @@ -29,11 +29,6 @@ const LEGENDS: { configEmojis, urlConfigs, ignoreConfig, - }: { - plugin: Plugin; - configEmojis: ConfigEmojis; - ignoreConfig: string[]; - urlConfigs?: string; }) => { /* istanbul ignore next -- this shouldn't happen */ if (!plugin.configs) { @@ -83,7 +78,7 @@ const LEGENDS: { }, // Legends are included for each rule type present. - [COLUMN_TYPE.TYPE]: ({ plugin }: { plugin: Plugin }) => { + [COLUMN_TYPE.TYPE]: ({ plugin }) => { /* istanbul ignore next -- this shouldn't happen */ if (!plugin.rules) { throw new Error( diff --git a/lib/rule-list-columns.ts b/lib/rule-list-columns.ts index cca85d1d..1f7992d4 100644 --- a/lib/rule-list-columns.ts +++ b/lib/rule-list-columns.ts @@ -35,19 +35,11 @@ export const COLUMN_HEADER: { configNames: string[]; configEmojis: ConfigEmojis; ignoreConfig: string[]; + details: RuleDetails[]; }) => string); } = { // Use the general config emoji if there are multiple configs or the sole config doesn't have an emoji. - [COLUMN_TYPE.CONFIGS]: ({ - configNames, - configEmojis, - ignoreConfig, - }: { - configNames: string[]; - configEmojis: ConfigEmojis; - ignoreConfig: string[]; - urlConfigs?: string; - }) => { + [COLUMN_TYPE.CONFIGS]: ({ configNames, configEmojis, ignoreConfig }) => { const configNamesWithoutIgnored = configNames.filter( (configName) => !ignoreConfig?.includes(configName) ); @@ -58,12 +50,35 @@ export const COLUMN_HEADER: { )?.emoji ?? EMOJI_CONFIG; }, + [COLUMN_TYPE.NAME]: ({ details }) => { + const ruleNames = details.map((detail) => detail.name); + const longestRuleNameLength = Math.max( + ...ruleNames.map(({ length }) => length) + ); + const ruleDescriptions = details.map((detail) => detail.description); + const longestRuleDescriptionLength = Math.max( + ...ruleDescriptions.map((description) => + description ? description.length : 0 + ) + ); + + const title = 'Name'; + + // Add nbsp spaces to prevent rule names from wrapping to multiple lines. + // Generally only needed when long descriptions are present causing the name column to wrap. + const spaces = + ruleNames.length > 0 && longestRuleDescriptionLength >= 60 + ? ' '.repeat(longestRuleNameLength - title.length) + : ''; + + return `${title}${spaces}`; + }, + // Simple strings. [COLUMN_TYPE.DEPRECATED]: EMOJI_DEPRECATED, [COLUMN_TYPE.DESCRIPTION]: 'Description', [COLUMN_TYPE.FIXABLE]: EMOJI_FIXABLE, [COLUMN_TYPE.HAS_SUGGESTIONS]: EMOJI_HAS_SUGGESTIONS, - [COLUMN_TYPE.NAME]: 'Name', [COLUMN_TYPE.REQUIRES_TYPE_CHECKING]: EMOJI_REQUIRES_TYPE_CHECKING, [COLUMN_TYPE.TYPE]: EMOJI_TYPE, }; diff --git a/lib/rule-list.ts b/lib/rule-list.ts index 1c6ccf53..09095266 100644 --- a/lib/rule-list.ts +++ b/lib/rule-list.ts @@ -104,6 +104,7 @@ function generateRulesListMarkdown( configNames: Object.keys(configsToRules), configEmojis, ignoreConfig, + details, }) : headerStrOrFn, ]; diff --git a/lib/rule-notices.ts b/lib/rule-notices.ts index e1597340..dd661aca 100644 --- a/lib/rule-notices.ts +++ b/lib/rule-notices.ts @@ -49,15 +49,7 @@ const RULE_NOTICES: { }) => string); } = { // Configs notice varies based on whether the rule is enabled in one or more configs. - [NOTICE_TYPE.CONFIGS]: ({ - configsEnabled, - configEmojis, - urlConfigs, - }: { - configsEnabled?: string[]; - configEmojis: ConfigEmojis; - urlConfigs?: string; - }) => { + [NOTICE_TYPE.CONFIGS]: ({ configsEnabled, configEmojis, urlConfigs }) => { // Add link to configs documentation if provided. const configsLinkOrWord = urlConfigs ? `[configs](${urlConfigs})` @@ -93,18 +85,14 @@ const RULE_NOTICES: { }, // Deprecated notice has optional "replaced by" rules list. - [NOTICE_TYPE.DEPRECATED]: ({ - replacedBy, - }: { - replacedBy?: readonly string[] | undefined; - }) => + [NOTICE_TYPE.DEPRECATED]: ({ replacedBy }) => `${EMOJI_DEPRECATED} This rule is deprecated.${ replacedBy && replacedBy.length > 0 ? ` It was replaced by ${ruleNamesToList(replacedBy)}.` : '' }`, - [NOTICE_TYPE.TYPE]: ({ type }: { type?: RULE_TYPE }) => { + [NOTICE_TYPE.TYPE]: ({ type }) => { /* istanbul ignore next -- this shouldn't happen */ if (!type) { throw new Error( diff --git a/test/lib/__snapshots__/generator-test.ts.snap b/test/lib/__snapshots__/generator-test.ts.snap index 85aa999f..4785eae6 100644 --- a/test/lib/__snapshots__/generator-test.ts.snap +++ b/test/lib/__snapshots__/generator-test.ts.snap @@ -439,6 +439,25 @@ Pre-existing notice about the rule being recommended. Details." `; +exports[`generator #generate rule with long-enough description to require name column wrapping avoidance adds spaces to the name column 1`] = ` +"## Rules + + +| Name   | Description | +| :----------------------------- | :---------------------------------------------------------------------------------- | +| [no-foo](docs/rules/no-foo.md) | over 60 chars over 60 chars over 60 chars over 60 chars over 60 chars over 60 chars | + + +" +`; + +exports[`generator #generate rule with long-enough description to require name column wrapping avoidance adds spaces to the name column 2`] = ` +"# Over 60 chars over 60 chars over 60 chars over 60 chars over 60 chars over 60 chars (\`test/no-foo\`) + + +" +`; + exports[`generator #generate rule with type, type column enabled displays the type 1`] = ` "## Rules diff --git a/test/lib/generator-test.ts b/test/lib/generator-test.ts index 188624e7..8b5d2209 100644 --- a/test/lib/generator-test.ts +++ b/test/lib/generator-test.ts @@ -3146,5 +3146,44 @@ describe('generator', function () { expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot(); }); }); + + describe('rule with long-enough description to require name column wrapping avoidance', function () { + beforeEach(function () { + mockFs({ + 'package.json': JSON.stringify({ + name: 'eslint-plugin-test', + main: 'index.js', + type: 'module', + }), + + 'index.js': ` + export default { + rules: { + 'no-foo': { meta: { docs: { description: 'over 60 chars over 60 chars over 60 chars over 60 chars over 60 chars over 60 chars'} }, create(context) {} }, + }, + };`, + + 'README.md': '## Rules\n', + + 'docs/rules/no-foo.md': '', + + // Needed for some of the test infrastructure to work. + node_modules: mockFs.load( + resolve(__dirname, '..', '..', 'node_modules') + ), + }); + }); + + afterEach(function () { + mockFs.restore(); + jest.resetModules(); + }); + + it('adds spaces to the name column', async function () { + await generate('.'); + expect(readFileSync('README.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot(); + }); + }); }); });