diff --git a/.README/README.md b/.README/README.md index e0d8ece74..46f6e608a 100644 --- a/.README/README.md +++ b/.README/README.md @@ -469,23 +469,28 @@ properties: 1. For `require-jsdoc`, there is also a `inlineCommentBlock` property. See that rule for details. -2. For other rules, there is a `comment` property which adds to the `context` +1. For `no-missing-syntax` and `no-restricted-syntax`, there is also a + `message` property which allows customization of the message to be shown + when the rule is triggered. +1. For `no-missing-syntax`, there is also a `minimum` property. See that rule. +1. For other rules, there is a `comment` property which adds to the `context` in requiring that the `comment` AST condition is also met, e.g., to require that certain tags are present and/or or types and type operators - are in use. Note that this AST has not been standardized and should be - considered experimental. - In addition to being generally useful for precision in specifying contexts, - it is hoped that the ability to specify required tags on structures can - be used for requiring `@type` or other types for a minimalist yet adequate - specification of types which can be used to compile JavaScript+JSDoc (JJ) - to WebAssembly (e.g., by converting it to TypeSscript and then using - AssemblyScript to convert to WebAssembly). (It may be possible that one - will need to require types with certain structures beyond function - declarations and the like, as well as optionally requiring specification - of number types.) + are in use. Note that this AST (either for `JSDoc*` or `JSDocType*` AST) + has not been standardized and should be considered experimental. + Note that this property might also become obsolete if parsers begin to + include JSDoc-structured AST. Work has begun on [such a parser](https://github.com/brettz9/jsdoc-eslint-parser/) which is intended to support comment AST as + a first class citizen where comment/comment types can be used anywhere + within a normal AST selector but this is also experimental. When using + such a parser, you need not use `comment` and can just use a plain + string context. The determination of the node on which the comment is + attached is also subject to change. It may be currently possible for + different structures to map to the same comment block. This is because + normally when querying to find either the declaration of the function + expression for `const quux = function () {}`, the associated comment would, + in both cases, generally be expected to be on the line above both, rather + than to be immediately preceding the funciton (in the case of the + function). #### Discovering available AST definitions @@ -545,7 +550,7 @@ selector). {"gitdown": "include", "file": "./rules/newline-after-description.md"} {"gitdown": "include", "file": "./rules/no-bad-blocks.md"} {"gitdown": "include", "file": "./rules/no-defaults.md"} -{"gitdown": "include", "file": "./rules/no-missing-syntax"} +{"gitdown": "include", "file": "./rules/no-missing-syntax.md"} {"gitdown": "include", "file": "./rules/no-restricted-syntax.md"} {"gitdown": "include", "file": "./rules/no-types.md"} {"gitdown": "include", "file": "./rules/no-undefined-types.md"} diff --git a/.README/rules/no-missing-syntax.md b/.README/rules/no-missing-syntax.md index e69de29bb..bd801e574 100644 --- a/.README/rules/no-missing-syntax.md +++ b/.README/rules/no-missing-syntax.md @@ -0,0 +1,62 @@ +### `no-missing-syntax` + +This rule lets you report when certain comment structures are always expected. +This rule might be especially useful with [`overrides`](https://eslint.org/docs/user-guide/configuring/configuration-files#how-do-overrides-work) +where you need only require tags and/or types within specific directories +(e.g., to enforce that a plugins or locale directory always has a certain form +of export). + +This (along with `no-restricted-syntax`) is a bit similar to Schematron for +XML or jsontron for JSON--you can validate expectations of there being +arbitrary structures. + +This differs from the rule of the same name in [`eslint-plugin-query`](https://github.com/brettz9/eslint-plugin-query) +in that this always looks for a comment above a structure (whether or not +you have a `comment` condition). + +In addition to being generally useful for precision in requiring contexts, +it is hoped that the ability to specify required tags on structures can +be used for requiring `@type` or other types for a minimalist yet adequate +specification of types which can be used to compile JavaScript+JSDoc (JJ) +to WebAssembly (e.g., by converting it to TypeSscript and then using +AssemblyScript to convert to WebAssembly). (It may be possible that one +will need to require types with certain structures beyond function +declarations and the like, as well as optionally requiring specification +of number types.) + +Note that you can use selectors which make use of negators like `:not()` +including with asterisk, e.g., `*:not(FunctionDeclaration)` to indicate types +which are not adequate to satisfy a condition, e.g., +`FunctionDeclaration:not(FunctionDeclaration[id.name="ignoreMe"])` would +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 +`context` and `comment` properties) where you wish the rule to be applied. + +Use the `minimum` property (defaults to 1) to indicate how many are required +for the rule to be reported. + +Use the `message` property to indicate the specific error to be shown when an +error is reported for that context being found missing. + +Set to `"any"` if you want the rule to apply to any jsdoc block throughout +your files (as is necessary for finding function blocks not attached to a +function declaration or expression, i.e., `@callback` or `@function` (or its +aliases `@func` or `@method`) (including those associated with an `@interface`). + +See the ["AST and Selectors"](#eslint-plugin-jsdoc-advanced-ast-and-selectors) +section of our README for more on the expected format. + +||| +|---|---| +|Context|None except those indicated by `contexts`| +|Tags|Any if indicated by AST| +|Recommended|false| +|Options|`contexts`| + + diff --git a/.README/rules/no-restricted-syntax.md b/.README/rules/no-restricted-syntax.md index e69de29bb..5172dcc4a 100644 --- a/.README/rules/no-restricted-syntax.md +++ b/.README/rules/no-restricted-syntax.md @@ -0,0 +1,41 @@ +### `no-restricted-syntax` + +Reports when certain comment structures are present. + +Note that this rule differs from ESLint's [no-restricted-syntax](https://eslint.org/docs/rules/no-restricted-syntax) rule in expecting values within a single options object's +`contexts` property, and with the property `context` being used in place of +`selector` (as well as allowing for `comment`). The format also differs from +the format expected by [`eslint-plugin-query`](https://github.com/brettz9/eslint-plugin-query). + +Unlike those rules, this is specific to finding comments attached to +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 +`context` and `comment` properties) where you wish the rule to be applied. + +Use the `message` property to indicate the specific error to be shown when an +error is reported for that context being found. + +Set to `"any"` if you want the rule to apply to any jsdoc block throughout +your files (as is necessary for finding function blocks not attached to a +function declaration or expression, i.e., `@callback` or `@function` (or its +aliases `@func` or `@method`) (including those associated with an `@interface`). + +See the ["AST and Selectors"](#eslint-plugin-jsdoc-advanced-ast-and-selectors) +section of our README for more on the expected format. + +||| +|---|---| +|Context|None except those indicated by `contexts`| +|Tags|Any if indicated by AST| +|Recommended|false| +|Options|`contexts`| + + diff --git a/README.md b/README.md index bf4acd3dd..0bd7b633f 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ JSDoc linting rules for ESLint. * [`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) + * [`no-missing-syntax`](#eslint-plugin-jsdoc-rules-no-missing-syntax) + * [`no-restricted-syntax`](#eslint-plugin-jsdoc-rules-no-restricted-syntax) * [`no-types`](#eslint-plugin-jsdoc-rules-no-types) * [`no-undefined-types`](#eslint-plugin-jsdoc-rules-no-undefined-types) * [`require-description-complete-sentence`](#eslint-plugin-jsdoc-rules-require-description-complete-sentence) @@ -541,23 +543,28 @@ properties: 1. For `require-jsdoc`, there is also a `inlineCommentBlock` property. See that rule for details. -2. For other rules, there is a `comment` property which adds to the `context` +1. For `no-missing-syntax` and `no-restricted-syntax`, there is also a + `message` property which allows customization of the message to be shown + when the rule is triggered. +1. For `no-missing-syntax`, there is also a `minimum` property. See that rule. +1. For other rules, there is a `comment` property which adds to the `context` in requiring that the `comment` AST condition is also met, e.g., to require that certain tags are present and/or or types and type operators - are in use. Note that this AST has not been standardized and should be - considered experimental. - In addition to being generally useful for precision in specifying contexts, - it is hoped that the ability to specify required tags on structures can - be used for requiring `@type` or other types for a minimalist yet adequate - specification of types which can be used to compile JavaScript+JSDoc (JJ) - to WebAssembly (e.g., by converting it to TypeSscript and then using - AssemblyScript to convert to WebAssembly). (It may be possible that one - will need to require types with certain structures beyond function - declarations and the like, as well as optionally requiring specification - of number types.) + are in use. Note that this AST (either for `JSDoc*` or `JSDocType*` AST) + has not been standardized and should be considered experimental. + Note that this property might also become obsolete if parsers begin to + include JSDoc-structured AST. Work has begun on [such a parser](https://github.com/brettz9/jsdoc-eslint-parser/) which is intended to support comment AST as + a first class citizen where comment/comment types can be used anywhere + within a normal AST selector but this is also experimental. When using + such a parser, you need not use `comment` and can just use a plain + string context. The determination of the node on which the comment is + attached is also subject to change. It may be currently possible for + different structures to map to the same comment block. This is because + normally when querying to find either the declaration of the function + expression for `const quux = function () {}`, the associated comment would, + in both cases, generally be expected to be on the line above both, rather + than to be immediately preceding the funciton (in the case of the + function). #### Discovering available AST definitions @@ -5862,8 +5869,8 @@ function is adhering. ##### contexts -Set this to an array of strings representing the AST context -where you wish the rule to be applied. +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary @@ -6144,8 +6151,9 @@ checking it by setting it to `false`. ##### contexts -Set this to an array of strings representing the AST context -where you wish the rule to be applied (e.g., `ClassDeclaration` for ES6 +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. +(e.g., `ClassDeclaration` for ES6 classes). Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files. @@ -7222,8 +7230,8 @@ the presence of ES6 default parameters (bearing in mind that such ##### contexts -Set this to an array of strings representing the AST context -where you wish the rule to be applied. +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary for finding function blocks not attached to a function declaration or @@ -7367,6 +7375,139 @@ const a = {}; ```` + +### no-missing-syntax + +This rule lets you report when certain comment structures are always expected. +This rule might be especially useful with [`overrides`](https://eslint.org/docs/user-guide/configuring/configuration-files#how-do-overrides-work) +where you need only require tags and/or types within specific directories +(e.g., to enforce that a plugins or locale directory always has a certain form +of export). + +This (along with `no-restricted-syntax`) is a bit similar to Schematron for +XML or jsontron for JSON--you can validate expectations of there being +arbitrary structures. + +This differs from the rule of the same name in [`eslint-plugin-query`](https://github.com/brettz9/eslint-plugin-query) +in that this always looks for a comment above a structure (whether or not +you have a `comment` condition). + +In addition to being generally useful for precision in requiring contexts, +it is hoped that the ability to specify required tags on structures can +be used for requiring `@type` or other types for a minimalist yet adequate +specification of types which can be used to compile JavaScript+JSDoc (JJ) +to WebAssembly (e.g., by converting it to TypeSscript and then using +AssemblyScript to convert to WebAssembly). (It may be possible that one +will need to require types with certain structures beyond function +declarations and the like, as well as optionally requiring specification +of number types.) + +Note that you can use selectors which make use of negators like `:not()` +including with asterisk, e.g., `*:not(FunctionDeclaration)` to indicate types +which are not adequate to satisfy a condition, e.g., +`FunctionDeclaration:not(FunctionDeclaration[id.name="ignoreMe"])` would +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 +`context` and `comment` properties) where you wish the rule to be applied. + +Use the `minimum` property (defaults to 1) to indicate how many are required +for the rule to be reported. + +Use the `message` property to indicate the specific error to be shown when an +error is reported for that context being found missing. + +Set to `"any"` if you want the rule to apply to any jsdoc block throughout +your files (as is necessary for finding function blocks not attached to a +function declaration or expression, i.e., `@callback` or `@function` (or its +aliases `@func` or `@method`) (including those associated with an `@interface`). + +See the ["AST and Selectors"](#eslint-plugin-jsdoc-advanced-ast-and-selectors) +section of our README for more on the expected format. + +||| +|---|---| +|Context|None except those indicated by `contexts`| +|Tags|Any if indicated by AST| +|Recommended|false| +|Options|`contexts`| + +The following patterns are considered problems: + +````js + +```` + +The following patterns are not considered problems: + +````js + +```` + + + +### no-restricted-syntax + +Reports when certain comment structures are present. + +Note that this rule differs from ESLint's [no-restricted-syntax](https://eslint.org/docs/rules/no-restricted-syntax) rule in expecting values within a single options object's +`contexts` property, and with the property `context` being used in place of +`selector` (as well as allowing for `comment`). The format also differs from +the format expected by [`eslint-plugin-query`](https://github.com/brettz9/eslint-plugin-query). + +Unlike those rules, this is specific to finding comments attached to +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 +`context` and `comment` properties) where you wish the rule to be applied. + +Use the `message` property to indicate the specific error to be shown when an +error is reported for that context being found. + +Set to `"any"` if you want the rule to apply to any jsdoc block throughout +your files (as is necessary for finding function blocks not attached to a +function declaration or expression, i.e., `@callback` or `@function` (or its +aliases `@func` or `@method`) (including those associated with an `@interface`). + +See the ["AST and Selectors"](#eslint-plugin-jsdoc-advanced-ast-and-selectors) +section of our README for more on the expected format. + +||| +|---|---| +|Context|None except those indicated by `contexts`| +|Tags|Any if indicated by AST| +|Recommended|false| +|Options|`contexts`| + +The following patterns are considered problems: + +````js + +```` + +The following patterns are not considered problems: + +````js + +```` + + ### no-types @@ -7375,14 +7516,14 @@ 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 -where you wish the rule to be applied. +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary for finding function blocks not attached to a function declaration or @@ -7548,7 +7689,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: @@ -8115,10 +8256,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 @@ -8142,14 +8283,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 @@ -8820,7 +8961,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: @@ -9294,12 +9435,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 @@ -9308,35 +9449,36 @@ 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 -where you wish the rule to be applied (e.g., `ClassDeclaration` for ES6 +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. +(e.g., `ClassDeclaration` for ES6 classes). Overrides the default contexts (see below). Set to `"any"` if you 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`. @@ -9628,10 +9770,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 @@ -9914,7 +10056,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. @@ -10145,12 +10287,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 @@ -10166,7 +10308,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 @@ -10179,7 +10321,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 @@ -10195,7 +10337,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 @@ -10204,7 +10346,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. @@ -10213,14 +10355,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 @@ -10229,7 +10371,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 @@ -10238,7 +10380,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). @@ -11767,14 +11909,14 @@ 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 -where you wish the rule to be applied. +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary for finding function blocks not attached to a function declaration or @@ -11891,14 +12033,14 @@ 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 -where you wish the rule to be applied. +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary for finding function blocks not attached to a function declaration or @@ -12027,14 +12169,14 @@ 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 -where you wish the rule to be applied. +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary for finding function blocks not attached to a function declaration or @@ -12328,30 +12470,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 @@ -12405,13 +12547,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 @@ -12437,7 +12579,7 @@ function quux ({foo}, [bar], {baz}) { */ ``` - + ##### exemptedBy Array of tags (e.g., `['type']`) whose presence on the document block @@ -12446,7 +12588,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` @@ -12481,40 +12623,40 @@ 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 -where you wish the rule to be applied. Overrides the default -contexts (see below). May be useful for adding such as +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. +Overrides the default contexts (see below). May be useful for adding such as `TSMethodSignature` in TypeScript or restricting the contexts 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 @@ -12527,7 +12669,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 @@ -14130,7 +14272,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 @@ -14657,14 +14799,14 @@ 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 -where you wish the rule to be applied. +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary for finding function blocks not attached to a function declaration or @@ -14813,14 +14955,14 @@ function quux () { Requires that `@returns` tag has `type` value. - + #### Options - + ##### contexts -Set this to an array of strings representing the AST context -where you wish the rule to be applied. +Set this to an array of strings representing the AST context (or an object with +`context` and `comment` properties) where you wish the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary for finding function blocks not attached to a function declaration or @@ -14936,7 +15078,7 @@ Requires that returns are documented. Will also report if multiple `@returns` tags are present. - + #### Options - `checkConstructors` - A value indicating whether `constructor`s should @@ -14961,7 +15103,8 @@ Will also report if multiple `@returns` tags are present. object. This may be useful for flagging that there has been consideration of return type. Defaults to `false`. - `contexts` - Set this to an array of strings representing the AST context - where you wish the rule to be applied. + (or an object with `context` and `comment` properties) where you wish + the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary for finding function blocks not attached to a function declaration or @@ -15956,7 +16099,7 @@ async function foo() { Requires that throw statements are documented. - + #### Options - `exemptedBy` - Array of tags (e.g., `['type']`) whose presence on the @@ -15965,7 +16108,8 @@ Requires that throw statements are documented. so be sure to add back `inheritdoc` if you wish its presence to cause exemption of the rule. - `contexts` - Set this to an array of strings representing the AST context - where you wish the rule to be applied. + (or an object with `context` and `comment` properties) where you wish + the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary for finding function blocks not attached to a function declaration or @@ -16231,7 +16375,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 @@ -16245,7 +16389,8 @@ option to expect a non-standard `@next` tag. that a project is aware of an `undefined`/`void` yield. Defaults to `false`. - `contexts` - Set this to an array of strings representing the AST context - where you wish the rule to be applied. + (or an object with `context` and `comment` properties) where you wish + the rule to be applied. Overrides the default contexts (see below). Set to `"any"` if you want the rule to apply to any jsdoc block throughout your files (as is necessary for finding function blocks not attached to a function declaration or @@ -17040,7 +17185,7 @@ function bodies. Will also report if multiple `@yields` tags are present. - + #### Options - `checkGeneratorsOnly` - Avoids checking the function body and merely insists @@ -17593,7 +17738,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/iterateJsdoc.js b/src/iterateJsdoc.js index 5751c5b73..79caaef5c 100644 --- a/src/iterateJsdoc.js +++ b/src/iterateJsdoc.js @@ -689,6 +689,226 @@ const makeReport = (context, commentNode) => { return report; }; +const stripEncapsulatingBrackets = (container, isArr) => { + if (isArr) { + const firstItem = container[0]; + firstItem.rawType = firstItem.rawType.replace( + /^\{/u, '', + ); + + const lastItem = container[container.length - 1]; + lastItem.rawType = lastItem.rawType.replace(/\}$/u, ''); + + return; + } + container.rawType = container.rawType.replace( + /^\{/u, '', + ).replace(/\}$/u, ''); +}; + +const toCamelCase = (str) => { + return str.toLowerCase().replace(/^[a-z]/, (init) => { + return init.toUpperCase(); + }).replace(/_([a-z])/, (__, wordInit) => { + return wordInit.toUpperCase(); + }); +}; + +/** + * @callback CommentHandler + * @param {string} commentSelector + * @param {Node} jsdoc + * @returns {boolean} + */ + +/** + * @param {Settings} settings + * @returns {CommentHandler} + */ +const commentHandler = (settings) => { + /** + * @type {CommentHandler} + */ + return (commentSelector, jsdoc) => { + const {mode} = settings; + + const selector = esquery.parse(commentSelector); + + const {tokens: { + delimiter: delimiterRoot, + postDelimiter: postDelimiterRoot, + end: endRoot, + description: descriptionRoot, + }} = jsdoc.source[0]; + const ast = { + delimiter: delimiterRoot, + description: descriptionRoot, + + descriptionLines: [], + + // `end` will be overwritten if there are other entries + end: endRoot, + postDelimiter: postDelimiterRoot, + + type: 'JSDocBlock', + }; + + const tags = []; + let lastDescriptionLine; + let lastTag = null; + jsdoc.source.slice(1).forEach((info, idx) => { + const {tokens} = info; + const { + delimiter, + description, + postDelimiter, + start, + tag, + end, + type: rawType, + } = tokens; + + if (tag || end) { + if (lastDescriptionLine === undefined) { + lastDescriptionLine = idx; + } + + // Clean-up with last tag before end or new tag + if (lastTag) { + // Strip out `}` that encapsulates and is not part of + // the type + stripEncapsulatingBrackets(lastTag); + stripEncapsulatingBrackets(lastTag.typeLines, true); + + // With even a multiline type now in full, add parsing + let parsedType = null; + try { + parsedType = jsdoctypeParse(lastTag.rawType, {mode}); + } catch { + // Ignore + } + + // Todo: See about getting jsdoctypeparser to make these + // changes; the AST might also be rethought to use + // fewer types and more properties + const sel = esquery.parse('*[type]'); + esquery.traverse(parsedType, sel, (node) => { + const {type} = node; + + node.type = `JSDocType${toCamelCase(type)}`; + }); + + lastTag.parsedType = parsedType; + } + + if (end) { + ast.end = end; + + return; + } + + const { + // eslint-disable-next-line no-unused-vars -- Discarding + end: ed, + ...tkns + } = tokens; + + const tagObj = { + ...tkns, + descriptionLines: [], + rawType: '', + type: 'JSDocTag', + typeLines: [], + }; + lastTag = tagObj; + + tags.push(tagObj); + } + + if (rawType) { + // Will strip rawType brackets after this tag + lastTag.typeLines.push( + { + delimiter, + postDelimiter, + rawType, + start, + type: 'JSDocTypeLine', + }, + ); + lastTag.rawType += rawType; + } + + if (description) { + const holder = lastTag || ast; + holder.descriptionLines.push({ + delimiter, + description, + postDelimiter, + start, + type: 'JSDocDescriptionLine', + }); + holder.description += holder.description ? + '\n' + description : + description; + } + }); + + ast.lastDescriptionLine = lastDescriptionLine; + ast.tags = tags; + + /* eslint-disable sort-keys-fix/sort-keys-fix -- Keep in order */ + const typeVisitorKeys = { + NAME: [], + NAMED_PARAMETER: ['typeName'], + MEMBER: ['owner'], + UNION: ['left', 'right'], + INTERSECTION: ['left', 'right'], + VARIADIC: ['value'], + RECORD: ['entries'], + RECORD_ENTRY: ['value'], + TUPLE: ['entries'], + GENERIC: ['subject', 'objects'], + MODULE: ['value'], + OPTIONAL: ['value'], + NULLABLE: ['value'], + NOT_NULLABLE: ['value'], + FUNCTION: ['params', 'returns', 'this', 'new'], + ARROW: ['params', 'returns'], + ANY: [], + UNKNOWN: [], + INNER_MEMBER: ['owner'], + INSTANCE_MEMBER: ['owner'], + STRING_VALUE: [], + NUMBER_VALUE: [], + EXTERNAL: [], + FILE_PATH: [], + PARENTHESIS: ['value'], + TYPE_QUERY: ['name'], + KEY_QUERY: ['value'], + IMPORT: ['path'], + /* eslint-enable sort-keys-fix/sort-keys-fix -- Keep in order */ + }; + + const convertedTypeVisitorKeys = Object.entries( + typeVisitorKeys, + ).reduce((object, [key, value]) => { + object[`JSDocType${toCamelCase(key)}`] = value; + + return object; + }, {}); + + return esquery.matches(ast, selector, null, { + visitorKeys: { + ...convertedTypeVisitorKeys, + JSDocBlock: ['tags', 'descriptionLines'], + JSDocDescriptionLine: [], + JSDocTag: ['descriptionLines', 'typeLines', 'parsedType'], + }, + }); + }; +}; + /** * @typedef {ReturnType} Utils * @typedef {ReturnType} Settings @@ -708,6 +928,7 @@ const makeReport = (context, commentNode) => { */ const iterate = ( + info, indent, jsdoc, ruleConfig, context, lines, jsdocNode, node, settings, sourceCode, iterator, state, iteratingAll, @@ -747,6 +968,7 @@ const iterate = ( context, globalState, indent, + info, iteratingAll, jsdoc, jsdocNode, @@ -793,6 +1015,7 @@ const iterateAllJsdocs = (iterator, ruleConfig) => { ); iterate( + null, indent, jsdoc, ruleConfig, context, lines, jsdocNode, node, settings, sourceCode, iterator, @@ -901,19 +1124,12 @@ export { parseComment, }; -const toCamelCase = (str) => { - return str.toLowerCase().replace(/^[a-z]/, (init) => { - return init.toUpperCase(); - }).replace(/_([a-z])/, (__, wordInit) => { - return wordInit.toUpperCase(); - }); -}; - /** * @param {JsdocVisitor} iterator * @param {{ * meta: any, * contextDefaults?: true | string[], + * contextSelected?: true, * iterateAllJsdocs?: true, * }} ruleConfig */ @@ -946,9 +1162,9 @@ export default function iterateJsdoc (iterator, ruleConfig) { */ create (context) { let contexts; - if (ruleConfig.contextDefaults) { + if (ruleConfig.contextDefaults || ruleConfig.contextSelected) { contexts = jsdocUtils.enforcedContexts(context, ruleConfig.contextDefaults); - if (contexts.includes('any')) { + if (contexts?.includes('any')) { return iterateAllJsdocs(iterator, ruleConfig).create(context); } } @@ -957,9 +1173,10 @@ export default function iterateJsdoc (iterator, ruleConfig) { if (!settings) { return {}; } - const {mode} = settings; const {lines} = sourceCode; + const state = {}; + const checkJsdoc = (info, handler, node) => { const jsdocNode = getJSDocComment(sourceCode, node, settings); @@ -981,232 +1198,42 @@ export default function iterateJsdoc (iterator, ruleConfig) { } iterate( - indent, jsdoc, + info, indent, jsdoc, ruleConfig, context, lines, jsdocNode, node, - settings, sourceCode, iterator, + settings, sourceCode, iterator, state, ); }; - if (ruleConfig.contextDefaults) { - return jsdocUtils.getContextObject( + let contextObject = {}; + + if (contexts && (ruleConfig.contextDefaults || ruleConfig.contextSelected)) { + contextObject = jsdocUtils.getContextObject( contexts, checkJsdoc, - (commentSelector, jsdoc) => { - const selector = esquery.parse(commentSelector); - - const {tokens: { - delimiter: delimiterRoot, - postDelimiter: postDelimiterRoot, - end: endRoot, - description: descriptionRoot, - }} = jsdoc.source[0]; - const obj = { - delimiter: delimiterRoot, - description: descriptionRoot, - - // This will be overwritten if there are other entries - end: endRoot, - - postDelimiter: postDelimiterRoot, - }; - const ast = { - type: 'JSDocBlock', - ...obj, - }; - - const tags = []; - let lastDescriptionLine; - let lastTagDescriptionHolder = false; - let lastTagTypeHolder = false; - jsdoc.source.slice(1).forEach((info, idx) => { - const {tokens} = info; - if (tokens.tag || tokens.end) { - if (lastDescriptionLine === undefined) { - lastDescriptionLine = idx; - } - if (tokens.end) { - ast.end = tokens.end; - } else { - const { - // eslint-disable-next-line no-unused-vars -- Discarding - end: ed, - ...tkns - } = tokens; - - let parsedType = null; - try { - parsedType = jsdoctypeParse(tokens.type, {mode}); - } catch { - // Ignore - } - - // Todo: See about getting jsdoctypeparser to make these - // changes; the AST might also be rethought to use - // fewer types and more properties - const sel = esquery.parse('*[type]'); - esquery.traverse(parsedType, sel, (node) => { - const {type} = node; - - node.type = `JSDocType${toCamelCase(type)}`; - }); - - const tag = { - ...tkns, - descriptionLines: [], - parsedType, - rawType: tokens.type, - type: 'JSDocTag', - typeLines: [], - }; - if (!lastTagDescriptionHolder) { - const { - delimiter, - description, - postDelimiter, - start, - } = tkns; - tag.descriptionLines = lastTagDescriptionHolder = [ - { - delimiter, - description, - postDelimiter, - start, - type: 'JSDocDescriptionLine', - }, - ]; - } - if (!lastTagTypeHolder) { - const { - delimiter, - type: rawType, - postDelimiter, - start, - } = tkns; - tag.typeLines = lastTagTypeHolder = [ - { - delimiter, - postDelimiter, - rawType, - start, - type: 'JSDocTypeLine', - }, - ]; - } - tags.push(tag); - } - - return; - - // Multi-line tag descriptions - } - - if (lastTagDescriptionHolder && tokens.description) { - const { - delimiter, - description, - postDelimiter, - start, - } = tokens; - lastTagDescriptionHolder.push( - { - delimiter, - description, - postDelimiter, - start, - type: 'JSDocDescriptionLine', - }, - ); - - return; - } - if (lastTagTypeHolder && tokens.type) { - const { - delimiter, - postDelimiter, - start, - type: rawType, - } = tokens; - lastTagDescriptionHolder.push( - { - delimiter, - postDelimiter, - rawType, - start, - type: 'JSDocTypeLine', - }, - ); - - return; - } - ast.description += '\n' + tokens.description; - }); - - ast.lastDescriptionLine = lastDescriptionLine; - ast.tags = tags; - - console.log('jsdoc', jsdoc); - console.log('ast', ast); - - /* eslint-disable sort-keys-fix/sort-keys-fix -- Keep in order */ - const typeVisitorKeys = { - NAME: [], - NAMED_PARAMETER: ['typeName'], - MEMBER: ['owner'], - UNION: ['left', 'right'], - INTERSECTION: ['left', 'right'], - VARIADIC: ['value'], - RECORD: ['entries'], - RECORD_ENTRY: ['value'], - TUPLE: ['entries'], - GENERIC: ['subject', 'objects'], - MODULE: ['value'], - OPTIONAL: ['value'], - NULLABLE: ['value'], - NOT_NULLABLE: ['value'], - FUNCTION: ['params', 'returns', 'this', 'new'], - ARROW: ['params', 'returns'], - ANY: [], - UNKNOWN: [], - INNER_MEMBER: ['owner'], - INSTANCE_MEMBER: ['owner'], - STRING_VALUE: [], - NUMBER_VALUE: [], - EXTERNAL: [], - FILE_PATH: [], - PARENTHESIS: ['value'], - TYPE_QUERY: ['name'], - KEY_QUERY: ['value'], - IMPORT: ['path'], - /* eslint-enable sort-keys-fix/sort-keys-fix -- Keep in order */ - }; - - const convertedTypeVisitorKeys = Object.entries( - typeVisitorKeys, - ).reduce((object, [key, value]) => { - object[`JSDocType${toCamelCase(key)}`] = value; - - return object; - }, {}); - - return esquery.matches(ast, selector, null, { - visitorKeys: { - ...convertedTypeVisitorKeys, - JSDocBlock: ['tags'], - JSDocDescriptionLine: [], - JSDocTag: ['descriptionLines', 'typeLines', 'parsedType'], - }, - }); - }, + commentHandler(settings), ); + } else { + [ + 'ArrowFunctionExpression', + 'FunctionDeclaration', + 'FunctionExpression', + ].forEach((prop) => { + contextObject[prop] = checkJsdoc.bind(null, { + selector: prop, + }, null); + }); } - const checkJsdocNoHandler = checkJsdoc.bind(null, null, null); + if (ruleConfig.exit) { + contextObject['Program:exit'] = () => { + ruleConfig.exit({ + context, + state, + }); + }; + } - return { - ArrowFunctionExpression: checkJsdocNoHandler, - FunctionDeclaration: checkJsdocNoHandler, - FunctionExpression: checkJsdocNoHandler, - }; + return contextObject; }, meta: ruleConfig.meta, }; diff --git a/src/jsdocUtils.js b/src/jsdocUtils.js index 1d8acc7b3..8b7286be1 100644 --- a/src/jsdocUtils.js +++ b/src/jsdocUtils.js @@ -1091,15 +1091,24 @@ const getContextObject = (contexts, checkJsdoc, handler) => { contexts.forEach((prop) => { if (typeof prop === 'object') { + const selInfo = { + selector: prop.context, + }; if (prop.comment) { properties[prop.context] = checkJsdoc.bind( - null, null, handler.bind(null, prop.comment), + null, { + ...selInfo, + comment: prop.comment, + }, handler.bind(null, prop.comment), ); } else { - properties[prop.context] = checkJsdoc.bind(null, null, null); + properties[prop.context] = checkJsdoc.bind(null, selInfo, null); } } else { - properties[prop] = checkJsdoc.bind(null, null, null); + const selInfo = { + selector: prop, + }; + properties[prop] = checkJsdoc.bind(null, selInfo, null); } }); diff --git a/src/rules/noMissingSyntax.js b/src/rules/noMissingSyntax.js index e69de29bb..3654796d7 100644 --- a/src/rules/noMissingSyntax.js +++ b/src/rules/noMissingSyntax.js @@ -0,0 +1,121 @@ +import iterateJsdoc from '../iterateJsdoc'; + +const setDefaults = (state) => { + if (!state.selectorMap) { + state.selectorMap = {}; + } +}; + +const incrementSelector = (state, selector, comment) => { + if (!state.selectorMap[selector]) { + state.selectorMap[selector] = {}; + } + if (!state.selectorMap[selector][comment]) { + state.selectorMap[selector][comment] = 0; + } + state.selectorMap[selector][comment]++; +}; + +export default iterateJsdoc(({ + info: {selector, comment}, + state, +}) => { + setDefaults(state); + + incrementSelector(state, selector, comment); +}, { + contextSelected: true, + exit ({ + context, state, + }) { + if (!context.options.length) { + context.report({ + loc: { + start: { + column: 1, + line: 1, + }, + }, + message: 'Rule `no-restricted-syntax` is missing a `context` option.', + }); + + return; + } + setDefaults(state); + + const { + contexts, + } = context.options[0]; + + // Report when MISSING + contexts.some((cntxt) => { + const contextStr = typeof cntxt === 'object' ? cntxt.context : cntxt; + const comment = cntxt?.comment ?? ''; + + if (!state.selectorMap[contextStr] || + !state.selectorMap[contextStr][comment] || + state.selectorMap[contextStr][comment] < (cntxt?.minimum ?? 1) + ) { + const message = cntxt?.message ?? 'Syntax is required: {{context}}'; + context.report({ + data: { + context: contextStr, + }, + loc: { + end: {line: 1}, + start: {line: 1}, + }, + message, + }); + + return true; + } + + return false; + }); + }, + meta: { + docs: { + description: 'Reports when certain comment structures are always expected.', + url: 'https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-rules-no-missing-syntax', + }, + fixable: 'code', + schema: [ + { + additionalProperties: false, + properties: { + contexts: { + items: { + anyOf: [ + { + type: 'string', + }, + { + additionalProperties: false, + properties: { + comment: { + type: 'string', + }, + context: { + type: 'string', + }, + message: { + type: 'string', + }, + minimum: { + type: 'integer', + }, + }, + type: 'object', + }, + ], + }, + type: 'array', + }, + }, + type: 'object', + }, + ], + type: 'suggestion', + }, +}); diff --git a/src/rules/noRestrictedSyntax.js b/src/rules/noRestrictedSyntax.js index e69de29bb..8034f972e 100644 --- a/src/rules/noRestrictedSyntax.js +++ b/src/rules/noRestrictedSyntax.js @@ -0,0 +1,74 @@ +import iterateJsdoc from '../iterateJsdoc'; + +export default iterateJsdoc(({ + context, + info: {selector, comment}, + report, +}) => { + if (!context.options.length) { + report('Rule `no-restricted-syntax` is missing a `context` option.'); + + return; + } + const {contexts} = context.options[0]; + + const foundContext = contexts.find((cntxt) => { + return cntxt === selector || + typeof cntxt === 'object' && selector === cntxt.context && + comment === cntxt.comment; + }); + + const contextStr = typeof foundContext === 'object' ? + foundContext.context : + foundContext; + const message = foundContext?.message ?? + 'Syntax is restricted: {{context}}.'; + + report(message, null, null, { + context: contextStr, + }); +}, { + contextSelected: true, + meta: { + docs: { + description: 'Reports when certain comment structures are present.', + url: 'https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-rules-no-restricted-syntax', + }, + fixable: 'code', + schema: [ + { + additionalProperties: false, + properties: { + contexts: { + items: { + anyOf: [ + { + type: 'string', + }, + { + additionalProperties: false, + properties: { + comment: { + type: 'string', + }, + context: { + type: 'string', + }, + message: { + type: 'string', + }, + }, + type: 'object', + }, + ], + }, + type: 'array', + }, + }, + required: ['contexts'], + type: 'object', + }, + ], + type: 'suggestion', + }, +}); diff --git a/src/rules/requireJsdoc.js b/src/rules/requireJsdoc.js index 7a4a253d9..bfb392815 100644 --- a/src/rules/requireJsdoc.js +++ b/src/rules/requireJsdoc.js @@ -196,7 +196,7 @@ export default { publicOnly, exemptEmptyFunctions, exemptEmptyConstructors, enableFixer, } = getOptions(context); - const checkJsDoc = (isFunctionContext, handler, node) => { + const checkJsDoc = (info, handler, node) => { const jsDocNode = getJSDocComment(sourceCode, node, settings); if (jsDocNode) { @@ -214,7 +214,7 @@ export default { if ( // Avoid reporting param-less, return-less functions (when // `exemptEmptyFunctions` option is set) - exemptEmptyFunctions && isFunctionContext || + exemptEmptyFunctions && info.isFunctionContext || // Avoid reporting param-less, return-less constructor methods (when // `exemptEmptyConstructors` option is set) @@ -304,7 +304,7 @@ export default { ['VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration'].includes(node.parent.type) || ['Property', 'ObjectProperty', 'ClassProperty'].includes(node.parent.type) && node === node.parent.value ) { - checkJsDoc(true, null, node); + checkJsDoc({isFunctionContext: true}, null, node); } }, @@ -313,7 +313,7 @@ export default { return; } - checkJsDoc(false, null, node); + checkJsDoc({isFunctionContext: false}, null, node); }, ClassExpression (node) { @@ -321,7 +321,7 @@ export default { return; } - checkJsDoc(false, null, node); + checkJsDoc({isFunctionContext: false}, null, node); }, FunctionDeclaration (node) { @@ -329,12 +329,12 @@ export default { return; } - checkJsDoc(true, null, node); + checkJsDoc({isFunctionContext: true}, null, node); }, FunctionExpression (node) { if (hasOption('MethodDefinition') && node.parent.type === 'MethodDefinition') { - checkJsDoc(true, null, node); + checkJsDoc({isFunctionContext: true}, null, node); return; } @@ -347,7 +347,7 @@ export default { ['VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration'].includes(node.parent.type) || ['Property', 'ObjectProperty', 'ClassProperty'].includes(node.parent.type) && node === node.parent.value ) { - checkJsDoc(true, null, node); + checkJsDoc({isFunctionContext: true}, null, node); } }, }; diff --git a/test/rules/assertions/noMissingSyntax.js b/test/rules/assertions/noMissingSyntax.js index e69de29bb..ee1f34d3e 100644 --- a/test/rules/assertions/noMissingSyntax.js +++ b/test/rules/assertions/noMissingSyntax.js @@ -0,0 +1,164 @@ +export default { + invalid: [ + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + `, + errors: [ + { + message: 'Syntax is required: FunctionDeclaration', + }, + ], + options: [{ + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTypeUnion[left.name="Foo"])', + context: 'FunctionDeclaration', + }, + ], + }], + }, + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + `, + errors: [ + { + message: 'Problematic function syntax: `FunctionDeclaration`.', + }, + ], + options: [{ + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTypeUnion[left.name="Foo"])', + context: 'FunctionDeclaration', + message: 'Problematic function syntax: `{{context}}`.', + }, + ], + }], + }, + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + `, + errors: [ + { + message: 'Syntax is required: FunctionDeclaration', + }, + ], + options: [{ + contexts: [ + 'FunctionDeclaration', + ], + }], + }, + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + `, + errors: [ + { + message: 'Rule `no-restricted-syntax` is missing a `context` option.', + }, + ], + }, + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + + `, + errors: [ + { + message: 'Syntax is required: FunctionDeclaration', + }, + ], + options: [{ + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTypeUnion[left.name="Bar"])', + context: 'FunctionDeclaration', + minimum: 2, + }, + ], + }], + }, + ], + valid: [ + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + `, + options: [{ + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTypeUnion[left.name="Bar"])', + context: 'FunctionDeclaration', + }, + ], + }], + }, + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + + /** + * @implements {Bar|Foo} + */ + function bar () { + + } + + /** + * @implements {Bar|Foo} + */ + function baz () { + + } + `, + options: [{ + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTypeUnion[left.name="Bar"])', + context: 'FunctionDeclaration', + minimum: 2, + }, + ], + }], + }, + ], +}; diff --git a/test/rules/assertions/noRestrictedSyntax.js b/test/rules/assertions/noRestrictedSyntax.js index e69de29bb..721cd8100 100644 --- a/test/rules/assertions/noRestrictedSyntax.js +++ b/test/rules/assertions/noRestrictedSyntax.js @@ -0,0 +1,149 @@ +export default { + invalid: [ + { + code: ` + /** + * + */ + function quux () { + + } + `, + errors: [ + { + message: 'Syntax is restricted: FunctionDeclaration.', + }, + ], + options: [{ + contexts: [ + 'FunctionDeclaration', + ], + }], + }, + { + code: ` + /** + * + */ + function quux () { + + } + `, + errors: [ + { + message: 'Oops: `FunctionDeclaration`.', + }, + ], + options: [{ + contexts: [ + { + context: 'FunctionDeclaration', + message: 'Oops: `{{context}}`.', + }, + ], + }], + }, + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + `, + errors: [ + { + message: 'Syntax is restricted: FunctionDeclaration.', + }, + ], + options: [{ + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTypeUnion[left.name="Bar"])', + context: 'FunctionDeclaration', + }, + ], + }], + }, + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + `, + errors: [ + { + message: 'The bar one: FunctionDeclaration.', + }, + ], + options: [{ + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTypeUnion[left.name="Foo"])', + context: 'FunctionDeclaration', + message: 'The foo one: {{context}}.', + }, + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTypeUnion[left.name="Bar"])', + context: 'FunctionDeclaration', + message: 'The bar one: {{context}}.', + }, + ], + }], + }, + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + `, + errors: [ + { + message: 'Rule `no-restricted-syntax` is missing a `context` option.', + }, + ], + }, + ], + valid: [ + { + code: ` + /** + * + */ + function quux () { + + } + `, + options: [{ + contexts: [ + 'FunctionExpression', + ], + }], + }, + { + code: ` + /** + * @implements {Bar|Foo} + */ + function quux () { + + } + `, + options: [{ + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTypeUnion[left.name="Foo"])', + context: 'FunctionDeclaration', + }, + ], + }], + }, + ], +}; diff --git a/test/rules/assertions/requireDescription.js b/test/rules/assertions/requireDescription.js index eb6fdcf87..58ace7c1d 100644 --- a/test/rules/assertions/requireDescription.js +++ b/test/rules/assertions/requireDescription.js @@ -519,7 +519,6 @@ export default { { code: ` /** - * @class * @implements {Bar} */ class quux { @@ -535,14 +534,83 @@ export default { { contexts: [ { - // comment: 'JSDocBlock[whitespace=/\\s{4}/] > JSDocTag[name="class"]', - comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTag)', + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTag[rawType="Bar"])', context: 'ClassDeclaration', }, ], descriptionStyle: 'tag', }, ], + settings: { + jsdoc: { + implementsReplacesDocs: false, + }, + }, + }, + { + code: ` + /** + * Has some + * description already. + * @implements {Bar} + */ + class quux { + + } + `, + errors: [ + { + message: 'Missing JSDoc @description declaration.', + }, + ], + options: [ + { + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTag[rawType="Bar"])', + context: 'ClassDeclaration', + }, + ], + descriptionStyle: 'tag', + }, + ], + settings: { + jsdoc: { + implementsReplacesDocs: false, + }, + }, + }, + { + code: ` + /** + * @implements {Bar + * | Foo} + */ + class quux { + + } + `, + errors: [ + { + message: 'Missing JSDoc @description declaration.', + }, + ], + options: [ + { + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTypeUnion[left.name="Bar"])', + context: 'ClassDeclaration', + }, + ], + descriptionStyle: 'tag', + }, + ], + settings: { + jsdoc: { + implementsReplacesDocs: false, + }, + }, }, ], valid: [ @@ -892,5 +960,53 @@ export default { }, ], }, + { + code: ` + /** + * @implements {Bar} + */ + class quux { + + } + `, + options: [ + { + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=/\\s{4}/]:has(JSDocTag[rawType="class"])', + context: 'ClassDeclaration', + }, + ], + descriptionStyle: 'tag', + }, + ], + settings: { + jsdoc: { + implementsReplacesDocs: false, + }, + }, + }, + { + code: ` + /** + * Has some + * description already. + */ + class quux { + + } + `, + options: [ + { + contexts: [ + { + comment: 'JSDocBlock[postDelimiter=""]:has(JSDocTag[rawType="{Bar}"])', + context: 'ClassDeclaration', + }, + ], + descriptionStyle: 'tag', + }, + ], + }, ], };