From 02a21021cf2a33d241107d30c79e8fa306de6f25 Mon Sep 17 00:00:00 2001 From: Eli Skeggs Date: Fri, 13 Dec 2019 17:00:53 -0800 Subject: [PATCH] feat: add require-asterisk-prefix rule This rule checks that each line of the jsdoc comment starts with an asterisk. For composability with other rules, this does not check the alignment or indentation of the comment content. fix #199 --- .README/README.md | 2 + .README/rules/require-asterisk-prefix.md | 12 +++ README.md | 41 ++++++--- src/index.js | 7 +- src/rules/requireAsteriskPrefix.js | 41 +++++++++ .../rules/assertions/requireAsteriskPrefix.js | 91 +++++++++++++++++++ test/rules/index.js | 1 + 7 files changed, 181 insertions(+), 14 deletions(-) create mode 100644 .README/rules/require-asterisk-prefix.md create mode 100644 src/rules/requireAsteriskPrefix.js create mode 100644 test/rules/assertions/requireAsteriskPrefix.js diff --git a/.README/README.md b/.README/README.md index 9a0b13bff..405860df7 100644 --- a/.README/README.md +++ b/.README/README.md @@ -44,6 +44,7 @@ Finally, enable all of the rules that you would like to use. "jsdoc/check-examples": 1, "jsdoc/check-indentation": 1, "jsdoc/check-param-names": 1, // Recommended + "jsdoc/require-asterisk-prefix": 1, "jsdoc/check-syntax": 1, "jsdoc/check-tag-names": 1, // Recommended "jsdoc/check-types": 1, // Recommended @@ -337,6 +338,7 @@ only (e.g., to match `Array` if the type is `Array` vs. `Array.`). {"gitdown": "include", "file": "./rules/newline-after-description.md"} {"gitdown": "include", "file": "./rules/no-types.md"} {"gitdown": "include", "file": "./rules/no-undefined-types.md"} +{"gitdown": "include", "file": "./rules/require-asterisk-prefix.md"} {"gitdown": "include", "file": "./rules/require-description-complete-sentence.md"} {"gitdown": "include", "file": "./rules/require-description.md"} {"gitdown": "include", "file": "./rules/require-example.md"} diff --git a/.README/rules/require-asterisk-prefix.md b/.README/rules/require-asterisk-prefix.md new file mode 100644 index 000000000..83b80ad50 --- /dev/null +++ b/.README/rules/require-asterisk-prefix.md @@ -0,0 +1,12 @@ +### `require-asterisk-prefix` + +Requires that each JSDoc line starts with an `*`. + +#### Options + +This rule allows one optional string argument. If it is `"always"` then a problem is raised when there is no asterisk prefix on a given jsdoc line. If it is `"never"` then a problem is raised when there is an asterisk present. The default value is `"always"`. + +||| +|---|---| +|Context|everywhere| +|Tags|N/a| diff --git a/README.md b/README.md index d0e276efe..f9b47782d 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ JSDoc linting rules for ESLint. * [`newline-after-description`](#eslint-plugin-jsdoc-rules-newline-after-description) * [`no-types`](#eslint-plugin-jsdoc-rules-no-types) * [`no-undefined-types`](#eslint-plugin-jsdoc-rules-no-undefined-types) + * [`require-asterisk-prefix`](#eslint-plugin-jsdoc-rules-require-asterisk-prefix) * [`require-description-complete-sentence`](#eslint-plugin-jsdoc-rules-require-description-complete-sentence) * [`require-description`](#eslint-plugin-jsdoc-rules-require-description) * [`require-example`](#eslint-plugin-jsdoc-rules-require-example) @@ -87,6 +88,7 @@ Finally, enable all of the rules that you would like to use. "jsdoc/check-examples": 1, "jsdoc/check-indentation": 1, "jsdoc/check-param-names": 1, // Recommended + "jsdoc/require-asterisk-prefix": 1, "jsdoc/check-syntax": 1, "jsdoc/check-tag-names": 1, // Recommended "jsdoc/check-types": 1, // Recommended @@ -5107,6 +5109,21 @@ function quux () {} ```` + +### require-asterisk-prefix + +Requires that each JSDoc line starts with an `*`. + + +#### Options + +This rule allows one optional string argument. If it is `"always"` then a problem is raised when there is no asterisk prefix on a given jsdoc line. If it is `"never"` then a problem is raised when there is an asterisk present. The default value is `"always"`. + +||| +|---|---| +|Context|everywhere| +|Tags|N/a| + ### require-description-complete-sentence @@ -5121,10 +5138,10 @@ tag descriptions are written in complete sentences, i.e., * A colon or semi-colon followed by two line breaks is still part of the containing paragraph (unlike normal dual line breaks). - + #### Options - + ##### tags If you want additional tags to be checked for their descriptions, you may @@ -5612,7 +5629,7 @@ Requires that all functions have a description. `"tag"`) must have a non-empty description that explains the purpose of the method. - + #### Options An options object may have any of the following properties: @@ -5897,25 +5914,25 @@ Requires that all functions have examples. * All functions must have one or more `@example` tags. * Every example tag must have a non-empty description that explains the method's usage. - + #### Options This rule has an object option. - + ##### exemptedBy Array of tags (e.g., `['type']`) whose presence on the document block avoids the need for an `@example`. Defaults to an empty array. - + ##### avoidExampleOnConstructors Set to `true` to avoid the need for an example on a constructor (whether indicated as such by a jsdoc tag or by being within an ES6 `class`). Defaults to `false`. - + ##### contexts Set this to an array of strings representing the AST context @@ -6093,7 +6110,7 @@ function quux () { Requires a hyphen before the `@param` description. - + #### Options This rule takes one optional string argument. If it is `"always"` then a problem is raised when there is no hyphen before the description. If it is `"never"` then a problem is raised when there is a hyphen before the description. The default value is `"always"`. @@ -6199,7 +6216,7 @@ function quux () { Checks for presence of jsdoc comments, on class declarations as well as functions. - + #### Options Accepts one optional options object with the following optional keys. @@ -7387,7 +7404,7 @@ function quux (foo) { Requires that all function parameters are documented. - + #### Options An options object accepts one optional property: @@ -8487,7 +8504,7 @@ Requires returns are documented. Will also report if multiple `@returns` tags are present. - + #### Options - `exemptedBy` - Array of tags (e.g., `['type']`) whose presence on the document @@ -8952,7 +8969,7 @@ Also impacts behaviors on namepath (or event)-defining and pointing tags: allow `#`, `.`, or `~` at the end (which is not allowed at the end of normal paths). - + #### Options - `allowEmptyNamepaths` (default: true) - Set to `false` to disallow diff --git a/src/index.js b/src/index.js index d91180a37..a7bfdfd9a 100644 --- a/src/index.js +++ b/src/index.js @@ -14,13 +14,14 @@ import matchDescription from './rules/matchDescription'; import newlineAfterDescription from './rules/newlineAfterDescription'; import noTypes from './rules/noTypes'; import noUndefinedTypes from './rules/noUndefinedTypes'; -import requireDescriptionCompleteSentence from './rules/requireDescriptionCompleteSentence'; +import requireAsteriskPrefix from './rules/requireAsteriskPrefix'; import requireDescription from './rules/requireDescription'; +import requireDescriptionCompleteSentence from './rules/requireDescriptionCompleteSentence'; import requireExample from './rules/requireExample'; import requireHyphenBeforeParamDescription from './rules/requireHyphenBeforeParamDescription'; -import requireParamName from './rules/requireParamName'; import requireParam from './rules/requireParam'; import requireParamDescription from './rules/requireParamDescription'; +import requireParamName from './rules/requireParamName'; import requireParamType from './rules/requireParamType'; import requireReturns from './rules/requireReturns'; import requireReturnsCheck from './rules/requireReturnsCheck'; @@ -49,6 +50,7 @@ export default { 'jsdoc/newline-after-description': 'warn', 'jsdoc/no-types': 'off', 'jsdoc/no-undefined-types': 'warn', + 'jsdoc/require-asterisk-prefix': 'off', 'jsdoc/require-description': 'off', 'jsdoc/require-description-complete-sentence': 'off', 'jsdoc/require-example': 'off', @@ -82,6 +84,7 @@ export default { 'newline-after-description': newlineAfterDescription, 'no-types': noTypes, 'no-undefined-types': noUndefinedTypes, + 'require-asterisk-prefix': requireAsteriskPrefix, 'require-description': requireDescription, 'require-description-complete-sentence': requireDescriptionCompleteSentence, 'require-example': requireExample, diff --git a/src/rules/requireAsteriskPrefix.js b/src/rules/requireAsteriskPrefix.js new file mode 100644 index 000000000..5bd4f63f1 --- /dev/null +++ b/src/rules/requireAsteriskPrefix.js @@ -0,0 +1,41 @@ +import iterateJsdoc from '../iterateJsdoc'; + +const prefixMatch = /^(\s+)(?:\*( ?))?/u; +const validPrefix = /^\s+\*(?:\/?$| )/u; + +export default iterateJsdoc(({ + sourceCode, + jsdocNode, + report, +}) => { + const fix = (fixer) => { + const replacement = sourceCode.getText(jsdocNode).split('\n') + .map((line, index) => { + return index && !validPrefix.test(line) ? line.replace(prefixMatch, (_, $1, $2) => { + return `${$1}*${$2 || ' '}`; + }) : line; + }) + .join('\n'); + + return fixer.replaceText(jsdocNode, replacement); + }; + + sourceCode.getText(jsdocNode).split('\n').some((line, index) => { + const lineNum = parseInt(index, 10); + if (lineNum && !validPrefix.test(line)) { + report('Expected JSDoc block to have the prefix.', fix, { + line: lineNum, + }); + + return true; + } + + return false; + }); +}, { + iterateAllJsdocs: true, + meta: { + fixable: 'code', + type: 'layout', + }, +}); diff --git a/test/rules/assertions/requireAsteriskPrefix.js b/test/rules/assertions/requireAsteriskPrefix.js new file mode 100644 index 000000000..8d363687d --- /dev/null +++ b/test/rules/assertions/requireAsteriskPrefix.js @@ -0,0 +1,91 @@ +export default { + invalid: [ + { + code: ` + + /** + @param {Number} foo + */ + function quux (foo) { + // with spaces + } + `, + errors: [ + { + line: 4, + message: 'Expected JSDoc block to have the prefix.', + }, + ], + output: ` + + /** + * @param {Number} foo + */ + function quux (foo) { + // with spaces + } + `, + }, + { + code: ` + /** + @param {Number} foo + */function quux (foo) { + // with spaces + } + `, + errors: [ + { + line: 3, + message: 'Expected JSDoc block to have the prefix.', + }, + ], + output: ` + /** + * @param {Number} foo + */function quux (foo) { + // with spaces + } + `, + }, + ], + valid: [ + { + code: ` + /** + * Desc + * + * @param {Number} foo + * This is more comment. + */ + function quux (foo) { + + } + `, + }, + { + code: ` + /** + * Desc + * + * @param {{ + * foo: Bar, + * bar: Baz + * }} foo + * + */ + function quux (foo) { + + } + `, + }, + { + code: ` + /* <- JSDoc must start with 2 stars. + So this is unchecked. + */ + function quux (foo) {} + `, + }, + ], +}; diff --git a/test/rules/index.js b/test/rules/index.js index 57d114389..2ebbad0b5 100644 --- a/test/rules/index.js +++ b/test/rules/index.js @@ -23,6 +23,7 @@ const ruleTester = new RuleTester(); 'newline-after-description', 'no-types', 'no-undefined-types', + 'require-asterisk-prefix', 'require-description', 'require-description-complete-sentence', 'require-example',