jsx-newline.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /**
  2. * @fileoverview Require or prevent a new line after jsx elements and expressions.
  3. * @author Johnny Zabala
  4. * @author Joseph Stiles
  5. */
  6. 'use strict';
  7. const docsUrl = require('../util/docsUrl');
  8. // ------------------------------------------------------------------------------
  9. // Rule Definition
  10. // ------------------------------------------------------------------------------
  11. module.exports = {
  12. meta: {
  13. docs: {
  14. description: 'Require or prevent a new line after jsx elements and expressions.',
  15. category: 'Stylistic Issues',
  16. recommended: false,
  17. url: docsUrl('jsx-newline')
  18. },
  19. fixable: 'code',
  20. messages: {
  21. require: 'JSX element should start in a new line',
  22. prevent: 'JSX element should not start in a new line'
  23. },
  24. schema: [
  25. {
  26. type: 'object',
  27. properties: {
  28. prevent: {
  29. default: false,
  30. type: 'boolean'
  31. }
  32. },
  33. additionalProperties: false
  34. }
  35. ]
  36. },
  37. create(context) {
  38. const jsxElementParents = new Set();
  39. const sourceCode = context.getSourceCode();
  40. return {
  41. 'Program:exit'() {
  42. jsxElementParents.forEach((parent) => {
  43. parent.children.forEach((element, index, elements) => {
  44. if (element.type === 'JSXElement' || element.type === 'JSXExpressionContainer') {
  45. const firstAdjacentSibling = elements[index + 1];
  46. const secondAdjacentSibling = elements[index + 2];
  47. const hasSibling = firstAdjacentSibling
  48. && secondAdjacentSibling
  49. && (firstAdjacentSibling.type === 'Literal' || firstAdjacentSibling.type === 'JSXText');
  50. if (!hasSibling) return;
  51. // Check adjacent sibling has the proper amount of newlines
  52. const isWithoutNewLine = !/\n\s*\n/.test(firstAdjacentSibling.value);
  53. const prevent = !!(context.options[0] || {}).prevent;
  54. if (isWithoutNewLine === prevent) return;
  55. const messageId = prevent
  56. ? 'prevent'
  57. : 'require';
  58. const regex = prevent
  59. ? /(\n\n)(?!.*\1)/g
  60. : /(\n)(?!.*\1)/g;
  61. const replacement = prevent
  62. ? '\n'
  63. : '\n\n';
  64. context.report({
  65. node: secondAdjacentSibling,
  66. messageId,
  67. fix(fixer) {
  68. return fixer.replaceText(
  69. firstAdjacentSibling,
  70. // double or remove the last newline
  71. sourceCode.getText(firstAdjacentSibling)
  72. .replace(regex, replacement)
  73. );
  74. }
  75. });
  76. }
  77. });
  78. });
  79. },
  80. ':matches(JSXElement, JSXFragment) > :matches(JSXElement, JSXExpressionContainer)': (node) => {
  81. jsxElementParents.add(node.parent);
  82. }
  83. };
  84. }
  85. };