项目原始demo,不改动
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
Ce dépôt est archivé. Vous pouvez voir les fichiers et le cloner, mais vous ne pouvez pas pousser ni ouvrir de ticket/demande d'ajout.
 
 
 
 

300 lignes
11 KiB

  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("../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. multiline = 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. docs: {
  120. description: "enforce consistent line breaks inside braces",
  121. category: "Stylistic Issues",
  122. recommended: false,
  123. url: "https://eslint.org/docs/rules/object-curly-newline"
  124. },
  125. fixable: "whitespace",
  126. schema: [
  127. {
  128. oneOf: [
  129. OPTION_VALUE,
  130. {
  131. type: "object",
  132. properties: {
  133. ObjectExpression: OPTION_VALUE,
  134. ObjectPattern: OPTION_VALUE,
  135. ImportDeclaration: OPTION_VALUE,
  136. ExportDeclaration: OPTION_VALUE
  137. },
  138. additionalProperties: false,
  139. minProperties: 1
  140. }
  141. ]
  142. }
  143. ]
  144. },
  145. create(context) {
  146. const sourceCode = context.getSourceCode();
  147. const normalizedOptions = normalizeOptions(context.options[0]);
  148. /**
  149. * Reports a given node if it violated this rule.
  150. * @param {ASTNode} node - A node to check. This is an ObjectExpression, ObjectPattern, ImportDeclaration or ExportNamedDeclaration node.
  151. * @param {{multiline: boolean, minProperties: number, consistent: boolean}} options - An option object.
  152. * @returns {void}
  153. */
  154. function check(node) {
  155. const options = normalizedOptions[node.type];
  156. if (
  157. (node.type === "ImportDeclaration" &&
  158. !node.specifiers.some(specifier => specifier.type === "ImportSpecifier")) ||
  159. (node.type === "ExportNamedDeclaration" &&
  160. !node.specifiers.some(specifier => specifier.type === "ExportSpecifier"))
  161. ) {
  162. return;
  163. }
  164. const openBrace = sourceCode.getFirstToken(node, token => token.value === "{");
  165. let closeBrace;
  166. if (node.typeAnnotation) {
  167. closeBrace = sourceCode.getTokenBefore(node.typeAnnotation);
  168. } else {
  169. closeBrace = sourceCode.getLastToken(node, token => token.value === "}");
  170. }
  171. let first = sourceCode.getTokenAfter(openBrace, { includeComments: true });
  172. let last = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
  173. const needsLineBreaks = areLineBreaksRequired(node, options, first, last);
  174. const hasCommentsFirstToken = astUtils.isCommentToken(first);
  175. const hasCommentsLastToken = astUtils.isCommentToken(last);
  176. /*
  177. * Use tokens or comments to check multiline or not.
  178. * But use only tokens to check whether line breaks are needed.
  179. * This allows:
  180. * var obj = { // eslint-disable-line foo
  181. * a: 1
  182. * }
  183. */
  184. first = sourceCode.getTokenAfter(openBrace);
  185. last = sourceCode.getTokenBefore(closeBrace);
  186. if (needsLineBreaks) {
  187. if (astUtils.isTokenOnSameLine(openBrace, first)) {
  188. context.report({
  189. message: "Expected a line break after this opening brace.",
  190. node,
  191. loc: openBrace.loc.start,
  192. fix(fixer) {
  193. if (hasCommentsFirstToken) {
  194. return null;
  195. }
  196. return fixer.insertTextAfter(openBrace, "\n");
  197. }
  198. });
  199. }
  200. if (astUtils.isTokenOnSameLine(last, closeBrace)) {
  201. context.report({
  202. message: "Expected a line break before this closing brace.",
  203. node,
  204. loc: closeBrace.loc.start,
  205. fix(fixer) {
  206. if (hasCommentsLastToken) {
  207. return null;
  208. }
  209. return fixer.insertTextBefore(closeBrace, "\n");
  210. }
  211. });
  212. }
  213. } else {
  214. const consistent = options.consistent;
  215. const hasLineBreakBetweenOpenBraceAndFirst = !astUtils.isTokenOnSameLine(openBrace, first);
  216. const hasLineBreakBetweenCloseBraceAndLast = !astUtils.isTokenOnSameLine(last, closeBrace);
  217. if (
  218. (!consistent && hasLineBreakBetweenOpenBraceAndFirst) ||
  219. (consistent && hasLineBreakBetweenOpenBraceAndFirst && !hasLineBreakBetweenCloseBraceAndLast)
  220. ) {
  221. context.report({
  222. message: "Unexpected line break after this opening brace.",
  223. node,
  224. loc: openBrace.loc.start,
  225. fix(fixer) {
  226. if (hasCommentsFirstToken) {
  227. return null;
  228. }
  229. return fixer.removeRange([
  230. openBrace.range[1],
  231. first.range[0]
  232. ]);
  233. }
  234. });
  235. }
  236. if (
  237. (!consistent && hasLineBreakBetweenCloseBraceAndLast) ||
  238. (consistent && !hasLineBreakBetweenOpenBraceAndFirst && hasLineBreakBetweenCloseBraceAndLast)
  239. ) {
  240. context.report({
  241. message: "Unexpected line break before this closing brace.",
  242. node,
  243. loc: closeBrace.loc.start,
  244. fix(fixer) {
  245. if (hasCommentsLastToken) {
  246. return null;
  247. }
  248. return fixer.removeRange([
  249. last.range[1],
  250. closeBrace.range[0]
  251. ]);
  252. }
  253. });
  254. }
  255. }
  256. }
  257. return {
  258. ObjectExpression: check,
  259. ObjectPattern: check,
  260. ImportDeclaration: check,
  261. ExportNamedDeclaration: check
  262. };
  263. }
  264. };