Skip to content

Commit

Permalink
[new-rule] react-no-unnecessary-fragment (#245)
Browse files Browse the repository at this point in the history
  • Loading branch information
tanmoyopenroot authored and adidahiya committed Aug 21, 2019
1 parent 182810a commit 43296c8
Show file tree
Hide file tree
Showing 3 changed files with 285 additions and 0 deletions.
98 changes: 98 additions & 0 deletions src/rules/reactNoUnnecessaryFragmentRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* @license
* Copyright 2019 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 {
isJsxElement,
isJsxFragment,
isJsxOpeningFragment,
isJsxSelfClosingElement,
isJsxText,
} from "tsutils/typeguard/3.0";
import * as ts from "typescript";

const FRAGMENT_TAGNAME = "Fragment";
const REACT_FRAGEMNT_TAGNAME = "React.Fragment";

export class Rule extends Lint.Rules.AbstractRule {
public static metadata: Lint.IRuleMetadata = {
description: Lint.Utils.dedent`
Warn if unnecessary fragment is used'
`,
optionExamples: ["true"],
options: null,
optionsDescription: "",
ruleName: "react-no-unnecessary-fragment",
type: "style",
typescriptOnly: false,
};

public static FAILURE_STRING = "Unnecessary Fragment are forbidden";

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

function walk(ctx: Lint.WalkContext<void>): void {
return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
if (
(isJsxFragment(node) && isJsxOpeningFragment(node.openingFragment)) ||
(isJsxElement(node) && isJSXFragmentElement(node.openingElement))
) {
let numValidChildren = 0;

for (const child of node.children) {
if (isJsxText(child)) {
if (!isInvalidJSXText(child)) {
numValidChildren += 1;
}
} else {
numValidChildren += 1;
}

if (numValidChildren > 1) {
break;
}
}

if (numValidChildren <= 1) {
ctx.addFailureAtNode(node, Rule.FAILURE_STRING);
}

} else if (isJsxSelfClosingElement(node) && isJSXFragmentElement(node)) {
ctx.addFailureAtNode(node, Rule.FAILURE_STRING);
}

return ts.forEachChild(node, cb);
});
}

function isJSXFragmentElement(node: ts.JsxSelfClosingElement | ts.JsxOpeningElement): boolean {
if (
node.tagName.getText() === FRAGMENT_TAGNAME ||
node.tagName.getText() === REACT_FRAGEMNT_TAGNAME
) {
return true;
}

return false;
}

function isInvalidJSXText(node: ts.JsxText): boolean {
return node.getText().trim() === "" ? true : false;
}
182 changes: 182 additions & 0 deletions test/rules/react-no-unnecessary-fragment/test.tsx.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<React.Fragment>
~~~~~~~~~~~~~~~~
<div>
~~~~~~~~~
<Component1 />
~~~~~~~~~~~~~~~~~~~~~~
<Component2 />
~~~~~~~~~~~~~~~~~~~~~~
<Component3 />
~~~~~~~~~~~~~~~~~~~~~~
</div>
~~~~~~~~~~
</React.Fragment>
~~~~~~~~~~~~~~~~~ [FAILURE_STRING]

<React.Fragment>test
<div>
<Component1 />
<Component2 />
<Component3 />
</div></React.Fragment>

<React.Fragment>
~~~~~~~~~~~~~~~~
<React.Fragment>
~~~~~~~~~~~~~~~~~~~~
<Component1 />
~~~~~~~~~~~~~~~~~~~~~~
<Component2 />
~~~~~~~~~~~~~~~~~~~~~~
<Component3 />
~~~~~~~~~~~~~~~~~~~~~~
</React.Fragment>
~~~~~~~~~~~~~~~~~~~~~
</React.Fragment>
~~~~~~~~~~~~~~~~~ [FAILURE_STRING]

<React.Fragment>
~~~~~~~~~~~~~~~~
<React.Fragment>
~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~
<>
~~~~~~~~~~
~~~~~~~~~~
<Component1 />
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
<Component2 />
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
<Component3 />
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
</>
~~~~~~~~~~~
~~~~~~~~~~~
</React.Fragment>
~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~ [FAILURE_STRING]
</React.Fragment>
~~~~~~~~~~~~~~~~~ [FAILURE_STRING]

<>
~~
<>
~~~~~~
~~
<>
~~~~~~~~~~
~~~~~~~~~~
<Component1 />
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
<Component2 />
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
<Component3 />
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
</>
~~~~~~~~~~~
~~~~~~~~~~~
</>
~~~~~~~
~~~~~~~ [FAILURE_STRING]
</>
~~~ [FAILURE_STRING]

<>
~~
<Fragment>
~~~~~~~~~~~~~~
~~~~~~~~~~
<>
~~~~~~~~~~
~~~~~~~~~~
<Component1 />
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
<Component2 />
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
<Component3 />
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
</>
~~~~~~~~~~~
~~~~~~~~~~~
</Fragment>
~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~ [FAILURE_STRING]
</>
~~~ [FAILURE_STRING]

<Fragment>
~~~~~~~~~~
<Fragment>
~~~~~~~~~~~~~~
<Component1 />
~~~~~~~~~~~~~~~~~~~~~~
<Component2 />
~~~~~~~~~~~~~~~~~~~~~~
<Component3 />
~~~~~~~~~~~~~~~~~~~~~~
</Fragment>
~~~~~~~~~~~~~~~
</Fragment>
~~~~~~~~~~~ [FAILURE_STRING]

<Fragment>
test
<div>
<Component1 />
<Component2 />
<Component3 />
</div></Fragment>

<Fragment>
<Component1 />
<Component2 />
</Fragment>

<Fragment>
~~~~~~~~~~
</Fragment>
~~~~~~~~~~~ [FAILURE_STRING]

<React.Fragment>
~~~~~~~~~~~~~~~~
</React.Fragment>
~~~~~~~~~~~~~~~~~ [FAILURE_STRING]

<Fragment><Component1 /><Component2 /></Fragment>

<Fragment></Fragment>
~~~~~~~~~~~~~~~~~~~~~ [FAILURE_STRING]

<React.Fragment></React.Fragment>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [FAILURE_STRING]

<Fragment />
~~~~~~~~~~~~ [FAILURE_STRING]

<React.Fragment />
~~~~~~~~~~~~~~~~~~ [FAILURE_STRING]

<>
~~
<div>
~~~~~~~~~
<Component1 />
~~~~~~~~~~~~~~~~~~~~~~
<Component2 />
~~~~~~~~~~~~~~~~~~~~~~
<Component3 />
~~~~~~~~~~~~~~~~~~~~~~
</div>
~~~~~~~~~~
</>
~~~ [FAILURE_STRING]
[FAILURE_STRING]: Unnecessary Fragment are forbidden
5 changes: 5 additions & 0 deletions test/rules/react-no-unnecessary-fragment/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"react-no-unnecessary-fragment": true
}
}

0 comments on commit 43296c8

Please sign in to comment.