Skip to content

Commit 078cf1a

Browse files
committed
Support aligned indent for JSX props
This is the default style used by the JetBrains IDEs. closes jsx-eslint#398
1 parent 5890533 commit 078cf1a

File tree

2 files changed

+142
-4
lines changed

2 files changed

+142
-4
lines changed

lib/rules/jsx-indent-props.js

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ module.exports = {
4747

4848
schema: [{
4949
oneOf: [{
50-
enum: ['tab']
50+
enum: ['tab', 'aligned']
5151
}, {
5252
type: 'integer'
5353
}]
@@ -64,7 +64,10 @@ module.exports = {
6464
const sourceCode = context.getSourceCode();
6565

6666
if (context.options.length) {
67-
if (context.options[0] === 'tab') {
67+
if (context.options[0] === 'aligned') {
68+
indentSize = 'aligned';
69+
indentType = 'space';
70+
} else if (context.options[0] === 'tab') {
6871
indentSize = 1;
6972
indentType = 'tab';
7073
} else if (typeof context.options[0] === 'number') {
@@ -160,8 +163,33 @@ module.exports = {
160163

161164
return {
162165
JSXOpeningElement: function(node) {
163-
const elementIndent = getNodeIndent(node);
164-
checkNodesIndent(node.attributes, elementIndent + indentSize);
166+
if (!node.attributes.length) {
167+
return;
168+
}
169+
let propIndent;
170+
if (indentSize === 'aligned') {
171+
const firstPropNode = node.attributes[0];
172+
// '<' and any space following it
173+
const beforeNameLength = node.name.range[0] - node.range[0];
174+
// the name itself
175+
const nameLength = node.name.range[1] - node.name.range[0];
176+
// any space between the name and the first attribute
177+
const afterNameLength = Math.max(1, firstPropNode.loc.start.column - node.name.loc.end.column);
178+
if (afterNameLength !== 1) {
179+
// this is not easily fixable, so we just report another error for it.
180+
// it should be unlikely enough to be triggered anyway
181+
context.report({
182+
node: node,
183+
message: 'Found too much whitespace between tag name and first attribute.'
184+
});
185+
}
186+
// use the indentation of the element relative to the beginning of the line
187+
propIndent = node.loc.start.column + beforeNameLength + nameLength + afterNameLength;
188+
} else {
189+
const elementIndent = getNodeIndent(node);
190+
propIndent = elementIndent + indentSize;
191+
}
192+
checkNodesIndent(node.attributes, propIndent);
165193
}
166194
};
167195
}

tests/lib/rules/jsx-indent-props.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,51 @@ ruleTester.run('jsx-indent-props', rule, {
5959
'/>'
6060
].join('\n'),
6161
options: ['tab']
62+
}, {
63+
code: [
64+
'<App/>'
65+
].join('\n'),
66+
options: ['aligned']
67+
}, {
68+
code: [
69+
'<App aaa',
70+
' b',
71+
' cc',
72+
'/>'
73+
].join('\n'),
74+
options: ['aligned']
75+
}, {
76+
code: [
77+
'const test = <App aaa',
78+
' b',
79+
' cc',
80+
' />'
81+
].join('\n'),
82+
options: ['aligned']
83+
}, {
84+
code: [
85+
'<App aaa',
86+
' b',
87+
'>',
88+
' <Child c',
89+
' d/>',
90+
'</App>'
91+
].join('\n'),
92+
options: ['aligned']
93+
}, {
94+
code: [
95+
'<Fragment>',
96+
' <App aaa',
97+
' b',
98+
' cc',
99+
' />',
100+
' <OtherApp a',
101+
' bbb',
102+
' c',
103+
' />',
104+
'</Fragment>'
105+
].join('\n'),
106+
options: ['aligned']
62107
}],
63108

64109
invalid: [{
@@ -112,5 +157,70 @@ ruleTester.run('jsx-indent-props', rule, {
112157
].join('\n'),
113158
options: ['tab'],
114159
errors: [{message: 'Expected indentation of 1 tab character but found 3.'}]
160+
}, {
161+
code: [
162+
'<App a',
163+
' b',
164+
'/>'
165+
].join('\n'),
166+
output: [
167+
'<App a',
168+
' b',
169+
'/>'
170+
].join('\n'),
171+
options: ['aligned'],
172+
errors: [{message: 'Expected indentation of 5 space characters but found 2.'}]
173+
}, {
174+
code: [
175+
'<App a',
176+
' b',
177+
'/>'
178+
].join('\n'),
179+
output: [
180+
'<App a',
181+
' b',
182+
'/>'
183+
].join('\n'),
184+
options: ['aligned'],
185+
errors: [
186+
{message: 'Found too much whitespace between tag name and first attribute.'},
187+
{message: 'Expected indentation of 6 space characters but found 3.'}
188+
]
189+
}, {
190+
code: [
191+
'<App',
192+
' a',
193+
' b',
194+
'/>'
195+
].join('\n'),
196+
output: [
197+
'<App',
198+
' a',
199+
' b',
200+
'/>'
201+
].join('\n'),
202+
options: ['aligned'],
203+
errors: [
204+
{message: 'Found too much whitespace between tag name and first attribute.'},
205+
{message: 'Expected indentation of 6 space characters but found 3.'}
206+
]
207+
}, {
208+
code: [
209+
'<App',
210+
' a',
211+
' b',
212+
'/>'
213+
].join('\n'),
214+
output: [
215+
'<App',
216+
' a',
217+
' b',
218+
'/>'
219+
].join('\n'),
220+
options: ['aligned'],
221+
errors: [
222+
{message: 'Expected indentation of 5 space characters but found 2.'},
223+
{message: 'Expected indentation of 5 space characters but found 2.'}
224+
]
115225
}]
116226
});

0 commit comments

Comments
 (0)