SourceMap解析CLI工具实现(2)

简介: 简单做合并后的方法如下

SourceMap解析CLI工具实现(1):https://developer.aliyun.com/article/1394851

简单做合并后的方法如下

const isHTTPSource = (sourcePath: string) =>
  sourcePath.startsWith('http')
async function getSourceMapFilePath(sourceJsPath: string) {
  if (!isHTTPSource(sourceJsPath)) {
    return getLocalSourceMapFilePath(sourceJsPath)
  }
  return getRemoteSourceMapFilePath(sourceJsPath)
}

本小节示例代码

还原报错源码

有了前面的基础,咱们第一个整合功能就可以实现了 根据报错资源信息,还原源码和行列号,先给出方法的定义

interface SourceResult {
  /**
   * 源码
   */
  sourceCode: string
  /**
   * 源码文件路径
   */
  source: string
  /**
   * 行号
   */
  line: number
  /**
   * 列号
   */
  column: number
}
/**
 * 根据报错资源信息,获取对应源码信息
 * @param url 报错资源
 * @param line 行号
 * @param column 列号
 */
async function getErrorSourceResult(
  url: string,
  line: number,
  column: number
): Promise<SourceResult>

利用上面实现的getSourceMapFilePath,配合source-map的2个API即可实现originalPositionFor,sourceContentFor

import fs from 'fs/promises'
const sourceMapURL = await getSourceMapFilePath(url)
// sourceMap 内容
const sourceMapCode = await (isHTTPSource(sourceMapURL)
  ? getRemoteSource(sourceMapURL).then((v) => v.body)
  : fs.readFile(sourceMapURL, 'utf-8'))
const consumer = await createSourceMapConsumer(sourceMapCode)
// 解析出原来的行列号,源文件路径等信息
const { name, ...rest } = consumer.originalPositionFor({
  line,
  column
})
// 获取源码
const sourceCode = consumer.sourceContentFor(rest.source!)
const result = {
  ...rest,
  sourceCode
}

方便终端里预览结果,可以编写一个printSourceResult方法,友好的打印输出一些内容

getErrorSourceResult(
  'https://script.sugarat.top/js/tests/index.9bb0da5c.js',
  24,
  17596
).then(printResult)

image.png

方法实现如下(详细释义见注释)

/**
 * @param result
 * @param showMaxLine 控制显示的行数
 */
export function printResult(result: SourceResult, showMaxLine = 5) {
  const { sourceCode, source, line, column } = result
  // 源码拆成数租
  const lines = sourceCode.split('\n')
  // 打印错误路径
  console.log(`error in  ${source}:${line}:${column}`)
  console.log()
  // 计算要展示的行的起始位置,起始行号不能小于1
  const startLine = Math.max(1, line - Math.floor(showMaxLine / 2))
  // 结束位置不能大于总行数
  const endLine = Math.min(lines.length, startLine + showMaxLine - 1)
  const showCode = lines
    // 截取需要展示的内容
    .slice(startLine - 1, endLine)
    .map(
      (v, idx) =>
        // 加上黄色行号
        `${yellowStr(startLine + idx)} ${
          // 针对错误的行进行下划线+红色展示
          idx + startLine === line
            ? // 从错误的列号开始展示
              v.slice(0, column - 1) + redStr(underlineStr(v.slice(column - 1)))
            : v
        }`
    )
    .join('\n')
  console.log(showCode)
}

打印彩色的场景有限,这里直接将需要的效果颜色对应的ANSI Escape codechalk库中截取出来

image.png

const underlineStr = (v: any) => `\x1B[4m${v}\x1B[24m`
const yellowStr = (v: any) => `\x1B[33m${v}\x1B[39m`
const redStr = (v: any) => `\x1B[31m${v}\x1B[39m`

到此第一个功能的核心代码就封装好了

本小节示例代码

完整source生成

都知道通过sourceMap可以获取完整的源码,所以一般的非开源应用,都是对sourceMap文件做了环境隔离,防止源码泄露。

这部分就封装1个方法,实现将sourceMap中包含的所有源文件输出到本地指定目录

首先实现1个方法,将sourceMap中需要的信息解析出来

export async function getSourcesBySourceMapCode(sourceMapCode: string) {
  const consumer = await createSourceMapConsumer(sourceMapCode)
  const { sources } = consumer
  const result = sources.map((source) => {
    return {
      source,
      code: consumer.sourceContentFor(source)
    }
  })
  return result
}

配合文件操作(fs模块),将内容输出到文件系统

import { existsSync, mkdirSync, writeFileSync } from 'fs'
async function outPutSources(
  sources: SourceItem[],
  outPutDir = 'source-map-result/project'
) {
  for (const sourceItem of sources) {
    const { source, code } = sourceItem
    const filepath = path.resolve(process.cwd(), outPutDir, source)
    if (!existsSync(path.dirname(filepath))) {
      mkdirSync(path.dirname(filepath), { recursive: true })
    }
    writeFileSync(filepath, code, 'utf-8')
  }
}

示例代码与运行结果如下

getRemoteSource(
  'https://script.sugarat.top/js/tests/index.9bb0da5c.js.map'
).then(async ({ body }) => {
  const sources = await getSourcesBySourceMapCode(body)
  console.log(sources.length, '个文件')
  outPutSources(sources)
})

image.png

本小节示例代码

到此常用的2个能力的核心实现就完成了,下面将把其封装为一个CLI工具,方便接入使用

封装CLI

基于commander进行实践

parse指令

首先是指令的定义

主要功能就是将指定的 error js 资源的通过sourcemap还原出具体的报错源码

program
  // sourceUrl 格式 <url>[:line][:column]
  .command('parse <sourceUrl>')
  .description('parse error form url source')
  .alias('p')
  // 标明sourceUrl 是否为 sourceMap 资源
  .option('-s, --source-map', 'set url source as sourceMap type')
  // 单独设置行号
  .option('-l, --line <number>', 'set line number')
  // 单独设置列号
  .option('-c, --column <number>', 'set column number')
  // 将结果输出到文件
  .option('-o, --output [string]', 'set log output dir')
  // 设置展示的错误信息行数
  .option('-n, --show-num <number>', 'set show error source lines', '5')
  .action(parseCommand)

后续的处理逻辑只需要把url,line,column3个参数传给前面实现的getErrorSourceResult方法即可

效果如下

image.png

本小节源码

sources指令

sources指令定义

program
  .command('sources <sourceUrl>')
  .description('generating source files by source-map')
  .alias('s')
  .option('-s, --source-map', 'set url source as sourceMap type')
  .option('-o, --output [string]', 'set log output dir')
  .action(sourcesCommand)

image.png

本小节源码

最后

这个CLI本身能力比较简单,依赖的核心库也只有source-map。主要用于弥补缺失平台自动解析source-map能力的场景,协助定位js error的报错源码

后续再出一篇在线sourcemap解析的工具,功能与CLI类似,不过是Web版的

CLI完整源码见GitHub

附录

其它同类 Web&CLI 工具

Web

  • decodeSourceMap

CLI

  • restore-source-tree
  • source-map-tools
  • source-map-cli
  • source-map-to-source
  • kaifu
  • @hl-cli/restore-code


相关文章
|
8月前
|
机器学习/深度学习 人工智能 JSON
Resume Matcher:增加面试机会!开源AI简历优化工具,一键解析简历和职位描述并优化
Resume Matcher 是一款开源AI简历优化工具,通过解析简历和职位描述,提取关键词并计算文本相似性,帮助求职者优化简历内容,提升通过自动化筛选系统(ATS)的概率,增加面试机会。
902 18
Resume Matcher:增加面试机会!开源AI简历优化工具,一键解析简历和职位描述并优化
|
7月前
|
存储 人工智能 API
离线VS强制登录?Apipost与Apifox的API工具理念差异深度解析
在代码开发中,工具是助手还是枷锁?本文通过对比Apipost和Apifox在断网环境下的表现,探讨API工具的选择对开发自由度的影响。Apifox强制登录限制了离线使用,而Apipost支持游客模式与本地存储,尊重开发者数据主权。文章从登录策略、离线能力、协作模式等方面深入分析,揭示工具背后的设计理念与行业趋势,帮助开发者明智选择,掌握数据控制权并提升工作效率。
|
9月前
|
数据采集 存储 调度
BeautifulSoup VS Scrapy:如何选择适合的HTML解析工具?
在Python网页抓取领域,BeautifulSoup和Scrapy是两款备受推崇的工具。BeautifulSoup易于上手、灵活性高,适合初学者和简单任务;Scrapy则是一个高效的爬虫框架,内置请求调度、数据存储等功能,适合大规模数据抓取和复杂逻辑处理。两者结合使用可以发挥各自优势,例如用Scrapy进行请求调度,用BeautifulSoup解析HTML。示例代码展示了如何在Scrapy中设置代理IP、User-Agent和Cookies,并使用BeautifulSoup解析响应内容。选择工具应根据项目需求,简单任务选BeautifulSoup,复杂任务选Scrapy。
192 1
BeautifulSoup VS Scrapy:如何选择适合的HTML解析工具?
|
8月前
|
数据可视化 测试技术 API
前后端分离开发:如何高效调试API?有工具 vs 无工具全解析
在前后端分离开发中,API调试至关重要。本文探讨有无调试工具时如何高效调试API,重点分析Postman、Swagger等工具优势及无工具代码调试方法。通过实际场景如用户登录接口,对比两者特性。同时介绍Apipost-Hepler(IDEA插件),将可视化与代码调试结合,提供全局请求头配置、历史记录保存等功能,优化团队协作与开发效率,助力API调试进入全新阶段。
|
8月前
|
JSON 监控 物联网
WebSocket 调试全攻略:核心解析、工具选择与对比!
WebSocket 是一种全双工、实时交互的网络通信协议,适用于即时通信、实时数据流、多人协作、IoT 等场景。调试 WebSocket 时,工具应具备握手管理、实时消息收发、自定义 Header、消息大小告警、分组管理、多连接支持和断线重现等功能。主流调试工具如 Postman、ApiPost 和 ApiFox 各有优劣:Postman 界面友好适合基础调试;ApiPost 支持高级功能如消息分组和自动重连;ApiFox 则强化了多连接支持。选择工具时需根据具体需求和团队熟悉度决定。
|
8月前
|
数据可视化 测试技术 API
前后端分离开发:如何高效调试API?有工具 vs 无工具全解析
在前后端分离的开发模式中,API 调试的效率直接影响项目的质量和交付速度。通过本文的对比分析,我们可以看到无工具调试模式虽具备灵活性和代码复用能力,但在操作便利性和团队协作上稍显不足。而传统的外部调试工具带来了可视化、高效协作与扩展性,却可能存在工具切换带来的开发链路断层问题。Apipost-Hepler 融合了两者的优势,让开发者无需离开熟悉的 IDEA 环境,就能享受可视化调试工具的强大功能。
236 5
|
8月前
|
JSON 监控 物联网
#WebSocket 调试全攻略:Postman、Apipost和Apifox核心解析、工具选择与对比!
WebSocket 是一种现代化的全双工通信协议,允许客户端和服务端通过持久连接实时双向传输数据。它适用于即时通讯、实时通知、金融行情、在线协作、物联网等场景。调试 WebSocket 时,工具应具备握手管理、实时消息收发、自定义 Header、消息大小监控、分组管理、多连接支持等功能。
694 1
|
11月前
|
数据可视化 项目管理
个人和团队都好用的年度复盘工具:看板与KPT方法解析
本文带你了解高效方法KPT复盘法(Keep、Problem、Try),结合看板工具,帮助你理清头绪,快速完成年度复盘。
839 7
个人和团队都好用的年度复盘工具:看板与KPT方法解析
|
10月前
|
网络协议 Unix Linux
深入解析:Linux网络配置工具ifconfig与ip命令的全面对比
虽然 `ifconfig`作为一个经典的网络配置工具,简单易用,但其功能已经不能满足现代网络配置的需求。相比之下,`ip`命令不仅功能全面,而且提供了一致且简洁的语法,适用于各种网络配置场景。因此,在实际使用中,推荐逐步过渡到 `ip`命令,以更好地适应现代网络管理需求。
429 11
|
10月前
|
监控 数据可视化 数据挖掘
直播电商复盘全解析:如何通过工具提升团队效率
直播电商作为新兴商业模式,正改变传统零售格局。其成功不仅依赖主播表现和产品吸引力,更需团队高效协作与分工优化。复盘是提升执行力的关键环节,通过总结经验、发现问题、优化流程,结合在线工具如板栗看板,可提升复盘效率。明确团队角色、建立沟通机制、制定优化方案,确保数据驱动决策,从而在竞争中保持领先。

推荐镜像

更多
  • DNS