import * as t from '@babel/types' ; import { NodePath } from '@babel/traverse' ; import { JSXNode , Options } from './types' ; import { EXPRESSION_BLOCK , LEAF , TEMPLATE_ID } from './constants' ; import cleanJSXElementLiteralChild from './helpers/cleanJSXElementLiteralChild' ; /**
 * 从 JSXElement 中取出 Host Component 的名称
 * 必须已经确定是 JSXElement 属于 Host Component
 * 可以通过 isHostComponent 判断
 */ export function getHostComponentName ( node , path ) { if ( t . isJSXIdentifier ( node . openingElement . name ) ) { const tag = node . openingElement . name . name ; const binding = path . scope . getBinding ( tag ) ; if ( ! binding ) { return tag ; } const importPath = binding . path ; if ( importPath && t . isImportSpecifier ( importPath . node ) && t . isIdentifier ( importPath . node . imported ) ) { return importPath . node . imported . name ; } return tag ; } if ( t . isJSXMemberExpression ( node . openingElement . name ) ) { const property = node . openingElement . name . property ; return property . name ; } } /**
 * 判断 JSXElement 是否是 Host Component
 */ export function isHostComponentElement ( node , path , options ) { if ( t . isJSXFragment ( node ) ) { return false ; } /**
     * case:
     * import { View, View as CustomView } from 'components';
     * <View></View>
     * <CustomView></CustomView>
     */ if ( t . isJSXIdentifier ( node . openingElement . name ) ) { const tag = node . openingElement . name . name ; const binding = path . scope . getBinding ( tag ) ; if ( ! binding ) { if ( tag === EXPRESSION_BLOCK ) { return true ; } return false ; } const importPath = binding . path ; if ( importPath && t . isImportSpecifier ( importPath . node ) && t . isImportDeclaration ( importPath . parent ) && options . isHostComponentPackage ( importPath . parent . source . value ) ) { return true ; } return false ; } /**
     * case:
     * import * as Com from 'components';
     * <Com.View></Com.View>
     */ if ( t . isJSXMemberExpression ( node . openingElement . name ) ) { const object = node . openingElement . name . object ; if ( ! t . isJSXIdentifier ( object ) ) { return false ; } const binding = path . scope . getBinding ( object . name ) ; if ( ! binding ) { return false ; } const importPath = binding . path ; if ( importPath && t . isImportNamespaceSpecifier ( importPath . node ) && t . isImportDeclaration ( importPath . parent ) && options . isHostComponentPackage ( importPath . parent . source . value ) ) { return true ; } return false ; } return false ; } /**
 * 判断是否是 expression block
 */ export function isExpressionBlock ( path ) { return path ?. node ?. openingElement ?. name ?. name === EXPRESSION_BLOCK ; } /**
 * 判断是否是 <React.Fragment></React.Fragment> 或 <></>
 */ export function isReactFragment ( node , path ) { if ( t . isJSXFragment ( node ) ) { return true ; } /**
     * case:
     * import { Fragment } from 'react';
     * <Fragment></Fragment>
     */ if ( t . isJSXIdentifier ( node . openingElement . name ) ) { const tag = node . openingElement . name . name ; const binding = path . scope . getBinding ( tag ) ; if ( ! binding ) { return false ; } const importPath = binding . path ; if ( importPath && t . isImportSpecifier ( importPath . node ) && t . isImportDeclaration ( importPath . parent ) && t . isIdentifier ( importPath . node . imported ) && importPath . parent . source . value . startsWith ( 'react' ) ) { return importPath . node . imported . name === 'Fragment' ; } return false ; } /**
     * case:
     * import * as React from 'react';
     * <React.Fragment></React.Fragment>
     */ if ( t . isJSXMemberExpression ( node . openingElement . name ) ) { const object = node . openingElement . name . object ; const property = node . openingElement . name . property ; if ( ! t . isJSXIdentifier ( object ) ) { return false ; } return object . name . toLowerCase ( ) === 'react' && property . name === 'Fragment' ; } return false ; } export param { string } name param { t . JSXOpeningElement [ 'attributes' ] } attributes returns export function getAttributeByName ( name , attributes ) { return attributes . find ( attr => t . isJSXAttribute ( attr ) && attr . name . name === name ) ; } export param { t . JSXOpeningElement } element returns export function getLeafAttribute ( element ) { const attribute = getAttributeByName ( LEAF , element . attributes ) as t . JSXAttribute | undefined ; return attribute ; } export param { t . JSXOpeningElement } element returns export function getTemplateID ( element ) { const attribute = getAttributeByName ( TEMPLATE_ID , element . attributes ) ; return attribute ?. value ?. value ; } /**
 * 判断是否是 Leaf 节点
 * 组件标记了 leaf 属性时，说明其子节点是单节点
 * 目前只有 plain text 文本情况
 */ export function isPlainTextLeaf ( node , path , options ) { if ( ! t . isJSXElement ( node ) ) { return false ; } if ( ! isHostComponentElement ( node , path , options ) ) { return false ; } return getLeafAttribute ( node . openingElement ) ; } /**
 * 用标签包裹当前 node
 */ export function wrappedByElement ( name , node , path ) { path . replaceWith ( t . jsxElement ( t . jsxOpeningElement ( t . jsxIdentifier ( name ) , [ ] ) , t . jsxClosingElement ( t . jsxIdentifier ( name ) ) , [ node ] , false ) as any ) ; } /**
 * 用 <block> 标签包裹，用于处理无法静态化的标签和表达式
 *
 */ export function wrappedByExpressionBlock ( node , path ) { if ( ! t . isJSXElement ( path . parentPath . node ) && ! t . isJSXFragment ( path . parentPath . node ) ) { return ; } if ( isExpressionBlock ( path . parentPath ) ) { return ; } wrappedByElement ( EXPRESSION_BLOCK , node , path ) ; } /**
 * 将标签替换成 JSXFragment
 */ export function replacedWithJSXFragment ( node , path ) { path . replaceWith ( t . jsxFragment ( t . jsxOpeningFragment ( ) , t . jsxClosingFragment ( ) , node . children ) as any ) ; } export param node returns export function isEmptyText ( node ) { if ( ! t . isJSXText ( node ) ) { return false ; } return ! cleanJSXElementLiteralChild ( node ) ; } export param { string } literal returns export function normalizeLiteral ( literal ) { let value = literal . toString ( ) ; if ( value . startsWith ( '\n' ) ) { value = value . trimLeft ( ) ; } if ( value . trim ( ) . indexOf ( '\n' ) !== - 1 ) { value = value . replace ( /\n\s+/g , ' ' ) ; } if ( value . indexOf ( '\n' ) !== - 1 ) { value = value . trimRight ( ) ; } return value ; } param node export function hasSpreadAttribute ( node ) { return node . openingElement . attributes . some ( attr => t . isJSXSpreadAttribute ( attr ) ) ; } /**
 * 判断一个元素属性里是不是有 children
 */ export function hasChildrenAttribute ( node ) { return node . openingElement . attributes . some ( attr => t . isJSXAttribute ( attr ) && attr . name . name === 'children' ) ; }