From af1c346ed37a8ae5a0d2a04a560762fc5d568b18 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Fri, 14 May 2021 18:07:07 +0800 Subject: [PATCH] feat(`multiline-blocks`): allow control of how and whether jsdoc blocks can be expressed as single or multiple line blocks --- .README/README.md | 1 + .README/rules/multiline-blocks.md | 84 +++ README.md | 484 ++++++++++++++--- src/index.js | 3 + src/iterateJsdoc.js | 18 +- src/rules/multilineBlocks.js | 261 +++++++++ test/rules/assertions/multilineBlocks.js | 647 +++++++++++++++++++++++ test/rules/ruleNames.json | 1 + 8 files changed, 1421 insertions(+), 78 deletions(-) create mode 100644 .README/rules/multiline-blocks.md create mode 100644 src/rules/multilineBlocks.js create mode 100644 test/rules/assertions/multilineBlocks.js diff --git a/.README/README.md b/.README/README.md index 83d329e95..595e2d0ac 100644 --- a/.README/README.md +++ b/.README/README.md @@ -552,6 +552,7 @@ selector). {"gitdown": "include", "file": "./rules/empty-tags.md"} {"gitdown": "include", "file": "./rules/implements-on-classes.md"} {"gitdown": "include", "file": "./rules/match-description.md"} +{"gitdown": "include", "file": "./rules/multiline-blocks.md"} {"gitdown": "include", "file": "./rules/newline-after-description.md"} {"gitdown": "include", "file": "./rules/no-bad-blocks.md"} {"gitdown": "include", "file": "./rules/no-defaults.md"} diff --git a/.README/rules/multiline-blocks.md b/.README/rules/multiline-blocks.md new file mode 100644 index 000000000..74f3b49f8 --- /dev/null +++ b/.README/rules/multiline-blocks.md @@ -0,0 +1,84 @@ +### `multiline-blocks` + +Controls how and whether jsdoc blocks can be expressed as single or multiple +line blocks. + +Note that if you set `noSingleLineBlocks` and `noMultilineBlocks` to `true` +and configure them in a certain manner, you might effectively be prohibiting +all jsdoc blocks! + +#### Options + +A single options object with the following properties. + +##### `noZeroLineText` (defaults to `true`) + +For multiline blocks, any non-whitespace text immediately after the `/**` and +space will be reported. (Text after a newline is not reported.) + +`noMultilineBlocks` will have priority over this rule if it applies. + +##### `noSingleLineBlocks` (defaults to `false`) + +If this is `true`, any single line blocks will be reported, except those which +are whitelisted in `singleLineTags`. + +##### `singleLineTags` (defaults to `['lends', 'type']`) + +An array of tags which can nevertheless be allowed as single line blocks when +`noSingleLineBlocks` is set. You may set this to a empty array to +cause all single line blocks to be reported. If `'*'` is present, then +the presence of a tag will allow single line blocks (but not if a tag is +missing). + +##### `noMultilineBlocks` (defaults to `false`) + +Requires that jsdoc blocks are restricted to single lines only unless impacted +by the options `minimumLengthForMultiline`, `multilineTags`, or +`allowMultipleTags`. + +##### `minimumLengthForMultiline` (defaults to not being in effect) + +If `noMultilineBlocks` is set with this numeric option, multiline blocks will +be permitted if containing at least the given amount of text. + +If not set, multiline blocks will not be permitted regardless of length unless +a relevant tag is present and `multilineTags` is set. + +##### `multilineTags` (defaults to `['*']`) + +If `noMultilineBlocks` is set with this option, multiline blocks may be allowed +regardless of length as long as a tag or a tag of a certain type is present. + +If `*` is included in the array, the presence of a tags will allow for +multiline blocks (but not when without any tags unless the amount of text is +over an amount specified by `minimumLengthForMultiline`). + +If the array does not include `*` but lists certain tags, the presence of +such a tag will cause multiline blocks to be allowed. + +You may set this to an empty array to prevent any tag from permitting multiple +lines. + +##### `allowMultipleTags` (defaults to `true`) + +If `noMultilineBlocks` is set to `true` with this option and multiple tags are +found in a block, an error will not be reported. + +Since multiple-tagged lines cannot be collapsed into a single line, this option +prevents them from being reported. Set to `false` if you really want to report +any blocks. + +This option will also be applied when there is a block description and a single +tag (since a description cannot precede a tag on a single line, and also +cannot be reliably added after the tag either). + +||| +|---|---| +|Context|everywhere| +|Tags|Any (though `singleLineTags` and `multilineTags` control the application)| +|Recommended|true| +|Settings|| +|Options|`noZeroLineText`, `noSingleLineBlocks`, `singleLineTags`, `noMultilineBlocks`, `minimumLengthForMultiline`, `multilineTags`, `allowMultipleTags`| + + diff --git a/README.md b/README.md index ef5d44b74..3b56f58b5 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ JSDoc linting rules for ESLint. * [`empty-tags`](#eslint-plugin-jsdoc-rules-empty-tags) * [`implements-on-classes`](#eslint-plugin-jsdoc-rules-implements-on-classes) * [`match-description`](#eslint-plugin-jsdoc-rules-match-description) + * [`multiline-blocks`](#eslint-plugin-jsdoc-rules-multiline-blocks) * [`newline-after-description`](#eslint-plugin-jsdoc-rules-newline-after-description) * [`no-bad-blocks`](#eslint-plugin-jsdoc-rules-no-bad-blocks) * [`no-defaults`](#eslint-plugin-jsdoc-rules-no-defaults) @@ -7048,12 +7049,343 @@ function quux (foo) { ```` + +### multiline-blocks + +Controls how and whether jsdoc blocks can be expressed as single or multiple +line blocks. + +Note that if you set `noSingleLineBlocks` and `noMultilineBlocks` to `true` +and configure them in a certain manner, you might effectively be prohibiting +all jsdoc blocks! + + +#### Options + +A single options object with the following properties. + + +##### noZeroLineText (defaults to true) + +For multiline blocks, any non-whitespace text immediately after the `/**` and +space will be reported. (Text after a newline is not reported.) + +`noMultilineBlocks` will have priority over this rule if it applies. + + +##### noSingleLineBlocks (defaults to false) + +If this is `true`, any single line blocks will be reported, except those which +are whitelisted in `singleLineTags`. + + +##### singleLineTags (defaults to ['lends', 'type']) + +An array of tags which can nevertheless be allowed as single line blocks when +`noSingleLineBlocks` is set. You may set this to a empty array to +cause all single line blocks to be reported. If `'*'` is present, then +the presence of a tag will allow single line blocks (but not if a tag is +missing). + + +##### noMultilineBlocks (defaults to false) + +Requires that jsdoc blocks are restricted to single lines only unless impacted +by the options `minimumLengthForMultiline`, `multilineTags`, or +`allowMultipleTags`. + + +##### minimumLengthForMultiline (defaults to not being in effect) + +If `noMultilineBlocks` is set with this numeric option, multiline blocks will +be permitted if containing at least the given amount of text. + +If not set, multiline blocks will not be permitted regardless of length unless +a relevant tag is present and `multilineTags` is set. + + +##### multilineTags (defaults to ['*']) + +If `noMultilineBlocks` is set with this option, multiline blocks may be allowed +regardless of length as long as a tag or a tag of a certain type is present. + +If `*` is included in the array, the presence of a tags will allow for +multiline blocks (but not when without any tags unless the amount of text is +over an amount specified by `minimumLengthForMultiline`). + +If the array does not include `*` but lists certain tags, the presence of +such a tag will cause multiline blocks to be allowed. + +You may set this to an empty array to prevent any tag from permitting multiple +lines. + + +##### allowMultipleTags (defaults to true) + +If `noMultilineBlocks` is set to `true` with this option and multiple tags are +found in a block, an error will not be reported. + +Since multiple-tagged lines cannot be collapsed into a single line, this option +prevents them from being reported. Set to `false` if you really want to report +any blocks. + +This option will also be applied when there is a block description and a single +tag (since a description cannot precede a tag on a single line, and also +cannot be reliably added after the tag either). + +||| +|---|---| +|Context|everywhere| +|Tags|Any (though `singleLineTags` and `multilineTags` control the application)| +|Recommended|true| +|Settings|| +|Options|`noZeroLineText`, `noSingleLineBlocks`, `singleLineTags`, `noMultilineBlocks`, `minimumLengthForMultiline`, `multilineTags`, `allowMultipleTags`| + +The following patterns are considered problems: + +````js +/** Reported up here + * because the rest is multiline + */ +// Message: Should have no text on the "0th" line (after the `/**`). + +/** Reported up here + * because the rest is multiline + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noZeroLineText":true}] +// Message: Should have no text on the "0th" line (after the `/**`). + +/** @abc {aType} aName Reported up here + * because the rest is multiline + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noZeroLineText":true}] +// Message: Should have no text on the "0th" line (after the `/**`). + +/** @tag */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noSingleLineBlocks":true}] +// Message: Single line blocks are not permitted by your configuration. + +/** @tag {someType} */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noSingleLineBlocks":true}] +// Message: Single line blocks are not permitted by your configuration. + +/** @tag {someType} aName */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noSingleLineBlocks":true}] +// Message: Single line blocks are not permitted by your configuration. + +/** @tag */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noSingleLineBlocks":true,"singleLineTags":["someOtherTag"]}] +// Message: Single line blocks are not permitted by your configuration. + +/** desc */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noSingleLineBlocks":true,"singleLineTags":["*"]}] +// Message: Single line blocks are not permitted by your configuration. + +/** + * Desc. + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration. + +/** desc + * + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration. + +/** desc + * + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noMultilineBlocks":true,"noSingleLineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration but fixing would result in a single line block which you have prohibited with `noSingleLineBlocks`. + +/** + * + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration. + +/** + * This is not long enough to be permitted. + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"minimumLengthForMultiline":100,"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration. + +/** + * This is not long enough to be permitted. + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"allowMultipleTags":true,"minimumLengthForMultiline":100,"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration. + +/** + * This has the wrong tags so is not permitted. + * @notTheRightTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"allowMultipleTags":false,"multilineTags":["onlyThisIsExempted"],"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration but the block has a description with a tag. + +/** + * This has too many tags so cannot be fixed ot a single line. + * @oneTag + * @anotherTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"allowMultipleTags":false,"multilineTags":[],"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration but the block has multiple tags. + +/** + * This has a tag and description so cannot be fixed ot a single line. + * @oneTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"allowMultipleTags":false,"multilineTags":[],"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration but the block has a description with a tag. + +/** + * This has no tags so is not permitted. + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"multilineTags":["*"],"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration. + +/** + * This has the wrong tags so is not permitted. + * @notTheRightTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"allowMultipleTags":false,"minimumLengthForMultiline":500,"multilineTags":["onlyThisIsExempted"],"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration but the block has a description with a tag. + +/** + * @lends This can be safely fixed to a single line. + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"multilineTags":[],"noMultilineBlocks":true,"noSingleLineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration. + +/** + * @type {aType} This can be safely fixed to a single line. + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"multilineTags":[],"noMultilineBlocks":true,"noSingleLineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration. + +/** + * @aTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"multilineTags":[],"noMultilineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration. + +/** + * This is a problem when single and multiline are blocked. + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noMultilineBlocks":true,"noSingleLineBlocks":true}] +// Message: Multiline jsdoc blocks are prohibited by your configuration but fixing would result in a single line block which you have prohibited with `noSingleLineBlocks`. +```` + +The following patterns are not considered problems: + +````js +/** Not reported */ + +/** + * Not reported + */ + +/** Reported up here + * because the rest is multiline + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noZeroLineText":false}] + +/** @tag */ + +/** @lends */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noSingleLineBlocks":true}] + +/** @tag */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noSingleLineBlocks":true,"singleLineTags":["tag"]}] + +/** @tag */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noSingleLineBlocks":true,"singleLineTags":["*"]}] + +/** + * + */ + +/** + * + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noMultilineBlocks":false}] + +/** Test */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noMultilineBlocks":true}] + +/** + * This is long enough to be permitted by our config. + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"minimumLengthForMultiline":25,"noMultilineBlocks":true}] + +/** + * This is long enough to be permitted by our config. + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"minimumLengthForMultiline":50,"noMultilineBlocks":true}] + +/** + * This has the right tag so is permitted. + * @theRightTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"multilineTags":["theRightTag"],"noMultilineBlocks":true}] + +/** This has no tags but is single line so is not permitted. */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"multilineTags":["*"],"noMultilineBlocks":true}] + +/** + * This has the wrong tags so is not permitted. + * @notTheRightTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"minimumLengthForMultiline":10,"multilineTags":["onlyThisIsExempted"],"noMultilineBlocks":true}] + +/** + * This has the wrong tags so is not permitted. + * @theRightTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"minimumLengthForMultiline":500,"multilineTags":["theRightTag"],"noMultilineBlocks":true}] + +/** tag */ + +/** + * @lends This is ok per multiline + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"noMultilineBlocks":true,"noSingleLineBlocks":true}] + +/** + * This has too many tags so cannot be fixed ot a single line. + * @oneTag + * @anotherTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"multilineTags":[],"noMultilineBlocks":true}] + +/** + * This has too many tags so cannot be fixed ot a single line. + * @oneTag + * @anotherTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"allowMultipleTags":true,"multilineTags":[],"noMultilineBlocks":true}] + +/** + * This has a tag and description so cannot be fixed ot a single line. + * @oneTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"allowMultipleTags":true,"multilineTags":[],"noMultilineBlocks":true}] + +/** + * This has a tag and description so cannot be fixed ot a single line. + * @oneTag + */ +// "jsdoc/multiline-blocks": ["error"|"warn", {"allowMultipleTags":false,"multilineTags":["oneTag"],"noMultilineBlocks":true}] +```` + + ### newline-after-description Enforces a consistent padding of the block description. - + #### Options This rule allows one optional string argument. If it is `"always"` then a @@ -7296,12 +7628,12 @@ asterisks, but which appear to be intended as jsdoc blocks due to the presence of whitespace followed by whitespace or asterisks, and an at-sign (`@`) and some non-whitespace (as with a jsdoc block tag). - + #### Options Takes an optional options object with the following. - + ##### ignore An array of directives that will not be reported if present at the beginning of @@ -7310,7 +7642,7 @@ a multi-comment block and at-sign `/* @`. Defaults to `['ts-check', 'ts-expect-error', 'ts-ignore', 'ts-nocheck']` (some directives [used by TypeScript](https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html#ts-check)). - + ##### preventAllMultiAsteriskBlocks A boolean (defaulting to `false`) which if `true` will prevent all @@ -7446,10 +7778,10 @@ tag is attached). Unless your `@default` is on a function, you will need to set `contexts` to an appropriate context, including, if you wish, "any". - + #### Options - + ##### noOptionalParamNames Set this to `true` to report the presence of optional parameters. May be @@ -7458,7 +7790,7 @@ the presence of ES6 default parameters (bearing in mind that such "defaults" are only applied when the supplied value is missing or `undefined` but not for `null` or other "falsey" values). - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -7640,10 +7972,10 @@ which are not adequate to satisfy a condition, e.g., not report if there were only a function declaration of the name "ignoreMe" (though it would report by function declarations of other names). - + #### Options - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -7771,10 +8103,10 @@ structures, (whether or not you add a specific `comment` condition). Note that if your parser supports comment AST (as [jsdoc-eslint-parser/](https://github.com/brettz9/jsdoc-eslint-parser/) is designed to do), you can just use ESLint's rule. - + #### Options - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -7875,10 +8207,10 @@ This rule reports types being used on `@param` or `@returns`. The rule is intended to prevent the indication of types on tags where the type information would be redundant with TypeScript. - + #### Options - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -8048,7 +8380,7 @@ reporting on use of that namepath elsewhere) and/or that a tag's `type` is `false` (and should not be checked for types). If the `type` is an array, that array's items will be considered as defined for the purposes of that tag. - + #### Options An option object may have the following key: @@ -8603,7 +8935,7 @@ class Test { Requires that each JSDoc line starts with an `*`. - + #### Options This rule allows an optional string argument. If it is `"always"` then a @@ -8614,7 +8946,7 @@ and use the `tags` option to apply to specific tags only. After the string option, one may add an object with the following. - + ##### tags If you want different values to apply to specific tags, you may use @@ -8888,10 +9220,10 @@ Requires that block description, explicit `@description`, and * Periods after items within the `abbreviations` option array are not treated as sentence endings. - + #### Options - + ##### tags If you want additional tags to be checked for their descriptions, you may @@ -8915,14 +9247,14 @@ its "description" (e.g., for `@returns {someType} some description`, the description is `some description` while for `@some-tag xyz`, the description is `xyz`). - + ##### abbreviations You can provide an `abbreviations` options array to avoid such strings of text being treated as sentence endings when followed by dots. The `.` is not necessary at the end of the array items. - + ##### newlineBeforeCapsAssumesBadSentenceEnd When `false` (the new default), we will not assume capital letters after @@ -9593,7 +9925,7 @@ Requires that all functions have a description. is `"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: @@ -10118,12 +10450,12 @@ Requires that all functions have examples. * 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 @@ -10132,13 +10464,13 @@ block avoids the need for an `@example`. Defaults to an array with so be sure to add back `inheritdoc` if you wish its presence to cause exemption of the rule. - + ##### exemptNoArguments Boolean to indicate that no-argument functions should not be reported for missing `@example` declarations. - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -10150,18 +10482,18 @@ want the rule to apply to any jsdoc block throughout your files. See the ["AST and Selectors"](#eslint-plugin-jsdoc-advanced-ast-and-selectors) section of our README for more on the expected format. - + ##### checkConstructors A value indicating whether `constructor`s should be checked. Defaults to `true`. - + ##### checkGetters A value indicating whether getters should be checked. Defaults to `false`. - + ##### checkSetters A value indicating whether setters should be checked. Defaults to `false`. @@ -10453,10 +10785,10 @@ Checks that: as being when the overview tag is not preceded by anything other than a comment. - + #### Options - + ##### tags The keys of this object are tag names, and the values are configuration @@ -10739,7 +11071,7 @@ function quux () { Requires (or disallows) a hyphen before the `@param` description. - + #### Options This rule takes one optional string argument and an optional options object. @@ -10970,12 +11302,12 @@ function main(argv) { Checks for presence of jsdoc comments, on class declarations as well as functions. - + #### Options Accepts one optional options object with the following optional keys. - + ##### publicOnly This option will insist that missing jsdoc blocks are only reported for @@ -10991,7 +11323,7 @@ otherwise noted): - `cjs` - CommonJS exports are checked for JSDoc comments (Defaults to `true`) - `window` - Window global exports are checked for JSDoc comments - + ##### require An object with the following optional boolean keys which all default to @@ -11004,7 +11336,7 @@ An object with the following optional boolean keys which all default to - `FunctionExpression` - `MethodDefinition` - + ##### contexts Set this to an array of strings or objects representing the additional AST @@ -11020,7 +11352,7 @@ if you are specifying a more precise form in `contexts` (e.g., `MethodDefinition See the ["AST and Selectors"](#eslint-plugin-jsdoc-advanced-ast-and-selectors) section of our README for more on the expected format. - + ##### exemptEmptyConstructors Default: true @@ -11029,7 +11361,7 @@ When `true`, the rule will not report missing jsdoc blocks above constructors with no parameters or return values (this is enabled by default as the class name or description should be seen as sufficient to convey intent). - + ##### exemptEmptyFunctions Default: false. @@ -11038,14 +11370,14 @@ When `true`, the rule will not report missing jsdoc blocks above functions/methods with no parameters or return values (intended where function/method names are sufficient for themselves as documentation). - + ##### checkConstructors A value indicating whether `constructor`s should be checked. Defaults to `true`. When `true`, `exemptEmptyConstructors` may still avoid reporting when no parameters or return values are found. - + ##### checkGetters A value indicating whether getters should be checked. Besides setting as a @@ -11054,7 +11386,7 @@ getters should be checked but only when there is no setter. This may be useful if one only wishes documentation on one of the two accessors. Defaults to `false`. - + ##### checkSetters A value indicating whether setters should be checked. Besides setting as a @@ -11063,7 +11395,7 @@ setters should be checked but only when there is no getter. This may be useful if one only wishes documentation on one of the two accessors. Defaults to `false`. - + ##### enableFixer A boolean on whether to enable the fixer (which adds an empty jsdoc block). @@ -12592,10 +12924,10 @@ export class User { Requires that each `@param` tag has a `description` value. - + #### Options - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -12716,10 +13048,10 @@ Requires that all function parameters have names. > > [JSDoc](https://jsdoc.app/tags-param.html#overview) - + #### Options - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -12852,10 +13184,10 @@ function example(cb) { Requires that each `@param` tag has a `type` value. - + #### Options - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -13153,30 +13485,30 @@ other properties, so in looking at the docs alone without looking at the function signature, it may appear that there is an actual property named `extra`. - + #### Options An options object accepts the following optional properties: - + ##### enableFixer Whether to enable the fixer. Defaults to `true`. - + ##### enableRootFixer Whether to enable the auto-adding of incrementing roots (see the "Fixer" section). Defaults to `true`. Has no effect if `enableFixer` is set to `false`. - + ##### enableRestElementFixer Whether to enable the rest element fixer (see "Rest Element (`RestElement`) insertions"). Defaults to `true`. - + ##### checkRestProperty If set to `true`, will report (and add fixer insertions) for missing rest @@ -13230,13 +13562,13 @@ function quux ({num, ...extra}) { } ``` - + ##### autoIncrementBase Numeric to indicate the number at which to begin auto-incrementing roots. Defaults to `0`. - + ##### unnamedRootBase An array of root names to use in the fixer when roots are missing. Defaults @@ -13262,7 +13594,7 @@ function quux ({foo}, [bar], {baz}) { */ ``` - + ##### exemptedBy Array of tags (e.g., `['type']`) whose presence on the document block @@ -13271,7 +13603,7 @@ avoids the need for a `@param`. Defaults to an array with so be sure to add back `inheritdoc` if you wish its presence to cause exemption of the rule. - + ##### checkTypesPattern When one specifies a type, unless it is of a generic type, like `object` @@ -13306,7 +13638,7 @@ You could set this regular expression to a more expansive list, or you could restrict it such that even types matching those strings would not need destructuring. - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -13318,28 +13650,28 @@ which are checked. See the ["AST and Selectors"](#eslint-plugin-jsdoc-advanced-ast-and-selectors) section of our README for more on the expected format. - + ##### checkConstructors A value indicating whether `constructor`s should be checked. Defaults to `true`. - + ##### checkGetters A value indicating whether getters should be checked. Defaults to `false`. - + ##### checkSetters A value indicating whether setters should be checked. Defaults to `false`. - + ##### checkDestructured Whether to require destructured properties. Defaults to `true`. - + ##### checkDestructuredRoots Whether to check the existence of a corresponding `@param` for root objects @@ -13352,7 +13684,7 @@ implied to be `false` (i.e., the inside of the roots will not be checked either, e.g., it will also not complain if `a` or `b` do not have their own documentation). Defaults to `true`. - + ##### useDefaultObjectProperties Set to `true` if you wish to expect documentation of properties on objects @@ -14955,7 +15287,7 @@ is set to `false` no non-`undefined` returned or resolved value is found. Will also report if multiple `@returns` tags are present. - + #### Options - `exemptAsync` - By default, functions which return a `Promise` that are not @@ -15482,10 +15814,10 @@ Requires that the `@returns` tag has a `description` value. The error will not be reported if the return value is `void` or `undefined` or if it is `Promise` or `Promise`. - + #### Options - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -15638,10 +15970,10 @@ function quux () { Requires that `@returns` tag has `type` value. - + #### Options - + ##### contexts Set this to an array of strings representing the AST context (or an object with @@ -15761,7 +16093,7 @@ Requires that returns are documented. Will also report if multiple `@returns` tags are present. - + #### Options - `checkConstructors` - A value indicating whether `constructor`s should @@ -16782,7 +17114,7 @@ async function foo() { Requires that throw statements are documented. - + #### Options - `exemptedBy` - Array of tags (e.g., `['type']`) whose presence on the @@ -17058,7 +17390,7 @@ Will also report if multiple `@yields` tags are present. See the `next`, `forceRequireNext`, and `nextWithGeneratorTag` options for an option to expect a non-standard `@next` tag. - + #### Options - `exemptedBy` - Array of tags (e.g., `['type']`) whose presence on the @@ -17868,7 +18200,7 @@ function bodies. Will also report if multiple `@yields` tags are present. - + #### Options - `checkGeneratorsOnly` - Avoids checking the function body and merely insists @@ -18346,7 +18678,7 @@ function * quux (foo) { Enforces lines (or no lines) between tags. - + #### Options The first option is a single string set to "always" or "never" (defaults to @@ -18354,12 +18686,12 @@ The first option is a single string set to "always" or "never" (defaults to 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. - + ##### noEndLines (defaults to false) Use with "always" to indicate the normal lines to be added after tags should @@ -18572,7 +18904,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. - + #### Options - `allowEmptyNamepaths` (default: true) - Set to `false` to bulk disallow diff --git a/src/index.js b/src/index.js index 93b6f2471..2966c1954 100644 --- a/src/index.js +++ b/src/index.js @@ -12,6 +12,7 @@ import checkValues from './rules/checkValues'; import emptyTags from './rules/emptyTags'; import implementsOnClasses from './rules/implementsOnClasses'; import matchDescription from './rules/matchDescription'; +import multilineBlocks from './rules/multilineBlocks'; import newlineAfterDescription from './rules/newlineAfterDescription'; import noBadBlocks from './rules/noBadBlocks'; import noDefaults from './rules/noDefaults'; @@ -63,6 +64,7 @@ export default { 'jsdoc/empty-tags': 'warn', 'jsdoc/implements-on-classes': 'warn', 'jsdoc/match-description': 'off', + 'jsdoc/multiline-blocks': 'off', 'jsdoc/newline-after-description': 'warn', 'jsdoc/no-bad-blocks': 'off', 'jsdoc/no-defaults': 'off', @@ -112,6 +114,7 @@ export default { 'empty-tags': emptyTags, 'implements-on-classes': implementsOnClasses, 'match-description': matchDescription, + 'multiline-blocks': multilineBlocks, 'newline-after-description': newlineAfterDescription, 'no-bad-blocks': noBadBlocks, 'no-defaults': noDefaults, diff --git a/src/iterateJsdoc.js b/src/iterateJsdoc.js index 791b17c6c..a572e205c 100644 --- a/src/iterateJsdoc.js +++ b/src/iterateJsdoc.js @@ -244,6 +244,20 @@ const getUtils = ( }); }; + utils.seedTokens = seedTokens; + + utils.addLine = (sourceIndex, tokens) => { + const number = (jsdoc.source[sourceIndex - 1]?.number || 0) + 1; + jsdoc.source.splice(sourceIndex, 0, { + number, + source: '', + tokens: seedTokens(tokens), + }); + + // If necessary, we can rewire the tags (misnamed method) + // rewireSource(jsdoc); + }; + utils.addLines = (tagIndex, tagSourceOffset, numLines) => { const {source: tagSource} = jsdoc.tags[tagIndex]; let lastIndex; @@ -343,8 +357,8 @@ const getUtils = ( return jsdocUtils.isValidTag(context, mode, name, definedTags); }; - utils.hasATag = (name) => { - return jsdocUtils.hasATag(jsdoc, name); + utils.hasATag = (names) => { + return jsdocUtils.hasATag(jsdoc, names); }; utils.hasTag = (name) => { diff --git a/src/rules/multilineBlocks.js b/src/rules/multilineBlocks.js new file mode 100644 index 000000000..ef570478e --- /dev/null +++ b/src/rules/multilineBlocks.js @@ -0,0 +1,261 @@ +import iterateJsdoc from '../iterateJsdoc'; + +export default iterateJsdoc(({ + context, + indent, + jsdoc, + utils, +}) => { + const { + allowMultipleTags = true, + noZeroLineText = true, + noSingleLineBlocks = false, + singleLineTags = ['lends', 'type'], + noMultilineBlocks = false, + minimumLengthForMultiline = Number.POSITIVE_INFINITY, + multilineTags = ['*'], + } = context.options[0] || {}; + + const {source: [{tokens}]} = jsdoc; + const {postDelimiter, description, tag, name, type} = tokens; + const sourceLength = jsdoc.source.length; + + const emptyTokens = () => { + [ + 'start', + 'postDelimiter', + 'tag', + 'type', + 'postType', + 'postTag', + 'name', + 'postName', + 'description', + 'end', + ].forEach((prop) => { + tokens[prop] = ''; + }); + }; + + const isInvalidSingleLine = (tagName) => { + return noSingleLineBlocks && + (!tagName || + !singleLineTags.includes(tagName) && !singleLineTags.includes('*')); + }; + + if (sourceLength === 1) { + if (!isInvalidSingleLine(tag.slice(1))) { + return; + } + + const fixer = () => { + let {tokens: { + postName, postTag, postType, + }} = jsdoc.source[0]; + + // Strip trailing leftovers from single line ending + if (!description) { + if (postName) { + postName = ''; + } else if (postType) { + postType = ''; + // eslint-disable-next-line max-len, no-inline-comments + } else /* istanbul ignore else -- `comment-parser` prevents empty blocks currently per https://github.com/syavorsky/comment-parser/issues/128 */ if (postTag) { + postTag = ''; + } + } + + emptyTokens(); + + utils.addLine(1, { + delimiter: '*', + + // If a description were present, it may have whitespace attached + // due to being at the end of the single line + description: description.trimEnd(), + name, + postDelimiter, + postName, + postTag, + postType, + start: indent + ' ', + tag, + type, + }); + utils.addLine(2, { + end: '*/', + start: indent + ' ', + }); + }; + + utils.reportJSDoc( + 'Single line blocks are not permitted by your configuration.', + null, fixer, true, + ); + + return; + } + + if (noMultilineBlocks) { + if ( + jsdoc.tags.length && + (multilineTags.includes('*') || utils.hasATag(multilineTags)) + ) { + return; + } + + if (jsdoc.description.length >= minimumLengthForMultiline) { + return; + } + + if ( + noSingleLineBlocks && + (!jsdoc.tags.length || + !utils.filterTags(({tag: tg}) => { + return !isInvalidSingleLine(tg); + }).length) + ) { + utils.reportJSDoc( + 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration but fixing would result in a single ' + + 'line block which you have prohibited with `noSingleLineBlocks`.', + ); + } else if (jsdoc.tags.length > 1) { + if (allowMultipleTags) { + return; + } + utils.reportJSDoc( + 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration but the block has multiple tags.', + ); + } else if (jsdoc.tags.length === 1 && jsdoc.description.trim()) { + if (allowMultipleTags) { + return; + } + utils.reportJSDoc( + 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration but the block has a description with a tag.', + ); + } else { + const fixer = () => { + jsdoc.source = [{ + number: 1, + source: '', + tokens: jsdoc.source.reduce((obj, { + tokens: { + description: desc, tag: tg, type: typ, name: nme, + postType, postName, postTag, + }, + }, idx, arr) => { + if (typ) { + obj.type = typ; + } + if (tg && typ && nme) { + obj.postType = postType; + } + if (nme) { + obj.name += nme; + } + if (nme && desc) { + obj.postName = postName; + } + obj.description += desc; + + if ((obj.name || obj.description) && idx === arr.length - 1) { + obj.description += ' '; + } + + // Already filtered for multiple tags + obj.tag += tg; + if (tg) { + obj.postTag = postTag || ' '; + } + + return obj; + }, utils.seedTokens({ + delimiter: '/**', + description: '', + end: '*/', + postDelimiter: ' ', + tag: '', + })), + }]; + }; + utils.reportJSDoc( + 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration.', + null, fixer, + ); + } + + return; + } + + if ( + noZeroLineText && + (tag || description) + ) { + const fixer = () => { + const line = {...tokens}; + emptyTokens(); + const {tokens: {delimiter, start}} = jsdoc.source[1]; + utils.addLine(1, {...line, delimiter, start}); + }; + utils.reportJSDoc( + 'Should have no text on the "0th" line (after the `/**`).', + null, fixer, + ); + } +}, { + iterateAllJsdocs: true, + meta: { + docs: { + description: 'Controls how and whether jsdoc blocks can be expressed as single or multiple line blocks.', + url: 'https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-rules-multiline-blocks', + }, + fixable: 'code', + schema: [ + { + additionalProperies: false, + properties: { + allowMultipleTags: { + type: 'boolean', + }, + minimumLengthForMultiline: { + type: 'integer', + }, + multilineTags: { + anyOf: [ + { + enum: ['*'], + type: 'string', + }, { + items: { + type: 'string', + }, + type: 'array', + }, + ], + }, + noMultilineBlocks: { + type: 'boolean', + }, + noSingleLineBlocks: { + type: 'boolean', + }, + noZeroLineText: { + type: 'boolean', + }, + singleLineTags: { + items: { + type: 'string', + }, + type: 'array', + }, + }, + type: 'object', + }, + ], + type: 'suggestion', + }, +}); diff --git a/test/rules/assertions/multilineBlocks.js b/test/rules/assertions/multilineBlocks.js new file mode 100644 index 000000000..5911ef92c --- /dev/null +++ b/test/rules/assertions/multilineBlocks.js @@ -0,0 +1,647 @@ +export default { + invalid: [ + { + code: ` + /** Reported up here + * because the rest is multiline + */ + `, + errors: [{ + line: 2, + message: 'Should have no text on the "0th" line (after the `/**`).', + }], + output: ` + /** + * Reported up here + * because the rest is multiline + */ + `, + }, + { + code: ` + /** Reported up here + * because the rest is multiline + */ + `, + errors: [{ + line: 2, + message: 'Should have no text on the "0th" line (after the `/**`).', + }], + options: [{ + noZeroLineText: true, + }], + output: ` + /** + * Reported up here + * because the rest is multiline + */ + `, + }, + { + code: ` + /** @abc {aType} aName Reported up here + * because the rest is multiline + */ + `, + errors: [{ + line: 2, + message: 'Should have no text on the "0th" line (after the `/**`).', + }], + options: [{ + noZeroLineText: true, + }], + output: ` + /** + * @abc {aType} aName Reported up here + * because the rest is multiline + */ + `, + }, + { + code: ` + /** @tag */ + `, + errors: [{ + line: 2, + message: 'Single line blocks are not permitted by your configuration.', + }], + options: [{ + noSingleLineBlocks: true, + }], + output: ` + /** + * @tag + */ + `, + }, + { + code: ` + /** @tag {someType} */ + `, + errors: [{ + line: 2, + message: 'Single line blocks are not permitted by your configuration.', + }], + options: [{ + noSingleLineBlocks: true, + }], + output: ` + /** + * @tag {someType} + */ + `, + }, + { + code: ` + /** @tag {someType} aName */ + `, + errors: [{ + line: 2, + message: 'Single line blocks are not permitted by your configuration.', + }], + options: [{ + noSingleLineBlocks: true, + }], + output: ` + /** + * @tag {someType} aName + */ + `, + }, + { + code: ` + /** @tag */ + `, + errors: [{ + line: 2, + message: 'Single line blocks are not permitted by your configuration.', + }], + options: [{ + noSingleLineBlocks: true, + singleLineTags: ['someOtherTag'], + }], + output: ` + /** + * @tag + */ + `, + }, + { + code: ` + /** desc */ + `, + errors: [{ + line: 2, + message: 'Single line blocks are not permitted by your configuration.', + }], + options: [{ + noSingleLineBlocks: true, + singleLineTags: ['*'], + }], + output: ` + /** + * desc + */ + `, + }, + { + code: ` + /** + * Desc. + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration.', + }], + options: [{ + noMultilineBlocks: true, + }], + output: ` + /** Desc. */ + `, + }, + { + code: ` + /** desc + * + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration.', + }], + options: [{ + noMultilineBlocks: true, + }], + output: ` + /** desc */ + `, + }, + { + code: ` + /** desc + * + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration but fixing would result in a single ' + + 'line block which you have prohibited with ' + + '`noSingleLineBlocks`.', + }], + options: [{ + noMultilineBlocks: true, + noSingleLineBlocks: true, + }], + }, + { + code: ` + /** + * + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration.', + }], + options: [{ + noMultilineBlocks: true, + }], + output: ` + /** */ + `, + }, + { + code: ` + /** + * This is not long enough to be permitted. + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration.', + }], + options: [{ + minimumLengthForMultiline: 100, + noMultilineBlocks: true, + }], + output: ` + /** This is not long enough to be permitted. */ + `, + }, + { + code: ` + /** + * This is not long enough to be permitted. + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration.', + }], + options: [{ + allowMultipleTags: true, + minimumLengthForMultiline: 100, + noMultilineBlocks: true, + }], + output: ` + /** This is not long enough to be permitted. */ + `, + }, + { + code: ` + /** + * This has the wrong tags so is not permitted. + * @notTheRightTag + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by your ' + + 'configuration but the block has a description with a tag.', + }], + options: [{ + allowMultipleTags: false, + multilineTags: ['onlyThisIsExempted'], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This has too many tags so cannot be fixed ot a single line. + * @oneTag + * @anotherTag + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration but the block has multiple tags.', + }], + options: [{ + allowMultipleTags: false, + multilineTags: [], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This has a tag and description so cannot be fixed ot a single line. + * @oneTag + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by your ' + + 'configuration but the block has a description with a tag.', + }], + options: [{ + allowMultipleTags: false, + multilineTags: [], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This has no tags so is not permitted. + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration.', + }], + options: [{ + multilineTags: ['*'], + noMultilineBlocks: true, + }], + output: ` + /** This has no tags so is not permitted. */ + `, + }, + { + code: ` + /** + * This has the wrong tags so is not permitted. + * @notTheRightTag + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by your ' + + 'configuration but the block has a description with a tag.', + }], + options: [{ + allowMultipleTags: false, + minimumLengthForMultiline: 500, + multilineTags: ['onlyThisIsExempted'], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * @lends This can be safely fixed to a single line. + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration.', + }], + options: [{ + multilineTags: [], + noMultilineBlocks: true, + noSingleLineBlocks: true, + }], + output: ` + /** @lends This can be safely fixed to a single line. */ + `, + }, + { + code: ` + /** + * @type {aType} This can be safely fixed to a single line. + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration.', + }], + options: [{ + multilineTags: [], + noMultilineBlocks: true, + noSingleLineBlocks: true, + }], + output: ` + /** @type {aType} This can be safely fixed to a single line. */ + `, + }, + { + code: ` + /** + * @aTag + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration.', + }], + options: [{ + multilineTags: [], + noMultilineBlocks: true, + }], + output: ` + /** @aTag */ + `, + }, + { + code: ` + /** + * This is a problem when single and multiline are blocked. + */ + `, + errors: [{ + line: 2, + message: 'Multiline jsdoc blocks are prohibited by ' + + 'your configuration but fixing would result in a single ' + + 'line block which you have prohibited with ' + + '`noSingleLineBlocks`.', + }], + options: [{ + noMultilineBlocks: true, + noSingleLineBlocks: true, + }], + }, + ], + valid: [ + { + code: ` + /** Not reported */ + `, + }, + { + code: ` + /** + * Not reported + */ + `, + }, + { + code: ` + /** Reported up here + * because the rest is multiline + */ + `, + options: [{ + noZeroLineText: false, + }], + }, + { + code: ` + /** @tag */ + `, + }, + { + code: ` + /** @lends */ + `, + options: [{ + noSingleLineBlocks: true, + }], + }, + { + code: ` + /** @tag */ + `, + options: [{ + noSingleLineBlocks: true, + singleLineTags: ['tag'], + }], + }, + { + code: ` + /** @tag */ + `, + options: [{ + noSingleLineBlocks: true, + singleLineTags: ['*'], + }], + }, + { + code: ` + /** + * + */ + `, + }, + { + code: ` + /** + * + */ + `, + options: [{ + noMultilineBlocks: false, + }], + }, + { + code: ` + /** Test */ + `, + options: [{ + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This is long enough to be permitted by our config. + */ + `, + options: [{ + minimumLengthForMultiline: 25, + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This is long enough to be permitted by our config. + */ + `, + options: [{ + minimumLengthForMultiline: 50, + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This has the right tag so is permitted. + * @theRightTag + */ + `, + options: [{ + multilineTags: ['theRightTag'], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** This has no tags but is single line so is not permitted. */ + `, + options: [{ + multilineTags: ['*'], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This has the wrong tags so is not permitted. + * @notTheRightTag + */ + `, + options: [{ + minimumLengthForMultiline: 10, + multilineTags: ['onlyThisIsExempted'], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This has the wrong tags so is not permitted. + * @theRightTag + */ + `, + options: [{ + minimumLengthForMultiline: 500, + multilineTags: ['theRightTag'], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** tag */ + `, + }, + { + code: ` + /** + * @lends This is ok per multiline + */ + `, + options: [{ + noMultilineBlocks: true, + noSingleLineBlocks: true, + }], + }, + { + code: ` + /** + * This has too many tags so cannot be fixed ot a single line. + * @oneTag + * @anotherTag + */ + `, + options: [{ + multilineTags: [], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This has too many tags so cannot be fixed ot a single line. + * @oneTag + * @anotherTag + */ + `, + options: [{ + allowMultipleTags: true, + multilineTags: [], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This has a tag and description so cannot be fixed ot a single line. + * @oneTag + */ + `, + options: [{ + allowMultipleTags: true, + multilineTags: [], + noMultilineBlocks: true, + }], + }, + { + code: ` + /** + * This has a tag and description so cannot be fixed ot a single line. + * @oneTag + */ + `, + options: [{ + allowMultipleTags: false, + multilineTags: ['oneTag'], + noMultilineBlocks: true, + }], + }, + ], +}; diff --git a/test/rules/ruleNames.json b/test/rules/ruleNames.json index aef64baa8..4240808ec 100644 --- a/test/rules/ruleNames.json +++ b/test/rules/ruleNames.json @@ -13,6 +13,7 @@ "empty-tags", "implements-on-classes", "match-description", + "multiline-blocks", "newline-after-description", "no-bad-blocks", "no-defaults",