803 lines
No EOL
22 KiB
JavaScript
Executable file
803 lines
No EOL
22 KiB
JavaScript
Executable file
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.default = void 0;
|
|
|
|
var _lodash = _interopRequireDefault(require("lodash"));
|
|
|
|
var _WarnSettings = _interopRequireDefault(require("./WarnSettings"));
|
|
|
|
var _getDefaultTagStructureForMode = _interopRequireDefault(require("./getDefaultTagStructureForMode"));
|
|
|
|
var _tagNames = require("./tagNames");
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
let tagStructure;
|
|
|
|
const setTagStructure = mode => {
|
|
tagStructure = (0, _getDefaultTagStructureForMode.default)(mode);
|
|
}; // Given a nested array of property names, reduce them to a single array,
|
|
// appending the name of the root element along the way if present.
|
|
|
|
|
|
const flattenRoots = (params, root = '') => {
|
|
let hasRestElement = false;
|
|
let hasPropertyRest = false;
|
|
const rests = []; // eslint-disable-next-line unicorn/no-reduce
|
|
|
|
const names = params.reduce((acc, cur) => {
|
|
if (Array.isArray(cur)) {
|
|
let nms;
|
|
|
|
if (Array.isArray(cur[1])) {
|
|
nms = cur[1];
|
|
} else {
|
|
if (cur[1].hasRestElement) {
|
|
hasRestElement = true;
|
|
}
|
|
|
|
if (cur[1].hasPropertyRest) {
|
|
hasPropertyRest = true;
|
|
}
|
|
|
|
nms = cur[1].names;
|
|
}
|
|
|
|
const flattened = flattenRoots(nms, root ? `${root}.${cur[0]}` : cur[0]);
|
|
|
|
if (flattened.hasRestElement) {
|
|
hasRestElement = true;
|
|
}
|
|
|
|
if (flattened.hasPropertyRest) {
|
|
hasPropertyRest = true;
|
|
}
|
|
|
|
const inner = [root ? `${root}.${cur[0]}` : cur[0], ...flattened.names].filter(Boolean);
|
|
rests.push(false, ...flattened.rests);
|
|
return acc.concat(inner);
|
|
}
|
|
|
|
if (typeof cur === 'object') {
|
|
if (cur.isRestProperty) {
|
|
hasPropertyRest = true;
|
|
rests.push(true);
|
|
} else {
|
|
rests.push(false);
|
|
}
|
|
|
|
if (cur.restElement) {
|
|
hasRestElement = true;
|
|
}
|
|
|
|
acc.push(root ? `${root}.${cur.name}` : cur.name);
|
|
} else if (typeof cur !== 'undefined') {
|
|
rests.push(false);
|
|
acc.push(root ? `${root}.${cur}` : cur);
|
|
}
|
|
|
|
return acc;
|
|
}, []);
|
|
return {
|
|
hasPropertyRest,
|
|
hasRestElement,
|
|
names,
|
|
rests
|
|
};
|
|
};
|
|
|
|
const getPropertiesFromPropertySignature = propSignature => {
|
|
if (propSignature.type === 'TSIndexSignature' || propSignature.type === 'TSConstructSignatureDeclaration' || propSignature.type === 'TSCallSignatureDeclaration') {
|
|
return undefined;
|
|
}
|
|
|
|
if (propSignature.typeAnnotation && propSignature.typeAnnotation.typeAnnotation.type === 'TSTypeLiteral') {
|
|
return [propSignature.key.name, propSignature.typeAnnotation.typeAnnotation.members.map(member => {
|
|
return getPropertiesFromPropertySignature(member);
|
|
})];
|
|
}
|
|
|
|
return propSignature.key.name;
|
|
};
|
|
|
|
const getFunctionParameterNames = functionNode => {
|
|
// eslint-disable-next-line complexity
|
|
const getParamName = (param, isProperty) => {
|
|
var _param$left, _param$left3;
|
|
|
|
if (_lodash.default.has(param, 'typeAnnotation') || _lodash.default.has(param, 'left.typeAnnotation')) {
|
|
const typeAnnotation = _lodash.default.has(param, 'left.typeAnnotation') ? param.left.typeAnnotation : param.typeAnnotation;
|
|
|
|
if (typeAnnotation.typeAnnotation.type === 'TSTypeLiteral') {
|
|
const propertyNames = typeAnnotation.typeAnnotation.members.map(member => {
|
|
return getPropertiesFromPropertySignature(member);
|
|
});
|
|
const flattened = { ...flattenRoots(propertyNames),
|
|
annotationParamName: param.name
|
|
};
|
|
|
|
if (_lodash.default.has(param, 'name') || _lodash.default.has(param, 'left.name')) {
|
|
return [_lodash.default.has(param, 'left.name') ? param.left.name : param.name, flattened];
|
|
}
|
|
|
|
return [undefined, flattened];
|
|
}
|
|
}
|
|
|
|
if (_lodash.default.has(param, 'name')) {
|
|
return param.name;
|
|
}
|
|
|
|
if (_lodash.default.has(param, 'left.name')) {
|
|
return param.left.name;
|
|
}
|
|
|
|
if (param.type === 'ObjectPattern' || ((_param$left = param.left) === null || _param$left === void 0 ? void 0 : _param$left.type) === 'ObjectPattern') {
|
|
var _param$left2;
|
|
|
|
const properties = param.properties || ((_param$left2 = param.left) === null || _param$left2 === void 0 ? void 0 : _param$left2.properties);
|
|
const roots = properties.map(prop => {
|
|
return getParamName(prop, true);
|
|
});
|
|
return [undefined, flattenRoots(roots)];
|
|
}
|
|
|
|
if (param.type === 'Property') {
|
|
if (param.value.type === 'ArrayPattern') {
|
|
return [param.key.name, param.value.elements.map((prop, idx) => {
|
|
return {
|
|
name: idx,
|
|
restElement: prop.type === 'RestElement'
|
|
};
|
|
})];
|
|
}
|
|
|
|
if (param.value.type === 'ObjectPattern') {
|
|
return [param.key.name, param.value.properties.map(prop => {
|
|
return getParamName(prop, isProperty);
|
|
})];
|
|
}
|
|
|
|
if (param.value.type === 'AssignmentPattern') {
|
|
if (param.value.left.type === 'ObjectPattern') {
|
|
return [param.key.name, param.value.left.properties.map(prop => {
|
|
return getParamName(prop, isProperty);
|
|
})];
|
|
}
|
|
|
|
if (param.value.left.type === 'ArrayPattern') {
|
|
return [param.key.name, param.value.left.elements.map((prop, idx) => {
|
|
return {
|
|
name: idx,
|
|
restElement: prop.type === 'RestElement'
|
|
};
|
|
})];
|
|
}
|
|
} // As function parameters, these do not allow dynamic properties, etc.
|
|
|
|
/* istanbul ignore else */
|
|
|
|
|
|
if (param.key.type === 'Identifier') {
|
|
return param.key.name;
|
|
} // The key of an object could also be a string or number
|
|
|
|
/* istanbul ignore else */
|
|
|
|
|
|
if (param.key.type === 'Literal') {
|
|
return param.key.raw || // istanbul ignore next -- `raw` may not be present in all parsers
|
|
param.key.value;
|
|
}
|
|
}
|
|
|
|
if (param.type === 'ArrayPattern' || ((_param$left3 = param.left) === null || _param$left3 === void 0 ? void 0 : _param$left3.type) === 'ArrayPattern') {
|
|
var _param$left4;
|
|
|
|
const elements = param.elements || ((_param$left4 = param.left) === null || _param$left4 === void 0 ? void 0 : _param$left4.elements);
|
|
const roots = elements.map((prop, idx) => {
|
|
return {
|
|
name: idx,
|
|
restElement: prop.type === 'RestElement'
|
|
};
|
|
});
|
|
return [undefined, flattenRoots(roots)];
|
|
}
|
|
|
|
if (['RestElement', 'ExperimentalRestProperty'].includes(param.type)) {
|
|
return {
|
|
isRestProperty: isProperty,
|
|
name: param.argument.name,
|
|
restElement: true
|
|
};
|
|
}
|
|
|
|
if (param.type === 'TSParameterProperty') {
|
|
return getParamName(param.parameter, true);
|
|
}
|
|
|
|
throw new Error('Unsupported function signature format.');
|
|
};
|
|
|
|
return (functionNode.params || functionNode.value.params).map(param => {
|
|
return getParamName(param);
|
|
});
|
|
};
|
|
|
|
const hasParams = functionNode => {
|
|
// Should also check `functionNode.value.params` if supporting `MethodDefinition`
|
|
return functionNode.params.length;
|
|
};
|
|
/**
|
|
* Gets all names of the target type, including those that refer to a path, e.g.
|
|
* "@param foo; @param foo.bar".
|
|
*/
|
|
|
|
|
|
const getJsdocTagsDeep = (jsdoc, targetTagName) => {
|
|
const ret = [];
|
|
(jsdoc.tags || []).forEach(({
|
|
name,
|
|
tag,
|
|
type
|
|
}, idx) => {
|
|
if (tag !== targetTagName) {
|
|
return;
|
|
}
|
|
|
|
ret.push({
|
|
idx,
|
|
name,
|
|
type
|
|
});
|
|
});
|
|
return ret;
|
|
};
|
|
|
|
const modeWarnSettings = (0, _WarnSettings.default)();
|
|
|
|
const getTagNamesForMode = (mode, context) => {
|
|
switch (mode) {
|
|
case 'jsdoc':
|
|
return _tagNames.jsdocTags;
|
|
|
|
case 'typescript':
|
|
return _tagNames.typeScriptTags;
|
|
|
|
case 'closure':
|
|
case 'permissive':
|
|
return _tagNames.closureTags;
|
|
|
|
default:
|
|
if (!modeWarnSettings.hasBeenWarned(context, 'mode')) {
|
|
context.report({
|
|
loc: {
|
|
start: {
|
|
column: 1,
|
|
line: 1
|
|
}
|
|
},
|
|
message: `Unrecognized value \`${mode}\` for \`settings.jsdoc.mode\`.`
|
|
});
|
|
modeWarnSettings.markSettingAsWarned(context, 'mode');
|
|
} // We'll avoid breaking too many other rules
|
|
|
|
|
|
return _tagNames.jsdocTags;
|
|
}
|
|
};
|
|
|
|
const getPreferredTagName = (context, mode, name, tagPreference = {}) => {
|
|
var _Object$entries$find;
|
|
|
|
const prefValues = Object.values(tagPreference);
|
|
|
|
if (prefValues.includes(name) || prefValues.some(prefVal => {
|
|
return prefVal && typeof prefVal === 'object' && prefVal.replacement === name;
|
|
})) {
|
|
return name;
|
|
} // Allow keys to have a 'tag ' prefix to avoid upstream bug in ESLint
|
|
// that disallows keys that conflict with Object.prototype,
|
|
// e.g. 'tag constructor' for 'constructor':
|
|
// https://github.com/eslint/eslint/issues/13289
|
|
// https://github.com/gajus/eslint-plugin-jsdoc/issues/537
|
|
|
|
|
|
const tagPreferenceFixed = _lodash.default.mapKeys(tagPreference, (_value, key) => {
|
|
return key.replace('tag ', '');
|
|
});
|
|
|
|
if (_lodash.default.has(tagPreferenceFixed, name)) {
|
|
return tagPreferenceFixed[name];
|
|
}
|
|
|
|
const tagNames = getTagNamesForMode(mode, context);
|
|
const preferredTagName = (_Object$entries$find = Object.entries(tagNames).find(([, aliases]) => {
|
|
return aliases.includes(name);
|
|
})) === null || _Object$entries$find === void 0 ? void 0 : _Object$entries$find[0];
|
|
|
|
if (preferredTagName) {
|
|
return preferredTagName;
|
|
}
|
|
|
|
return name;
|
|
};
|
|
|
|
const isValidTag = (context, mode, name, definedTags) => {
|
|
const tagNames = getTagNamesForMode(mode, context);
|
|
const validTagNames = Object.keys(tagNames).concat(_lodash.default.flatten(Object.values(tagNames)));
|
|
const additionalTags = definedTags;
|
|
const allTags = validTagNames.concat(additionalTags);
|
|
return allTags.includes(name);
|
|
};
|
|
|
|
const hasTag = (jsdoc, targetTagName) => {
|
|
const targetTagLower = targetTagName.toLowerCase();
|
|
return _lodash.default.some(jsdoc.tags, doc => {
|
|
return doc.tag.toLowerCase() === targetTagLower;
|
|
});
|
|
};
|
|
|
|
const hasATag = (jsdoc, targetTagNames) => {
|
|
return targetTagNames.some(targetTagName => {
|
|
return hasTag(jsdoc, targetTagName);
|
|
});
|
|
};
|
|
/**
|
|
* Checks if the JSDoc comment declares a return value.
|
|
*
|
|
* @param {JsDocTag} tag
|
|
* the tag which should be checked.
|
|
* @returns {boolean}
|
|
* true in case a return value is declared; otherwise false.
|
|
*/
|
|
|
|
|
|
const hasDefinedTypeReturnTag = tag => {
|
|
// The function should not continue in the event @returns is not defined...
|
|
if (typeof tag === 'undefined' || tag === null) {
|
|
return false;
|
|
} // .. same applies if it declares `@returns {undefined}` or `@returns {void}`
|
|
|
|
|
|
const tagType = tag.type.trim();
|
|
|
|
if (tagType === 'undefined' || tagType === 'void') {
|
|
return false;
|
|
} // In any other case, something must be returned, and
|
|
// a return statement is expected
|
|
|
|
|
|
return true;
|
|
};
|
|
|
|
const ensureMap = (map, tag) => {
|
|
if (!map.has(tag)) {
|
|
map.set(tag, new Map());
|
|
}
|
|
|
|
return map.get(tag);
|
|
};
|
|
|
|
const overrideTagStructure = (structuredTags, tagMap = tagStructure) => {
|
|
Object.entries(structuredTags).forEach(([tag, {
|
|
name,
|
|
type,
|
|
required = []
|
|
}]) => {
|
|
const tagStruct = ensureMap(tagMap, tag);
|
|
tagStruct.set('nameContents', name);
|
|
tagStruct.set('typeAllowed', type);
|
|
const requiredName = required.includes('name');
|
|
|
|
if (requiredName && name === false) {
|
|
throw new Error('Cannot add "name" to `require` with the tag\'s `name` set to `false`');
|
|
}
|
|
|
|
tagStruct.set('nameRequired', requiredName);
|
|
const requiredType = required.includes('type');
|
|
|
|
if (requiredType && type === false) {
|
|
throw new Error('Cannot add "type" to `require` with the tag\'s `type` set to `false`');
|
|
}
|
|
|
|
tagStruct.set('typeRequired', requiredType);
|
|
const typeOrNameRequired = required.includes('typeOrNameRequired');
|
|
|
|
if (typeOrNameRequired && name === false) {
|
|
throw new Error('Cannot add "typeOrNameRequired" to `require` with the tag\'s `name` set to `false`');
|
|
}
|
|
|
|
if (typeOrNameRequired && type === false) {
|
|
throw new Error('Cannot add "typeOrNameRequired" to `require` with the tag\'s `type` set to `false`');
|
|
}
|
|
|
|
tagStruct.set('typeOrNameRequired', typeOrNameRequired);
|
|
});
|
|
};
|
|
|
|
const getTagStructureForMode = (mode, structuredTags) => {
|
|
const tagStruct = (0, _getDefaultTagStructureForMode.default)(mode);
|
|
|
|
try {
|
|
overrideTagStructure(structuredTags, tagStruct);
|
|
} catch {//
|
|
}
|
|
|
|
return tagStruct;
|
|
};
|
|
|
|
const isNamepathDefiningTag = (tag, tagMap = tagStructure) => {
|
|
const tagStruct = ensureMap(tagMap, tag);
|
|
return tagStruct.get('nameContents') === 'namepath-defining';
|
|
};
|
|
|
|
const tagMustHaveTypePosition = (tag, tagMap = tagStructure) => {
|
|
const tagStruct = ensureMap(tagMap, tag);
|
|
return tagStruct.get('typeRequired');
|
|
};
|
|
|
|
const tagMightHaveTypePosition = (tag, tagMap = tagStructure) => {
|
|
if (tagMustHaveTypePosition(tag, tagMap)) {
|
|
return true;
|
|
}
|
|
|
|
const tagStruct = ensureMap(tagMap, tag);
|
|
const ret = tagStruct.get('typeAllowed');
|
|
return ret === undefined ? true : ret;
|
|
};
|
|
|
|
const namepathTypes = new Set(['namepath-defining', 'namepath-referencing']);
|
|
|
|
const tagMightHaveNamePosition = (tag, tagMap = tagStructure) => {
|
|
const tagStruct = ensureMap(tagMap, tag);
|
|
const ret = tagStruct.get('nameContents');
|
|
return ret === undefined ? true : Boolean(ret);
|
|
};
|
|
|
|
const tagMightHaveNamepath = (tag, tagMap = tagStructure) => {
|
|
const tagStruct = ensureMap(tagMap, tag);
|
|
return namepathTypes.has(tagStruct.get('nameContents'));
|
|
};
|
|
|
|
const tagMustHaveNamePosition = (tag, tagMap = tagStructure) => {
|
|
const tagStruct = ensureMap(tagMap, tag);
|
|
return tagStruct.get('nameRequired');
|
|
};
|
|
|
|
const tagMightHaveEitherTypeOrNamePosition = (tag, tagMap) => {
|
|
return tagMightHaveTypePosition(tag, tagMap) || tagMightHaveNamepath(tag, tagMap);
|
|
};
|
|
|
|
const tagMustHaveEitherTypeOrNamePosition = (tag, tagMap) => {
|
|
const tagStruct = ensureMap(tagMap, tag);
|
|
return tagStruct.get('typeOrNameRequired');
|
|
};
|
|
|
|
const tagMissingRequiredTypeOrNamepath = (tag, tagMap = tagStructure) => {
|
|
const mustHaveTypePosition = tagMustHaveTypePosition(tag.tag, tagMap);
|
|
const mightHaveTypePosition = tagMightHaveTypePosition(tag.tag, tagMap);
|
|
const hasTypePosition = mightHaveTypePosition && Boolean(tag.type);
|
|
const hasNameOrNamepathPosition = (tagMustHaveNamePosition(tag.tag, tagMap) || tagMightHaveNamepath(tag.tag, tagMap)) && Boolean(tag.name);
|
|
const mustHaveEither = tagMustHaveEitherTypeOrNamePosition(tag.tag, tagMap);
|
|
const hasEither = tagMightHaveEitherTypeOrNamePosition(tag.tag, tagMap) && (hasTypePosition || hasNameOrNamepathPosition);
|
|
return mustHaveEither && !hasEither && !mustHaveTypePosition;
|
|
};
|
|
/**
|
|
* Checks if a node has a return statement. Void return does not count.
|
|
*
|
|
* @param {object} node
|
|
* @returns {boolean}
|
|
*/
|
|
// eslint-disable-next-line complexity
|
|
|
|
|
|
const hasReturnValue = node => {
|
|
if (!node) {
|
|
return false;
|
|
}
|
|
|
|
switch (node.type) {
|
|
case 'FunctionExpression':
|
|
case 'FunctionDeclaration':
|
|
case 'ArrowFunctionExpression':
|
|
{
|
|
return node.expression || hasReturnValue(node.body);
|
|
}
|
|
|
|
case 'BlockStatement':
|
|
{
|
|
return node.body.some(bodyNode => {
|
|
return bodyNode.type !== 'FunctionDeclaration' && hasReturnValue(bodyNode);
|
|
});
|
|
}
|
|
|
|
case 'WhileStatement':
|
|
case 'DoWhileStatement':
|
|
case 'ForStatement':
|
|
case 'ForInStatement':
|
|
case 'ForOfStatement':
|
|
case 'WithStatement':
|
|
{
|
|
return hasReturnValue(node.body);
|
|
}
|
|
|
|
case 'IfStatement':
|
|
{
|
|
return hasReturnValue(node.consequent) || hasReturnValue(node.alternate);
|
|
}
|
|
|
|
case 'TryStatement':
|
|
{
|
|
return hasReturnValue(node.block) || hasReturnValue(node.handler && node.handler.body) || hasReturnValue(node.finalizer);
|
|
}
|
|
|
|
case 'SwitchStatement':
|
|
{
|
|
return node.cases.some(someCase => {
|
|
return someCase.consequent.some(hasReturnValue);
|
|
});
|
|
}
|
|
|
|
case 'ReturnStatement':
|
|
{
|
|
// void return does not count.
|
|
if (node.argument === null) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Checks if a node has a throws statement.
|
|
*
|
|
* @param {object} node
|
|
* @param {boolean} innerFunction
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
const hasThrowValue = (node, innerFunction) => {
|
|
if (!node) {
|
|
return false;
|
|
}
|
|
|
|
switch (node.type) {
|
|
case 'FunctionExpression':
|
|
case 'FunctionDeclaration':
|
|
case 'ArrowFunctionExpression':
|
|
{
|
|
return !innerFunction && hasThrowValue(node.body, true);
|
|
}
|
|
|
|
case 'BlockStatement':
|
|
{
|
|
return node.body.some(bodyNode => {
|
|
return bodyNode.type !== 'FunctionDeclaration' && hasThrowValue(bodyNode);
|
|
});
|
|
}
|
|
|
|
case 'WhileStatement':
|
|
case 'DoWhileStatement':
|
|
case 'ForStatement':
|
|
case 'ForInStatement':
|
|
case 'ForOfStatement':
|
|
case 'WithStatement':
|
|
{
|
|
return hasThrowValue(node.body);
|
|
}
|
|
|
|
case 'IfStatement':
|
|
{
|
|
return hasThrowValue(node.consequent) || hasThrowValue(node.alternate);
|
|
}
|
|
// We only consider it to throw an error if the catch or finally blocks throw an error.
|
|
|
|
case 'TryStatement':
|
|
{
|
|
return hasThrowValue(node.handler && node.handler.body) || hasThrowValue(node.finalizer);
|
|
}
|
|
|
|
case 'SwitchStatement':
|
|
{
|
|
return node.cases.some(someCase => {
|
|
return someCase.consequent.some(hasThrowValue);
|
|
});
|
|
}
|
|
|
|
case 'ThrowStatement':
|
|
{
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
/** @param {string} tag */
|
|
|
|
/*
|
|
const isInlineTag = (tag) => {
|
|
return /^(@link|@linkcode|@linkplain|@tutorial) /u.test(tag);
|
|
};
|
|
*/
|
|
|
|
/**
|
|
* Parses GCC Generic/Template types
|
|
*
|
|
* @see {https://github.com/google/closure-compiler/wiki/Generic-Types}
|
|
* @see {https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#template}
|
|
* @param {JsDocTag} tag
|
|
* @returns {Array<string>}
|
|
*/
|
|
|
|
|
|
const parseClosureTemplateTag = tag => {
|
|
return tag.name.split(',').map(type => {
|
|
return type.trim();
|
|
});
|
|
};
|
|
/**
|
|
* Checks user option for `contexts` array, defaulting to
|
|
* contexts designated by the rule. Returns an array of
|
|
* ESTree AST types, indicating allowable contexts.
|
|
*
|
|
* @param {*} context
|
|
* @param {true|string[]} defaultContexts
|
|
* @returns {string[]}
|
|
*/
|
|
|
|
|
|
const enforcedContexts = (context, defaultContexts) => {
|
|
const {
|
|
contexts = defaultContexts === true ? ['ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression'] : defaultContexts
|
|
} = context.options[0] || {};
|
|
return contexts;
|
|
};
|
|
/**
|
|
* @param {string[]} contexts
|
|
* @param {Function} checkJsdoc
|
|
*/
|
|
|
|
|
|
const getContextObject = (contexts, checkJsdoc) => {
|
|
const properties = {};
|
|
contexts.forEach(prop => {
|
|
if (typeof prop === 'object') {
|
|
properties[prop.context] = checkJsdoc;
|
|
} else {
|
|
properties[prop] = checkJsdoc;
|
|
}
|
|
});
|
|
return properties;
|
|
};
|
|
|
|
const filterTags = (tags = [], filter) => {
|
|
return tags.filter(filter);
|
|
};
|
|
|
|
const tagsWithNamesAndDescriptions = new Set(['param', 'arg', 'argument', 'property', 'prop', 'template', // These two are parsed by our custom parser as though having a `name`
|
|
'returns', 'return']);
|
|
|
|
const getTagsByType = (context, mode, tags, tagPreference) => {
|
|
const descName = getPreferredTagName(context, mode, 'description', tagPreference);
|
|
const tagsWithoutNames = [];
|
|
const tagsWithNames = filterTags(tags, tag => {
|
|
const {
|
|
tag: tagName
|
|
} = tag;
|
|
const tagWithName = tagsWithNamesAndDescriptions.has(tagName);
|
|
|
|
if (!tagWithName && tagName !== descName) {
|
|
tagsWithoutNames.push(tag);
|
|
}
|
|
|
|
return tagWithName;
|
|
});
|
|
return {
|
|
tagsWithNames,
|
|
tagsWithoutNames
|
|
};
|
|
};
|
|
|
|
const getIndent = sourceCode => {
|
|
let indent = sourceCode.text.match(/^\n*([ \t]+)/u);
|
|
indent = indent ? indent[1] + ' ' : ' ';
|
|
return indent;
|
|
};
|
|
|
|
const isConstructor = node => {
|
|
var _node$parent;
|
|
|
|
return (node === null || node === void 0 ? void 0 : node.type) === 'MethodDefinition' && node.kind === 'constructor' || (node === null || node === void 0 ? void 0 : (_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.kind) === 'constructor';
|
|
};
|
|
|
|
const isGetter = node => {
|
|
return node && node.parent.kind === 'get';
|
|
};
|
|
|
|
const isSetter = node => {
|
|
return node && node.parent.kind === 'set';
|
|
};
|
|
|
|
const exemptSpeciaMethods = (jsdoc, node, context, schema) => {
|
|
const hasSchemaOption = prop => {
|
|
var _context$options$0$pr, _context$options$;
|
|
|
|
const schemaProperties = schema[0].properties;
|
|
return (_context$options$0$pr = (_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$[prop]) !== null && _context$options$0$pr !== void 0 ? _context$options$0$pr : schemaProperties[prop] && schemaProperties[prop].default;
|
|
};
|
|
|
|
return !hasSchemaOption('checkConstructors') && (isConstructor(node) || hasATag(jsdoc, ['class', 'constructor'])) || !hasSchemaOption('checkGetters') && isGetter(node) || !hasSchemaOption('checkSetters') && isSetter(node);
|
|
};
|
|
/**
|
|
* Since path segments may be unquoted (if matching a reserved word,
|
|
* identifier or numeric literal) or single or double quoted, in either
|
|
* the `@param` or in source, we need to strip the quotes to give a fair
|
|
* comparison.
|
|
*
|
|
* @param {string} str
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
const dropPathSegmentQuotes = str => {
|
|
return str.replace(/\.(['"])(.*)\1/gu, '.$2');
|
|
};
|
|
|
|
const comparePaths = name => {
|
|
return otherPathName => {
|
|
return otherPathName === name || dropPathSegmentQuotes(otherPathName) === dropPathSegmentQuotes(name);
|
|
};
|
|
};
|
|
|
|
var _default = {
|
|
comparePaths,
|
|
dropPathSegmentQuotes,
|
|
enforcedContexts,
|
|
exemptSpeciaMethods,
|
|
filterTags,
|
|
flattenRoots,
|
|
getContextObject,
|
|
getFunctionParameterNames,
|
|
getIndent,
|
|
getJsdocTagsDeep,
|
|
getPreferredTagName,
|
|
getTagsByType,
|
|
getTagStructureForMode,
|
|
hasATag,
|
|
hasDefinedTypeReturnTag,
|
|
hasParams,
|
|
hasReturnValue,
|
|
hasTag,
|
|
hasThrowValue,
|
|
isConstructor,
|
|
isGetter,
|
|
isNamepathDefiningTag,
|
|
isSetter,
|
|
isValidTag,
|
|
overrideTagStructure,
|
|
parseClosureTemplateTag,
|
|
setTagStructure,
|
|
tagMightHaveNamepath,
|
|
tagMightHaveNamePosition,
|
|
tagMightHaveTypePosition,
|
|
tagMissingRequiredTypeOrNamepath,
|
|
tagMustHaveNamePosition,
|
|
tagMustHaveTypePosition
|
|
};
|
|
exports.default = _default;
|
|
module.exports = exports.default;
|
|
//# sourceMappingURL=jsdocUtils.js.map
|