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.

object-curly-newline.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /**
  2. * @fileoverview Rule to require or disallow line breaks inside braces.
  3. * @author Toru Nagashima
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("../util/ast-utils");
  10. const lodash = require("lodash");
  11. //------------------------------------------------------------------------------
  12. // Helpers
  13. //------------------------------------------------------------------------------
  14. // Schema objects.
  15. const OPTION_VALUE = {
  16. oneOf: [
  17. {
  18. enum: ["always", "never"]
  19. },
  20. {
  21. type: "object",
  22. properties: {
  23. multiline: {
  24. type: "boolean"
  25. },
  26. minProperties: {
  27. type: "integer",
  28. minimum: 0
  29. },
  30. consistent: {
  31. type: "boolean"
  32. }
  33. },
  34. additionalProperties: false,
  35. minProperties: 1
  36. }
  37. ]
  38. };
  39. /**
  40. * Normalizes a given option value.
  41. *
  42. * @param {string|Object|undefined} value - An option value to parse.
  43. * @returns {{multiline: boolean, minProperties: number, consistent: boolean}} Normalized option object.
  44. */
  45. function normalizeOptionValue(value) {
  46. let multiline = false;
  47. let minProperties = Number.POSITIVE_INFINITY;
  48. let consistent = false;
  49. if (value) {
  50. if (value === "always") {
  51. minProperties = 0;
  52. } else if (value === "never") {
  53. minProperties = Number.POSITIVE_INFINITY;
  54. } else {
  55. multiline = Boolean(value.multiline);
  56. minProperties = value.minProperties || Number.POSITIVE_INFINITY;
  57. consistent = Boolean(value.consistent);
  58. }
  59. } else {
  60. consistent = true;
  61. }
  62. return { multiline, minProperties, consistent };
  63. }
  64. /**
  65. * Normalizes a given option value.
  66. *
  67. * @param {string|Object|undefined} options - An option value to parse.
  68. * @returns {{
  69. * ObjectExpression: {multiline: boolean, minProperties: number, consistent: boolean},
  70. * ObjectPattern: {multiline: boolean, minProperties: number, consistent: boolean},
  71. * ImportDeclaration: {multiline: boolean, minProperties: number, consistent: boolean},
  72. * ExportNamedDeclaration : {multiline: boolean, minProperties: number, consistent: boolean}
  73. * }} Normalized option object.
  74. */
  75. function normalizeOptions(options) {
  76. const isNodeSpecificOption = lodash.overSome([lodash.isPlainObject, lodash.isString]);
  77. if (lodash.isPlainObject(options) && lodash.some(options, isNodeSpecificOption)) {
  78. return {
  79. ObjectExpression: normalizeOptionValue(options.ObjectExpression),
  80. ObjectPattern: normalizeOptionValue(options.ObjectPattern),
  81. ImportDeclaration: normalizeOptionValue(options.ImportDeclaration),
  82. ExportNamedDeclaration: normalizeOptionValue(options.ExportDeclaration)
  83. };
  84. }
  85. const value = normalizeOptionValue(options);
  86. return { ObjectExpression: value, ObjectPattern: value, ImportDeclaration: value, ExportNamedDeclaration: value };
  87. }
  88. /**
  89. * Determines if ObjectExpression, ObjectPattern, ImportDeclaration or ExportNamedDeclaration
  90. * node needs to be checked for missing line breaks
  91. *
  92. * @param {ASTNode} node - Node under inspection
  93. * @param {Object} options - option specific to node type
  94. * @param {Token} first - First object property
  95. * @param {Token} last - Last object property
  96. * @returns {boolean} `true` if node needs to be checked for missing line breaks
  97. */
  98. function areLineBreaksRequired(node, options, first, last) {
  99. let objectProperties;
  100. if (node.type === "ObjectExpression" || node.type === "ObjectPattern") {
  101. objectProperties = node.properties;
  102. } else {
  103. // is ImportDeclaration or ExportNamedDeclaration
  104. objectProperties = node.specifiers
  105. .filter(s => s.type === "ImportSpecifier" || s.type === "ExportSpecifier");
  106. }
  107. return objectProperties.length >= options.minProperties ||
  108. (
  109. options.multiline &&
  110. objectProperties.length > 0 &&
  111. first.loc.start.line !== last.loc.end.line
  112. );
  113. }
  114. //------------------------------------------------------------------------------
  115. // Rule Definition
  116. //------------------------------------------------------------------------------
  117. module.exports = {
  118. meta: {
  119. type: "layout",
  120. docs: {
  121. description: "enforce consistent line breaks inside braces",
  122. category: "Stylistic Issues",
  123. recommended: false,
  124. url: "https://eslint.org/docs/rules/object-curly-newline"
  125. },
  126. fixable: "whitespace",
  127. schema: [
  128. {
  129. oneOf: [
  130. OPTION_VALUE,
  131. {
  132. type: "object",
  133. properties: {
  134. ObjectExpression: OPTION_VALUE,
  135. ObjectPattern: OPTION_VALUE,
  136. ImportDeclaration: OPTION_VALUE,
  137. ExportDeclaration: OPTION_VALUE
  138. },
  139. additionalProperties: false,
  140. minProperties: 1
  141. }
  142. ]
  143. }
  144. ]
  145. },
  146. create(context) {
  147. const sourceCode = context.getSourceCode();
  148. const normalizedOptions = normalizeOptions(context.options[0]);
  149. /**
  150. * Reports a given node if it violated this rule.
  151. * @param {ASTNode} node - A node to check. This is an ObjectExpression, ObjectPattern, ImportDeclaration or ExportNamedDeclaration node.
  152. * @param {{multiline: boolean, minProperties: number, consistent: boolean}} options - An option object.
  153. * @returns {void}
  154. */
  155. function check(node) {
  156. const options = normalizedOptions[node.type];
  157. if (
  158. (node.type === "ImportDeclaration" &&
  159. !node.specifiers.some(specifier => specifier.type === "ImportSpecifier")) ||
  160. (node.type === "ExportNamedDeclaration" &&
  161. !node.specifiers.some(specifier => specifier.type === "ExportSpecifier"))
  162. ) {
  163. return;
  164. }
  165. const openBrace = sourceCode.getFirstToken(node, token => token.value === "{");
  166. let closeBrace;
  167. if (node.typeAnnotation) {
  168. closeBrace = sourceCode.getTokenBefore(node.typeAnnotation);
  169. } else {
  170. closeBrace = sourceCode.getLastToken(node, token => token.value === "}");
  171. }
  172. let first = sourceCode.getTokenAfter(openBrace, { includeComments: true });
  173. let last = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
  174. const needsLineBreaks = areLineBreaksRequired(node, options, first, last);
  175. const hasCommentsFirstToken = astUtils.isCommentToken(first);
  176. const hasCommentsLastToken = astUtils.isCommentToken(last);
  177. /*
  178. * Use tokens or comments to check multiline or not.
  179. * But use only tokens to check whether line breaks are needed.
  180. * This allows:
  181. * var obj = { // eslint-disable-line foo
  182. * a: 1
  183. * }
  184. */
  185. first = sourceCode.getTokenAfter(openBrace);
  186. last = sourceCode.getTokenBefore(closeBrace);
  187. if (needsLineBreaks) {
  188. if (astUtils.isTokenOnSameLine(openBrace, first)) {
  189. context.report({
  190. message: "Expected a line break after this opening brace.",
  191. node,
  192. loc: openBrace.loc.start,
  193. fix(fixer) {
  194. if (hasCommentsFirstToken) {
  195. return null;
  196. }
  197. return fixer.insertTextAfter(openBrace, "\n");
  198. }
  199. });
  200. }
  201. if (astUtils.isTokenOnSameLine(last, closeBrace)) {
  202. context.report({
  203. message: "Expected a line break before this closing brace.",
  204. node,
  205. loc: closeBrace.loc.start,
  206. fix(fixer) {
  207. if (hasCommentsLastToken) {
  208. return null;
  209. }
  210. return fixer.insertTextBefore(closeBrace, "\n");
  211. }
  212. });
  213. }
  214. } else {
  215. const consistent = options.consistent;
  216. const hasLineBreakBetweenOpenBraceAndFirst = !astUtils.isTokenOnSameLine(openBrace, first);
  217. const hasLineBreakBetweenCloseBraceAndLast = !astUtils.isTokenOnSameLine(last, closeBrace);
  218. if (
  219. (!consistent && hasLineBreakBetweenOpenBraceAndFirst) ||
  220. (consistent && hasLineBreakBetweenOpenBraceAndFirst && !hasLineBreakBetweenCloseBraceAndLast)
  221. ) {
  222. context.report({
  223. message: "Unexpected line break after this opening brace.",
  224. node,
  225. loc: openBrace.loc.start,
  226. fix(fixer) {
  227. if (hasCommentsFirstToken) {
  228. return null;
  229. }
  230. return fixer.removeRange([
  231. openBrace.range[1],
  232. first.range[0]
  233. ]);
  234. }
  235. });
  236. }
  237. if (
  238. (!consistent && hasLineBreakBetweenCloseBraceAndLast) ||
  239. (consistent && !hasLineBreakBetweenOpenBraceAndFirst && hasLineBreakBetweenCloseBraceAndLast)
  240. ) {
  241. context.report({
  242. message: "Unexpected line break before this closing brace.",
  243. node,
  244. loc: closeBrace.loc.start,
  245. fix(fixer) {
  246. if (hasCommentsLastToken) {
  247. return null;
  248. }
  249. return fixer.removeRange([
  250. last.range[1],
  251. closeBrace.range[0]
  252. ]);
  253. }
  254. });
  255. }
  256. }
  257. }
  258. return {
  259. ObjectExpression: check,
  260. ObjectPattern: check,
  261. ImportDeclaration: check,
  262. ExportNamedDeclaration: check
  263. };
  264. }
  265. };