Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

array-element-newline.js 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /**
  2. * @fileoverview Rule to enforce line breaks after each array element
  3. * @author Jan Peer Stöcklmair <https://github.com/JPeer264>
  4. */
  5. "use strict";
  6. const astUtils = require("../util/ast-utils");
  7. //------------------------------------------------------------------------------
  8. // Rule Definition
  9. //------------------------------------------------------------------------------
  10. module.exports = {
  11. meta: {
  12. type: "layout",
  13. docs: {
  14. description: "enforce line breaks after each array element",
  15. category: "Stylistic Issues",
  16. recommended: false,
  17. url: "https://eslint.org/docs/rules/array-element-newline"
  18. },
  19. fixable: "whitespace",
  20. schema: [
  21. {
  22. oneOf: [
  23. {
  24. enum: ["always", "never", "consistent"]
  25. },
  26. {
  27. type: "object",
  28. properties: {
  29. multiline: {
  30. type: "boolean"
  31. },
  32. minItems: {
  33. type: ["integer", "null"],
  34. minimum: 0
  35. }
  36. },
  37. additionalProperties: false
  38. }
  39. ]
  40. }
  41. ],
  42. messages: {
  43. unexpectedLineBreak: "There should be no linebreak here.",
  44. missingLineBreak: "There should be a linebreak after this element."
  45. }
  46. },
  47. create(context) {
  48. const sourceCode = context.getSourceCode();
  49. //----------------------------------------------------------------------
  50. // Helpers
  51. //----------------------------------------------------------------------
  52. /**
  53. * Normalizes a given option value.
  54. *
  55. * @param {string|Object|undefined} providedOption - An option value to parse.
  56. * @returns {{multiline: boolean, minItems: number}} Normalized option object.
  57. */
  58. function normalizeOptionValue(providedOption) {
  59. let consistent = false;
  60. let multiline = false;
  61. let minItems;
  62. const option = providedOption || "always";
  63. if (!option || option === "always" || option.minItems === 0) {
  64. minItems = 0;
  65. } else if (option === "never") {
  66. minItems = Number.POSITIVE_INFINITY;
  67. } else if (option === "consistent") {
  68. consistent = true;
  69. minItems = Number.POSITIVE_INFINITY;
  70. } else {
  71. multiline = Boolean(option.multiline);
  72. minItems = option.minItems || Number.POSITIVE_INFINITY;
  73. }
  74. return { consistent, multiline, minItems };
  75. }
  76. /**
  77. * Normalizes a given option value.
  78. *
  79. * @param {string|Object|undefined} options - An option value to parse.
  80. * @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object.
  81. */
  82. function normalizeOptions(options) {
  83. const value = normalizeOptionValue(options);
  84. return { ArrayExpression: value, ArrayPattern: value };
  85. }
  86. /**
  87. * Reports that there shouldn't be a line break after the first token
  88. * @param {Token} token - The token to use for the report.
  89. * @returns {void}
  90. */
  91. function reportNoLineBreak(token) {
  92. const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true });
  93. context.report({
  94. loc: {
  95. start: tokenBefore.loc.end,
  96. end: token.loc.start
  97. },
  98. messageId: "unexpectedLineBreak",
  99. fix(fixer) {
  100. if (astUtils.isCommentToken(tokenBefore)) {
  101. return null;
  102. }
  103. if (!astUtils.isTokenOnSameLine(tokenBefore, token)) {
  104. return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ");
  105. }
  106. /*
  107. * This will check if the comma is on the same line as the next element
  108. * Following array:
  109. * [
  110. * 1
  111. * , 2
  112. * , 3
  113. * ]
  114. *
  115. * will be fixed to:
  116. * [
  117. * 1, 2, 3
  118. * ]
  119. */
  120. const twoTokensBefore = sourceCode.getTokenBefore(tokenBefore, { includeComments: true });
  121. if (astUtils.isCommentToken(twoTokensBefore)) {
  122. return null;
  123. }
  124. return fixer.replaceTextRange([twoTokensBefore.range[1], tokenBefore.range[0]], "");
  125. }
  126. });
  127. }
  128. /**
  129. * Reports that there should be a line break after the first token
  130. * @param {Token} token - The token to use for the report.
  131. * @returns {void}
  132. */
  133. function reportRequiredLineBreak(token) {
  134. const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true });
  135. context.report({
  136. loc: {
  137. start: tokenBefore.loc.end,
  138. end: token.loc.start
  139. },
  140. messageId: "missingLineBreak",
  141. fix(fixer) {
  142. return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n");
  143. }
  144. });
  145. }
  146. /**
  147. * Reports a given node if it violated this rule.
  148. *
  149. * @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node.
  150. * @param {{multiline: boolean, minItems: number}} options - An option object.
  151. * @returns {void}
  152. */
  153. function check(node) {
  154. const elements = node.elements;
  155. const normalizedOptions = normalizeOptions(context.options[0]);
  156. const options = normalizedOptions[node.type];
  157. let elementBreak = false;
  158. /*
  159. * MULTILINE: true
  160. * loop through every element and check
  161. * if at least one element has linebreaks inside
  162. * this ensures that following is not valid (due to elements are on the same line):
  163. *
  164. * [
  165. * 1,
  166. * 2,
  167. * 3
  168. * ]
  169. */
  170. if (options.multiline) {
  171. elementBreak = elements
  172. .filter(element => element !== null)
  173. .some(element => element.loc.start.line !== element.loc.end.line);
  174. }
  175. const linebreaksCount = node.elements.map((element, i) => {
  176. const previousElement = elements[i - 1];
  177. if (i === 0 || element === null || previousElement === null) {
  178. return false;
  179. }
  180. const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, astUtils.isCommaToken);
  181. const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken);
  182. const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken);
  183. return !astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement);
  184. }).filter(isBreak => isBreak === true).length;
  185. const needsLinebreaks = (
  186. elements.length >= options.minItems ||
  187. (
  188. options.multiline &&
  189. elementBreak
  190. ) ||
  191. (
  192. options.consistent &&
  193. linebreaksCount > 0 &&
  194. linebreaksCount < node.elements.length
  195. )
  196. );
  197. elements.forEach((element, i) => {
  198. const previousElement = elements[i - 1];
  199. if (i === 0 || element === null || previousElement === null) {
  200. return;
  201. }
  202. const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, astUtils.isCommaToken);
  203. const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken);
  204. const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken);
  205. if (needsLinebreaks) {
  206. if (astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
  207. reportRequiredLineBreak(firstTokenOfCurrentElement);
  208. }
  209. } else {
  210. if (!astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
  211. reportNoLineBreak(firstTokenOfCurrentElement);
  212. }
  213. }
  214. });
  215. }
  216. //----------------------------------------------------------------------
  217. // Public
  218. //----------------------------------------------------------------------
  219. return {
  220. ArrayPattern: check,
  221. ArrayExpression: check
  222. };
  223. }
  224. };