diff --git a/conf/rule-type-list.json b/conf/rule-type-list.json index 6ca730f34f0..f48454bb630 100644 --- a/conf/rule-type-list.json +++ b/conf/rule-type-list.json @@ -23,6 +23,8 @@ { "removed": "space-in-brackets", "replacedBy": ["object-curly-spacing", "array-bracket-spacing"] }, { "removed": "space-return-throw-case", "replacedBy": ["keyword-spacing"] }, { "removed": "space-unary-word-ops", "replacedBy": ["space-unary-ops"] }, - { "removed": "spaced-line-comment", "replacedBy": ["spaced-comment"] } + { "removed": "spaced-line-comment", "replacedBy": ["spaced-comment"] }, + { "removed": "valid-jsdoc", "replacedBy": [] }, + { "removed": "require-jsdoc", "replacedBy": [] } ] } diff --git a/docs/src/_data/rules.json b/docs/src/_data/rules.json index b2502bfedbf..11e0c65b296 100644 --- a/docs/src/_data/rules.json +++ b/docs/src/_data/rules.json @@ -652,7 +652,7 @@ "description": "Disallow `Array` constructors", "recommended": false, "fixable": false, - "hasSuggestions": true + "hasSuggestions": false }, { "name": "no-bitwise", @@ -680,7 +680,7 @@ "description": "Disallow the use of `console`", "recommended": false, "fixable": false, - "hasSuggestions": true + "hasSuggestions": false }, { "name": "no-continue", diff --git a/docs/src/_data/rules_meta.json b/docs/src/_data/rules_meta.json index 7f310da9f23..dd60d09b2e5 100644 --- a/docs/src/_data/rules_meta.json +++ b/docs/src/_data/rules_meta.json @@ -2371,16 +2371,6 @@ "url": "https://eslint.org/docs/latest/rules/require-await" } }, - "require-jsdoc": { - "type": "suggestion", - "docs": { - "description": "Require JSDoc comments", - "recommended": false, - "url": "https://eslint.org/docs/latest/rules/require-jsdoc" - }, - "deprecated": true, - "replacedBy": [] - }, "require-unicode-regexp": { "type": "suggestion", "docs": { @@ -2602,17 +2592,6 @@ "url": "https://eslint.org/docs/latest/rules/use-isnan" } }, - "valid-jsdoc": { - "type": "suggestion", - "docs": { - "description": "Enforce valid JSDoc comments", - "recommended": false, - "url": "https://eslint.org/docs/latest/rules/valid-jsdoc" - }, - "fixable": "code", - "deprecated": true, - "replacedBy": [] - }, "valid-typeof": { "type": "problem", "docs": { diff --git a/docs/src/rules/max-lines.md b/docs/src/rules/max-lines.md index 9aa4ae4d6d1..128fb0d0616 100644 --- a/docs/src/rules/max-lines.md +++ b/docs/src/rules/max-lines.md @@ -33,12 +33,12 @@ This rule has a number or object option: ### max -Examples of **incorrect** code for this rule with a max value of `2`: +Examples of **incorrect** code for this rule with a max value of `3`: ::: incorrect ```js -/*eslint max-lines: ["error", 2]*/ +/*eslint max-lines: ["error", 3]*/ var a, b, c; @@ -49,7 +49,7 @@ var a, ::: incorrect ```js -/*eslint max-lines: ["error", 2]*/ +/*eslint max-lines: ["error", 3]*/ var a, b,c; @@ -60,7 +60,7 @@ var a, ::: incorrect ```js -/*eslint max-lines: ["error", 2]*/ +/*eslint max-lines: ["error", 3]*/ // a comment var a, b,c; @@ -68,12 +68,12 @@ var a, ::: -Examples of **correct** code for this rule with a max value of `2`: +Examples of **correct** code for this rule with a max value of `3`: ::: correct ```js -/*eslint max-lines: ["error", 2]*/ +/*eslint max-lines: ["error", 3]*/ var a, b, c; ``` @@ -83,7 +83,7 @@ var a, ::: correct ```js -/*eslint max-lines: ["error", 2]*/ +/*eslint max-lines: ["error", 3]*/ var a, b, c; ``` @@ -93,7 +93,7 @@ var a, b, c; ::: correct ```js -/*eslint max-lines: ["error", 2]*/ +/*eslint max-lines: ["error", 3]*/ // a comment var a, b, c; ``` @@ -107,7 +107,7 @@ Examples of **incorrect** code for this rule with the `{ "skipBlankLines": true ::: incorrect ```js -/*eslint max-lines: ["error", {"max": 2, "skipBlankLines": true}]*/ +/*eslint max-lines: ["error", {"max": 3, "skipBlankLines": true}]*/ var a, b, @@ -121,7 +121,7 @@ Examples of **correct** code for this rule with the `{ "skipBlankLines": true }` ::: correct ```js -/*eslint max-lines: ["error", {"max": 2, "skipBlankLines": true}]*/ +/*eslint max-lines: ["error", {"max": 3, "skipBlankLines": true}]*/ var a, b, c; diff --git a/docs/src/rules/require-jsdoc.md b/docs/src/rules/require-jsdoc.md index 82ea4b43222..37f2cd78173 100644 --- a/docs/src/rules/require-jsdoc.md +++ b/docs/src/rules/require-jsdoc.md @@ -5,8 +5,9 @@ related_rules: - valid-jsdoc --- - -This rule was [**deprecated**](https://eslint.org/blog/2018/11/jsdoc-end-of-life) in ESLint v5.10.0. +:::important +This rule was removed in ESLint v9.0.0 and replaced by the [`eslint-plugin-jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc) equivalent. +::: [JSDoc](http://usejsdoc.org) is a JavaScript API documentation generator. It uses specially-formatted comments inside of code to generate API documentation automatically. For example, this is what a JSDoc comment looks like for a function: diff --git a/docs/src/rules/valid-jsdoc.md b/docs/src/rules/valid-jsdoc.md index 33b48dcbf3d..5a859fac346 100644 --- a/docs/src/rules/valid-jsdoc.md +++ b/docs/src/rules/valid-jsdoc.md @@ -7,9 +7,9 @@ further_reading: - https://jsdoc.app --- - - -This rule was [**deprecated**](https://eslint.org/blog/2018/11/jsdoc-end-of-life) in ESLint v5.10.0. +:::important +This rule was removed in ESLint v9.0.0 and replaced by the [`eslint-plugin-jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc) equivalent. +::: [JSDoc](http://usejsdoc.org) generates application programming interface (API) documentation from specially-formatted comments in JavaScript code. For example, this is a JSDoc comment for a function: diff --git a/docs/src/use/rule-deprecation.md b/docs/src/use/rule-deprecation.md index bd15405396f..a4cbfb5a845 100644 --- a/docs/src/use/rule-deprecation.md +++ b/docs/src/use/rule-deprecation.md @@ -7,10 +7,12 @@ Balancing the trade-offs of improving a tool and the frustration these changes c The ESLint team is committed to making upgrading as easy and painless as possible. To that end, the team has agreed upon the following set of guidelines for deprecating rules in the future. The goal of these guidelines is to allow for improvements and changes to be made without breaking existing configurations. -* Rules will never be removed from ESLint. +* Rules will never be removed from ESLint unless one of the following is true: + * The rule has been replaced by another core rule. + * A plugin exists with a functionally equivalent rule. * Rules will be deprecated as needed, and marked as such in all documentation. * After a rule has been deprecated, the team will no longer do any work on it. This includes bug fixes, enhancements, and updates to the rule's documentation. Issues and pull requests related to deprecated rule will not be accepted and will be closed. -Since deprecated rules will never be removed, you can continue to use them indefinitely if they are working for you. However, keep in mind that deprecated rules will effectively be unmaintained. +You can continue to use deprecated rules indefinitely if they are working for you. However, keep in mind that deprecated rules will effectively be unmaintained and may be removed at some point. We hope that by following these guidelines we will be able to continue improving and working to make ESLint the best tool it can be while causing as little disruption to our users as possible during the process. diff --git a/lib/linter/config-comment-parser.js b/lib/linter/config-comment-parser.js index 9d33c55273c..e20c678d8db 100644 --- a/lib/linter/config-comment-parser.js +++ b/lib/linter/config-comment-parser.js @@ -40,7 +40,7 @@ module.exports = class ConfigCommentParser { /** * Parses a list of "name:string_value" or/and "name" options divided by comma or - * whitespace. Used for "global" and "exported" comments. + * whitespace. Used for "global" comments. * @param {string} string The string to parse. * @param {Comment} comment The comment node which has the string. * @returns {Object} Result map object of names and string values, or null values if no value was provided diff --git a/lib/linter/linter.js b/lib/linter/linter.js index b0b06675a46..fe0311ed46f 100644 --- a/lib/linter/linter.js +++ b/lib/linter/linter.js @@ -392,7 +392,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig) { } case "exported": - Object.assign(exportedVariables, commentParser.parseStringConfig(directiveValue, comment)); + Object.assign(exportedVariables, commentParser.parseListConfig(directiveValue, comment)); break; case "globals": diff --git a/lib/rules/index.js b/lib/rules/index.js index 840abe73b0f..5e449459f2f 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -273,7 +273,6 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ radix: () => require("./radix"), "require-atomic-updates": () => require("./require-atomic-updates"), "require-await": () => require("./require-await"), - "require-jsdoc": () => require("./require-jsdoc"), "require-unicode-regexp": () => require("./require-unicode-regexp"), "require-yield": () => require("./require-yield"), "rest-spread-spacing": () => require("./rest-spread-spacing"), @@ -296,7 +295,6 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "template-tag-spacing": () => require("./template-tag-spacing"), "unicode-bom": () => require("./unicode-bom"), "use-isnan": () => require("./use-isnan"), - "valid-jsdoc": () => require("./valid-jsdoc"), "valid-typeof": () => require("./valid-typeof"), "vars-on-top": () => require("./vars-on-top"), "wrap-iife": () => require("./wrap-iife"), diff --git a/lib/rules/require-jsdoc.js b/lib/rules/require-jsdoc.js deleted file mode 100644 index b6fedf2289f..00000000000 --- a/lib/rules/require-jsdoc.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @fileoverview Rule to check for jsdoc presence. - * @author Gyandeep Singh - * @deprecated in ESLint v5.10.0 - */ -"use strict"; - -/** @type {import('../shared/types').Rule} */ -module.exports = { - meta: { - type: "suggestion", - - docs: { - description: "Require JSDoc comments", - recommended: false, - url: "https://eslint.org/docs/latest/rules/require-jsdoc" - }, - - schema: [ - { - type: "object", - properties: { - require: { - type: "object", - properties: { - ClassDeclaration: { - type: "boolean", - default: false - }, - MethodDefinition: { - type: "boolean", - default: false - }, - FunctionDeclaration: { - type: "boolean", - default: true - }, - ArrowFunctionExpression: { - type: "boolean", - default: false - }, - FunctionExpression: { - type: "boolean", - default: false - } - }, - additionalProperties: false, - default: {} - } - }, - additionalProperties: false - } - ], - - deprecated: true, - replacedBy: [], - - messages: { - missingJSDocComment: "Missing JSDoc comment." - } - }, - - create(context) { - const source = context.sourceCode; - const DEFAULT_OPTIONS = { - FunctionDeclaration: true, - MethodDefinition: false, - ClassDeclaration: false, - ArrowFunctionExpression: false, - FunctionExpression: false - }; - const options = Object.assign(DEFAULT_OPTIONS, context.options[0] && context.options[0].require); - - /** - * Report the error message - * @param {ASTNode} node node to report - * @returns {void} - */ - function report(node) { - context.report({ node, messageId: "missingJSDocComment" }); - } - - /** - * Check if the jsdoc comment is present or not. - * @param {ASTNode} node node to examine - * @returns {void} - */ - function checkJsDoc(node) { - const jsdocComment = source.getJSDocComment(node); - - if (!jsdocComment) { - report(node); - } - } - - return { - FunctionDeclaration(node) { - if (options.FunctionDeclaration) { - checkJsDoc(node); - } - }, - FunctionExpression(node) { - if ( - (options.MethodDefinition && node.parent.type === "MethodDefinition") || - (options.FunctionExpression && (node.parent.type === "VariableDeclarator" || (node.parent.type === "Property" && node === node.parent.value))) - ) { - checkJsDoc(node); - } - }, - ClassDeclaration(node) { - if (options.ClassDeclaration) { - checkJsDoc(node); - } - }, - ArrowFunctionExpression(node) { - if (options.ArrowFunctionExpression && node.parent.type === "VariableDeclarator") { - checkJsDoc(node); - } - } - }; - } -}; diff --git a/lib/rules/valid-jsdoc.js b/lib/rules/valid-jsdoc.js deleted file mode 100644 index 919975fe101..00000000000 --- a/lib/rules/valid-jsdoc.js +++ /dev/null @@ -1,516 +0,0 @@ -/** - * @fileoverview Validates JSDoc comments are syntactically correct - * @author Nicholas C. Zakas - * @deprecated in ESLint v5.10.0 - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const doctrine = require("doctrine"); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -/** @type {import('../shared/types').Rule} */ -module.exports = { - meta: { - type: "suggestion", - - docs: { - description: "Enforce valid JSDoc comments", - recommended: false, - url: "https://eslint.org/docs/latest/rules/valid-jsdoc" - }, - - schema: [ - { - type: "object", - properties: { - prefer: { - type: "object", - additionalProperties: { - type: "string" - } - }, - preferType: { - type: "object", - additionalProperties: { - type: "string" - } - }, - requireReturn: { - type: "boolean", - default: true - }, - requireParamDescription: { - type: "boolean", - default: true - }, - requireReturnDescription: { - type: "boolean", - default: true - }, - matchDescription: { - type: "string" - }, - requireReturnType: { - type: "boolean", - default: true - }, - requireParamType: { - type: "boolean", - default: true - } - }, - additionalProperties: false - } - ], - - fixable: "code", - messages: { - unexpectedTag: "Unexpected @{{title}} tag; function has no return statement.", - expected: "Expected JSDoc for '{{name}}' but found '{{jsdocName}}'.", - use: "Use @{{name}} instead.", - useType: "Use '{{expectedTypeName}}' instead of '{{currentTypeName}}'.", - syntaxError: "JSDoc syntax error.", - missingBrace: "JSDoc type missing brace.", - missingParamDesc: "Missing JSDoc parameter description for '{{name}}'.", - missingParamType: "Missing JSDoc parameter type for '{{name}}'.", - missingReturnType: "Missing JSDoc return type.", - missingReturnDesc: "Missing JSDoc return description.", - missingReturn: "Missing JSDoc @{{returns}} for function.", - missingParam: "Missing JSDoc for parameter '{{name}}'.", - duplicateParam: "Duplicate JSDoc parameter '{{name}}'.", - unsatisfiedDesc: "JSDoc description does not satisfy the regex pattern." - }, - - deprecated: true, - replacedBy: [] - }, - - create(context) { - - const options = context.options[0] || {}, - prefer = options.prefer || {}, - sourceCode = context.sourceCode, - - // these both default to true, so you have to explicitly make them false - requireReturn = options.requireReturn !== false, - requireParamDescription = options.requireParamDescription !== false, - requireReturnDescription = options.requireReturnDescription !== false, - requireReturnType = options.requireReturnType !== false, - requireParamType = options.requireParamType !== false, - preferType = options.preferType || {}, - checkPreferType = Object.keys(preferType).length !== 0; - - //-------------------------------------------------------------------------- - // Helpers - //-------------------------------------------------------------------------- - - // Using a stack to store if a function returns or not (handling nested functions) - const fns = []; - - /** - * Check if node type is a Class - * @param {ASTNode} node node to check. - * @returns {boolean} True is its a class - * @private - */ - function isTypeClass(node) { - return node.type === "ClassExpression" || node.type === "ClassDeclaration"; - } - - /** - * When parsing a new function, store it in our function stack. - * @param {ASTNode} node A function node to check. - * @returns {void} - * @private - */ - function startFunction(node) { - fns.push({ - returnPresent: (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") || - isTypeClass(node) || node.async - }); - } - - /** - * Indicate that return has been found in the current function. - * @param {ASTNode} node The return node. - * @returns {void} - * @private - */ - function addReturn(node) { - const functionState = fns[fns.length - 1]; - - if (functionState && node.argument !== null) { - functionState.returnPresent = true; - } - } - - /** - * Check if return tag type is void or undefined - * @param {Object} tag JSDoc tag - * @returns {boolean} True if its of type void or undefined - * @private - */ - function isValidReturnType(tag) { - return tag.type === null || tag.type.name === "void" || tag.type.type === "UndefinedLiteral"; - } - - /** - * Check if type should be validated based on some exceptions - * @param {Object} type JSDoc tag - * @returns {boolean} True if it can be validated - * @private - */ - function canTypeBeValidated(type) { - return type !== "UndefinedLiteral" && // {undefined} as there is no name property available. - type !== "NullLiteral" && // {null} - type !== "NullableLiteral" && // {?} - type !== "FunctionType" && // {function(a)} - type !== "AllLiteral"; // {*} - } - - /** - * Extract the current and expected type based on the input type object - * @param {Object} type JSDoc tag - * @returns {{currentType: Doctrine.Type, expectedTypeName: string}} The current type annotation and - * the expected name of the annotation - * @private - */ - function getCurrentExpectedTypes(type) { - let currentType; - - if (type.name) { - currentType = type; - } else if (type.expression) { - currentType = type.expression; - } - - return { - currentType, - expectedTypeName: currentType && preferType[currentType.name] - }; - } - - /** - * Gets the location of a JSDoc node in a file - * @param {Token} jsdocComment The comment that this node is parsed from - * @param {{range: number[]}} parsedJsdocNode A tag or other node which was parsed from this comment - * @returns {{start: SourceLocation, end: SourceLocation}} The 0-based source location for the tag - */ - function getAbsoluteRange(jsdocComment, parsedJsdocNode) { - return { - start: sourceCode.getLocFromIndex(jsdocComment.range[0] + 2 + parsedJsdocNode.range[0]), - end: sourceCode.getLocFromIndex(jsdocComment.range[0] + 2 + parsedJsdocNode.range[1]) - }; - } - - /** - * Validate type for a given JSDoc node - * @param {Object} jsdocNode JSDoc node - * @param {Object} type JSDoc tag - * @returns {void} - * @private - */ - function validateType(jsdocNode, type) { - if (!type || !canTypeBeValidated(type.type)) { - return; - } - - const typesToCheck = []; - let elements = []; - - switch (type.type) { - case "TypeApplication": // {Array.} - elements = type.applications[0].type === "UnionType" ? type.applications[0].elements : type.applications; - typesToCheck.push(getCurrentExpectedTypes(type)); - break; - case "RecordType": // {{20:String}} - elements = type.fields; - break; - case "UnionType": // {String|number|Test} - case "ArrayType": // {[String, number, Test]} - elements = type.elements; - break; - case "FieldType": // Array.<{count: number, votes: number}> - if (type.value) { - typesToCheck.push(getCurrentExpectedTypes(type.value)); - } - break; - default: - typesToCheck.push(getCurrentExpectedTypes(type)); - } - - elements.forEach(validateType.bind(null, jsdocNode)); - - typesToCheck.forEach(typeToCheck => { - if (typeToCheck.expectedTypeName && - typeToCheck.expectedTypeName !== typeToCheck.currentType.name) { - context.report({ - node: jsdocNode, - messageId: "useType", - loc: getAbsoluteRange(jsdocNode, typeToCheck.currentType), - data: { - currentTypeName: typeToCheck.currentType.name, - expectedTypeName: typeToCheck.expectedTypeName - }, - fix(fixer) { - return fixer.replaceTextRange( - typeToCheck.currentType.range.map(indexInComment => jsdocNode.range[0] + 2 + indexInComment), - typeToCheck.expectedTypeName - ); - } - }); - } - }); - } - - /** - * Validate the JSDoc node and output warnings if anything is wrong. - * @param {ASTNode} node The AST node to check. - * @returns {void} - * @private - */ - function checkJSDoc(node) { - const jsdocNode = sourceCode.getJSDocComment(node), - functionData = fns.pop(), - paramTagsByName = Object.create(null), - paramTags = []; - let hasReturns = false, - returnsTag, - hasConstructor = false, - isInterface = false, - isOverride = false, - isAbstract = false; - - // make sure only to validate JSDoc comments - if (jsdocNode) { - let jsdoc; - - try { - jsdoc = doctrine.parse(jsdocNode.value, { - strict: true, - unwrap: true, - sloppy: true, - range: true - }); - } catch (ex) { - - if (/braces/iu.test(ex.message)) { - context.report({ node: jsdocNode, messageId: "missingBrace" }); - } else { - context.report({ node: jsdocNode, messageId: "syntaxError" }); - } - - return; - } - - jsdoc.tags.forEach(tag => { - - switch (tag.title.toLowerCase()) { - - case "param": - case "arg": - case "argument": - paramTags.push(tag); - break; - - case "return": - case "returns": - hasReturns = true; - returnsTag = tag; - break; - - case "constructor": - case "class": - hasConstructor = true; - break; - - case "override": - case "inheritdoc": - isOverride = true; - break; - - case "abstract": - case "virtual": - isAbstract = true; - break; - - case "interface": - isInterface = true; - break; - - // no default - } - - // check tag preferences - if (Object.prototype.hasOwnProperty.call(prefer, tag.title) && tag.title !== prefer[tag.title]) { - const entireTagRange = getAbsoluteRange(jsdocNode, tag); - - context.report({ - node: jsdocNode, - messageId: "use", - loc: { - start: entireTagRange.start, - end: { - line: entireTagRange.start.line, - column: entireTagRange.start.column + `@${tag.title}`.length - } - }, - data: { name: prefer[tag.title] }, - fix(fixer) { - return fixer.replaceTextRange( - [ - jsdocNode.range[0] + tag.range[0] + 3, - jsdocNode.range[0] + tag.range[0] + tag.title.length + 3 - ], - prefer[tag.title] - ); - } - }); - } - - // validate the types - if (checkPreferType && tag.type) { - validateType(jsdocNode, tag.type); - } - }); - - paramTags.forEach(param => { - if (requireParamType && !param.type) { - context.report({ - node: jsdocNode, - messageId: "missingParamType", - loc: getAbsoluteRange(jsdocNode, param), - data: { name: param.name } - }); - } - if (!param.description && requireParamDescription) { - context.report({ - node: jsdocNode, - messageId: "missingParamDesc", - loc: getAbsoluteRange(jsdocNode, param), - data: { name: param.name } - }); - } - if (paramTagsByName[param.name]) { - context.report({ - node: jsdocNode, - messageId: "duplicateParam", - loc: getAbsoluteRange(jsdocNode, param), - data: { name: param.name } - }); - } else if (!param.name.includes(".")) { - paramTagsByName[param.name] = param; - } - }); - - if (hasReturns) { - if (!requireReturn && !functionData.returnPresent && (returnsTag.type === null || !isValidReturnType(returnsTag)) && !isAbstract) { - context.report({ - node: jsdocNode, - messageId: "unexpectedTag", - loc: getAbsoluteRange(jsdocNode, returnsTag), - data: { - title: returnsTag.title - } - }); - } else { - if (requireReturnType && !returnsTag.type) { - context.report({ node: jsdocNode, messageId: "missingReturnType" }); - } - - if (!isValidReturnType(returnsTag) && !returnsTag.description && requireReturnDescription) { - context.report({ node: jsdocNode, messageId: "missingReturnDesc" }); - } - } - } - - // check for functions missing @returns - if (!isOverride && !hasReturns && !hasConstructor && !isInterface && - node.parent.kind !== "get" && node.parent.kind !== "constructor" && - node.parent.kind !== "set" && !isTypeClass(node)) { - if (requireReturn || (functionData.returnPresent && !node.async)) { - context.report({ - node: jsdocNode, - messageId: "missingReturn", - data: { - returns: prefer.returns || "returns" - } - }); - } - } - - // check the parameters - const jsdocParamNames = Object.keys(paramTagsByName); - - if (node.params) { - node.params.forEach((param, paramsIndex) => { - const bindingParam = param.type === "AssignmentPattern" - ? param.left - : param; - - // TODO(nzakas): Figure out logical things to do with destructured, default, rest params - if (bindingParam.type === "Identifier") { - const name = bindingParam.name; - - if (jsdocParamNames[paramsIndex] && (name !== jsdocParamNames[paramsIndex])) { - context.report({ - node: jsdocNode, - messageId: "expected", - loc: getAbsoluteRange(jsdocNode, paramTagsByName[jsdocParamNames[paramsIndex]]), - data: { - name, - jsdocName: jsdocParamNames[paramsIndex] - } - }); - } else if (!paramTagsByName[name] && !isOverride) { - context.report({ - node: jsdocNode, - messageId: "missingParam", - data: { - name - } - }); - } - } - }); - } - - if (options.matchDescription) { - const regex = new RegExp(options.matchDescription, "u"); - - if (!regex.test(jsdoc.description)) { - context.report({ node: jsdocNode, messageId: "unsatisfiedDesc" }); - } - } - - } - - } - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - return { - ArrowFunctionExpression: startFunction, - FunctionExpression: startFunction, - FunctionDeclaration: startFunction, - ClassExpression: startFunction, - ClassDeclaration: startFunction, - "ArrowFunctionExpression:exit": checkJSDoc, - "FunctionExpression:exit": checkJSDoc, - "FunctionDeclaration:exit": checkJSDoc, - "ClassExpression:exit": checkJSDoc, - "ClassDeclaration:exit": checkJSDoc, - ReturnStatement: addReturn - }; - - } -}; diff --git a/lib/source-code/source-code.js b/lib/source-code/source-code.js index ce8d89e7843..c4f51bc3139 100644 --- a/lib/source-code/source-code.js +++ b/lib/source-code/source-code.js @@ -885,7 +885,7 @@ class SourceCode extends TokenStore { switch (directiveText) { case "exported": - Object.assign(exportedVariables, commentParser.parseStringConfig(directiveValue, comment)); + Object.assign(exportedVariables, commentParser.parseListConfig(directiveValue, comment)); break; case "globals": diff --git a/package.json b/package.json index 00a045572b9..788d2bb7493 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "build:site": "node Makefile.js gensite", "build:webpack": "node Makefile.js webpack", "build:readme": "node tools/update-readme.js", + "build:rules-index": "node Makefile.js generateRuleIndexPage", "lint": "node Makefile.js lint", "lint:docs:js": "node Makefile.js lintDocsJS", "lint:docs:rule-examples": "node Makefile.js checkRuleExamples", @@ -74,7 +75,6 @@ "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", diff --git a/packages/eslint-config-eslint/base.js b/packages/eslint-config-eslint/base.js index 2e2ea40657a..97aa590ed3d 100644 --- a/packages/eslint-config-eslint/base.js +++ b/packages/eslint-config-eslint/base.js @@ -334,7 +334,7 @@ const jsdocConfigs = [jsdoc.configs["flat/recommended"], { "jsdoc/empty-tags": "error", "jsdoc/implements-on-classes": "error", "jsdoc/multiline-blocks": "error", - "jsdoc/no-multi-asterisks": "error", + "jsdoc/no-multi-asterisks": ["error", { allowWhitespace: true }], "jsdoc/require-jsdoc": ["error", { require: { ClassDeclaration: true } }], "jsdoc/require-param": "error", "jsdoc/require-param-description": "error", diff --git a/packages/eslint-config-eslint/eslintrc.js b/packages/eslint-config-eslint/eslintrc.js index 81840082371..7e786926562 100644 --- a/packages/eslint-config-eslint/eslintrc.js +++ b/packages/eslint-config-eslint/eslintrc.js @@ -154,7 +154,7 @@ module.exports = { "jsdoc/empty-tags": "error", "jsdoc/implements-on-classes": "error", "jsdoc/multiline-blocks": "error", - "jsdoc/no-multi-asterisks": "error", + "jsdoc/no-multi-asterisks": ["error", { allowWhitespace: true }], "jsdoc/require-jsdoc": [ "error", { diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index 3546d611948..ad737f3fc57 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -1757,8 +1757,7 @@ describe("CLIEngine", () => { useEslintrc: false, rules: { "indent-legacy": 1, - "require-jsdoc": 1, - "valid-jsdoc": 1 + "callback-return": 1 } }); @@ -1768,8 +1767,7 @@ describe("CLIEngine", () => { report.usedDeprecatedRules, [ { ruleId: "indent-legacy", replacedBy: ["indent"] }, - { ruleId: "require-jsdoc", replacedBy: [] }, - { ruleId: "valid-jsdoc", replacedBy: [] } + { ruleId: "callback-return", replacedBy: [] } ] ); assert.strictEqual(report.results[0].suppressedMessages.length, 0); @@ -1779,7 +1777,7 @@ describe("CLIEngine", () => { engine = new CLIEngine({ cwd: originalDir, useEslintrc: false, - rules: { eqeqeq: 1, "valid-jsdoc": 0, "require-jsdoc": 0 } + rules: { eqeqeq: 1, "callback-return": 0 } }); const report = engine.executeOnFiles(["lib/cli*.js"]); diff --git a/tests/lib/eslint/eslint.js b/tests/lib/eslint/eslint.js index 42e64c13335..e7ff4208d05 100644 --- a/tests/lib/eslint/eslint.js +++ b/tests/lib/eslint/eslint.js @@ -1798,8 +1798,7 @@ describe("ESLint", () => { overrideConfig: { rules: { "indent-legacy": 1, - "require-jsdoc": 1, - "valid-jsdoc": 1 + "callback-return": 1 } } }); @@ -1809,8 +1808,7 @@ describe("ESLint", () => { results[0].usedDeprecatedRules, [ { ruleId: "indent-legacy", replacedBy: ["indent"] }, - { ruleId: "require-jsdoc", replacedBy: [] }, - { ruleId: "valid-jsdoc", replacedBy: [] } + { ruleId: "callback-return", replacedBy: [] } ] ); }); @@ -1820,7 +1818,7 @@ describe("ESLint", () => { cwd: originalDir, useEslintrc: false, overrideConfig: { - rules: { eqeqeq: 1, "valid-jsdoc": 0, "require-jsdoc": 0 } + rules: { eqeqeq: 1, "callback-return": 0 } } }); const results = await eslint.lintFiles(["lib/cli*.js"]); diff --git a/tests/lib/eslint/flat-eslint.js b/tests/lib/eslint/flat-eslint.js index c29f0574f1d..adf48119073 100644 --- a/tests/lib/eslint/flat-eslint.js +++ b/tests/lib/eslint/flat-eslint.js @@ -2016,8 +2016,7 @@ describe("FlatESLint", () => { overrideConfig: { rules: { "indent-legacy": 1, - "require-jsdoc": 1, - "valid-jsdoc": 1 + "callback-return": 1 } } }); @@ -2027,8 +2026,7 @@ describe("FlatESLint", () => { results[0].usedDeprecatedRules, [ { ruleId: "indent-legacy", replacedBy: ["indent"] }, - { ruleId: "require-jsdoc", replacedBy: [] }, - { ruleId: "valid-jsdoc", replacedBy: [] } + { ruleId: "callback-return", replacedBy: [] } ] ); }); @@ -2038,7 +2036,7 @@ describe("FlatESLint", () => { cwd: originalDir, overrideConfigFile: true, overrideConfig: { - rules: { eqeqeq: 1, "valid-jsdoc": 0, "require-jsdoc": 0 } + rules: { eqeqeq: 1, "callback-return": 0 } } }); const results = await eslint.lintFiles(["lib/cli*.js"]); diff --git a/tests/lib/linter/linter.js b/tests/lib/linter/linter.js index e78d13ca0ef..d5576864208 100644 --- a/tests/lib/linter/linter.js +++ b/tests/lib/linter/linter.js @@ -817,6 +817,96 @@ describe("Linter", () => { linter.verify(code, config, filename, true); }); + it("variable should be exported ", () => { + const code = "/* exported horse */\n\nvar horse;"; + const config = { rules: { checker: "error" } }; + let spy; + + linter.defineRule("checker", { + create(context) { + spy = sinon.spy(node => { + const scope = context.sourceCode.getScope(node), + horse = getVariable(scope, "horse"); + + assert.isTrue(horse.eslintUsed); + }); + + return { Program: spy }; + } + }); + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("`key: value` pair variable should not be exported", () => { + const code = "/* exported horse: true */\n\nvar horse;"; + const config = { rules: { checker: "error" } }; + let spy; + + linter.defineRule("checker", { + create(context) { + spy = sinon.spy(node => { + const scope = context.sourceCode.getScope(node), + horse = getVariable(scope, "horse"); + + assert.notOk(horse.eslintUsed); + }); + + return { Program: spy }; + } + }); + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("variables with comma should be exported", () => { + const code = "/* exported horse, dog */\n\nvar horse, dog;"; + const config = { rules: { checker: "error" } }; + let spy; + + linter.defineRule("checker", { + create(context) { + spy = sinon.spy(node => { + const scope = context.sourceCode.getScope(node); + + ["horse", "dog"].forEach(name => { + assert.isTrue(getVariable(scope, name).eslintUsed); + }); + }); + + return { Program: spy }; + } + }); + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("variables without comma should not be exported", () => { + const code = "/* exported horse dog */\n\nvar horse, dog;"; + const config = { rules: { checker: "error" } }; + let spy; + + linter.defineRule("checker", { + create(context) { + spy = sinon.spy(node => { + const scope = context.sourceCode.getScope(node); + + ["horse", "dog"].forEach(name => { + assert.notOk(getVariable(scope, name).eslintUsed); + }); + }); + + return { Program: spy }; + } + }); + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + it("variables should be exported", () => { const code = "/* exported horse */\n\nvar horse = 'circus'"; const config = { rules: { checker: "error" } }; @@ -9833,6 +9923,136 @@ describe("Linter with FlatConfigArray", () => { linter.verify(code, config, filename, true); }); + it("variable should be exported", () => { + const code = "/* exported horse */\n\nvar horse;"; + let spy; + const config = { + plugins: { + test: { + rules: { + checker: { + create(context) { + spy = sinon.spy(node => { + const scope = context.sourceCode.getScope(node), + horse = getVariable(scope, "horse"); + + assert.isTrue(horse.eslintUsed); + }); + + return { Program: spy }; + } + } + } + } + }, + languageOptions: { + sourceType: "script" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("`key: value` pair variable should not be exported", () => { + const code = "/* exported horse: true */\n\nvar horse;"; + let spy; + const config = { + plugins: { + test: { + rules: { + checker: { + create(context) { + spy = sinon.spy(node => { + const scope = context.sourceCode.getScope(node), + horse = getVariable(scope, "horse"); + + assert.notOk(horse.eslintUsed); + }); + + return { Program: spy }; + } + } + } + } + }, + languageOptions: { + sourceType: "script" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("variables with comma should be exported", () => { + const code = "/* exported horse, dog */\n\nvar horse, dog;"; + let spy; + const config = { + plugins: { + test: { + rules: { + checker: { + create(context) { + spy = sinon.spy(node => { + const scope = context.sourceCode.getScope(node); + + ["horse", "dog"].forEach(name => { + assert.isTrue(getVariable(scope, name).eslintUsed); + }); + }); + + return { Program: spy }; + } + } + } + } + }, + languageOptions: { + sourceType: "script" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("variables without comma should not be exported", () => { + const code = "/* exported horse dog */\n\nvar horse, dog;"; + let spy; + const config = { + plugins: { + test: { + rules: { + checker: { + create(context) { + spy = sinon.spy(node => { + const scope = context.sourceCode.getScope(node); + + ["horse", "dog"].forEach(name => { + assert.notOk(getVariable(scope, name).eslintUsed); + }); + }); + + return { Program: spy }; + } + } + } + } + }, + languageOptions: { + sourceType: "script" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + it("variables should be exported", () => { const code = "/* exported horse */\n\nvar horse = 'circus'"; let spy; diff --git a/tests/lib/rules/require-jsdoc.js b/tests/lib/rules/require-jsdoc.js deleted file mode 100644 index 0ad2fb15f41..00000000000 --- a/tests/lib/rules/require-jsdoc.js +++ /dev/null @@ -1,421 +0,0 @@ -/** - * @fileoverview Test file for require-jsdoc rule - * @author Gyandeep Singh - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/require-jsdoc"), - RuleTester = require("../../../lib/rule-tester/flat-rule-tester"); - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -ruleTester.run("require-jsdoc", rule, { - valid: [ - "var array = [1,2,3];\narray.forEach(function() {});", - "/**\n @class MyClass \n*/\nfunction MyClass() {}", - "/**\n Function doing something\n*/\nfunction myFunction() {}", - "/**\n Function doing something\n*/\nvar myFunction = function() {};", - "/**\n Function doing something\n*/\nObject.myFunction = function () {};", - "var obj = { \n /**\n Function doing something\n*/\n myFunction: function () {} };", - - "/**\n @func myFunction \n*/\nfunction myFunction() {}", - "/**\n @method myFunction\n*/\nfunction myFunction() {}", - "/**\n @function myFunction\n*/\nfunction myFunction() {}", - - "/**\n @func myFunction \n*/\nvar myFunction = function () {}", - "/**\n @method myFunction\n*/\nvar myFunction = function () {}", - "/**\n @function myFunction\n*/\nvar myFunction = function () {}", - - "/**\n @func myFunction \n*/\nObject.myFunction = function() {}", - "/**\n @method myFunction\n*/\nObject.myFunction = function() {}", - "/**\n @function myFunction\n*/\nObject.myFunction = function() {}", - "(function(){})();", - - "var object = {\n/**\n @func myFunction - Some function \n*/\nmyFunction: function() {} }", - "var object = {\n/**\n @method myFunction - Some function \n*/\nmyFunction: function() {} }", - "var object = {\n/**\n @function myFunction - Some function \n*/\nmyFunction: function() {} }", - - "var array = [1,2,3];\narray.filter(function() {});", - "Object.keys(this.options.rules || {}).forEach(function(name) {}.bind(this));", - "var object = { name: 'key'};\nObject.keys(object).forEach(function() {})", - { - code: "function myFunction() {}", - options: [{ - require: { - FunctionDeclaration: false, - MethodDefinition: true, - ClassDeclaration: true - } - }] - }, - { - code: "var myFunction = function() {}", - options: [{ - require: { - FunctionDeclaration: false, - MethodDefinition: true, - ClassDeclaration: true - } - }] - }, - { - code: - "/**\n" + - " * Description for A.\n" + - " */\n" + - "class A {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ - require: { - MethodDefinition: true, - ClassDeclaration: true - } - }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: - "/**\n" + - " * Description for A.\n" + - " */\n" + - "class App extends Component {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ - require: { - MethodDefinition: true, - ClassDeclaration: true - } - }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: - "/**\n" + - " * Description for A.\n" + - " */\n" + - "export default class App extends Component {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ - require: { - MethodDefinition: true, - ClassDeclaration: true - } - }], - languageOptions: { ecmaVersion: 6, sourceType: "module" } - }, - { - code: - "/**\n" + - " * Description for A.\n" + - " */\n" + - "export class App extends Component {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ - require: { - MethodDefinition: true, - ClassDeclaration: true - } - }], - languageOptions: { ecmaVersion: 6, sourceType: "module" } - }, - { - code: - "class A {\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ - require: { - MethodDefinition: false, - ClassDeclaration: false - } - }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: "/**\n Function doing something\n*/\nvar myFunction = () => {}", - options: [{ - require: { - ArrowFunctionExpression: true - } - }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: "/**\n Function doing something\n*/\nvar myFunction = () => () => {}", - options: [{ - require: { - ArrowFunctionExpression: true - } - }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: "setTimeout(() => {}, 10);", - options: [{ - require: { - ArrowFunctionExpression: true - } - }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: "/**\nJSDoc Block\n*/\nvar foo = function() {}", - options: [{ - require: { - FunctionExpression: true - } - }] - }, - { - code: "const foo = {/**\nJSDoc Block\n*/\nbar() {}}", - options: [{ - require: { - FunctionExpression: true - } - }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: "var foo = {/**\nJSDoc Block\n*/\nbar: function() {}}", - options: [{ - require: { - FunctionExpression: true - } - }] - }, - { - code: " var foo = { [function() {}]: 1 };", - options: [{ - require: { - FunctionExpression: true - } - }], - languageOptions: { ecmaVersion: 6 } - } - ], - - invalid: [ - { - code: "function myFunction() {}", - errors: [{ - messageId: "missingJSDocComment", - type: "FunctionDeclaration" - }] - }, - { - code: - "/**\n" + - " * Description for A.\n" + - " */\n" + - "class A {\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ - require: { - MethodDefinition: true, - ClassDeclaration: true - } - }], - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "missingJSDocComment", - type: "FunctionExpression" - }] - }, - { - code: - "class A {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ - require: { - MethodDefinition: true, - ClassDeclaration: true - } - }], - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "missingJSDocComment", - type: "ClassDeclaration" - }] - }, - { - code: - "class A extends B {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ - require: { - MethodDefinition: true, - ClassDeclaration: true - } - }], - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "missingJSDocComment", - type: "ClassDeclaration" - }] - }, - { - code: - "export class A extends B {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ - require: { - MethodDefinition: true, - ClassDeclaration: true - } - }], - languageOptions: { ecmaVersion: 6, sourceType: "module" }, - errors: [{ - messageId: "missingJSDocComment", - type: "ClassDeclaration" - }] - }, - { - code: - "export default class A extends B {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ - require: { - MethodDefinition: true, - ClassDeclaration: true - } - }], - languageOptions: { ecmaVersion: 6, sourceType: "module" }, - errors: [{ - messageId: "missingJSDocComment", - type: "ClassDeclaration" - }] - }, - { - code: "var myFunction = () => {}", - options: [{ - require: { - ArrowFunctionExpression: true - } - }], - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "missingJSDocComment", - type: "ArrowFunctionExpression" - }] - }, - { - code: "var myFunction = () => () => {}", - options: [{ - require: { - ArrowFunctionExpression: true - } - }], - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "missingJSDocComment", - type: "ArrowFunctionExpression" - }] - }, - { - code: "var foo = function() {}", - options: [{ - require: { - FunctionExpression: true - } - }], - errors: [{ - messageId: "missingJSDocComment", - type: "FunctionExpression" - }] - }, - { - code: "const foo = {bar() {}}", - options: [{ - require: { - FunctionExpression: true - } - }], - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "missingJSDocComment", - type: "FunctionExpression" - }] - }, - { - code: "var foo = {bar: function() {}}", - options: [{ - require: { - FunctionExpression: true - } - }], - errors: [{ - messageId: "missingJSDocComment", - type: "FunctionExpression" - }] - } - ] -}); diff --git a/tests/lib/rules/valid-jsdoc.js b/tests/lib/rules/valid-jsdoc.js deleted file mode 100644 index 1f2cd367336..00000000000 --- a/tests/lib/rules/valid-jsdoc.js +++ /dev/null @@ -1,2116 +0,0 @@ -/** - * @fileoverview Validates JSDoc comments are syntactically correct - * @author Nicholas C. Zakas - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/valid-jsdoc"), - RuleTester = require("../../../lib/rule-tester/flat-rule-tester"); - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -ruleTester.run("valid-jsdoc", rule, { - - valid: [ - "/**\n* Description\n * @param {Object[]} screenings Array of screenings.\n * @param {Number} screenings[].timestamp its a time stamp \n @return {void} */\nfunction foo(){}", - "/**\n* Description\n */\nvar x = new Foo(function foo(){})", - "/**\n* Description\n* @returns {void} */\nfunction foo(){}", - "/**\n* Description\n* @returns {undefined} */\nfunction foo(){}", - "/**\n* Description\n* @alias Test#test\n* @returns {void} */\nfunction foo(){}", - "/**\n* Description\n*@extends MyClass\n* @returns {void} */\nfunction foo(){}", - "/**\n* Description\n* @constructor */\nfunction Foo(){}", - "/**\n* Description\n* @class */\nfunction Foo(){}", - "/**\n* Description\n* @param {string} p bar\n* @returns {string} desc */\nfunction foo(p){}", - "/**\n* Description\n* @arg {string} p bar\n* @returns {string} desc */\nfunction foo(p){}", - "/**\n* Description\n* @argument {string} p bar\n* @returns {string} desc */\nfunction foo(p){}", - "/**\n* Description\n* @param {string} [p] bar\n* @returns {string} desc */\nfunction foo(p){}", - "/**\n* Description\n* @param {Object} p bar\n* @param {string} p.name bar\n* @returns {string} desc */\nFoo.bar = function(p){};", - "(function(){\n/**\n* Description\n* @param {string} p bar\n* @returns {string} desc */\nfunction foo(p){}\n}())", - "var o = {\n/**\n* Description\n* @param {string} p bar\n* @returns {string} desc */\nfoo: function(p){}\n};", - "/**\n* Description\n* @param {Object} p bar\n* @param {string[]} p.files qux\n* @param {Function} cb baz\n* @returns {void} */\nfunction foo(p, cb){}", - "/**\n* Description\n* @override */\nfunction foo(arg1, arg2){ return ''; }", - "/**\n* Description\n* @inheritdoc */\nfunction foo(arg1, arg2){ return ''; }", - "/**\n* Description\n* @inheritDoc */\nfunction foo(arg1, arg2){ return ''; }", - "/**\n* Description\n* @Returns {void} */\nfunction foo(){}", - { - code: - "call(\n" + - " /**\n" + - " * Doc for a function expression in a call expression.\n" + - " * @param {string} argName This is the param description.\n" + - " * @return {string} This is the return description.\n" + - " */\n" + - " function(argName) {\n" + - " return 'the return';\n" + - " }\n" + - ");\n", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Create a new thing.\n" + - "*/\n" + - "var thing = new Thing({\n" + - " foo: function() {\n" + - " return 'bar';\n" + - " }\n" + - "});\n", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Create a new thing.\n" + - "*/\n" + - "var thing = new Thing({\n" + - " /**\n" + - " * @return {string} A string.\n" + - " */\n" + - " foo: function() {\n" + - " return 'bar';\n" + - " }\n" + - "});\n", - options: [{ requireReturn: false }] - }, - { - code: "/**\n* Description\n* @return {void} */\nfunction foo(){}", - options: [{}] - }, - { - code: "/**\n* Description\n* @param {string} p bar\n*/\nFoo.bar = (p) => {};", - options: [{ requireReturn: false }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: "/**\n* Description\n* @param {string} p bar\n*/\nFoo.bar = function({p}){};", - options: [{ requireReturn: false }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: "/**\n* Description\n* @param {string} p bar\n*/\nFoo.bar = function(p){};", - options: [{ requireReturn: false }] - }, - { - code: "/**\n* Description\n* @param {string} p mytest\n*/\nFoo.bar = function(p){var t = function(){return p;}};", - options: [{ requireReturn: false }] - }, - { - code: "/**\n* Description\n* @param {string} p mytest\n*/\nFoo.bar = function(p){function func(){return p;}};", - options: [{ requireReturn: false }] - }, - { - code: "/**\n* Description\n* @param {string} p mytest\n*/\nFoo.bar = function(p){var t = false; if(t){ return; }};", - options: [{ requireReturn: false }] - }, - { - code: "/**\n* Description\n* @param {string} p mytest\n* @returns {void} */\nFoo.bar = function(p){var t = false; if(t){ return; }};", - options: [{ requireReturn: false }] - }, - { - code: "/**\n* Description\n* @param {string} p mytest\n*/\nFoo.bar = function(p){var t = function(){function name(){return p;}}};", - options: [{ requireReturn: false }] - }, - { - code: "/**\n* Description\n* @param {string} p mytest\n*/\nFoo.bar = function(p){var t = function(){function name(){}; return name;}};", - options: [{ requireReturn: false }] - }, - { - code: "/**\n* Description\n* @param {string} p\n* @returns {void}*/\nFoo.bar = function(p){var t = function(){function name(){}; return name;}};", - options: [{ requireParamDescription: false }] - }, - { - code: "/**\n* Description\n* @param {string} p mytest\n* @returns {Object}*/\nFoo.bar = function(p){return name;};", - options: [{ requireReturnDescription: false }] - }, - "var obj = {\n /**\n * Getter\n * @type {string}\n */\n get location() {\n return this._location;\n }\n }", - "var obj = {\n /**\n * Setter\n * @param {string} value The location\n */\n set location(value) {\n this._location = value;\n }\n }", - { - code: "/**\n * Description for A.\n */\n class A {\n /**\n * Description for constructor.\n * @param {object[]} xs - xs\n */\n constructor(xs) {\n /**\n * Description for this.xs;\n * @type {object[]}\n */\n this.xs = xs.filter(x => x != null);\n }\n}", - options: [{ requireReturn: false }], - languageOptions: { - ecmaVersion: 6 - } - }, - { - code: "/** @returns {object} foo */ var foo = () => bar();", - options: [{ requireReturn: false }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: "/** @returns {object} foo */ var foo = () => { return bar(); };", - options: [{ requireReturn: false }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: "/** foo */ var foo = () => { bar(); };", - options: [{ requireReturn: false }], - languageOptions: { ecmaVersion: 6 } - }, - { - code: "/**\n* Start with caps and end with period.\n* @return {void} */\nfunction foo(){}", - options: [{ - matchDescription: "^[A-Z][A-Za-z0-9\\s]*[.]$" - }] - }, - { - code: "/** Foo \n@return {void} Foo\n */\nfunction foo(){}", - options: [{ prefer: { return: "return" } }] - }, - { - code: "/** Foo \n@return Foo\n */\nfunction foo(){}", - options: [{ requireReturnType: false }] - }, - { - code: "/**\n* Description\n* @param p bar\n* @returns {void}*/\nFoo.bar = function(p){var t = function(){function name(){}; return name;}};", - options: [{ requireParamType: false }] - }, - { - code: - "/**\n" + - " * A thing interface. \n" + - " * @interface\n" + - " */\n" + - "function Thing() {}", - options: [{ requireReturn: true }] - }, - - // classes - { - code: - "/**\n" + - " * Description for A.\n" + - " */\n" + - "class A {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ requireReturn: true }], - languageOptions: { - ecmaVersion: 6 - } - }, - { - code: - "/**\n" + - " * Description for A.\n" + - " */\n" + - "class A {\n" + - " /**\n" + - " * Description for method.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " print(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [{ requireReturn: false }], - languageOptions: { - ecmaVersion: 6 - } - }, - { - code: - "/**\n" + - " * Description for A.\n" + - " */\n" + - "class A {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " * @returns {void}\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - " /**\n" + - " * Description for method.\n" + - " * @param {object[]} xs - xs\n" + - " * @returns {void}\n" + - " */\n" + - " print(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - options: [], - languageOptions: { - ecmaVersion: 6 - } - }, - - - { - code: - "/**\n" + - " * Use of this with a 'namepath'.\n" + - " * @this some.name\n" + - " */\n" + - "function foo() {}", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - " * Use of this with a type expression.\n" + - " * @this {some.name}\n" + - " */\n" + - "function foo() {}", - options: [{ requireReturn: false }] - }, - - // async function - { - code: - "/**\n" + - " * An async function. Options requires return.\n" + - " * @returns {Promise} that is empty\n" + - " */\n" + - "async function a() {}", - options: [{ requireReturn: true }], - languageOptions: { - ecmaVersion: 2017 - } - }, - { - code: - "/**\n" + - " * An async function. Options do not require return.\n" + - " * @returns {Promise} that is empty\n" + - " */\n" + - "async function a() {}", - options: [{ requireReturn: false }], - languageOptions: { - ecmaVersion: 2017 - } - }, - { - code: - "/**\n" + - " * An async function. Options do not require return.\n" + - " */\n" + - "async function a() {}", - options: [{ requireReturn: false }], - languageOptions: { - ecmaVersion: 2017 - } - }, - - // type validations - { - code: - "/**\n" + - "* Foo\n" + - "* @param {Array.<*>} hi - desc\n" + - "* @returns {*} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - Astnode: "ASTNode" - } - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {string} hi - desc\n" + - "* @returns {ASTNode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - Astnode: "ASTNode" - } - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {{20:string}} hi - desc\n" + - "* @returns {Astnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - astnode: "ASTNode" - } - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {{String:foo}} hi - desc\n" + - "* @returns {ASTNode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - astnode: "ASTNode" - } - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {String|number|Test} hi - desc\n" + - "* @returns {Astnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - test: "Test" - } - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {Array.} hi - desc\n" + - "* @returns {Astnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - astnode: "ASTNode" - } - }] - }, - { - code: - "/**\n" + - " * Test dash and slash.\n" + - " * @extends module:stb/emitter~Emitter\n" + - " */\n" + - "function foo() {}", - options: [{ - requireReturn: false - }] - }, - { - code: - "/**\n" + - " * Test dash and slash.\n" + - " * @requires module:config\n" + - " * @requires module:modules/notifications\n" + - " */\n" + - "function foo() {}", - options: [{ - requireReturn: false - }] - }, - { - code: - "/**\n" + - " * Foo\n" + - " * @module module-name\n" + - " */\n" + - "function foo() {}", - options: [{ - requireReturn: false - }] - }, - { - code: - "/**\n" + - " * Foo\n" + - " * @alias module:module-name\n" + - " */\n" + - "function foo() {}", - options: [{ - requireReturn: false - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {Array.} hi - desc\n" + - "* @returns {Array.} desc\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string" - } - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {Array.} hi - desc\n" + - "* @returns {Array.} desc\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string" - } - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {Array.<{id: number, votes: number}>} hi - desc\n" + - "* @returns {Array.<{summary: string}>} desc\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - Number: "number", - String: "string" - } - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {Array.<[string, number]>} hi - desc\n" + - "* @returns {Array.<[string, string]>} desc\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - Number: "number", - String: "string" - } - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {Object>} hi - because why not\n" + - "* @returns {Boolean} desc\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - Number: "number", - String: "string" - } - }] - }, - { - code: "/**\n* Description\n* @param {string} a bar\n* @returns {string} desc */\nfunction foo(a = 1){}", - languageOptions: { - ecmaVersion: 6 - } - }, - { - code: "/**\n* Description\n* @param {string} b bar\n* @param {string} a bar\n* @returns {string} desc */\nfunction foo(b, a = 1){}", - languageOptions: { - ecmaVersion: 6 - } - }, - - // abstract - { - code: - "/**\n" + - "* Description\n" + - "* @abstract\n" + - "* @returns {Number} desc\n" + - "*/\n" + - "function foo(){ throw new Error('Not Implemented'); }", - options: [{ requireReturn: false }] - }, - - // https://github.com/eslint/eslint/issues/9412 - different orders for jsdoc tags - { - code: - "/**\n" + - "* Description\n" + - "* @return {Number} desc\n" + - "* @constructor \n" + - "* @override\n" + - "* @abstract\n" + - "* @interface\n" + - "* @param {string} hi - desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @returns {Number} desc\n" + - "* @class \n" + - "* @inheritdoc\n" + - "* @virtual\n" + - "* @interface\n" + - "* @param {string} hi - desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @return {Number} desc\n" + - "* @constructor \n" + - "* @override\n" + - "* @abstract\n" + - "* @interface\n" + - "* @arg {string} hi - desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @returns {Number} desc\n" + - "* @class \n" + - "* @inheritdoc\n" + - "* @virtual\n" + - "* @interface\n" + - "* @arg {string} hi - desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @return {Number} desc\n" + - "* @constructor \n" + - "* @override\n" + - "* @abstract\n" + - "* @interface\n" + - "* @argument {string} hi - desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @returns {Number} desc\n" + - "* @class \n" + - "* @inheritdoc\n" + - "* @virtual\n" + - "* @interface\n" + - "* @argument {string} hi - desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @constructor \n" + - "* @override\n" + - "* @abstract\n" + - "* @interface\n" + - "* @param {string} hi - desc\n" + - "* @return {Number} desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @class \n" + - "* @inheritdoc\n" + - "* @virtual\n" + - "* @interface\n" + - "* @arg {string} hi - desc\n" + - "* @return {Number} desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @argument {string} hi - desc\n" + - "* @return {Number} desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @constructor \n" + - "* @override\n" + - "* @abstract\n" + - "* @interface\n" + - "* @param {string} hi - desc\n" + - "* @returns {Number} desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @class \n" + - "* @inheritdoc\n" + - "* @virtual\n" + - "* @interface\n" + - "* @arg {string} hi - desc\n" + - "* @returns {Number} desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @argument {string} hi - desc\n" + - "* @returns {Number} desc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @override\n" + - "* @abstract\n" + - "* @interface\n" + - "* @param {string} hi - desc\n" + - "* @return {Number} desc\n" + - "* @constructor\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @inheritdoc\n" + - "* @virtual\n" + - "* @interface\n" + - "* @arg {string} hi - desc\n" + - "* @returns {Number} desc\n" + - "* @constructor\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @argument {string} hi - desc\n" + - "* @constructor\n" + - "*/\n" + - "function foo(hi){}", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @override\n" + - "* @abstract\n" + - "* @interface\n" + - "* @param {string} hi - desc\n" + - "* @return {Number} desc\n" + - "* @class\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @inheritdoc\n" + - "* @virtual\n" + - "* @interface\n" + - "* @arg {string} hi - desc\n" + - "* @returns {Number} desc\n" + - "* @class\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @argument {string} hi - desc\n" + - "* @class \n" + - "*/\n" + - "function foo(hi){}", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @abstract\n" + - "* @interface\n" + - "* @param {string} hi - desc\n" + - "* @return {Number} desc\n" + - "* @constructor\n" + - "* @override\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @virtual\n" + - "* @interface\n" + - "* @arg {string} hi - desc\n" + - "* @returns {Number} desc\n" + - "* @class\n" + - "* @override\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @argument {string} hi - desc\n" + - "* @override\n" + - "*/\n" + - "function foo(hi){}", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @abstract\n" + - "* @interface\n" + - "* @param {string} hi - desc\n" + - "* @return {Number} desc\n" + - "* @constructor\n" + - "* @inheritdoc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @virtual\n" + - "* @interface\n" + - "* @arg {string} hi - desc\n" + - "* @returns {Number} desc\n" + - "* @class\n" + - "* @inheritdoc\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @argument {string} hi - desc\n" + - "* @inheritdoc\n" + - "*/\n" + - "function foo(hi){}", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @interface\n" + - "* @param {string} hi - desc\n" + - "* @return {Number} desc\n" + - "* @constructor\n" + - "* @override\n" + - "* @abstract\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @interface\n" + - "* @arg {string} hi - desc\n" + - "* @returns {Number} desc\n" + - "* @class\n" + - "* @override\n" + - "* @abstract\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @argument {string} hi - desc\n" + - "* @abstract\n" + - "*/\n" + - "function foo(hi){}", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @interface\n" + - "* @param {string} hi - desc\n" + - "* @return {Number} desc\n" + - "* @constructor\n" + - "* @override\n" + - "* @virtual\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @interface\n" + - "* @arg {string} hi - desc\n" + - "* @returns {Number} desc\n" + - "* @class\n" + - "* @override\n" + - "* @virtual\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @argument {string} hi - desc\n" + - "* @virtual\n" + - "*/\n" + - "function foo(hi){}", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @param {string} hi - desc\n" + - "* @return {Number} desc\n" + - "* @constructor \n" + - "* @override\n" + - "* @abstract\n" + - "* @interface\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @arg {string} hi - desc\n" + - "* @returns {Number} desc\n" + - "* @class\n" + - "* @override\n" + - "* @virtual\n" + - "* @interface\n" + - "*/\n" + - "function foo(hi){ return 1; }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @argument {string} hi - desc\n" + - "* @interface\n" + - "*/\n" + - "function foo(hi){}", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @virtual\n" + - "* @returns {Number} desc\n" + - "*/\n" + - "function foo(){ throw new Error('Not Implemented'); }", - options: [{ requireReturn: false }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @abstract\n" + - "* @returns {Number} desc\n" + - "*/\n" + - "function foo(){ throw new Error('Not Implemented'); }", - options: [{ requireReturn: true }] - }, - { - code: - "/**\n" + - "* Description\n" + - "* @abstract\n" + - "* @returns {Number} desc\n" + - "*/\n" + - "function foo(){}", - options: [{ requireReturn: true }] - }, - { - code: [ - "/**", - " * @param {string} a - a.", - " * @param {object} [obj] - obj.", - " * @param {string} obj.b - b.", - " * @param {string} obj.c - c.", - " * @returns {void}", - " */", - "function foo(a, {b, c} = {}) {", - " // empty", - "}" - ].join("\n"), - languageOptions: { ecmaVersion: 6 } - }, - { - code: [ - "/**", - " * @param {string} a - a.", - " * @param {any[]} [list] - list.", - " * @returns {void}", - " */", - "function foo(a, [b, c] = []) {", - " // empty", - "}" - ].join("\n"), - languageOptions: { ecmaVersion: 6 } - }, - - // https://github.com/eslint/eslint/issues/7184 - "/**\n" + - "* Foo\n" + - "* @param {{foo}} hi - desc\n" + - "* @returns {ASTNode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - "/**\n" + - "* Foo\n" + - "* @param {{foo:String, bar, baz:Array}} hi - desc\n" + - "* @returns {ASTNode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - { - code: - "/**\n" + - "* Foo\n" + - "* @param {{String}} hi - desc\n" + - "* @returns {ASTNode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - astnode: "ASTNode" - } - }] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {{foo:string, astnode:Object, bar}} hi - desc\n" + - "* @returns {ASTNode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - astnode: "ASTNode" - } - }] - } - ], - - invalid: [ - { - code: - "call(\n" + - " /**\n" + - " * Doc for a function expression in a call expression.\n" + - " * @param {string} bogusName This is the param description.\n" + - " * @return {string} This is the return description.\n" + - " */\n" + - " function(argName) {\n" + - " return 'the return';\n" + - " }\n" + - ");\n", - output: null, - options: [{ requireReturn: false }], - errors: [{ - messageId: "expected", - data: { name: "argName", jsdocName: "bogusName" }, - type: "Block", - line: 4, - column: 6, - endLine: 4, - endColumn: 62 - }] - }, - { - code: "/** @@foo */\nfunction foo(){}", - output: null, - errors: [{ - messageId: "syntaxError", - type: "Block" - }] - }, - { - code: - "/**\n" + - "* Create a new thing.\n" + - "*/\n" + - "var thing = new Thing({\n" + - " /**\n" + - " * Missing return tag.\n" + - " */\n" + - " foo: function() {\n" + - " return 'bar';\n" + - " }\n" + - "});\n", - output: null, - options: [{ requireReturn: false }], - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }] - }, - { - code: "/** @@returns {void} Foo */\nfunction foo(){}", - output: null, - errors: [{ - messageId: "syntaxError", - type: "Block" - }] - }, - { - code: "/** Foo \n@returns {void Foo\n */\nfunction foo(){}", - output: null, - errors: [{ - messageId: "missingBrace", - type: "Block" - }] - }, - { - code: "/** Foo \n@return {void} Foo\n */\nfunction foo(){}", - output: "/** Foo \n@returns {void} Foo\n */\nfunction foo(){}", - options: [{ prefer: { return: "returns" } }], - errors: [{ - messageId: "use", - data: { name: "returns" }, - type: "Block", - line: 2, - column: 1, - endLine: 2, - endColumn: 8 - }] - }, - { - code: "/** Foo \n@argument {int} bar baz\n */\nfunction foo(bar){}", - output: "/** Foo \n@arg {int} bar baz\n */\nfunction foo(bar){}", - options: [{ prefer: { argument: "arg" } }], - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }, { - messageId: "use", - data: { name: "arg" }, - type: "Block", - line: 2, - column: 1, - endLine: 2, - endColumn: 10 - }] - }, - { - code: "/** Foo \n */\nfunction foo(){}", - output: null, - options: [{ prefer: { returns: "return" } }], - errors: [{ - messageId: "missingReturn", - data: { returns: "return" }, - type: "Block" - }] - }, - { - code: "/** Foo \n@return {void} Foo\n */\nfoo.bar = () => {}", - output: "/** Foo \n@returns {void} Foo\n */\nfoo.bar = () => {}", - options: [{ prefer: { return: "returns" } }], - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "use", - data: { name: "returns" }, - type: "Block", - line: 2, - column: 1, - endLine: 2, - endColumn: 8 - }] - }, - { - code: "/** Foo \n@param {void Foo\n */\nfunction foo(){}", - output: null, - errors: [{ - messageId: "missingBrace", - type: "Block" - }] - }, - { - code: "/** Foo \n@param {} p Bar\n */\nfunction foo(){}", - output: null, - errors: [{ - messageId: "syntaxError", - type: "Block" - }] - }, - { - code: "/** Foo \n@param {void Foo */\nfunction foo(){}", - output: null, - errors: [{ - messageId: "missingBrace", - type: "Block" - }] - }, - { - code: "/** Foo\n* @param p Desc \n*/\nfunction foo(){}", - output: null, - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }, { - messageId: "missingParamType", - data: { name: "p" }, - type: "Block", - line: 2, - column: 3, - endLine: 2, - endColumn: 16 - }] - }, - { - code: "/**\n* Foo\n* @param {string} p \n*/\nfunction foo(){}", - output: null, - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }, { - messageId: "missingParamDesc", - data: { name: "p" }, - type: "Block", - line: 3, - column: 3, - endLine: 3, - endColumn: 20 - }] - }, - { - code: "/**\n* Foo\n* @param {string} p \n*/\nvar foo = function(){}", - output: null, - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }, { - messageId: "missingParamDesc", - data: { name: "p" }, - type: "Block", - line: 3, - column: 3, - endLine: 3, - endColumn: 20 - }] - }, - { - code: "/**\n* Foo\n* @param {string} p \n*/\nvar foo = \nfunction(){}", - output: null, - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }, { - messageId: "missingParamDesc", - data: { name: "p" }, - type: "Block", - line: 3, - column: 3, - endLine: 3, - endColumn: 20 - }] - }, - { - code: - "/**\n" + - " * Description for a\n" + - " */\n" + - "var A = \n" + - " class {\n" + - " /**\n" + - " * Description for method.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " print(xs) {\n" + - " this.a = xs;" + - " }\n" + - "};", - output: null, - options: [{ - requireReturn: true, - matchDescription: "^[A-Z][A-Za-z0-9\\s]*[.]$" - }], - languageOptions: { - ecmaVersion: 6 - }, - errors: [ - { - messageId: "unsatisfiedDesc", - type: "Block" - }, - { - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - } - ] - }, - { - code: "/**\n* Foo\n* @returns {string} \n*/\nfunction foo(){}", - output: null, - errors: [{ - messageId: "missingReturnDesc", - type: "Block" - }] - }, - { - code: "/**\n* Foo\n* @returns {string} something \n*/\nfunction foo(p){}", - output: null, - errors: [{ - messageId: "missingParam", - data: { name: "p" }, - type: "Block" - }] - }, - { - code: "/**\n* Foo\n* @returns {string} something \n*/\nvar foo = \nfunction foo(a = 1){}", - output: null, - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "missingParam", - data: { name: "a" }, - type: "Block" - }] - }, - { - code: "/**\n* Foo\n* @param {string} a Description \n* @param {string} b Description \n* @returns {string} something \n*/\nvar foo = \nfunction foo(b, a = 1){}", - output: null, - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "expected", - data: { name: "b", jsdocName: "a" }, - type: "Block", - line: 3, - column: 3, - endLine: 3, - endColumn: 32 - }, - { - messageId: "expected", - data: { name: "a", jsdocName: "b" }, - type: "Block", - line: 4, - column: 3, - endLine: 4, - endColumn: 32 - }] - }, - { - code: "/**\n* Foo\n* @param {string} p desc\n* @param {string} p desc \n*/\nfunction foo(){}", - output: null, - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }, { - messageId: "duplicateParam", - data: { name: "p" }, - type: "Block", - line: 4, - column: 3, - endLine: 4, - endColumn: 25 - }] - }, - { - code: "/**\n* Foo\n* @param {string} a desc\n@returns {void}*/\nfunction foo(b){}", - output: null, - errors: [{ - messageId: "expected", - data: { name: "b", jsdocName: "a" }, - type: "Block", - line: 3, - column: 3, - endLine: 3, - endColumn: 25 - }] - }, - { - code: "/**\n* Foo\n* @override\n* @param {string} a desc\n */\nfunction foo(b){}", - output: null, - errors: [{ - messageId: "expected", - data: { name: "b", jsdocName: "a" }, - type: "Block", - line: 4, - column: 3, - endLine: 4, - endColumn: 25 - }] - }, - { - code: "/**\n* Foo\n* @inheritdoc\n* @param {string} a desc\n */\nfunction foo(b){}", - output: null, - errors: [{ - messageId: "expected", - data: { name: "b", jsdocName: "a" }, - type: "Block", - line: 4, - column: 3, - endLine: 4, - endColumn: 25 - }] - }, - { - code: "/**\n* Foo\n* @param {string} a desc\n*/\nfunction foo(a){var t = false; if(t) {return t;}}", - output: null, - options: [{ requireReturn: false }], - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }] - }, - { - code: "/**\n* Foo\n* @param {string} a desc\n*/\nfunction foo(a){var t = false; if(t) {return null;}}", - output: null, - options: [{ requireReturn: false }], - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }] - }, - { - code: "/**\n* Foo\n* @param {string} a desc\n@returns {MyClass}*/\nfunction foo(a){var t = false; if(t) {process(t);}}", - output: null, - options: [{ requireReturn: false }], - errors: [{ - messageId: "unexpectedTag", - data: { title: "returns" }, - type: "Block", - line: 4, - column: 1, - endLine: 4, - endColumn: 19 - }] - }, - { - code: "/**\n * Does something. \n* @param {string} a - this is a \n* @return {Array} The result of doing it \n*/\n export function doSomething(a) { }", - output: "/**\n * Does something. \n* @param {string} a - this is a \n* @returns {Array} The result of doing it \n*/\n export function doSomething(a) { }", - options: [{ prefer: { return: "returns" } }], - languageOptions: { ecmaVersion: 6, sourceType: "module" }, - errors: [{ - messageId: "use", - data: { name: "returns" }, - type: "Block", - line: 4, - column: 3, - endLine: 4, - endColumn: 10 - }] - }, - { - code: "/**\n * Does something. \n* @param {string} a - this is a \n* @return {Array} The result of doing it \n*/\n export default function doSomething(a) { }", - output: "/**\n * Does something. \n* @param {string} a - this is a \n* @returns {Array} The result of doing it \n*/\n export default function doSomething(a) { }", - options: [{ prefer: { return: "returns" } }], - languageOptions: { ecmaVersion: 6, sourceType: "module" }, - errors: [{ - messageId: "use", - data: { name: "returns" }, - type: "Block", - line: 4, - column: 3, - endLine: 4, - endColumn: 10 - }] - }, - { - code: "/** foo */ var foo = () => bar();", - output: null, - options: [{ requireReturn: false }], - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }] - }, - { - code: "/** foo */ var foo = () => { return bar(); };", - output: null, - options: [{ requireReturn: false }], - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }] - }, - { - code: "/** @returns {object} foo */ var foo = () => { bar(); };", - output: null, - options: [{ requireReturn: false }], - languageOptions: { ecmaVersion: 6 }, - errors: [{ - messageId: "unexpectedTag", - data: { title: "returns" }, - type: "Block", - line: 1, - column: 5, - endLine: 1, - endColumn: 26 - }] - }, - { - code: "/**\n* @param fields [Array]\n */\n function foo(){}", - output: null, - errors: [ - { - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }, - { - messageId: "missingParamType", - data: { name: "fields" }, - type: "Block", - line: 2, - column: 3, - endLine: 2, - endColumn: 24 - } - ] - }, - { - code: "/**\n* Start with caps and end with period\n* @return {void} */\nfunction foo(){}", - output: null, - options: [{ - matchDescription: "^[A-Z][A-Za-z0-9\\s]*[.]$" - }], - errors: [{ - messageId: "unsatisfiedDesc", - type: "Block" - }] - }, - { - code: "/** Foo \n@return Foo\n */\nfunction foo(){}", - output: null, - options: [{ prefer: { return: "return" } }], - errors: [{ - messageId: "missingReturnType", - type: "Block" - }] - }, - { - code: "/** Foo \n@return sdf\n */\nfunction foo(){}", - output: null, - options: [{ - prefer: { return: "return" }, - requireReturn: false - }], - errors: [{ - messageId: "unexpectedTag", - data: { title: "return" }, - type: "Block", - line: 2, - column: 1, - endLine: 2, - endColumn: 12 - }] - }, - - // classes - { - code: - "/**\n" + - " * Description for A\n" + - " */\n" + - "class A {\n" + - " /**\n" + - " * Description for constructor\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - output: null, - options: [{ - requireReturn: false, - matchDescription: "^[A-Z][A-Za-z0-9\\s]*[.]$" - }], - languageOptions: { - ecmaVersion: 6 - }, - errors: [ - { - messageId: "unsatisfiedDesc", - type: "Block" - }, - { - messageId: "unsatisfiedDesc", - type: "Block" - } - ] - }, - { - code: - "/**\n" + - " * Description for a\n" + - " */\n" + - "var A = class {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " */\n" + - " print(xs) {\n" + - " this.a = xs;" + - " }\n" + - "};", - output: null, - options: [{ - requireReturn: true, - matchDescription: "^[A-Z][A-Za-z0-9\\s]*[.]$" - }], - languageOptions: { - ecmaVersion: 6 - }, - errors: [ - { - messageId: "unsatisfiedDesc", - type: "Block" - }, - { - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - } - ] - }, - { - code: - "/**\n" + - " * Description for A.\n" + - " */\n" + - "class A {\n" + - " /**\n" + - " * Description for constructor.\n" + - " * @param {object[]} xs - xs\n" + - " * @returns {void}\n" + - " */\n" + - " constructor(xs) {\n" + - " this.a = xs;" + - " }\n" + - " /**\n" + - " * Description for method.\n" + - " */\n" + - " print(xs) {\n" + - " this.a = xs;" + - " }\n" + - "}", - output: null, - options: [], - languageOptions: { - ecmaVersion: 6 - }, - errors: [ - { - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }, - { - messageId: "missingParam", - data: { name: "xs" }, - type: "Block" - } - ] - }, - { - code: - "/**\n" + - " * Use of this with an invalid type expression\n" + - " * @this {not.a.valid.type.expression\n" + - " */\n" + - "function foo() {}", - output: null, - options: [{ requireReturn: false }], - errors: [{ - messageId: "missingBrace", - type: "Block" - }] - }, - { - code: - "/**\n" + - " * Use of this with a type that is not a member expression\n" + - " * @this {Array}\n" + - " */\n" + - "function foo() {}", - output: null, - options: [{ requireReturn: false }], - errors: [{ - messageId: "syntaxError", - type: "Block" - }] - }, - - // async function - { - code: - "/**\n" + - " * An async function. Options requires return.\n" + - " */\n" + - "async function a() {}", - output: null, - options: [{ requireReturn: true }], - languageOptions: { - ecmaVersion: 2017 - }, - errors: [{ - messageId: "missingReturn", - data: { returns: "returns" }, - type: "Block" - }] - }, - - // type validations - { - code: - "/**\n" + - "* Foo\n" + - "* @param {String} hi - desc\n" + - "* @returns {Astnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - output: - "/**\n" + - "* Foo\n" + - "* @param {string} hi - desc\n" + - "* @returns {ASTNode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - Astnode: "ASTNode" - } - }], - errors: [ - { - messageId: "useType", - data: { expectedTypeName: "string", currentTypeName: "String" }, - type: "Block", - line: 3, - column: 11, - endLine: 3, - endColumn: 17 - }, - { - messageId: "useType", - data: { expectedTypeName: "ASTNode", currentTypeName: "Astnode" }, - type: "Block", - line: 4, - column: 13, - endLine: 4, - endColumn: 20 - } - ] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {{20:String}} hi - desc\n" + - "* @returns {Astnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - output: - "/**\n" + - "* Foo\n" + - "* @param {{20:string}} hi - desc\n" + - "* @returns {ASTNode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - Astnode: "ASTNode" - } - }], - errors: [ - { - messageId: "useType", - data: { expectedTypeName: "string", currentTypeName: "String" }, - type: "Block", - line: 3, - column: 15, - endLine: 3, - endColumn: 21 - }, - { - messageId: "useType", - data: { expectedTypeName: "ASTNode", currentTypeName: "Astnode" }, - type: "Block", - line: 4, - column: 13, - endLine: 4, - endColumn: 20 - } - ] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {String|number|test} hi - desc\n" + - "* @returns {Astnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - output: - "/**\n" + - "* Foo\n" + - "* @param {String|number|Test} hi - desc\n" + - "* @returns {Astnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - test: "Test" - } - }], - errors: [ - { - messageId: "useType", - data: { expectedTypeName: "Test", currentTypeName: "test" }, - type: "Block", - line: 3, - column: 25, - endLine: 3, - endColumn: 29 - } - ] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {Array.} hi - desc\n" + - "* @returns {Astnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - output: - "/**\n" + - "* Foo\n" + - "* @param {Array.} hi - desc\n" + - "* @returns {Astnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - astnode: "ASTNode" - } - }], - errors: [ - { - messageId: "useType", - data: { expectedTypeName: "string", currentTypeName: "String" }, - type: "Block", - line: 3, - column: 18, - endLine: 3, - endColumn: 24 - } - ] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {Array.<{id: Number, votes: Number}>} hi - desc\n" + - "* @returns {Array.<{summary: String}>} desc\n" + - "*/\n" + - "function foo(hi){}", - output: - "/**\n" + - "* Foo\n" + - "* @param {Array.<{id: number, votes: number}>} hi - desc\n" + - "* @returns {Array.<{summary: string}>} desc\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - Number: "number", - String: "string" - } - }], - errors: [ - { - messageId: "useType", - data: { expectedTypeName: "number", currentTypeName: "Number" }, - type: "Block", - line: 3, - column: 23, - endLine: 3, - endColumn: 29 - }, - { - messageId: "useType", - data: { expectedTypeName: "number", currentTypeName: "Number" }, - type: "Block", - line: 3, - column: 38, - endLine: 3, - endColumn: 44 - }, - { - messageId: "useType", - data: { expectedTypeName: "string", currentTypeName: "String" }, - type: "Block", - line: 4, - column: 30, - endLine: 4, - endColumn: 36 - } - ] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {Array.<[String, Number]>} hi - desc\n" + - "* @returns {Array.<[String, String]>} desc\n" + - "*/\n" + - "function foo(hi){}", - output: - "/**\n" + - "* Foo\n" + - "* @param {Array.<[string, number]>} hi - desc\n" + - "* @returns {Array.<[string, string]>} desc\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - Number: "number", - String: "string" - } - }], - errors: [ - { - messageId: "useType", - data: { expectedTypeName: "string", currentTypeName: "String" }, - type: "Block", - line: 3, - column: 19, - endLine: 3, - endColumn: 25 - }, - { - messageId: "useType", - data: { expectedTypeName: "number", currentTypeName: "Number" }, - type: "Block", - line: 3, - column: 27, - endLine: 3, - endColumn: 33 - }, - { - messageId: "useType", - data: { expectedTypeName: "string", currentTypeName: "String" }, - type: "Block", - line: 4, - column: 21, - endLine: 4, - endColumn: 27 - }, - { - messageId: "useType", - data: { expectedTypeName: "string", currentTypeName: "String" }, - type: "Block", - line: 4, - column: 29, - endLine: 4, - endColumn: 35 - } - ] - }, - { - code: - "/**\n" + - "* Foo\n" + - "* @param {object>} hi - because why not\n" + - "* @returns {Boolean} desc\n" + - "*/\n" + - "function foo(hi){}", - output: - "/**\n" + - "* Foo\n" + - "* @param {Object>} hi - because why not\n" + - "* @returns {Boolean} desc\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - Number: "number", - String: "string", - object: "Object" - } - }], - errors: [ - { - messageId: "useType", - data: { expectedTypeName: "Object", currentTypeName: "object" }, - type: "Block", - line: 3, - column: 11, - endLine: 3, - endColumn: 17 - }, - { - messageId: "useType", - data: { expectedTypeName: "string", currentTypeName: "String" }, - type: "Block", - line: 3, - column: 18, - endLine: 3, - endColumn: 24 - }, - { - messageId: "useType", - data: { expectedTypeName: "Object", currentTypeName: "object" }, - type: "Block", - line: 3, - column: 25, - endLine: 3, - endColumn: 31 - }, - { - messageId: "useType", - data: { expectedTypeName: "string", currentTypeName: "String" }, - type: "Block", - line: 3, - column: 32, - endLine: 3, - endColumn: 38 - }, - { - messageId: "useType", - data: { expectedTypeName: "number", currentTypeName: "Number" }, - type: "Block", - line: 3, - column: 40, - endLine: 3, - endColumn: 46 - } - ] - }, - - // https://github.com/eslint/eslint/issues/7184 - { - code: - "/**\n" + - "* Foo\n" + - "* @param {{foo:String, astnode:Object, bar}} hi - desc\n" + - "* @returns {ASTnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - output: - "/**\n" + - "* Foo\n" + - "* @param {{foo:string, astnode:Object, bar}} hi - desc\n" + - "* @returns {ASTnode} returns a node\n" + - "*/\n" + - "function foo(hi){}", - options: [{ - preferType: { - String: "string", - astnode: "ASTNode" - } - }], - errors: [ - { - messageId: "useType", - data: { expectedTypeName: "string", currentTypeName: "String" }, - type: "Block", - line: 3, - column: 16, - endLine: 3, - endColumn: 22 - } - ] - } - ] -}); diff --git a/tests/lib/source-code/source-code.js b/tests/lib/source-code/source-code.js index 6d3e07a2f54..468070212d7 100644 --- a/tests/lib/source-code/source-code.js +++ b/tests/lib/source-code/source-code.js @@ -3128,26 +3128,74 @@ describe("SourceCode", () => { assert.isTrue(variable.writeable); }); + describe("exported variables", () => { - it("should mark exported variables", () => { + /** + * GlobalScope + * @param {string} code the code to check + * @returns {Scope} globalScope + */ + function loadGlobalScope(code) { + const ast = espree.parse(code, DEFAULT_CONFIG); + const scopeManager = eslintScope.analyze(ast, { + ignoreEval: true, + ecmaVersion: 6 + }); + const sourceCode = new SourceCode({ text: code, ast, scopeManager }); - const code = "/*exported foo */ var foo;"; - const ast = espree.parse(code, DEFAULT_CONFIG); - const scopeManager = eslintScope.analyze(ast, { - ignoreEval: true, - ecmaVersion: 6 + sourceCode.applyInlineConfig(); + sourceCode.finalize(); + + const globalScope = sourceCode.scopeManager.scopes[0].set; + + return globalScope; + } + + it("should mark exported variable", () => { + const code = "/*exported foo */ var foo;"; + const globalScope = loadGlobalScope(code); + const variable = globalScope.get("foo"); + + assert.isDefined(variable); + assert.isTrue(variable.eslintUsed); + assert.isTrue(variable.eslintExported); }); - const sourceCode = new SourceCode({ text: code, ast, scopeManager }); - sourceCode.applyInlineConfig(); - sourceCode.finalize(); + it("should not mark exported variable with `key: value` pair", () => { + const code = "/*exported foo: true */ var foo;"; + const globalScope = loadGlobalScope(code); + const variable = globalScope.get("foo"); - const globalScope = sourceCode.scopeManager.scopes[0]; - const variable = globalScope.set.get("foo"); + assert.isDefined(variable); + assert.notOk(variable.eslintUsed); + assert.notOk(variable.eslintExported); + }); - assert.isDefined(variable); - assert.isTrue(variable.eslintUsed); - assert.isTrue(variable.eslintExported); + it("should mark exported variables with comma", () => { + const code = "/*exported foo, bar */ var foo, bar;"; + const globalScope = loadGlobalScope(code); + + ["foo", "bar"].forEach(name => { + const variable = globalScope.get(name); + + assert.isDefined(variable); + assert.isTrue(variable.eslintUsed); + assert.isTrue(variable.eslintExported); + }); + }); + + it("should not mark exported variables without comma", () => { + const code = "/*exported foo bar */ var foo, bar;"; + const globalScope = loadGlobalScope(code); + + ["foo", "bar"].forEach(name => { + const variable = globalScope.get(name); + + assert.isDefined(variable); + assert.notOk(variable.eslintUsed); + assert.notOk(variable.eslintExported); + }); + }); }); it("should extract rule configuration", () => {