Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple potential property locations for config description #508

Merged
merged 1 commit into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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