Skip to content

Commit

Permalink
feat: added granular flat TypeScript configs (#1298)
Browse files Browse the repository at this point in the history
* feat: added granular flat TypeScript configs

---------
Co-authored-by: Brett Zamir <brettz9@yahoo.com>

* docs: add rationales section
  • Loading branch information
JoshuaKGoldberg authored Aug 13, 2024
1 parent 6e82aeb commit aed3194
Show file tree
Hide file tree
Showing 3 changed files with 259 additions and 0 deletions.
69 changes: 69 additions & 0 deletions .README/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,75 @@ const config = [
export default config;
```

The general starting rulesets you can extend from in flat config are:

- `jsdoc.configs['flat/recommended']`: Recommended starting rules for enforcing proper tag values, that common tags exist, and that tags are formatted and styled consistently
- `jsdoc.configs['flat/recommended-error']`: The same, reporting with failing errors instead of mere warnings
- `jsdoc.configs['flat/recommended-typescript']`: A similar recommended starting list, adjusted for projects using TypeScript syntax (and not just the "typescript" `mode` setting)
- `jsdoc.configs['flat/recommended-typescript-error']`: The same, reporting with failing errors instead of mere warnings
- `jsdoc.configs['flat/recommended-typescript-flavor']`: A similar recommended starting list, adjusted for projects using JavaScript syntax (source files that are still `.js`) but using TypeScript flavor within JSDoc (i.e., the default "typescript" `mode` in `eslint-plugin-jsdoc`)
- `jsdoc.configs['flat/recommended-typescript-flavor-error']`: The same, reporting with failing errors instead of mere warnings

#### Granular Flat Configs

There also exist several more granular, standalone TypeScript rulesets you can extend from.
These each only enable mostly or only rules from the recommended starting rules:

- **Contents**: rules that check names and descriptions
- `jsdoc.configs['flat/contents-typescript']`: for TypeScript files, with reports set to warn
- `jsdoc.configs['flat/contents-typescript-error']`: for TypeScript files, with reports set to error
- `jsdoc.configs['flat/contents-typescript-flavor']`: for files using JavaScript syntax and JSDoc types, with reports set to warn
- `jsdoc.configs['flat/contents-typescript-flavor-error']`: for files using JavaScript syntax and JSDoc types, with reports set to error
- **Logical**: rules that enforce proper tag values
- `jsdoc.configs['flat/logical-typescript']`: for TypeScript files, with reports set to warn
- `jsdoc.configs['flat/logical-typescript-error']`: for TypeScript files, with reports set to error
- `jsdoc.configs['flat/logical-typescript-flavor']`: for files using JavaScript syntax and JSDoc types, with reports set to warn
- `jsdoc.configs['flat/logical-typescript-flavor-error']`: for files using JavaScript syntax and JSDoc types, with reports set to error
- **Requirements**: rules that enforce tags exist
- `jsdoc.configs['flat/requirements-typescript']`: for TypeScript files, with reports set to warn
- `jsdoc.configs['flat/requirements-typescript-error']`: for TypeScript files, with reports set to error
- `jsdoc.configs['flat/requirements-typescript-flavor']`: for files using JavaScript syntax and JSDoc types, with reports set to warn
- `jsdoc.configs['flat/requirements-typescript-flavor-error']`: for files using JavaScript syntax and JSDoc types, with reports set to error
- **Stylistic**: rules that enforce clear, consistent tag formatting and styles
- `jsdoc.configs['flat/stylistic-typescript']`: for TypeScript files, with reports set to warn
- `jsdoc.configs['flat/stylistic-typescript-error']`: for TypeScript files, with reports set to error
- `jsdoc.configs['flat/stylistic-typescript-flavor']`: for files using JavaScript syntax and JSDoc types, with reports set to warn
- `jsdoc.configs['flat/stylistic-typescript-flavor-error']`: for files using JavaScript syntax and JSDoc types, with reports set to error

For example, to enforce only that any JSDoc tags and their contents are valid and styled consistently in TypeScript files, without enforcing that tags must always exist:

```js
import jsdoc from 'eslint-plugin-jsdoc';

export default [
jsdoc.configs['flat/contents-typescript-error'],
jsdoc.configs['flat/logical-typescript-error'],
jsdoc.configs['flat/stylistic-typescript-error'],
];
```

##### Why certain rules were excluded from the granular configs

A few rules were left out of the granular configs. Here is why:

Rules which might have been added to `required`:
- [`require-throws`](./docs/rules/require-throws.md#readme) - Since this can't enforce all cases, some may not wish this rule enforced.
- [`require-file-overview`](./docs/rules/require-file-overview.md#readme) - Too demanding for all projects
- [`convert-to-jsdoc-comments`](./docs/rules/convert-to-jsdoc-comments.md#readme) - Overly aggressive for some projects

Rules which might have been added to `logical`:
- [`no-missing-syntax`](./docs/rules/no-missing-syntax.md#readme) - Has no default options.
- [`no-restricted-syntax`](./docs/rules/no-restricted-syntax.md#readme) - Has no default options.

Rules which might have been added to `contents`:
- [`match-name`](./docs/rules/match-name.md#readme) - Has no default options.
- [`require-description`](./docs/rules/require-description.md#readme) - Too demanding for all projects
- [`require-description-complete-sentence`](./docs/rules/require-description-complete-sentence.md#readme) - Too demanding for all projects

Rules which might have been added to `stylistic`:
- [`check-indentation`](./docs/rules/check-indentation.md#readme) - May not be desired by all projects
- [`sort-tags`](./docs/rules/sort-tags.md#readme) - Too project-specific

### `eslintrc`

Add `plugins` section to [.eslintrc.*](https://eslint.org/docs/user-guide/configuring#configuration-file-formats)
Expand Down
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,79 @@ const config = [
export default config;
```

The general starting rulesets you can extend from in flat config are:

- `jsdoc.configs['flat/recommended']`: Recommended starting rules for enforcing proper tag values, that common tags exist, and that tags are formatted and styled consistently
- `jsdoc.configs['flat/recommended-error']`: The same, reporting with failing errors instead of mere warnings
- `jsdoc.configs['flat/recommended-typescript']`: A similar recommended starting list, adjusted for projects using TypeScript syntax (and not just the "typescript" `mode` setting)
- `jsdoc.configs['flat/recommended-typescript-error']`: The same, reporting with failing errors instead of mere warnings
- `jsdoc.configs['flat/recommended-typescript-flavor']`: A similar recommended starting list, adjusted for projects using JavaScript syntax (source files that are still `.js`) but using TypeScript flavor within JSDoc (i.e., the default "typescript" `mode` in `eslint-plugin-jsdoc`)
- `jsdoc.configs['flat/recommended-typescript-flavor-error']`: The same, reporting with failing errors instead of mere warnings

<a name="user-content-eslint-plugin-jsdoc-configuration-flat-config-granular-flat-configs"></a>
<a name="eslint-plugin-jsdoc-configuration-flat-config-granular-flat-configs"></a>
#### Granular Flat Configs

There also exist several more granular, standalone TypeScript rulesets you can extend from.
These each only enable mostly or only rules from the recommended starting rules:

- **Contents**: rules that check names and descriptions
- `jsdoc.configs['flat/contents-typescript']`: for TypeScript files, with reports set to warn
- `jsdoc.configs['flat/contents-typescript-error']`: for TypeScript files, with reports set to error
- `jsdoc.configs['flat/contents-typescript-flavor']`: for files using JavaScript syntax and JSDoc types, with reports set to warn
- `jsdoc.configs['flat/contents-typescript-flavor-error']`: for files using JavaScript syntax and JSDoc types, with reports set to error
- **Logical**: rules that enforce proper tag values
- `jsdoc.configs['flat/logical-typescript']`: for TypeScript files, with reports set to warn
- `jsdoc.configs['flat/logical-typescript-error']`: for TypeScript files, with reports set to error
- `jsdoc.configs['flat/logical-typescript-flavor']`: for files using JavaScript syntax and JSDoc types, with reports set to warn
- `jsdoc.configs['flat/logical-typescript-flavor-error']`: for files using JavaScript syntax and JSDoc types, with reports set to error
- **Requirements**: rules that enforce tags exist
- `jsdoc.configs['flat/requirements-typescript']`: for TypeScript files, with reports set to warn
- `jsdoc.configs['flat/requirements-typescript-error']`: for TypeScript files, with reports set to error
- `jsdoc.configs['flat/requirements-typescript-flavor']`: for files using JavaScript syntax and JSDoc types, with reports set to warn
- `jsdoc.configs['flat/requirements-typescript-flavor-error']`: for files using JavaScript syntax and JSDoc types, with reports set to error
- **Stylistic**: rules that enforce clear, consistent tag formatting and styles
- `jsdoc.configs['flat/stylistic-typescript']`: for TypeScript files, with reports set to warn
- `jsdoc.configs['flat/stylistic-typescript-error']`: for TypeScript files, with reports set to error
- `jsdoc.configs['flat/stylistic-typescript-flavor']`: for files using JavaScript syntax and JSDoc types, with reports set to warn
- `jsdoc.configs['flat/stylistic-typescript-flavor-error']`: for files using JavaScript syntax and JSDoc types, with reports set to error

For example, to enforce only that any JSDoc tags and their contents are valid and styled consistently in TypeScript files, without enforcing that tags must always exist:

```js
import jsdoc from 'eslint-plugin-jsdoc';

export default [
jsdoc.configs['flat/contents-typescript-error'],
jsdoc.configs['flat/logical-typescript-error'],
jsdoc.configs['flat/stylistic-typescript-error'],
];
```

<a name="user-content-eslint-plugin-jsdoc-configuration-flat-config-granular-flat-configs-why-certain-rules-were-excluded-from-the-granular-configs"></a>
<a name="eslint-plugin-jsdoc-configuration-flat-config-granular-flat-configs-why-certain-rules-were-excluded-from-the-granular-configs"></a>
##### Why certain rules were excluded from the granular configs

A few rules were left out of the granular configs. Here is why:

Rules which might have been added to `required`:
- [`require-throws`](./docs/rules/require-throws.md#readme) - Since this can't enforce all cases, some may not wish this rule enforced.
- [`require-file-overview`](./docs/rules/require-file-overview.md#readme) - Too demanding for all projects
- [`convert-to-jsdoc-comments`](./docs/rules/convert-to-jsdoc-comments.md#readme) - Overly aggressive for some projects

Rules which might have been added to `logical`:
- [`no-missing-syntax`](./docs/rules/no-missing-syntax.md#readme) - Has no default options.
- [`no-restricted-syntax`](./docs/rules/no-restricted-syntax.md#readme) - Has no default options.

Rules which might have been added to `contents`:
- [`match-name`](./docs/rules/match-name.md#readme) - Has no default options.
- [`require-description`](./docs/rules/require-description.md#readme) - Too demanding for all projects
- [`require-description-complete-sentence`](./docs/rules/require-description-complete-sentence.md#readme) - Too demanding for all projects

Rules which might have been added to `stylistic`:
- [`check-indentation`](./docs/rules/check-indentation.md#readme) - May not be desired by all projects
- [`sort-tags`](./docs/rules/sort-tags.md#readme) - Too project-specific

<a name="user-content-eslint-plugin-jsdoc-configuration-eslintrc"></a>
<a name="eslint-plugin-jsdoc-configuration-eslintrc"></a>
### <code>eslintrc</code>
Expand Down
117 changes: 117 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,106 @@ const createRecommendedTypeScriptFlavorRuleset = (warnOrError, flatName) => {
};
};

/**
* @param {(string | unknown[])[]} ruleNames
*/
const createStandaloneRulesetFactory = (ruleNames) => {
/**
* @param {"warn"|"error"} warnOrError
* @param {string} [flatName]
* @returns {import('eslint').Linter.FlatConfig}
*/
return (warnOrError, flatName) => {
return {
name: 'jsdoc/' + flatName,
plugins: { jsdoc: index },
rules: Object.fromEntries(
ruleNames.map(
ruleName =>
typeof ruleName === "string"
? [ruleName, warnOrError]
: [ruleName[0], warnOrError, ...ruleName.slice(1)]
)
),
};
};
}

const contentsRules = [
'jsdoc/informative-docs',
'jsdoc/match-description',
'jsdoc/no-blank-block-descriptions',
'jsdoc/no-blank-blocks',
['jsdoc/text-escaping', { escapeHTML: true }]
]

const createContentsTypescriptRuleset = createStandaloneRulesetFactory(contentsRules);

const createContentsTypescriptFlavorRuleset = createStandaloneRulesetFactory(contentsRules);

const logicalRules = [
'jsdoc/check-access',
'jsdoc/check-param-names',
'jsdoc/check-property-names',
'jsdoc/check-syntax',
'jsdoc/check-tag-names',
'jsdoc/check-template-names',
'jsdoc/check-types',
'jsdoc/check-values',
'jsdoc/empty-tags',
'jsdoc/implements-on-classes',
'jsdoc/require-returns-check',
'jsdoc/require-yields-check',
'jsdoc/no-bad-blocks',
'jsdoc/no-defaults',
'jsdoc/no-types',
'jsdoc/no-undefined-types',
'jsdoc/valid-types',
];

const createLogicalTypescriptRuleset = createStandaloneRulesetFactory(logicalRules);

const createLogicalTypescriptFlavorRuleset = createStandaloneRulesetFactory(logicalRules);

const requirementsRules = [
'jsdoc/require-example',
'jsdoc/require-jsdoc',
'jsdoc/require-param',
'jsdoc/require-param-description',
'jsdoc/require-param-name',
'jsdoc/require-property',
'jsdoc/require-property-description',
'jsdoc/require-property-name',
'jsdoc/require-returns',
'jsdoc/require-returns-description',
'jsdoc/require-yields',
];

const createRequirementsTypeScriptRuleset = createStandaloneRulesetFactory(requirementsRules);

const createRequirementsTypeScriptFlavorRuleset = createStandaloneRulesetFactory([
...requirementsRules,
'jsdoc/require-param-type',
'jsdoc/require-property-type',
'jsdoc/require-returns-type',
'jsdoc/require-template',
]);

const stylisticRules = [
'jsdoc/check-alignment',
'jsdoc/check-line-alignment',
'jsdoc/lines-before-block',
'jsdoc/multiline-blocks',
'jsdoc/no-multi-asterisks',
'jsdoc/require-asterisk-prefix',
['jsdoc/require-hyphen-before-param-description', 'never'],
'jsdoc/tag-lines',
];

const createStylisticTypeScriptRuleset = createStandaloneRulesetFactory(stylisticRules);

const createStylisticTypeScriptFlavorRuleset = createStandaloneRulesetFactory(stylisticRules);

/* c8 ignore next 3 -- TS */
if (!index.configs) {
throw new Error('TypeScript guard');
Expand All @@ -279,6 +379,23 @@ index.configs['flat/recommended-typescript-error'] = createRecommendedTypeScript
index.configs['flat/recommended-typescript-flavor'] = createRecommendedTypeScriptFlavorRuleset('warn', 'flat/recommended-typescript-flavor');
index.configs['flat/recommended-typescript-flavor-error'] = createRecommendedTypeScriptFlavorRuleset('error', 'flat/recommended-typescript-flavor-error');

index.configs['flat/contents-typescript'] = createContentsTypescriptRuleset('warn', 'flat/contents-typescript');
index.configs['flat/contents-typescript-error'] = createContentsTypescriptRuleset('error', 'flat/contents-typescript-error');
index.configs['flat/contents-typescript-flavor'] = createContentsTypescriptFlavorRuleset('warn', 'flat/contents-typescript-flavor');
index.configs['flat/contents-typescript-flavor-error'] = createContentsTypescriptFlavorRuleset('error', 'flat/contents-typescript-error-flavor');
index.configs['flat/logical-typescript'] = createLogicalTypescriptRuleset('warn', 'flat/logical-typescript');
index.configs['flat/logical-typescript-error'] = createLogicalTypescriptRuleset('error', 'flat/logical-typescript-error');
index.configs['flat/logical-typescript-flavor'] = createLogicalTypescriptFlavorRuleset('warn', 'flat/logical-typescript-flavor');
index.configs['flat/logical-typescript-flavor-error'] = createLogicalTypescriptFlavorRuleset('error', 'flat/logical-typescript-error-flavor');
index.configs['flat/requirements-typescript'] = createRequirementsTypeScriptRuleset('warn', 'flat/requirements-typescript');
index.configs['flat/requirements-typescript-error'] = createRequirementsTypeScriptRuleset('error', 'flat/requirements-typescript-error');
index.configs['flat/requirements-typescript-flavor'] = createRequirementsTypeScriptFlavorRuleset('warn', 'flat/requirements-typescript-flavor');
index.configs['flat/requirements-typescript-flavor-error'] = createRequirementsTypeScriptFlavorRuleset('error', 'flat/requirements-typescript-error-flavor');
index.configs['flat/stylistic-typescript'] = createStylisticTypeScriptRuleset('warn', 'flat/stylistic-typescript');
index.configs['flat/stylistic-typescript-error'] = createStylisticTypeScriptRuleset('error', 'flat/stylistic-typescript-error');
index.configs['flat/stylistic-typescript-flavor'] = createStylisticTypeScriptFlavorRuleset('warn', 'flat/stylistic-typescript-flavor');
index.configs['flat/stylistic-typescript-flavor-error'] = createStylisticTypeScriptFlavorRuleset('error', 'flat/stylistic-typescript-error-flavor');

index.configs.examples = /** @type {import('eslint').Linter.FlatConfig[]} */ ([
{
name: 'jsdoc/examples/processor',
Expand Down

0 comments on commit aed3194

Please sign in to comment.