Dev server `.map` ignores the source map a custom serializer returns

未关闭
#2,214 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
45/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
活跃
技术栈
javascript, react-native

调研方向

Read the dev server paths that build .map responses and handle /symbolicate, then compare them with Server.build() and the serializer contract in the issue. Use the ErrorBundle.js integration fixture and the provided custom serializer to reproduce the mismatched source locations. Done means the served map and /symbolicate resolve frames consistently with the served bundle, including when a custom serializer returns a map.

由索引模型根据 Issue 内容生成。

描述

Filing this AI-reported issue related to #1284 for reference:


Summary

When serializer.customSerializer returns {code, map}, the dev server serves the serializer's code for .bundle requests, but it never calls the serializer for .map requests. Instead, .map is built from Metro's default placement: prepend followed by the graph's modules sorted by id. If the serializer lays the bundle out differently from baseJSBundle, the served map doesn't match the served bundle. This happens even when the serializer follows the contract: it treats preModules and graph as read-only and returns a correct map. Server.build() (runBuild, metro build) already uses the returned map, so only the dev server is affected.

The common case is a serializer that adds a module before the app code by prepending it to a copy of preModules. Today, the only way to get such a module into the dev server's .map is to mutate preModules in place. The ReadonlyArray type forbids that, and because preModules is the graph revision's shared prepend array, a serializer that doesn't dedupe adds another copy on every request.

#1284 makes .map use the serializer's map. /symbolicate has the same gap, since it places modules the same way without calling the serializer, and #1284 doesn't change it.

Reproduction

This uses Metro main at d233537, the integration test fixture packages/metro/src/integration_tests/basic_bundle/ErrorBundle.js, and this serializer:

const baseJSBundle = require('metro/private/DeltaBundler/Serializers/baseJSBundle').default;
const {sourceMapString} = require('metro/private/DeltaBundler/Serializers/sourceMapString');
const bundleToString = require('metro/private/lib/bundleToString').default;
const CountingSet = require('metro/private/lib/CountingSet').default;

const code = 'var a = 1;\nvar b = 2;\nvar c = 3;';
const injected = {
  path: '__injected__',
  dependencies: new Map(),
  inverseDependencies: new CountingSet(),
  getSource: () => Buffer.from(code),
  output: [{type: 'js/script/virtual', data: {code, lineCount: 3, map: []}}],
};

async function customSerializer(entryPoint, preModules, graph, options) {
  // A copy: preModules is never mutated.
  const pre = [preModules[0], injected, ...preModules.slice(1)];
  const modules = [...graph.dependencies.values()].sort(
    (a, b) => options.createModuleId(a.path) - options.createModuleId(b.path),
  );
  return {
    code: bundleToString(baseJSBundle(entryPoint, pre, graph, options)).code,
    map: sourceMapString([...pre, ...modules], {
      excludeSource: false,
      processModuleFilter: options.processModuleFilter,
      shouldAddToIgnoreList: options.shouldAddToIgnoreList,
      getSourceUrl: options.getSourceUrl,
    }),
  };
}
  1. Start the dev server with this serializer and fetch /ErrorBundle.bundle?platform=ios&dev=true&minify=false.
  2. Find the generated position of new Error('SOURCEMAP: in the response (line 683, column 16).
  3. Look that position up in /ErrorBundle.map?platform=ios&dev=true&minify=false with source-map, and send the same frame to /symbolicate.
Expected main With #1284
.map ErrorBundle.js:18 ErrorBundle.js:23 ErrorBundle.js:18
/symbolicate ErrorBundle.js:18 ErrorBundle.js:23 ErrorBundle.js:23

Without the custom serializer, both resolve to line 18.

Expected behavior

For a given bundle URL, .map and /symbolicate should agree with the code the dev server serves for that URL. When a custom serializer returns a map, the dev server should use it.

Notes
  • A serializer that changes the layout but returns only a string (for example, a banner, or modules emitted in a different order) can't be fixed by using its map, because there isn't one. Those also give wrong .map and /symbolicate results today. Fixing them needs either a rule that a layout-changing serializer must return a map, or a way for serializers to add pre-modules that Metro places itself.
  • Sentry's serializer prepends modules to a copy of preModules in this way. From reading its source (not reproduced), its module-metadata module is also injected in dev when moduleMetadata is set, and should shift dev .map and /symbolicate results by one line.
主要语言
JavaScript
星标
5.7k
派生
715
平均合并
1 天 15 小时
30 天内合并 PR
96

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

react/metro 的其他 Issue

查看 react/metro 的全部 Issue

相似的 Issue

更多 JavaScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。