import * as Lint from 'tslint' ; import * as ts from 'typescript' ; import { isVersionGreater } from './utils' ; export class Rule extends Lint . Rules . AbstractRule { static FAILURE_STRING = "Wix-Style-React is imported in a way that does not support tree shaking. Use a direct import, for example: `import Button from 'wix-style-react/Button';`" ; static LIB_NAME = 'wix-style-react' ; static MAX_VERSION = '5.8.1' ; public apply ( sourceFile ) { return this . applyWithWalker ( new Walk ( sourceFile , this . getOptions ( ) ) ) ; } } class Walk extends Lint . RuleWalker { private shouldIgnoreNamedImports : boolean ; constructor ( sourceFile , options ) { super ( sourceFile , options ) ; this . shouldIgnoreNamedImports = isVersionGreater ( Rule . LIB_NAME , Rule . MAX_VERSION ) ; } visitImportDeclaration ( node ) { if ( ! this . shouldIgnoreNamedImports && this . isWSRImport ( node ) ) { this . addFailureAt ( node . getStart ( ) , node . getEnd ( ) - node . getStart ( ) , Rule . FAILURE_STRING , this . fixSelectiveImports ( node ) ) ; } super . visitImportDeclaration ( node ) ; } visitVariableStatement ( node ) { node . declarationList . declarations . forEach ( variableDeclaration => { if ( this . isWSRRequireStatement ( variableDeclaration ) ) { if ( this . isRequireAllLib ( variableDeclaration ) ) { this . addFailureAt ( variableDeclaration . getStart ( ) , variableDeclaration . getEnd ( ) - variableDeclaration . getStart ( ) , Rule . FAILURE_STRING ) ; } else if ( this . isRequireWithDestructure ( variableDeclaration ) ) { this . addFailureAt ( variableDeclaration . parent . getStart ( ) , variableDeclaration . parent . getEnd ( ) - variableDeclaration . parent . getStart ( ) , Rule . FAILURE_STRING , node . declarationList . declarations && node . declarationList . declarations . length === 1 ? this . fixSelectiveRequire ( node , variableDeclaration ) : undefined ) ; } } else if ( this . isWSRRequireWithProperty ( variableDeclaration ) ) { this . addFailureAt ( variableDeclaration . parent . getStart ( ) , variableDeclaration . parent . getEnd ( ) - variableDeclaration . parent . getStart ( ) , Rule . FAILURE_STRING , node . declarationList . declarations && node . declarationList . declarations . length === 1 ? this . fixPropertyRequire ( node , variableDeclaration ) : undefined ) ; } } ) ; super . visitVariableStatement ( node ) ; } private isRequireWithDestructure ( variableDeclaration ) { return ts . isObjectBindingPattern ( variableDeclaration . name ) ; } private isRequireAllLib ( variableDeclaration ) { return ts . isIdentifier ( variableDeclaration . name ) ; } private isWSRRequireWithProperty ( variableDeclaration ) { return ( variableDeclaration . initializer && ts . isPropertyAccessExpression ( variableDeclaration . initializer ) && ts . isCallExpression ( variableDeclaration . initializer . expression ) && variableDeclaration . initializer . expression . expression . getText ( ) === 'require' && variableDeclaration . initializer . expression . arguments && variableDeclaration . initializer . expression . arguments . length && ( variableDeclaration . initializer . expression . arguments [ 0 ] as any ) . text === Rule . LIB_NAME ) ; } private isWSRImport ( node ) { return ( node . moduleSpecifier && ( node . moduleSpecifier as any ) . text === Rule . LIB_NAME && node . importClause && ( node . importClause . namedBindings as any ) . elements && ( node . importClause . namedBindings as any ) . elements . length ) ; } private isWSRRequireStatement ( variableDeclaration ) { return ( variableDeclaration . initializer && ts . isCallExpression ( variableDeclaration . initializer ) && ts . isIdentifier ( variableDeclaration . initializer . expression ) && variableDeclaration . initializer . expression . getText ( ) === 'require' && variableDeclaration . initializer . arguments && variableDeclaration . initializer . arguments . length && ( variableDeclaration . initializer . arguments [ 0 ] as any ) . text === Rule . LIB_NAME ) ; } private fixSelectiveImports ( node ) { const specifiers = ( node . importClause . namedBindings as any ) . elements . map ( specifier => specifier . name . text ) ; const importStatements = specifiers . map ( specifier => this . compileImportStatement ( specifier ) ) ; return new Lint . Replacement ( node . getStart ( ) , node . getWidth ( ) , importStatements . join ( '\n' ) ) ; } private fixSelectiveRequire ( variableStatementNode , variableDeclarationNode ) { if ( ts . isObjectBindingPattern ( variableDeclarationNode . name ) ) { const specifiers = variableDeclarationNode . name . elements . map ( element => element . name . getText ( ) ) ; const importStatements = specifiers . map ( specifier => this . compileImportStatement ( specifier ) ) ; return new Lint . Replacement ( variableStatementNode . getStart ( ) , variableStatementNode . getWidth ( ) , importStatements . join ( '\n' ) ) ; } } private fixPropertyRequire ( variableStatementNode , variableDeclarationNode ) { if ( variableDeclarationNode . initializer && ts . isPropertyAccessExpression ( variableDeclarationNode . initializer ) && ts . isIdentifier ( variableDeclarationNode . initializer . name ) ) { const identifier = variableDeclarationNode . name . getText ( ) ; const specifier = variableDeclarationNode . initializer && variableDeclarationNode . initializer . name . getText ( ) ; return new Lint . Replacement ( variableStatementNode . getStart ( ) , variableStatementNode . getWidth ( ) , this . compileImportStatement ( identifier , specifier ) ) ; } } private compileImportStatement ( name , source = name ) { return `import ${ name } from '${ Rule . LIB_NAME }/${ source }';` ; } }