import * as path from 'path' ; import JsPlugin from "./lib/plugin/js-plugin" ; import XmlPlugin from "./lib/plugin/xml-plugin" ; import CssPlugin from "./lib/plugin/css-plugin" ; import PluginEs5 from "./lib/plugin/js/plugin-es5" ; import { AppletType } from "./lib/type/applet-type" ; import { overrideSync } from "./lib/utils/file-system/file-system" ; import { info , error , warn , red , cyan , green , orange , yellow , magenta , white } from "./lib/utils/log" ; author CaMnter
  export interface AppletsConversionToolParams { src ? : string ; out ? : string ; target ? : string ; expect ? : string ; options ? : { es5 ? : boolean ; filter ? : Array < string > ; [ option ] : any ; } ; } export interface Plugins { jsPlugin ? : JsPlugin ; cssPlugin ? : CssPlugin ; xmlPlugin ? : XmlPlugin ; } export enum Plan { wxToMy = 'wxToMy' , myToWx = 'myToWx' } export enum HookExtName { js = '.js' , ts = '.ts' , axml = '.axml' , wxml = '.wxml' , acss = '.acss' , wxss = '.wxss' , json = '.json' } interface ConversionError { error ? : Error ; filePath ? : string ; errorMessage ? : string ; } export const appletTypeList = [ AppletType . wx , AppletType . my ] ; export const appletTypeListPrintText = appletTypeList . join ( ', ' ) ; export const defaultFilter = [ 'node_modules' , '.tea' , '.idea' ] ; param params export function appletsConversionTool ( params ) { info ( magenta , '转换开始' ) ; let { src , out , target , expect , options } = params ; const currentAbsolutePath = path . resolve ( process . cwd ( ) ) ; if ( ! target || ! checkAppletsType ( 'target' , target ) ) { error ( red , `缺少必填参数 -t or --target「目标类型 ${ appletTypeListPrintText }」` ) ; return ; } if ( ! expect || ! checkAppletsType ( 'expect' , expect ) ) { error ( red , `缺少必填参数 -e or --expect「期望类型 ${ appletTypeListPrintText }」` ) ; return ; } if ( target === expect ) { error ( red , `--t or --target 不能和 -e or --expect 一致，target = ${ target }  target = ${ expect }` ) ; return ; } const plan = getPlan ( target , expect ) ; if ( ! plan ) { error ( red , `--t or --target 或 -e or --expect 校验失败，target = ${ target }  target = ${ expect }` ) ; return ; } let useSrcDefaultPath = false ; let useOutDefaultPath = false ; if ( ! src || '' === src ) { src = currentAbsolutePath ; useSrcDefaultPath = true ; } else { src = path . resolve ( currentAbsolutePath , src ) ; useSrcDefaultPath = false ; } if ( useSrcDefaultPath ) { info ( yellow , `缺少 src 参数，默认为当前命令行目录 src = ${ src }` ) ; } else { info ( yellow , `src = ${ src }` ) ; } if ( ! out || '' === out ) { out = src ; useOutDefaultPath = true ; } else { out = path . resolve ( currentAbsolutePath , out ) ; useOutDefaultPath = false ; } if ( useOutDefaultPath ) { info ( yellow , `缺少 out 参数，默认为当前命令行目录 out = ${ out }` ) ; } else { info ( yellow , `out = ${ out }` ) ; } let es5 ; let filter = defaultFilter ; if ( options ) { es5 = options . es5 ; const optionsFilter = options . filter ; if ( optionsFilter && Array . isArray ( optionsFilter ) && optionsFilter . length > 0 ) { filter = [ ... filter , ... optionsFilter ] ; } } const printParams = { src , out , target , expect , ... options } ; Object . keys ( printParams ) . forEach ( ( key ) => { const value = ( printParams as any ) [ key ] ; if ( Array . isArray ( value ) ) { info ( orange , `${ key } -> ${ JSON . stringify ( value ) }` ) ; } else { info ( orange , `${ key } -> ${ value }` ) ; } } ) ; const plugins = getPlugins ( plan , options ) ; const expectSrc = src ; const expectOut = out ; const errorList = [ ] ; /**
     * src is the absolute path
     * out is the absolute path
     */ overrideSync ( src , out , { es5 , filter , hitFilter : function ( absolutePath ) { warn ( cyan , `「filter」${ absolutePath }` ) ; } , override : function ( content , absolutePath , relativePath ) { const extName = path . extname ( absolutePath ) ; const hookPath = filePathHook ( extName , absolutePath , plan ) ; const layerPath = path . relative ( expectSrc , hookPath ) ; const filePath = path . resolve ( expectOut , layerPath ) ; let expectContent = content ; if ( isHookExtName ( extName ) ) { try { expectContent = contentHook ( extName , content , plan , plugins ) ; info ( green , `${ absolutePath } -> ${ filePath }` ) ; } catch ( e ) { const conversionError = createConversionError ( e ) ; if ( conversionError ) { conversionError . filePath = absolutePath ; errorList . push ( conversionError ) ; } } return { filePath , content : expectContent } ; } else { info ( green , `${ absolutePath } -> ${ filePath }` ) ; /**
                 * 走普通文件复制流程
                 */ return { filePath , normalCopy : true } ; } } } ) ; printConversionErrors ( errorList ) ; info ( magenta , '转换结束' ) ; } param errorList export function printConversionErrors ( errorList ) { if ( errorList . length > 0 ) { errorList . forEach ( ( conversionError ) => { const { filePath , errorMessage } = conversionError ; if ( filePath ) { warn ( red , filePath ) ; } if ( errorMessage ) { warn ( white , errorMessage ) ; } warn ( yellow , '[注] 可以试试加个 --es5 试试' ) ; } ) ; } } param error export function createConversionError ( error ) { let conversionError ; if ( error && error . stack ) { const stack = error . stack ; let stackList = stack . split ( '\n\n' ) ; const length = stackList . length ; if ( stackList . length > 1 ) { stackList = stackList . slice ( 0 , length - 1 ) ; } const errorMessage = stackList . join ( '\n\n' ) ; conversionError = { error , errorMessage } ; } return conversionError ; } param paramsName param target export function checkAppletsType ( paramsName , target ? ) { if ( ! target || '' === target ) { return false ; } const some = appletTypeList . some ( ( value ) => { return value === target ; } ) ; if ( ! some ) { error ( red , `${ paramsName } 参数错误，应为「${ appletTypeListPrintText }」` ) ; } return some ; } param target param expect export function getPlan ( target , expect ) { switch ( target ) { case AppletType . wx : switch ( expect ) { case AppletType . my : return Plan . wxToMy ; } break ; case AppletType . my : switch ( expect ) { case AppletType . wx : return Plan . myToWx ; } break ; } return ; } param plan export function getPlugins ( plan , options ? ) { let jsPlugin ; let cssPlugin ; let xmlPlugin ; switch ( plan ) { case Plan . wxToMy : jsPlugin = new JsPlugin ( AppletType . wx , AppletType . my ) ; cssPlugin = new CssPlugin ( AppletType . wx , AppletType . my ) ; xmlPlugin = new XmlPlugin ( AppletType . wx , AppletType . my ) ; break ; case Plan . myToWx : jsPlugin = new JsPlugin ( AppletType . my , AppletType . wx ) ; cssPlugin = new CssPlugin ( AppletType . my , AppletType . wx ) ; xmlPlugin = new XmlPlugin ( AppletType . my , AppletType . wx ) ; break ; } const es5 = options && options . es5 ? options . es5 : false ; if ( jsPlugin ) { if ( es5 ) { jsPlugin . plugins = [ new PluginEs5 ( ) ] ; } } return { jsPlugin , cssPlugin , xmlPlugin , } ; } param extName param content param plan param plugins export function contentHook ( extName , content , plan , plugins ) { const { jsPlugin , cssPlugin , xmlPlugin } = plugins ; let expectContent = content ; switch ( plan ) { case Plan . wxToMy : /**
             *「js」
             *「wxss」
             *「wxml」
             */ switch ( extName ) { case HookExtName . js : if ( jsPlugin ) { expectContent = jsPlugin . run ( content ) ; } break ; case HookExtName . wxss : if ( cssPlugin ) { expectContent = cssPlugin . run ( content ) ; } break ; case HookExtName . wxml : if ( xmlPlugin ) { expectContent = xmlPlugin . run ( content ) ; } break ; } break ; case Plan . myToWx : /**
             *「js」
             *「acss」
             *「axml」
             */ switch ( extName ) { case HookExtName . js : if ( jsPlugin ) { expectContent = jsPlugin . run ( content ) ; } break ; case HookExtName . acss : if ( cssPlugin ) { expectContent = cssPlugin . run ( content ) ; } break ; case HookExtName . axml : if ( xmlPlugin ) { expectContent = xmlPlugin . run ( content ) ; } break ; } break ; } return expectContent ; } param extName param filePath param plan export function filePathHook ( extName , filePath , plan ) { let expectFilePath = filePath ; switch ( plan ) { case Plan . wxToMy : /**
             *「wxss」
             *「wxml」
             */ switch ( extName ) { case HookExtName . wxss : expectFilePath = expectFilePath . replace ( new RegExp ( `${ HookExtName . wxss }$` ) , HookExtName . acss ) ; break ; case HookExtName . wxml : expectFilePath = expectFilePath . replace ( new RegExp ( `${ HookExtName . wxml }$` ) , HookExtName . axml ) ; break ; } break ; case Plan . myToWx : /**
             *「acss」
             *「axml」
             */ switch ( extName ) { case HookExtName . acss : expectFilePath = expectFilePath . replace ( new RegExp ( `${ HookExtName . acss }$` ) , HookExtName . wxss ) ; break ; case HookExtName . axml : expectFilePath = expectFilePath . replace ( new RegExp ( `${ HookExtName . axml }$` ) , HookExtName . wxml ) ; break ; } break ; } return expectFilePath ; } param extName export function isHookExtName ( extName ) { const keys = Object . keys ( HookExtName ) ; return keys . some ( ( value , index , array ) => { return HookExtName [ value as any ] === extName ; } ) ; }