SourceMap解析CLI工具实现(1)

简介: 前言SourceMap 大家都不陌生了,通常情况就是产物里的xx.js.map文件里的内容。可用于对压缩混淆后的代码还原,通常用于帮助定位源码问题。区别于构建时的配置(以webpack 的devtool配置项为例)不同配置,source-map暴露的信息程度也就也不一样一般公司里的项目,是会把.map文件上传到内网环境,不耽误问题排查,也不暴露源码个人的开源项目,一般就没这么讲究了,直接和产物一起传了。前端监控平台,一般都支持错误堆栈解析,通过.map,还原出错代码位置调用堆栈信息。有时候没有自动解析的平台可用的时候(比如一些商用监控平台,免费版通常不提供自动source-map

前言

SourceMap 大家都不陌生了,通常情况就是产物里的xx.js.map文件里的内容。

可用于对压缩混淆后的代码还原,通常用于帮助定位源码问题。

区别于构建时的配置(以webpack 的devtool配置项为例)不同配置,source-map暴露的信息程度也就也不一样

一般公司里的项目,是会把.map文件上传到内网环境,不耽误问题排查,也不暴露源码

个人的开源项目,一般就没这么讲究了,直接和产物一起传了。

前端监控平台,一般都支持错误堆栈解析,通过.map,还原出错代码位置调用堆栈信息。

有时候没有自动解析的平台可用的时候(比如一些商用监控平台,免费版通常不提供自动source-map解析能力)

就会实用source-map解析工具凑合一下,包含在线网页,以及CLI版本的。笔者也体验使用了一些(都贴到最后附录列表中,读者有其它推荐的也可评论区补充)。

本文将综合现有的source-map cli解析工具优缺点,取长补短,🐴一个缝合怪(主要包含报错源码解析根据sourceMap文件生成源码2个能力)。

先上个GIF演示,有兴趣的读者可接着往下看

npm i -g @sugarat/source-map-cli

image.png

source-map库的简介

npm地址:source-map

可以用于生成和解析sourcemap的库,本文主要用到其解析的能力,关注API:SourceMapConsumer即可

下面是示例,其返回值consumer是一个Promise

import sourceMap from 'source-map'
function createSourceMapConsumer(sourceMapCode: string) {
  const consumer = new sourceMap.SourceMapConsumer(sourceMapCode)
  return consumer
}

consumer中包含一个sources属性,标明了包含的所用到的源码文件路径信息,通过实例上的sourceContentFor方法即可获取到对应文件(source)源码(sourceCode)

// mapContent 内容来源 https://script.sugarat.top/js/tests/index.9bb0da5c.js.map
;(async () => {
  const consumer = await createSourceMapConsumer(mapContent)
  // [
  // '../../vite/modulepreload-polyfill',
  // '../../node_modules/.pnpm/@vue+shared@3.2.37/node_modules/@vue/shared/dist/shared.esm-bundler.js',
  // 类似的源文件路径
  // ]
  const sourceFileNames = consumer.sources
  // 源文件个数
  const sourceCount = sourceFileNames.length
  // 第一个源文件的内容
  const sourceCode = consumer.sourceContentFor(sourceFileNames[0])
})()

consumer实例上的另一个最常用的方法originalPositionFor可以通过压缩混淆后的代码行列号,解析出源文件信息。

包含源文件source,行号line,列号column,name

// 第一个源文件的内容
const sourceCode = consumer.sourceContentFor(sourceFileNames[0])
// 通过压缩混淆后的代码的行列号,定位到源文件
const sourceInfo = consumer.originalPositionFor({
  line: 24,
  column: 17596
})
// 这个例子的结果如下
console.log(sourceInfo)
//   {
//     source: '../../node_modules/.pnpm/vue-router@4.0.14_vue@3.2.37/node_modules/vue-router/dist/vue-router.esm-bundler.js',
//     line: 2882,
//     column: 12,
//     name: null
//   }

通过如上2个简单的API即可完成常用能力的封装。

本小节示例代码

.map资源加载

通常每个js产物都对应有一份.map文件,文件命名为原文件名.js.map

在不考虑特殊的约定条件情况下,通常情况是在js产物末尾都有1个// #sourceMappingURL=xx.js.map注释表明js资源关联的map文件路径

于是乎咱们,可以先写个方法来获取传入文件对应的sourceMap文件路径

本地sourceMap路径获取

先是考虑本地的情况,通过路径拼接.map与读取文件文件末尾sourceMappingURL2种方式相结合

function getLocalSourceMapFilePath(sourceJsPath: string) {
  // 文件不存在
  if (!existsSync(sourceJsPath)) {
    return NOT_FOUND
  }
  // 先直接判断是否存在.js.map文件存在
  if (existsSync(`${sourceJsPath}.map`)) {
    return `${sourceJsPath}.map`
  }
  // 获取代码里的 // #sourceMappingURL= 注释的内容
  const jsCode = readFileSync(sourceJsPath, 'utf-8')
  const flag = '//# sourceMappingURL='
  const flagIdx = jsCode.lastIndexOf(flag)
  if (flagIdx === -1) {
    return NOT_FOUND
  }
  const sourceMappingURL = jsCode.slice(flagIdx + flag.length)
  // 如果是http路径表明 是绝对路径 直接返回
  if (isHTTPSource(sourceMappingURL)) {
    return sourceMappingURL
  }
  // 否则拼接js资源的目录
  return path.resolve(path.dirname(sourceJsPath), sourceMappingURL)
}

本小节示例代码

远程资源加载

除了本地情况那也有线上资源的情况,比如用于测试的https://script.sugarat.top/js/tests/index.9bb0da5c.js

下面介绍3种常见方式获取http资源,http,axios,fetch

首先是http,node内置网络模块,使用上的感官和web里的XMLHttpRequest差不多,不太优雅

简单场景书写代码量也在可接受的范围

import http from 'http'
import https from 'https'
function getRemoteSource(
  url: string
): Promise<{ body: string; code?: number }> {
  return new Promise((resolve, reject) => {
    // 区别https与http资源
    const HTTP = url.startsWith('https://') ? https : http
    // 通过回调的形式获取
    HTTP.get(url, (res) => {
      // 设置可读流的字符编码
      res.setEncoding('utf-8')
      // 响应内容拼接
      let content = ''
      res.on('data', (chunk) => {
        content += chunk
      })
      // 读完对外暴露内容和状态码
      res.on('end', () => {
        resolve({
          body: content,
          code: res.statusCode
        })
      })
      res.on('error', (err) => {
        reject(err)
      })
    })
  })
}

axios,前端常用的跨平台网络请求库(web/node/其它场景提供adaptor层做适配)

用这个代码量就更简洁了,3行就能搞定

function getRemoteSourceByAxios(url: string) {
  return axios.get(url).then((v) => {
    return {
      code: v.status,
      body: v.data
    }
  })
}

fetch,在web侧已经出现很久了,Node.js>=v17.5.0 内置,低版本可使用第三方的node-fetch

这里使用node-fetch进行举例,使用也是非常简单

import fetch from 'node-fetch'
function getRemoteSourceByFetch(url: string) {
  return fetch(url).then(async (v) => {
    const code = v.status
    const body = await v.text()
    return {
      code,
      body
    }
  })
}

包含但不限于以上三种方式达到需要的目的。本小节示例代码

远程sourceMap路径获取

思路和本地的资源逻辑基本一致,只是内容获取和判断需要走网络,实现如下,接近一半都是重复代码,有优化空间,这里不赘述了

async function getRemoteSourceMapFilePath(sourceJsPath: string) {
  const context = await getRemoteSource(sourceJsPath)
  if (context.code === 404) {
    return NOT_FOUND
  }
  if ((await getRemoteSource(`${sourceJsPath}.map`)).code === 200) {
    return `${sourceJsPath}.map`
  }
  const jsCode = context.body
  const flag = '//# sourceMappingURL='
  const flagIdx = jsCode.lastIndexOf(flag)
  if (flagIdx === -1) {
    return NOT_FOUND
  }
  const sourceMappingURL = jsCode.slice(flagIdx + flag.length)
  if (isHTTPSource(sourceMappingURL)) {
    return sourceMappingURL
  }
  return path.resolve(path.dirname(sourceJsPath), sourceMappingURL)
}

 SourceMap解析CLI工具实现(2):https://developer.aliyun.com/article/1394856?spm=a2c6h.13148508.setting.31.55964f0ez7IHhI

相关文章
|
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