Skip to content

Commit ffdf69a

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

File tree

3 files changed

+159
-11
lines changed

3 files changed

+159
-11
lines changed

lib/rules/jsx-indent.js

+41-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' || node.type === 'Literal') {
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
@@ -290,6 +297,29 @@ module.exports = {
290297
}
291298
}
292299

300+
/**
301+
* Check indent for Literal Node or JSXText Node
302+
* @param {ASTNode} node The node to check
303+
* @param {Number} indent needed indent
304+
*/
305+
function checkLiteralNodeIndent(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+
match => (match[1] ? match[1].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);
@@ -340,6 +370,14 @@ module.exports = {
340370
checkNodesIndent(firstInLine, indent);
341371
}
342372

373+
function handleLiteral(node) {
374+
if (!node.parent) {
375+
return;
376+
}
377+
const parentNodeIndent = getNodeIndent(node.parent);
378+
checkLiteralNodeIndent(node, parentNodeIndent + indentSize);
379+
}
380+
343381
return {
344382
JSXOpeningElement: handleOpeningElement,
345383
JSXOpeningFragment: handleOpeningElement,
@@ -352,7 +390,9 @@ module.exports = {
352390
}
353391
const parentNodeIndent = getNodeIndent(node.parent);
354392
checkNodesIndent(node, parentNodeIndent + indentSize);
355-
}
393+
},
394+
Literal: handleLiteral,
395+
JSXText: handleLiteral
356396
};
357397
}
358398
};

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
"object.fromentries": "^2.0.2",
3737
"object.values": "^1.1.1",
3838
"prop-types": "^15.7.2",
39-
"resolve": "^1.14.2"
39+
"resolve": "^1.14.2",
40+
"string.prototype.matchall": "^4.0.2"
4041
},
4142
"devDependencies": {
4243
"@types/eslint": "^6.1.3",

tests/lib/rules/jsx-indent.js

+116-9
Original file line numberDiff line numberDiff line change
@@ -300,22 +300,21 @@ 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
}, {
313312
code: [
314313
'<>',
315-
'bar <>',
316-
' bar',
317-
' bar {foo}',
318-
'bar </>',
314+
' bar <>',
315+
' bar',
316+
' bar {foo}',
317+
' bar </>',
319318
'</>'
320319
].join('\n'),
321320
parser: parsers.BABEL_ESLINT
@@ -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,69 @@ 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+
output: [
1936+
'<div>',
1937+
' text',
1938+
'</div>'
1939+
].join('\n'),
1940+
errors: [
1941+
{message: 'Expected indentation of 4 space characters but found 0.'}
1942+
]
1943+
}, {
1944+
code: [
1945+
'<div>',
1946+
' text',
1947+
'text',
1948+
'</div>'
1949+
].join('\n'),
1950+
output: [
1951+
'<div>',
1952+
' text',
1953+
' text',
1954+
'</div>'
1955+
].join('\n'),
1956+
errors: [
1957+
{message: 'Expected indentation of 4 space characters but found 2.'},
1958+
{message: 'Expected indentation of 4 space characters but found 0.'}
1959+
]
1960+
}, {
1961+
code: [
1962+
'<div>',
1963+
'\t text',
1964+
' \t text',
1965+
'</div>'
1966+
].join('\n'),
1967+
output: [
1968+
'<div>',
1969+
' text',
1970+
' text',
1971+
'</div>'
1972+
].join('\n'),
1973+
errors: [
1974+
{message: 'Expected indentation of 4 space characters but found 0.'},
1975+
{message: 'Expected indentation of 4 space characters but found 2.'}
1976+
]
1977+
}, {
1978+
code: [
1979+
'<div>',
1980+
'\t\ttext',
1981+
'</div>'
1982+
].join('\n'),
1983+
parser: parsers.BABEL_ESLINT,
1984+
options: ['tab'],
1985+
output: [
1986+
'<div>',
1987+
'\ttext',
1988+
'</div>'
1989+
].join('\n'),
1990+
errors: [
1991+
{message: 'Expected indentation of 1 tab character but found 2.'}
1992+
]
18861993
}]
18871994
});

0 commit comments

Comments
 (0)