vue文件转AST,并恢复成vue文件(适用于antdv版本升级)

vue文件转AST,并恢复成vue文件---antdvV3升级V4

  • vue文件转AST,重新转回原文件过程
    • 如何获取项目路径
    • 读取项目文件,判断文件类型
    • 分别获取vue文件 template js(vue2和vue3)
    • 处理vue 文件template部分
    • 处理vue script部分
    • utils--transform.ts(主要转换函数)
    • utils--- antdv3_4
    • utils--excapeRe.ts
    • 思路流程图

vue文件转AST,重新转回原文件过程

## 项目结构![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/7203466177fc4b1398d21bb31b83b487.png)
将打包后的dist上传到node,在本地安装。建议安装全局,方便全局使用。
安装:

npm install @my-cli -g

检查是否安装成功

 bdi-cli  --help . 

使用: < path > 替换成项目绝对路径

bdi-cli --antdv <path>

如何获取项目路径

  1. 配置bin
#!/usr/bin/env node

import { program } from 'commander';
import antvUpdateV3ToV4 from '../src/antdv_v3_v4'
program
  .name('my-cli')
  .description('CLI工具')
  .version('1.0')
  .option('--antdv <cwd>', 'antdv v3升级v4工具')
program.parse();
const options = program.opts();
if (options.antdv) {
  antvUpdateV3ToV4(options.antdv);
}

在脚本的顶部使用 #!/usr/bin/env node 这一行被称为 “shebang”(或 “hashbang”)。它在类 Unix 操作系统中用于指示应使用哪个解释器来执行该脚本。以下是它的作用的详细解释:

**Shebang (#!):**这是一个字符序列,告诉操作系统该文件应该使用某个解释器来执行。

**/usr/bin/env:**这是一个命令,用于定位并运行指定的解释器。使用 env 是一种常见做法,因为它会搜索用户的 PATH 环境变量来找到解释器,从而使脚本在不同系统上更具可移植性,因为解释器可能安装在不同的位置。

**node:**这指定了脚本应该使用 Node.js 解释器来运行。

2.配置package.json

在这里插入图片描述

读取项目文件,判断文件类型

index.ts

import { glob } from 'glob'
import { readFile, writeFile, access, mkdir } from 'node:fs/promises'
import { extname } from 'node:path'
import * as pathtool from 'path'
import { vueParser } from './parser/vue'
import { templateTransformer } from './transformer/template'
import { javascriptTransformer, JavaScriptCodeType } from './transformer/javascript'
let antdv_v3_v4: { [prop: string]: Object[] } = {}
let jsRepalceKeysArray: { [prop: string]: {}[] } = {}
let handlePropArray: { [prop: string]: {}[] } = {}
async function vueHandler(content: string, path: string) {
  console.log('vueHandlerpath: ', path);
  let resultCode = ''
  let changeArr: boolean[] = []
  const { headerComment, template, scriptInfo, otherContent } = vueParser(content)
  // 头部注释
  resultCode += `${headerComment}\n`
  // 处理template
  const { result: templateResult, handlePropArray: handleProp, hasChange: templateHasChange, jsRepalceKeysArray: jsRepalceKeys } = await templateTransformer(template)
  jsRepalceKeysArray[path] = jsRepalceKeys
  handlePropArray[path] = handleProp
  // resultCode += templateResult
  resultCode += `${templateResult}\n`
  changeArr.push(templateHasChange)
  antdv_v3_v4[path] = handleProp

  // 处理script
  for (const item of scriptInfo) {
    const codeType = item.type === 'setup' ? JavaScriptCodeType.Vue3Composition : JavaScriptCodeType.Vue2;
    const { hasChange, result, } = await javascriptTransformer(item.content, codeType, jsRepalceKeys);
    resultCode += `\n${item.head}\n${result}\n</script>\n`;
    changeArr.push(hasChange);
  }

  resultCode += `\n${otherContent}\n`

  if (changeArr.includes(true)) {//文件是否有变更,变更重新写入,没有不做操作
    const filePath = path
    const dir = pathtool.dirname(filePath);
    try {//检查目录是否存在
      await access(dir);
    } catch (error) {
      await mkdir(dir, { recursive: true });
    }
    await writeFile(filePath, resultCode)
  }

}

const main = async (cwd: string) => {
  // 获取文件
  const matchFiles = await glob('**/*.{vue,js,ts}', {
    cwd,//文件名称(绝对路径)
    absolute: true
  })
  let i = 0
  for (const path of matchFiles) {
    if (path.includes('locales')) continue
    // 读取文件内容
    const fileContent = await readFile(path, 'utf-8')
    // 获取后缀
    const ext = extname(path)
    switch (ext) {
      case '.vue': {
        await vueHandler(fileContent, path)
        break
      }
    }
  }

  // 生成日志
  generateLog(cwd + '/ant3_4.json', handlePropArray, jsRepalceKeysArray)
}
const generateLog = async (cwd, templateObj, jsObj) => {
  const result = {}
  for (const filePath in templateObj) {
    result[filePath] = {
      template: templateObj[filePath],
      js: jsObj[filePath]
    }
  }
  await writeFile(cwd, JSON.stringify(result, null, 2))
}


export default main;

分别获取vue文件 template js(vue2和vue3)

parser vue.ts

import { parse } from '@vue/compiler-dom'
import type { ElementNode } from '@vue/compiler-dom'

function getScriptHead(props) {
  let attr: string[] = []
  for (const prop of props) {
    let val =  ''
    if (prop.value) {
      val = `="${prop.value.content}"`
    }
    attr.push(`${prop.name}${val}`)
  }
  const separator = attr.length === 0 ? '' : ' '
  return `<script${separator + attr.join(' ')}>`
}
function extractHeaderComment(content) {

  // 使用正则表达式匹配头部注释
  const match = content.match(/<!--[\s\S]*?@Description:[\s\S]*?-->/);

  if (match) {
    return match[0];
  } else {
    return '';
  }
}

interface ScriptInfo {
  type: 'setup' | 'optionapi',
  head: string
  content: string
}

export function vueParser(rawContent: string) {
  const result = parse(rawContent)
  let headerComment: string = extractHeaderComment(rawContent)
  let template: string = ''
  let script: string = ''
  let scriptSetup: string = ''
  let otherContent: string = ''
  let scriptHead: string = ''
  const scriptInfo: ScriptInfo[] = []
  result.children.forEach((item) => {
    if ((item as ElementNode)?.tag === 'template') {
      template = item.loc.source
    } else if (item.type === 1 && item.tag === 'script') {
      const tempInfo:ScriptInfo = {
        type: 'setup',
        head: getScriptHead(item.props),
        content: ''
      }
      scriptHead = getScriptHead(item.props)
      if ((item as ElementNode)?.props?.some?.((prop) => prop.name === 'setup') || item.loc.source.includes('defineComponent')) {
        scriptSetup = (item as ElementNode).children.length ? (item as ElementNode).children[0].loc.source : ''
        tempInfo.type = 'setup'
        tempInfo.content = scriptSetup
      } else {
        script = (item as ElementNode).children.length ? (item as ElementNode).children[0].loc.source : ''
        tempInfo.type = 'optionapi'
        tempInfo.content = script
      }
      scriptInfo.push(tempInfo)
    } else if (item.type === 1 && item.tag === 'style') {
      otherContent += item.loc.source ?? ''
    }
  })
  return {
    headerComment,
    template,
    scriptHead,
    script,
    scriptSetup,
    otherContent,
    scriptInfo
  }
}

处理vue 文件template部分

transformer – template.js


import { VueTransform } from '../utils/transform';
import {
  ElementNode,
  type AttributeNode,
  type DirectiveNode
} from '@vue/compiler-dom'
// import { containsChinese, isI18nKeyPattern, validateValue, i18nKeyPattern, containsAntdvProps } from '../utils/regex'
// import { handlePropsTransform } from '../utils/antdv3_4';
import { changedComponentPropsMap, componentTuple, handlePropsTransform } from '../utils/antdv3_4'

export async function templateTransformer(rawContent: string) {
  const handlePropArray: Object[] = []
  const jsRepalceKeysArray: Object[] = []
  let hasChange = false
  let deletePropsIndex: number[] //存储已存在
  const transform = new VueTransform({
    onelement(element: ElementNode) {
      const tag = element.tag
      const antdvComponentName = componentTuple.includes(tag) ? tag : tag.slice(2).split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
      if (element.props && element.props.length && (componentTuple.includes(antdvComponentName))) {
        const props = element.props as any
        deletePropsIndex = []
        for (const prop of props) {
          switch (prop.type) {
            case 6:
              if (changedComponentPropsMap[antdvComponentName][prop.name]) {
                onAntdvReplaceAttr(prop, changedComponentPropsMap[antdvComponentName], antdvComponentName)
              }
              break
            case 7: // Vue 指令
              if (prop.exp) {
                if (prop.arg && prop.arg.content && changedComponentPropsMap[antdvComponentName][prop.arg.content]) {
                  onAntdvReplaceDirective(prop, changedComponentPropsMap[antdvComponentName], antdvComponentName, element)
                }
              }
              break
          }
        }
        if (deletePropsIndex.length) {
          //必须倒序,数组的末尾开始删除,这样不会影响到未处理的元素的索引
          deletePropsIndex.reverse().forEach(idx => {
            element.props.splice(idx, 1)
          })
        }

      }

    },

  })
  // key='value'
  function onAntdvReplaceAttr(node: AttributeNode | any, config?: any, antdvComponentName?: string) {
    let obj = {}
    let NewPropName: { NewPropName: string; propSubKeyValue: string; } | string
    let oldProp = ''
    // 处理情况二:
    NewPropName = handlePropsTransform(node.name, config)
    oldProp = node.name + ''
    if (NewPropName === oldProp) return node
    if (NewPropName !== 'v-if') {
      /**  
       <a-select
     --  dropdownClassName="my-select-popup"
     ++  popupClassName="my-select-popup"
       />
     **/
      node.name = NewPropName
      if (node.nameLoc)
        node.nameLoc.source = NewPropName
    } else {
      /**
      --<a-tag visible> tag</a-tag> 
      ++ <a-tag v-if="visible">tag</a-tag>  
      */
      delete node.nameLoc
      delete node.value
      node.type = 7
      node.name = 'if'
      node.exp = {
        type: 4,
        loc: node.loc,
        content: oldProp,
        isStatic: false,
        constType: 0,
      }
      node.arg = null
      node.modifiers = []
    }
    obj[antdvComponentName as string] = {
      [oldProp]: NewPropName
    }
    handlePropArray.push(obj)
    hasChange = true

  }
  // :key='value'
  function onAntdvReplaceDirective(node: DirectiveNode | any, config: any, antdvComponentName: string, Element: ElementNode,): void {

    let obj = {}
    let NewPropName = ''
    let oldProp = ''
    let propSubKeyValue = ''
    if (!node.arg) return
    let result = node.arg.content ? handlePropsTransform(node.arg.content, config, node.exp, jsRepalceKeysArray) : ''
    oldProp = node.arg.content + ''
    if (result === node.arg.content) return
    if (typeof result === 'string') {
      // 处理情况一:
      // (1):
      // -- <a-modal :visible="visible">content</a-modal>
      // ++ <a-modal :open="visible">content</a-modal>
      NewPropName = result
      if (NewPropName === 'v-if') {
        // (2):
        // --<a-tag :visible="visible"> tag</a-tag> 
        // ++ <a-tag v-if="visible">tag</a-tag>
        node.name = 'if'
        delete node.rawName
        node.arg = null
        node.modifiers = []
      }
    } else if (result.propSubKeyValue) {
      propSubKeyValue = result.propSubKeyValue
      NewPropName = result.NewPropName

      if (node.exp) {
        const index = Element.props.findLastIndex((prop: any) => prop.arg && prop.arg.content === NewPropName)
        if (index > -1) {
          //(3)
          //   <a-slider
          // --:tooltip.sync="{open2:visible2}"
          // --:tooltipPlacement.sync="visible"
          //++ :tooltip.sync="{open2:visible2,open:visible}"
          // :visible="visible"/>
          const prop = Element.props[index] as DirectiveNode | any
          if (prop.arg && prop.arg.content === NewPropName) {
            //匹配内容
            if (prop.exp.content.startsWith('{') && prop.exp.content.endsWith('}')) {
              //将新增的内容加到最新需要变更的属性
              node.exp.content = `{${prop.exp.content.slice(1, -1)},${propSubKeyValue}:${node.exp.content}}`
              node.exp.loc.source = `{${prop.exp.loc.source.slice(1, -1)},${propSubKeyValue}:${node.exp.loc.source}}`
              //删除旧的prop
              deletePropsIndex.push(index)
            }
          }


        } else {
          // (4):
          // -- <a-slider :tooltipVisible="visible" />
          // ++ <a-slider :tooltip="{ open: visible }" />
          node.exp.content = `{${propSubKeyValue}:${node.exp.content}}`
          node.exp.loc.source = `{${propSubKeyValue}:${node.exp.loc.source}}`
        }

      }
    }
    if (node.arg || node.rawName) {
      node.arg.content = NewPropName
      node.arg.loc.source = NewPropName
      node.rawName = NewPropName !== 'v-if' ? `:${NewPropName}` : NewPropName
    }

    obj[antdvComponentName] = {
      [oldProp]: NewPropName
    }
    handlePropArray.push(obj)
    hasChange = true
  }
  const ast = transform.parser(rawContent)
  // AST转template
  const result = transform.generate(ast)
  // console.log(handlePropArray);
  return {
    result: result,
    hasChange,
    handlePropArray,
    jsRepalceKeysArray
  }
}

处理vue script部分

这里也可以用于vue项目中.js或者.ts文件处理

import { parse } from '@babel/parser'
import _traverse from '@babel/traverse'
import _generate from '@babel/generator'
import { changedComponentJsMap } from '../utils/antdv3_4'

// https://github.com/babel/babel/issues/13855
// @ts-ignore
const traverse = _traverse.default || _traverse
// @ts-ignore
const generate = _generate.default || _generate

export enum JavaScriptCodeType {
  Vanilla,
  Vue2,
  Vue2Composition,
  Vue3,
  Vue3Composition
}

export async function javascriptTransformer(rawContent: string, type: JavaScriptCodeType, jsRepalceKeysArray?: Object[]) {
  let hasChange = false
  const handleArray: string[] = []
 
  const ast = parse(rawContent, {
    sourceType: 'unambiguous',
    plugins: ['jsx', 'typescript']
  })
  traverse(ast, {
    // 处理情况:
    // const arr = [{
    //   title: 'Name',
    //   dataIndex: 'name',
    // --filterDropdownVisible: visible,
    // ++filterDropdownOpen: visible,
    //   visible: filterDropdown
    // }];
    ArrayExpression(path) { //确定array
      path.node.elements && path.node.elements.forEach((element: any) => {
        if (element.type === 'ObjectExpression') {//确定对象
          if (element.properties && element.properties.length) {
            element.properties.forEach(prop => {
              if (prop.type === 'ObjectProperty') {//确定key
                const keyName = prop.key.name || prop.key.value;
                if (jsRepalceKeysArray && jsRepalceKeysArray.length) {
                  jsRepalceKeysArray.forEach(obj => {
                    if (obj[keyName]) {
                      prop.key.name = obj[keyName]
                      hasChange = true
                    }
                  })
                } else if (changedComponentJsMap[keyName]) {
                  prop.key.name = changedComponentJsMap[keyName].replacer
                  hasChange = true
                }

              }
            })
          }
        }
      })
    },
  })

  const output = generate(ast, {
    jsescOption: {
      // 中文不转unicode
      minimal: true,
      quotes: 'single'
    }
  })
  // console.log(output.code);
  return {
    result: output.code,
    handleArray,
    hasChange
  }
}

utils–transform.ts(主要转换函数)

将template转AST 区分节点类型进行不同处理

import {
  type AttributeNode,// HTML 元素的属性节点。
  type DirectiveNode,//Vue 指令节点
  type ExpressionNode,//表达式节点,可以是在模板中使用的各种表达式,如插值表达式、指令表达式等
  type RootNode,//模板的根节点
  type TemplateChildNode,//模板中的子节点,可以是文本节点、元素节点等
  type TextNode,//文本节点
  baseParse, ElementNode,//解析 Vue 模板字符串,返回一个RootNode,代表模板的抽象语法树(AST)
  SimpleExpressionNode//表示简单表达式节点,通常是一个常量或变量引用
} from '@vue/compiler-dom'
import { escapeHtml } from './excapeRe'

export interface Options {
  onelement?: (node: ElementNode) => void
  ontext?: (node: TextNode) => TextNode
  onattr?: (node: AttributeNode) => AttributeNode
  ondirective?: (node: DirectiveNode,) => DirectiveNode
  oninterpolation?: (node: SimpleExpressionNode) => SimpleExpressionNode
}

export class VueTransform {
  options: Options | undefined
  _lastStartLine: number = 1
  _lastColumn: number = 1
  _voidTag = ['img', 'br', 'hr']
  constructor(options?: Options) {
    this.options = options
  }

  parser(template: string) { //将template转成AST树
    // console.log(JSON.stringify(baseParse(template, {
    //   isVoidTag: (tag: string) => {
    //     return this._voidTag.includes(tag)
    //   }
    // })))
    return baseParse(template, { //将template转成AST树
      isVoidTag: (tag: string) => {//判断给定的标签是否是一个 “空标签”(void tag)
        return this._voidTag.includes(tag)
      }
    })
  }

  generate(tree: RootNode) {
    // 表示root,这一层不解析
    if (tree.type === 0) {
      this._lastStartLine = tree.loc.start.line
      this._lastColumn = 1
    }
    return this.html(tree.children)
  }

  html(tree: TemplateChildNode[]): string {
    let result = ''
    for (const node of tree) {
      // 根据给定的列数和当前列信息,返回相应数量的空格
      result += this.createNewline(this._lastStartLine, node.loc.start.line)
      this._lastStartLine = node.loc.start.line
      // 根据起始行和结束行的差值,返回相应数量的换行符,并在有换行时重置列信息。
      result += this.createNewsColumns(node.loc.start.column)
      this._lastColumn = node.loc.end.column
      // 包装$t
      switch (node.type) {
        case 1: // ELEMENT
          this.onElement(node)
          result += `<${node.tag}`
          if (node.props && node.props.length) {
            this._lastColumn = node.loc.start.column + `<${node.tag}`.length
            result += this.attrs(node.props, node)
          }
          if (node.isSelfClosing) {
            result += this.createNewline(this._lastStartLine, node.loc.end.line)
            this._lastStartLine = node.loc.end.line
            if (!node.props.length) {
              // 当标签无属性,则计算结束标签与标签之间的column 标签结束位置 = 标签起始位置 + 标签长度 + 空格
              this._lastColumn = node.loc.start.column + `<${node.tag}`.length
            }
            // 取当前节点的结束column时,没算上 /> 所占的字符长度,故需要减去 2
            result += this.createNewsColumns(node.loc.end.column - 2)
            this._lastColumn = node.loc.end.column
            result += '/>'
          } else {
            // TODO VUE解析出来的 ast 信息不全
            // 如果此时非自闭合标签结束符 单起一行,在 ast 中无法记录该信息,故无法还原此时的换行情况
            // 不过总体行数不会影响,副作用:自闭合标签结束符单起一行的样式丢失,该children计算会多出一行
            result += '>'
          }
          if (node.children && node.children.length) {
            result += this.html(node.children)
          }
          if (!node.isSelfClosing && !this._voidTag.includes(node.tag)) {
            result += this.createNewline(this._lastStartLine, node.loc.end.line)
            this._lastStartLine = node.loc.end.line
            result += this.createNewsColumns(node.loc.start.column)
            this._lastColumn = node.loc.end.column
            result += `</${node.tag}>`
          }
          break
        case 2: // TEXT
          result += escapeHtml(this.onText(node)?.content || node.content)
          break

        case 3: // COMMENT
          result += `<!--${escapeHtml(node.content)}-->`
          break

        case 5: // INTERPOLATION  插值 {{'中文'}}  ${'中文'}
          // TODO 此处 {{ 括号难以还原位置,暂不处理

          if (node.content.type === 4) {
            this.onInterpolation(node.content)
          }

          result += `{{ ${this.attrValue(node.content)} }}`
          break
      }
    }
    return result
  }
  attrs(props: Array<AttributeNode | DirectiveNode>, node: ElementNode): string {
    let attr = ''
    for (const prop of props) {
      attr += this.createNewline(this._lastStartLine, prop.loc.start.line)
      this._lastStartLine = prop.loc.end.line
      // 重置 lastColumn
      attr += this.createNewsColumns(prop.loc.start.column)
      this._lastColumn = prop.loc.end.column
      switch (prop.type) {
        case 6: // ATTRIBUTE  key="value"  
          let val = ''
          if (prop.value) {
            this.onAttr(prop)
            val = `="${this.attrValue(prop.value)}"`
          }
          attr += `${prop.name}${val}`
          break
        case 7: // DIRECTIVE  :key='value'
          if (prop.exp) {
            this.onDirective(prop)
          }
          let modifiers = ''
          if (prop.modifiers) {
            prop.modifiers.forEach(modifier => {
              modifiers += `.${modifier.content}`
            })
          }
          if (prop.name === 'slot') {
            // slot 统一解析成 #xxx
            if (prop.arg) {
              attr += `#${this.attrValue(prop.arg)}`
            } else {
              attr += `v-${prop.name}`
            }
            if (prop.exp) {
              attr += `="${this.attrValue(prop.exp)}"`
            }
          } else if (prop.name === 'bind') {
            if (prop.arg) {


              // 如果参数名存在,bind 统一解析成 :xxx="xxx" 的模式
              attr += `:${this.attrValue(prop.arg)}${modifiers}="${this.attrValue(prop.exp)}"`
            } else {
              attr += `v-bind="${this.attrValue(prop.exp)}"`
            }
          } else if (prop.name === 'on') {
            // 事件绑定统一解析成 @xxx=xxx
            if (prop.exp) {
              attr += `@${this.attrValue(prop.arg)}${modifiers}="${this.attrValue(prop.exp)}"`
            } else {
              attr += `@${this.attrValue(prop.arg)}${modifiers}`
            }
          } else {
            if (prop.exp) {


              attr += `v-${prop.name}${modifiers}${prop.arg ? ':' + this.attrValue(prop.arg) : ''}="${this.attrValue(prop.exp)}"`
            } else {
              attr += `v-${prop.name}${modifiers}${prop.arg ? ':' + this.attrValue(prop.arg) : ''}`
            }

          }
          break

      }
    }
    return attr
  }

  attrValue(value: TextNode | ExpressionNode | undefined): string {
    if (!value) return ''
    let val = ''
    try {
      val += this.createNewline(this._lastStartLine, value.loc.start.line)
      this._lastStartLine = value.loc.end.line
      // lastColumn = value.loc.end.column
      switch (value.type) {
        case 2: // TEXT
          val += escapeHtml(value.content)
          break
        case 4: // SIMPLE_EXPRESSION
          val += value.content
          break
      }
    } catch (error) {
      console.log(error)
    }

    return val
  }
  onElement(element: ElementNode) {
    return this.options?.onelement && this.options.onelement(element)
  }
  onText(node: TextNode): TextNode {
    return this.options?.ontext && this.options.ontext(node) || node
  }

  onAttr(node: AttributeNode): AttributeNode {
    return this.options?.onattr && this.options.onattr(node) || node
  }

  onDirective(node: DirectiveNode): DirectiveNode {
    return this.options?.ondirective && this.options.ondirective(node,) || node
  }

  onInterpolation(node: SimpleExpressionNode): SimpleExpressionNode {
    return this.options?.oninterpolation && this.options.oninterpolation(node) || node
  }
  // onAntdvReplace(node: DirectiveNode, config?: any, props?: Array<AttributeNode | DirectiveNode>, antdvComponentName?: string, type?: number): DirectiveNode {
  //   return this.options?.onAntdvReplace && this.options.onAntdvReplace(node, config, props, antdvComponentName, type) || node
  // }
  // onAntdv(oldProp: string, newProp: string, antdvComponentName: string) {
  //   this.options?.onAntdv && this.options.onAntdv(oldProp, newProp, antdvComponentName)
  // }
  createNewsColumns(num: number) {
    return ' '.repeat(Math.max(num - this._lastColumn, 0))
  }

  createNewline(start: number, end: number) {
    // return ''
    const lines = Math.max(end - start, 0)
    // 没换行后重新初始化列信息
    if (lines > 0) {
      this._lastColumn = 1
    }
    return '\n'.repeat(lines)
  }

}


utils— antdv3_4

antdv升级模块


//antdv prop 版本升级部分
const changedComponentPropsMap = {
  AutoComplete: {
    dropdownClassName: {
      action: 'rename',
      replacer: 'popupClassName',
    },
  },
  Cascader: {
    dropdownClassName: {
      action: 'rename',
      replacer: 'popupClassName',
    },
  },
  Select: {
    dropdownClassName: {
      action: 'rename',
      replacer: 'popupClassName',
    },
  },
  TreeSelect: {
    dropdownClassName: {
      action: 'rename',
      replacer: 'popupClassName',
    },
  },
  // 处理 compound components: TimePicker.RangePicker
  TimePicker: {
    dropdownClassName: {
      action: 'rename',
      replacer: 'popupClassName',
    },
  },
  // 处理 compound components: DatePicker.RangePicker
  DatePicker: {
    dropdownClassName: {
      action: 'rename',
      replacer: 'popupClassName',
    },
  },
  RangePicker: {
    dropdownClassName: {
      action: 'rename',
      replacer: 'popupClassName',
    },
  },
  Mentions: {
    dropdownClassName: {
      action: 'rename',
      replacer: 'popupClassName',
    },
  },
  Drawer: {
    visible: {
      action: 'rename',
      replacer: 'open',
    },
    className: {
      action: 'rename',
      replacer: 'rootClassName',
    },
    style: {
      action: 'rename',
      replacer: 'rootStyle',
    },
  },
  Modal: {
    visible: {
      action: 'rename',
      replacer: 'open',
    },
  },
  Dropdown: {
    visible: {
      action: 'rename',
      replacer: 'open',
    },
  },
  Tooltip: {
    visible: {
      action: 'rename',
      replacer: 'open',
    },
  },
  Tag: {
    visible: {
      action: 'remove',
    },
  },
  Slider: {
    tipFormatter: {
      action: 'rename',
      replacer: 'tooltip.formatter',
    },
    tooltipPlacement: {
      action: 'rename',
      replacer: 'tooltip.placement',
    },
    tooltipVisible: {
      action: 'rename',
      replacer: 'tooltip.open',
    },
  },
  Table: {
    columns: {
      filterDropdownVisible: {
        action: 'rename',
        replacer: 'filterDropdownOpen',
      },
      filterDropdown: {
        action: 'rename',
        replacer: 'filterDropdownVisible3',
      },
    }
  },
};
//antdv js 版本升级部分
const changedComponentJsMap = {
  filterDropdownVisible: {
    action: 'rename',
    replacer: 'filterDropdownOpen',
  },
}
// 将map对象key转成数组 [ DatePicker, RangePicker]
const componentTuple = Object.keys(changedComponentPropsMap)
//处理需要升级组件 prop 部分
function handlePropsTransform(propNode, componentConfig, attrValue?: any, jsRepalceKeysArray?: string[] | any): string | { NewPropName: string, propSubKeyValue: string } {
  let hasChanged = false;
  let NewPropName = ''
  let propSubKeyValue = ''
  let exp = ''
  Object.keys(componentConfig).forEach((propName) => {
    if (!Object.keys(componentConfig[propName]).includes('action')) {
      Object.keys(componentConfig[propName]).forEach(key => {
        //只有匹配到colums就添加到js需要变更属性当中
        const config = componentConfig[propName][key]
        jsRepalceKeysArray.push({ [key]: config.replacer })
        if (attrValue?.content.indexOf(key + ':') >= 0) {
          //处理5:
          //  <a-table
          //   :data="[]"
          //   :columns="[
          //     {
          //       title: 'Name',
          //       dataIndex: 'name',
          //     --filterDropdownVisible: visible,
          //     ++filterDropdownVisible: visible,
          //     },
          //   ]"
          // />
          if (config.action === 'rename') {
            attrValue.content = attrValue.content.replace(new RegExp(`\\b${key}:`, 'g'), `${config.replacer}:`);
          }
        }
      })
      return propNode
    } else {
      const { action, replacer } = componentConfig[propName];
      if (action === 'rename' && replacer) {
        if (replacer.includes('.')) {
          const [propKey, propSubKey] = replacer.split('.');
          if (propNode === propName) {
            propSubKeyValue = propSubKey
            NewPropName = propKey
            hasChanged = true;
            return { NewPropName, propSubKeyValue }
          }
        } else {
          if (propNode === propName) {
            NewPropName = replacer
            hasChanged = true;
            return NewPropName
          }
        }
      }
      if (action === 'remove') {
        NewPropName = 'v-if'
        hasChanged = true;
        return NewPropName
      }
    }

  });
  // console.log(1, hasChanged, 2, propSubKeyValue, 3, NewPropName, 4, exp, 5, propNode, 6);

  return hasChanged ? (propSubKeyValue ? { NewPropName, propSubKeyValue } : exp ? { exp } : NewPropName) : propNode
}

export { changedComponentPropsMap, changedComponentJsMap, componentTuple, handlePropsTransform }

utils–excapeRe.ts

node默认转换&lt;为> ,排除这部分处理,不让他转换

const escapeRE = /["'&<>]/

export function escapeHtml(string: unknown): string {
  const str = '' + string
  const match = escapeRE.exec(str)

  if (!match) {
    return str
  }

  let html = ''
  let escaped: string
  let index: number
  let lastIndex = 0
  for (index = match.index; index < str.length; index++) {
    switch (str.charCodeAt(index)) {
      case 34: // "
        escaped = '&quot;'
        break
      case 38: // &
        escaped = '&amp;'
        break
      case 39: // '
        escaped = '&#39;'
        break
      case 60: // <
        escaped = '&lt;'
        break
      case 62: // >
        escaped = '&gt;'
        break
      default:
        continue
    }

    if (lastIndex !== index) {
      html += str.slice(lastIndex, index)
    }

    lastIndex = index + 1
    html += escaped
  }

  return lastIndex !== index ? html + str.slice(lastIndex, index) : html
}

// https://www.w3.org/TR/html52/syntax.html#comments
const commentStripRE = /^-?>|<!--|-->|--!>|<!-$/g

export function escapeHtmlComment(src: string): string {
  return src.replace(commentStripRE, '')
}

export const cssVarNameEscapeSymbolsRE: RegExp =
  /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g

export function getEscapedCssVarName(
  key: string,
  doubleEscape: boolean,
): string {
  return key.replace(cssVarNameEscapeSymbolsRE, s =>
    doubleEscape ? (s === '"' ? '\\\\\\"' : `\\\\${s}`) : `\\${s}`,
  )
}

思路流程图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/902193.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【线下+线上会议|国内外双会场】2024年第四届数字化社会与智能系统国际学术会议(DSInS 2024)-悉尼/郑州双会场

2024年第四届数字化社会与智能系统国际学术会议&#xff08;DSInS 2024&#xff09;-悉尼/郑州双会场 2024 4th International Conference on Digital Society and Intelligent Systems 会议官网&#xff1a;www.dsins.org 2024 4th International Conference on Digital Soc…

龙迅#LT89101 适用于 MIPI DSI/CSI摄像头和 LVDS 中继信号延长功能,分辨率可支持 1080P@60HZ!

1. 描述 Lontium LT89101 是一款高性能 MIPI DSI/CSI-2 和 LVDS 中继器&#xff0c;用于汽车系统应用的移动显示或摄像头信号整形。 LT89101采用先进的 CMOS 工艺制造&#xff0c;并采用小外形 7.5mm x 7.5mm QFN64 封装。该封装符合 RoHS 标准&#xff0c;额定工作温度范围为 …

MySQL8.0.40编译安装

近期MySQL发布了8.0.40版本&#xff0c;与之前的版本相比&#xff0c;部分依赖包发生了变化&#xff0c;因此重新编译一版&#xff0c;也便于大家参考。 1. 下载源码 选择对应的版本、选择源码、操作系统 如果没有登录或者没有MySQL官网账号&#xff0c;可以选择只下载 2. 进…

element 按钮变形 el-button样式异常

什么都没动&#xff0c;element UI的按钮变形了&#xff0c;莫名其妙&#xff0c;连官网的也变形了&#xff0c;换了其它浏览器又正常&#xff0c; 难道这是element UI的问题&#xff1f;NO&#xff0c;是浏览器的插件影响到了&#xff01;去扩展插件里面一个个关闭扩展&#x…

MySql中的锁的分类

锁的分类 MySQL锁可以按模式分类为&#xff1a;乐观锁与悲观锁。按粒度分可以分为全局锁、表级锁、页级锁、行级锁。按属性可以分为&#xff1a;共享锁、排它锁。按状态分为&#xff1a;意向共享锁、意向排它锁。按算法分为&#xff1a;间隙锁、临键锁、记录锁。 二、全局锁、表…

ClickHouse与各种组件的关系

ClickHouse和其他组件关系如下&#xff1a; Flink支持ClickHouse Sink支持Hive/SparkSQL数据批量导入ClickHouseHetuEngine支持ClickHouse数据源常用第三方工具如DBeaver支持ClickHouse对接ClickHouse依赖ZooKeeper实现了分布式DDL执行以及ReplicatedMergeTree表主备节点之间的…

多线程—— JUC 的常见类

目录 前言 一、Callable 接口 1.Callable 介绍 2.代码示例 3.创建线程的方式 二、ReentrantLock 类 1.ReentrantLock 介绍 2.代码示例 3.与 synchronized 的区别 三、信号量 Semaphore 类 1.Semaphore 介绍 2.代码示例 3.保证线程安全的方式 四、CountDownLatch …

二、Spring的执行流程

文章目录 1. spring的初始化过程1.1 ClassPathXmlApplicationContext的构造方法1.2 refresh方法&#xff08;核心流程&#xff09;1.2.1 prepareRefresh() 方法1.2.2 obtainFreshBeanFactory() 方法1.2.3 prepareBeanFactory() 方法1.2.4 invokeBeanFactoryPostProcessors() 方…

(linux驱动学习 - 12). IIC 驱动实验

目录 一.IIC 总线驱动相关结构体与函数 1.i2c_adapter 结构体 2.i2c_algorithm 结构体 3.向系统注册设置好的 i2c_adapter 结构体 - i2c_add_adapter 4.向系统注册设置好的 i2c_adapter 结构体 - i2c_add_numbered_adapter 5.删除 I2C 适配器 - i2c_del_adapter 二.IIC 设…

【C++算法】11.滑动窗口_最大连续1的个数lll

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解 题目链接&#xff1a; 1004. 最大连续 1 的个数 III 题目描述&#xff1a; 解法 解法一&#xff1a;暴力枚举zero计数器 转化找出最长的子数组&#xff0c;0的个数不超过k个。 例如&#xf…

计算机网络——有连接传输层协议TCP

序号 序号一般不从0开始&#xff0c;这个在双方建立连接后约定一个数 这样做可以避免网络中滞留的TCP段对新的连接的干扰

Flutter状态管理

StatefulWidget按状态划分StatelessWidgetStatefulWidget 按照作用域划分组件内私有状态实现跨组件状态管理全局状态 状态组件的组成 DataTableInheritedWidget生命周期无状态组件有状态组件initState()didChangeDependencies()build()setState()didUpdateWidget()deactivate()…

Redis 集群 总结

前言 相关系列 《Redis & 目录》&#xff08;持续更新&#xff09;《Redis & 集群 & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Redis & 集群 & 总结》&#xff08;学习总结/最新最准/持续更新&#xff09;《Redis & 集群…

二十二、Python基础语法(模块)

模块(module)&#xff1a;在python中&#xff0c;每个代码文件就是一个模块&#xff0c;在模块中定义的变量、函数、类别人都可以直接使用&#xff0c;如果想要使用别人写好的模块&#xff0c;就必须先导入别人的模块&#xff0c;模块名须满足标识符规则&#xff08;由字母、数…

【国潮来袭】华为原生鸿蒙 HarmonyOS NEXT(5.0)正式发布:鸿蒙诞生以来最大升级,碰一碰、小艺圈选重磅上线

在昨日晚间的原生鸿蒙之夜暨华为全场景新品发布会上&#xff0c;华为原生鸿蒙 HarmonyOS NEXT&#xff08;5.0&#xff09;正式发布。 华为官方透露&#xff0c;截至目前&#xff0c;鸿蒙操作系统在中国市场份额占据 Top2 的领先地位&#xff0c;拥有超过 1.1 亿 的代码行和 6…

布隆过滤器:极简存储,高效检索

引言 在海量数据的存储与检索中&#xff0c;如何在保持快速检索的同时&#xff0c;降低内存占用是个巨大的挑战。有没有一种既能快速检索又能节省内存的方案&#xff1f;布隆过滤器&#xff08;Bloom Filter&#xff09;就是这样一种数据结构。 布隆过滤器的基本原理 如果我…

Vue.js 学习总结(11)—— Vue3 Hook 函数实战总结

前言 在 Vue 3 中&#xff0c;Hook 函数是一种特殊的函数&#xff0c;用于封装可重用的逻辑和状态管理。Hook 函数允许你在 Vue 组件中提取和复用逻辑&#xff0c;而不是将所有逻辑都放在组件的选项对象中。它们可以帮助你更好地组织代码&#xff0c;提高代码的可维护性和可测…

算法题总结(十九)——图论

图论 DFS框架 void dfs(参数) { if (终止条件) {存放结果;return; }for (选择&#xff1a;本节点所连接的其他节点) {处理节点;dfs(图&#xff0c;选择的节点); // 递归回溯&#xff0c;撤销处理结果 } }深搜三部曲 确认递归函数&#xff0c;参数确认终止条件处理目前搜索节…

JAVA基础:IO流 (学习笔记)

IO流 一&#xff0c;IO流的理解 i &#xff1a; input 输入 o&#xff1a;output 输入 流&#xff1a;方式&#xff0c;传递数据的方式---------相当于生活中的“管道”&#xff0c;“车”&#xff0c;将资源从一个位置&#xff0c;传递到另一个位置 二&#xff0c;IO流的分…

从0开始深度学习(16)——暂退法(Dropout)

上一章的过拟合是由于数据不足导致的&#xff0c;但如果我们有比特征多得多的样本&#xff0c;深度神经网络也有可能过拟合 1 扰动的稳健性 经典泛化理论认为&#xff0c;为了缩小训练和测试性能之间的差距&#xff0c;应该以简单的模型为目标&#xff0c;即模型以较小的维度的…