Skip to content

Commit

Permalink
feat(lines-before-block): add new rule; fixes #1209
Browse files Browse the repository at this point in the history
BREAKING CHANGE:

Adds new rule to recommended
  • Loading branch information
brettz9 committed Aug 6, 2024
1 parent ce230a8 commit 0265923
Show file tree
Hide file tree
Showing 8 changed files with 540 additions and 0 deletions.
1 change: 1 addition & 0 deletions .README/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ non-default-recommended fixer).
|:heavy_check_mark:|:wrench:|[empty-tags](./docs/rules/empty-tags.md#readme)|Checks tags that are expected to be empty (e.g., `@abstract` or `@async`), reporting if they have content|
|:heavy_check_mark:||[implements-on-classes](./docs/rules/implements-on-classes.md#readme)|Prohibits use of `@implements` on non-constructor functions (to enforce the tag only being used on classes/constructors)|
|||[informative-docs](./docs/rules/informative-docs.md#readme)|Reports on JSDoc texts that serve only to restate their attached name.|
|:heavy_check_mark:||[lines-before-block](./docs/rules/lines-before-block.md#readme)|Enforces minimum number of newlines before JSDoc comment blocks|
|||[match-description](./docs/rules/match-description.md#readme)|Defines customizable regular expression rules for your tag descriptions|
||:wrench:|[match-name](./docs/rules/match-name.md#readme)|Reports the name portion of a JSDoc tag if matching or not matching a given regular expression|
|:heavy_check_mark:|:wrench:|[multiline-blocks](./docs/rules/multiline-blocks.md#readme)|Controls how and whether jsdoc blocks can be expressed as single or multiple line blocks|
Expand Down
37 changes: 37 additions & 0 deletions .README/rules/lines-before-block.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# `lines-before-block`

This rule enforces minimum number of newlines before JSDoc comment blocks
(except at the beginning of a file).

## Options

### `lines`

The minimum number of lines to require. Defaults to 1.

### `ignoreSameLine`

This option excludes cases where the JSDoc block occurs on the same line as a
preceding code or comment. Defaults to `true`.

### `excludedTags`

An array of tags whose presence in the JSDoc block will prevent the
application of the rule. Defaults to `['type']` (i.e., if `@type` is present,
lines before the block will not be added).

|||
|---|---|
|Context|everywhere|
|Tags|N/A|
|Recommended|true|
|Settings||
|Options|`excludedTags`, `ignoreSameLine`, `lines`|

## Failing examples

<!-- assertions-failing linesBeforeBlock -->

## Passing examples

<!-- assertions-passing linesBeforeBlock -->
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ non-default-recommended fixer).
|:heavy_check_mark:|:wrench:|[empty-tags](./docs/rules/empty-tags.md#readme)|Checks tags that are expected to be empty (e.g., `@abstract` or `@async`), reporting if they have content|
|:heavy_check_mark:||[implements-on-classes](./docs/rules/implements-on-classes.md#readme)|Prohibits use of `@implements` on non-constructor functions (to enforce the tag only being used on classes/constructors)|
|||[informative-docs](./docs/rules/informative-docs.md#readme)|Reports on JSDoc texts that serve only to restate their attached name.|
|:heavy_check_mark:||[lines-before-block](./docs/rules/lines-before-block.md#readme)|Enforces minimum number of newlines before JSDoc comment blocks|
|||[match-description](./docs/rules/match-description.md#readme)|Defines customizable regular expression rules for your tag descriptions|
||:wrench:|[match-name](./docs/rules/match-name.md#readme)|Reports the name portion of a JSDoc tag if matching or not matching a given regular expression|
|:heavy_check_mark:|:wrench:|[multiline-blocks](./docs/rules/multiline-blocks.md#readme)|Controls how and whether jsdoc blocks can be expressed as single or multiple line blocks|
Expand Down
150 changes: 150 additions & 0 deletions docs/rules/lines-before-block.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<a name="user-content-lines-before-block"></a>
<a name="lines-before-block"></a>
# <code>lines-before-block</code>

This rule enforces minimum number of newlines before JSDoc comment blocks
(except at the beginning of a file).

<a name="user-content-lines-before-block-options"></a>
<a name="lines-before-block-options"></a>
## Options

<a name="user-content-lines-before-block-options-lines"></a>
<a name="lines-before-block-options-lines"></a>
### <code>lines</code>

The minimum number of lines to require. Defaults to 1.

<a name="user-content-lines-before-block-options-ignoresameline"></a>
<a name="lines-before-block-options-ignoresameline"></a>
### <code>ignoreSameLine</code>

This option excludes cases where the JSDoc block occurs on the same line as a
preceding code or comment. Defaults to `true`.

<a name="user-content-lines-before-block-options-excludedtags"></a>
<a name="lines-before-block-options-excludedtags"></a>
### <code>excludedTags</code>

An array of tags whose presence in the JSDoc block will prevent the
application of the rule. Defaults to `['type']` (i.e., if `@type` is present,
lines before the block will not be added).

|||
|---|---|
|Context|everywhere|
|Tags|N/A|
|Recommended|true|
|Settings||
|Options|`excludedTags`, `ignoreSameLine`, `lines`|

<a name="user-content-lines-before-block-failing-examples"></a>
<a name="lines-before-block-failing-examples"></a>
## Failing examples

The following patterns are considered problems:

````ts
someCode;
/**
*
*/
// Message: Required 1 line(s) before JSDoc block

someCode; /**
*
*/
// "jsdoc/lines-before-block": ["error"|"warn", {"ignoreSameLine":false}]
// Message: Required 1 line(s) before JSDoc block

someCode; /** */
// "jsdoc/lines-before-block": ["error"|"warn", {"ignoreSameLine":false}]
// Message: Required 1 line(s) before JSDoc block

someCode;
/**
*
*/
// "jsdoc/lines-before-block": ["error"|"warn", {"lines":2}]
// Message: Required 2 line(s) before JSDoc block

// Some comment
/**
*
*/
// Message: Required 1 line(s) before JSDoc block

/* Some comment */
/**
*
*/
// Message: Required 1 line(s) before JSDoc block

/**
* Some comment
*/
/**
*
*/
// Message: Required 1 line(s) before JSDoc block
````



<a name="user-content-lines-before-block-passing-examples"></a>
<a name="lines-before-block-passing-examples"></a>
## Passing examples

The following patterns are not considered problems:

````ts
/**
*
*/

someCode;

/**
*
*/

someCode;


/**
*
*/
// "jsdoc/lines-before-block": ["error"|"warn", {"lines":2}]

// Some comment

/**
*
*/

/* Some comment */

/**
*
*/

/**
* Some comment
*/

/**
*
*/

someCode; /** */

const a = {
someProp: /** @type {SomeCast} */ (someVal)
};

const a = /** @lends SomeClass */ {
someProp: (someVal)
};
// "jsdoc/lines-before-block": ["error"|"warn", {"excludedTags":["lends"],"ignoreSameLine":false}]
````

3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import emptyTags from './rules/emptyTags.js';
import implementsOnClasses from './rules/implementsOnClasses.js';
import importsAsDependencies from './rules/importsAsDependencies.js';
import informativeDocs from './rules/informativeDocs.js';
import linesBeforeBlock from './rules/linesBeforeBlock.js';
import matchDescription from './rules/matchDescription.js';
import matchName from './rules/matchName.js';
import multilineBlocks from './rules/multilineBlocks.js';
Expand Down Expand Up @@ -92,6 +93,7 @@ const index = {
'implements-on-classes': implementsOnClasses,
'imports-as-dependencies': importsAsDependencies,
'informative-docs': informativeDocs,
'lines-before-block': linesBeforeBlock,
'match-description': matchDescription,
'match-name': matchName,
'multiline-blocks': multilineBlocks,
Expand Down Expand Up @@ -167,6 +169,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
'jsdoc/implements-on-classes': warnOrError,
'jsdoc/imports-as-dependencies': 'off',
'jsdoc/informative-docs': 'off',
'jsdoc/lines-before-block': warnOrError,
'jsdoc/match-description': 'off',
'jsdoc/match-name': 'off',
'jsdoc/multiline-blocks': warnOrError,
Expand Down
101 changes: 101 additions & 0 deletions src/rules/linesBeforeBlock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import iterateJsdoc from '../iterateJsdoc.js';

export default iterateJsdoc(({
context,
jsdocNode,
sourceCode,
report,
utils,
}) => {
const {
lines = 1,
ignoreSameLine = true,
excludedTags = ['type']
} = context.options[0] || {};

if (utils.hasATag(excludedTags)) {
return;
}

const tokensBefore = sourceCode.getTokensBefore(jsdocNode, {includeComments: true});
const tokenBefore = tokensBefore.slice(-1)[0];
if (!tokenBefore) {
return;
}

if (tokenBefore.loc?.end?.line + lines >=
/** @type {number} */
(jsdocNode.loc?.start?.line)
) {
const startLine = jsdocNode.loc?.start?.line;
const sameLine = tokenBefore.loc?.end?.line === startLine;

if (sameLine && ignoreSameLine) {
return;
}

/** @type {import('eslint').Rule.ReportFixer} */
const fix = (fixer) => {
let indent = '';
if (sameLine) {
const spaceDiff = /** @type {number} */ (jsdocNode.loc?.start?.column) -
/** @type {number} */ (tokenBefore.loc?.end?.column);
// @ts-expect-error Should be a comment
indent = /** @type {import('estree').Comment} */ (
jsdocNode
).value.match(/^\*\n([ \t]*) \*/)?.[1]?.slice(spaceDiff);
if (!indent) {
/** @type {import('eslint').AST.Token|import('estree').Comment|undefined} */
let tokenPrior = tokenBefore;
let startColumn;
while (tokenPrior && tokenPrior?.loc?.start?.line === startLine) {
startColumn = tokenPrior.loc?.start?.column;
tokenPrior = tokensBefore.pop();
}
indent = ' '.repeat(
/* c8 ignore next */
/** @type {number} */ (startColumn ? startColumn - 1 : 0)
);
}
}

return fixer.insertTextAfter(
/** @type {import('eslint').AST.Token} */
(tokenBefore),
'\n'.repeat(lines) +
(sameLine ? '\n' + indent : '')
);
};
report(`Required ${lines} line(s) before JSDoc block`, fix);
}
}, {
iterateAllJsdocs: true,
meta: {
fixable: 'code',
docs: {
description: 'Enforces minimum number of newlines before JSDoc comment blocks',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/lines-before-block.md#repos-sticky-header',
},
schema: [
{
additionalProperties: false,
properties: {
excludedTags: {
type: 'array',
items: {
type: 'string'
}
},
ignoreSameLine: {
type: 'boolean',
},
lines: {
type: 'integer'
}
},
type: 'object',
},
],
type: 'suggestion',
},
});
Loading

0 comments on commit 0265923

Please sign in to comment.