requireYields.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.js"));
  7. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  8. /**
  9. * We can skip checking for a yield value, in case the documentation is inherited
  10. * or the method has a constructor or abstract tag.
  11. *
  12. * In either of these cases the yield value is optional or not defined.
  13. * @param {import('../iterateJsdoc.js').Utils} utils a reference to the utils which are used to probe if a tag is present or not.
  14. * @returns {boolean} true in case deep checking can be skipped; otherwise false.
  15. */
  16. const canSkip = utils => {
  17. return utils.hasATag([
  18. // inheritdoc implies that all documentation is inherited
  19. // see https://jsdoc.app/tags-inheritdoc.html
  20. //
  21. // Abstract methods are by definition incomplete,
  22. // so it is not an error if it declares a yield value but does not implement it.
  23. 'abstract', 'virtual',
  24. // Constructors do not have a yield value
  25. // so we can bail out here, too.
  26. 'class', 'constructor',
  27. // Yield (and any `next`) type is specified accompanying the targeted
  28. // @type
  29. 'type',
  30. // This seems to imply a class as well
  31. 'interface']) || utils.avoidDocs();
  32. };
  33. /**
  34. * @param {import('../iterateJsdoc.js').Utils} utils
  35. * @param {import('../iterateJsdoc.js').Report} report
  36. * @param {string} tagName
  37. * @returns {[preferredTagName?: string, missingTag?: boolean]}
  38. */
  39. const checkTagName = (utils, report, tagName) => {
  40. const preferredTagName = /** @type {string} */utils.getPreferredTagName({
  41. tagName
  42. });
  43. if (!preferredTagName) {
  44. return [];
  45. }
  46. const tags = utils.getTags(preferredTagName);
  47. if (tags.length > 1) {
  48. report(`Found more than one @${preferredTagName} declaration.`);
  49. }
  50. // In case the code yields something, we expect a yields value in JSDoc.
  51. const [tag] = tags;
  52. const missingTag = typeof tag === 'undefined' || tag === null;
  53. return [preferredTagName, missingTag];
  54. };
  55. var _default = exports.default = (0, _iterateJsdoc.default)(({
  56. report,
  57. utils,
  58. context
  59. }) => {
  60. const {
  61. next = false,
  62. nextWithGeneratorTag = false,
  63. forceRequireNext = false,
  64. forceRequireYields = false,
  65. withGeneratorTag = true
  66. } = context.options[0] || {};
  67. // A preflight check. We do not need to run a deep check
  68. // in case the @yield comment is optional or undefined.
  69. if (canSkip(utils)) {
  70. return;
  71. }
  72. const iteratingFunction = utils.isIteratingFunction();
  73. const [preferredYieldTagName, missingYieldTag] = checkTagName(utils, report, 'yields');
  74. if (preferredYieldTagName) {
  75. const shouldReportYields = () => {
  76. if (!missingYieldTag) {
  77. return false;
  78. }
  79. if (withGeneratorTag && utils.hasTag('generator') || forceRequireYields && iteratingFunction && utils.isGenerator()) {
  80. return true;
  81. }
  82. return iteratingFunction && utils.isGenerator() && utils.hasYieldValue();
  83. };
  84. if (shouldReportYields()) {
  85. report(`Missing JSDoc @${preferredYieldTagName} declaration.`);
  86. }
  87. }
  88. if (next || nextWithGeneratorTag || forceRequireNext) {
  89. const [preferredNextTagName, missingNextTag] = checkTagName(utils, report, 'next');
  90. if (!preferredNextTagName) {
  91. return;
  92. }
  93. const shouldReportNext = () => {
  94. if (!missingNextTag) {
  95. return false;
  96. }
  97. if (nextWithGeneratorTag && utils.hasTag('generator')) {
  98. return true;
  99. }
  100. if (!next && !forceRequireNext || !iteratingFunction || !utils.isGenerator()) {
  101. return false;
  102. }
  103. return forceRequireNext || utils.hasYieldReturnValue();
  104. };
  105. if (shouldReportNext()) {
  106. report(`Missing JSDoc @${preferredNextTagName} declaration.`);
  107. }
  108. }
  109. }, {
  110. contextDefaults: true,
  111. meta: {
  112. docs: {
  113. description: 'Requires yields are documented.',
  114. url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-yields.md#repos-sticky-header'
  115. },
  116. schema: [{
  117. additionalProperties: false,
  118. properties: {
  119. contexts: {
  120. items: {
  121. anyOf: [{
  122. type: 'string'
  123. }, {
  124. additionalProperties: false,
  125. properties: {
  126. comment: {
  127. type: 'string'
  128. },
  129. context: {
  130. type: 'string'
  131. }
  132. },
  133. type: 'object'
  134. }]
  135. },
  136. type: 'array'
  137. },
  138. exemptedBy: {
  139. items: {
  140. type: 'string'
  141. },
  142. type: 'array'
  143. },
  144. forceRequireNext: {
  145. default: false,
  146. type: 'boolean'
  147. },
  148. forceRequireYields: {
  149. default: false,
  150. type: 'boolean'
  151. },
  152. next: {
  153. default: false,
  154. type: 'boolean'
  155. },
  156. nextWithGeneratorTag: {
  157. default: false,
  158. type: 'boolean'
  159. },
  160. withGeneratorTag: {
  161. default: true,
  162. type: 'boolean'
  163. }
  164. },
  165. type: 'object'
  166. }],
  167. type: 'suggestion'
  168. }
  169. });
  170. module.exports = exports.default;
  171. //# sourceMappingURL=requireYields.js.map