diff --git a/package.json b/package.json index 7d42fd719..054c45695 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "url": "http://gajus.com" }, "dependencies": { + "@es-joy/jsdoccomment": "^0.1.1", "comment-parser": "1.1.5", "debug": "^4.3.1", "jsdoctypeparser": "^9.0.0", @@ -16,12 +17,12 @@ "description": "JSDoc linting rules for ESLint.", "devDependencies": { "@babel/cli": "^7.13.16", - "@babel/core": "^7.13.16", + "@babel/core": "^7.14.0", "@babel/eslint-parser": "^7.13.14", "@babel/node": "^7.13.13", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-transform-flow-strip-types": "^7.13.0", - "@babel/preset-env": "^7.13.15", + "@babel/preset-env": "^7.14.0", "@babel/register": "^7.13.16", "@hkdobrev/run-if-changed": "^0.3.1", "@typescript-eslint/parser": "^4.22.0", diff --git a/src/eslint/LICENSE-MIT.txt b/src/eslint/LICENSE-MIT.txt deleted file mode 100644 index 7fe552a86..000000000 --- a/src/eslint/LICENSE-MIT.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright JS Foundation and other contributors, https://js.foundation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/src/eslint/getJSDocComment.js b/src/eslint/getJSDocComment.js deleted file mode 100644 index 6ef95520f..000000000 --- a/src/eslint/getJSDocComment.js +++ /dev/null @@ -1,235 +0,0 @@ -/** - * Obtained originally from {@link https://github.com/eslint/eslint/blob/master/lib/util/source-code.js#L313} - * - * @license MIT - */ - -/** - * Checks if the given token is a comment token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is a comment token. - */ -const isCommentToken = (token) => { - return token.type === 'Line' || token.type === 'Block' || token.type === 'Shebang'; -}; - -const getDecorator = (node) => { - return node?.declaration?.decorators?.[0] || node?.decorators?.[0] || - node?.parent?.decorators?.[0]; -}; - -/** - * Check to see if its a ES6 export declaration. - * - * @param {ASTNode} astNode An AST node. - * @returns {boolean} whether the given node represents an export declaration. - * @private - */ -const looksLikeExport = function (astNode) { - return astNode.type === 'ExportDefaultDeclaration' || astNode.type === 'ExportNamedDeclaration' || - astNode.type === 'ExportAllDeclaration' || astNode.type === 'ExportSpecifier'; -}; - -const getTSFunctionComment = function (astNode) { - const {parent} = astNode; - const grandparent = parent.parent; - const greatGrandparent = grandparent.parent; - const greatGreatGrandparent = greatGrandparent && greatGrandparent.parent; - - // istanbul ignore if - if (parent.type !== 'TSTypeAnnotation') { - return astNode; - } - - switch (grandparent.type) { - case 'ClassProperty': - case 'TSDeclareFunction': - case 'TSMethodSignature': - case 'TSPropertySignature': - return grandparent; - case 'ArrowFunctionExpression': - // istanbul ignore else - if ( - greatGrandparent.type === 'VariableDeclarator' - - // && greatGreatGrandparent.parent.type === 'VariableDeclaration' - ) { - return greatGreatGrandparent.parent; - } - - // istanbul ignore next - return astNode; - case 'FunctionExpression': - // istanbul ignore else - if (greatGrandparent.type === 'MethodDefinition') { - return greatGrandparent; - } - - // Fallthrough - default: - // istanbul ignore if - if (grandparent.type !== 'Identifier') { - // istanbul ignore next - return astNode; - } - } - - // istanbul ignore next - switch (greatGrandparent.type) { - case 'ArrowFunctionExpression': - // istanbul ignore else - if ( - greatGreatGrandparent.type === 'VariableDeclarator' && - greatGreatGrandparent.parent.type === 'VariableDeclaration' - ) { - return greatGreatGrandparent.parent; - } - - // istanbul ignore next - return astNode; - case 'FunctionDeclaration': - return greatGrandparent; - case 'VariableDeclarator': - // istanbul ignore else - if (greatGreatGrandparent.type === 'VariableDeclaration') { - return greatGreatGrandparent; - } - - // Fallthrough - default: - // istanbul ignore next - return astNode; - } -}; - -const invokedExpression = new Set(['CallExpression', 'OptionalCallExpression', 'NewExpression']); -const allowableCommentNode = new Set([ - 'VariableDeclaration', - 'ExpressionStatement', - 'MethodDefinition', - 'Property', - 'ObjectProperty', - 'ClassProperty', -]); - -/* eslint-disable complexity */ -/** - * Reduces the provided node to the appropriate node for evaluating JSDoc comment status. - * - * @param {ASTNode} node An AST node. - * @param {SourceCode} sourceCode The ESLint SourceCode. - * @returns {ASTNode} The AST node that can be evaluated for appropriate JSDoc comments. - * @private - */ -const getReducedASTNode = function (node, sourceCode) { - /* eslint-enable complexity */ - let {parent} = node; - - switch (node.type) { - case 'TSFunctionType': - return getTSFunctionComment(node); - case 'TSInterfaceDeclaration': - case 'TSTypeAliasDeclaration': - case 'TSEnumDeclaration': - case 'ClassDeclaration': - case 'FunctionDeclaration': - return looksLikeExport(parent) ? parent : node; - - case 'TSDeclareFunction': - case 'ClassExpression': - case 'ObjectExpression': - case 'ArrowFunctionExpression': - case 'TSEmptyBodyFunctionExpression': - case 'FunctionExpression': - if ( - !invokedExpression.has(parent.type) - ) { - while ( - !sourceCode.getCommentsBefore(parent).length && - !/Function/u.test(parent.type) && - !allowableCommentNode.has(parent.type) - ) { - parent = parent.parent; - - if (!parent) { - break; - } - } - if (parent && parent.type !== 'FunctionDeclaration' && parent.type !== 'Program') { - if (parent.parent && parent.parent.type === 'ExportNamedDeclaration') { - return parent.parent; - } - - return parent; - } - } - - return node; - - default: - return node; - } -}; - -/** - * Checks for the presence of a JSDoc comment for the given node and returns it. - * - * @param {ASTNode} astNode The AST node to get the comment for. - * @returns {Token|null} The Block comment token containing the JSDoc comment - * for the given node or null if not found. - * @private - */ -const findJSDocComment = (astNode, sourceCode, settings) => { - const {minLines, maxLines} = settings; - let currentNode = astNode; - let tokenBefore = null; - - while (currentNode) { - const decorator = getDecorator(currentNode); - if (decorator) { - currentNode = decorator; - } - tokenBefore = sourceCode.getTokenBefore(currentNode, {includeComments: true}); - if (!tokenBefore || !isCommentToken(tokenBefore)) { - return null; - } - if (tokenBefore.type === 'Line') { - currentNode = tokenBefore; - continue; - } - break; - } - - if ( - tokenBefore.type === 'Block' && - tokenBefore.value.charAt(0) === '*' && - currentNode.loc.start.line - tokenBefore.loc.end.line >= minLines && - currentNode.loc.start.line - tokenBefore.loc.end.line <= maxLines - ) { - return tokenBefore; - } - - return null; -}; - -/** - * Retrieves the JSDoc comment for a given node. - * - * @param {SourceCode} sourceCode The ESLint SourceCode - * @param {ASTNode} node The AST node to get the comment for. - * @param {object} settings The settings in context - * @returns {Token|null} The Block comment token containing the JSDoc comment - * for the given node or null if not found. - * @public - */ -const getJSDocComment = function (sourceCode, node, settings) { - const reducedNode = getReducedASTNode(node, sourceCode); - - return findJSDocComment(reducedNode, sourceCode, settings); -}; - -export { - getReducedASTNode, getJSDocComment, getDecorator, findJSDocComment, -}; -export default getJSDocComment; diff --git a/src/exportParser.js b/src/exportParser.js index 794f69d3c..c156a5bd8 100644 --- a/src/exportParser.js +++ b/src/exportParser.js @@ -1,7 +1,7 @@ -import debugModule from 'debug'; import { findJSDocComment, -} from './eslint/getJSDocComment'; +} from '@es-joy/jsdoccomment'; +import debugModule from 'debug'; const debug = debugModule('requireExportJsdoc'); diff --git a/src/iterateJsdoc.js b/src/iterateJsdoc.js index a841ae2bc..a5e81fcb5 100644 --- a/src/iterateJsdoc.js +++ b/src/iterateJsdoc.js @@ -1,3 +1,6 @@ +import { + getReducedASTNode, getJSDocComment, +} from '@es-joy/jsdoccomment'; import { parse as commentParser, stringify as commentStringify, } from 'comment-parser'; @@ -15,9 +18,6 @@ import { seedTokens, } from 'comment-parser/lib/util'; import _ from 'lodash'; -import { - getJSDocComment, getReducedASTNode, -} from './eslint/getJSDocComment'; import jsdocUtils from './jsdocUtils'; /* diff --git a/src/rules/noUndefinedTypes.js b/src/rules/noUndefinedTypes.js index f492db0d4..62f1c8d3d 100644 --- a/src/rules/noUndefinedTypes.js +++ b/src/rules/noUndefinedTypes.js @@ -1,10 +1,10 @@ +import { + getJSDocComment, +} from '@es-joy/jsdoccomment'; import { parse as parseType, traverse, } from 'jsdoctypeparser'; import _ from 'lodash'; -import { - getJSDocComment, -} from '../eslint/getJSDocComment'; import iterateJsdoc, { parseComment, } from '../iterateJsdoc'; diff --git a/src/rules/requireJsdoc.js b/src/rules/requireJsdoc.js index 93cf19348..45d628780 100644 --- a/src/rules/requireJsdoc.js +++ b/src/rules/requireJsdoc.js @@ -1,7 +1,7 @@ -import _ from 'lodash'; import { getJSDocComment, getReducedASTNode, getDecorator, -} from '../eslint/getJSDocComment'; +} from '@es-joy/jsdoccomment'; +import _ from 'lodash'; import exportParser from '../exportParser'; import { getSettings, diff --git a/test/eslint/getJSDocComment.js b/test/eslint/getJSDocComment.js deleted file mode 100644 index 554e72685..000000000 --- a/test/eslint/getJSDocComment.js +++ /dev/null @@ -1,59 +0,0 @@ -import { - RuleTester, -} from 'eslint'; -import { - getJSDocComment, -} from '../../src/eslint/getJSDocComment'; -import { - getSettings, -} from '../../src/iterateJsdoc'; - -const rule = { - create (context) { - const sourceCode = context.getSourceCode(); - const settings = getSettings(context); - if (!settings) { - return {}; - } - - return { - ObjectExpression (node) { - const comment = getJSDocComment(sourceCode, node, settings); - if (comment !== null) { - return; - } - context.report({ - messageId: 'missingJsDoc', - node, - }); - }, - }; - }, - meta: { - messages: { - missingJsDoc: 'Missing JSDoc comment.', - }, - type: 'layout', - }, -}; - -const ruleTester = new RuleTester(); - -ruleTester.run('getJSDocComment', rule, { - invalid: [{ - code: 'var a = {};', - errors: [{messageId: 'missingJsDoc'}], - }], - valid: [{ - code: ` - /** Doc */ - var a = {}; - `, - }, { - code: ` - /** Doc */ - // eslint-disable-next-line no-var - var a = {}; - `, - }], -});