ESCheck工具原理解析及增强实现(3)

简介: 完整demo3代码如有一些边界情况也是在 catch err部分根据 message做一下过滤即可比如下代码

ESCheck工具原理解析及增强实现(2):https://developer.aliyun.com/article/1394836?spm=a2c6h.13148508.setting.15.50c84f0e0ugg2G

完整demo3代码

如有一些边界情况也是在 catch err部分根据 message做一下过滤即可

比如下代码

var { boolean:hello } = {}

image.png

完整demo4代码

做一下过滤,catch message添加过滤逻辑

const filterMessage = [/^The keyword /]
if (filterMessage.find((r) => r.test(error.message))) {
  return
}

调整后的报错信息就是解构赋值的语法错误了

image.png

完整demo5代码

至此基本能完成了单文件的多次es-check检测,虽然不像mpx-es-check那样用直白的语言直接说面是什么语法。但还有改进空间嘛,后面再单独写个文章做个工具检测目标代码用了哪些ES6+特性。就不再这里赘述了

sourcemap解析

这个主要针对检测资源是build产物的一项优化,通过source-map解析报错信息对应的源码

前面的代码我们只获取了问题源码的起止字符位置start,end

通过source-map解析,首先要获取报错代码在资源中的行列信息

这里通过acorn.getLineInfo方法可直接获取行列信息

// 省略了重复代码
const codeErrorList: any[] = []
acornWalk.full(ast, (node, _state, _type) => {
  // 节点对应的源码
  const codeSnippet = code.slice(node.start, node.end)
  try {
    acorn.parse(codeSnippet, {
      ecmaVersion: '5'
    } as any)
  } catch (error) {
    const locStart = acorn.getLineInfo(code, node.start)
    const locEnd = acorn.getLineInfo(code, node.end)
    codeErrorList.push({
      loc: {
        start: locStart,
        end: locEnd
      }
    })
  }
})
console.dir(codeErrorList, {
  depth: 3
})

结果如下,完整demo1代码

image.png

有了行列号,我们就可以根据*.map文件进行源码的解析

默认map文件由原文件名加.map后缀

function getSourcemapFileContent(file: string) {
  const sourceMapFile = `${file}.map`
  if (fs.existsSync(sourceMapFile)) {
    return fs.readFileSync(sourceMapFile, 'utf-8')
  }
  return ''
}

解析map文件直接使用 sourceMap.SourceMapConsumer,返回的实例是1个Promise,使用时需注意

function parseSourceMap(code: string) {
  const consumer = new sourceMap.SourceMapConsumer(code)
  return consumer
}

根据前面source-map解析的例子,把这块逻辑放到checkCode之后即可

const code = fs.readFileSync(file, 'utf-8')
// ps: checkCode 即为上一小节实现代码检测能力的封装
const codeErrorList = checkCode(code)
const sourceMapContent = getSourcemapFileContent(file)
if (sourceMapContent) {
  const consumer = await parseSourceMap(sourceMapContent)
  codeErrorList.forEach((v) => {
    // 解析获取原文件信息
    const smStart = consumer.originalPositionFor({
      line: v.loc.start.line,
      column: v.loc.start.column
    })
    const smEnd = consumer.originalPositionFor({
      line: v.loc.end.line,
      column: v.loc.end.column
    })
    // start对应源码所在行的代码
    const sourceStartCode = consumer
      .sourceContentFor(smStart.source!)
      ?.split(/\r?\n/g)[smStart.line! - 1]
    const sourceEndCode = consumer
      .sourceContentFor(smEnd.source!)
      ?.split(/\r?\n/g)[smEnd.line! - 1]
    // 省略 console 打印代码
  })
}

完整demo2代码

image.png

这块就对齐了mpx-es-checksource-map解析能力

HTML支持

这个就比较好办了,只需要将script里的内容提取出来,调用上述的checkCode方法,然后对结果进行一个行列号的优化即可

这里提取的方法很多,可以

  1. 正则匹配
  2. cheerio:像jQuery一样操作
  3. parse5:生成AST,递归遍历需要的节点
  4. htmlparser2:生成AST,相比parse5更加,解析策略更加”包容“

小试对比了一下,最后发现是用parse5更符合这个场景(编写代码更少)

import * as parse5 from 'parse5'
const htmlAST = parse5.parse(code, {
  sourceCodeLocationInfo: true
})

下面是生成的AST示例: astexplorer.net/#/gist/0372…

通过nodeName或者tagName就可以区分节点类型,这里简单写个遍历方法

节点可以通过childNodes属性区分是否包含子节点

function traverse(ast: any, traverseSchema: Record<string, any>) {
  traverseSchema?.[ast?.nodeName]?.(ast)
  if (ast?.nodeName !== ast?.tagName) {
    traverseSchema?.[ast?.tagName]?.(ast)
  }
  ast?.childNodes?.forEach((n) => {
    traverse(n, traverseSchema)
  })
}

这里遍历一下demo代码生成的ast

traverse(htmlAST, {
  script(node: any) {
    const code = `${node.childNodes.map((n) => n.value)}`
    const loc = node.sourceCodeLocation
    if (code) {
      console.log(code)
      console.log(loc)
    }
  }
})

完整demo1代码

image.png

获得对应的源码后就可以调用之前的checkCode方法,对错误行号做一个拼接即可得到错误信息

traverse(htmlAST, {
  script(node: any) {
    const code = `${node.childNodes.map((n) => n.value)}`
    const loc = node.sourceCodeLocation
    if (code) {
      const errList = checkCode(code)
      errList.forEach((err) => {
        console.log(
          'line:',
          loc.startLine + err.loc.start.line - 1,
          'column:',
          err.loc.start.column
        )
        console.log(err.source)
        console.log()
      })
    }
  }
})

完整demo2代码

image.png

组建CLI能力

这里就不再赘述CLI过程代码,核心的已在前面阐述,这里直接上最终成品的使用演示,参数同es-check保持一致

npm i @sugarat/es-check -g

检测目标文件

escheck es5 testProject/**/*.js testProject/**/*.html

image.png

日志输出到文件

escheck es5 testProject/**/*.js testProject/**/*.html --out

image.png

最终对比

image.png

取了2者的优点相结合然后做了一定的增强

最后

当然这个工具可能存在bug,遗漏部分场景等情况,读者试用可以评论区给反馈,或者库里直接提issues

有其它功能上的建议也可评论区留言交流

完整源码移步=>Github

参考

  • es-check:社区出品
  • mpx-es-check:滴滴出品 MPX 框架的配套工具


相关文章
|
1天前
|
机器学习/深度学习 存储 算法
卷积神经网络(CNN)的数学原理解析
卷积神经网络(CNN)的数学原理解析
32 1
卷积神经网络(CNN)的数学原理解析
|
1天前
|
传感器 数据采集 存储
岩土工程监测仪器之一:振弦采集仪的工作原理解析
岩土工程监测仪器之一:振弦采集仪的工作原理解析
岩土工程监测仪器之一:振弦采集仪的工作原理解析
|
1天前
|
人工智能 自然语言处理 机器人
销售利器大集结:13种智能销售工具全面解析
该文探讨了人工智能在销售领域的应用,测试了13款领先工具,如Zoho CRM、Email Subject Line Generator和ChatGPT Plus等,这些工具通过数据分析、自动化任务和智能交互提升销售效率。然而,使用AI也带来人机交互和数据安全的挑战。文章强调,结合人工智能和人类销售人员的优势是关键,同时应谨慎处理相关问题。
18 4
|
1天前
|
XML JavaScript 数据格式
Beautiful Soup 库的工作原理基于解析器和 DOM(文档对象模型)树的概念
【5月更文挑战第10天】Beautiful Soup 使用解析器(如 html.parser, lxml, html5lib)解析HTML/XML文档,构建DOM树。它提供方法查询和操作DOM,如find(), find_all()查找元素,get_text(), get()提取信息。还能修改DOM,添加、修改或删除元素,并通过prettify()输出格式化字符串。它是处理网页数据的利器,尤其在处理不规则结构时。
37 2
|
1天前
|
机器学习/深度学习 人工智能 数据可视化
号称能打败MLP的KAN到底行不行?数学核心原理全面解析
Kolmogorov-Arnold Networks (KANs) 是一种新型神经网络架构,挑战了多层感知器(mlp)的基础,通过在权重而非节点上使用可学习的激活函数(如b样条),提高了准确性和可解释性。KANs利用Kolmogorov-Arnold表示定理,将复杂函数分解为简单函数的组合,简化了神经网络的近似过程。与mlp相比,KAN在参数量较少的情况下能达到类似或更好的性能,并能直观地可视化,增强了模型的可解释性。尽管仍需更多研究验证其优势,KAN为深度学习领域带来了新的思路。
108 5
|
1天前
|
敏捷开发 测试技术 持续交付
极限编程(XP)原理与技巧:深入解析与实践
【5月更文挑战第8天】极限编程(XP)是一种敏捷开发方法,注重快速反馈、迭代开发和简单设计,以提高软件质量和项目灵活性。关键原则包括客户合作、集体代码所有权、持续集成等。实践中,使用故事卡片描述需求,遵循编程约定,实行TDD,持续重构,结对编程,并定期举行迭代会议。通过理解和应用XP,团队能提升效率,应对变化。
|
1天前
|
缓存 自然语言处理 JavaScript
万字长文深度解析JDK序列化原理及Fury高度兼容的极致性能实现
Fury是一个基于JIT动态编译的高性能多语言原生序列化框架,支持Java/Python/Golang/C++/JavaScript等语言,提供全自动的对象多语言/跨语言序列化能力,以及相比于别的框架最高20~200倍的性能。
168477 0
|
1天前
PandasTA 源码解析(二十三)
PandasTA 源码解析(二十三)
40 0
|
1天前
PandasTA 源码解析(二十二)(3)
PandasTA 源码解析(二十二)
34 0
|
1天前
PandasTA 源码解析(二十二)(2)
PandasTA 源码解析(二十二)
39 2

推荐镜像

更多