"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _exportParser = _interopRequireDefault(require("../exportParser.cjs")); var _iterateJsdoc = require("../iterateJsdoc.cjs"); var _jsdocUtils = require("../jsdocUtils.cjs"); var _jsdoccomment = require("@es-joy/jsdoccomment"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } /** * @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, properties: { checkConstructors: { default: true, type: 'boolean' }, checkGetters: { anyOf: [{ type: 'boolean' }, { enum: ['no-setter'], type: 'string' }], default: true }, checkSetters: { anyOf: [{ type: 'boolean' }, { enum: ['no-getter'], type: 'string' }], default: true }, contexts: { items: { anyOf: [{ type: 'string' }, { additionalProperties: false, properties: { context: { type: 'string' }, inlineCommentBlock: { type: 'boolean' }, minLineCount: { type: 'integer' } }, type: 'object' }] }, type: 'array' }, enableFixer: { default: true, type: 'boolean' }, exemptEmptyConstructors: { default: false, type: 'boolean' }, exemptEmptyFunctions: { default: false, type: 'boolean' }, fixerMessage: { default: '', type: 'string' }, minLineCount: { type: 'integer' }, publicOnly: { 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: {}, properties: { ArrowFunctionExpression: { default: false, type: 'boolean' }, ClassDeclaration: { default: false, type: 'boolean' }, ClassExpression: { default: false, type: 'boolean' }, FunctionDeclaration: { default: true, type: 'boolean' }, FunctionExpression: { default: false, type: 'boolean' }, MethodDefinition: { default: false, type: 'boolean' } }, type: 'object' } }, type: 'object' }; /** * @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 {{ * contexts: (string|{ * context: string, * inlineCommentBlock: boolean, * minLineCount: import('../iterateJsdoc.js').Integer * })[], * enableFixer: boolean, * exemptEmptyConstructors: boolean, * exemptEmptyFunctions: 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 { publicOnly, contexts = settings.contexts || [], exemptEmptyConstructors = true, exemptEmptyFunctions = false, enableFixer = true, fixerMessage = '', minLineCount = undefined } = context.options[0] || {}; return { contexts, enableFixer, exemptEmptyConstructors, exemptEmptyFunctions, 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) }; }; /** @type {import('eslint').Rule.RuleModule} */ var _default = exports.default = { create(context) { /* c8 ignore next -- Fallback to deprecated method */ const { sourceCode = context.getSourceCode() } = context; const settings = (0, _iterateJsdoc.getSettings)(context); if (!settings) { return {}; } const opts = getOptions(context, settings); const { require: requireOption, contexts, exemptEmptyFunctions, exemptEmptyConstructors, enableFixer, fixerMessage, minLineCount } = 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 => { var _sourceCode$getText$m; return count !== undefined && count > (((_sourceCode$getText$m = sourceCode.getText(node).match(/\n/gu)) === null || _sourceCode$getText$m === void 0 ? void 0 : _sourceCode$getText$m.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; } } const jsDocNode = (0, _jsdoccomment.getJSDocComment)(sourceCode, node, settings); if (jsDocNode) { return; } // For those who have options configured against ANY constructors (or // setters or getters) being reported if ((0, _jsdocUtils.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 && (0, _jsdocUtils.isConstructor)(node)) { const functionParameterNames = (0, _jsdocUtils.getFunctionParameterNames)(node); if (!functionParameterNames.length && !(0, _jsdocUtils.hasReturnValue)(node)) { 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 = (0, _jsdoccomment.getReducedASTNode)(node, sourceCode); const decorator = (0, _jsdoccomment.getDecorator)( /** @type {import('eslint').Rule.Node} */ baseNode); if (decorator) { baseNode = decorator; } const indent = (0, _jsdocUtils.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 === null || publicOnly === void 0 ? void 0 : publicOnly.ancestorsOnly) ?? false), esm: Boolean((publicOnly === null || publicOnly === void 0 ? void 0 : publicOnly.esm) ?? true), initModuleExports: Boolean((publicOnly === null || publicOnly === void 0 ? void 0 : publicOnly.cjs) ?? true), initWindow: Boolean((publicOnly === null || publicOnly === void 0 ? void 0 : publicOnly.window) ?? false) }; const exported = _exportParser.default.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 { ...(0, _jsdocUtils.getContextObject)((0, _jsdocUtils.enforcedContexts)(context, [], settings), checkJsDoc), ArrowFunctionExpression(node) { if (!hasOption('ArrowFunctionExpression')) { return; } if (['VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration'].includes(node.parent.type) || ['Property', 'ObjectProperty', 'ClassProperty', '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 (['VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration'].includes(node.parent.type) || ['Property', 'ObjectProperty', 'ClassProperty', '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: 'Require JSDoc comments', 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' } }; module.exports = exports.default; //# sourceMappingURL=requireJsdoc.cjs.map