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 ) { return this . applyWithFunction ( sourceFile , walk ) ; } } function walk ( ctx ) { return ts . forEachChild ( ctx . sourceFile , function cb ( 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 ) { if ( node . tagName . getText ( ) === FRAGMENT_TAGNAME || node . tagName . getText ( ) === REACT_FRAGEMNT_TAGNAME ) { return true ; } return false ; } function isInvalidJSXText ( node ) { return node . getText ( ) . trim ( ) === "" ? true : false ; }