Website Structure
This commit is contained in:
parent
62812f2090
commit
71f0676a62
22365 changed files with 4265753 additions and 791 deletions
34
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/WarnSettings.js
generated
vendored
Normal file
34
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/WarnSettings.js
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
const WarnSettings = function () {
|
||||
/** @type {WeakMap<object, Set<string>>} */
|
||||
const warnedSettings = new WeakMap();
|
||||
|
||||
return {
|
||||
/**
|
||||
* Warn only once for each context and setting
|
||||
* @param {import('eslint').Rule.RuleContext} context
|
||||
* @param {string} setting
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasBeenWarned (context, setting) {
|
||||
return warnedSettings.has(context) && /** @type {Set<string>} */ (
|
||||
warnedSettings.get(context)
|
||||
).has(setting);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.RuleContext} context
|
||||
* @param {string} setting
|
||||
* @returns {void}
|
||||
*/
|
||||
markSettingAsWarned (context, setting) {
|
||||
// c8 ignore else
|
||||
if (!warnedSettings.has(context)) {
|
||||
warnedSettings.set(context, new Set());
|
||||
}
|
||||
|
||||
/** @type {Set<string>} */ (warnedSettings.get(context)).add(setting);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default WarnSettings;
|
||||
444
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/alignTransform.js
generated
vendored
Normal file
444
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/alignTransform.js
generated
vendored
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
/**
|
||||
* Transform based on https://github.com/syavorsky/comment-parser/blob/master/src/transforms/align.ts
|
||||
*
|
||||
* It contains some customizations to align based on the tags, and some custom options.
|
||||
*/
|
||||
|
||||
import {
|
||||
// `comment-parser/primitives` export
|
||||
util,
|
||||
} from 'comment-parser';
|
||||
|
||||
/**
|
||||
* Detects if a line starts with a markdown list marker
|
||||
* Supports: -, *, numbered lists (1., 2., etc.)
|
||||
* This explicitly excludes hyphens that are part of JSDoc tag syntax
|
||||
* @param {string} text - The text to check
|
||||
* @param {boolean} isFirstLineOfTag - True if this is the first line (tag line)
|
||||
* @returns {boolean} - True if the text starts with a list marker
|
||||
*/
|
||||
const startsWithListMarker = (text, isFirstLineOfTag = false) => {
|
||||
// On the first line of a tag, the hyphen is typically the JSDoc separator,
|
||||
// not a list marker
|
||||
if (isFirstLineOfTag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match lines that start with optional whitespace, then a list marker
|
||||
// - or * followed by a space
|
||||
// or a number followed by . or ) and a space
|
||||
return /^\s*(?:[\-*]|\d+(?:\.|\)))\s+/v.test(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* hasNoTypes: boolean,
|
||||
* maxNamedTagLength: import('./iterateJsdoc.js').Integer,
|
||||
* maxUnnamedTagLength: import('./iterateJsdoc.js').Integer
|
||||
* }} TypelessInfo
|
||||
*/
|
||||
|
||||
const {
|
||||
rewireSource,
|
||||
} = util;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* name: import('./iterateJsdoc.js').Integer,
|
||||
* start: import('./iterateJsdoc.js').Integer,
|
||||
* tag: import('./iterateJsdoc.js').Integer,
|
||||
* type: import('./iterateJsdoc.js').Integer
|
||||
* }} Width
|
||||
*/
|
||||
|
||||
/** @type {Width} */
|
||||
const zeroWidth = {
|
||||
name: 0,
|
||||
start: 0,
|
||||
tag: 0,
|
||||
type: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string[]} tags
|
||||
* @param {import('./iterateJsdoc.js').Integer} index
|
||||
* @param {import('comment-parser').Line[]} source
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const shouldAlign = (tags, index, source) => {
|
||||
const tag = source[index].tokens.tag.replace('@', '');
|
||||
const includesTag = tags.includes(tag);
|
||||
|
||||
if (includesTag) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tag !== '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let iterator = index; iterator >= 0; iterator--) {
|
||||
const previousTag = source[iterator].tokens.tag.replace('@', '');
|
||||
|
||||
if (previousTag !== '') {
|
||||
if (tags.includes(previousTag)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string[]} tags
|
||||
* @returns {(
|
||||
* width: Width,
|
||||
* line: {
|
||||
* tokens: import('comment-parser').Tokens
|
||||
* },
|
||||
* index: import('./iterateJsdoc.js').Integer,
|
||||
* source: import('comment-parser').Line[]
|
||||
* ) => Width}
|
||||
*/
|
||||
const getWidth = (tags) => {
|
||||
return (width, {
|
||||
tokens,
|
||||
}, index, source) => {
|
||||
if (!shouldAlign(tags, index, source)) {
|
||||
return width;
|
||||
}
|
||||
|
||||
return {
|
||||
name: Math.max(width.name, tokens.name.length),
|
||||
start: tokens.delimiter === '/**' ? tokens.start.length : width.start,
|
||||
tag: Math.max(width.tag, tokens.tag.length),
|
||||
type: Math.max(width.type, tokens.type.length),
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* description: string;
|
||||
* tags: import('comment-parser').Spec[];
|
||||
* problems: import('comment-parser').Problem[];
|
||||
* }} fields
|
||||
* @returns {TypelessInfo}
|
||||
*/
|
||||
const getTypelessInfo = (fields) => {
|
||||
const hasNoTypes = fields.tags.every(({
|
||||
type,
|
||||
}) => {
|
||||
return !type;
|
||||
});
|
||||
const maxNamedTagLength = Math.max(...fields.tags.map(({
|
||||
name,
|
||||
tag,
|
||||
}) => {
|
||||
return name.length === 0 ? -1 : tag.length;
|
||||
}).filter((length) => {
|
||||
return length !== -1;
|
||||
})) + 1;
|
||||
const maxUnnamedTagLength = Math.max(...fields.tags.map(({
|
||||
name,
|
||||
tag,
|
||||
}) => {
|
||||
return name.length === 0 ? tag.length : -1;
|
||||
}).filter((length) => {
|
||||
return length !== -1;
|
||||
})) + 1;
|
||||
return {
|
||||
hasNoTypes,
|
||||
maxNamedTagLength,
|
||||
maxUnnamedTagLength,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./iterateJsdoc.js').Integer} len
|
||||
* @returns {string}
|
||||
*/
|
||||
const space = (len) => {
|
||||
return ''.padStart(len, ' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a tag or any of its lines contain list markers
|
||||
* @param {import('./iterateJsdoc.js').Integer} index - Current line index
|
||||
* @param {import('comment-parser').Line[]} source - All source lines
|
||||
* @returns {{hasListMarker: boolean, tagStartIndex: import('./iterateJsdoc.js').Integer}}
|
||||
*/
|
||||
const checkForListMarkers = (index, source) => {
|
||||
let hasListMarker = false;
|
||||
let tagStartIndex = index;
|
||||
while (tagStartIndex > 0 && source[tagStartIndex].tokens.tag === '') {
|
||||
tagStartIndex--;
|
||||
}
|
||||
|
||||
for (let idx = tagStartIndex; idx <= index; idx++) {
|
||||
const isFirstLine = (idx === tagStartIndex);
|
||||
if (source[idx]?.tokens?.description && startsWithListMarker(source[idx].tokens.description, isFirstLine)) {
|
||||
hasListMarker = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hasListMarker,
|
||||
tagStartIndex,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate extra indentation for list items relative to the first continuation line
|
||||
* @param {import('./iterateJsdoc.js').Integer} index - Current line index
|
||||
* @param {import('./iterateJsdoc.js').Integer} tagStartIndex - Index of the tag line
|
||||
* @param {import('comment-parser').Line[]} source - All source lines
|
||||
* @returns {string} - Extra indentation spaces
|
||||
*/
|
||||
const calculateListExtraIndent = (index, tagStartIndex, source) => {
|
||||
// Find the first continuation line to use as baseline
|
||||
let firstContinuationIndent = null;
|
||||
for (let idx = tagStartIndex + 1; idx < source.length; idx++) {
|
||||
if (source[idx].tokens.description && !source[idx].tokens.tag) {
|
||||
firstContinuationIndent = source[idx].tokens.postDelimiter.length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the extra indentation of current line relative to the first continuation line
|
||||
const currentOriginalIndent = source[index].tokens.postDelimiter.length;
|
||||
const extraIndent = firstContinuationIndent !== null && currentOriginalIndent > firstContinuationIndent ?
|
||||
' '.repeat(currentOriginalIndent - firstContinuationIndent) :
|
||||
'';
|
||||
|
||||
return extraIndent;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* customSpacings: import('../src/rules/checkLineAlignment.js').CustomSpacings,
|
||||
* tags: string[],
|
||||
* indent: string,
|
||||
* preserveMainDescriptionPostDelimiter: boolean,
|
||||
* wrapIndent: string,
|
||||
* disableWrapIndent: boolean,
|
||||
* }} cfg
|
||||
* @returns {(
|
||||
* block: import('comment-parser').Block
|
||||
* ) => import('comment-parser').Block}
|
||||
*/
|
||||
const alignTransform = ({
|
||||
customSpacings,
|
||||
disableWrapIndent,
|
||||
indent,
|
||||
preserveMainDescriptionPostDelimiter,
|
||||
tags,
|
||||
wrapIndent,
|
||||
}) => {
|
||||
let intoTags = false;
|
||||
/** @type {Width} */
|
||||
let width;
|
||||
|
||||
/**
|
||||
* @param {import('comment-parser').Tokens} tokens
|
||||
* @param {TypelessInfo} typelessInfo
|
||||
* @returns {import('comment-parser').Tokens}
|
||||
*/
|
||||
const alignTokens = (tokens, typelessInfo) => {
|
||||
const nothingAfter = {
|
||||
delim: false,
|
||||
name: false,
|
||||
tag: false,
|
||||
type: false,
|
||||
};
|
||||
|
||||
if (tokens.description === '') {
|
||||
nothingAfter.name = true;
|
||||
tokens.postName = '';
|
||||
|
||||
if (tokens.name === '') {
|
||||
nothingAfter.type = true;
|
||||
tokens.postType = '';
|
||||
|
||||
if (tokens.type === '') {
|
||||
nothingAfter.tag = true;
|
||||
tokens.postTag = '';
|
||||
|
||||
/* c8 ignore next: Never happens because the !intoTags return. But it's here for consistency with the original align transform */
|
||||
if (tokens.tag === '') {
|
||||
nothingAfter.delim = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let untypedNameAdjustment = 0;
|
||||
let untypedTypeAdjustment = 0;
|
||||
if (typelessInfo.hasNoTypes) {
|
||||
nothingAfter.tag = true;
|
||||
tokens.postTag = '';
|
||||
if (tokens.name === '') {
|
||||
untypedNameAdjustment = typelessInfo.maxNamedTagLength - tokens.tag.length;
|
||||
} else {
|
||||
untypedNameAdjustment = typelessInfo.maxNamedTagLength > typelessInfo.maxUnnamedTagLength ? 0 :
|
||||
Math.max(0, typelessInfo.maxUnnamedTagLength - (tokens.tag.length + tokens.name.length + 1));
|
||||
untypedTypeAdjustment = typelessInfo.maxNamedTagLength - tokens.tag.length;
|
||||
}
|
||||
}
|
||||
|
||||
// Todo: Avoid fixing alignment of blocks with multiline wrapping of type
|
||||
if (tokens.tag === '' && tokens.type) {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
const spacings = {
|
||||
postDelimiter: customSpacings?.postDelimiter || 1,
|
||||
postName: customSpacings?.postName || 1,
|
||||
postTag: customSpacings?.postTag || 1,
|
||||
postType: customSpacings?.postType || 1,
|
||||
};
|
||||
|
||||
tokens.postDelimiter = nothingAfter.delim ? '' : space(spacings.postDelimiter);
|
||||
|
||||
if (!nothingAfter.tag) {
|
||||
tokens.postTag = space(width.tag - tokens.tag.length + spacings.postTag);
|
||||
}
|
||||
|
||||
if (!nothingAfter.type) {
|
||||
tokens.postType = space(width.type - tokens.type.length + spacings.postType + untypedTypeAdjustment);
|
||||
}
|
||||
|
||||
if (!nothingAfter.name) {
|
||||
// If post name is empty for all lines (name width 0), don't add post name spacing.
|
||||
tokens.postName = width.name === 0 ? '' : space(width.name - tokens.name.length + spacings.postName + untypedNameAdjustment);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('comment-parser').Line} line
|
||||
* @param {import('./iterateJsdoc.js').Integer} index
|
||||
* @param {import('comment-parser').Line[]} source
|
||||
* @param {TypelessInfo} typelessInfo
|
||||
* @param {string|false} indentTag
|
||||
* @returns {import('comment-parser').Line}
|
||||
*/
|
||||
const update = (line, index, source, typelessInfo, indentTag) => {
|
||||
/** @type {import('comment-parser').Tokens} */
|
||||
const tokens = {
|
||||
...line.tokens,
|
||||
};
|
||||
|
||||
if (tokens.tag !== '') {
|
||||
intoTags = true;
|
||||
}
|
||||
|
||||
const isEmpty =
|
||||
tokens.tag === '' &&
|
||||
tokens.name === '' &&
|
||||
tokens.type === '' &&
|
||||
tokens.description === '';
|
||||
|
||||
// dangling '*/'
|
||||
if (tokens.end === '*/' && isEmpty) {
|
||||
tokens.start = indent + ' ';
|
||||
|
||||
return {
|
||||
...line,
|
||||
tokens,
|
||||
};
|
||||
}
|
||||
|
||||
switch (tokens.delimiter) {
|
||||
case '*':
|
||||
tokens.start = indent + ' ';
|
||||
break;
|
||||
case '/**':
|
||||
tokens.start = indent;
|
||||
break;
|
||||
default:
|
||||
tokens.delimiter = '';
|
||||
|
||||
// compensate delimiter
|
||||
tokens.start = indent + ' ';
|
||||
}
|
||||
|
||||
if (!intoTags) {
|
||||
if (tokens.description === '') {
|
||||
tokens.postDelimiter = '';
|
||||
} else if (!preserveMainDescriptionPostDelimiter) {
|
||||
tokens.postDelimiter = ' ';
|
||||
}
|
||||
|
||||
return {
|
||||
...line,
|
||||
tokens,
|
||||
};
|
||||
}
|
||||
|
||||
const postHyphenSpacing = customSpacings?.postHyphen ?? 1;
|
||||
const hyphenSpacing = /^\s*-\s+/v;
|
||||
tokens.description = tokens.description.replace(
|
||||
hyphenSpacing, '-' + ''.padStart(postHyphenSpacing, ' '),
|
||||
);
|
||||
|
||||
// Not align.
|
||||
if (shouldAlign(tags, index, source)) {
|
||||
alignTokens(tokens, typelessInfo);
|
||||
|
||||
if (!disableWrapIndent && indentTag) {
|
||||
const {
|
||||
hasListMarker,
|
||||
tagStartIndex,
|
||||
} = checkForListMarkers(index, source);
|
||||
|
||||
if (hasListMarker && index > tagStartIndex) {
|
||||
const extraIndent = calculateListExtraIndent(index, tagStartIndex, source);
|
||||
tokens.postDelimiter += wrapIndent + extraIndent;
|
||||
} else {
|
||||
// Normal case: add wrapIndent after the aligned delimiter
|
||||
tokens.postDelimiter += wrapIndent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...line,
|
||||
tokens,
|
||||
};
|
||||
};
|
||||
|
||||
return ({
|
||||
source,
|
||||
...fields
|
||||
}) => {
|
||||
width = source.reduce(getWidth(tags), {
|
||||
...zeroWidth,
|
||||
});
|
||||
|
||||
const typelessInfo = getTypelessInfo(fields);
|
||||
|
||||
let tagIndentMode = false;
|
||||
|
||||
return rewireSource({
|
||||
...fields,
|
||||
source: source.map((line, index) => {
|
||||
const indentTag = !disableWrapIndent && tagIndentMode && !line.tokens.tag && line.tokens.description;
|
||||
const ret = update(line, index, source, typelessInfo, indentTag);
|
||||
|
||||
if (!disableWrapIndent && line.tokens.tag) {
|
||||
tagIndentMode = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}),
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export default alignTransform;
|
||||
106
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/buildForbidRuleDefinition.js
generated
vendored
Normal file
106
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/buildForbidRuleDefinition.js
generated
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import iterateJsdoc from './iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @typedef {(string|{
|
||||
* comment: string,
|
||||
* context: string,
|
||||
* message?: string
|
||||
* })[]} Contexts
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* contexts?: Contexts,
|
||||
* description?: string,
|
||||
* getContexts?: (
|
||||
* ctxt: import('eslint').Rule.RuleContext,
|
||||
* report: import('./iterateJsdoc.js').Report
|
||||
* ) => Contexts|false,
|
||||
* contextName?: string,
|
||||
* modifyContext?: (context: import('eslint').Rule.RuleContext) => import('eslint').Rule.RuleContext,
|
||||
* schema?: import('eslint').Rule.RuleMetaData['schema']
|
||||
* url?: string,
|
||||
* }} cfg
|
||||
* @returns {import('eslint').Rule.RuleModule}
|
||||
*/
|
||||
export const buildForbidRuleDefinition = ({
|
||||
contextName,
|
||||
contexts: cntxts,
|
||||
description,
|
||||
getContexts,
|
||||
modifyContext,
|
||||
schema,
|
||||
url,
|
||||
}) => {
|
||||
return iterateJsdoc(({
|
||||
context,
|
||||
info: {
|
||||
comment,
|
||||
},
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
/** @type {Contexts|boolean|undefined} */
|
||||
let contexts = cntxts;
|
||||
|
||||
if (getContexts) {
|
||||
contexts = getContexts(context, report);
|
||||
if (!contexts) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
contextStr,
|
||||
foundContext,
|
||||
} = utils.findContext(/** @type {Contexts} */ (contexts), comment);
|
||||
|
||||
// We are not on the *particular* matching context/comment, so don't assume
|
||||
// we need reporting
|
||||
if (!foundContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = /** @type {import('./iterateJsdoc.js').ContextObject} */ (
|
||||
foundContext
|
||||
)?.message ??
|
||||
'Syntax is restricted: {{context}}' +
|
||||
(comment ? ' with {{comment}}' : '');
|
||||
|
||||
report(message, null, null, comment ? {
|
||||
comment,
|
||||
context: contextStr,
|
||||
} : {
|
||||
context: contextStr,
|
||||
});
|
||||
}, {
|
||||
contextSelected: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: description ?? contextName ?? 'Reports when certain comment structures are present.',
|
||||
url: url ?? 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/advanced.md#user-content-advanced-creating-your-own-rules',
|
||||
},
|
||||
schema: schema ?? [],
|
||||
type: 'suggestion',
|
||||
},
|
||||
modifyContext: modifyContext ?? (getContexts ? undefined : (context) => {
|
||||
// Reproduce context object with our own `contexts`
|
||||
const propertyDescriptors = Object.getOwnPropertyDescriptors(context);
|
||||
return Object.create(
|
||||
Object.getPrototypeOf(context),
|
||||
{
|
||||
...propertyDescriptors,
|
||||
options: {
|
||||
...propertyDescriptors.options,
|
||||
value: [
|
||||
{
|
||||
contexts: cntxts,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
}),
|
||||
nonGlobalSettings: true,
|
||||
});
|
||||
};
|
||||
481
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/buildRejectOrPreferRuleDefinition.js
generated
vendored
Normal file
481
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/buildRejectOrPreferRuleDefinition.js
generated
vendored
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
import iterateJsdoc from './iterateJsdoc.js';
|
||||
import {
|
||||
parse,
|
||||
stringify,
|
||||
traverse,
|
||||
tryParse,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
/**
|
||||
* Adjusts the parent type node `meta` for generic matches (or type node
|
||||
* `type` for `JsdocTypeAny`) and sets the type node `value`.
|
||||
* @param {string} type The actual type
|
||||
* @param {string} preferred The preferred type
|
||||
* @param {boolean} isGenericMatch
|
||||
* @param {string} typeNodeName
|
||||
* @param {import('jsdoc-type-pratt-parser').NonRootResult} node
|
||||
* @param {import('jsdoc-type-pratt-parser').NonRootResult|undefined} parentNode
|
||||
* @returns {void}
|
||||
*/
|
||||
const adjustNames = (type, preferred, isGenericMatch, typeNodeName, node, parentNode) => {
|
||||
let ret = preferred;
|
||||
if (isGenericMatch) {
|
||||
const parentMeta = /** @type {import('jsdoc-type-pratt-parser').GenericResult} */ (
|
||||
parentNode
|
||||
).meta;
|
||||
if (preferred === '[]') {
|
||||
parentMeta.brackets = 'square';
|
||||
parentMeta.dot = false;
|
||||
ret = 'Array';
|
||||
} else {
|
||||
const dotBracketEnd = preferred.match(/\.(?:<>)?$/v);
|
||||
if (dotBracketEnd) {
|
||||
parentMeta.brackets = 'angle';
|
||||
parentMeta.dot = true;
|
||||
ret = preferred.slice(0, -dotBracketEnd[0].length);
|
||||
} else {
|
||||
const bracketEnd = preferred.endsWith('<>');
|
||||
if (bracketEnd) {
|
||||
parentMeta.brackets = 'angle';
|
||||
parentMeta.dot = false;
|
||||
ret = preferred.slice(0, -2);
|
||||
} else if (
|
||||
parentMeta?.brackets === 'square' &&
|
||||
(typeNodeName === '[]' || typeNodeName === 'Array')
|
||||
) {
|
||||
parentMeta.brackets = 'angle';
|
||||
parentMeta.dot = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type === 'JsdocTypeAny') {
|
||||
node.type = 'JsdocTypeName';
|
||||
}
|
||||
|
||||
/** @type {import('jsdoc-type-pratt-parser').NameResult} */ (
|
||||
node
|
||||
).value = ret.replace(/(?:\.|<>|\.<>|\[\])$/v, '');
|
||||
|
||||
// For bare pseudo-types like `<>`
|
||||
if (!ret) {
|
||||
/** @type {import('jsdoc-type-pratt-parser').NameResult} */ (
|
||||
node
|
||||
).value = typeNodeName;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean} [upperCase]
|
||||
* @returns {string}
|
||||
*/
|
||||
const getMessage = (upperCase) => {
|
||||
return 'Use object shorthand or index signatures instead of ' +
|
||||
'`' + (upperCase ? 'O' : 'o') + 'bject`, e.g., `{[key: string]: string}`';
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
* message: string,
|
||||
* replacement: false
|
||||
* }}
|
||||
*/
|
||||
const info = {
|
||||
message: getMessage(),
|
||||
replacement: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
* message: string,
|
||||
* replacement: false
|
||||
* }}
|
||||
*/
|
||||
const infoUC = {
|
||||
message: getMessage(true),
|
||||
replacement: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* checkNativeTypes?: import('./rules/checkTypes.js').CheckNativeTypes|null
|
||||
* overrideSettings?: import('./iterateJsdoc.js').Settings['preferredTypes']|null,
|
||||
* description?: string,
|
||||
* schema?: import('eslint').Rule.RuleMetaData['schema'],
|
||||
* typeName?: string,
|
||||
* url?: string,
|
||||
* }} cfg
|
||||
* @returns {import('eslint').Rule.RuleModule}
|
||||
*/
|
||||
export const buildRejectOrPreferRuleDefinition = ({
|
||||
checkNativeTypes = null,
|
||||
typeName,
|
||||
description = typeName ?? 'Reports types deemed invalid (customizable and with defaults, for preventing and/or recommending replacements).',
|
||||
overrideSettings = null,
|
||||
schema = [],
|
||||
url = 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-types.md#repos-sticky-header',
|
||||
}) => {
|
||||
return iterateJsdoc(
|
||||
({
|
||||
context,
|
||||
jsdocNode,
|
||||
report,
|
||||
settings,
|
||||
sourceCode,
|
||||
utils,
|
||||
}) => {
|
||||
const jsdocTagsWithPossibleType = utils.filterTags((tag) => {
|
||||
return Boolean(utils.tagMightHaveTypePosition(tag.tag));
|
||||
});
|
||||
|
||||
const
|
||||
/**
|
||||
* @type {{
|
||||
* preferredTypes: import('./iterateJsdoc.js').PreferredTypes,
|
||||
* structuredTags: import('./iterateJsdoc.js').StructuredTags,
|
||||
* mode: import('./jsdocUtils.js').ParserMode
|
||||
* }}
|
||||
*/
|
||||
{
|
||||
mode,
|
||||
preferredTypes: preferredTypesOriginal,
|
||||
structuredTags,
|
||||
} = overrideSettings ? {
|
||||
mode: settings.mode,
|
||||
preferredTypes: overrideSettings,
|
||||
structuredTags: {},
|
||||
} : settings;
|
||||
|
||||
const injectObjectPreferredTypes = !overrideSettings &&
|
||||
!('Object' in preferredTypesOriginal ||
|
||||
'object' in preferredTypesOriginal ||
|
||||
'object.<>' in preferredTypesOriginal ||
|
||||
'Object.<>' in preferredTypesOriginal ||
|
||||
'object<>' in preferredTypesOriginal);
|
||||
|
||||
/** @type {import('./iterateJsdoc.js').PreferredTypes} */
|
||||
const typeToInject = mode === 'typescript' ?
|
||||
{
|
||||
Object: 'object',
|
||||
'object.<>': info,
|
||||
'Object.<>': infoUC,
|
||||
'object<>': info,
|
||||
'Object<>': infoUC,
|
||||
} :
|
||||
{
|
||||
Object: 'object',
|
||||
'object.<>': 'Object<>',
|
||||
'Object.<>': 'Object<>',
|
||||
'object<>': 'Object<>',
|
||||
};
|
||||
|
||||
/** @type {import('./iterateJsdoc.js').PreferredTypes} */
|
||||
const preferredTypes = {
|
||||
...injectObjectPreferredTypes ?
|
||||
typeToInject :
|
||||
{},
|
||||
...preferredTypesOriginal,
|
||||
};
|
||||
|
||||
const
|
||||
/**
|
||||
* @type {{
|
||||
* noDefaults: boolean,
|
||||
* unifyParentAndChildTypeChecks: boolean,
|
||||
* exemptTagContexts: ({
|
||||
* tag: string,
|
||||
* types: true|string[]
|
||||
* })[]
|
||||
* }}
|
||||
*/ {
|
||||
exemptTagContexts = [],
|
||||
noDefaults,
|
||||
unifyParentAndChildTypeChecks,
|
||||
} = context.options[0] || {};
|
||||
|
||||
/**
|
||||
* Gets information about the preferred type: whether there is a matching
|
||||
* preferred type, what the type is, and whether it is a match to a generic.
|
||||
* @param {string} _type Not currently in use
|
||||
* @param {string} typeNodeName
|
||||
* @param {import('jsdoc-type-pratt-parser').NonRootResult|undefined} parentNode
|
||||
* @param {string|undefined} property
|
||||
* @returns {[hasMatchingPreferredType: boolean, typeName: string, isGenericMatch: boolean]}
|
||||
*/
|
||||
const getPreferredTypeInfo = (_type, typeNodeName, parentNode, property) => {
|
||||
let hasMatchingPreferredType = false;
|
||||
let isGenericMatch = false;
|
||||
let typName = typeNodeName;
|
||||
|
||||
const isNameOfGeneric = parentNode !== undefined && parentNode.type === 'JsdocTypeGeneric' && property === 'left';
|
||||
|
||||
const brackets = /** @type {import('jsdoc-type-pratt-parser').GenericResult} */ (
|
||||
parentNode
|
||||
)?.meta?.brackets;
|
||||
const dot = /** @type {import('jsdoc-type-pratt-parser').GenericResult} */ (
|
||||
parentNode
|
||||
)?.meta?.dot;
|
||||
|
||||
if (brackets === 'angle') {
|
||||
const checkPostFixes = dot ? [
|
||||
'.', '.<>',
|
||||
] : [
|
||||
'<>',
|
||||
];
|
||||
isGenericMatch = checkPostFixes.some((checkPostFix) => {
|
||||
const preferredType = preferredTypes?.[typeNodeName + checkPostFix];
|
||||
|
||||
// Does `unifyParentAndChildTypeChecks` need to be checked here?
|
||||
if (
|
||||
(unifyParentAndChildTypeChecks || isNameOfGeneric ||
|
||||
/* c8 ignore next 2 -- If checking `unifyParentAndChildTypeChecks` */
|
||||
(typeof preferredType === 'object' &&
|
||||
preferredType?.unifyParentAndChildTypeChecks)
|
||||
) &&
|
||||
preferredType !== undefined
|
||||
) {
|
||||
typName += checkPostFix;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!isGenericMatch && property &&
|
||||
/** @type {import('jsdoc-type-pratt-parser').NonRootResult} */ (
|
||||
parentNode
|
||||
).type === 'JsdocTypeGeneric'
|
||||
) {
|
||||
const checkPostFixes = dot ? [
|
||||
'.', '.<>',
|
||||
] : [
|
||||
brackets === 'angle' ? '<>' : '[]',
|
||||
];
|
||||
|
||||
isGenericMatch = checkPostFixes.some((checkPostFix) => {
|
||||
const preferredType = preferredTypes?.[checkPostFix];
|
||||
if (
|
||||
// Does `unifyParentAndChildTypeChecks` need to be checked here?
|
||||
(unifyParentAndChildTypeChecks || isNameOfGeneric ||
|
||||
/* c8 ignore next 2 -- If checking `unifyParentAndChildTypeChecks` */
|
||||
(typeof preferredType === 'object' &&
|
||||
preferredType?.unifyParentAndChildTypeChecks)) &&
|
||||
preferredType !== undefined
|
||||
) {
|
||||
typName = checkPostFix;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
const prefType = preferredTypes?.[typeNodeName];
|
||||
const directNameMatch = prefType !== undefined &&
|
||||
!Object.values(preferredTypes).includes(typeNodeName);
|
||||
const specificUnify = typeof prefType === 'object' &&
|
||||
prefType?.unifyParentAndChildTypeChecks;
|
||||
const unifiedSyntaxParentMatch = property && directNameMatch && (unifyParentAndChildTypeChecks || specificUnify);
|
||||
isGenericMatch = isGenericMatch || Boolean(unifiedSyntaxParentMatch);
|
||||
|
||||
hasMatchingPreferredType = isGenericMatch ||
|
||||
directNameMatch && !property;
|
||||
|
||||
return [
|
||||
hasMatchingPreferredType, typName, isGenericMatch,
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Collect invalid type info.
|
||||
* @param {string} type
|
||||
* @param {string} value
|
||||
* @param {string} tagName
|
||||
* @param {string} nameInTag
|
||||
* @param {number} idx
|
||||
* @param {string|undefined} property
|
||||
* @param {import('jsdoc-type-pratt-parser').NonRootResult} node
|
||||
* @param {import('jsdoc-type-pratt-parser').NonRootResult|undefined} parentNode
|
||||
* @param {(string|false|undefined)[][]} invalidTypes
|
||||
* @returns {void}
|
||||
*/
|
||||
const getInvalidTypes = (type, value, tagName, nameInTag, idx, property, node, parentNode, invalidTypes) => {
|
||||
let typeNodeName = type === 'JsdocTypeAny' ? '*' : value;
|
||||
|
||||
const [
|
||||
hasMatchingPreferredType,
|
||||
typName,
|
||||
isGenericMatch,
|
||||
] = getPreferredTypeInfo(type, typeNodeName, parentNode, property);
|
||||
|
||||
let preferred;
|
||||
let types;
|
||||
if (hasMatchingPreferredType) {
|
||||
const preferredSetting = preferredTypes[typName];
|
||||
typeNodeName = typName === '[]' ? typName : typeNodeName;
|
||||
|
||||
if (!preferredSetting) {
|
||||
invalidTypes.push([
|
||||
typeNodeName,
|
||||
]);
|
||||
} else if (typeof preferredSetting === 'string') {
|
||||
preferred = preferredSetting;
|
||||
invalidTypes.push([
|
||||
typeNodeName, preferred,
|
||||
]);
|
||||
} else if (preferredSetting && typeof preferredSetting === 'object') {
|
||||
const nextItem = preferredSetting.skipRootChecking && jsdocTagsWithPossibleType[idx + 1];
|
||||
|
||||
if (!nextItem || !nextItem.name.startsWith(`${nameInTag}.`)) {
|
||||
preferred = preferredSetting.replacement;
|
||||
invalidTypes.push([
|
||||
typeNodeName,
|
||||
preferred,
|
||||
preferredSetting.message,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
utils.reportSettings(
|
||||
'Invalid `settings.jsdoc.preferredTypes`. Values must be falsy, a string, or an object.',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
} else if (Object.entries(structuredTags).some(([
|
||||
tag,
|
||||
{
|
||||
type: typs,
|
||||
},
|
||||
]) => {
|
||||
types = typs;
|
||||
|
||||
return tag === tagName &&
|
||||
Array.isArray(types) &&
|
||||
!types.includes(typeNodeName);
|
||||
})) {
|
||||
invalidTypes.push([
|
||||
typeNodeName, types,
|
||||
]);
|
||||
} else if (checkNativeTypes && !noDefaults && type === 'JsdocTypeName') {
|
||||
preferred = checkNativeTypes(
|
||||
preferredTypes, typeNodeName, preferred, parentNode, invalidTypes,
|
||||
);
|
||||
}
|
||||
|
||||
// For fixer
|
||||
if (preferred) {
|
||||
adjustNames(type, preferred, isGenericMatch, typeNodeName, node, parentNode);
|
||||
}
|
||||
};
|
||||
|
||||
for (const [
|
||||
idx,
|
||||
jsdocTag,
|
||||
] of jsdocTagsWithPossibleType.entries()) {
|
||||
/** @type {(string|false|undefined)[][]} */
|
||||
const invalidTypes = [];
|
||||
let typeAst;
|
||||
|
||||
try {
|
||||
typeAst = mode === 'permissive' ? tryParse(jsdocTag.type) : parse(jsdocTag.type, mode);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
const {
|
||||
name: nameInTag,
|
||||
tag: tagName,
|
||||
} = jsdocTag;
|
||||
|
||||
traverse(typeAst, (node, parentNode, property) => {
|
||||
const {
|
||||
type,
|
||||
value,
|
||||
} =
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').NameResult}
|
||||
*/ (node);
|
||||
if (![
|
||||
'JsdocTypeAny', 'JsdocTypeName',
|
||||
].includes(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
getInvalidTypes(type, value, tagName, nameInTag, idx, property, node, parentNode, invalidTypes);
|
||||
});
|
||||
|
||||
if (invalidTypes.length) {
|
||||
const fixedType = stringify(typeAst);
|
||||
|
||||
/**
|
||||
* @type {import('eslint').Rule.ReportFixer}
|
||||
*/
|
||||
const fix = (fixer) => {
|
||||
return fixer.replaceText(
|
||||
jsdocNode,
|
||||
sourceCode.getText(jsdocNode).replace(
|
||||
`{${jsdocTag.type}}`,
|
||||
`{${fixedType}}`,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
for (const [
|
||||
badType,
|
||||
preferredType = '',
|
||||
msg,
|
||||
] of invalidTypes) {
|
||||
const tagValue = jsdocTag.name ? ` "${jsdocTag.name}"` : '';
|
||||
if (exemptTagContexts.some(({
|
||||
tag,
|
||||
types,
|
||||
}) => {
|
||||
return tag === tagName &&
|
||||
(types === true || types.includes(jsdocTag.type));
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
report(
|
||||
msg ||
|
||||
`Invalid JSDoc @${tagName}${tagValue} type "${badType}"` +
|
||||
(preferredType ? '; ' : '.') +
|
||||
(preferredType ? `prefer: ${JSON.stringify(preferredType)}.` : ''),
|
||||
preferredType ? fix : null,
|
||||
jsdocTag,
|
||||
msg ? {
|
||||
tagName,
|
||||
tagValue,
|
||||
} : undefined,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description,
|
||||
url,
|
||||
},
|
||||
...(!overrideSettings || (Object.values(overrideSettings).some((os) => {
|
||||
return os && typeof os === 'object' ?
|
||||
/* c8 ignore next -- Ok */
|
||||
os.replacement :
|
||||
typeof os === 'string';
|
||||
})) ?
|
||||
{
|
||||
fixable: 'code',
|
||||
} :
|
||||
{}
|
||||
),
|
||||
schema,
|
||||
type: 'suggestion',
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
169
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/defaultTagOrder.js
generated
vendored
Normal file
169
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/defaultTagOrder.js
generated
vendored
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
const defaultTagOrder = [
|
||||
{
|
||||
tags: [
|
||||
// Brief descriptions
|
||||
'summary',
|
||||
'typeSummary',
|
||||
|
||||
// Module/file-level
|
||||
'module',
|
||||
'exports',
|
||||
'file',
|
||||
'fileoverview',
|
||||
'overview',
|
||||
'import',
|
||||
|
||||
// Identifying (name, type)
|
||||
'template',
|
||||
'typedef',
|
||||
'interface',
|
||||
'record',
|
||||
'name',
|
||||
'kind',
|
||||
'type',
|
||||
'alias',
|
||||
'external',
|
||||
'host',
|
||||
'callback',
|
||||
'func',
|
||||
'function',
|
||||
'method',
|
||||
'class',
|
||||
'constructor',
|
||||
|
||||
// Relationships
|
||||
'modifies',
|
||||
'mixes',
|
||||
'mixin',
|
||||
'mixinClass',
|
||||
'mixinFunction',
|
||||
'namespace',
|
||||
'borrows',
|
||||
'constructs',
|
||||
'lends',
|
||||
'implements',
|
||||
'requires',
|
||||
|
||||
// Long descriptions
|
||||
'desc',
|
||||
'description',
|
||||
'classdesc',
|
||||
'tutorial',
|
||||
'copyright',
|
||||
'license',
|
||||
|
||||
// Simple annotations
|
||||
|
||||
// TypeScript
|
||||
'internal',
|
||||
'overload',
|
||||
|
||||
'const',
|
||||
'constant',
|
||||
'final',
|
||||
'global',
|
||||
'readonly',
|
||||
'abstract',
|
||||
'virtual',
|
||||
'var',
|
||||
'member',
|
||||
'memberof',
|
||||
'memberof!',
|
||||
'inner',
|
||||
'instance',
|
||||
'inheritdoc',
|
||||
'inheritDoc',
|
||||
'override',
|
||||
'hideconstructor',
|
||||
|
||||
// Core function/object info
|
||||
'param',
|
||||
'arg',
|
||||
'argument',
|
||||
'prop',
|
||||
'property',
|
||||
'return',
|
||||
'returns',
|
||||
|
||||
// Important behavior details
|
||||
'async',
|
||||
'generator',
|
||||
'default',
|
||||
'defaultvalue',
|
||||
'enum',
|
||||
'augments',
|
||||
'extends',
|
||||
'throws',
|
||||
'exception',
|
||||
'yield',
|
||||
'yields',
|
||||
'event',
|
||||
'fires',
|
||||
'emits',
|
||||
'listens',
|
||||
'this',
|
||||
|
||||
// TypeScript
|
||||
'satisfies',
|
||||
|
||||
// Access
|
||||
'static',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'access',
|
||||
'package',
|
||||
|
||||
'-other',
|
||||
|
||||
// Supplementary descriptions
|
||||
'see',
|
||||
'example',
|
||||
|
||||
// METADATA
|
||||
|
||||
// Other Closure (undocumented) metadata
|
||||
'closurePrimitive',
|
||||
'customElement',
|
||||
'expose',
|
||||
'hidden',
|
||||
'idGenerator',
|
||||
'meaning',
|
||||
'ngInject',
|
||||
'owner',
|
||||
'wizaction',
|
||||
|
||||
// Other Closure (documented) metadata
|
||||
'define',
|
||||
'dict',
|
||||
'export',
|
||||
'externs',
|
||||
'implicitCast',
|
||||
'noalias',
|
||||
'nocollapse',
|
||||
'nocompile',
|
||||
'noinline',
|
||||
'nosideeffects',
|
||||
'polymer',
|
||||
'polymerBehavior',
|
||||
'preserve',
|
||||
'struct',
|
||||
'suppress',
|
||||
'unrestricted',
|
||||
|
||||
// @homer0/prettier-plugin-jsdoc metadata
|
||||
'category',
|
||||
|
||||
// Non-Closure metadata
|
||||
'ignore',
|
||||
'author',
|
||||
'version',
|
||||
'variation',
|
||||
'since',
|
||||
'deprecated',
|
||||
'todo',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default defaultTagOrder;
|
||||
973
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/exportParser.js
generated
vendored
Normal file
973
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/exportParser.js
generated
vendored
Normal file
|
|
@ -0,0 +1,973 @@
|
|||
import {
|
||||
findJSDocComment,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
import debugModule from 'debug';
|
||||
|
||||
const debug = debugModule('requireExportJsdoc');
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* value: string
|
||||
* }} ValueObject
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* type?: string,
|
||||
* value?: ValueObject|import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node,
|
||||
* props: {
|
||||
* [key: string]: CreatedNode|null,
|
||||
* },
|
||||
* special?: true,
|
||||
* globalVars?: CreatedNode,
|
||||
* exported?: boolean,
|
||||
* ANONYMOUS_DEFAULT?: import('eslint').Rule.Node
|
||||
* }} CreatedNode
|
||||
*/
|
||||
|
||||
/**
|
||||
* @returns {CreatedNode}
|
||||
*/
|
||||
const createNode = function () {
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {CreatedNode|null} symbol
|
||||
* @returns {string|null}
|
||||
*/
|
||||
const getSymbolValue = function (symbol) {
|
||||
/* c8 ignore next 3 */
|
||||
if (!symbol) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* c8 ignore else */
|
||||
if (symbol.type === 'literal') {
|
||||
return /** @type {ValueObject} */ (symbol.value).value;
|
||||
}
|
||||
/* c8 ignore next 2 */
|
||||
// eslint-disable-next-line @stylistic/padding-line-between-statements -- c8
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('estree').Identifier} node
|
||||
* @param {CreatedNode} globals
|
||||
* @param {CreatedNode} scope
|
||||
* @param {SymbolOptions} opts
|
||||
* @returns {CreatedNode|null}
|
||||
*/
|
||||
const getIdentifier = function (node, globals, scope, opts) {
|
||||
if (opts.simpleIdentifier) {
|
||||
// Type is Identier for noncomputed properties
|
||||
const identifierLiteral = createNode();
|
||||
identifierLiteral.type = 'literal';
|
||||
identifierLiteral.value = {
|
||||
value: node.name,
|
||||
};
|
||||
|
||||
return identifierLiteral;
|
||||
}
|
||||
|
||||
/* c8 ignore next */
|
||||
const block = scope || globals;
|
||||
|
||||
// As scopes are not currently supported, they are not traversed upwards recursively
|
||||
if (block.props[node.name]) {
|
||||
return block.props[node.name];
|
||||
}
|
||||
|
||||
// Seems this will only be entered once scopes added and entered
|
||||
/* c8 ignore next 3 */
|
||||
if (globals.props[node.name]) {
|
||||
return globals.props[node.name];
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback CreateSymbol
|
||||
* @param {import('eslint').Rule.Node|null} node
|
||||
* @param {CreatedNode} globals
|
||||
* @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node|null} value
|
||||
* @param {CreatedNode} [scope]
|
||||
* @param {boolean|SymbolOptions} [isGlobal]
|
||||
* @returns {CreatedNode|null}
|
||||
*/
|
||||
|
||||
/** @type {CreateSymbol} */
|
||||
let createSymbol; // eslint-disable-line prefer-const
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* simpleIdentifier?: boolean
|
||||
* }} SymbolOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node} node
|
||||
* @param {CreatedNode} globals
|
||||
* @param {CreatedNode} scope
|
||||
* @param {SymbolOptions} [opt]
|
||||
* @returns {CreatedNode|null}
|
||||
*/
|
||||
const getSymbol = function (node, globals, scope, opt) {
|
||||
const opts = opt || {};
|
||||
/* c8 ignore next */
|
||||
switch (node.type) {
|
||||
/* c8 ignore next 4 -- No longer needed? */
|
||||
case 'ArrowFunctionExpression':
|
||||
|
||||
// Fallthrough
|
||||
case 'ClassDeclaration':
|
||||
|
||||
case 'FunctionDeclaration':
|
||||
|
||||
case 'FunctionExpression':
|
||||
case 'TSEnumDeclaration':
|
||||
case 'TSInterfaceDeclaration':
|
||||
case 'TSTypeAliasDeclaration': {
|
||||
const val = createNode();
|
||||
val.props.prototype = createNode();
|
||||
val.props.prototype.type = 'object';
|
||||
val.type = 'object';
|
||||
val.value = node;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
case 'AssignmentExpression': {
|
||||
return createSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.left),
|
||||
globals,
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.right),
|
||||
scope,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
|
||||
case 'ClassBody': {
|
||||
const val = createNode();
|
||||
for (const method of node.body) {
|
||||
// StaticBlock
|
||||
if (!('key' in method)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
val.props[
|
||||
/** @type {import('estree').Identifier} */ (
|
||||
/** @type {import('estree').MethodDefinition} */ (
|
||||
method
|
||||
).key
|
||||
).name
|
||||
] = createNode();
|
||||
/** @type {{[key: string]: CreatedNode}} */ (val.props)[
|
||||
/** @type {import('estree').Identifier} */ (
|
||||
/** @type {import('estree').MethodDefinition} */ (
|
||||
method
|
||||
).key
|
||||
).name
|
||||
].type = 'object';
|
||||
/** @type {{[key: string]: CreatedNode}} */ (val.props)[
|
||||
/** @type {import('estree').Identifier} */ (
|
||||
/** @type {import('estree').MethodDefinition} */ (
|
||||
method
|
||||
).key
|
||||
).name
|
||||
].value = /** @type {import('eslint').Rule.Node} */ (
|
||||
/** @type {import('estree').MethodDefinition} */ (method).value
|
||||
);
|
||||
}
|
||||
|
||||
val.type = 'object';
|
||||
val.value = node.parent;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
case 'ClassExpression': {
|
||||
return getSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.body),
|
||||
globals,
|
||||
scope,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
|
||||
case 'Identifier': {
|
||||
return getIdentifier(node, globals, scope, opts);
|
||||
}
|
||||
|
||||
case 'Literal': {
|
||||
const val = createNode();
|
||||
val.type = 'literal';
|
||||
val.value = node;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
case 'MemberExpression': {
|
||||
const obj = getSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.object),
|
||||
globals,
|
||||
scope,
|
||||
opts,
|
||||
);
|
||||
const propertySymbol = getSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.property),
|
||||
globals,
|
||||
scope,
|
||||
{
|
||||
simpleIdentifier: !node.computed,
|
||||
},
|
||||
);
|
||||
const propertyValue = getSymbolValue(propertySymbol);
|
||||
|
||||
/* c8 ignore else */
|
||||
if (obj && propertyValue && obj.props[propertyValue]) {
|
||||
const block = obj.props[propertyValue];
|
||||
|
||||
return block;
|
||||
}
|
||||
/* c8 ignore next 11 */
|
||||
/*
|
||||
if (opts.createMissingProps && propertyValue) {
|
||||
obj.props[propertyValue] = createNode();
|
||||
|
||||
return obj.props[propertyValue];
|
||||
}
|
||||
*/
|
||||
// eslint-disable-next-line @stylistic/padding-line-between-statements -- c8
|
||||
debug(`MemberExpression: Missing property ${
|
||||
/** @type {import('estree').PrivateIdentifier} */ (node.property).name
|
||||
}`);
|
||||
/* c8 ignore next 2 */
|
||||
return null;
|
||||
}
|
||||
|
||||
case 'ObjectExpression': {
|
||||
const val = createNode();
|
||||
val.type = 'object';
|
||||
for (const prop of node.properties) {
|
||||
if ([
|
||||
// @babel/eslint-parser
|
||||
'ExperimentalSpreadProperty',
|
||||
|
||||
// typescript-eslint, espree, acorn, etc.
|
||||
'SpreadElement',
|
||||
].includes(prop.type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const propVal = getSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */ (
|
||||
/** @type {import('estree').Property} */
|
||||
(prop).value
|
||||
),
|
||||
globals,
|
||||
scope,
|
||||
opts,
|
||||
);
|
||||
/* c8 ignore next 8 */
|
||||
if (propVal) {
|
||||
val.props[
|
||||
/** @type {import('estree').PrivateIdentifier} */
|
||||
(
|
||||
/** @type {import('estree').Property} */ (prop).key
|
||||
).name
|
||||
] = propVal;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
/* c8 ignore next 2 */
|
||||
// eslint-disable-next-line @stylistic/padding-line-between-statements -- c8
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {CreatedNode} block
|
||||
* @param {string} name
|
||||
* @param {CreatedNode|null} value
|
||||
* @param {CreatedNode} globals
|
||||
* @param {boolean|SymbolOptions|undefined} isGlobal
|
||||
* @returns {void}
|
||||
*/
|
||||
const createBlockSymbol = function (block, name, value, globals, isGlobal) {
|
||||
block.props[name] = value;
|
||||
if (isGlobal && globals.props.window && globals.props.window.special) {
|
||||
globals.props.window.props[name] = value;
|
||||
}
|
||||
};
|
||||
|
||||
createSymbol = function (node, globals, value, scope, isGlobal) {
|
||||
const block = scope || globals;
|
||||
/* c8 ignore next 3 */
|
||||
if (!node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let symbol;
|
||||
switch (node.type) {
|
||||
case 'ClassDeclaration':
|
||||
/* c8 ignore next */
|
||||
// @ts-expect-error TS OK
|
||||
// Fall through
|
||||
case 'FunctionDeclaration': case 'TSEnumDeclaration':
|
||||
/* c8 ignore next */
|
||||
// @ts-expect-error TS OK
|
||||
// Fall through
|
||||
case 'TSInterfaceDeclaration': case 'TSTypeAliasDeclaration': {
|
||||
const nde = /** @type {import('estree').ClassDeclaration} */ (node);
|
||||
/* c8 ignore else */
|
||||
if (nde.id && nde.id.type === 'Identifier') {
|
||||
return createSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */ (nde.id),
|
||||
globals,
|
||||
node,
|
||||
globals,
|
||||
);
|
||||
}
|
||||
/* c8 ignore next 3 */
|
||||
// eslint-disable-next-line @stylistic/padding-line-between-statements -- c8
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Identifier': {
|
||||
const nde = /** @type {import('estree').Identifier} */ (node);
|
||||
if (value) {
|
||||
const valueSymbol = getSymbol(value, globals, block);
|
||||
/* c8 ignore else */
|
||||
if (valueSymbol) {
|
||||
createBlockSymbol(block, nde.name, valueSymbol, globals, isGlobal);
|
||||
|
||||
return block.props[nde.name];
|
||||
}
|
||||
/* c8 ignore next 2 */
|
||||
// eslint-disable-next-line @stylistic/padding-line-between-statements -- c8
|
||||
debug('Identifier: Missing value symbol for %s', nde.name);
|
||||
} else {
|
||||
createBlockSymbol(block, nde.name, createNode(), globals, isGlobal);
|
||||
|
||||
return block.props[nde.name];
|
||||
}
|
||||
/* c8 ignore next 3 */
|
||||
// eslint-disable-next-line @stylistic/padding-line-between-statements -- c8
|
||||
break;
|
||||
}
|
||||
|
||||
case 'MemberExpression': {
|
||||
const nde = /** @type {import('estree').MemberExpression} */ (node);
|
||||
symbol = getSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */ (nde.object), globals, block,
|
||||
);
|
||||
|
||||
const propertySymbol = getSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */ (nde.property),
|
||||
globals,
|
||||
block,
|
||||
{
|
||||
simpleIdentifier: !nde.computed,
|
||||
},
|
||||
);
|
||||
const propertyValue = getSymbolValue(propertySymbol);
|
||||
if (symbol && propertyValue) {
|
||||
createBlockSymbol(symbol, propertyValue, getSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(value), globals, block,
|
||||
), globals, isGlobal);
|
||||
return symbol.props[propertyValue];
|
||||
}
|
||||
|
||||
debug(
|
||||
'MemberExpression: Missing symbol: %s',
|
||||
/** @type {import('estree').Identifier} */ (
|
||||
nde.property
|
||||
).name,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates variables from variable definitions
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {CreatedNode} globals
|
||||
* @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opts
|
||||
* @returns {void}
|
||||
*/
|
||||
const initVariables = function (node, globals, opts) {
|
||||
switch (node.type) {
|
||||
case 'ExportNamedDeclaration': {
|
||||
if (node.declaration) {
|
||||
initVariables(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.declaration),
|
||||
globals,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ExpressionStatement': {
|
||||
initVariables(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.expression),
|
||||
globals,
|
||||
opts,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Program': {
|
||||
for (const childNode of node.body) {
|
||||
initVariables(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(childNode),
|
||||
globals,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'VariableDeclaration': {
|
||||
for (const declaration of node.declarations) {
|
||||
// let and const
|
||||
const symbol = createSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(declaration.id),
|
||||
globals,
|
||||
null,
|
||||
globals,
|
||||
);
|
||||
if (opts.initWindow && node.kind === 'var' && globals.props.window) {
|
||||
// If var, also add to window
|
||||
globals.props.window.props[
|
||||
/** @type {import('estree').Identifier} */
|
||||
(declaration.id).name
|
||||
] = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Populates variable maps using AST
|
||||
* @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node} node
|
||||
* @param {CreatedNode} globals
|
||||
* @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opt
|
||||
* @param {true} [isExport]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const mapVariables = function (node, globals, opt, isExport) {
|
||||
/* c8 ignore next */
|
||||
const opts = opt || {};
|
||||
/* c8 ignore next */
|
||||
switch (node.type) {
|
||||
case 'AssignmentExpression': {
|
||||
createSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.left),
|
||||
globals,
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.right),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ClassDeclaration': {
|
||||
createSymbol(
|
||||
/** @type {import('eslint').Rule.Node|null} */ (node.id),
|
||||
globals,
|
||||
/** @type {import('eslint').Rule.Node} */ (node.body),
|
||||
globals,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ExportDefaultDeclaration': {
|
||||
const symbol = createSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.declaration),
|
||||
globals,
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.declaration),
|
||||
);
|
||||
if (symbol) {
|
||||
symbol.exported = true;
|
||||
/* c8 ignore next 6 */
|
||||
} else {
|
||||
// if (!node.id) {
|
||||
globals.ANONYMOUS_DEFAULT = /** @type {import('eslint').Rule.Node} */ (
|
||||
node.declaration
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ExportNamedDeclaration': {
|
||||
if (node.declaration) {
|
||||
if (node.declaration.type === 'VariableDeclaration') {
|
||||
mapVariables(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.declaration),
|
||||
globals,
|
||||
opts,
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
const symbol = createSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.declaration),
|
||||
globals,
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.declaration),
|
||||
);
|
||||
/* c8 ignore next 3 */
|
||||
if (symbol) {
|
||||
symbol.exported = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const specifier of node.specifiers) {
|
||||
mapVariables(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(specifier),
|
||||
globals,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ExportSpecifier': {
|
||||
const symbol = getSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.local),
|
||||
globals,
|
||||
globals,
|
||||
);
|
||||
/* c8 ignore next 3 */
|
||||
if (symbol) {
|
||||
symbol.exported = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ExpressionStatement': {
|
||||
mapVariables(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.expression),
|
||||
globals,
|
||||
opts,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'FunctionDeclaration':
|
||||
|
||||
case 'TSTypeAliasDeclaration': {
|
||||
/* c8 ignore next 10 */
|
||||
if (/** @type {import('estree').Identifier} */ (node.id).type === 'Identifier') {
|
||||
createSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(node.id),
|
||||
globals,
|
||||
node,
|
||||
globals,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Program': {
|
||||
if (opts.ancestorsOnly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const childNode of node.body) {
|
||||
mapVariables(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(childNode),
|
||||
globals,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'VariableDeclaration': {
|
||||
for (const declaration of node.declarations) {
|
||||
const isGlobal = Boolean(opts.initWindow && node.kind === 'var' && globals.props.window);
|
||||
const symbol = createSymbol(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(declaration.id),
|
||||
globals,
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(declaration.init),
|
||||
globals,
|
||||
isGlobal,
|
||||
);
|
||||
if (symbol && isExport) {
|
||||
symbol.exported = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
/* c8 ignore next */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {CreatedNode|ValueObject|string|undefined|
|
||||
* import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node} block
|
||||
* @param {(CreatedNode|ValueObject|string|
|
||||
* import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node)[]} [cache]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const findNode = function (node, block, cache) {
|
||||
let blockCache = cache || [];
|
||||
if (!block || blockCache.includes(block)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
blockCache = blockCache.slice();
|
||||
blockCache.push(block);
|
||||
|
||||
if (
|
||||
typeof block === 'object' &&
|
||||
'type' in block &&
|
||||
(block.type === 'object' || block.type === 'MethodDefinition') &&
|
||||
block.value === node
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof block !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const props = ('props' in block && block.props) || ('body' in block && block.body);
|
||||
for (const propval of Object.values(props || {})) {
|
||||
if (Array.isArray(propval)) {
|
||||
/* c8 ignore next 5 */
|
||||
if (propval.some((val) => {
|
||||
return findNode(node, val, blockCache);
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
} else if (findNode(node, propval, blockCache)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const exportTypes = new Set([
|
||||
'ExportDefaultDeclaration', 'ExportNamedDeclaration',
|
||||
]);
|
||||
const ignorableNestedTypes = new Set([
|
||||
'ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression',
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.Node} nde
|
||||
* @returns {import('eslint').Rule.Node|false}
|
||||
*/
|
||||
const getExportAncestor = function (nde) {
|
||||
/** @type {import('eslint').Rule.Node|null} */
|
||||
let node = nde;
|
||||
let idx = 0;
|
||||
const ignorableIfDeep = ignorableNestedTypes.has(nde?.type);
|
||||
while (node) {
|
||||
// Ignore functions nested more deeply than say `export default function () {}`
|
||||
if (idx >= 2 && ignorableIfDeep) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (exportTypes.has(node.type)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
node = node.parent;
|
||||
idx++;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const canBeExportedByAncestorType = new Set([
|
||||
'ClassProperty',
|
||||
'Method',
|
||||
'PropertyDefinition',
|
||||
'TSMethodSignature',
|
||||
'TSPropertySignature',
|
||||
]);
|
||||
|
||||
const canExportChildrenType = new Set([
|
||||
'ClassBody',
|
||||
'ClassDeclaration',
|
||||
'ClassDefinition',
|
||||
'ClassExpression',
|
||||
'Program',
|
||||
'TSInterfaceBody',
|
||||
'TSInterfaceDeclaration',
|
||||
'TSTypeAliasDeclaration',
|
||||
'TSTypeLiteral',
|
||||
'TSTypeParameterInstantiation',
|
||||
'TSTypeReference',
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.Node} nde
|
||||
* @returns {false|import('eslint').Rule.Node}
|
||||
*/
|
||||
const isExportByAncestor = function (nde) {
|
||||
if (!canBeExportedByAncestorType.has(nde.type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let node = nde.parent;
|
||||
while (node) {
|
||||
if (exportTypes.has(node.type)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (!canExportChildrenType.has(node.type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {CreatedNode} block
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {CreatedNode[]} [cache] Currently unused
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const findExportedNode = function (block, node, cache) {
|
||||
/* c8 ignore next 3 */
|
||||
if (block === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const blockCache = cache || [];
|
||||
const {
|
||||
props,
|
||||
} = block;
|
||||
for (const propval of Object.values(props)) {
|
||||
const pval = /** @type {CreatedNode} */ (propval);
|
||||
blockCache.push(pval);
|
||||
if (pval.exported && (node === pval.value || findNode(node, pval.value))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// No need to check `propval` for exported nodes as ESM
|
||||
// exports are only global
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {CreatedNode} globals
|
||||
* @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opt
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isNodeExported = function (node, globals, opt) {
|
||||
const moduleExports = globals.props.module?.props?.exports;
|
||||
if (
|
||||
opt.initModuleExports && moduleExports && findNode(node, moduleExports)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (opt.initWindow && globals.props.window && findNode(node, globals.props.window)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (opt.esm && findExportedNode(globals, node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {CreatedNode} globalVars
|
||||
* @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opts
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const parseRecursive = function (node, globalVars, opts) {
|
||||
// Iterate from top using recursion - stop at first processed node from top
|
||||
if (node.parent && parseRecursive(node.parent, globalVars, opts)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return mapVariables(node, globalVars, opts);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('eslint').Rule.Node} ast
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opt
|
||||
* @returns {CreatedNode}
|
||||
*/
|
||||
const parse = function (ast, node, opt) {
|
||||
/* c8 ignore next 6 */
|
||||
const opts = opt || {
|
||||
ancestorsOnly: false,
|
||||
esm: true,
|
||||
initModuleExports: true,
|
||||
initWindow: true,
|
||||
};
|
||||
|
||||
const globalVars = createNode();
|
||||
if (opts.initModuleExports) {
|
||||
globalVars.props.module = createNode();
|
||||
globalVars.props.module.props.exports = createNode();
|
||||
globalVars.props.exports = globalVars.props.module.props.exports;
|
||||
}
|
||||
|
||||
if (opts.initWindow) {
|
||||
globalVars.props.window = createNode();
|
||||
globalVars.props.window.special = true;
|
||||
}
|
||||
|
||||
if (opts.ancestorsOnly) {
|
||||
parseRecursive(node, globalVars, opts);
|
||||
} else {
|
||||
initVariables(ast, globalVars, opts);
|
||||
mapVariables(ast, globalVars, opts);
|
||||
}
|
||||
|
||||
return {
|
||||
globalVars,
|
||||
props: {},
|
||||
};
|
||||
};
|
||||
|
||||
const accessibilityNodes = new Set([
|
||||
'MethodDefinition',
|
||||
'PropertyDefinition',
|
||||
]);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isPrivate = (node) => {
|
||||
return accessibilityNodes.has(node.type) &&
|
||||
(
|
||||
'accessibility' in node &&
|
||||
node.accessibility !== 'public' && node.accessibility !== undefined
|
||||
) ||
|
||||
'key' in node &&
|
||||
node.key.type === 'PrivateIdentifier';
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {import('eslint').SourceCode} sourceCode
|
||||
* @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opt
|
||||
* @param {import('./iterateJsdoc.js').Settings} settings
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isUncommentedExport = function (node, sourceCode, opt, settings) {
|
||||
// console.log({node});
|
||||
// Optimize with ancestor check for esm
|
||||
if (opt.esm) {
|
||||
if (isPrivate(node) ||
|
||||
node.parent && isPrivate(node.parent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const exportNode = getExportAncestor(node);
|
||||
|
||||
// Is export node comment
|
||||
if (exportNode && !findJSDocComment(exportNode, sourceCode, settings)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some typescript types are not in variable map, but inherit exported (interface property and method)
|
||||
*/
|
||||
if (
|
||||
isExportByAncestor(node) &&
|
||||
!findJSDocComment(node, sourceCode, settings)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const ast = /** @type {unknown} */ (sourceCode.ast);
|
||||
|
||||
const parseResult = parse(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(ast),
|
||||
node,
|
||||
opt,
|
||||
);
|
||||
|
||||
return isNodeExported(
|
||||
node, /** @type {CreatedNode} */ (parseResult.globalVars), opt,
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
isUncommentedExport,
|
||||
parse,
|
||||
};
|
||||
968
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/getDefaultTagStructureForMode.js
generated
vendored
Normal file
968
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/getDefaultTagStructureForMode.js
generated
vendored
Normal file
|
|
@ -0,0 +1,968 @@
|
|||
/**
|
||||
* @typedef {Map<string, Map<string, (string|boolean)>>} TagStructure
|
||||
*/
|
||||
/**
|
||||
* @param {import('./jsdocUtils.js').ParserMode} mode
|
||||
* @returns {TagStructure}
|
||||
*/
|
||||
const getDefaultTagStructureForMode = (mode) => {
|
||||
const isJsdoc = mode === 'jsdoc';
|
||||
const isClosure = mode === 'closure';
|
||||
const isTypescript = mode === 'typescript';
|
||||
const isPermissive = mode === 'permissive';
|
||||
|
||||
const isJsdocOrPermissive = isJsdoc || isPermissive;
|
||||
const isJsdocOrTypescript = isJsdoc || isTypescript;
|
||||
const isTypescriptOrClosure = isTypescript || isClosure;
|
||||
const isClosureOrPermissive = isClosure || isPermissive;
|
||||
const isJsdocTypescriptOrPermissive = isJsdocOrTypescript || isPermissive;
|
||||
|
||||
// Properties:
|
||||
// `namepathRole` - 'namepath-referencing'|'name-defining'|'namepath-defining'|'namepath-or-url-referencing'|'text'|false
|
||||
// `typeAllowed` - boolean
|
||||
// `nameRequired` - boolean
|
||||
// `typeRequired` - boolean
|
||||
// `typeOrNameRequired` - boolean
|
||||
|
||||
// All of `typeAllowed` have a signature with "type" except for
|
||||
// `augments`/`extends` ("namepath")
|
||||
// `param`/`arg`/`argument` (no signature)
|
||||
// `property`/`prop` (no signature)
|
||||
// `modifies` (undocumented)
|
||||
|
||||
// None of the `namepathRole: 'namepath-defining'` show as having curly
|
||||
// brackets for their name/namepath
|
||||
|
||||
// Among `namepath-defining` and `namepath-referencing`, these do not seem
|
||||
// to allow curly brackets in their doc signature or examples (`modifies`
|
||||
// references namepaths within its type brackets)
|
||||
|
||||
// Todo: Should support a `tutorialID` type (for `@tutorial` block and
|
||||
// inline)
|
||||
|
||||
/**
|
||||
* @type {TagStructure}
|
||||
*/
|
||||
return new Map([
|
||||
[
|
||||
'alias', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "namepath" (and no counter-examples)
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// "namepath"
|
||||
[
|
||||
'typeOrNameRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'arg', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// See `param`
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
// Has no formal signature in the docs but shows curly brackets
|
||||
// in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'argument', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// See `param`
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
// Has no formal signature in the docs but shows curly brackets
|
||||
// in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'augments', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "namepath" (and no counter-examples)
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
// Does not show curly brackets in either the signature or examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
|
||||
// "namepath"
|
||||
[
|
||||
'typeOrNameRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'borrows', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// `borrows` has a different format, however, so needs special parsing;
|
||||
// seems to require both, and as "namepath"'s
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
// "namepath"
|
||||
[
|
||||
'typeOrNameRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'callback', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Seems to require a "namepath" in the signature (with no
|
||||
// counter-examples); TypeScript does not enforce but seems
|
||||
// problematic as not attached so presumably not useable without it
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// "namepath"
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'class', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Not in use, but should be this value if using to power `empty-tags`
|
||||
[
|
||||
'nameAllowed', true,
|
||||
],
|
||||
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'const', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
[
|
||||
'constant', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
[
|
||||
'constructor', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'constructs', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
[
|
||||
'nameRequired', false,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'define', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'typeRequired', isClosure,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'emits', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "name" (of an event) and no counter-examples
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'enum', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'namepathRole', 'name-defining',
|
||||
],
|
||||
// Has example showing curly brackets but not in doc signature
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'event', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Appears to require a "name" in its signature, albeit somewhat
|
||||
// different from other "name"'s (including as described
|
||||
// at https://jsdoc.app/about-namepaths.html )
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// The doc signature of `event` seems to require a "name"
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'exception', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows curly brackets in the signature and in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
// Closure
|
||||
[
|
||||
'export', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'typeAllowed', isClosureOrPermissive,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'exports', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
[
|
||||
'nameRequired', isJsdoc,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', isClosureOrPermissive,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'extends', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "namepath" (and no counter-examples)
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
[
|
||||
'nameRequired', isJsdoc,
|
||||
],
|
||||
|
||||
// Does not show curly brackets in either the signature or examples
|
||||
[
|
||||
'typeAllowed', isTypescriptOrClosure || isPermissive,
|
||||
],
|
||||
|
||||
// "namepath"
|
||||
[
|
||||
'typeOrNameRequired', isTypescriptOrClosure || isPermissive,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'external', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Appears to require a "name" in its signature, albeit somewhat
|
||||
// different from other "name"'s (including as described
|
||||
// at https://jsdoc.app/about-namepaths.html )
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// "name" (and a special syntax for the `external` name)
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'fires', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "name" (of an event) and no
|
||||
// counter-examples
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'func', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
])),
|
||||
],
|
||||
[
|
||||
'function', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
[
|
||||
'nameRequired', false,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'host', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Appears to require a "name" in its signature, albeit somewhat
|
||||
// different from other "name"'s (including as described
|
||||
// at https://jsdoc.app/about-namepaths.html )
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// See `external`
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'implements', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows curly brackets in the doc signature and examples
|
||||
// "typeExpression"
|
||||
[
|
||||
'typeRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'interface', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Not in use, but should be this value if using to power `empty-tags`
|
||||
[
|
||||
'nameAllowed', isClosure,
|
||||
],
|
||||
|
||||
// Allows for "name" in signature, but indicates as optional
|
||||
[
|
||||
'namepathRole',
|
||||
isJsdocTypescriptOrPermissive ? 'namepath-defining' : false,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'internal', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Not in use, but should be this value if using to power `empty-tags`
|
||||
[
|
||||
'nameAllowed', false,
|
||||
],
|
||||
// https://www.typescriptlang.org/tsconfig/#stripInternal
|
||||
[
|
||||
'namepathRole', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'lends', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "namepath" (and no counter-examples)
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
// "namepath"
|
||||
[
|
||||
'typeOrNameRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'link', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a namepath OR URL and might be checked as such.
|
||||
[
|
||||
'namepathRole', 'namepath-or-url-referencing',
|
||||
],
|
||||
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'linkcode', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Synonym for "link"
|
||||
// Signature seems to require a namepath OR URL and might be checked as such.
|
||||
[
|
||||
'namepathRole', 'namepath-or-url-referencing',
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'linkplain', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Synonym for "link"
|
||||
// Signature seems to require a namepath OR URL and might be checked as such.
|
||||
[
|
||||
'namepathRole', 'namepath-or-url-referencing',
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'listens', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "name" (of an event) and no
|
||||
// counter-examples
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'member', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// Has example showing curly brackets but not in doc signature
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'memberof!', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "namepath" (and no counter-examples),
|
||||
// though it allows an incomplete namepath ending with connecting symbol
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
// "namepath"
|
||||
[
|
||||
'typeOrNameRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
[
|
||||
'memberof', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "namepath" (and no counter-examples),
|
||||
// though it allows an incomplete namepath ending with connecting symbol
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
// "namepath"
|
||||
[
|
||||
'typeOrNameRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'method', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
])),
|
||||
],
|
||||
[
|
||||
'mixes', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "OtherObjectPath" with no
|
||||
// counter-examples
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
// "OtherObjectPath"
|
||||
[
|
||||
'typeOrNameRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'mixin', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
[
|
||||
'nameRequired', false,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'modifies', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Has no documentation, but test example has curly brackets, and
|
||||
// "name" would be suggested rather than "namepath" based on example;
|
||||
// not sure if name is required
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'module', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Optional "name" and no curly brackets
|
||||
// this block impacts `no-undefined-types` and `valid-types` (search for
|
||||
// "isNameOrNamepathDefiningTag|tagMightHaveNameOrNamepath|tagMightHaveEitherTypeOrNamePosition")
|
||||
[
|
||||
'namepathRole', isJsdoc ? 'namepath-defining' : 'text',
|
||||
],
|
||||
|
||||
// Shows the signature with curly brackets but not in the example
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'name', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Seems to require a "namepath" in the signature (with no
|
||||
// counter-examples)
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// "namepath"
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
// "namepath"
|
||||
[
|
||||
'typeOrNameRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'namespace', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// Shows the signature with curly brackets but not in the example
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
[
|
||||
'package', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows the signature with curly brackets but not in the example
|
||||
// "typeExpression"
|
||||
[
|
||||
'typeAllowed', isClosureOrPermissive,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'param', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// Though no signature provided requiring, per
|
||||
// https://jsdoc.app/tags-param.html:
|
||||
// "The @param tag requires you to specify the name of the parameter you
|
||||
// are documenting."
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
// Has no formal signature in the docs but shows curly brackets
|
||||
// in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'private', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows the signature with curly brackets but not in the example
|
||||
// "typeExpression"
|
||||
[
|
||||
'typeAllowed', isClosureOrPermissive,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'prop', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// See `property`
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
// Has no formal signature in the docs but shows curly brackets
|
||||
// in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'property', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// No docs indicate required, but since parallel to `param`, we treat as
|
||||
// such:
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
// Has no formal signature in the docs but shows curly brackets
|
||||
// in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'protected', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows the signature with curly brackets but not in the example
|
||||
// "typeExpression"
|
||||
[
|
||||
'typeAllowed', isClosureOrPermissive,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'public', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Does not show a signature nor show curly brackets in the example
|
||||
[
|
||||
'typeAllowed', isClosureOrPermissive,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'requires', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// <someModuleName>
|
||||
[
|
||||
'namepathRole', 'namepath-referencing',
|
||||
],
|
||||
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'return', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows curly brackets in the signature and in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
[
|
||||
'returns', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows curly brackets in the signature and in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'satisfies', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows curly brackets in the doc signature and examples
|
||||
[
|
||||
'typeRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'see', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature allows for "namepath" or text, so user must configure to
|
||||
// 'namepath-referencing' to enforce checks
|
||||
[
|
||||
'namepathRole', 'text',
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'static', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Does not show a signature nor show curly brackets in the example
|
||||
[
|
||||
'typeAllowed', isClosureOrPermissive,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'suppress', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'namepathRole', !isClosure,
|
||||
],
|
||||
[
|
||||
'typeRequired', isClosure,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'template', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
[
|
||||
'namepathRole', isJsdoc ? 'text' : 'namepath-referencing',
|
||||
],
|
||||
|
||||
[
|
||||
'nameRequired', !isJsdoc,
|
||||
],
|
||||
|
||||
// Though defines `namepathRole: 'namepath-defining'` in a sense, it is
|
||||
// not parseable in the same way for template (e.g., allowing commas),
|
||||
// so not adding
|
||||
[
|
||||
'typeAllowed', isTypescriptOrClosure || isPermissive,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'this', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Signature seems to require a "namepath" (and no counter-examples)
|
||||
// Not used with namepath in Closure/TypeScript, however
|
||||
[
|
||||
'namepathRole', isJsdoc ? 'namepath-referencing' : false,
|
||||
],
|
||||
|
||||
// namepath
|
||||
[
|
||||
'typeOrNameRequired', isJsdoc,
|
||||
],
|
||||
|
||||
[
|
||||
'typeRequired', isTypescriptOrClosure,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'throws', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows curly brackets in the signature and in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'tutorial', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// (a tutorial ID)
|
||||
[
|
||||
'nameRequired', true,
|
||||
],
|
||||
|
||||
[
|
||||
'typeAllowed', false,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'type', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows curly brackets in the doc signature and examples
|
||||
// "typeName"
|
||||
[
|
||||
'typeRequired', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'typedef', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Seems to require a "namepath" in the signature (with no
|
||||
// counter-examples)
|
||||
[
|
||||
'namepathRole', 'name-defining',
|
||||
],
|
||||
|
||||
// TypeScript may allow it to be dropped if followed by @property or @member;
|
||||
// also shown as missing in Closure
|
||||
// "namepath"
|
||||
[
|
||||
'nameRequired', isJsdocOrPermissive,
|
||||
],
|
||||
|
||||
// Is not `typeRequired` for TypeScript because it gives an error:
|
||||
// JSDoc '@typedef' tag should either have a type annotation or be followed by '@property' or '@member' tags.
|
||||
|
||||
// Has example showing curly brackets but not in doc signature
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
|
||||
// TypeScript may allow it to be dropped if followed by @property or @member
|
||||
// "namepath"
|
||||
[
|
||||
'typeOrNameRequired', !isTypescript,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'var', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Allows for "name"'s in signature, but indicated as optional
|
||||
[
|
||||
'namepathRole', 'namepath-defining',
|
||||
],
|
||||
|
||||
// Has example showing curly brackets but not in doc signature
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
|
||||
[
|
||||
'yield', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows curly brackets in the signature and in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
[
|
||||
'yields', new Map(/** @type {[string, string|boolean][]} */ ([
|
||||
// Shows curly brackets in the signature and in the examples
|
||||
[
|
||||
'typeAllowed', true,
|
||||
],
|
||||
])),
|
||||
],
|
||||
]);
|
||||
};
|
||||
|
||||
export default getDefaultTagStructureForMode;
|
||||
5
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/getJsdocProcessorPlugin.cts
generated
vendored
Normal file
5
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/getJsdocProcessorPlugin.cts
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import {getJsdocProcessorPlugin} from './getJsdocProcessorPlugin.js';
|
||||
|
||||
export = {
|
||||
getJsdocProcessorPlugin: getJsdocProcessorPlugin as typeof getJsdocProcessorPlugin
|
||||
};
|
||||
692
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/getJsdocProcessorPlugin.js
generated
vendored
Normal file
692
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/getJsdocProcessorPlugin.js
generated
vendored
Normal file
|
|
@ -0,0 +1,692 @@
|
|||
import {
|
||||
forEachPreferredTag,
|
||||
getPreferredTagName,
|
||||
getRegexFromString,
|
||||
getTagDescription,
|
||||
hasTag,
|
||||
} from './jsdocUtils.js';
|
||||
import {
|
||||
parseComment,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
import * as espree from 'espree';
|
||||
import {
|
||||
decode,
|
||||
} from 'html-entities';
|
||||
import {
|
||||
readFileSync,
|
||||
} from 'node:fs';
|
||||
import {
|
||||
join,
|
||||
} from 'node:path';
|
||||
/**
|
||||
* @import {
|
||||
* Integer,
|
||||
* JsdocBlockWithInline,
|
||||
* } from './iterateJsdoc.js';
|
||||
* @import {
|
||||
* ESLint,
|
||||
* Linter,
|
||||
* } from 'eslint';
|
||||
*/
|
||||
|
||||
const {
|
||||
version,
|
||||
} = JSON.parse(
|
||||
readFileSync(join(import.meta.dirname, '../package.json'), 'utf8'),
|
||||
);
|
||||
|
||||
// const zeroBasedLineIndexAdjust = -1;
|
||||
const likelyNestedJSDocIndentSpace = 1;
|
||||
const preTagSpaceLength = 1;
|
||||
|
||||
// If a space is present, we should ignore it
|
||||
const firstLinePrefixLength = preTagSpaceLength;
|
||||
|
||||
const hasCaptionRegex = /^\s*<caption>([\s\S]*?)<\/caption>/v;
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
const escapeStringRegexp = (str) => {
|
||||
return str.replaceAll(/[.*+?^$\{\}\(\)\|\[\]\\]/gv, '\\$&');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @param {string} ch
|
||||
* @returns {Integer}
|
||||
*/
|
||||
const countChars = (str, ch) => {
|
||||
return (str.match(new RegExp(escapeStringRegexp(ch), 'gv')) || []).length;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns {[
|
||||
* Integer,
|
||||
* Integer
|
||||
* ]}
|
||||
*/
|
||||
const getLinesCols = (text) => {
|
||||
const matchLines = countChars(text, '\n');
|
||||
|
||||
const colDelta = matchLines ?
|
||||
text.slice(text.lastIndexOf('\n') + 1).length :
|
||||
text.length;
|
||||
|
||||
return [
|
||||
matchLines, colDelta,
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {number} Integer
|
||||
*/
|
||||
/**
|
||||
* @typedef {object} JsdocProcessorOptions
|
||||
* @property {boolean} [captionRequired] Require captions for example tags
|
||||
* @property {Integer} [paddedIndent] See docs
|
||||
* @property {boolean} [checkDefaults] See docs
|
||||
* @property {boolean} [checkParams] See docs
|
||||
* @property {boolean} [checkExamples] See docs
|
||||
* @property {boolean} [checkProperties] See docs
|
||||
* @property {string} [matchingFileName] See docs
|
||||
* @property {string} [matchingFileNameDefaults] See docs
|
||||
* @property {string} [matchingFileNameParams] See docs
|
||||
* @property {string} [matchingFileNameProperties] See docs
|
||||
* @property {string|RegExp} [exampleCodeRegex] See docs
|
||||
* @property {string|RegExp} [rejectExampleCodeRegex] See docs
|
||||
* @property {string[]} [allowedLanguagesToProcess] See docs
|
||||
* @property {"script"|"module"} [sourceType] See docs
|
||||
* @property {import('eslint').Linter.ESTreeParser|import('eslint').Linter.NonESTreeParser} [parser] See docs
|
||||
*/
|
||||
|
||||
/**
|
||||
* We use a function for the ability of the user to pass in a config, but
|
||||
* without requiring all users of the plugin to do so.
|
||||
* @param {JsdocProcessorOptions} [options]
|
||||
* @returns {ESLint.Plugin}
|
||||
*/
|
||||
export const getJsdocProcessorPlugin = (options = {}) => {
|
||||
/**
|
||||
* @typedef {{
|
||||
* text: string,
|
||||
* filename: string|null|undefined
|
||||
* }} TextAndFileName
|
||||
*/
|
||||
|
||||
const {
|
||||
allowedLanguagesToProcess = [
|
||||
'js', 'ts', 'javascript', 'typescript',
|
||||
],
|
||||
captionRequired = false,
|
||||
checkDefaults = false,
|
||||
checkExamples = true,
|
||||
checkParams = false,
|
||||
checkProperties = false,
|
||||
exampleCodeRegex = null,
|
||||
matchingFileName = null,
|
||||
matchingFileNameDefaults = null,
|
||||
matchingFileNameParams = null,
|
||||
matchingFileNameProperties = null,
|
||||
paddedIndent = 0,
|
||||
parser = undefined,
|
||||
rejectExampleCodeRegex = null,
|
||||
sourceType = 'module',
|
||||
} = options;
|
||||
|
||||
/** @type {RegExp} */
|
||||
let exampleCodeRegExp;
|
||||
/** @type {RegExp} */
|
||||
let rejectExampleCodeRegExp;
|
||||
|
||||
if (exampleCodeRegex) {
|
||||
exampleCodeRegExp = typeof exampleCodeRegex === 'string' ?
|
||||
getRegexFromString(exampleCodeRegex) :
|
||||
exampleCodeRegex;
|
||||
}
|
||||
|
||||
if (rejectExampleCodeRegex) {
|
||||
rejectExampleCodeRegExp = typeof rejectExampleCodeRegex === 'string' ?
|
||||
getRegexFromString(rejectExampleCodeRegex) :
|
||||
rejectExampleCodeRegex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
* targetTagName: string,
|
||||
* ext: string,
|
||||
* codeStartLine: number,
|
||||
* codeStartCol: number,
|
||||
* nonJSPrefacingCols: number,
|
||||
* commentLineCols: [number, number]
|
||||
* }[]}
|
||||
*/
|
||||
const otherInfo = [];
|
||||
|
||||
/** @type {import('eslint').Linter.LintMessage[]} */
|
||||
let extraMessages = [];
|
||||
|
||||
/**
|
||||
* @param {JsdocBlockWithInline} jsdoc
|
||||
* @param {string} jsFileName
|
||||
* @param {[number, number]} commentLineCols
|
||||
*/
|
||||
const getTextsAndFileNames = (jsdoc, jsFileName, commentLineCols) => {
|
||||
/**
|
||||
* @type {TextAndFileName[]}
|
||||
*/
|
||||
const textsAndFileNames = [];
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* filename: string|null,
|
||||
* defaultFileName: string|undefined,
|
||||
* source: string,
|
||||
* targetTagName: string,
|
||||
* rules?: import('eslint').Linter.RulesRecord|undefined,
|
||||
* lines?: Integer,
|
||||
* cols?: Integer,
|
||||
* skipInit?: boolean,
|
||||
* ext: string,
|
||||
* sources?: {
|
||||
* nonJSPrefacingCols: Integer,
|
||||
* nonJSPrefacingLines: Integer,
|
||||
* string: string,
|
||||
* }[],
|
||||
* tag?: import('comment-parser').Spec & {
|
||||
* line?: Integer,
|
||||
* }|{
|
||||
* line: Integer,
|
||||
* }
|
||||
* }} cfg
|
||||
*/
|
||||
const checkSource = ({
|
||||
cols = 0,
|
||||
defaultFileName,
|
||||
ext,
|
||||
filename,
|
||||
lines = 0,
|
||||
skipInit,
|
||||
source,
|
||||
sources = [],
|
||||
tag = {
|
||||
line: 0,
|
||||
},
|
||||
targetTagName,
|
||||
}) => {
|
||||
if (!skipInit) {
|
||||
sources.push({
|
||||
nonJSPrefacingCols: cols,
|
||||
nonJSPrefacingLines: lines,
|
||||
string: source,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* nonJSPrefacingCols: Integer,
|
||||
* nonJSPrefacingLines: Integer,
|
||||
* string: string
|
||||
* }} cfg
|
||||
*/
|
||||
const addSourceInfo = function ({
|
||||
nonJSPrefacingCols,
|
||||
nonJSPrefacingLines,
|
||||
string,
|
||||
}) {
|
||||
const src = paddedIndent ?
|
||||
string.replaceAll(new RegExp(`(^|\n) {${paddedIndent}}(?!$)`, 'gv'), '\n') :
|
||||
string;
|
||||
|
||||
// Programmatic ESLint API: https://eslint.org/docs/developer-guide/nodejs-api
|
||||
const file = filename || defaultFileName;
|
||||
|
||||
if (!('line' in tag)) {
|
||||
tag.line = tag.source[0].number;
|
||||
}
|
||||
|
||||
// NOTE: `tag.line` can be 0 if of form `/** @tag ... */`
|
||||
const codeStartLine = /**
|
||||
* @type {import('comment-parser').Spec & {
|
||||
* line: Integer,
|
||||
* }}
|
||||
*/ (tag).line + nonJSPrefacingLines;
|
||||
const codeStartCol = likelyNestedJSDocIndentSpace;
|
||||
|
||||
textsAndFileNames.push({
|
||||
filename: file,
|
||||
// See https://github.com/gajus/eslint-plugin-jsdoc/issues/710
|
||||
text: src.replaceAll(/(?<=\*)\\(?=\\*\/)/gv, '').replaceAll(/&([^\s;]+);/gv, (_, code) => {
|
||||
// Dec
|
||||
if ((/^#\d+$/v).test(code)) {
|
||||
return String.fromCodePoint(Number.parseInt(code.slice(1), 10));
|
||||
}
|
||||
|
||||
// Hex
|
||||
if ((/^#x\d+$/v).test(code)) {
|
||||
return String.fromCodePoint(Number.parseInt(code.slice(2), 16));
|
||||
}
|
||||
|
||||
return decode(_, {
|
||||
level: 'html5',
|
||||
});
|
||||
}),
|
||||
});
|
||||
otherInfo.push({
|
||||
codeStartCol,
|
||||
codeStartLine,
|
||||
commentLineCols,
|
||||
ext,
|
||||
nonJSPrefacingCols,
|
||||
targetTagName,
|
||||
});
|
||||
};
|
||||
|
||||
for (const targetSource of sources) {
|
||||
addSourceInfo(targetSource);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string|null} filename
|
||||
* @param {string} [ext] Since `eslint-plugin-markdown` v2, and
|
||||
* ESLint 7, this is the default which other JS-fenced rules will used.
|
||||
* Formerly "md" was the default.
|
||||
* @returns {{
|
||||
* defaultFileName: string|undefined,
|
||||
* filename: string|null,
|
||||
* ext: string
|
||||
* }}
|
||||
*/
|
||||
const getFilenameInfo = (filename, ext = 'md/*.js') => {
|
||||
let defaultFileName;
|
||||
if (!filename) {
|
||||
if (typeof jsFileName === 'string' && jsFileName.includes('.')) {
|
||||
defaultFileName = jsFileName.replace(/\.[^.]*$/v, `.${ext}`);
|
||||
} else {
|
||||
defaultFileName = `dummy.${ext}`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
defaultFileName,
|
||||
ext,
|
||||
filename,
|
||||
};
|
||||
};
|
||||
|
||||
if (checkDefaults) {
|
||||
const filenameInfo = getFilenameInfo(matchingFileNameDefaults, 'jsdoc-defaults');
|
||||
forEachPreferredTag(jsdoc, 'default', (tag, targetTagName) => {
|
||||
if (!tag.description.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkSource({
|
||||
source: `(${getTagDescription(tag)})`,
|
||||
targetTagName,
|
||||
...filenameInfo,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (checkParams) {
|
||||
const filenameInfo = getFilenameInfo(matchingFileNameParams, 'jsdoc-params');
|
||||
forEachPreferredTag(jsdoc, 'param', (tag, targetTagName) => {
|
||||
if (!tag.default || !tag.default.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkSource({
|
||||
source: `(${tag.default})`,
|
||||
targetTagName,
|
||||
...filenameInfo,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (checkProperties) {
|
||||
const filenameInfo = getFilenameInfo(matchingFileNameProperties, 'jsdoc-properties');
|
||||
forEachPreferredTag(jsdoc, 'property', (tag, targetTagName) => {
|
||||
if (!tag.default || !tag.default.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkSource({
|
||||
source: `(${tag.default})`,
|
||||
targetTagName,
|
||||
...filenameInfo,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!checkExamples) {
|
||||
return textsAndFileNames;
|
||||
}
|
||||
|
||||
const tagName = /** @type {string} */ (getPreferredTagName(jsdoc, {
|
||||
tagName: 'example',
|
||||
}));
|
||||
if (!hasTag(jsdoc, tagName)) {
|
||||
return textsAndFileNames;
|
||||
}
|
||||
|
||||
const matchingFilenameInfo = getFilenameInfo(matchingFileName);
|
||||
|
||||
forEachPreferredTag(jsdoc, 'example', (tag, targetTagName) => {
|
||||
let source = /** @type {string} */ (getTagDescription(tag));
|
||||
const match = source.match(hasCaptionRegex);
|
||||
|
||||
if (captionRequired && (!match || !match[1].trim())) {
|
||||
extraMessages.push({
|
||||
column: commentLineCols[1] + 1,
|
||||
line: 1 + commentLineCols[0] + (tag.line ?? tag.source[0].number),
|
||||
message: `@${targetTagName} error - Caption is expected for examples.`,
|
||||
ruleId: 'jsdoc/example-missing-caption',
|
||||
severity: 2,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
source = source.replace(hasCaptionRegex, '');
|
||||
const [
|
||||
lines,
|
||||
cols,
|
||||
] = match ? getLinesCols(match[0]) : [
|
||||
0, 0,
|
||||
];
|
||||
|
||||
if (exampleCodeRegex && !exampleCodeRegExp.test(source) ||
|
||||
rejectExampleCodeRegex && rejectExampleCodeRegExp.test(source)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If `allowedLanguagesToProcess` is falsy, all languages should be processed.
|
||||
if (allowedLanguagesToProcess) {
|
||||
const matches = (/^\s*```(?<language>\S+)([\s\S]*)```\s*$/v).exec(source);
|
||||
if (matches?.groups && !allowedLanguagesToProcess.includes(
|
||||
matches.groups.language.toLowerCase(),
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const sources = [];
|
||||
let skipInit = false;
|
||||
if (exampleCodeRegex) {
|
||||
let nonJSPrefacingCols = 0;
|
||||
let nonJSPrefacingLines = 0;
|
||||
|
||||
let startingIndex = 0;
|
||||
let lastStringCount = 0;
|
||||
|
||||
let exampleCode;
|
||||
exampleCodeRegExp.lastIndex = 0;
|
||||
while ((exampleCode = exampleCodeRegExp.exec(source)) !== null) {
|
||||
const {
|
||||
'0': n0,
|
||||
'1': n1,
|
||||
index,
|
||||
} = exampleCode;
|
||||
|
||||
// Count anything preceding user regex match (can affect line numbering)
|
||||
const preMatch = source.slice(startingIndex, index);
|
||||
|
||||
const [
|
||||
preMatchLines,
|
||||
colDelta,
|
||||
] = getLinesCols(preMatch);
|
||||
|
||||
let nonJSPreface;
|
||||
let nonJSPrefaceLineCount;
|
||||
if (n1) {
|
||||
const idx = n0.indexOf(n1);
|
||||
nonJSPreface = n0.slice(0, idx);
|
||||
nonJSPrefaceLineCount = countChars(nonJSPreface, '\n');
|
||||
} else {
|
||||
nonJSPreface = '';
|
||||
nonJSPrefaceLineCount = 0;
|
||||
}
|
||||
|
||||
nonJSPrefacingLines += lastStringCount + preMatchLines + nonJSPrefaceLineCount;
|
||||
|
||||
// Ignore `preMatch` delta if newlines here
|
||||
if (nonJSPrefaceLineCount) {
|
||||
const charsInLastLine = nonJSPreface.slice(nonJSPreface.lastIndexOf('\n') + 1).length;
|
||||
|
||||
nonJSPrefacingCols += charsInLastLine;
|
||||
} else {
|
||||
nonJSPrefacingCols += colDelta + nonJSPreface.length;
|
||||
}
|
||||
|
||||
const string = n1 || n0;
|
||||
sources.push({
|
||||
nonJSPrefacingCols,
|
||||
nonJSPrefacingLines,
|
||||
string,
|
||||
});
|
||||
startingIndex = exampleCodeRegExp.lastIndex;
|
||||
lastStringCount = countChars(string, '\n');
|
||||
if (!exampleCodeRegExp.global) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
skipInit = true;
|
||||
}
|
||||
|
||||
checkSource({
|
||||
cols,
|
||||
lines,
|
||||
skipInit,
|
||||
source,
|
||||
sources,
|
||||
tag,
|
||||
targetTagName,
|
||||
...matchingFilenameInfo,
|
||||
});
|
||||
});
|
||||
|
||||
return textsAndFileNames;
|
||||
};
|
||||
|
||||
// See https://eslint.org/docs/latest/extend/plugins#processors-in-plugins
|
||||
// See https://eslint.org/docs/latest/extend/custom-processors
|
||||
// From https://github.com/eslint/eslint/issues/14745#issuecomment-869457265
|
||||
/*
|
||||
{
|
||||
"files": ["*.js", "*.ts"],
|
||||
"processor": "jsdoc/example" // a pretended value here
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.js/*_jsdoc-example.js",
|
||||
"*.ts/*_jsdoc-example.js",
|
||||
"*.js/*_jsdoc-example.ts"
|
||||
],
|
||||
"rules": {
|
||||
// specific rules for examples in jsdoc only here
|
||||
// And other rules for `.js` and `.ts` will also be enabled for them
|
||||
}
|
||||
}
|
||||
*/
|
||||
return {
|
||||
meta: {
|
||||
name: 'eslint-plugin-jsdoc/processor',
|
||||
version,
|
||||
},
|
||||
processors: {
|
||||
examples: {
|
||||
meta: {
|
||||
name: 'eslint-plugin-jsdoc/preprocessor',
|
||||
version,
|
||||
},
|
||||
/**
|
||||
* @param {import('eslint').Linter.LintMessage[][]} messages
|
||||
* @param {string} filename
|
||||
*/
|
||||
postprocess ([
|
||||
jsMessages,
|
||||
...messages
|
||||
// eslint-disable-next-line no-unused-vars -- Placeholder
|
||||
], filename) {
|
||||
for (const [
|
||||
idx,
|
||||
message,
|
||||
] of messages.entries()) {
|
||||
const {
|
||||
codeStartCol,
|
||||
codeStartLine,
|
||||
commentLineCols,
|
||||
nonJSPrefacingCols,
|
||||
targetTagName,
|
||||
} = otherInfo[idx];
|
||||
|
||||
for (const msg of message) {
|
||||
const {
|
||||
column,
|
||||
endColumn,
|
||||
endLine,
|
||||
fatal,
|
||||
line,
|
||||
message: messageText,
|
||||
ruleId,
|
||||
severity,
|
||||
|
||||
// Todo: Make fixable
|
||||
// fix
|
||||
// fix: {range: [number, number], text: string}
|
||||
// suggestions: {desc: , messageId:, fix: }[],
|
||||
} = msg;
|
||||
delete msg.fix;
|
||||
|
||||
const [
|
||||
codeCtxLine,
|
||||
codeCtxColumn,
|
||||
] = commentLineCols;
|
||||
const startLine = codeCtxLine + codeStartLine + line;
|
||||
|
||||
// Seems to need one more now
|
||||
const startCol = 1 +
|
||||
codeCtxColumn + codeStartCol + (
|
||||
// This might not work for line 0, but line 0 is unlikely for examples
|
||||
line <= 1 ? nonJSPrefacingCols + firstLinePrefixLength : preTagSpaceLength
|
||||
) + column;
|
||||
|
||||
msg.message = '@' + targetTagName + ' ' + (severity === 2 ? 'error' : 'warning') +
|
||||
(ruleId ? ' (' + ruleId + ')' : '') + ': ' +
|
||||
(fatal ? 'Fatal: ' : '') +
|
||||
messageText;
|
||||
msg.line = startLine;
|
||||
msg.column = startCol;
|
||||
msg.endLine = endLine ? startLine + endLine : startLine;
|
||||
// added `- column` to offset what `endColumn` already seemed to include
|
||||
msg.endColumn = endColumn ? startCol - column + endColumn : startCol;
|
||||
}
|
||||
}
|
||||
|
||||
const ret = [
|
||||
...jsMessages,
|
||||
].concat(...messages, ...extraMessages);
|
||||
extraMessages = [];
|
||||
return ret;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {string} filename
|
||||
* @returns {(string | Linter.ProcessorFile)[]}
|
||||
*/
|
||||
preprocess (text, filename) {
|
||||
try {
|
||||
let ast;
|
||||
|
||||
// May be running a second time so catch and ignore
|
||||
try {
|
||||
ast = parser ?
|
||||
// @ts-expect-error Should be present
|
||||
parser.parseForESLint(text, {
|
||||
comment: true,
|
||||
ecmaVersion: 'latest',
|
||||
sourceType,
|
||||
}).ast :
|
||||
espree.parse(text, {
|
||||
comment: true,
|
||||
ecmaVersion: 'latest',
|
||||
sourceType,
|
||||
});
|
||||
} catch {
|
||||
return [
|
||||
text,
|
||||
];
|
||||
}
|
||||
|
||||
/** @type {[number, number][]} */
|
||||
const commentLineCols = [];
|
||||
const jsdocComments = /** @type {import('estree').Comment[]} */ (
|
||||
/**
|
||||
* @type {import('estree').Program & {
|
||||
* comments?: import('estree').Comment[]
|
||||
* }}
|
||||
*/
|
||||
(ast).comments
|
||||
).filter((comment) => {
|
||||
return (/^\*\s/v).test(comment.value);
|
||||
}).map((comment) => {
|
||||
const [
|
||||
start,
|
||||
/* c8 ignore next -- Unsupporting processors only? */
|
||||
] = comment.range ?? [];
|
||||
const textToStart = text.slice(0, start);
|
||||
|
||||
const [
|
||||
lines,
|
||||
cols,
|
||||
] = getLinesCols(textToStart);
|
||||
|
||||
// const lines = [...textToStart.matchAll(/\n/gv)].length
|
||||
// const lastLinePos = textToStart.lastIndexOf('\n');
|
||||
// const cols = lastLinePos === -1
|
||||
// ? 0
|
||||
// : textToStart.slice(lastLinePos).length;
|
||||
commentLineCols.push([
|
||||
lines, cols,
|
||||
]);
|
||||
return parseComment(comment);
|
||||
});
|
||||
|
||||
return [
|
||||
text,
|
||||
...jsdocComments.flatMap((jsdoc, idx) => {
|
||||
return getTextsAndFileNames(
|
||||
jsdoc,
|
||||
filename,
|
||||
commentLineCols[idx],
|
||||
);
|
||||
}).filter(
|
||||
/**
|
||||
* @param {TextAndFileName} file
|
||||
* @returns {file is Linter.ProcessorFile}
|
||||
*/
|
||||
(file) => {
|
||||
return file !== null && file !== undefined;
|
||||
},
|
||||
),
|
||||
];
|
||||
/* c8 ignore next 6 */
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console -- Debugging
|
||||
console.log('err', filename, error);
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
supportsAutofix: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
720
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/index-cjs.js
generated
vendored
Normal file
720
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/index-cjs.js
generated
vendored
Normal file
|
|
@ -0,0 +1,720 @@
|
|||
import {
|
||||
buildForbidRuleDefinition,
|
||||
} from './buildForbidRuleDefinition.js';
|
||||
import {
|
||||
buildRejectOrPreferRuleDefinition,
|
||||
} from './buildRejectOrPreferRuleDefinition.js';
|
||||
import {
|
||||
getJsdocProcessorPlugin,
|
||||
} from './getJsdocProcessorPlugin.js';
|
||||
import checkAccess from './rules/checkAccess.js';
|
||||
import checkAlignment from './rules/checkAlignment.js';
|
||||
import checkExamples from './rules/checkExamples.js';
|
||||
import checkIndentation from './rules/checkIndentation.js';
|
||||
import checkLineAlignment from './rules/checkLineAlignment.js';
|
||||
import checkParamNames from './rules/checkParamNames.js';
|
||||
import checkPropertyNames from './rules/checkPropertyNames.js';
|
||||
import checkSyntax from './rules/checkSyntax.js';
|
||||
import checkTagNames from './rules/checkTagNames.js';
|
||||
import checkTemplateNames from './rules/checkTemplateNames.js';
|
||||
import checkTypes from './rules/checkTypes.js';
|
||||
import checkValues from './rules/checkValues.js';
|
||||
import convertToJsdocComments from './rules/convertToJsdocComments.js';
|
||||
import emptyTags from './rules/emptyTags.js';
|
||||
import escapeInlineTags from './rules/escapeInlineTags.js';
|
||||
import implementsOnClasses from './rules/implementsOnClasses.js';
|
||||
import importsAsDependencies from './rules/importsAsDependencies.js';
|
||||
import informativeDocs from './rules/informativeDocs.js';
|
||||
import linesBeforeBlock from './rules/linesBeforeBlock.js';
|
||||
import matchDescription from './rules/matchDescription.js';
|
||||
import matchName from './rules/matchName.js';
|
||||
import multilineBlocks from './rules/multilineBlocks.js';
|
||||
import noBadBlocks from './rules/noBadBlocks.js';
|
||||
import noBlankBlockDescriptions from './rules/noBlankBlockDescriptions.js';
|
||||
import noBlankBlocks from './rules/noBlankBlocks.js';
|
||||
import noDefaults from './rules/noDefaults.js';
|
||||
import noMissingSyntax from './rules/noMissingSyntax.js';
|
||||
import noMultiAsterisks from './rules/noMultiAsterisks.js';
|
||||
import noRestrictedSyntax from './rules/noRestrictedSyntax.js';
|
||||
import noTypes from './rules/noTypes.js';
|
||||
import noUndefinedTypes from './rules/noUndefinedTypes.js';
|
||||
import preferImportTag from './rules/preferImportTag.js';
|
||||
import requireAsteriskPrefix from './rules/requireAsteriskPrefix.js';
|
||||
import requireDescription from './rules/requireDescription.js';
|
||||
import requireDescriptionCompleteSentence from './rules/requireDescriptionCompleteSentence.js';
|
||||
import requireExample from './rules/requireExample.js';
|
||||
import requireFileOverview from './rules/requireFileOverview.js';
|
||||
import requireHyphenBeforeParamDescription from './rules/requireHyphenBeforeParamDescription.js';
|
||||
import requireJsdoc from './rules/requireJsdoc.js';
|
||||
import requireParam from './rules/requireParam.js';
|
||||
import requireParamDescription from './rules/requireParamDescription.js';
|
||||
import requireParamName from './rules/requireParamName.js';
|
||||
import requireParamType from './rules/requireParamType.js';
|
||||
import requireProperty from './rules/requireProperty.js';
|
||||
import requirePropertyDescription from './rules/requirePropertyDescription.js';
|
||||
import requirePropertyName from './rules/requirePropertyName.js';
|
||||
import requirePropertyType from './rules/requirePropertyType.js';
|
||||
import requireRejects from './rules/requireRejects.js';
|
||||
import requireReturns from './rules/requireReturns.js';
|
||||
import requireReturnsCheck from './rules/requireReturnsCheck.js';
|
||||
import requireReturnsDescription from './rules/requireReturnsDescription.js';
|
||||
import requireReturnsType from './rules/requireReturnsType.js';
|
||||
import requireTags from './rules/requireTags.js';
|
||||
import requireTemplate from './rules/requireTemplate.js';
|
||||
import requireThrows from './rules/requireThrows.js';
|
||||
import requireYields from './rules/requireYields.js';
|
||||
import requireYieldsCheck from './rules/requireYieldsCheck.js';
|
||||
import sortTags from './rules/sortTags.js';
|
||||
import tagLines from './rules/tagLines.js';
|
||||
import textEscaping from './rules/textEscaping.js';
|
||||
import tsMethodSignatureStyle from './rules/tsMethodSignatureStyle.js';
|
||||
import tsNoEmptyObjectType from './rules/tsNoEmptyObjectType.js';
|
||||
import tsNoUnnecessaryTemplateExpression from './rules/tsNoUnnecessaryTemplateExpression.js';
|
||||
import tsPreferFunctionType from './rules/tsPreferFunctionType.js';
|
||||
import typeFormatting from './rules/typeFormatting.js';
|
||||
import validTypes from './rules/validTypes.js';
|
||||
|
||||
/**
|
||||
* @typedef {"recommended" | "stylistic" | "contents" | "logical" | "requirements"} ConfigGroups
|
||||
* @typedef {"" | "-typescript" | "-typescript-flavor"} ConfigVariants
|
||||
* @typedef {"" | "-error"} ErrorLevelVariants
|
||||
* @type {import('eslint').ESLint.Plugin & {
|
||||
* configs: Record<
|
||||
* `flat/${ConfigGroups}${ConfigVariants}${ErrorLevelVariants}`,
|
||||
* import('eslint').Linter.Config
|
||||
* > &
|
||||
* Record<
|
||||
* "examples"|"default-expressions"|"examples-and-default-expressions",
|
||||
* import('eslint').Linter.Config[]
|
||||
* > &
|
||||
* Record<"flat/recommended-mixed", import('eslint').Linter.Config[]>
|
||||
* }}
|
||||
*/
|
||||
const index = {};
|
||||
index.configs = {};
|
||||
index.rules = {
|
||||
'check-access': checkAccess,
|
||||
'check-alignment': checkAlignment,
|
||||
'check-examples': checkExamples,
|
||||
'check-indentation': checkIndentation,
|
||||
'check-line-alignment': checkLineAlignment,
|
||||
'check-param-names': checkParamNames,
|
||||
'check-property-names': checkPropertyNames,
|
||||
'check-syntax': checkSyntax,
|
||||
'check-tag-names': checkTagNames,
|
||||
'check-template-names': checkTemplateNames,
|
||||
'check-types': checkTypes,
|
||||
'check-values': checkValues,
|
||||
'convert-to-jsdoc-comments': convertToJsdocComments,
|
||||
'empty-tags': emptyTags,
|
||||
'escape-inline-tags': escapeInlineTags,
|
||||
'implements-on-classes': implementsOnClasses,
|
||||
'imports-as-dependencies': importsAsDependencies,
|
||||
'informative-docs': informativeDocs,
|
||||
'lines-before-block': linesBeforeBlock,
|
||||
'match-description': matchDescription,
|
||||
'match-name': matchName,
|
||||
'multiline-blocks': multilineBlocks,
|
||||
'no-bad-blocks': noBadBlocks,
|
||||
'no-blank-block-descriptions': noBlankBlockDescriptions,
|
||||
'no-blank-blocks': noBlankBlocks,
|
||||
'no-defaults': noDefaults,
|
||||
'no-missing-syntax': noMissingSyntax,
|
||||
'no-multi-asterisks': noMultiAsterisks,
|
||||
'no-restricted-syntax': noRestrictedSyntax,
|
||||
'no-types': noTypes,
|
||||
'no-undefined-types': noUndefinedTypes,
|
||||
'prefer-import-tag': preferImportTag,
|
||||
'reject-any-type': buildRejectOrPreferRuleDefinition({
|
||||
description: 'Reports use of `any` or `*` type',
|
||||
overrideSettings: {
|
||||
'*': {
|
||||
message: 'Prefer a more specific type to `*`',
|
||||
replacement: false,
|
||||
unifyParentAndChildTypeChecks: true,
|
||||
},
|
||||
any: {
|
||||
message: 'Prefer a more specific type to `any`',
|
||||
replacement: false,
|
||||
unifyParentAndChildTypeChecks: true,
|
||||
},
|
||||
},
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/reject-any-type.md#repos-sticky-header',
|
||||
}),
|
||||
'reject-function-type': buildRejectOrPreferRuleDefinition({
|
||||
description: 'Reports use of `Function` type',
|
||||
overrideSettings: {
|
||||
Function: {
|
||||
message: 'Prefer a more specific type to `Function`',
|
||||
replacement: false,
|
||||
unifyParentAndChildTypeChecks: true,
|
||||
},
|
||||
},
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/reject-function-type.md#repos-sticky-header',
|
||||
}),
|
||||
'require-asterisk-prefix': requireAsteriskPrefix,
|
||||
'require-description': requireDescription,
|
||||
'require-description-complete-sentence': requireDescriptionCompleteSentence,
|
||||
'require-example': requireExample,
|
||||
'require-file-overview': requireFileOverview,
|
||||
'require-hyphen-before-param-description': requireHyphenBeforeParamDescription,
|
||||
'require-jsdoc': requireJsdoc,
|
||||
'require-next-description': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=next]:not([name!=""]):not([description!=""]))',
|
||||
context: 'any',
|
||||
message: '@next should have a description',
|
||||
},
|
||||
],
|
||||
description: 'Requires a description for `@next` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-next-description.md#repos-sticky-header',
|
||||
}),
|
||||
'require-next-type': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=next]:not([parsedType.type]))',
|
||||
context: 'any',
|
||||
message: '@next should have a type',
|
||||
},
|
||||
],
|
||||
description: 'Requires a type for `@next` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-next-type.md#repos-sticky-header',
|
||||
}),
|
||||
'require-param': requireParam,
|
||||
'require-param-description': requireParamDescription,
|
||||
'require-param-name': requireParamName,
|
||||
'require-param-type': requireParamType,
|
||||
'require-property': requireProperty,
|
||||
'require-property-description': requirePropertyDescription,
|
||||
'require-property-name': requirePropertyName,
|
||||
'require-property-type': requirePropertyType,
|
||||
'require-rejects': requireRejects,
|
||||
'require-returns': requireReturns,
|
||||
'require-returns-check': requireReturnsCheck,
|
||||
'require-returns-description': requireReturnsDescription,
|
||||
'require-returns-type': requireReturnsType,
|
||||
'require-tags': requireTags,
|
||||
'require-template': requireTemplate,
|
||||
'require-template-description': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=template]:not([description!=""]))',
|
||||
context: 'any',
|
||||
message: '@template should have a description',
|
||||
},
|
||||
],
|
||||
description: 'Requires a description for `@template` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-template-description.md#repos-sticky-header',
|
||||
}),
|
||||
'require-throws': requireThrows,
|
||||
'require-throws-description': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=/^(?:throws|exception)$/]:not([description!=""]))',
|
||||
context: 'any',
|
||||
message: '@throws should have a description',
|
||||
},
|
||||
],
|
||||
description: 'Requires a description for `@throws` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-throws-description.md#repos-sticky-header',
|
||||
}),
|
||||
'require-throws-type': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=/^(?:throws|exception)$/]:not([parsedType.type]))',
|
||||
context: 'any',
|
||||
message: '@throws should have a type',
|
||||
},
|
||||
],
|
||||
description: 'Requires a type for `@throws` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-throws-type.md#repos-sticky-header',
|
||||
}),
|
||||
'require-yields': requireYields,
|
||||
'require-yields-check': requireYieldsCheck,
|
||||
'require-yields-description': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=/^yields?$/]:not([name!=""]):not([description!=""]))',
|
||||
context: 'any',
|
||||
message: '@yields should have a description',
|
||||
},
|
||||
],
|
||||
description: 'Requires a description for `@yields` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-yields-description.md#repos-sticky-header',
|
||||
}),
|
||||
'require-yields-type': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=/^yields?$/]:not([parsedType.type]))',
|
||||
context: 'any',
|
||||
message: '@yields should have a type',
|
||||
},
|
||||
],
|
||||
description: 'Requires a type for `@yields` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-yields-type.md#repos-sticky-header',
|
||||
}),
|
||||
'sort-tags': sortTags,
|
||||
'tag-lines': tagLines,
|
||||
'text-escaping': textEscaping,
|
||||
'ts-method-signature-style': tsMethodSignatureStyle,
|
||||
'ts-no-empty-object-type': tsNoEmptyObjectType,
|
||||
'ts-no-unnecessary-template-expression': tsNoUnnecessaryTemplateExpression,
|
||||
'ts-prefer-function-type': tsPreferFunctionType,
|
||||
'type-formatting': typeFormatting,
|
||||
'valid-types': validTypes,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {"warn"|"error"} warnOrError
|
||||
* @param {string} [flatName]
|
||||
* @returns {import('eslint').Linter.Config}
|
||||
*/
|
||||
const createRecommendedRuleset = (warnOrError, flatName) => {
|
||||
return {
|
||||
...(flatName ? {
|
||||
name: 'jsdoc/' + flatName,
|
||||
} : {}),
|
||||
// @ts-expect-error ESLint 8 plugins
|
||||
plugins:
|
||||
flatName ? {
|
||||
jsdoc: index,
|
||||
} : [
|
||||
'jsdoc',
|
||||
],
|
||||
rules: {
|
||||
'jsdoc/check-access': warnOrError,
|
||||
'jsdoc/check-alignment': warnOrError,
|
||||
'jsdoc/check-examples': 'off',
|
||||
'jsdoc/check-indentation': 'off',
|
||||
'jsdoc/check-line-alignment': 'off',
|
||||
'jsdoc/check-param-names': warnOrError,
|
||||
'jsdoc/check-property-names': warnOrError,
|
||||
'jsdoc/check-syntax': 'off',
|
||||
'jsdoc/check-tag-names': warnOrError,
|
||||
'jsdoc/check-template-names': 'off',
|
||||
'jsdoc/check-types': warnOrError,
|
||||
'jsdoc/check-values': warnOrError,
|
||||
'jsdoc/convert-to-jsdoc-comments': 'off',
|
||||
'jsdoc/empty-tags': warnOrError,
|
||||
'jsdoc/escape-inline-tags': warnOrError,
|
||||
'jsdoc/implements-on-classes': warnOrError,
|
||||
'jsdoc/imports-as-dependencies': 'off',
|
||||
'jsdoc/informative-docs': 'off',
|
||||
'jsdoc/lines-before-block': 'off',
|
||||
'jsdoc/match-description': 'off',
|
||||
'jsdoc/match-name': 'off',
|
||||
'jsdoc/multiline-blocks': warnOrError,
|
||||
'jsdoc/no-bad-blocks': 'off',
|
||||
'jsdoc/no-blank-block-descriptions': 'off',
|
||||
'jsdoc/no-blank-blocks': 'off',
|
||||
'jsdoc/no-defaults': warnOrError,
|
||||
'jsdoc/no-missing-syntax': 'off',
|
||||
'jsdoc/no-multi-asterisks': warnOrError,
|
||||
'jsdoc/no-restricted-syntax': 'off',
|
||||
'jsdoc/no-types': 'off',
|
||||
'jsdoc/no-undefined-types': warnOrError,
|
||||
'jsdoc/prefer-import-tag': 'off',
|
||||
'jsdoc/reject-any-type': warnOrError,
|
||||
'jsdoc/reject-function-type': warnOrError,
|
||||
'jsdoc/require-asterisk-prefix': 'off',
|
||||
'jsdoc/require-description': 'off',
|
||||
'jsdoc/require-description-complete-sentence': 'off',
|
||||
'jsdoc/require-example': 'off',
|
||||
'jsdoc/require-file-overview': 'off',
|
||||
'jsdoc/require-hyphen-before-param-description': 'off',
|
||||
'jsdoc/require-jsdoc': warnOrError,
|
||||
'jsdoc/require-next-description': 'off',
|
||||
'jsdoc/require-next-type': warnOrError,
|
||||
'jsdoc/require-param': warnOrError,
|
||||
'jsdoc/require-param-description': warnOrError,
|
||||
'jsdoc/require-param-name': warnOrError,
|
||||
'jsdoc/require-param-type': warnOrError,
|
||||
'jsdoc/require-property': warnOrError,
|
||||
'jsdoc/require-property-description': warnOrError,
|
||||
'jsdoc/require-property-name': warnOrError,
|
||||
'jsdoc/require-property-type': warnOrError,
|
||||
'jsdoc/require-rejects': 'off',
|
||||
'jsdoc/require-returns': warnOrError,
|
||||
'jsdoc/require-returns-check': warnOrError,
|
||||
'jsdoc/require-returns-description': warnOrError,
|
||||
'jsdoc/require-returns-type': warnOrError,
|
||||
'jsdoc/require-tags': 'off',
|
||||
'jsdoc/require-template': 'off',
|
||||
'jsdoc/require-template-description': 'off',
|
||||
'jsdoc/require-throws': 'off',
|
||||
'jsdoc/require-throws-description': 'off',
|
||||
'jsdoc/require-throws-type': warnOrError,
|
||||
'jsdoc/require-yields': warnOrError,
|
||||
'jsdoc/require-yields-check': warnOrError,
|
||||
'jsdoc/require-yields-description': 'off',
|
||||
'jsdoc/require-yields-type': warnOrError,
|
||||
'jsdoc/sort-tags': 'off',
|
||||
'jsdoc/tag-lines': warnOrError,
|
||||
'jsdoc/text-escaping': 'off',
|
||||
'jsdoc/ts-method-signature-style': 'off',
|
||||
'jsdoc/ts-no-empty-object-type': warnOrError,
|
||||
'jsdoc/ts-no-unnecessary-template-expression': 'off',
|
||||
'jsdoc/ts-prefer-function-type': 'off',
|
||||
'jsdoc/type-formatting': 'off',
|
||||
'jsdoc/valid-types': warnOrError,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {"warn"|"error"} warnOrError
|
||||
* @param {string} [flatName]
|
||||
* @returns {import('eslint').Linter.Config}
|
||||
*/
|
||||
const createRecommendedTypeScriptRuleset = (warnOrError, flatName) => {
|
||||
const ruleset = createRecommendedRuleset(warnOrError, flatName);
|
||||
|
||||
return {
|
||||
...ruleset,
|
||||
rules: {
|
||||
...ruleset.rules,
|
||||
/* eslint-disable @stylistic/indent -- Extra indent to avoid use by auto-rule-editing */
|
||||
'jsdoc/check-tag-names': [
|
||||
warnOrError, {
|
||||
typed: true,
|
||||
},
|
||||
],
|
||||
'jsdoc/no-types': warnOrError,
|
||||
'jsdoc/no-undefined-types': 'off',
|
||||
'jsdoc/require-param-type': 'off',
|
||||
'jsdoc/require-property-type': 'off',
|
||||
'jsdoc/require-returns-type': 'off',
|
||||
/* eslint-enable @stylistic/indent */
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {"warn"|"error"} warnOrError
|
||||
* @param {string} [flatName]
|
||||
* @returns {import('eslint').Linter.Config}
|
||||
*/
|
||||
const createRecommendedTypeScriptFlavorRuleset = (warnOrError, flatName) => {
|
||||
const ruleset = createRecommendedRuleset(warnOrError, flatName);
|
||||
|
||||
return {
|
||||
...ruleset,
|
||||
rules: {
|
||||
...ruleset.rules,
|
||||
/* eslint-disable @stylistic/indent -- Extra indent to avoid use by auto-rule-editing */
|
||||
'jsdoc/no-undefined-types': 'off',
|
||||
/* eslint-enable @stylistic/indent */
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {(string | unknown[])[]} ruleNames
|
||||
*/
|
||||
const createStandaloneRulesetFactory = (ruleNames) => {
|
||||
/**
|
||||
* @param {"warn"|"error"} warnOrError
|
||||
* @param {string} [flatName]
|
||||
* @returns {import('eslint').Linter.Config}
|
||||
*/
|
||||
return (warnOrError, flatName) => {
|
||||
return {
|
||||
name: 'jsdoc/' + flatName,
|
||||
plugins: {
|
||||
jsdoc: index,
|
||||
},
|
||||
rules: Object.fromEntries(
|
||||
ruleNames.map(
|
||||
(ruleName) => {
|
||||
return (typeof ruleName === 'string' ?
|
||||
[
|
||||
ruleName, warnOrError,
|
||||
] :
|
||||
[
|
||||
ruleName[0], [
|
||||
warnOrError, ...ruleName.slice(1),
|
||||
],
|
||||
]);
|
||||
},
|
||||
),
|
||||
),
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const contentsRules = [
|
||||
'jsdoc/informative-docs',
|
||||
'jsdoc/match-description',
|
||||
'jsdoc/no-blank-block-descriptions',
|
||||
'jsdoc/no-blank-blocks',
|
||||
[
|
||||
'jsdoc/text-escaping', {
|
||||
escapeHTML: true,
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
const createContentsTypescriptRuleset = createStandaloneRulesetFactory(contentsRules);
|
||||
|
||||
const createContentsTypescriptFlavorRuleset = createStandaloneRulesetFactory(contentsRules);
|
||||
|
||||
const logicalRules = [
|
||||
'jsdoc/check-access',
|
||||
'jsdoc/check-param-names',
|
||||
'jsdoc/check-property-names',
|
||||
'jsdoc/check-syntax',
|
||||
'jsdoc/check-tag-names',
|
||||
'jsdoc/check-template-names',
|
||||
'jsdoc/check-types',
|
||||
'jsdoc/check-values',
|
||||
'jsdoc/empty-tags',
|
||||
'jsdoc/escape-inline-tags',
|
||||
'jsdoc/implements-on-classes',
|
||||
'jsdoc/require-returns-check',
|
||||
'jsdoc/require-yields-check',
|
||||
'jsdoc/no-bad-blocks',
|
||||
'jsdoc/no-defaults',
|
||||
'jsdoc/no-types',
|
||||
'jsdoc/no-undefined-types',
|
||||
'jsdoc/valid-types',
|
||||
];
|
||||
|
||||
const createLogicalTypescriptRuleset = createStandaloneRulesetFactory(logicalRules);
|
||||
|
||||
const createLogicalTypescriptFlavorRuleset = createStandaloneRulesetFactory(logicalRules);
|
||||
|
||||
const requirementsRules = [
|
||||
'jsdoc/require-example',
|
||||
'jsdoc/require-jsdoc',
|
||||
'jsdoc/require-next-type',
|
||||
'jsdoc/require-param',
|
||||
'jsdoc/require-param-description',
|
||||
'jsdoc/require-param-name',
|
||||
'jsdoc/require-property',
|
||||
'jsdoc/require-property-description',
|
||||
'jsdoc/require-property-name',
|
||||
'jsdoc/require-returns',
|
||||
'jsdoc/require-returns-description',
|
||||
'jsdoc/require-throws-type',
|
||||
'jsdoc/require-yields',
|
||||
'jsdoc/require-yields-type',
|
||||
];
|
||||
|
||||
const createRequirementsTypeScriptRuleset = createStandaloneRulesetFactory(requirementsRules);
|
||||
|
||||
const createRequirementsTypeScriptFlavorRuleset = createStandaloneRulesetFactory([
|
||||
...requirementsRules,
|
||||
'jsdoc/require-param-type',
|
||||
'jsdoc/require-property-type',
|
||||
'jsdoc/require-returns-type',
|
||||
'jsdoc/require-template',
|
||||
]);
|
||||
|
||||
const stylisticRules = [
|
||||
'jsdoc/check-alignment',
|
||||
'jsdoc/check-line-alignment',
|
||||
'jsdoc/lines-before-block',
|
||||
'jsdoc/multiline-blocks',
|
||||
'jsdoc/no-multi-asterisks',
|
||||
'jsdoc/require-asterisk-prefix',
|
||||
[
|
||||
'jsdoc/require-hyphen-before-param-description', 'never',
|
||||
],
|
||||
'jsdoc/tag-lines',
|
||||
];
|
||||
|
||||
const createStylisticTypeScriptRuleset = createStandaloneRulesetFactory(stylisticRules);
|
||||
|
||||
const createStylisticTypeScriptFlavorRuleset = createStandaloneRulesetFactory(stylisticRules);
|
||||
|
||||
/* c8 ignore next 3 -- TS */
|
||||
if (!index.configs) {
|
||||
throw new Error('TypeScript guard');
|
||||
}
|
||||
|
||||
index.configs.recommended = createRecommendedRuleset('warn');
|
||||
index.configs['recommended-error'] = createRecommendedRuleset('error');
|
||||
index.configs['recommended-typescript'] = createRecommendedTypeScriptRuleset('warn');
|
||||
index.configs['recommended-typescript-error'] = createRecommendedTypeScriptRuleset('error');
|
||||
index.configs['recommended-typescript-flavor'] = createRecommendedTypeScriptFlavorRuleset('warn');
|
||||
index.configs['recommended-typescript-flavor-error'] = createRecommendedTypeScriptFlavorRuleset('error');
|
||||
|
||||
index.configs['flat/recommended'] = createRecommendedRuleset('warn', 'flat/recommended');
|
||||
index.configs['flat/recommended-error'] = createRecommendedRuleset('error', 'flat/recommended-error');
|
||||
index.configs['flat/recommended-typescript'] = createRecommendedTypeScriptRuleset('warn', 'flat/recommended-typescript');
|
||||
index.configs['flat/recommended-typescript-error'] = createRecommendedTypeScriptRuleset('error', 'flat/recommended-typescript-error');
|
||||
index.configs['flat/recommended-typescript-flavor'] = createRecommendedTypeScriptFlavorRuleset('warn', 'flat/recommended-typescript-flavor');
|
||||
index.configs['flat/recommended-typescript-flavor-error'] = createRecommendedTypeScriptFlavorRuleset('error', 'flat/recommended-typescript-flavor-error');
|
||||
|
||||
index.configs['flat/contents-typescript'] = createContentsTypescriptRuleset('warn', 'flat/contents-typescript');
|
||||
index.configs['flat/contents-typescript-error'] = createContentsTypescriptRuleset('error', 'flat/contents-typescript-error');
|
||||
index.configs['flat/contents-typescript-flavor'] = createContentsTypescriptFlavorRuleset('warn', 'flat/contents-typescript-flavor');
|
||||
index.configs['flat/contents-typescript-flavor-error'] = createContentsTypescriptFlavorRuleset('error', 'flat/contents-typescript-error-flavor');
|
||||
index.configs['flat/logical-typescript'] = createLogicalTypescriptRuleset('warn', 'flat/logical-typescript');
|
||||
index.configs['flat/logical-typescript-error'] = createLogicalTypescriptRuleset('error', 'flat/logical-typescript-error');
|
||||
index.configs['flat/logical-typescript-flavor'] = createLogicalTypescriptFlavorRuleset('warn', 'flat/logical-typescript-flavor');
|
||||
index.configs['flat/logical-typescript-flavor-error'] = createLogicalTypescriptFlavorRuleset('error', 'flat/logical-typescript-error-flavor');
|
||||
index.configs['flat/requirements-typescript'] = createRequirementsTypeScriptRuleset('warn', 'flat/requirements-typescript');
|
||||
index.configs['flat/requirements-typescript-error'] = createRequirementsTypeScriptRuleset('error', 'flat/requirements-typescript-error');
|
||||
index.configs['flat/requirements-typescript-flavor'] = createRequirementsTypeScriptFlavorRuleset('warn', 'flat/requirements-typescript-flavor');
|
||||
index.configs['flat/requirements-typescript-flavor-error'] = createRequirementsTypeScriptFlavorRuleset('error', 'flat/requirements-typescript-error-flavor');
|
||||
index.configs['flat/stylistic-typescript'] = createStylisticTypeScriptRuleset('warn', 'flat/stylistic-typescript');
|
||||
index.configs['flat/stylistic-typescript-error'] = createStylisticTypeScriptRuleset('error', 'flat/stylistic-typescript-error');
|
||||
index.configs['flat/stylistic-typescript-flavor'] = createStylisticTypeScriptFlavorRuleset('warn', 'flat/stylistic-typescript-flavor');
|
||||
index.configs['flat/stylistic-typescript-flavor-error'] = createStylisticTypeScriptFlavorRuleset('error', 'flat/stylistic-typescript-error-flavor');
|
||||
|
||||
index.configs.examples = /** @type {import('eslint').Linter.Config[]} */ ([
|
||||
{
|
||||
files: [
|
||||
'**/*.js',
|
||||
],
|
||||
name: 'jsdoc/examples/processor',
|
||||
plugins: {
|
||||
examples: getJsdocProcessorPlugin(),
|
||||
},
|
||||
processor: 'examples/examples',
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'**/*.md/*.js',
|
||||
],
|
||||
name: 'jsdoc/examples/rules',
|
||||
rules: {
|
||||
// "always" newline rule at end unlikely in sample code
|
||||
'@stylistic/eol-last': 0,
|
||||
// Often wish to start `@example` code after newline; also may use
|
||||
// empty lines for spacing
|
||||
'@stylistic/no-multiple-empty-lines': 0,
|
||||
|
||||
// Can generally look nicer to pad a little even if code imposes more stringency
|
||||
'@stylistic/padded-blocks': 0,
|
||||
|
||||
'@typescript-eslint/no-unused-vars': 0,
|
||||
|
||||
// "always" newline rule at end unlikely in sample code
|
||||
'eol-last': 0,
|
||||
|
||||
// Wouldn't generally expect example paths to resolve relative to JS file
|
||||
'import/no-unresolved': 0,
|
||||
|
||||
// Snippets likely too short to always include import/export info
|
||||
'import/unambiguous': 0,
|
||||
|
||||
'jsdoc/require-file-overview': 0,
|
||||
|
||||
// The end of a multiline comment would end the comment the example is in.
|
||||
'jsdoc/require-jsdoc': 0,
|
||||
|
||||
// See import/no-unresolved
|
||||
'n/no-missing-import': 0,
|
||||
|
||||
'n/no-missing-require': 0,
|
||||
|
||||
// Unlikely to have inadvertent debugging within examples
|
||||
'no-console': 0,
|
||||
|
||||
// Often wish to start `@example` code after newline; also may use
|
||||
// empty lines for spacing
|
||||
'no-multiple-empty-lines': 0,
|
||||
|
||||
// Many variables in examples will be `undefined`
|
||||
'no-undef': 0,
|
||||
// Common to define variables for clarity without always using them
|
||||
'no-unused-vars': 0,
|
||||
|
||||
// See import/no-unresolved
|
||||
'node/no-missing-import': 0,
|
||||
'node/no-missing-require': 0,
|
||||
|
||||
// Can generally look nicer to pad a little even if code imposes more stringency
|
||||
'padded-blocks': 0,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
index.configs['default-expressions'] = /** @type {import('eslint').Linter.Config[]} */ ([
|
||||
{
|
||||
files: [
|
||||
'**/*.js',
|
||||
],
|
||||
name: 'jsdoc/default-expressions/processor',
|
||||
plugins: {
|
||||
examples: getJsdocProcessorPlugin({
|
||||
checkDefaults: true,
|
||||
checkParams: true,
|
||||
checkProperties: true,
|
||||
}),
|
||||
},
|
||||
processor: 'examples/examples',
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'**/*.jsdoc-defaults', '**/*.jsdoc-params', '**/*.jsdoc-properties',
|
||||
],
|
||||
name: 'jsdoc/default-expressions/rules',
|
||||
rules: {
|
||||
...index.configs.examples[1].rules,
|
||||
'@stylistic/quotes': [
|
||||
'error', 'double',
|
||||
],
|
||||
'@stylistic/semi': [
|
||||
'error', 'never',
|
||||
],
|
||||
'@typescript-eslint/no-unused-expressions': 0,
|
||||
'chai-friendly/no-unused-expressions': 0,
|
||||
'no-empty-function': 0,
|
||||
'no-new': 0,
|
||||
'no-unused-expressions': 0,
|
||||
quotes: [
|
||||
'error', 'double',
|
||||
],
|
||||
semi: [
|
||||
'error', 'never',
|
||||
],
|
||||
strict: 0,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
index.configs['examples-and-default-expressions'] = /** @type {import('eslint').Linter.Config[]} */ ([
|
||||
{
|
||||
name: 'jsdoc/examples-and-default-expressions',
|
||||
plugins: {
|
||||
examples: getJsdocProcessorPlugin({
|
||||
checkDefaults: true,
|
||||
checkParams: true,
|
||||
checkProperties: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
...index.configs.examples.map((config) => {
|
||||
return {
|
||||
...config,
|
||||
plugins: {},
|
||||
};
|
||||
}),
|
||||
...index.configs['default-expressions'].map((config) => {
|
||||
return {
|
||||
...config,
|
||||
plugins: {},
|
||||
};
|
||||
}),
|
||||
]);
|
||||
|
||||
index.configs['flat/recommended-mixed'] = [
|
||||
{
|
||||
...index.configs['flat/recommended-typescript-flavor'],
|
||||
files: [
|
||||
'**/*.{js,jsx,cjs,mjs}',
|
||||
],
|
||||
},
|
||||
{
|
||||
...index.configs['flat/recommended-typescript'],
|
||||
files: [
|
||||
'**/*.{ts,tsx,cts,mts}',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default index;
|
||||
196
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/index-esm.js
generated
vendored
Normal file
196
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/index-esm.js
generated
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/* eslint-disable perfectionist/sort-imports -- For auto-generate; Do not remove */
|
||||
import {
|
||||
merge,
|
||||
} from 'object-deep-merge';
|
||||
|
||||
// BEGIN REPLACE
|
||||
import index from './index-cjs.js';
|
||||
import {
|
||||
buildForbidRuleDefinition,
|
||||
} from './buildForbidRuleDefinition.js';
|
||||
import {
|
||||
buildRejectOrPreferRuleDefinition,
|
||||
} from './buildRejectOrPreferRuleDefinition.js';
|
||||
|
||||
// eslint-disable-next-line unicorn/prefer-export-from --- Reusing `index`
|
||||
export default index;
|
||||
// END REPLACE
|
||||
|
||||
/**
|
||||
* @type {((
|
||||
* cfg?: import('eslint').Linter.Config & {
|
||||
* config?: `flat/${import('./index-cjs.js').ConfigGroups}${import('./index-cjs.js').ConfigVariants}${import('./index-cjs.js').ErrorLevelVariants}`,
|
||||
* mergeSettings?: boolean,
|
||||
* settings?: Partial<import('./iterateJsdoc.js').Settings>,
|
||||
* rules?: {[key in keyof import('./rules.d.ts').Rules]?: import('eslint').Linter.RuleEntry<import('./rules.d.ts').Rules[key]>},
|
||||
* extraRuleDefinitions?: {
|
||||
* forbid?: {
|
||||
* [contextName: string]: {
|
||||
* description?: string,
|
||||
* url?: string,
|
||||
* contexts: (string|{
|
||||
* message: string,
|
||||
* context: string,
|
||||
* comment: string
|
||||
* })[]
|
||||
* }
|
||||
* },
|
||||
* preferTypes?: {
|
||||
* [typeName: string]: {
|
||||
* description: string,
|
||||
* overrideSettings: {
|
||||
* [typeNodeName: string]: {
|
||||
* message: string,
|
||||
* replacement?: false|string,
|
||||
* unifyParentAndChildTypeChecks?: boolean,
|
||||
* }
|
||||
* },
|
||||
* url: string,
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ) => import('eslint').Linter.Config)}
|
||||
*/
|
||||
export const jsdoc = function (cfg) {
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
let outputConfig = {
|
||||
plugins: {
|
||||
jsdoc: index,
|
||||
},
|
||||
};
|
||||
|
||||
if (cfg) {
|
||||
if (cfg.config) {
|
||||
// @ts-expect-error Security check
|
||||
if (cfg.config === '__proto__') {
|
||||
throw new TypeError('Disallowed config value');
|
||||
}
|
||||
|
||||
outputConfig = /** @type {import('eslint').Linter.Config} */ (index.configs[cfg.config]);
|
||||
}
|
||||
|
||||
if (cfg.rules) {
|
||||
outputConfig.rules = {
|
||||
...outputConfig.rules,
|
||||
...cfg.rules,
|
||||
};
|
||||
}
|
||||
|
||||
if (cfg.plugins) {
|
||||
outputConfig.plugins = {
|
||||
...outputConfig.plugins,
|
||||
...cfg.plugins,
|
||||
};
|
||||
}
|
||||
|
||||
if (cfg.name) {
|
||||
outputConfig.name = cfg.name;
|
||||
}
|
||||
|
||||
if (cfg.basePath) {
|
||||
outputConfig.basePath = cfg.basePath;
|
||||
}
|
||||
|
||||
if (cfg.files) {
|
||||
outputConfig.files = cfg.files;
|
||||
}
|
||||
|
||||
if (cfg.ignores) {
|
||||
outputConfig.ignores = cfg.ignores;
|
||||
}
|
||||
|
||||
if (cfg.language) {
|
||||
outputConfig.language = cfg.language;
|
||||
}
|
||||
|
||||
if (cfg.languageOptions) {
|
||||
outputConfig.languageOptions = cfg.languageOptions;
|
||||
}
|
||||
|
||||
if (cfg.linterOptions) {
|
||||
outputConfig.linterOptions = cfg.linterOptions;
|
||||
}
|
||||
|
||||
if (cfg.processor) {
|
||||
outputConfig.processor = cfg.processor;
|
||||
}
|
||||
|
||||
if (cfg.extraRuleDefinitions) {
|
||||
if (!outputConfig.plugins?.jsdoc?.rules) {
|
||||
throw new Error('JSDoc plugin required for `extraRuleDefinitions`');
|
||||
}
|
||||
|
||||
if (cfg.extraRuleDefinitions.forbid) {
|
||||
for (const [
|
||||
contextName,
|
||||
{
|
||||
contexts,
|
||||
description,
|
||||
url,
|
||||
},
|
||||
] of Object.entries(cfg.extraRuleDefinitions.forbid)) {
|
||||
outputConfig.plugins.jsdoc.rules[`forbid-${contextName}`] =
|
||||
buildForbidRuleDefinition({
|
||||
contextName,
|
||||
contexts,
|
||||
description,
|
||||
url,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg.extraRuleDefinitions.preferTypes) {
|
||||
for (const [
|
||||
typeName,
|
||||
{
|
||||
description,
|
||||
overrideSettings,
|
||||
url,
|
||||
},
|
||||
] of Object.entries(cfg.extraRuleDefinitions.preferTypes)) {
|
||||
outputConfig.plugins.jsdoc.rules[`prefer-type-${typeName}`] =
|
||||
buildRejectOrPreferRuleDefinition({
|
||||
description,
|
||||
overrideSettings,
|
||||
typeName,
|
||||
url,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputConfig.settings = {
|
||||
jsdoc: cfg?.mergeSettings === false ?
|
||||
cfg.settings :
|
||||
merge(
|
||||
{},
|
||||
cfg?.settings ?? {},
|
||||
cfg?.config?.includes('recommended') ?
|
||||
{
|
||||
// We may need to drop these for "typescript" (non-"flavor") configs,
|
||||
// if support is later added: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
|
||||
structuredTags: {
|
||||
next: {
|
||||
required: [
|
||||
'type',
|
||||
],
|
||||
},
|
||||
rejects: {
|
||||
required: [
|
||||
'type',
|
||||
],
|
||||
},
|
||||
},
|
||||
} :
|
||||
{},
|
||||
),
|
||||
};
|
||||
|
||||
return outputConfig;
|
||||
};
|
||||
|
||||
export {
|
||||
getJsdocProcessorPlugin,
|
||||
} from './getJsdocProcessorPlugin.js';
|
||||
3
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/index.cjs.cts
generated
vendored
Normal file
3
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/index.cjs.cts
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import items from './index-cjs.js';
|
||||
|
||||
export = items;
|
||||
905
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/index.js
generated
vendored
Normal file
905
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,905 @@
|
|||
/* AUTO-GENERATED BY build SCRIPT */
|
||||
/* eslint-disable perfectionist/sort-imports -- For auto-generate; Do not remove */
|
||||
import {
|
||||
merge,
|
||||
} from 'object-deep-merge';
|
||||
|
||||
import {
|
||||
buildForbidRuleDefinition,
|
||||
} from './buildForbidRuleDefinition.js';
|
||||
import {
|
||||
buildRejectOrPreferRuleDefinition,
|
||||
} from './buildRejectOrPreferRuleDefinition.js';
|
||||
import {
|
||||
getJsdocProcessorPlugin,
|
||||
} from './getJsdocProcessorPlugin.js';
|
||||
import checkAccess from './rules/checkAccess.js';
|
||||
import checkAlignment from './rules/checkAlignment.js';
|
||||
import checkExamples from './rules/checkExamples.js';
|
||||
import checkIndentation from './rules/checkIndentation.js';
|
||||
import checkLineAlignment from './rules/checkLineAlignment.js';
|
||||
import checkParamNames from './rules/checkParamNames.js';
|
||||
import checkPropertyNames from './rules/checkPropertyNames.js';
|
||||
import checkSyntax from './rules/checkSyntax.js';
|
||||
import checkTagNames from './rules/checkTagNames.js';
|
||||
import checkTemplateNames from './rules/checkTemplateNames.js';
|
||||
import checkTypes from './rules/checkTypes.js';
|
||||
import checkValues from './rules/checkValues.js';
|
||||
import convertToJsdocComments from './rules/convertToJsdocComments.js';
|
||||
import emptyTags from './rules/emptyTags.js';
|
||||
import escapeInlineTags from './rules/escapeInlineTags.js';
|
||||
import implementsOnClasses from './rules/implementsOnClasses.js';
|
||||
import importsAsDependencies from './rules/importsAsDependencies.js';
|
||||
import informativeDocs from './rules/informativeDocs.js';
|
||||
import linesBeforeBlock from './rules/linesBeforeBlock.js';
|
||||
import matchDescription from './rules/matchDescription.js';
|
||||
import matchName from './rules/matchName.js';
|
||||
import multilineBlocks from './rules/multilineBlocks.js';
|
||||
import noBadBlocks from './rules/noBadBlocks.js';
|
||||
import noBlankBlockDescriptions from './rules/noBlankBlockDescriptions.js';
|
||||
import noBlankBlocks from './rules/noBlankBlocks.js';
|
||||
import noDefaults from './rules/noDefaults.js';
|
||||
import noMissingSyntax from './rules/noMissingSyntax.js';
|
||||
import noMultiAsterisks from './rules/noMultiAsterisks.js';
|
||||
import noRestrictedSyntax from './rules/noRestrictedSyntax.js';
|
||||
import noTypes from './rules/noTypes.js';
|
||||
import noUndefinedTypes from './rules/noUndefinedTypes.js';
|
||||
import preferImportTag from './rules/preferImportTag.js';
|
||||
import requireAsteriskPrefix from './rules/requireAsteriskPrefix.js';
|
||||
import requireDescription from './rules/requireDescription.js';
|
||||
import requireDescriptionCompleteSentence from './rules/requireDescriptionCompleteSentence.js';
|
||||
import requireExample from './rules/requireExample.js';
|
||||
import requireFileOverview from './rules/requireFileOverview.js';
|
||||
import requireHyphenBeforeParamDescription from './rules/requireHyphenBeforeParamDescription.js';
|
||||
import requireJsdoc from './rules/requireJsdoc.js';
|
||||
import requireParam from './rules/requireParam.js';
|
||||
import requireParamDescription from './rules/requireParamDescription.js';
|
||||
import requireParamName from './rules/requireParamName.js';
|
||||
import requireParamType from './rules/requireParamType.js';
|
||||
import requireProperty from './rules/requireProperty.js';
|
||||
import requirePropertyDescription from './rules/requirePropertyDescription.js';
|
||||
import requirePropertyName from './rules/requirePropertyName.js';
|
||||
import requirePropertyType from './rules/requirePropertyType.js';
|
||||
import requireRejects from './rules/requireRejects.js';
|
||||
import requireReturns from './rules/requireReturns.js';
|
||||
import requireReturnsCheck from './rules/requireReturnsCheck.js';
|
||||
import requireReturnsDescription from './rules/requireReturnsDescription.js';
|
||||
import requireReturnsType from './rules/requireReturnsType.js';
|
||||
import requireTags from './rules/requireTags.js';
|
||||
import requireTemplate from './rules/requireTemplate.js';
|
||||
import requireThrows from './rules/requireThrows.js';
|
||||
import requireYields from './rules/requireYields.js';
|
||||
import requireYieldsCheck from './rules/requireYieldsCheck.js';
|
||||
import sortTags from './rules/sortTags.js';
|
||||
import tagLines from './rules/tagLines.js';
|
||||
import textEscaping from './rules/textEscaping.js';
|
||||
import tsMethodSignatureStyle from './rules/tsMethodSignatureStyle.js';
|
||||
import tsNoEmptyObjectType from './rules/tsNoEmptyObjectType.js';
|
||||
import tsNoUnnecessaryTemplateExpression from './rules/tsNoUnnecessaryTemplateExpression.js';
|
||||
import tsPreferFunctionType from './rules/tsPreferFunctionType.js';
|
||||
import typeFormatting from './rules/typeFormatting.js';
|
||||
import validTypes from './rules/validTypes.js';
|
||||
|
||||
/**
|
||||
* @typedef {"recommended" | "stylistic" | "contents" | "logical" | "requirements"} ConfigGroups
|
||||
* @typedef {"" | "-typescript" | "-typescript-flavor"} ConfigVariants
|
||||
* @typedef {"" | "-error"} ErrorLevelVariants
|
||||
* @type {import('eslint').ESLint.Plugin & {
|
||||
* configs: Record<
|
||||
* `flat/${ConfigGroups}${ConfigVariants}${ErrorLevelVariants}`,
|
||||
* import('eslint').Linter.Config
|
||||
* > &
|
||||
* Record<
|
||||
* "examples"|"default-expressions"|"examples-and-default-expressions",
|
||||
* import('eslint').Linter.Config[]
|
||||
* > &
|
||||
* Record<"flat/recommended-mixed", import('eslint').Linter.Config[]>
|
||||
* }}
|
||||
*/
|
||||
const index = {};
|
||||
index.configs = {};
|
||||
index.rules = {
|
||||
'check-access': checkAccess,
|
||||
'check-alignment': checkAlignment,
|
||||
'check-examples': checkExamples,
|
||||
'check-indentation': checkIndentation,
|
||||
'check-line-alignment': checkLineAlignment,
|
||||
'check-param-names': checkParamNames,
|
||||
'check-property-names': checkPropertyNames,
|
||||
'check-syntax': checkSyntax,
|
||||
'check-tag-names': checkTagNames,
|
||||
'check-template-names': checkTemplateNames,
|
||||
'check-types': checkTypes,
|
||||
'check-values': checkValues,
|
||||
'convert-to-jsdoc-comments': convertToJsdocComments,
|
||||
'empty-tags': emptyTags,
|
||||
'escape-inline-tags': escapeInlineTags,
|
||||
'implements-on-classes': implementsOnClasses,
|
||||
'imports-as-dependencies': importsAsDependencies,
|
||||
'informative-docs': informativeDocs,
|
||||
'lines-before-block': linesBeforeBlock,
|
||||
'match-description': matchDescription,
|
||||
'match-name': matchName,
|
||||
'multiline-blocks': multilineBlocks,
|
||||
'no-bad-blocks': noBadBlocks,
|
||||
'no-blank-block-descriptions': noBlankBlockDescriptions,
|
||||
'no-blank-blocks': noBlankBlocks,
|
||||
'no-defaults': noDefaults,
|
||||
'no-missing-syntax': noMissingSyntax,
|
||||
'no-multi-asterisks': noMultiAsterisks,
|
||||
'no-restricted-syntax': noRestrictedSyntax,
|
||||
'no-types': noTypes,
|
||||
'no-undefined-types': noUndefinedTypes,
|
||||
'prefer-import-tag': preferImportTag,
|
||||
'reject-any-type': buildRejectOrPreferRuleDefinition({
|
||||
description: 'Reports use of `any` or `*` type',
|
||||
overrideSettings: {
|
||||
'*': {
|
||||
message: 'Prefer a more specific type to `*`',
|
||||
replacement: false,
|
||||
unifyParentAndChildTypeChecks: true,
|
||||
},
|
||||
any: {
|
||||
message: 'Prefer a more specific type to `any`',
|
||||
replacement: false,
|
||||
unifyParentAndChildTypeChecks: true,
|
||||
},
|
||||
},
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/reject-any-type.md#repos-sticky-header',
|
||||
}),
|
||||
'reject-function-type': buildRejectOrPreferRuleDefinition({
|
||||
description: 'Reports use of `Function` type',
|
||||
overrideSettings: {
|
||||
Function: {
|
||||
message: 'Prefer a more specific type to `Function`',
|
||||
replacement: false,
|
||||
unifyParentAndChildTypeChecks: true,
|
||||
},
|
||||
},
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/reject-function-type.md#repos-sticky-header',
|
||||
}),
|
||||
'require-asterisk-prefix': requireAsteriskPrefix,
|
||||
'require-description': requireDescription,
|
||||
'require-description-complete-sentence': requireDescriptionCompleteSentence,
|
||||
'require-example': requireExample,
|
||||
'require-file-overview': requireFileOverview,
|
||||
'require-hyphen-before-param-description': requireHyphenBeforeParamDescription,
|
||||
'require-jsdoc': requireJsdoc,
|
||||
'require-next-description': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=next]:not([name!=""]):not([description!=""]))',
|
||||
context: 'any',
|
||||
message: '@next should have a description',
|
||||
},
|
||||
],
|
||||
description: 'Requires a description for `@next` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-next-description.md#repos-sticky-header',
|
||||
}),
|
||||
'require-next-type': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=next]:not([parsedType.type]))',
|
||||
context: 'any',
|
||||
message: '@next should have a type',
|
||||
},
|
||||
],
|
||||
description: 'Requires a type for `@next` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-next-type.md#repos-sticky-header',
|
||||
}),
|
||||
'require-param': requireParam,
|
||||
'require-param-description': requireParamDescription,
|
||||
'require-param-name': requireParamName,
|
||||
'require-param-type': requireParamType,
|
||||
'require-property': requireProperty,
|
||||
'require-property-description': requirePropertyDescription,
|
||||
'require-property-name': requirePropertyName,
|
||||
'require-property-type': requirePropertyType,
|
||||
'require-rejects': requireRejects,
|
||||
'require-returns': requireReturns,
|
||||
'require-returns-check': requireReturnsCheck,
|
||||
'require-returns-description': requireReturnsDescription,
|
||||
'require-returns-type': requireReturnsType,
|
||||
'require-tags': requireTags,
|
||||
'require-template': requireTemplate,
|
||||
'require-template-description': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=template]:not([description!=""]))',
|
||||
context: 'any',
|
||||
message: '@template should have a description',
|
||||
},
|
||||
],
|
||||
description: 'Requires a description for `@template` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-template-description.md#repos-sticky-header',
|
||||
}),
|
||||
'require-throws': requireThrows,
|
||||
'require-throws-description': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=/^(?:throws|exception)$/]:not([description!=""]))',
|
||||
context: 'any',
|
||||
message: '@throws should have a description',
|
||||
},
|
||||
],
|
||||
description: 'Requires a description for `@throws` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-throws-description.md#repos-sticky-header',
|
||||
}),
|
||||
'require-throws-type': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=/^(?:throws|exception)$/]:not([parsedType.type]))',
|
||||
context: 'any',
|
||||
message: '@throws should have a type',
|
||||
},
|
||||
],
|
||||
description: 'Requires a type for `@throws` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-throws-type.md#repos-sticky-header',
|
||||
}),
|
||||
'require-yields': requireYields,
|
||||
'require-yields-check': requireYieldsCheck,
|
||||
'require-yields-description': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=/^yields?$/]:not([name!=""]):not([description!=""]))',
|
||||
context: 'any',
|
||||
message: '@yields should have a description',
|
||||
},
|
||||
],
|
||||
description: 'Requires a description for `@yields` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-yields-description.md#repos-sticky-header',
|
||||
}),
|
||||
'require-yields-type': buildForbidRuleDefinition({
|
||||
contexts: [
|
||||
{
|
||||
comment: 'JsdocBlock:has(JsdocTag[tag=/^yields?$/]:not([parsedType.type]))',
|
||||
context: 'any',
|
||||
message: '@yields should have a type',
|
||||
},
|
||||
],
|
||||
description: 'Requires a type for `@yields` tags',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-yields-type.md#repos-sticky-header',
|
||||
}),
|
||||
'sort-tags': sortTags,
|
||||
'tag-lines': tagLines,
|
||||
'text-escaping': textEscaping,
|
||||
'ts-method-signature-style': tsMethodSignatureStyle,
|
||||
'ts-no-empty-object-type': tsNoEmptyObjectType,
|
||||
'ts-no-unnecessary-template-expression': tsNoUnnecessaryTemplateExpression,
|
||||
'ts-prefer-function-type': tsPreferFunctionType,
|
||||
'type-formatting': typeFormatting,
|
||||
'valid-types': validTypes,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {"warn"|"error"} warnOrError
|
||||
* @param {string} [flatName]
|
||||
* @returns {import('eslint').Linter.Config}
|
||||
*/
|
||||
const createRecommendedRuleset = (warnOrError, flatName) => {
|
||||
return {
|
||||
...(flatName ? {
|
||||
name: 'jsdoc/' + flatName,
|
||||
} : {}),
|
||||
// @ts-expect-error ESLint 8 plugins
|
||||
plugins:
|
||||
flatName ? {
|
||||
jsdoc: index,
|
||||
} : [
|
||||
'jsdoc',
|
||||
],
|
||||
rules: {
|
||||
'jsdoc/check-access': warnOrError,
|
||||
'jsdoc/check-alignment': warnOrError,
|
||||
'jsdoc/check-examples': 'off',
|
||||
'jsdoc/check-indentation': 'off',
|
||||
'jsdoc/check-line-alignment': 'off',
|
||||
'jsdoc/check-param-names': warnOrError,
|
||||
'jsdoc/check-property-names': warnOrError,
|
||||
'jsdoc/check-syntax': 'off',
|
||||
'jsdoc/check-tag-names': warnOrError,
|
||||
'jsdoc/check-template-names': 'off',
|
||||
'jsdoc/check-types': warnOrError,
|
||||
'jsdoc/check-values': warnOrError,
|
||||
'jsdoc/convert-to-jsdoc-comments': 'off',
|
||||
'jsdoc/empty-tags': warnOrError,
|
||||
'jsdoc/escape-inline-tags': warnOrError,
|
||||
'jsdoc/implements-on-classes': warnOrError,
|
||||
'jsdoc/imports-as-dependencies': 'off',
|
||||
'jsdoc/informative-docs': 'off',
|
||||
'jsdoc/lines-before-block': 'off',
|
||||
'jsdoc/match-description': 'off',
|
||||
'jsdoc/match-name': 'off',
|
||||
'jsdoc/multiline-blocks': warnOrError,
|
||||
'jsdoc/no-bad-blocks': 'off',
|
||||
'jsdoc/no-blank-block-descriptions': 'off',
|
||||
'jsdoc/no-blank-blocks': 'off',
|
||||
'jsdoc/no-defaults': warnOrError,
|
||||
'jsdoc/no-missing-syntax': 'off',
|
||||
'jsdoc/no-multi-asterisks': warnOrError,
|
||||
'jsdoc/no-restricted-syntax': 'off',
|
||||
'jsdoc/no-types': 'off',
|
||||
'jsdoc/no-undefined-types': warnOrError,
|
||||
'jsdoc/prefer-import-tag': 'off',
|
||||
'jsdoc/reject-any-type': warnOrError,
|
||||
'jsdoc/reject-function-type': warnOrError,
|
||||
'jsdoc/require-asterisk-prefix': 'off',
|
||||
'jsdoc/require-description': 'off',
|
||||
'jsdoc/require-description-complete-sentence': 'off',
|
||||
'jsdoc/require-example': 'off',
|
||||
'jsdoc/require-file-overview': 'off',
|
||||
'jsdoc/require-hyphen-before-param-description': 'off',
|
||||
'jsdoc/require-jsdoc': warnOrError,
|
||||
'jsdoc/require-next-description': 'off',
|
||||
'jsdoc/require-next-type': warnOrError,
|
||||
'jsdoc/require-param': warnOrError,
|
||||
'jsdoc/require-param-description': warnOrError,
|
||||
'jsdoc/require-param-name': warnOrError,
|
||||
'jsdoc/require-param-type': warnOrError,
|
||||
'jsdoc/require-property': warnOrError,
|
||||
'jsdoc/require-property-description': warnOrError,
|
||||
'jsdoc/require-property-name': warnOrError,
|
||||
'jsdoc/require-property-type': warnOrError,
|
||||
'jsdoc/require-rejects': 'off',
|
||||
'jsdoc/require-returns': warnOrError,
|
||||
'jsdoc/require-returns-check': warnOrError,
|
||||
'jsdoc/require-returns-description': warnOrError,
|
||||
'jsdoc/require-returns-type': warnOrError,
|
||||
'jsdoc/require-tags': 'off',
|
||||
'jsdoc/require-template': 'off',
|
||||
'jsdoc/require-template-description': 'off',
|
||||
'jsdoc/require-throws': 'off',
|
||||
'jsdoc/require-throws-description': 'off',
|
||||
'jsdoc/require-throws-type': warnOrError,
|
||||
'jsdoc/require-yields': warnOrError,
|
||||
'jsdoc/require-yields-check': warnOrError,
|
||||
'jsdoc/require-yields-description': 'off',
|
||||
'jsdoc/require-yields-type': warnOrError,
|
||||
'jsdoc/sort-tags': 'off',
|
||||
'jsdoc/tag-lines': warnOrError,
|
||||
'jsdoc/text-escaping': 'off',
|
||||
'jsdoc/ts-method-signature-style': 'off',
|
||||
'jsdoc/ts-no-empty-object-type': warnOrError,
|
||||
'jsdoc/ts-no-unnecessary-template-expression': 'off',
|
||||
'jsdoc/ts-prefer-function-type': 'off',
|
||||
'jsdoc/type-formatting': 'off',
|
||||
'jsdoc/valid-types': warnOrError,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {"warn"|"error"} warnOrError
|
||||
* @param {string} [flatName]
|
||||
* @returns {import('eslint').Linter.Config}
|
||||
*/
|
||||
const createRecommendedTypeScriptRuleset = (warnOrError, flatName) => {
|
||||
const ruleset = createRecommendedRuleset(warnOrError, flatName);
|
||||
|
||||
return {
|
||||
...ruleset,
|
||||
rules: {
|
||||
...ruleset.rules,
|
||||
/* eslint-disable @stylistic/indent -- Extra indent to avoid use by auto-rule-editing */
|
||||
'jsdoc/check-tag-names': [
|
||||
warnOrError, {
|
||||
typed: true,
|
||||
},
|
||||
],
|
||||
'jsdoc/no-types': warnOrError,
|
||||
'jsdoc/no-undefined-types': 'off',
|
||||
'jsdoc/require-param-type': 'off',
|
||||
'jsdoc/require-property-type': 'off',
|
||||
'jsdoc/require-returns-type': 'off',
|
||||
/* eslint-enable @stylistic/indent */
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {"warn"|"error"} warnOrError
|
||||
* @param {string} [flatName]
|
||||
* @returns {import('eslint').Linter.Config}
|
||||
*/
|
||||
const createRecommendedTypeScriptFlavorRuleset = (warnOrError, flatName) => {
|
||||
const ruleset = createRecommendedRuleset(warnOrError, flatName);
|
||||
|
||||
return {
|
||||
...ruleset,
|
||||
rules: {
|
||||
...ruleset.rules,
|
||||
/* eslint-disable @stylistic/indent -- Extra indent to avoid use by auto-rule-editing */
|
||||
'jsdoc/no-undefined-types': 'off',
|
||||
/* eslint-enable @stylistic/indent */
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {(string | unknown[])[]} ruleNames
|
||||
*/
|
||||
const createStandaloneRulesetFactory = (ruleNames) => {
|
||||
/**
|
||||
* @param {"warn"|"error"} warnOrError
|
||||
* @param {string} [flatName]
|
||||
* @returns {import('eslint').Linter.Config}
|
||||
*/
|
||||
return (warnOrError, flatName) => {
|
||||
return {
|
||||
name: 'jsdoc/' + flatName,
|
||||
plugins: {
|
||||
jsdoc: index,
|
||||
},
|
||||
rules: Object.fromEntries(
|
||||
ruleNames.map(
|
||||
(ruleName) => {
|
||||
return (typeof ruleName === 'string' ?
|
||||
[
|
||||
ruleName, warnOrError,
|
||||
] :
|
||||
[
|
||||
ruleName[0], [
|
||||
warnOrError, ...ruleName.slice(1),
|
||||
],
|
||||
]);
|
||||
},
|
||||
),
|
||||
),
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const contentsRules = [
|
||||
'jsdoc/informative-docs',
|
||||
'jsdoc/match-description',
|
||||
'jsdoc/no-blank-block-descriptions',
|
||||
'jsdoc/no-blank-blocks',
|
||||
[
|
||||
'jsdoc/text-escaping', {
|
||||
escapeHTML: true,
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
const createContentsTypescriptRuleset = createStandaloneRulesetFactory(contentsRules);
|
||||
|
||||
const createContentsTypescriptFlavorRuleset = createStandaloneRulesetFactory(contentsRules);
|
||||
|
||||
const logicalRules = [
|
||||
'jsdoc/check-access',
|
||||
'jsdoc/check-param-names',
|
||||
'jsdoc/check-property-names',
|
||||
'jsdoc/check-syntax',
|
||||
'jsdoc/check-tag-names',
|
||||
'jsdoc/check-template-names',
|
||||
'jsdoc/check-types',
|
||||
'jsdoc/check-values',
|
||||
'jsdoc/empty-tags',
|
||||
'jsdoc/escape-inline-tags',
|
||||
'jsdoc/implements-on-classes',
|
||||
'jsdoc/require-returns-check',
|
||||
'jsdoc/require-yields-check',
|
||||
'jsdoc/no-bad-blocks',
|
||||
'jsdoc/no-defaults',
|
||||
'jsdoc/no-types',
|
||||
'jsdoc/no-undefined-types',
|
||||
'jsdoc/valid-types',
|
||||
];
|
||||
|
||||
const createLogicalTypescriptRuleset = createStandaloneRulesetFactory(logicalRules);
|
||||
|
||||
const createLogicalTypescriptFlavorRuleset = createStandaloneRulesetFactory(logicalRules);
|
||||
|
||||
const requirementsRules = [
|
||||
'jsdoc/require-example',
|
||||
'jsdoc/require-jsdoc',
|
||||
'jsdoc/require-next-type',
|
||||
'jsdoc/require-param',
|
||||
'jsdoc/require-param-description',
|
||||
'jsdoc/require-param-name',
|
||||
'jsdoc/require-property',
|
||||
'jsdoc/require-property-description',
|
||||
'jsdoc/require-property-name',
|
||||
'jsdoc/require-returns',
|
||||
'jsdoc/require-returns-description',
|
||||
'jsdoc/require-throws-type',
|
||||
'jsdoc/require-yields',
|
||||
'jsdoc/require-yields-type',
|
||||
];
|
||||
|
||||
const createRequirementsTypeScriptRuleset = createStandaloneRulesetFactory(requirementsRules);
|
||||
|
||||
const createRequirementsTypeScriptFlavorRuleset = createStandaloneRulesetFactory([
|
||||
...requirementsRules,
|
||||
'jsdoc/require-param-type',
|
||||
'jsdoc/require-property-type',
|
||||
'jsdoc/require-returns-type',
|
||||
'jsdoc/require-template',
|
||||
]);
|
||||
|
||||
const stylisticRules = [
|
||||
'jsdoc/check-alignment',
|
||||
'jsdoc/check-line-alignment',
|
||||
'jsdoc/lines-before-block',
|
||||
'jsdoc/multiline-blocks',
|
||||
'jsdoc/no-multi-asterisks',
|
||||
'jsdoc/require-asterisk-prefix',
|
||||
[
|
||||
'jsdoc/require-hyphen-before-param-description', 'never',
|
||||
],
|
||||
'jsdoc/tag-lines',
|
||||
];
|
||||
|
||||
const createStylisticTypeScriptRuleset = createStandaloneRulesetFactory(stylisticRules);
|
||||
|
||||
const createStylisticTypeScriptFlavorRuleset = createStandaloneRulesetFactory(stylisticRules);
|
||||
|
||||
/* c8 ignore next 3 -- TS */
|
||||
if (!index.configs) {
|
||||
throw new Error('TypeScript guard');
|
||||
}
|
||||
|
||||
index.configs.recommended = createRecommendedRuleset('warn');
|
||||
index.configs['recommended-error'] = createRecommendedRuleset('error');
|
||||
index.configs['recommended-typescript'] = createRecommendedTypeScriptRuleset('warn');
|
||||
index.configs['recommended-typescript-error'] = createRecommendedTypeScriptRuleset('error');
|
||||
index.configs['recommended-typescript-flavor'] = createRecommendedTypeScriptFlavorRuleset('warn');
|
||||
index.configs['recommended-typescript-flavor-error'] = createRecommendedTypeScriptFlavorRuleset('error');
|
||||
|
||||
index.configs['flat/recommended'] = createRecommendedRuleset('warn', 'flat/recommended');
|
||||
index.configs['flat/recommended-error'] = createRecommendedRuleset('error', 'flat/recommended-error');
|
||||
index.configs['flat/recommended-typescript'] = createRecommendedTypeScriptRuleset('warn', 'flat/recommended-typescript');
|
||||
index.configs['flat/recommended-typescript-error'] = createRecommendedTypeScriptRuleset('error', 'flat/recommended-typescript-error');
|
||||
index.configs['flat/recommended-typescript-flavor'] = createRecommendedTypeScriptFlavorRuleset('warn', 'flat/recommended-typescript-flavor');
|
||||
index.configs['flat/recommended-typescript-flavor-error'] = createRecommendedTypeScriptFlavorRuleset('error', 'flat/recommended-typescript-flavor-error');
|
||||
|
||||
index.configs['flat/contents-typescript'] = createContentsTypescriptRuleset('warn', 'flat/contents-typescript');
|
||||
index.configs['flat/contents-typescript-error'] = createContentsTypescriptRuleset('error', 'flat/contents-typescript-error');
|
||||
index.configs['flat/contents-typescript-flavor'] = createContentsTypescriptFlavorRuleset('warn', 'flat/contents-typescript-flavor');
|
||||
index.configs['flat/contents-typescript-flavor-error'] = createContentsTypescriptFlavorRuleset('error', 'flat/contents-typescript-error-flavor');
|
||||
index.configs['flat/logical-typescript'] = createLogicalTypescriptRuleset('warn', 'flat/logical-typescript');
|
||||
index.configs['flat/logical-typescript-error'] = createLogicalTypescriptRuleset('error', 'flat/logical-typescript-error');
|
||||
index.configs['flat/logical-typescript-flavor'] = createLogicalTypescriptFlavorRuleset('warn', 'flat/logical-typescript-flavor');
|
||||
index.configs['flat/logical-typescript-flavor-error'] = createLogicalTypescriptFlavorRuleset('error', 'flat/logical-typescript-error-flavor');
|
||||
index.configs['flat/requirements-typescript'] = createRequirementsTypeScriptRuleset('warn', 'flat/requirements-typescript');
|
||||
index.configs['flat/requirements-typescript-error'] = createRequirementsTypeScriptRuleset('error', 'flat/requirements-typescript-error');
|
||||
index.configs['flat/requirements-typescript-flavor'] = createRequirementsTypeScriptFlavorRuleset('warn', 'flat/requirements-typescript-flavor');
|
||||
index.configs['flat/requirements-typescript-flavor-error'] = createRequirementsTypeScriptFlavorRuleset('error', 'flat/requirements-typescript-error-flavor');
|
||||
index.configs['flat/stylistic-typescript'] = createStylisticTypeScriptRuleset('warn', 'flat/stylistic-typescript');
|
||||
index.configs['flat/stylistic-typescript-error'] = createStylisticTypeScriptRuleset('error', 'flat/stylistic-typescript-error');
|
||||
index.configs['flat/stylistic-typescript-flavor'] = createStylisticTypeScriptFlavorRuleset('warn', 'flat/stylistic-typescript-flavor');
|
||||
index.configs['flat/stylistic-typescript-flavor-error'] = createStylisticTypeScriptFlavorRuleset('error', 'flat/stylistic-typescript-error-flavor');
|
||||
|
||||
index.configs.examples = /** @type {import('eslint').Linter.Config[]} */ ([
|
||||
{
|
||||
files: [
|
||||
'**/*.js',
|
||||
],
|
||||
name: 'jsdoc/examples/processor',
|
||||
plugins: {
|
||||
examples: getJsdocProcessorPlugin(),
|
||||
},
|
||||
processor: 'examples/examples',
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'**/*.md/*.js',
|
||||
],
|
||||
name: 'jsdoc/examples/rules',
|
||||
rules: {
|
||||
// "always" newline rule at end unlikely in sample code
|
||||
'@stylistic/eol-last': 0,
|
||||
// Often wish to start `@example` code after newline; also may use
|
||||
// empty lines for spacing
|
||||
'@stylistic/no-multiple-empty-lines': 0,
|
||||
|
||||
// Can generally look nicer to pad a little even if code imposes more stringency
|
||||
'@stylistic/padded-blocks': 0,
|
||||
|
||||
'@typescript-eslint/no-unused-vars': 0,
|
||||
|
||||
// "always" newline rule at end unlikely in sample code
|
||||
'eol-last': 0,
|
||||
|
||||
// Wouldn't generally expect example paths to resolve relative to JS file
|
||||
'import/no-unresolved': 0,
|
||||
|
||||
// Snippets likely too short to always include import/export info
|
||||
'import/unambiguous': 0,
|
||||
|
||||
'jsdoc/require-file-overview': 0,
|
||||
|
||||
// The end of a multiline comment would end the comment the example is in.
|
||||
'jsdoc/require-jsdoc': 0,
|
||||
|
||||
// See import/no-unresolved
|
||||
'n/no-missing-import': 0,
|
||||
|
||||
'n/no-missing-require': 0,
|
||||
|
||||
// Unlikely to have inadvertent debugging within examples
|
||||
'no-console': 0,
|
||||
|
||||
// Often wish to start `@example` code after newline; also may use
|
||||
// empty lines for spacing
|
||||
'no-multiple-empty-lines': 0,
|
||||
|
||||
// Many variables in examples will be `undefined`
|
||||
'no-undef': 0,
|
||||
// Common to define variables for clarity without always using them
|
||||
'no-unused-vars': 0,
|
||||
|
||||
// See import/no-unresolved
|
||||
'node/no-missing-import': 0,
|
||||
'node/no-missing-require': 0,
|
||||
|
||||
// Can generally look nicer to pad a little even if code imposes more stringency
|
||||
'padded-blocks': 0,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
index.configs['default-expressions'] = /** @type {import('eslint').Linter.Config[]} */ ([
|
||||
{
|
||||
files: [
|
||||
'**/*.js',
|
||||
],
|
||||
name: 'jsdoc/default-expressions/processor',
|
||||
plugins: {
|
||||
examples: getJsdocProcessorPlugin({
|
||||
checkDefaults: true,
|
||||
checkParams: true,
|
||||
checkProperties: true,
|
||||
}),
|
||||
},
|
||||
processor: 'examples/examples',
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'**/*.jsdoc-defaults', '**/*.jsdoc-params', '**/*.jsdoc-properties',
|
||||
],
|
||||
name: 'jsdoc/default-expressions/rules',
|
||||
rules: {
|
||||
...index.configs.examples[1].rules,
|
||||
'@stylistic/quotes': [
|
||||
'error', 'double',
|
||||
],
|
||||
'@stylistic/semi': [
|
||||
'error', 'never',
|
||||
],
|
||||
'@typescript-eslint/no-unused-expressions': 0,
|
||||
'chai-friendly/no-unused-expressions': 0,
|
||||
'no-empty-function': 0,
|
||||
'no-new': 0,
|
||||
'no-unused-expressions': 0,
|
||||
quotes: [
|
||||
'error', 'double',
|
||||
],
|
||||
semi: [
|
||||
'error', 'never',
|
||||
],
|
||||
strict: 0,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
index.configs['examples-and-default-expressions'] = /** @type {import('eslint').Linter.Config[]} */ ([
|
||||
{
|
||||
name: 'jsdoc/examples-and-default-expressions',
|
||||
plugins: {
|
||||
examples: getJsdocProcessorPlugin({
|
||||
checkDefaults: true,
|
||||
checkParams: true,
|
||||
checkProperties: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
...index.configs.examples.map((config) => {
|
||||
return {
|
||||
...config,
|
||||
plugins: {},
|
||||
};
|
||||
}),
|
||||
...index.configs['default-expressions'].map((config) => {
|
||||
return {
|
||||
...config,
|
||||
plugins: {},
|
||||
};
|
||||
}),
|
||||
]);
|
||||
|
||||
index.configs['flat/recommended-mixed'] = [
|
||||
{
|
||||
...index.configs['flat/recommended-typescript-flavor'],
|
||||
files: [
|
||||
'**/*.{js,jsx,cjs,mjs}',
|
||||
],
|
||||
},
|
||||
{
|
||||
...index.configs['flat/recommended-typescript'],
|
||||
files: [
|
||||
'**/*.{ts,tsx,cts,mts}',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default index;
|
||||
|
||||
/**
|
||||
* @type {((
|
||||
* cfg?: import('eslint').Linter.Config & {
|
||||
* config?: `flat/${ConfigGroups}${ConfigVariants}${ErrorLevelVariants}`,
|
||||
* mergeSettings?: boolean,
|
||||
* settings?: Partial<import('./iterateJsdoc.js').Settings>,
|
||||
* rules?: {[key in keyof import('./rules.d.ts').Rules]?: import('eslint').Linter.RuleEntry<import('./rules.d.ts').Rules[key]>},
|
||||
* extraRuleDefinitions?: {
|
||||
* forbid?: {
|
||||
* [contextName: string]: {
|
||||
* description?: string,
|
||||
* url?: string,
|
||||
* contexts: (string|{
|
||||
* message: string,
|
||||
* context: string,
|
||||
* comment: string
|
||||
* })[]
|
||||
* }
|
||||
* },
|
||||
* preferTypes?: {
|
||||
* [typeName: string]: {
|
||||
* description: string,
|
||||
* overrideSettings: {
|
||||
* [typeNodeName: string]: {
|
||||
* message: string,
|
||||
* replacement?: false|string,
|
||||
* unifyParentAndChildTypeChecks?: boolean,
|
||||
* }
|
||||
* },
|
||||
* url: string,
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ) => import('eslint').Linter.Config)}
|
||||
*/
|
||||
export const jsdoc = function (cfg) {
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
let outputConfig = {
|
||||
plugins: {
|
||||
jsdoc: index,
|
||||
},
|
||||
};
|
||||
|
||||
if (cfg) {
|
||||
if (cfg.config) {
|
||||
// @ts-expect-error Security check
|
||||
if (cfg.config === '__proto__') {
|
||||
throw new TypeError('Disallowed config value');
|
||||
}
|
||||
|
||||
outputConfig = /** @type {import('eslint').Linter.Config} */ (index.configs[cfg.config]);
|
||||
}
|
||||
|
||||
if (cfg.rules) {
|
||||
outputConfig.rules = {
|
||||
...outputConfig.rules,
|
||||
...cfg.rules,
|
||||
};
|
||||
}
|
||||
|
||||
if (cfg.plugins) {
|
||||
outputConfig.plugins = {
|
||||
...outputConfig.plugins,
|
||||
...cfg.plugins,
|
||||
};
|
||||
}
|
||||
|
||||
if (cfg.name) {
|
||||
outputConfig.name = cfg.name;
|
||||
}
|
||||
|
||||
if (cfg.basePath) {
|
||||
outputConfig.basePath = cfg.basePath;
|
||||
}
|
||||
|
||||
if (cfg.files) {
|
||||
outputConfig.files = cfg.files;
|
||||
}
|
||||
|
||||
if (cfg.ignores) {
|
||||
outputConfig.ignores = cfg.ignores;
|
||||
}
|
||||
|
||||
if (cfg.language) {
|
||||
outputConfig.language = cfg.language;
|
||||
}
|
||||
|
||||
if (cfg.languageOptions) {
|
||||
outputConfig.languageOptions = cfg.languageOptions;
|
||||
}
|
||||
|
||||
if (cfg.linterOptions) {
|
||||
outputConfig.linterOptions = cfg.linterOptions;
|
||||
}
|
||||
|
||||
if (cfg.processor) {
|
||||
outputConfig.processor = cfg.processor;
|
||||
}
|
||||
|
||||
if (cfg.extraRuleDefinitions) {
|
||||
if (!outputConfig.plugins?.jsdoc?.rules) {
|
||||
throw new Error('JSDoc plugin required for `extraRuleDefinitions`');
|
||||
}
|
||||
|
||||
if (cfg.extraRuleDefinitions.forbid) {
|
||||
for (const [
|
||||
contextName,
|
||||
{
|
||||
contexts,
|
||||
description,
|
||||
url,
|
||||
},
|
||||
] of Object.entries(cfg.extraRuleDefinitions.forbid)) {
|
||||
outputConfig.plugins.jsdoc.rules[`forbid-${contextName}`] =
|
||||
buildForbidRuleDefinition({
|
||||
contextName,
|
||||
contexts,
|
||||
description,
|
||||
url,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg.extraRuleDefinitions.preferTypes) {
|
||||
for (const [
|
||||
typeName,
|
||||
{
|
||||
description,
|
||||
overrideSettings,
|
||||
url,
|
||||
},
|
||||
] of Object.entries(cfg.extraRuleDefinitions.preferTypes)) {
|
||||
outputConfig.plugins.jsdoc.rules[`prefer-type-${typeName}`] =
|
||||
buildRejectOrPreferRuleDefinition({
|
||||
description,
|
||||
overrideSettings,
|
||||
typeName,
|
||||
url,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputConfig.settings = {
|
||||
jsdoc: cfg?.mergeSettings === false ?
|
||||
cfg.settings :
|
||||
merge(
|
||||
{},
|
||||
cfg?.settings ?? {},
|
||||
cfg?.config?.includes('recommended') ?
|
||||
{
|
||||
// We may need to drop these for "typescript" (non-"flavor") configs,
|
||||
// if support is later added: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
|
||||
structuredTags: {
|
||||
next: {
|
||||
required: [
|
||||
'type',
|
||||
],
|
||||
},
|
||||
rejects: {
|
||||
required: [
|
||||
'type',
|
||||
],
|
||||
},
|
||||
},
|
||||
} :
|
||||
{},
|
||||
),
|
||||
};
|
||||
|
||||
return outputConfig;
|
||||
};
|
||||
|
||||
export {
|
||||
getJsdocProcessorPlugin,
|
||||
} from './getJsdocProcessorPlugin.js';
|
||||
7
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/iterateJsdoc.cts
generated
vendored
Normal file
7
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/iterateJsdoc.cts
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import iterateJsdoc, {getSettings, parseComment} from './iterateJsdoc.js';
|
||||
|
||||
export = {
|
||||
default: iterateJsdoc as typeof iterateJsdoc,
|
||||
getSettings: getSettings as typeof getSettings,
|
||||
parseComment: parseComment as typeof parseComment
|
||||
};
|
||||
2601
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/iterateJsdoc.js
generated
vendored
Normal file
2601
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/iterateJsdoc.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
2155
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/jsdocUtils.js
generated
vendored
Normal file
2155
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/jsdocUtils.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
3191
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules.d.ts
generated
vendored
Normal file
3191
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
45
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkAccess.js
generated
vendored
Normal file
45
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkAccess.js
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
const accessLevels = [
|
||||
'package', 'private', 'protected', 'public',
|
||||
];
|
||||
|
||||
export default iterateJsdoc(({
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
utils.forEachPreferredTag('access', (jsdocParameter, targetTagName) => {
|
||||
const desc = jsdocParameter.name + ' ' + jsdocParameter.description;
|
||||
|
||||
if (!accessLevels.includes(desc.trim())) {
|
||||
report(
|
||||
`Missing valid JSDoc @${targetTagName} level.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
});
|
||||
const accessLength = utils.getTags('access').length;
|
||||
const individualTagLength = utils.getPresentTags(accessLevels).length;
|
||||
if (accessLength && individualTagLength) {
|
||||
report(
|
||||
'The @access tag may not be used with specific access-control tags (@package, @private, @protected, or @public).',
|
||||
);
|
||||
}
|
||||
|
||||
if (accessLength > 1 || individualTagLength > 1) {
|
||||
report(
|
||||
'At most one access-control tag may be present on a JSDoc block.',
|
||||
);
|
||||
}
|
||||
}, {
|
||||
checkPrivate: true,
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Checks that `@access` tags have a valid value.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-access.md#repos-sticky-header',
|
||||
},
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
82
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkAlignment.js
generated
vendored
Normal file
82
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkAlignment.js
generated
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
indent,
|
||||
jsdocNode,
|
||||
report,
|
||||
sourceCode,
|
||||
}) => {
|
||||
const {
|
||||
innerIndent = 1,
|
||||
} = context.options[0] || {};
|
||||
|
||||
// `indent` is whitespace from line 1 (`/**`), so slice and account for "/".
|
||||
const indentLevel = indent.length + innerIndent;
|
||||
const sourceLines = sourceCode.getText(jsdocNode).split('\n')
|
||||
.slice(1)
|
||||
.map((line, number) => {
|
||||
return {
|
||||
line: line.split('*')[0],
|
||||
number,
|
||||
};
|
||||
})
|
||||
.filter(({
|
||||
line,
|
||||
}) => {
|
||||
return !line.trimStart().length;
|
||||
});
|
||||
|
||||
/** @type {import('eslint').Rule.ReportFixer} */
|
||||
const fix = (fixer) => {
|
||||
const replacement = sourceCode.getText(jsdocNode).split('\n')
|
||||
.map((line, index) => {
|
||||
// Ignore the first line and all lines not starting with `*`
|
||||
const ignored = !index || line.split('*')[0].trimStart().length;
|
||||
|
||||
return ignored ? line : `${indent}${''.padStart(innerIndent, ' ')}${line.trimStart()}`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
return fixer.replaceText(jsdocNode, replacement);
|
||||
};
|
||||
|
||||
sourceLines.some(({
|
||||
line,
|
||||
number,
|
||||
}) => {
|
||||
if (line.length !== indentLevel) {
|
||||
report('Expected JSDoc block to be aligned.', fix, {
|
||||
line: number + 1,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Reports invalid alignment of JSDoc block asterisks.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-alignment.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
innerIndent: {
|
||||
default: 1,
|
||||
description: `Set to 0 if you wish to avoid the normal requirement for an inner indentation of
|
||||
one space. Defaults to 1 (one space of normal inner indentation).`,
|
||||
type: 'integer',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'layout',
|
||||
},
|
||||
});
|
||||
612
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkExamples.js
generated
vendored
Normal file
612
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkExamples.js
generated
vendored
Normal file
|
|
@ -0,0 +1,612 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import eslint, {
|
||||
ESLint,
|
||||
} from 'eslint';
|
||||
import semver from 'semver';
|
||||
|
||||
const {
|
||||
// @ts-expect-error Older ESLint
|
||||
CLIEngine,
|
||||
} = eslint;
|
||||
|
||||
const zeroBasedLineIndexAdjust = -1;
|
||||
const likelyNestedJSDocIndentSpace = 1;
|
||||
const preTagSpaceLength = 1;
|
||||
|
||||
// If a space is present, we should ignore it
|
||||
const firstLinePrefixLength = preTagSpaceLength;
|
||||
|
||||
const hasCaptionRegex = /^\s*<caption>([\s\S]*?)<\/caption>/v;
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
const escapeStringRegexp = (str) => {
|
||||
return str.replaceAll(/[.*+?^$\{\}\(\)\|\[\]\\]/gv, '\\$&');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @param {string} ch
|
||||
* @returns {import('../iterateJsdoc.js').Integer}
|
||||
*/
|
||||
const countChars = (str, ch) => {
|
||||
return (str.match(new RegExp(escapeStringRegexp(ch), 'gv')) || []).length;
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Linter.RulesRecord} */
|
||||
const defaultMdRules = {
|
||||
// "always" newline rule at end unlikely in sample code
|
||||
'@stylistic/eol-last': 0,
|
||||
|
||||
// Often wish to start `@example` code after newline; also may use
|
||||
// empty lines for spacing
|
||||
'@stylistic/no-multiple-empty-lines': 0,
|
||||
|
||||
// Can generally look nicer to pad a little even if code imposes more stringency
|
||||
'@stylistic/padded-blocks': 0,
|
||||
|
||||
'@typescript-eslint/no-unused-vars': 0,
|
||||
|
||||
// "always" newline rule at end unlikely in sample code
|
||||
'eol-last': 0,
|
||||
|
||||
// Wouldn't generally expect example paths to resolve relative to JS file
|
||||
'import/no-unresolved': 0,
|
||||
|
||||
// Snippets likely too short to always include import/export info
|
||||
'import/unambiguous': 0,
|
||||
|
||||
'jsdoc/require-file-overview': 0,
|
||||
|
||||
// The end of a multiline comment would end the comment the example is in.
|
||||
'jsdoc/require-jsdoc': 0,
|
||||
|
||||
// See import/no-unresolved
|
||||
'n/no-missing-import': 0,
|
||||
'n/no-missing-require': 0,
|
||||
|
||||
// Unlikely to have inadvertent debugging within examples
|
||||
'no-console': 0,
|
||||
|
||||
// Often wish to start `@example` code after newline; also may use
|
||||
// empty lines for spacing
|
||||
'no-multiple-empty-lines': 0,
|
||||
// Many variables in examples will be `undefined`
|
||||
'no-undef': 0,
|
||||
|
||||
// Common to define variables for clarity without always using them
|
||||
'no-unused-vars': 0,
|
||||
|
||||
// See import/no-unresolved
|
||||
'node/no-missing-import': 0,
|
||||
|
||||
'node/no-missing-require': 0,
|
||||
|
||||
// Can generally look nicer to pad a little even if code imposes more stringency
|
||||
'padded-blocks': 0,
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Linter.RulesRecord} */
|
||||
const defaultExpressionRules = {
|
||||
...defaultMdRules,
|
||||
'@stylistic/quotes': [
|
||||
'error', 'double',
|
||||
],
|
||||
'@stylistic/semi': [
|
||||
'error', 'never',
|
||||
],
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
'chai-friendly/no-unused-expressions': 'off',
|
||||
'no-empty-function': 'off',
|
||||
'no-new': 'off',
|
||||
'no-unused-expressions': 'off',
|
||||
quotes: [
|
||||
'error', 'double',
|
||||
],
|
||||
semi: [
|
||||
'error', 'never',
|
||||
],
|
||||
strict: 'off',
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns {[
|
||||
* import('../iterateJsdoc.js').Integer,
|
||||
* import('../iterateJsdoc.js').Integer
|
||||
* ]}
|
||||
*/
|
||||
const getLinesCols = (text) => {
|
||||
const matchLines = countChars(text, '\n');
|
||||
|
||||
const colDelta = matchLines ?
|
||||
text.slice(text.lastIndexOf('\n') + 1).length :
|
||||
text.length;
|
||||
|
||||
return [
|
||||
matchLines, colDelta,
|
||||
];
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
globalState,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
if (semver.gte(ESLint.version, '8.0.0')) {
|
||||
report(
|
||||
'This rule does not work for ESLint 8+; you should disable this rule and use' +
|
||||
'the processor mentioned in the docs.',
|
||||
null,
|
||||
{
|
||||
column: 1,
|
||||
line: 1,
|
||||
},
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!globalState.has('checkExamples-matchingFileName')) {
|
||||
globalState.set('checkExamples-matchingFileName', new Map());
|
||||
}
|
||||
|
||||
const matchingFileNameMap = /** @type {Map<string, string>} */ (
|
||||
globalState.get('checkExamples-matchingFileName')
|
||||
);
|
||||
|
||||
const options = context.options[0] || {};
|
||||
let {
|
||||
exampleCodeRegex = null,
|
||||
rejectExampleCodeRegex = null,
|
||||
} = options;
|
||||
const {
|
||||
allowInlineConfig = true,
|
||||
baseConfig = {},
|
||||
captionRequired = false,
|
||||
checkDefaults = false,
|
||||
checkEslintrc = true,
|
||||
checkParams = false,
|
||||
checkProperties = false,
|
||||
configFile,
|
||||
matchingFileName = null,
|
||||
matchingFileNameDefaults = null,
|
||||
matchingFileNameParams = null,
|
||||
matchingFileNameProperties = null,
|
||||
noDefaultExampleRules = false,
|
||||
paddedIndent = 0,
|
||||
reportUnusedDisableDirectives = true,
|
||||
} = options;
|
||||
|
||||
// Make this configurable?
|
||||
/**
|
||||
* @type {never[]}
|
||||
*/
|
||||
const rulePaths = [];
|
||||
|
||||
const mdRules = noDefaultExampleRules ? undefined : defaultMdRules;
|
||||
|
||||
const expressionRules = noDefaultExampleRules ? undefined : defaultExpressionRules;
|
||||
|
||||
if (exampleCodeRegex) {
|
||||
exampleCodeRegex = utils.getRegexFromString(exampleCodeRegex);
|
||||
}
|
||||
|
||||
if (rejectExampleCodeRegex) {
|
||||
rejectExampleCodeRegex = utils.getRegexFromString(rejectExampleCodeRegex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* filename: string,
|
||||
* defaultFileName: string|undefined,
|
||||
* source: string,
|
||||
* targetTagName: string,
|
||||
* rules?: import('eslint').Linter.RulesRecord|undefined,
|
||||
* lines?: import('../iterateJsdoc.js').Integer,
|
||||
* cols?: import('../iterateJsdoc.js').Integer,
|
||||
* skipInit?: boolean,
|
||||
* sources?: {
|
||||
* nonJSPrefacingCols: import('../iterateJsdoc.js').Integer,
|
||||
* nonJSPrefacingLines: import('../iterateJsdoc.js').Integer,
|
||||
* string: string,
|
||||
* }[],
|
||||
* tag?: import('comment-parser').Spec & {
|
||||
* line?: import('../iterateJsdoc.js').Integer,
|
||||
* }|{
|
||||
* line: import('../iterateJsdoc.js').Integer,
|
||||
* }
|
||||
* }} cfg
|
||||
*/
|
||||
const checkSource = ({
|
||||
cols = 0,
|
||||
defaultFileName,
|
||||
filename,
|
||||
lines = 0,
|
||||
rules = expressionRules,
|
||||
skipInit,
|
||||
source,
|
||||
sources = [],
|
||||
tag = {
|
||||
line: 0,
|
||||
},
|
||||
targetTagName,
|
||||
}) => {
|
||||
if (!skipInit) {
|
||||
sources.push({
|
||||
nonJSPrefacingCols: cols,
|
||||
nonJSPrefacingLines: lines,
|
||||
string: source,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* nonJSPrefacingCols: import('../iterateJsdoc.js').Integer,
|
||||
* nonJSPrefacingLines: import('../iterateJsdoc.js').Integer,
|
||||
* string: string
|
||||
* }} cfg
|
||||
*/
|
||||
const checkRules = function ({
|
||||
nonJSPrefacingCols,
|
||||
nonJSPrefacingLines,
|
||||
string,
|
||||
}) {
|
||||
const cliConfig = {
|
||||
allowInlineConfig,
|
||||
baseConfig,
|
||||
configFile,
|
||||
reportUnusedDisableDirectives,
|
||||
rulePaths,
|
||||
rules,
|
||||
useEslintrc: checkEslintrc,
|
||||
};
|
||||
const cliConfigStr = JSON.stringify(cliConfig);
|
||||
|
||||
const src = paddedIndent ?
|
||||
string.replaceAll(new RegExp(`(^|\n) {${paddedIndent}}(?!$)`, 'gv'), '\n') :
|
||||
string;
|
||||
|
||||
// Programmatic ESLint API: https://eslint.org/docs/developer-guide/nodejs-api
|
||||
const fileNameMapKey = filename ?
|
||||
'a' + cliConfigStr + filename :
|
||||
'b' + cliConfigStr + defaultFileName;
|
||||
const file = filename || defaultFileName;
|
||||
let cliFile;
|
||||
if (matchingFileNameMap.has(fileNameMapKey)) {
|
||||
cliFile = matchingFileNameMap.get(fileNameMapKey);
|
||||
} else {
|
||||
const cli = new CLIEngine(cliConfig);
|
||||
let config;
|
||||
if (filename || checkEslintrc) {
|
||||
config = cli.getConfigForFile(file);
|
||||
}
|
||||
|
||||
// We need a new instance to ensure that the rules that may only
|
||||
// be available to `file` (if it has its own `.eslintrc`),
|
||||
// will be defined.
|
||||
cliFile = new CLIEngine({
|
||||
allowInlineConfig,
|
||||
baseConfig: {
|
||||
...baseConfig,
|
||||
...config,
|
||||
},
|
||||
configFile,
|
||||
reportUnusedDisableDirectives,
|
||||
rulePaths,
|
||||
rules,
|
||||
useEslintrc: false,
|
||||
});
|
||||
matchingFileNameMap.set(fileNameMapKey, cliFile);
|
||||
}
|
||||
|
||||
const {
|
||||
results: [
|
||||
{
|
||||
messages,
|
||||
},
|
||||
],
|
||||
} = cliFile.executeOnText(src);
|
||||
|
||||
if (!('line' in tag)) {
|
||||
tag.line = tag.source[0].number;
|
||||
}
|
||||
|
||||
// NOTE: `tag.line` can be 0 if of form `/** @tag ... */`
|
||||
const codeStartLine = /**
|
||||
* @type {import('comment-parser').Spec & {
|
||||
* line: import('../iterateJsdoc.js').Integer,
|
||||
* }}
|
||||
*/ (tag).line + nonJSPrefacingLines;
|
||||
const codeStartCol = likelyNestedJSDocIndentSpace;
|
||||
|
||||
for (const {
|
||||
column,
|
||||
line,
|
||||
message,
|
||||
ruleId,
|
||||
severity,
|
||||
} of messages) {
|
||||
const startLine = codeStartLine + line + zeroBasedLineIndexAdjust;
|
||||
const startCol = codeStartCol + (
|
||||
|
||||
// This might not work for line 0, but line 0 is unlikely for examples
|
||||
line <= 1 ? nonJSPrefacingCols + firstLinePrefixLength : preTagSpaceLength
|
||||
) + column;
|
||||
|
||||
report(
|
||||
'@' + targetTagName + ' ' + (severity === 2 ? 'error' : 'warning') +
|
||||
(ruleId ? ' (' + ruleId + ')' : '') + ': ' +
|
||||
message,
|
||||
null,
|
||||
{
|
||||
column: startCol,
|
||||
line: startLine,
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
for (const targetSource of sources) {
|
||||
checkRules(targetSource);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} filename
|
||||
* @param {string} [ext] Since `eslint-plugin-markdown` v2, and
|
||||
* ESLint 7, this is the default which other JS-fenced rules will used.
|
||||
* Formerly "md" was the default.
|
||||
* @returns {{defaultFileName: string|undefined, filename: string}}
|
||||
*/
|
||||
const getFilenameInfo = (filename, ext = 'md/*.js') => {
|
||||
let defaultFileName;
|
||||
if (!filename) {
|
||||
const jsFileName = context.getFilename();
|
||||
if (typeof jsFileName === 'string' && jsFileName.includes('.')) {
|
||||
defaultFileName = jsFileName.replace(/\.[^.]*$/v, `.${ext}`);
|
||||
} else {
|
||||
defaultFileName = `dummy.${ext}`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
defaultFileName,
|
||||
filename,
|
||||
};
|
||||
};
|
||||
|
||||
if (checkDefaults) {
|
||||
const filenameInfo = getFilenameInfo(matchingFileNameDefaults, 'jsdoc-defaults');
|
||||
utils.forEachPreferredTag('default', (tag, targetTagName) => {
|
||||
if (!tag.description.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkSource({
|
||||
source: `(${utils.getTagDescription(tag)})`,
|
||||
targetTagName,
|
||||
...filenameInfo,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (checkParams) {
|
||||
const filenameInfo = getFilenameInfo(matchingFileNameParams, 'jsdoc-params');
|
||||
utils.forEachPreferredTag('param', (tag, targetTagName) => {
|
||||
if (!tag.default || !tag.default.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkSource({
|
||||
source: `(${tag.default})`,
|
||||
targetTagName,
|
||||
...filenameInfo,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (checkProperties) {
|
||||
const filenameInfo = getFilenameInfo(matchingFileNameProperties, 'jsdoc-properties');
|
||||
utils.forEachPreferredTag('property', (tag, targetTagName) => {
|
||||
if (!tag.default || !tag.default.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkSource({
|
||||
source: `(${tag.default})`,
|
||||
targetTagName,
|
||||
...filenameInfo,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const tagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'example',
|
||||
}));
|
||||
if (!utils.hasTag(tagName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matchingFilenameInfo = getFilenameInfo(matchingFileName);
|
||||
|
||||
utils.forEachPreferredTag('example', (tag, targetTagName) => {
|
||||
let source = /** @type {string} */ (utils.getTagDescription(tag));
|
||||
const match = source.match(hasCaptionRegex);
|
||||
|
||||
if (captionRequired && (!match || !match[1].trim())) {
|
||||
report('Caption is expected for examples.', null, tag);
|
||||
}
|
||||
|
||||
source = source.replace(hasCaptionRegex, '');
|
||||
const [
|
||||
lines,
|
||||
cols,
|
||||
] = match ? getLinesCols(match[0]) : [
|
||||
0, 0,
|
||||
];
|
||||
|
||||
if (exampleCodeRegex && !exampleCodeRegex.test(source) ||
|
||||
rejectExampleCodeRegex && rejectExampleCodeRegex.test(source)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sources = [];
|
||||
let skipInit = false;
|
||||
if (exampleCodeRegex) {
|
||||
let nonJSPrefacingCols = 0;
|
||||
let nonJSPrefacingLines = 0;
|
||||
|
||||
let startingIndex = 0;
|
||||
let lastStringCount = 0;
|
||||
|
||||
let exampleCode;
|
||||
exampleCodeRegex.lastIndex = 0;
|
||||
while ((exampleCode = exampleCodeRegex.exec(source)) !== null) {
|
||||
const {
|
||||
'0': n0,
|
||||
'1': n1,
|
||||
index,
|
||||
} = exampleCode;
|
||||
|
||||
// Count anything preceding user regex match (can affect line numbering)
|
||||
const preMatch = source.slice(startingIndex, index);
|
||||
|
||||
const [
|
||||
preMatchLines,
|
||||
colDelta,
|
||||
] = getLinesCols(preMatch);
|
||||
|
||||
let nonJSPreface;
|
||||
let nonJSPrefaceLineCount;
|
||||
if (n1) {
|
||||
const idx = n0.indexOf(n1);
|
||||
nonJSPreface = n0.slice(0, idx);
|
||||
nonJSPrefaceLineCount = countChars(nonJSPreface, '\n');
|
||||
} else {
|
||||
nonJSPreface = '';
|
||||
nonJSPrefaceLineCount = 0;
|
||||
}
|
||||
|
||||
nonJSPrefacingLines += lastStringCount + preMatchLines + nonJSPrefaceLineCount;
|
||||
|
||||
// Ignore `preMatch` delta if newlines here
|
||||
if (nonJSPrefaceLineCount) {
|
||||
const charsInLastLine = nonJSPreface.slice(nonJSPreface.lastIndexOf('\n') + 1).length;
|
||||
|
||||
nonJSPrefacingCols += charsInLastLine;
|
||||
} else {
|
||||
nonJSPrefacingCols += colDelta + nonJSPreface.length;
|
||||
}
|
||||
|
||||
const string = n1 || n0;
|
||||
sources.push({
|
||||
nonJSPrefacingCols,
|
||||
nonJSPrefacingLines,
|
||||
string,
|
||||
});
|
||||
startingIndex = exampleCodeRegex.lastIndex;
|
||||
lastStringCount = countChars(string, '\n');
|
||||
if (!exampleCodeRegex.global) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
skipInit = true;
|
||||
}
|
||||
|
||||
checkSource({
|
||||
cols,
|
||||
lines,
|
||||
rules: mdRules,
|
||||
skipInit,
|
||||
source,
|
||||
sources,
|
||||
tag,
|
||||
targetTagName,
|
||||
...matchingFilenameInfo,
|
||||
});
|
||||
});
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: '@deprecated - Use `getJsdocProcessorPlugin` processor; ensures that (JavaScript) samples within `@example` tags adhere to ESLint rules.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-examples.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowInlineConfig: {
|
||||
default: true,
|
||||
type: 'boolean',
|
||||
},
|
||||
baseConfig: {
|
||||
type: 'object',
|
||||
},
|
||||
captionRequired: {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkDefaults: {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkEslintrc: {
|
||||
default: true,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkParams: {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkProperties: {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
configFile: {
|
||||
type: 'string',
|
||||
},
|
||||
exampleCodeRegex: {
|
||||
type: 'string',
|
||||
},
|
||||
matchingFileName: {
|
||||
type: 'string',
|
||||
},
|
||||
matchingFileNameDefaults: {
|
||||
type: 'string',
|
||||
},
|
||||
matchingFileNameParams: {
|
||||
type: 'string',
|
||||
},
|
||||
matchingFileNameProperties: {
|
||||
type: 'string',
|
||||
},
|
||||
noDefaultExampleRules: {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
paddedIndent: {
|
||||
default: 0,
|
||||
type: 'integer',
|
||||
},
|
||||
rejectExampleCodeRegex: {
|
||||
type: 'string',
|
||||
},
|
||||
reportUnusedDisableDirectives: {
|
||||
default: true,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
176
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkIndentation.js
generated
vendored
Normal file
176
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkIndentation.js
generated
vendored
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @param {string[]} excludeTags
|
||||
* @returns {string}
|
||||
*/
|
||||
const maskExcludedContent = (str, excludeTags) => {
|
||||
const regContent = new RegExp(`([ \\t]+\\*)[ \\t]@(?:${excludeTags.join('|')})(?=[ \\n])([\\w\\|\\W]*?\\n)(?=[ \\t]*\\*(?:[ \\t]*@\\w+\\s|\\/))`, 'gv');
|
||||
|
||||
return str.replace(regContent, (_match, margin, code) => {
|
||||
return (margin + '\n').repeat(code.match(/\n/gv).length);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
const maskCodeBlocks = (str) => {
|
||||
const regContent = /([ \t]+\*)[ \t]```[^\n]*?([\w\|\W]*?\n)(?=[ \t]*\*(?:[ \t]*(?:```|@\w+\s)|\/))/gv;
|
||||
|
||||
return str.replaceAll(regContent, (_match, margin, code) => {
|
||||
return (margin + '\n').repeat(code.match(/\n/gv).length);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string[]} lines
|
||||
* @param {number} lineIndex
|
||||
* @returns {number}
|
||||
*/
|
||||
const getLineNumber = (lines, lineIndex) => {
|
||||
const precedingText = lines.slice(0, lineIndex).join('\n');
|
||||
const lineBreaks = precedingText.match(/\n/gv) || [];
|
||||
return lineBreaks.length + 1;
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdocNode,
|
||||
report,
|
||||
sourceCode,
|
||||
}) => {
|
||||
const options = context.options[0] || {};
|
||||
const /** @type {{excludeTags: string[], allowIndentedSections: boolean}} */ {
|
||||
allowIndentedSections = false,
|
||||
excludeTags = [
|
||||
'example',
|
||||
],
|
||||
} = options;
|
||||
|
||||
const textWithoutCodeBlocks = maskCodeBlocks(sourceCode.getText(jsdocNode));
|
||||
const text = excludeTags.length ? maskExcludedContent(textWithoutCodeBlocks, excludeTags) : textWithoutCodeBlocks;
|
||||
|
||||
if (allowIndentedSections) {
|
||||
// When allowIndentedSections is enabled, only check for indentation on tag lines
|
||||
// and the very first line of the main description
|
||||
const lines = text.split('\n');
|
||||
let hasSeenContent = false;
|
||||
let currentSectionIndent = null;
|
||||
|
||||
for (const [
|
||||
lineIndex,
|
||||
line,
|
||||
] of lines.entries()) {
|
||||
// Check for indentation (two or more spaces after *)
|
||||
const indentMatch = line.match(/^(?:\/?\**|[\t ]*)\*([\t ]{2,})/v);
|
||||
|
||||
if (indentMatch) {
|
||||
// Check what comes after the indentation
|
||||
const afterIndent = line.slice(indentMatch[0].length);
|
||||
const indentAmount = indentMatch[1].length;
|
||||
|
||||
// If this is a tag line with indentation, always report
|
||||
if (/^@\w+/v.test(afterIndent)) {
|
||||
report('There must be no indentation.', null, {
|
||||
line: getLineNumber(lines, lineIndex),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If we haven't seen any content yet (main description first line) and there's content, report
|
||||
if (!hasSeenContent && afterIndent.trim().length > 0) {
|
||||
report('There must be no indentation.', null, {
|
||||
line: getLineNumber(lines, lineIndex),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// For continuation lines, check consistency
|
||||
if (hasSeenContent && afterIndent.trim().length > 0) {
|
||||
if (currentSectionIndent === null) {
|
||||
// First indented line in this section, set the indent level
|
||||
currentSectionIndent = indentAmount;
|
||||
} else if (indentAmount < currentSectionIndent) {
|
||||
// Indentation is less than the established level (inconsistent)
|
||||
report('There must be no indentation.', null, {
|
||||
line: getLineNumber(lines, lineIndex),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (/^\s*\*\s+\S/v.test(line)) {
|
||||
// No indentation on this line, reset section indent tracking
|
||||
// (unless it's just whitespace or a closing comment)
|
||||
currentSectionIndent = null;
|
||||
}
|
||||
|
||||
// Track if we've seen any content (non-whitespace after the *)
|
||||
if (/^\s*\*\s+\S/v.test(line)) {
|
||||
hasSeenContent = true;
|
||||
}
|
||||
|
||||
// Reset section indent when we encounter a tag
|
||||
if (/@\w+/v.test(line)) {
|
||||
currentSectionIndent = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const reg = /^(?:\/?\**|[ \t]*)\*[ \t]{2}/gmv;
|
||||
if (reg.test(text)) {
|
||||
const lineBreaks = text.slice(0, reg.lastIndex).match(/\n/gv) || [];
|
||||
report('There must be no indentation.', null, {
|
||||
line: lineBreaks.length,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Reports invalid padding inside JSDoc blocks.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-indentation.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowIndentedSections: {
|
||||
description: 'Allows indentation of nested sections on subsequent lines (like bullet lists)',
|
||||
type: 'boolean',
|
||||
},
|
||||
excludeTags: {
|
||||
description: `Array of tags (e.g., \`['example', 'description']\`) whose content will be
|
||||
"hidden" from the \`check-indentation\` rule. Defaults to \`['example']\`.
|
||||
|
||||
By default, the whole JSDoc block will be checked for invalid padding.
|
||||
That would include \`@example\` blocks too, which can get in the way
|
||||
of adding full, readable examples of code without ending up with multiple
|
||||
linting issues.
|
||||
|
||||
When disabled (by passing \`excludeTags: []\` option), the following code *will*
|
||||
report a padding issue:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @example
|
||||
* anArray.filter((a) => {
|
||||
* return a.b;
|
||||
* });
|
||||
*/
|
||||
\`\`\``,
|
||||
items: {
|
||||
pattern: '^\\S+$',
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'layout',
|
||||
},
|
||||
});
|
||||
453
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkLineAlignment.js
generated
vendored
Normal file
453
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkLineAlignment.js
generated
vendored
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
import alignTransform from '../alignTransform.js';
|
||||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
transforms,
|
||||
} from 'comment-parser';
|
||||
|
||||
const {
|
||||
flow: commentFlow,
|
||||
} = transforms;
|
||||
|
||||
/**
|
||||
* Detects if a line starts with a markdown list marker
|
||||
* Supports: -, *, numbered lists (1., 2., etc.)
|
||||
* This explicitly excludes hyphens that are part of JSDoc tag syntax
|
||||
* @param {string} text - The text to check
|
||||
* @param {boolean} isFirstLineOfTag - True if this is the first line (tag line)
|
||||
* @returns {boolean} - True if the text starts with a list marker
|
||||
*/
|
||||
const startsWithListMarker = (text, isFirstLineOfTag = false) => {
|
||||
// On the first line of a tag, the hyphen is typically the JSDoc separator,
|
||||
// not a list marker
|
||||
if (isFirstLineOfTag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match lines that start with optional whitespace, then a list marker
|
||||
// - or * followed by a space
|
||||
// or a number followed by . or ) and a space
|
||||
return /^\s*(?:[\-*]|\d+(?:\.|\)))\s+/v.test(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if we should allow extra indentation beyond wrapIndent.
|
||||
* This is true for list continuation lines (lines with more indent than wrapIndent
|
||||
* that follow a list item).
|
||||
* @param {import('comment-parser').Spec} tag - The tag being checked
|
||||
* @param {import('../iterateJsdoc.js').Integer} idx - Current line index (0-based in tag.source.slice(1))
|
||||
* @returns {boolean} - True if extra indentation should be allowed
|
||||
*/
|
||||
const shouldAllowExtraIndent = (tag, idx) => {
|
||||
// Check if any previous line in this tag had a list marker
|
||||
// idx is 0-based in the continuation lines (tag.source.slice(1))
|
||||
// So tag.source[0] is the tag line, tag.source[idx+1] is current line
|
||||
let hasSeenListMarker = false;
|
||||
|
||||
// Check all lines from the tag line onwards
|
||||
for (let lineIdx = 0; lineIdx <= idx + 1; lineIdx++) {
|
||||
const line = tag.source[lineIdx];
|
||||
const isFirstLine = lineIdx === 0;
|
||||
if (line?.tokens?.description && startsWithListMarker(line.tokens.description, isFirstLine)) {
|
||||
hasSeenListMarker = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hasSeenListMarker;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* postDelimiter: import('../iterateJsdoc.js').Integer,
|
||||
* postHyphen: import('../iterateJsdoc.js').Integer,
|
||||
* postName: import('../iterateJsdoc.js').Integer,
|
||||
* postTag: import('../iterateJsdoc.js').Integer,
|
||||
* postType: import('../iterateJsdoc.js').Integer,
|
||||
* }} CustomSpacings
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* @param {import('comment-parser').Spec & {
|
||||
* line: import('../iterateJsdoc.js').Integer
|
||||
* }} tag
|
||||
* @param {CustomSpacings} customSpacings
|
||||
*/
|
||||
const checkNotAlignedPerTag = (utils, tag, customSpacings) => {
|
||||
/*
|
||||
start +
|
||||
delimiter +
|
||||
postDelimiter +
|
||||
tag +
|
||||
postTag +
|
||||
type +
|
||||
postType +
|
||||
name +
|
||||
postName +
|
||||
description +
|
||||
end +
|
||||
lineEnd
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {"tag"|"type"|"name"|"description"} ContentProp
|
||||
*/
|
||||
|
||||
/** @type {("postDelimiter"|"postTag"|"postType"|"postName")[]} */
|
||||
let spacerProps;
|
||||
/** @type {ContentProp[]} */
|
||||
let contentProps;
|
||||
const mightHaveNamepath = utils.tagMightHaveNameOrNamepath(tag.tag);
|
||||
if (mightHaveNamepath) {
|
||||
spacerProps = [
|
||||
'postDelimiter', 'postTag', 'postType', 'postName',
|
||||
];
|
||||
contentProps = [
|
||||
'tag', 'type', 'name', 'description',
|
||||
];
|
||||
} else {
|
||||
spacerProps = [
|
||||
'postDelimiter', 'postTag', 'postType',
|
||||
];
|
||||
contentProps = [
|
||||
'tag', 'type', 'description',
|
||||
];
|
||||
}
|
||||
|
||||
const {
|
||||
tokens,
|
||||
} = tag.source[0];
|
||||
|
||||
/**
|
||||
* @param {import('../iterateJsdoc.js').Integer} idx
|
||||
* @param {(notRet: boolean, contentProp: ContentProp) => void} [callbck]
|
||||
*/
|
||||
const followedBySpace = (idx, callbck) => {
|
||||
const nextIndex = idx + 1;
|
||||
|
||||
return spacerProps.slice(nextIndex).some((spacerProp, innerIdx) => {
|
||||
const contentProp = contentProps[nextIndex + innerIdx];
|
||||
|
||||
const spacePropVal = tokens[spacerProp];
|
||||
|
||||
const ret = spacePropVal;
|
||||
|
||||
if (callbck) {
|
||||
callbck(!ret, contentProp);
|
||||
}
|
||||
|
||||
return ret && (callbck || !contentProp);
|
||||
});
|
||||
};
|
||||
|
||||
const postHyphenSpacing = customSpacings?.postHyphen ?? 1;
|
||||
const exactHyphenSpacing = new RegExp(`^\\s*-\\s{${postHyphenSpacing},${postHyphenSpacing}}(?!\\s)`, 'v');
|
||||
const hasNoHyphen = !(/^\s*-(?!$)(?=\s)/v).test(tokens.description);
|
||||
const hasExactHyphenSpacing = exactHyphenSpacing.test(
|
||||
tokens.description,
|
||||
);
|
||||
|
||||
// If checking alignment on multiple lines, need to check other `source`
|
||||
// items
|
||||
// Go through `post*` spacing properties and exit to indicate problem if
|
||||
// extra spacing detected
|
||||
const ok = !spacerProps.some((spacerProp, idx) => {
|
||||
const contentProp = contentProps[idx];
|
||||
const contentPropVal = tokens[contentProp];
|
||||
const spacerPropVal = tokens[spacerProp];
|
||||
const spacing = customSpacings?.[spacerProp] || 1;
|
||||
|
||||
// There will be extra alignment if...
|
||||
|
||||
// 1. The spaces don't match the space it should have (1 or custom spacing) OR
|
||||
return spacerPropVal.length !== spacing && spacerPropVal.length !== 0 ||
|
||||
|
||||
// 2. There is a (single) space, no immediate content, and yet another
|
||||
// space is found subsequently (not separated by intervening content)
|
||||
spacerPropVal && !contentPropVal && followedBySpace(idx);
|
||||
}) && (hasNoHyphen || hasExactHyphenSpacing);
|
||||
if (ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fix = () => {
|
||||
for (const [
|
||||
idx,
|
||||
spacerProp,
|
||||
] of spacerProps.entries()) {
|
||||
const contentProp = contentProps[idx];
|
||||
const contentPropVal = tokens[contentProp];
|
||||
|
||||
if (contentPropVal) {
|
||||
const spacing = customSpacings?.[spacerProp] || 1;
|
||||
tokens[spacerProp] = ''.padStart(spacing, ' ');
|
||||
followedBySpace(idx, (hasSpace, contentPrp) => {
|
||||
if (hasSpace) {
|
||||
tokens[contentPrp] = '';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
tokens[spacerProp] = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasExactHyphenSpacing) {
|
||||
const hyphenSpacing = /^\s*-\s+/v;
|
||||
tokens.description = tokens.description.replace(
|
||||
hyphenSpacing, '-' + ''.padStart(postHyphenSpacing, ' '),
|
||||
);
|
||||
}
|
||||
|
||||
utils.setTag(tag, tokens);
|
||||
};
|
||||
|
||||
utils.reportJSDoc('Expected JSDoc block lines to not be aligned.', tag, fix, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {object} cfg
|
||||
* @param {CustomSpacings} cfg.customSpacings
|
||||
* @param {string} cfg.indent
|
||||
* @param {import('comment-parser').Block} cfg.jsdoc
|
||||
* @param {import('eslint').Rule.Node & {
|
||||
* range: [number, number]
|
||||
* }} cfg.jsdocNode
|
||||
* @param {boolean} cfg.preserveMainDescriptionPostDelimiter
|
||||
* @param {import('../iterateJsdoc.js').Report} cfg.report
|
||||
* @param {string[]} cfg.tags
|
||||
* @param {import('../iterateJsdoc.js').Utils} cfg.utils
|
||||
* @param {string} cfg.wrapIndent
|
||||
* @param {boolean} cfg.disableWrapIndent
|
||||
* @returns {void}
|
||||
*/
|
||||
const checkAlignment = ({
|
||||
customSpacings,
|
||||
disableWrapIndent,
|
||||
indent,
|
||||
jsdoc,
|
||||
jsdocNode,
|
||||
preserveMainDescriptionPostDelimiter,
|
||||
report,
|
||||
tags,
|
||||
utils,
|
||||
wrapIndent,
|
||||
}) => {
|
||||
const transform = commentFlow(
|
||||
alignTransform({
|
||||
customSpacings,
|
||||
disableWrapIndent,
|
||||
indent,
|
||||
preserveMainDescriptionPostDelimiter,
|
||||
tags,
|
||||
wrapIndent,
|
||||
}),
|
||||
);
|
||||
const transformedJsdoc = transform(jsdoc);
|
||||
|
||||
const comment = '/*' +
|
||||
/**
|
||||
* @type {import('eslint').Rule.Node & {
|
||||
* range: [number, number], value: string
|
||||
* }}
|
||||
*/ (jsdocNode).value + '*/';
|
||||
|
||||
const formatted = utils.stringify(transformedJsdoc)
|
||||
.trimStart();
|
||||
|
||||
if (comment !== formatted) {
|
||||
report(
|
||||
'Expected JSDoc block lines to be aligned.',
|
||||
/** @type {import('eslint').Rule.ReportFixer} */ (fixer) => {
|
||||
return fixer.replaceText(jsdocNode, formatted);
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
indent,
|
||||
jsdoc,
|
||||
jsdocNode,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
customSpacings,
|
||||
disableWrapIndent = false,
|
||||
preserveMainDescriptionPostDelimiter,
|
||||
tags: applicableTags = [
|
||||
'param', 'arg', 'argument', 'property', 'prop', 'returns', 'return', 'template',
|
||||
],
|
||||
wrapIndent = '',
|
||||
} = context.options[1] || {};
|
||||
|
||||
if (context.options[0] === 'always') {
|
||||
// Skip if it contains only a single line.
|
||||
if (!(
|
||||
/**
|
||||
* @type {import('eslint').Rule.Node & {
|
||||
* range: [number, number], value: string
|
||||
* }}
|
||||
*/
|
||||
(jsdocNode).value.includes('\n')
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkAlignment({
|
||||
customSpacings,
|
||||
disableWrapIndent,
|
||||
indent,
|
||||
jsdoc,
|
||||
jsdocNode,
|
||||
preserveMainDescriptionPostDelimiter,
|
||||
report,
|
||||
tags: applicableTags,
|
||||
utils,
|
||||
wrapIndent,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const foundTags = utils.getPresentTags(applicableTags);
|
||||
if (context.options[0] !== 'any') {
|
||||
for (const tag of foundTags) {
|
||||
checkNotAlignedPerTag(
|
||||
utils,
|
||||
/**
|
||||
* @type {import('comment-parser').Spec & {
|
||||
* line: import('../iterateJsdoc.js').Integer
|
||||
* }}
|
||||
*/
|
||||
(tag),
|
||||
customSpacings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const tag of foundTags) {
|
||||
if (tag.source.length > 1) {
|
||||
let idx = 0;
|
||||
for (const {
|
||||
tokens,
|
||||
// Avoid the tag line
|
||||
} of tag.source.slice(1)) {
|
||||
idx++;
|
||||
|
||||
if (
|
||||
!tokens.description ||
|
||||
// Avoid first lines after multiline type
|
||||
tokens.type ||
|
||||
tokens.name
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't include a single separating space/tab
|
||||
const actualIndent = tokens.postDelimiter.slice(1);
|
||||
const hasCorrectWrapIndent = actualIndent === wrapIndent;
|
||||
|
||||
// Allow extra indentation if this line or previous lines contain list markers
|
||||
// This preserves nested list structure
|
||||
const hasExtraIndent = actualIndent.length > wrapIndent.length &&
|
||||
actualIndent.startsWith(wrapIndent);
|
||||
const isInListContext = shouldAllowExtraIndent(tag, idx - 1);
|
||||
|
||||
if (!disableWrapIndent && !hasCorrectWrapIndent &&
|
||||
!(hasExtraIndent && isInListContext)) {
|
||||
utils.reportJSDoc('Expected wrap indent', {
|
||||
line: tag.source[0].number + idx,
|
||||
}, () => {
|
||||
tokens.postDelimiter = tokens.postDelimiter.charAt(0) + wrapIndent;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Reports invalid alignment of JSDoc block lines.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-line-alignment.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'whitespace',
|
||||
schema: [
|
||||
{
|
||||
description: `If the string value is
|
||||
\`"always"\` then a problem is raised when the lines are not aligned.
|
||||
If it is \`"never"\` then a problem should be raised when there is more than
|
||||
one space between each line's parts. If it is \`"any"\`, no alignment is made.
|
||||
Defaults to \`"never"\`.
|
||||
|
||||
Note that in addition to alignment, the "never" and "always" options will both
|
||||
ensure that at least one space is present after the asterisk delimiter.`,
|
||||
enum: [
|
||||
'always', 'never', 'any',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
customSpacings: {
|
||||
additionalProperties: false,
|
||||
description: `An object with any of the following spacing keys set to an integer.
|
||||
If a spacing is not defined, it defaults to one.`,
|
||||
properties: {
|
||||
postDelimiter: {
|
||||
description: 'Affects spacing after the asterisk (e.g., `* @param`)',
|
||||
type: 'integer',
|
||||
},
|
||||
postHyphen: {
|
||||
description: 'Affects spacing after any hyphens in the description (e.g., `* @param {someType} name - A description`)',
|
||||
type: 'integer',
|
||||
},
|
||||
postName: {
|
||||
description: 'Affects spacing after the name (e.g., `* @param {someType} name `)',
|
||||
type: 'integer',
|
||||
},
|
||||
postTag: {
|
||||
description: 'Affects spacing after the tag (e.g., `* @param `)',
|
||||
type: 'integer',
|
||||
},
|
||||
postType: {
|
||||
description: 'Affects spacing after the type (e.g., `* @param {someType} `)',
|
||||
type: 'integer',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
disableWrapIndent: {
|
||||
description: 'Disables `wrapIndent`; existing wrap indentation is preserved without changes.',
|
||||
type: 'boolean',
|
||||
},
|
||||
preserveMainDescriptionPostDelimiter: {
|
||||
default: false,
|
||||
description: `A boolean to determine whether to preserve the post-delimiter spacing of the
|
||||
main description. If \`false\` or unset, will be set to a single space.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
tags: {
|
||||
description: `Use this to change the tags which are sought for alignment changes. Defaults to an array of
|
||||
\`['param', 'arg', 'argument', 'property', 'prop', 'returns', 'return', 'template']\`.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
wrapIndent: {
|
||||
description: `The indent that will be applied for tag text after the first line.
|
||||
Default to the empty string (no indent).`,
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'layout',
|
||||
},
|
||||
});
|
||||
535
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkParamNames.js
generated
vendored
Normal file
535
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkParamNames.js
generated
vendored
Normal file
|
|
@ -0,0 +1,535 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @param {string} targetTagName
|
||||
* @param {boolean} allowExtraTrailingParamDocs
|
||||
* @param {boolean} checkDestructured
|
||||
* @param {boolean} checkRestProperty
|
||||
* @param {RegExp} checkTypesRegex
|
||||
* @param {boolean} disableExtraPropertyReporting
|
||||
* @param {boolean} disableMissingParamChecks
|
||||
* @param {boolean} enableFixer
|
||||
* @param {import('../jsdocUtils.js').ParamNameInfo[]} functionParameterNames
|
||||
* @param {import('comment-parser').Block} jsdoc
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* @param {import('../iterateJsdoc.js').Report} report
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const validateParameterNames = (
|
||||
targetTagName,
|
||||
allowExtraTrailingParamDocs,
|
||||
checkDestructured,
|
||||
checkRestProperty,
|
||||
checkTypesRegex,
|
||||
disableExtraPropertyReporting,
|
||||
disableMissingParamChecks,
|
||||
enableFixer,
|
||||
functionParameterNames, jsdoc, utils, report,
|
||||
) => {
|
||||
const paramTags = Object.entries(jsdoc.tags).filter(([
|
||||
, tag,
|
||||
]) => {
|
||||
return tag.tag === targetTagName;
|
||||
});
|
||||
const paramTagsNonNested = paramTags.filter(([
|
||||
, tag,
|
||||
]) => {
|
||||
return !tag.name.includes('.');
|
||||
});
|
||||
|
||||
let dotted = 0;
|
||||
let thisOffset = 0;
|
||||
|
||||
return paramTags.some(([
|
||||
, tag,
|
||||
// eslint-disable-next-line complexity
|
||||
], index) => {
|
||||
/** @type {import('../iterateJsdoc.js').Integer} */
|
||||
let tagsIndex;
|
||||
const dupeTagInfo = paramTags.find(([
|
||||
tgsIndex,
|
||||
tg,
|
||||
], idx) => {
|
||||
tagsIndex = Number(tgsIndex);
|
||||
|
||||
return tg.name === tag.name && idx !== index;
|
||||
});
|
||||
if (dupeTagInfo) {
|
||||
utils.reportJSDoc(`Duplicate @${targetTagName} "${tag.name}"`, dupeTagInfo[1], enableFixer ? () => {
|
||||
utils.removeTag(tagsIndex);
|
||||
} : null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tag.name.includes('.')) {
|
||||
dotted++;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let functionParameterName = functionParameterNames[index - dotted + thisOffset];
|
||||
if (functionParameterName === 'this' && tag.name.trim() !== 'this') {
|
||||
++thisOffset;
|
||||
functionParameterName = functionParameterNames[index - dotted + thisOffset];
|
||||
}
|
||||
|
||||
if (!functionParameterName) {
|
||||
if (allowExtraTrailingParamDocs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
report(
|
||||
`@${targetTagName} "${tag.name}" does not match an existing function parameter.`,
|
||||
null,
|
||||
tag,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof functionParameterName === 'object' &&
|
||||
'name' in functionParameterName &&
|
||||
Array.isArray(functionParameterName.name)
|
||||
) {
|
||||
const actualName = tag.name.trim();
|
||||
const expectedName = functionParameterName.name[index];
|
||||
if (actualName === expectedName) {
|
||||
thisOffset--;
|
||||
return false;
|
||||
}
|
||||
|
||||
report(
|
||||
`Expected @${targetTagName} name to be "${expectedName}". Got "${actualName}".`,
|
||||
null,
|
||||
tag,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(functionParameterName)) {
|
||||
if (!checkDestructured) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tag.type && tag.type.search(checkTypesRegex) === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const [
|
||||
parameterName,
|
||||
{
|
||||
annotationParamName,
|
||||
hasPropertyRest,
|
||||
names: properties,
|
||||
rests,
|
||||
},
|
||||
] =
|
||||
/**
|
||||
* @type {[string | undefined, import('../jsdocUtils.js').FlattendRootInfo & {
|
||||
* annotationParamName?: string | undefined;
|
||||
}]} */ (functionParameterName);
|
||||
if (annotationParamName !== undefined) {
|
||||
const name = tag.name.trim();
|
||||
if (name !== annotationParamName) {
|
||||
report(`@${targetTagName} "${name}" does not match parameter name "${annotationParamName}"`, null, tag);
|
||||
}
|
||||
}
|
||||
|
||||
const tagName = parameterName === undefined ? tag.name.trim() : parameterName;
|
||||
const expectedNames = properties.map((name) => {
|
||||
return `${tagName}.${name}`;
|
||||
});
|
||||
const actualNames = paramTags.map(([
|
||||
, paramTag,
|
||||
]) => {
|
||||
return paramTag.name.trim();
|
||||
});
|
||||
const actualTypes = paramTags.map(([
|
||||
, paramTag,
|
||||
]) => {
|
||||
return paramTag.type;
|
||||
});
|
||||
|
||||
const missingProperties = [];
|
||||
|
||||
/** @type {string[]} */
|
||||
const notCheckingNames = [];
|
||||
|
||||
for (const [
|
||||
idx,
|
||||
name,
|
||||
] of expectedNames.entries()) {
|
||||
if (notCheckingNames.some((notCheckingName) => {
|
||||
return name.startsWith(notCheckingName);
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const actualNameIdx = actualNames.findIndex((actualName) => {
|
||||
return utils.comparePaths(name)(actualName);
|
||||
});
|
||||
if (actualNameIdx === -1) {
|
||||
if (!checkRestProperty && rests[idx]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const missingIndex = actualNames.findIndex((actualName) => {
|
||||
return utils.pathDoesNotBeginWith(name, actualName);
|
||||
});
|
||||
const line = tag.source[0].number - 1 + (missingIndex > -1 ? missingIndex : actualNames.length);
|
||||
missingProperties.push({
|
||||
name,
|
||||
tagPlacement: {
|
||||
line: line === 0 ? 1 : line,
|
||||
},
|
||||
});
|
||||
} else if (actualTypes[actualNameIdx].search(checkTypesRegex) === -1 && actualTypes[actualNameIdx] !== '') {
|
||||
notCheckingNames.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
const hasMissing = missingProperties.length;
|
||||
if (hasMissing) {
|
||||
for (const {
|
||||
name: missingProperty,
|
||||
tagPlacement,
|
||||
} of missingProperties) {
|
||||
report(`Missing @${targetTagName} "${missingProperty}"`, null, tagPlacement);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasPropertyRest || checkRestProperty) {
|
||||
/** @type {[string, import('comment-parser').Spec][]} */
|
||||
const extraProperties = [];
|
||||
for (const [
|
||||
idx,
|
||||
name,
|
||||
] of actualNames.entries()) {
|
||||
const match = name.startsWith(tag.name.trim() + '.');
|
||||
if (
|
||||
match && !expectedNames.some(
|
||||
utils.comparePaths(name),
|
||||
) && !utils.comparePaths(name)(tag.name) &&
|
||||
(!disableExtraPropertyReporting || properties.some((prop) => {
|
||||
return prop.split('.').length >= name.split('.').length - 1;
|
||||
}))
|
||||
) {
|
||||
extraProperties.push([
|
||||
name, paramTags[idx][1],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if (extraProperties.length) {
|
||||
for (const [
|
||||
extraProperty,
|
||||
tg,
|
||||
] of extraProperties) {
|
||||
report(`@${targetTagName} "${extraProperty}" does not exist on ${tag.name}`, null, tg);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return hasMissing;
|
||||
}
|
||||
|
||||
let funcParamName;
|
||||
if (typeof functionParameterName === 'object') {
|
||||
const {
|
||||
name,
|
||||
} = functionParameterName;
|
||||
funcParamName = name;
|
||||
} else {
|
||||
funcParamName = functionParameterName;
|
||||
}
|
||||
|
||||
if (funcParamName !== tag.name.trim()) {
|
||||
// Todo: Improve for array or object child items
|
||||
const actualNames = paramTagsNonNested.map(([
|
||||
, {
|
||||
name,
|
||||
},
|
||||
]) => {
|
||||
return name.trim();
|
||||
});
|
||||
|
||||
const expectedNames = functionParameterNames.map((item, idx) => {
|
||||
if (/**
|
||||
* @type {[string|undefined, (import('../jsdocUtils.js').FlattendRootInfo & {
|
||||
* annotationParamName?: string,
|
||||
})]} */ (item)?.[1]?.names) {
|
||||
return actualNames[idx];
|
||||
}
|
||||
|
||||
return item;
|
||||
}).filter((item) => {
|
||||
return item !== 'this';
|
||||
});
|
||||
|
||||
// When disableMissingParamChecks is true tag names can be omitted.
|
||||
// Report when the tag names do not match the expected names or they are used out of order.
|
||||
if (disableMissingParamChecks) {
|
||||
const usedExpectedNames = expectedNames.map((a) => {
|
||||
return a?.toString();
|
||||
}).filter((expectedName) => {
|
||||
return expectedName && actualNames.includes(expectedName);
|
||||
});
|
||||
const usedInOrder = actualNames.every((actualName, idx) => {
|
||||
return actualName === usedExpectedNames[idx];
|
||||
});
|
||||
if (usedInOrder) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
report(
|
||||
`Expected @${targetTagName} names to be "${
|
||||
expectedNames.map((expectedName) => {
|
||||
return typeof expectedName === 'object' &&
|
||||
'name' in expectedName &&
|
||||
expectedName.restElement ?
|
||||
'...' + expectedName.name :
|
||||
expectedName;
|
||||
}).join(', ')
|
||||
}". Got "${actualNames.join(', ')}".`,
|
||||
null,
|
||||
tag,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} targetTagName
|
||||
* @param {boolean} _allowExtraTrailingParamDocs
|
||||
* @param {{
|
||||
* name: string,
|
||||
* idx: import('../iterateJsdoc.js').Integer
|
||||
* }[]} jsdocParameterNames
|
||||
* @param {import('comment-parser').Block} jsdoc
|
||||
* @param {import('../iterateJsdoc.js').Report} report
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const validateParameterNamesDeep = (
|
||||
targetTagName, _allowExtraTrailingParamDocs,
|
||||
jsdocParameterNames, jsdoc, report,
|
||||
) => {
|
||||
/** @type {string} */
|
||||
let lastRealParameter;
|
||||
|
||||
return jsdocParameterNames.some(({
|
||||
idx,
|
||||
name: jsdocParameterName,
|
||||
}) => {
|
||||
const isPropertyPath = jsdocParameterName.includes('.');
|
||||
|
||||
if (isPropertyPath) {
|
||||
if (!lastRealParameter) {
|
||||
report(`@${targetTagName} path declaration ("${jsdocParameterName}") appears before any real parameter.`, null, jsdoc.tags[idx]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
let pathRootNodeName = jsdocParameterName.slice(0, jsdocParameterName.indexOf('.'));
|
||||
|
||||
if (pathRootNodeName.endsWith('[]')) {
|
||||
pathRootNodeName = pathRootNodeName.slice(0, -2);
|
||||
}
|
||||
|
||||
if (pathRootNodeName !== lastRealParameter) {
|
||||
report(
|
||||
`@${targetTagName} path declaration ("${jsdocParameterName}") root node name ("${pathRootNodeName}") ` +
|
||||
`does not match previous real parameter name ("${lastRealParameter}").`,
|
||||
null,
|
||||
jsdoc.tags[idx],
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
lastRealParameter = jsdocParameterName;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
const allowedNodes = [
|
||||
'ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression', 'TSDeclareFunction',
|
||||
// Add this to above defaults
|
||||
'TSMethodSignature',
|
||||
];
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
node,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
allowExtraTrailingParamDocs,
|
||||
checkDestructured = true,
|
||||
checkRestProperty = false,
|
||||
checkTypesPattern = '/^(?:[oO]bject|[aA]rray|PlainObject|Generic(?:Object|Array))$/',
|
||||
disableExtraPropertyReporting = false,
|
||||
disableMissingParamChecks = false,
|
||||
enableFixer = false,
|
||||
useDefaultObjectProperties = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
// Although we might just remove global settings contexts from applying to
|
||||
// this rule (as they can cause problems with `getFunctionParameterNames`
|
||||
// checks if they are not functions but say variables), the user may
|
||||
// instead wish to narrow contexts in those settings, so this check
|
||||
// is still useful
|
||||
if (!allowedNodes.includes(/** @type {import('estree').Node} */ (node).type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const checkTypesRegex = utils.getRegexFromString(checkTypesPattern);
|
||||
|
||||
const jsdocParameterNamesDeep = utils.getJsdocTagsDeep('param');
|
||||
if (!jsdocParameterNamesDeep || !jsdocParameterNamesDeep.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const functionParameterNames = utils.getFunctionParameterNames(useDefaultObjectProperties);
|
||||
|
||||
const targetTagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'param',
|
||||
}));
|
||||
const isError = validateParameterNames(
|
||||
targetTagName,
|
||||
allowExtraTrailingParamDocs,
|
||||
checkDestructured,
|
||||
checkRestProperty,
|
||||
checkTypesRegex,
|
||||
disableExtraPropertyReporting,
|
||||
disableMissingParamChecks,
|
||||
enableFixer,
|
||||
functionParameterNames,
|
||||
jsdoc,
|
||||
utils,
|
||||
report,
|
||||
);
|
||||
|
||||
if (isError || !checkDestructured) {
|
||||
return;
|
||||
}
|
||||
|
||||
validateParameterNamesDeep(
|
||||
targetTagName, allowExtraTrailingParamDocs, jsdocParameterNamesDeep, jsdoc, report,
|
||||
);
|
||||
}, {
|
||||
contextDefaults: allowedNodes,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Checks for dupe `@param` names, that nested param names have roots, and that parameter names in function declarations match JSDoc param names.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-param-names.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowExtraTrailingParamDocs: {
|
||||
description: `If set to \`true\`, this option will allow extra \`@param\` definitions (e.g.,
|
||||
representing future expected or virtual params) to be present without needing
|
||||
their presence within the function signature. Other inconsistencies between
|
||||
\`@param\`'s and present function parameters will still be reported.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkDestructured: {
|
||||
description: 'Whether to check destructured properties. Defaults to `true`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
checkRestProperty: {
|
||||
description: `If set to \`true\`, will require that rest properties are documented and
|
||||
that any extraneous properties (which may have been within the rest property)
|
||||
are documented. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkTypesPattern: {
|
||||
description: `Defines a regular expression pattern to indicate which types should be
|
||||
checked for destructured content (and that those not matched should not
|
||||
be checked).
|
||||
|
||||
When one specifies a type, unless it is of a generic type, like \`object\`
|
||||
or \`array\`, it may be considered unnecessary to have that object's
|
||||
destructured components required, especially where generated docs will
|
||||
link back to the specified type. For example:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @param {SVGRect} bbox - a SVGRect
|
||||
*/
|
||||
export const bboxToObj = function ({x, y, width, height}) {
|
||||
return {x, y, width, height};
|
||||
};
|
||||
\`\`\`
|
||||
|
||||
By default \`checkTypesPattern\` is set to
|
||||
\`/^(?:[oO]bject|[aA]rray|PlainObject|Generic(?:Object|Array))$/v\`,
|
||||
meaning that destructuring will be required only if the type of the \`@param\`
|
||||
(the text between curly brackets) is a match for "Object" or "Array" (with or
|
||||
without initial caps), "PlainObject", or "GenericObject", "GenericArray" (or
|
||||
if no type is present). So in the above example, the lack of a match will
|
||||
mean that no complaint will be given about the undocumented destructured
|
||||
parameters.
|
||||
|
||||
Note that the \`/\` delimiters are optional, but necessary to add flags.
|
||||
|
||||
Defaults to using (only) the \`v\` flag, so to add your own flags, encapsulate
|
||||
your expression as a string, but like a literal, e.g., \`/^object$/vi\`.
|
||||
|
||||
You could set this regular expression to a more expansive list, or you
|
||||
could restrict it such that even types matching those strings would not
|
||||
need destructuring.`,
|
||||
type: 'string',
|
||||
},
|
||||
disableExtraPropertyReporting: {
|
||||
description: `Whether to check for extra destructured properties. Defaults to \`false\`. Change
|
||||
to \`true\` if you want to be able to document properties which are not actually
|
||||
destructured. Keep as \`false\` if you expect properties to be documented in
|
||||
their own types. Note that extra properties will always be reported if another
|
||||
item at the same level is destructured as destructuring will prevent other
|
||||
access and this option is only intended to permit documenting extra properties
|
||||
that are available and actually used in the function.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
disableMissingParamChecks: {
|
||||
description: 'Whether to avoid checks for missing `@param` definitions. Defaults to `false`. Change to `true` if you want to be able to omit properties.',
|
||||
type: 'boolean',
|
||||
},
|
||||
enableFixer: {
|
||||
description: `Set to \`true\` to auto-remove \`@param\` duplicates (based on identical
|
||||
names).
|
||||
|
||||
Note that this option will remove duplicates of the same name even if
|
||||
the definitions do not match in other ways (e.g., the second param will
|
||||
be removed even if it has a different type or description).`,
|
||||
type: 'boolean',
|
||||
},
|
||||
useDefaultObjectProperties: {
|
||||
description: `Set to \`true\` if you wish to avoid reporting of child property documentation
|
||||
where instead of destructuring, a whole plain object is supplied as default
|
||||
value but you wish its keys to be considered as signalling that the properties
|
||||
are present and can therefore be documented. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
174
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkPropertyNames.js
generated
vendored
Normal file
174
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkPropertyNames.js
generated
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @param {string} targetTagName
|
||||
* @param {boolean} enableFixer
|
||||
* @param {import('comment-parser').Block} jsdoc
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const validatePropertyNames = (
|
||||
targetTagName,
|
||||
enableFixer,
|
||||
jsdoc, utils,
|
||||
) => {
|
||||
const jsdocTypedefs = utils.getJsdocTagsDeep('typedef');
|
||||
let propertyTagGroups;
|
||||
if (jsdocTypedefs && jsdocTypedefs.length > 1) {
|
||||
propertyTagGroups = jsdocTypedefs.map(({
|
||||
idx,
|
||||
}, index) => {
|
||||
return Object.entries(jsdoc.tags).slice(idx, jsdocTypedefs[index + 1]?.idx);
|
||||
});
|
||||
} else {
|
||||
propertyTagGroups = [
|
||||
Object.entries(jsdoc.tags),
|
||||
];
|
||||
}
|
||||
|
||||
return propertyTagGroups.some((propertyTagGroup) => {
|
||||
const propertyTags = propertyTagGroup.filter(([
|
||||
, tag,
|
||||
]) => {
|
||||
return tag.tag === targetTagName;
|
||||
});
|
||||
|
||||
return propertyTags.some(([
|
||||
, tag,
|
||||
], index) => {
|
||||
/** @type {import('../iterateJsdoc.js').Integer} */
|
||||
let tagsIndex;
|
||||
const dupeTagInfo = propertyTags.find(([
|
||||
tgsIndex,
|
||||
tg,
|
||||
], idx) => {
|
||||
tagsIndex = Number(tgsIndex);
|
||||
|
||||
return tg.name === tag.name && idx !== index;
|
||||
});
|
||||
if (dupeTagInfo) {
|
||||
utils.reportJSDoc(`Duplicate @${targetTagName} "${tag.name}"`, dupeTagInfo[1], enableFixer ? () => {
|
||||
utils.removeTag(tagsIndex);
|
||||
} : null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} targetTagName
|
||||
* @param {{
|
||||
* idx: number;
|
||||
* name: string;
|
||||
* type: string;
|
||||
* }[]} jsdocPropertyNames
|
||||
* @param {import('comment-parser').Block} jsdoc
|
||||
* @param {import('../iterateJsdoc.js').Report} report
|
||||
*/
|
||||
const validatePropertyNamesDeep = (
|
||||
targetTagName,
|
||||
jsdocPropertyNames, jsdoc, report,
|
||||
) => {
|
||||
/** @type {string} */
|
||||
let lastRealProperty;
|
||||
|
||||
return jsdocPropertyNames.some(({
|
||||
idx,
|
||||
name: jsdocPropertyName,
|
||||
}) => {
|
||||
const isPropertyPath = jsdocPropertyName.includes('.');
|
||||
|
||||
if (isPropertyPath) {
|
||||
if (!lastRealProperty) {
|
||||
report(`@${targetTagName} path declaration ("${jsdocPropertyName}") appears before any real property.`, null, jsdoc.tags[idx]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
let pathRootNodeName = jsdocPropertyName.slice(0, jsdocPropertyName.indexOf('.'));
|
||||
|
||||
if (pathRootNodeName.endsWith('[]')) {
|
||||
pathRootNodeName = pathRootNodeName.slice(0, -2);
|
||||
}
|
||||
|
||||
if (pathRootNodeName !== lastRealProperty) {
|
||||
report(
|
||||
`@${targetTagName} path declaration ("${jsdocPropertyName}") root node name ("${pathRootNodeName}") ` +
|
||||
`does not match previous real property name ("${lastRealProperty}").`,
|
||||
null,
|
||||
jsdoc.tags[idx],
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
lastRealProperty = jsdocPropertyName;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
enableFixer = false,
|
||||
} = context.options[0] || {};
|
||||
const jsdocPropertyNamesDeep = utils.getJsdocTagsDeep('property');
|
||||
if (!jsdocPropertyNamesDeep || !jsdocPropertyNamesDeep.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetTagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'property',
|
||||
}));
|
||||
const isError = validatePropertyNames(
|
||||
targetTagName,
|
||||
enableFixer,
|
||||
jsdoc,
|
||||
utils,
|
||||
);
|
||||
|
||||
if (isError) {
|
||||
return;
|
||||
}
|
||||
|
||||
validatePropertyNamesDeep(
|
||||
targetTagName, jsdocPropertyNamesDeep, jsdoc, report,
|
||||
);
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Ensures that property names in JSDoc are not duplicated on the same block and that nested properties have defined roots.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-property-names.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
enableFixer: {
|
||||
description: `Set to \`true\` to auto-remove \`@property\` duplicates (based on
|
||||
identical names).
|
||||
|
||||
Note that this option will remove duplicates of the same name even if
|
||||
the definitions do not match in other ways (e.g., the second property will
|
||||
be removed even if it has a different type or description).`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
30
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkSyntax.js
generated
vendored
Normal file
30
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkSyntax.js
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
jsdoc,
|
||||
report,
|
||||
settings,
|
||||
}) => {
|
||||
const {
|
||||
mode,
|
||||
} = settings;
|
||||
|
||||
// Don't check for "permissive" and "closure"
|
||||
if (mode === 'jsdoc' || mode === 'typescript') {
|
||||
for (const tag of jsdoc.tags) {
|
||||
if (tag.type.slice(-1) === '=') {
|
||||
report('Syntax should not be Google Closure Compiler style.', null, tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Reports against syntax not valid for the mode (e.g., Google Closure Compiler in non-Closure mode).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-syntax.md#repos-sticky-header',
|
||||
},
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
406
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkTagNames.js
generated
vendored
Normal file
406
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkTagNames.js
generated
vendored
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
|
||||
// https://babeljs.io/docs/en/babel-plugin-transform-react-jsx/
|
||||
const jsxTagNames = new Set([
|
||||
'jsx',
|
||||
'jsxFrag',
|
||||
'jsxImportSource',
|
||||
'jsxRuntime',
|
||||
]);
|
||||
|
||||
const typedTagsAlwaysUnnecessary = new Set([
|
||||
'augments',
|
||||
'callback',
|
||||
'class',
|
||||
'enum',
|
||||
'implements',
|
||||
'private',
|
||||
'property',
|
||||
'protected',
|
||||
'public',
|
||||
'readonly',
|
||||
'this',
|
||||
'type',
|
||||
'typedef',
|
||||
]);
|
||||
|
||||
const typedTagsNeedingName = new Set([
|
||||
'template',
|
||||
]);
|
||||
|
||||
const typedTagsUnnecessaryOutsideDeclare = new Set([
|
||||
'abstract',
|
||||
'access',
|
||||
'class',
|
||||
'constant',
|
||||
'constructs',
|
||||
'default',
|
||||
'enum',
|
||||
'export',
|
||||
'exports',
|
||||
'function',
|
||||
'global',
|
||||
'inherits',
|
||||
'instance',
|
||||
'interface',
|
||||
'member',
|
||||
'memberof',
|
||||
'memberOf',
|
||||
'method',
|
||||
'mixes',
|
||||
'mixin',
|
||||
'module',
|
||||
'name',
|
||||
'namespace',
|
||||
'override',
|
||||
'property',
|
||||
'requires',
|
||||
'static',
|
||||
'this',
|
||||
]);
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
jsdocNode,
|
||||
node,
|
||||
report,
|
||||
settings,
|
||||
sourceCode,
|
||||
utils,
|
||||
}) => {
|
||||
const
|
||||
/**
|
||||
* @type {{
|
||||
* definedTags: string[],
|
||||
* enableFixer: boolean,
|
||||
* inlineTags: string[],
|
||||
* jsxTags: boolean,
|
||||
* typed: boolean
|
||||
}} */ {
|
||||
definedTags = [],
|
||||
enableFixer = true,
|
||||
inlineTags = [
|
||||
'link', 'linkcode', 'linkplain', 'tutorial',
|
||||
],
|
||||
jsxTags,
|
||||
typed,
|
||||
} = context.options[0] || {};
|
||||
|
||||
/** @type {(string|undefined)[]} */
|
||||
let definedPreferredTags = [];
|
||||
const {
|
||||
structuredTags,
|
||||
tagNamePreference,
|
||||
} = settings;
|
||||
const definedStructuredTags = Object.keys(structuredTags);
|
||||
const definedNonPreferredTags = Object.keys(tagNamePreference);
|
||||
if (definedNonPreferredTags.length) {
|
||||
definedPreferredTags = Object.values(tagNamePreference).map((preferredTag) => {
|
||||
if (typeof preferredTag === 'string') {
|
||||
// May become an empty string but will be filtered out below
|
||||
return preferredTag;
|
||||
}
|
||||
|
||||
if (!preferredTag) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof preferredTag !== 'object') {
|
||||
utils.reportSettings(
|
||||
'Invalid `settings.jsdoc.tagNamePreference`. Values must be falsy, a string, or an object.',
|
||||
);
|
||||
}
|
||||
|
||||
return preferredTag.replacement;
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.Node} subNode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isInAmbientContext = (subNode) => {
|
||||
return subNode.type === 'Program' ?
|
||||
/* c8 ignore next -- Support old ESLint */
|
||||
(context.filename ?? context.getFilename()).endsWith('.d.ts') :
|
||||
Boolean(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.VariableDeclaration} */ (
|
||||
subNode
|
||||
).declare,
|
||||
) || isInAmbientContext(subNode.parent);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('comment-parser').Spec} jsdocTag
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const tagIsRedundantWhenTyped = (jsdocTag) => {
|
||||
if (!typedTagsUnnecessaryOutsideDeclare.has(jsdocTag.tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (jsdocTag.tag === 'default') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* c8 ignore next -- Support old ESLint */
|
||||
if ((context.filename ?? context.getFilename()).endsWith('.d.ts') && [
|
||||
null, 'Program', undefined,
|
||||
].includes(node?.parent?.type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isInAmbientContext(/** @type {import('eslint').Rule.Node} */ (node))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {import('comment-parser').Spec} jsdocTag
|
||||
* @param {import('../iterateJsdoc.js').Integer} tagIndex
|
||||
* @param {Partial<import('comment-parser').Tokens>} [additionalTagChanges]
|
||||
* @returns {void}
|
||||
*/
|
||||
const reportWithTagRemovalFixer = (message, jsdocTag, tagIndex, additionalTagChanges) => {
|
||||
utils.reportJSDoc(message, jsdocTag, enableFixer ? () => {
|
||||
if (jsdocTag.description.trim()) {
|
||||
utils.changeTag(jsdocTag, {
|
||||
postType: '',
|
||||
type: '',
|
||||
...additionalTagChanges,
|
||||
});
|
||||
} else {
|
||||
utils.removeTag(tagIndex, {
|
||||
removeEmptyBlock: true,
|
||||
});
|
||||
}
|
||||
} : null, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('comment-parser').Spec} jsdocTag
|
||||
* @param {import('../iterateJsdoc.js').Integer} tagIndex
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const checkTagForTypedValidity = (jsdocTag, tagIndex) => {
|
||||
if (typedTagsAlwaysUnnecessary.has(jsdocTag.tag)) {
|
||||
reportWithTagRemovalFixer(
|
||||
`'@${jsdocTag.tag}' is redundant when using a type system.`,
|
||||
jsdocTag,
|
||||
tagIndex,
|
||||
{
|
||||
postTag: '',
|
||||
tag: '',
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tagIsRedundantWhenTyped(jsdocTag)) {
|
||||
reportWithTagRemovalFixer(
|
||||
`'@${jsdocTag.tag}' is redundant outside of ambient (\`declare\`/\`.d.ts\`) contexts when using a type system.`,
|
||||
jsdocTag,
|
||||
tagIndex,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typedTagsNeedingName.has(jsdocTag.tag) && !jsdocTag.name) {
|
||||
reportWithTagRemovalFixer(
|
||||
`'@${jsdocTag.tag}' without a name is redundant when using a type system.`,
|
||||
jsdocTag,
|
||||
tagIndex,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
for (let tagIndex = 0; tagIndex < jsdoc.tags.length; tagIndex += 1) {
|
||||
const jsdocTag = jsdoc.tags[tagIndex];
|
||||
const tagName = jsdocTag.tag;
|
||||
if (jsxTags && jsxTagNames.has(tagName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typed && checkTagForTypedValidity(jsdocTag, tagIndex)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const validTags = [
|
||||
...definedTags,
|
||||
...(/** @type {string[]} */ (definedPreferredTags)),
|
||||
...definedNonPreferredTags,
|
||||
...definedStructuredTags,
|
||||
...typed ? typedTagsNeedingName : [],
|
||||
];
|
||||
|
||||
if (utils.isValidTag(tagName, validTags)) {
|
||||
let preferredTagName = utils.getPreferredTagName({
|
||||
allowObjectReturn: true,
|
||||
defaultMessage: `Blacklisted tag found (\`@${tagName}\`)`,
|
||||
tagName,
|
||||
});
|
||||
if (!preferredTagName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let message;
|
||||
if (typeof preferredTagName === 'object') {
|
||||
({
|
||||
message,
|
||||
replacement: preferredTagName,
|
||||
} = /** @type {{message: string; replacement?: string | undefined;}} */ (
|
||||
preferredTagName
|
||||
));
|
||||
}
|
||||
|
||||
if (!message) {
|
||||
message = `Invalid JSDoc tag (preference). Replace "${tagName}" JSDoc tag with "${preferredTagName}".`;
|
||||
}
|
||||
|
||||
if (preferredTagName !== tagName) {
|
||||
report(message, (fixer) => {
|
||||
const replacement = sourceCode.getText(jsdocNode).replace(
|
||||
new RegExp(`@${escapeStringRegexp(tagName)}\\b`, 'v'),
|
||||
`@${preferredTagName}`,
|
||||
);
|
||||
|
||||
return fixer.replaceText(jsdocNode, replacement);
|
||||
}, jsdocTag);
|
||||
}
|
||||
} else {
|
||||
report(`Invalid JSDoc tag name "${tagName}".`, null, jsdocTag);
|
||||
}
|
||||
}
|
||||
|
||||
for (const inlineTag of utils.getInlineTags()) {
|
||||
if (!inlineTags.includes(inlineTag.tag)) {
|
||||
report(`Invalid JSDoc inline tag name "${inlineTag.tag}"`, null, inlineTag);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Reports invalid block tag names.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-tag-names.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
definedTags: {
|
||||
description: `Use an array of \`definedTags\` strings to configure additional, allowed tags.
|
||||
The format is as follows:
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"definedTags": ["note", "record"]
|
||||
}
|
||||
\`\`\``,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
enableFixer: {
|
||||
description: 'Set to `false` to disable auto-removal of types that are redundant with the [`typed` option](#typed).',
|
||||
type: 'boolean',
|
||||
},
|
||||
inlineTags: {
|
||||
description: `List of tags to allow inline.
|
||||
|
||||
Defaults to array of \`'link', 'linkcode', 'linkplain', 'tutorial'\``,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
jsxTags: {
|
||||
description: `If this is set to \`true\`, all of the following tags used to control JSX output are allowed:
|
||||
|
||||
\`\`\`
|
||||
jsx
|
||||
jsxFrag
|
||||
jsxImportSource
|
||||
jsxRuntime
|
||||
\`\`\`
|
||||
|
||||
For more information, see the [babel documentation](https://babeljs.io/docs/en/babel-plugin-transform-react-jsx).`,
|
||||
type: 'boolean',
|
||||
},
|
||||
typed: {
|
||||
description: `If this is set to \`true\`, additionally checks for tag names that are redundant when using a type checker such as TypeScript.
|
||||
|
||||
These tags are always unnecessary when using TypeScript or similar:
|
||||
|
||||
\`\`\`
|
||||
augments
|
||||
callback
|
||||
class
|
||||
enum
|
||||
implements
|
||||
private
|
||||
property
|
||||
protected
|
||||
public
|
||||
readonly
|
||||
this
|
||||
type
|
||||
typedef
|
||||
\`\`\`
|
||||
|
||||
These tags are unnecessary except when inside a TypeScript \`declare\` context:
|
||||
|
||||
\`\`\`
|
||||
abstract
|
||||
access
|
||||
class
|
||||
constant
|
||||
constructs
|
||||
default
|
||||
enum
|
||||
export
|
||||
exports
|
||||
function
|
||||
global
|
||||
inherits
|
||||
instance
|
||||
interface
|
||||
member
|
||||
memberof
|
||||
memberOf
|
||||
method
|
||||
mixes
|
||||
mixin
|
||||
module
|
||||
name
|
||||
namespace
|
||||
override
|
||||
property
|
||||
requires
|
||||
static
|
||||
this
|
||||
\`\`\``,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
208
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkTemplateNames.js
generated
vendored
Normal file
208
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkTemplateNames.js
generated
vendored
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
import iterateJsdoc, {
|
||||
parseComment,
|
||||
} from '../iterateJsdoc.js';
|
||||
import {
|
||||
getTags,
|
||||
} from '../jsdocUtils.js';
|
||||
import {
|
||||
getJSDocComment,
|
||||
parse as parseType,
|
||||
traverse,
|
||||
tryParse as tryParseType,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
jsdoc,
|
||||
node,
|
||||
report,
|
||||
settings,
|
||||
sourceCode,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
mode,
|
||||
} = settings;
|
||||
|
||||
const tgName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'template',
|
||||
}));
|
||||
if (!tgName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const templateTags = utils.getTags(tgName);
|
||||
|
||||
const usedNames = new Set();
|
||||
/**
|
||||
* @param {string} potentialType
|
||||
*/
|
||||
const checkForUsedTypes = (potentialType) => {
|
||||
let parsedType;
|
||||
try {
|
||||
parsedType = mode === 'permissive' ?
|
||||
tryParseType(/** @type {string} */ (potentialType)) :
|
||||
parseType(/** @type {string} */ (potentialType), mode);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
traverse(parsedType, (nde) => {
|
||||
const {
|
||||
type,
|
||||
value,
|
||||
} = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde);
|
||||
if (type === 'JsdocTypeName') {
|
||||
usedNames.add(value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const checkParamsAndReturnsTags = (jsdc = jsdoc) => {
|
||||
const paramName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'param',
|
||||
}));
|
||||
const paramTags = getTags(jsdc, paramName);
|
||||
for (const paramTag of paramTags) {
|
||||
checkForUsedTypes(paramTag.type);
|
||||
}
|
||||
|
||||
const returnsName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'returns',
|
||||
}));
|
||||
const returnsTags = getTags(jsdc, returnsName);
|
||||
for (const returnsTag of returnsTags) {
|
||||
checkForUsedTypes(returnsTag.type);
|
||||
}
|
||||
};
|
||||
|
||||
const checkTemplateTags = () => {
|
||||
for (const tag of templateTags) {
|
||||
const names = utils.parseClosureTemplateTag(tag);
|
||||
for (const nme of names) {
|
||||
if (!usedNames.has(nme)) {
|
||||
report(`@${tgName} ${nme} not in use`, null, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('@typescript-eslint/types').TSESTree.FunctionDeclaration|
|
||||
* import('@typescript-eslint/types').TSESTree.ClassDeclaration|
|
||||
* import('@typescript-eslint/types').TSESTree.TSInterfaceDeclaration|
|
||||
* import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration
|
||||
* @param {boolean} [checkParamsAndReturns]
|
||||
*/
|
||||
const checkParameters = (aliasDeclaration, checkParamsAndReturns) => {
|
||||
/* c8 ignore next -- Guard */
|
||||
const {
|
||||
params,
|
||||
} = aliasDeclaration.typeParameters ?? {
|
||||
params: [],
|
||||
};
|
||||
for (const {
|
||||
name: {
|
||||
name,
|
||||
},
|
||||
} of params) {
|
||||
usedNames.add(name);
|
||||
}
|
||||
|
||||
if (checkParamsAndReturns) {
|
||||
checkParamsAndReturnsTags();
|
||||
} else if (aliasDeclaration.type === 'ClassDeclaration') {
|
||||
/* c8 ignore next -- TS */
|
||||
for (const nde of aliasDeclaration?.body?.body ?? []) {
|
||||
// @ts-expect-error Should be ok
|
||||
const commentNode = getJSDocComment(sourceCode, nde, settings);
|
||||
if (!commentNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const innerJsdoc = parseComment(commentNode, '');
|
||||
checkParamsAndReturnsTags(innerJsdoc);
|
||||
|
||||
const typeName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'type',
|
||||
}));
|
||||
const typeTags = getTags(innerJsdoc, typeName);
|
||||
for (const typeTag of typeTags) {
|
||||
checkForUsedTypes(typeTag.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkTemplateTags();
|
||||
};
|
||||
|
||||
const handleTypeAliases = () => {
|
||||
const nde = /** @type {import('@typescript-eslint/types').TSESTree.Node} */ (
|
||||
node
|
||||
);
|
||||
if (!nde) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (nde.type) {
|
||||
case 'ClassDeclaration':
|
||||
case 'TSInterfaceDeclaration':
|
||||
case 'TSTypeAliasDeclaration':
|
||||
checkParameters(nde);
|
||||
break;
|
||||
case 'ExportDefaultDeclaration':
|
||||
case 'ExportNamedDeclaration':
|
||||
switch (nde.declaration?.type) {
|
||||
case 'ClassDeclaration':
|
||||
case 'TSInterfaceDeclaration':
|
||||
case 'TSTypeAliasDeclaration':
|
||||
checkParameters(nde.declaration);
|
||||
break;
|
||||
case 'FunctionDeclaration':
|
||||
checkParameters(nde.declaration, true);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'FunctionDeclaration':
|
||||
checkParameters(nde, true);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const callbackTags = utils.getTags('callback');
|
||||
const functionTags = utils.getTags('function');
|
||||
if (callbackTags.length || functionTags.length) {
|
||||
checkParamsAndReturnsTags();
|
||||
checkTemplateTags();
|
||||
return;
|
||||
}
|
||||
|
||||
const typedefTags = utils.getTags('typedef');
|
||||
if (!typedefTags.length || typedefTags.length >= 2) {
|
||||
handleTypeAliases();
|
||||
return;
|
||||
}
|
||||
|
||||
const potentialTypedefType = typedefTags[0].type;
|
||||
checkForUsedTypes(potentialTypedefType);
|
||||
|
||||
const propertyName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'property',
|
||||
}));
|
||||
const propertyTags = utils.getTags(propertyName);
|
||||
for (const propertyTag of propertyTags) {
|
||||
checkForUsedTypes(propertyTag.type);
|
||||
}
|
||||
|
||||
checkTemplateTags();
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Checks that any `@template` names are actually used in the connected `@typedef` or type alias.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-template-names.md#repos-sticky-header',
|
||||
},
|
||||
schema: [],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
130
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkTypes.js
generated
vendored
Normal file
130
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkTypes.js
generated
vendored
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import {
|
||||
buildRejectOrPreferRuleDefinition,
|
||||
} from '../buildRejectOrPreferRuleDefinition.js';
|
||||
import {
|
||||
strictNativeTypes,
|
||||
} from '../jsdocUtils.js';
|
||||
|
||||
/**
|
||||
* @callback CheckNativeTypes
|
||||
* Iterates strict types to see if any should be added to `invalidTypes` (and
|
||||
* the the relevant strict type returned as the new preferred type).
|
||||
* @param {import('../iterateJsdoc.js').PreferredTypes} preferredTypes
|
||||
* @param {string} typeNodeName
|
||||
* @param {string|undefined} preferred
|
||||
* @param {import('jsdoc-type-pratt-parser').NonRootResult|undefined} parentNode
|
||||
* @param {(string|false|undefined)[][]} invalidTypes
|
||||
* @returns {string|undefined} The `preferred` type string, optionally changed
|
||||
*/
|
||||
|
||||
/** @type {CheckNativeTypes} */
|
||||
const checkNativeTypes = (preferredTypes, typeNodeName, preferred, parentNode, invalidTypes) => {
|
||||
let changedPreferred = preferred;
|
||||
for (const strictNativeType of strictNativeTypes) {
|
||||
if (
|
||||
strictNativeType === 'object' &&
|
||||
(
|
||||
// This is not set to remap with exact type match (e.g.,
|
||||
// `object: 'Object'`), so can ignore (including if circular)
|
||||
!preferredTypes?.[typeNodeName] ||
|
||||
// Although present on `preferredTypes` for remapping, this is a
|
||||
// parent object without a parent match (and not
|
||||
// `unifyParentAndChildTypeChecks`) and we don't want
|
||||
// `object<>` given TypeScript issue https://github.com/microsoft/TypeScript/issues/20555
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').GenericResult}
|
||||
*/
|
||||
(
|
||||
parentNode
|
||||
)?.elements?.length && (
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').GenericResult}
|
||||
*/
|
||||
(
|
||||
parentNode
|
||||
)?.left?.type === 'JsdocTypeName' &&
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').GenericResult}
|
||||
*/
|
||||
(parentNode)?.left?.value === 'Object'
|
||||
)
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strictNativeType !== typeNodeName &&
|
||||
strictNativeType.toLowerCase() === typeNodeName.toLowerCase() &&
|
||||
|
||||
// Don't report if user has own map for a strict native type
|
||||
(!preferredTypes || preferredTypes?.[strictNativeType] === undefined)
|
||||
) {
|
||||
changedPreferred = strictNativeType;
|
||||
invalidTypes.push([
|
||||
typeNodeName, changedPreferred,
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return changedPreferred;
|
||||
};
|
||||
|
||||
export default buildRejectOrPreferRuleDefinition({
|
||||
checkNativeTypes,
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
exemptTagContexts: {
|
||||
description: 'Avoids reporting when a bad type is found on a specified tag.',
|
||||
items: {
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
tag: {
|
||||
description: 'Set a key `tag` to the tag to exempt',
|
||||
type: 'string',
|
||||
},
|
||||
types: {
|
||||
description: `Set to \`true\` to indicate that any types on that tag will be allowed,
|
||||
or to an array of strings which will only allow specific bad types.
|
||||
If an array of strings is given, these must match the type exactly,
|
||||
e.g., if you only allow \`"object"\`, it will not allow
|
||||
\`"object<string, string>"\`. Note that this is different from the
|
||||
behavior of \`settings.jsdoc.preferredTypes\`. This option is useful
|
||||
for normally restricting generic types like \`object\` with
|
||||
\`preferredTypes\`, but allowing \`typedef\` to indicate that its base
|
||||
type is \`object\`.`,
|
||||
oneOf: [
|
||||
{
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
noDefaults: {
|
||||
description: `Insists that only the supplied option type
|
||||
map is to be used, and that the default preferences (such as "string"
|
||||
over "String") will not be enforced. The option's default is \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
unifyParentAndChildTypeChecks: {
|
||||
description: `@deprecated Use the \`preferredTypes[preferredType]\` setting of the same name instead.
|
||||
If this option is \`true\`, will currently override \`unifyParentAndChildTypeChecks\` on the \`preferredTypes\` setting.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
});
|
||||
264
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkValues.js
generated
vendored
Normal file
264
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/checkValues.js
generated
vendored
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
parseImportsExports,
|
||||
} from 'parse-imports-exports';
|
||||
import semver from 'semver';
|
||||
import spdxExpressionParse from 'spdx-expression-parse';
|
||||
|
||||
const allowedKinds = new Set([
|
||||
'class',
|
||||
'constant',
|
||||
'event',
|
||||
'external',
|
||||
'file',
|
||||
'function',
|
||||
'member',
|
||||
'mixin',
|
||||
'module',
|
||||
'namespace',
|
||||
'typedef',
|
||||
]);
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
report,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
const options = context.options[0] || {};
|
||||
const {
|
||||
allowedAuthors = null,
|
||||
allowedLicenses = null,
|
||||
licensePattern = '/([^\n\r]*)/gv',
|
||||
numericOnlyVariation = false,
|
||||
} = options;
|
||||
|
||||
utils.forEachPreferredTag('version', (jsdocParameter, targetTagName) => {
|
||||
const version = /** @type {string} */ (
|
||||
utils.getTagDescription(jsdocParameter)
|
||||
).trim();
|
||||
if (!version) {
|
||||
report(
|
||||
`Missing JSDoc @${targetTagName} value.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
} else if (!semver.valid(version)) {
|
||||
report(
|
||||
`Invalid JSDoc @${targetTagName}: "${utils.getTagDescription(jsdocParameter)}".`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
utils.forEachPreferredTag('kind', (jsdocParameter, targetTagName) => {
|
||||
const kind = /** @type {string} */ (
|
||||
utils.getTagDescription(jsdocParameter)
|
||||
).trim();
|
||||
if (!kind) {
|
||||
report(
|
||||
`Missing JSDoc @${targetTagName} value.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
} else if (!allowedKinds.has(kind)) {
|
||||
report(
|
||||
`Invalid JSDoc @${targetTagName}: "${utils.getTagDescription(jsdocParameter)}"; ` +
|
||||
`must be one of: ${[
|
||||
...allowedKinds,
|
||||
].join(', ')}.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (numericOnlyVariation) {
|
||||
utils.forEachPreferredTag('variation', (jsdocParameter, targetTagName) => {
|
||||
const variation = /** @type {string} */ (
|
||||
utils.getTagDescription(jsdocParameter)
|
||||
).trim();
|
||||
if (!variation) {
|
||||
report(
|
||||
`Missing JSDoc @${targetTagName} value.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
} else if (
|
||||
!Number.isInteger(Number(variation)) ||
|
||||
Number(variation) <= 0
|
||||
) {
|
||||
report(
|
||||
`Invalid JSDoc @${targetTagName}: "${utils.getTagDescription(jsdocParameter)}".`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
utils.forEachPreferredTag('since', (jsdocParameter, targetTagName) => {
|
||||
const version = /** @type {string} */ (
|
||||
utils.getTagDescription(jsdocParameter)
|
||||
).trim();
|
||||
if (!version) {
|
||||
report(
|
||||
`Missing JSDoc @${targetTagName} value.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
} else if (!semver.valid(version)) {
|
||||
report(
|
||||
`Invalid JSDoc @${targetTagName}: "${utils.getTagDescription(jsdocParameter)}".`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
});
|
||||
utils.forEachPreferredTag('license', (jsdocParameter, targetTagName) => {
|
||||
const licenseRegex = utils.getRegexFromString(licensePattern, 'g');
|
||||
const matches = /** @type {string} */ (
|
||||
utils.getTagDescription(jsdocParameter)
|
||||
).matchAll(licenseRegex);
|
||||
let positiveMatch = false;
|
||||
for (const match of matches) {
|
||||
const license = match[1] || match[0];
|
||||
if (license) {
|
||||
positiveMatch = true;
|
||||
}
|
||||
|
||||
if (!license.trim()) {
|
||||
// Avoid reporting again as empty match
|
||||
if (positiveMatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
report(
|
||||
`Missing JSDoc @${targetTagName} value.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
} else if (allowedLicenses) {
|
||||
if (allowedLicenses !== true && !allowedLicenses.includes(license)) {
|
||||
report(
|
||||
`Invalid JSDoc @${targetTagName}: "${license}"; expected one of ${allowedLicenses.join(', ')}.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
spdxExpressionParse(license);
|
||||
} catch {
|
||||
report(
|
||||
`Invalid JSDoc @${targetTagName}: "${license}"; expected SPDX expression: https://spdx.org/licenses/.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (settings.mode === 'typescript') {
|
||||
utils.forEachPreferredTag('import', (tag) => {
|
||||
const {
|
||||
description,
|
||||
name,
|
||||
type,
|
||||
} = tag;
|
||||
const typePart = type ? `{${type}} ` : '';
|
||||
const imprt = 'import ' + (description ?
|
||||
`${typePart}${name} ${description}` :
|
||||
`${typePart}${name}`);
|
||||
|
||||
const importsExports = parseImportsExports(imprt.trim());
|
||||
|
||||
if (importsExports.errors) {
|
||||
report(
|
||||
'Bad @import tag',
|
||||
null,
|
||||
tag,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
utils.forEachPreferredTag('author', (jsdocParameter, targetTagName) => {
|
||||
const author = /** @type {string} */ (
|
||||
utils.getTagDescription(jsdocParameter)
|
||||
).trim();
|
||||
if (!author) {
|
||||
report(
|
||||
`Missing JSDoc @${targetTagName} value.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
} else if (allowedAuthors && !allowedAuthors.includes(author)) {
|
||||
report(
|
||||
`Invalid JSDoc @${targetTagName}: "${utils.getTagDescription(jsdocParameter)}"; expected one of ${allowedAuthors.join(', ')}.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'This rule checks the values for a handful of tags: `@version`, `@since`, `@license` and `@author`.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-values.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowedAuthors: {
|
||||
description: `An array of allowable author values. If absent, only non-whitespace will
|
||||
be checked for.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
allowedLicenses: {
|
||||
anyOf: [
|
||||
{
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
},
|
||||
],
|
||||
description: `An array of allowable license values or \`true\` to allow any license text.
|
||||
If present as an array, will be used in place of [SPDX identifiers](https://spdx.org/licenses/).`,
|
||||
},
|
||||
licensePattern: {
|
||||
description: `A string to be converted into a \`RegExp\` (with \`v\` flag) and whose first
|
||||
parenthetical grouping, if present, will match the portion of the license
|
||||
description to check (if no grouping is present, then the whole portion
|
||||
matched will be used). Defaults to \`/([^\\n\\r]*)/gv\`, i.e., the SPDX expression
|
||||
is expected before any line breaks.
|
||||
|
||||
Note that the \`/\` delimiters are optional, but necessary to add flags.
|
||||
|
||||
Defaults to using the \`v\` flag, so to add your own flags, encapsulate
|
||||
your expression as a string, but like a literal, e.g., \`/^mit$/vi\`.`,
|
||||
type: 'string',
|
||||
},
|
||||
numericOnlyVariation: {
|
||||
description: `Whether to enable validation that \`@variation\` must be a number. Defaults to
|
||||
\`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
443
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/convertToJsdocComments.js
generated
vendored
Normal file
443
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/convertToJsdocComments.js
generated
vendored
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
import {
|
||||
getSettings,
|
||||
} from '../iterateJsdoc.js';
|
||||
import {
|
||||
enforcedContexts,
|
||||
getContextObject,
|
||||
getIndent,
|
||||
} from '../jsdocUtils.js';
|
||||
import {
|
||||
getDecorator,
|
||||
getFollowingComment,
|
||||
getNonJsdocComment,
|
||||
getReducedASTNode,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
export default {
|
||||
create (context) {
|
||||
/**
|
||||
* @typedef {import('eslint').AST.Token | import('estree').Comment | {
|
||||
* type: import('eslint').AST.TokenType|"Line"|"Block"|"Shebang",
|
||||
* range: [number, number],
|
||||
* value: string
|
||||
* }} Token
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback AddComment
|
||||
* @param {boolean|undefined} inlineCommentBlock
|
||||
* @param {Token} comment
|
||||
* @param {string} indent
|
||||
* @param {number} lines
|
||||
* @param {import('eslint').Rule.RuleFixer} fixer
|
||||
*/
|
||||
|
||||
/* c8 ignore next -- Fallback to deprecated method */
|
||||
const {
|
||||
sourceCode = context.getSourceCode(),
|
||||
} = context;
|
||||
const settings = getSettings(context);
|
||||
if (!settings) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const {
|
||||
allowedPrefixes = [
|
||||
'@ts-', 'istanbul ', 'c8 ', 'v8 ', 'eslint', 'prettier-',
|
||||
],
|
||||
contexts = settings.contexts || [],
|
||||
contextsAfter = /** @type {string[]} */ ([]),
|
||||
contextsBeforeAndAfter = [
|
||||
'VariableDeclarator', 'TSPropertySignature', 'PropertyDefinition',
|
||||
],
|
||||
enableFixer = true,
|
||||
enforceJsdocLineStyle = 'multi',
|
||||
lineOrBlockStyle = 'both',
|
||||
} = context.options[0] ?? {};
|
||||
|
||||
let reportingNonJsdoc = false;
|
||||
|
||||
/**
|
||||
* @param {string} messageId
|
||||
* @param {import('estree').Comment|Token} comment
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {import('eslint').Rule.ReportFixer} fixer
|
||||
*/
|
||||
const report = (messageId, comment, node, fixer) => {
|
||||
const loc = {
|
||||
end: {
|
||||
column: 0,
|
||||
/* c8 ignore next 2 -- Guard */
|
||||
// @ts-expect-error Ok
|
||||
line: (comment.loc?.start?.line ?? 1),
|
||||
},
|
||||
start: {
|
||||
column: 0,
|
||||
/* c8 ignore next 2 -- Guard */
|
||||
// @ts-expect-error Ok
|
||||
line: (comment.loc?.start?.line ?? 1),
|
||||
},
|
||||
};
|
||||
|
||||
context.report({
|
||||
fix: enableFixer ? fixer : null,
|
||||
loc,
|
||||
messageId,
|
||||
node,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {import('eslint').AST.Token | import('estree').Comment | {
|
||||
* type: import('eslint').AST.TokenType|"Line"|"Block"|"Shebang",
|
||||
* range: [number, number],
|
||||
* value: string
|
||||
* }} comment
|
||||
* @param {AddComment} addComment
|
||||
* @param {import('../iterateJsdoc.js').Context[]} ctxts
|
||||
*/
|
||||
const getFixer = (node, comment, addComment, ctxts) => {
|
||||
return /** @type {import('eslint').Rule.ReportFixer} */ (fixer) => {
|
||||
// Default to one line break if the `minLines`/`maxLines` settings allow
|
||||
const lines = settings.minLines === 0 && settings.maxLines >= 1 ? 1 : settings.minLines;
|
||||
let baseNode =
|
||||
/**
|
||||
* @type {import('@typescript-eslint/types').TSESTree.Node|import('eslint').Rule.Node}
|
||||
*/ (
|
||||
getReducedASTNode(node, sourceCode)
|
||||
);
|
||||
|
||||
const decorator = getDecorator(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(baseNode),
|
||||
);
|
||||
if (decorator) {
|
||||
baseNode = /** @type {import('@typescript-eslint/types').TSESTree.Decorator} */ (
|
||||
decorator
|
||||
);
|
||||
}
|
||||
|
||||
const indent = getIndent({
|
||||
text: sourceCode.getText(
|
||||
/** @type {import('eslint').Rule.Node} */ (baseNode),
|
||||
/** @type {import('eslint').AST.SourceLocation} */
|
||||
(
|
||||
/** @type {import('eslint').Rule.Node} */ (baseNode).loc
|
||||
).start.column,
|
||||
),
|
||||
});
|
||||
|
||||
const {
|
||||
inlineCommentBlock,
|
||||
} =
|
||||
/**
|
||||
* @type {{
|
||||
* context: string,
|
||||
* inlineCommentBlock: boolean,
|
||||
* minLineCount: import('../iterateJsdoc.js').Integer
|
||||
* }[]}
|
||||
*/ (ctxts).find((contxt) => {
|
||||
if (typeof contxt === 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
context: ctxt,
|
||||
} = contxt;
|
||||
return ctxt === node.type;
|
||||
}) || {};
|
||||
|
||||
return addComment(inlineCommentBlock, comment, indent, lines, fixer);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('eslint').AST.Token | import('estree').Comment | {
|
||||
* type: import('eslint').AST.TokenType|"Line"|"Block"|"Shebang",
|
||||
* range: [number, number],
|
||||
* value: string
|
||||
* }} comment
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {AddComment} addComment
|
||||
* @param {import('../iterateJsdoc.js').Context[]} ctxts
|
||||
*/
|
||||
const reportings = (comment, node, addComment, ctxts) => {
|
||||
const fixer = getFixer(node, comment, addComment, ctxts);
|
||||
|
||||
if (comment.type === 'Block') {
|
||||
if (lineOrBlockStyle === 'line') {
|
||||
return;
|
||||
}
|
||||
|
||||
report('blockCommentsJsdocStyle', comment, node, fixer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comment.type === 'Line') {
|
||||
if (lineOrBlockStyle === 'block') {
|
||||
return;
|
||||
}
|
||||
|
||||
report('lineCommentsJsdocStyle', comment, node, fixer);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {import('../iterateJsdoc.js').CheckJsdoc}
|
||||
*/
|
||||
const checkNonJsdoc = (_info, _handler, node) => {
|
||||
const comment = getNonJsdocComment(sourceCode, node, settings);
|
||||
|
||||
if (
|
||||
!comment ||
|
||||
/** @type {string[]} */
|
||||
(allowedPrefixes).some((prefix) => {
|
||||
return comment.value.trimStart().startsWith(prefix);
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
reportingNonJsdoc = true;
|
||||
|
||||
/** @type {AddComment} */
|
||||
const addComment = (inlineCommentBlock, commentToAdd, indent, lines, fixer) => {
|
||||
const insertion = (
|
||||
inlineCommentBlock || enforceJsdocLineStyle === 'single' ?
|
||||
`/** ${commentToAdd.value.trim()} ` :
|
||||
`/**\n${indent}*${commentToAdd.value.trimEnd()}\n${indent}`
|
||||
) +
|
||||
`*/${'\n'.repeat((lines || 1) - 1)}`;
|
||||
|
||||
return fixer.replaceText(
|
||||
/** @type {import('eslint').AST.Token} */
|
||||
(commentToAdd),
|
||||
insertion,
|
||||
);
|
||||
};
|
||||
|
||||
reportings(comment, node, addComment, contexts);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {import('../iterateJsdoc.js').Context[]} ctxts
|
||||
*/
|
||||
const checkNonJsdocAfter = (node, ctxts) => {
|
||||
const comment = getFollowingComment(sourceCode, node);
|
||||
|
||||
if (
|
||||
!comment ||
|
||||
comment.value.startsWith('*') ||
|
||||
/** @type {string[]} */
|
||||
(allowedPrefixes).some((prefix) => {
|
||||
return comment.value.trimStart().startsWith(prefix);
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {AddComment} */
|
||||
const addComment = (inlineCommentBlock, commentToAdd, indent, lines, fixer) => {
|
||||
const insertion = (
|
||||
inlineCommentBlock || enforceJsdocLineStyle === 'single' ?
|
||||
`/** ${commentToAdd.value.trim()} ` :
|
||||
`/**\n${indent}*${commentToAdd.value.trimEnd()}\n${indent}`
|
||||
) +
|
||||
`*/${'\n'.repeat((lines || 1) - 1)}${lines ? `\n${indent.slice(1)}` : ' '}`;
|
||||
|
||||
return [
|
||||
fixer.remove(
|
||||
/** @type {import('eslint').AST.Token} */
|
||||
(commentToAdd),
|
||||
), fixer.insertTextBefore(
|
||||
node.type === 'VariableDeclarator' ? node.parent : node,
|
||||
insertion,
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
reportings(comment, node, addComment, ctxts);
|
||||
};
|
||||
|
||||
// Todo: add contexts to check after (and handle if want both before and after)
|
||||
return {
|
||||
...getContextObject(
|
||||
enforcedContexts(context, true, settings),
|
||||
checkNonJsdoc,
|
||||
),
|
||||
...getContextObject(
|
||||
contextsAfter,
|
||||
(_info, _handler, node) => {
|
||||
checkNonJsdocAfter(node, contextsAfter);
|
||||
},
|
||||
),
|
||||
...getContextObject(
|
||||
contextsBeforeAndAfter,
|
||||
(_info, _handler, node) => {
|
||||
checkNonJsdoc({}, null, node);
|
||||
if (!reportingNonJsdoc) {
|
||||
checkNonJsdocAfter(node, contextsBeforeAndAfter);
|
||||
}
|
||||
},
|
||||
),
|
||||
};
|
||||
},
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Converts non-JSDoc comments preceding or following nodes into JSDoc ones',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/convert-to-jsdoc-comments.md#repos-sticky-header',
|
||||
},
|
||||
|
||||
fixable: 'code',
|
||||
|
||||
messages: {
|
||||
blockCommentsJsdocStyle: 'Block comments should be JSDoc-style.',
|
||||
lineCommentsJsdocStyle: 'Line comments should be JSDoc-style.',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowedPrefixes: {
|
||||
description: `An array of prefixes to allow at the beginning of a comment.
|
||||
|
||||
Defaults to \`['@ts-', 'istanbul ', 'c8 ', 'v8 ', 'eslint', 'prettier-']\`.
|
||||
|
||||
Supplying your own value overrides the defaults.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
contexts: {
|
||||
description: `The contexts array which will be checked for preceding content.
|
||||
|
||||
Can either be strings or an object with a \`context\` string and an optional, default \`false\` \`inlineCommentBlock\` boolean.
|
||||
|
||||
Defaults to \`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`, \`TSDeclareFunction\`.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
inlineCommentBlock: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
contextsAfter: {
|
||||
description: `The contexts array which will be checked for content on the same line after.
|
||||
|
||||
Can either be strings or an object with a \`context\` string and an optional, default \`false\` \`inlineCommentBlock\` boolean.
|
||||
|
||||
Defaults to an empty array.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
inlineCommentBlock: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
contextsBeforeAndAfter: {
|
||||
description: `The contexts array which will be checked for content before and on the same
|
||||
line after.
|
||||
|
||||
Can either be strings or an object with a \`context\` string and an optional, default \`false\` \`inlineCommentBlock\` boolean.
|
||||
|
||||
Defaults to \`VariableDeclarator\`, \`TSPropertySignature\`, \`PropertyDefinition\`.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
inlineCommentBlock: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
enableFixer: {
|
||||
description: 'Set to `false` to disable fixing.',
|
||||
type: 'boolean',
|
||||
},
|
||||
enforceJsdocLineStyle: {
|
||||
description: `What policy to enforce on the conversion of non-JSDoc comments without
|
||||
line breaks. (Non-JSDoc (mulitline) comments with line breaks will always
|
||||
be converted to \`multi\` style JSDoc comments.)
|
||||
|
||||
- \`multi\` - Convert to multi-line style
|
||||
\`\`\`js
|
||||
/**
|
||||
* Some text
|
||||
*/
|
||||
\`\`\`
|
||||
- \`single\` - Convert to single-line style
|
||||
\`\`\`js
|
||||
/** Some text */
|
||||
\`\`\`
|
||||
|
||||
Defaults to \`multi\`.`,
|
||||
enum: [
|
||||
'multi', 'single',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
lineOrBlockStyle: {
|
||||
description: `What style of comments to which to apply JSDoc conversion.
|
||||
|
||||
- \`block\` - Applies to block-style comments (\`/* ... */\`)
|
||||
- \`line\` - Applies to line-style comments (\`// ...\`)
|
||||
- \`both\` - Applies to both block and line-style comments
|
||||
|
||||
Defaults to \`both\`.`,
|
||||
enum: [
|
||||
'block', 'line', 'both',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
};
|
||||
106
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/emptyTags.js
generated
vendored
Normal file
106
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/emptyTags.js
generated
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
const defaultEmptyTags = new Set([
|
||||
'abstract', 'async', 'generator', 'global', 'hideconstructor',
|
||||
|
||||
// jsdoc doesn't use this form in its docs, but allow for compatibility with
|
||||
// TypeScript which allows and Closure which requires
|
||||
'ignore',
|
||||
|
||||
// jsdoc doesn't use but allow for TypeScript
|
||||
'inheritDoc', 'inner', 'instance',
|
||||
'internal',
|
||||
|
||||
'overload',
|
||||
|
||||
'override',
|
||||
'readonly',
|
||||
]);
|
||||
|
||||
const emptyIfNotClosure = new Set([
|
||||
// Closure doesn't allow with this casing
|
||||
'inheritdoc', 'package', 'private', 'protected', 'public',
|
||||
|
||||
'static',
|
||||
]);
|
||||
|
||||
const emptyIfClosure = new Set([
|
||||
'interface',
|
||||
]);
|
||||
|
||||
export default iterateJsdoc(({
|
||||
jsdoc,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
const emptyTags = utils.filterTags(({
|
||||
tag: tagName,
|
||||
}) => {
|
||||
return defaultEmptyTags.has(tagName) ||
|
||||
utils.hasOptionTag(tagName) && jsdoc.tags.some(({
|
||||
tag,
|
||||
}) => {
|
||||
return tag === tagName;
|
||||
}) ||
|
||||
settings.mode === 'closure' && emptyIfClosure.has(tagName) ||
|
||||
settings.mode !== 'closure' && emptyIfNotClosure.has(tagName);
|
||||
});
|
||||
|
||||
for (const [
|
||||
key,
|
||||
tag,
|
||||
] of emptyTags.entries()) {
|
||||
const content = tag.name || tag.description || tag.type;
|
||||
if (content.trim() && (
|
||||
// Allow for JSDoc-block final asterisks
|
||||
key !== emptyTags.length - 1 || !(/^\s*\*+$/v).test(content)
|
||||
)) {
|
||||
const fix = () => {
|
||||
// By time of call in fixer, `tag` will have `line` added
|
||||
utils.setTag(
|
||||
/**
|
||||
* @type {import('comment-parser').Spec & {
|
||||
* line: import('../iterateJsdoc.js').Integer
|
||||
* }}
|
||||
*/ (tag),
|
||||
);
|
||||
};
|
||||
|
||||
utils.reportJSDoc(`@${tag.tag} should be empty.`, tag, fix, true);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
checkInternal: true,
|
||||
checkPrivate: true,
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Checks tags that are expected to be empty (e.g., `@abstract` or `@async`), reporting if they have content',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/empty-tags.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
tags: {
|
||||
description: `If you want additional tags to be checked for their descriptions, you may
|
||||
add them within this option.
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
'jsdoc/empty-tags': ['error', {tags: ['event']}]
|
||||
}
|
||||
\`\`\``,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
189
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/escapeInlineTags.js
generated
vendored
Normal file
189
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/escapeInlineTags.js
generated
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
mode,
|
||||
} = settings;
|
||||
|
||||
if (mode !== 'typescript') {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
allowedInlineTags = [],
|
||||
enableFixer = false,
|
||||
fixType = 'backslash',
|
||||
} = context.options[0] || {};
|
||||
|
||||
const {
|
||||
description,
|
||||
} = utils.getDescription();
|
||||
|
||||
/** @type {string[]} */
|
||||
const tagNames = [];
|
||||
|
||||
/** @type {number[]} */
|
||||
const indexes = [];
|
||||
|
||||
const unescapedInlineTagRegex = /(?:^|\s)@(\w+)/gv;
|
||||
for (const [
|
||||
idx,
|
||||
descLine,
|
||||
] of (
|
||||
description.startsWith('\n') ? description.slice(1) : description
|
||||
).split('\n').entries()
|
||||
) {
|
||||
descLine.replaceAll(unescapedInlineTagRegex, (_, tagName) => {
|
||||
if (allowedInlineTags.includes(tagName)) {
|
||||
return _;
|
||||
}
|
||||
|
||||
tagNames.push(tagName);
|
||||
indexes.push(idx);
|
||||
|
||||
return _;
|
||||
});
|
||||
}
|
||||
|
||||
for (const [
|
||||
idx,
|
||||
tagName,
|
||||
] of tagNames.entries()) {
|
||||
utils.reportJSDoc(
|
||||
`Unexpected inline JSDoc tag. Did you mean to use {@${tagName}}, \\@${tagName}, or \`@${tagName}\`?`,
|
||||
{
|
||||
line: indexes[idx] + 1,
|
||||
},
|
||||
enableFixer ?
|
||||
() => {
|
||||
utils.setBlockDescription((info, seedTokens, descLines) => {
|
||||
return descLines.map((desc) => {
|
||||
const newDesc = desc.replaceAll(
|
||||
new RegExp(`(^|\\s)@${
|
||||
// No need to escape, as contains only safe characters
|
||||
tagName
|
||||
}`, 'gv'),
|
||||
fixType === 'backticks' ? '$1`@' + tagName + '`' : '$1\\@' + tagName,
|
||||
);
|
||||
|
||||
return {
|
||||
number: 0,
|
||||
source: '',
|
||||
tokens: seedTokens({
|
||||
...info,
|
||||
description: newDesc,
|
||||
postDelimiter: newDesc.trim() ? ' ' : '',
|
||||
}),
|
||||
};
|
||||
});
|
||||
});
|
||||
} :
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} tagName
|
||||
* @returns {[
|
||||
* RegExp,
|
||||
* (description: string) => string
|
||||
* ]}
|
||||
*/
|
||||
const escapeInlineTags = (tagName) => {
|
||||
const regex = new RegExp(`(^|\\s)@${
|
||||
// No need to escape, as contains only safe characters
|
||||
tagName
|
||||
}`, 'gv');
|
||||
|
||||
return [
|
||||
regex,
|
||||
/**
|
||||
* @param {string} desc
|
||||
*/
|
||||
(desc) => {
|
||||
return desc.replaceAll(
|
||||
regex,
|
||||
fixType === 'backticks' ? '$1`@' + tagName + '`' : '$1\\@' + tagName,
|
||||
);
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
for (const tag of jsdoc.tags) {
|
||||
if (tag.tag === 'example') {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @type {string} */
|
||||
let tagName = '';
|
||||
while (/** @type {string[]} */ (
|
||||
utils.getTagDescription(tag, true)
|
||||
// eslint-disable-next-line no-loop-func -- Safe
|
||||
).some((desc) => {
|
||||
tagName = unescapedInlineTagRegex.exec(desc)?.[1] ?? '';
|
||||
if (allowedInlineTags.includes(tagName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return tagName;
|
||||
})) {
|
||||
const line = utils.setTagDescription(tag, ...escapeInlineTags(tagName)) +
|
||||
tag.source[0].number;
|
||||
utils.reportJSDoc(
|
||||
`Unexpected inline JSDoc tag. Did you mean to use {@${tagName}}, \\@${tagName}, or \`@${tagName}\`?`,
|
||||
{
|
||||
line,
|
||||
},
|
||||
enableFixer ? () => {} : null,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Reports use of JSDoc tags in non-tag positions (in the default "typescript" mode).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/escape-inline-tags.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowedInlineTags: {
|
||||
description: 'A listing of tags you wish to allow unescaped. Defaults to an empty array.',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
enableFixer: {
|
||||
description: 'Whether to enable the fixer. Defaults to `false`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
fixType: {
|
||||
description: `How to escape the inline tag.
|
||||
|
||||
May be "backticks" to enclose tags in backticks (treating as code segments), or
|
||||
"backslash" to escape tags with a backslash, i.e., \`\\@\`
|
||||
|
||||
Defaults to "backslash".`,
|
||||
enum: [
|
||||
'backticks',
|
||||
'backslash',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
78
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/implementsOnClasses.js
generated
vendored
Normal file
78
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/implementsOnClasses.js
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
const iteratingFunction = utils.isIteratingFunctionOrVariable();
|
||||
|
||||
if (iteratingFunction) {
|
||||
if (utils.hasATag([
|
||||
'class',
|
||||
'constructor',
|
||||
]) ||
|
||||
utils.isConstructor()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
} else if (!utils.isVirtualFunction()) {
|
||||
return;
|
||||
}
|
||||
|
||||
utils.forEachPreferredTag('implements', (tag) => {
|
||||
report('@implements used on a non-constructor function', null, tag);
|
||||
});
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Prohibits use of `@implements` on non-constructor functions (to enforce the tag only being used on classes/constructors).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/implements-on-classes.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
\`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
132
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/importsAsDependencies.js
generated
vendored
Normal file
132
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/importsAsDependencies.js
generated
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
parse,
|
||||
traverse,
|
||||
tryParse,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
import * as resolve from '@es-joy/resolve.exports';
|
||||
import {
|
||||
readFileSync,
|
||||
} from 'node:fs';
|
||||
import {
|
||||
isBuiltin as isBuiltinModule,
|
||||
} from 'node:module';
|
||||
import {
|
||||
join,
|
||||
} from 'node:path';
|
||||
|
||||
/**
|
||||
* @type {Set<string>|null}
|
||||
*/
|
||||
let deps;
|
||||
|
||||
const setDeps = function () {
|
||||
try {
|
||||
const pkg = JSON.parse(
|
||||
readFileSync(join(process.cwd(), './package.json'), 'utf8'),
|
||||
);
|
||||
deps = new Set([
|
||||
...(pkg.dependencies ?
|
||||
/* c8 ignore next 2 */
|
||||
Object.keys(pkg.dependencies) :
|
||||
[]),
|
||||
...(pkg.devDependencies ?
|
||||
/* c8 ignore next 2 */
|
||||
Object.keys(pkg.devDependencies) :
|
||||
[]),
|
||||
]);
|
||||
/* c8 ignore next -- our package.json exists */
|
||||
} catch (error) {
|
||||
/* c8 ignore next -- our package.json exists */
|
||||
deps = null;
|
||||
/* c8 ignore next 4 -- our package.json exists */
|
||||
/* eslint-disable no-console -- Inform user */
|
||||
console.log(error);
|
||||
/* eslint-enable no-console -- Inform user */
|
||||
}
|
||||
};
|
||||
|
||||
const moduleCheck = new Map();
|
||||
|
||||
export default iterateJsdoc(({
|
||||
jsdoc,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
if (deps === undefined) {
|
||||
setDeps();
|
||||
}
|
||||
|
||||
/* c8 ignore next 3 -- our package.json exists */
|
||||
if (deps === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
mode,
|
||||
} = settings;
|
||||
|
||||
for (const tag of jsdoc.tags) {
|
||||
let typeAst;
|
||||
try {
|
||||
typeAst = mode === 'permissive' ? tryParse(tag.type) : parse(tag.type, mode);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-loop-func -- Safe
|
||||
traverse(typeAst, (nde) => {
|
||||
/* c8 ignore next 3 -- TS guard */
|
||||
if (deps === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nde.type === 'JsdocTypeImport') {
|
||||
let mod = nde.element.value.replace(
|
||||
/^(@[^\/]+\/[^\/]+|[^\/]+).*$/v, '$1',
|
||||
);
|
||||
|
||||
if ((/^[.\/]/v).test(mod)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBuiltinModule(mod)) {
|
||||
// mod = '@types/node';
|
||||
// moduleCheck.set(mod, !deps.has(mod));
|
||||
return;
|
||||
} else if (!moduleCheck.has(mod)) {
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(
|
||||
readFileSync(join(process.cwd(), 'node_modules', mod, './package.json'), 'utf8'),
|
||||
);
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
if (!pkg || (!pkg.types && !pkg.typings && !resolve.types(pkg))) {
|
||||
mod = `@types/${mod}`;
|
||||
}
|
||||
|
||||
moduleCheck.set(mod, !deps.has(mod));
|
||||
}
|
||||
|
||||
if (moduleCheck.get(mod)) {
|
||||
utils.reportJSDoc(
|
||||
'import points to package which is not found in dependencies',
|
||||
tag,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Reports if JSDoc `import()` statements point to a package which is not listed in `dependencies` or `devDependencies`',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/imports-as-dependencies.md#repos-sticky-header',
|
||||
},
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
228
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/informativeDocs.js
generated
vendored
Normal file
228
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/informativeDocs.js
generated
vendored
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
areDocsInformative,
|
||||
} from 'are-docs-informative';
|
||||
|
||||
const defaultAliases = {
|
||||
a: [
|
||||
'an', 'our',
|
||||
],
|
||||
};
|
||||
|
||||
const defaultUselessWords = [
|
||||
'a', 'an', 'i', 'in', 'of', 's', 'the',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node|null|undefined} node
|
||||
* @returns {string[]}
|
||||
*/
|
||||
const getNamesFromNode = (node) => {
|
||||
switch (node?.type) {
|
||||
case 'AccessorProperty':
|
||||
case 'MethodDefinition':
|
||||
case 'PropertyDefinition':
|
||||
case 'TSAbstractAccessorProperty':
|
||||
case 'TSAbstractMethodDefinition':
|
||||
case 'TSAbstractPropertyDefinition':
|
||||
return [
|
||||
...getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */ (
|
||||
node.parent
|
||||
).parent,
|
||||
),
|
||||
...getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */
|
||||
(node.key),
|
||||
),
|
||||
];
|
||||
|
||||
case 'ClassDeclaration':
|
||||
case 'ClassExpression':
|
||||
case 'FunctionDeclaration':
|
||||
case 'FunctionExpression':
|
||||
case 'TSDeclareFunction':
|
||||
case 'TSEnumDeclaration':
|
||||
case 'TSEnumMember':
|
||||
case 'TSInterfaceDeclaration':
|
||||
case 'TSMethodSignature':
|
||||
case 'TSModuleDeclaration':
|
||||
case 'TSTypeAliasDeclaration':
|
||||
return getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.ClassDeclaration} */
|
||||
(node).id,
|
||||
);
|
||||
case 'ExportDefaultDeclaration':
|
||||
case 'ExportNamedDeclaration':
|
||||
return getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.ExportNamedDeclaration} */
|
||||
(node).declaration,
|
||||
);
|
||||
case 'Identifier':
|
||||
return [
|
||||
node.name,
|
||||
];
|
||||
case 'Property':
|
||||
return getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */
|
||||
(node.key),
|
||||
);
|
||||
case 'VariableDeclaration':
|
||||
return getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */
|
||||
(node.declarations[0]),
|
||||
);
|
||||
case 'VariableDeclarator':
|
||||
return [
|
||||
...getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */
|
||||
(node.id),
|
||||
),
|
||||
...getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */
|
||||
(node.init),
|
||||
),
|
||||
].filter(Boolean);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
node,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
const /** @type {{aliases: {[key: string]: string[]}, excludedTags: string[], uselessWords: string[]}} */ {
|
||||
aliases = defaultAliases,
|
||||
excludedTags = [],
|
||||
uselessWords = defaultUselessWords,
|
||||
} = context.options[0] || {};
|
||||
const nodeNames = getNamesFromNode(node);
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {string} extraName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const descriptionIsRedundant = (text, extraName = '') => {
|
||||
const textTrimmed = text.trim();
|
||||
return Boolean(textTrimmed) && !areDocsInformative(textTrimmed, [
|
||||
extraName, nodeNames,
|
||||
].filter(Boolean).join(' '), {
|
||||
aliases,
|
||||
uselessWords,
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
description,
|
||||
lastDescriptionLine,
|
||||
} = utils.getDescription();
|
||||
let descriptionReported = false;
|
||||
|
||||
for (const tag of jsdoc.tags) {
|
||||
if (excludedTags.includes(tag.tag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (descriptionIsRedundant(tag.description, tag.name)) {
|
||||
utils.reportJSDoc(
|
||||
'This tag description only repeats the name it describes.',
|
||||
tag,
|
||||
);
|
||||
}
|
||||
|
||||
descriptionReported ||= tag.description === description &&
|
||||
/** @type {import('comment-parser').Spec & {line: import('../iterateJsdoc.js').Integer}} */
|
||||
(tag).line === lastDescriptionLine;
|
||||
}
|
||||
|
||||
if (!descriptionReported && descriptionIsRedundant(description)) {
|
||||
report('This description only repeats the name it describes.');
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description:
|
||||
'This rule reports doc comments that only restate their attached name.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
aliases: {
|
||||
description: `The \`aliases\` option allows indicating words as synonyms (aliases) of each other.
|
||||
|
||||
For example, with \`{ aliases: { emoji: ["smiley", "winkey"] } }\`, the following comment would be considered uninformative:
|
||||
|
||||
\`\`\`js
|
||||
/** A smiley/winkey. */
|
||||
let emoji;
|
||||
\`\`\`
|
||||
|
||||
The default \`aliases\` option is:
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"a": ["an", "our"]
|
||||
}
|
||||
\`\`\``,
|
||||
patternProperties: {
|
||||
'.*': {
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
},
|
||||
excludedTags: {
|
||||
description: `Tags that should not be checked for valid contents.
|
||||
|
||||
For example, with \`{ excludedTags: ["category"] }\`, the following comment would not be considered uninformative:
|
||||
|
||||
\`\`\`js
|
||||
/** @category Types */
|
||||
function computeTypes(node) {
|
||||
// ...
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
No tags are excluded by default.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
uselessWords: {
|
||||
description: `Words that are ignored when searching for one that adds meaning.
|
||||
|
||||
For example, with \`{ uselessWords: ["our"] }\`, the following comment would be considered uninformative:
|
||||
|
||||
\`\`\`js
|
||||
/** Our text. */
|
||||
let text;
|
||||
\`\`\`
|
||||
|
||||
The default \`uselessWords\` option is:
|
||||
|
||||
\`\`\`json
|
||||
["a", "an", "i", "in", "of", "s", "the"]
|
||||
\`\`\``,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
144
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/linesBeforeBlock.js
generated
vendored
Normal file
144
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/linesBeforeBlock.js
generated
vendored
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* Punctuators that begin a logical group should not require a line before it skipped. Specifically
|
||||
* `[` starts an array, `{` starts an object or block, `(` starts a grouping, and `=` starts a
|
||||
* declaration (like a variable or a type alias).
|
||||
*/
|
||||
const startPunctuators = new Set([
|
||||
'(', '=', '[', '{',
|
||||
]);
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdocNode,
|
||||
report,
|
||||
sourceCode,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
checkBlockStarts,
|
||||
excludedTags = [
|
||||
'type',
|
||||
],
|
||||
ignoreSameLine = true,
|
||||
ignoreSingleLines = true,
|
||||
lines = 1,
|
||||
} = context.options[0] || {};
|
||||
|
||||
if (utils.hasATag(excludedTags)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tokensBefore = sourceCode.getTokensBefore(jsdocNode, {
|
||||
includeComments: true,
|
||||
});
|
||||
const tokenBefore = tokensBefore.at(-1);
|
||||
if (
|
||||
!tokenBefore || (
|
||||
tokenBefore.type === 'Punctuator' &&
|
||||
!checkBlockStarts &&
|
||||
startPunctuators.has(tokenBefore.value)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tokenBefore.loc?.end?.line + lines >=
|
||||
/** @type {number} */
|
||||
(jsdocNode.loc?.start?.line)
|
||||
) {
|
||||
const startLine = jsdocNode.loc?.start?.line;
|
||||
const sameLine = tokenBefore.loc?.end?.line === startLine;
|
||||
|
||||
if (sameLine && ignoreSameLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignoreSingleLines && jsdocNode.loc?.start.line === jsdocNode.loc?.end.line) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {import('eslint').Rule.ReportFixer} */
|
||||
const fix = (fixer) => {
|
||||
let indent = '';
|
||||
if (sameLine) {
|
||||
const spaceDiff = /** @type {number} */ (jsdocNode.loc?.start?.column) -
|
||||
/** @type {number} */ (tokenBefore.loc?.end?.column);
|
||||
// @ts-expect-error Should be a comment
|
||||
indent = /** @type {import('estree').Comment} */ (
|
||||
jsdocNode
|
||||
).value.match(/^\*\n([\t ]*) \*/v)?.[1]?.slice(spaceDiff);
|
||||
if (!indent) {
|
||||
/** @type {import('eslint').AST.Token|import('estree').Comment|undefined} */
|
||||
let tokenPrior = tokenBefore;
|
||||
let startColumn;
|
||||
while (tokenPrior && tokenPrior?.loc?.start?.line === startLine) {
|
||||
startColumn = tokenPrior.loc?.start?.column;
|
||||
tokenPrior = tokensBefore.pop();
|
||||
}
|
||||
|
||||
indent = ' '.repeat(
|
||||
/* c8 ignore next */
|
||||
/** @type {number} */ (startColumn ? startColumn - 1 : 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return fixer.insertTextAfter(
|
||||
/** @type {import('eslint').AST.Token} */
|
||||
(tokenBefore),
|
||||
'\n'.repeat(lines) +
|
||||
(sameLine ? '\n' + indent : ''),
|
||||
);
|
||||
};
|
||||
|
||||
report(`Required ${lines} line(s) before JSDoc block`, fix);
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Enforces minimum number of newlines before JSDoc comment blocks',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/lines-before-block.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
checkBlockStarts: {
|
||||
description: `Whether to additionally check the start of blocks, such as classes or functions.
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
excludedTags: {
|
||||
description: `An array of tags whose presence in the JSDoc block will prevent the
|
||||
application of the rule. Defaults to \`['type']\` (i.e., if \`@type\` is present,
|
||||
lines before the block will not be added).`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
ignoreSameLine: {
|
||||
description: `This option excludes cases where the JSDoc block occurs on the same line as a
|
||||
preceding code or comment. Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
ignoreSingleLines: {
|
||||
description: `This option excludes cases where the JSDoc block is only one line long.
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
lines: {
|
||||
description: 'The minimum number of lines to require. Defaults to 1.',
|
||||
type: 'integer',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
413
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/matchDescription.js
generated
vendored
Normal file
413
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/matchDescription.js
generated
vendored
Normal file
|
|
@ -0,0 +1,413 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
// If supporting Node >= 10, we could loosen the default to this for the
|
||||
// initial letter: \\p{Upper}
|
||||
const matchDescriptionDefault = '^\n?([A-Z`\\d_][\\s\\S]*[.?!`\\p{RGI_Emoji}]\\s*)?$';
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @param {string} userDefault
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringOrDefault = (value, userDefault) => {
|
||||
return typeof value === 'string' ?
|
||||
value :
|
||||
userDefault || matchDescriptionDefault;
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
mainDescription,
|
||||
matchDescription,
|
||||
message,
|
||||
nonemptyTags = true,
|
||||
tags = {},
|
||||
} = context.options[0] || {};
|
||||
|
||||
/**
|
||||
* @param {string} desc
|
||||
* @param {import('comment-parser').Spec} [tag]
|
||||
* @returns {void}
|
||||
*/
|
||||
const validateDescription = (desc, tag) => {
|
||||
let mainDescriptionMatch = mainDescription;
|
||||
let errorMessage = message;
|
||||
if (typeof mainDescription === 'object') {
|
||||
mainDescriptionMatch = mainDescription.match;
|
||||
errorMessage = mainDescription.message;
|
||||
}
|
||||
|
||||
if (mainDescriptionMatch === false && (
|
||||
!tag || !Object.hasOwn(tags, tag.tag))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tagValue = mainDescriptionMatch;
|
||||
if (tag) {
|
||||
const tagName = tag.tag;
|
||||
if (typeof tags[tagName] === 'object') {
|
||||
tagValue = tags[tagName].match;
|
||||
errorMessage = tags[tagName].message;
|
||||
} else {
|
||||
tagValue = tags[tagName];
|
||||
}
|
||||
}
|
||||
|
||||
const regex = utils.getRegexFromString(
|
||||
stringOrDefault(tagValue, matchDescription),
|
||||
);
|
||||
|
||||
if (!regex.test(desc)) {
|
||||
report(
|
||||
errorMessage || 'JSDoc description does not satisfy the regex pattern.',
|
||||
null,
|
||||
tag || {
|
||||
// Add one as description would typically be into block
|
||||
line: jsdoc.source[0].number + 1,
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
description,
|
||||
} = utils.getDescription();
|
||||
if (description) {
|
||||
validateDescription(description);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} tagName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasNoTag = (tagName) => {
|
||||
return !tags[tagName];
|
||||
};
|
||||
|
||||
for (const tag of [
|
||||
'description',
|
||||
'summary',
|
||||
'file',
|
||||
'classdesc',
|
||||
]) {
|
||||
utils.forEachPreferredTag(tag, (matchingJsdocTag, targetTagName) => {
|
||||
const desc = (matchingJsdocTag.name + ' ' + utils.getTagDescription(matchingJsdocTag)).trim();
|
||||
if (hasNoTag(targetTagName)) {
|
||||
validateDescription(desc, matchingJsdocTag);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
if (nonemptyTags) {
|
||||
for (const tag of [
|
||||
'copyright',
|
||||
'example',
|
||||
'see',
|
||||
'todo',
|
||||
]) {
|
||||
utils.forEachPreferredTag(tag, (matchingJsdocTag, targetTagName) => {
|
||||
const desc = (matchingJsdocTag.name + ' ' + utils.getTagDescription(matchingJsdocTag)).trim();
|
||||
|
||||
if (hasNoTag(targetTagName) && !(/.+/v).test(desc)) {
|
||||
report(
|
||||
'JSDoc description must not be empty.',
|
||||
null,
|
||||
matchingJsdocTag,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!Object.keys(tags).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} tagName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasOptionTag = (tagName) => {
|
||||
return Boolean(tags[tagName]);
|
||||
};
|
||||
|
||||
const whitelistedTags = utils.filterTags(({
|
||||
tag: tagName,
|
||||
}) => {
|
||||
return hasOptionTag(tagName);
|
||||
});
|
||||
const {
|
||||
tagsWithNames,
|
||||
tagsWithoutNames,
|
||||
} = utils.getTagsByType(whitelistedTags);
|
||||
|
||||
tagsWithNames.some((tag) => {
|
||||
const desc = /** @type {string} */ (
|
||||
utils.getTagDescription(tag)
|
||||
).replace(/^[\- ]*/v, '')
|
||||
.trim();
|
||||
|
||||
return validateDescription(desc, tag);
|
||||
});
|
||||
|
||||
tagsWithoutNames.some((tag) => {
|
||||
const desc = (tag.name + ' ' + utils.getTagDescription(tag)).trim();
|
||||
|
||||
return validateDescription(desc, tag);
|
||||
});
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Enforces a regular expression pattern on descriptions.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/match-description.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied (e.g.,
|
||||
\`ClassDeclaration\` for ES6 classes).
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want the rule to apply to any
|
||||
JSDoc block throughout your files.
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
mainDescription: {
|
||||
description: `If you wish to override the main block description without changing the
|
||||
default \`match-description\` (which can cascade to the \`tags\` with \`true\`),
|
||||
you may use \`mainDescription\`:
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
'jsdoc/match-description': ['error', {
|
||||
mainDescription: '[A-Z].*\\\\.',
|
||||
tags: {
|
||||
param: true,
|
||||
returns: true
|
||||
}
|
||||
}]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
There is no need to add \`mainDescription: true\`, as by default, the main
|
||||
block description (and only the main block description) is linted, though you
|
||||
may disable checking it by setting it to \`false\`.
|
||||
|
||||
You may also provide an object with \`message\`:
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
'jsdoc/match-description': ['error', {
|
||||
mainDescription: {
|
||||
message: 'Capitalize first word of JSDoc block descriptions',
|
||||
match: '[A-Z].*\\\\.'
|
||||
},
|
||||
tags: {
|
||||
param: true,
|
||||
returns: true
|
||||
}
|
||||
}]
|
||||
}
|
||||
\`\`\``,
|
||||
oneOf: [
|
||||
{
|
||||
format: 'regex',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
match: {
|
||||
oneOf: [
|
||||
{
|
||||
format: 'regex',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
},
|
||||
],
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
matchDescription: {
|
||||
description: `You can supply your own expression to override the default, passing a
|
||||
\`matchDescription\` string on the options object.
|
||||
|
||||
Defaults to using (only) the \`v\` flag, so
|
||||
to add your own flags, encapsulate your expression as a string, but like a
|
||||
literal, e.g., \`/[A-Z].*\\./vi\`.
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
'jsdoc/match-description': ['error', {matchDescription: '[A-Z].*\\\\.'}]
|
||||
}
|
||||
\`\`\``,
|
||||
format: 'regex',
|
||||
type: 'string',
|
||||
},
|
||||
message: {
|
||||
description: `You may provide a custom default message by using the following format:
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
'jsdoc/match-description': ['error', {
|
||||
message: 'The default description should begin with a capital letter.'
|
||||
}]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
This can be overridden per tag or for the main block description by setting
|
||||
\`message\` within \`tags\` or \`mainDescription\`, respectively.`,
|
||||
type: 'string',
|
||||
},
|
||||
nonemptyTags: {
|
||||
description: `If not set to \`false\`, will enforce that the following tags have at least
|
||||
some content:
|
||||
|
||||
- \`@copyright\`
|
||||
- \`@example\`
|
||||
- \`@see\`
|
||||
- \`@todo\`
|
||||
|
||||
If you supply your own tag description for any of the above tags in \`tags\`,
|
||||
your description will take precedence.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
tags: {
|
||||
description: `If you want different regular expressions to apply to tags, you may use
|
||||
the \`tags\` option object:
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
'jsdoc/match-description': ['error', {tags: {
|
||||
param: '\\\\- [A-Z].*\\\\.',
|
||||
returns: '[A-Z].*\\\\.'
|
||||
}}]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
In place of a string, you can also add \`true\` to indicate that a particular
|
||||
tag should be linted with the \`matchDescription\` value (or the default).
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
'jsdoc/match-description': ['error', {tags: {
|
||||
param: true,
|
||||
returns: true
|
||||
}}]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
Alternatively, you may supply an object with a \`message\` property to indicate
|
||||
the error message for that tag.
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
'jsdoc/match-description': ['error', {tags: {
|
||||
param: {message: 'Begin with a hyphen', match: '\\\\- [A-Z].*\\\\.'},
|
||||
returns: {message: 'Capitalize for returns (the default)', match: true}
|
||||
}}]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
The tags \`@param\`/\`@arg\`/\`@argument\` and \`@property\`/\`@prop\` will be properly
|
||||
parsed to ensure that the matched "description" text includes only the text
|
||||
after the name.
|
||||
|
||||
All other tags will treat the text following the tag name, a space, and
|
||||
an optional curly-bracketed type expression (and another space) as part of
|
||||
its "description" (e.g., for \`@returns {someType} some description\`, the
|
||||
description is \`some description\` while for \`@some-tag xyz\`, the description
|
||||
is \`xyz\`).`,
|
||||
patternProperties: {
|
||||
'.*': {
|
||||
oneOf: [
|
||||
{
|
||||
format: 'regex',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
enum: [
|
||||
true,
|
||||
],
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
match: {
|
||||
oneOf: [
|
||||
{
|
||||
format: 'regex',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
enum: [
|
||||
true,
|
||||
],
|
||||
type: 'boolean',
|
||||
},
|
||||
],
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
179
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/matchName.js
generated
vendored
Normal file
179
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/matchName.js
generated
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
info: {
|
||||
lastIndex,
|
||||
},
|
||||
jsdoc,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
match,
|
||||
} = context.options[0] || {};
|
||||
if (!match) {
|
||||
report('Rule `no-restricted-syntax` is missing a `match` option.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
allowName,
|
||||
disallowName,
|
||||
replacement,
|
||||
tags = [
|
||||
'*',
|
||||
],
|
||||
} = match[/** @type {import('../iterateJsdoc.js').Integer} */ (lastIndex)];
|
||||
|
||||
const allowNameRegex = allowName && utils.getRegexFromString(allowName);
|
||||
const disallowNameRegex = disallowName && utils.getRegexFromString(disallowName);
|
||||
|
||||
let applicableTags = jsdoc.tags;
|
||||
if (!tags.includes('*')) {
|
||||
applicableTags = utils.getPresentTags(tags);
|
||||
}
|
||||
|
||||
let reported = false;
|
||||
for (const tag of applicableTags) {
|
||||
const tagName = tag.name.replace(/^\[/v, '').replace(/(=.*)?\]$/v, '');
|
||||
const allowed = !allowNameRegex || allowNameRegex.test(tagName);
|
||||
const disallowed = disallowNameRegex && disallowNameRegex.test(tagName);
|
||||
const hasRegex = allowNameRegex || disallowNameRegex;
|
||||
if (hasRegex && allowed && !disallowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hasRegex && reported) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fixer = () => {
|
||||
for (const src of tag.source) {
|
||||
if (src.tokens.name) {
|
||||
src.tokens.name = src.tokens.name.replace(
|
||||
disallowNameRegex, replacement,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let {
|
||||
message,
|
||||
} = match[/** @type {import('../iterateJsdoc.js').Integer} */ (lastIndex)];
|
||||
if (!message) {
|
||||
if (hasRegex) {
|
||||
message = disallowed ?
|
||||
`Only allowing names not matching \`${disallowNameRegex}\` but found "${tagName}".` :
|
||||
`Only allowing names matching \`${allowNameRegex}\` but found "${tagName}".`;
|
||||
} else {
|
||||
message = `Prohibited context for "${tagName}".`;
|
||||
}
|
||||
}
|
||||
|
||||
utils.reportJSDoc(
|
||||
message,
|
||||
hasRegex ? tag : null,
|
||||
|
||||
// We could match up
|
||||
disallowNameRegex && replacement !== undefined ?
|
||||
fixer :
|
||||
null,
|
||||
false,
|
||||
{
|
||||
// Could also supply `context`, `comment`, `tags`
|
||||
allowName,
|
||||
disallowName,
|
||||
name: tagName,
|
||||
},
|
||||
);
|
||||
if (!hasRegex) {
|
||||
reported = true;
|
||||
}
|
||||
}
|
||||
}, {
|
||||
matchContext: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Reports the name portion of a JSDoc tag if matching or not matching a given regular expression.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/match-name.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
match: {
|
||||
description: `\`match\` is a required option containing an array of objects which determine
|
||||
the conditions whereby a name is reported as being problematic.
|
||||
|
||||
These objects can have any combination of the following groups of optional
|
||||
properties, all of which act to confine one another.
|
||||
|
||||
Note that \`comment\`, even if targeting a specific tag, is used to match the
|
||||
whole block. So if a \`comment\` finds its specific tag, it may still apply
|
||||
fixes found by the likes of \`disallowName\` even when a different tag has the
|
||||
disallowed name. An alternative is to ensure that \`comment\` finds the specific
|
||||
tag of the desired tag and/or name and no \`disallowName\` (or \`allowName\`) is
|
||||
supplied. In such a case, only one error will be reported, but no fixer will
|
||||
be applied, however.`,
|
||||
items: {
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowName: {
|
||||
description: `Indicates which names are allowed for the given tag (or \`*\`).
|
||||
Accepts a string regular expression (optionally wrapped between two
|
||||
\`/\` delimiters followed by optional flags) used to match the name.`,
|
||||
type: 'string',
|
||||
},
|
||||
comment: {
|
||||
description: 'As with `context` but AST for the JSDoc block comment and types.',
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
description: `AST to confine the allowing or disallowing to JSDoc blocks
|
||||
associated with a particular context. See the
|
||||
["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
type: 'string',
|
||||
},
|
||||
disallowName: {
|
||||
description: 'As with `allowName` but indicates names that are not allowed.',
|
||||
type: 'string',
|
||||
},
|
||||
message: {
|
||||
description: 'An optional custom message to use when there is a match.',
|
||||
type: 'string',
|
||||
},
|
||||
replacement: {
|
||||
description: `If \`disallowName\` is supplied and this value is present, it
|
||||
will replace the matched \`disallowName\` text.`,
|
||||
type: 'string',
|
||||
},
|
||||
tags: {
|
||||
description: `This array should include tag names or \`*\` to indicate the
|
||||
match will apply for all tags (except as confined by any context
|
||||
properties). If \`*\` is not used, then these rules will only apply to
|
||||
the specified tags. If \`tags\` is omitted, then \`*\` is assumed.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
required: [
|
||||
'match',
|
||||
],
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
562
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/multilineBlocks.js
generated
vendored
Normal file
562
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/multilineBlocks.js
generated
vendored
Normal file
|
|
@ -0,0 +1,562 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @param {import('@es-joy/jsdoccomment').JsdocBlockWithInline} jsdoc
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* @param {number} requireSingleLineUnderCount
|
||||
*/
|
||||
const checkForShortTags = (jsdoc, utils, requireSingleLineUnderCount) => {
|
||||
if (!requireSingleLineUnderCount || !jsdoc.tags.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let lastLineWithTag = 0;
|
||||
let isUnderCountLimit = false;
|
||||
let hasMultiDescOrType = false;
|
||||
const tagLines = jsdoc.source.reduce((acc, {
|
||||
tokens: {
|
||||
delimiter,
|
||||
description: desc,
|
||||
name,
|
||||
postDelimiter,
|
||||
postName,
|
||||
postTag,
|
||||
postType,
|
||||
start,
|
||||
tag,
|
||||
type,
|
||||
},
|
||||
}, idx) => {
|
||||
if (tag.length) {
|
||||
lastLineWithTag = idx;
|
||||
if (
|
||||
start.length + delimiter.length + postDelimiter.length +
|
||||
type.length + postType.length + name.length + postName.length +
|
||||
tag.length + postTag.length + desc.length <
|
||||
requireSingleLineUnderCount
|
||||
) {
|
||||
isUnderCountLimit = true;
|
||||
}
|
||||
|
||||
return acc + 1;
|
||||
} else if (desc.length || type.length) {
|
||||
hasMultiDescOrType = true;
|
||||
return acc;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, 0);
|
||||
// Could be tagLines > 1
|
||||
if (!hasMultiDescOrType && isUnderCountLimit && tagLines === 1) {
|
||||
const fixer = () => {
|
||||
const tokens = jsdoc.source[lastLineWithTag].tokens;
|
||||
jsdoc.source = [
|
||||
{
|
||||
number: 0,
|
||||
source: '',
|
||||
tokens: utils.seedTokens({
|
||||
delimiter: '/**',
|
||||
description: tokens.description.trimEnd() + ' ',
|
||||
end: '*/',
|
||||
name: tokens.name,
|
||||
postDelimiter: ' ',
|
||||
postName: tokens.postName,
|
||||
postTag: tokens.postTag,
|
||||
postType: tokens.postType,
|
||||
start: jsdoc.source[0].tokens.start,
|
||||
tag: tokens.tag,
|
||||
type: tokens.type,
|
||||
}),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Description is too short to be multi-line.',
|
||||
null,
|
||||
fixer,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('@es-joy/jsdoccomment').JsdocBlockWithInline} jsdoc
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* @param {number} requireSingleLineUnderCount
|
||||
*/
|
||||
const checkForShortDescriptions = (jsdoc, utils, requireSingleLineUnderCount) => {
|
||||
if (!requireSingleLineUnderCount || jsdoc.tags.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let lastLineWithDesc = 0;
|
||||
let isUnderCountLimit = false;
|
||||
const descLines = jsdoc.source.reduce((acc, {
|
||||
tokens: {
|
||||
delimiter,
|
||||
description: desc,
|
||||
postDelimiter,
|
||||
start,
|
||||
},
|
||||
}, idx) => {
|
||||
if (desc.length) {
|
||||
lastLineWithDesc = idx;
|
||||
if (
|
||||
start.length + delimiter.length + postDelimiter.length + desc.length <
|
||||
requireSingleLineUnderCount
|
||||
) {
|
||||
isUnderCountLimit = true;
|
||||
}
|
||||
|
||||
return acc + 1;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, 0);
|
||||
// Could be descLines > 1
|
||||
if (isUnderCountLimit && descLines === 1) {
|
||||
const fixer = () => {
|
||||
const desc = jsdoc.source[lastLineWithDesc].tokens.description;
|
||||
jsdoc.source = [
|
||||
{
|
||||
number: 0,
|
||||
source: '',
|
||||
tokens: utils.seedTokens({
|
||||
delimiter: '/**',
|
||||
description: desc.trimEnd() + ' ',
|
||||
end: '*/',
|
||||
postDelimiter: ' ',
|
||||
start: jsdoc.source[0].tokens.start,
|
||||
}),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Description is too short to be multi-line.',
|
||||
null,
|
||||
fixer,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
allowMultipleTags = true,
|
||||
minimumLengthForMultiline = Number.POSITIVE_INFINITY,
|
||||
multilineTags = [
|
||||
'*',
|
||||
],
|
||||
noFinalLineText = true,
|
||||
noMultilineBlocks = false,
|
||||
noSingleLineBlocks = false,
|
||||
noZeroLineText = true,
|
||||
requireSingleLineUnderCount = null,
|
||||
singleLineTags = [
|
||||
'lends', 'type',
|
||||
],
|
||||
} = context.options[0] || {};
|
||||
|
||||
const {
|
||||
source: [
|
||||
{
|
||||
tokens,
|
||||
},
|
||||
],
|
||||
} = jsdoc;
|
||||
const {
|
||||
description,
|
||||
tag,
|
||||
} = tokens;
|
||||
const sourceLength = jsdoc.source.length;
|
||||
|
||||
/**
|
||||
* @param {string} tagName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isInvalidSingleLine = (tagName) => {
|
||||
return noSingleLineBlocks &&
|
||||
(!tagName ||
|
||||
!singleLineTags.includes(tagName) && !singleLineTags.includes('*'));
|
||||
};
|
||||
|
||||
if (sourceLength === 1) {
|
||||
if (!isInvalidSingleLine(tag.slice(1))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fixer = () => {
|
||||
utils.makeMultiline();
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Single line blocks are not permitted by your configuration.',
|
||||
null,
|
||||
fixer,
|
||||
true,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkForShortDescriptions(jsdoc, utils, requireSingleLineUnderCount)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkForShortTags(jsdoc, utils, requireSingleLineUnderCount)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lineChecks = () => {
|
||||
if (
|
||||
noZeroLineText &&
|
||||
(tag || description)
|
||||
) {
|
||||
const fixer = () => {
|
||||
const line = {
|
||||
...tokens,
|
||||
};
|
||||
utils.emptyTokens(tokens);
|
||||
const {
|
||||
tokens: {
|
||||
delimiter,
|
||||
start,
|
||||
},
|
||||
} = jsdoc.source[1];
|
||||
utils.addLine(1, {
|
||||
...line,
|
||||
delimiter,
|
||||
start,
|
||||
});
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Should have no text on the "0th" line (after the `/**`).',
|
||||
null,
|
||||
fixer,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const finalLine = jsdoc.source[jsdoc.source.length - 1];
|
||||
const finalLineTokens = finalLine.tokens;
|
||||
if (
|
||||
noFinalLineText &&
|
||||
finalLineTokens.description.trim()
|
||||
) {
|
||||
const fixer = () => {
|
||||
const line = {
|
||||
...finalLineTokens,
|
||||
};
|
||||
line.description = line.description.trimEnd();
|
||||
|
||||
const {
|
||||
delimiter,
|
||||
} = line;
|
||||
|
||||
for (const prop of [
|
||||
'delimiter',
|
||||
'postDelimiter',
|
||||
'tag',
|
||||
'type',
|
||||
'lineEnd',
|
||||
'postType',
|
||||
'postTag',
|
||||
'name',
|
||||
'postName',
|
||||
'description',
|
||||
]) {
|
||||
finalLineTokens[
|
||||
/**
|
||||
* @type {"delimiter"|"postDelimiter"|"tag"|"type"|
|
||||
* "lineEnd"|"postType"|"postTag"|"name"|
|
||||
* "postName"|"description"}
|
||||
*/ (
|
||||
prop
|
||||
)
|
||||
] = '';
|
||||
}
|
||||
|
||||
utils.addLine(jsdoc.source.length - 1, {
|
||||
...line,
|
||||
delimiter,
|
||||
end: '',
|
||||
});
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Should have no text on the final line (before the `*/`).',
|
||||
null,
|
||||
fixer,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (noMultilineBlocks) {
|
||||
if (
|
||||
jsdoc.tags.length &&
|
||||
(multilineTags.includes('*') || utils.hasATag(multilineTags))
|
||||
) {
|
||||
lineChecks();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsdoc.description.length >= minimumLengthForMultiline) {
|
||||
lineChecks();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
noSingleLineBlocks &&
|
||||
(!jsdoc.tags.length ||
|
||||
!utils.filterTags(({
|
||||
tag: tg,
|
||||
}) => {
|
||||
return !isInvalidSingleLine(tg);
|
||||
}).length)
|
||||
) {
|
||||
utils.reportJSDoc(
|
||||
'Multiline JSDoc blocks are prohibited by ' +
|
||||
'your configuration but fixing would result in a single ' +
|
||||
'line block which you have prohibited with `noSingleLineBlocks`.',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsdoc.tags.length > 1) {
|
||||
if (!allowMultipleTags) {
|
||||
utils.reportJSDoc(
|
||||
'Multiline JSDoc blocks are prohibited by ' +
|
||||
'your configuration but the block has multiple tags.',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
} else if (jsdoc.tags.length === 1 && jsdoc.description.trim()) {
|
||||
if (!allowMultipleTags) {
|
||||
utils.reportJSDoc(
|
||||
'Multiline JSDoc blocks are prohibited by ' +
|
||||
'your configuration but the block has a description with a tag.',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const fixer = () => {
|
||||
jsdoc.source = [
|
||||
{
|
||||
number: 1,
|
||||
source: '',
|
||||
tokens: jsdoc.source.reduce((obj, {
|
||||
tokens: {
|
||||
description: desc,
|
||||
lineEnd,
|
||||
name: nme,
|
||||
postName,
|
||||
postTag,
|
||||
postType,
|
||||
tag: tg,
|
||||
type: typ,
|
||||
},
|
||||
}) => {
|
||||
if (typ) {
|
||||
obj.type = typ;
|
||||
}
|
||||
|
||||
if (tg && typ && nme) {
|
||||
obj.postType = postType;
|
||||
}
|
||||
|
||||
if (nme) {
|
||||
obj.name += nme;
|
||||
}
|
||||
|
||||
if (nme && desc) {
|
||||
obj.postName = postName;
|
||||
}
|
||||
|
||||
obj.description += desc;
|
||||
|
||||
const nameOrDescription = obj.description || obj.name;
|
||||
if (
|
||||
nameOrDescription && nameOrDescription.slice(-1) !== ' '
|
||||
) {
|
||||
obj.description += ' ';
|
||||
}
|
||||
|
||||
obj.lineEnd = lineEnd;
|
||||
|
||||
// Already filtered for multiple tags
|
||||
obj.tag += tg;
|
||||
if (tg) {
|
||||
obj.postTag = postTag || ' ';
|
||||
}
|
||||
|
||||
return obj;
|
||||
}, utils.seedTokens({
|
||||
delimiter: '/**',
|
||||
end: '*/',
|
||||
postDelimiter: ' ',
|
||||
})),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Multiline JSDoc blocks are prohibited by ' +
|
||||
'your configuration.',
|
||||
null,
|
||||
fixer,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lineChecks();
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Controls how and whether JSDoc blocks can be expressed as single or multiple line blocks.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/multiline-blocks.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowMultipleTags: {
|
||||
description: `If \`noMultilineBlocks\` is set to \`true\` with this option and multiple tags are
|
||||
found in a block, an error will not be reported.
|
||||
|
||||
Since multiple-tagged lines cannot be collapsed into a single line, this option
|
||||
prevents them from being reported. Set to \`false\` if you really want to report
|
||||
any blocks.
|
||||
|
||||
This option will also be applied when there is a block description and a single
|
||||
tag (since a description cannot precede a tag on a single line, and also
|
||||
cannot be reliably added after the tag either).
|
||||
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
minimumLengthForMultiline: {
|
||||
description: `If \`noMultilineBlocks\` is set with this numeric option, multiline blocks will
|
||||
be permitted if containing at least the given amount of text.
|
||||
|
||||
If not set, multiline blocks will not be permitted regardless of length unless
|
||||
a relevant tag is present and \`multilineTags\` is set.
|
||||
|
||||
Defaults to not being in effect.`,
|
||||
type: 'integer',
|
||||
},
|
||||
multilineTags: {
|
||||
anyOf: [
|
||||
{
|
||||
enum: [
|
||||
'*',
|
||||
],
|
||||
type: 'string',
|
||||
}, {
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
],
|
||||
description: `If \`noMultilineBlocks\` is set with this option, multiline blocks may be allowed
|
||||
regardless of length as long as a tag or a tag of a certain type is present.
|
||||
|
||||
If \`*\` is included in the array, the presence of a tags will allow for
|
||||
multiline blocks (but not when without any tags unless the amount of text is
|
||||
over an amount specified by \`minimumLengthForMultiline\`).
|
||||
|
||||
If the array does not include \`*\` but lists certain tags, the presence of
|
||||
such a tag will cause multiline blocks to be allowed.
|
||||
|
||||
You may set this to an empty array to prevent any tag from permitting multiple
|
||||
lines.
|
||||
|
||||
Defaults to \`['*']\`.`,
|
||||
},
|
||||
noFinalLineText: {
|
||||
description: `For multiline blocks, any non-whitespace text preceding the \`*/\` on the final
|
||||
line will be reported. (Text preceding a newline is not reported.)
|
||||
|
||||
\`noMultilineBlocks\` will have priority over this rule if it applies.
|
||||
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
noMultilineBlocks: {
|
||||
description: `Requires that JSDoc blocks are restricted to single lines only unless impacted
|
||||
by the options \`minimumLengthForMultiline\`, \`multilineTags\`, or
|
||||
\`allowMultipleTags\`.
|
||||
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
noSingleLineBlocks: {
|
||||
description: `If this is \`true\`, any single line blocks will be reported, except those which
|
||||
are whitelisted in \`singleLineTags\`.
|
||||
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
noZeroLineText: {
|
||||
description: `For multiline blocks, any non-whitespace text immediately after the \`/**\` and
|
||||
space will be reported. (Text after a newline is not reported.)
|
||||
|
||||
\`noMultilineBlocks\` will have priority over this rule if it applies.
|
||||
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
requireSingleLineUnderCount: {
|
||||
description: `If this number is set, it indicates a minimum line width for a single line of
|
||||
JSDoc content spread over a multi-line comment block. If a single line is under
|
||||
the minimum length, it will be reported so as to enforce single line JSDoc blocks
|
||||
for such cases. Blocks are not reported which have multi-line descriptions,
|
||||
multiple tags, a block description and tag, or tags with multi-line types or
|
||||
descriptions.
|
||||
|
||||
Defaults to \`null\`.`,
|
||||
type: 'number',
|
||||
},
|
||||
singleLineTags: {
|
||||
description: `An array of tags which can nevertheless be allowed as single line blocks when
|
||||
\`noSingleLineBlocks\` is set. You may set this to a empty array to
|
||||
cause all single line blocks to be reported. If \`'*'\` is present, then
|
||||
the presence of a tag will allow single line blocks (but not if a tag is
|
||||
missing).
|
||||
|
||||
Defaults to \`['lends', 'type']\`.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
127
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noBadBlocks.js
generated
vendored
Normal file
127
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noBadBlocks.js
generated
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
parse as commentParser,
|
||||
} from 'comment-parser';
|
||||
|
||||
// Neither a single nor 3+ asterisks are valid JSDoc per
|
||||
// https://jsdoc.app/about-getting-started.html#adding-documentation-comments-to-your-code
|
||||
const commentRegexp = /^\/\*(?!\*)/v;
|
||||
const extraAsteriskCommentRegexp = /^\/\*{3,}/v;
|
||||
|
||||
export default iterateJsdoc(({
|
||||
allComments,
|
||||
context,
|
||||
makeReport,
|
||||
sourceCode,
|
||||
}) => {
|
||||
const [
|
||||
{
|
||||
ignore = [
|
||||
'ts-check',
|
||||
'ts-expect-error',
|
||||
'ts-ignore',
|
||||
'ts-nocheck',
|
||||
],
|
||||
preventAllMultiAsteriskBlocks = false,
|
||||
} = {},
|
||||
] = context.options;
|
||||
|
||||
let extraAsterisks = false;
|
||||
const nonJsdocNodes = /** @type {import('estree').Node[]} */ (
|
||||
allComments
|
||||
).filter((comment) => {
|
||||
const commentText = sourceCode.getText(comment);
|
||||
|
||||
const initialText = commentText.replace(commentRegexp, '').trimStart();
|
||||
if ([
|
||||
'eslint',
|
||||
].some((directive) => {
|
||||
return initialText.startsWith(directive);
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let sliceIndex = 2;
|
||||
if (!commentRegexp.test(commentText)) {
|
||||
const multiline = extraAsteriskCommentRegexp.exec(commentText)?.[0];
|
||||
if (!multiline) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sliceIndex = multiline.length;
|
||||
extraAsterisks = true;
|
||||
if (preventAllMultiAsteriskBlocks) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const tags = (commentParser(
|
||||
`${commentText.slice(0, 2)}*${commentText.slice(sliceIndex)}`,
|
||||
)[0] || {}).tags ?? [];
|
||||
|
||||
return tags.length && !tags.some(({
|
||||
tag,
|
||||
}) => {
|
||||
return ignore.includes(tag);
|
||||
});
|
||||
});
|
||||
|
||||
if (!nonJsdocNodes.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const node of nonJsdocNodes) {
|
||||
const report = /** @type {import('../iterateJsdoc.js').MakeReport} */ (
|
||||
makeReport
|
||||
)(context, node);
|
||||
|
||||
// eslint-disable-next-line no-loop-func
|
||||
const fix = /** @type {import('eslint').Rule.ReportFixer} */ (fixer) => {
|
||||
const text = sourceCode.getText(node);
|
||||
|
||||
return fixer.replaceText(
|
||||
node,
|
||||
extraAsterisks ?
|
||||
text.replace(extraAsteriskCommentRegexp, '/**') :
|
||||
text.replace('/*', '/**'),
|
||||
);
|
||||
};
|
||||
|
||||
report('Expected JSDoc-like comment to begin with two asterisks.', fix);
|
||||
}
|
||||
}, {
|
||||
checkFile: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'This rule checks for multi-line-style comments which fail to meet the criteria of a JSDoc block.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-bad-blocks.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
ignore: {
|
||||
description: `An array of directives that will not be reported if present at the beginning of
|
||||
a multi-comment block and at-sign \`/* @\`.
|
||||
|
||||
Defaults to \`['ts-check', 'ts-expect-error', 'ts-ignore', 'ts-nocheck']\`
|
||||
(some directives [used by TypeScript](https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html#ts-check)).`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
preventAllMultiAsteriskBlocks: {
|
||||
description: `A boolean (defaulting to \`false\`) which if \`true\` will prevent all
|
||||
JSDoc-like blocks with more than two initial asterisks even those without
|
||||
apparent tag content.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'layout',
|
||||
},
|
||||
});
|
||||
69
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noBlankBlockDescriptions.js
generated
vendored
Normal file
69
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noBlankBlockDescriptions.js
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
const anyWhitespaceLines = /^\s*$/v;
|
||||
const atLeastTwoLinesWhitespace = /^[ \t]*\n[ \t]*\n\s*$/v;
|
||||
|
||||
export default iterateJsdoc(({
|
||||
jsdoc,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
description,
|
||||
descriptions,
|
||||
lastDescriptionLine,
|
||||
} = utils.getDescription();
|
||||
|
||||
const regex = jsdoc.tags.length ?
|
||||
anyWhitespaceLines :
|
||||
atLeastTwoLinesWhitespace;
|
||||
|
||||
if (descriptions.length && regex.test(description)) {
|
||||
if (jsdoc.tags.length) {
|
||||
utils.reportJSDoc(
|
||||
'There should be no blank lines in block descriptions followed by tags.',
|
||||
{
|
||||
line: lastDescriptionLine,
|
||||
},
|
||||
() => {
|
||||
utils.setBlockDescription(() => {
|
||||
// Remove all lines
|
||||
return [];
|
||||
});
|
||||
},
|
||||
);
|
||||
} else {
|
||||
utils.reportJSDoc(
|
||||
'There should be no extra blank lines in block descriptions not followed by tags.',
|
||||
{
|
||||
line: lastDescriptionLine,
|
||||
},
|
||||
() => {
|
||||
utils.setBlockDescription((info, seedTokens) => {
|
||||
return [
|
||||
// Keep the starting line
|
||||
{
|
||||
number: 0,
|
||||
source: '',
|
||||
tokens: seedTokens({
|
||||
...info,
|
||||
description: '',
|
||||
}),
|
||||
},
|
||||
];
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'If tags are present, this rule will prevent empty lines in the block description. If no tags are present, this rule will prevent extra empty lines in the block description.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-blank-block-descriptions.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'whitespace',
|
||||
schema: [],
|
||||
type: 'layout',
|
||||
},
|
||||
});
|
||||
55
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noBlankBlocks.js
generated
vendored
Normal file
55
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noBlankBlocks.js
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
utils,
|
||||
}) => {
|
||||
if (jsdoc.tags.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
description,
|
||||
lastDescriptionLine,
|
||||
} = utils.getDescription();
|
||||
if (description.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
enableFixer,
|
||||
} = context.options[0] || {};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'No empty blocks',
|
||||
{
|
||||
line: lastDescriptionLine,
|
||||
},
|
||||
enableFixer ? () => {
|
||||
jsdoc.source.splice(0);
|
||||
} : null,
|
||||
);
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Removes empty blocks with nothing but possibly line breaks',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-blank-blocks.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
enableFixer: {
|
||||
description: 'Whether or not to auto-remove the blank block. Defaults to `false`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
104
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noDefaults.js
generated
vendored
Normal file
104
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noDefaults.js
generated
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
noOptionalParamNames,
|
||||
} = context.options[0] || {};
|
||||
const paramTags = utils.getPresentTags([
|
||||
'param', 'arg', 'argument',
|
||||
]);
|
||||
for (const tag of paramTags) {
|
||||
if (noOptionalParamNames && tag.optional) {
|
||||
utils.reportJSDoc(`Optional param names are not permitted on @${tag.tag}.`, tag, () => {
|
||||
utils.changeTag(tag, {
|
||||
name: tag.name.replace(/([^=]*)(=.+)?/v, '$1'),
|
||||
});
|
||||
});
|
||||
} else if (tag.default) {
|
||||
utils.reportJSDoc(`Defaults are not permitted on @${tag.tag}.`, tag, () => {
|
||||
utils.changeTag(tag, {
|
||||
name: tag.name.replace(/([^=]*)(=.+)?/v, '[$1]'),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const defaultTags = utils.getPresentTags([
|
||||
'default', 'defaultvalue',
|
||||
]);
|
||||
for (const tag of defaultTags) {
|
||||
if (tag.description.trim()) {
|
||||
utils.reportJSDoc(`Default values are not permitted on @${tag.tag}.`, tag, () => {
|
||||
utils.changeTag(tag, {
|
||||
description: '',
|
||||
postTag: '',
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'This rule reports defaults being used on the relevant portion of `@param` or `@default`.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-defaults.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
noOptionalParamNames: {
|
||||
description: `Set this to \`true\` to report the presence of optional parameters. May be
|
||||
used if the project is insisting on optionality being indicated by
|
||||
the presence of ES6 default parameters (bearing in mind that such
|
||||
"defaults" are only applied when the supplied value is missing or
|
||||
\`undefined\` but not for \`null\` or other "falsey" values).`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
215
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noMissingSyntax.js
generated
vendored
Normal file
215
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noMissingSyntax.js
generated
vendored
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* comment: string,
|
||||
* context: string,
|
||||
* message: string,
|
||||
* minimum: import('../iterateJsdoc.js').Integer
|
||||
* }} ContextObject
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {string|ContextObject} Context
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {import('../iterateJsdoc.js').StateObject} state
|
||||
* @returns {void}
|
||||
*/
|
||||
const setDefaults = (state) => {
|
||||
if (!state.selectorMap) {
|
||||
state.selectorMap = {};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('../iterateJsdoc.js').StateObject} state
|
||||
* @param {string} selector
|
||||
* @param {string} comment
|
||||
* @returns {void}
|
||||
*/
|
||||
const incrementSelector = (state, selector, comment) => {
|
||||
if (!state.selectorMap[selector]) {
|
||||
state.selectorMap[selector] = {};
|
||||
}
|
||||
|
||||
if (!state.selectorMap[selector][comment]) {
|
||||
state.selectorMap[selector][comment] = 0;
|
||||
}
|
||||
|
||||
state.selectorMap[selector][comment]++;
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
info: {
|
||||
comment,
|
||||
},
|
||||
state,
|
||||
utils,
|
||||
}) => {
|
||||
if (!context.options[0]) {
|
||||
// Handle error later
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Context[]}
|
||||
*/
|
||||
const contexts = context.options[0].contexts;
|
||||
|
||||
const {
|
||||
contextStr,
|
||||
} = utils.findContext(contexts, comment);
|
||||
|
||||
setDefaults(state);
|
||||
|
||||
incrementSelector(state, contextStr, String(comment));
|
||||
}, {
|
||||
contextSelected: true,
|
||||
exit ({
|
||||
context,
|
||||
settings,
|
||||
state,
|
||||
}) {
|
||||
if (!context.options.length && !settings.contexts) {
|
||||
context.report({
|
||||
loc: {
|
||||
end: {
|
||||
column: 1,
|
||||
line: 1,
|
||||
},
|
||||
start: {
|
||||
column: 1,
|
||||
line: 1,
|
||||
},
|
||||
},
|
||||
message: 'Rule `no-missing-syntax` is missing a `contexts` option.',
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setDefaults(state);
|
||||
|
||||
/**
|
||||
* @type {Context[]}
|
||||
*/
|
||||
const contexts = (context.options[0] ?? {}).contexts ?? settings?.contexts;
|
||||
|
||||
// Report when MISSING
|
||||
contexts.some((cntxt) => {
|
||||
const contextStr = typeof cntxt === 'object' ? cntxt.context ?? 'any' : cntxt;
|
||||
const comment = typeof cntxt === 'string' ? '' : cntxt?.comment ?? '';
|
||||
|
||||
const contextKey = contextStr === 'any' ? 'undefined' : contextStr;
|
||||
|
||||
if (
|
||||
(!state.selectorMap[contextKey] ||
|
||||
!state.selectorMap[contextKey][comment] ||
|
||||
state.selectorMap[contextKey][comment] < (
|
||||
// @ts-expect-error comment would need an object, not string
|
||||
cntxt?.minimum ?? 1
|
||||
)) &&
|
||||
(contextStr !== 'any' || Object.values(state.selectorMap).every((cmmnt) => {
|
||||
return !cmmnt[comment] || cmmnt[comment] < (
|
||||
// @ts-expect-error comment would need an object, not string
|
||||
cntxt?.minimum ?? 1
|
||||
);
|
||||
}))
|
||||
) {
|
||||
const message = typeof cntxt === 'string' ?
|
||||
'Syntax is required: {{context}}' :
|
||||
cntxt?.message ?? ('Syntax is required: {{context}}' +
|
||||
(comment ? ' with {{comment}}' : ''));
|
||||
context.report({
|
||||
data: {
|
||||
comment,
|
||||
context: contextStr,
|
||||
},
|
||||
loc: {
|
||||
end: {
|
||||
column: 1,
|
||||
line: 1,
|
||||
},
|
||||
start: {
|
||||
column: 1,
|
||||
line: 1,
|
||||
},
|
||||
},
|
||||
message,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
},
|
||||
matchContext: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Reports when certain comment structures are always expected.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-missing-syntax.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Use the \`minimum\` property (defaults to 1) to indicate how many are required
|
||||
for the rule to be reported.
|
||||
|
||||
Use the \`message\` property to indicate the specific error to be shown when an
|
||||
error is reported for that context being found missing. You may use
|
||||
\`{{context}}\` and \`{{comment}}\` with such messages. Defaults to
|
||||
\`"Syntax is required: {{context}}"\`, or with a comment, to
|
||||
\`"Syntax is required: {{context}} with {{comment}}"\`.
|
||||
|
||||
Set to \`"any"\` if you want the rule to apply to any JSDoc block throughout
|
||||
your files (as is necessary for finding function blocks not attached to a
|
||||
function declaration or expression, i.e., \`@callback\` or \`@function\` (or its
|
||||
aliases \`@func\` or \`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
},
|
||||
minimum: {
|
||||
type: 'integer',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
162
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noMultiAsterisks.js
generated
vendored
Normal file
162
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noMultiAsterisks.js
generated
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
const middleAsterisksBlockWS = /^([\t ]|\*(?!\*))+/v;
|
||||
const middleAsterisksNoBlockWS = /^\*+/v;
|
||||
|
||||
const endAsterisksSingleLineBlockWS = /\*((?:\*|(?: |\t))*)\*$/v;
|
||||
const endAsterisksMultipleLineBlockWS = /((?:\*|(?: |\t))*)\*$/v;
|
||||
|
||||
const endAsterisksSingleLineNoBlockWS = /\*(\**)\*$/v;
|
||||
const endAsterisksMultipleLineNoBlockWS = /(\**)\*$/v;
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
allowWhitespace = false,
|
||||
preventAtEnd = true,
|
||||
preventAtMiddleLines = true,
|
||||
} = context.options[0] || {};
|
||||
|
||||
const middleAsterisks = allowWhitespace ? middleAsterisksNoBlockWS : middleAsterisksBlockWS;
|
||||
|
||||
jsdoc.source.some(({
|
||||
number,
|
||||
tokens,
|
||||
}) => {
|
||||
const {
|
||||
delimiter,
|
||||
description,
|
||||
end,
|
||||
name,
|
||||
postDelimiter,
|
||||
tag,
|
||||
type,
|
||||
} = tokens;
|
||||
|
||||
if (
|
||||
preventAtMiddleLines &&
|
||||
!end && !tag && !type && !name &&
|
||||
(
|
||||
!allowWhitespace && middleAsterisks.test(description) ||
|
||||
allowWhitespace && middleAsterisks.test(postDelimiter + description)
|
||||
)
|
||||
) {
|
||||
// console.log('description', JSON.stringify(description));
|
||||
const fix = () => {
|
||||
tokens.description = description.replace(middleAsterisks, '');
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Should be no multiple asterisks on middle lines.',
|
||||
{
|
||||
line: number,
|
||||
},
|
||||
fix,
|
||||
true,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!preventAtEnd || !end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isSingleLineBlock = delimiter === '/**';
|
||||
const delim = isSingleLineBlock ? '*' : delimiter;
|
||||
const endAsterisks = allowWhitespace ?
|
||||
(isSingleLineBlock ? endAsterisksSingleLineNoBlockWS : endAsterisksMultipleLineNoBlockWS) :
|
||||
(isSingleLineBlock ? endAsterisksSingleLineBlockWS : endAsterisksMultipleLineBlockWS);
|
||||
|
||||
const endingAsterisksAndSpaces = (
|
||||
allowWhitespace ? postDelimiter + description + delim : description + delim
|
||||
).match(
|
||||
endAsterisks,
|
||||
);
|
||||
|
||||
if (
|
||||
!endingAsterisksAndSpaces ||
|
||||
!isSingleLineBlock && endingAsterisksAndSpaces[1] && !endingAsterisksAndSpaces[1].trim()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const endFix = () => {
|
||||
if (!isSingleLineBlock) {
|
||||
tokens.delimiter = '';
|
||||
}
|
||||
|
||||
tokens.description = (description + delim).replace(endAsterisks, '');
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Should be no multiple asterisks on end lines.',
|
||||
{
|
||||
line: number,
|
||||
},
|
||||
endFix,
|
||||
true,
|
||||
);
|
||||
|
||||
return true;
|
||||
});
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Prevents use of multiple asterisks at the beginning of lines.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-multi-asterisks.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowWhitespace: {
|
||||
description: `Set to \`true\` if you wish to allow asterisks after a space (as with Markdown):
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* *bold* text
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
preventAtEnd: {
|
||||
description: `Prevent the likes of this:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
*
|
||||
*
|
||||
**/
|
||||
\`\`\`
|
||||
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
preventAtMiddleLines: {
|
||||
description: `Prevent the likes of this:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
*
|
||||
**
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
72
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noRestrictedSyntax.js
generated
vendored
Normal file
72
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noRestrictedSyntax.js
generated
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import {
|
||||
buildForbidRuleDefinition,
|
||||
} from '../buildForbidRuleDefinition.js';
|
||||
|
||||
export default buildForbidRuleDefinition({
|
||||
getContexts (context, report) {
|
||||
if (!context.options.length) {
|
||||
report('Rule `no-restricted-syntax` is missing a `contexts` option.');
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
contexts,
|
||||
} = context.options[0];
|
||||
|
||||
return contexts;
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
\`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Use the \`message\` property to indicate the specific error to be shown when an
|
||||
error is reported for that context being found. Defaults to
|
||||
\`"Syntax is restricted: {{context}}"\`, or with a comment, to
|
||||
\`"Syntax is restricted: {{context}} with {{comment}}"\`.
|
||||
|
||||
Set to \`"any"\` if you want the rule to apply to any JSDoc block throughout
|
||||
your files (as is necessary for finding function blocks not attached to a
|
||||
function declaration or expression, i.e., \`@callback\` or \`@function\` (or its
|
||||
aliases \`@func\` or \`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
required: [
|
||||
'contexts',
|
||||
],
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-restricted-syntax.md#repos-sticky-header',
|
||||
});
|
||||
108
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noTypes.js
generated
vendored
Normal file
108
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noTypes.js
generated
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @param {import('comment-parser').Line} line
|
||||
*/
|
||||
const removeType = ({
|
||||
tokens,
|
||||
}) => {
|
||||
tokens.postTag = '';
|
||||
tokens.type = '';
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
node,
|
||||
utils,
|
||||
}) => {
|
||||
if (!utils.isIteratingFunctionOrVariable() && !utils.isVirtualFunction()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tags = utils.getPresentTags([
|
||||
'param', 'arg', 'argument', 'returns', 'return',
|
||||
]);
|
||||
|
||||
for (const tag of tags) {
|
||||
if (tag.type) {
|
||||
utils.reportJSDoc(`Types are not permitted on @${tag.tag}.`, tag, () => {
|
||||
for (const source of tag.source) {
|
||||
removeType(source);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (node?.type === 'ClassDeclaration') {
|
||||
const propertyTags = utils.getPresentTags([
|
||||
'prop', 'property',
|
||||
]);
|
||||
for (const tag of propertyTags) {
|
||||
if (tag.type) {
|
||||
utils.reportJSDoc(`Types are not permitted on @${tag.tag} in the supplied context.`, tag, () => {
|
||||
for (const source of tag.source) {
|
||||
removeType(source);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
contextDefaults: [
|
||||
'ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression', 'TSDeclareFunction',
|
||||
// Add this to above defaults
|
||||
'TSMethodSignature', 'ClassDeclaration',
|
||||
],
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'This rule reports types being used on `@param` or `@returns` (redundant with TypeScript).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-types.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`, \`TSDeclareFunction\`, \`TSMethodSignature\`,
|
||||
\`ClassDeclaration\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
677
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noUndefinedTypes.js
generated
vendored
Normal file
677
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/noUndefinedTypes.js
generated
vendored
Normal file
|
|
@ -0,0 +1,677 @@
|
|||
import iterateJsdoc, {
|
||||
parseComment,
|
||||
} from '../iterateJsdoc.js';
|
||||
import {
|
||||
getJSDocComment,
|
||||
parse as parseType,
|
||||
traverse,
|
||||
tryParse as tryParseType,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
import {
|
||||
parseImportsExports,
|
||||
} from 'parse-imports-exports';
|
||||
|
||||
const extraTypes = [
|
||||
'null', 'undefined', 'void', 'string', 'boolean', 'object',
|
||||
'function', 'symbol',
|
||||
'number', 'bigint', 'NaN', 'Infinity',
|
||||
'any', '*', 'never', 'unknown', 'const',
|
||||
'this', 'true', 'false',
|
||||
'Array', 'Object', 'RegExp', 'Date', 'Function', 'Intl',
|
||||
];
|
||||
|
||||
const globalTypes = [
|
||||
'globalThis', 'global', 'window', 'self',
|
||||
];
|
||||
|
||||
const typescriptGlobals = [
|
||||
// https://www.typescriptlang.org/docs/handbook/utility-types.html
|
||||
'Awaited',
|
||||
'Partial',
|
||||
'Required',
|
||||
'Readonly',
|
||||
'Record',
|
||||
'Pick',
|
||||
'Omit',
|
||||
'Exclude',
|
||||
'Extract',
|
||||
'NonNullable',
|
||||
'Parameters',
|
||||
'ConstructorParameters',
|
||||
'ReturnType',
|
||||
'InstanceType',
|
||||
'ThisParameterType',
|
||||
'OmitThisParameter',
|
||||
'ThisType',
|
||||
'Uppercase',
|
||||
'Lowercase',
|
||||
'Capitalize',
|
||||
'Uncapitalize',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {string|false|undefined} [str]
|
||||
* @returns {undefined|string|false}
|
||||
*/
|
||||
const stripPseudoTypes = (str) => {
|
||||
return str && str.replace(/(?:\.|<>|\.<>|\[\])$/v, '');
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
node,
|
||||
report,
|
||||
settings,
|
||||
sourceCode,
|
||||
state,
|
||||
utils,
|
||||
}) => {
|
||||
/** @type {string[]} */
|
||||
const foundTypedefValues = [];
|
||||
|
||||
const {
|
||||
scopeManager,
|
||||
} = sourceCode;
|
||||
|
||||
// When is this ever `null`?
|
||||
const globalScope = /** @type {import('eslint').Scope.Scope} */ (
|
||||
scopeManager.globalScope
|
||||
);
|
||||
|
||||
const
|
||||
/**
|
||||
* @type {{
|
||||
* checkUsedTypedefs: boolean
|
||||
* definedTypes: string[],
|
||||
* disableReporting: boolean,
|
||||
* markVariablesAsUsed: boolean,
|
||||
* }}
|
||||
*/ {
|
||||
checkUsedTypedefs = false,
|
||||
definedTypes = [],
|
||||
disableReporting = false,
|
||||
markVariablesAsUsed = true,
|
||||
} = context.options[0] || {};
|
||||
|
||||
/** @type {(string|undefined)[]} */
|
||||
let definedPreferredTypes = [];
|
||||
const {
|
||||
mode,
|
||||
preferredTypes,
|
||||
structuredTags,
|
||||
} = settings;
|
||||
if (Object.keys(preferredTypes).length) {
|
||||
definedPreferredTypes = /** @type {string[]} */ (Object.values(preferredTypes).map((preferredType) => {
|
||||
if (typeof preferredType === 'string') {
|
||||
// May become an empty string but will be filtered out below
|
||||
return stripPseudoTypes(preferredType);
|
||||
}
|
||||
|
||||
if (!preferredType) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof preferredType !== 'object') {
|
||||
utils.reportSettings(
|
||||
'Invalid `settings.jsdoc.preferredTypes`. Values must be falsy, a string, or an object.',
|
||||
);
|
||||
}
|
||||
|
||||
return stripPseudoTypes(preferredType.replacement);
|
||||
})
|
||||
.filter(Boolean));
|
||||
}
|
||||
|
||||
const allComments = sourceCode.getAllComments();
|
||||
const comments = allComments
|
||||
.filter((comment) => {
|
||||
return (/^\*(?!\*)/v).test(comment.value);
|
||||
})
|
||||
.map((commentNode) => {
|
||||
return parseComment(commentNode, '');
|
||||
});
|
||||
|
||||
const globals = allComments
|
||||
.filter((comment) => {
|
||||
return (/^\s*globals/v).test(comment.value);
|
||||
}).flatMap((commentNode) => {
|
||||
return commentNode.value.replace(/^\s*globals/v, '').trim().split(/,\s*/v);
|
||||
}).concat(Object.keys(context.languageOptions.globals ?? []));
|
||||
|
||||
const typedefs = comments
|
||||
.flatMap((doc) => {
|
||||
return doc.tags.filter(({
|
||||
tag,
|
||||
}) => {
|
||||
return utils.isNameOrNamepathDefiningTag(tag) && ![
|
||||
'arg',
|
||||
'argument',
|
||||
'param',
|
||||
'prop',
|
||||
'property',
|
||||
].includes(tag);
|
||||
});
|
||||
});
|
||||
|
||||
const typedefDeclarations = typedefs
|
||||
.map((tag) => {
|
||||
return tag.name;
|
||||
});
|
||||
|
||||
const importTags = settings.mode === 'typescript' ? /** @type {string[]} */ (comments.flatMap((doc) => {
|
||||
return doc.tags.filter(({
|
||||
tag,
|
||||
}) => {
|
||||
return tag === 'import';
|
||||
});
|
||||
}).flatMap((tag) => {
|
||||
const {
|
||||
description,
|
||||
name,
|
||||
type,
|
||||
} = tag;
|
||||
const typePart = type ? `{${type}} ` : '';
|
||||
const imprt = 'import ' + (description ?
|
||||
`${typePart}${name} ${description}` :
|
||||
`${typePart}${name}`);
|
||||
|
||||
const importsExports = parseImportsExports(imprt.trim());
|
||||
|
||||
const types = [];
|
||||
const namedImports = Object.values(importsExports.namedImports || {})[0]?.[0];
|
||||
if (namedImports) {
|
||||
if (namedImports.default) {
|
||||
types.push(namedImports.default);
|
||||
}
|
||||
|
||||
if (namedImports.names) {
|
||||
types.push(...Object.keys(namedImports.names));
|
||||
}
|
||||
}
|
||||
|
||||
const namespaceImports = Object.values(importsExports.namespaceImports || {})[0]?.[0];
|
||||
if (namespaceImports) {
|
||||
if (namespaceImports.namespace) {
|
||||
types.push(namespaceImports.namespace);
|
||||
}
|
||||
|
||||
if (namespaceImports.default) {
|
||||
types.push(namespaceImports.default);
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}).filter(Boolean)) : [];
|
||||
|
||||
const ancestorNodes = [];
|
||||
|
||||
let currentNode = node;
|
||||
// No need for Program node?
|
||||
while (currentNode?.parent) {
|
||||
ancestorNodes.push(currentNode);
|
||||
currentNode = currentNode.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.Node} ancestorNode
|
||||
* @returns {import('comment-parser').Spec[]}
|
||||
*/
|
||||
const getTemplateTags = function (ancestorNode) {
|
||||
const commentNode = getJSDocComment(sourceCode, ancestorNode, settings);
|
||||
if (!commentNode) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const jsdc = parseComment(commentNode, '');
|
||||
|
||||
return jsdc.tags.filter((tag) => {
|
||||
return tag.tag === 'template';
|
||||
});
|
||||
};
|
||||
|
||||
// `currentScope` may be `null` or `Program`, so in such a case,
|
||||
// we look to present tags instead
|
||||
const templateTags = ancestorNodes.length ?
|
||||
ancestorNodes.flatMap((ancestorNode) => {
|
||||
return getTemplateTags(ancestorNode);
|
||||
}) :
|
||||
utils.getPresentTags([
|
||||
'template',
|
||||
]);
|
||||
|
||||
const closureGenericTypes = templateTags.flatMap((tag) => {
|
||||
return utils.parseClosureTemplateTag(tag);
|
||||
});
|
||||
|
||||
// In modules, including Node, there is a global scope at top with the
|
||||
// Program scope inside
|
||||
const cjsOrESMScope = globalScope.childScopes[0]?.block?.type === 'Program';
|
||||
|
||||
/**
|
||||
* @param {import("eslint").Scope.Scope | null} scope
|
||||
* @returns {Set<string>}
|
||||
*/
|
||||
const getValidRuntimeIdentifiers = (scope) => {
|
||||
const result = new Set();
|
||||
|
||||
let scp = scope;
|
||||
while (scp) {
|
||||
for (const {
|
||||
name,
|
||||
} of scp.variables) {
|
||||
result.add(name);
|
||||
}
|
||||
|
||||
scp = scp.upper;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* We treat imports differently as we can't introspect their children.
|
||||
* @type {string[]}
|
||||
*/
|
||||
const imports = [];
|
||||
|
||||
const allDefinedTypes = new Set(globalScope.variables.map(({
|
||||
name,
|
||||
}) => {
|
||||
return name;
|
||||
})
|
||||
|
||||
// If the file is a module, concat the variables from the module scope.
|
||||
.concat(
|
||||
cjsOrESMScope ?
|
||||
globalScope.childScopes.flatMap(({
|
||||
variables,
|
||||
}) => {
|
||||
return variables;
|
||||
}).flatMap(({
|
||||
identifiers,
|
||||
name,
|
||||
}) => {
|
||||
const globalItem = /** @type {import('estree').Identifier & {parent: import('@typescript-eslint/types').TSESTree.Node}} */ (
|
||||
identifiers?.[0]
|
||||
)?.parent;
|
||||
switch (globalItem?.type) {
|
||||
case 'ClassDeclaration':
|
||||
return [
|
||||
name,
|
||||
...globalItem.body.body.map((item) => {
|
||||
const property = /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.PropertyDefinition} */ (
|
||||
item)?.key)?.name;
|
||||
/* c8 ignore next 3 -- Guard */
|
||||
if (!property) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `${name}.${property}`;
|
||||
}).filter(Boolean),
|
||||
];
|
||||
case 'ImportDefaultSpecifier':
|
||||
case 'ImportNamespaceSpecifier':
|
||||
case 'ImportSpecifier':
|
||||
imports.push(name);
|
||||
break;
|
||||
case 'TSInterfaceDeclaration':
|
||||
return [
|
||||
name,
|
||||
...globalItem.body.body.map((item) => {
|
||||
const property = /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.TSPropertySignature} */ (
|
||||
item)?.key)?.name;
|
||||
/* c8 ignore next 3 -- Guard */
|
||||
if (!property) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `${name}.${property}`;
|
||||
}).filter(Boolean),
|
||||
];
|
||||
case 'VariableDeclarator':
|
||||
if (/** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.CallExpression} */ (
|
||||
globalItem?.init
|
||||
)?.callee)?.name === 'require'
|
||||
) {
|
||||
imports.push(/** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
globalItem.id
|
||||
).name);
|
||||
break;
|
||||
}
|
||||
|
||||
// Module scope names are also defined
|
||||
return [
|
||||
name,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
name,
|
||||
];
|
||||
/* c8 ignore next */
|
||||
}) : [],
|
||||
)
|
||||
.concat(extraTypes)
|
||||
.concat(typedefDeclarations)
|
||||
.concat(importTags)
|
||||
.concat(definedTypes)
|
||||
.concat(/** @type {string[]} */ (definedPreferredTypes))
|
||||
.concat((() => {
|
||||
// Other methods are not in scope, but we need them, and we grab them here
|
||||
if (node?.type === 'MethodDefinition') {
|
||||
return /** @type {import('estree').ClassBody} */ (node.parent).body.flatMap((methodOrProp) => {
|
||||
if (methodOrProp.type === 'MethodDefinition') {
|
||||
// eslint-disable-next-line unicorn/no-lonely-if -- Pattern
|
||||
if (methodOrProp.key.type === 'Identifier') {
|
||||
return [
|
||||
methodOrProp.key.name,
|
||||
`${/** @type {import('estree').ClassDeclaration} */ (
|
||||
node.parent?.parent
|
||||
)?.id?.name}.${methodOrProp.key.name}`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (methodOrProp.type === 'PropertyDefinition') {
|
||||
// eslint-disable-next-line unicorn/no-lonely-if -- Pattern
|
||||
if (methodOrProp.key.type === 'Identifier') {
|
||||
return [
|
||||
methodOrProp.key.name,
|
||||
`${/** @type {import('estree').ClassDeclaration} */ (
|
||||
node.parent?.parent
|
||||
)?.id?.name}.${methodOrProp.key.name}`,
|
||||
];
|
||||
}
|
||||
}
|
||||
/* c8 ignore next 2 -- Not yet built */
|
||||
|
||||
return '';
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
return [];
|
||||
})())
|
||||
.concat(...getValidRuntimeIdentifiers(node && (
|
||||
(sourceCode.getScope &&
|
||||
/* c8 ignore next 3 */
|
||||
sourceCode.getScope(node)) ||
|
||||
// @ts-expect-error ESLint 8
|
||||
context.getScope()
|
||||
)))
|
||||
.concat(
|
||||
settings.mode === 'jsdoc' ?
|
||||
[] :
|
||||
[
|
||||
...settings.mode === 'typescript' ? typescriptGlobals : [],
|
||||
...closureGenericTypes,
|
||||
],
|
||||
));
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* parsedType: import('jsdoc-type-pratt-parser').RootResult;
|
||||
* tag: import('comment-parser').Spec|import('@es-joy/jsdoccomment').JsdocInlineTagNoType & {
|
||||
* line?: import('../iterateJsdoc.js').Integer
|
||||
* }
|
||||
* }} TypeAndTagInfo
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} propertyName
|
||||
* @returns {(tag: (import('@es-joy/jsdoccomment').JsdocInlineTagNoType & {
|
||||
* name?: string,
|
||||
* type?: string,
|
||||
* line?: import('../iterateJsdoc.js').Integer
|
||||
* })|import('comment-parser').Spec & {
|
||||
* namepathOrURL?: string
|
||||
* }
|
||||
* ) => undefined|TypeAndTagInfo}
|
||||
*/
|
||||
const tagToParsedType = (propertyName) => {
|
||||
return (tag) => {
|
||||
try {
|
||||
const potentialType = tag[
|
||||
/** @type {"type"|"name"|"namepathOrURL"} */ (propertyName)
|
||||
];
|
||||
return {
|
||||
parsedType: mode === 'permissive' ?
|
||||
tryParseType(/** @type {string} */ (potentialType)) :
|
||||
parseType(/** @type {string} */ (potentialType), mode),
|
||||
tag,
|
||||
};
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const typeTags = utils.filterTags(({
|
||||
tag,
|
||||
}) => {
|
||||
return tag !== 'import' && utils.tagMightHaveTypePosition(tag) && (tag !== 'suppress' || settings.mode !== 'closure');
|
||||
}).map(tagToParsedType('type'));
|
||||
|
||||
const namepathReferencingTags = utils.filterTags(({
|
||||
tag,
|
||||
}) => {
|
||||
return utils.isNamepathReferencingTag(tag);
|
||||
}).map(tagToParsedType('name'));
|
||||
|
||||
const namepathOrUrlReferencingTags = utils.filterAllTags(({
|
||||
tag,
|
||||
}) => {
|
||||
return utils.isNamepathOrUrlReferencingTag(tag);
|
||||
}).map(tagToParsedType('namepathOrURL'));
|
||||
|
||||
const definedNamesAndNamepaths = new Set(utils.filterTags(({
|
||||
tag,
|
||||
}) => {
|
||||
return utils.isNameOrNamepathDefiningTag(tag);
|
||||
}).map(({
|
||||
name,
|
||||
}) => {
|
||||
return name;
|
||||
}));
|
||||
|
||||
const tagsWithTypes = /** @type {TypeAndTagInfo[]} */ ([
|
||||
...typeTags,
|
||||
...namepathReferencingTags,
|
||||
...namepathOrUrlReferencingTags,
|
||||
// Remove types which failed to parse
|
||||
].filter(Boolean));
|
||||
|
||||
for (const {
|
||||
parsedType,
|
||||
tag,
|
||||
} of tagsWithTypes) {
|
||||
// eslint-disable-next-line complexity -- Refactor
|
||||
traverse(parsedType, (nde, parentNode) => {
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').NameResult & {
|
||||
* _parent?: import('jsdoc-type-pratt-parser').NonRootResult
|
||||
* }}
|
||||
*/
|
||||
// eslint-disable-next-line canonical/id-match -- Avoid clashes
|
||||
(nde)._parent = parentNode;
|
||||
const {
|
||||
type,
|
||||
value,
|
||||
} = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde);
|
||||
|
||||
let val = value;
|
||||
|
||||
/** @type {import('jsdoc-type-pratt-parser').NonRootResult|undefined} */
|
||||
let currNode = nde;
|
||||
do {
|
||||
currNode =
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').NameResult & {
|
||||
* _parent?: import('jsdoc-type-pratt-parser').NonRootResult
|
||||
* }}
|
||||
*/ (currNode)._parent;
|
||||
if (
|
||||
// Avoid appending for imports and globals since we don't want to
|
||||
// check their properties which may or may not exist
|
||||
!imports.includes(val) && !globals.includes(val) &&
|
||||
!importTags.includes(val) &&
|
||||
!extraTypes.includes(val) &&
|
||||
!typedefDeclarations.includes(val) &&
|
||||
!globalTypes.includes(val) &&
|
||||
currNode && 'right' in currNode &&
|
||||
currNode.right?.type === 'JsdocTypeProperty'
|
||||
) {
|
||||
val = val + '.' + currNode.right.value;
|
||||
}
|
||||
} while (currNode?.type === 'JsdocTypeNamePath');
|
||||
|
||||
if (type === 'JsdocTypeName') {
|
||||
const structuredTypes = structuredTags[tag.tag]?.type;
|
||||
if (!allDefinedTypes.has(val) &&
|
||||
!definedNamesAndNamepaths.has(val) &&
|
||||
(!Array.isArray(structuredTypes) || !structuredTypes.includes(val))
|
||||
) {
|
||||
const parent =
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').RootResult & {
|
||||
* _parent?: import('jsdoc-type-pratt-parser').NonRootResult
|
||||
* }}
|
||||
*/ (nde)._parent;
|
||||
if (parent?.type === 'JsdocTypeTypeParameter') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parent?.type === 'JsdocTypeFunction' &&
|
||||
/** @type {import('jsdoc-type-pratt-parser').FunctionResult} */
|
||||
(parent)?.typeParameters?.some((typeParam) => {
|
||||
return value === typeParam.name.value;
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!disableReporting) {
|
||||
report(`The type '${val}' is undefined.`, null, tag);
|
||||
}
|
||||
} else if (markVariablesAsUsed && !extraTypes.includes(val)) {
|
||||
if (sourceCode.markVariableAsUsed) {
|
||||
sourceCode.markVariableAsUsed(val);
|
||||
/* c8 ignore next 4 */
|
||||
} else {
|
||||
// @ts-expect-error ESLint 8
|
||||
context.markVariableAsUsed(val);
|
||||
}
|
||||
}
|
||||
|
||||
if (checkUsedTypedefs && typedefDeclarations.includes(val)) {
|
||||
foundTypedefValues.push(val);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
state.foundTypedefValues = foundTypedefValues;
|
||||
}, {
|
||||
// We use this method rather than checking at end of handler above because
|
||||
// in that case, it is invoked too many times and would thus report errors
|
||||
// too many times.
|
||||
exit ({
|
||||
context,
|
||||
state,
|
||||
utils,
|
||||
}) {
|
||||
const {
|
||||
checkUsedTypedefs = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
if (!checkUsedTypedefs) {
|
||||
return;
|
||||
}
|
||||
|
||||
const allComments = context.sourceCode.getAllComments();
|
||||
const comments = allComments
|
||||
.filter((comment) => {
|
||||
return (/^\*(?!\*)/v).test(comment.value);
|
||||
})
|
||||
.map((commentNode) => {
|
||||
return {
|
||||
doc: parseComment(commentNode, ''),
|
||||
loc: commentNode.loc,
|
||||
};
|
||||
});
|
||||
const typedefs = comments
|
||||
.flatMap(({
|
||||
doc,
|
||||
loc,
|
||||
}) => {
|
||||
const tags = doc.tags.filter(({
|
||||
tag,
|
||||
}) => {
|
||||
return utils.isNameOrNamepathDefiningTag(tag);
|
||||
});
|
||||
if (!tags.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return {
|
||||
loc,
|
||||
tags,
|
||||
};
|
||||
});
|
||||
|
||||
for (const typedef of typedefs) {
|
||||
if (
|
||||
!state.foundTypedefValues.includes(typedef.tags[0].name)
|
||||
) {
|
||||
context.report({
|
||||
loc: /** @type {import('@eslint/core').SourceLocation} */ (typedef.loc),
|
||||
message: 'This typedef was not used within the file',
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Besides some expected built-in types, prohibits any types not specified as globals or within `@typedef`.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-undefined-types.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
checkUsedTypedefs: {
|
||||
description: 'Whether to check typedefs for use within the file',
|
||||
type: 'boolean',
|
||||
},
|
||||
definedTypes: {
|
||||
description: `This array can be populated to indicate other types which
|
||||
are automatically considered as defined (in addition to globals, etc.).
|
||||
Defaults to an empty array.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
disableReporting: {
|
||||
description: `Whether to disable reporting of errors. Defaults to
|
||||
\`false\`. This may be set to \`true\` in order to take advantage of only
|
||||
marking defined variables as used or checking used typedefs.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
markVariablesAsUsed: {
|
||||
description: `Whether to mark variables as used for the purposes
|
||||
of the \`no-unused-vars\` rule when they are not found to be undefined.
|
||||
Defaults to \`true\`. May be set to \`false\` to enforce a practice of not
|
||||
importing types unless used in code.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
486
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/preferImportTag.js
generated
vendored
Normal file
486
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/preferImportTag.js
generated
vendored
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
import iterateJsdoc, {
|
||||
parseComment,
|
||||
} from '../iterateJsdoc.js';
|
||||
import {
|
||||
commentParserToESTree,
|
||||
estreeToString,
|
||||
// getJSDocComment,
|
||||
parse as parseType,
|
||||
stringify,
|
||||
traverse,
|
||||
tryParse as tryParseType,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
import {
|
||||
parseImportsExports,
|
||||
} from 'parse-imports-exports';
|
||||
import toValidIdentifier from 'to-valid-identifier';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
indent,
|
||||
jsdoc,
|
||||
settings,
|
||||
sourceCode,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
mode,
|
||||
} = settings;
|
||||
|
||||
const {
|
||||
enableFixer = true,
|
||||
exemptTypedefs = true,
|
||||
outputType = 'namespaced-import',
|
||||
} = context.options[0] || {};
|
||||
|
||||
const allComments = sourceCode.getAllComments();
|
||||
const comments = allComments
|
||||
.filter((comment) => {
|
||||
return (/^\*(?!\*)/v).test(comment.value);
|
||||
})
|
||||
.map((commentNode) => {
|
||||
return commentParserToESTree(
|
||||
parseComment(commentNode, ''), mode === 'permissive' ? 'typescript' : mode,
|
||||
);
|
||||
});
|
||||
|
||||
const typedefs = comments
|
||||
.flatMap((doc) => {
|
||||
return doc.tags.filter(({
|
||||
tag,
|
||||
}) => {
|
||||
return utils.isNameOrNamepathDefiningTag(tag);
|
||||
});
|
||||
});
|
||||
|
||||
const imports = comments
|
||||
.flatMap((doc) => {
|
||||
return doc.tags.filter(({
|
||||
tag,
|
||||
}) => {
|
||||
return tag === 'import';
|
||||
});
|
||||
}).map((tag) => {
|
||||
// Causes problems with stringification otherwise
|
||||
tag.delimiter = '';
|
||||
return tag;
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('@es-joy/jsdoccomment').JsdocTagWithInline} tag
|
||||
*/
|
||||
const iterateInlineImports = (tag) => {
|
||||
const potentialType = tag.type;
|
||||
let parsedType;
|
||||
try {
|
||||
parsedType = mode === 'permissive' ?
|
||||
tryParseType(/** @type {string} */ (potentialType)) :
|
||||
parseType(/** @type {string} */ (potentialType), mode);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
traverse(parsedType, (nde, parentNode) => {
|
||||
// @ts-expect-error Adding our own property for use below
|
||||
nde.parentNode = parentNode;
|
||||
});
|
||||
|
||||
traverse(parsedType, (nde) => {
|
||||
const {
|
||||
element,
|
||||
type,
|
||||
} = /** @type {import('jsdoc-type-pratt-parser').ImportResult} */ (nde);
|
||||
if (type !== 'JsdocTypeImport') {
|
||||
return;
|
||||
}
|
||||
|
||||
let currentNode = nde;
|
||||
|
||||
/** @type {string[]} */
|
||||
const pathSegments = [];
|
||||
|
||||
/** @type {import('jsdoc-type-pratt-parser').NamePathResult[]} */
|
||||
const nodes = [];
|
||||
|
||||
/** @type {string[]} */
|
||||
const extraPathSegments = [];
|
||||
|
||||
/** @type {(import('jsdoc-type-pratt-parser').QuoteStyle|undefined)[]} */
|
||||
const quotes = [];
|
||||
|
||||
const propertyOrBrackets = /** @type {import('jsdoc-type-pratt-parser').NamePathResult['pathType'][]} */ ([]);
|
||||
|
||||
// @ts-expect-error Referencing our own property added above
|
||||
while (currentNode && currentNode.parentNode) {
|
||||
// @ts-expect-error Referencing our own property added above
|
||||
currentNode = currentNode.parentNode;
|
||||
/* c8 ignore next 3 -- Guard */
|
||||
if (currentNode.type !== 'JsdocTypeNamePath') {
|
||||
break;
|
||||
}
|
||||
|
||||
pathSegments.unshift(
|
||||
currentNode.right.type === 'JsdocTypeIndexedAccessIndex' ?
|
||||
stringify(currentNode.right.right) :
|
||||
currentNode.right.value,
|
||||
);
|
||||
nodes.unshift(currentNode);
|
||||
propertyOrBrackets.unshift(currentNode.pathType);
|
||||
quotes.unshift(
|
||||
currentNode.right.type === 'JsdocTypeIndexedAccessIndex' ?
|
||||
undefined :
|
||||
currentNode.right.meta.quote,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string[]} extrPathSegments
|
||||
*/
|
||||
const getFixer = (name, extrPathSegments) => {
|
||||
const matchingName = toValidIdentifier(name);
|
||||
return () => {
|
||||
/** @type {import('jsdoc-type-pratt-parser').NamePathResult|undefined} */
|
||||
let node = nodes.at(0);
|
||||
if (!node) {
|
||||
// Not really a NamePathResult, but will be converted later anyways
|
||||
node = /** @type {import('jsdoc-type-pratt-parser').NamePathResult} */ (
|
||||
/** @type {unknown} */
|
||||
(nde)
|
||||
);
|
||||
}
|
||||
|
||||
const keys = /** @type {(keyof import('jsdoc-type-pratt-parser').NamePathResult)[]} */ (
|
||||
Object.keys(node)
|
||||
);
|
||||
|
||||
for (const key of keys) {
|
||||
delete node[key];
|
||||
}
|
||||
|
||||
if (extrPathSegments.length) {
|
||||
let newNode = /** @type {import('jsdoc-type-pratt-parser').NamePathResult} */ (
|
||||
/** @type {unknown} */
|
||||
(node)
|
||||
);
|
||||
while (extrPathSegments.length && newNode) {
|
||||
newNode.type = 'JsdocTypeNamePath';
|
||||
newNode.right = {
|
||||
meta: {
|
||||
quote: quotes.shift(),
|
||||
},
|
||||
type: 'JsdocTypeProperty',
|
||||
value: /** @type {string} */ (extrPathSegments.shift()),
|
||||
};
|
||||
|
||||
newNode.pathType = /** @type {import('jsdoc-type-pratt-parser').NamePathResult['pathType']} */ (
|
||||
propertyOrBrackets.shift()
|
||||
);
|
||||
// @ts-expect-error Temporary
|
||||
newNode.left = {};
|
||||
newNode = /** @type {import('jsdoc-type-pratt-parser').NamePathResult} */ (
|
||||
newNode.left
|
||||
);
|
||||
}
|
||||
|
||||
const nameNode = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (
|
||||
/** @type {unknown} */
|
||||
(newNode)
|
||||
);
|
||||
nameNode.type = 'JsdocTypeName';
|
||||
nameNode.value = matchingName;
|
||||
} else {
|
||||
const newNode = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (
|
||||
/** @type {unknown} */
|
||||
(node)
|
||||
);
|
||||
newNode.type = 'JsdocTypeName';
|
||||
newNode.value = matchingName;
|
||||
}
|
||||
|
||||
for (const src of tag.source) {
|
||||
if (src.tokens.type) {
|
||||
src.tokens.type = `{${stringify(parsedType)}}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/** @type {string[]} */
|
||||
let unusedPathSegments = [];
|
||||
|
||||
const findMatchingTypedef = () => {
|
||||
// Don't want typedefs to find themselves
|
||||
if (!exemptTypedefs) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const pthSegments = [
|
||||
...pathSegments,
|
||||
];
|
||||
return typedefs.find((typedef) => {
|
||||
let typedefNode = typedef.parsedType;
|
||||
let namepathMatch;
|
||||
while (typedefNode && typedefNode.type === 'JsdocTypeNamePath') {
|
||||
const pathSegment = pthSegments.shift();
|
||||
if (!pathSegment) {
|
||||
namepathMatch = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
(typedefNode.right.type === 'JsdocTypeIndexedAccessIndex' &&
|
||||
stringify(typedefNode.right.right) !== pathSegment) ||
|
||||
(typedefNode.right.type !== 'JsdocTypeIndexedAccessIndex' &&
|
||||
typedefNode.right.value !== pathSegment)
|
||||
) {
|
||||
if (namepathMatch === true) {
|
||||
// It stopped matching, so stop
|
||||
break;
|
||||
}
|
||||
|
||||
extraPathSegments.push(pathSegment);
|
||||
namepathMatch = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
namepathMatch = true;
|
||||
|
||||
unusedPathSegments = pthSegments;
|
||||
|
||||
typedefNode = typedefNode.left;
|
||||
}
|
||||
|
||||
return namepathMatch &&
|
||||
// `import('eslint')` matches
|
||||
typedefNode &&
|
||||
typedefNode.type === 'JsdocTypeImport' &&
|
||||
typedefNode.element.value === element.value;
|
||||
});
|
||||
};
|
||||
|
||||
// Check @typedef's first as should be longest match, allowing
|
||||
// for shorter abbreviations
|
||||
const matchingTypedef = findMatchingTypedef();
|
||||
if (matchingTypedef) {
|
||||
utils.reportJSDoc(
|
||||
'Inline `import()` found; using `@typedef`',
|
||||
tag,
|
||||
enableFixer ? getFixer(matchingTypedef.name, [
|
||||
...extraPathSegments,
|
||||
...unusedPathSegments.slice(-1),
|
||||
...unusedPathSegments.slice(0, -1),
|
||||
]) : null,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const findMatchingImport = () => {
|
||||
for (const imprt of imports) {
|
||||
const parsedImport = parseImportsExports(
|
||||
estreeToString(imprt).replace(/^\s*@/v, '').trim(),
|
||||
);
|
||||
|
||||
const namedImportsModuleSpecifier = Object.keys(parsedImport.namedImports || {})[0];
|
||||
|
||||
const namedImports = Object.values(parsedImport.namedImports || {})[0]?.[0];
|
||||
const namedImportNames = (namedImports && namedImports.names && Object.keys(namedImports.names)) ?? [];
|
||||
|
||||
const namespaceImports = Object.values(parsedImport.namespaceImports || {})[0]?.[0];
|
||||
|
||||
const namespaceImportsDefault = namespaceImports && namespaceImports.default;
|
||||
const namespaceImportsNamespace = namespaceImports && namespaceImports.namespace;
|
||||
const namespaceImportsModuleSpecifier = Object.keys(parsedImport.namespaceImports || {})[0];
|
||||
|
||||
const lastPathSegment = pathSegments.at(-1);
|
||||
|
||||
if (
|
||||
(namespaceImportsDefault &&
|
||||
namespaceImportsModuleSpecifier === element.value) ||
|
||||
(element.value === namedImportsModuleSpecifier && (
|
||||
(lastPathSegment && namedImportNames.includes(lastPathSegment)) ||
|
||||
lastPathSegment === 'default'
|
||||
)) ||
|
||||
(namespaceImportsNamespace &&
|
||||
namespaceImportsModuleSpecifier === element.value)
|
||||
) {
|
||||
return {
|
||||
namedImportNames,
|
||||
namedImports,
|
||||
namedImportsModuleSpecifier,
|
||||
namespaceImports,
|
||||
namespaceImportsDefault,
|
||||
namespaceImportsModuleSpecifier,
|
||||
namespaceImportsNamespace,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const matchingImport = findMatchingImport();
|
||||
if (matchingImport) {
|
||||
const {
|
||||
namedImportNames,
|
||||
namedImports,
|
||||
namedImportsModuleSpecifier,
|
||||
namespaceImportsNamespace,
|
||||
} = matchingImport;
|
||||
if (!namedImportNames.length && namedImportsModuleSpecifier && namedImports.default) {
|
||||
utils.reportJSDoc(
|
||||
'Inline `import()` found; prefer `@import`',
|
||||
tag,
|
||||
enableFixer ? getFixer(namedImports.default, []) : null,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const lastPthSegment = pathSegments.at(-1);
|
||||
if (lastPthSegment && namedImportNames.includes(lastPthSegment)) {
|
||||
utils.reportJSDoc(
|
||||
'Inline `import()` found; prefer `@import`',
|
||||
tag,
|
||||
enableFixer ? getFixer(lastPthSegment, pathSegments.slice(0, -1)) : null,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (namespaceImportsNamespace) {
|
||||
utils.reportJSDoc(
|
||||
'Inline `import()` found; prefer `@import`',
|
||||
tag,
|
||||
enableFixer ? getFixer(namespaceImportsNamespace, [
|
||||
...pathSegments,
|
||||
]) : null,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pathSegments.length) {
|
||||
utils.reportJSDoc(
|
||||
'Inline `import()` found; prefer `@import`',
|
||||
tag,
|
||||
enableFixer ? (fixer) => {
|
||||
getFixer(element.value, [])();
|
||||
|
||||
const programNode = sourceCode.ast;
|
||||
const commentNodes = sourceCode.getCommentsBefore(programNode);
|
||||
return fixer.insertTextBefore(
|
||||
// @ts-expect-error Ok
|
||||
commentNodes[0] ?? programNode,
|
||||
`/** @import * as ${toValidIdentifier(element.value)} from '${element.value}'; */${
|
||||
commentNodes[0] ? '\n' + indent : ''
|
||||
}`,
|
||||
);
|
||||
} : null,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const lstPathSegment = pathSegments.at(-1);
|
||||
if (lstPathSegment && lstPathSegment === 'default') {
|
||||
utils.reportJSDoc(
|
||||
'Inline `import()` found; prefer `@import`',
|
||||
tag,
|
||||
enableFixer ? (fixer) => {
|
||||
getFixer(element.value, [])();
|
||||
|
||||
const programNode = sourceCode.ast;
|
||||
const commentNodes = sourceCode.getCommentsBefore(programNode);
|
||||
|
||||
return fixer.insertTextBefore(
|
||||
// @ts-expect-error Ok
|
||||
commentNodes[0] ?? programNode,
|
||||
`/** @import ${element.value} from '${element.value}'; */${
|
||||
commentNodes[0] ? '\n' + indent : ''
|
||||
}`,
|
||||
);
|
||||
} : null,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Inline `import()` found; prefer `@import`',
|
||||
tag,
|
||||
enableFixer ? (fixer) => {
|
||||
if (outputType === 'namespaced-import') {
|
||||
getFixer(element.value, [
|
||||
...pathSegments,
|
||||
])();
|
||||
} else {
|
||||
getFixer(
|
||||
/** @type {string} */ (pathSegments.at(-1)),
|
||||
pathSegments.slice(0, -1),
|
||||
)();
|
||||
}
|
||||
|
||||
const programNode = sourceCode.ast;
|
||||
const commentNodes = sourceCode.getCommentsBefore(programNode);
|
||||
return fixer.insertTextBefore(
|
||||
// @ts-expect-error Ok
|
||||
commentNodes[0] ?? programNode,
|
||||
outputType === 'namespaced-import' ?
|
||||
`/** @import * as ${toValidIdentifier(element.value)} from '${element.value}'; */${
|
||||
commentNodes[0] ? '\n' + indent : ''
|
||||
}` :
|
||||
`/** @import { ${toValidIdentifier(
|
||||
/* c8 ignore next -- TS */
|
||||
pathSegments.at(-1) ?? '',
|
||||
)} } from '${element.value}'; */${
|
||||
commentNodes[0] ? '\n' + indent : ''
|
||||
}`,
|
||||
);
|
||||
} : null,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
for (const tag of jsdoc.tags) {
|
||||
const mightHaveTypePosition = utils.tagMightHaveTypePosition(tag.tag);
|
||||
const hasTypePosition = mightHaveTypePosition === true && Boolean(tag.type);
|
||||
if (hasTypePosition && (!exemptTypedefs || !utils.isNameOrNamepathDefiningTag(tag.tag))) {
|
||||
iterateInlineImports(tag);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Prefer `@import` tags to inline `import()` statements.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/prefer-import-tag.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
enableFixer: {
|
||||
description: 'Whether or not to enable the fixer to add `@import` tags.',
|
||||
type: 'boolean',
|
||||
},
|
||||
exemptTypedefs: {
|
||||
description: 'Whether to allow `import()` statements within `@typedef`',
|
||||
type: 'boolean',
|
||||
},
|
||||
|
||||
// We might add `typedef` and `typedef-local-only`, but also raises
|
||||
// question of how deep the generated typedef should be
|
||||
outputType: {
|
||||
description: 'What kind of `@import` to generate when no matching `@typedef` or `@import` is found',
|
||||
enum: [
|
||||
'named-import',
|
||||
'namespaced-import',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
217
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireAsteriskPrefix.js
generated
vendored
Normal file
217
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireAsteriskPrefix.js
generated
vendored
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
indent,
|
||||
jsdoc,
|
||||
utils,
|
||||
}) => {
|
||||
const [
|
||||
defaultRequireValue = 'always',
|
||||
{
|
||||
tags: tagMap = {},
|
||||
} = {},
|
||||
] = context.options;
|
||||
|
||||
const {
|
||||
source,
|
||||
} = jsdoc;
|
||||
|
||||
const always = defaultRequireValue === 'always';
|
||||
const never = defaultRequireValue === 'never';
|
||||
|
||||
/** @type {string} */
|
||||
let currentTag;
|
||||
source.some(({
|
||||
number,
|
||||
tokens,
|
||||
}) => {
|
||||
const {
|
||||
delimiter,
|
||||
description,
|
||||
end,
|
||||
tag,
|
||||
} = tokens;
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
const neverFix = () => {
|
||||
tokens.delimiter = '';
|
||||
tokens.postDelimiter = '';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} checkValue
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const checkNever = (checkValue) => {
|
||||
if (delimiter && delimiter !== '/**' && (
|
||||
never && !tagMap.always?.includes(checkValue) ||
|
||||
tagMap.never?.includes(checkValue)
|
||||
)) {
|
||||
utils.reportJSDoc('Expected JSDoc line to have no prefix.', {
|
||||
column: 0,
|
||||
line: number,
|
||||
}, neverFix);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
const alwaysFix = () => {
|
||||
if (!tokens.start) {
|
||||
tokens.start = indent + ' ';
|
||||
}
|
||||
|
||||
tokens.delimiter = '*';
|
||||
tokens.postDelimiter = tag || description ? ' ' : '';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} checkValue
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const checkAlways = (checkValue) => {
|
||||
if (
|
||||
!delimiter && (
|
||||
always && !tagMap.never?.includes(checkValue) ||
|
||||
tagMap.always?.includes(checkValue)
|
||||
)
|
||||
) {
|
||||
utils.reportJSDoc('Expected JSDoc line to have the prefix.', {
|
||||
column: 0,
|
||||
line: number,
|
||||
}, alwaysFix);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (tag) {
|
||||
// Remove at sign
|
||||
currentTag = tag.slice(1);
|
||||
}
|
||||
|
||||
if (
|
||||
// If this is the end but has a tag, the delimiter will also be
|
||||
// populated and will be safely ignored later
|
||||
end && !tag
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!currentTag) {
|
||||
if (tagMap.any?.includes('*description')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkNever('*description')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (checkAlways('*description')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tagMap.any?.includes(currentTag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkNever(currentTag)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (checkAlways(currentTag)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description:
|
||||
'Requires that each JSDoc line starts with an `*`.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-asterisk-prefix.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
description: `If it is \`"always"\` then a problem is raised when there is no asterisk
|
||||
prefix on a given JSDoc line. If it is \`"never"\` then a problem is raised
|
||||
when there is an asterisk present.
|
||||
|
||||
The default value is \`"always"\`. You may also set the default to \`"any"\`
|
||||
and use the \`tags\` option to apply to specific tags only.`,
|
||||
enum: [
|
||||
'always', 'never', 'any',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
tags: {
|
||||
additionalProperties: false,
|
||||
description: `If you want different values to apply to specific tags, you may use
|
||||
the \`tags\` option object. The keys are \`always\`, \`never\`, or \`any\` and
|
||||
the values are arrays of tag names or the special value \`*description\`
|
||||
which applies to the main JSDoc block description.
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
'jsdoc/require-asterisk-prefix': ['error', 'always', {
|
||||
tags: {
|
||||
always: ['*description'],
|
||||
any: ['example', 'license'],
|
||||
never: ['copyright']
|
||||
}
|
||||
}]
|
||||
}
|
||||
\`\`\``,
|
||||
properties: {
|
||||
always: {
|
||||
description: `If it is \`"always"\` then a problem is raised when there is no asterisk
|
||||
prefix on a given JSDoc line.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
any: {
|
||||
description: 'No problem is raised regardless of asterisk presence or non-presence.',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
never: {
|
||||
description: `If it is \`"never"\` then a problem is raised
|
||||
when there is an asterisk present.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'layout',
|
||||
},
|
||||
});
|
||||
190
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireDescription.js
generated
vendored
Normal file
190
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireDescription.js
generated
vendored
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @param {string} description
|
||||
* @returns {import('../iterateJsdoc.js').Integer}
|
||||
*/
|
||||
const checkDescription = (description) => {
|
||||
return description
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
.length;
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
if (utils.avoidDocs()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
descriptionStyle = 'body',
|
||||
} = context.options[0] || {};
|
||||
|
||||
let targetTagName = utils.getPreferredTagName({
|
||||
// We skip reporting except when `@description` is essential to the rule,
|
||||
// so user can block the tag and still meaningfully use this rule
|
||||
// even if the tag is present (and `check-tag-names` is the one to
|
||||
// normally report the fact that it is blocked but present)
|
||||
skipReportingBlockedTag: descriptionStyle !== 'tag',
|
||||
tagName: 'description',
|
||||
});
|
||||
if (!targetTagName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isBlocked = typeof targetTagName === 'object' && 'blocked' in targetTagName && targetTagName.blocked;
|
||||
if (isBlocked) {
|
||||
targetTagName = /** @type {{blocked: true; tagName: string;}} */ (
|
||||
targetTagName
|
||||
).tagName;
|
||||
}
|
||||
|
||||
if (descriptionStyle !== 'tag') {
|
||||
const {
|
||||
description,
|
||||
} = utils.getDescription();
|
||||
if (checkDescription(description || '')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (descriptionStyle === 'body') {
|
||||
const descTags = utils.getPresentTags([
|
||||
'desc', 'description',
|
||||
]);
|
||||
if (descTags.length) {
|
||||
const [
|
||||
{
|
||||
tag: tagName,
|
||||
},
|
||||
] = descTags;
|
||||
report(`Remove the @${tagName} tag to leave a plain block description or add additional description text above the @${tagName} line.`);
|
||||
} else {
|
||||
report('Missing JSDoc block description.');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const functionExamples = isBlocked ?
|
||||
[] :
|
||||
jsdoc.tags.filter(({
|
||||
tag,
|
||||
}) => {
|
||||
return tag === targetTagName;
|
||||
});
|
||||
|
||||
if (!functionExamples.length) {
|
||||
report(
|
||||
descriptionStyle === 'any' ?
|
||||
`Missing JSDoc block description or @${targetTagName} declaration.` :
|
||||
`Missing JSDoc @${targetTagName} declaration.`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (const example of functionExamples) {
|
||||
if (!checkDescription(`${example.name} ${utils.getTagDescription(example)}`)) {
|
||||
report(`Missing JSDoc @${targetTagName} description.`, null, example);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that all functions (and potentially other contexts) have a description.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-description.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
checkConstructors: {
|
||||
default: true,
|
||||
description: `A value indicating whether \`constructor\`s should be
|
||||
checked. Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkGetters: {
|
||||
default: true,
|
||||
description: `A value indicating whether getters should be checked.
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkSetters: {
|
||||
default: true,
|
||||
description: `A value indicating whether setters should be checked.
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
contexts: {
|
||||
description: `Set to an array of strings representing the AST context
|
||||
where you wish the rule to be applied (e.g., \`ClassDeclaration\` for ES6
|
||||
classes).
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`,
|
||||
\`FunctionDeclaration\`, \`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
descriptionStyle: {
|
||||
description: `Whether to accept implicit descriptions (\`"body"\`) or
|
||||
\`@description\` tags (\`"tag"\`) as satisfying the rule. Set to \`"any"\` to
|
||||
accept either style. Defaults to \`"body"\`.`,
|
||||
enum: [
|
||||
'body', 'tag', 'any',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
exemptedBy: {
|
||||
description: `Array of tags (e.g., \`['type']\`) whose presence on the
|
||||
document block avoids the need for a \`@description\`. Defaults to an
|
||||
array with \`inheritdoc\`. If you set this array, it will overwrite the
|
||||
default, so be sure to add back \`inheritdoc\` if you wish its presence
|
||||
to cause exemption of the rule.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
361
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireDescriptionCompleteSentence.js
generated
vendored
Normal file
361
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireDescriptionCompleteSentence.js
generated
vendored
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
|
||||
const otherDescriptiveTags = new Set([
|
||||
'classdesc', 'deprecated', 'exception', 'file', 'fileoverview', 'overview',
|
||||
// 'copyright' and 'see' might be good addition, but as the former may be
|
||||
// sensitive text, and the latter may have just a link, they are not
|
||||
// included by default
|
||||
'summary', 'throws', 'todo', 'yield', 'yields',
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns {string[]}
|
||||
*/
|
||||
const extractParagraphs = (text) => {
|
||||
return text.split(/(?<![;:])\n\n+/v);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {string|RegExp} abbreviationsRegex
|
||||
* @returns {string[]}
|
||||
*/
|
||||
const extractSentences = (text, abbreviationsRegex) => {
|
||||
const txt = text
|
||||
// Remove all {} tags.
|
||||
.replaceAll(/(?<!^)\{[\s\S]*?\}\s*/gv, '')
|
||||
|
||||
// Remove custom abbreviations
|
||||
.replace(abbreviationsRegex, '');
|
||||
|
||||
const sentenceEndGrouping = /([.?!])(?:\s+|$)/gv;
|
||||
|
||||
const puncts = [
|
||||
...txt.matchAll(sentenceEndGrouping),
|
||||
].map((sentEnd) => {
|
||||
return sentEnd[0];
|
||||
});
|
||||
|
||||
return txt
|
||||
.split(/[.?!](?:\s+|$)/v)
|
||||
|
||||
// Re-add the dot.
|
||||
.map((sentence, idx) => {
|
||||
return !puncts[idx] && /^\s*$/v.test(sentence) ? sentence : `${sentence}${puncts[idx] || ''}`;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isNewLinePrecededByAPeriod = (text) => {
|
||||
/** @type {boolean} */
|
||||
let lastLineEndsSentence;
|
||||
|
||||
const lines = text.split('\n');
|
||||
|
||||
return !lines.some((line) => {
|
||||
if (lastLineEndsSentence === false && /^[A-Z][a-z]/v.test(line)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
lastLineEndsSentence = /[.:?!\|]$/v.test(line);
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isCapitalized = (str) => {
|
||||
return str[0] === str[0].toUpperCase();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isTable = (str) => {
|
||||
return str.charAt(0) === '|';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
const capitalize = (str) => {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} description
|
||||
* @param {import('../iterateJsdoc.js').Report} reportOrig
|
||||
* @param {import('eslint').Rule.Node} jsdocNode
|
||||
* @param {string|RegExp} abbreviationsRegex
|
||||
* @param {import('eslint').SourceCode} sourceCode
|
||||
* @param {import('comment-parser').Spec|{
|
||||
* line: import('../iterateJsdoc.js').Integer
|
||||
* }} tag
|
||||
* @param {boolean} newlineBeforeCapsAssumesBadSentenceEnd
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const validateDescription = (
|
||||
description, reportOrig, jsdocNode, abbreviationsRegex,
|
||||
sourceCode, tag, newlineBeforeCapsAssumesBadSentenceEnd,
|
||||
) => {
|
||||
if (!description || (/^\n+$/v).test(description)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const descriptionNoHeadings = description.replaceAll(/^\s*#[^\n]*(\n|$)/gmv, '');
|
||||
|
||||
const paragraphs = extractParagraphs(descriptionNoHeadings).filter(Boolean);
|
||||
|
||||
return paragraphs.some((paragraph, parIdx) => {
|
||||
const sentences = extractSentences(paragraph, abbreviationsRegex);
|
||||
|
||||
const fix = /** @type {import('eslint').Rule.ReportFixer} */ (fixer) => {
|
||||
let text = sourceCode.getText(jsdocNode);
|
||||
|
||||
if (!/[.:?!]$/v.test(paragraph)) {
|
||||
const line = paragraph.split('\n').findLast(Boolean);
|
||||
text = text.replace(new RegExp(`${escapeStringRegexp(
|
||||
/** @type {string} */
|
||||
(line),
|
||||
)}$`, 'mv'), `${line}.`);
|
||||
}
|
||||
|
||||
for (const sentence of sentences.filter((sentence_) => {
|
||||
return !(/^\s*$/v).test(sentence_) && !isCapitalized(sentence_) &&
|
||||
!isTable(sentence_);
|
||||
})) {
|
||||
const beginning = sentence.split('\n')[0];
|
||||
|
||||
if ('tag' in tag && tag.tag) {
|
||||
const reg = new RegExp(`(@${escapeStringRegexp(tag.tag)}.*)${escapeStringRegexp(beginning)}`, 'v');
|
||||
|
||||
text = text.replace(reg, (_$0, $1) => {
|
||||
return $1 + capitalize(beginning);
|
||||
});
|
||||
} else {
|
||||
text = text.replace(new RegExp('((?:[.?!]|\\*|\\})\\s*)' + escapeStringRegexp(beginning), 'v'), '$1' + capitalize(beginning));
|
||||
}
|
||||
}
|
||||
|
||||
return fixer.replaceText(jsdocNode, text);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} msg
|
||||
* @param {import('eslint').Rule.ReportFixer | null | undefined} fixer
|
||||
* @param {{
|
||||
* line?: number | undefined;
|
||||
* column?: number | undefined;
|
||||
* } | (import('comment-parser').Spec & {
|
||||
* line?: number | undefined;
|
||||
* column?: number | undefined;
|
||||
* })} tagObj
|
||||
* @returns {void}
|
||||
*/
|
||||
const report = (msg, fixer, tagObj) => {
|
||||
if ('line' in tagObj) {
|
||||
/**
|
||||
* @type {{
|
||||
* line: number;
|
||||
* }}
|
||||
*/ (tagObj).line += parIdx * 2;
|
||||
} else {
|
||||
/** @type {import('comment-parser').Spec} */ (
|
||||
tagObj
|
||||
).source[0].number += parIdx * 2;
|
||||
}
|
||||
|
||||
// Avoid errors if old column doesn't exist here
|
||||
tagObj.column = 0;
|
||||
reportOrig(msg, fixer, tagObj);
|
||||
};
|
||||
|
||||
if (sentences.some((sentence) => {
|
||||
return (/^[.?!]$/v).test(sentence);
|
||||
})) {
|
||||
report('Sentences must be more than punctuation.', null, tag);
|
||||
}
|
||||
|
||||
if (sentences.some((sentence) => {
|
||||
return !(/^\s*$/v).test(sentence) && !isCapitalized(sentence) && !isTable(sentence);
|
||||
})) {
|
||||
report('Sentences should start with an uppercase character.', fix, tag);
|
||||
}
|
||||
|
||||
const paragraphNoAbbreviations = paragraph.replace(abbreviationsRegex, '');
|
||||
|
||||
if (!/(?:[.?!\|]|```)\s*$/v.test(paragraphNoAbbreviations)) {
|
||||
report('Sentences must end with a period.', fix, tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (newlineBeforeCapsAssumesBadSentenceEnd && !isNewLinePrecededByAPeriod(paragraphNoAbbreviations)) {
|
||||
report('A line of text is started with an uppercase character, but the preceding line does not end the sentence.', null, tag);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
jsdocNode,
|
||||
report,
|
||||
sourceCode,
|
||||
utils,
|
||||
}) => {
|
||||
const /** @type {{abbreviations: string[], newlineBeforeCapsAssumesBadSentenceEnd: boolean}} */ {
|
||||
abbreviations = [],
|
||||
newlineBeforeCapsAssumesBadSentenceEnd = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
const abbreviationsRegex = abbreviations.length ?
|
||||
new RegExp('\\b' + abbreviations.map((abbreviation) => {
|
||||
return escapeStringRegexp(abbreviation.replaceAll(/\.$/gv, '') + '.');
|
||||
}).join('|') + '(?:$|\\s)', 'gv') :
|
||||
'';
|
||||
|
||||
let {
|
||||
description,
|
||||
} = utils.getDescription();
|
||||
|
||||
const indices = [
|
||||
...description.matchAll(/```[\s\S]*```/gv),
|
||||
].map((match) => {
|
||||
const {
|
||||
index,
|
||||
} = match;
|
||||
const [
|
||||
{
|
||||
length,
|
||||
},
|
||||
] = match;
|
||||
return {
|
||||
index,
|
||||
length,
|
||||
};
|
||||
}).toReversed();
|
||||
|
||||
for (const {
|
||||
index,
|
||||
length,
|
||||
} of indices) {
|
||||
description = description.slice(0, index) +
|
||||
description.slice(/** @type {import('../iterateJsdoc.js').Integer} */ (
|
||||
index
|
||||
) + length);
|
||||
}
|
||||
|
||||
if (validateDescription(description, report, jsdocNode, abbreviationsRegex, sourceCode, {
|
||||
line: jsdoc.source[0].number + 1,
|
||||
}, newlineBeforeCapsAssumesBadSentenceEnd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
utils.forEachPreferredTag('description', (matchingJsdocTag) => {
|
||||
const desc = `${matchingJsdocTag.name} ${utils.getTagDescription(matchingJsdocTag)}`.trim();
|
||||
validateDescription(desc, report, jsdocNode, abbreviationsRegex, sourceCode, matchingJsdocTag, newlineBeforeCapsAssumesBadSentenceEnd);
|
||||
}, true);
|
||||
|
||||
const {
|
||||
tagsWithNames,
|
||||
} = utils.getTagsByType(jsdoc.tags);
|
||||
const tagsWithoutNames = utils.filterTags(({
|
||||
tag: tagName,
|
||||
}) => {
|
||||
return otherDescriptiveTags.has(tagName) ||
|
||||
utils.hasOptionTag(tagName) && !tagsWithNames.some(({
|
||||
tag,
|
||||
}) => {
|
||||
// If user accidentally adds tags with names (or like `returns`
|
||||
// get parsed as having names), do not add to this list
|
||||
return tag === tagName;
|
||||
});
|
||||
});
|
||||
|
||||
tagsWithNames.some((tag) => {
|
||||
const desc = /** @type {string} */ (
|
||||
utils.getTagDescription(tag)
|
||||
).replace(/^- /v, '').trimEnd();
|
||||
|
||||
return validateDescription(desc, report, jsdocNode, abbreviationsRegex, sourceCode, tag, newlineBeforeCapsAssumesBadSentenceEnd);
|
||||
});
|
||||
|
||||
tagsWithoutNames.some((tag) => {
|
||||
const desc = `${tag.name} ${utils.getTagDescription(tag)}`.trim();
|
||||
|
||||
return validateDescription(desc, report, jsdocNode, abbreviationsRegex, sourceCode, tag, newlineBeforeCapsAssumesBadSentenceEnd);
|
||||
});
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that block description, explicit `@description`, and `@param`/`@returns` tag descriptions are written in complete sentences.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-description-complete-sentence.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
abbreviations: {
|
||||
description: `You can provide an \`abbreviations\` options array to avoid such strings of text
|
||||
being treated as sentence endings when followed by dots. The \`.\` is not
|
||||
necessary at the end of the array items.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
newlineBeforeCapsAssumesBadSentenceEnd: {
|
||||
description: `When \`false\` (the new default), we will not assume capital letters after
|
||||
newlines are an incorrect way to end the sentence (they may be proper
|
||||
nouns, for example).`,
|
||||
type: 'boolean',
|
||||
},
|
||||
tags: {
|
||||
description: `If you want additional tags to be checked for their descriptions, you may
|
||||
add them within this option.
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
'jsdoc/require-description-complete-sentence': ['error', {
|
||||
tags: ['see', 'copyright']
|
||||
}]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
The tags \`@param\`/\`@arg\`/\`@argument\` and \`@property\`/\`@prop\` will be properly
|
||||
parsed to ensure that the checked "description" text includes only the text
|
||||
after the name.
|
||||
|
||||
All other tags will treat the text following the tag name, a space, and
|
||||
an optional curly-bracketed type expression (and another space) as part of
|
||||
its "description" (e.g., for \`@returns {someType} some description\`, the
|
||||
description is \`some description\` while for \`@some-tag xyz\`, the description
|
||||
is \`xyz\`).`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
143
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireExample.js
generated
vendored
Normal file
143
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireExample.js
generated
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
if (utils.avoidDocs()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
enableFixer = true,
|
||||
exemptNoArguments = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
const targetTagName = 'example';
|
||||
|
||||
const functionExamples = jsdoc.tags.filter(({
|
||||
tag,
|
||||
}) => {
|
||||
return tag === targetTagName;
|
||||
});
|
||||
|
||||
if (!functionExamples.length) {
|
||||
if (exemptNoArguments && utils.isIteratingFunctionOrVariable() &&
|
||||
!utils.hasParams()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
utils.reportJSDoc(`Missing JSDoc @${targetTagName} declaration.`, null, () => {
|
||||
if (enableFixer) {
|
||||
utils.addTag(targetTagName);
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (const example of functionExamples) {
|
||||
const exampleContent = `${example.name} ${utils.getTagDescription(example)}`
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter(Boolean);
|
||||
|
||||
if (!exampleContent.length) {
|
||||
report(`Missing JSDoc @${targetTagName} description.`, null, example);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that all functions (and potentially other contexts) have examples.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-example.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
checkConstructors: {
|
||||
default: true,
|
||||
description: `A value indicating whether \`constructor\`s should be checked.
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkGetters: {
|
||||
default: false,
|
||||
description: 'A value indicating whether getters should be checked. Defaults to `false`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
checkSetters: {
|
||||
default: false,
|
||||
description: 'A value indicating whether setters should be checked. Defaults to `false`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
(e.g., \`ClassDeclaration\` for ES6 classes).
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want the rule to apply to any
|
||||
JSDoc block throughout your files.
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
enableFixer: {
|
||||
default: true,
|
||||
description: `A boolean on whether to enable the fixer (which adds an empty \`@example\` block).
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
exemptedBy: {
|
||||
description: `Array of tags (e.g., \`['type']\`) whose presence on the document
|
||||
block avoids the need for an \`@example\`. Defaults to an array with
|
||||
\`inheritdoc\`. If you set this array, it will overwrite the default,
|
||||
so be sure to add back \`inheritdoc\` if you wish its presence to cause
|
||||
exemption of the rule.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
exemptNoArguments: {
|
||||
default: false,
|
||||
description: `Boolean to indicate that no-argument functions should not be reported for
|
||||
missing \`@example\` declarations.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
213
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireFileOverview.js
generated
vendored
Normal file
213
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireFileOverview.js
generated
vendored
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
const defaultTags = {
|
||||
file: {
|
||||
initialCommentsOnly: true,
|
||||
mustExist: true,
|
||||
preventDuplicates: true,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('../iterateJsdoc.js').StateObject} state
|
||||
* @returns {void}
|
||||
*/
|
||||
const setDefaults = (state) => {
|
||||
// First iteration
|
||||
if (!state.globalTags) {
|
||||
state.globalTags = true;
|
||||
state.hasDuplicates = {};
|
||||
state.hasTag = {};
|
||||
state.hasNonCommentBeforeTag = {};
|
||||
}
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdocNode,
|
||||
state,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
tags = defaultTags,
|
||||
} = context.options[0] || {};
|
||||
|
||||
setDefaults(state);
|
||||
|
||||
for (const tagName of Object.keys(tags)) {
|
||||
const targetTagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName,
|
||||
}));
|
||||
|
||||
const hasTag = Boolean(targetTagName && utils.hasTag(targetTagName));
|
||||
|
||||
state.hasTag[tagName] = hasTag || state.hasTag[tagName];
|
||||
|
||||
const hasDuplicate = state.hasDuplicates[tagName];
|
||||
|
||||
if (hasDuplicate === false) {
|
||||
// Was marked before, so if a tag now, is a dupe
|
||||
state.hasDuplicates[tagName] = hasTag;
|
||||
} else if (!hasDuplicate && hasTag) {
|
||||
// No dupes set before, but has first tag, so change state
|
||||
// from `undefined` to `false` so can detect next time
|
||||
state.hasDuplicates[tagName] = false;
|
||||
state.hasNonCommentBeforeTag[tagName] = state.hasNonComment &&
|
||||
state.hasNonComment < jsdocNode.range[0];
|
||||
}
|
||||
}
|
||||
}, {
|
||||
exit ({
|
||||
context,
|
||||
state,
|
||||
utils,
|
||||
}) {
|
||||
setDefaults(state);
|
||||
const {
|
||||
tags = defaultTags,
|
||||
} = context.options[0] || {};
|
||||
|
||||
for (const [
|
||||
tagName,
|
||||
{
|
||||
initialCommentsOnly = false,
|
||||
mustExist = false,
|
||||
preventDuplicates = false,
|
||||
},
|
||||
] of Object.entries(tags)) {
|
||||
const obj = utils.getPreferredTagNameObject({
|
||||
tagName,
|
||||
});
|
||||
if (obj && typeof obj === 'object' && 'blocked' in obj) {
|
||||
utils.reportSettings(
|
||||
`\`settings.jsdoc.tagNamePreference\` cannot block @${obj.tagName} ` +
|
||||
'for the `require-file-overview` rule',
|
||||
);
|
||||
} else {
|
||||
const targetTagName = (
|
||||
obj && typeof obj === 'object' && obj.replacement
|
||||
) || obj;
|
||||
if (mustExist && !state.hasTag[tagName]) {
|
||||
utils.reportSettings(`Missing @${targetTagName}`);
|
||||
}
|
||||
|
||||
if (preventDuplicates && state.hasDuplicates[tagName]) {
|
||||
utils.reportSettings(
|
||||
`Duplicate @${targetTagName}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (initialCommentsOnly &&
|
||||
state.hasNonCommentBeforeTag[tagName]
|
||||
) {
|
||||
utils.reportSettings(
|
||||
`@${targetTagName} should be at the beginning of the file`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Checks that all files have one `@file`, `@fileoverview`, or `@overview` tag at the beginning of the file.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-file-overview.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
tags: {
|
||||
description: `The keys of this object are tag names, and the values are configuration
|
||||
objects indicating what will be checked for these whole-file tags.
|
||||
|
||||
Each configuration object has 3 potential boolean keys (which default
|
||||
to \`false\` when this option is supplied).
|
||||
|
||||
1. \`mustExist\` - enforces that all files have a \`@file\`, \`@fileoverview\`, or \`@overview\` tag.
|
||||
2. \`preventDuplicates\` - enforces that duplicate file overview tags within a given file will be reported
|
||||
3. \`initialCommentsOnly\` - reports file overview tags which are not, as per
|
||||
[the docs](https://jsdoc.app/tags-file.html), "at the beginning of
|
||||
the file"–where beginning of the file is interpreted in this rule
|
||||
as being when the overview tag is not preceded by anything other than
|
||||
a comment.
|
||||
|
||||
When no \`tags\` is present, the default is:
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"file": {
|
||||
"initialCommentsOnly": true,
|
||||
"mustExist": true,
|
||||
"preventDuplicates": true,
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
You can add additional tag names and/or override \`file\` if you supply this
|
||||
option, e.g., in place of or in addition to \`file\`, giving other potential
|
||||
file global tags like \`@license\`, \`@copyright\`, \`@author\`, \`@module\` or
|
||||
\`@exports\`, optionally restricting them to a single use or preventing them
|
||||
from being preceded by anything besides comments.
|
||||
|
||||
For example:
|
||||
|
||||
\`\`\`js
|
||||
{
|
||||
"license": {
|
||||
"mustExist": true,
|
||||
"preventDuplicates": true,
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
This would require one and only one \`@license\` in the file, though because
|
||||
\`initialCommentsOnly\` is absent and defaults to \`false\`, the \`@license\`
|
||||
can be anywhere.
|
||||
|
||||
In the case of \`@license\`, you can use this rule along with the
|
||||
\`check-values\` rule (with its \`allowedLicenses\` or \`licensePattern\` options),
|
||||
to enforce a license whitelist be present on every JS file.
|
||||
|
||||
Note that if you choose to use \`preventDuplicates\` with \`license\`, you still
|
||||
have a way to allow multiple licenses for the whole page by using the SPDX
|
||||
"AND" expression, e.g., \`@license (MIT AND GPL-3.0)\`.
|
||||
|
||||
Note that the tag names are the main JSDoc tag name, so you should use \`file\`
|
||||
in this configuration object regardless of whether you have configured
|
||||
\`fileoverview\` instead of \`file\` on \`tagNamePreference\` (i.e., \`fileoverview\`
|
||||
will be checked, but you must use \`file\` on the configuration object).`,
|
||||
patternProperties: {
|
||||
'.*': {
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
initialCommentsOnly: {
|
||||
type: 'boolean',
|
||||
},
|
||||
mustExist: {
|
||||
type: 'boolean',
|
||||
},
|
||||
preventDuplicates: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
nonComment ({
|
||||
node,
|
||||
state,
|
||||
}) {
|
||||
if (!state.hasNonComment) {
|
||||
state.hasNonComment = /** @type {[number, number]} */ (node.range)?.[0];
|
||||
}
|
||||
},
|
||||
});
|
||||
210
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireHyphenBeforeParamDescription.js
generated
vendored
Normal file
210
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireHyphenBeforeParamDescription.js
generated
vendored
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
utils,
|
||||
}) => {
|
||||
const [
|
||||
mainCircumstance,
|
||||
{
|
||||
tags = null,
|
||||
} = {},
|
||||
] = context.options;
|
||||
|
||||
const tgs = /**
|
||||
* @type {null|"any"|{[key: string]: "always"|"never"}}
|
||||
*/ (tags);
|
||||
|
||||
/**
|
||||
* @param {import('@es-joy/jsdoccomment').JsdocTagWithInline} jsdocTag
|
||||
* @param {string} targetTagName
|
||||
* @param {"always"|"never"} [circumstance]
|
||||
* @returns {void}
|
||||
*/
|
||||
const checkHyphens = (jsdocTag, targetTagName, circumstance = mainCircumstance) => {
|
||||
const always = !circumstance || circumstance === 'always';
|
||||
const desc = /** @type {string} */ (utils.getTagDescription(jsdocTag));
|
||||
if (!desc.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startsWithHyphen = (/^\s*-/v).test(desc);
|
||||
const hyphenNewline = (/^\s*-\n/v).test(desc);
|
||||
|
||||
let lines = 0;
|
||||
for (const {
|
||||
tokens,
|
||||
} of jsdocTag.source) {
|
||||
if (tokens.description) {
|
||||
break;
|
||||
}
|
||||
|
||||
lines++;
|
||||
}
|
||||
|
||||
if (always && !hyphenNewline) {
|
||||
if (!startsWithHyphen) {
|
||||
let fixIt = true;
|
||||
for (const {
|
||||
tokens,
|
||||
} of jsdocTag.source) {
|
||||
if (tokens.description) {
|
||||
tokens.description = tokens.description.replace(
|
||||
/^(\s*)/v, '$1- ',
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Linebreak after name since has no description
|
||||
if (tokens.name) {
|
||||
fixIt = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fixIt) {
|
||||
utils.reportJSDoc(
|
||||
`There must be a hyphen before @${targetTagName} description.`,
|
||||
{
|
||||
line: jsdocTag.source[0].number + lines,
|
||||
},
|
||||
() => {},
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (startsWithHyphen) {
|
||||
utils.reportJSDoc(
|
||||
always ?
|
||||
`There must be no hyphen followed by newline after the @${targetTagName} name.` :
|
||||
`There must be no hyphen before @${targetTagName} description.`,
|
||||
{
|
||||
line: jsdocTag.source[0].number + lines,
|
||||
},
|
||||
() => {
|
||||
for (const {
|
||||
tokens,
|
||||
} of jsdocTag.source) {
|
||||
if (tokens.description) {
|
||||
tokens.description = tokens.description.replace(
|
||||
/^\s*-\s*/v, '',
|
||||
);
|
||||
if (hyphenNewline) {
|
||||
tokens.postName = '';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
utils.forEachPreferredTag('param', checkHyphens);
|
||||
if (tgs) {
|
||||
const tagEntries = Object.entries(tgs);
|
||||
for (const [
|
||||
tagName,
|
||||
circumstance,
|
||||
] of tagEntries) {
|
||||
if (tagName === '*') {
|
||||
const preferredParamTag = utils.getPreferredTagName({
|
||||
tagName: 'param',
|
||||
});
|
||||
for (const {
|
||||
tag,
|
||||
} of jsdoc.tags) {
|
||||
if (tag === preferredParamTag || tagEntries.some(([
|
||||
tagNme,
|
||||
]) => {
|
||||
return tagNme !== '*' && tagNme === tag;
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
utils.forEachPreferredTag(tag, (jsdocTag, targetTagName) => {
|
||||
checkHyphens(
|
||||
jsdocTag,
|
||||
targetTagName,
|
||||
/** @type {"always"|"never"} */ (circumstance),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
utils.forEachPreferredTag(tagName, (jsdocTag, targetTagName) => {
|
||||
checkHyphens(
|
||||
jsdocTag,
|
||||
targetTagName,
|
||||
/** @type {"always"|"never"} */ (circumstance),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires a hyphen before the `@param` description (and optionally before `@property` descriptions).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-hyphen-before-param-description.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
description: `If the string is \`"always"\` then a problem is raised when there is no hyphen
|
||||
before the description. If it is \`"never"\` then a problem is raised when there
|
||||
is a hyphen before the description. The default value is \`"always"\`.
|
||||
|
||||
Even if hyphens are set to "always" appear after the tag name, they will
|
||||
actually be forbidden in the event that they are followed immediately by
|
||||
the end of a line (this will otherwise cause Visual Studio Code to display
|
||||
incorrectly).`,
|
||||
enum: [
|
||||
'always', 'never',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
description: `The options object may have the following property to indicate behavior for
|
||||
other tags besides the \`@param\` tag (or the \`@arg\` tag if so set).`,
|
||||
properties: {
|
||||
tags: {
|
||||
anyOf: [
|
||||
{
|
||||
patternProperties: {
|
||||
'.*': {
|
||||
enum: [
|
||||
'always', 'never',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
{
|
||||
enum: [
|
||||
'any',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
description: `Object whose keys indicate different tags to check for the
|
||||
presence or absence of hyphens; the key value should be "always" or "never",
|
||||
indicating how hyphens are to be applied, e.g., \`{property: 'never'}\`
|
||||
to ensure \`@property\` never uses hyphens. A key can also be set as \`*\`, e.g.,
|
||||
\`'*': 'always'\` to apply hyphen checking to any tag (besides the preferred
|
||||
\`@param\` tag which follows the main string option setting and besides any
|
||||
other \`tags\` entries).`,
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'layout',
|
||||
},
|
||||
});
|
||||
897
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireJsdoc.js
generated
vendored
Normal file
897
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireJsdoc.js
generated
vendored
Normal file
|
|
@ -0,0 +1,897 @@
|
|||
import exportParser from '../exportParser.js';
|
||||
import {
|
||||
getSettings,
|
||||
} from '../iterateJsdoc.js';
|
||||
import {
|
||||
enforcedContexts,
|
||||
exemptSpeciaMethods,
|
||||
getContextObject,
|
||||
getFunctionParameterNames,
|
||||
getIndent,
|
||||
hasReturnValue,
|
||||
isConstructor,
|
||||
} from '../jsdocUtils.js';
|
||||
import {
|
||||
getDecorator,
|
||||
getJSDocComment,
|
||||
getReducedASTNode,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* ancestorsOnly: boolean,
|
||||
* esm: boolean,
|
||||
* initModuleExports: boolean,
|
||||
* initWindow: boolean
|
||||
* }} RequireJsdocOpts
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('eslint').Rule.Node|
|
||||
* import('@typescript-eslint/types').TSESTree.Node} ESLintOrTSNode
|
||||
*/
|
||||
|
||||
/** @type {import('json-schema').JSONSchema4} */
|
||||
const OPTIONS_SCHEMA = {
|
||||
additionalProperties: false,
|
||||
description: 'Has the following optional keys.\n',
|
||||
properties: {
|
||||
checkAllFunctionExpressions: {
|
||||
default: false,
|
||||
description: `Normally, when \`FunctionExpression\` is checked, additional checks are
|
||||
added to check the parent contexts where reporting is likely to be desired. If you really
|
||||
want to check *all* function expressions, then set this to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkConstructors: {
|
||||
default: true,
|
||||
description: `A value indicating whether \`constructor\`s should be checked. Defaults to
|
||||
\`true\`. When \`true\`, \`exemptEmptyConstructors\` may still avoid reporting when
|
||||
no parameters or return values are found.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkGetters: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
enum: [
|
||||
'no-setter',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
default: true,
|
||||
description: `A value indicating whether getters should be checked. Besides setting as a
|
||||
boolean, this option can be set to the string \`"no-setter"\` to indicate that
|
||||
getters should be checked but only when there is no setter. This may be useful
|
||||
if one only wishes documentation on one of the two accessors. Defaults to
|
||||
\`false\`.`,
|
||||
},
|
||||
checkSetters: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
enum: [
|
||||
'no-getter',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
default: true,
|
||||
description: `A value indicating whether setters should be checked. Besides setting as a
|
||||
boolean, this option can be set to the string \`"no-getter"\` to indicate that
|
||||
setters should be checked but only when there is no getter. This may be useful
|
||||
if one only wishes documentation on one of the two accessors. Defaults to
|
||||
\`false\`.`,
|
||||
},
|
||||
contexts: {
|
||||
description: `Set this to an array of strings or objects representing the additional AST
|
||||
contexts where you wish the rule to be applied (e.g., \`Property\` for
|
||||
properties). If specified as an object, it should have a \`context\` property
|
||||
and can have an \`inlineCommentBlock\` property which, if set to \`true\`, will
|
||||
add an inline \`/** */\` instead of the regular, multi-line, indented jsdoc
|
||||
block which will otherwise be added. Defaults to an empty array. Contexts
|
||||
may also have their own \`minLineCount\` property which is an integer
|
||||
indicating a minimum number of lines expected for a node in order
|
||||
for it to require documentation.
|
||||
|
||||
Note that you may need to disable \`require\` items (e.g., \`MethodDefinition\`)
|
||||
if you are specifying a more precise form in \`contexts\` (e.g., \`MethodDefinition:not([accessibility="private"] > FunctionExpression\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
inlineCommentBlock: {
|
||||
type: 'boolean',
|
||||
},
|
||||
minLineCount: {
|
||||
type: 'integer',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
enableFixer: {
|
||||
default: true,
|
||||
description: `A boolean on whether to enable the fixer (which adds an empty JSDoc block).
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
exemptEmptyConstructors: {
|
||||
default: false,
|
||||
description: `When \`true\`, the rule will not report missing JSDoc blocks above constructors
|
||||
with no parameters or return values (this is enabled by default as the class
|
||||
name or description should be seen as sufficient to convey intent).
|
||||
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
exemptEmptyFunctions: {
|
||||
default: false,
|
||||
description: `When \`true\`, the rule will not report missing JSDoc blocks above
|
||||
functions/methods with no parameters or return values (intended where
|
||||
function/method names are sufficient for themselves as documentation).
|
||||
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
exemptOverloadedImplementations: {
|
||||
default: false,
|
||||
description: `If set to \`true\` will avoid checking an overloaded function's implementation.
|
||||
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
fixerMessage: {
|
||||
default: '',
|
||||
description: `An optional message to add to the inserted JSDoc block. Defaults to the
|
||||
empty string.`,
|
||||
type: 'string',
|
||||
},
|
||||
minLineCount: {
|
||||
description: `An integer to indicate a minimum number of lines expected for a node in order
|
||||
for it to require documentation. Defaults to \`undefined\`. This option will
|
||||
apply to any context; see \`contexts\` for line counts specific to a context.`,
|
||||
type: 'integer',
|
||||
},
|
||||
publicOnly: {
|
||||
description: `This option will insist that missing JSDoc blocks are only reported for
|
||||
function bodies / class declarations that are exported from the module.
|
||||
May be a boolean or object. If set to \`true\`, the defaults below will be
|
||||
used. If unset, JSDoc block reporting will not be limited to exports.
|
||||
|
||||
This object supports the following optional boolean keys (\`false\` unless
|
||||
otherwise noted):
|
||||
|
||||
- \`ancestorsOnly\` - Optimization to only check node ancestors to check if node is exported
|
||||
- \`esm\` - ESM exports are checked for JSDoc comments (Defaults to \`true\`)
|
||||
- \`cjs\` - CommonJS exports are checked for JSDoc comments (Defaults to \`true\`)
|
||||
- \`window\` - Window global exports are checked for JSDoc comments`,
|
||||
oneOf: [
|
||||
{
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
default: {},
|
||||
properties: {
|
||||
ancestorsOnly: {
|
||||
type: 'boolean',
|
||||
},
|
||||
cjs: {
|
||||
type: 'boolean',
|
||||
},
|
||||
esm: {
|
||||
type: 'boolean',
|
||||
},
|
||||
window: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
require: {
|
||||
additionalProperties: false,
|
||||
default: {},
|
||||
description: `An object with the following optional boolean keys which all default to
|
||||
\`false\` except for \`FunctionDeclaration\` which defaults to \`true\`.`,
|
||||
properties: {
|
||||
ArrowFunctionExpression: {
|
||||
default: false,
|
||||
description: 'Whether to check arrow functions like `() => {}`',
|
||||
type: 'boolean',
|
||||
},
|
||||
ClassDeclaration: {
|
||||
default: false,
|
||||
description: 'Whether to check declarations like `class A {}`',
|
||||
type: 'boolean',
|
||||
},
|
||||
ClassExpression: {
|
||||
default: false,
|
||||
description: 'Whether to check class expressions like `const myClass = class {}`',
|
||||
type: 'boolean',
|
||||
},
|
||||
FunctionDeclaration: {
|
||||
default: true,
|
||||
description: 'Whether to check function declarations like `function a {}`',
|
||||
type: 'boolean',
|
||||
},
|
||||
FunctionExpression: {
|
||||
default: false,
|
||||
description: 'Whether to check function expressions like `const a = function {}`',
|
||||
type: 'boolean',
|
||||
},
|
||||
MethodDefinition: {
|
||||
default: false,
|
||||
description: 'Whether to check method definitions like `class A { someMethodDefinition () {} }`',
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
skipInterveningOverloadedDeclarations: {
|
||||
default: true,
|
||||
description: `If \`true\`, will skip above uncommented overloaded functions to check
|
||||
for a comment block (e.g., at the top of a set of overloaded functions).
|
||||
|
||||
If \`false\`, will force each overloaded function to be checked for a
|
||||
comment block.
|
||||
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} interfaceName
|
||||
* @param {string} methodName
|
||||
* @param {import("eslint").Scope.Scope | null} scope
|
||||
* @returns {import('@typescript-eslint/types').TSESTree.TSMethodSignature|null}
|
||||
*/
|
||||
const getMethodOnInterface = (interfaceName, methodName, scope) => {
|
||||
let scp = scope;
|
||||
while (scp) {
|
||||
for (const {
|
||||
identifiers,
|
||||
name,
|
||||
} of scp.variables) {
|
||||
if (interfaceName !== name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const identifier of identifiers) {
|
||||
const interfaceDeclaration = /** @type {import('@typescript-eslint/types').TSESTree.Identifier & {parent: import('@typescript-eslint/types').TSESTree.TSInterfaceDeclaration}} */ (
|
||||
identifier
|
||||
).parent;
|
||||
/* c8 ignore next 3 -- TS */
|
||||
if (interfaceDeclaration.type !== 'TSInterfaceDeclaration') {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const bodyItem of interfaceDeclaration.body.body) {
|
||||
const methodSig = /** @type {import('@typescript-eslint/types').TSESTree.TSMethodSignature} */ (
|
||||
bodyItem
|
||||
);
|
||||
if (methodName === /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
methodSig.key
|
||||
).name) {
|
||||
return methodSig;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scp = scp.upper;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {import('eslint').SourceCode} sourceCode
|
||||
* @param {import('eslint').Rule.RuleContext} context
|
||||
* @param {import('../iterateJsdoc.js').Settings} settings
|
||||
*/
|
||||
const isExemptedImplementer = (node, sourceCode, context, settings) => {
|
||||
if (node.type === 'FunctionExpression' &&
|
||||
node.parent.type === 'MethodDefinition' &&
|
||||
node.parent.parent.type === 'ClassBody' &&
|
||||
node.parent.parent.parent.type === 'ClassDeclaration' &&
|
||||
'implements' in node.parent.parent.parent
|
||||
) {
|
||||
const implments = /** @type {import('@typescript-eslint/types').TSESTree.TSClassImplements[]} */ (
|
||||
node.parent.parent.parent.implements
|
||||
);
|
||||
|
||||
const {
|
||||
name: methodName,
|
||||
} = /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
node.parent.key
|
||||
);
|
||||
|
||||
for (const impl of implments) {
|
||||
const {
|
||||
name: interfaceName,
|
||||
} = /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
impl.expression
|
||||
);
|
||||
|
||||
const interfaceMethodNode = getMethodOnInterface(interfaceName, methodName, node && (
|
||||
(sourceCode.getScope &&
|
||||
/* c8 ignore next 3 */
|
||||
sourceCode.getScope(node)) ||
|
||||
// @ts-expect-error ESLint 8
|
||||
context.getScope()
|
||||
));
|
||||
if (interfaceMethodNode) {
|
||||
// @ts-expect-error Ok
|
||||
const comment = getJSDocComment(sourceCode, interfaceMethodNode, settings);
|
||||
if (comment) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.RuleContext} context
|
||||
* @param {import('json-schema').JSONSchema4Object} baseObject
|
||||
* @param {string} option
|
||||
* @param {string} key
|
||||
* @returns {boolean|undefined}
|
||||
*/
|
||||
const getOption = (context, baseObject, option, key) => {
|
||||
if (context.options[0] && option in context.options[0] &&
|
||||
// Todo: boolean shouldn't be returning property, but
|
||||
// tests currently require
|
||||
(typeof context.options[0][option] === 'boolean' ||
|
||||
key in context.options[0][option])
|
||||
) {
|
||||
return context.options[0][option][key];
|
||||
}
|
||||
|
||||
return /** @type {{[key: string]: {default?: boolean|undefined}}} */ (
|
||||
baseObject.properties
|
||||
)[key].default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.RuleContext} context
|
||||
* @param {import('../iterateJsdoc.js').Settings} settings
|
||||
* @returns {{
|
||||
* checkAllFunctionExpressions: boolean,
|
||||
* contexts: (string|{
|
||||
* context: string,
|
||||
* inlineCommentBlock: boolean,
|
||||
* minLineCount: import('../iterateJsdoc.js').Integer
|
||||
* })[],
|
||||
* enableFixer: boolean,
|
||||
* exemptEmptyConstructors: boolean,
|
||||
* exemptEmptyFunctions: boolean,
|
||||
* skipInterveningOverloadedDeclarations: boolean,
|
||||
* exemptOverloadedImplementations: boolean,
|
||||
* fixerMessage: string,
|
||||
* minLineCount: undefined|import('../iterateJsdoc.js').Integer,
|
||||
* publicOnly: boolean|{[key: string]: boolean|undefined}
|
||||
* require: {[key: string]: boolean|undefined}
|
||||
* }}
|
||||
*/
|
||||
const getOptions = (context, settings) => {
|
||||
const {
|
||||
checkAllFunctionExpressions = false,
|
||||
contexts = settings.contexts || [],
|
||||
enableFixer = true,
|
||||
exemptEmptyConstructors = true,
|
||||
exemptEmptyFunctions = false,
|
||||
exemptOverloadedImplementations = false,
|
||||
fixerMessage = '',
|
||||
minLineCount = undefined,
|
||||
publicOnly,
|
||||
skipInterveningOverloadedDeclarations = true,
|
||||
} = context.options[0] || {};
|
||||
|
||||
return {
|
||||
checkAllFunctionExpressions,
|
||||
contexts,
|
||||
enableFixer,
|
||||
exemptEmptyConstructors,
|
||||
exemptEmptyFunctions,
|
||||
exemptOverloadedImplementations,
|
||||
fixerMessage,
|
||||
minLineCount,
|
||||
publicOnly: ((baseObj) => {
|
||||
if (!publicOnly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @type {{[key: string]: boolean|undefined}} */
|
||||
const properties = {};
|
||||
for (const prop of Object.keys(
|
||||
/** @type {import('json-schema').JSONSchema4Object} */ (
|
||||
/** @type {import('json-schema').JSONSchema4Object} */ (
|
||||
baseObj
|
||||
).properties),
|
||||
)) {
|
||||
const opt = getOption(
|
||||
context,
|
||||
/** @type {import('json-schema').JSONSchema4Object} */ (baseObj),
|
||||
'publicOnly',
|
||||
prop,
|
||||
);
|
||||
|
||||
properties[prop] = opt;
|
||||
}
|
||||
|
||||
return properties;
|
||||
})(
|
||||
/** @type {import('json-schema').JSONSchema4Object} */
|
||||
(
|
||||
/** @type {import('json-schema').JSONSchema4Object} */
|
||||
(
|
||||
/** @type {import('json-schema').JSONSchema4Object} */
|
||||
(
|
||||
OPTIONS_SCHEMA.properties
|
||||
).publicOnly
|
||||
).oneOf
|
||||
)[1],
|
||||
),
|
||||
require: ((baseObj) => {
|
||||
/** @type {{[key: string]: boolean|undefined}} */
|
||||
const properties = {};
|
||||
for (const prop of Object.keys(
|
||||
/** @type {import('json-schema').JSONSchema4Object} */ (
|
||||
/** @type {import('json-schema').JSONSchema4Object} */ (
|
||||
baseObj
|
||||
).properties),
|
||||
)) {
|
||||
const opt = getOption(
|
||||
context,
|
||||
/** @type {import('json-schema').JSONSchema4Object} */
|
||||
(baseObj),
|
||||
'require',
|
||||
prop,
|
||||
);
|
||||
properties[prop] = opt;
|
||||
}
|
||||
|
||||
return properties;
|
||||
})(
|
||||
/** @type {import('json-schema').JSONSchema4Object} */
|
||||
(OPTIONS_SCHEMA.properties).require,
|
||||
),
|
||||
skipInterveningOverloadedDeclarations,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ESLintOrTSNode} node
|
||||
*/
|
||||
const isFunctionWithOverload = (node) => {
|
||||
if (node.type !== 'FunctionDeclaration') {
|
||||
return false;
|
||||
}
|
||||
|
||||
let parent;
|
||||
let child;
|
||||
|
||||
if (node.parent?.type === 'Program') {
|
||||
parent = node.parent;
|
||||
child = node;
|
||||
} else if (node.parent?.type === 'ExportNamedDeclaration' &&
|
||||
node.parent?.parent.type === 'Program') {
|
||||
parent = node.parent?.parent;
|
||||
child = node.parent;
|
||||
}
|
||||
|
||||
if (!child || !parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const functionName = node.id?.name;
|
||||
|
||||
const idx = parent.body.indexOf(child);
|
||||
const prevSibling = parent.body[idx - 1];
|
||||
|
||||
return (
|
||||
// @ts-expect-error Should be ok
|
||||
(prevSibling?.type === 'TSDeclareFunction' &&
|
||||
// @ts-expect-error Should be ok
|
||||
functionName === prevSibling.id.name) ||
|
||||
(prevSibling?.type === 'ExportNamedDeclaration' &&
|
||||
// @ts-expect-error Should be ok
|
||||
prevSibling.declaration?.type === 'TSDeclareFunction' &&
|
||||
// @ts-expect-error Should be ok
|
||||
prevSibling.declaration?.id?.name === functionName)
|
||||
);
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
export default {
|
||||
create (context) {
|
||||
/* c8 ignore next -- Fallback to deprecated method */
|
||||
const {
|
||||
sourceCode = context.getSourceCode(),
|
||||
} = context;
|
||||
const settings = getSettings(context);
|
||||
if (!settings) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const opts = getOptions(context, settings);
|
||||
|
||||
const {
|
||||
checkAllFunctionExpressions,
|
||||
contexts,
|
||||
enableFixer,
|
||||
exemptEmptyConstructors,
|
||||
exemptEmptyFunctions,
|
||||
exemptOverloadedImplementations,
|
||||
fixerMessage,
|
||||
minLineCount,
|
||||
require: requireOption,
|
||||
skipInterveningOverloadedDeclarations,
|
||||
} = opts;
|
||||
|
||||
const publicOnly =
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
* [key: string]: boolean | undefined;
|
||||
* }}
|
||||
*/ (
|
||||
opts.publicOnly
|
||||
);
|
||||
|
||||
/**
|
||||
* @type {import('../iterateJsdoc.js').CheckJsdoc}
|
||||
*/
|
||||
const checkJsDoc = (info, _handler, node) => {
|
||||
if (
|
||||
// Optimize
|
||||
minLineCount !== undefined || contexts.some((ctxt) => {
|
||||
if (typeof ctxt === 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
minLineCount: count,
|
||||
} = ctxt;
|
||||
return count !== undefined;
|
||||
})
|
||||
) {
|
||||
/**
|
||||
* @param {undefined|import('../iterateJsdoc.js').Integer} count
|
||||
*/
|
||||
const underMinLine = (count) => {
|
||||
return count !== undefined && count >
|
||||
(sourceCode.getText(node).match(/\n/gv)?.length ?? 0) + 1;
|
||||
};
|
||||
|
||||
if (underMinLine(minLineCount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
minLineCount: contextMinLineCount,
|
||||
} =
|
||||
/**
|
||||
* @type {{
|
||||
* context: string;
|
||||
* inlineCommentBlock: boolean;
|
||||
* minLineCount: number;
|
||||
* }}
|
||||
*/ (contexts.find((ctxt) => {
|
||||
if (typeof ctxt === 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
context: ctx,
|
||||
} = ctxt;
|
||||
return ctx === (info.selector || node.type);
|
||||
})) || {};
|
||||
if (underMinLine(contextMinLineCount)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (exemptOverloadedImplementations && isFunctionWithOverload(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jsDocNode = getJSDocComment(
|
||||
sourceCode, node, settings, {
|
||||
checkOverloads: skipInterveningOverloadedDeclarations,
|
||||
},
|
||||
);
|
||||
|
||||
if (jsDocNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For those who have options configured against ANY constructors (or
|
||||
// setters or getters) being reported
|
||||
if (exemptSpeciaMethods(
|
||||
{
|
||||
description: '',
|
||||
inlineTags: [],
|
||||
problems: [],
|
||||
source: [],
|
||||
tags: [],
|
||||
},
|
||||
node,
|
||||
context,
|
||||
[
|
||||
OPTIONS_SCHEMA,
|
||||
],
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
// Avoid reporting param-less, return-less functions (when
|
||||
// `exemptEmptyFunctions` option is set)
|
||||
exemptEmptyFunctions && info.isFunctionContext ||
|
||||
|
||||
// Avoid reporting param-less, return-less constructor methods (when
|
||||
// `exemptEmptyConstructors` option is set)
|
||||
exemptEmptyConstructors && isConstructor(node)
|
||||
) {
|
||||
const functionParameterNames = getFunctionParameterNames(node);
|
||||
if (!functionParameterNames.length && !hasReturnValue(node)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isExemptedImplementer(node, sourceCode, context, settings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fix = /** @type {import('eslint').Rule.ReportFixer} */ (fixer) => {
|
||||
// Default to one line break if the `minLines`/`maxLines` settings allow
|
||||
const lines = settings.minLines === 0 && settings.maxLines >= 1 ? 1 : settings.minLines;
|
||||
/** @type {ESLintOrTSNode|import('@typescript-eslint/types').TSESTree.Decorator} */
|
||||
let baseNode = getReducedASTNode(node, sourceCode);
|
||||
|
||||
const decorator = getDecorator(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(baseNode),
|
||||
);
|
||||
if (decorator) {
|
||||
baseNode = decorator;
|
||||
}
|
||||
|
||||
const indent = getIndent({
|
||||
text: sourceCode.getText(
|
||||
/** @type {import('eslint').Rule.Node} */ (baseNode),
|
||||
/** @type {import('eslint').AST.SourceLocation} */
|
||||
(
|
||||
/** @type {import('eslint').Rule.Node} */ (baseNode).loc
|
||||
).start.column,
|
||||
),
|
||||
});
|
||||
|
||||
const {
|
||||
inlineCommentBlock,
|
||||
} =
|
||||
/**
|
||||
* @type {{
|
||||
* context: string,
|
||||
* inlineCommentBlock: boolean,
|
||||
* minLineCount: import('../iterateJsdoc.js').Integer
|
||||
* }}
|
||||
*/ (contexts.find((contxt) => {
|
||||
if (typeof contxt === 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
context: ctxt,
|
||||
} = contxt;
|
||||
return ctxt === node.type;
|
||||
})) || {};
|
||||
const insertion = (inlineCommentBlock ?
|
||||
`/** ${fixerMessage}` :
|
||||
`/**\n${indent}*${fixerMessage}\n${indent}`) +
|
||||
`*/${'\n'.repeat(lines)}${indent.slice(0, -1)}`;
|
||||
|
||||
return fixer.insertTextBefore(
|
||||
/** @type {import('eslint').Rule.Node} */
|
||||
(baseNode),
|
||||
insertion,
|
||||
);
|
||||
};
|
||||
|
||||
const report = () => {
|
||||
const {
|
||||
start,
|
||||
} = /** @type {import('eslint').AST.SourceLocation} */ (node.loc);
|
||||
const loc = {
|
||||
end: {
|
||||
column: 0,
|
||||
line: start.line + 1,
|
||||
},
|
||||
start,
|
||||
};
|
||||
context.report({
|
||||
fix: enableFixer ? fix : null,
|
||||
loc,
|
||||
messageId: 'missingJsDoc',
|
||||
node,
|
||||
});
|
||||
};
|
||||
|
||||
if (publicOnly) {
|
||||
/** @type {RequireJsdocOpts} */
|
||||
const opt = {
|
||||
ancestorsOnly: Boolean(publicOnly?.ancestorsOnly ?? false),
|
||||
esm: Boolean(publicOnly?.esm ?? true),
|
||||
initModuleExports: Boolean(publicOnly?.cjs ?? true),
|
||||
initWindow: Boolean(publicOnly?.window ?? false),
|
||||
};
|
||||
const exported = exportParser.isUncommentedExport(node, sourceCode, opt, settings);
|
||||
|
||||
if (exported) {
|
||||
report();
|
||||
}
|
||||
} else {
|
||||
report();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} prop
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasOption = (prop) => {
|
||||
return requireOption[prop] || contexts.some((ctxt) => {
|
||||
return typeof ctxt === 'object' ? ctxt.context === prop : ctxt === prop;
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
...getContextObject(
|
||||
enforcedContexts(context, [], settings),
|
||||
checkJsDoc,
|
||||
),
|
||||
ArrowFunctionExpression (node) {
|
||||
if (!hasOption('ArrowFunctionExpression')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
[
|
||||
'AssignmentExpression', 'ExportDefaultDeclaration', 'VariableDeclarator',
|
||||
].includes(node.parent.type) ||
|
||||
[
|
||||
'ClassProperty', 'ObjectProperty', 'Property', 'PropertyDefinition',
|
||||
].includes(node.parent.type) &&
|
||||
node ===
|
||||
/**
|
||||
* @type {import('@typescript-eslint/types').TSESTree.Property|
|
||||
* import('@typescript-eslint/types').TSESTree.PropertyDefinition
|
||||
* }
|
||||
*/
|
||||
(node.parent).value
|
||||
) {
|
||||
checkJsDoc({
|
||||
isFunctionContext: true,
|
||||
}, null, node);
|
||||
}
|
||||
},
|
||||
|
||||
ClassDeclaration (node) {
|
||||
if (!hasOption('ClassDeclaration')) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkJsDoc({
|
||||
isFunctionContext: false,
|
||||
}, null, node);
|
||||
},
|
||||
|
||||
ClassExpression (node) {
|
||||
if (!hasOption('ClassExpression')) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkJsDoc({
|
||||
isFunctionContext: false,
|
||||
}, null, node);
|
||||
},
|
||||
|
||||
FunctionDeclaration (node) {
|
||||
if (!hasOption('FunctionDeclaration')) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkJsDoc({
|
||||
isFunctionContext: true,
|
||||
}, null, node);
|
||||
},
|
||||
|
||||
FunctionExpression (node) {
|
||||
if (!hasOption('FunctionExpression')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkAllFunctionExpressions ||
|
||||
[
|
||||
'AssignmentExpression', 'ExportDefaultDeclaration', 'VariableDeclarator',
|
||||
].includes(node.parent.type) ||
|
||||
[
|
||||
'ClassProperty', 'ObjectProperty', 'Property', 'PropertyDefinition',
|
||||
].includes(node.parent.type) &&
|
||||
node ===
|
||||
/**
|
||||
* @type {import('@typescript-eslint/types').TSESTree.Property|
|
||||
* import('@typescript-eslint/types').TSESTree.PropertyDefinition
|
||||
* }
|
||||
*/
|
||||
(node.parent).value
|
||||
) {
|
||||
checkJsDoc({
|
||||
isFunctionContext: true,
|
||||
}, null, node);
|
||||
}
|
||||
},
|
||||
|
||||
MethodDefinition (node) {
|
||||
if (!hasOption('MethodDefinition')) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkJsDoc({
|
||||
isFunctionContext: true,
|
||||
selector: 'MethodDefinition',
|
||||
}, null, /** @type {import('eslint').Rule.Node} */ (node.value));
|
||||
},
|
||||
};
|
||||
},
|
||||
meta: {
|
||||
docs: {
|
||||
category: 'Stylistic Issues',
|
||||
description: 'Checks for presence of JSDoc comments, on functions and potentially other contexts (optionally limited to exports).',
|
||||
recommended: true,
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-jsdoc.md#repos-sticky-header',
|
||||
},
|
||||
|
||||
fixable: 'code',
|
||||
|
||||
messages: {
|
||||
missingJsDoc: 'Missing JSDoc comment.',
|
||||
},
|
||||
|
||||
schema: [
|
||||
OPTIONS_SCHEMA,
|
||||
],
|
||||
|
||||
type: 'suggestion',
|
||||
},
|
||||
};
|
||||
848
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireParam.js
generated
vendored
Normal file
848
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireParam.js
generated
vendored
Normal file
|
|
@ -0,0 +1,848 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @typedef {[string, boolean, () => RootNamerReturn]} RootNamerReturn
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string[]} desiredRoots
|
||||
* @param {number} currentIndex
|
||||
* @returns {RootNamerReturn}
|
||||
*/
|
||||
const rootNamer = (desiredRoots, currentIndex) => {
|
||||
/** @type {string} */
|
||||
let name;
|
||||
let idx = currentIndex;
|
||||
const incremented = desiredRoots.length <= 1;
|
||||
if (incremented) {
|
||||
const base = desiredRoots[0];
|
||||
const suffix = idx++;
|
||||
name = `${base}${suffix}`;
|
||||
} else {
|
||||
name = /** @type {string} */ (desiredRoots.shift());
|
||||
}
|
||||
|
||||
return [
|
||||
name,
|
||||
incremented,
|
||||
() => {
|
||||
return rootNamer(desiredRoots, idx);
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
/* eslint-disable complexity -- Temporary */
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
node,
|
||||
utils,
|
||||
}) => {
|
||||
/* eslint-enable complexity -- Temporary */
|
||||
if (utils.avoidDocs()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Param type is specified by type in @type
|
||||
if (utils.hasTag('type')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
autoIncrementBase = 0,
|
||||
checkDestructured = true,
|
||||
checkDestructuredRoots = true,
|
||||
checkRestProperty = false,
|
||||
checkTypesPattern = '/^(?:[oO]bject|[aA]rray|PlainObject|Generic(?:Object|Array))$/',
|
||||
enableFixer = true,
|
||||
enableRestElementFixer = true,
|
||||
enableRootFixer = true,
|
||||
ignoreWhenAllParamsMissing = false,
|
||||
interfaceExemptsParamsCheck = false,
|
||||
unnamedRootBase = [
|
||||
'root',
|
||||
],
|
||||
useDefaultObjectProperties = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
if (interfaceExemptsParamsCheck && node &&
|
||||
node.parent?.type === 'VariableDeclarator' &&
|
||||
'typeAnnotation' in node.parent.id && node.parent.id.typeAnnotation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const preferredTagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'param',
|
||||
}));
|
||||
if (!preferredTagName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const functionParameterNames = utils.getFunctionParameterNames(useDefaultObjectProperties, interfaceExemptsParamsCheck);
|
||||
if (!functionParameterNames.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jsdocParameterNames =
|
||||
/**
|
||||
* @type {{
|
||||
* idx: import('../iterateJsdoc.js').Integer;
|
||||
* name: string;
|
||||
* type: string;
|
||||
* }[]}
|
||||
*/ (utils.getJsdocTagsDeep(preferredTagName));
|
||||
|
||||
if (ignoreWhenAllParamsMissing && !jsdocParameterNames.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shallowJsdocParameterNames = jsdocParameterNames.filter((tag) => {
|
||||
return !tag.name.includes('.');
|
||||
}).map((tag, idx) => {
|
||||
return {
|
||||
...tag,
|
||||
idx,
|
||||
};
|
||||
});
|
||||
|
||||
const checkTypesRegex = utils.getRegexFromString(checkTypesPattern);
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
* functionParameterIdx: import('../iterateJsdoc.js').Integer,
|
||||
* functionParameterName: string,
|
||||
* inc: boolean|undefined,
|
||||
* remove?: true,
|
||||
* type?: string|undefined
|
||||
* }[]}
|
||||
*/
|
||||
const missingTags = [];
|
||||
const flattenedRoots = utils.flattenRoots(functionParameterNames).names;
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
* [key: string]: import('../iterateJsdoc.js').Integer
|
||||
* }}
|
||||
*/
|
||||
const paramIndex = {};
|
||||
|
||||
/**
|
||||
* @param {string} cur
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasParamIndex = (cur) => {
|
||||
return utils.dropPathSegmentQuotes(String(cur)) in paramIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string|number|undefined} cur
|
||||
* @returns {import('../iterateJsdoc.js').Integer}
|
||||
*/
|
||||
const getParamIndex = (cur) => {
|
||||
return paramIndex[utils.dropPathSegmentQuotes(String(cur))];
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} cur
|
||||
* @param {import('../iterateJsdoc.js').Integer} idx
|
||||
* @returns {void}
|
||||
*/
|
||||
const setParamIndex = (cur, idx) => {
|
||||
paramIndex[utils.dropPathSegmentQuotes(String(cur))] = idx;
|
||||
};
|
||||
|
||||
for (const [
|
||||
idx,
|
||||
cur,
|
||||
] of flattenedRoots.entries()) {
|
||||
setParamIndex(cur, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {(import('@es-joy/jsdoccomment').JsdocTagWithInline & {
|
||||
* newAdd?: boolean
|
||||
* })[]} jsdocTags
|
||||
* @param {import('../iterateJsdoc.js').Integer} indexAtFunctionParams
|
||||
* @returns {{
|
||||
* foundIndex: import('../iterateJsdoc.js').Integer,
|
||||
* tagLineCount: import('../iterateJsdoc.js').Integer,
|
||||
* }}
|
||||
*/
|
||||
const findExpectedIndex = (jsdocTags, indexAtFunctionParams) => {
|
||||
// Get the parameters that come after the current index in the flattened order
|
||||
const remainingFlattenedRoots = flattenedRoots.slice((indexAtFunctionParams || 0) + 1);
|
||||
|
||||
// Find the first existing tag that comes after the current parameter in the flattened order
|
||||
const foundIndex = jsdocTags.findIndex(({
|
||||
name,
|
||||
newAdd,
|
||||
}) => {
|
||||
if (newAdd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the tag name matches any of the remaining flattened roots
|
||||
return remainingFlattenedRoots.some((flattenedRoot) => {
|
||||
// The flattened roots don't have the root prefix (e.g., "bar", "bar.baz")
|
||||
// but JSDoc tags do (e.g., "root0", "root0.bar", "root0.bar.baz")
|
||||
// So we need to check if the tag name ends with the flattened root
|
||||
|
||||
// Check if tag name ends with ".<flattenedRoot>"
|
||||
if (name.endsWith(`.${flattenedRoot}`)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Also check if tag name exactly matches the flattenedRoot
|
||||
// (for single-level params)
|
||||
if (name === flattenedRoot) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
const tags = foundIndex > -1 ?
|
||||
jsdocTags.slice(0, foundIndex) :
|
||||
jsdocTags.filter(({
|
||||
tag,
|
||||
}) => {
|
||||
return tag === preferredTagName;
|
||||
});
|
||||
|
||||
let tagLineCount = 0;
|
||||
for (const {
|
||||
source,
|
||||
} of tags) {
|
||||
for (const {
|
||||
tokens: {
|
||||
end,
|
||||
},
|
||||
} of source) {
|
||||
if (!end) {
|
||||
tagLineCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
foundIndex,
|
||||
tagLineCount,
|
||||
};
|
||||
};
|
||||
|
||||
let [
|
||||
nextRootName,
|
||||
incremented,
|
||||
namer,
|
||||
] = rootNamer([
|
||||
...unnamedRootBase,
|
||||
], autoIncrementBase);
|
||||
|
||||
const thisOffset = functionParameterNames[0] === 'this' ? 1 : 0;
|
||||
|
||||
for (const [
|
||||
functionParameterIdx,
|
||||
functionParameterName,
|
||||
] of functionParameterNames.entries()) {
|
||||
let inc;
|
||||
if (Array.isArray(functionParameterName)) {
|
||||
const matchedJsdoc = shallowJsdocParameterNames[functionParameterIdx - thisOffset];
|
||||
|
||||
/** @type {string} */
|
||||
let rootName;
|
||||
if (functionParameterName[0]) {
|
||||
rootName = functionParameterName[0];
|
||||
} else if (matchedJsdoc && matchedJsdoc.name) {
|
||||
rootName = matchedJsdoc.name;
|
||||
if (matchedJsdoc.type && matchedJsdoc.type.search(checkTypesRegex) === -1) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
rootName = nextRootName;
|
||||
inc = incremented;
|
||||
}
|
||||
|
||||
[
|
||||
nextRootName,
|
||||
incremented,
|
||||
namer,
|
||||
] = namer();
|
||||
|
||||
const {
|
||||
hasPropertyRest,
|
||||
hasRestElement,
|
||||
names,
|
||||
rests,
|
||||
} = /**
|
||||
* @type {import('../jsdocUtils.js').FlattendRootInfo & {
|
||||
* annotationParamName?: string | undefined;
|
||||
* }}
|
||||
*/ (functionParameterName[1]);
|
||||
const notCheckingNames = [];
|
||||
if (!enableRestElementFixer && hasRestElement) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!checkDestructuredRoots) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const [
|
||||
idx,
|
||||
paramName,
|
||||
] of names.entries()) {
|
||||
// Add root if the root name is not in the docs (and is not already
|
||||
// in the tags to be fixed)
|
||||
if (!jsdocParameterNames.find(({
|
||||
name,
|
||||
}) => {
|
||||
return name === rootName;
|
||||
}) && !missingTags.find(({
|
||||
functionParameterName: fpn,
|
||||
}) => {
|
||||
return fpn === rootName;
|
||||
})) {
|
||||
const emptyParamIdx = jsdocParameterNames.findIndex(({
|
||||
name,
|
||||
}) => {
|
||||
return !name;
|
||||
});
|
||||
|
||||
if (emptyParamIdx > -1) {
|
||||
missingTags.push({
|
||||
functionParameterIdx: emptyParamIdx,
|
||||
functionParameterName: rootName,
|
||||
inc,
|
||||
remove: true,
|
||||
});
|
||||
} else {
|
||||
missingTags.push({
|
||||
functionParameterIdx: hasParamIndex(rootName) ?
|
||||
getParamIndex(rootName) :
|
||||
getParamIndex(paramName),
|
||||
functionParameterName: rootName,
|
||||
inc,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!checkDestructured) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!checkRestProperty && rests[idx]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fullParamName = `${rootName}.${paramName}`;
|
||||
|
||||
const notCheckingName = jsdocParameterNames.find(({
|
||||
name,
|
||||
type: paramType,
|
||||
}) => {
|
||||
return utils.comparePaths(name)(fullParamName) && paramType.search(checkTypesRegex) === -1 && paramType !== '';
|
||||
});
|
||||
|
||||
if (notCheckingName !== undefined) {
|
||||
notCheckingNames.push(notCheckingName.name);
|
||||
}
|
||||
|
||||
if (notCheckingNames.find((name) => {
|
||||
return fullParamName.startsWith(name);
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (jsdocParameterNames && !jsdocParameterNames.find(({
|
||||
name,
|
||||
}) => {
|
||||
return utils.comparePaths(name)(fullParamName);
|
||||
})) {
|
||||
missingTags.push({
|
||||
functionParameterIdx: getParamIndex(
|
||||
functionParameterName[0] ? fullParamName : paramName,
|
||||
),
|
||||
functionParameterName: fullParamName,
|
||||
inc,
|
||||
type: hasRestElement && !hasPropertyRest ? '{...any}' : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @type {string} */
|
||||
let funcParamName;
|
||||
let type;
|
||||
if (typeof functionParameterName === 'object') {
|
||||
if (!enableRestElementFixer && functionParameterName.restElement) {
|
||||
continue;
|
||||
}
|
||||
|
||||
funcParamName = /** @type {string} */ (functionParameterName.name);
|
||||
type = '{...any}';
|
||||
} else {
|
||||
funcParamName = /** @type {string} */ (functionParameterName);
|
||||
}
|
||||
|
||||
if (jsdocParameterNames && !jsdocParameterNames.find(({
|
||||
name,
|
||||
}) => {
|
||||
return name === funcParamName;
|
||||
}) && funcParamName !== 'this') {
|
||||
missingTags.push({
|
||||
functionParameterIdx: getParamIndex(funcParamName),
|
||||
functionParameterName: funcParamName,
|
||||
inc,
|
||||
type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {{
|
||||
* functionParameterIdx: import('../iterateJsdoc.js').Integer,
|
||||
* functionParameterName: string,
|
||||
* remove?: true,
|
||||
* inc?: boolean,
|
||||
* type?: string
|
||||
* }} cfg
|
||||
*/
|
||||
const fix = ({
|
||||
functionParameterIdx,
|
||||
functionParameterName,
|
||||
inc,
|
||||
remove,
|
||||
type,
|
||||
}) => {
|
||||
if (inc && !enableRootFixer) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('../iterateJsdoc.js').Integer} tagIndex
|
||||
* @param {import('../iterateJsdoc.js').Integer} sourceIndex
|
||||
* @param {import('../iterateJsdoc.js').Integer} spliceCount
|
||||
* @returns {void}
|
||||
*/
|
||||
const createTokens = (tagIndex, sourceIndex, spliceCount) => {
|
||||
// console.log(sourceIndex, tagIndex, jsdoc.tags, jsdoc.source);
|
||||
const tokens = {
|
||||
number: sourceIndex + 1,
|
||||
source: '',
|
||||
tokens: {
|
||||
delimiter: '*',
|
||||
description: '',
|
||||
end: '',
|
||||
lineEnd: '',
|
||||
name: functionParameterName,
|
||||
newAdd: true,
|
||||
postDelimiter: ' ',
|
||||
postName: '',
|
||||
postTag: ' ',
|
||||
postType: type ? ' ' : '',
|
||||
start: jsdoc.source[sourceIndex].tokens.start,
|
||||
tag: `@${preferredTagName}`,
|
||||
type: type ?? '',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(import('@es-joy/jsdoccomment').JsdocTagWithInline & {
|
||||
* newAdd?: true
|
||||
* })[]}
|
||||
*/ (jsdoc.tags).splice(tagIndex, spliceCount, {
|
||||
description: '',
|
||||
inlineTags: [],
|
||||
name: functionParameterName,
|
||||
newAdd: true,
|
||||
optional: false,
|
||||
problems: [],
|
||||
source: [
|
||||
tokens,
|
||||
],
|
||||
tag: preferredTagName,
|
||||
type: type ?? '',
|
||||
});
|
||||
const firstNumber = jsdoc.source[0].number;
|
||||
jsdoc.source.splice(sourceIndex, spliceCount, tokens);
|
||||
for (const [
|
||||
idx,
|
||||
src,
|
||||
] of jsdoc.source.slice(sourceIndex).entries()) {
|
||||
src.number = firstNumber + sourceIndex + idx;
|
||||
}
|
||||
};
|
||||
|
||||
const offset = jsdoc.source.findIndex(({
|
||||
tokens: {
|
||||
end,
|
||||
tag,
|
||||
},
|
||||
}) => {
|
||||
return tag || end;
|
||||
});
|
||||
if (remove) {
|
||||
createTokens(functionParameterIdx, offset + functionParameterIdx, 1);
|
||||
} else {
|
||||
const {
|
||||
foundIndex,
|
||||
tagLineCount: expectedIdx,
|
||||
} =
|
||||
findExpectedIndex(jsdoc.tags, functionParameterIdx);
|
||||
|
||||
const firstParamLine = jsdoc.source.findIndex(({
|
||||
tokens,
|
||||
}) => {
|
||||
return tokens.tag === `@${preferredTagName}`;
|
||||
});
|
||||
const baseOffset = foundIndex > -1 || firstParamLine === -1 ?
|
||||
offset :
|
||||
firstParamLine;
|
||||
|
||||
createTokens(expectedIdx, baseOffset + expectedIdx, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
const fixer = () => {
|
||||
for (const missingTag of missingTags) {
|
||||
fix(missingTag);
|
||||
}
|
||||
};
|
||||
|
||||
if (missingTags.length && jsdoc.source.length === 1) {
|
||||
utils.makeMultiline();
|
||||
}
|
||||
|
||||
for (const {
|
||||
functionParameterName,
|
||||
} of missingTags) {
|
||||
utils.reportJSDoc(
|
||||
`Missing JSDoc @${preferredTagName} "${functionParameterName}" declaration.`,
|
||||
null,
|
||||
enableFixer ? fixer : null,
|
||||
);
|
||||
}
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that all function parameters are documented with a `@param` tag.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-param.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
autoIncrementBase: {
|
||||
default: 0,
|
||||
description: `Numeric to indicate the number at which to begin auto-incrementing roots.
|
||||
Defaults to \`0\`.`,
|
||||
type: 'integer',
|
||||
},
|
||||
checkConstructors: {
|
||||
default: true,
|
||||
description: `A value indicating whether \`constructor\`s should be checked. Defaults to
|
||||
\`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkDestructured: {
|
||||
default: true,
|
||||
description: 'Whether to require destructured properties. Defaults to `true`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
checkDestructuredRoots: {
|
||||
default: true,
|
||||
description: `Whether to check the existence of a corresponding \`@param\` for root objects
|
||||
of destructured properties (e.g., that for \`function ({a, b}) {}\`, that there
|
||||
is something like \`@param myRootObj\` defined that can correspond to
|
||||
the \`{a, b}\` object parameter).
|
||||
|
||||
If \`checkDestructuredRoots\` is \`false\`, \`checkDestructured\` will also be
|
||||
implied to be \`false\` (i.e., the inside of the roots will not be checked
|
||||
either, e.g., it will also not complain if \`a\` or \`b\` do not have their own
|
||||
documentation). Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkGetters: {
|
||||
default: false,
|
||||
description: 'A value indicating whether getters should be checked. Defaults to `false`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
checkRestProperty: {
|
||||
default: false,
|
||||
description: `If set to \`true\`, will report (and add fixer insertions) for missing rest
|
||||
properties. Defaults to \`false\`.
|
||||
|
||||
If set to \`true\`, note that you can still document the subproperties of the
|
||||
rest property using other jsdoc features, e.g., \`@typedef\`:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @typedef ExtraOptions
|
||||
* @property innerProp1
|
||||
* @property innerProp2
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param cfg
|
||||
* @param cfg.num
|
||||
* @param {ExtraOptions} extra
|
||||
*/
|
||||
function quux ({num, ...extra}) {
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
Setting this option to \`false\` (the default) may be useful in cases where
|
||||
you already have separate \`@param\` definitions for each of the properties
|
||||
within the rest property.
|
||||
|
||||
For example, with the option disabled, this will not give an error despite
|
||||
\`extra\` not having any definition:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @param cfg
|
||||
* @param cfg.num
|
||||
*/
|
||||
function quux ({num, ...extra}) {
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
Nor will this:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @param cfg
|
||||
* @param cfg.num
|
||||
* @param cfg.innerProp1
|
||||
* @param cfg.innerProp2
|
||||
*/
|
||||
function quux ({num, ...extra}) {
|
||||
}
|
||||
\`\`\``,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkSetters: {
|
||||
default: false,
|
||||
description: 'A value indicating whether setters should be checked. Defaults to `false`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
checkTypesPattern: {
|
||||
description: `When one specifies a type, unless it is of a generic type, like \`object\`
|
||||
or \`array\`, it may be considered unnecessary to have that object's
|
||||
destructured components required, especially where generated docs will
|
||||
link back to the specified type. For example:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @param {SVGRect} bbox - a SVGRect
|
||||
*/
|
||||
export const bboxToObj = function ({x, y, width, height}) {
|
||||
return {x, y, width, height};
|
||||
};
|
||||
\`\`\`
|
||||
|
||||
By default \`checkTypesPattern\` is set to
|
||||
\`/^(?:[oO]bject|[aA]rray|PlainObject|Generic(?:Object|Array))$/v\`,
|
||||
meaning that destructuring will be required only if the type of the \`@param\`
|
||||
(the text between curly brackets) is a match for "Object" or "Array" (with or
|
||||
without initial caps), "PlainObject", or "GenericObject", "GenericArray" (or
|
||||
if no type is present). So in the above example, the lack of a match will
|
||||
mean that no complaint will be given about the undocumented destructured
|
||||
parameters.
|
||||
|
||||
Note that the \`/\` delimiters are optional, but necessary to add flags.
|
||||
|
||||
Defaults to using (only) the \`v\` flag, so to add your own flags, encapsulate
|
||||
your expression as a string, but like a literal, e.g., \`/^object$/vi\`.
|
||||
|
||||
You could set this regular expression to a more expansive list, or you
|
||||
could restrict it such that even types matching those strings would not
|
||||
need destructuring.`,
|
||||
type: 'string',
|
||||
},
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). May be useful for adding such as
|
||||
\`TSMethodSignature\` in TypeScript or restricting the contexts
|
||||
which are checked.
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
enableFixer: {
|
||||
description: 'Whether to enable the fixer. Defaults to `true`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
enableRestElementFixer: {
|
||||
description: `Whether to enable the rest element fixer.
|
||||
|
||||
The fixer will automatically report/insert
|
||||
[JSDoc repeatable parameters](https://jsdoc.app/tags-param.html#multiple-types-and-repeatable-parameters)
|
||||
if missing.
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @param {GenericArray} cfg
|
||||
* @param {number} cfg."0"
|
||||
*/
|
||||
function baar ([a, ...extra]) {
|
||||
//
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
...becomes:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @param {GenericArray} cfg
|
||||
* @param {number} cfg."0"
|
||||
* @param {...any} cfg."1"
|
||||
*/
|
||||
function baar ([a, ...extra]) {
|
||||
//
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
Note that the type \`any\` is included since we don't know of any specific
|
||||
type to use.
|
||||
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
enableRootFixer: {
|
||||
description: `Whether to enable the auto-adding of incrementing roots.
|
||||
|
||||
The default behavior of \`true\` is for "root" to be auto-inserted for missing
|
||||
roots, followed by a 0-based auto-incrementing number.
|
||||
|
||||
So for:
|
||||
|
||||
\`\`\`js
|
||||
function quux ({foo}, {bar}, {baz}) {
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
...the default JSDoc that would be added if the fixer is enabled would be:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @param root0
|
||||
* @param root0.foo
|
||||
* @param root1
|
||||
* @param root1.bar
|
||||
* @param root2
|
||||
* @param root2.baz
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
Has no effect if \`enableFixer\` is set to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
exemptedBy: {
|
||||
description: `Array of tags (e.g., \`['type']\`) whose presence on the document block
|
||||
avoids the need for a \`@param\`. Defaults to an array with
|
||||
\`inheritdoc\`. If you set this array, it will overwrite the default,
|
||||
so be sure to add back \`inheritdoc\` if you wish its presence to cause
|
||||
exemption of the rule.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
ignoreWhenAllParamsMissing: {
|
||||
description: `Set to \`true\` to ignore reporting when all params are missing. Defaults to
|
||||
\`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
interfaceExemptsParamsCheck: {
|
||||
description: `Set if you wish TypeScript interfaces to exempt checks for the existence of
|
||||
\`@param\`'s.
|
||||
|
||||
Will check for a type defining the function itself (on a variable
|
||||
declaration) or if there is a single destructured object with a type.
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
unnamedRootBase: {
|
||||
description: `An array of root names to use in the fixer when roots are missing. Defaults
|
||||
to \`['root']\`. Note that only when all items in the array besides the last
|
||||
are exhausted will auto-incrementing occur. So, with
|
||||
\`unnamedRootBase: ['arg', 'config']\`, the following:
|
||||
|
||||
\`\`\`js
|
||||
function quux ({foo}, [bar], {baz}) {
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
...will get the following JSDoc block added:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @param arg
|
||||
* @param arg.foo
|
||||
* @param config0
|
||||
* @param config0."0" (\`bar\`)
|
||||
* @param config1
|
||||
* @param config1.baz
|
||||
*/
|
||||
\`\`\``,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
useDefaultObjectProperties: {
|
||||
description: `Set to \`true\` if you wish to expect documentation of properties on objects
|
||||
supplied as default values. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
|
||||
// We cannot cache comment nodes as the contexts may recur with the
|
||||
// same comment node but a different JS node, and we may need the different
|
||||
// JS node to ensure we iterate its context
|
||||
noTracking: true,
|
||||
});
|
||||
110
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireParamDescription.js
generated
vendored
Normal file
110
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireParamDescription.js
generated
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
report,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
defaultDestructuredRootDescription = 'The root object',
|
||||
setDefaultDestructuredRootDescription = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
const functionParameterNames = utils.getFunctionParameterNames();
|
||||
|
||||
let rootCount = -1;
|
||||
utils.forEachPreferredTag('param', (jsdocParameter, targetTagName) => {
|
||||
rootCount += jsdocParameter.name.includes('.') ? 0 : 1;
|
||||
if (!jsdocParameter.description.trim()) {
|
||||
if (Array.isArray(functionParameterNames[rootCount])) {
|
||||
if (settings.exemptDestructuredRootsFromChecks) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (setDefaultDestructuredRootDescription) {
|
||||
utils.reportJSDoc(`Missing root description for @${targetTagName}.`, jsdocParameter, () => {
|
||||
utils.changeTag(jsdocParameter, {
|
||||
description: defaultDestructuredRootDescription,
|
||||
postName: ' ',
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
report(
|
||||
`Missing JSDoc @${targetTagName} "${jsdocParameter.name}" description.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that each `@param` tag has a `description` value.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-param-description.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
defaultDestructuredRootDescription: {
|
||||
description: `The description string to set by default for destructured roots. Defaults to
|
||||
"The root object".`,
|
||||
type: 'string',
|
||||
},
|
||||
setDefaultDestructuredRootDescription: {
|
||||
description: `Whether to set a default destructured root description. For example, you may
|
||||
wish to avoid manually having to set the description for a \`@param\`
|
||||
corresponding to a destructured root object as it should always be the same
|
||||
type of object. Uses \`defaultDestructuredRootDescription\` for the description
|
||||
string. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
69
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireParamName.js
generated
vendored
Normal file
69
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireParamName.js
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
utils.forEachPreferredTag('param', (jsdocParameter, targetTagName) => {
|
||||
if (jsdocParameter.tag && jsdocParameter.name === '') {
|
||||
report(
|
||||
`There must be an identifier after @${targetTagName} ${jsdocParameter.type === '' ? 'type' : 'tag'}.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that all `@param` tags have names.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-param-name.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
109
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireParamType.js
generated
vendored
Normal file
109
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireParamType.js
generated
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
report,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
defaultDestructuredRootType = 'object',
|
||||
setDefaultDestructuredRootType = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
const functionParameterNames = utils.getFunctionParameterNames();
|
||||
|
||||
let rootCount = -1;
|
||||
utils.forEachPreferredTag('param', (jsdocParameter, targetTagName) => {
|
||||
rootCount += jsdocParameter.name.includes('.') ? 0 : 1;
|
||||
if (!jsdocParameter.type) {
|
||||
if (Array.isArray(functionParameterNames[rootCount])) {
|
||||
if (settings.exemptDestructuredRootsFromChecks) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (setDefaultDestructuredRootType) {
|
||||
utils.reportJSDoc(`Missing root type for @${targetTagName}.`, jsdocParameter, () => {
|
||||
utils.changeTag(jsdocParameter, {
|
||||
postType: ' ',
|
||||
type: `{${defaultDestructuredRootType}}`,
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
report(
|
||||
`Missing JSDoc @${targetTagName} "${jsdocParameter.name}" type.`,
|
||||
null,
|
||||
jsdocParameter,
|
||||
);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that each `@param` tag has a type value (in curly brackets).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-param-type.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
defaultDestructuredRootType: {
|
||||
description: 'The type string to set by default for destructured roots. Defaults to "object".',
|
||||
type: 'string',
|
||||
},
|
||||
setDefaultDestructuredRootType: {
|
||||
description: `Whether to set a default destructured root type. For example, you may wish
|
||||
to avoid manually having to set the type for a \`@param\`
|
||||
corresponding to a destructured root object as it is always going to be an
|
||||
object. Uses \`defaultDestructuredRootType\` for the type string. Defaults to
|
||||
\`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
48
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireProperty.js
generated
vendored
Normal file
48
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireProperty.js
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
utils,
|
||||
}) => {
|
||||
const propertyAssociatedTags = utils.filterTags(({
|
||||
tag,
|
||||
}) => {
|
||||
return [
|
||||
'namespace', 'typedef',
|
||||
].includes(tag);
|
||||
});
|
||||
if (!propertyAssociatedTags.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetTagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'property',
|
||||
}));
|
||||
|
||||
if (utils.hasATag([
|
||||
targetTagName,
|
||||
])) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const propertyAssociatedTag of propertyAssociatedTags) {
|
||||
if (![
|
||||
'object', 'Object', 'PlainObject',
|
||||
].includes(propertyAssociatedTag.type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
utils.reportJSDoc(`Missing JSDoc @${targetTagName}.`, null, () => {
|
||||
utils.addTag(targetTagName);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that all `@typedef` and `@namespace` tags have `@property` when their type is a plain `object`, `Object`, or `PlainObject`.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-property.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
25
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requirePropertyDescription.js
generated
vendored
Normal file
25
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requirePropertyDescription.js
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
utils.forEachPreferredTag('property', (jsdoc, targetTagName) => {
|
||||
if (!jsdoc.description.trim()) {
|
||||
report(
|
||||
`Missing JSDoc @${targetTagName} "${jsdoc.name}" description.`,
|
||||
null,
|
||||
jsdoc,
|
||||
);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that each `@property` tag has a `description` value.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-property-description.md#repos-sticky-header',
|
||||
},
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
25
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requirePropertyName.js
generated
vendored
Normal file
25
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requirePropertyName.js
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
utils.forEachPreferredTag('property', (jsdoc, targetTagName) => {
|
||||
if (jsdoc.tag && jsdoc.name === '') {
|
||||
report(
|
||||
`There must be an identifier after @${targetTagName} ${jsdoc.type === '' ? 'type' : 'tag'}.`,
|
||||
null,
|
||||
jsdoc,
|
||||
);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that all `@property` tags have names.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-property-name.md#repos-sticky-header',
|
||||
},
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
25
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requirePropertyType.js
generated
vendored
Normal file
25
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requirePropertyType.js
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
utils.forEachPreferredTag('property', (jsdoc, targetTagName) => {
|
||||
if (!jsdoc.type) {
|
||||
report(
|
||||
`Missing JSDoc @${targetTagName} "${jsdoc.name}" type.`,
|
||||
null,
|
||||
jsdoc,
|
||||
);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that each `@property` tag has a type value (in curly brackets).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-property-type.md#repos-sticky-header',
|
||||
},
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
246
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireRejects.js
generated
vendored
Normal file
246
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireRejects.js
generated
vendored
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* Checks if a node or its children contain Promise rejection patterns
|
||||
* @param {import('eslint').Rule.Node} node
|
||||
* @param {boolean} [innerFunction]
|
||||
* @param {boolean} [isAsync]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
// eslint-disable-next-line complexity -- Temporary
|
||||
const hasRejectValue = (node, innerFunction, isAsync) => {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case 'ArrowFunctionExpression':
|
||||
case 'FunctionDeclaration':
|
||||
case 'FunctionExpression': {
|
||||
// For inner functions in async contexts, check if they throw
|
||||
// (they could be called and cause rejection)
|
||||
if (innerFunction) {
|
||||
// Check inner functions for throws - if called from async context, throws become rejections
|
||||
const innerIsAsync = node.async;
|
||||
// Pass isAsync=true if the inner function is async OR if we're already in an async context
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.body), false, innerIsAsync || isAsync);
|
||||
}
|
||||
|
||||
// This is the top-level function we're checking
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.body), true, node.async);
|
||||
}
|
||||
|
||||
case 'BlockStatement': {
|
||||
return node.body.some((bodyNode) => {
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (bodyNode), innerFunction, isAsync);
|
||||
});
|
||||
}
|
||||
|
||||
case 'CallExpression': {
|
||||
// Check for Promise.reject()
|
||||
if (node.callee.type === 'MemberExpression' &&
|
||||
node.callee.object.type === 'Identifier' &&
|
||||
node.callee.object.name === 'Promise' &&
|
||||
node.callee.property.type === 'Identifier' &&
|
||||
node.callee.property.name === 'reject') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for reject() call (in Promise executor context)
|
||||
if (node.callee.type === 'Identifier' && node.callee.name === 'reject') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if this is calling an inner function that might reject
|
||||
if (innerFunction && node.callee.type === 'Identifier') {
|
||||
// We found a function call inside - check if it could be calling a function that rejects
|
||||
// We'll handle this in function body traversal
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
case 'DoWhileStatement':
|
||||
case 'ForInStatement':
|
||||
case 'ForOfStatement':
|
||||
case 'ForStatement':
|
||||
case 'LabeledStatement':
|
||||
case 'WhileStatement':
|
||||
|
||||
case 'WithStatement': {
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.body), innerFunction, isAsync);
|
||||
}
|
||||
|
||||
case 'ExpressionStatement': {
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.expression), innerFunction, isAsync);
|
||||
}
|
||||
|
||||
case 'IfStatement': {
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.consequent), innerFunction, isAsync) || hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.alternate), innerFunction, isAsync);
|
||||
}
|
||||
|
||||
case 'NewExpression': {
|
||||
// Check for new Promise((resolve, reject) => { reject(...) })
|
||||
if (node.callee.type === 'Identifier' && node.callee.name === 'Promise' && node.arguments.length > 0) {
|
||||
const executor = node.arguments[0];
|
||||
if (executor.type === 'ArrowFunctionExpression' || executor.type === 'FunctionExpression') {
|
||||
// Check if the executor has reject() calls
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (executor.body), false, false);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
case 'ReturnStatement': {
|
||||
if (node.argument) {
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.argument), innerFunction, isAsync);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
case 'SwitchStatement': {
|
||||
return node.cases.some(
|
||||
(someCase) => {
|
||||
return someCase.consequent.some((nde) => {
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (nde), innerFunction, isAsync);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Throw statements in async functions become rejections
|
||||
case 'ThrowStatement': {
|
||||
return isAsync === true;
|
||||
}
|
||||
|
||||
case 'TryStatement': {
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.handler && node.handler.body), innerFunction, isAsync) ||
|
||||
hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node.finalizer), innerFunction, isAsync);
|
||||
}
|
||||
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* We can skip checking for a rejects value, in case the documentation is inherited
|
||||
* or the method is abstract.
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const canSkip = (utils) => {
|
||||
return utils.hasATag([
|
||||
'abstract',
|
||||
'virtual',
|
||||
'type',
|
||||
]) ||
|
||||
utils.avoidDocs();
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
node,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
if (canSkip(utils)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'rejects',
|
||||
}));
|
||||
if (!tagName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tags = utils.getTags(tagName);
|
||||
const iteratingFunction = utils.isIteratingFunction();
|
||||
|
||||
const [
|
||||
tag,
|
||||
] = tags;
|
||||
const missingRejectsTag = typeof tag === 'undefined' || tag === null;
|
||||
|
||||
const shouldReport = () => {
|
||||
if (!missingRejectsTag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if this is an async function or returns a Promise
|
||||
const isAsync = utils.isAsync();
|
||||
if (!isAsync && !iteratingFunction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For async functions, check for throw statements
|
||||
// For regular functions, check for Promise.reject or reject calls
|
||||
return hasRejectValue(/** @type {import('eslint').Rule.Node} */ (node));
|
||||
};
|
||||
|
||||
if (shouldReport()) {
|
||||
report('Promise-rejecting function requires `@rejects` tag');
|
||||
}
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that Promise rejections are documented with `@rejects` tags.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-rejects.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context
|
||||
(or objects with optional \`context\` and \`comment\` properties) where you wish
|
||||
the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`).`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
exemptedBy: {
|
||||
description: `Array of tags (e.g., \`['type']\`) whose presence on the
|
||||
document block avoids the need for a \`@rejects\`. Defaults to an array
|
||||
with \`abstract\`, \`virtual\`, and \`type\`. If you set this array, it will overwrite the default,
|
||||
so be sure to add back those tags if you wish their presence to cause
|
||||
exemption of the rule.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
290
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireReturns.js
generated
vendored
Normal file
290
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireReturns.js
generated
vendored
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
import exportParser from '../exportParser.js';
|
||||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* We can skip checking for a return value, in case the documentation is inherited
|
||||
* or the method is either a constructor or an abstract method.
|
||||
*
|
||||
* In either of these cases the return value is optional or not defined.
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* a reference to the utils which are used to probe if a tag is present or not.
|
||||
* @returns {boolean}
|
||||
* true in case deep checking can be skipped; otherwise false.
|
||||
*/
|
||||
const canSkip = (utils) => {
|
||||
return utils.hasATag([
|
||||
// inheritdoc implies that all documentation is inherited
|
||||
// see https://jsdoc.app/tags-inheritdoc.html
|
||||
//
|
||||
// Abstract methods are by definition incomplete,
|
||||
// so it is not an error if it declares a return value but does not implement it.
|
||||
'abstract',
|
||||
'virtual',
|
||||
|
||||
// Constructors do not have a return value by definition (https://jsdoc.app/tags-class.html)
|
||||
// So we can bail out here, too.
|
||||
'class',
|
||||
'constructor',
|
||||
|
||||
// Return type is specified by type in @type
|
||||
'type',
|
||||
|
||||
// This seems to imply a class as well
|
||||
'interface',
|
||||
]) ||
|
||||
utils.avoidDocs();
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
info: {
|
||||
comment,
|
||||
},
|
||||
node,
|
||||
report,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
contexts,
|
||||
enableFixer = false,
|
||||
forceRequireReturn = false,
|
||||
forceReturnsWithAsync = false,
|
||||
publicOnly = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
// A preflight check. We do not need to run a deep check
|
||||
// in case the @returns comment is optional or undefined.
|
||||
if (canSkip(utils)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {boolean|undefined} */
|
||||
let forceRequireReturnContext;
|
||||
if (contexts) {
|
||||
const {
|
||||
foundContext,
|
||||
} = utils.findContext(contexts, comment);
|
||||
if (typeof foundContext === 'object') {
|
||||
forceRequireReturnContext = foundContext.forceRequireReturn;
|
||||
}
|
||||
}
|
||||
|
||||
const tagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'returns',
|
||||
}));
|
||||
if (!tagName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tags = utils.getTags(tagName);
|
||||
|
||||
if (tags.length > 1) {
|
||||
report(`Found more than one @${tagName} declaration.`);
|
||||
}
|
||||
|
||||
const iteratingFunction = utils.isIteratingFunction();
|
||||
|
||||
// In case the code returns something, we expect a return value in JSDoc.
|
||||
const [
|
||||
tag,
|
||||
] = tags;
|
||||
const missingReturnTag = typeof tag === 'undefined' || tag === null;
|
||||
|
||||
const shouldReport = () => {
|
||||
if (!missingReturnTag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (publicOnly) {
|
||||
/** @type {import('./requireJsdoc.js').RequireJsdocOpts} */
|
||||
const opt = {
|
||||
ancestorsOnly: Boolean(publicOnly?.ancestorsOnly ?? false),
|
||||
esm: Boolean(publicOnly?.esm ?? true),
|
||||
initModuleExports: Boolean(publicOnly?.cjs ?? true),
|
||||
initWindow: Boolean(publicOnly?.window ?? false),
|
||||
};
|
||||
/* c8 ignore next -- Fallback to deprecated method */
|
||||
const {
|
||||
sourceCode = context.getSourceCode(),
|
||||
} = context;
|
||||
const exported = exportParser.isUncommentedExport(
|
||||
/** @type {import('eslint').Rule.Node} */ (node), sourceCode, opt, settings,
|
||||
);
|
||||
|
||||
if (!exported) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((forceRequireReturn || forceRequireReturnContext) && (
|
||||
iteratingFunction || utils.isVirtualFunction()
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const isAsync = !iteratingFunction && utils.hasTag('async') ||
|
||||
iteratingFunction && utils.isAsync();
|
||||
|
||||
if (forceReturnsWithAsync && isAsync) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return iteratingFunction && utils.hasValueOrExecutorHasNonEmptyResolveValue(
|
||||
forceReturnsWithAsync,
|
||||
);
|
||||
};
|
||||
|
||||
if (shouldReport()) {
|
||||
utils.reportJSDoc(`Missing JSDoc @${tagName} declaration.`, null, enableFixer ? () => {
|
||||
utils.addTag(tagName);
|
||||
} : null);
|
||||
}
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that returns are documented with `@returns`.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-returns.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
checkConstructors: {
|
||||
default: false,
|
||||
description: `A value indicating whether \`constructor\`s should
|
||||
be checked for \`@returns\` tags. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
checkGetters: {
|
||||
default: true,
|
||||
description: `Boolean to determine whether getter methods should
|
||||
be checked for \`@returns\` tags. Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context
|
||||
(or objects with optional \`context\` and \`comment\` properties) where you wish
|
||||
the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`). This
|
||||
rule will only apply on non-default contexts when there is such a tag
|
||||
present and the \`forceRequireReturn\` option is set or if the
|
||||
\`forceReturnsWithAsync\` option is set with a present \`@async\` tag
|
||||
(since we are not checking against the actual \`return\` values in these
|
||||
cases).`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
forceRequireReturn: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
enableFixer: {
|
||||
description: `Whether to enable the fixer to add a blank \`@returns\`.
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
exemptedBy: {
|
||||
description: `Array of tags (e.g., \`['type']\`) whose presence on the
|
||||
document block avoids the need for a \`@returns\`. Defaults to an array
|
||||
with \`inheritdoc\`. If you set this array, it will overwrite the default,
|
||||
so be sure to add back \`inheritdoc\` if you wish its presence to cause
|
||||
exemption of the rule.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
forceRequireReturn: {
|
||||
default: false,
|
||||
description: `Set to \`true\` to always insist on
|
||||
\`@returns\` documentation regardless of implicit or explicit \`return\`'s
|
||||
in the function. May be desired to flag that a project is aware of an
|
||||
\`undefined\`/\`void\` return. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
forceReturnsWithAsync: {
|
||||
default: false,
|
||||
description: `By default \`async\` functions that do not explicitly
|
||||
return a value pass this rule as an \`async\` function will always return a
|
||||
\`Promise\`, even if the \`Promise\` resolves to void. You can force all
|
||||
\`async\` functions (including ones with an explicit \`Promise\` but no
|
||||
detected non-\`undefined\` \`resolve\` value) to require \`@return\`
|
||||
documentation by setting \`forceReturnsWithAsync\` to \`true\` on the options
|
||||
object. This may be useful for flagging that there has been consideration
|
||||
of return type. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
publicOnly: {
|
||||
description: `This option will insist that missing \`@returns\` are only reported for
|
||||
function bodies / class declarations that are exported from the module.
|
||||
May be a boolean or object. If set to \`true\`, the defaults below will be
|
||||
used. If unset, \`@returns\` reporting will not be limited to exports.
|
||||
|
||||
This object supports the following optional boolean keys (\`false\` unless
|
||||
otherwise noted):
|
||||
|
||||
- \`ancestorsOnly\` - Optimization to only check node ancestors to check if node is exported
|
||||
- \`esm\` - ESM exports are checked for \`@returns\` JSDoc comments (Defaults to \`true\`)
|
||||
- \`cjs\` - CommonJS exports are checked for \`@returns\` JSDoc comments (Defaults to \`true\`)
|
||||
- \`window\` - Window global exports are checked for \`@returns\` JSDoc comments`,
|
||||
oneOf: [
|
||||
{
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
default: {},
|
||||
properties: {
|
||||
ancestorsOnly: {
|
||||
type: 'boolean',
|
||||
},
|
||||
cjs: {
|
||||
type: 'boolean',
|
||||
},
|
||||
esm: {
|
||||
type: 'boolean',
|
||||
},
|
||||
window: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
180
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireReturnsCheck.js
generated
vendored
Normal file
180
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireReturnsCheck.js
generated
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
strictNativeTypes,
|
||||
} from '../jsdocUtils.js';
|
||||
|
||||
/**
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* @param {import('../iterateJsdoc.js').Settings} settings
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const canSkip = (utils, settings) => {
|
||||
const voidingTags = [
|
||||
// An abstract function is by definition incomplete
|
||||
// so it is perfectly fine if a return is documented but
|
||||
// not present within the function.
|
||||
// A subclass may inherit the doc and implement the
|
||||
// missing return.
|
||||
'abstract',
|
||||
'virtual',
|
||||
|
||||
// A constructor function returns `this` by default, so may be `@returns`
|
||||
// tag indicating this but no explicit return
|
||||
'class',
|
||||
'constructor',
|
||||
'interface',
|
||||
];
|
||||
|
||||
if (settings.mode === 'closure') {
|
||||
// Structural Interface in GCC terms, equivalent to @interface tag as far as this rule is concerned
|
||||
voidingTags.push('record');
|
||||
}
|
||||
|
||||
return utils.hasATag(voidingTags) ||
|
||||
utils.isConstructor() ||
|
||||
utils.classHasTag('interface') ||
|
||||
settings.mode === 'closure' && utils.classHasTag('record');
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
node,
|
||||
report,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
exemptAsync = true,
|
||||
exemptGenerators = settings.mode === 'typescript',
|
||||
noNativeTypes = true,
|
||||
reportMissingReturnForUndefinedTypes = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
if (canSkip(utils, settings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isAsync = utils.isAsync();
|
||||
if (exemptAsync && isAsync) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'returns',
|
||||
}));
|
||||
if (!tagName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tags = utils.getTags(tagName);
|
||||
|
||||
if (tags.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tags.length > 1) {
|
||||
report(`Found more than one @${tagName} declaration.`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const [
|
||||
tag,
|
||||
] = tags;
|
||||
|
||||
const type = tag.type.trim();
|
||||
|
||||
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions
|
||||
if (/asserts\s/v.test(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const returnNever = type === 'never';
|
||||
|
||||
if (returnNever && utils.hasValueOrExecutorHasNonEmptyResolveValue(false)) {
|
||||
report(`JSDoc @${tagName} declaration set with "never" but return expression is present in function.`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (noNativeTypes && isAsync && strictNativeTypes.includes(type)) {
|
||||
report('Function is async or otherwise returns a Promise but the return type is a native type.');
|
||||
return;
|
||||
}
|
||||
|
||||
// In case a return value is declared in JSDoc, we also expect one in the code.
|
||||
if (
|
||||
!returnNever &&
|
||||
(
|
||||
reportMissingReturnForUndefinedTypes ||
|
||||
!utils.mayBeUndefinedTypeTag(tag)
|
||||
) &&
|
||||
(tag.type === '' && !utils.hasValueOrExecutorHasNonEmptyResolveValue(
|
||||
exemptAsync,
|
||||
) ||
|
||||
tag.type !== '' && !utils.hasValueOrExecutorHasNonEmptyResolveValue(
|
||||
exemptAsync,
|
||||
true,
|
||||
)) &&
|
||||
Boolean(
|
||||
!exemptGenerators || !node ||
|
||||
!('generator' in /** @type {import('../iterateJsdoc.js').Node} */ (node)) ||
|
||||
!(/** @type {import('@typescript-eslint/types').TSESTree.FunctionDeclaration} */ (node)).generator,
|
||||
)
|
||||
) {
|
||||
report(`JSDoc @${tagName} declaration present but return expression not available in function.`);
|
||||
}
|
||||
}, {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires a return statement in function body if a `@returns` tag is specified in JSDoc comment(and reports if multiple `@returns` tags are present).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-returns-check.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
exemptAsync: {
|
||||
default: true,
|
||||
description: `By default, functions which return a \`Promise\` that are not
|
||||
detected as resolving with a non-\`undefined\` value and \`async\` functions
|
||||
(even ones that do not explicitly return a value, as these are returning a
|
||||
\`Promise\` implicitly) will be exempted from reporting by this rule.
|
||||
If you wish to insist that only \`Promise\`'s which resolve to
|
||||
non-\`undefined\` values or \`async\` functions with explicit \`return\`'s will
|
||||
be exempted from reporting (i.e., that \`async\` functions can be reported
|
||||
if they lack an explicit (non-\`undefined\`) \`return\` when a \`@returns\` is
|
||||
present), you can set \`exemptAsync\` to \`false\` on the options object.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
exemptGenerators: {
|
||||
description: `Because a generator might be labeled as having a
|
||||
\`IterableIterator\` \`@returns\` value (along with an iterator type
|
||||
corresponding to the type of any \`yield\` statements), projects might wish to
|
||||
leverage \`@returns\` in generators even without a \`return\` statement. This
|
||||
option is therefore \`true\` by default in \`typescript\` mode (in "jsdoc" mode,
|
||||
one might be more likely to take advantage of \`@yields\`). Set it to \`false\`
|
||||
if you wish for a missing \`return\` to be flagged regardless.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
noNativeTypes: {
|
||||
description: `Whether to check that async functions do not
|
||||
indicate they return non-native types. Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
reportMissingReturnForUndefinedTypes: {
|
||||
default: false,
|
||||
description: `If \`true\` and no return or
|
||||
resolve value is found, this setting will even insist that reporting occur
|
||||
with \`void\` or \`undefined\` (including as an indicated \`Promise\` type).
|
||||
Unlike \`require-returns\`, with this option in the rule, one can
|
||||
*discourage* the labeling of \`undefined\` types. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
73
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireReturnsDescription.js
generated
vendored
Normal file
73
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireReturnsDescription.js
generated
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
utils.forEachPreferredTag('returns', (jsdocTag, targetTagName) => {
|
||||
const type = jsdocTag.type && jsdocTag.type.trim();
|
||||
|
||||
if ([
|
||||
'Promise<undefined>', 'Promise<void>', 'undefined', 'void',
|
||||
].includes(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jsdocTag.description.trim()) {
|
||||
report(`Missing JSDoc @${targetTagName} description.`, null, jsdocTag);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that the `@returns` tag has a `description` value (not including `void`/`undefined` type returns).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-returns-description.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
65
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireReturnsType.js
generated
vendored
Normal file
65
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireReturnsType.js
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
utils.forEachPreferredTag('returns', (jsdocTag, targetTagName) => {
|
||||
if (!jsdocTag.type) {
|
||||
report(`Missing JSDoc @${targetTagName} type.`, null, jsdocTag);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that `@returns` tag has type value (in curly brackets).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-returns-type.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context (or an object with
|
||||
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`).
|
||||
|
||||
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
|
||||
section of our Advanced docs for more on the expected format.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
85
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireTags.js
generated
vendored
Normal file
85
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireTags.js
generated
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import {
|
||||
buildForbidRuleDefinition,
|
||||
} from '../buildForbidRuleDefinition.js';
|
||||
|
||||
export default buildForbidRuleDefinition({
|
||||
description: 'Requires tags be present, optionally for specific contexts',
|
||||
getContexts (context, report) {
|
||||
// Transformed options to this option in `modifyContext`:
|
||||
if (!context.options[0].contexts) {
|
||||
report('Rule `require-tags` is missing a `tags` option.');
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
contexts,
|
||||
} = context.options[0];
|
||||
|
||||
return contexts;
|
||||
},
|
||||
modifyContext (context) {
|
||||
const tags = /** @type {(string|{tag: string, context: string})[]} */ (
|
||||
context.options?.[0]?.tags
|
||||
);
|
||||
|
||||
const cntxts = tags?.map((tag) => {
|
||||
const tagName = typeof tag === 'string' ? tag : tag.tag;
|
||||
return {
|
||||
comment: `JsdocBlock:not(*:has(JsdocTag[tag=${
|
||||
tagName
|
||||
}]))`,
|
||||
context: typeof tag === 'string' ? 'any' : tag.context,
|
||||
message: `Missing required tag "${tagName}"`,
|
||||
};
|
||||
});
|
||||
|
||||
// Reproduce context object with our own `contexts`
|
||||
const propertyDescriptors = Object.getOwnPropertyDescriptors(context);
|
||||
return Object.create(
|
||||
Object.getPrototypeOf(context),
|
||||
{
|
||||
...propertyDescriptors,
|
||||
options: {
|
||||
...propertyDescriptors.options,
|
||||
value: [
|
||||
{
|
||||
contexts: cntxts,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
tags: {
|
||||
description: `May be an array of either strings or objects with
|
||||
a string \`tag\` property and \`context\` string property.`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
tag: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-tags.md#repos-sticky-header',
|
||||
});
|
||||
235
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireTemplate.js
generated
vendored
Normal file
235
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireTemplate.js
generated
vendored
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
parse as parseType,
|
||||
traverse,
|
||||
tryParse as tryParseType,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
node,
|
||||
report,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
if (utils.avoidDocs()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
requireSeparateTemplates = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
const {
|
||||
mode,
|
||||
} = settings;
|
||||
|
||||
const usedNames = new Set();
|
||||
|
||||
const tgName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'template',
|
||||
}));
|
||||
if (!tgName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const templateTags = utils.getTags(tgName);
|
||||
|
||||
const templateNames = templateTags.flatMap((tag) => {
|
||||
return utils.parseClosureTemplateTag(tag);
|
||||
});
|
||||
|
||||
if (requireSeparateTemplates) {
|
||||
for (const tag of templateTags) {
|
||||
const names = utils.parseClosureTemplateTag(tag);
|
||||
if (names.length > 1) {
|
||||
report(`Missing separate @${tgName} for ${names[1]}`, null, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@typescript-eslint/types').TSESTree.FunctionDeclaration|
|
||||
* import('@typescript-eslint/types').TSESTree.ClassDeclaration|
|
||||
* import('@typescript-eslint/types').TSESTree.TSDeclareFunction|
|
||||
* import('@typescript-eslint/types').TSESTree.TSInterfaceDeclaration|
|
||||
* import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration
|
||||
*/
|
||||
const checkTypeParams = (aliasDeclaration) => {
|
||||
const {
|
||||
params,
|
||||
/* c8 ignore next -- Guard */
|
||||
} = aliasDeclaration.typeParameters ?? {
|
||||
/* c8 ignore next -- Guard */
|
||||
params: [],
|
||||
};
|
||||
for (const {
|
||||
name: {
|
||||
name,
|
||||
},
|
||||
} of params) {
|
||||
usedNames.add(name);
|
||||
}
|
||||
|
||||
for (const usedName of usedNames) {
|
||||
if (!templateNames.includes(usedName)) {
|
||||
report(`Missing @${tgName} ${usedName}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleTypes = () => {
|
||||
const nde = /** @type {import('@typescript-eslint/types').TSESTree.Node} */ (
|
||||
node
|
||||
);
|
||||
if (!nde) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (nde.type) {
|
||||
case 'ClassDeclaration':
|
||||
case 'FunctionDeclaration':
|
||||
case 'TSDeclareFunction':
|
||||
case 'TSInterfaceDeclaration':
|
||||
case 'TSTypeAliasDeclaration':
|
||||
checkTypeParams(nde);
|
||||
break;
|
||||
case 'ExportDefaultDeclaration':
|
||||
switch (nde.declaration?.type) {
|
||||
case 'ClassDeclaration':
|
||||
case 'FunctionDeclaration':
|
||||
case 'TSInterfaceDeclaration':
|
||||
checkTypeParams(nde.declaration);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'ExportNamedDeclaration':
|
||||
switch (nde.declaration?.type) {
|
||||
case 'ClassDeclaration':
|
||||
case 'FunctionDeclaration':
|
||||
case 'TSDeclareFunction':
|
||||
case 'TSInterfaceDeclaration':
|
||||
case 'TSTypeAliasDeclaration':
|
||||
checkTypeParams(nde.declaration);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const usedNameToTag = new Map();
|
||||
|
||||
/**
|
||||
* @param {import('comment-parser').Spec} potentialTag
|
||||
*/
|
||||
const checkForUsedTypes = (potentialTag) => {
|
||||
let parsedType;
|
||||
try {
|
||||
parsedType = mode === 'permissive' ?
|
||||
tryParseType(/** @type {string} */ (potentialTag.type)) :
|
||||
parseType(/** @type {string} */ (potentialTag.type), mode);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
traverse(parsedType, (nde) => {
|
||||
const {
|
||||
type,
|
||||
value,
|
||||
} = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde);
|
||||
if (type === 'JsdocTypeName' && (/^[A-Z]$/v).test(value)) {
|
||||
usedNames.add(value);
|
||||
if (!usedNameToTag.has(value)) {
|
||||
usedNameToTag.set(value, potentialTag);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string[]} tagNames
|
||||
*/
|
||||
const checkTagsAndTemplates = (tagNames) => {
|
||||
for (const tagName of tagNames) {
|
||||
const preferredTagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName,
|
||||
}));
|
||||
const matchingTags = utils.getTags(preferredTagName);
|
||||
for (const matchingTag of matchingTags) {
|
||||
checkForUsedTypes(matchingTag);
|
||||
}
|
||||
}
|
||||
|
||||
// Could check against whitelist/blacklist
|
||||
for (const usedName of usedNames) {
|
||||
if (!templateNames.includes(usedName)) {
|
||||
report(`Missing @${tgName} ${usedName}`, null, usedNameToTag.get(usedName));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const callbackTags = utils.getTags('callback');
|
||||
const functionTags = utils.getTags('function');
|
||||
if (callbackTags.length || functionTags.length) {
|
||||
checkTagsAndTemplates([
|
||||
'param', 'returns',
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
const typedefTags = utils.getTags('typedef');
|
||||
if (!typedefTags.length || typedefTags.length >= 2) {
|
||||
handleTypes();
|
||||
return;
|
||||
}
|
||||
|
||||
const potentialTypedef = typedefTags[0];
|
||||
checkForUsedTypes(potentialTypedef);
|
||||
|
||||
checkTagsAndTemplates([
|
||||
'property',
|
||||
]);
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires `@template` tags be present when type parameters are used.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-template.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
exemptedBy: {
|
||||
description: `Array of tags (e.g., \`['type']\`) whose presence on the document
|
||||
block avoids the need for a \`@template\`. Defaults to an array with
|
||||
\`inheritdoc\`. If you set this array, it will overwrite the default,
|
||||
so be sure to add back \`inheritdoc\` if you wish its presence to cause
|
||||
exemption of the rule.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
requireSeparateTemplates: {
|
||||
description: `Requires that each template have its own separate line, i.e., preventing
|
||||
templates of this format:
|
||||
|
||||
\`\`\`js
|
||||
/**
|
||||
* @template T, U, V
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
128
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireThrows.js
generated
vendored
Normal file
128
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireThrows.js
generated
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* We can skip checking for a throws value, in case the documentation is inherited
|
||||
* or the method is either a constructor or an abstract method.
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils a reference to the utils which are used to probe if a tag is present or not.
|
||||
* @returns {boolean} true in case deep checking can be skipped; otherwise false.
|
||||
*/
|
||||
const canSkip = (utils) => {
|
||||
return utils.hasATag([
|
||||
// inheritdoc implies that all documentation is inherited
|
||||
// see https://jsdoc.app/tags-inheritdoc.html
|
||||
//
|
||||
// Abstract methods are by definition incomplete,
|
||||
// so it is not necessary to document that they throw an error.
|
||||
'abstract',
|
||||
'virtual',
|
||||
|
||||
// The designated type can itself document `@throws`
|
||||
'type',
|
||||
]) ||
|
||||
utils.avoidDocs();
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
// A preflight check. We do not need to run a deep check for abstract
|
||||
// functions.
|
||||
if (canSkip(utils)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName: 'throws',
|
||||
}));
|
||||
if (!tagName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tags = utils.getTags(tagName);
|
||||
const iteratingFunction = utils.isIteratingFunction();
|
||||
|
||||
// In case the code returns something, we expect a return value in JSDoc.
|
||||
const [
|
||||
tag,
|
||||
] = tags;
|
||||
const missingThrowsTag = typeof tag === 'undefined' || tag === null;
|
||||
|
||||
const shouldReport = () => {
|
||||
if (!missingThrowsTag) {
|
||||
if (tag.type.trim() === 'never' && iteratingFunction && utils.hasThrowValue()) {
|
||||
report(`JSDoc @${tagName} declaration set to "never" but throw value found.`);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return iteratingFunction && utils.hasThrowValue();
|
||||
};
|
||||
|
||||
if (shouldReport()) {
|
||||
report(`Missing JSDoc @${tagName} declaration.`);
|
||||
}
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires that throw statements are documented with `@throws` tags.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-throws.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context
|
||||
(or objects with optional \`context\` and \`comment\` properties) where you wish
|
||||
the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`).`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
exemptedBy: {
|
||||
description: `Array of tags (e.g., \`['type']\`) whose presence on the
|
||||
document block avoids the need for a \`@throws\`. Defaults to an array
|
||||
with \`inheritdoc\`. If you set this array, it will overwrite the default,
|
||||
so be sure to add back \`inheritdoc\` if you wish its presence to cause
|
||||
exemption of the rule.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
265
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireYields.js
generated
vendored
Normal file
265
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireYields.js
generated
vendored
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* We can skip checking for a yield value, in case the documentation is inherited
|
||||
* or the method has a constructor or abstract tag.
|
||||
*
|
||||
* In either of these cases the yield value is optional or not defined.
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils a reference to the utils which are used to probe if a tag is present or not.
|
||||
* @returns {boolean} true in case deep checking can be skipped; otherwise false.
|
||||
*/
|
||||
const canSkip = (utils) => {
|
||||
return utils.hasATag([
|
||||
// inheritdoc implies that all documentation is inherited
|
||||
// see https://jsdoc.app/tags-inheritdoc.html
|
||||
//
|
||||
// Abstract methods are by definition incomplete,
|
||||
// so it is not an error if it declares a yield value but does not implement it.
|
||||
'abstract',
|
||||
'virtual',
|
||||
|
||||
// Constructors do not have a yield value
|
||||
// so we can bail out here, too.
|
||||
'class',
|
||||
'constructor',
|
||||
|
||||
// Yield (and any `next`) type is specified accompanying the targeted
|
||||
// @type
|
||||
'type',
|
||||
|
||||
// This seems to imply a class as well
|
||||
'interface',
|
||||
]) ||
|
||||
utils.avoidDocs();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* @param {import('../iterateJsdoc.js').Report} report
|
||||
* @param {string} tagName
|
||||
* @returns {[preferredTagName?: string, missingTag?: boolean]}
|
||||
*/
|
||||
const checkTagName = (utils, report, tagName) => {
|
||||
const preferredTagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName,
|
||||
}));
|
||||
if (!preferredTagName) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const tags = utils.getTags(preferredTagName);
|
||||
|
||||
if (tags.length > 1) {
|
||||
report(`Found more than one @${preferredTagName} declaration.`);
|
||||
}
|
||||
|
||||
// In case the code yields something, we expect a yields value in JSDoc.
|
||||
const [
|
||||
tag,
|
||||
] = tags;
|
||||
const missingTag = typeof tag === 'undefined' || tag === null;
|
||||
|
||||
return [
|
||||
preferredTagName, missingTag,
|
||||
];
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
forceRequireNext = false,
|
||||
forceRequireYields = false,
|
||||
next = false,
|
||||
nextWithGeneratorTag = false,
|
||||
withGeneratorTag = true,
|
||||
} = context.options[0] || {};
|
||||
|
||||
// A preflight check. We do not need to run a deep check
|
||||
// in case the @yield comment is optional or undefined.
|
||||
if (canSkip(utils)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const iteratingFunction = utils.isIteratingFunction();
|
||||
|
||||
const [
|
||||
preferredYieldTagName,
|
||||
missingYieldTag,
|
||||
] = checkTagName(
|
||||
utils, report, 'yields',
|
||||
);
|
||||
if (preferredYieldTagName) {
|
||||
const shouldReportYields = () => {
|
||||
if (!missingYieldTag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
withGeneratorTag && utils.hasTag('generator') ||
|
||||
forceRequireYields && iteratingFunction && utils.isGenerator()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return iteratingFunction && utils.isGenerator() && utils.hasYieldValue();
|
||||
};
|
||||
|
||||
if (shouldReportYields()) {
|
||||
report(`Missing JSDoc @${preferredYieldTagName} declaration.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (next || nextWithGeneratorTag || forceRequireNext) {
|
||||
const [
|
||||
preferredNextTagName,
|
||||
missingNextTag,
|
||||
] = checkTagName(
|
||||
utils, report, 'next',
|
||||
);
|
||||
if (!preferredNextTagName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldReportNext = () => {
|
||||
if (!missingNextTag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
nextWithGeneratorTag && utils.hasTag('generator')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
!next && !forceRequireNext ||
|
||||
!iteratingFunction ||
|
||||
!utils.isGenerator()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return forceRequireNext || utils.hasYieldReturnValue();
|
||||
};
|
||||
|
||||
if (shouldReportNext()) {
|
||||
report(`Missing JSDoc @${preferredNextTagName} declaration.`);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
contextDefaults: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires yields are documented with `@yields` tags.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-yields.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context
|
||||
(or objects with optional \`context\` and \`comment\` properties) where you wish
|
||||
the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`). Set to \`"any"\` if you want
|
||||
the rule to apply to any JSDoc block throughout your files (as is necessary
|
||||
for finding function blocks not attached to a function declaration or
|
||||
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
|
||||
\`@method\`) (including those associated with an \`@interface\`). This
|
||||
rule will only apply on non-default contexts when there is such a tag
|
||||
present and the \`forceRequireYields\` option is set or if the
|
||||
\`withGeneratorTag\` option is set with a present \`@generator\` tag
|
||||
(since we are not checking against the actual \`yield\` values in these
|
||||
cases).`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
exemptedBy: {
|
||||
description: `Array of tags (e.g., \`['type']\`) whose presence on the
|
||||
document block avoids the need for a \`@yields\`. Defaults to an array
|
||||
with \`inheritdoc\`. If you set this array, it will overwrite the default,
|
||||
so be sure to add back \`inheritdoc\` if you wish its presence to cause
|
||||
exemption of the rule.`,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
forceRequireNext: {
|
||||
default: false,
|
||||
description: `Set to \`true\` to always insist on
|
||||
\`@next\` documentation even if there are no \`yield\` statements in the
|
||||
function or none return values. May be desired to flag that a project is
|
||||
aware of the expected yield return being \`undefined\`. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
forceRequireYields: {
|
||||
default: false,
|
||||
description: `Set to \`true\` to always insist on
|
||||
\`@yields\` documentation for generators even if there are only
|
||||
expressionless \`yield\` statements in the function. May be desired to flag
|
||||
that a project is aware of an \`undefined\`/\`void\` yield. Defaults to
|
||||
\`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
next: {
|
||||
default: false,
|
||||
description: `If \`true\`, this option will insist that any use of a \`yield\` return
|
||||
value (e.g., \`const rv = yield;\` or \`const rv = yield value;\`) has a
|
||||
(non-standard) \`@next\` tag (in addition to any \`@yields\` tag) so as to be
|
||||
able to document the type expected to be supplied into the iterator
|
||||
(the \`Generator\` iterator that is returned by the call to the generator
|
||||
function) to the iterator (e.g., \`it.next(value)\`). The tag will not be
|
||||
expected if the generator function body merely has plain \`yield;\` or
|
||||
\`yield value;\` statements without returning the values. Defaults to
|
||||
\`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
nextWithGeneratorTag: {
|
||||
default: false,
|
||||
description: `If a \`@generator\` tag is present on a block, require
|
||||
(non-standard ) \`@next\` (see \`next\` option). This will require using \`void\`
|
||||
or \`undefined\` in cases where generators do not use the \`next()\`-supplied
|
||||
incoming \`yield\`-returned value. Defaults to \`false\`. See \`contexts\` to
|
||||
\`any\` if you want to catch \`@generator\` with \`@callback\` or such not
|
||||
attached to a function.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
withGeneratorTag: {
|
||||
default: true,
|
||||
description: `If a \`@generator\` tag is present on a block, require
|
||||
\`@yields\`/\`@yield\`. Defaults to \`true\`. See \`contexts\` to \`any\` if you want
|
||||
to catch \`@generator\` with \`@callback\` or such not attached to a function.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
226
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireYieldsCheck.js
generated
vendored
Normal file
226
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/requireYieldsCheck.js
generated
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* @param {import('../iterateJsdoc.js').Settings} settings
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const canSkip = (utils, settings) => {
|
||||
const voidingTags = [
|
||||
// An abstract function is by definition incomplete
|
||||
// so it is perfectly fine if a yield is documented but
|
||||
// not present within the function.
|
||||
// A subclass may inherit the doc and implement the
|
||||
// missing yield.
|
||||
'abstract',
|
||||
'virtual',
|
||||
|
||||
// Constructor functions do not have a yield value
|
||||
// so we can bail here, too.
|
||||
'class',
|
||||
'constructor',
|
||||
|
||||
// This seems to imply a class as well
|
||||
'interface',
|
||||
];
|
||||
|
||||
if (settings.mode === 'closure') {
|
||||
// Structural Interface in GCC terms, equivalent to @interface tag as far as this rule is concerned
|
||||
voidingTags.push('record');
|
||||
}
|
||||
|
||||
return utils.hasATag(voidingTags) ||
|
||||
utils.isConstructor() ||
|
||||
utils.classHasTag('interface') ||
|
||||
settings.mode === 'closure' && utils.classHasTag('record');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('../iterateJsdoc.js').Utils} utils
|
||||
* @param {import('../iterateJsdoc.js').Report} report
|
||||
* @param {string} tagName
|
||||
* @returns {[]|[preferredTagName: string, tag: import('comment-parser').Spec]}
|
||||
*/
|
||||
const checkTagName = (utils, report, tagName) => {
|
||||
const preferredTagName = /** @type {string} */ (utils.getPreferredTagName({
|
||||
tagName,
|
||||
}));
|
||||
if (!preferredTagName) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const tags = utils.getTags(preferredTagName);
|
||||
|
||||
if (tags.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (tags.length > 1) {
|
||||
report(`Found more than one @${preferredTagName} declaration.`);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
preferredTagName, tags[0],
|
||||
];
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
report,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
if (canSkip(utils, settings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
checkGeneratorsOnly = false,
|
||||
next = false,
|
||||
} = context.options[0] || {};
|
||||
|
||||
const [
|
||||
preferredYieldTagName,
|
||||
yieldTag,
|
||||
] = checkTagName(
|
||||
utils, report, 'yields',
|
||||
);
|
||||
if (preferredYieldTagName) {
|
||||
const shouldReportYields = () => {
|
||||
if (
|
||||
/** @type {import('comment-parser').Spec} */ (
|
||||
yieldTag
|
||||
).type.trim() === 'never'
|
||||
) {
|
||||
if (utils.hasYieldValue()) {
|
||||
report(`JSDoc @${preferredYieldTagName} declaration set with "never" but yield expression is present in function.`);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkGeneratorsOnly && !utils.isGenerator()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !utils.mayBeUndefinedTypeTag(
|
||||
/** @type {import('comment-parser').Spec} */
|
||||
(yieldTag),
|
||||
) && !utils.hasYieldValue();
|
||||
};
|
||||
|
||||
// In case a yield value is declared in JSDoc, we also expect one in the code.
|
||||
if (shouldReportYields()) {
|
||||
report(`JSDoc @${preferredYieldTagName} declaration present but yield expression not available in function.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (next) {
|
||||
const [
|
||||
preferredNextTagName,
|
||||
nextTag,
|
||||
] = checkTagName(
|
||||
utils, report, 'next',
|
||||
);
|
||||
if (preferredNextTagName) {
|
||||
const shouldReportNext = () => {
|
||||
if (
|
||||
/** @type {import('comment-parser').Spec} */ (
|
||||
nextTag
|
||||
).type.trim() === 'never'
|
||||
) {
|
||||
if (utils.hasYieldReturnValue()) {
|
||||
report(`JSDoc @${preferredNextTagName} declaration set with "never" but yield expression with return value is present in function.`);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkGeneratorsOnly && !utils.isGenerator()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !utils.mayBeUndefinedTypeTag(
|
||||
/** @type {import('comment-parser').Spec} */
|
||||
(nextTag),
|
||||
) && !utils.hasYieldReturnValue();
|
||||
};
|
||||
|
||||
if (shouldReportNext()) {
|
||||
report(`JSDoc @${preferredNextTagName} declaration present but yield expression with return value not available in function.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Ensures that if a `@yields` is present that a `yield` (or `yield` with a value) is present in the function body (or that if a `@next` is present that there is a yield with a return value present).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-yields-check.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
checkGeneratorsOnly: {
|
||||
default: false,
|
||||
description: `Avoids checking the function body and merely insists
|
||||
that all generators have \`@yields\`. This can be an optimization with the
|
||||
ESLint \`require-yield\` rule, as that rule already ensures a \`yield\` is
|
||||
present in generators, albeit assuming the generator is not empty).
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
contexts: {
|
||||
description: `Set this to an array of strings representing the AST context
|
||||
(or objects with optional \`context\` and \`comment\` properties) where you wish
|
||||
the rule to be applied.
|
||||
|
||||
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
|
||||
|
||||
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
|
||||
\`FunctionExpression\`).`,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
next: {
|
||||
default: false,
|
||||
description: `If \`true\`, this option will insist that any use of a (non-standard)
|
||||
\`@next\` tag (in addition to any \`@yields\` tag) will be matched by a \`yield\`
|
||||
which uses a return value in the body of the generator (e.g.,
|
||||
\`const rv = yield;\` or \`const rv = yield value;\`). This (non-standard)
|
||||
tag is intended to be used to indicate a type and/or description of
|
||||
the value expected to be supplied by the user when supplied to the iterator
|
||||
by its \`next\` method, as with \`it.next(value)\` (with the iterator being
|
||||
the \`Generator\` iterator that is returned by the call to the generator
|
||||
function). This option will report an error if the generator function body
|
||||
merely has plain \`yield;\` or \`yield value;\` statements without returning
|
||||
the values. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
766
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/sortTags.js
generated
vendored
Normal file
766
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/sortTags.js
generated
vendored
Normal file
|
|
@ -0,0 +1,766 @@
|
|||
import defaultTagOrder from '../defaultTagOrder.js';
|
||||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
utils,
|
||||
// eslint-disable-next-line complexity -- Temporary
|
||||
}) => {
|
||||
const
|
||||
/**
|
||||
* @type {{
|
||||
* linesBetween: import('../iterateJsdoc.js').Integer,
|
||||
* tagExceptions: Record<string, number>,
|
||||
* tagSequence: {
|
||||
* tags: string[]
|
||||
* }[],
|
||||
* alphabetizeExtras: boolean,
|
||||
* reportTagGroupSpacing: boolean,
|
||||
* reportIntraTagGroupSpacing: boolean,
|
||||
* }}
|
||||
*/ {
|
||||
alphabetizeExtras = false,
|
||||
linesBetween = 1,
|
||||
reportIntraTagGroupSpacing = true,
|
||||
reportTagGroupSpacing = true,
|
||||
tagExceptions = {},
|
||||
tagSequence = defaultTagOrder,
|
||||
} = context.options[0] || {};
|
||||
|
||||
const tagList = tagSequence.flatMap((obj) => {
|
||||
/* typeof obj === 'string' ? obj : */
|
||||
return obj.tags;
|
||||
});
|
||||
|
||||
const otherPos = tagList.indexOf('-other');
|
||||
const endPos = otherPos > -1 ? otherPos : tagList.length;
|
||||
|
||||
let ongoingCount = 0;
|
||||
for (const [
|
||||
idx,
|
||||
tag,
|
||||
] of
|
||||
/**
|
||||
* @type {(
|
||||
* import('@es-joy/jsdoccomment').JsdocTagWithInline & {
|
||||
* originalIndex: import('../iterateJsdoc.js').Integer,
|
||||
* originalLine: import('../iterateJsdoc.js').Integer,
|
||||
* }
|
||||
* )[]}
|
||||
*/ (jsdoc.tags).entries()) {
|
||||
tag.originalIndex = idx;
|
||||
ongoingCount += tag.source.length;
|
||||
tag.originalLine = ongoingCount;
|
||||
}
|
||||
|
||||
/** @type {import('../iterateJsdoc.js').Integer|undefined} */
|
||||
let firstChangedTagLine;
|
||||
/** @type {import('../iterateJsdoc.js').Integer|undefined} */
|
||||
let firstChangedTagIndex;
|
||||
|
||||
/**
|
||||
* @type {(import('comment-parser').Spec & {
|
||||
* originalIndex: import('../iterateJsdoc.js').Integer,
|
||||
* originalLine: import('../iterateJsdoc.js').Integer,
|
||||
* })[]}
|
||||
*/
|
||||
const sortedTags = JSON.parse(JSON.stringify(jsdoc.tags));
|
||||
sortedTags.sort(({
|
||||
tag: tagNew,
|
||||
}, {
|
||||
originalIndex,
|
||||
originalLine,
|
||||
tag: tagOld,
|
||||
}) => {
|
||||
// Optimize: Just keep relative positions if the same tag name
|
||||
if (tagNew === tagOld) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const checkOrSetFirstChanged = () => {
|
||||
if (!firstChangedTagLine || originalLine < firstChangedTagLine) {
|
||||
firstChangedTagLine = originalLine;
|
||||
firstChangedTagIndex = originalIndex;
|
||||
}
|
||||
};
|
||||
|
||||
const newPos = tagList.indexOf(tagNew);
|
||||
const oldPos = tagList.indexOf(tagOld);
|
||||
|
||||
const preferredNewPos = newPos === -1 ? endPos : newPos;
|
||||
const preferredOldPos = oldPos === -1 ? endPos : oldPos;
|
||||
|
||||
if (preferredNewPos < preferredOldPos) {
|
||||
checkOrSetFirstChanged();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (preferredNewPos > preferredOldPos) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// preferredNewPos === preferredOldPos
|
||||
if (
|
||||
!alphabetizeExtras ||
|
||||
|
||||
// Optimize: If tagNew (or tagOld which is the same) was found in the
|
||||
// priority array, it can maintain its relative position—without need
|
||||
// of alphabetizing (secondary sorting)
|
||||
newPos >= 0
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tagNew < tagOld) {
|
||||
checkOrSetFirstChanged();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// tagNew > tagOld
|
||||
return 1;
|
||||
});
|
||||
|
||||
if (firstChangedTagLine === undefined) {
|
||||
// Should be ordered by now
|
||||
|
||||
/**
|
||||
* @type {import('comment-parser').Spec[]}
|
||||
*/
|
||||
const lastTagsOfGroup = [];
|
||||
|
||||
/**
|
||||
* @type {[
|
||||
* import('comment-parser').Spec,
|
||||
* import('../iterateJsdoc.js').Integer
|
||||
* ][]}
|
||||
*/
|
||||
const badLastTagsOfGroup = [];
|
||||
|
||||
/**
|
||||
* @param {import('comment-parser').Spec} tag
|
||||
*/
|
||||
const countTagEmptyLines = (tag) => {
|
||||
return tag.source.reduce((acc, {
|
||||
tokens: {
|
||||
description,
|
||||
end,
|
||||
name,
|
||||
tag: tg,
|
||||
type,
|
||||
},
|
||||
}) => {
|
||||
const empty = !tg && !type && !name && !description;
|
||||
// Reset the count so long as there is content
|
||||
return empty ? acc + Number(empty && !end) : 0;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
let idx = 0;
|
||||
for (const {
|
||||
tags,
|
||||
} of tagSequence) {
|
||||
let innerIdx;
|
||||
/** @type {import('comment-parser').Spec} */
|
||||
let currentTag;
|
||||
/** @type {import('comment-parser').Spec|undefined} */
|
||||
let lastTag;
|
||||
do {
|
||||
currentTag = jsdoc.tags[idx];
|
||||
if (!currentTag) {
|
||||
idx++;
|
||||
break;
|
||||
}
|
||||
|
||||
innerIdx = tags.indexOf(currentTag.tag);
|
||||
|
||||
if (
|
||||
innerIdx === -1 &&
|
||||
// eslint-disable-next-line no-loop-func -- Safe
|
||||
(!tags.includes('-other') || tagSequence.some(({
|
||||
tags: tgs,
|
||||
}) => {
|
||||
return tgs.includes(currentTag.tag);
|
||||
}))
|
||||
) {
|
||||
idx++;
|
||||
break;
|
||||
}
|
||||
|
||||
lastTag = currentTag;
|
||||
|
||||
idx++;
|
||||
} while (true);
|
||||
|
||||
idx--;
|
||||
|
||||
if (lastTag) {
|
||||
lastTagsOfGroup.push(lastTag);
|
||||
const ct = countTagEmptyLines(lastTag);
|
||||
if (
|
||||
ct !== linesBetween &&
|
||||
// Use another rule for adding to end (should be of interest outside this rule)
|
||||
jsdoc.tags[idx]
|
||||
) {
|
||||
badLastTagsOfGroup.push([
|
||||
lastTag, ct,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reportTagGroupSpacing && badLastTagsOfGroup.length) {
|
||||
/**
|
||||
* @param {import('comment-parser').Spec} tg
|
||||
* @returns {() => void}
|
||||
*/
|
||||
const fixer = (tg) => {
|
||||
return () => {
|
||||
// Due to https://github.com/syavorsky/comment-parser/issues/110 ,
|
||||
// we have to modify `jsdoc.source` rather than just modify tags
|
||||
// directly
|
||||
for (const [
|
||||
currIdx,
|
||||
{
|
||||
tokens,
|
||||
},
|
||||
] of jsdoc.source.entries()) {
|
||||
if (tokens.tag !== '@' + tg.tag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cannot be `tokens.end`, as dropped off last tag, so safe to
|
||||
// go on
|
||||
let newIdx = currIdx;
|
||||
|
||||
const emptyLine = () => {
|
||||
return {
|
||||
number: 0,
|
||||
source: '',
|
||||
tokens: utils.seedTokens({
|
||||
delimiter: '*',
|
||||
start: jsdoc.source[newIdx - 1].tokens.start,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
let existingEmptyLines = 0;
|
||||
while (true) {
|
||||
const nextTokens = jsdoc.source[++newIdx]?.tokens;
|
||||
|
||||
/* c8 ignore next 3 -- Guard */
|
||||
if (!nextTokens) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Should be no `nextTokens.end` to worry about since ignored
|
||||
// if not followed by tag
|
||||
|
||||
if (nextTokens.tag) {
|
||||
// Haven't made it to last tag instance yet, so keep looking
|
||||
if (nextTokens.tag === tokens.tag) {
|
||||
existingEmptyLines = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
const lineDiff = linesBetween - existingEmptyLines;
|
||||
if (lineDiff > 0) {
|
||||
const lines = Array.from({
|
||||
length: lineDiff,
|
||||
}, () => {
|
||||
return emptyLine();
|
||||
});
|
||||
jsdoc.source.splice(newIdx, 0, ...lines);
|
||||
} else {
|
||||
// lineDiff < 0
|
||||
jsdoc.source.splice(
|
||||
newIdx + lineDiff,
|
||||
-lineDiff,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const empty = !nextTokens.type && !nextTokens.name &&
|
||||
!nextTokens.description;
|
||||
|
||||
if (empty) {
|
||||
existingEmptyLines++;
|
||||
} else {
|
||||
// Has content again, so reset empty line count
|
||||
existingEmptyLines = 0;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for (const [
|
||||
srcIdx,
|
||||
src,
|
||||
] of jsdoc.source.entries()) {
|
||||
src.number = srcIdx;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
for (const [
|
||||
tg,
|
||||
] of badLastTagsOfGroup) {
|
||||
utils.reportJSDoc(
|
||||
'Tag groups do not have the expected whitespace',
|
||||
tg,
|
||||
fixer(tg),
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reportIntraTagGroupSpacing) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [
|
||||
tagIdx,
|
||||
tag,
|
||||
] of jsdoc.tags.entries()) {
|
||||
if (!jsdoc.tags[tagIdx + 1] || lastTagsOfGroup.includes(tag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ct = countTagEmptyLines(tag);
|
||||
if (ct && (!tagExceptions[tag.tag] || tagExceptions[tag.tag] < ct)) {
|
||||
const fixer = () => {
|
||||
let foundFirstTag = false;
|
||||
|
||||
/** @type {string|undefined} */
|
||||
let currentTag;
|
||||
|
||||
for (const [
|
||||
currIdx,
|
||||
{
|
||||
tokens: {
|
||||
description,
|
||||
end,
|
||||
name,
|
||||
tag: tg,
|
||||
type,
|
||||
},
|
||||
},
|
||||
] of jsdoc.source.entries()) {
|
||||
if (tg) {
|
||||
foundFirstTag = true;
|
||||
currentTag = tg;
|
||||
}
|
||||
|
||||
if (!foundFirstTag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentTag && !tg && !type && !name && !description && !end) {
|
||||
let nextIdx = currIdx;
|
||||
|
||||
let ignore = true;
|
||||
// Even if a tag of the same name as the last tags in a group,
|
||||
// could still be an earlier tag in that group
|
||||
|
||||
// eslint-disable-next-line no-loop-func -- Safe
|
||||
if (lastTagsOfGroup.some((lastTagOfGroup) => {
|
||||
return currentTag === '@' + lastTagOfGroup.tag;
|
||||
})) {
|
||||
while (true) {
|
||||
const nextTokens = jsdoc.source[++nextIdx]?.tokens;
|
||||
if (!nextTokens) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!nextTokens.tag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Followed by the same tag name, so not actually last in group,
|
||||
// and of interest
|
||||
if (nextTokens.tag === currentTag) {
|
||||
ignore = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (true) {
|
||||
const nextTokens = jsdoc.source[++nextIdx]?.tokens;
|
||||
if (!nextTokens || nextTokens.end) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Not the very last tag, so don't ignore
|
||||
if (nextTokens.tag) {
|
||||
ignore = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignore) {
|
||||
jsdoc.source.splice(currIdx, 1);
|
||||
for (const [
|
||||
srcIdx,
|
||||
src,
|
||||
] of jsdoc.source.entries()) {
|
||||
src.number = srcIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Intra-group tags have unexpected whitespace',
|
||||
tag,
|
||||
fixer,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const firstLine = utils.getFirstLine();
|
||||
|
||||
const fix = () => {
|
||||
const itemsToMoveRange = [
|
||||
...Array.from({
|
||||
length: jsdoc.tags.length -
|
||||
/** @type {import('../iterateJsdoc.js').Integer} */ (
|
||||
firstChangedTagIndex
|
||||
),
|
||||
}).keys(),
|
||||
];
|
||||
|
||||
const unchangedPriorTagDescriptions = jsdoc.tags.slice(
|
||||
0,
|
||||
firstChangedTagIndex,
|
||||
).reduce((ct, {
|
||||
source,
|
||||
}) => {
|
||||
return ct + source.length - 1;
|
||||
}, 0);
|
||||
|
||||
// This offset includes not only the offset from where the first tag
|
||||
// must begin, and the additional offset of where the first changed
|
||||
// tag begins, but it must also account for prior descriptions
|
||||
const initialOffset = /** @type {import('../iterateJsdoc.js').Integer} */ (
|
||||
firstLine
|
||||
) + /** @type {import('../iterateJsdoc.js').Integer} */ (firstChangedTagIndex) +
|
||||
|
||||
// May be the first tag, so don't try finding a prior one if so
|
||||
unchangedPriorTagDescriptions;
|
||||
|
||||
// Use `firstChangedTagLine` for line number to begin reporting/splicing
|
||||
for (const idx of itemsToMoveRange) {
|
||||
utils.removeTag(
|
||||
idx +
|
||||
/** @type {import('../iterateJsdoc.js').Integer} */ (
|
||||
firstChangedTagIndex
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const changedTags = sortedTags.slice(firstChangedTagIndex);
|
||||
let extraTagCount = 0;
|
||||
|
||||
for (const idx of itemsToMoveRange) {
|
||||
const changedTag = changedTags[idx];
|
||||
|
||||
utils.addTag(
|
||||
changedTag.tag,
|
||||
extraTagCount + initialOffset + idx,
|
||||
{
|
||||
...changedTag.source[0].tokens,
|
||||
|
||||
// `comment-parser` puts the `end` within the `tags` section, so
|
||||
// avoid adding another to jsdoc.source
|
||||
end: '',
|
||||
},
|
||||
);
|
||||
|
||||
for (const {
|
||||
tokens,
|
||||
} of changedTag.source.slice(1)) {
|
||||
if (!tokens.end) {
|
||||
utils.addLine(
|
||||
extraTagCount + initialOffset + idx + 1,
|
||||
{
|
||||
...tokens,
|
||||
end: '',
|
||||
},
|
||||
);
|
||||
extraTagCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
`Tags are not in the prescribed order: ${
|
||||
tagList.join(', ') || '(alphabetical)'
|
||||
}`,
|
||||
jsdoc.tags[/** @type {import('../iterateJsdoc.js').Integer} */ (
|
||||
firstChangedTagIndex
|
||||
)],
|
||||
fix,
|
||||
true,
|
||||
);
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Sorts tags by a specified sequence according to tag name, optionally adding line breaks between tag groups.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/sort-tags.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
alphabetizeExtras: {
|
||||
description: `Defaults to \`false\`. Alphabetizes any items not within \`tagSequence\` after any
|
||||
items within \`tagSequence\` (or in place of the special \`-other\` pseudo-tag)
|
||||
are sorted.
|
||||
|
||||
If you want all your tags alphabetized, you can supply an empty array for
|
||||
\`tagSequence\` along with setting this option to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
linesBetween: {
|
||||
description: `Indicates the number of lines to be added between tag groups. Defaults to 1.
|
||||
Do not set to 0 or 2+ if you are using \`tag-lines\` and \`"always"\` and do not
|
||||
set to 1+ if you are using \`tag-lines\` and \`"never"\`.`,
|
||||
type: 'integer',
|
||||
},
|
||||
reportIntraTagGroupSpacing: {
|
||||
description: `Whether to enable reporting and fixing of line breaks within tags of a given
|
||||
tag group. Defaults to \`true\` which will remove any line breaks at the end of
|
||||
such tags. Do not use with \`true\` if you are using \`tag-lines\` and \`always\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
reportTagGroupSpacing: {
|
||||
description: `Whether to enable reporting and fixing of line breaks between tag groups
|
||||
as set by \`linesBetween\`. Defaults to \`true\`. Note that the very last tag
|
||||
will not have spacing applied regardless. For adding line breaks there, you
|
||||
may wish to use the \`endLines\` option of the \`tag-lines\` rule.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
tagExceptions: {
|
||||
description: 'Allows specification by tag of a specific higher maximum number of lines. Keys are tags and values are the maximum number of lines allowed for such tags. Overrides `linesBetween`. Defaults to no special exceptions per tag.',
|
||||
patternProperties: {
|
||||
'.*': {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
tagSequence: {
|
||||
description: `An array of tag group objects indicating the preferred sequence for sorting tags.
|
||||
|
||||
Each item in the array should be an object with a \`tags\` property set to an array
|
||||
of tag names.
|
||||
|
||||
Tag names earlier in the list will be arranged first. The relative position of
|
||||
tags of the same name will not be changed.
|
||||
|
||||
Earlier groups will also be arranged before later groups, but with the added
|
||||
feature that additional line breaks may be added between (or before or after)
|
||||
such groups (depending on the setting of \`linesBetween\`).
|
||||
|
||||
Tag names not in the list will be grouped together at the end. The pseudo-tag
|
||||
\`-other\` can be used to place them anywhere else if desired. The tags will be
|
||||
placed in their order of appearance, or alphabetized if \`alphabetizeExtras\`
|
||||
is enabled, see more below about that option.
|
||||
|
||||
Defaults to the array below (noting that it is just a single tag group with
|
||||
no lines between groups by default).
|
||||
|
||||
Please note that this order is still experimental, so if you want to retain
|
||||
a fixed order that doesn't change into the future, supply your own
|
||||
\`tagSequence\`.
|
||||
|
||||
\`\`\`js
|
||||
[{tags: [
|
||||
// Brief descriptions
|
||||
'summary',
|
||||
'typeSummary',
|
||||
|
||||
// Module/file-level
|
||||
'module',
|
||||
'exports',
|
||||
'file',
|
||||
'fileoverview',
|
||||
'overview',
|
||||
'import',
|
||||
|
||||
// Identifying (name, type)
|
||||
'typedef',
|
||||
'interface',
|
||||
'record',
|
||||
'template',
|
||||
'name',
|
||||
'kind',
|
||||
'type',
|
||||
'alias',
|
||||
'external',
|
||||
'host',
|
||||
'callback',
|
||||
'func',
|
||||
'function',
|
||||
'method',
|
||||
'class',
|
||||
'constructor',
|
||||
|
||||
// Relationships
|
||||
'modifies',
|
||||
'mixes',
|
||||
'mixin',
|
||||
'mixinClass',
|
||||
'mixinFunction',
|
||||
'namespace',
|
||||
'borrows',
|
||||
'constructs',
|
||||
'lends',
|
||||
'implements',
|
||||
'requires',
|
||||
|
||||
// Long descriptions
|
||||
'desc',
|
||||
'description',
|
||||
'classdesc',
|
||||
'tutorial',
|
||||
'copyright',
|
||||
'license',
|
||||
|
||||
// Simple annotations
|
||||
'const',
|
||||
'constant',
|
||||
'final',
|
||||
'global',
|
||||
'readonly',
|
||||
'abstract',
|
||||
'virtual',
|
||||
'var',
|
||||
'member',
|
||||
'memberof',
|
||||
'memberof!',
|
||||
'inner',
|
||||
'instance',
|
||||
'inheritdoc',
|
||||
'inheritDoc',
|
||||
'override',
|
||||
'hideconstructor',
|
||||
|
||||
// Core function/object info
|
||||
'param',
|
||||
'arg',
|
||||
'argument',
|
||||
'prop',
|
||||
'property',
|
||||
'return',
|
||||
'returns',
|
||||
|
||||
// Important behavior details
|
||||
'async',
|
||||
'generator',
|
||||
'default',
|
||||
'defaultvalue',
|
||||
'enum',
|
||||
'augments',
|
||||
'extends',
|
||||
'throws',
|
||||
'exception',
|
||||
'yield',
|
||||
'yields',
|
||||
'event',
|
||||
'fires',
|
||||
'emits',
|
||||
'listens',
|
||||
'this',
|
||||
|
||||
// Access
|
||||
'static',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'access',
|
||||
'package',
|
||||
|
||||
'-other',
|
||||
|
||||
// Supplementary descriptions
|
||||
'see',
|
||||
'example',
|
||||
|
||||
// METADATA
|
||||
|
||||
// Other Closure (undocumented) metadata
|
||||
'closurePrimitive',
|
||||
'customElement',
|
||||
'expose',
|
||||
'hidden',
|
||||
'idGenerator',
|
||||
'meaning',
|
||||
'ngInject',
|
||||
'owner',
|
||||
'wizaction',
|
||||
|
||||
// Other Closure (documented) metadata
|
||||
'define',
|
||||
'dict',
|
||||
'export',
|
||||
'externs',
|
||||
'implicitCast',
|
||||
'noalias',
|
||||
'nocollapse',
|
||||
'nocompile',
|
||||
'noinline',
|
||||
'nosideeffects',
|
||||
'polymer',
|
||||
'polymerBehavior',
|
||||
'preserve',
|
||||
'struct',
|
||||
'suppress',
|
||||
'unrestricted',
|
||||
|
||||
// @homer0/prettier-plugin-jsdoc metadata
|
||||
'category',
|
||||
|
||||
// Non-Closure metadata
|
||||
'ignore',
|
||||
'author',
|
||||
'version',
|
||||
'variation',
|
||||
'since',
|
||||
'deprecated',
|
||||
'todo',
|
||||
]}];
|
||||
\`\`\``,
|
||||
items: {
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
tags: {
|
||||
description: 'See description on `tagSequence`.',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
477
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/tagLines.js
generated
vendored
Normal file
477
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/tagLines.js
generated
vendored
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* maxBlockLines: null|number,
|
||||
* startLines: null|number,
|
||||
* utils: import('../iterateJsdoc.js').Utils
|
||||
* }} cfg
|
||||
*/
|
||||
const checkMaxBlockLines = ({
|
||||
maxBlockLines,
|
||||
startLines,
|
||||
utils,
|
||||
}) => {
|
||||
if (typeof maxBlockLines !== 'number') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof startLines === 'number' && maxBlockLines < startLines) {
|
||||
utils.reportJSDoc(
|
||||
'If set to a number, `maxBlockLines` must be greater than or equal to `startLines`.',
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
const {
|
||||
description,
|
||||
} = utils.getDescription();
|
||||
const excessBlockLinesRegex = new RegExp('\n{' + (maxBlockLines + 2) + ',}', 'v');
|
||||
const excessBlockLinesMatch = description.match(excessBlockLinesRegex);
|
||||
const excessBlockLines = excessBlockLinesMatch?.[0]?.length ?? 0;
|
||||
if (excessBlockLinesMatch) {
|
||||
const excessIndexLine = description.slice(0, excessBlockLinesMatch.index).match(/\n/gv)?.length ?? 0;
|
||||
utils.reportJSDoc(
|
||||
`Expected a maximum of ${maxBlockLines} line${maxBlockLines === 1 ? '' : 's'} within block description`,
|
||||
{
|
||||
line: excessIndexLine,
|
||||
},
|
||||
() => {
|
||||
utils.setBlockDescription((info, seedTokens, descLines, postDelims) => {
|
||||
const newPostDelims = [
|
||||
...postDelims.slice(0, excessIndexLine),
|
||||
...postDelims.slice(excessIndexLine + excessBlockLines - 1 - maxBlockLines),
|
||||
];
|
||||
return [
|
||||
...descLines.slice(0, excessIndexLine),
|
||||
...descLines.slice(excessIndexLine + excessBlockLines - 1 - maxBlockLines),
|
||||
].map((desc, idx) => {
|
||||
return {
|
||||
number: 0,
|
||||
source: '',
|
||||
tokens: seedTokens({
|
||||
...info,
|
||||
description: desc,
|
||||
postDelimiter: newPostDelims[idx],
|
||||
}),
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
utils,
|
||||
}) => {
|
||||
const [
|
||||
alwaysNever = 'never',
|
||||
{
|
||||
applyToEndTag = true,
|
||||
count = 1,
|
||||
endLines = 0,
|
||||
maxBlockLines = null,
|
||||
startLines = 0,
|
||||
tags = {},
|
||||
} = {},
|
||||
] = context.options;
|
||||
|
||||
jsdoc.tags.some((tg, tagIdx) => {
|
||||
let lastTag;
|
||||
|
||||
/**
|
||||
* @type {null|import('../iterateJsdoc.js').Integer}
|
||||
*/
|
||||
let lastEmpty = null;
|
||||
|
||||
/**
|
||||
* @type {null|import('../iterateJsdoc.js').Integer}
|
||||
*/
|
||||
let reportIndex = null;
|
||||
let emptyLinesCount = 0;
|
||||
for (const [
|
||||
idx,
|
||||
{
|
||||
tokens: {
|
||||
description,
|
||||
end,
|
||||
name,
|
||||
tag,
|
||||
type,
|
||||
},
|
||||
},
|
||||
] of tg.source.entries()) {
|
||||
// May be text after a line break within a tag description
|
||||
if (description) {
|
||||
reportIndex = null;
|
||||
}
|
||||
|
||||
if (lastTag && [
|
||||
'always', 'any',
|
||||
].includes(tags[lastTag.slice(1)]?.lines)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const empty = !tag && !name && !type && !description;
|
||||
if (
|
||||
empty && !end &&
|
||||
(alwaysNever === 'never' ||
|
||||
lastTag && tags[lastTag.slice(1)]?.lines === 'never'
|
||||
)
|
||||
) {
|
||||
reportIndex = idx;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!end) {
|
||||
if (empty) {
|
||||
emptyLinesCount++;
|
||||
} else {
|
||||
emptyLinesCount = 0;
|
||||
}
|
||||
|
||||
lastEmpty = empty ? idx : null;
|
||||
}
|
||||
|
||||
lastTag = tag;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof endLines === 'number' &&
|
||||
lastEmpty !== null && tagIdx === jsdoc.tags.length - 1
|
||||
) {
|
||||
const lineDiff = endLines - emptyLinesCount;
|
||||
|
||||
if (lineDiff < 0) {
|
||||
const fixer = () => {
|
||||
utils.removeTag(tagIdx, {
|
||||
tagSourceOffset: /** @type {import('../iterateJsdoc.js').Integer} */ (
|
||||
lastEmpty
|
||||
) + lineDiff + 1,
|
||||
});
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
`Expected ${endLines} trailing lines`,
|
||||
{
|
||||
line: tg.source[lastEmpty].number + lineDiff + 1,
|
||||
},
|
||||
fixer,
|
||||
);
|
||||
} else if (lineDiff > 0) {
|
||||
const fixer = () => {
|
||||
utils.addLines(
|
||||
tagIdx,
|
||||
/** @type {import('../iterateJsdoc.js').Integer} */ (lastEmpty),
|
||||
endLines - emptyLinesCount,
|
||||
);
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
`Expected ${endLines} trailing lines`,
|
||||
{
|
||||
line: tg.source[lastEmpty].number,
|
||||
},
|
||||
fixer,
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (reportIndex !== null) {
|
||||
const fixer = () => {
|
||||
utils.removeTag(tagIdx, {
|
||||
tagSourceOffset: /** @type {import('../iterateJsdoc.js').Integer} */ (
|
||||
reportIndex
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Expected no lines between tags',
|
||||
{
|
||||
line: tg.source[0].number + 1,
|
||||
},
|
||||
fixer,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
(applyToEndTag ? jsdoc.tags : jsdoc.tags.slice(0, -1)).some((tg, tagIdx) => {
|
||||
/**
|
||||
* @type {{
|
||||
* idx: import('../iterateJsdoc.js').Integer,
|
||||
* number: import('../iterateJsdoc.js').Integer
|
||||
* }[]}
|
||||
*/
|
||||
const lines = [];
|
||||
|
||||
let currentTag;
|
||||
let tagSourceIdx = 0;
|
||||
for (const [
|
||||
idx,
|
||||
{
|
||||
number,
|
||||
tokens: {
|
||||
description,
|
||||
end,
|
||||
name,
|
||||
tag,
|
||||
type,
|
||||
},
|
||||
},
|
||||
] of tg.source.entries()) {
|
||||
if (description) {
|
||||
lines.splice(0);
|
||||
tagSourceIdx = idx;
|
||||
}
|
||||
|
||||
if (tag) {
|
||||
currentTag = tag;
|
||||
}
|
||||
|
||||
if (!tag && !name && !type && !description && !end) {
|
||||
lines.push({
|
||||
idx,
|
||||
number,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const currentTg = currentTag && tags[currentTag.slice(1)];
|
||||
const tagCount = currentTg?.count;
|
||||
|
||||
const defaultAlways = alwaysNever === 'always' && currentTg?.lines !== 'never' &&
|
||||
currentTg?.lines !== 'any' && lines.length < count;
|
||||
|
||||
let overrideAlways;
|
||||
let fixCount = count;
|
||||
if (!defaultAlways) {
|
||||
fixCount = typeof tagCount === 'number' ? tagCount : count;
|
||||
overrideAlways = currentTg?.lines === 'always' &&
|
||||
lines.length < fixCount;
|
||||
}
|
||||
|
||||
if (defaultAlways || overrideAlways) {
|
||||
const fixer = () => {
|
||||
utils.addLines(tagIdx, lines[lines.length - 1]?.idx || tagSourceIdx + 1, fixCount - lines.length);
|
||||
};
|
||||
|
||||
const line = lines[lines.length - 1]?.number || tg.source[tagSourceIdx].number;
|
||||
utils.reportJSDoc(
|
||||
`Expected ${fixCount} line${fixCount === 1 ? '' : 's'} between tags but found ${lines.length}`,
|
||||
{
|
||||
line,
|
||||
},
|
||||
fixer,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (checkMaxBlockLines({
|
||||
maxBlockLines,
|
||||
startLines,
|
||||
utils,
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof startLines === 'number') {
|
||||
if (!jsdoc.tags.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
description,
|
||||
lastDescriptionLine,
|
||||
} = utils.getDescription();
|
||||
if (!(/\S/v).test(description)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trailingLines = description.match(/\n+$/v)?.[0]?.length;
|
||||
const trailingDiff = (trailingLines ?? 0) - startLines;
|
||||
if (trailingDiff > 0) {
|
||||
utils.reportJSDoc(
|
||||
`Expected only ${startLines} line${startLines === 1 ? '' : 's'} after block description`,
|
||||
{
|
||||
line: lastDescriptionLine - trailingDiff,
|
||||
},
|
||||
() => {
|
||||
utils.setBlockDescription((info, seedTokens, descLines, postDelims) => {
|
||||
return descLines.slice(0, -trailingDiff).map((desc, idx) => {
|
||||
return {
|
||||
number: 0,
|
||||
source: '',
|
||||
tokens: seedTokens({
|
||||
...info,
|
||||
description: desc,
|
||||
postDelimiter: postDelims[idx],
|
||||
}),
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
} else if (trailingDiff < 0) {
|
||||
utils.reportJSDoc(
|
||||
`Expected ${startLines} lines after block description`,
|
||||
{
|
||||
line: lastDescriptionLine,
|
||||
},
|
||||
() => {
|
||||
utils.setBlockDescription((info, seedTokens, descLines, postDelims) => {
|
||||
return [
|
||||
...descLines,
|
||||
...Array.from({
|
||||
length: -trailingDiff,
|
||||
}, () => {
|
||||
return '';
|
||||
}),
|
||||
].map((desc, idx) => {
|
||||
return {
|
||||
number: 0,
|
||||
source: '',
|
||||
tokens: seedTokens({
|
||||
...info,
|
||||
description: desc,
|
||||
postDelimiter: desc.trim() ? postDelims[idx] : '',
|
||||
}),
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Enforces lines (or no lines) before, after, or between tags.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/tag-lines.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
description: `Defaults to "never". "any" is only useful with \`tags\` (allowing non-enforcement of lines except
|
||||
for particular tags) or with \`startLines\`, \`endLines\`, or \`maxBlockLines\`. It is also
|
||||
necessary if using the linebreak-setting options of the \`sort-tags\` rule
|
||||
so that the two rules won't conflict in both attempting to set lines
|
||||
between tags.`,
|
||||
enum: [
|
||||
'always', 'any', 'never',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
applyToEndTag: {
|
||||
description: `Set to \`false\` and use with "always" to indicate the normal lines to be
|
||||
added after tags should not be added after the final tag.
|
||||
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
count: {
|
||||
description: `Use with "always" to indicate the number of lines to require be present.
|
||||
|
||||
Defaults to 1.`,
|
||||
type: 'integer',
|
||||
},
|
||||
endLines: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'integer',
|
||||
},
|
||||
{
|
||||
type: 'null',
|
||||
},
|
||||
],
|
||||
description: `If not set to \`null\`, will enforce end lines to the given count on the
|
||||
final tag only.
|
||||
|
||||
Defaults to \`0\`.`,
|
||||
},
|
||||
maxBlockLines: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'integer',
|
||||
},
|
||||
{
|
||||
type: 'null',
|
||||
},
|
||||
],
|
||||
description: `If not set to \`null\`, will enforce a maximum number of lines to the given count anywhere in the block description.
|
||||
|
||||
Note that if non-\`null\`, \`maxBlockLines\` must be greater than or equal to \`startLines\`.
|
||||
|
||||
Defaults to \`null\`.`,
|
||||
},
|
||||
startLines: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'integer',
|
||||
},
|
||||
{
|
||||
type: 'null',
|
||||
},
|
||||
],
|
||||
description: `If not set to \`null\`, will enforce end lines to the given count before the
|
||||
first tag only, unless there is only whitespace content, in which case,
|
||||
a line count will not be enforced.
|
||||
|
||||
Defaults to \`0\`.`,
|
||||
},
|
||||
tags: {
|
||||
description: `Overrides the default behavior depending on specific tags.
|
||||
|
||||
An object whose keys are tag names and whose values are objects with the
|
||||
following keys:
|
||||
|
||||
1. \`lines\` - Set to \`always\`, \`never\`, or \`any\` to override.
|
||||
2. \`count\` - Overrides main \`count\` (for "always")
|
||||
|
||||
Defaults to empty object.`,
|
||||
patternProperties: {
|
||||
'.*': {
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
count: {
|
||||
type: 'integer',
|
||||
},
|
||||
lines: {
|
||||
enum: [
|
||||
'always', 'never', 'any',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
158
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/textEscaping.js
generated
vendored
Normal file
158
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/textEscaping.js
generated
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
|
||||
// We could disallow raw gt, quot, and apos, but allow for parity; but we do
|
||||
// not allow hex or decimal character references
|
||||
const htmlRegex = /(<|&(?!(?:amp|lt|gt|quot|apos);))(?=\S)/v;
|
||||
const markdownRegex = /(?<!\\)(`+)([^`]+)\1(?!`)/v;
|
||||
|
||||
/**
|
||||
* @param {string} desc
|
||||
* @returns {string}
|
||||
*/
|
||||
const htmlReplacer = (desc) => {
|
||||
return desc.replaceAll(new RegExp(htmlRegex, 'gv'), (_) => {
|
||||
if (_ === '<') {
|
||||
return '<';
|
||||
}
|
||||
|
||||
return '&';
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} desc
|
||||
* @returns {string}
|
||||
*/
|
||||
const markdownReplacer = (desc) => {
|
||||
return desc.replaceAll(new RegExp(markdownRegex, 'gv'), (_, backticks, encapsed) => {
|
||||
const bookend = '`'.repeat(backticks.length);
|
||||
return `\\${bookend}${encapsed}${bookend}`;
|
||||
});
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
escapeHTML,
|
||||
escapeMarkdown,
|
||||
} = context.options[0] || {};
|
||||
|
||||
if (!escapeHTML && !escapeMarkdown) {
|
||||
context.report({
|
||||
loc: {
|
||||
end: {
|
||||
column: 1,
|
||||
line: 1,
|
||||
},
|
||||
start: {
|
||||
column: 1,
|
||||
line: 1,
|
||||
},
|
||||
},
|
||||
message: 'You must include either `escapeHTML` or `escapeMarkdown`',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
descriptions,
|
||||
} = utils.getDescription();
|
||||
|
||||
if (escapeHTML) {
|
||||
if (descriptions.some((desc) => {
|
||||
return htmlRegex.test(desc);
|
||||
})) {
|
||||
const line = utils.setDescriptionLines(htmlRegex, htmlReplacer);
|
||||
utils.reportJSDoc('You have unescaped HTML characters < or &', {
|
||||
line,
|
||||
}, () => {}, true);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const tag of jsdoc.tags) {
|
||||
if (tag.tag === 'example') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/** @type {string[]} */ (
|
||||
utils.getTagDescription(tag, true)
|
||||
).some((desc) => {
|
||||
return htmlRegex.test(desc);
|
||||
})) {
|
||||
const line = utils.setTagDescription(tag, htmlRegex, htmlReplacer) +
|
||||
tag.source[0].number;
|
||||
utils.reportJSDoc('You have unescaped HTML characters < or & in a tag', {
|
||||
line,
|
||||
}, () => {}, true);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (descriptions.some((desc) => {
|
||||
return markdownRegex.test(desc);
|
||||
})) {
|
||||
const line = utils.setDescriptionLines(markdownRegex, markdownReplacer);
|
||||
utils.reportJSDoc('You have unescaped Markdown backtick sequences', {
|
||||
line,
|
||||
}, () => {}, true);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const tag of jsdoc.tags) {
|
||||
if (tag.tag === 'example') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/** @type {string[]} */ (
|
||||
utils.getTagDescription(tag, true)
|
||||
).some((desc) => {
|
||||
return markdownRegex.test(desc);
|
||||
})) {
|
||||
const line = utils.setTagDescription(
|
||||
tag, markdownRegex, markdownReplacer,
|
||||
) + tag.source[0].number;
|
||||
utils.reportJSDoc(
|
||||
'You have unescaped Markdown backtick sequences in a tag',
|
||||
{
|
||||
line,
|
||||
},
|
||||
() => {},
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Auto-escape certain characters that are input within block and tag descriptions.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/text-escaping.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
escapeHTML: {
|
||||
description: `This option escapes all \`<\` and \`&\` characters (except those followed by
|
||||
whitespace which are treated as literals by Visual Studio Code). Defaults to
|
||||
\`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
escapeMarkdown: {
|
||||
description: `This option escapes the first backtick (\`\` \` \`\`) in a paired sequence.
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
300
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/tsMethodSignatureStyle.js
generated
vendored
Normal file
300
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/tsMethodSignatureStyle.js
generated
vendored
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
rewireByParsedType,
|
||||
} from '../jsdocUtils.js';
|
||||
import {
|
||||
parse as parseType,
|
||||
traverse,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
indent,
|
||||
jsdoc,
|
||||
utils,
|
||||
}) => {
|
||||
const functionType = context.options[0] ?? 'property';
|
||||
const {
|
||||
enableFixer = true,
|
||||
} = context.options[1] ?? {};
|
||||
|
||||
/**
|
||||
* @param {import('@es-joy/jsdoccomment').JsdocTagWithInline} tag
|
||||
*/
|
||||
const checkType = (tag) => {
|
||||
const potentialType = tag.type;
|
||||
let parsedType;
|
||||
try {
|
||||
parsedType = parseType(
|
||||
/** @type {string} */ (potentialType), 'typescript',
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
traverse(parsedType, (nde, parentNode) => {
|
||||
// @ts-expect-error Adding our own property for use below
|
||||
nde.parentNode = parentNode;
|
||||
});
|
||||
|
||||
traverse(parsedType, (nde, parentNode, property, idx) => {
|
||||
switch (nde.type) {
|
||||
case 'JsdocTypeFunction': {
|
||||
if (functionType !== 'method') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (parentNode?.type === 'JsdocTypeObjectField' &&
|
||||
typeof parentNode.key === 'string'
|
||||
) {
|
||||
utils.reportJSDoc(
|
||||
'Found function property; prefer method signature.',
|
||||
tag,
|
||||
enableFixer ? () => {
|
||||
const objectField = parentNode;
|
||||
const obj =
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').ObjectFieldResult & {
|
||||
* parentNode: import('jsdoc-type-pratt-parser').ObjectResult
|
||||
* }}
|
||||
*/
|
||||
(objectField).parentNode;
|
||||
|
||||
const index = obj.elements.indexOf(parentNode);
|
||||
|
||||
obj.elements[index] = {
|
||||
/* c8 ignore next 5 -- Guard */
|
||||
meta: nde.meta ?
|
||||
{
|
||||
quote: objectField.meta.quote,
|
||||
...nde.meta,
|
||||
} :
|
||||
{
|
||||
quote: objectField.meta.quote,
|
||||
},
|
||||
name: /** @type {string} */ (objectField.key),
|
||||
parameters: nde.parameters,
|
||||
returnType: /** @type {import('jsdoc-type-pratt-parser').RootResult} */ (
|
||||
nde.returnType
|
||||
),
|
||||
type: 'JsdocTypeMethodSignature',
|
||||
typeParameters: nde.typeParameters,
|
||||
};
|
||||
|
||||
rewireByParsedType(jsdoc, tag, parsedType, indent);
|
||||
} : null,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (parentNode?.type === 'JsdocTypeParenthesis' &&
|
||||
// @ts-expect-error Our own added API
|
||||
parentNode.parentNode?.type === 'JsdocTypeIntersection' &&
|
||||
// @ts-expect-error Our own added API
|
||||
parentNode.parentNode.parentNode.type === 'JsdocTypeObjectField' &&
|
||||
// @ts-expect-error Our own added API
|
||||
typeof parentNode.parentNode.parentNode.key === 'string'
|
||||
) {
|
||||
// @ts-expect-error Our own added API
|
||||
const intersection = parentNode.parentNode;
|
||||
const objectField = intersection.parentNode;
|
||||
const object = objectField.parentNode;
|
||||
// const objFieldIndex = object.elements.indexOf(objectField);
|
||||
|
||||
/**
|
||||
* @param {import('jsdoc-type-pratt-parser').FunctionResult} func
|
||||
*/
|
||||
const convertToMethod = (func) => {
|
||||
return /** @type {import('jsdoc-type-pratt-parser').MethodSignatureResult} */ ({
|
||||
/* c8 ignore next 5 -- Guard */
|
||||
meta: func.meta ?
|
||||
{
|
||||
quote: objectField.meta.quote,
|
||||
...func.meta,
|
||||
} :
|
||||
{
|
||||
quote: objectField.meta.quote,
|
||||
},
|
||||
name: /** @type {string} */ (objectField.key),
|
||||
parameters: func.parameters,
|
||||
returnType: /** @type {import('jsdoc-type-pratt-parser').RootResult} */ (
|
||||
func.returnType
|
||||
),
|
||||
type: 'JsdocTypeMethodSignature',
|
||||
typeParameters: func.typeParameters,
|
||||
});
|
||||
};
|
||||
|
||||
/** @type {import('jsdoc-type-pratt-parser').MethodSignatureResult[]} */
|
||||
const methods = [];
|
||||
/** @type {number[]} */
|
||||
const methodIndexes = [];
|
||||
for (const [
|
||||
index,
|
||||
element,
|
||||
] of intersection.elements.entries()) {
|
||||
if (
|
||||
element.type !== 'JsdocTypeParenthesis' ||
|
||||
element.element.type !== 'JsdocTypeFunction'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
methods.push(convertToMethod(element.element));
|
||||
methodIndexes.push(index);
|
||||
}
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Found function property; prefer method signature.',
|
||||
tag,
|
||||
enableFixer ? () => {
|
||||
for (const methodIndex of methodIndexes.toReversed()) {
|
||||
object.elements.splice(methodIndex, 1);
|
||||
}
|
||||
|
||||
object.elements.splice(methodIndexes[0], 0, ...methods);
|
||||
|
||||
rewireByParsedType(jsdoc, tag, parsedType, indent);
|
||||
} : null,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'JsdocTypeMethodSignature': {
|
||||
if (functionType !== 'property') {
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('jsdoc-type-pratt-parser').MethodSignatureResult} node
|
||||
*/
|
||||
const convertToFunction = (node) => {
|
||||
return {
|
||||
arrow: true,
|
||||
constructor: false,
|
||||
meta: /** @type {Required<import('jsdoc-type-pratt-parser').MethodSignatureResult['meta']>} */ (
|
||||
node.meta
|
||||
),
|
||||
parameters: node.parameters,
|
||||
parenthesis: true,
|
||||
returnType: node.returnType,
|
||||
type: 'JsdocTypeFunction',
|
||||
typeParameters: node.typeParameters,
|
||||
};
|
||||
};
|
||||
|
||||
utils.reportJSDoc(
|
||||
'Found method signature; prefer function property.',
|
||||
tag,
|
||||
enableFixer ? () => {
|
||||
/* c8 ignore next 3 -- TS guard */
|
||||
if (!parentNode || !property || typeof idx !== 'number') {
|
||||
throw new Error('Unexpected lack of parent or property');
|
||||
}
|
||||
|
||||
const object = /** @type {import('jsdoc-type-pratt-parser').ObjectResult} */ (
|
||||
parentNode
|
||||
);
|
||||
|
||||
const funcs = [];
|
||||
const removals = [];
|
||||
|
||||
for (const [
|
||||
index,
|
||||
element,
|
||||
] of object.elements.entries()) {
|
||||
if (element.type === 'JsdocTypeMethodSignature' &&
|
||||
element.name === nde.name
|
||||
) {
|
||||
funcs.push(convertToFunction(element));
|
||||
if (index !== idx) {
|
||||
removals.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (funcs.length === 1) {
|
||||
object.elements[idx] = /** @type {import('jsdoc-type-pratt-parser').ObjectFieldResult} */ ({
|
||||
key: nde.name,
|
||||
meta: nde.meta,
|
||||
optional: false,
|
||||
readonly: false,
|
||||
right: funcs[0],
|
||||
type: 'JsdocTypeObjectField',
|
||||
});
|
||||
} else {
|
||||
for (const removal of removals.toReversed()) {
|
||||
object.elements.splice(removal, 1);
|
||||
}
|
||||
|
||||
object.elements[idx] = {
|
||||
key: nde.name,
|
||||
meta: nde.meta,
|
||||
optional: false,
|
||||
readonly: false,
|
||||
right: {
|
||||
elements: funcs.map((func) => {
|
||||
return /** @type {import('jsdoc-type-pratt-parser').ParenthesisResult} */ ({
|
||||
element: func,
|
||||
type: 'JsdocTypeParenthesis',
|
||||
});
|
||||
}),
|
||||
type: 'JsdocTypeIntersection',
|
||||
},
|
||||
type: 'JsdocTypeObjectField',
|
||||
};
|
||||
}
|
||||
|
||||
rewireByParsedType(jsdoc, tag, parsedType, indent);
|
||||
} : null,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const tags = utils.filterTags(({
|
||||
tag,
|
||||
}) => {
|
||||
return Boolean(tag !== 'import' && utils.tagMightHaveTypePosition(tag));
|
||||
});
|
||||
|
||||
for (const tag of tags) {
|
||||
if (tag.type) {
|
||||
checkType(tag);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Prefers either function properties or method signatures',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/ts-method-signature-style.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
enum: [
|
||||
'method',
|
||||
'property',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
enableFixer: {
|
||||
description: 'Whether to enable the fixer. Defaults to `true`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
61
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/tsNoEmptyObjectType.js
generated
vendored
Normal file
61
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/tsNoEmptyObjectType.js
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
parse as parseType,
|
||||
traverse,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
if (settings.mode !== 'typescript') {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@es-joy/jsdoccomment').JsdocTagWithInline} tag
|
||||
*/
|
||||
const checkType = (tag) => {
|
||||
const potentialType = tag.type;
|
||||
let parsedType;
|
||||
try {
|
||||
parsedType = parseType(
|
||||
/** @type {string} */ (potentialType), 'typescript',
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
traverse(parsedType, (nde) => {
|
||||
switch (nde.type) {
|
||||
case 'JsdocTypeObject': {
|
||||
if (!nde.elements.length) {
|
||||
utils.reportJSDoc('No empty object type.', tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const tags = utils.filterTags(({
|
||||
tag,
|
||||
}) => {
|
||||
return Boolean(tag !== 'import' && utils.tagMightHaveTypePosition(tag));
|
||||
});
|
||||
|
||||
for (const tag of tags) {
|
||||
if (tag.type) {
|
||||
checkType(tag);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Warns against use of the empty object type',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/ts-no-empty-object-type.md#repos-sticky-header',
|
||||
},
|
||||
schema: [],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
130
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/tsNoUnnecessaryTemplateExpression.js
generated
vendored
Normal file
130
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/tsNoUnnecessaryTemplateExpression.js
generated
vendored
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
rewireByParsedType,
|
||||
} from '../jsdocUtils.js';
|
||||
import {
|
||||
parse as parseType,
|
||||
traverse,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
indent,
|
||||
jsdoc,
|
||||
settings,
|
||||
utils,
|
||||
}) => {
|
||||
if (settings.mode !== 'typescript') {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
enableFixer = true,
|
||||
} = context.options[0] ?? {};
|
||||
|
||||
/**
|
||||
* @param {import('@es-joy/jsdoccomment').JsdocTagWithInline} tag
|
||||
*/
|
||||
const checkType = (tag) => {
|
||||
const potentialType = tag.type;
|
||||
/** @type {import('jsdoc-type-pratt-parser').RootResult} */
|
||||
let parsedType;
|
||||
try {
|
||||
parsedType = parseType(
|
||||
/** @type {string} */ (potentialType), 'typescript',
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
traverse(parsedType, (nde, parentNode, property, index) => {
|
||||
switch (nde.type) {
|
||||
case 'JsdocTypeTemplateLiteral': {
|
||||
const stringInterpolationIndex = nde.interpolations.findIndex((interpolation) => {
|
||||
return interpolation.type === 'JsdocTypeStringValue';
|
||||
});
|
||||
if (stringInterpolationIndex > -1) {
|
||||
utils.reportJSDoc(
|
||||
'Found an unnecessary string literal within a template.',
|
||||
tag,
|
||||
enableFixer ? () => {
|
||||
nde.literals.splice(
|
||||
stringInterpolationIndex,
|
||||
2,
|
||||
nde.literals[stringInterpolationIndex] +
|
||||
/** @type {import('jsdoc-type-pratt-parser').StringValueResult} */
|
||||
(nde.interpolations[stringInterpolationIndex]).value +
|
||||
nde.literals[stringInterpolationIndex + 1],
|
||||
);
|
||||
|
||||
nde.interpolations.splice(
|
||||
stringInterpolationIndex, 1,
|
||||
);
|
||||
|
||||
rewireByParsedType(jsdoc, tag, parsedType, indent);
|
||||
} : null,
|
||||
);
|
||||
} else if (nde.literals.length === 2 && nde.literals[0] === '' &&
|
||||
nde.literals[1] === ''
|
||||
) {
|
||||
utils.reportJSDoc(
|
||||
'Found a lone template expression within a template.',
|
||||
tag,
|
||||
enableFixer ? () => {
|
||||
const interpolation = nde.interpolations[0];
|
||||
|
||||
if (parentNode && property) {
|
||||
if (typeof index === 'number') {
|
||||
// @ts-expect-error Safe
|
||||
parentNode[property][index] = interpolation;
|
||||
} else {
|
||||
// @ts-expect-error Safe
|
||||
parentNode[property] = interpolation;
|
||||
}
|
||||
} else {
|
||||
parsedType = interpolation;
|
||||
}
|
||||
|
||||
rewireByParsedType(jsdoc, tag, parsedType, indent);
|
||||
} : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const tags = utils.filterTags(({
|
||||
tag,
|
||||
}) => {
|
||||
return Boolean(tag !== 'import' && utils.tagMightHaveTypePosition(tag));
|
||||
});
|
||||
|
||||
for (const tag of tags) {
|
||||
if (tag.type) {
|
||||
checkType(tag);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Catches unnecessary template expressions such as string expressions within a template literal.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/ts-no-unnecessary-template-expression.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
enableFixer: {
|
||||
description: 'Whether to enable the fixer. Defaults to `true`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
127
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/tsPreferFunctionType.js
generated
vendored
Normal file
127
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/tsPreferFunctionType.js
generated
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
rewireByParsedType,
|
||||
} from '../jsdocUtils.js';
|
||||
import {
|
||||
parse as parseType,
|
||||
traverse,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
indent,
|
||||
jsdoc,
|
||||
utils,
|
||||
}) => {
|
||||
const {
|
||||
enableFixer = true,
|
||||
} = context.options[0] || {};
|
||||
|
||||
/**
|
||||
* @param {import('@es-joy/jsdoccomment').JsdocTagWithInline} tag
|
||||
*/
|
||||
const checkType = (tag) => {
|
||||
const potentialType = tag.type;
|
||||
|
||||
/** @type {import('jsdoc-type-pratt-parser').RootResult} */
|
||||
let parsedType;
|
||||
try {
|
||||
parsedType = parseType(
|
||||
/** @type {string} */ (potentialType), 'typescript',
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
traverse(parsedType, (nde, parentNode) => {
|
||||
// @ts-expect-error Adding our own property for use below
|
||||
nde.parentNode = parentNode;
|
||||
});
|
||||
|
||||
traverse(parsedType, (nde, parentNode, property, index) => {
|
||||
switch (nde.type) {
|
||||
case 'JsdocTypeCallSignature': {
|
||||
const object = /** @type {import('jsdoc-type-pratt-parser').ObjectResult} */ (
|
||||
parentNode
|
||||
);
|
||||
if (typeof index === 'number' && object.elements.length === 1) {
|
||||
utils.reportJSDoc(
|
||||
'Call signature found; function type preferred.',
|
||||
tag,
|
||||
enableFixer ? () => {
|
||||
const func = /** @type {import('jsdoc-type-pratt-parser').FunctionResult} */ ({
|
||||
arrow: true,
|
||||
constructor: false,
|
||||
meta: /** @type {Required<import('jsdoc-type-pratt-parser').MethodSignatureResult['meta']>} */ (
|
||||
nde.meta
|
||||
),
|
||||
parameters: nde.parameters,
|
||||
parenthesis: true,
|
||||
returnType: nde.returnType,
|
||||
type: 'JsdocTypeFunction',
|
||||
typeParameters: nde.typeParameters,
|
||||
});
|
||||
|
||||
if (property && 'parentNode' in object && object.parentNode) {
|
||||
if (typeof object.parentNode === 'object' &&
|
||||
'elements' in object.parentNode &&
|
||||
Array.isArray(object.parentNode.elements)
|
||||
) {
|
||||
const idx = object.parentNode.elements.indexOf(object);
|
||||
object.parentNode.elements[idx] = func;
|
||||
/* c8 ignore next 6 -- Guard */
|
||||
} else {
|
||||
throw new Error(
|
||||
// @ts-expect-error Ok
|
||||
`Rule currently unable to handle type ${object.parentNode.type}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
parsedType = func;
|
||||
}
|
||||
|
||||
rewireByParsedType(jsdoc, tag, parsedType, indent);
|
||||
} : null,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const tags = utils.filterTags(({
|
||||
tag,
|
||||
}) => {
|
||||
return Boolean(tag !== 'import' && utils.tagMightHaveTypePosition(tag));
|
||||
});
|
||||
|
||||
for (const tag of tags) {
|
||||
if (tag.type) {
|
||||
checkType(tag);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Prefers function types over call signatures when there are no other properties.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/ts-prefer-function-type.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
enableFixer: {
|
||||
description: 'Whether to enable the fixer or not',
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
670
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/typeFormatting.js
generated
vendored
Normal file
670
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/typeFormatting.js
generated
vendored
Normal file
|
|
@ -0,0 +1,670 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
rewireByParsedType,
|
||||
} from '../jsdocUtils.js';
|
||||
import {
|
||||
parse as parseType,
|
||||
stringify,
|
||||
traverse,
|
||||
tryParse as tryParseType,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
const digitRegex = (/^(\d+(\.\d*)?|\.\d+)([eE][\-+]?\d+)?$/v);
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
indent,
|
||||
jsdoc,
|
||||
settings,
|
||||
utils,
|
||||
// eslint-disable-next-line complexity -- Todo
|
||||
}) => {
|
||||
const {
|
||||
arrayBrackets = 'square',
|
||||
arrowFunctionPostReturnMarkerSpacing = ' ',
|
||||
arrowFunctionPreReturnMarkerSpacing = ' ',
|
||||
enableFixer = true,
|
||||
functionOrClassParameterSpacing = ' ',
|
||||
functionOrClassPostGenericSpacing = '',
|
||||
functionOrClassPostReturnMarkerSpacing = ' ',
|
||||
functionOrClassPreReturnMarkerSpacing = '',
|
||||
functionOrClassTypeParameterSpacing = ' ',
|
||||
genericAndTupleElementSpacing = ' ',
|
||||
genericDot = false,
|
||||
keyValuePostColonSpacing = ' ',
|
||||
keyValuePostKeySpacing = '',
|
||||
keyValuePostOptionalSpacing = '',
|
||||
keyValuePostVariadicSpacing = '',
|
||||
methodQuotes = 'double',
|
||||
objectFieldIndent = '',
|
||||
objectFieldQuote = null,
|
||||
objectFieldSeparator = 'comma',
|
||||
objectFieldSeparatorOptionalLinebreak = true,
|
||||
objectFieldSeparatorTrailingPunctuation = false,
|
||||
parameterDefaultValueSpacing = ' ',
|
||||
postMethodNameSpacing = '',
|
||||
postNewSpacing = ' ',
|
||||
// propertyQuotes = null,
|
||||
separatorForSingleObjectField = false,
|
||||
stringQuotes = 'double',
|
||||
typeBracketSpacing = '',
|
||||
unionSpacing = ' ',
|
||||
} = context.options[0] || {};
|
||||
|
||||
const {
|
||||
mode,
|
||||
} = settings;
|
||||
|
||||
/**
|
||||
* @param {import('@es-joy/jsdoccomment').JsdocTagWithInline} tag
|
||||
*/
|
||||
const checkTypeFormats = (tag) => {
|
||||
const potentialType = tag.type;
|
||||
let parsedType;
|
||||
try {
|
||||
parsedType = mode === 'permissive' ?
|
||||
tryParseType(/** @type {string} */ (potentialType)) :
|
||||
parseType(/** @type {string} */ (potentialType), mode);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
const fix = () => {
|
||||
rewireByParsedType(jsdoc, tag, parsedType, indent, typeBracketSpacing);
|
||||
};
|
||||
|
||||
/** @type {string[]} */
|
||||
const errorMessages = [];
|
||||
|
||||
if (typeBracketSpacing && (!tag.type.startsWith(typeBracketSpacing) || !tag.type.endsWith(typeBracketSpacing))) {
|
||||
errorMessages.push(`Must have initial and final "${typeBracketSpacing}" spacing`);
|
||||
} else if (!typeBracketSpacing && ((/^\s/v).test(tag.type) || (/\s$/v).test(tag.type))) {
|
||||
errorMessages.push('Must have no initial spacing');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity -- Todo
|
||||
traverse(parsedType, (nde) => {
|
||||
let errorMessage = '';
|
||||
|
||||
/**
|
||||
* @param {Partial<import('jsdoc-type-pratt-parser').FunctionResult['meta']> & {
|
||||
* postNewSpacing?: string,
|
||||
* postMethodNameSpacing?: string
|
||||
* }} meta
|
||||
* @returns {Required<import('jsdoc-type-pratt-parser').FunctionResult['meta']> & {
|
||||
* postNewSpacing?: string,
|
||||
* postMethodNameSpacing?: string
|
||||
* }}
|
||||
*/
|
||||
const conditionalAdds = (meta) => {
|
||||
const typNode =
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').FunctionResult|
|
||||
* import('jsdoc-type-pratt-parser').CallSignatureResult|
|
||||
* import('jsdoc-type-pratt-parser').ComputedMethodResult|
|
||||
* import('jsdoc-type-pratt-parser').ConstructorSignatureResult|
|
||||
* import('jsdoc-type-pratt-parser').MethodSignatureResult
|
||||
* }
|
||||
*/ (nde);
|
||||
|
||||
/**
|
||||
* @type {Required<import('jsdoc-type-pratt-parser').FunctionResult['meta']> & {
|
||||
* postNewSpacing?: string,
|
||||
* postMethodNameSpacing?: string
|
||||
* }}
|
||||
*/
|
||||
const newMeta = {
|
||||
parameterSpacing: meta.parameterSpacing ?? typNode.meta?.parameterSpacing ?? ' ',
|
||||
postGenericSpacing: meta.postGenericSpacing ?? typNode.meta?.postGenericSpacing ?? '',
|
||||
postReturnMarkerSpacing: meta.postReturnMarkerSpacing ?? typNode.meta?.postReturnMarkerSpacing ?? ' ',
|
||||
preReturnMarkerSpacing: meta.preReturnMarkerSpacing ?? typNode.meta?.preReturnMarkerSpacing ?? '',
|
||||
typeParameterSpacing: meta.typeParameterSpacing ?? typNode.meta?.typeParameterSpacing ?? ' ',
|
||||
};
|
||||
|
||||
if (typNode.type === 'JsdocTypeConstructorSignature') {
|
||||
newMeta.postNewSpacing = meta.postNewSpacing;
|
||||
}
|
||||
|
||||
if (typNode.type === 'JsdocTypeMethodSignature') {
|
||||
newMeta.postMethodNameSpacing = meta.postMethodNameSpacing ?? typNode.meta?.postMethodNameSpacing ?? '';
|
||||
}
|
||||
|
||||
return newMeta;
|
||||
};
|
||||
|
||||
switch (nde.type) {
|
||||
case 'JsdocTypeConstructorSignature': {
|
||||
const typeNode = /** @type {import('jsdoc-type-pratt-parser').ConstructorSignatureResult} */ (nde);
|
||||
/* c8 ignore next -- Guard */
|
||||
if ((typeNode.meta?.postNewSpacing ?? ' ') !== postNewSpacing) {
|
||||
typeNode.meta =
|
||||
/**
|
||||
* @type {Required<import('jsdoc-type-pratt-parser').FunctionResult['meta']> & {
|
||||
* postNewSpacing: string,
|
||||
* }}
|
||||
*/ (conditionalAdds({
|
||||
postNewSpacing,
|
||||
}));
|
||||
errorMessage = `Post-\`new\` spacing should be "${postNewSpacing}"`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case 'JsdocTypeFunction': {
|
||||
const typeNode =
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').FunctionResult}
|
||||
*/ nde;
|
||||
if ('arrow' in typeNode && typeNode.arrow) {
|
||||
/* c8 ignore next -- Guard */
|
||||
if ((typeNode.meta?.postReturnMarkerSpacing ?? ' ') !== arrowFunctionPostReturnMarkerSpacing) {
|
||||
typeNode.meta =
|
||||
/**
|
||||
* @type {Required<import('jsdoc-type-pratt-parser').FunctionResult['meta']> & {
|
||||
* postNewSpacing: string,
|
||||
* }}
|
||||
*/ (conditionalAdds({
|
||||
postReturnMarkerSpacing: arrowFunctionPostReturnMarkerSpacing,
|
||||
/* c8 ignore next -- Guard */
|
||||
preReturnMarkerSpacing: typeNode.meta?.preReturnMarkerSpacing ?? ' ',
|
||||
}));
|
||||
errorMessage = `Post-return-marker spacing should be "${arrowFunctionPostReturnMarkerSpacing}"`;
|
||||
break;
|
||||
/* c8 ignore next -- Guard */
|
||||
} else if ((typeNode.meta?.preReturnMarkerSpacing ?? ' ') !== arrowFunctionPreReturnMarkerSpacing) {
|
||||
typeNode.meta =
|
||||
/**
|
||||
* @type {Required<import('jsdoc-type-pratt-parser').FunctionResult['meta']> & {
|
||||
* postNewSpacing: string,
|
||||
* }}
|
||||
*/ (conditionalAdds({
|
||||
/* c8 ignore next -- Guard */
|
||||
postReturnMarkerSpacing: typeNode.meta?.postReturnMarkerSpacing ?? ' ',
|
||||
preReturnMarkerSpacing: arrowFunctionPreReturnMarkerSpacing,
|
||||
}));
|
||||
errorMessage = `Pre-return-marker spacing should be "${arrowFunctionPreReturnMarkerSpacing}"`;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case 'JsdocTypeCallSignature':
|
||||
case 'JsdocTypeComputedMethod':
|
||||
case 'JsdocTypeMethodSignature': {
|
||||
const typeNode =
|
||||
/**
|
||||
* @type {import('jsdoc-type-pratt-parser').FunctionResult|
|
||||
* import('jsdoc-type-pratt-parser').CallSignatureResult|
|
||||
* import('jsdoc-type-pratt-parser').ComputedMethodResult|
|
||||
* import('jsdoc-type-pratt-parser').ConstructorSignatureResult|
|
||||
* import('jsdoc-type-pratt-parser').MethodSignatureResult
|
||||
* }
|
||||
*/ (nde);
|
||||
if (typeNode.type === 'JsdocTypeMethodSignature' &&
|
||||
(typeNode.meta?.postMethodNameSpacing ?? '') !== postMethodNameSpacing
|
||||
) {
|
||||
typeNode.meta = {
|
||||
quote: typeNode.meta.quote,
|
||||
...conditionalAdds({
|
||||
postMethodNameSpacing,
|
||||
}),
|
||||
};
|
||||
errorMessage = `Post-method-name spacing should be "${postMethodNameSpacing}"`;
|
||||
break;
|
||||
} else if (typeNode.type === 'JsdocTypeMethodSignature' &&
|
||||
typeNode.meta.quote !== undefined &&
|
||||
typeNode.meta.quote !== methodQuotes
|
||||
) {
|
||||
typeNode.meta = {
|
||||
...conditionalAdds({
|
||||
postMethodNameSpacing: typeNode.meta.postMethodNameSpacing ?? '',
|
||||
}),
|
||||
quote: methodQuotes,
|
||||
};
|
||||
errorMessage = `Method quoting style should be "${methodQuotes}"`;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((typeNode.meta?.parameterSpacing ?? ' ') !== functionOrClassParameterSpacing) {
|
||||
typeNode.meta = conditionalAdds({
|
||||
parameterSpacing: functionOrClassParameterSpacing,
|
||||
});
|
||||
errorMessage = `Parameter spacing should be "${functionOrClassParameterSpacing}"`;
|
||||
} else if ((typeNode.meta?.postGenericSpacing ?? '') !== functionOrClassPostGenericSpacing) {
|
||||
typeNode.meta = conditionalAdds({
|
||||
postGenericSpacing: functionOrClassPostGenericSpacing,
|
||||
});
|
||||
errorMessage = `Post-generic spacing should be "${functionOrClassPostGenericSpacing}"`;
|
||||
} else if ((typeNode.meta?.postReturnMarkerSpacing ?? ' ') !== functionOrClassPostReturnMarkerSpacing) {
|
||||
typeNode.meta = conditionalAdds({
|
||||
postReturnMarkerSpacing: functionOrClassPostReturnMarkerSpacing,
|
||||
});
|
||||
errorMessage = `Post-return-marker spacing should be "${functionOrClassPostReturnMarkerSpacing}"`;
|
||||
} else if ((typeNode.meta?.preReturnMarkerSpacing ?? '') !== functionOrClassPreReturnMarkerSpacing) {
|
||||
typeNode.meta = conditionalAdds({
|
||||
preReturnMarkerSpacing: functionOrClassPreReturnMarkerSpacing,
|
||||
});
|
||||
errorMessage = `Pre-return-marker spacing should be "${functionOrClassPreReturnMarkerSpacing}"`;
|
||||
} else if ((typeNode.meta?.typeParameterSpacing ?? ' ') !== functionOrClassTypeParameterSpacing) {
|
||||
typeNode.meta = conditionalAdds({
|
||||
typeParameterSpacing: functionOrClassTypeParameterSpacing,
|
||||
});
|
||||
errorMessage = `Type parameter spacing should be "${functionOrClassTypeParameterSpacing}"`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'JsdocTypeGeneric': {
|
||||
const typeNode = /** @type {import('jsdoc-type-pratt-parser').GenericResult} */ (nde);
|
||||
if ('value' in typeNode.left && typeNode.left.value === 'Array') {
|
||||
if (typeNode.meta.brackets !== arrayBrackets) {
|
||||
typeNode.meta.brackets = arrayBrackets;
|
||||
errorMessage = `Array bracket style should be ${arrayBrackets}`;
|
||||
}
|
||||
} else if (typeNode.meta.dot !== genericDot) {
|
||||
typeNode.meta.dot = genericDot;
|
||||
errorMessage = `Dot usage should be ${genericDot}`;
|
||||
} else if ((typeNode.meta.elementSpacing ?? ' ') !== genericAndTupleElementSpacing) {
|
||||
typeNode.meta.elementSpacing = genericAndTupleElementSpacing;
|
||||
errorMessage = `Element spacing should be "${genericAndTupleElementSpacing}"`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'JsdocTypeKeyValue': {
|
||||
const typeNode = /** @type {import('jsdoc-type-pratt-parser').KeyValueResult} */ (nde);
|
||||
/* c8 ignore next -- Guard */
|
||||
if ((typeNode.meta?.postKeySpacing ?? '') !== keyValuePostKeySpacing) {
|
||||
typeNode.meta = {
|
||||
/* c8 ignore next -- Guard */
|
||||
postColonSpacing: typeNode.meta?.postColonSpacing ?? ' ',
|
||||
postKeySpacing: keyValuePostKeySpacing,
|
||||
/* c8 ignore next 2 -- Guard */
|
||||
postOptionalSpacing: typeNode.meta?.postOptionalSpacing ?? '',
|
||||
postVariadicSpacing: typeNode.meta?.postVariadicSpacing ?? '',
|
||||
};
|
||||
errorMessage = `Post key spacing should be "${keyValuePostKeySpacing}"`;
|
||||
/* c8 ignore next -- Guard */
|
||||
} else if ((typeNode.meta?.postColonSpacing ?? ' ') !== keyValuePostColonSpacing) {
|
||||
typeNode.meta = {
|
||||
postColonSpacing: keyValuePostColonSpacing,
|
||||
/* c8 ignore next 3 -- Guard */
|
||||
postKeySpacing: typeNode.meta?.postKeySpacing ?? '',
|
||||
postOptionalSpacing: typeNode.meta?.postOptionalSpacing ?? '',
|
||||
postVariadicSpacing: typeNode.meta?.postVariadicSpacing ?? '',
|
||||
};
|
||||
errorMessage = `Post colon spacing should be "${keyValuePostColonSpacing}"`;
|
||||
/* c8 ignore next -- Guard */
|
||||
} else if ((typeNode.meta?.postOptionalSpacing ?? '') !== keyValuePostOptionalSpacing) {
|
||||
typeNode.meta = {
|
||||
/* c8 ignore next 2 -- Guard */
|
||||
postColonSpacing: typeNode.meta?.postColonSpacing ?? ' ',
|
||||
postKeySpacing: typeNode.meta?.postKeySpacing ?? '',
|
||||
postOptionalSpacing: keyValuePostOptionalSpacing,
|
||||
/* c8 ignore next -- Guard */
|
||||
postVariadicSpacing: typeNode.meta?.postVariadicSpacing ?? '',
|
||||
};
|
||||
errorMessage = `Post optional (\`?\`) spacing should be "${keyValuePostOptionalSpacing}"`;
|
||||
/* c8 ignore next -- Guard */
|
||||
} else if (typeNode.variadic && (typeNode.meta?.postVariadicSpacing ?? '') !== keyValuePostVariadicSpacing) {
|
||||
typeNode.meta = {
|
||||
/* c8 ignore next 3 -- Guard */
|
||||
postColonSpacing: typeNode.meta?.postColonSpacing ?? ' ',
|
||||
postKeySpacing: typeNode.meta?.postKeySpacing ?? '',
|
||||
postOptionalSpacing: typeNode.meta?.postOptionalSpacing ?? '',
|
||||
postVariadicSpacing: keyValuePostVariadicSpacing,
|
||||
};
|
||||
errorMessage = `Post variadic (\`...\`) spacing should be "${keyValuePostVariadicSpacing}"`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'JsdocTypeObject': {
|
||||
const typeNode = /** @type {import('jsdoc-type-pratt-parser').ObjectResult} */ (nde);
|
||||
/* c8 ignore next -- Guard */
|
||||
const separator = typeNode.meta.separator ?? 'comma';
|
||||
if (
|
||||
(separator !== objectFieldSeparator &&
|
||||
(!objectFieldSeparatorOptionalLinebreak ||
|
||||
!(objectFieldSeparator.endsWith('-linebreak') &&
|
||||
objectFieldSeparator.startsWith(separator)))) ||
|
||||
(typeNode.meta.separatorForSingleObjectField ?? false) !== separatorForSingleObjectField ||
|
||||
((typeNode.meta.propertyIndent ?? '') !== objectFieldIndent &&
|
||||
separator.endsWith('-linebreak')) ||
|
||||
(typeNode.meta.trailingPunctuation ?? false) !== objectFieldSeparatorTrailingPunctuation
|
||||
) {
|
||||
typeNode.meta.separator = objectFieldSeparatorOptionalLinebreak && !separator.endsWith('and-linebreak') ?
|
||||
objectFieldSeparator.replace(/-and-linebreak$/v, '') :
|
||||
objectFieldSeparator;
|
||||
typeNode.meta.separatorForSingleObjectField = separatorForSingleObjectField;
|
||||
typeNode.meta.propertyIndent = objectFieldIndent;
|
||||
typeNode.meta.trailingPunctuation = objectFieldSeparatorTrailingPunctuation;
|
||||
errorMessage = `Inconsistent ${objectFieldSeparator} separator usage`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'JsdocTypeObjectField': {
|
||||
const typeNode = /** @type {import('jsdoc-type-pratt-parser').ObjectFieldResult} */ (nde);
|
||||
if ((objectFieldQuote ||
|
||||
(typeof typeNode.key === 'string' &&
|
||||
(
|
||||
(/^[\p{ID_Start}$_][\p{ID_Continue}$\u200C\u200D]*$/v).test(typeNode.key) ||
|
||||
digitRegex.test(typeNode.key)
|
||||
)
|
||||
)) &&
|
||||
typeNode.meta.quote !== (objectFieldQuote ?? undefined) &&
|
||||
(typeof typeNode.key !== 'string' ||
|
||||
!digitRegex.test(typeNode.key))
|
||||
) {
|
||||
typeNode.meta.quote = objectFieldQuote ?? undefined;
|
||||
errorMessage = `Inconsistent object field quotes ${objectFieldQuote}`;
|
||||
} else if ((typeNode.meta?.postKeySpacing ?? '') !== keyValuePostKeySpacing) {
|
||||
typeNode.meta.postKeySpacing = keyValuePostKeySpacing;
|
||||
errorMessage = `Post key spacing should be "${keyValuePostKeySpacing}"`;
|
||||
} else if ((typeNode.meta?.postColonSpacing ?? ' ') !== keyValuePostColonSpacing) {
|
||||
typeNode.meta.postColonSpacing = keyValuePostColonSpacing;
|
||||
errorMessage = `Post colon spacing should be "${keyValuePostColonSpacing}"`;
|
||||
} else if ((typeNode.meta?.postOptionalSpacing ?? '') !== keyValuePostOptionalSpacing) {
|
||||
typeNode.meta.postOptionalSpacing = keyValuePostOptionalSpacing;
|
||||
errorMessage = `Post optional (\`?\`) spacing should be "${keyValuePostOptionalSpacing}"`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'JsdocTypeStringValue': {
|
||||
const typeNode = /** @type {import('jsdoc-type-pratt-parser').StringValueResult} */ (nde);
|
||||
if (typeNode.meta.quote !== stringQuotes) {
|
||||
typeNode.meta.quote = stringQuotes;
|
||||
errorMessage = `Inconsistent ${stringQuotes} string quotes usage`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Only suitable for namepaths (and would need changes); see https://github.com/gajus/eslint-plugin-jsdoc/issues/1524
|
||||
// case 'JsdocTypeProperty': {
|
||||
// const typeNode = /** @type {import('jsdoc-type-pratt-parser').PropertyResult} */ (nde);
|
||||
|
||||
// if ((propertyQuotes ||
|
||||
// (typeof typeNode.value === 'string' && !(/\s/v).test(typeNode.value))) &&
|
||||
// typeNode.meta.quote !== (propertyQuotes ?? undefined)
|
||||
// ) {
|
||||
// typeNode.meta.quote = propertyQuotes ?? undefined;
|
||||
// errorMessage = `Inconsistent ${propertyQuotes} property quotes usage`;
|
||||
// }
|
||||
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'JsdocTypeTuple': {
|
||||
const typeNode = /** @type {import('jsdoc-type-pratt-parser').TupleResult} */ (nde);
|
||||
/* c8 ignore next -- Guard */
|
||||
if ((typeNode.meta?.elementSpacing ?? ' ') !== genericAndTupleElementSpacing) {
|
||||
typeNode.meta = {
|
||||
elementSpacing: genericAndTupleElementSpacing,
|
||||
};
|
||||
errorMessage = `Element spacing should be "${genericAndTupleElementSpacing}"`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'JsdocTypeTypeParameter': {
|
||||
const typeNode = /** @type {import('jsdoc-type-pratt-parser').TypeParameterResult} */ (nde);
|
||||
/* c8 ignore next -- Guard */
|
||||
if (typeNode.defaultValue && (typeNode.meta?.defaultValueSpacing ?? ' ') !== parameterDefaultValueSpacing) {
|
||||
typeNode.meta = {
|
||||
defaultValueSpacing: parameterDefaultValueSpacing,
|
||||
};
|
||||
errorMessage = `Default value spacing should be "${parameterDefaultValueSpacing}"`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'JsdocTypeUnion': {
|
||||
const typeNode = /** @type {import('jsdoc-type-pratt-parser').UnionResult} */ (nde);
|
||||
/* c8 ignore next -- Guard */
|
||||
if ((typeNode.meta?.spacing ?? ' ') !== unionSpacing) {
|
||||
typeNode.meta = {
|
||||
spacing: unionSpacing,
|
||||
};
|
||||
errorMessage = `Inconsistent "${unionSpacing}" union spacing usage`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (errorMessage) {
|
||||
errorMessages.push(errorMessage);
|
||||
}
|
||||
});
|
||||
|
||||
const differentResult = tag.type !==
|
||||
typeBracketSpacing + stringify(parsedType) + typeBracketSpacing;
|
||||
|
||||
if (errorMessages.length && differentResult) {
|
||||
for (const errorMessage of errorMessages) {
|
||||
utils.reportJSDoc(
|
||||
errorMessage, tag, enableFixer ? fix : null,
|
||||
);
|
||||
}
|
||||
// Stringification may have been equal previously (and thus no error reported)
|
||||
// because the stringification doesn't preserve everything
|
||||
} else if (differentResult) {
|
||||
utils.reportJSDoc(
|
||||
'There was an error with type formatting', tag, enableFixer ? fix : null,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const tags = utils.getPresentTags([
|
||||
'param',
|
||||
'property',
|
||||
'returns',
|
||||
'this',
|
||||
'throws',
|
||||
'type',
|
||||
'typedef',
|
||||
'yields',
|
||||
]);
|
||||
for (const tag of tags) {
|
||||
if (tag.type) {
|
||||
checkTypeFormats(tag);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Formats JSDoc type values.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/type-formatting.md#repos-sticky-header',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
arrayBrackets: {
|
||||
description: 'Determines how array generics are represented. Set to `angle` for the style `Array<type>` or `square` for the style `type[]`. Defaults to "square".',
|
||||
enum: [
|
||||
'angle',
|
||||
'square',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
arrowFunctionPostReturnMarkerSpacing: {
|
||||
description: 'The space character (if any) to use after return markers (`=>`). Defaults to " ".',
|
||||
type: 'string',
|
||||
},
|
||||
arrowFunctionPreReturnMarkerSpacing: {
|
||||
description: 'The space character (if any) to use before return markers (`=>`). Defaults to " ".',
|
||||
type: 'string',
|
||||
},
|
||||
enableFixer: {
|
||||
description: 'Whether to enable the fixer. Defaults to `true`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
functionOrClassParameterSpacing: {
|
||||
description: 'The space character (if any) to use between function or class parameters. Defaults to " ".',
|
||||
type: 'string',
|
||||
},
|
||||
functionOrClassPostGenericSpacing: {
|
||||
description: 'The space character (if any) to use after a generic expression in a function or class. Defaults to "".',
|
||||
type: 'string',
|
||||
},
|
||||
functionOrClassPostReturnMarkerSpacing: {
|
||||
description: 'The space character (if any) to use after return markers (`:`). Defaults to "".',
|
||||
type: 'string',
|
||||
},
|
||||
functionOrClassPreReturnMarkerSpacing: {
|
||||
description: 'The space character (if any) to use before return markers (`:`). Defaults to "".',
|
||||
type: 'string',
|
||||
},
|
||||
functionOrClassTypeParameterSpacing: {
|
||||
description: 'The space character (if any) to use between type parameters in a function or class. Defaults to " ".',
|
||||
type: 'string',
|
||||
},
|
||||
genericAndTupleElementSpacing: {
|
||||
description: 'The space character (if any) to use between elements in generics and tuples. Defaults to " ".',
|
||||
type: 'string',
|
||||
},
|
||||
genericDot: {
|
||||
description: 'Boolean value of whether to use a dot before the angled brackets of a generic (e.g., `SomeType.<AnotherType>`). Defaults to `false`.',
|
||||
type: 'boolean',
|
||||
},
|
||||
keyValuePostColonSpacing: {
|
||||
description: 'The amount of spacing (if any) after the colon of a key-value or object-field pair. Defaults to " ".',
|
||||
type: 'string',
|
||||
},
|
||||
keyValuePostKeySpacing: {
|
||||
description: 'The amount of spacing (if any) immediately after keys in a key-value or object-field pair. Defaults to "".',
|
||||
type: 'string',
|
||||
},
|
||||
keyValuePostOptionalSpacing: {
|
||||
description: 'The amount of spacing (if any) after the optional operator (`?`) in a key-value or object-field pair. Defaults to "".',
|
||||
type: 'string',
|
||||
},
|
||||
keyValuePostVariadicSpacing: {
|
||||
description: 'The amount of spacing (if any) after a variadic operator (`...`) in a key-value pair. Defaults to "".',
|
||||
type: 'string',
|
||||
},
|
||||
methodQuotes: {
|
||||
description: 'The style of quotation mark for surrounding method names when quoted. Defaults to `double`',
|
||||
enum: [
|
||||
'double',
|
||||
'single',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
objectFieldIndent: {
|
||||
description: `A string indicating the whitespace to be added on each line preceding an
|
||||
object property-value field. Defaults to the empty string.`,
|
||||
type: 'string',
|
||||
},
|
||||
objectFieldQuote: {
|
||||
description: `Whether and how object field properties should be quoted (e.g., \`{"a": string}\`).
|
||||
Set to \`single\`, \`double\`, or \`null\`. Defaults to \`null\` (no quotes unless
|
||||
required due to special characters within the field). Digits will be kept as is,
|
||||
regardless of setting (they can either represent a digit or a string digit).`,
|
||||
enum: [
|
||||
'double',
|
||||
'single',
|
||||
null,
|
||||
],
|
||||
},
|
||||
objectFieldSeparator: {
|
||||
description: `For object properties, specify whether a "semicolon", "comma", "linebreak",
|
||||
"semicolon-and-linebreak", or "comma-and-linebreak" should be used after
|
||||
each object property-value pair.
|
||||
|
||||
Defaults to \`"comma"\`.`,
|
||||
enum: [
|
||||
'comma',
|
||||
'comma-and-linebreak',
|
||||
'linebreak',
|
||||
'semicolon',
|
||||
'semicolon-and-linebreak',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
objectFieldSeparatorOptionalLinebreak: {
|
||||
description: `Whether \`objectFieldSeparator\` set to \`"semicolon-and-linebreak"\` or
|
||||
\`"comma-and-linebreak"\` should be allowed to optionally drop the linebreak.
|
||||
|
||||
Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
objectFieldSeparatorTrailingPunctuation: {
|
||||
description: `If \`separatorForSingleObjectField\` is not in effect (i.e., if it is \`false\`
|
||||
or there are multiple property-value object fields present), this property
|
||||
will determine whether to add punctuation corresponding to the
|
||||
\`objectFieldSeparator\` (e.g., a semicolon) to the final object field.
|
||||
Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
parameterDefaultValueSpacing: {
|
||||
description: 'The space character (if any) to use between the equal signs of a default value. Defaults to " ".',
|
||||
type: 'string',
|
||||
},
|
||||
postMethodNameSpacing: {
|
||||
description: 'The space character (if any) to add after a method name. Defaults to "".',
|
||||
type: 'string',
|
||||
},
|
||||
postNewSpacing: {
|
||||
description: 'The space character (if any) to add after "new" in a constructor. Defaults to " ".',
|
||||
type: 'string',
|
||||
},
|
||||
// propertyQuotes: {
|
||||
// description: `Whether and how namepath properties should be quoted (e.g., \`ab."cd"."ef"\`).
|
||||
// Set to \`single\`, \`double\`, or \`null\`. Defaults to \`null\` (no quotes unless
|
||||
// required due to whitespace within the property).`,
|
||||
// enum: [
|
||||
// 'double',
|
||||
// 'single',
|
||||
// null,
|
||||
// ],
|
||||
// },
|
||||
separatorForSingleObjectField: {
|
||||
description: `Whether to apply the \`objectFieldSeparator\` (e.g., a semicolon) when there
|
||||
is only one property-value object field present. Defaults to \`false\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
stringQuotes: {
|
||||
description: `How string literals should be quoted (e.g., \`"abc"\`). Set to \`single\`
|
||||
or \`double\`. Defaults to 'double'.`,
|
||||
enum: [
|
||||
'double',
|
||||
'single',
|
||||
],
|
||||
type: 'string',
|
||||
},
|
||||
typeBracketSpacing: {
|
||||
description: `A string of spaces that will be added immediately after the type's initial
|
||||
curly bracket and immediately before its ending curly bracket. Defaults
|
||||
to the empty string.`,
|
||||
type: 'string',
|
||||
},
|
||||
unionSpacing: {
|
||||
description: 'Determines the spacing to add to unions (`|`). Defaults to a single space (`" "`).',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
449
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/validTypes.js
generated
vendored
Normal file
449
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/rules/validTypes.js
generated
vendored
Normal file
|
|
@ -0,0 +1,449 @@
|
|||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
parse,
|
||||
parseName,
|
||||
parseNamePath,
|
||||
traverse,
|
||||
tryParse,
|
||||
} from '@es-joy/jsdoccomment';
|
||||
|
||||
const inlineTags = new Set([
|
||||
'link', 'linkcode', 'linkplain',
|
||||
'tutorial',
|
||||
]);
|
||||
|
||||
const asExpression = /as\s+/v;
|
||||
|
||||
const suppressTypes = new Set([
|
||||
// https://github.com/google/closure-compiler/wiki/@suppress-annotations
|
||||
// https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/parsing/ParserConfig.properties#L154
|
||||
'accessControls',
|
||||
'checkDebuggerStatement',
|
||||
'checkEs5InheritanceCorrectnessConditions',
|
||||
'checkPrototypalTypes',
|
||||
'checkRegExp',
|
||||
'checkTypes',
|
||||
'checkVars',
|
||||
'closureClassChecks',
|
||||
'closureDepMethodUsageChecks',
|
||||
'const',
|
||||
'constantProperty',
|
||||
'dangerousUnrecognizedTypeError',
|
||||
'deprecated',
|
||||
'duplicate',
|
||||
'es5Strict',
|
||||
'externsValidation',
|
||||
'extraProvide',
|
||||
'extraRequire',
|
||||
'globalThis',
|
||||
'invalidCasts',
|
||||
'lateProvide',
|
||||
'legacyGoogScopeRequire',
|
||||
'lintChecks',
|
||||
'lintVarDeclarations',
|
||||
'messageConventions',
|
||||
'misplacedTypeAnnotation',
|
||||
'missingOverride',
|
||||
'missingPolyfill',
|
||||
'missingProperties',
|
||||
'missingProvide',
|
||||
'missingRequire',
|
||||
'missingReturn',
|
||||
'missingSourcesWarnings',
|
||||
'moduleLoad',
|
||||
'msgDescriptions',
|
||||
'nonStandardJsDocs',
|
||||
'partialAlias',
|
||||
'polymer',
|
||||
'reportUnknownTypes',
|
||||
'strictCheckTypes',
|
||||
'strictMissingProperties',
|
||||
'strictModuleDepCheck',
|
||||
'strictPrimitiveOperators',
|
||||
'suspiciousCode',
|
||||
|
||||
// Not documented in enum
|
||||
'switch',
|
||||
'transitionalSuspiciousCodeWarnings',
|
||||
'undefinedNames',
|
||||
'undefinedVars',
|
||||
'underscore',
|
||||
'unknownDefines',
|
||||
'untranspilableFeatures',
|
||||
'unusedLocalVariables',
|
||||
|
||||
// Not documented?
|
||||
'unusedPrivateMembers',
|
||||
'uselessCode',
|
||||
'useOfGoogProvide',
|
||||
'visibility',
|
||||
'with',
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {import('jsdoc-type-pratt-parser').ParseMode|"permissive"} mode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const tryParsePathIgnoreError = (path, mode) => {
|
||||
try {
|
||||
parseNamePath(path, mode === 'permissive' ? 'jsdoc' : mode, {
|
||||
includeSpecial: true,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
// Keep the original error for including the whole type
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {import('jsdoc-type-pratt-parser').ParseMode} mode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const tryParseNameIgnoreError = (name, mode) => {
|
||||
try {
|
||||
parseName(name, mode);
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
// Keep the original error for including the whole type
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
report,
|
||||
settings,
|
||||
utils,
|
||||
// eslint-disable-next-line complexity
|
||||
}) => {
|
||||
const {
|
||||
allowEmptyNamepaths = false,
|
||||
} = context.options[0] || {};
|
||||
const {
|
||||
mode,
|
||||
} = settings;
|
||||
|
||||
for (const tag of jsdoc.tags) {
|
||||
/**
|
||||
* @param {string} namepath
|
||||
* @param {string} [tagName]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const validNamepathParsing = function (namepath, tagName) {
|
||||
if (
|
||||
tryParsePathIgnoreError(namepath, mode)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let handled = false;
|
||||
|
||||
if (tagName) {
|
||||
switch (tagName) {
|
||||
case 'memberof':
|
||||
case 'memberof!': {
|
||||
const endChar = namepath.slice(-1);
|
||||
if ([
|
||||
'#', '.', '~',
|
||||
].includes(endChar)) {
|
||||
handled = tryParsePathIgnoreError(namepath.slice(0, -1), mode);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'module': case 'requires': {
|
||||
if (!namepath.startsWith('module:')) {
|
||||
handled = tryParsePathIgnoreError(`module:${namepath}`, mode);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'borrows': {
|
||||
const startChar = namepath.charAt(0);
|
||||
if ([
|
||||
'#', '.', '~',
|
||||
].includes(startChar)) {
|
||||
handled = tryParsePathIgnoreError(namepath.slice(1), mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
report(`Syntax error in namepath: ${namepath}`, null, tag);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const validTypeParsing = function (type) {
|
||||
let parsedTypes;
|
||||
try {
|
||||
if (mode === 'permissive') {
|
||||
parsedTypes = tryParse(type, undefined, {
|
||||
classContext: true,
|
||||
});
|
||||
} else {
|
||||
parsedTypes = parse(type, mode, {
|
||||
classContext: true,
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
report(`Syntax error in type: ${type}`, null, tag);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode === 'closure' || mode === 'typescript') {
|
||||
traverse(parsedTypes, (node) => {
|
||||
const {
|
||||
type: typ,
|
||||
} = node;
|
||||
|
||||
if (
|
||||
(typ === 'JsdocTypeObjectField' || typ === 'JsdocTypeKeyValue') &&
|
||||
node.right?.type === 'JsdocTypeNullable' &&
|
||||
node.right?.meta?.position === 'suffix'
|
||||
) {
|
||||
report(`Syntax error in type: ${node.right.type}`, null, tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (tag.problems.length) {
|
||||
const msg = tag.problems.reduce((str, {
|
||||
message,
|
||||
}) => {
|
||||
return str + '; ' + message;
|
||||
}, '').slice(2);
|
||||
report(`Invalid name: ${msg}`, null, tag);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag.tag === 'import') {
|
||||
// A named import will look like a type, but not be valid; we also don't
|
||||
// need to check the name/namepath
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag.tag === 'borrows') {
|
||||
const thisNamepath = /** @type {string} */ (
|
||||
utils.getTagDescription(tag)
|
||||
).replace(asExpression, '')
|
||||
.trim();
|
||||
|
||||
if (!asExpression.test(/** @type {string} */ (
|
||||
utils.getTagDescription(tag)
|
||||
)) || !thisNamepath) {
|
||||
report(`@borrows must have an "as" expression. Found "${utils.getTagDescription(tag)}"`, null, tag);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (validNamepathParsing(thisNamepath, 'borrows')) {
|
||||
const thatNamepath = tag.name;
|
||||
|
||||
validNamepathParsing(thatNamepath);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag.tag === 'suppress' && mode === 'closure') {
|
||||
let parsedTypes;
|
||||
|
||||
try {
|
||||
parsedTypes = tryParse(tag.type);
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
if (parsedTypes) {
|
||||
traverse(parsedTypes, (node) => {
|
||||
let type;
|
||||
if ('value' in node && typeof node.value === 'string') {
|
||||
type = node.value;
|
||||
}
|
||||
|
||||
if (type !== undefined && !suppressTypes.has(type)) {
|
||||
report(`Syntax error in suppress type: ${type}`, null, tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const otherModeMaps = /** @type {import('../jsdocUtils.js').ParserMode[]} */ ([
|
||||
'jsdoc', 'typescript', 'closure', 'permissive',
|
||||
]).filter(
|
||||
(mde) => {
|
||||
return mde !== mode;
|
||||
},
|
||||
).map((mde) => {
|
||||
return utils.getTagStructureForMode(mde);
|
||||
});
|
||||
|
||||
const tagMightHaveNamePosition = utils.tagMightHaveNamePosition(tag.tag, otherModeMaps);
|
||||
if (tagMightHaveNamePosition !== true && tag.name) {
|
||||
const modeInfo = tagMightHaveNamePosition === false ? '' : ` in "${mode}" mode`;
|
||||
report(`@${tag.tag} should not have a name${modeInfo}.`, null, tag);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Documentation like `@returns {@link SomeType}` is technically ambiguous. Specifically it
|
||||
// could either be intepreted as a type `"@link SomeType"` or a description `"{@link SomeType}"`.
|
||||
// However this is a good heuristic.
|
||||
if (tag.type.trim().startsWith('@')) {
|
||||
tag.description = `{${tag.type}} ${tag.description}`;
|
||||
tag.type = '';
|
||||
}
|
||||
|
||||
const mightHaveTypePosition = utils.tagMightHaveTypePosition(tag.tag, otherModeMaps);
|
||||
if (mightHaveTypePosition !== true && tag.type) {
|
||||
const modeInfo = mightHaveTypePosition === false ? '' : ` in "${mode}" mode`;
|
||||
report(`@${tag.tag} should not have a bracketed type${modeInfo}.`, null, tag);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// REQUIRED NAME
|
||||
const tagMustHaveNamePosition = utils.tagMustHaveNamePosition(tag.tag, otherModeMaps);
|
||||
|
||||
// Don't handle `@param` here though it does require name as handled by
|
||||
// `require-param-name` (`@property` would similarly seem to require one,
|
||||
// but is handled by `require-property-name`)
|
||||
if (tagMustHaveNamePosition !== false && !tag.name && !allowEmptyNamepaths && ![
|
||||
'arg', 'argument', 'param',
|
||||
'prop', 'property',
|
||||
].includes(tag.tag) &&
|
||||
(tag.tag !== 'see' || !utils.getTagDescription(tag).includes('{@link'))
|
||||
) {
|
||||
const modeInfo = tagMustHaveNamePosition === true ? '' : ` in "${mode}" mode`;
|
||||
report(`Tag @${tag.tag} must have a name/namepath${modeInfo}.`, null, tag);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// REQUIRED TYPE
|
||||
const mustHaveTypePosition = utils.tagMustHaveTypePosition(tag.tag, otherModeMaps);
|
||||
if (mustHaveTypePosition !== false && !tag.type &&
|
||||
// Auto-added to settings and has own rule already, so don't duplicate
|
||||
tag.tag !== 'next'
|
||||
) {
|
||||
const modeInfo = mustHaveTypePosition === true ? '' : ` in "${mode}" mode`;
|
||||
report(`Tag @${tag.tag} must have a type${modeInfo}.`, null, tag);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// REQUIRED TYPE OR NAME/NAMEPATH
|
||||
const tagMissingRequiredTypeOrNamepath = utils.tagMissingRequiredTypeOrNamepath(tag, otherModeMaps);
|
||||
if (tagMissingRequiredTypeOrNamepath !== false && !allowEmptyNamepaths) {
|
||||
const modeInfo = tagMissingRequiredTypeOrNamepath === true ? '' : ` in "${mode}" mode`;
|
||||
report(`Tag @${tag.tag} must have either a type or namepath${modeInfo}.`, null, tag);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// VALID TYPE
|
||||
const hasTypePosition = mightHaveTypePosition === true && Boolean(tag.type);
|
||||
if (hasTypePosition && (tag.type !== 'const' || tag.tag !== 'type')) {
|
||||
validTypeParsing(tag.type);
|
||||
}
|
||||
|
||||
// VALID NAME/NAMEPATH
|
||||
const hasNamepathPosition = (
|
||||
tagMustHaveNamePosition !== false ||
|
||||
utils.tagMightHaveNamepath(tag.tag)
|
||||
) && Boolean(tag.name);
|
||||
|
||||
if (hasNamepathPosition) {
|
||||
if (mode !== 'jsdoc' && tag.tag === 'template') {
|
||||
if (!tryParsePathIgnoreError(
|
||||
// May be an issue with the commas of
|
||||
// `utils.parseClosureTemplateTag`, so first try a raw
|
||||
// value; we really need a proper parser instead, however.
|
||||
tag.name.trim().replace(/^\[?(?<name>.*?)=.*$/v, '$<name>'),
|
||||
mode,
|
||||
)) {
|
||||
for (const namepath of utils.parseClosureTemplateTag(tag)) {
|
||||
validNamepathParsing(namepath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validNamepathParsing(tag.name, tag.tag);
|
||||
}
|
||||
}
|
||||
|
||||
const hasNamePosition = utils.tagMightHaveName(tag.tag) &&
|
||||
Boolean(tag.name);
|
||||
if (
|
||||
hasNamePosition &&
|
||||
mode === 'typescript' &&
|
||||
!tryParseNameIgnoreError(tag.name, mode)
|
||||
) {
|
||||
report(`Syntax error in name: ${tag.name}`, null, tag);
|
||||
} else if (hasNamePosition && mode !== 'typescript') {
|
||||
validNamepathParsing(tag.name, tag.tag);
|
||||
}
|
||||
|
||||
for (const inlineTag of tag.inlineTags) {
|
||||
if (inlineTags.has(inlineTag.tag) && !inlineTag.text && !inlineTag.namepathOrURL) {
|
||||
report(`Inline tag "${inlineTag.tag}" missing content`, null, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const inlineTag of jsdoc.inlineTags) {
|
||||
if (inlineTags.has(inlineTag.tag) && !inlineTag.text && !inlineTag.namepathOrURL) {
|
||||
report(`Inline tag "${inlineTag.tag}" missing content`);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Requires all types/namepaths to be valid JSDoc, Closure compiler, or TypeScript types (configurable in settings).',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/valid-types.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowEmptyNamepaths: {
|
||||
default: false,
|
||||
description: `Set to \`false\` to bulk disallow
|
||||
empty name paths with namepath groups 2 and 4 (these might often be
|
||||
expected to have an accompanying name path, though they have some
|
||||
indicative value without one; these may also allow names to be defined
|
||||
in another manner elsewhere in the block); you can use
|
||||
\`settings.jsdoc.structuredTags\` with the \`required\` key set to "name" if you
|
||||
wish to require name paths on a tag-by-tag basis. Defaults to \`true\`.`,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
241
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/tagNames.js
generated
vendored
Normal file
241
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/tagNames.js
generated
vendored
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/**
|
||||
* @typedef {{
|
||||
* [key: string]: string[]
|
||||
* }} AliasedTags
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {AliasedTags}
|
||||
*/
|
||||
const jsdocTagsUndocumented = {
|
||||
// Undocumented but present; see
|
||||
// https://github.com/jsdoc/jsdoc/issues/1283#issuecomment-516816802
|
||||
// https://github.com/jsdoc/jsdoc/blob/master/packages/jsdoc/lib/jsdoc/tag/dictionary/definitions.js#L594
|
||||
modifies: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {AliasedTags}
|
||||
*/
|
||||
const jsdocTags = {
|
||||
...jsdocTagsUndocumented,
|
||||
abstract: [
|
||||
'virtual',
|
||||
],
|
||||
access: [],
|
||||
alias: [],
|
||||
async: [],
|
||||
augments: [
|
||||
'extends',
|
||||
],
|
||||
author: [],
|
||||
borrows: [],
|
||||
callback: [],
|
||||
class: [
|
||||
'constructor',
|
||||
],
|
||||
classdesc: [],
|
||||
constant: [
|
||||
'const',
|
||||
],
|
||||
constructs: [],
|
||||
copyright: [],
|
||||
default: [
|
||||
'defaultvalue',
|
||||
],
|
||||
deprecated: [],
|
||||
description: [
|
||||
'desc',
|
||||
],
|
||||
enum: [],
|
||||
event: [],
|
||||
example: [],
|
||||
exports: [],
|
||||
external: [
|
||||
'host',
|
||||
],
|
||||
file: [
|
||||
'fileoverview',
|
||||
'overview',
|
||||
],
|
||||
fires: [
|
||||
'emits',
|
||||
],
|
||||
function: [
|
||||
'func',
|
||||
'method',
|
||||
],
|
||||
generator: [],
|
||||
global: [],
|
||||
hideconstructor: [],
|
||||
ignore: [],
|
||||
implements: [],
|
||||
inheritdoc: [],
|
||||
|
||||
// Allowing casing distinct from jsdoc `definitions.js` (required in Closure)
|
||||
inheritDoc: [],
|
||||
|
||||
inner: [],
|
||||
instance: [],
|
||||
interface: [],
|
||||
kind: [],
|
||||
lends: [],
|
||||
license: [],
|
||||
listens: [],
|
||||
member: [
|
||||
'var',
|
||||
],
|
||||
memberof: [],
|
||||
'memberof!': [],
|
||||
mixes: [],
|
||||
mixin: [],
|
||||
|
||||
module: [],
|
||||
name: [],
|
||||
namespace: [],
|
||||
override: [],
|
||||
package: [],
|
||||
param: [
|
||||
'arg',
|
||||
'argument',
|
||||
],
|
||||
private: [],
|
||||
property: [
|
||||
'prop',
|
||||
],
|
||||
protected: [],
|
||||
public: [],
|
||||
readonly: [],
|
||||
requires: [],
|
||||
returns: [
|
||||
'return',
|
||||
],
|
||||
see: [],
|
||||
since: [],
|
||||
static: [],
|
||||
summary: [],
|
||||
|
||||
this: [],
|
||||
throws: [
|
||||
'exception',
|
||||
],
|
||||
todo: [],
|
||||
tutorial: [],
|
||||
type: [],
|
||||
typedef: [],
|
||||
variation: [],
|
||||
version: [],
|
||||
yields: [
|
||||
'yield',
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {AliasedTags}
|
||||
*/
|
||||
const typeScriptTags = {
|
||||
...jsdocTags,
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/22160
|
||||
// https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#the-jsdoc-import-tag
|
||||
import: [],
|
||||
|
||||
// https://www.typescriptlang.org/tsconfig/#stripInternal
|
||||
internal: [],
|
||||
|
||||
// https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#overload-support-in-jsdoc
|
||||
overload: [],
|
||||
|
||||
// https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#satisfies-support-in-jsdoc
|
||||
satisfies: [],
|
||||
|
||||
// `@template` is also in TypeScript per:
|
||||
// https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#template
|
||||
template: [
|
||||
// Alias as per https://typedoc.org/documents/Tags._typeParam.html
|
||||
'typeParam',
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {AliasedTags}
|
||||
*/
|
||||
const undocumentedClosureTags = {
|
||||
// These are in Closure source but not in jsdoc source nor in the Closure
|
||||
// docs: https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/parsing/Annotation.java
|
||||
closurePrimitive: [],
|
||||
customElement: [],
|
||||
expose: [],
|
||||
hidden: [],
|
||||
idGenerator: [],
|
||||
meaning: [],
|
||||
mixinClass: [],
|
||||
mixinFunction: [],
|
||||
ngInject: [],
|
||||
owner: [],
|
||||
typeSummary: [],
|
||||
wizaction: [],
|
||||
};
|
||||
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
inheritdoc,
|
||||
internal,
|
||||
overload,
|
||||
// Will be inverted to prefer `return`
|
||||
returns,
|
||||
|
||||
satisfies,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...typeScriptTagsInClosure
|
||||
} = typeScriptTags;
|
||||
|
||||
/**
|
||||
* @type {AliasedTags}
|
||||
*/
|
||||
const closureTags = {
|
||||
...typeScriptTagsInClosure,
|
||||
...undocumentedClosureTags,
|
||||
|
||||
// From https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler
|
||||
// These are all recognized in https://github.com/jsdoc/jsdoc/blob/master/packages/jsdoc/lib/jsdoc/tag/dictionary/definitions.js
|
||||
// except for the experimental `noinline` and the casing differences noted below
|
||||
|
||||
// Defined as a synonym of `const` in jsdoc `definitions.js`
|
||||
define: [],
|
||||
|
||||
dict: [],
|
||||
export: [],
|
||||
externs: [],
|
||||
final: [],
|
||||
|
||||
// With casing distinct from jsdoc `definitions.js`
|
||||
implicitCast: [],
|
||||
|
||||
noalias: [],
|
||||
nocollapse: [],
|
||||
nocompile: [],
|
||||
noinline: [],
|
||||
nosideeffects: [],
|
||||
polymer: [],
|
||||
polymerBehavior: [],
|
||||
preserve: [],
|
||||
|
||||
// Defined as a synonym of `interface` in jsdoc `definitions.js`
|
||||
record: [],
|
||||
|
||||
return: [
|
||||
'returns',
|
||||
],
|
||||
|
||||
struct: [],
|
||||
suppress: [],
|
||||
|
||||
unrestricted: [],
|
||||
};
|
||||
|
||||
export {
|
||||
closureTags,
|
||||
jsdocTags,
|
||||
typeScriptTags,
|
||||
};
|
||||
572
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/utils/hasReturnValue.js
generated
vendored
Normal file
572
Frontend-Learner/node_modules/eslint-plugin-jsdoc/src/utils/hasReturnValue.js
generated
vendored
Normal file
|
|
@ -0,0 +1,572 @@
|
|||
/**
|
||||
* @typedef {import('estree').Node|
|
||||
* import('@typescript-eslint/types').TSESTree.Node} ESTreeOrTypeScriptNode
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if a node is a promise but has no resolve value or an empty value.
|
||||
* An `undefined` resolve does not count.
|
||||
* @param {ESTreeOrTypeScriptNode|undefined|null} node
|
||||
* @returns {boolean|undefined|null}
|
||||
*/
|
||||
const isNewPromiseExpression = (node) => {
|
||||
return node && node.type === 'NewExpression' && node.callee.type === 'Identifier' &&
|
||||
node.callee.name === 'Promise';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ESTreeOrTypeScriptNode|null|undefined} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isVoidPromise = (node) => {
|
||||
return /** @type {import('@typescript-eslint/types').TSESTree.TSTypeReference} */ (node)?.typeArguments?.params?.[0]?.type === 'TSVoidKeyword'
|
||||
/* c8 ignore next 5 */
|
||||
// eslint-disable-next-line @stylistic/operator-linebreak -- c8
|
||||
|| /** @type {import('@typescript-eslint/types').TSESTree.TSTypeReference} */ (
|
||||
node
|
||||
// @ts-expect-error Ok
|
||||
)?.typeParameters?.params?.[0]?.type === 'TSVoidKeyword';
|
||||
};
|
||||
|
||||
const undefinedKeywords = new Set([
|
||||
'TSNeverKeyword', 'TSUndefinedKeyword', 'TSVoidKeyword',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Checks if a node has a return statement. Void return does not count.
|
||||
* @param {ESTreeOrTypeScriptNode|undefined|null} node
|
||||
* @param {boolean} [throwOnNullReturn]
|
||||
* @param {PromiseFilter} [promFilter]
|
||||
* @returns {boolean|undefined}
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
const hasReturnValue = (node, throwOnNullReturn, promFilter) => {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case 'ArrowFunctionExpression':
|
||||
case 'FunctionDeclaration':
|
||||
case 'FunctionExpression': {
|
||||
return 'expression' in node && node.expression && (!isNewPromiseExpression(
|
||||
node.body,
|
||||
) || !isVoidPromise(node.body)) ||
|
||||
hasReturnValue(node.body, throwOnNullReturn, promFilter);
|
||||
}
|
||||
|
||||
case 'BlockStatement': {
|
||||
return node.body.some((bodyNode) => {
|
||||
return bodyNode.type !== 'FunctionDeclaration' && hasReturnValue(bodyNode, throwOnNullReturn, promFilter);
|
||||
});
|
||||
}
|
||||
|
||||
case 'DoWhileStatement':
|
||||
case 'ForInStatement':
|
||||
case 'ForOfStatement':
|
||||
|
||||
case 'ForStatement':
|
||||
|
||||
case 'LabeledStatement':
|
||||
case 'WhileStatement':
|
||||
case 'WithStatement': {
|
||||
return hasReturnValue(node.body, throwOnNullReturn, promFilter);
|
||||
}
|
||||
|
||||
case 'IfStatement': {
|
||||
return hasReturnValue(node.consequent, throwOnNullReturn, promFilter) ||
|
||||
hasReturnValue(node.alternate, throwOnNullReturn, promFilter);
|
||||
}
|
||||
|
||||
case 'MethodDefinition':
|
||||
return hasReturnValue(node.value, throwOnNullReturn, promFilter);
|
||||
case 'ReturnStatement': {
|
||||
// void return does not count.
|
||||
if (node.argument === null) {
|
||||
if (throwOnNullReturn) {
|
||||
throw new Error('Null return');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (promFilter && isNewPromiseExpression(node.argument)) {
|
||||
// Let caller decide how to filter, but this is, at the least,
|
||||
// a return of sorts and truthy
|
||||
return promFilter(node.argument);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case 'SwitchStatement': {
|
||||
return node.cases.some(
|
||||
(someCase) => {
|
||||
return someCase.consequent.some((nde) => {
|
||||
return hasReturnValue(nde, throwOnNullReturn, promFilter);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
case 'TryStatement': {
|
||||
return hasReturnValue(node.block, throwOnNullReturn, promFilter) ||
|
||||
hasReturnValue(node.handler && node.handler.body, throwOnNullReturn, promFilter) ||
|
||||
hasReturnValue(node.finalizer, throwOnNullReturn, promFilter);
|
||||
}
|
||||
|
||||
case 'TSDeclareFunction':
|
||||
|
||||
case 'TSFunctionType':
|
||||
|
||||
case 'TSMethodSignature': {
|
||||
const type = node?.returnType?.typeAnnotation?.type;
|
||||
return type && !undefinedKeywords.has(type);
|
||||
}
|
||||
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a node has a return statement. Void return does not count.
|
||||
* @param {ESTreeOrTypeScriptNode|null|undefined} node
|
||||
* @param {PromiseFilter} promFilter
|
||||
* @returns {undefined|boolean|ESTreeOrTypeScriptNode}
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
const allBrancheshaveReturnValues = (node, promFilter) => {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
// case 'MethodDefinition':
|
||||
// return allBrancheshaveReturnValues(node.value, promFilter);
|
||||
case 'ArrowFunctionExpression':
|
||||
case 'FunctionDeclaration':
|
||||
case 'FunctionExpression': {
|
||||
return 'expression' in node && node.expression && (!isNewPromiseExpression(node.body) || !isVoidPromise(node.body)) ||
|
||||
allBrancheshaveReturnValues(node.body, promFilter) ||
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.BlockStatement} */
|
||||
(node.body).body.some((nde) => {
|
||||
return nde.type === 'ReturnStatement';
|
||||
});
|
||||
}
|
||||
|
||||
case 'BlockStatement': {
|
||||
const lastBodyNode = node.body.slice(-1)[0];
|
||||
return allBrancheshaveReturnValues(lastBodyNode, promFilter);
|
||||
}
|
||||
|
||||
case 'DoWhileStatement':
|
||||
case 'WhileStatement':
|
||||
if (
|
||||
/**
|
||||
* @type {import('@typescript-eslint/types').TSESTree.Literal}
|
||||
*/
|
||||
(node.test).value === true
|
||||
) {
|
||||
// If this is an infinite loop, we assume only one branch
|
||||
// is needed to provide a return
|
||||
return hasReturnValue(node.body, false, promFilter);
|
||||
}
|
||||
|
||||
// Fallthrough
|
||||
case 'ForStatement':
|
||||
if (node.test === null) {
|
||||
// If this is an infinite loop, we assume only one branch
|
||||
// is needed to provide a return
|
||||
return hasReturnValue(node.body, false, promFilter);
|
||||
}
|
||||
|
||||
case 'ForInStatement':
|
||||
case 'ForOfStatement':
|
||||
|
||||
case 'LabeledStatement':
|
||||
|
||||
case 'WithStatement': {
|
||||
return allBrancheshaveReturnValues(node.body, promFilter);
|
||||
}
|
||||
|
||||
case 'IfStatement': {
|
||||
return allBrancheshaveReturnValues(node.consequent, promFilter) &&
|
||||
allBrancheshaveReturnValues(node.alternate, promFilter);
|
||||
}
|
||||
|
||||
case 'ReturnStatement': {
|
||||
// void return does not count.
|
||||
if (node.argument === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (promFilter && isNewPromiseExpression(node.argument)) {
|
||||
// Let caller decide how to filter, but this is, at the least,
|
||||
// a return of sorts and truthy
|
||||
return promFilter(node.argument);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case 'SwitchStatement': {
|
||||
return /** @type {import('@typescript-eslint/types').TSESTree.SwitchStatement} */ (node).cases.every(
|
||||
(someCase) => {
|
||||
return !someCase.consequent.some((consNode) => {
|
||||
return consNode.type === 'BreakStatement' ||
|
||||
consNode.type === 'ReturnStatement' && consNode.argument === null;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
case 'ThrowStatement': {
|
||||
return true;
|
||||
}
|
||||
|
||||
case 'TryStatement': {
|
||||
// If `finally` returns, all return
|
||||
return node.finalizer && allBrancheshaveReturnValues(node.finalizer, promFilter) ||
|
||||
// Return in `try`/`catch` may still occur despite `finally`
|
||||
allBrancheshaveReturnValues(node.block, promFilter) &&
|
||||
(!node.handler ||
|
||||
allBrancheshaveReturnValues(node.handler && node.handler.body, promFilter)) &&
|
||||
(!node.finalizer || (() => {
|
||||
try {
|
||||
hasReturnValue(node.finalizer, true, promFilter);
|
||||
} catch (error) {
|
||||
if (/** @type {Error} */ (error).message === 'Null return') {
|
||||
return false;
|
||||
}
|
||||
/* c8 ignore next 3 */
|
||||
// eslint-disable-next-line @stylistic/padding-line-between-statements -- c8
|
||||
throw error;
|
||||
}
|
||||
|
||||
// As long as not an explicit empty return, then return true
|
||||
return true;
|
||||
})());
|
||||
}
|
||||
|
||||
case 'TSDeclareFunction':
|
||||
|
||||
case 'TSFunctionType':
|
||||
|
||||
case 'TSMethodSignature': {
|
||||
const type = node?.returnType?.typeAnnotation?.type;
|
||||
return type && !undefinedKeywords.has(type);
|
||||
}
|
||||
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback PromiseFilter
|
||||
* @param {ESTreeOrTypeScriptNode|undefined} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Avoids further checking child nodes if a nested function shadows the
|
||||
* resolver, but otherwise, if name is used (by call or passed in as an
|
||||
* argument to another function), will be considered as non-empty.
|
||||
*
|
||||
* This could check for redeclaration of the resolver, but as such is
|
||||
* unlikely, we avoid the performance cost of checking everywhere for
|
||||
* (re)declarations or assignments.
|
||||
* @param {import('@typescript-eslint/types').TSESTree.Node|null|undefined} node
|
||||
* @param {string} resolverName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
const hasNonEmptyResolverCall = (node, resolverName) => {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Arrow function without block
|
||||
switch (node.type) {
|
||||
case 'ArrayExpression':
|
||||
case 'ArrayPattern':
|
||||
return node.elements.some((element) => {
|
||||
return hasNonEmptyResolverCall(element, resolverName);
|
||||
});
|
||||
case 'ArrowFunctionExpression':
|
||||
case 'FunctionDeclaration':
|
||||
case 'FunctionExpression': {
|
||||
// Shadowing
|
||||
if (/** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
node.params[0]
|
||||
)?.name === resolverName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasNonEmptyResolverCall(node.body, resolverName);
|
||||
}
|
||||
|
||||
case 'AssignmentExpression':
|
||||
case 'BinaryExpression':
|
||||
case 'LogicalExpression': {
|
||||
return hasNonEmptyResolverCall(node.left, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.right, resolverName);
|
||||
}
|
||||
|
||||
case 'AssignmentPattern':
|
||||
return hasNonEmptyResolverCall(node.right, resolverName);
|
||||
case 'AwaitExpression':
|
||||
|
||||
case 'SpreadElement':
|
||||
case 'UnaryExpression':
|
||||
case 'YieldExpression':
|
||||
return hasNonEmptyResolverCall(node.argument, resolverName);
|
||||
case 'BlockStatement':
|
||||
case 'ClassBody':
|
||||
return node.body.some((bodyNode) => {
|
||||
return hasNonEmptyResolverCall(bodyNode, resolverName);
|
||||
});
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
case 'CallExpression':
|
||||
// @ts-expect-error Babel?
|
||||
case 'OptionalCallExpression':
|
||||
return /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
node.callee
|
||||
).name === resolverName && (
|
||||
|
||||
// Implicit or explicit undefined
|
||||
node.arguments.length > 1 || node.arguments[0] !== undefined
|
||||
) ||
|
||||
node.arguments.some((nde) => {
|
||||
// Being passed in to another function (which might invoke it)
|
||||
return nde.type === 'Identifier' && nde.name === resolverName ||
|
||||
|
||||
// Handle nested items
|
||||
hasNonEmptyResolverCall(nde, resolverName);
|
||||
});
|
||||
|
||||
case 'ChainExpression':
|
||||
case 'Decorator':
|
||||
|
||||
case 'ExpressionStatement':
|
||||
return hasNonEmptyResolverCall(node.expression, resolverName);
|
||||
|
||||
case 'ClassDeclaration':
|
||||
|
||||
case 'ClassExpression':
|
||||
return hasNonEmptyResolverCall(node.body, resolverName);
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'ClassMethod':
|
||||
|
||||
case 'MethodDefinition':
|
||||
return node.decorators && node.decorators.some((decorator) => {
|
||||
return hasNonEmptyResolverCall(decorator, resolverName);
|
||||
}) ||
|
||||
node.computed && hasNonEmptyResolverCall(node.key, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.value, resolverName);
|
||||
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'ClassProperty':
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'ObjectProperty':
|
||||
case 'Property':
|
||||
|
||||
case 'PropertyDefinition':
|
||||
return node.computed && hasNonEmptyResolverCall(node.key, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.value, resolverName);
|
||||
case 'ConditionalExpression':
|
||||
|
||||
case 'IfStatement': {
|
||||
return hasNonEmptyResolverCall(node.test, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.consequent, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.alternate, resolverName);
|
||||
}
|
||||
|
||||
case 'DoWhileStatement':
|
||||
case 'ForInStatement':
|
||||
case 'ForOfStatement':
|
||||
|
||||
case 'ForStatement':
|
||||
case 'LabeledStatement':
|
||||
case 'WhileStatement':
|
||||
case 'WithStatement': {
|
||||
return hasNonEmptyResolverCall(node.body, resolverName);
|
||||
}
|
||||
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'Import':
|
||||
|
||||
case 'ImportExpression':
|
||||
return hasNonEmptyResolverCall(node.source, resolverName);
|
||||
// ?.
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
case 'MemberExpression':
|
||||
|
||||
// @ts-expect-error Babel?
|
||||
case 'OptionalMemberExpression':
|
||||
return hasNonEmptyResolverCall(node.object, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.property, resolverName);
|
||||
case 'ObjectExpression':
|
||||
case 'ObjectPattern':
|
||||
return node.properties.some((property) => {
|
||||
return hasNonEmptyResolverCall(property, resolverName);
|
||||
});
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'ObjectMethod':
|
||||
/* c8 ignore next 6 -- In Babel? */
|
||||
// @ts-expect-error
|
||||
return node.computed && hasNonEmptyResolverCall(node.key, resolverName) ||
|
||||
// @ts-expect-error
|
||||
node.arguments.some((nde) => {
|
||||
return hasNonEmptyResolverCall(nde, resolverName);
|
||||
});
|
||||
|
||||
case 'ReturnStatement': {
|
||||
if (node.argument === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasNonEmptyResolverCall(node.argument, resolverName);
|
||||
}
|
||||
|
||||
// Comma
|
||||
case 'SequenceExpression':
|
||||
|
||||
case 'TemplateLiteral':
|
||||
return node.expressions.some((subExpression) => {
|
||||
return hasNonEmptyResolverCall(subExpression, resolverName);
|
||||
});
|
||||
|
||||
case 'SwitchStatement': {
|
||||
return node.cases.some(
|
||||
(someCase) => {
|
||||
return someCase.consequent.some((nde) => {
|
||||
return hasNonEmptyResolverCall(nde, resolverName);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
case 'TaggedTemplateExpression':
|
||||
return hasNonEmptyResolverCall(node.quasi, resolverName);
|
||||
|
||||
case 'TryStatement': {
|
||||
return hasNonEmptyResolverCall(node.block, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.handler && node.handler.body, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.finalizer, resolverName);
|
||||
}
|
||||
|
||||
case 'VariableDeclaration': {
|
||||
return node.declarations.some((nde) => {
|
||||
return hasNonEmptyResolverCall(nde, resolverName);
|
||||
});
|
||||
}
|
||||
|
||||
case 'VariableDeclarator': {
|
||||
return hasNonEmptyResolverCall(node.id, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.init, resolverName);
|
||||
}
|
||||
|
||||
/*
|
||||
// Shouldn't need to parse literals/literal components, etc.
|
||||
|
||||
case 'Identifier':
|
||||
case 'TemplateElement':
|
||||
case 'Super':
|
||||
// Exports not relevant in this context
|
||||
*/
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a Promise executor has no resolve value or an empty value.
|
||||
* An `undefined` resolve does not count.
|
||||
* @param {ESTreeOrTypeScriptNode} node
|
||||
* @param {boolean} anyPromiseAsReturn
|
||||
* @param {boolean} [allBranches]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasValueOrExecutorHasNonEmptyResolveValue = (node, anyPromiseAsReturn, allBranches) => {
|
||||
const hasReturnMethod = allBranches ?
|
||||
/**
|
||||
* @param {ESTreeOrTypeScriptNode} nde
|
||||
* @param {PromiseFilter} promiseFilter
|
||||
* @returns {boolean}
|
||||
*/
|
||||
(nde, promiseFilter) => {
|
||||
let hasReturn;
|
||||
try {
|
||||
hasReturn = hasReturnValue(nde, true, promiseFilter);
|
||||
} catch (error) {
|
||||
// c8 ignore else
|
||||
if (/** @type {Error} */ (error).message === 'Null return') {
|
||||
return false;
|
||||
}
|
||||
/* c8 ignore next 3 */
|
||||
// eslint-disable-next-line @stylistic/padding-line-between-statements -- c8
|
||||
throw error;
|
||||
}
|
||||
|
||||
// `hasReturn` check needed since `throw` treated as valid return by
|
||||
// `allBrancheshaveReturnValues`
|
||||
return Boolean(hasReturn && allBrancheshaveReturnValues(nde, promiseFilter));
|
||||
} :
|
||||
/**
|
||||
* @param {ESTreeOrTypeScriptNode} nde
|
||||
* @param {PromiseFilter} promiseFilter
|
||||
* @returns {boolean}
|
||||
*/
|
||||
(nde, promiseFilter) => {
|
||||
return Boolean(hasReturnValue(nde, false, promiseFilter));
|
||||
};
|
||||
|
||||
return hasReturnMethod(node, (prom) => {
|
||||
if (anyPromiseAsReturn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isVoidPromise(prom)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
body,
|
||||
params,
|
||||
} =
|
||||
/**
|
||||
* @type {import('@typescript-eslint/types').TSESTree.FunctionExpression|
|
||||
* import('@typescript-eslint/types').TSESTree.ArrowFunctionExpression}
|
||||
*/ (
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.NewExpression} */ (
|
||||
prom
|
||||
).arguments[0]
|
||||
) || {};
|
||||
|
||||
if (!params?.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
name: resolverName,
|
||||
} = /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
params[0]
|
||||
);
|
||||
|
||||
return hasNonEmptyResolverCall(body, resolverName);
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
hasReturnValue,
|
||||
hasValueOrExecutorHasNonEmptyResolveValue,
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue