1537 lines
52 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.hasParams = exports.hasATag = exports.getTagsByType = exports.getTags = exports.getTagStructureForMode = exports.getTagDescription = exports.getRegexFromString = exports.getPreferredTagNameSimple = exports.getPreferredTagName = exports.getJsdocTagsDeep = exports.getIndent = exports.getFunctionParameterNames = exports.getContextObject = exports.getAllTags = exports.forEachPreferredTag = exports.flattenRoots = exports.filterTags = exports.exemptSpeciaMethods = exports.enforcedContexts = exports.dropPathSegmentQuotes = exports.comparePaths = void 0;
Object.defineProperty(exports, "hasReturnValue", {
enumerable: true,
get: function () {
return _hasReturnValue.hasReturnValue;
}
});
exports.hasThrowValue = exports.hasTag = void 0;
Object.defineProperty(exports, "hasValueOrExecutorHasNonEmptyResolveValue", {
enumerable: true,
get: function () {
return _hasReturnValue.hasValueOrExecutorHasNonEmptyResolveValue;
}
});
exports.tagMustHaveTypePosition = exports.tagMustHaveNamePosition = exports.tagMissingRequiredTypeOrNamepath = exports.tagMightHaveTypePosition = exports.tagMightHaveNamepath = exports.tagMightHaveNamePosition = exports.tagMightHaveEitherTypeOrNamePosition = exports.setTagStructure = exports.pathDoesNotBeginWith = exports.parseClosureTemplateTag = exports.overrideTagStructure = exports.mayBeUndefinedTypeTag = exports.isValidTag = exports.isSetter = exports.isNamepathReferencingTag = exports.isNamepathOrUrlReferencingTag = exports.isNamepathDefiningTag = exports.isGetter = exports.isConstructor = exports.hasYieldValue = void 0;
var _getDefaultTagStructureForMode = _interopRequireDefault(require("./getDefaultTagStructureForMode.cjs"));
var _tagNames = require("./tagNames.cjs");
var _hasReturnValue = require("./utils/hasReturnValue.cjs");
var _WarnSettings = _interopRequireDefault(require("./WarnSettings.cjs"));
var _jsdoccomment = require("@es-joy/jsdoccomment");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* @typedef {number} Integer
*/
/**
* @typedef {import('./utils/hasReturnValue.js').ESTreeOrTypeScriptNode} ESTreeOrTypeScriptNode
*/
/**
* @typedef {"jsdoc"|"typescript"|"closure"|"permissive"} ParserMode
*/
/**
* @type {import('./getDefaultTagStructureForMode.js').TagStructure}
*/
let tagStructure;
/**
* @param {ParserMode} mode
* @returns {void}
*/
const setTagStructure = mode => {
tagStructure = (0, _getDefaultTagStructureForMode.default)(mode);
};
/**
* @typedef {undefined|string|{
* name: Integer,
* restElement: boolean
* }|{
* isRestProperty: boolean|undefined,
* name: string,
* restElement: boolean
* }|{
* name: string,
* restElement: boolean
* }} ParamCommon
*/
/**
* @typedef {ParamCommon|[string|undefined, (FlattendRootInfo & {
* annotationParamName?: string,
* })]|NestedParamInfo} ParamNameInfo
*/
/**
* @typedef {{
* hasPropertyRest: boolean,
* hasRestElement: boolean,
* names: string[],
* rests: boolean[],
* }} FlattendRootInfo
*/
/**
* @typedef {[string, (string[]|ParamInfo[])]} NestedParamInfo
*/
/**
* @typedef {ParamCommon|
* [string|undefined, (FlattendRootInfo & {
* annotationParamName?: string
* })]|
* NestedParamInfo} ParamInfo
*/
/**
* 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.
* @callback FlattenRoots
* @param {ParamInfo[]} params
* @param {string} [root]
* @returns {FlattendRootInfo}
*/
/** @type {FlattenRoots} */
exports.setTagStructure = setTagStructure;
const flattenRoots = (params, root = '') => {
let hasRestElement = false;
let hasPropertyRest = false;
/**
* @type {boolean[]}
*/
const rests = [];
const names = params.reduce(
/**
* @param {string[]} acc
* @param {ParamInfo} cur
* @returns {string[]}
*/
(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 = /** @type {string[]} */[root ? `${root}.${cur[0]}` : cur[0], ...flattened.names].filter(Boolean);
rests.push(false, ...flattened.rests);
return acc.concat(inner);
}
if (typeof cur === 'object') {
if ('isRestProperty' in cur && cur.isRestProperty) {
hasPropertyRest = true;
rests.push(true);
} else {
rests.push(false);
}
if ('restElement' in cur && cur.restElement) {
hasRestElement = true;
}
acc.push(root ? `${root}.${String(cur.name)}` : String(cur.name));
} else if (typeof cur !== 'undefined') {
rests.push(false);
acc.push(root ? `${root}.${cur}` : cur);
}
return acc;
}, []);
return {
hasPropertyRest,
hasRestElement,
names,
rests
};
};
/**
* @param {import('@typescript-eslint/types').TSESTree.TSIndexSignature|
* import('@typescript-eslint/types').TSESTree.TSConstructSignatureDeclaration|
* import('@typescript-eslint/types').TSESTree.TSCallSignatureDeclaration|
* import('@typescript-eslint/types').TSESTree.TSPropertySignature} propSignature
* @returns {undefined|string|[string, string[]]}
*/
exports.flattenRoots = flattenRoots;
const getPropertiesFromPropertySignature = propSignature => {
if (propSignature.type === 'TSIndexSignature' || propSignature.type === 'TSConstructSignatureDeclaration' || propSignature.type === 'TSCallSignatureDeclaration') {
return undefined;
}
if (propSignature.typeAnnotation && propSignature.typeAnnotation.typeAnnotation.type === 'TSTypeLiteral') {
return [/** @type {import('@typescript-eslint/types').TSESTree.Identifier} */propSignature.key.name, propSignature.typeAnnotation.typeAnnotation.members.map(member => {
return /** @type {string} */(
getPropertiesFromPropertySignature( /** @type {import('@typescript-eslint/types').TSESTree.TSPropertySignature} */
member)
);
})];
}
return /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */propSignature.key.name;
};
/**
* @param {ESTreeOrTypeScriptNode|null} functionNode
* @param {boolean} [checkDefaultObjects]
* @throws {Error}
* @returns {ParamNameInfo[]}
*/
const getFunctionParameterNames = (functionNode, checkDefaultObjects) => {
var _functionNode$value;
/* eslint-disable complexity -- Temporary */
/**
* @param {import('estree').Identifier|import('estree').AssignmentPattern|
* import('estree').ObjectPattern|import('estree').Property|
* import('estree').RestElement|import('estree').ArrayPattern|
* import('@typescript-eslint/types').TSESTree.TSParameterProperty|
* import('@typescript-eslint/types').TSESTree.Property|
* import('@typescript-eslint/types').TSESTree.RestElement|
* import('@typescript-eslint/types').TSESTree.Identifier|
* import('@typescript-eslint/types').TSESTree.ObjectPattern|
* import('@typescript-eslint/types').TSESTree.BindingName|
* import('@typescript-eslint/types').TSESTree.Parameter
* } param
* @param {boolean} [isProperty]
* @returns {ParamNameInfo|[string, ParamNameInfo[]]}
*/
const getParamName = (param, isProperty) => {
var _param$left2;
/* eslint-enable complexity -- Temporary */
const hasLeftTypeAnnotation = 'left' in param && 'typeAnnotation' in param.left;
if ('typeAnnotation' in param || hasLeftTypeAnnotation) {
var _typeAnnotation$typeA;
const typeAnnotation = hasLeftTypeAnnotation ? /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */param.left.typeAnnotation : /** @type {import('@typescript-eslint/types').TSESTree.Identifier|import('@typescript-eslint/types').TSESTree.ObjectPattern} */
param.typeAnnotation;
if ((typeAnnotation === null || typeAnnotation === void 0 || (_typeAnnotation$typeA = typeAnnotation.typeAnnotation) === null || _typeAnnotation$typeA === void 0 ? void 0 : _typeAnnotation$typeA.type) === 'TSTypeLiteral') {
const propertyNames = typeAnnotation.typeAnnotation.members.map(member => {
return getPropertiesFromPropertySignature( /** @type {import('@typescript-eslint/types').TSESTree.TSPropertySignature} */
member);
});
const flattened = {
...flattenRoots(propertyNames),
annotationParamName: 'name' in param ? param.name : undefined
};
const hasLeftName = 'left' in param && 'name' in param.left;
if ('name' in param || hasLeftName) {
return [hasLeftName ? /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */param.left.name : /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */param.name, flattened];
}
return [undefined, flattened];
}
}
if ('name' in param) {
return param.name;
}
if ('left' in param && 'name' in param.left) {
return param.left.name;
}
if (param.type === 'ObjectPattern' || 'left' in param && param.left.type === 'ObjectPattern') {
var _param$left;
const properties = /** @type {import('@typescript-eslint/types').TSESTree.ObjectPattern} */param.properties || ( /** @type {import('estree').ObjectPattern} */(_param$left = /** @type {import('@typescript-eslint/types').TSESTree.AssignmentPattern} */param.left) === null || _param$left === void 0 ? void 0 : _param$left.properties);
const roots = properties.map(prop => {
return getParamName(prop, true);
});
return [undefined, flattenRoots(roots)];
}
if (param.type === 'Property') {
switch (param.value.type) {
case 'ArrayPattern':
{
return [/** @type {import('estree').Identifier} */
param.key.name, /** @type {import('estree').ArrayPattern} */param.value.elements.map((prop, idx) => {
return {
name: idx,
restElement: (prop === null || prop === void 0 ? void 0 : prop.type) === 'RestElement'
};
})];
}
case 'ObjectPattern':
{
return [/** @type {import('estree').Identifier} */param.key.name, /** @type {import('estree').ObjectPattern} */param.value.properties.map(prop => {
return /** @type {string|[string, string[]]} */getParamName(prop, isProperty);
})];
}
case 'AssignmentPattern':
{
switch (param.value.left.type) {
case 'Identifier':
// Default parameter
if (checkDefaultObjects && param.value.right.type === 'ObjectExpression') {
return [/** @type {import('estree').Identifier} */param.key.name, /** @type {import('estree').AssignmentPattern} */param.value.right.properties.map(prop => {
return /** @type {string} */getParamName( /** @type {import('estree').Property} */
prop, isProperty);
})];
}
break;
case 'ObjectPattern':
return [/** @type {import('estree').Identifier} */
param.key.name, /** @type {import('estree').ObjectPattern} */param.value.left.properties.map(prop => {
return getParamName(prop, isProperty);
})];
case 'ArrayPattern':
return [/** @type {import('estree').Identifier} */
param.key.name, /** @type {import('estree').ArrayPattern} */param.value.left.elements.map((prop, idx) => {
return {
name: idx,
restElement: (prop === null || prop === void 0 ? void 0 : prop.type) === 'RestElement'
};
})];
}
}
}
switch (param.key.type) {
case 'Identifier':
return param.key.name;
// The key of an object could also be a string or number
case 'Literal':
/* c8 ignore next 2 -- `raw` may not be present in all parsers */
return /** @type {string} */param.key.raw || param.key.value;
// case 'MemberExpression':
default:
// Todo: We should really create a structure (and a corresponding
// option analogous to `checkRestProperty`) which allows for
// (and optionally requires) dynamic properties to have a single
// line of documentation
return undefined;
}
}
if (param.type === 'ArrayPattern' || /** @type {import('estree').AssignmentPattern} */((_param$left2 = param.left) === null || _param$left2 === void 0 ? void 0 : _param$left2.type) === 'ArrayPattern') {
var _param$left3;
const elements = /** @type {import('estree').ArrayPattern} */param.elements || ( /** @type {import('estree').ArrayPattern} */(_param$left3 = /** @type {import('estree').AssignmentPattern} */param.left) === null || _param$left3 === void 0 ? void 0 : _param$left3.elements);
const roots = elements.map((prop, idx) => {
return {
name: `"${idx}"`,
restElement: (prop === null || prop === void 0 ? void 0 : prop.type) === 'RestElement'
};
});
return [undefined, flattenRoots(roots)];
}
if (['RestElement', 'ExperimentalRestProperty'].includes(param.type)) {
var _param$argument;
return {
isRestProperty: isProperty,
name: /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */( /** @type {import('@typescript-eslint/types').TSESTree.RestElement} */param
// @ts-expect-error Ok
.argument).name ?? (param === null || param === void 0 || (_param$argument = param.argument) === null || _param$argument === void 0 || (_param$argument = _param$argument.elements) === null || _param$argument === void 0 ? void 0 : _param$argument.map(({
name
}) => {
return name;
})),
restElement: true
};
}
if (param.type === 'TSParameterProperty') {
return getParamName( /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */
/** @type {import('@typescript-eslint/types').TSESTree.TSParameterProperty} */param.parameter, true);
}
throw new Error(`Unsupported function signature format: \`${param.type}\`.`);
};
if (!functionNode) {
return [];
}
return ( /** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */functionNode.params || ( /** @type {import('@typescript-eslint/types').TSESTree.MethodDefinition} */(_functionNode$value = functionNode.value) === null || _functionNode$value === void 0 ? void 0 : _functionNode$value.params) || []).map(param => {
return getParamName(param);
});
};
/**
* @param {ESTreeOrTypeScriptNode} functionNode
* @returns {Integer}
*/
exports.getFunctionParameterNames = getFunctionParameterNames;
const hasParams = functionNode => {
// Should also check `functionNode.value.params` if supporting `MethodDefinition`
return /** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */functionNode.params.length;
};
/**
* Gets all names of the target type, including those that refer to a path, e.g.
* `foo` or `foo.bar`.
* @param {import('comment-parser').Block} jsdoc
* @param {string} targetTagName
* @returns {{
* idx: Integer,
* name: string,
* type: string
* }[]}
*/
exports.hasParams = hasParams;
const getJsdocTagsDeep = (jsdoc, targetTagName) => {
const ret = [];
for (const [idx, {
name,
tag,
type
}] of jsdoc.tags.entries()) {
if (tag !== targetTagName) {
continue;
}
ret.push({
idx,
name,
type
});
}
return ret;
};
exports.getJsdocTagsDeep = getJsdocTagsDeep;
const modeWarnSettings = (0, _WarnSettings.default)();
/**
* @param {ParserMode|undefined} mode
* @param {Reporter} context
* @returns {import('./tagNames.js').AliasedTags}
*/
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: {
end: {
column: 1,
line: 1
},
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;
}
};
/**
* @param {import('comment-parser').Spec} tg
* @param {boolean} [returnArray]
* @returns {string[]|string}
*/
const getTagDescription = (tg, returnArray) => {
/**
* @type {string[]}
*/
const descriptions = [];
tg.source.some(({
tokens: {
end,
lineEnd,
postDelimiter,
tag,
postTag,
name,
type,
description
}
}) => {
const desc = (tag && postTag || !tag && !name && !type && postDelimiter || ''
// Remove space
).slice(1) + (description || '') + (lineEnd || '');
if (end) {
if (desc) {
descriptions.push(desc);
}
return true;
}
descriptions.push(desc);
return false;
});
return returnArray ? descriptions : descriptions.join('\n');
};
/**
* @typedef {{
* report: (descriptor: import('eslint').Rule.ReportDescriptor) => void
* }} Reporter
*/
/**
* @param {string} name
* @param {ParserMode|undefined} mode
* @param {TagNamePreference} tagPreference
* @param {Reporter} context
* @returns {string|false|{
* message: string;
* replacement?: string|undefined;
* }}
*/
exports.getTagDescription = getTagDescription;
const getPreferredTagNameSimple = (name, mode, tagPreference = {}, context = {
report() {
// No-op
}
}) => {
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 = Object.fromEntries(Object.entries(tagPreference).map(([key, value]) => {
return [key.replace(/^tag /u, ''), value];
}));
if (Object.prototype.hasOwnProperty.call(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;
};
/**
* @param {import('eslint').Rule.RuleContext} context
* @param {ParserMode|undefined} mode
* @param {string} name
* @param {string[]} definedTags
* @returns {boolean}
*/
exports.getPreferredTagNameSimple = getPreferredTagNameSimple;
const isValidTag = (context, mode, name, definedTags) => {
const tagNames = getTagNamesForMode(mode, context);
const validTagNames = Object.keys(tagNames).concat(Object.values(tagNames).flat());
const additionalTags = definedTags;
const allTags = validTagNames.concat(additionalTags);
return allTags.includes(name);
};
/**
* @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
* @param {string} targetTagName
* @returns {boolean}
*/
exports.isValidTag = isValidTag;
const hasTag = (jsdoc, targetTagName) => {
const targetTagLower = targetTagName.toLowerCase();
return jsdoc.tags.some(doc => {
return doc.tag.toLowerCase() === targetTagLower;
});
};
/**
* @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
* @param {(tag: import('@es-joy/jsdoccomment').JsdocTagWithInline) => boolean} filter
* @returns {import('@es-joy/jsdoccomment').JsdocTagWithInline[]}
*/
exports.hasTag = hasTag;
const filterTags = (jsdoc, filter) => {
return jsdoc.tags.filter(tag => {
return filter(tag);
});
};
/**
* @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
* @param {string} tagName
* @returns {import('comment-parser').Spec[]}
*/
exports.filterTags = filterTags;
const getTags = (jsdoc, tagName) => {
return filterTags(jsdoc, item => {
return item.tag === tagName;
});
};
/**
* @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
* @param {{
* tagName: string,
* context?: import('eslint').Rule.RuleContext,
* mode?: ParserMode,
* report?: import('./iterateJsdoc.js').Report
* tagNamePreference?: TagNamePreference
* skipReportingBlockedTag?: boolean,
* allowObjectReturn?: boolean,
* defaultMessage?: string,
* }} cfg
* @returns {string|undefined|false|{
* message: string;
* replacement?: string|undefined;
* }|{
* blocked: true,
* tagName: string
* }}
*/
exports.getTags = getTags;
const getPreferredTagName = (jsdoc, {
tagName,
context,
mode,
tagNamePreference,
report = () => {},
skipReportingBlockedTag = false,
allowObjectReturn = false,
defaultMessage = `Unexpected tag \`@${tagName}\``
}) => {
const ret = getPreferredTagNameSimple(tagName, mode, tagNamePreference, context);
const isObject = ret && typeof ret === 'object';
if (hasTag(jsdoc, tagName) && (ret === false || isObject && !ret.replacement)) {
if (skipReportingBlockedTag) {
return {
blocked: true,
tagName
};
}
const message = isObject && ret.message || defaultMessage;
report(message, null, getTags(jsdoc, tagName)[0]);
return false;
}
return isObject && !allowObjectReturn ? ret.replacement : ret;
};
/**
* @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
* @param {string} tagName
* @param {(
* matchingJsdocTag: import('@es-joy/jsdoccomment').JsdocTagWithInline,
* targetTagName: string
* ) => void} arrayHandler
* @param {object} cfg
* @param {import('eslint').Rule.RuleContext} [cfg.context]
* @param {ParserMode} [cfg.mode]
* @param {import('./iterateJsdoc.js').Report} [cfg.report]
* @param {TagNamePreference} [cfg.tagNamePreference]
* @param {boolean} [cfg.skipReportingBlockedTag]
* @returns {void}
*/
exports.getPreferredTagName = getPreferredTagName;
const forEachPreferredTag = (jsdoc, tagName, arrayHandler, {
context,
mode,
report,
tagNamePreference,
skipReportingBlockedTag = false
} = {}) => {
const targetTagName = /** @type {string|false} */
getPreferredTagName(jsdoc, {
skipReportingBlockedTag,
tagName,
context,
mode,
report,
tagNamePreference
});
if (!targetTagName || skipReportingBlockedTag && targetTagName && typeof targetTagName === 'object') {
return;
}
const matchingJsdocTags = jsdoc.tags.filter(({
tag
}) => {
return tag === targetTagName;
});
for (const matchingJsdocTag of matchingJsdocTags) {
arrayHandler(
/**
* @type {import('@es-joy/jsdoccomment').JsdocTagWithInline}
*/
matchingJsdocTag, targetTagName);
}
};
/**
* Get all tags, inline tags and inline tags in tags
* @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
* @returns {(import('comment-parser').Spec|
* import('@es-joy/jsdoccomment').JsdocInlineTagNoType)[]}
*/
exports.forEachPreferredTag = forEachPreferredTag;
const getAllTags = jsdoc => {
return [...jsdoc.tags, ...jsdoc.inlineTags.map(inlineTag => {
// Tags don't have source or line numbers, so add before returning
let line = -1;
for (const {
tokens: {
description
}
} of jsdoc.source) {
line++;
if (description && description.includes(`{@${inlineTag.tag}`)) {
break;
}
}
inlineTag.line = line;
return inlineTag;
}), ...jsdoc.tags.flatMap(tag => {
let tagBegins = -1;
for (const {
tokens: {
tag: tg
}
} of jsdoc.source) {
tagBegins++;
if (tg) {
break;
}
}
for (const inlineTag of tag.inlineTags) {
/** @type {import('./iterateJsdoc.js').Integer} */
let line = 0;
for (const {
number,
tokens: {
description
}
} of tag.source) {
if (description && description.includes(`{@${inlineTag.tag}`)) {
line = number;
break;
}
}
inlineTag.line = tagBegins + line - 1;
}
return (
/**
* @type {import('comment-parser').Spec & {
* inlineTags: import('@es-joy/jsdoccomment').JsdocInlineTagNoType[]
* }}
*/
tag.inlineTags
);
})];
};
/**
* @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
* @param {string[]} targetTagNames
* @returns {boolean}
*/
exports.getAllTags = getAllTags;
const hasATag = (jsdoc, targetTagNames) => {
return targetTagNames.some(targetTagName => {
return hasTag(jsdoc, targetTagName);
});
};
/**
* Checks if the JSDoc comment has an undefined type.
* @param {import('comment-parser').Spec|null|undefined} tag
* the tag which should be checked.
* @param {ParserMode} mode
* @returns {boolean}
* true in case a defined type is undeclared; otherwise false.
*/
exports.hasATag = hasATag;
const mayBeUndefinedTypeTag = (tag, mode) => {
// The function should not continue in the event the type is not defined...
if (typeof tag === 'undefined' || tag === null) {
return true;
}
// .. same applies if it declares an `{undefined}` or `{void}` type
const tagType = tag.type.trim();
// Exit early if matching
if (tagType === 'undefined' || tagType === 'void' || tagType === '*' || tagType === 'any') {
return true;
}
let parsedTypes;
try {
parsedTypes = (0, _jsdoccomment.tryParse)(tagType, mode === 'permissive' ? undefined : [mode]);
} catch {
// Ignore
}
if (
// We do not traverse deeply as it could be, e.g., `Promise<void>`
parsedTypes && parsedTypes.type === 'JsdocTypeUnion' && parsedTypes.elements.find(elem => {
return elem.type === 'JsdocTypeUndefined' || elem.type === 'JsdocTypeName' && elem.value === 'void';
})) {
return true;
}
// In any other case, a type is present
return false;
};
/**
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} map
* @param {string} tag
* @returns {Map<string, string|string[]|boolean|undefined>}
*/
exports.mayBeUndefinedTypeTag = mayBeUndefinedTypeTag;
const ensureMap = (map, tag) => {
if (!map.has(tag)) {
map.set(tag, new Map());
}
return /** @type {Map<string, string | boolean>} */map.get(tag);
};
/**
* @param {import('./iterateJsdoc.js').StructuredTags} structuredTags
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {void}
*/
const overrideTagStructure = (structuredTags, tagMap = tagStructure) => {
for (const [tag, {
name,
type,
required = []
}] of Object.entries(structuredTags)) {
const tagStruct = ensureMap(tagMap, tag);
tagStruct.set('namepathRole', 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);
}
};
/**
* @param {ParserMode} mode
* @param {import('./iterateJsdoc.js').StructuredTags} structuredTags
* @returns {import('./getDefaultTagStructureForMode.js').TagStructure}
*/
exports.overrideTagStructure = overrideTagStructure;
const getTagStructureForMode = (mode, structuredTags) => {
const tagStruct = (0, _getDefaultTagStructureForMode.default)(mode);
try {
overrideTagStructure(structuredTags, tagStruct);
/* c8 ignore next 3 */
} catch {
//
}
return tagStruct;
};
/**
* @param {string} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean}
*/
exports.getTagStructureForMode = getTagStructureForMode;
const isNamepathDefiningTag = (tag, tagMap = tagStructure) => {
const tagStruct = ensureMap(tagMap, tag);
return tagStruct.get('namepathRole') === 'namepath-defining';
};
/**
* @param {string} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean}
*/
exports.isNamepathDefiningTag = isNamepathDefiningTag;
const isNamepathReferencingTag = (tag, tagMap = tagStructure) => {
const tagStruct = ensureMap(tagMap, tag);
return tagStruct.get('namepathRole') === 'namepath-referencing';
};
/**
* @param {string} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean}
*/
exports.isNamepathReferencingTag = isNamepathReferencingTag;
const isNamepathOrUrlReferencingTag = (tag, tagMap = tagStructure) => {
const tagStruct = ensureMap(tagMap, tag);
return tagStruct.get('namepathRole') === 'namepath-or-url-referencing';
};
/**
* @param {string} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean|undefined}
*/
exports.isNamepathOrUrlReferencingTag = isNamepathOrUrlReferencingTag;
const tagMustHaveTypePosition = (tag, tagMap = tagStructure) => {
const tagStruct = ensureMap(tagMap, tag);
return /** @type {boolean|undefined} */tagStruct.get('typeRequired');
};
/**
* @param {string} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean|string}
*/
exports.tagMustHaveTypePosition = tagMustHaveTypePosition;
const tagMightHaveTypePosition = (tag, tagMap = tagStructure) => {
if (tagMustHaveTypePosition(tag, tagMap)) {
return true;
}
const tagStruct = ensureMap(tagMap, tag);
const ret = /** @type {boolean|undefined} */tagStruct.get('typeAllowed');
return ret === undefined ? true : ret;
};
exports.tagMightHaveTypePosition = tagMightHaveTypePosition;
const namepathTypes = new Set(['namepath-defining', 'namepath-referencing']);
/**
* @param {string} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean}
*/
const tagMightHaveNamePosition = (tag, tagMap = tagStructure) => {
const tagStruct = ensureMap(tagMap, tag);
const ret = tagStruct.get('namepathRole');
return ret === undefined ? true : Boolean(ret);
};
/**
* @param {string} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean}
*/
exports.tagMightHaveNamePosition = tagMightHaveNamePosition;
const tagMightHaveNamepath = (tag, tagMap = tagStructure) => {
const tagStruct = ensureMap(tagMap, tag);
const nampathRole = tagStruct.get('namepathRole');
return nampathRole !== false && namepathTypes.has( /** @type {string} */nampathRole);
};
/**
* @param {string} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean|undefined}
*/
exports.tagMightHaveNamepath = tagMightHaveNamepath;
const tagMustHaveNamePosition = (tag, tagMap = tagStructure) => {
const tagStruct = ensureMap(tagMap, tag);
return /** @type {boolean|undefined} */tagStruct.get('nameRequired');
};
/**
* @param {string} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean}
*/
exports.tagMustHaveNamePosition = tagMustHaveNamePosition;
const tagMightHaveEitherTypeOrNamePosition = (tag, tagMap) => {
return Boolean(tagMightHaveTypePosition(tag, tagMap)) || tagMightHaveNamepath(tag, tagMap);
};
/**
* @param {string} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean|undefined}
*/
exports.tagMightHaveEitherTypeOrNamePosition = tagMightHaveEitherTypeOrNamePosition;
const tagMustHaveEitherTypeOrNamePosition = (tag, tagMap) => {
const tagStruct = ensureMap(tagMap, tag);
return /** @type {boolean} */tagStruct.get('typeOrNameRequired');
};
/**
* @param {import('comment-parser').Spec} tag
* @param {import('./getDefaultTagStructureForMode.js').TagStructure} tagMap
* @returns {boolean|undefined}
*/
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;
};
/* eslint-disable complexity -- Temporary */
/**
* @param {ESTreeOrTypeScriptNode|null|undefined} node
* @param {boolean} [checkYieldReturnValue]
* @returns {boolean}
*/
exports.tagMissingRequiredTypeOrNamepath = tagMissingRequiredTypeOrNamepath;
const hasNonFunctionYield = (node, checkYieldReturnValue) => {
/* eslint-enable complexity -- Temporary */
if (!node) {
return false;
}
switch (node.type) {
case 'BlockStatement':
{
return node.body.some(bodyNode => {
return !['ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression'].includes(bodyNode.type) && hasNonFunctionYield(bodyNode, checkYieldReturnValue);
});
}
/* c8 ignore next 2 -- In Babel? */
// @ts-expect-error In Babel?
case 'OptionalCallExpression':
case 'CallExpression':
return node.arguments.some(element => {
return hasNonFunctionYield(element, checkYieldReturnValue);
});
case 'ChainExpression':
case 'ExpressionStatement':
{
return hasNonFunctionYield(node.expression, checkYieldReturnValue);
}
case 'LabeledStatement':
case 'WhileStatement':
case 'DoWhileStatement':
case 'ForStatement':
case 'ForInStatement':
case 'ForOfStatement':
case 'WithStatement':
{
return hasNonFunctionYield(node.body, checkYieldReturnValue);
}
case 'ConditionalExpression':
case 'IfStatement':
{
return hasNonFunctionYield(node.test, checkYieldReturnValue) || hasNonFunctionYield(node.consequent, checkYieldReturnValue) || hasNonFunctionYield(node.alternate, checkYieldReturnValue);
}
case 'TryStatement':
{
return hasNonFunctionYield(node.block, checkYieldReturnValue) || hasNonFunctionYield(node.handler && node.handler.body, checkYieldReturnValue) || hasNonFunctionYield( /** @type {import('@typescript-eslint/types').TSESTree.BlockStatement} */
node.finalizer, checkYieldReturnValue);
}
case 'SwitchStatement':
{
return node.cases.some(someCase => {
return someCase.consequent.some(nde => {
return hasNonFunctionYield(nde, checkYieldReturnValue);
});
});
}
case 'ArrayPattern':
case 'ArrayExpression':
return node.elements.some(element => {
return hasNonFunctionYield(element, checkYieldReturnValue);
});
case 'AssignmentPattern':
return hasNonFunctionYield(node.right, checkYieldReturnValue);
case 'VariableDeclaration':
{
return node.declarations.some(nde => {
return hasNonFunctionYield(nde, checkYieldReturnValue);
});
}
case 'VariableDeclarator':
{
return hasNonFunctionYield(node.id, checkYieldReturnValue) || hasNonFunctionYield(node.init, checkYieldReturnValue);
}
case 'AssignmentExpression':
case 'BinaryExpression':
case 'LogicalExpression':
{
return hasNonFunctionYield(node.left, checkYieldReturnValue) || hasNonFunctionYield(node.right, checkYieldReturnValue);
}
// Comma
case 'SequenceExpression':
case 'TemplateLiteral':
return node.expressions.some(subExpression => {
return hasNonFunctionYield(subExpression, checkYieldReturnValue);
});
case 'ObjectPattern':
case 'ObjectExpression':
return node.properties.some(property => {
return hasNonFunctionYield(property, checkYieldReturnValue);
});
/* c8 ignore next -- In Babel? */
case 'PropertyDefinition':
/* eslint-disable no-fallthrough */
/* c8 ignore next 2 -- In Babel? */
// @ts-expect-error In Babel?
case 'ObjectProperty':
/* c8 ignore next 2 -- In Babel? */
// @ts-expect-error In Babel?
case 'ClassProperty':
case 'Property':
/* eslint-enable no-fallthrough */
return node.computed && hasNonFunctionYield(node.key, checkYieldReturnValue) || hasNonFunctionYield(node.value, checkYieldReturnValue);
/* c8 ignore next 2 -- In Babel? */
// @ts-expect-error In Babel?
case 'ObjectMethod':
/* c8 ignore next 6 -- In Babel? */
// @ts-expect-error In Babel?
return node.computed && hasNonFunctionYield(node.key, checkYieldReturnValue) ||
// @ts-expect-error In Babel?
node.arguments.some(nde => {
return hasNonFunctionYield(nde, checkYieldReturnValue);
});
case 'SpreadElement':
case 'UnaryExpression':
return hasNonFunctionYield(node.argument, checkYieldReturnValue);
case 'TaggedTemplateExpression':
return hasNonFunctionYield(node.quasi, checkYieldReturnValue);
// ?.
/* c8 ignore next 2 -- In Babel? */
// @ts-expect-error In Babel?
case 'OptionalMemberExpression':
case 'MemberExpression':
return hasNonFunctionYield(node.object, checkYieldReturnValue) || hasNonFunctionYield(node.property, checkYieldReturnValue);
/* c8 ignore next 2 -- In Babel? */
// @ts-expect-error In Babel?
case 'Import':
case 'ImportExpression':
return hasNonFunctionYield(node.source, checkYieldReturnValue);
case 'ReturnStatement':
{
if (node.argument === null) {
return false;
}
return hasNonFunctionYield(node.argument, checkYieldReturnValue);
}
case 'YieldExpression':
{
if (checkYieldReturnValue) {
if ( /** @type {import('eslint').Rule.Node} */node.parent.type === 'VariableDeclarator') {
return true;
}
return false;
}
// void return does not count.
if (node.argument === null) {
return false;
}
return true;
}
default:
{
return false;
}
}
};
/**
* Checks if a node has a return statement. Void return does not count.
* @param {ESTreeOrTypeScriptNode} node
* @param {boolean} [checkYieldReturnValue]
* @returns {boolean}
*/
const hasYieldValue = (node, checkYieldReturnValue) => {
return /** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */node.generator && ( /** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */node.expression || hasNonFunctionYield( /** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */
node.body, checkYieldReturnValue));
};
/**
* Checks if a node has a throws statement.
* @param {ESTreeOrTypeScriptNode|null|undefined} node
* @param {boolean} [innerFunction]
* @returns {boolean}
*/
// eslint-disable-next-line complexity
exports.hasYieldValue = hasYieldValue;
const hasThrowValue = (node, innerFunction) => {
if (!node) {
return false;
}
// There are cases where a function may execute its inner function which
// throws, but we're treating functions atomically rather than trying to
// follow them
switch (node.type) {
case 'FunctionExpression':
case 'FunctionDeclaration':
case 'ArrowFunctionExpression':
{
return !innerFunction && !node.async && hasThrowValue(node.body, true);
}
case 'BlockStatement':
{
return node.body.some(bodyNode => {
return bodyNode.type !== 'FunctionDeclaration' && hasThrowValue(bodyNode);
});
}
case 'LabeledStatement':
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(nde => {
return hasThrowValue(nde);
});
});
}
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 {@link https://github.com/google/closure-compiler/wiki/Generic-Types}
* @see {@link https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#template}
* @param {import('comment-parser').Spec} tag
* @returns {string[]}
*/
exports.hasThrowValue = hasThrowValue;
const parseClosureTemplateTag = tag => {
return tag.name.split(',').map(type => {
return type.trim().replace(/^\[(?<name>.*?)=.*\]$/u, '$<name>');
});
};
/**
* @typedef {true|string[]} DefaultContexts
*/
/**
* Checks user option for `contexts` array, defaulting to
* contexts designated by the rule. Returns an array of
* ESTree AST types, indicating allowable contexts.
* @param {import('eslint').Rule.RuleContext} context
* @param {DefaultContexts|undefined} defaultContexts
* @param {{
* contexts?: import('./iterateJsdoc.js').Context[]
* }} settings
* @returns {(string|import('./iterateJsdoc.js').ContextObject)[]}
*/
exports.parseClosureTemplateTag = parseClosureTemplateTag;
const enforcedContexts = (context, defaultContexts, settings) => {
var _context$options$;
const contexts = ((_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$.contexts) || settings.contexts || (defaultContexts === true ? ['ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression', 'TSDeclareFunction'] : defaultContexts);
return contexts;
};
/**
* @param {import('./iterateJsdoc.js').Context[]} contexts
* @param {import('./iterateJsdoc.js').CheckJsdoc} checkJsdoc
* @param {import('@es-joy/jsdoccomment').CommentHandler} [handler]
* @returns {import('eslint').Rule.RuleListener}
*/
exports.enforcedContexts = enforcedContexts;
const getContextObject = (contexts, checkJsdoc, handler) => {
/** @type {import('eslint').Rule.RuleListener} */
const properties = {};
for (const [idx, prop] of contexts.entries()) {
/** @type {string} */
let property;
/** @type {(node: import('eslint').Rule.Node) => void} */
let value;
if (typeof prop === 'object') {
const selInfo = {
lastIndex: idx,
selector: prop.context
};
if (prop.comment) {
property = /** @type {string} */prop.context;
value = checkJsdoc.bind(null, {
...selInfo,
comment: prop.comment
},
/**
* @type {(jsdoc: import('@es-joy/jsdoccomment').JsdocBlockWithInline) => boolean}
*/
/** @type {import('@es-joy/jsdoccomment').CommentHandler} */
handler.bind(null, prop.comment));
} else {
property = /** @type {string} */prop.context;
value = checkJsdoc.bind(null, selInfo, null);
}
} else {
const selInfo = {
lastIndex: idx,
selector: prop
};
property = prop;
value = checkJsdoc.bind(null, selInfo, null);
}
const old =
/**
* @type {((node: import('eslint').Rule.Node) => void)}
*/
properties[property];
properties[property] = old ?
/**
* @type {((node: import('eslint').Rule.Node) => void)}
*/
function (node) {
old(node);
value(node);
} : value;
}
return properties;
};
exports.getContextObject = getContextObject;
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']);
/**
* @typedef {{
* [key: string]: false|string|
* {message: string, replacement?: string}
* }} TagNamePreference
*/
/**
* @param {import('eslint').Rule.RuleContext} context
* @param {ParserMode|undefined} mode
* @param {import('comment-parser').Spec[]} tags
* @returns {{
* tagsWithNames: import('comment-parser').Spec[],
* tagsWithoutNames: import('comment-parser').Spec[]
* }}
*/
const getTagsByType = (context, mode, tags) => {
/**
* @type {import('comment-parser').Spec[]}
*/
const tagsWithoutNames = [];
const tagsWithNames = tags.filter(tag => {
const {
tag: tagName
} = tag;
const tagWithName = tagsWithNamesAndDescriptions.has(tagName);
if (!tagWithName) {
tagsWithoutNames.push(tag);
}
return tagWithName;
});
return {
tagsWithNames,
tagsWithoutNames
};
};
/**
* @param {import('eslint').SourceCode|{
* text: string
* }} sourceCode
* @returns {string}
*/
exports.getTagsByType = getTagsByType;
const getIndent = sourceCode => {
var _sourceCode$text$matc;
return (((_sourceCode$text$matc = sourceCode.text.match(/^\n*([ \t]+)/u)) === null || _sourceCode$text$matc === void 0 ? void 0 : _sourceCode$text$matc[1]) ?? '') + ' ';
};
/**
* @param {import('eslint').Rule.Node|null} node
* @returns {boolean}
*/
exports.getIndent = getIndent;
const isConstructor = node => {
var _node$parent;
return (node === null || node === void 0 ? void 0 : node.type) === 'MethodDefinition' && node.kind === 'constructor' || /** @type {import('@typescript-eslint/types').TSESTree.MethodDefinition} */(node === null || node === void 0 || (_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.kind) === 'constructor';
};
/**
* @param {import('eslint').Rule.Node|null} node
* @returns {boolean}
*/
exports.isConstructor = isConstructor;
const isGetter = node => {
var _node$parent2;
return node !== null &&
/**
* @type {import('@typescript-eslint/types').TSESTree.MethodDefinition|
* import('@typescript-eslint/types').TSESTree.Property}
*/
((_node$parent2 = node.parent) === null || _node$parent2 === void 0 ? void 0 : _node$parent2.kind) === 'get';
};
/**
* @param {import('eslint').Rule.Node|null} node
* @returns {boolean}
*/
exports.isGetter = isGetter;
const isSetter = node => {
var _node$parent3;
return node !== null &&
/**
* @type {import('@typescript-eslint/types').TSESTree.MethodDefinition|
* import('@typescript-eslint/types').TSESTree.Property}
*/
((_node$parent3 = node.parent) === null || _node$parent3 === void 0 ? void 0 : _node$parent3.kind) === 'set';
};
/**
* @param {import('eslint').Rule.Node} node
* @returns {boolean}
*/
exports.isSetter = isSetter;
const hasAccessorPair = node => {
const {
type,
kind: sourceKind,
key
} =
/**
* @type {import('@typescript-eslint/types').TSESTree.MethodDefinition|
* import('@typescript-eslint/types').TSESTree.Property}
*/
node;
const sourceName = /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */key.name;
const oppositeKind = sourceKind === 'get' ? 'set' : 'get';
const sibling = type === 'MethodDefinition' ? /** @type {import('@typescript-eslint/types').TSESTree.ClassBody} */node.parent.body : /** @type {import('@typescript-eslint/types').TSESTree.ObjectExpression} */node.parent.properties;
return sibling.some(child => {
const {
kind,
key: ky
} =
/**
* @type {import('@typescript-eslint/types').TSESTree.MethodDefinition|
* import('@typescript-eslint/types').TSESTree.Property}
*/
child;
const name = /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ky.name;
return kind === oppositeKind && name === sourceName;
});
};
/**
* @param {import('./iterateJsdoc.js').JsdocBlockWithInline} jsdoc
* @param {import('eslint').Rule.Node|null} node
* @param {import('eslint').Rule.RuleContext} context
* @param {import('json-schema').JSONSchema4} schema
* @returns {boolean}
*/
const exemptSpeciaMethods = (jsdoc, node, context, schema) => {
/**
* @param {"checkGetters"|"checkSetters"|"checkConstructors"} prop
* @returns {boolean|"no-setter"|"no-getter"}
*/
const hasSchemaOption = prop => {
var _context$options$2;
const schemaProperties = schema[0].properties;
return ((_context$options$2 = context.options[0]) === null || _context$options$2 === void 0 ? void 0 : _context$options$2[prop]) ?? (schemaProperties[prop] && schemaProperties[prop].default);
};
const checkGetters = hasSchemaOption('checkGetters');
const checkSetters = hasSchemaOption('checkSetters');
return !hasSchemaOption('checkConstructors') && (isConstructor(node) || hasATag(jsdoc, ['class', 'constructor'])) || isGetter(node) && (!checkGetters || checkGetters === 'no-setter' && hasAccessorPair( /** @type {import('./iterateJsdoc.js').Node} */node.parent)) || isSetter(node) && (!checkSetters || checkSetters === 'no-getter' && hasAccessorPair( /** @type {import('./iterateJsdoc.js').Node} */node.parent));
};
/**
* 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}
*/
exports.exemptSpeciaMethods = exemptSpeciaMethods;
const dropPathSegmentQuotes = str => {
return str.replaceAll(/\.(['"])(.*)\1/gu, '.$2');
};
/**
* @param {string} name
* @returns {(otherPathName: string) => boolean}
*/
exports.dropPathSegmentQuotes = dropPathSegmentQuotes;
const comparePaths = name => {
return otherPathName => {
return otherPathName === name || dropPathSegmentQuotes(otherPathName) === dropPathSegmentQuotes(name);
};
};
/**
* @callback PathDoesNotBeginWith
* @param {string} name
* @param {string} otherPathName
* @returns {boolean}
*/
/** @type {PathDoesNotBeginWith} */
exports.comparePaths = comparePaths;
const pathDoesNotBeginWith = (name, otherPathName) => {
return !name.startsWith(otherPathName) && !dropPathSegmentQuotes(name).startsWith(dropPathSegmentQuotes(otherPathName));
};
/**
* @param {string} regexString
* @param {string} [requiredFlags]
* @returns {RegExp}
*/
exports.pathDoesNotBeginWith = pathDoesNotBeginWith;
const getRegexFromString = (regexString, requiredFlags) => {
const match = regexString.match(/^\/(.*)\/([gimyus]*)$/us);
let flags = 'u';
let regex = regexString;
if (match) {
[, regex, flags] = match;
if (!flags) {
flags = 'u';
}
}
const uniqueFlags = [...new Set(flags + (requiredFlags || ''))];
flags = uniqueFlags.join('');
return new RegExp(regex, flags);
};
exports.getRegexFromString = getRegexFromString;
//# sourceMappingURL=jsdocUtils.cjs.map