Skip to content

Commit 137abcd

Browse files
toshi-tomaljharb
authored andcommitted
[Fix] jsx-indent: Does not check indents for JSXText
Fixes #2467. Fixes #2484. Fixes #1136.
1 parent e69b113 commit 137abcd

File tree

3 files changed

+129
-7
lines changed

3 files changed

+129
-7
lines changed

lib/rules/jsx-indent.js

+38-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
'use strict';
3232

33+
const matchAll = require('string.prototype.matchall');
34+
3335
const astUtil = require('../util/ast');
3436
const docsUrl = require('../util/docsUrl');
3537

@@ -97,6 +99,11 @@ module.exports = {
9799
function getFixerFunction(node, needed) {
98100
return function fix(fixer) {
99101
const indent = Array(needed + 1).join(indentChar);
102+
if (node.type === 'JSXText') {
103+
const regExp = /\n[\t ]*(\S)/g;
104+
const fixedText = node.raw.replace(regExp, (match, p1) => `\n${indent}${p1}`);
105+
return fixer.replaceText(node, fixedText);
106+
}
100107
return fixer.replaceTextRange(
101108
[node.range[0] - node.loc.start.column, node.range[0]],
102109
indent
@@ -108,7 +115,7 @@ module.exports = {
108115
* Reports a given indent violation and properly pluralizes the message
109116
* @param {ASTNode} node Node violating the indent rule
110117
* @param {Number} needed Expected indentation character count
111-
* @param {Number} gotten Indentation character count in the actual node/code
118+
* @param {Number|Array<number>} gotten Indentation character count in the actual node/code
112119
* @param {Object} [loc] Error line and column location
113120
*/
114121
function report(node, needed, gotten, loc) {
@@ -290,6 +297,29 @@ module.exports = {
290297
}
291298
}
292299

300+
/**
301+
* Check indent for JSXText
302+
* @param {ASTNode} node The node to check
303+
* @param {Number} indent needed indent
304+
*/
305+
function checkJSXTextNodeIndent(node, indent) {
306+
const value = node.value;
307+
const regExp = indentType === 'space' ? /\n( *)[\t ]*\S/g : /\n(\t*)[\t ]*\S/g;
308+
const nodeIndentsPerLine = Array.from(
309+
matchAll(value, regExp),
310+
([, firstMatch]) => (firstMatch ? firstMatch.length : 0)
311+
);
312+
const hasFirstInLineNode = nodeIndentsPerLine.length > 0;
313+
if (
314+
hasFirstInLineNode &&
315+
!nodeIndentsPerLine.every(actualIndent => actualIndent === indent)
316+
) {
317+
nodeIndentsPerLine.forEach((nodeIndent) => {
318+
report(node, indent, nodeIndent);
319+
});
320+
}
321+
}
322+
293323
function handleOpeningElement(node) {
294324
const sourceCode = context.getSourceCode();
295325
let prevToken = sourceCode.getTokenBefore(node);
@@ -352,6 +382,13 @@ module.exports = {
352382
}
353383
const parentNodeIndent = getNodeIndent(node.parent);
354384
checkNodesIndent(node, parentNodeIndent + indentSize);
385+
},
386+
JSXText(node) {
387+
if (!node.parent) {
388+
return;
389+
}
390+
const parentNodeIndent = getNodeIndent(node.parent);
391+
checkJSXTextNodeIndent(node, parentNodeIndent + indentSize);
355392
}
356393
};
357394
}

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"object.fromentries": "^2.0.2",
3535
"object.values": "^1.1.1",
3636
"prop-types": "^15.7.2",
37-
"resolve": "^1.14.2"
37+
"resolve": "^1.14.2",
38+
"string.prototype.matchall": "^4.0.2"
3839
},
3940
"devDependencies": {
4041
"@types/eslint": "^6.1.3",

tests/lib/rules/jsx-indent.js

+89-5
Original file line numberDiff line numberDiff line change
@@ -300,13 +300,12 @@ ruleTester.run('jsx-indent', rule, {
300300
].join('\n'),
301301
parser: parsers.BABEL_ESLINT
302302
}, {
303-
// Literals indentation is not touched
304303
code: [
305304
'<div>',
306-
'bar <div>',
307-
' bar',
308-
' bar {foo}',
309-
'bar </div>',
305+
' bar <div>',
306+
' bar',
307+
' bar {foo}',
308+
' bar </div>',
310309
'</div>'
311310
].join('\n')
312311
}, {
@@ -956,9 +955,53 @@ const Component = () => (
956955
}
957956
`,
958957
options: [2, {indentLogicalExpressions: true}]
958+
}, {
959+
code: [
960+
'<App>',
961+
' text',
962+
'</App>'
963+
].join('\n')
964+
}, {
965+
code: [
966+
'<App>',
967+
' text',
968+
' text',
969+
' text',
970+
'</App>'
971+
].join('\n')
972+
}, {
973+
code: [
974+
'<App>',
975+
'\ttext',
976+
'</App>'
977+
].join('\n'),
978+
options: ['tab']
959979
}],
960980

961981
invalid: [{
982+
code: [
983+
'<div>',
984+
'bar <div>',
985+
' bar',
986+
' bar {foo}',
987+
' bar </div>',
988+
'</div>'
989+
].join('\n'),
990+
output: [
991+
'<div>',
992+
' bar <div>',
993+
' bar',
994+
' bar {foo}',
995+
' bar </div>',
996+
'</div>'
997+
].join('\n'),
998+
errors: [
999+
{message: 'Expected indentation of 4 space characters but found 0.'},
1000+
{message: 'Expected indentation of 4 space characters but found 3.'},
1001+
{message: 'Expected indentation of 4 space characters but found 3.'},
1002+
{message: 'Expected indentation of 4 space characters but found 3.'}
1003+
]
1004+
}, {
9621005
code: [
9631006
'<App>',
9641007
' <Foo />',
@@ -1883,5 +1926,46 @@ const Component = () => (
18831926
errors: [
18841927
{message: 'Expected indentation of 8 space characters but found 4.'}
18851928
]
1929+
}, {
1930+
code: [
1931+
'<div>',
1932+
'text',
1933+
'</div>'
1934+
].join('\n'),
1935+
errors: [
1936+
{message: 'Expected indentation of 4 space characters but found 0.'}
1937+
]
1938+
}, {
1939+
code: [
1940+
'<div>',
1941+
' text',
1942+
'text',
1943+
'</div>'
1944+
].join('\n'),
1945+
errors: [
1946+
{message: 'Expected indentation of 4 space characters but found 2.'},
1947+
{message: 'Expected indentation of 4 space characters but found 0.'}
1948+
]
1949+
}, {
1950+
code: [
1951+
'<div>',
1952+
'\t text',
1953+
' \t text',
1954+
'</div>'
1955+
].join('\n'),
1956+
errors: [
1957+
{message: 'Expected indentation of 4 space characters but found 0.'},
1958+
{message: 'Expected indentation of 4 space characters but found 2.'}
1959+
]
1960+
}, {
1961+
code: [
1962+
'<div>',
1963+
'\t\ttext',
1964+
'</div>'
1965+
].join('\n'),
1966+
options: ['tab'],
1967+
errors: [
1968+
{message: 'Expected indentation of 1 tab character but found 2.'}
1969+
]
18861970
}]
18871971
});

0 commit comments

Comments
 (0)