requireYieldsCheck.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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. * @param {import('../iterateJsdoc.js').Utils} utils
  10. * @param {import('../iterateJsdoc.js').Settings} settings
  11. * @returns {boolean}
  12. */
  13. const canSkip = (utils, settings) => {
  14. const voidingTags = [
  15. // An abstract function is by definition incomplete
  16. // so it is perfectly fine if a yield is documented but
  17. // not present within the function.
  18. // A subclass may inherit the doc and implement the
  19. // missing yield.
  20. 'abstract', 'virtual',
  21. // Constructor functions do not have a yield value
  22. // so we can bail here, too.
  23. 'class', 'constructor',
  24. // This seems to imply a class as well
  25. 'interface'];
  26. if (settings.mode === 'closure') {
  27. // Structural Interface in GCC terms, equivalent to @interface tag as far as this rule is concerned
  28. voidingTags.push('record');
  29. }
  30. return utils.hasATag(voidingTags) || utils.isConstructor() || utils.classHasTag('interface') || settings.mode === 'closure' && utils.classHasTag('record');
  31. };
  32. /**
  33. * @param {import('../iterateJsdoc.js').Utils} utils
  34. * @param {import('../iterateJsdoc.js').Report} report
  35. * @param {string} tagName
  36. * @returns {[]|[preferredTagName: string, tag: import('comment-parser').Spec]}
  37. */
  38. const checkTagName = (utils, report, tagName) => {
  39. const preferredTagName = /** @type {string} */utils.getPreferredTagName({
  40. tagName
  41. });
  42. if (!preferredTagName) {
  43. return [];
  44. }
  45. const tags = utils.getTags(preferredTagName);
  46. if (tags.length === 0) {
  47. return [];
  48. }
  49. if (tags.length > 1) {
  50. report(`Found more than one @${preferredTagName} declaration.`);
  51. return [];
  52. }
  53. return [preferredTagName, tags[0]];
  54. };
  55. var _default = exports.default = (0, _iterateJsdoc.default)(({
  56. context,
  57. report,
  58. settings,
  59. utils
  60. }) => {
  61. if (canSkip(utils, settings)) {
  62. return;
  63. }
  64. const {
  65. next = false,
  66. checkGeneratorsOnly = false
  67. } = context.options[0] || {};
  68. const [preferredYieldTagName, yieldTag] = checkTagName(utils, report, 'yields');
  69. if (preferredYieldTagName) {
  70. const shouldReportYields = () => {
  71. if ( /** @type {import('comment-parser').Spec} */yieldTag.type.trim() === 'never') {
  72. if (utils.hasYieldValue()) {
  73. report(`JSDoc @${preferredYieldTagName} declaration set with "never" but yield expression is present in function.`);
  74. }
  75. return false;
  76. }
  77. if (checkGeneratorsOnly && !utils.isGenerator()) {
  78. return true;
  79. }
  80. return !utils.mayBeUndefinedTypeTag( /** @type {import('comment-parser').Spec} */
  81. yieldTag) && !utils.hasYieldValue();
  82. };
  83. // In case a yield value is declared in JSDoc, we also expect one in the code.
  84. if (shouldReportYields()) {
  85. report(`JSDoc @${preferredYieldTagName} declaration present but yield expression not available in function.`);
  86. }
  87. }
  88. if (next) {
  89. const [preferredNextTagName, nextTag] = checkTagName(utils, report, 'next');
  90. if (preferredNextTagName) {
  91. const shouldReportNext = () => {
  92. if ( /** @type {import('comment-parser').Spec} */nextTag.type.trim() === 'never') {
  93. if (utils.hasYieldReturnValue()) {
  94. report(`JSDoc @${preferredNextTagName} declaration set with "never" but yield expression with return value is present in function.`);
  95. }
  96. return false;
  97. }
  98. if (checkGeneratorsOnly && !utils.isGenerator()) {
  99. return true;
  100. }
  101. return !utils.mayBeUndefinedTypeTag( /** @type {import('comment-parser').Spec} */
  102. nextTag) && !utils.hasYieldReturnValue();
  103. };
  104. if (shouldReportNext()) {
  105. report(`JSDoc @${preferredNextTagName} declaration present but yield expression with return value not available in function.`);
  106. }
  107. }
  108. }
  109. }, {
  110. meta: {
  111. docs: {
  112. description: 'Requires a yield statement in function body if a `@yields` tag is specified in jsdoc comment.',
  113. url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-yields-check.md#repos-sticky-header'
  114. },
  115. schema: [{
  116. additionalProperties: false,
  117. properties: {
  118. checkGeneratorsOnly: {
  119. default: false,
  120. type: 'boolean'
  121. },
  122. contexts: {
  123. items: {
  124. anyOf: [{
  125. type: 'string'
  126. }, {
  127. additionalProperties: false,
  128. properties: {
  129. comment: {
  130. type: 'string'
  131. },
  132. context: {
  133. type: 'string'
  134. }
  135. },
  136. type: 'object'
  137. }]
  138. },
  139. type: 'array'
  140. },
  141. exemptedBy: {
  142. items: {
  143. type: 'string'
  144. },
  145. type: 'array'
  146. },
  147. next: {
  148. default: false,
  149. type: 'boolean'
  150. }
  151. },
  152. type: 'object'
  153. }],
  154. type: 'suggestion'
  155. }
  156. });
  157. module.exports = exports.default;
  158. //# sourceMappingURL=requireYieldsCheck.js.map