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

Đang mở
#2,214 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
45/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
javascript, react-native

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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.
Ngôn ngữ chính
JavaScript
Star
5.7k
Fork
715
Merge trung bình
1 ngày 15 giờ
Pull request đã merge (30 ngày)
96

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của react/metro

Tất cả issue của react/metro

Issue tương tự

Thêm issue về JavaScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.