Source Map
source map
是一个十分重要的概念,在调试阶段,我们往往需要查看打包后的代码,但是打包后的代码是经过压缩、混淆、合并等操作的,这使得我们很难直接阅读和 调试,而 source map
就是为了解决这个问题而诞生的。
source map
本质上是一个 json
文件,里面包含了打包后的代码与源码之间的 映射关系,这样在调试时,我们就可以根据 source map
中的位置信息找到打包后的代码在源码中的位置,从而调试的时候可以快速定位到源码中的位置。
在 rollup
中,source map
的生成与 rollup
的 output
配置项息息相关,比如 output.sourcemap
配置项,当其值为 true
时,rollup
会为打包后的文件生成 source map
,当其值为 'inline'
时,rollup
会将 source map
以 base64
编码的形式内联到打包后的文件中,当其值为 'hidden'
时,rollup
会为打包后的文件生成 source map
,但是不会将其内联到打包后的文件中,而是单独生成一个 source map
文件。本章节将详细介绍 rollup
中 source map
的生成原理。
代码映射的原理
映射本质上就是提供映射关系给编辑器。换句话说我需要提供一份信息,信息中需要包含了 打包后的代码的字符坐标 和与其相对应的 源代码中字符坐标,同时还要告知 源代码文件位置。也就是后续会提到的 mapping
记录的位置信息:
- 修改后位置的行坐标
- 修改后位置的列坐标
- 原副本的路径
- 与其对应的源码位置的行坐标
- 与其对应的源码位置的列坐标
有了这些引导信息,那么编辑器就可以很容易的在调试阶段跳转调试节点对应的源代码。
但是需要每一个字符都做映射吗?
答案是不一定需要,虽然映射每一个字符可以做到绝对的精确,但是这样会产生大量的映射关系,从而导致 source map
文件过大,那么接下来就介绍有哪些代码映射方式。
代码映射的方式
代码映射最基本的要点就是我要为哪些字符做 mapping
处理,sourcemap
中的 mapping
信息可以包含如下几种:
- 对单词的词边界进行映射:记录了代码的单词边界的
mapping
信息。 - 对每一个字符进行坐标映射:最精确的
mapping
记录方式,记录了代码的每一个字符的坐标的mapping
信息。 - 对词法位置坐标进行映射:记录了代码的词法位置的
mapping
信息。 - 对行坐标进行映射:最基本的
mapping
记录方式,记录了代码的行数的mapping
信息,不能再减少记录,否则可能存在映射异常的问题。
以上四种方式均可以做到调试源码时构建产物与源码之间的映射关系。但区别在于不同方式的映射 精度 不一样,生成的 sourcemap
体积也不一样。
magic string
其实就是为了便捷做代码映射,详细的介绍可以参考 文章,这里不对 magic string
的内部实现做阐述。
magic string
内部并不单一使用上述四种方式之一,而是根据不同的场景使用不同的方式,同时还为用户提供可控的映射精度,以达到最优的映射效果,具体可参考 magic string 中 mapping
信息的确定。
实现原理
rollup
与 magic-string
的关系
rollup
中 source map
的生成原理主要依赖于 magic string
包。
从 Rollup
和 magic-string
的 git 更新日志中可以看出,两个仓库的作者均是 Rich Harris。Rollup
项目在早期(magic-string
最早的第三个发布版本 v0.5.1
)就使用了 magic-string
来处理源码修改后的 mapping
映射关系,个人感觉 magic-string
包很可能是为了 Rollup
项目为了优化 source map
的生成而设计的。
rollup
的 mapping
记录方式
rollup
深度集成了 magic-string
,除了 magic-string
的默认记录方式之下还会额外提供 词法位置
的标记。
那么如何实现 词法位置
的标记呢?
rollup
借助 swc
的能力来生成标准 ast
结构。生成 ast
之前需要对源码进行 词法分析。
补充说明
词法分析的实现可参考 acorn 的词法分析实现。不过 rollup
中的词法分析是由 swc
来实现的,生成的 ast
结构是非标准 ast
,rollup
需要对 swc
转译后的 ast
结构做些许调整为标准 ast
结构。不过即使不是标准 ast
结构,但 词法位置
的信息均会提供。
词法分析 阶段会为每一个 ast
节点记录与源码对应的 词法位置。换句话说词法分析阶段会为每一个 ast node
提供与其相对应源码位置的 start
与 end
索引信息。
有了 ast node
提供的 词法位置
信息,rollup
就可以在初始化 ast
节点(即 DFS 回溯阶段)时,额外记录 词法位置
供后续 magic-string
生成对应的 mapping
信息。
class NodeBase extends ExpressionEntity implements ExpressionNode {
initialise(): void {
this.scope.context.magicString.addSourcemapLocation(this.start);
this.scope.context.magicString.addSourcemapLocation(this.end);
}
}