diff --git a/.README/README.md b/.README/README.md index d4902aa25..9be098081 100644 --- a/.README/README.md +++ b/.README/README.md @@ -199,6 +199,8 @@ Or one may set the targeted tag to an object with a custom `message`, but withou } ``` +Note that the preferred tags indicated in the `settings.jsdoc.tagNamePreference` +map will be assumed to be defined by `check-tag-names`. The defaults in `eslint-plugin-jsdoc` (for tags which offer aliases) are as follows: @@ -236,24 +238,6 @@ This setting is utilized by the the rule for tag name checking - `require-returns-description` - `require-returns-type` -### Additional Tag Names - -Use `settings.jsdoc.additionalTagNames` to configure additional, allowed JSDoc -tags in the rule `check-tag-names`. The format of the configuration is as follows: - -```json -{ - "rules": {}, - "settings": { - "jsdoc": { - "additionalTagNames": { - "customTags": ["define", "record"] - } - } - } -} -``` - ### `@override`/`@augments`/`@extends`/`@implements` Without Accompanying `@param`/`@description`/`@example`/`@returns` The following settings allows the element(s) they reference to be omitted diff --git a/.README/rules/check-tag-names.md b/.README/rules/check-tag-names.md index 23162ac36..c896ae027 100644 --- a/.README/rules/check-tag-names.md +++ b/.README/rules/check-tag-names.md @@ -75,10 +75,26 @@ version yields ``` +Note that the tags indicated as replacements in `settings.jsdoc.tagNamePreference` will automatically be considered as valid. + +#### Options + +##### `definedTags` + +Use an array of `definedTags` strings to configure additional, allowed JSDoc tags. +The format is as follows: + +```json +{ + "definedTags": ["define", "record"] +} +``` + ||| |---|---| |Context|everywhere| |Tags|N/A| -|Settings|`tagNamePreference`, `additionalTagNames`| +|Options|`definedTags`| +|Settings|`tagNamePreference`| diff --git a/README.md b/README.md index 7d6e8f32b..fb9db051a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ JSDoc linting rules for ESLint. * [Allow `@private` to disable rules for that comment block](#eslint-plugin-jsdoc-settings-allow-private-to-disable-rules-for-that-comment-block) * [Exempting empty functions from `require-jsdoc`](#eslint-plugin-jsdoc-settings-exempting-empty-functions-from-require-jsdoc) * [Alias Preference](#eslint-plugin-jsdoc-settings-alias-preference) - * [Additional Tag Names](#eslint-plugin-jsdoc-settings-additional-tag-names) * [`@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) * [Settings to Configure `valid-types`](#eslint-plugin-jsdoc-settings-settings-to-configure-valid-types) @@ -249,6 +248,8 @@ Or one may set the targeted tag to an object with a custom `message`, but withou } ``` +Note that the preferred tags indicated in the `settings.jsdoc.tagNamePreference` +map will be assumed to be defined by `check-tag-names`. The defaults in `eslint-plugin-jsdoc` (for tags which offer aliases) are as follows: @@ -286,25 +287,6 @@ This setting is utilized by the the rule for tag name checking - `require-returns-description` - `require-returns-type` - -### Additional Tag Names - -Use `settings.jsdoc.additionalTagNames` to configure additional, allowed JSDoc -tags in the rule `check-tag-names`. The format of the configuration is as follows: - -```json -{ - "rules": {}, - "settings": { - "jsdoc": { - "additionalTagNames": { - "customTags": ["define", "record"] - } - } - } -} -``` - ### @override/@augments/@extends/@implements Without Accompanying @param/@description/@example/@returns @@ -1249,11 +1231,29 @@ version yields ``` +Note that the tags indicated as replacements in `settings.jsdoc.tagNamePreference` will automatically be considered as valid. + + +#### Options + + +##### definedTags + +Use an array of `definedTags` strings to configure additional, allowed JSDoc tags. +The format is as follows: + +```json +{ + "definedTags": ["define", "record"] +} +``` + ||| |---|---| |Context|everywhere| |Tags|N/A| -|Settings|`tagNamePreference`, `additionalTagNames`| +|Options|`definedTags`| +|Settings|`tagNamePreference`| The following patterns are considered problems: @@ -1327,7 +1327,7 @@ function quux (foo) { function quux (foo) { } -// Settings: {"jsdoc":{"additionalTagNames":{"customTags":["bar"]}}} +// Options: [{"definedTags":["bar"]}] // Message: Invalid JSDoc tag name "baz". /** @@ -1337,7 +1337,7 @@ function quux (foo) { function quux (foo) { } -// Settings: {"jsdoc":{"additionalTagNames":{"customTags":["bar"]}}} +// Options: [{"definedTags":["bar"]}] // Message: Invalid JSDoc tag name "baz". /** @@ -1375,6 +1375,15 @@ function quux () { } // Settings: {"jsdoc":{"tagNamePreference":{"todo":{"message":"Please use x-todo instead of todo","replacement":"x-todo"}}}} // Message: Please use x-todo instead of todo + +/** + * @todo + */ +function quux () { + +} +// Settings: {"jsdoc":{"tagNamePreference":{"todo":55}}} +// Message: Invalid `settings.jsdoc.tagNamePreference`. Values must be falsy, a string, or an object. ```` The following patterns are not considered problems: @@ -1408,7 +1417,15 @@ function quux (foo) { function quux (foo) { } -// Settings: {"jsdoc":{"additionalTagNames":{"customTags":["bar"]}}} +// Options: [{"definedTags":["bar"]}] + +/** + * @baz @bar foo + */ +function quux (foo) { + +} +// Options: [{"definedTags":["baz","bar"]}] /** * @baz @bar foo @@ -1416,7 +1433,7 @@ function quux (foo) { function quux (foo) { } -// Settings: {"jsdoc":{"additionalTagNames":{"customTags":["baz","bar"]}}} +// Settings: {"jsdoc":{"tagNamePreference":{"param":"baz","returns":{"message":"Prefer `bar`","replacement":"bar"},"todo":false}}} /** * @abstract @@ -1521,7 +1538,7 @@ Date RegExp ``` - + #### Options `check-types` allows one option: @@ -2402,10 +2419,10 @@ by our supported Node versions): ``^([A-Z]|[`\\d_])[\\s\\S]*[.?!`]$`` - + #### Options - + ##### matchDescription You can supply your own expression to override the default, passing a @@ -2420,7 +2437,7 @@ You can supply your own expression to override the default, passing a As with the default, the supplied regular expression will be applied with the Unicode (`"u"`) flag and is *not* case-insensitive. - + ##### tags If you want different regular expressions to apply to tags, you may use @@ -2447,7 +2464,7 @@ tag should be linted with the `matchDescription` value (or the default). } ``` - + ##### mainDescription If you wish to override the main function description without changing the @@ -2469,7 +2486,7 @@ There is no need to add `mainDescription: true`, as by default, the main function (and only the main function) is linted, though you may disable checking it by setting it to `false`. - + ##### contexts Set this to an array of strings representing the AST context @@ -2915,7 +2932,7 @@ const q = { Enforces a consistent padding of the block description. - + #### Options This rule allows one optional string argument. If it is `"always"` then a problem is raised when there is a newline after the description. If it is `"never"` then a problem is raised when there is no newline after the description. The default value is `"always"`. @@ -3075,7 +3092,7 @@ The following types are always considered defined. - `any`, `*` - `Array`, `Object`, `RegExp`, `Date`, `Function` - + #### Options An option object may have the following keys: @@ -3671,7 +3688,7 @@ Requires that all functions have a description. * All functions must have a `@description` tag. * Every description tag must have a non-empty description that explains the purpose of the method. - + #### Options An options object may have any of the following properties: @@ -3865,7 +3882,7 @@ Requires that all functions have examples. * All functions must have one or more `@example` tags. * Every example tag must have a non-empty description that explains the method's usage. - + #### Options Has an object option with one optional property: @@ -3996,7 +4013,7 @@ function quux () { Requires a hyphen before the `@param` description. - + #### Options This rule takes one optional string argument. If it is `"always"` then a problem is raised when there is no hyphen before the description. If it is `"never"` then a problem is raised when there is a hyphen before the description. The default value is `"always"`. @@ -4102,7 +4119,7 @@ function quux () { Checks for presence of jsdoc comments, on class declarations as well as functions. - + #### Options Accepts one optional options object with the following optional keys. @@ -5138,7 +5155,7 @@ function quux (foo) { Requires that all function parameters are documented. - + #### Options An options object accepts one optional property: @@ -6065,7 +6082,7 @@ function quux () { Requires returns are documented. - + #### Options - `exemptedBy` - Array of tags (e.g., `['type']`) whose presence on the document diff --git a/src/iterateJsdoc.js b/src/iterateJsdoc.js index 7affb6be9..bc43a4bbb 100644 --- a/src/iterateJsdoc.js +++ b/src/iterateJsdoc.js @@ -28,7 +28,6 @@ const getUtils = ( jsdocNode, { tagNamePreference, - additionalTagNames, allowEmptyNamepaths, overrideReplacesDocs, implementsReplacesDocs, @@ -89,8 +88,8 @@ const getUtils = ( return isObject && !allowObjectReturn ? ret.replacement : ret; }; - utils.isValidTag = (name) => { - return jsdocUtils.isValidTag(name, additionalTagNames); + utils.isValidTag = (name, definedTags) => { + return jsdocUtils.isValidTag(name, definedTags); }; utils.hasATag = (name) => { @@ -261,9 +260,6 @@ const getSettings = (context) => { settings.implementsReplacesDocs = _.get(context, 'settings.jsdoc.implementsReplacesDocs'); settings.augmentsExtendsReplacesDocs = _.get(context, 'settings.jsdoc.augmentsExtendsReplacesDocs'); - // `check-tag-names` only - settings.additionalTagNames = _.get(context, 'settings.jsdoc.additionalTagNames') || {}; - // `check-examples` only settings.exampleCodeRegex = _.get(context, 'settings.jsdoc.exampleCodeRegex') || null; settings.rejectExampleCodeRegex = _.get(context, 'settings.jsdoc.rejectExampleCodeRegex') || null; diff --git a/src/jsdocUtils.js b/src/jsdocUtils.js index b77321912..b421e5d86 100644 --- a/src/jsdocUtils.js +++ b/src/jsdocUtils.js @@ -79,9 +79,9 @@ const getPreferredTagName = (name : string, tagPreference : Object = {}) : strin return name; }; -const isValidTag = (name : string, additionalTagNames : Object) : boolean => { +const isValidTag = (name : string, definedTags : Array) : boolean => { const validTagNames = _.keys(tagNames).concat(_.flatten(_.values(tagNames))); - const additionalTags = additionalTagNames.customTags || []; + const additionalTags = definedTags; const allTags = validTagNames.concat(additionalTags); return allTags.includes(name); diff --git a/src/rules/checkTagNames.js b/src/rules/checkTagNames.js index c6489504c..e9afdc12d 100644 --- a/src/rules/checkTagNames.js +++ b/src/rules/checkTagNames.js @@ -1,3 +1,4 @@ +import _ from 'lodash'; import iterateJsdoc from '../iterateJsdoc'; export default iterateJsdoc(({ @@ -5,14 +6,41 @@ export default iterateJsdoc(({ jsdoc, report, utils, + context, jsdocNode }) => { if (!jsdoc.tags) { return; } + const {definedTags = []} = context.options[0] || {}; + + let definedPreferredTags = []; + const preferredTags = _.get(context, 'settings.jsdoc.tagNamePreference'); + if (preferredTags) { + // Replace `_.values` with `Object.values` when we may start requiring Node 7+ + definedPreferredTags = _.values(preferredTags).map((preferredTag) => { + if (typeof preferredTag === 'string') { + // May become an empty string but will be filtered out below + return preferredTag; + } + if (!preferredTag) { + return undefined; + } + if (typeof preferredTag !== 'object') { + report( + 'Invalid `settings.jsdoc.tagNamePreference`. Values must be falsy, a string, or an object.' + ); + } + + return preferredTag.replacement; + }).filter((preferredType) => { + return preferredType; + }); + } + jsdoc.tags.forEach((jsdocTag) => { const tagName = jsdocTag.tag; - if (utils.isValidTag(tagName)) { + if (utils.isValidTag(tagName, [...definedTags, ...definedPreferredTags])) { let preferredTagName = utils.getPreferredTagName( tagName, true, @@ -41,6 +69,20 @@ export default iterateJsdoc(({ iterateAllJsdocs: true, meta: { fixable: 'code', + schema: [ + { + additionalProperties: false, + properties: { + definedTags: { + items: { + type: 'string' + }, + type: 'array' + } + }, + type: 'object' + } + ], type: 'suggestion' } }); diff --git a/test/jsdocUtils.js b/test/jsdocUtils.js index 6dec68ed9..30119f25c 100644 --- a/test/jsdocUtils.js +++ b/test/jsdocUtils.js @@ -26,22 +26,22 @@ describe('jsdocUtils', () => { describe('isValidTag()', () => { context('tag is invalid', () => { it('returns false', () => { - expect(jsdocUtils.isValidTag('foo', {})).to.equal(false); + expect(jsdocUtils.isValidTag('foo', [])).to.equal(false); }); }); context('tag is valid', () => { it('returns true', () => { - expect(jsdocUtils.isValidTag('param', {})).to.equal(true); + expect(jsdocUtils.isValidTag('param', [])).to.equal(true); }); }); context('tag is valid alias', () => { it('returns true', () => { - expect(jsdocUtils.isValidTag('arg', {})).to.equal(true); + expect(jsdocUtils.isValidTag('arg', [])).to.equal(true); }); }); context('tag is valid and customized', () => { it('returns true', () => { - expect(jsdocUtils.isValidTag('foobar', {customTags: ['foobar']})).to.equal(true); + expect(jsdocUtils.isValidTag('foobar', ['foobar'])).to.equal(true); }); }); }); diff --git a/test/rules/assertions/checkTagNames.js b/test/rules/assertions/checkTagNames.js index 261d83678..820e8851a 100644 --- a/test/rules/assertions/checkTagNames.js +++ b/test/rules/assertions/checkTagNames.js @@ -164,13 +164,9 @@ export default { message: 'Invalid JSDoc tag name "baz".' } ], - settings: { - jsdoc: { - additionalTagNames: { - customTags: ['bar'] - } - } - } + options: [{ + definedTags: ['bar'] + }] }, { code: ` @@ -188,13 +184,9 @@ export default { message: 'Invalid JSDoc tag name "baz".' } ], - settings: { - jsdoc: { - additionalTagNames: { - customTags: ['bar'] - } - } - } + options: [{ + definedTags: ['bar'] + }] }, { code: ` @@ -291,6 +283,31 @@ export default { } } } + }, + { + code: ` + /** + * @todo + */ + function quux () { + + } + `, + errors: [ + { + message: 'Invalid `settings.jsdoc.tagNamePreference`. Values must be falsy, a string, or an object.' + }, + { + message: 'Invalid JSDoc tag (preference). Replace "todo" JSDoc tag with "55".' + } + ], + settings: { + jsdoc: { + tagNamePreference: { + todo: 55 + } + } + } } ], valid: [ @@ -340,13 +357,22 @@ export default { } `, - settings: { - jsdoc: { - additionalTagNames: { - customTags: ['bar'] + options: [{ + definedTags: ['bar'] + }] + }, + { + code: ` + /** + * @baz @bar foo + */ + function quux (foo) { + } - } - } + `, + options: [{ + definedTags: ['baz', 'bar'] + }] }, { code: ` @@ -359,8 +385,13 @@ export default { `, settings: { jsdoc: { - additionalTagNames: { - customTags: ['baz', 'bar'] + tagNamePreference: { + param: 'baz', + returns: { + message: 'Prefer `bar`', + replacement: 'bar' + }, + todo: false } } }