工程实践

生产环境与 SourceMap

by Cheng, 2021-06-02


为了不泄露源代码,很多项目都在生产环境关闭了 sourcemap,这样带来显而易见的困扰就是一旦线上除了问题,比较难调试。一个比较容易理解和实施的方案就是将 sourcemap 部署到内网的服务器上,这样当线上出问题之后,通过连接到内网就可以通过 sourcemap 进行问题定位和调试,同时处于外网的访问者因为访问不到对应的服务器,同时也就无法看到源码。这里分组件和实际的项目进行阐述具体的方法。

组件

通常组件都是通过编译成对应的 npm 包进行分发,组件在被其它项目使用时,还会被进一步打包,因此将 sourcemap 与组件一起分发作用不大,因此很少看到通过 npm install 安装的第三方库提供 sourcemap。这里采取一个折中的方案:将组件生成的 sourcemap 部署到内网环境的服务器,同时改写生成的 js 文件里的 sourceMappingURL。

对于使用 babel, rollup 之类的工具生成的组件库,没法使用 webpack.SourceMapDevToolPlugin这么方便的工具,好在手写一个也不难,核心就是文件遍历与正则替换。

const fs = require("fs");
const path = require("path");
const LIBRARY_PATH = path.resolve(__dirname, "..", "lib");
const PUBLIC_PATH = "http://192.168.5.20:8080/sourcemaps";
function getAllScripts(dir) {
  let files = [];
  const entries = fs.readdirSync(dir);
  for (let entry of entries) {
    const item = path.join(dir, entry);
    if (fs.statSync(item).isDirectory()) {
      files = files.concat(getAllScripts(item));
    }
    if (/.js$/.test(item)) {
      files.push(item);
    }
  }
  return files;
}

const files = getAllScripts(LIBRARY_PATH);
files.forEach((file) => {
  const dir = path.relative(
    path.resolve(__dirname, "..", "lib"),
    path.dirname(file)
  );
  let content = fs.readFileSync(file, { encoding: "utf8" });
  content = content.replace(
    /\/\/# sourceMappingURL=(.+\.map)/,
    `\n//# sourceMappingURL=${PUBLIC_PATH}/${dir}/$1`
  );
  fs.writeFileSync(file, content, { encoding: "utf8" });
});

在改写了组件 js 文件里的 sourceMappingURL 之后,可以在 publish 组件的同时,将 sourcemap scp 到服务器。注意,这些文件里的 sourcemap 是没法直接使用的,因为浏览器指挥加载通过 script 标签引入的 javascript 文件里边的 sourcemap, 也就是说,需要引入组件的项目在构建的同时,引入组件里的 sourcemap 然后输出到最终生成的项目 javascript。

项目

使用 webpack 构建的项目可以通过source-map-loader将第三方组件提供的 sourcemap 文件进行打包,但是 source-map-loader 当前的版本不支持从 http/https 远程请求 sourcemap,幸运的是对 source-map-loader 进行 patch 很简单。这样就可以将组件的 sourcemap 一同打包了。

项目可以使用 webpack.SourceMapDevToolPlugin 很容易的通过 publicPath 改写 sourceMappingURL, 最后将 sourcemap 上传到对应的内网服务器即可。

webpackSourceMap

作者: Cheng

2025 © typecho & elise & Cheng