jsx-first-prop-new-line.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. /**
  2. * @fileoverview Ensure proper position of the first property in JSX
  3. * @author Joachim Seminck
  4. */
  5. 'use strict';
  6. const docsUrl = require('../util/docsUrl');
  7. const report = require('../util/report');
  8. const propsUtil = require('../util/props');
  9. // ------------------------------------------------------------------------------
  10. // Rule Definition
  11. // ------------------------------------------------------------------------------
  12. const messages = {
  13. propOnNewLine: 'Property should be placed on a new line',
  14. propOnSameLine: 'Property should be placed on the same line as the component declaration',
  15. };
  16. /** @type {import('eslint').Rule.RuleModule} */
  17. module.exports = {
  18. meta: {
  19. docs: {
  20. description: 'Enforce proper position of the first property in JSX',
  21. category: 'Stylistic Issues',
  22. recommended: false,
  23. url: docsUrl('jsx-first-prop-new-line'),
  24. },
  25. fixable: 'code',
  26. messages,
  27. schema: [{
  28. enum: ['always', 'never', 'multiline', 'multiline-multiprop', 'multiprop'],
  29. }],
  30. },
  31. create(context) {
  32. const configuration = context.options[0] || 'multiline-multiprop';
  33. function isMultilineJSX(jsxNode) {
  34. return jsxNode.loc.start.line < jsxNode.loc.end.line;
  35. }
  36. return {
  37. JSXOpeningElement(node) {
  38. if (
  39. (configuration === 'multiline' && isMultilineJSX(node))
  40. || (configuration === 'multiline-multiprop' && isMultilineJSX(node) && node.attributes.length > 1)
  41. || (configuration === 'multiprop' && node.attributes.length > 1)
  42. || (configuration === 'always')
  43. ) {
  44. node.attributes.some((decl) => {
  45. if (decl.loc.start.line === node.loc.start.line) {
  46. report(context, messages.propOnNewLine, 'propOnNewLine', {
  47. node: decl,
  48. fix(fixer) {
  49. const nodeTypeArguments = propsUtil.getTypeArguments(node);
  50. return fixer.replaceTextRange([(nodeTypeArguments || node.name).range[1], decl.range[0]], '\n');
  51. },
  52. });
  53. }
  54. return true;
  55. });
  56. } else if (
  57. (configuration === 'never' && node.attributes.length > 0)
  58. || (configuration === 'multiprop' && isMultilineJSX(node) && node.attributes.length <= 1)
  59. ) {
  60. const firstNode = node.attributes[0];
  61. if (node.loc.start.line < firstNode.loc.start.line) {
  62. report(context, messages.propOnSameLine, 'propOnSameLine', {
  63. node: firstNode,
  64. fix(fixer) {
  65. return fixer.replaceTextRange([node.name.range[1], firstNode.range[0]], ' ');
  66. },
  67. });
  68. }
  69. }
  70. },
  71. };
  72. },
  73. };