Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: gajus/eslint-plugin-jsdoc
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v34.7.0
Choose a base ref
...
head repository: gajus/eslint-plugin-jsdoc
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v34.8.0
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on May 18, 2021

  1. Copy the full SHA
    4c6fda7 View commit details

Commits on May 19, 2021

  1. feat(tag-lines): add tags option to override behavior on a tag-sp…

    …ecific basis (including only applying to tag(s)); fixes #734
    brettz9 committed May 19, 2021
    Copy the full SHA
    6e5e76d View commit details
Showing with 521 additions and 30 deletions.
  1. +15 −2 .README/rules/tag-lines.md
  2. +122 −2 README.md
  3. +75 −26 src/rules/tagLines.js
  4. +32 −0 test/rules/assertions/checkLineAlignment.js
  5. +277 −0 test/rules/assertions/tagLines.js
17 changes: 15 additions & 2 deletions .README/rules/tag-lines.md
Original file line number Diff line number Diff line change
@@ -4,8 +4,11 @@ Enforces lines (or no lines) between tags.

#### Options

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

"any" is only useful with `tags` (allowing non-enforcement of lines except
for particular tags).

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

@@ -18,6 +21,16 @@ Use with "always" to indicate the number of lines to require be present.
Use with "always" to indicate the normal lines to be added after tags should
not be added after the final tag.

##### `tags` (default to empty object)

Overrides the default behavior depending on specific tags.

An object whose keys are tag names and whose values are objects with the
following keys:

1. `lines` - Set to `always` or `never` to override.
2. `count` - Overrides main `count` (for "always")

|||
|---|---|
|Context|everywhere|
124 changes: 122 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -2320,6 +2320,15 @@ const fn = ( lorem, sit ) => {}
const fn = ( lorem, sit ) => {}
// "jsdoc/check-line-alignment": ["error"|"warn", "never",{"customSpacings":{"postName":3}}]
// Message: Expected JSDoc block lines to not be aligned.

/**
* @param {{
* ids: number[]
* }} params
*/
const myMethod = ({ids}) => {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]
// Message: Expected JSDoc block lines to be aligned.
````

The following patterns are not considered problems:
@@ -18989,8 +18998,11 @@ Enforces lines (or no lines) between tags.
<a name="eslint-plugin-jsdoc-rules-tag-lines-options-39"></a>
#### Options

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

"any" is only useful with `tags` (allowing non-enforcement of lines except
for particular tags).

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

@@ -19005,6 +19017,17 @@ Use with "always" to indicate the number of lines to require be present.
Use with "always" to indicate the normal lines to be added after tags should
not be added after the final tag.

<a name="eslint-plugin-jsdoc-rules-tag-lines-options-39-tags-default-to-empty-object"></a>
##### <code>tags</code> (default to empty object)

Overrides the default behavior depending on specific tags.

An object whose keys are tag names and whose values are objects with the
following keys:

1. `lines` - Set to `always` or `never` to override.
2. `count` - Overrides main `count` (for "always")

|||
|---|---|
|Context|everywhere|
@@ -19077,6 +19100,57 @@ The following patterns are considered problems:
*/
// "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", "never",{"tags":{"param":{"lines":"always"}}}]
// Message: Expected 1 line between tags but found 0

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

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

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

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

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

The following patterns are not considered problems:
@@ -19129,6 +19203,52 @@ The following patterns are not considered problems:
*
*/
// "jsdoc/tag-lines": ["error"|"warn", "always",{"count":2}]

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

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

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

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

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

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


101 changes: 75 additions & 26 deletions src/rules/tagLines.js
Original file line number Diff line number Diff line change
@@ -10,50 +10,80 @@ export default iterateJsdoc(({
{
count = 1,
noEndLines = false,
tags = {},
} = {},
] = 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',
{line: tg.source[0].number + 1},
fixer,
true,
);

return true;
}
jsdoc.tags.some((tg, tagIdx) => {
let lastTag;

return tg.source.some(({tokens: {tag, name, type, description, end}}, idx) => {
const fixer = () => {
utils.removeTagItem(tagIdx, idx);
};
if (lastTag && tags[lastTag.slice(1)]?.lines === 'always') {
return false;
});
});
}

return;
}
if (
!tag && !name && !type && !description && !end &&
(alwaysNever === 'never' ||
lastTag && tags[lastTag.slice(1)]?.lines === 'never'
)
) {
utils.reportJSDoc(
'Expected no lines between tags',
{line: tg.source[0].number + 1},
fixer,
);

return true;
}

lastTag = tag;

return false;
});
});

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

let currentTag;
tg.source.forEach(({number, tokens: {tag, name, type, description, end}}, idx) => {
if (tag) {
currentTag = tag;
}
if (!tag && !name && !type && !description && !end) {
lines.push({idx, number});
}
});
if (lines.length < count) {

const currentTg = currentTag && tags[currentTag.slice(1)];
const tagCount = currentTg?.count;

const defaultAlways = alwaysNever === 'always' && currentTg?.lines !== 'never' &&
currentTg?.lines !== 'any' && lines.length < count;

let overrideAlways;
let fixCount = count;
if (!defaultAlways) {
fixCount = typeof tagCount === 'number' ? tagCount : count;
overrideAlways = currentTg?.lines === 'always' &&
lines.length < fixCount;
}

if (defaultAlways || overrideAlways) {
const fixer = () => {
utils.addLines(tagIdx, lines[lines.length - 1]?.idx || 1, count - lines.length);
utils.addLines(tagIdx, lines[lines.length - 1]?.idx || 1, fixCount - lines.length);
};
const line = lines[lines.length - 1]?.number || tg.source[0].number;
utils.reportJSDoc(
`Expected ${count} line${count === 1 ? '' : 's'} between tags but found ${lines.length}`,
{line: lines[lines.length - 1]?.number || tg.source[0].number},
`Expected ${fixCount} line${fixCount === 1 ? '' : 's'} between tags but found ${lines.length}`,
{
line,
},
fixer,
true,
);

return true;
@@ -71,7 +101,7 @@ export default iterateJsdoc(({
fixable: 'code',
schema: [
{
enum: ['always', 'never'],
enum: ['always', 'any', 'never'],
type: 'string',
},
{
@@ -83,6 +113,25 @@ export default iterateJsdoc(({
noEndLines: {
type: 'boolean',
},
tags: {
properties: {
patternProperties: {
'.*': {
additionalProperties: false,
properties: {
count: {
type: 'integer',
},
lines: {
enum: ['always', 'never'],
type: 'string',
},
},
},
},
},
type: 'object',
},
},
type: 'object',
},
32 changes: 32 additions & 0 deletions test/rules/assertions/checkLineAlignment.js
Original file line number Diff line number Diff line change
@@ -1057,6 +1057,38 @@ export default {
const fn = ( lorem, sit ) => {}
`,
},
{
/* eslint-disable no-tabs */
code: `
/**
* @param {{
* ids: number[]
* }} params
*/
const myMethod = ({ids}) => {}
`,
errors: [
{
line: 2,
message: 'Expected JSDoc block lines to be aligned.',
type: 'Block',
},
],
options: ['always'],
output: `
/**
* @param {{
* ids: number[]
* }} params
*/
const myMethod = ({ids}) => {}
`,
/* eslint-enable no-tabs */
parser: require.resolve('@babel/eslint-parser'),
parserOptions: {
ecmaVersion: 2_021,
},
},
],
valid: [
{
Loading