multilineBlocks.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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. var _default = exports.default = (0, _iterateJsdoc.default)(({
  9. context,
  10. jsdoc,
  11. utils
  12. }) => {
  13. const {
  14. allowMultipleTags = true,
  15. noFinalLineText = true,
  16. noZeroLineText = true,
  17. noSingleLineBlocks = false,
  18. singleLineTags = ['lends', 'type'],
  19. noMultilineBlocks = false,
  20. minimumLengthForMultiline = Number.POSITIVE_INFINITY,
  21. multilineTags = ['*']
  22. } = context.options[0] || {};
  23. const {
  24. source: [{
  25. tokens
  26. }]
  27. } = jsdoc;
  28. const {
  29. description,
  30. tag
  31. } = tokens;
  32. const sourceLength = jsdoc.source.length;
  33. /**
  34. * @param {string} tagName
  35. * @returns {boolean}
  36. */
  37. const isInvalidSingleLine = tagName => {
  38. return noSingleLineBlocks && (!tagName || !singleLineTags.includes(tagName) && !singleLineTags.includes('*'));
  39. };
  40. if (sourceLength === 1) {
  41. if (!isInvalidSingleLine(tag.slice(1))) {
  42. return;
  43. }
  44. const fixer = () => {
  45. utils.makeMultiline();
  46. };
  47. utils.reportJSDoc('Single line blocks are not permitted by your configuration.', null, fixer, true);
  48. return;
  49. }
  50. const lineChecks = () => {
  51. if (noZeroLineText && (tag || description)) {
  52. const fixer = () => {
  53. const line = {
  54. ...tokens
  55. };
  56. utils.emptyTokens(tokens);
  57. const {
  58. tokens: {
  59. delimiter,
  60. start
  61. }
  62. } = jsdoc.source[1];
  63. utils.addLine(1, {
  64. ...line,
  65. delimiter,
  66. start
  67. });
  68. };
  69. utils.reportJSDoc('Should have no text on the "0th" line (after the `/**`).', null, fixer);
  70. return;
  71. }
  72. const finalLine = jsdoc.source[jsdoc.source.length - 1];
  73. const finalLineTokens = finalLine.tokens;
  74. if (noFinalLineText && finalLineTokens.description.trim()) {
  75. const fixer = () => {
  76. const line = {
  77. ...finalLineTokens
  78. };
  79. line.description = line.description.trimEnd();
  80. const {
  81. delimiter
  82. } = line;
  83. for (const prop of ['delimiter', 'postDelimiter', 'tag', 'type', 'lineEnd', 'postType', 'postTag', 'name', 'postName', 'description']) {
  84. finalLineTokens[(
  85. /**
  86. * @type {"delimiter"|"postDelimiter"|"tag"|"type"|
  87. * "lineEnd"|"postType"|"postTag"|"name"|
  88. * "postName"|"description"}
  89. */
  90. prop)] = '';
  91. }
  92. utils.addLine(jsdoc.source.length - 1, {
  93. ...line,
  94. delimiter,
  95. end: ''
  96. });
  97. };
  98. utils.reportJSDoc('Should have no text on the final line (before the `*/`).', null, fixer);
  99. }
  100. };
  101. if (noMultilineBlocks) {
  102. if (jsdoc.tags.length && (multilineTags.includes('*') || utils.hasATag(multilineTags))) {
  103. lineChecks();
  104. return;
  105. }
  106. if (jsdoc.description.length >= minimumLengthForMultiline) {
  107. lineChecks();
  108. return;
  109. }
  110. if (noSingleLineBlocks && (!jsdoc.tags.length || !utils.filterTags(({
  111. tag: tg
  112. }) => {
  113. return !isInvalidSingleLine(tg);
  114. }).length)) {
  115. utils.reportJSDoc('Multiline jsdoc blocks are prohibited by ' + 'your configuration but fixing would result in a single ' + 'line block which you have prohibited with `noSingleLineBlocks`.');
  116. return;
  117. }
  118. if (jsdoc.tags.length > 1) {
  119. if (!allowMultipleTags) {
  120. utils.reportJSDoc('Multiline jsdoc blocks are prohibited by ' + 'your configuration but the block has multiple tags.');
  121. return;
  122. }
  123. } else if (jsdoc.tags.length === 1 && jsdoc.description.trim()) {
  124. if (!allowMultipleTags) {
  125. utils.reportJSDoc('Multiline jsdoc blocks are prohibited by ' + 'your configuration but the block has a description with a tag.');
  126. return;
  127. }
  128. } else {
  129. const fixer = () => {
  130. jsdoc.source = [{
  131. number: 1,
  132. source: '',
  133. tokens: jsdoc.source.reduce((obj, {
  134. tokens: {
  135. description: desc,
  136. tag: tg,
  137. type: typ,
  138. name: nme,
  139. lineEnd,
  140. postType,
  141. postName,
  142. postTag
  143. }
  144. }) => {
  145. if (typ) {
  146. obj.type = typ;
  147. }
  148. if (tg && typ && nme) {
  149. obj.postType = postType;
  150. }
  151. if (nme) {
  152. obj.name += nme;
  153. }
  154. if (nme && desc) {
  155. obj.postName = postName;
  156. }
  157. obj.description += desc;
  158. const nameOrDescription = obj.description || obj.name;
  159. if (nameOrDescription && nameOrDescription.slice(-1) !== ' ') {
  160. obj.description += ' ';
  161. }
  162. obj.lineEnd = lineEnd;
  163. // Already filtered for multiple tags
  164. obj.tag += tg;
  165. if (tg) {
  166. obj.postTag = postTag || ' ';
  167. }
  168. return obj;
  169. }, utils.seedTokens({
  170. delimiter: '/**',
  171. end: '*/',
  172. postDelimiter: ' '
  173. }))
  174. }];
  175. };
  176. utils.reportJSDoc('Multiline jsdoc blocks are prohibited by ' + 'your configuration.', null, fixer);
  177. return;
  178. }
  179. }
  180. lineChecks();
  181. }, {
  182. iterateAllJsdocs: true,
  183. meta: {
  184. docs: {
  185. description: 'Controls how and whether jsdoc blocks can be expressed as single or multiple line blocks.',
  186. url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/multiline-blocks.md#repos-sticky-header'
  187. },
  188. fixable: 'code',
  189. schema: [{
  190. additionalProperties: false,
  191. properties: {
  192. allowMultipleTags: {
  193. type: 'boolean'
  194. },
  195. minimumLengthForMultiline: {
  196. type: 'integer'
  197. },
  198. multilineTags: {
  199. anyOf: [{
  200. enum: ['*'],
  201. type: 'string'
  202. }, {
  203. items: {
  204. type: 'string'
  205. },
  206. type: 'array'
  207. }]
  208. },
  209. noFinalLineText: {
  210. type: 'boolean'
  211. },
  212. noMultilineBlocks: {
  213. type: 'boolean'
  214. },
  215. noSingleLineBlocks: {
  216. type: 'boolean'
  217. },
  218. noZeroLineText: {
  219. type: 'boolean'
  220. },
  221. singleLineTags: {
  222. items: {
  223. type: 'string'
  224. },
  225. type: 'array'
  226. }
  227. },
  228. type: 'object'
  229. }],
  230. type: 'suggestion'
  231. }
  232. });
  233. module.exports = exports.default;
  234. //# sourceMappingURL=multilineBlocks.js.map