123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- 'use strict';
-
- Object.defineProperty(exports, '__esModule', { value: true });
-
- var jsdocTypePrattParser = require('jsdoc-type-pratt-parser');
- var esquery = require('esquery');
- var commentParser = require('comment-parser');
-
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
-
- var esquery__default = /*#__PURE__*/_interopDefaultLegacy(esquery);
-
- const stripEncapsulatingBrackets = (container, isArr) => {
- if (isArr) {
- const firstItem = container[0];
- firstItem.rawType = firstItem.rawType.replace(/^\{/u, '');
- const lastItem = container[container.length - 1];
- lastItem.rawType = lastItem.rawType.replace(/\}$/u, '');
- return;
- }
-
- container.rawType = container.rawType.replace(/^\{/u, '').replace(/\}$/u, '');
- };
-
- const commentParserToESTree = (jsdoc, mode) => {
- const {
- tokens: {
- delimiter: delimiterRoot,
- lineEnd: lineEndRoot,
- postDelimiter: postDelimiterRoot,
- end: endRoot,
- description: descriptionRoot
- }
- } = jsdoc.source[0];
- const ast = {
- delimiter: delimiterRoot,
- description: descriptionRoot,
- descriptionLines: [],
- // `end` will be overwritten if there are other entries
- end: endRoot,
- postDelimiter: postDelimiterRoot,
- lineEnd: lineEndRoot,
- type: 'JsdocBlock'
- };
- const tags = [];
- let lastDescriptionLine;
- let lastTag = null;
- jsdoc.source.slice(1).forEach((info, idx) => {
- const {
- tokens
- } = info;
- const {
- delimiter,
- description,
- postDelimiter,
- start,
- tag,
- end,
- type: rawType
- } = tokens;
-
- if (tag || end) {
- if (lastDescriptionLine === undefined) {
- lastDescriptionLine = idx;
- } // Clean-up with last tag before end or new tag
-
-
- if (lastTag) {
- // Strip out `}` that encapsulates and is not part of
- // the type
- stripEncapsulatingBrackets(lastTag);
-
- if (lastTag.typeLines.length) {
- stripEncapsulatingBrackets(lastTag.typeLines, true);
- } // With even a multiline type now in full, add parsing
-
-
- let parsedType = null;
-
- try {
- parsedType = jsdocTypePrattParser.parse(lastTag.rawType, mode);
- } catch {// Ignore
- }
-
- lastTag.parsedType = parsedType;
- }
-
- if (end) {
- ast.end = end;
- return;
- }
-
- const {
- end: ed,
- ...tkns
- } = tokens;
- const tagObj = { ...tkns,
- descriptionLines: [],
- rawType: '',
- type: 'JsdocTag',
- typeLines: []
- };
- tagObj.tag = tagObj.tag.replace(/^@/u, '');
- lastTag = tagObj;
- tags.push(tagObj);
- }
-
- if (rawType) {
- // Will strip rawType brackets after this tag
- lastTag.typeLines.push({
- delimiter,
- postDelimiter,
- rawType,
- start,
- type: 'JsdocTypeLine'
- });
- lastTag.rawType += rawType;
- }
-
- if (description) {
- const holder = lastTag || ast;
- holder.descriptionLines.push({
- delimiter,
- description,
- postDelimiter,
- start,
- type: 'JsdocDescriptionLine'
- });
- holder.description += holder.description ? '\n' + description : description;
- }
- });
- ast.lastDescriptionLine = lastDescriptionLine;
- ast.tags = tags;
- return ast;
- };
-
- const jsdocVisitorKeys = {
- JsdocBlock: ['tags', 'descriptionLines'],
- JsdocDescriptionLine: [],
- JsdocTypeLine: [],
- JsdocTag: ['descriptionLines', 'typeLines', 'parsedType']
- };
-
- /**
- * @callback CommentHandler
- * @param {string} commentSelector
- * @param {Node} jsdoc
- * @returns {boolean}
- */
-
- /**
- * @param {Settings} settings
- * @returns {CommentHandler}
- */
-
- const commentHandler = settings => {
- /**
- * @type {CommentHandler}
- */
- return (commentSelector, jsdoc) => {
- const {
- mode
- } = settings;
- const selector = esquery__default['default'].parse(commentSelector);
- const ast = commentParserToESTree(jsdoc, mode);
- return esquery__default['default'].matches(ast, selector, null, {
- visitorKeys: { ...jsdocTypePrattParser.visitorKeys,
- ...jsdocVisitorKeys
- }
- });
- };
- };
-
- const toCamelCase = str => {
- return str.toLowerCase().replace(/^[a-z]/gu, init => {
- return init.toUpperCase();
- }).replace(/_(?<wordInit>[a-z])/gu, (_, n1, o, s, {
- wordInit
- }) => {
- return wordInit.toUpperCase();
- });
- };
-
- /* eslint-disable prefer-named-capture-group -- Temporary */
- const {
- seedBlock,
- seedTokens
- } = commentParser.util;
- const {
- name: nameTokenizer,
- tag: tagTokenizer,
- type: typeTokenizer,
- description: descriptionTokenizer
- } = commentParser.tokenizers;
- const hasSeeWithLink = spec => {
- return spec.tag === 'see' && /\{@link.+?\}/u.test(spec.source[0].source);
- };
- const defaultNoTypes = ['default', 'defaultvalue', 'see'];
- const defaultNoNames = ['access', 'author', 'default', 'defaultvalue', 'description', 'example', 'exception', 'license', 'return', 'returns', 'since', 'summary', 'throws', 'version', 'variation'];
-
- const getTokenizers = ({
- noTypes = defaultNoTypes,
- noNames = defaultNoNames
- } = {}) => {
- // trim
- return [// Tag
- tagTokenizer(), // Type
- spec => {
- if (noTypes.includes(spec.tag)) {
- return spec;
- }
-
- return typeTokenizer()(spec);
- }, // Name
- spec => {
- if (spec.tag === 'template') {
- // const preWS = spec.postTag;
- const remainder = spec.source[0].tokens.description;
- const pos = remainder.search(/(?<![\s,])\s/u);
- const name = pos === -1 ? remainder : remainder.slice(0, pos);
- const extra = remainder.slice(pos + 1);
- let postName = '',
- description = '',
- lineEnd = '';
-
- if (pos > -1) {
- [, postName, description, lineEnd] = extra.match(/(\s*)([^\r]*)(\r)?/u);
- }
-
- spec.name = name;
- spec.optional = false;
- const {
- tokens
- } = spec.source[0];
- tokens.name = name;
- tokens.postName = postName;
- tokens.description = description;
- tokens.lineEnd = lineEnd || '';
- return spec;
- }
-
- if (noNames.includes(spec.tag) || hasSeeWithLink(spec)) {
- return spec;
- }
-
- return nameTokenizer()(spec);
- }, // Description
- spec => {
- return descriptionTokenizer('preserve')(spec);
- }];
- };
- /**
- *
- * @param {PlainObject} commentNode
- * @param {string} indent Whitespace
- * @returns {PlainObject}
- */
-
-
- const parseComment = (commentNode, indent) => {
- // Preserve JSDoc block start/end indentation.
- return commentParser.parse(`/*${commentNode.value}*/`, {
- // @see https://github.com/yavorskiy/comment-parser/issues/21
- tokenizers: getTokenizers()
- })[0] || seedBlock({
- source: [{
- number: 0,
- tokens: seedTokens({
- delimiter: '/**'
- })
- }, {
- number: 1,
- tokens: seedTokens({
- end: '*/',
- start: indent + ' '
- })
- }]
- });
- };
-
- /**
- * 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 => {
- var _node$declaration, _node$declaration$dec, _node$decorators, _node$parent, _node$parent$decorato;
-
- return (node === null || node === void 0 ? void 0 : (_node$declaration = node.declaration) === null || _node$declaration === void 0 ? void 0 : (_node$declaration$dec = _node$declaration.decorators) === null || _node$declaration$dec === void 0 ? void 0 : _node$declaration$dec[0]) || (node === null || node === void 0 ? void 0 : (_node$decorators = node.decorators) === null || _node$decorators === void 0 ? void 0 : _node$decorators[0]) || (node === null || node === void 0 ? void 0 : (_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : (_node$parent$decorato = _node$parent.decorators) === null || _node$parent$decorato === void 0 ? void 0 : _node$parent$decorato[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']);
- /**
- * 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) {
- 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);
-
- 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.
- * @param {SourceCode} sourceCode
- * @param {{maxLines: Integer, minLines: Integer}} settings
- * @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 {PlainObject} 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);
- };
-
- Object.defineProperty(exports, 'jsdocTypeVisitorKeys', {
- enumerable: true,
- get: function () {
- return jsdocTypePrattParser.visitorKeys;
- }
- });
- exports.commentHandler = commentHandler;
- exports.commentParserToESTree = commentParserToESTree;
- exports.defaultNoNames = defaultNoNames;
- exports.defaultNoTypes = defaultNoTypes;
- exports.findJSDocComment = findJSDocComment;
- exports.getDecorator = getDecorator;
- exports.getJSDocComment = getJSDocComment;
- exports.getReducedASTNode = getReducedASTNode;
- exports.getTokenizers = getTokenizers;
- exports.hasSeeWithLink = hasSeeWithLink;
- exports.jsdocVisitorKeys = jsdocVisitorKeys;
- exports.parseComment = parseComment;
- exports.toCamelCase = toCamelCase;
|