Skip to content

Commit

Permalink
feat: add jsx-whitespace-literal rule (#243)
Browse files Browse the repository at this point in the history
  • Loading branch information
tanmoyopenroot authored and adidahiya committed Aug 1, 2019
1 parent 6f907b5 commit 55d8a53
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 0 deletions.
92 changes: 92 additions & 0 deletions src/rules/jsxWhitespaceLiteralRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @license
* Copyright 2017 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as Lint from "tslint";
import { isJsxText } from "tsutils/typeguard/3.0";
import * as ts from "typescript";

const RESERVED_ENTITY = " ";

export class Rule extends Lint.Rules.AbstractRule {
public static metadata: Lint.IRuleMetadata = {
description: Lint.Utils.dedent
`Warn if ' ' is used in JXS markup. Prefer {" "} over ' '`,
optionExamples: ["true"],
options: null,
optionsDescription: "",
ruleName: "jsx-whitespace-literal",
type: "functionality",
typescriptOnly: false,
};

public static FAILURE_STRING = `Expected '{" "}' instead of ' ' in JSX markup`;

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
}
}

function getSpaces(numOfSpaces: number): string {
return " ".repeat(numOfSpaces);
}

function walk(ctx: Lint.WalkContext<void>): void {
return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
if (isJsxText(node)) {
if (node.getText().indexOf(RESERVED_ENTITY) > -1) {
const text: string = node.getText();
const regex: RegExp = new RegExp(RESERVED_ENTITY, "g");
const startIndices: number[] = [];
const endIndices: number[] = [];
let countEnitiy: number = -1;
let result: RegExpExecArray | null;

do {
result = regex.exec(text);
if (result !== null) {
if (
startIndices[countEnitiy] !== undefined &&
endIndices[countEnitiy] !== undefined &&
startIndices[countEnitiy] + endIndices[countEnitiy] === result.index
) {
endIndices[countEnitiy] = endIndices[countEnitiy] + RESERVED_ENTITY.length;
} else {
startIndices.push(result.index);
endIndices.push(RESERVED_ENTITY.length);
countEnitiy += 1;
}
}
} while (result !== null);

startIndices.forEach((startIndex, index) => {
const start = node.getStart() + startIndex;
const end = endIndices[index];
const fix = Lint.Replacement.replaceFromTo(
start,
start + end,
`{"${getSpaces(end / RESERVED_ENTITY.length)}"}`,
);

ctx.addFailureAt(start, end, Rule.FAILURE_STRING, fix);

});
}
}

return ts.forEachChild(node, cb);
});
}
35 changes: 35 additions & 0 deletions test/rules/jsx-whitespace-literal/test.tsx.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<div>
<p>Some Text</p>
<hr></hr>
Some Text{" "}
<select>
<option></option>
</select>
<button type="button">Some Text</button>
<button type="button">Some Text{" "}</button>
<button type="button">Some Text {" "} </button>
<button type="button">Some Text &NBSP; </button>
<button type="button">Some Text &nBSP; </button>
<br/>
Some Text{" "}
<select>
<option></option>
</select>
<button type="button">Some Text{" "}</button>
<button type="button">Some Text {" "} </button>
<hr></hr>
Some Text{" "}
<br/>
<button type="button">
Some Text{" "}
</button>
<button type="button">
Some Text{" "}
</button>
</div>

<p>
<img src="images/img1.gif" width="50" height="50"/>{" "}
<img src="images/img2.gif" width="50" height="50"/>
</p>

46 changes: 46 additions & 0 deletions test/rules/jsx-whitespace-literal/test.tsx.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<div>
<p>Some Text</p>
<hr></hr>
Some Text&nbsp;&nbsp;
~~~~~~~~~~~~ [0]
<select>
<option></option>
</select>
<button type="button">Some Text</button>
<button type="button">Some Text&nbsp;</button>
~~~~~~ [0]
<button type="button">Some Text &nbsp; </button>
~~~~~~ [0]
<button type="button">Some Text &NBSP; </button>
<button type="button">Some Text &nBSP; </button>
<br/>
Some Text&nbsp;
~~~~~~ [0]
<select>
<option></option>
</select>
<button type="button">Some Text&nbsp;&nbsp;&nbsp;&nbsp;</button>
~~~~~~~~~~~~~~~~~~~~~~~~ [0]
<button type="button">Some Text &nbsp;&nbsp;&nbsp;&nbsp; </button>
~~~~~~~~~~~~~~~~~~~~~~~~ [0]
<hr></hr>
Some Text&nbsp;&nbsp;&nbsp;
~~~~~~~~~~~~~~~~~~ [0]
<br/>
<button type="button">
Some Text&nbsp;
~~~~~~ [0]
</button>
<button type="button">
Some Text&nbsp;&nbsp;&nbsp;&nbsp;
~~~~~~~~~~~~~~~~~~~~~~~~ [0]
</button>
</div>

<p>
<img src="images/img1.gif" width="50" height="50"/>&nbsp;
~~~~~~ [0]
<img src="images/img2.gif" width="50" height="50"/>
</p>

[0]: Expected '{" "}' instead of '&nbsp;' in JSX markup
5 changes: 5 additions & 0 deletions test/rules/jsx-whitespace-literal/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"jsx-whitespace-literal": true
}
}

0 comments on commit 55d8a53

Please sign in to comment.