From 7257e44bdf70c4cfc07bb6d0e426e6820744eead Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Sat, 19 Jun 2021 15:40:47 +0800 Subject: [PATCH] feat: `message` options for `match-description`; fixes #688 --- .README/rules/match-description.md | 53 +++++++++- README.md | 102 ++++++++++++++++++- src/rules/matchDescription.js | 86 ++++++++++++++-- test/rules/assertions/matchDescription.js | 116 ++++++++++++++++++++++ 4 files changed, 338 insertions(+), 19 deletions(-) diff --git a/.README/rules/match-description.md b/.README/rules/match-description.md index a88cdc90d..0b434276d 100644 --- a/.README/rules/match-description.md +++ b/.README/rules/match-description.md @@ -37,6 +37,21 @@ You can supply your own expression to override the default, passing a } ``` +##### `message` + +You may provide a custom default message by using the following format: + +```js +{ + 'jsdoc/match-description': ['error', { + message: 'The default dscription should begin with a capital letter.' + }] +} +``` + +This can be overridden per tag or for the main block description by setting +`message` within `tags` or `mainDescription`, respectively. + ##### `tags` If you want different regular expressions to apply to tags, you may use @@ -63,6 +78,18 @@ tag should be linted with the `matchDescription` value (or the default). } ``` +Alternatively, you may supply an object with a `message` property to indicate +the error message for that tag. + +```js +{ + 'jsdoc/match-description': ['error', {tags: { + param: {message: 'Begin with a hyphen', match: '\\- [A-Z].*\\.'}, + returns: {message: 'Capitalize for returns (the default)', match: true} + }}] +} +``` + The tags `@param`/`@arg`/`@argument` and `@property`/`@prop` will be properly parsed to ensure that the matched "description" text includes only the text after the name. @@ -75,8 +102,9 @@ is `xyz`). ##### `mainDescription` -If you wish to override the main function description without changing the -default `match-description`, you may use `mainDescription`: +If you wish to override the main block description without changing the +default `match-description` (which can cascade to the `tags` with `true`), +you may use `mainDescription`: ```js { @@ -91,8 +119,25 @@ default `match-description`, you may use `mainDescription`: ``` 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`. +block description (and only the main block description) is linted, though you +may disable checking it by setting it to `false`. + +You may also provide an object with `message`: + +```js +{ + 'jsdoc/match-description': ['error', { + mainDescription: { + message: 'Capitalize first word of JSDoc block descriptions', + match: '[A-Z].*\\.' + }, + tags: { + param: true, + returns: true + } + }] +} +``` ##### `contexts` diff --git a/README.md b/README.md index 9e228f980..22df65943 100644 --- a/README.md +++ b/README.md @@ -6390,6 +6390,22 @@ You can supply your own expression to override the default, passing a } ``` + +##### message + +You may provide a custom default message by using the following format: + +```js +{ + 'jsdoc/match-description': ['error', { + message: 'The default dscription should begin with a capital letter.' + }] +} +``` + +This can be overridden per tag or for the main block description by setting +`message` within `tags` or `mainDescription`, respectively. + ##### tags @@ -6417,6 +6433,18 @@ tag should be linted with the `matchDescription` value (or the default). } ``` +Alternatively, you may supply an object with a `message` property to indicate +the error message for that tag. + +```js +{ + 'jsdoc/match-description': ['error', {tags: { + param: {message: 'Begin with a hyphen', match: '\\- [A-Z].*\\.'}, + returns: {message: 'Capitalize for returns (the default)', match: true} + }}] +} +``` + The tags `@param`/`@arg`/`@argument` and `@property`/`@prop` will be properly parsed to ensure that the matched "description" text includes only the text after the name. @@ -6430,8 +6458,9 @@ is `xyz`). ##### mainDescription -If you wish to override the main function description without changing the -default `match-description`, you may use `mainDescription`: +If you wish to override the main block description without changing the +default `match-description` (which can cascade to the `tags` with `true`), +you may use `mainDescription`: ```js { @@ -6446,8 +6475,25 @@ default `match-description`, you may use `mainDescription`: ``` 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`. +block description (and only the main block description) is linted, though you +may disable checking it by setting it to `false`. + +You may also provide an object with `message`: + +```js +{ + 'jsdoc/match-description': ['error', { + mainDescription: { + message: 'Capitalize first word of JSDoc block descriptions', + match: '[A-Z].*\\.' + }, + tags: { + param: true, + returns: true + } + }] +} +``` ##### contexts @@ -6482,6 +6528,15 @@ const q = class { // "jsdoc/match-description": ["error"|"warn", {"contexts":["ClassExpression"]}] // Message: JSDoc description does not satisfy the regex pattern. +/** + * foo. + */ +const q = class { + +} +// "jsdoc/match-description": ["error"|"warn", {"contexts":["ClassExpression"],"message":"Needs to begin with a capital letter and end with an end mark."}] +// Message: Needs to begin with a capital letter and end with an end mark. + /** * foo. */ @@ -6528,6 +6583,15 @@ function quux () { // "jsdoc/match-description": ["error"|"warn", {"matchDescription":"[А-Я][А-я]+\\."}] // Message: JSDoc description does not satisfy the regex pattern. +/** + * тест. + */ +function quux () { + +} +// "jsdoc/match-description": ["error"|"warn", {"matchDescription":"[А-Я][А-я]+\\.","message":"Needs to begin with a capital letter and end with an end mark."}] +// Message: Needs to begin with a capital letter and end with an end mark. + /** * Abc. */ @@ -6537,6 +6601,15 @@ function quux () { // "jsdoc/match-description": ["error"|"warn", {"mainDescription":"[А-Я][А-я]+\\.","tags":{"param":true}}] // Message: JSDoc description does not satisfy the regex pattern. +/** + * Abc. + */ +function quux () { + +} +// "jsdoc/match-description": ["error"|"warn", {"mainDescription":{"match":"[А-Я][А-я]+\\.","message":"Needs to begin with a Cyrillic capital letter and end with a period."},"tags":{"param":true}}] +// Message: Needs to begin with a Cyrillic capital letter and end with a period. + /** * Foo */ @@ -6640,6 +6713,17 @@ function quux (foo) { */ function quux (foo) { +} +// "jsdoc/match-description": ["error"|"warn", {"mainDescription":{"match":"^[a-zA-Z]*$","message":"Letters only"},"tags":{"param":{"match":true,"message":"Needs to begin with a capital letter and end with a period."}}}] +// Message: Needs to begin with a capital letter and end with a period. + +/** + * Foo + * + * @param foo foo. + */ +function quux (foo) { + } // "jsdoc/match-description": ["error"|"warn", {"mainDescription":false,"tags":{"param":true}}] // Message: JSDoc description does not satisfy the regex pattern. @@ -6858,6 +6942,16 @@ function quux () { } +/** + * Foo. + * + * Bar. + */ +function quux () { + +} +// "jsdoc/match-description": ["error"|"warn", {"message":"This won't be shown"}] + /** * Тест. */ diff --git a/src/rules/matchDescription.js b/src/rules/matchDescription.js index 595e5cc82..96df6b717 100644 --- a/src/rules/matchDescription.js +++ b/src/rules/matchDescription.js @@ -16,28 +16,48 @@ export default iterateJsdoc(({ context, utils, }) => { - const options = context.options[0] || {}; + const { + mainDescription, + matchDescription, + message, + tags, + } = context.options[0] || {}; const validateDescription = (description, tag) => { - if (!tag && options.mainDescription === false) { + let mainDescriptionMatch = mainDescription; + let errorMessage = message; + if (typeof mainDescription === 'object') { + mainDescriptionMatch = mainDescription.match; + errorMessage = mainDescription.message; + } + if (!tag && mainDescriptionMatch === false) { return; } - let tagValue = options.mainDescription; + let tagValue = mainDescriptionMatch; if (tag) { const tagName = tag.tag; - tagValue = options.tags[tagName]; + if (typeof tags[tagName] === 'object') { + tagValue = tags[tagName].match; + errorMessage = tags[tagName].message; + } else { + tagValue = tags[tagName]; + } } const regex = utils.getRegexFromString( - stringOrDefault(tagValue, options.matchDescription), + stringOrDefault(tagValue, matchDescription), ); if (!regex.test(description)) { - report('JSDoc description does not satisfy the regex pattern.', null, tag || { - // Add one as description would typically be into block - line: jsdoc.source[0].number + 1, - }); + report( + errorMessage || 'JSDoc description does not satisfy the regex pattern.', + null, + tag || { + // Add one as description would typically be into block + line: jsdoc.source[0].number + 1, + }, + ); } }; @@ -48,12 +68,12 @@ export default iterateJsdoc(({ ); } - if (!options.tags || !Object.keys(options.tags).length) { + if (!tags || !Object.keys(tags).length) { return; } const hasOptionTag = (tagName) => { - return Boolean(options.tags[tagName]); + return Boolean(tags[tagName]); }; utils.forEachPreferredTag('description', (matchingJsdocTag, targetTagName) => { @@ -121,12 +141,35 @@ export default iterateJsdoc(({ { type: 'boolean', }, + { + additionalProperties: false, + properties: { + match: { + oneOf: [ + { + format: 'regex', + type: 'string', + }, + { + type: 'boolean', + }, + ], + }, + message: { + type: 'string', + }, + }, + type: 'object', + }, ], }, matchDescription: { format: 'regex', type: 'string', }, + message: { + type: 'string', + }, tags: { patternProperties: { '.*': { @@ -139,6 +182,27 @@ export default iterateJsdoc(({ enum: [true], type: 'boolean', }, + { + additionalProperties: false, + properties: { + match: { + oneOf: [ + { + format: 'regex', + type: 'string', + }, + { + enum: [true], + type: 'boolean', + }, + ], + }, + message: { + type: 'string', + }, + }, + type: 'object', + }, ], }, }, diff --git a/test/rules/assertions/matchDescription.js b/test/rules/assertions/matchDescription.js index 85ac34012..c2c2973a1 100644 --- a/test/rules/assertions/matchDescription.js +++ b/test/rules/assertions/matchDescription.js @@ -23,6 +23,30 @@ export default { }, ], }, + { + code: ` + /** + * foo. + */ + const q = class { + + } + `, + errors: [ + { + line: 3, + message: 'Needs to begin with a capital letter and end with an end mark.', + }, + ], + options: [ + { + contexts: [ + 'ClassExpression', + ], + message: 'Needs to begin with a capital letter and end with an end mark.', + }, + ], + }, { code: ` /** @@ -137,6 +161,26 @@ export default { matchDescription: '[\u0410-\u042F][\u0410-\u044F]+\\.', }], }, + { + code: ` + /** + * тест. + */ + function quux () { + + } + `, + errors: [ + { + line: 3, + message: 'Needs to begin with a capital letter and end with an end mark.', + }, + ], + options: [{ + matchDescription: '[\u0410-\u042F][\u0410-\u044F]+\\.', + message: 'Needs to begin with a capital letter and end with an end mark.', + }], + }, { code: ` /** @@ -159,6 +203,31 @@ export default { }, }], }, + { + code: ` + /** + * Abc. + */ + function quux () { + + } + `, + errors: [ + { + line: 3, + message: 'Needs to begin with a Cyrillic capital letter and end with a period.', + }, + ], + options: [{ + mainDescription: { + match: '[\u0410-\u042F][\u0410-\u044F]+\\.', + message: 'Needs to begin with a Cyrillic capital letter and end with a period.', + }, + tags: { + param: true, + }, + }], + }, { code: ` /** @@ -387,6 +456,38 @@ export default { } `, + errors: [ + { + line: 5, + message: 'Needs to begin with a capital letter and end with a period.', + }, + ], + options: [ + { + mainDescription: { + match: '^[a-zA-Z]*$', + message: 'Letters only', + }, + tags: { + param: { + match: true, + message: 'Needs to begin with a capital letter and end with a period.', + }, + }, + }, + ], + }, + { + code: ` + /** + * Foo + * + * @param foo foo. + */ + function quux (foo) { + + } + `, errors: [ { line: 5, @@ -866,6 +967,21 @@ export default { } `, }, + { + code: ` + /** + * Foo. + * + * Bar. + */ + function quux () { + + } + `, + options: [{ + message: 'This won\'t be shown', + }], + }, { code: ` /**