Skip to content

Commit 55d8a53

Browse files
tanmoyopenrootadidahiya
authored andcommitted
feat: add jsx-whitespace-literal rule (#243)
1 parent 6f907b5 commit 55d8a53

File tree

4 files changed

+178
-0
lines changed

4 files changed

+178
-0
lines changed

src/rules/jsxWhitespaceLiteralRule.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @license
3+
* Copyright 2017 Palantir Technologies, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import * as Lint from "tslint";
19+
import { isJsxText } from "tsutils/typeguard/3.0";
20+
import * as ts from "typescript";
21+
22+
const RESERVED_ENTITY = " ";
23+
24+
export class Rule extends Lint.Rules.AbstractRule {
25+
public static metadata: Lint.IRuleMetadata = {
26+
description: Lint.Utils.dedent
27+
`Warn if ' ' is used in JXS markup. Prefer {" "} over ' '`,
28+
optionExamples: ["true"],
29+
options: null,
30+
optionsDescription: "",
31+
ruleName: "jsx-whitespace-literal",
32+
type: "functionality",
33+
typescriptOnly: false,
34+
};
35+
36+
public static FAILURE_STRING = `Expected '{" "}' instead of ' ' in JSX markup`;
37+
38+
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
39+
return this.applyWithFunction(sourceFile, walk);
40+
}
41+
}
42+
43+
function getSpaces(numOfSpaces: number): string {
44+
return " ".repeat(numOfSpaces);
45+
}
46+
47+
function walk(ctx: Lint.WalkContext<void>): void {
48+
return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
49+
if (isJsxText(node)) {
50+
if (node.getText().indexOf(RESERVED_ENTITY) > -1) {
51+
const text: string = node.getText();
52+
const regex: RegExp = new RegExp(RESERVED_ENTITY, "g");
53+
const startIndices: number[] = [];
54+
const endIndices: number[] = [];
55+
let countEnitiy: number = -1;
56+
let result: RegExpExecArray | null;
57+
58+
do {
59+
result = regex.exec(text);
60+
if (result !== null) {
61+
if (
62+
startIndices[countEnitiy] !== undefined &&
63+
endIndices[countEnitiy] !== undefined &&
64+
startIndices[countEnitiy] + endIndices[countEnitiy] === result.index
65+
) {
66+
endIndices[countEnitiy] = endIndices[countEnitiy] + RESERVED_ENTITY.length;
67+
} else {
68+
startIndices.push(result.index);
69+
endIndices.push(RESERVED_ENTITY.length);
70+
countEnitiy += 1;
71+
}
72+
}
73+
} while (result !== null);
74+
75+
startIndices.forEach((startIndex, index) => {
76+
const start = node.getStart() + startIndex;
77+
const end = endIndices[index];
78+
const fix = Lint.Replacement.replaceFromTo(
79+
start,
80+
start + end,
81+
`{"${getSpaces(end / RESERVED_ENTITY.length)}"}`,
82+
);
83+
84+
ctx.addFailureAt(start, end, Rule.FAILURE_STRING, fix);
85+
86+
});
87+
}
88+
}
89+
90+
return ts.forEachChild(node, cb);
91+
});
92+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<div>
2+
<p>Some Text</p>
3+
<hr></hr>
4+
Some Text{" "}
5+
<select>
6+
<option></option>
7+
</select>
8+
<button type="button">Some Text</button>
9+
<button type="button">Some Text{" "}</button>
10+
<button type="button">Some Text {" "} </button>
11+
<button type="button">Some Text &NBSP; </button>
12+
<button type="button">Some Text &nBSP; </button>
13+
<br/>
14+
Some Text{" "}
15+
<select>
16+
<option></option>
17+
</select>
18+
<button type="button">Some Text{" "}</button>
19+
<button type="button">Some Text {" "} </button>
20+
<hr></hr>
21+
Some Text{" "}
22+
<br/>
23+
<button type="button">
24+
Some Text{" "}
25+
</button>
26+
<button type="button">
27+
Some Text{" "}
28+
</button>
29+
</div>
30+
31+
<p>
32+
<img src="images/img1.gif" width="50" height="50"/>{" "}
33+
<img src="images/img2.gif" width="50" height="50"/>
34+
</p>
35+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<div>
2+
<p>Some Text</p>
3+
<hr></hr>
4+
Some Text&nbsp;&nbsp;
5+
~~~~~~~~~~~~ [0]
6+
<select>
7+
<option></option>
8+
</select>
9+
<button type="button">Some Text</button>
10+
<button type="button">Some Text&nbsp;</button>
11+
~~~~~~ [0]
12+
<button type="button">Some Text &nbsp; </button>
13+
~~~~~~ [0]
14+
<button type="button">Some Text &NBSP; </button>
15+
<button type="button">Some Text &nBSP; </button>
16+
<br/>
17+
Some Text&nbsp;
18+
~~~~~~ [0]
19+
<select>
20+
<option></option>
21+
</select>
22+
<button type="button">Some Text&nbsp;&nbsp;&nbsp;&nbsp;</button>
23+
~~~~~~~~~~~~~~~~~~~~~~~~ [0]
24+
<button type="button">Some Text &nbsp;&nbsp;&nbsp;&nbsp; </button>
25+
~~~~~~~~~~~~~~~~~~~~~~~~ [0]
26+
<hr></hr>
27+
Some Text&nbsp;&nbsp;&nbsp;
28+
~~~~~~~~~~~~~~~~~~ [0]
29+
<br/>
30+
<button type="button">
31+
Some Text&nbsp;
32+
~~~~~~ [0]
33+
</button>
34+
<button type="button">
35+
Some Text&nbsp;&nbsp;&nbsp;&nbsp;
36+
~~~~~~~~~~~~~~~~~~~~~~~~ [0]
37+
</button>
38+
</div>
39+
40+
<p>
41+
<img src="images/img1.gif" width="50" height="50"/>&nbsp;
42+
~~~~~~ [0]
43+
<img src="images/img2.gif" width="50" height="50"/>
44+
</p>
45+
46+
[0]: Expected '{" "}' instead of '&nbsp;' in JSX markup
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"rules": {
3+
"jsx-whitespace-literal": true
4+
}
5+
}

0 commit comments

Comments
 (0)