797 lines
No EOL
24 KiB
JavaScript
Executable file
797 lines
No EOL
24 KiB
JavaScript
Executable file
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.default = iterateJsdoc;
|
|
exports.parseComment = exports.getSettings = void 0;
|
|
|
|
var _commentParser = _interopRequireWildcard(require("comment-parser"));
|
|
|
|
var _lodash = _interopRequireDefault(require("lodash"));
|
|
|
|
var _getJSDocComment = require("./eslint/getJSDocComment");
|
|
|
|
var _jsdocUtils = _interopRequireDefault(require("./jsdocUtils"));
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
|
|
|
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
|
|
const globalState = new Map();
|
|
|
|
const skipSeeLink = parser => {
|
|
return (str, data) => {
|
|
if (data.tag === 'see' && /\{@link.+?\}/u.test(str)) {
|
|
return null;
|
|
}
|
|
|
|
return parser(str, data);
|
|
};
|
|
};
|
|
/**
|
|
*
|
|
* @param {object} commentNode
|
|
* @param {string} indent Whitespace
|
|
* @param {boolean} [trim=true]
|
|
* @returns {object}
|
|
*/
|
|
|
|
|
|
const parseComment = (commentNode, indent, trim = true) => {
|
|
// Preserve JSDoc block start/end indentation.
|
|
return (0, _commentParser.default)(`${indent}/*${commentNode.value}${indent}*/`, {
|
|
// @see https://github.com/yavorskiy/comment-parser/issues/21
|
|
parsers: [_commentParser.default.PARSERS.parse_tag, skipSeeLink((str, data) => {
|
|
if (['default', 'defaultvalue'].includes(data.tag)) {
|
|
return null;
|
|
}
|
|
|
|
return _commentParser.default.PARSERS.parse_type(str, data);
|
|
}), skipSeeLink((str, data) => {
|
|
if (data.tag === 'template') {
|
|
var _str$match$0$length, _str$match;
|
|
|
|
const preWS = (_str$match$0$length = (_str$match = str.match(/^[\t ]+/)) === null || _str$match === void 0 ? void 0 : _str$match[0].length) !== null && _str$match$0$length !== void 0 ? _str$match$0$length : 0;
|
|
const remainder = str.slice(preWS);
|
|
const pos = remainder.search(/(?<![\s,])\s/);
|
|
const name = pos === -1 ? remainder : remainder.slice(0, pos);
|
|
return {
|
|
data: {
|
|
name,
|
|
optional: false
|
|
},
|
|
source: str.slice(0, preWS + pos)
|
|
};
|
|
}
|
|
|
|
if (['example', 'return', 'returns', 'throws', 'exception', 'access', 'version', 'since', 'license', 'author', 'default', 'defaultvalue'].includes(data.tag)) {
|
|
return null;
|
|
}
|
|
|
|
return _commentParser.default.PARSERS.parse_name(str, data);
|
|
}), // parse_description
|
|
(str, data) => {
|
|
// Only expected throw in previous step is if bad name (i.e.,
|
|
// missing end bracket on optional name), but `@example`
|
|
// skips name parsing
|
|
|
|
/* istanbul ignore next */
|
|
if (data.errors && data.errors.length) {
|
|
return null;
|
|
} // Tweak original regex to capture only single optional space
|
|
|
|
|
|
const result = str.match(/^ ?((.|\s)+)?/u); // Always has at least whitespace due to `indent` we've added
|
|
|
|
/* istanbul ignore next */
|
|
|
|
if (result) {
|
|
return {
|
|
data: {
|
|
description: result[1] === undefined ? '' : result[1]
|
|
},
|
|
source: result[0]
|
|
};
|
|
} // Always has at least whitespace due to `indent` we've added
|
|
|
|
/* istanbul ignore next */
|
|
|
|
|
|
return null;
|
|
}],
|
|
trim
|
|
})[0] || {};
|
|
};
|
|
|
|
exports.parseComment = parseComment;
|
|
|
|
const getBasicUtils = (context, {
|
|
tagNamePreference,
|
|
mode
|
|
}) => {
|
|
const utils = {};
|
|
|
|
utils.reportSettings = message => {
|
|
context.report({
|
|
loc: {
|
|
start: {
|
|
column: 1,
|
|
line: 1
|
|
}
|
|
},
|
|
message
|
|
});
|
|
};
|
|
|
|
utils.parseClosureTemplateTag = tag => {
|
|
return _jsdocUtils.default.parseClosureTemplateTag(tag);
|
|
};
|
|
|
|
utils.getPreferredTagNameObject = ({
|
|
tagName
|
|
}) => {
|
|
const ret = _jsdocUtils.default.getPreferredTagName(context, mode, tagName, tagNamePreference);
|
|
|
|
const isObject = ret && typeof ret === 'object';
|
|
|
|
if (ret === false || isObject && !ret.replacement) {
|
|
return {
|
|
blocked: true,
|
|
tagName
|
|
};
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
return utils;
|
|
};
|
|
|
|
const getUtils = (node, jsdoc, jsdocNode, settings, report, context, iteratingAll, ruleConfig) => {
|
|
const ancestors = context.getAncestors();
|
|
const sourceCode = context.getSourceCode();
|
|
const utils = getBasicUtils(context, settings);
|
|
const {
|
|
tagNamePreference,
|
|
overrideReplacesDocs,
|
|
implementsReplacesDocs,
|
|
augmentsExtendsReplacesDocs,
|
|
maxLines,
|
|
minLines,
|
|
mode
|
|
} = settings;
|
|
|
|
utils.isIteratingFunction = () => {
|
|
return !iteratingAll || ['MethodDefinition', 'ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression'].includes(node && node.type);
|
|
};
|
|
|
|
utils.isVirtualFunction = () => {
|
|
return iteratingAll && utils.hasATag(['callback', 'function', 'func', 'method']);
|
|
};
|
|
|
|
utils.stringify = (tagBlock, tag) => {
|
|
const indent = tag ? _jsdocUtils.default.getIndent({
|
|
text: sourceCode.getText(tag, tag.loc.start.column)
|
|
}) : _jsdocUtils.default.getIndent(sourceCode);
|
|
|
|
if (ruleConfig.noTrim) {
|
|
const lastTag = tagBlock.tags[tagBlock.tags.length - 1];
|
|
lastTag.description = lastTag.description.replace(/\n$/, '');
|
|
}
|
|
|
|
return (0, _commentParser.stringify)([tagBlock], {
|
|
indent
|
|
}).slice(indent.length - 1);
|
|
};
|
|
|
|
utils.reportJSDoc = (msg, tag, handler) => {
|
|
report(msg, handler ? fixer => {
|
|
handler();
|
|
const replacement = utils.stringify(jsdoc, node);
|
|
return fixer.replaceText(jsdocNode, replacement);
|
|
} : null, tag);
|
|
};
|
|
|
|
utils.flattenRoots = params => {
|
|
return _jsdocUtils.default.flattenRoots(params);
|
|
};
|
|
|
|
utils.getFunctionParameterNames = () => {
|
|
return _jsdocUtils.default.getFunctionParameterNames(node);
|
|
};
|
|
|
|
utils.hasParams = () => {
|
|
return _jsdocUtils.default.hasParams(node);
|
|
};
|
|
|
|
utils.isConstructor = () => {
|
|
return _jsdocUtils.default.isConstructor(node);
|
|
};
|
|
|
|
utils.getJsdocTagsDeep = tagName => {
|
|
const name = utils.getPreferredTagName({
|
|
tagName
|
|
});
|
|
|
|
if (!name) {
|
|
return false;
|
|
}
|
|
|
|
return _jsdocUtils.default.getJsdocTagsDeep(jsdoc, name);
|
|
};
|
|
|
|
utils.getPreferredTagName = ({
|
|
tagName,
|
|
skipReportingBlockedTag = false,
|
|
allowObjectReturn = false,
|
|
defaultMessage = `Unexpected tag \`@${tagName}\``
|
|
}) => {
|
|
const ret = _jsdocUtils.default.getPreferredTagName(context, mode, tagName, tagNamePreference);
|
|
|
|
const isObject = ret && typeof ret === 'object';
|
|
|
|
if (utils.hasTag(tagName) && (ret === false || isObject && !ret.replacement)) {
|
|
if (skipReportingBlockedTag) {
|
|
return {
|
|
blocked: true,
|
|
tagName
|
|
};
|
|
}
|
|
|
|
const message = isObject && ret.message || defaultMessage;
|
|
report(message, null, utils.getTags(tagName)[0]);
|
|
return false;
|
|
}
|
|
|
|
return isObject && !allowObjectReturn ? ret.replacement : ret;
|
|
};
|
|
|
|
utils.isValidTag = (name, definedTags) => {
|
|
return _jsdocUtils.default.isValidTag(context, mode, name, definedTags);
|
|
};
|
|
|
|
utils.hasATag = name => {
|
|
return _jsdocUtils.default.hasATag(jsdoc, name);
|
|
};
|
|
|
|
utils.hasTag = name => {
|
|
return _jsdocUtils.default.hasTag(jsdoc, name);
|
|
};
|
|
|
|
utils.comparePaths = name => {
|
|
return _jsdocUtils.default.comparePaths(name);
|
|
};
|
|
|
|
utils.dropPathSegmentQuotes = name => {
|
|
return _jsdocUtils.default.dropPathSegmentQuotes(name);
|
|
};
|
|
|
|
utils.avoidDocs = () => {
|
|
var _context$options$0$ex, _context$options$;
|
|
|
|
if (overrideReplacesDocs !== false && (utils.hasTag('override') || utils.classHasTag('override')) || implementsReplacesDocs !== false && (utils.hasTag('implements') || utils.classHasTag('implements')) || augmentsExtendsReplacesDocs && (utils.hasATag(['augments', 'extends']) || utils.classHasTag('augments') || utils.classHasTag('extends'))) {
|
|
return true;
|
|
}
|
|
|
|
if (_jsdocUtils.default.exemptSpeciaMethods(jsdoc, node, context, ruleConfig.meta.schema)) {
|
|
return true;
|
|
}
|
|
|
|
const exemptedBy = (_context$options$0$ex = (_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$.exemptedBy) !== null && _context$options$0$ex !== void 0 ? _context$options$0$ex : ['inheritDoc', ...(mode === 'closure' ? [] : ['inheritdoc'])];
|
|
|
|
if (exemptedBy.length && utils.getPresentTags(exemptedBy).length) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
['tagMightHaveNamePosition', 'tagMightHaveTypePosition'].forEach(method => {
|
|
utils[method] = (tagName, otherModeMaps) => {
|
|
const result = _jsdocUtils.default[method](tagName);
|
|
|
|
if (result) {
|
|
return true;
|
|
}
|
|
|
|
if (!otherModeMaps) {
|
|
return false;
|
|
}
|
|
|
|
const otherResult = otherModeMaps.some(otherModeMap => {
|
|
return _jsdocUtils.default[method](tagName, otherModeMap);
|
|
});
|
|
return otherResult ? {
|
|
otherMode: true
|
|
} : false;
|
|
};
|
|
});
|
|
['tagMustHaveNamePosition', 'tagMustHaveTypePosition', 'tagMissingRequiredTypeOrNamepath'].forEach(method => {
|
|
utils[method] = (tagName, otherModeMaps) => {
|
|
const result = _jsdocUtils.default[method](tagName);
|
|
|
|
if (!result) {
|
|
return false;
|
|
} // if (!otherModeMaps) { return true; }
|
|
|
|
|
|
const otherResult = otherModeMaps.every(otherModeMap => {
|
|
return _jsdocUtils.default[method](tagName, otherModeMap);
|
|
});
|
|
return otherResult ? true : {
|
|
otherMode: false
|
|
};
|
|
};
|
|
});
|
|
['isNamepathDefiningTag', 'tagMightHaveNamepath'].forEach(method => {
|
|
utils[method] = tagName => {
|
|
return _jsdocUtils.default[method](tagName);
|
|
};
|
|
});
|
|
|
|
utils.getTagStructureForMode = mde => {
|
|
return _jsdocUtils.default.getTagStructureForMode(mde, settings.structuredTags);
|
|
};
|
|
|
|
utils.hasDefinedTypeReturnTag = tag => {
|
|
return _jsdocUtils.default.hasDefinedTypeReturnTag(tag);
|
|
};
|
|
|
|
utils.hasReturnValue = () => {
|
|
return _jsdocUtils.default.hasReturnValue(node);
|
|
};
|
|
|
|
utils.hasThrowValue = () => {
|
|
return _jsdocUtils.default.hasThrowValue(node);
|
|
};
|
|
|
|
utils.isAsync = () => {
|
|
return node.async;
|
|
};
|
|
|
|
utils.getTags = tagName => {
|
|
return utils.filterTags(item => {
|
|
return item.tag === tagName;
|
|
});
|
|
};
|
|
|
|
utils.getPresentTags = tagList => {
|
|
return utils.filterTags(tag => {
|
|
return tagList.includes(tag.tag);
|
|
});
|
|
};
|
|
|
|
utils.filterTags = filter => {
|
|
return _jsdocUtils.default.filterTags(jsdoc.tags, filter);
|
|
};
|
|
|
|
utils.getTagsByType = tags => {
|
|
return _jsdocUtils.default.getTagsByType(context, mode, tags, tagNamePreference);
|
|
};
|
|
|
|
utils.hasOptionTag = tagName => {
|
|
var _context$options$2;
|
|
|
|
const {
|
|
tags
|
|
} = (_context$options$2 = context.options[0]) !== null && _context$options$2 !== void 0 ? _context$options$2 : {};
|
|
return Boolean(tags && tags.includes(tagName));
|
|
};
|
|
|
|
utils.getClassNode = () => {
|
|
return [...ancestors, node].reverse().find(parent => {
|
|
return parent && ['ClassDeclaration', 'ClassExpression'].includes(parent.type);
|
|
}) || null;
|
|
};
|
|
|
|
utils.getClassJsdoc = () => {
|
|
const classNode = utils.getClassNode();
|
|
|
|
if (!classNode) {
|
|
return null;
|
|
}
|
|
|
|
const classJsdocNode = (0, _getJSDocComment.getJSDocComment)(sourceCode, classNode, {
|
|
maxLines,
|
|
minLines
|
|
});
|
|
|
|
if (classJsdocNode) {
|
|
const indent = ' '.repeat(classJsdocNode.loc.start.column);
|
|
return parseComment(classJsdocNode, indent);
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
utils.classHasTag = tagName => {
|
|
const classJsdoc = utils.getClassJsdoc();
|
|
return Boolean(classJsdoc) && _jsdocUtils.default.hasTag(classJsdoc, tagName);
|
|
};
|
|
|
|
utils.forEachPreferredTag = (tagName, arrayHandler, skipReportingBlockedTag = false) => {
|
|
const targetTagName = utils.getPreferredTagName({
|
|
skipReportingBlockedTag,
|
|
tagName
|
|
});
|
|
|
|
if (!targetTagName || skipReportingBlockedTag && targetTagName && typeof targetTagName === 'object') {
|
|
return;
|
|
}
|
|
|
|
const matchingJsdocTags = _lodash.default.filter(jsdoc.tags || [], {
|
|
tag: targetTagName
|
|
});
|
|
|
|
matchingJsdocTags.forEach(matchingJsdocTag => {
|
|
arrayHandler(matchingJsdocTag, targetTagName);
|
|
});
|
|
};
|
|
|
|
return utils;
|
|
};
|
|
|
|
const getSettings = context => {
|
|
var _context$settings$jsd, _context$settings$jsd2, _context$settings$jsd3, _context$settings$jsd4, _context$settings$jsd5, _context$settings$jsd6, _context$settings$jsd7, _context$settings$jsd8, _context$settings$jsd9, _context$settings$jsd10, _context$settings$jsd11, _context$settings$jsd12, _context$settings$jsd13, _context$settings$jsd14, _context$settings$jsd15, _context$settings$jsd16, _context$settings$jsd17;
|
|
|
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
const settings = {
|
|
// All rules
|
|
ignorePrivate: Boolean((_context$settings$jsd = context.settings.jsdoc) === null || _context$settings$jsd === void 0 ? void 0 : _context$settings$jsd.ignorePrivate),
|
|
ignoreInternal: Boolean((_context$settings$jsd2 = context.settings.jsdoc) === null || _context$settings$jsd2 === void 0 ? void 0 : _context$settings$jsd2.ignoreInternal),
|
|
maxLines: Number((_context$settings$jsd3 = (_context$settings$jsd4 = context.settings.jsdoc) === null || _context$settings$jsd4 === void 0 ? void 0 : _context$settings$jsd4.maxLines) !== null && _context$settings$jsd3 !== void 0 ? _context$settings$jsd3 : 1),
|
|
minLines: Number((_context$settings$jsd5 = (_context$settings$jsd6 = context.settings.jsdoc) === null || _context$settings$jsd6 === void 0 ? void 0 : _context$settings$jsd6.minLines) !== null && _context$settings$jsd5 !== void 0 ? _context$settings$jsd5 : 0),
|
|
// `check-tag-names` and many returns/param rules
|
|
tagNamePreference: (_context$settings$jsd7 = (_context$settings$jsd8 = context.settings.jsdoc) === null || _context$settings$jsd8 === void 0 ? void 0 : _context$settings$jsd8.tagNamePreference) !== null && _context$settings$jsd7 !== void 0 ? _context$settings$jsd7 : {},
|
|
// `check-types` and `no-undefined-types`
|
|
preferredTypes: (_context$settings$jsd9 = (_context$settings$jsd10 = context.settings.jsdoc) === null || _context$settings$jsd10 === void 0 ? void 0 : _context$settings$jsd10.preferredTypes) !== null && _context$settings$jsd9 !== void 0 ? _context$settings$jsd9 : {},
|
|
// `check-types`, `no-undefined-types`, `valid-types`
|
|
structuredTags: (_context$settings$jsd11 = (_context$settings$jsd12 = context.settings.jsdoc) === null || _context$settings$jsd12 === void 0 ? void 0 : _context$settings$jsd12.structuredTags) !== null && _context$settings$jsd11 !== void 0 ? _context$settings$jsd11 : {},
|
|
// `require-param`, `require-description`, `require-example`, `require-returns`
|
|
overrideReplacesDocs: (_context$settings$jsd13 = context.settings.jsdoc) === null || _context$settings$jsd13 === void 0 ? void 0 : _context$settings$jsd13.overrideReplacesDocs,
|
|
implementsReplacesDocs: (_context$settings$jsd14 = context.settings.jsdoc) === null || _context$settings$jsd14 === void 0 ? void 0 : _context$settings$jsd14.implementsReplacesDocs,
|
|
augmentsExtendsReplacesDocs: (_context$settings$jsd15 = context.settings.jsdoc) === null || _context$settings$jsd15 === void 0 ? void 0 : _context$settings$jsd15.augmentsExtendsReplacesDocs,
|
|
// Many rules, e.g., `check-tag-names`
|
|
mode: (_context$settings$jsd16 = (_context$settings$jsd17 = context.settings.jsdoc) === null || _context$settings$jsd17 === void 0 ? void 0 : _context$settings$jsd17.mode) !== null && _context$settings$jsd16 !== void 0 ? _context$settings$jsd16 : context.parserPath.includes('@typescript-eslint') ? 'typescript' : 'jsdoc'
|
|
};
|
|
/* eslint-enable sort-keys-fix/sort-keys-fix */
|
|
|
|
_jsdocUtils.default.setTagStructure(settings.mode);
|
|
|
|
try {
|
|
_jsdocUtils.default.overrideTagStructure(settings.structuredTags);
|
|
} catch (error) {
|
|
context.report({
|
|
loc: {
|
|
start: {
|
|
column: 1,
|
|
line: 1
|
|
}
|
|
},
|
|
message: error.message
|
|
});
|
|
return false;
|
|
}
|
|
|
|
return settings;
|
|
};
|
|
/**
|
|
* Create the report function
|
|
*
|
|
* @param {object} context
|
|
* @param {object} commentNode
|
|
*/
|
|
|
|
|
|
exports.getSettings = getSettings;
|
|
|
|
const makeReport = (context, commentNode) => {
|
|
const report = (message, fix = null, jsdocLoc = null, data = null) => {
|
|
let loc;
|
|
|
|
if (jsdocLoc) {
|
|
const lineNumber = commentNode.loc.start.line + jsdocLoc.line;
|
|
loc = {
|
|
end: {
|
|
line: lineNumber
|
|
},
|
|
start: {
|
|
line: lineNumber
|
|
}
|
|
};
|
|
|
|
if (jsdocLoc.column) {
|
|
const colNumber = commentNode.loc.start.column + jsdocLoc.column;
|
|
loc.end.column = colNumber;
|
|
loc.start.column = colNumber;
|
|
}
|
|
}
|
|
|
|
context.report({
|
|
data,
|
|
fix,
|
|
loc,
|
|
message,
|
|
node: commentNode
|
|
});
|
|
};
|
|
|
|
return report;
|
|
};
|
|
/**
|
|
* @typedef {ReturnType<typeof getUtils>} Utils
|
|
* @typedef {ReturnType<typeof getSettings>} Settings
|
|
* @typedef {(
|
|
* arg: {
|
|
* context: object,
|
|
* sourceCode: object,
|
|
* indent: string,
|
|
* jsdoc: object,
|
|
* jsdocNode: object,
|
|
* node: object | null,
|
|
* report: ReturnType<typeof makeReport>,
|
|
* settings: Settings,
|
|
* utils: Utils,
|
|
* }
|
|
* ) => any } JsdocVisitor
|
|
*/
|
|
|
|
|
|
const iterate = (ruleConfig, context, lines, jsdocNode, node, settings, sourceCode, iterator, state, iteratingAll) => {
|
|
const sourceLine = lines[jsdocNode.loc.start.line - 1];
|
|
const indent = sourceLine.charAt(0).repeat(jsdocNode.loc.start.column);
|
|
const jsdoc = parseComment(jsdocNode, indent, !ruleConfig.noTrim);
|
|
const report = makeReport(context, jsdocNode);
|
|
const utils = getUtils(node, jsdoc, jsdocNode, settings, report, context, iteratingAll, ruleConfig);
|
|
|
|
if (!ruleConfig.checkInternal && settings.ignoreInternal && utils.hasTag('internal')) {
|
|
return;
|
|
}
|
|
|
|
if (!ruleConfig.checkPrivate && settings.ignorePrivate && (utils.hasTag('private') || _lodash.default.filter(jsdoc.tags, {
|
|
tag: 'access'
|
|
}).some(({
|
|
description
|
|
}) => {
|
|
return description === 'private';
|
|
}))) {
|
|
return;
|
|
}
|
|
|
|
iterator({
|
|
context,
|
|
globalState,
|
|
indent,
|
|
iteratingAll,
|
|
jsdoc,
|
|
jsdocNode,
|
|
node,
|
|
report,
|
|
settings,
|
|
sourceCode,
|
|
state,
|
|
utils
|
|
});
|
|
};
|
|
/**
|
|
* Create an eslint rule that iterates over all JSDocs, regardless of whether
|
|
* they are attached to a function-like node.
|
|
*
|
|
* @param {JsdocVisitor} iterator
|
|
* @param {{meta: any}} ruleConfig
|
|
*/
|
|
|
|
|
|
const iterateAllJsdocs = (iterator, ruleConfig) => {
|
|
const trackedJsdocs = [];
|
|
let settings;
|
|
|
|
const callIterator = (context, node, jsdocNodes, state, lastCall) => {
|
|
const sourceCode = context.getSourceCode();
|
|
const {
|
|
lines
|
|
} = sourceCode;
|
|
const utils = getBasicUtils(context, settings);
|
|
jsdocNodes.forEach(jsdocNode => {
|
|
if (!/^\/\*\*\s/.test(sourceCode.getText(jsdocNode))) {
|
|
return;
|
|
}
|
|
|
|
iterate(ruleConfig, context, lines, jsdocNode, node, settings, sourceCode, iterator, state, true);
|
|
});
|
|
|
|
if (lastCall && ruleConfig.exit) {
|
|
ruleConfig.exit({
|
|
context,
|
|
state,
|
|
utils
|
|
});
|
|
}
|
|
};
|
|
|
|
return {
|
|
create(context) {
|
|
const sourceCode = context.getSourceCode();
|
|
settings = getSettings(context);
|
|
|
|
if (!settings) {
|
|
return {};
|
|
}
|
|
|
|
const state = {};
|
|
return {
|
|
'*:not(Program)'(node) {
|
|
const reducedNode = (0, _getJSDocComment.getReducedASTNode)(node, sourceCode);
|
|
|
|
if (node !== reducedNode) {
|
|
return;
|
|
}
|
|
|
|
const comment = (0, _getJSDocComment.getJSDocComment)(sourceCode, node, settings);
|
|
|
|
if (trackedJsdocs.includes(comment)) {
|
|
return;
|
|
}
|
|
|
|
if (!comment) {
|
|
if (ruleConfig.nonComment) {
|
|
ruleConfig.nonComment({
|
|
node,
|
|
state
|
|
});
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
trackedJsdocs.push(comment);
|
|
callIterator(context, node, [comment], state);
|
|
},
|
|
|
|
'Program:exit'() {
|
|
const allComments = sourceCode.getAllComments();
|
|
const untrackedJSdoc = allComments.filter(node => {
|
|
return !trackedJsdocs.includes(node);
|
|
});
|
|
callIterator(context, null, untrackedJSdoc, state, true);
|
|
}
|
|
|
|
};
|
|
},
|
|
|
|
meta: ruleConfig.meta
|
|
};
|
|
};
|
|
/**
|
|
* Create an eslint rule that iterates over all JSDocs, regardless of whether
|
|
* they are attached to a function-like node.
|
|
*
|
|
* @param {JsdocVisitor} iterator
|
|
* @param {{meta: any}} ruleConfig
|
|
*/
|
|
|
|
|
|
const checkFile = (iterator, ruleConfig) => {
|
|
return {
|
|
create(context) {
|
|
const sourceCode = context.getSourceCode();
|
|
const settings = getSettings(context);
|
|
|
|
if (!settings) {
|
|
return {};
|
|
}
|
|
|
|
return {
|
|
'Program:exit'() {
|
|
const allComments = sourceCode.getAllComments();
|
|
const {
|
|
lines
|
|
} = sourceCode;
|
|
const utils = getBasicUtils(context, settings);
|
|
iterator({
|
|
allComments,
|
|
context,
|
|
lines,
|
|
makeReport,
|
|
settings,
|
|
sourceCode,
|
|
utils
|
|
});
|
|
}
|
|
|
|
};
|
|
},
|
|
|
|
meta: ruleConfig.meta
|
|
};
|
|
};
|
|
|
|
/**
|
|
* @param {JsdocVisitor} iterator
|
|
* @param {{
|
|
* meta: any,
|
|
* contextDefaults?: true | string[],
|
|
* iterateAllJsdocs?: true,
|
|
* }} ruleConfig
|
|
*/
|
|
function iterateJsdoc(iterator, ruleConfig) {
|
|
var _ruleConfig$meta;
|
|
|
|
const metaType = ruleConfig === null || ruleConfig === void 0 ? void 0 : (_ruleConfig$meta = ruleConfig.meta) === null || _ruleConfig$meta === void 0 ? void 0 : _ruleConfig$meta.type;
|
|
|
|
if (!metaType || !['problem', 'suggestion', 'layout'].includes(metaType)) {
|
|
throw new TypeError('Rule must include `meta.type` option (with value "problem", "suggestion", or "layout")');
|
|
}
|
|
|
|
if (typeof iterator !== 'function') {
|
|
throw new TypeError('The iterator argument must be a function.');
|
|
}
|
|
|
|
if (ruleConfig.checkFile) {
|
|
return checkFile(iterator, ruleConfig);
|
|
}
|
|
|
|
if (ruleConfig.iterateAllJsdocs) {
|
|
return iterateAllJsdocs(iterator, ruleConfig);
|
|
}
|
|
|
|
return {
|
|
/**
|
|
* The entrypoint for the JSDoc rule.
|
|
*
|
|
* @param {*} context
|
|
* a reference to the context which hold all important information
|
|
* like settings and the sourcecode to check.
|
|
* @returns {object}
|
|
* a list with parser callback function.
|
|
*/
|
|
create(context) {
|
|
let contexts;
|
|
|
|
if (ruleConfig.contextDefaults) {
|
|
contexts = _jsdocUtils.default.enforcedContexts(context, ruleConfig.contextDefaults);
|
|
|
|
if (contexts.includes('any')) {
|
|
return iterateAllJsdocs(iterator, ruleConfig).create(context);
|
|
}
|
|
}
|
|
|
|
const sourceCode = context.getSourceCode();
|
|
const settings = getSettings(context);
|
|
|
|
if (!settings) {
|
|
return {};
|
|
}
|
|
|
|
const {
|
|
lines
|
|
} = sourceCode;
|
|
|
|
const checkJsdoc = node => {
|
|
const jsdocNode = (0, _getJSDocComment.getJSDocComment)(sourceCode, node, settings);
|
|
|
|
if (!jsdocNode) {
|
|
return;
|
|
}
|
|
|
|
iterate(ruleConfig, context, lines, jsdocNode, node, settings, sourceCode, iterator);
|
|
};
|
|
|
|
if (ruleConfig.contextDefaults) {
|
|
return _jsdocUtils.default.getContextObject(contexts, checkJsdoc);
|
|
}
|
|
|
|
return {
|
|
ArrowFunctionExpression: checkJsdoc,
|
|
FunctionDeclaration: checkJsdoc,
|
|
FunctionExpression: checkJsdoc
|
|
};
|
|
},
|
|
|
|
meta: ruleConfig.meta
|
|
};
|
|
}
|
|
//# sourceMappingURL=iterateJsdoc.js.map
|