diff --git a/.README/README.md b/.README/README.md
index 0401c82bb..7dac6a01d 100644
--- a/.README/README.md
+++ b/.README/README.md
@@ -380,6 +380,49 @@ key nor the value will be reported. Thus in `check-types`, this fact can
be used to allow both `object` and `Object` if one has a `preferredTypes`
key `object: 'Object'` and `Object: 'object'`.
+### `structuredTags`
+
+An object indicating tags whose types and names/namepaths (whether defining or
+referencing namepaths) will be checked, subject to configuration. If the tags
+have predefined behavior or `allowEmptyNamepaths` behavior, this option will
+override that behavior for any specified tags, though this option can also be
+used for tags without predefined behavior. Its keys are tag names and its
+values are objects with the following optional properties:
+ - `name` - String set to one of the following:
+ - `"text"` - When a name is present, plain text will be allowed in the
+ name position (non-whitespace immediately after the tag and whitespace),
+ e.g., in `@throws This is an error`, "This" would normally be the name,
+ but "text" allows non-name text here also. This is the default.
+ - `"namepath-defining"` - As with `namepath-referencing`, but also
+ indicates the tag adds a namepath to definitions, e.g., to prevent
+ `no-undefined-types` from reporting references to that namepath.
+ - `"namepath-referencing"` - This will cause any name position to be
+ checked to ensure it is a valid namepath. You might use this to ensure
+ that tags which normally allow free text, e.g., `@see` will instead
+ require a namepath.
+ - `false` - This will disallow any text in the name position.
+ - `type`:
+ - `true` - Allows valid types within brackets. This is the default.
+ - `false` - Explicitly disallows any brackets or bracketed type. You
+ might use this with `@throws` to suggest that only free form text
+ is being input or with `@augments` (for jsdoc mode) to disallow
+ Closure-style bracketed usage along with a required namepath.
+ - `required` - Array of one of the following (defaults to an empty array,
+ meaning none are required):
+ - One or both of the following strings (if both are included, then both
+ are required):
+ - `"name"` - Indicates that a name position is required (not just that
+ if present, it is a valid namepath). You might use this with `see`
+ to insist that a value (or namepath, depending on the `name` value)
+ is always present.
+ - `"type"` - Indicates that the type position (within curly brackets)
+ is required (not just that if present, it is a valid type). You
+ might use this with `@throws` or `@typedef` which might otherwise
+ normally have their types optional. See the type groups 3-5 above.
+ - `"typeOrName"` - Must have either type (e.g., `@throws {aType}`) or
+ name (`@throws Some text`); does not require that both exist but
+ disallows just an empty tag.
+
## Advanced
### AST and Selectors
diff --git a/.README/rules/check-types.md b/.README/rules/check-types.md
index 06a2d9860..1b84de147 100644
--- a/.README/rules/check-types.md
+++ b/.README/rules/check-types.md
@@ -120,6 +120,10 @@ Boolean | **boolean** | **boolean** | `(true) instanceof Boolean` -> **`false`**
Number | **number** | **number** | `(41) instanceof Number` -> **`false`**
String | **string** | **string** | `("test") instanceof String` -> **`false`**
+If you define your own tags and don't wish their bracketed portions checked
+for types, you can use `settings.jsdoc.structuredTags` with a tag `type` of
+`false`.
+
|||
|---|---|
|Context|everywhere|
@@ -127,6 +131,6 @@ String | **string** | **string** | `("test") instanceof String` -> **`false`**
|Aliases|`constructor`, `const`, `extends`, `var`, `arg`, `argument`, `prop`, `return`, `exception`, `yield`|
|Closure-only|`package`, `private`, `protected`, `public`, `static`|
|Options|`noDefaults`, `exemptTagContexts`, `unifyParentAndChildTypeChecks`|
-|Settings|`preferredTypes`, `mode`|
+|Settings|`preferredTypes`, `mode`, `structuredTags`|
diff --git a/.README/rules/no-undefined-types.md b/.README/rules/no-undefined-types.md
index a6081d704..e72fbbbd7 100644
--- a/.README/rules/no-undefined-types.md
+++ b/.README/rules/no-undefined-types.md
@@ -36,6 +36,11 @@ Also note that if there is an error [parsing](https://github.com/jsdoctypeparser
types for a tag, the function will silently ignore that tag, leaving it to
the `valid-types` rule to report parsing errors.
+If you define your own tags, you can use `settings.jsdoc.structuredTags`
+to indicate that a tag's `name` is "namepath-defining" (and should prevent
+reporting on use of that namepath elsewhere) and/or that a tag's `type` is
+`false` (and should not be checked for types).
+
#### Options
An option object may have the following key:
@@ -51,6 +56,6 @@ An option object may have the following key:
|Aliases|`constructor`, `const`, `extends`, `var`, `arg`, `argument`, `prop`, `return`, `exception`, `yield`|
|Closure-only|`package`, `private`, `protected`, `public`, `static`|
|Options|`definedTypes`|
-|Settings|`preferredTypes`, `mode`|
+|Settings|`preferredTypes`, `mode`, `structuredTags`|
diff --git a/.README/rules/valid-types.md b/.README/rules/valid-types.md
index a0d8b7ea0..b0b1b2175 100644
--- a/.README/rules/valid-types.md
+++ b/.README/rules/valid-types.md
@@ -31,15 +31,15 @@ e.g., `@modifies`):
The following tags have their name/namepath portion (the non-whitespace
text after the tag name) checked:
-1. Name(path)-defining tags requiring namepath: `@external`, `@host`,
- `@name`, `@typedef`, and `@template` (TypeScript/Closure only);
- `@param` (`@arg`, `@argument`) and `@property`
+1. Name(path)-defining tags requiring namepath: `@event`, `@callback`,
+ `@external`, `@host`, `@name`, `@typedef`, and `@template`
+ (TypeScript/Closure only); `@param` (`@arg`, `@argument`) and `@property`
(`@prop`) also fall into this category, but while this rule will check
their namepath validity, we leave the requiring of the name portion
to the rules `require-param-name` and `require-property-name`,
respectively.
1. Name(path)-defining tags (which may have value without namepath or their
- namepath can be expressed elsewhere on the block): `@event`, `@callback`,
+ namepath can be expressed elsewhere on the block):
`@class`, `@constructor`, `@constant`, `@const`, `@function`, `@func`,
`@method`, `@interface` (TypeScript tag only), `@member`, `@var`,
`@mixin`, `@namespace`, `@module` (module paths are not planned for
@@ -70,16 +70,22 @@ text after the tag name) checked:
allow `#`, `.`, or `~` at the end (which is not allowed at the end of
normal paths).
+If you define your own tags, `settings.jsdoc.structuredTags` will allow
+these custom tags to be checked, with the name portion of tags checked for
+valid namepaths (based on the tag's `name` value), their type portions checked
+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
empty name paths with namepath groups 2 and 4 (these might often be
expected to have an accompanying name path, though they have some
indicative value without one; these may also allow names to be defined
- in another manner elsewhere in the block)
-- `checkSeesForNamepaths` (default: false) - Set this to `true` to insist
- that `@see` only use name paths (the tag is normally permitted to
- allow other text)
+ in another manner elsewhere in the block); you can use
+ `settings.jsdoc.structuredTags` with the `required` key set to "name" if you
+ wish to require name paths on a tag-by-tag basis.
|||
|---|---|
@@ -87,7 +93,7 @@ text after the tag name) checked:
|Tags|For name only unless otherwise stated: `alias`, `augments`, `borrows`, `callback`, `class` (for name and type), `constant` (for name and type), `enum` (for type), `event`, `external`, `fires`, `function`, `implements` (for type), `interface`, `lends`, `listens`, `member` (for name and type), `memberof`, `memberof!`, `mixes`, `mixin`, `modifies`, `module` (for name and type), `name`, `namespace` (for name and type), `param` (for name and type), `property` (for name and type), `returns` (for type), `see` (optionally for name), `this`, `throws` (for type), `type` (for type), `typedef` (for name and type), `yields` (for type)|
|Aliases|`extends`, `constructor`, `const`, `host`, `emits`, `func`, `method`, `var`, `arg`, `argument`, `prop`, `return`, `exception`, `yield`|
|Closure-only|For type only: `package`, `private`, `protected`, `public`, `static`|
-|Options|`allowEmptyNamepaths`, `checkSeesForNamepaths`|
-|Settings|`mode`|
+|Options|`allowEmptyNamepaths`|
+|Settings|`mode`, `structuredTags`|
diff --git a/README.md b/README.md
index cf3064ede..c6c5a0e54 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@ JSDoc linting rules for ESLint.
* [Alias Preference](#eslint-plugin-jsdoc-settings-alias-preference)
* [`@override`/`@augments`/`@extends`/`@implements` Without Accompanying `@param`/`@description`/`@example`/`@returns`](#eslint-plugin-jsdoc-settings-override-augments-extends-implements-without-accompanying-param-description-example-returns)
* [Settings to Configure `check-types` and `no-undefined-types`](#eslint-plugin-jsdoc-settings-settings-to-configure-check-types-and-no-undefined-types)
+ * [`structuredTags`](#eslint-plugin-jsdoc-settings-structuredtags)
* [Advanced](#eslint-plugin-jsdoc-advanced)
* [AST and Selectors](#eslint-plugin-jsdoc-advanced-ast-and-selectors)
* [Rules](#eslint-plugin-jsdoc-rules)
@@ -442,6 +443,50 @@ key nor the value will be reported. Thus in `check-types`, this fact can
be used to allow both `object` and `Object` if one has a `preferredTypes`
key `object: 'Object'` and `Object: 'object'`.
+
+### structuredTags
+
+An object indicating tags whose types and names/namepaths (whether defining or
+referencing namepaths) will be checked, subject to configuration. If the tags
+have predefined behavior or `allowEmptyNamepaths` behavior, this option will
+override that behavior for any specified tags, though this option can also be
+used for tags without predefined behavior. Its keys are tag names and its
+values are objects with the following optional properties:
+ - `name` - String set to one of the following:
+ - `"text"` - When a name is present, plain text will be allowed in the
+ name position (non-whitespace immediately after the tag and whitespace),
+ e.g., in `@throws This is an error`, "This" would normally be the name,
+ but "text" allows non-name text here also. This is the default.
+ - `"namepath-defining"` - As with `namepath-referencing`, but also
+ indicates the tag adds a namepath to definitions, e.g., to prevent
+ `no-undefined-types` from reporting references to that namepath.
+ - `"namepath-referencing"` - This will cause any name position to be
+ checked to ensure it is a valid namepath. You might use this to ensure
+ that tags which normally allow free text, e.g., `@see` will instead
+ require a namepath.
+ - `false` - This will disallow any text in the name position.
+ - `type`:
+ - `true` - Allows valid types within brackets. This is the default.
+ - `false` - Explicitly disallows any brackets or bracketed type. You
+ might use this with `@throws` to suggest that only free form text
+ is being input or with `@augments` (for jsdoc mode) to disallow
+ Closure-style bracketed usage along with a required namepath.
+ - `required` - Array of one of the following (defaults to an empty array,
+ meaning none are required):
+ - One or both of the following strings (if both are included, then both
+ are required):
+ - `"name"` - Indicates that a name position is required (not just that
+ if present, it is a valid namepath). You might use this with `see`
+ to insist that a value (or namepath, depending on the `name` value)
+ is always present.
+ - `"type"` - Indicates that the type position (within curly brackets)
+ is required (not just that if present, it is a valid type). You
+ might use this with `@throws` or `@typedef` which might otherwise
+ normally have their types optional. See the type groups 3-5 above.
+ - `"typeOrName"` - Must have either type (e.g., `@throws {aType}`) or
+ name (`@throws Some text`); does not require that both exist but
+ disallows just an empty tag.
+
## Advanced
@@ -2049,6 +2094,15 @@ function testingEslint(options: {
return one + two + three;
}
// Message: Missing @param "options.three"
+
+/**
+ *
+ */
+function quux() {
+
+}
+// Settings: {"jsdoc":{"structuredTags":{"see":{"name":false,"required":["name"]}}}}
+// Message: Cannot add "name" to `require` with the tag's `name` set to `false`
````
The following patterns are not considered problems:
@@ -3500,6 +3554,10 @@ Boolean | **boolean** | **boolean** | `(true) instanceof Boolean` -> **`false`**
Number | **number** | **number** | `(41) instanceof Number` -> **`false`**
String | **string** | **string** | `("test") instanceof String` -> **`false`**
+If you define your own tags and don't wish their bracketed portions checked
+for types, you can use `settings.jsdoc.structuredTags` with a tag `type` of
+`false`.
+
|||
|---|---|
|Context|everywhere|
@@ -3507,7 +3565,7 @@ String | **string** | **string** | `("test") instanceof String` -> **`false`**
|Aliases|`constructor`, `const`, `extends`, `var`, `arg`, `argument`, `prop`, `return`, `exception`, `yield`|
|Closure-only|`package`, `private`, `protected`, `public`, `static`|
|Options|`noDefaults`, `exemptTagContexts`, `unifyParentAndChildTypeChecks`|
-|Settings|`preferredTypes`, `mode`|
+|Settings|`preferredTypes`, `mode`, `structuredTags`|
The following patterns are considered problems:
@@ -4081,6 +4139,12 @@ function a () {}
function b () {}
// Settings: {"jsdoc":{"mode":"typescript","preferredTypes":{"object":"Object"}}}
// Message: Invalid JSDoc @typedef "foo" type "object"; prefer: "Object".
+
+/**
+ * @aCustomTag {Number} foo
+ */
+// Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"type":true}}}}
+// Message: Invalid JSDoc @aCustomTag "foo" type "Number"; prefer: "number".
````
The following patterns are not considered problems:
@@ -4345,6 +4409,11 @@ function a () {}
*/
function b () {}
// Settings: {"jsdoc":{"mode":"typescript"}}
+
+/**
+ * @aCustomTag {Number} foo
+ */
+// Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"type":false}}}}
````
@@ -6057,6 +6126,11 @@ Also note that if there is an error [parsing](https://github.com/jsdoctypeparser
types for a tag, the function will silently ignore that tag, leaving it to
the `valid-types` rule to report parsing errors.
+If you define your own tags, you can use `settings.jsdoc.structuredTags`
+to indicate that a tag's `name` is "namepath-defining" (and should prevent
+reporting on use of that namepath elsewhere) and/or that a tag's `type` is
+`false` (and should not be checked for types).
+
#### Options
@@ -6073,7 +6147,7 @@ An option object may have the following key:
|Aliases|`constructor`, `const`, `extends`, `var`, `arg`, `argument`, `prop`, `return`, `exception`, `yield`|
|Closure-only|`package`, `private`, `protected`, `public`, `static`|
|Options|`definedTypes`|
-|Settings|`preferredTypes`, `mode`|
+|Settings|`preferredTypes`, `mode`, `structuredTags`|
The following patterns are considered problems:
@@ -6218,6 +6292,30 @@ function quux () {}
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
// Message: The type 'SomeType' is undefined.
+
+/**
+ * @aCustomTag {SomeType}
+ */
+function quux () {}
+// Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"type":true}}}}
+// Message: The type 'SomeType' is undefined.
+
+/**
+ * @namepathDefiner SomeType
+ */
+/**
+ * @type {SomeType}
+ */
+// Settings: {"jsdoc":{"structuredTags":{"namepathDefiner":{"name":"namepath-referencing"}}}}
+// Message: The type 'SomeType' is undefined.
+
+/**
+ * @namepathDefiner SomeType
+ */
+/**
+ * @type {SomeType}
+ */
+// Message: The type 'SomeType' is undefined.
````
The following patterns are not considered problems:
@@ -6510,6 +6608,20 @@ exports.resolve1 = function resolve1(value) {
* @typedef {ValueType} ValueFunc
*/
// Settings: {"jsdoc":{"mode":"typescript"}}
+
+/**
+ * @aCustomTag {SomeType}
+ */
+function quux () {}
+// Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"type":false}}}}
+
+/**
+ * @namepathDefiner SomeType
+ */
+/**
+ * @type {SomeType}
+ */
+// Settings: {"jsdoc":{"structuredTags":{"namepathDefiner":{"name":"namepath-defining"}}}}
````
@@ -9091,6 +9203,15 @@ class Foo {
}
// Options: [{"exemptEmptyConstructors":true,"require":{"MethodDefinition":true}}]
// Message: Missing JSDoc comment.
+
+/**
+ *
+ */
+function quux() {
+
+}
+// Settings: {"jsdoc":{"structuredTags":{"see":{"name":false,"required":["name"]}}}}
+// Message: Cannot add "name" to `require` with the tag's `name` set to `false`
````
The following patterns are not considered problems:
@@ -13599,15 +13720,15 @@ e.g., `@modifies`):
The following tags have their name/namepath portion (the non-whitespace
text after the tag name) checked:
-1. Name(path)-defining tags requiring namepath: `@external`, `@host`,
- `@name`, `@typedef`, and `@template` (TypeScript/Closure only);
- `@param` (`@arg`, `@argument`) and `@property`
+1. Name(path)-defining tags requiring namepath: `@event`, `@callback`,
+ `@external`, `@host`, `@name`, `@typedef`, and `@template`
+ (TypeScript/Closure only); `@param` (`@arg`, `@argument`) and `@property`
(`@prop`) also fall into this category, but while this rule will check
their namepath validity, we leave the requiring of the name portion
to the rules `require-param-name` and `require-property-name`,
respectively.
1. Name(path)-defining tags (which may have value without namepath or their
- namepath can be expressed elsewhere on the block): `@event`, `@callback`,
+ namepath can be expressed elsewhere on the block):
`@class`, `@constructor`, `@constant`, `@const`, `@function`, `@func`,
`@method`, `@interface` (TypeScript tag only), `@member`, `@var`,
`@mixin`, `@namespace`, `@module` (module paths are not planned for
@@ -13638,6 +13759,13 @@ text after the tag name) checked:
allow `#`, `.`, or `~` at the end (which is not allowed at the end of
normal paths).
+If you define your own tags, `settings.jsdoc.structuredTags` will allow
+these custom tags to be checked, with the name portion of tags checked for
+valid namepaths (based on the tag's `name` value), their type portions checked
+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
@@ -13645,10 +13773,9 @@ text after the tag name) checked:
empty name paths with namepath groups 2 and 4 (these might often be
expected to have an accompanying name path, though they have some
indicative value without one; these may also allow names to be defined
- in another manner elsewhere in the block)
-- `checkSeesForNamepaths` (default: false) - Set this to `true` to insist
- that `@see` only use name paths (the tag is normally permitted to
- allow other text)
+ in another manner elsewhere in the block); you can use
+ `settings.jsdoc.structuredTags` with the `required` key set to "name" if you
+ wish to require name paths on a tag-by-tag basis.
|||
|---|---|
@@ -13656,8 +13783,8 @@ text after the tag name) checked:
|Tags|For name only unless otherwise stated: `alias`, `augments`, `borrows`, `callback`, `class` (for name and type), `constant` (for name and type), `enum` (for type), `event`, `external`, `fires`, `function`, `implements` (for type), `interface`, `lends`, `listens`, `member` (for name and type), `memberof`, `memberof!`, `mixes`, `mixin`, `modifies`, `module` (for name and type), `name`, `namespace` (for name and type), `param` (for name and type), `property` (for name and type), `returns` (for type), `see` (optionally for name), `this`, `throws` (for type), `type` (for type), `typedef` (for name and type), `yields` (for type)|
|Aliases|`extends`, `constructor`, `const`, `host`, `emits`, `func`, `method`, `var`, `arg`, `argument`, `prop`, `return`, `exception`, `yield`|
|Closure-only|For type only: `package`, `private`, `protected`, `public`, `static`|
-|Options|`allowEmptyNamepaths`, `checkSeesForNamepaths`|
-|Settings|`mode`|
+|Options|`allowEmptyNamepaths`|
+|Settings|`mode`, `structuredTags`|
The following patterns are considered problems:
@@ -13732,7 +13859,7 @@ function quux() {
function quux() {
}
-// Options: [{"checkSeesForNamepaths":true}]
+// Settings: {"jsdoc":{"structuredTags":{"see":{"name":"namepath-referencing","required":["name"]}}}}
// Message: Syntax error in namepath: foo%
/**
@@ -13758,7 +13885,7 @@ function quux() {
}
// Options: [{"allowEmptyNamepaths":false}]
-// Message: Tag @callback must have a name/namepath
+// Message: Tag @callback must have a name/namepath.
/**
* @constant {str%ng}
@@ -13777,16 +13904,24 @@ function quux() {
// Message: Syntax error in namepath: UserStr%ng
/**
- * @extends
+ * @this
*/
class Bar {};
-// Message: Tag @extends must have either a type or namepath
+// Options: [{"allowEmptyNamepaths":false}]
+// Message: Tag @this must have either a type or namepath in "jsdoc" mode.
+
+/**
+ * @aCustomTag
+ */
+// Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"required":["typeOrNameRequired"]}}}}
+// Options: [{"allowEmptyNamepaths":false}]
+// Message: Tag @aCustomTag must have either a type or namepath.
/**
* @type
*/
let foo;
-// Message: Tag @type must have a type
+// Message: Tag @type must have a type.
/**
* @modifies {bar | foo<}
@@ -13813,14 +13948,14 @@ function quux () {}
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
-// Message: Tag @define must have a type
+// Message: Tag @define must have a type in "closure" mode.
/**
* @this
*/
let foo;
// Settings: {"jsdoc":{"mode":"closure"}}
-// Message: Tag @this must have a type
+// Message: Tag @this must have a type in "closure" mode.
/**
* Foo function.
@@ -13849,12 +13984,19 @@ function foo(bar) {}
// Settings: {"jsdoc":{"mode":"closure"}}
// Message: @interface should not have a name in "closure" mode.
+/**
+ * @aCustomTag name
+ */
+// Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"name":false}}}}
+// Message: @aCustomTag should not have a name.
+
/**
* @typedef {SomeType}
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"jsdoc"}}
-// Message: @typedef must have a name in "jsdoc" mode.
+// Options: [{"allowEmptyNamepaths":false}]
+// Message: Tag @typedef must have a name/namepath in "jsdoc" mode.
/**
* @private {SomeType}
@@ -13862,6 +14004,49 @@ function quux () {}
function quux () {}
// Settings: {"jsdoc":{"mode":"jsdoc"}}
// Message: @private should not have a bracketed type in "jsdoc" mode.
+
+/**
+ * @aCustomTag {SomeType}
+ */
+function quux () {}
+// Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"type":false}}}}
+// Message: @aCustomTag should not have a bracketed type.
+
+/**
+ * @see foo%
+ */
+function quux() {
+
+}
+// Settings: {"jsdoc":{"structuredTags":{"see":{"name":false,"required":["name"]}}}}
+// Message: Cannot add "name" to `require` with the tag's `name` set to `false`
+
+/**
+ * @see foo%
+ */
+function quux() {
+
+}
+// Settings: {"jsdoc":{"structuredTags":{"see":{"required":["type"],"type":false}}}}
+// Message: Cannot add "type" to `require` with the tag's `type` set to `false`
+
+/**
+ * @see foo%
+ */
+function quux() {
+
+}
+// Settings: {"jsdoc":{"structuredTags":{"see":{"name":false,"required":["typeOrNameRequired"]}}}}
+// Message: Cannot add "typeOrNameRequired" to `require` with the tag's `name` set to `false`
+
+/**
+ * @see foo%
+ */
+function quux() {
+
+}
+// Settings: {"jsdoc":{"structuredTags":{"see":{"required":["typeOrNameRequired"],"type":false}}}}
+// Message: Cannot add "typeOrNameRequired" to `require` with the tag's `type` set to `false`
````
The following patterns are not considered problems:
@@ -13944,7 +14129,7 @@ function quux() {
function quux() {
}
-// Options: [{"checkSeesForNamepaths":true}]
+// Settings: {"jsdoc":{"structuredTags":{"see":{"name":"namepath-referencing","required":["name"]}}}}
/**
*
@@ -13975,6 +14160,13 @@ function quux() {
}
+/**
+ * @aCustomTag
+ */
+function quux() {
+
+}
+
/**
* @constant {string}
*/
@@ -14075,6 +14267,7 @@ function foo(bar) {}
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
+// Options: [{"allowEmptyNamepaths":false}]
/**
* @private {SomeType}
@@ -14089,6 +14282,14 @@ function quux() {
}
// Options: [{"allowEmptyNamepaths":false}]
+
+/**
+ * @see
+ */
+function quux() {
+
+}
+// Settings: {"jsdoc":{"structuredTags":{"see":{"name":"namepath-referencing"}}}}
````
diff --git a/src/getDefaultTagStructureForMode.js b/src/getDefaultTagStructureForMode.js
new file mode 100644
index 000000000..7aba7ad68
--- /dev/null
+++ b/src/getDefaultTagStructureForMode.js
@@ -0,0 +1,464 @@
+const getDefaultTagStructureForMode = (mode) => {
+ const isJsdoc = mode === 'jsdoc';
+ const isClosure = mode === 'closure';
+ const isTypescript = mode === 'typescript';
+ const isPermissive = mode === 'permissive';
+
+ const isJsdocOrTypescript = isJsdoc || isTypescript;
+ const isTypescriptOrClosure = isTypescript || isClosure;
+ const isClosureOrPermissive = isClosure || isPermissive;
+ const isJsdocTypescriptOrPermissive = isJsdocOrTypescript || isPermissive;
+
+ // Properties:
+ // `nameContents` - 'namepath-referencing'|'namepath-defining'|'text'|false
+ // `typeAllowed` - boolean
+ // `nameRequired` - boolean
+ // `typeRequired` - boolean
+ // `typeOrNameRequired` - boolean
+
+ // All of `typeAllowed` have a signature with "type" except for
+ // `augments`/`extends` ("namepath")
+ // `param`/`arg`/`argument` (no signature)
+ // `property`/`prop` (no signature)
+ // `modifies` (undocumented)
+
+ // None of the `nameContents: 'namepath-defining'` show as having curly
+ // brackets for their name/namepath
+
+ // Among `namepath-defining` and `namepath-referencing`, these do not seem
+ // to allow curly brackets in their doc signature or examples (`modifies`
+ // references namepaths within its type brackets and `param` is
+ // name-defining but not namepath-defining, so not part of these groups)
+
+ // Todo: Should support special processing for "name" as distinct from
+ // "namepath" (e.g., param can't define a namepath)
+
+ // Once checking inline tags:
+ // Todo: Re: `typeOrNameRequired`, `@link` (or @linkcode/@linkplain) seems
+ // to require a namepath OR URL and might be checked as such.
+ // Todo: Should support a `tutorialID` type (for `@tutorial` block and
+ // inline)
+
+ return new Map([
+ ['alias', new Map([
+ // Signature seems to require a "namepath" (and no counter-examples)
+ ['nameContents', 'namepath-referencing'],
+
+ // "namepath"
+ ['typeOrNameRequired', true],
+ ])],
+
+ ['arg', new Map([
+ ['nameContents', 'namepath-defining'],
+
+ // See `param`
+ ['nameRequired', true],
+
+ // Has no formal signature in the docs but shows curly brackets
+ // in the examples
+ ['typeAllowed', true],
+ ])],
+
+ ['argument', new Map([
+ ['nameContents', 'namepath-defining'],
+
+ // See `param`
+ ['nameRequired', true],
+
+ // Has no formal signature in the docs but shows curly brackets
+ // in the examples
+ ['typeAllowed', true],
+ ])],
+
+ ['augments', new Map([
+ // Signature seems to require a "namepath" (and no counter-examples)
+ ['nameContents', 'namepath-referencing'],
+
+ // Does not show curly brackets in either the signature or examples
+ ['typeAllowed', true],
+
+ // "namepath"
+ ['typeOrNameRequired', true],
+ ])],
+
+ ['borrows', new Map([
+ // `borrows` has a different format, however, so needs special parsing;
+ // seems to require both, and as "namepath"'s
+ ['nameContents', 'namepath-referencing'],
+
+ // "namepath"
+ ['typeOrNameRequired', true],
+ ])],
+
+ ['callback', new Map([
+ // Seems to require a "namepath" in the signature (with no
+ // counter-examples)
+ ['nameContents', 'namepath-defining'],
+
+ // "namepath"
+ ['nameRequired', true],
+ ])],
+
+ ['class', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+
+ ['typeAllowed', true],
+ ])],
+
+ ['const', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+
+ ['typeAllowed', true],
+ ])],
+ ['constant', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+
+ ['typeAllowed', true],
+ ])],
+ ['constructor', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+
+ ['typeAllowed', true],
+ ])],
+
+ ['define', new Map([
+ ['typeRequired', isClosure],
+ ])],
+
+ ['emits', new Map([
+ // Signature seems to require a "name" (of an event) and no counter-examples
+ ['nameContents', 'namepath-referencing'],
+ ])],
+
+ ['enum', new Map([
+ // Has example showing curly brackets but not in doc signature
+ ['typeAllowed', true],
+ ])],
+
+ ['event', new Map([
+ // The doc signature of `event` seems to require a "name"
+ ['nameRequired', true],
+
+ // Appears to require a "name" in its signature, albeit somewhat
+ // different from other "name"'s (including as described
+ // at https://jsdoc.app/about-namepaths.html )
+ ['nameContents', 'namepath-defining'],
+ ])],
+
+ ['exception', new Map([
+ // Shows curly brackets in the signature and in the examples
+ ['typeAllowed', true],
+ ])],
+
+ ['export', new Map([
+ ['typeAllowed', isClosureOrPermissive],
+ ])],
+
+ ['extends', new Map([
+ // Signature seems to require a "namepath" (and no counter-examples)
+ ['nameContents', 'namepath-referencing'],
+
+ // Does not show curly brackets in either the signature or examples
+ ['typeAllowed', isClosureOrPermissive],
+
+ ['nameRequired', isJsdocOrTypescript],
+
+ // "namepath"
+ ['typeOrNameRequired', isClosureOrPermissive],
+ ])],
+
+ ['external', new Map([
+ // Appears to require a "name" in its signature, albeit somewhat
+ // different from other "name"'s (including as described
+ // at https://jsdoc.app/about-namepaths.html )
+ ['nameContents', 'namepath-defining'],
+
+ // "name" (and a special syntax for the `external` name)
+ ['nameRequired', true],
+ ])],
+
+ ['fires', new Map([
+ // Signature seems to require a "name" (of an event) and no
+ // counter-examples
+ ['nameContents', 'namepath-referencing'],
+ ])],
+
+ ['function', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+ ])],
+ ['func', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+ ])],
+
+ ['host', new Map([
+ // Appears to require a "name" in its signature, albeit somewhat
+ // different from other "name"'s (including as described
+ // at https://jsdoc.app/about-namepaths.html )
+ ['nameContents', 'namepath-defining'],
+
+ // See `external`
+ ['nameRequired', true],
+
+ // "namepath"
+ ['typeOrNameRequired', true],
+ ])],
+
+ ['interface', new Map([
+ // Allows for "name" in signature, but indicates as optional
+ [
+ 'nameContents',
+ isJsdocTypescriptOrPermissive ? 'namepath-defining' : false,
+ ],
+ ])],
+
+ ['implements', new Map([
+ // Shows curly brackets in the doc signature and examples
+ // "typeExpression"
+ ['typeRequired', true],
+ ])],
+
+ ['lends', new Map([
+ // Signature seems to require a "namepath" (and no counter-examples)
+ ['nameContents', 'namepath-referencing'],
+
+ // "namepath"
+ ['typeOrNameRequired', true],
+ ])],
+
+ ['listens', new Map([
+ // Signature seems to require a "name" (of an event) and no
+ // counter-examples
+ ['nameContents', 'namepath-referencing'],
+ ])],
+
+ ['member', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+
+ // Has example showing curly brackets but not in doc signature
+ ['typeAllowed', true],
+ ])],
+
+ ['memberof', new Map([
+ // Signature seems to require a "namepath" (and no counter-examples),
+ // though it allows an incomplete namepath ending with connecting symbol
+ ['nameContents', 'namepath-referencing'],
+
+ // "namepath"
+ ['typeOrNameRequired', true],
+ ])],
+ ['memberof!', new Map([
+ // Signature seems to require a "namepath" (and no counter-examples),
+ // though it allows an incomplete namepath ending with connecting symbol
+ ['nameContents', 'namepath-referencing'],
+
+ // "namepath"
+ ['typeOrNameRequired', true],
+ ])],
+
+ ['method', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+ ])],
+ ['mixes', new Map([
+ // Signature seems to require a "OtherObjectPath" with no
+ // counter-examples
+ ['nameContents', 'namepath-referencing'],
+
+ // "OtherObjectPath"
+ ['typeOrNameRequired', true],
+ ])],
+
+ ['mixin', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+ ])],
+
+ ['modifies', new Map([
+ // Has no documentation, but test example has curly brackets, and
+ // "name" would be suggested rather than "namepath" based on example;
+ // not sure if name is required
+ ['typeAllowed', true],
+ ])],
+
+ ['module', new Map([
+ // Optional "name" and no curly brackets
+ // this block impacts `no-undefined-types` and `valid-types` (search for
+ // "isNamepathDefiningTag|tagMightHaveNamepath|tagMightHaveEitherTypeOrNamePosition")
+ ['nameContents', isJsdoc ? 'namepath-defining' : 'text'],
+
+ // Shows the signature with curly brackets but not in the example
+ ['typeAllowed', true],
+ ])],
+
+ ['name', new Map([
+ // Seems to require a "namepath" in the signature (with no
+ // counter-examples)
+ ['nameContents', 'namepath-defining'],
+
+ // "namepath"
+ ['nameRequired', true],
+
+ // "namepath"
+ ['typeOrNameRequired', true],
+ ])],
+
+ ['namespace', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+
+ // Shows the signature with curly brackets but not in the example
+ ['typeAllowed', true],
+ ])],
+ ['package', new Map([
+ // Shows the signature with curly brackets but not in the example
+ // "typeExpression"
+ ['typeAllowed', isClosureOrPermissive],
+ ])],
+
+ ['param', new Map([
+ ['nameContents', 'namepath-defining'],
+
+ // Though no signature provided requiring, per
+ // https://jsdoc.app/tags-param.html:
+ // "The @param tag requires you to specify the name of the parameter you
+ // are documenting."
+ ['nameRequired', true],
+
+ // Has no formal signature in the docs but shows curly brackets
+ // in the examples
+ ['typeAllowed', true],
+ ])],
+
+ ['private', new Map([
+ // Shows the signature with curly brackets but not in the example
+ // "typeExpression"
+ ['typeAllowed', isClosureOrPermissive],
+ ])],
+
+ ['prop', new Map([
+ ['nameContents', 'namepath-defining'],
+
+ // See `property`
+ ['nameRequired', true],
+
+ // Has no formal signature in the docs but shows curly brackets
+ // in the examples
+ ['typeAllowed', true],
+ ])],
+
+ ['property', new Map([
+ ['nameContents', 'namepath-defining'],
+
+ // No docs indicate required, but since parallel to `param`, we treat as
+ // such:
+ ['nameRequired', true],
+
+ // Has no formal signature in the docs but shows curly brackets
+ // in the examples
+ ['typeAllowed', true],
+ ])],
+
+ ['protected', new Map([
+ // Shows the signature with curly brackets but not in the example
+ // "typeExpression"
+ ['typeAllowed', isClosureOrPermissive],
+ ])],
+
+ ['public', new Map([
+ // Does not show a signature nor show curly brackets in the example
+ ['typeAllowed', isClosureOrPermissive],
+ ])],
+
+ ['returns', new Map([
+ // Shows curly brackets in the signature and in the examples
+ ['typeAllowed', true],
+ ])],
+ ['return', new Map([
+ // Shows curly brackets in the signature and in the examples
+ ['typeAllowed', true],
+ ])],
+
+ ['see', new Map([
+ // Signature allows for "namepath" or text, so user must configure to
+ // 'namepath-referencing' to enforce checks
+ ['nameContents', 'text'],
+ ])],
+
+ ['static', new Map([
+ // Does not show a signature nor show curly brackets in the example
+ ['typeAllowed', isClosureOrPermissive],
+ ])],
+
+ ['template', new Map([
+ ['nameContents', isJsdoc ? 'text' : 'namepath-referencing'],
+
+ // Though defines `nameContents: 'namepath-defining'` in a sense, it is
+ // not parseable in the same way for template (e.g., allowing commas),
+ // so not adding
+ ['typeAllowed', isTypescriptOrClosure || isPermissive],
+ ])],
+
+ ['this', new Map([
+ // Signature seems to require a "namepath" (and no counter-examples)
+ // Not used with namepath in Closure/TypeScript, however
+ ['nameContents', isJsdoc ? 'namepath-referencing' : false],
+
+ ['typeRequired', isTypescriptOrClosure],
+
+ // namepath
+ ['typeOrNameRequired', isJsdoc],
+ ])],
+
+ ['throws', new Map([
+ // Shows curly brackets in the signature and in the examples
+ ['typeAllowed', true],
+ ])],
+
+ ['type', new Map([
+ // Shows curly brackets in the doc signature and examples
+ // "typeName"
+ ['typeRequired', true],
+ ])],
+
+ ['typedef', new Map([
+ // Seems to require a "namepath" in the signature (with no
+ // counter-examples)
+ ['nameContents', 'namepath-defining'],
+
+ // "namepath"
+ ['nameRequired', isJsdocTypescriptOrPermissive],
+
+ // Has example showing curly brackets but not in doc signature
+ ['typeAllowed', true],
+
+ // "namepath"
+ ['typeOrNameRequired', true],
+ ])],
+
+ ['var', new Map([
+ // Allows for "name"'s in signature, but indicated as optional
+ ['nameContents', 'namepath-defining'],
+
+ // Has example showing curly brackets but not in doc signature
+ ['typeAllowed', true],
+ ])],
+
+ ['yields', new Map([
+ // Shows curly brackets in the signature and in the examples
+ ['typeAllowed', true],
+ ])],
+ ['yield', new Map([
+ // Shows curly brackets in the signature and in the examples
+ ['typeAllowed', true],
+ ])],
+ ]);
+};
+
+export default getDefaultTagStructureForMode;
diff --git a/src/iterateJsdoc.js b/src/iterateJsdoc.js
index 07830e70d..098d73d0c 100644
--- a/src/iterateJsdoc.js
+++ b/src/iterateJsdoc.js
@@ -271,32 +271,60 @@ const getUtils = (
return false;
};
- utils.tagMustHaveEitherTypeOrNamePosition = (tagName) => {
- return jsdocUtils.tagMustHaveEitherTypeOrNamePosition(mode, tagName);
- };
+ [
+ 'tagMightHaveNamePosition',
+ 'tagMightHaveTypePosition',
+ ].forEach((method) => {
+ utils[method] = (tagName, otherModeMaps) => {
+ const result = jsdocUtils[method](tagName);
+ if (result) {
+ return true;
+ }
- utils.tagMightHaveEitherTypeOrNamePosition = (tagName) => {
- return jsdocUtils.tagMightHaveEitherTypeOrNamePosition(mode, tagName);
- };
+ if (!otherModeMaps) {
+ return false;
+ }
- utils.tagMustHaveNamePosition = (tagName) => {
- return jsdocUtils.tagMustHaveNamePosition(tagName);
- };
+ const otherResult = otherModeMaps.some((otherModeMap) => {
+ return jsdocUtils[method](tagName, otherModeMap);
+ });
- utils.tagMightHaveNamePosition = (tagName) => {
- return jsdocUtils.tagMightHaveNamePosition(mode, tagName);
- };
+ return otherResult ? {otherMode: true} : false;
+ };
+ });
- utils.tagMustHaveTypePosition = (tagName) => {
- return jsdocUtils.tagMustHaveTypePosition(mode, tagName);
- };
+ [
+ 'tagMustHaveNamePosition',
+ 'tagMustHaveTypePosition',
+ 'tagMissingRequiredTypeOrNamepath',
+ ].forEach((method) => {
+ utils[method] = (tagName, otherModeMaps) => {
+ const result = jsdocUtils[method](tagName);
+ if (!result) {
+ return false;
+ }
- utils.tagMightHaveTypePosition = (tagName) => {
- return jsdocUtils.tagMightHaveTypePosition(mode, tagName);
- };
+ // if (!otherModeMaps) { return true; }
- utils.isNamepathDefiningTag = (tagName) => {
- return jsdocUtils.isNamepathDefiningTag(mode, tagName);
+ const otherResult = otherModeMaps.every((otherModeMap) => {
+ return jsdocUtils[method](tagName, otherModeMap);
+ });
+
+ return otherResult ? true : {otherMode: false};
+ };
+ });
+
+ [
+ 'isNamepathDefiningTag',
+ 'tagMightHaveNamepath',
+ ].forEach((method) => {
+ utils[method] = (tagName) => {
+ return jsdocUtils[method](tagName);
+ };
+ });
+
+ utils.getTagStructureForMode = (mde) => {
+ return jsdocUtils.getTagStructureForMode(mde, settings.structuredTags);
};
utils.hasDefinedTypeReturnTag = (tag) => {
@@ -410,6 +438,9 @@ const getSettings = (context) => {
// `check-types` and `no-undefined-types`
preferredTypes: context.settings.jsdoc?.preferredTypes ?? {},
+ // `check-types`, `no-undefined-types`, `valid-types`
+ structuredTags: context.settings.jsdoc?.structuredTags ?? {},
+
// `require-param`, `require-description`, `require-example`, `require-returns`
overrideReplacesDocs: context.settings.jsdoc?.overrideReplacesDocs,
implementsReplacesDocs: context.settings.jsdoc?.implementsReplacesDocs,
@@ -421,6 +452,23 @@ const getSettings = (context) => {
};
/* eslint-enable sort-keys-fix/sort-keys-fix */
+ jsdocUtils.setTagStructure(settings.mode);
+ try {
+ jsdocUtils.overrideTagStructure(settings.structuredTags);
+ } catch (error) {
+ context.report({
+ loc: {
+ start: {
+ column: 1,
+ line: 1,
+ },
+ },
+ message: error.message,
+ });
+
+ return false;
+ }
+
return settings;
};
@@ -537,9 +585,9 @@ const iterate = (
const iterateAllJsdocs = (iterator, ruleConfig) => {
const trackedJsdocs = [];
+ let settings;
const callIterator = (context, node, jsdocNodes, state, lastCall) => {
const sourceCode = context.getSourceCode();
- const settings = getSettings(context);
const {lines} = sourceCode;
const utils = getBasicUtils(context, settings);
@@ -565,7 +613,11 @@ const iterateAllJsdocs = (iterator, ruleConfig) => {
return {
create (context) {
const sourceCode = context.getSourceCode();
- const settings = getSettings(context);
+ settings = getSettings(context);
+ if (!settings) {
+ return {};
+ }
+
const state = {};
return {
@@ -620,6 +672,9 @@ const checkFile = (iterator, ruleConfig) => {
create (context) {
const sourceCode = context.getSourceCode();
const settings = getSettings(context);
+ if (!settings) {
+ return {};
+ }
return {
'Program:exit' () {
@@ -694,6 +749,9 @@ export default function iterateJsdoc (iterator, ruleConfig) {
const sourceCode = context.getSourceCode();
const settings = getSettings(context);
+ if (!settings) {
+ return {};
+ }
const {lines} = sourceCode;
const checkJsdoc = (node) => {
diff --git a/src/jsdocUtils.js b/src/jsdocUtils.js
index a287a41b8..0cac1357d 100644
--- a/src/jsdocUtils.js
+++ b/src/jsdocUtils.js
@@ -1,9 +1,16 @@
import _ from 'lodash';
import {jsdocTags, closureTags, typeScriptTags} from './tagNames';
import WarnSettings from './WarnSettings';
+import getDefaultTagStructureForMode from './getDefaultTagStructureForMode';
type ParserMode = "jsdoc"|"typescript"|"closure";
+let tagStructure;
+
+const setTagStructure = (mode) => {
+ tagStructure = getDefaultTagStructureForMode(mode);
+};
+
// Given a nested array of property names, reduce them to a single array,
// appending the name of the root element along the way if present.
const flattenRoots = (params, root = '') => {
@@ -315,291 +322,129 @@ const hasDefinedTypeReturnTag = (tag) => {
return true;
};
-const tagsWithMandatoryTypePosition = new Set([
- // These both show curly brackets in the doc signature and examples
- // "typeExpression"
- 'implements',
-
- // "typeName"
- 'type',
-]);
-
-const tagsWithMandatoryTypePositionTypeScript = new Set([
- ...tagsWithMandatoryTypePosition,
- 'this',
-]);
-
-const tagsWithMandatoryTypePositionClosure = new Set([
- ...tagsWithMandatoryTypePositionTypeScript,
- 'define',
-]);
-
-// All of these have a signature with "type" except for
-// `augments`/`extends` ("namepath")
-// `param`/`arg`/`argument` (no signature)
-// `property`/`prop` (no signature)
-// `modifies` (undocumented)
-const tagsWithOptionalTypePosition = new Set([
- // These have the example showing curly brackets but not in their doc signature, e.g.: https://jsdoc.app/tags-enum.html
- 'enum',
- 'member', 'var',
-
- 'typedef',
-
- // These do not show curly brackets in either the signature or examples
- 'augments', 'extends',
-
- 'class', 'constructor',
- 'constant', 'const',
-
- // These show the signature with curly brackets but not in the example
- 'module',
- 'namespace',
-
- // These have no formal signature in the docs but show curly brackets
- // in the examples
- 'param', 'arg', 'argument',
- 'property', 'prop',
-
- // These show curly brackets in the signature and in the examples
- 'returns', 'return',
- 'throws', 'exception',
- 'yields', 'yield',
-
- // Has no documentation, but test example has curly brackets, and
- // "name" would be suggested rather than "namepath" based on example; not
- // sure if name is required
- 'modifies',
-]);
-
-const tagsWithOptionalTypePositionTypescript = new Set([
- 'template',
- ...tagsWithOptionalTypePosition,
-]);
-
-const tagsWithOptionalTypePositionClosure = new Set([
- ...tagsWithOptionalTypePositionTypescript,
-
- 'export',
-
- // Shows the signature with curly brackets but not in the example
- // "typeExpression"
- 'package',
- 'private',
- 'protected',
-
- // These do not show a signature nor show curly brackets in the example
- 'public',
- 'static',
-]);
-
-// None of these show as having curly brackets for their name/namepath
-const closureNamepathDefiningTags = new Set([
- // Though defines in a sense, it is not parseable in the same way
- // for template (e.g., allowing commas)
- // 'template',
-
- // These appear to require a "name" in their signature, albeit these
- // are somewhat different from other "name"'s (including as described
- // at https://jsdoc.app/about-namepaths.html )
- 'external', 'host',
- 'event',
-
- // These allow for "name"'s in their signature, but indicate as optional
- 'class', 'constructor',
- 'constant', 'const',
- 'function', 'func', 'method',
- 'member', 'var',
- 'mixin',
- 'namespace',
-
- // These seem to all require a "namepath" in their signatures (with no counter-examples)
- 'name',
- 'typedef',
- 'callback',
-]);
-
-const typescriptNamepathDefiningTags = new Set([
- ...closureNamepathDefiningTags,
-
- // Allows for "name" in signature, but indicates as optional
- 'interface',
-]);
-
-const namepathDefiningTags = new Set([
- ...typescriptNamepathDefiningTags,
-
- // Optional "name" and no curly brackets
- // this block impacts `no-undefined-types` and `valid-types` (search for
- // "isNamepathDefiningTag|tagMightHaveNamePosition|tagMightHaveEitherTypeOrNamePosition")
- 'module',
-]);
-
-// These *reference* names/namepaths and do not define them
-const tagsWithOptionalNamePositionBase = new Set([
- // `borrows` has a different format, however, so needs special parsing;
- // seems to require both, and as "namepath"'s
- 'borrows',
+const ensureMap = (map, tag) => {
+ if (!map.has(tag)) {
+ map.set(tag, new Map());
+ }
- // Signature seems to require a "name" (of an event) and no counter-examples
- 'emits', 'fires',
- 'listens',
+ return map.get(tag);
+};
- // Signature seems to require a "namepath" (and no counter-examples)
- 'alias',
- 'augments', 'extends',
- 'lends',
+const overrideTagStructure = (structuredTags, tagMap = tagStructure) => {
+ Object.entries(structuredTags).forEach(([tag, {
+ name, type, required = [],
+ }]) => {
+ const tagStruct = ensureMap(tagMap, tag);
- // Signature seems to require a "namepath" (and no counter-examples),
- // though it allows an incomplete namepath ending with connecting symbol
- 'memberof', 'memberof!',
+ tagStruct.set('nameContents', name);
+ tagStruct.set('typeAllowed', type);
- // Signature seems to require a "OtherObjectPath" with no counter-examples
- 'mixes',
+ const requiredName = required.includes('name');
+ if (requiredName && name === false) {
+ throw new Error('Cannot add "name" to `require` with the tag\'s `name` set to `false`');
+ }
+ tagStruct.set('nameRequired', requiredName);
- // Signature allows for "namepath" or text
- 'see',
-]);
+ const requiredType = required.includes('type');
+ if (requiredType && type === false) {
+ throw new Error('Cannot add "type" to `require` with the tag\'s `type` set to `false`');
+ }
+ tagStruct.set('typeRequired', requiredType);
-// The following do not seem to allow curly brackets in their doc
-// signature or examples (besides `modifies` and `param`)
-const tagsWithOptionalNamePosition = new Set([
- // Signature seems to require a "namepath" (and no counter-examples)
- // Not used with namepath in Closure/TypeScript, however
- 'this',
- ...namepathDefiningTags,
- ...tagsWithOptionalNamePositionBase,
-]);
+ const typeOrNameRequired = required.includes('typeOrNameRequired');
+ if (typeOrNameRequired && name === false) {
+ throw new Error('Cannot add "typeOrNameRequired" to `require` with the tag\'s `name` set to `false`');
+ }
+ if (typeOrNameRequired && type === false) {
+ throw new Error('Cannot add "typeOrNameRequired" to `require` with the tag\'s `type` set to `false`');
+ }
+ tagStruct.set('typeOrNameRequired', typeOrNameRequired);
+ });
+};
-const typescriptTagsWithOptionalNamePosition = new Set([
- 'template',
- ...typescriptNamepathDefiningTags,
- ...tagsWithOptionalNamePositionBase,
-]);
+const getTagStructureForMode = (mode, structuredTags) => {
+ const tagStruct = getDefaultTagStructureForMode(mode);
-const closureTagsWithOptionalNamePosition = new Set([
- 'template',
- ...closureNamepathDefiningTags,
- ...tagsWithOptionalNamePositionBase,
-]);
+ try {
+ overrideTagStructure(structuredTags, tagStruct);
+ } catch {
+ //
+ }
-// Todo: `@link` seems to require a namepath OR URL and might be checked as such.
+ return tagStruct;
+};
-// The doc signature of `event` seems to require a "name"
-const tagsWithMandatoryNamePosition = new Set([
- // Though no signature provided requiring, per https://jsdoc.app/tags-param.html:
- // "The @param tag requires you to specify the name of the parameter you are documenting."
- 'param',
- 'arg',
- 'argument',
+const isNamepathDefiningTag = (tag, tagMap = tagStructure) => {
+ const tagStruct = ensureMap(tagMap, tag);
- // No docs indicate required, but since parallel to `param`, we treat as such:
- 'property',
- 'prop',
+ return tagStruct.get('nameContents') === 'namepath-defining';
+};
- // "name" (and a special syntax for the `external` name)
- 'external', 'host',
+const tagMustHaveTypePosition = (tag, tagMap = tagStructure) => {
+ const tagStruct = ensureMap(tagMap, tag);
- // "namepath"
- 'callback',
- 'name',
- 'typedef',
-]);
+ return tagStruct.get('typeRequired');
+};
-const tagsWithMandatoryTypeOrNamePositionBase = new Set([
- // "namepath"
- 'alias',
- 'augments', 'extends',
- 'borrows',
- 'lends',
- 'memberof', 'memberof!',
- 'name',
- 'typedef',
+const tagMightHaveTypePosition = (tag, tagMap = tagStructure) => {
+ if (tagMustHaveTypePosition(tag, tagMap)) {
+ return true;
+ }
- 'external', 'host',
+ const tagStruct = ensureMap(tagMap, tag);
- // "OtherObjectPath"
- 'mixes',
-]);
+ const ret = tagStruct.get('typeAllowed');
-const tagsWithMandatoryTypeOrNamePosition = new Set([
- // namepath
- 'this',
- ...tagsWithMandatoryTypeOrNamePositionBase,
-]);
+ return ret === undefined ? true : ret;
+};
-const tagsWithMandatoryTypeOrNamePositionTypescript = new Set([
- ...tagsWithMandatoryTypeOrNamePositionBase,
+const namepathTypes = new Set([
+ 'namepath-defining', 'namepath-referencing',
]);
-const tagsWithMandatoryTypeOrNamePositionClosure = new Set([
- ...tagsWithMandatoryTypeOrNamePositionBase,
-]);
+const tagMightHaveNamePosition = (tag, tagMap = tagStructure) => {
+ const tagStruct = ensureMap(tagMap, tag);
-const isNamepathDefiningTag = (mode, tagName) => {
- if (mode === 'closure') {
- return closureNamepathDefiningTags.has(tagName);
- }
- if (mode === 'typescript') {
- return typescriptNamepathDefiningTags.has(tagName);
- }
+ const ret = tagStruct.get('nameContents');
- return namepathDefiningTags.has(tagName);
+ return ret === undefined ? true : Boolean(ret);
};
-const tagMightHaveTypePosition = (mode, tag) => {
- if (mode === 'closure') {
- return tagsWithMandatoryTypePositionClosure.has(tag) ||
- tagsWithOptionalTypePositionClosure.has(tag);
- }
- if (mode === 'typescript') {
- return tagsWithMandatoryTypePositionTypeScript.has(tag) ||
- tagsWithOptionalTypePositionTypescript.has(tag);
- }
+const tagMightHaveNamepath = (tag, tagMap = tagStructure) => {
+ const tagStruct = ensureMap(tagMap, tag);
- return tagsWithMandatoryTypePosition.has(tag) ||
- tagsWithOptionalTypePosition.has(tag);
+ return namepathTypes.has(tagStruct.get('nameContents'));
};
-const tagMustHaveTypePosition = (mode, tag) => {
- if (mode === 'closure') {
- return tagsWithMandatoryTypePositionClosure.has(tag);
- }
- if (mode === 'typescript') {
- return tagsWithMandatoryTypePositionTypeScript.has(tag);
- }
+const tagMustHaveNamePosition = (tag, tagMap = tagStructure) => {
+ const tagStruct = ensureMap(tagMap, tag);
- return tagsWithMandatoryTypePosition.has(tag);
+ return tagStruct.get('nameRequired');
};
-const tagMightHaveNamePosition = (mode, tag) => {
- if (mode === 'closure') {
- return closureTagsWithOptionalNamePosition.has(tag);
- }
- if (mode === 'typescript') {
- return typescriptTagsWithOptionalNamePosition.has(tag);
- }
-
- return tagsWithOptionalNamePosition.has(tag);
+const tagMightHaveEitherTypeOrNamePosition = (tag, tagMap) => {
+ return tagMightHaveTypePosition(tag, tagMap) || tagMightHaveNamepath(tag, tagMap);
};
-const tagMustHaveNamePosition = (tag) => {
- return tagsWithMandatoryNamePosition.has(tag);
-};
+const tagMustHaveEitherTypeOrNamePosition = (tag, tagMap) => {
+ const tagStruct = ensureMap(tagMap, tag);
-const tagMightHaveEitherTypeOrNamePosition = (mode, tag) => {
- return tagMightHaveTypePosition(mode, tag) || tagMightHaveNamePosition(mode, tag);
+ return tagStruct.get('typeOrNameRequired');
};
-const tagMustHaveEitherTypeOrNamePosition = (mode, tag) => {
- if (mode === 'closure') {
- return tagsWithMandatoryTypeOrNamePositionClosure.has(tag);
- }
- if (mode === 'typescript') {
- return tagsWithMandatoryTypeOrNamePositionTypescript.has(tag);
- }
+const tagMissingRequiredTypeOrNamepath = (tag, tagMap = tagStructure) => {
+ const mustHaveTypePosition = tagMustHaveTypePosition(tag.tag, tagMap);
+ const mightHaveTypePosition = tagMightHaveTypePosition(tag.tag, tagMap);
+ const hasTypePosition = mightHaveTypePosition && Boolean(tag.type);
+ const hasNameOrNamepathPosition = (
+ tagMustHaveNamePosition(tag.tag, tagMap) ||
+ tagMightHaveNamepath(tag.tag, tagMap)
+ ) && Boolean(tag.name);
+ const mustHaveEither = tagMustHaveEitherTypeOrNamePosition(tag.tag, tagMap);
+ const hasEither = tagMightHaveEitherTypeOrNamePosition(tag.tag, tagMap) &&
+ (hasTypePosition || hasNameOrNamepathPosition);
- return tagsWithMandatoryTypeOrNamePosition.has(tag);
+ return mustHaveEither && !hasEither && !mustHaveTypePosition;
};
/**
@@ -882,6 +727,7 @@ export default {
getJsdocTagsDeep,
getPreferredTagName,
getTagsByType,
+ getTagStructureForMode,
hasATag,
hasDefinedTypeReturnTag,
hasReturnValue,
@@ -892,11 +738,13 @@ export default {
isNamepathDefiningTag,
isSetter,
isValidTag,
+ overrideTagStructure,
parseClosureTemplateTag,
- tagMightHaveEitherTypeOrNamePosition,
+ setTagStructure,
+ tagMightHaveNamepath,
tagMightHaveNamePosition,
tagMightHaveTypePosition,
- tagMustHaveEitherTypeOrNamePosition,
+ tagMissingRequiredTypeOrNamepath,
tagMustHaveNamePosition,
tagMustHaveTypePosition,
};
diff --git a/src/rules/requireJsdoc.js b/src/rules/requireJsdoc.js
index e70629bd1..62a4a95f7 100644
--- a/src/rules/requireJsdoc.js
+++ b/src/rules/requireJsdoc.js
@@ -166,6 +166,9 @@ export default {
create (context) {
const sourceCode = context.getSourceCode();
const settings = getSettings(context);
+ if (!settings) {
+ return {};
+ }
const {
require: requireOption,
diff --git a/src/rules/validTypes.js b/src/rules/validTypes.js
index fa1107a0a..3eed1753c 100644
--- a/src/rules/validTypes.js
+++ b/src/rules/validTypes.js
@@ -12,7 +12,6 @@ export default iterateJsdoc(({
}) => {
const {
allowEmptyNamepaths = true,
- checkSeesForNamepaths = false,
} = context.options[0] || {};
const {mode} = settings;
if (!jsdoc.tags) {
@@ -72,29 +71,7 @@ export default iterateJsdoc(({
return true;
};
- const hasTypePosition = utils.tagMightHaveTypePosition(tag.tag) && Boolean(tag.type);
- const mustHaveTypePosition = utils.tagMustHaveTypePosition(tag.tag);
-
- const hasNameOrNamepathPosition = (
- utils.tagMustHaveNamePosition(tag.tag) ||
- utils.tagMightHaveNamePosition(tag.tag)
- ) && Boolean(tag.name) && !(tag.tag === 'see' && !checkSeesForNamepaths);
-
- // Don't handle `@param` here though it does require name as handled by
- // `require-param-name` (`@property` would similarly seem to require one,
- // but is handled by `require-property-name`)
- const mustHaveNameOrNamepathPosition = ![
- 'param', 'arg', 'argument',
- 'property', 'prop',
- ].includes(tag.tag) &&
- utils.tagMustHaveNamePosition(tag.tag) && !allowEmptyNamepaths;
-
- const hasEither = utils.tagMightHaveEitherTypeOrNamePosition(tag.tag) && (hasTypePosition || hasNameOrNamepathPosition);
- const mustHaveEither = utils.tagMustHaveEitherTypeOrNamePosition(tag.tag);
-
- let skip;
- switch (tag.tag) {
- case 'borrows': {
+ if (tag.tag === 'borrows') {
const thisNamepath = tag.description.replace(asExpression, '');
if (!asExpression.test(tag.description) || !thisNamepath) {
@@ -108,53 +85,82 @@ export default iterateJsdoc(({
validNamepathParsing(thatNamepath);
}
- break;
+
+ return;
}
- case 'extends':
- case 'package': case 'private': case 'protected': case 'public': case 'static': {
- if (mode !== 'closure' && mode !== 'permissive' && tag.type) {
- report(`@${tag.tag} should not have a bracketed type in "${mode}" mode.`, null, tag);
- break;
- }
- skip = true;
+
+ const otherModeMaps = ['jsdoc', 'typescript', 'closure', 'permissive'].filter(
+ (mde) => {
+ return mde !== mode;
+ },
+ ).map((mde) => {
+ return utils.getTagStructureForMode(mde);
+ });
+
+ const tagMightHaveNamePosition = utils.tagMightHaveNamePosition(tag.tag, otherModeMaps);
+ if (tagMightHaveNamePosition !== true && tag.name) {
+ const modeInfo = tagMightHaveNamePosition === false ? '' : ` in "${mode}" mode`;
+ report(`@${tag.tag} should not have a name${modeInfo}.`, null, tag);
+
+ return;
}
- // Fallthrough
- case 'typedef': {
- if (!skip && mode !== 'closure' && mode !== 'permissive' && !tag.name) {
- report(`@typedef must have a name in "${mode}" mode.`, null, tag);
- break;
- }
- skip = true;
+ const mightHaveTypePosition = utils.tagMightHaveTypePosition(tag.tag, otherModeMaps);
+ if (mightHaveTypePosition !== true && tag.type) {
+ const modeInfo = mightHaveTypePosition === false ? '' : ` in "${mode}" mode`;
+ report(`@${tag.tag} should not have a bracketed type${modeInfo}.`, null, tag);
+
+ return;
}
- // Fallthrough
- case 'interface': {
- if (!skip && mode === 'closure' && tag.name) {
- report('@interface should not have a name in "closure" mode.', null, tag);
- break;
- }
+ // REQUIRED NAME
+ const tagMustHaveNamePosition = utils.tagMustHaveNamePosition(tag.tag, otherModeMaps);
+
+ // Don't handle `@param` here though it does require name as handled by
+ // `require-param-name` (`@property` would similarly seem to require one,
+ // but is handled by `require-property-name`)
+ if (tagMustHaveNamePosition !== false && !tag.name && !allowEmptyNamepaths && ![
+ 'param', 'arg', 'argument',
+ 'property', 'prop',
+ ].includes(tag.tag)) {
+ const modeInfo = tagMustHaveNamePosition === true ? '' : ` in "${mode}" mode`;
+ report(`Tag @${tag.tag} must have a name/namepath${modeInfo}.`, null, tag);
+
+ return;
}
- // Fallthrough
- default: {
- if (mustHaveEither && !hasEither && !mustHaveTypePosition) {
- report(`Tag @${tag.tag} must have either a type or namepath`, null, tag);
+ // REQUIRED TYPE
+ const mustHaveTypePosition = utils.tagMustHaveTypePosition(tag.tag, otherModeMaps);
+ if (mustHaveTypePosition !== false && !tag.type) {
+ const modeInfo = mustHaveTypePosition === true ? '' : ` in "${mode}" mode`;
+ report(`Tag @${tag.tag} must have a type${modeInfo}.`, null, tag);
- return;
- }
- if (hasTypePosition) {
- validTypeParsing(tag.type);
- } else if (mustHaveTypePosition) {
- report(`Tag @${tag.tag} must have a type`, null, tag);
- }
+ return;
+ }
- if (hasNameOrNamepathPosition) {
- validNamepathParsing(tag.name, tag.tag);
- } else if (mustHaveNameOrNamepathPosition) {
- report(`Tag @${tag.tag} must have a name/namepath`, null, tag);
- }
+ // REQUIRED TYPE OR NAME/NAMEPATH
+ const tagMissingRequiredTypeOrNamepath = utils.tagMissingRequiredTypeOrNamepath(tag, otherModeMaps);
+ if (tagMissingRequiredTypeOrNamepath !== false && !allowEmptyNamepaths) {
+ const modeInfo = tagMissingRequiredTypeOrNamepath === true ? '' : ` in "${mode}" mode`;
+ report(`Tag @${tag.tag} must have either a type or namepath${modeInfo}.`, null, tag);
+
+ return;
}
+
+ // VALID TYPE
+ const hasTypePosition = mightHaveTypePosition === true && Boolean(tag.type);
+ if (hasTypePosition) {
+ validTypeParsing(tag.type);
+ }
+
+ // VALID NAME/NAMEPATH
+ const hasNameOrNamepathPosition = (
+ tagMustHaveNamePosition !== false ||
+ utils.tagMightHaveNamepath(tag.tag)
+ ) && Boolean(tag.name);
+
+ if (hasNameOrNamepathPosition) {
+ validNamepathParsing(tag.name, tag.tag);
}
});
}, {
@@ -171,10 +177,6 @@ export default iterateJsdoc(({
default: true,
type: 'boolean',
},
- checkSeesForNamepaths: {
- default: false,
- type: 'boolean',
- },
},
type: 'object',
},
diff --git a/test/eslint/getJSDocComment.js b/test/eslint/getJSDocComment.js
index 957bc8dcf..5634e7c42 100644
--- a/test/eslint/getJSDocComment.js
+++ b/test/eslint/getJSDocComment.js
@@ -8,6 +8,9 @@ const rule = {
create (context) {
const sourceCode = context.getSourceCode();
const settings = getSettings(context);
+ if (!settings) {
+ return {};
+ }
return {
ObjectExpression (node) {
diff --git a/test/rules/assertions/checkParamNames.js b/test/rules/assertions/checkParamNames.js
index f70caaa96..159e04d0c 100644
--- a/test/rules/assertions/checkParamNames.js
+++ b/test/rules/assertions/checkParamNames.js
@@ -852,6 +852,30 @@ export default {
],
parser: require.resolve('@typescript-eslint/parser'),
},
+ {
+ code: `
+ /**
+ *
+ */
+ function quux() {
+
+ }
+ `,
+ errors: [{
+ line: 1,
+ message: 'Cannot add "name" to `require` with the tag\'s `name` set to `false`',
+ }],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ see: {
+ name: false,
+ required: ['name'],
+ },
+ },
+ },
+ },
+ },
],
valid: [
{
diff --git a/test/rules/assertions/checkTypes.js b/test/rules/assertions/checkTypes.js
index 90475c97f..daebad665 100644
--- a/test/rules/assertions/checkTypes.js
+++ b/test/rules/assertions/checkTypes.js
@@ -1962,6 +1962,33 @@ export default {
},
},
},
+ {
+ code: `
+ /**
+ * @aCustomTag {Number} foo
+ */
+ `,
+ errors: [
+ {
+ line: 3,
+ message: 'Invalid JSDoc @aCustomTag "foo" type "Number"; prefer: "number".',
+ },
+ ],
+ output: `
+ /**
+ * @aCustomTag {number} foo
+ */
+ `,
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ aCustomTag: {
+ type: true,
+ },
+ },
+ },
+ },
+ },
],
valid: [
{
@@ -2483,5 +2510,21 @@ export default {
},
},
},
+ {
+ code: `
+ /**
+ * @aCustomTag {Number} foo
+ */
+ `,
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ aCustomTag: {
+ type: false,
+ },
+ },
+ },
+ },
+ },
],
};
diff --git a/test/rules/assertions/noBadBlocks.js b/test/rules/assertions/noBadBlocks.js
index 79a6e3a09..d51036c9c 100644
--- a/test/rules/assertions/noBadBlocks.js
+++ b/test/rules/assertions/noBadBlocks.js
@@ -42,6 +42,27 @@ export default {
*/
`,
},
+ {
+ code: `
+ function quux() {
+
+ }
+ `,
+ errors: [{
+ line: 1,
+ message: 'Cannot add "name" to `require` with the tag\'s `name` set to `false`',
+ }],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ see: {
+ name: false,
+ required: ['name'],
+ },
+ },
+ },
+ },
+ },
],
valid: [
{
diff --git a/test/rules/assertions/noUndefinedTypes.js b/test/rules/assertions/noUndefinedTypes.js
index 2b3048fa6..250771ffe 100644
--- a/test/rules/assertions/noUndefinedTypes.js
+++ b/test/rules/assertions/noUndefinedTypes.js
@@ -314,6 +314,70 @@ export default {
},
},
},
+ {
+ code: `
+ /**
+ * @aCustomTag {SomeType}
+ */
+ function quux () {}
+ `,
+ errors: [
+ {
+ line: 3,
+ message: 'The type \'SomeType\' is undefined.',
+ },
+ ],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ aCustomTag: {
+ type: true,
+ },
+ },
+ },
+ },
+ },
+ {
+ code: `
+ /**
+ * @namepathDefiner SomeType
+ */
+ /**
+ * @type {SomeType}
+ */
+ `,
+ errors: [
+ {
+ line: 6,
+ message: 'The type \'SomeType\' is undefined.',
+ },
+ ],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ namepathDefiner: {
+ name: 'namepath-referencing',
+ },
+ },
+ },
+ },
+ },
+ {
+ code: `
+ /**
+ * @namepathDefiner SomeType
+ */
+ /**
+ * @type {SomeType}
+ */
+ `,
+ errors: [
+ {
+ line: 6,
+ message: 'The type \'SomeType\' is undefined.',
+ },
+ ],
+ },
],
valid: [
{
@@ -782,5 +846,41 @@ export default {
},
},
},
+ {
+ code: `
+ /**
+ * @aCustomTag {SomeType}
+ */
+ function quux () {}
+ `,
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ aCustomTag: {
+ type: false,
+ },
+ },
+ },
+ },
+ },
+ {
+ code: `
+ /**
+ * @namepathDefiner SomeType
+ */
+ /**
+ * @type {SomeType}
+ */
+ `,
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ namepathDefiner: {
+ name: 'namepath-defining',
+ },
+ },
+ },
+ },
+ },
],
};
diff --git a/test/rules/assertions/requireJsdoc.js b/test/rules/assertions/requireJsdoc.js
index 9a9f9c13d..bad5d6571 100644
--- a/test/rules/assertions/requireJsdoc.js
+++ b/test/rules/assertions/requireJsdoc.js
@@ -2404,6 +2404,30 @@ export default {
}
`,
},
+ {
+ code: `
+ /**
+ *
+ */
+ function quux() {
+
+ }
+ `,
+ errors: [{
+ line: 1,
+ message: 'Cannot add "name" to `require` with the tag\'s `name` set to `false`',
+ }],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ see: {
+ name: false,
+ required: ['name'],
+ },
+ },
+ },
+ },
+ },
],
valid: [{
code: `
diff --git a/test/rules/assertions/validTypes.js b/test/rules/assertions/validTypes.js
index 66ff0ee68..366ccd7fa 100644
--- a/test/rules/assertions/validTypes.js
+++ b/test/rules/assertions/validTypes.js
@@ -133,9 +133,16 @@ export default {
line: 3,
message: 'Syntax error in namepath: foo%',
}],
- options: [{
- checkSeesForNamepaths: true,
- }],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ see: {
+ name: 'namepath-referencing',
+ required: ['name'],
+ },
+ },
+ },
+ },
},
{
code: `
@@ -176,7 +183,7 @@ export default {
`,
errors: [{
line: 3,
- message: 'Tag @callback must have a name/namepath',
+ message: 'Tag @callback must have a name/namepath.',
}],
options: [{
allowEmptyNamepaths: false,
@@ -225,17 +232,49 @@ export default {
{
code: `
/**
- * @extends
+ * @this
*/
class Bar {};
`,
errors: [
{
line: 3,
- message: 'Tag @extends must have either a type or namepath',
+ message: 'Tag @this must have either a type or namepath in "jsdoc" mode.',
+ },
+ ],
+ options: [
+ {
+ allowEmptyNamepaths: false,
},
],
},
+ {
+ code: `
+ /**
+ * @aCustomTag
+ */
+ `,
+ errors: [
+ {
+ line: 3,
+ message: 'Tag @aCustomTag must have either a type or namepath.',
+ },
+ ],
+ options: [
+ {
+ allowEmptyNamepaths: false,
+ },
+ ],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ aCustomTag: {
+ required: ['typeOrNameRequired'],
+ },
+ },
+ },
+ },
+ },
{
code: `
/**
@@ -246,7 +285,7 @@ export default {
errors: [
{
line: 3,
- message: 'Tag @type must have a type',
+ message: 'Tag @type must have a type.',
},
],
},
@@ -310,7 +349,7 @@ export default {
errors: [
{
line: 3,
- message: 'Tag @define must have a type',
+ message: 'Tag @define must have a type in "closure" mode.',
},
],
settings: {
@@ -329,7 +368,7 @@ export default {
errors: [
{
line: 3,
- message: 'Tag @this must have a type',
+ message: 'Tag @this must have a type in "closure" mode.',
},
],
settings: {
@@ -410,6 +449,27 @@ export default {
},
},
},
+ {
+ code: `
+ /**
+ * @aCustomTag name
+ */
+ `,
+ errors: [
+ {
+ message: '@aCustomTag should not have a name.',
+ },
+ ],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ aCustomTag: {
+ name: false,
+ },
+ },
+ },
+ },
+ },
{
code: `
/**
@@ -420,7 +480,12 @@ export default {
`,
errors: [
{
- message: '@typedef must have a name in "jsdoc" mode.',
+ message: 'Tag @typedef must have a name/namepath in "jsdoc" mode.',
+ },
+ ],
+ options: [
+ {
+ allowEmptyNamepaths: false,
},
],
settings: {
@@ -448,6 +513,125 @@ export default {
},
},
},
+ {
+ code: `
+ /**
+ * @aCustomTag {SomeType}
+ */
+ function quux () {}
+
+ `,
+ errors: [
+ {
+ message: '@aCustomTag should not have a bracketed type.',
+ },
+ ],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ aCustomTag: {
+ type: false,
+ },
+ },
+ },
+ },
+ },
+ {
+ code: `
+ /**
+ * @see foo%
+ */
+ function quux() {
+
+ }
+ `,
+ errors: [{
+ line: 1,
+ message: 'Cannot add "name" to `require` with the tag\'s `name` set to `false`',
+ }],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ see: {
+ name: false,
+ required: ['name'],
+ },
+ },
+ },
+ },
+ },
+ {
+ code: `
+ /**
+ * @see foo%
+ */
+ function quux() {
+
+ }
+ `,
+ errors: [{
+ line: 1,
+ message: 'Cannot add "type" to `require` with the tag\'s `type` set to `false`',
+ }],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ see: {
+ required: ['type'],
+ type: false,
+ },
+ },
+ },
+ },
+ },
+ {
+ code: `
+ /**
+ * @see foo%
+ */
+ function quux() {
+
+ }
+ `,
+ errors: [{
+ line: 1,
+ message: 'Cannot add "typeOrNameRequired" to `require` with the tag\'s `name` set to `false`',
+ }],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ see: {
+ name: false,
+ required: ['typeOrNameRequired'],
+ },
+ },
+ },
+ },
+ },
+ {
+ code: `
+ /**
+ * @see foo%
+ */
+ function quux() {
+
+ }
+ `,
+ errors: [{
+ line: 1,
+ message: 'Cannot add "typeOrNameRequired" to `require` with the tag\'s `type` set to `false`',
+ }],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ see: {
+ required: ['typeOrNameRequired'],
+ type: false,
+ },
+ },
+ },
+ },
+ },
],
valid: [
{
@@ -562,9 +746,16 @@ export default {
}
`,
- options: [{
- checkSeesForNamepaths: true,
- }],
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ see: {
+ name: 'namepath-referencing',
+ required: ['name'],
+ },
+ },
+ },
+ },
},
{
code: `
@@ -607,6 +798,16 @@ export default {
}
`,
},
+ {
+ code: `
+ /**
+ * @aCustomTag
+ */
+ function quux() {
+
+ }
+ `,
+ },
{
code: `
/**
@@ -791,6 +992,11 @@ export default {
function quux () {}
`,
+ options: [
+ {
+ allowEmptyNamepaths: false,
+ },
+ ],
settings: {
jsdoc: {
mode: 'closure',
@@ -826,5 +1032,24 @@ export default {
},
],
},
+ {
+ code: `
+ /**
+ * @see
+ */
+ function quux() {
+
+ }
+ `,
+ settings: {
+ jsdoc: {
+ structuredTags: {
+ see: {
+ name: 'namepath-referencing',
+ },
+ },
+ },
+ },
+ },
],
};