"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _lodash = _interopRequireDefault(require("lodash"));

var _jsdocUtils = _interopRequireDefault(require("../jsdocUtils"));

var _exportParser = _interopRequireDefault(require("../exportParser"));

var _getJSDocComment = _interopRequireDefault(require("../eslint/getJSDocComment"));

var _warnRemovedSettings = _interopRequireDefault(require("../warnRemovedSettings"));

var _iterateJsdoc = require("../iterateJsdoc");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const OPTIONS_SCHEMA = {
  additionalProperties: false,
  properties: {
    contexts: {
      items: {
        type: 'string'
      },
      type: 'array'
    },
    exemptEmptyFunctions: {
      default: false,
      type: 'boolean'
    },
    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'
};

const getOption = (context, baseObject, option, key) => {
  if (!_lodash.default.has(context, `options[0][${option}][${key}]`)) {
    return baseObject.properties[key].default;
  }

  return context.options[0][option][key];
};

const getOptions = context => {
  return {
    exemptEmptyFunctions: context.options[0] ? context.options[0].exemptEmptyFunctions : false,
    publicOnly: (baseObj => {
      const publicOnly = _lodash.default.get(context, 'options[0].publicOnly');

      if (!publicOnly) {
        return false;
      }

      return Object.keys(baseObj.properties).reduce((obj, prop) => {
        const opt = getOption(context, baseObj, 'publicOnly', prop);
        obj[prop] = opt;
        return obj;
      }, {});
    })(OPTIONS_SCHEMA.properties.publicOnly.oneOf[1]),
    require: (baseObj => {
      return Object.keys(baseObj.properties).reduce((obj, prop) => {
        const opt = getOption(context, baseObj, 'require', prop);
        obj[prop] = opt;
        return obj;
      }, {});
    })(OPTIONS_SCHEMA.properties.require)
  };
};

var _default = {
  create(context) {
    (0, _warnRemovedSettings.default)(context, 'require-jsdoc');
    const sourceCode = context.getSourceCode();
    const settings = (0, _iterateJsdoc.getSettings)(context);

    const _getOptions = getOptions(context),
          requireOption = _getOptions.require,
          publicOnly = _getOptions.publicOnly,
          exemptEmptyFunctions = _getOptions.exemptEmptyFunctions;

    const checkJsDoc = node => {
      const jsDocNode = (0, _getJSDocComment.default)(sourceCode, node, settings);

      if (jsDocNode) {
        return;
      }

      if (exemptEmptyFunctions) {
        const functionParameterNames = _jsdocUtils.default.getFunctionParameterNames(node);

        if (!functionParameterNames.length && !_jsdocUtils.default.hasReturnValue(node, context)) {
          return;
        }
      }

      const fix = fixer => {
        // Default to one line break if the `minLines`/`maxLines` settings allow
        const lines = settings.minLines === 0 && settings.maxLines >= 1 ? 1 : settings.minLines;

        const indent = _jsdocUtils.default.getIndent(sourceCode);

        const insertion = `/**\n${indent}*\n${indent}*/${'\n'.repeat(lines)}${indent.slice(0, -1)}`;
        const baseNode = ['ExportDefaultDeclaration', 'ExportNamedDeclaration'].includes(node.parent && node.parent.type) ? node.parent : node;
        return fixer.insertTextBefore(baseNode, insertion);
      };

      const report = () => {
        const loc = {
          end: node.loc.start + 1,
          start: node.loc.start
        };
        context.report({
          fix,
          loc,
          messageId: 'missingJsDoc',
          node
        });
      };

      if (publicOnly) {
        const opt = {
          ancestorsOnly: Boolean(_lodash.default.get(publicOnly, 'ancestorsOnly', false)),
          esm: Boolean(_lodash.default.get(publicOnly, 'esm', true)),
          initModuleExports: Boolean(_lodash.default.get(publicOnly, 'cjs', true)),
          initWindow: Boolean(_lodash.default.get(publicOnly, 'window', false))
        };

        const parseResult = _exportParser.default.parse(sourceCode.ast, node, opt);

        const exported = _exportParser.default.isExported(node, parseResult, opt);

        if (exported) {
          report();
        }
      } else {
        report();
      }
    }; // eslint-disable-next-line fp/no-mutating-assign


    return Object.assign(_jsdocUtils.default.getContextObject(_jsdocUtils.default.enforcedContexts(context, []), checkJsDoc), {
      ArrowFunctionExpression(node) {
        if (!requireOption.ArrowFunctionExpression) {
          return;
        }

        if (!['VariableDeclarator', 'ExportDefaultDeclaration'].includes(node.parent.type)) {
          return;
        }

        checkJsDoc(node);
      },

      ClassDeclaration(node) {
        if (!requireOption.ClassDeclaration) {
          return;
        }

        checkJsDoc(node);
      },

      ClassExpression(node) {
        if (!requireOption.ClassExpression) {
          return;
        }

        checkJsDoc(node);
      },

      FunctionDeclaration(node) {
        if (!requireOption.FunctionDeclaration) {
          return;
        }

        checkJsDoc(node);
      },

      FunctionExpression(node) {
        if (requireOption.MethodDefinition && node.parent.type === 'MethodDefinition') {
          checkJsDoc(node);
          return;
        }

        if (!requireOption.FunctionExpression) {
          return;
        }

        if (['VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration'].includes(node.parent.type)) {
          checkJsDoc(node);
        } else if (node.parent.type === 'Property' && node === node.parent.value) {
          checkJsDoc(node);
        }
      }

    });
  },

  meta: {
    doc: {
      category: 'Stylistic Issues',
      description: 'Require JSDoc comments',
      recommended: 'true',
      url: 'https://github.com/gajus/eslint-plugin-jsdoc'
    },
    fixable: 'code',
    messages: {
      missingJsDoc: 'Missing JSDoc comment.'
    },
    schema: [OPTIONS_SCHEMA],
    type: 'suggestion'
  }
};
exports.default = _default;
module.exports = exports.default;
//# sourceMappingURL=requireJsdoc.js.map