Skip to content

Commit

Permalink
feat: support multiple property locations for config description
Browse files Browse the repository at this point in the history
  • Loading branch information
bmish committed Jan 14, 2024
1 parent 3f299d0 commit a1f10d6
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 45 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Delete any old rules list from your `README.md`. A new one will be automatically
<!-- end auto-generated rules list -->
```

Optionally, add these marker comments to your `README.md` in a `## Configs` section or similar location (uses the `description` property exported by each config if available):
Optionally, add these marker comments to your `README.md` in a `## Configs` section or similar location (uses the `meta.docs.description` property exported by each config if available):

```md
<!-- begin auto-generated configs list -->
Expand Down
33 changes: 26 additions & 7 deletions lib/config-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,31 @@ import {
END_CONFIG_LIST_MARKER,
} from './comment-markers.js';
import { markdownTable } from 'markdown-table';
import type { ConfigsToRules, ConfigEmojis, Plugin } from './types.js';
import type { ConfigsToRules, ConfigEmojis, Plugin, Config } from './types.js';
import { ConfigFormat, configNameToDisplay } from './config-format.js';
import { sanitizeMarkdownTable } from './string.js';

/**
* Check potential locations for the config description.
* These are not official properties.
* The recommended/allowed way to add a description is still pending the outcome of: https://github.com/eslint/eslint/issues/17842
* @param config
* @returns the description if available
*/
function configToDescription(config: Config): string | undefined {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return (
// @ts-expect-error -- description is not an official config property.
config.description ||
// @ts-expect-error -- description is not an official config property.
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
config.meta?.description ||
// @ts-expect-error -- description is not an official config property.
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
config.meta?.docs?.description
);
}

function generateConfigListMarkdown(
plugin: Plugin,
configsToRules: ConfigsToRules,
Expand All @@ -17,10 +38,7 @@ function generateConfigListMarkdown(
): string {
/* istanbul ignore next -- configs are sure to exist at this point */
const configs = Object.values(plugin.configs || {});
const hasDescription = configs.some(
// @ts-expect-error -- description is not an official config property.
(config) => config.description
);
const hasDescription = configs.some((config) => configToDescription(config));
const listHeaderRow = ['', 'Name'];
if (hasDescription) {
listHeaderRow.push('Description');
Expand All @@ -33,8 +51,9 @@ function generateConfigListMarkdown(
.filter((configName) => !ignoreConfig.includes(configName))
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
.map((configName) => {
const description = // @ts-expect-error -- description is not an official config property.
plugin.configs?.[configName]?.description as string | undefined;
const config = plugin.configs?.[configName];
/* istanbul ignore next -- config should exist at this point */
const description = config ? configToDescription(config) : undefined;
return [
configEmojis.find((obj) => obj.config === configName)?.emoji || '',
`\`${configNameToDisplay(
Expand Down
46 changes: 43 additions & 3 deletions test/lib/generate/__snapshots__/configs-list-test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ exports[`generate (configs list) when a config description needs to be escaped i
<!-- end auto-generated configs list -->"
`;

exports[`generate (configs list) when a config exports a description generates the documentation 1`] = `
exports[`generate (configs list) when a config exports a description property=description generates the documentation 1`] = `
"## Rules
<!-- begin auto-generated rules list -->
Expand All @@ -47,8 +47,48 @@ exports[`generate (configs list) when a config exports a description generates t
| [no-foo](docs/rules/no-foo.md) | Description of no-foo. |
<!-- end auto-generated rules list -->
## Configs
<!-- begin auto-generated configs list -->
## Configs
<!-- begin auto-generated configs list -->
| | Name | Description |
| :- | :------------ | :--------------------------------------- |
| | \`foo\` | |
| ✅ | \`recommended\` | This config has the recommended rules... |
<!-- end auto-generated configs list -->"
`;

exports[`generate (configs list) when a config exports a description property=meta.description generates the documentation 1`] = `
"## Rules
<!-- begin auto-generated rules list -->
| Name | Description |
| :----------------------------- | :--------------------- |
| [no-foo](docs/rules/no-foo.md) | Description of no-foo. |
<!-- end auto-generated rules list -->
## Configs
<!-- begin auto-generated configs list -->
| | Name | Description |
| :- | :------------ | :--------------------------------------- |
| | \`foo\` | |
| ✅ | \`recommended\` | This config has the recommended rules... |
<!-- end auto-generated configs list -->"
`;

exports[`generate (configs list) when a config exports a description property=meta.docs.description generates the documentation 1`] = `
"## Rules
<!-- begin auto-generated rules list -->
| Name | Description |
| :----------------------------- | :--------------------- |
| [no-foo](docs/rules/no-foo.md) | Description of no-foo. |
<!-- end auto-generated rules list -->
## Configs
<!-- begin auto-generated configs list -->
| | Name | Description |
| :- | :------------ | :--------------------------------------- |
Expand Down
162 changes: 128 additions & 34 deletions test/lib/generate/configs-list-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,48 +193,142 @@ describe('generate (configs list)', function () {
});

describe('when a config exports a description', function () {
beforeEach(function () {
mockFs({
'package.json': JSON.stringify({
name: 'eslint-plugin-test',
exports: 'index.js',
type: 'module',
}),

'index.js': `
export default {
rules: {
'no-foo': {
meta: { docs: { description: 'Description of no-foo.' }, },
create(context) {}
describe('property=description', function () {
beforeEach(function () {
mockFs({
'package.json': JSON.stringify({
name: 'eslint-plugin-test',
exports: 'index.js',
type: 'module',
}),

'index.js': `
export default {
rules: {
'no-foo': {
meta: { docs: { description: 'Description of no-foo.' }, },
create(context) {}
},
},
},
configs: {
foo: {},
recommended: { description: 'This config has the recommended rules...' },
}
};`,

'README.md': `## Rules
## Configs
<!-- begin auto-generated configs list -->
<!-- end auto-generated configs list -->`,
configs: {
foo: {},
recommended: { description: 'This config has the recommended rules...' },
}
};`,

'README.md': `## Rules
## Configs
<!-- begin auto-generated configs list -->
<!-- end auto-generated configs list -->`,

'docs/rules/no-foo.md': '',

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

'docs/rules/no-foo.md': '',
afterEach(function () {
mockFs.restore();
jest.resetModules();
});

// Needed for some of the test infrastructure to work.
node_modules: mockFs.load(PATH_NODE_MODULES),
it('generates the documentation', async function () {
await generate('.');
expect(readFileSync('README.md', 'utf8')).toMatchSnapshot();
});
});

afterEach(function () {
mockFs.restore();
jest.resetModules();
describe('property=meta.description', function () {
beforeEach(function () {
mockFs({
'package.json': JSON.stringify({
name: 'eslint-plugin-test',
exports: 'index.js',
type: 'module',
}),

'index.js': `
export default {
rules: {
'no-foo': {
meta: { docs: { description: 'Description of no-foo.' }, },
create(context) {}
},
},
configs: {
foo: {},
recommended: { meta: { description: 'This config has the recommended rules...' } },
}
};`,

'README.md': `## Rules
## Configs
<!-- begin auto-generated configs list -->
<!-- end auto-generated configs list -->`,

'docs/rules/no-foo.md': '',

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

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

it('generates the documentation', async function () {
await generate('.');
expect(readFileSync('README.md', 'utf8')).toMatchSnapshot();
});
});

it('generates the documentation', async function () {
await generate('.');
expect(readFileSync('README.md', 'utf8')).toMatchSnapshot();
describe('property=meta.docs.description', function () {
beforeEach(function () {
mockFs({
'package.json': JSON.stringify({
name: 'eslint-plugin-test',
exports: 'index.js',
type: 'module',
}),

'index.js': `
export default {
rules: {
'no-foo': {
meta: { docs: { description: 'Description of no-foo.' }, },
create(context) {}
},
},
configs: {
foo: {},
recommended: { meta: { docs: { description: 'This config has the recommended rules...' } } },
}
};`,

'README.md': `## Rules
## Configs
<!-- begin auto-generated configs list -->
<!-- end auto-generated configs list -->`,

'docs/rules/no-foo.md': '',

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

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

it('generates the documentation', async function () {
await generate('.');
expect(readFileSync('README.md', 'utf8')).toMatchSnapshot();
});
});
});

Expand Down

0 comments on commit a1f10d6

Please sign in to comment.