forked from public/fvtt-cthulhu-eternal
527 lines
14 KiB
JavaScript
527 lines
14 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
exports.default = void 0;
|
||
|
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
|
||
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
||
|
/**
|
||
|
* @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 */
|
||
|
var _default = exports.default = (0, _iterateJsdoc.default)(({
|
||
|
jsdoc,
|
||
|
utils,
|
||
|
context
|
||
|
}) => {
|
||
|
/* eslint-enable complexity -- Temporary */
|
||
|
if (utils.avoidDocs()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Param type is specified by type in @type
|
||
|
if (utils.hasTag('type')) {
|
||
|
return;
|
||
|
}
|
||
|
const {
|
||
|
autoIncrementBase = 0,
|
||
|
checkRestProperty = false,
|
||
|
checkDestructured = true,
|
||
|
checkDestructuredRoots = true,
|
||
|
checkTypesPattern = '/^(?:[oO]bject|[aA]rray|PlainObject|Generic(?:Object|Array))$/',
|
||
|
enableFixer = true,
|
||
|
enableRootFixer = true,
|
||
|
enableRestElementFixer = true,
|
||
|
unnamedRootBase = ['root'],
|
||
|
useDefaultObjectProperties = false
|
||
|
} = context.options[0] || {};
|
||
|
const preferredTagName = /** @type {string} */utils.getPreferredTagName({
|
||
|
tagName: 'param'
|
||
|
});
|
||
|
if (!preferredTagName) {
|
||
|
return;
|
||
|
}
|
||
|
const functionParameterNames = utils.getFunctionParameterNames(useDefaultObjectProperties);
|
||
|
if (!functionParameterNames.length) {
|
||
|
return;
|
||
|
}
|
||
|
const jsdocParameterNames =
|
||
|
/**
|
||
|
* @type {{
|
||
|
* idx: import('../iterateJsdoc.js').Integer;
|
||
|
* name: string;
|
||
|
* type: string;
|
||
|
* }[]}
|
||
|
*/
|
||
|
utils.getJsdocTagsDeep(preferredTagName);
|
||
|
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 {import('../iterateJsdoc.js').Integer}
|
||
|
*/
|
||
|
const findExpectedIndex = (jsdocTags, indexAtFunctionParams) => {
|
||
|
const remainingRoots = functionParameterNames.slice(indexAtFunctionParams || 0);
|
||
|
const foundIndex = jsdocTags.findIndex(({
|
||
|
name,
|
||
|
newAdd
|
||
|
}) => {
|
||
|
return !newAdd && remainingRoots.some(remainingRoot => {
|
||
|
if (Array.isArray(remainingRoot)) {
|
||
|
return (
|
||
|
/**
|
||
|
* @type {import('../jsdocUtils.js').FlattendRootInfo & {
|
||
|
* annotationParamName?: string|undefined;
|
||
|
* }}
|
||
|
*/
|
||
|
remainingRoot[1].names.includes(name)
|
||
|
);
|
||
|
}
|
||
|
if (typeof remainingRoot === 'object') {
|
||
|
return name === remainingRoot.name;
|
||
|
}
|
||
|
return name === remainingRoot;
|
||
|
});
|
||
|
});
|
||
|
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 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 {
|
||
|
hasRestElement,
|
||
|
hasPropertyRest,
|
||
|
rests,
|
||
|
names
|
||
|
} =
|
||
|
/**
|
||
|
* @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,
|
||
|
remove,
|
||
|
inc,
|
||
|
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: {
|
||
|
tag,
|
||
|
end
|
||
|
}
|
||
|
}) => {
|
||
|
return tag || end;
|
||
|
});
|
||
|
if (remove) {
|
||
|
createTokens(functionParameterIdx, offset + functionParameterIdx, 1);
|
||
|
} else {
|
||
|
const expectedIdx = findExpectedIndex(jsdoc.tags, functionParameterIdx);
|
||
|
createTokens(expectedIdx, offset + 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.',
|
||
|
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,
|
||
|
type: 'integer'
|
||
|
},
|
||
|
checkConstructors: {
|
||
|
default: true,
|
||
|
type: 'boolean'
|
||
|
},
|
||
|
checkDestructured: {
|
||
|
default: true,
|
||
|
type: 'boolean'
|
||
|
},
|
||
|
checkDestructuredRoots: {
|
||
|
default: true,
|
||
|
type: 'boolean'
|
||
|
},
|
||
|
checkGetters: {
|
||
|
default: false,
|
||
|
type: 'boolean'
|
||
|
},
|
||
|
checkRestProperty: {
|
||
|
default: false,
|
||
|
type: 'boolean'
|
||
|
},
|
||
|
checkSetters: {
|
||
|
default: false,
|
||
|
type: 'boolean'
|
||
|
},
|
||
|
checkTypesPattern: {
|
||
|
type: 'string'
|
||
|
},
|
||
|
contexts: {
|
||
|
items: {
|
||
|
anyOf: [{
|
||
|
type: 'string'
|
||
|
}, {
|
||
|
additionalProperties: false,
|
||
|
properties: {
|
||
|
comment: {
|
||
|
type: 'string'
|
||
|
},
|
||
|
context: {
|
||
|
type: 'string'
|
||
|
}
|
||
|
},
|
||
|
type: 'object'
|
||
|
}]
|
||
|
},
|
||
|
type: 'array'
|
||
|
},
|
||
|
enableFixer: {
|
||
|
type: 'boolean'
|
||
|
},
|
||
|
enableRestElementFixer: {
|
||
|
type: 'boolean'
|
||
|
},
|
||
|
enableRootFixer: {
|
||
|
type: 'boolean'
|
||
|
},
|
||
|
exemptedBy: {
|
||
|
items: {
|
||
|
type: 'string'
|
||
|
},
|
||
|
type: 'array'
|
||
|
},
|
||
|
unnamedRootBase: {
|
||
|
items: {
|
||
|
type: 'string'
|
||
|
},
|
||
|
type: 'array'
|
||
|
},
|
||
|
useDefaultObjectProperties: {
|
||
|
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
|
||
|
});
|
||
|
module.exports = exports.default;
|
||
|
//# sourceMappingURL=requireParam.cjs.map
|