Skip to content

Commit

Permalink
feat: new rule tag-line; fixes gajus#93
Browse files Browse the repository at this point in the history
  • Loading branch information
brettz9 committed May 12, 2021
1 parent 28397c7 commit 170b1a0
Show file tree
Hide file tree
Showing 9 changed files with 520 additions and 4 deletions.
1 change: 1 addition & 0 deletions .README/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -581,4 +581,5 @@ selector).
{"gitdown": "include", "file": "./rules/require-throws.md"}
{"gitdown": "include", "file": "./rules/require-yields.md"}
{"gitdown": "include", "file": "./rules/require-yields-check.md"}
{"gitdown": "include", "file": "./rules/tag-lines.md"}
{"gitdown": "include", "file": "./rules/valid-types.md"}
28 changes: 28 additions & 0 deletions .README/rules/tag-lines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
### `tag-lines`

Enforces lines (or no lines) between tags.

#### Options

The first option is a single string set to "always" or "never" (defaults to
"never").

The second option is an object with the following optional properties.

##### `count` (defaults to 1)

Use with "always" to indicate the number of lines to require be present.

##### `noEndLine` (defaults to `false`)

Use with "always" to indicate tag lines should not be added at the end.

|||
|---|---|
|Context|everywhere|
|Tags|Any|
|Recommended|false|
|Settings|N/A|
|Options|(a string matching `"always" or "never"` and optional object with `count` and `noEndLine`)|

<!-- assertions tagLines -->
136 changes: 135 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ JSDoc linting rules for ESLint.
* [`require-throws`](#eslint-plugin-jsdoc-rules-require-throws)
* [`require-yields`](#eslint-plugin-jsdoc-rules-require-yields)
* [`require-yields-check`](#eslint-plugin-jsdoc-rules-require-yields-check)
* [`tag-lines`](#eslint-plugin-jsdoc-rules-tag-lines)
* [`valid-types`](#eslint-plugin-jsdoc-rules-valid-types)


Expand Down Expand Up @@ -18222,6 +18223,139 @@ function * quux (foo) {
````


<a name="eslint-plugin-jsdoc-rules-tag-lines"></a>
### <code>tag-lines</code>

Enforces lines (or no lines) between tags.

<a name="eslint-plugin-jsdoc-rules-tag-lines-options-37"></a>
#### Options

The first option is a single string set to "always" or "never" (defaults to
"never").

The second option is an object with the following optional properties.

<a name="eslint-plugin-jsdoc-rules-tag-lines-options-37-count-defaults-to-1"></a>
##### <code>count</code> (defaults to 1)

Use with "always" to indicate the number of lines to require be present.

<a name="eslint-plugin-jsdoc-rules-tag-lines-options-37-noendline-defaults-to-false"></a>
##### <code>noEndLine</code> (defaults to <code>false</code>)

Use with "always" to indicate tag lines should not be added at the end.

|||
|---|---|
|Context|everywhere|
|Tags|Any|
|Recommended|false|
|Settings|N/A|
|Options|(a string matching `"always" or "never"` and optional object with `count` and `noEndLine`)|

The following patterns are considered problems:

````js
/**
* Some description
* @param {string} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "always"]
// Message: Expected 1 line between tags but found 0

/**
* Some description
* @param {string} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "always",{"count":2}]
// Message: Expected 2 lines between tags but found 0

/**
* Some description
* @param {string} a
*
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "always",{"count":2}]
// Message: Expected 2 lines between tags but found 1

/**
* Some description
* @param {string} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "always",{"noEndLine":true}]
// Message: Expected 1 line between tags but found 0

/**
* Some description
* @param {string} a
*
* @param {number} b
*
*/
// "jsdoc/tag-lines": ["error"|"warn", "never"]
// Message: Expected no lines between tags

/**
* Some description
* @param {string} a
*
* @param {number} b
*
*/
// Message: Expected no lines between tags
````

The following patterns are not considered problems:

````js
/**
* Some description
* @param {string} a
* @param {number} b
*/

/**
* Some description
* @param {string} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "never"]

/**
* @param {string} a
*/
// "jsdoc/tag-lines": ["error"|"warn", "never",{"noEndLine":true}]

/** @param {number} b */
// "jsdoc/tag-lines": ["error"|"warn", "never",{"noEndLine":true}]

/**
* Some description
* @param {string} a
*
* @param {number} b
*
*/
// "jsdoc/tag-lines": ["error"|"warn", "always"]

/**
* Some description
* @param {string} a
*
*
* @param {number} b
*
*
*/
// "jsdoc/tag-lines": ["error"|"warn", "always",{"count":2}]
````


<a name="eslint-plugin-jsdoc-rules-valid-types"></a>
### <code>valid-types</code>

Expand Down Expand Up @@ -18302,7 +18436,7 @@ for valid types (based on the tag's `type` value), and either portion checked
for presence (based on `false` `name` or `type` values or their `required`
value). See the setting for more details.

<a name="eslint-plugin-jsdoc-rules-valid-types-options-37"></a>
<a name="eslint-plugin-jsdoc-rules-valid-types-options-38"></a>
#### Options

- `allowEmptyNamepaths` (default: true) - Set to `false` to bulk disallow
Expand Down
2 changes: 1 addition & 1 deletion src/bin/generateRule.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export default iterateJsdoc(({
await open([
// Could even add editor line column numbers like `${rulePath}:1:1`
ruleReadmePath,
rulePath,
ruleTestPath,
rulePath,
]);
})();
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import requireReturnsType from './rules/requireReturnsType';
import requireThrows from './rules/requireThrows';
import requireYields from './rules/requireYields';
import requireYieldsCheck from './rules/requireYieldsCheck';
import tagLines from './rules/tagLines';
import validTypes from './rules/validTypes';

export default {
Expand Down Expand Up @@ -91,6 +92,7 @@ export default {
'jsdoc/require-throws': 'off',
'jsdoc/require-yields': 'warn',
'jsdoc/require-yields-check': 'warn',
'jsdoc/tag-lines': 'warn',
'jsdoc/valid-types': 'warn',
},
},
Expand Down Expand Up @@ -139,6 +141,7 @@ export default {
'require-throws': requireThrows,
'require-yields': requireYields,
'require-yields-check': requireYieldsCheck,
'tag-lines': tagLines,
'valid-types': validTypes,
},
};
51 changes: 49 additions & 2 deletions src/iterateJsdoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,11 @@ const getUtils = (
}];
};

utils.removeTag = (tagIndex) => {
utils.removeTag = (idx) => {
return utils.removeTagItem(idx);
};

utils.removeTagItem = (tagIndex, tagSourceOffset = 0) => {
const {source: tagSource} = jsdoc.tags[tagIndex];
let lastIndex;
const firstNumber = jsdoc.source[0].number;
Expand All @@ -206,7 +210,8 @@ const getUtils = (

return true;
});
jsdoc.source.splice(sourceIndex, spliceCount);
jsdoc.source.splice(sourceIndex + tagSourceOffset, spliceCount - tagSourceOffset);
tagSource.splice(tagIdx + tagSourceOffset, spliceCount - tagSourceOffset);
lastIndex = sourceIndex;

return true;
Expand Down Expand Up @@ -237,6 +242,48 @@ const getUtils = (
});
};

utils.addLines = (tagIndex, tagSourceOffset, numLines) => {
const {source: tagSource} = jsdoc.tags[tagIndex];
let lastIndex;
const firstNumber = jsdoc.source[0].number;
tagSource.some(({number}, tagIdx) => {
const makeLine = () => {
return {
number,
source: '',
tokens: seedTokens({
delimiter: '*',
start: indent + ' ',
}),
};
};
const makeLines = () => {
return Array.from({length: numLines}, makeLine);
};
const sourceIndex = jsdoc.source.findIndex(({
number: srcNumber, tokens: {end},
}) => {
return number === srcNumber && !end;
});
// istanbul ignore else
if (sourceIndex > -1) {
const lines = makeLines();
jsdoc.source.splice(sourceIndex + tagSourceOffset, 0, ...lines);

// tagSource.splice(tagIdx + 1, 0, ...makeLines());
lastIndex = sourceIndex;

return true;
}

// istanbul ignore next
return false;
});
jsdoc.source.slice(lastIndex).forEach((src, idx) => {
src.number = firstNumber + lastIndex + idx;
});
};

utils.flattenRoots = (params) => {
return jsdocUtils.flattenRoots(params);
};
Expand Down
90 changes: 90 additions & 0 deletions src/rules/tagLines.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import iterateJsdoc from '../iterateJsdoc';

export default iterateJsdoc(({
context,
jsdoc,
utils,
}) => {
const [
alwaysNever = 'never',
{
count = 1,
noEndLine = false,
} = {},
] = context.options;

if (alwaysNever === 'never') {
jsdoc.tags.some((tg, tagIdx) => {
return tg.source.some(({tokens: {tag, name, type, description, end}}, idx) => {
const fixer = () => {
utils.removeTagItem(tagIdx, idx);
};
if (!tag && !name && !type && !description && !end) {
utils.reportJSDoc(
'Expected no lines between tags', tg, fixer, true,
);

return true;
}

return false;
});
});

return;
}

(noEndLine ? jsdoc.tags.slice(0, 1) : jsdoc.tags).some((tg, tagIdx) => {
const lines = [];

tg.source.forEach(({tokens: {tag, name, type, description, end}}, idx) => {
if (!tag && !name && !type && !description && !end) {
lines.push(idx);
}
});
if (lines.length < count) {
const fixer = () => {
utils.addLines(tagIdx, lines[lines.length - 1] || 1, count - lines.length);
};

utils.reportJSDoc(
`Expected ${count} line${count === 1 ? '' : 's'} between tags but found ${lines.length}`,
tg,
fixer,
true,
);

return true;
}

return false;
});
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Enforces lines (or no lines) between tags.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-rules-tag-lines',
},
fixable: true,
schema: [
{
enum: ['always', 'never'],
type: 'string',
},
{
additionalProperies: false,
properties: {
count: {
type: 'integer',
},
noEndLine: {
type: 'boolean',
},
},
type: 'object',
},
],
type: 'suggestion',
},
});

0 comments on commit 170b1a0

Please sign in to comment.