Skip to content

Source Map

source map 是一个十分重要的概念,在调试阶段,我们往往需要查看打包后的代码,但是打包后的代码是经过压缩、混淆、合并等操作的,这使得我们很难直接阅读和 调试,而 source map 就是为了解决这个问题而诞生的。

source map 本质上是一个 json 文件,里面包含了打包后的代码与源码之间的 映射关系,这样在调试时,我们就可以根据 source map 中的位置信息找到打包后的代码在源码中的位置,从而调试的时候可以快速定位到源码中的位置。

rollup 中,source map 的生成与 rollupoutput 配置项息息相关,比如 output.sourcemap 配置项,当其值为 true 时,rollup 会为打包后的文件生成 source map,当其值为 'inline' 时,rollup 会将 source mapbase64 编码的形式内联到打包后的文件中,当其值为 'hidden' 时,rollup 会为打包后的文件生成 source map,但是不会将其内联到打包后的文件中,而是单独生成一个 source map 文件。本章节将详细介绍 rollupsource map 的生成原理。

代码映射的原理

映射本质上就是提供映射关系给编辑器。换句话说我需要提供一份信息,信息中需要包含了 打包后的代码的字符坐标 和与其相对应的 源代码中字符坐标,同时还要告知 源代码文件位置。也就是后续会提到的 mapping 记录的位置信息:

  • 修改后位置的行坐标
  • 修改后位置的列坐标
  • 原副本的路径
  • 与其对应的源码位置的行坐标
  • 与其对应的源码位置的列坐标

有了这些引导信息,那么编辑器就可以很容易的在调试阶段跳转调试节点对应的源代码。

但是需要每一个字符都做映射吗?

答案是不一定需要,虽然映射每一个字符可以做到绝对的精确,但是这样会产生大量的映射关系,从而导致 source map 文件过大,那么接下来就介绍有哪些代码映射方式。

代码映射的方式

代码映射最基本的要点就是我要为哪些字符做 mapping 处理,sourcemap 中的 mapping 信息可以包含如下几种:

  1. 对单词的词边界进行映射:记录了代码的单词边界的 mapping 信息。
  2. 对每一个字符进行坐标映射:最精确的 mapping 记录方式,记录了代码的每一个字符的坐标的 mapping 信息。
  3. 对词法位置坐标进行映射:记录了代码的词法位置的 mapping 信息。
  4. 对行坐标进行映射:最基本的 mapping 记录方式,记录了代码的行数的 mapping 信息,不能再减少记录,否则可能存在映射异常的问题。

以上四种方式均可以做到调试源码时构建产物与源码之间的映射关系。但区别在于不同方式的映射 精度 不一样,生成的 sourcemap 体积也不一样。

magic string 其实就是为了便捷做代码映射,详细的介绍可以参考 文章,这里不对 magic string 的内部实现做阐述。

magic string 内部并不单一使用上述四种方式之一,而是根据不同的场景使用不同的方式,同时还为用户提供可控的映射精度,以达到最优的映射效果,具体可参考 magic string 中 mapping 信息的确定

实现原理

rollupmagic-string 的关系

rollupsource map 的生成原理主要依赖于 magic string 包。

Rollupmagic-stringgit 更新日志中可以看出,两个仓库的作者均是 Rich HarrisRollup 项目在早期(magic-string 最早的第三个发布版本 v0.5.1)就使用了 magic-string 来处理源码修改后的 mapping 映射关系,个人感觉 magic-string 包很可能是为了 Rollup 项目为了优化 source map 的生成而设计的。

rollupmapping 记录方式

rollup 深度集成了 magic-string,除了 magic-string 的默认记录方式之下还会额外提供 词法位置 的标记。

那么如何实现 词法位置 的标记呢?

rollup 借助 swc 的能力来生成标准 ast 结构。生成 ast 之前需要对源码进行 词法分析

补充说明

词法分析的实现可参考 acorn 的词法分析实现。不过 rollup 中的词法分析是由 swc 来实现的,生成的 ast 结构是非标准 astrollup 需要对 swc 转译后的 ast 结构做些许调整为标准 ast 结构。不过即使不是标准 ast 结构,但 词法位置 的信息均会提供。

词法分析 阶段会为每一个 ast 节点记录与源码对应的 词法位置。换句话说词法分析阶段会为每一个 ast node 提供与其相对应源码位置的 startend 索引信息。

有了 ast node 提供的 词法位置 信息,rollup 就可以在初始化 ast 节点(即 DFS 回溯阶段)时,额外记录 词法位置 供后续 magic-string 生成对应的 mapping 信息。

ts
class NodeBase extends ExpressionEntity implements ExpressionNode {
  initialise(): void {
    this.scope.context.magicString.addSourcemapLocation(this.start);
    this.scope.context.magicString.addSourcemapLocation(this.end);
  }
}

Contributors

Changelog

Discuss

Released under the CC BY-SA 4.0 License. (2619af4)