metro 0.82.1 → 0.82.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metro",
3
- "version": "0.82.1",
3
+ "version": "0.82.2",
4
4
  "description": "🚇 The JavaScript bundler for React Native.",
5
5
  "main": "src/index.js",
6
6
  "bin": "src/cli.js",
@@ -36,24 +36,24 @@
36
36
  "error-stack-parser": "^2.0.6",
37
37
  "flow-enums-runtime": "^0.0.6",
38
38
  "graceful-fs": "^4.2.4",
39
- "hermes-parser": "0.25.1",
39
+ "hermes-parser": "0.28.1",
40
40
  "image-size": "^1.0.2",
41
41
  "invariant": "^2.2.4",
42
42
  "jest-worker": "^29.7.0",
43
43
  "jsc-safe-url": "^0.2.2",
44
44
  "lodash.throttle": "^4.1.1",
45
- "metro-babel-transformer": "0.82.1",
46
- "metro-cache": "0.82.1",
47
- "metro-cache-key": "0.82.1",
48
- "metro-config": "0.82.1",
49
- "metro-core": "0.82.1",
50
- "metro-file-map": "0.82.1",
51
- "metro-resolver": "0.82.1",
52
- "metro-runtime": "0.82.1",
53
- "metro-source-map": "0.82.1",
54
- "metro-symbolicate": "0.82.1",
55
- "metro-transform-plugins": "0.82.1",
56
- "metro-transform-worker": "0.82.1",
45
+ "metro-babel-transformer": "0.82.2",
46
+ "metro-cache": "0.82.2",
47
+ "metro-cache-key": "0.82.2",
48
+ "metro-config": "0.82.2",
49
+ "metro-core": "0.82.2",
50
+ "metro-file-map": "0.82.2",
51
+ "metro-resolver": "0.82.2",
52
+ "metro-runtime": "0.82.2",
53
+ "metro-source-map": "0.82.2",
54
+ "metro-symbolicate": "0.82.2",
55
+ "metro-transform-plugins": "0.82.2",
56
+ "metro-transform-worker": "0.82.2",
57
57
  "mime-types": "^2.1.27",
58
58
  "nullthrows": "^1.1.1",
59
59
  "serialize-error": "^2.1.0",
@@ -71,8 +71,8 @@
71
71
  "dedent": "^0.7.0",
72
72
  "jest-snapshot": "^29.7.0",
73
73
  "jest-snapshot-serializer-raw": "^1.2.0",
74
- "metro-babel-register": "0.82.1",
75
- "metro-memory-fs": "0.82.1",
74
+ "metro-babel-register": "0.82.2",
75
+ "metro-memory-fs": "0.82.2",
76
76
  "mock-req": "^0.2.0",
77
77
  "mock-res": "^0.6.0",
78
78
  "stack-trace": "^0.0.10"
@@ -11,6 +11,9 @@ function asDeserializedBuffer(value) {
11
11
  if (value && value.type === "Buffer") {
12
12
  return Buffer.from(value.data);
13
13
  }
14
+ if (ArrayBuffer.isView(value)) {
15
+ return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
16
+ }
14
17
  return null;
15
18
  }
16
19
  async function transform(
@@ -66,6 +66,9 @@ function asDeserializedBuffer(value: any): Buffer | null {
66
66
  if (value && value.type === 'Buffer') {
67
67
  return Buffer.from(value.data);
68
68
  }
69
+ if (ArrayBuffer.isView(value)) {
70
+ return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
71
+ }
69
72
  return null;
70
73
  }
71
74
 
@@ -73,6 +73,7 @@ class WorkerFarm {
73
73
  env,
74
74
  },
75
75
  numWorkers,
76
+ workerSchedulingPolicy: "in-order",
76
77
  });
77
78
  }
78
79
  _computeWorkerKey(method, filename) {
@@ -128,6 +128,12 @@ class WorkerFarm {
128
128
  enableWorkerThreads: this._config.transformer.unstable_workerThreads,
129
129
  forkOptions: {env},
130
130
  numWorkers,
131
+ // Prefer using lower numbered available workers repeatedly rather than
132
+ // spreading jobs over the whole pool evenly (round-robin). This is more
133
+ // likely to use faster, hot workers earlier in graph traversal, when
134
+ // we want to be able to fan out as quickly as possible, and therefore
135
+ // gets us to full speed faster.
136
+ workerSchedulingPolicy: 'in-order',
131
137
  });
132
138
  }
133
139
 
@@ -89,13 +89,9 @@ export type ReadOnlyDependencies<T = MixedOutput> = $ReadOnlyMap<
89
89
  Module<T>,
90
90
  >;
91
91
 
92
- export type TransformInputOptions = $Diff<
92
+ export type TransformInputOptions = Omit<
93
93
  JsTransformOptions,
94
- {
95
- inlinePlatform: boolean,
96
- inlineRequires: boolean,
97
- ...
98
- },
94
+ 'inlinePlatform' | 'inlineRequires',
99
95
  >;
100
96
 
101
97
  export type GraphInputOptions = $ReadOnly<{
package/src/Server.js CHANGED
@@ -17,6 +17,7 @@ const ResourceNotFoundError = require("./IncrementalBundler/ResourceNotFoundErro
17
17
  const bundleToString = require("./lib/bundleToString");
18
18
  const formatBundlingError = require("./lib/formatBundlingError");
19
19
  const getGraphId = require("./lib/getGraphId");
20
+ const parseJsonBody = require("./lib/parseJsonBody");
20
21
  const parseOptionsFromUrl = require("./lib/parseOptionsFromUrl");
21
22
  const splitBundleOptions = require("./lib/splitBundleOptions");
22
23
  const transformHelpers = require("./lib/transformHelpers");
@@ -933,10 +934,13 @@ class Server {
933
934
  });
934
935
  async _symbolicate(req, res) {
935
936
  const getCodeFrame = (urls, symbolicatedStack) => {
937
+ const allFramesCollapsed = symbolicatedStack.every(
938
+ ({ collapse }) => collapse
939
+ );
936
940
  for (let i = 0; i < symbolicatedStack.length; i++) {
937
941
  const { collapse, column, file, lineNumber } = symbolicatedStack[i];
938
942
  if (
939
- collapse ||
943
+ (!allFramesCollapsed && collapse) ||
940
944
  lineNumber == null ||
941
945
  (file != null && urls.has(file))
942
946
  ) {
@@ -974,8 +978,13 @@ class Server {
974
978
  createActionStartEntry("Symbolicating")
975
979
  );
976
980
  debug("Start symbolication");
977
- const body = await req.rawBody;
978
- const parsedBody = JSON.parse(body);
981
+ let parsedBody;
982
+ if ("rawBody" in req) {
983
+ const body = await req.rawBody;
984
+ parsedBody = JSON.parse(body);
985
+ } else {
986
+ parsedBody = await parseJsonBody(req);
987
+ }
979
988
  const rewriteAndNormalizeStackFrame = (frame, lineNumber) => {
980
989
  invariant(
981
990
  frame != null && typeof frame === "object",
@@ -24,7 +24,7 @@ import type {
24
24
  import type {RevisionId} from './IncrementalBundler';
25
25
  import type {GraphId} from './lib/getGraphId';
26
26
  import type {Reporter} from './lib/reporting';
27
- import type {StackFrameOutput} from './Server/symbolicate';
27
+ import type {StackFrameInput, StackFrameOutput} from './Server/symbolicate';
28
28
  import type {
29
29
  BundleOptions,
30
30
  GraphOptions,
@@ -38,7 +38,6 @@ import type {ConfigT, RootPerfLogger} from 'metro-config/src/configTypes.flow';
38
38
  import type {
39
39
  ActionLogEntryData,
40
40
  ActionStartLogEntry,
41
- LogEntry,
42
41
  } from 'metro-core/src/Logger';
43
42
  import type {CustomResolverOptions} from 'metro-resolver/src/types';
44
43
  import type {CustomTransformOptions} from 'metro-transform-worker';
@@ -61,6 +60,7 @@ const ResourceNotFoundError = require('./IncrementalBundler/ResourceNotFoundErro
61
60
  const bundleToString = require('./lib/bundleToString');
62
61
  const formatBundlingError = require('./lib/formatBundlingError');
63
62
  const getGraphId = require('./lib/getGraphId');
63
+ const parseJsonBody = require('./lib/parseJsonBody');
64
64
  const parseOptionsFromUrl = require('./lib/parseOptionsFromUrl');
65
65
  const splitBundleOptions = require('./lib/splitBundleOptions');
66
66
  const transformHelpers = require('./lib/transformHelpers');
@@ -661,7 +661,7 @@ class Server {
661
661
  +createStartEntry: (context: ProcessStartContext) => ActionLogEntryData,
662
662
  +createEndEntry: (
663
663
  context: ProcessEndContext<T>,
664
- ) => $Rest<ActionStartLogEntry, LogEntry>,
664
+ ) => Partial<ActionStartLogEntry>,
665
665
  +build: (context: ProcessStartContext) => Promise<T>,
666
666
  +delete?: (context: ProcessDeleteContext) => Promise<void>,
667
667
  +finish: (context: ProcessEndContext<T>) => void,
@@ -1220,11 +1220,17 @@ class Server {
1220
1220
  urls: Set<string>,
1221
1221
  symbolicatedStack: $ReadOnlyArray<StackFrameOutput>,
1222
1222
  ) => {
1223
+ const allFramesCollapsed = symbolicatedStack.every(
1224
+ ({collapse}) => collapse,
1225
+ );
1226
+
1223
1227
  for (let i = 0; i < symbolicatedStack.length; i++) {
1224
1228
  const {collapse, column, file, lineNumber} = symbolicatedStack[i];
1225
1229
 
1226
1230
  if (
1227
- collapse ||
1231
+ // If all the frames are collapsed then we should ignore the collapse flag
1232
+ // and always show the first valid frame.
1233
+ (!allFramesCollapsed && collapse) ||
1228
1234
  lineNumber == null ||
1229
1235
  (file != null && urls.has(file))
1230
1236
  ) {
@@ -1262,9 +1268,20 @@ class Server {
1262
1268
  createActionStartEntry('Symbolicating'),
1263
1269
  );
1264
1270
  debug('Start symbolication');
1265
- /* $FlowFixMe: where is `rawBody` defined? Is it added by the `connect` framework? */
1266
- const body = await req.rawBody;
1267
- const parsedBody = JSON.parse(body);
1271
+
1272
+ let parsedBody;
1273
+ if ('rawBody' in req) {
1274
+ // TODO: Remove this branch once we are no longer targeting React Native
1275
+ // < 0.80 and Expo SDK < 53
1276
+ // $FlowFixMe[prop-missing] - rawBody assigned by legacy CLI integrations
1277
+ const body = await req.rawBody;
1278
+ parsedBody = JSON.parse(body);
1279
+ } else {
1280
+ parsedBody = (await parseJsonBody(req)) as {
1281
+ stack: $ReadOnlyArray<StackFrameInput>,
1282
+ extraData: {[string]: mixed},
1283
+ };
1284
+ }
1268
1285
 
1269
1286
  const rewriteAndNormalizeStackFrame = <T>(
1270
1287
  frame: T,
@@ -38,14 +38,17 @@ function formatBundlingError(error) {
38
38
  (error instanceof Error &&
39
39
  (error.type === "TransformError" || error.type === "NotFoundError"))
40
40
  ) {
41
- error.errors = [
42
- {
43
- description: error.message,
44
- filename: error.filename,
45
- lineNumber: error.lineNumber,
46
- },
47
- ];
48
- return serializeError(error);
41
+ return {
42
+ ...serializeError(error),
43
+ type: error.type,
44
+ errors: [
45
+ {
46
+ description: error.message,
47
+ filename: error.filename,
48
+ lineNumber: error.lineNumber,
49
+ },
50
+ ],
51
+ };
49
52
  } else if (error instanceof ResourceNotFoundError) {
50
53
  return {
51
54
  type: "ResourceNotFoundError",
@@ -62,15 +62,18 @@ function formatBundlingError(error: CustomError): FormattedError {
62
62
  (error instanceof Error &&
63
63
  (error.type === 'TransformError' || error.type === 'NotFoundError'))
64
64
  ) {
65
- error.errors = [
66
- {
67
- description: error.message,
68
- filename: error.filename,
69
- lineNumber: error.lineNumber,
70
- },
71
- ];
72
-
73
- return serializeError(error);
65
+ return {
66
+ ...serializeError(error),
67
+ // Ensure the type is passed to the client.
68
+ type: error.type,
69
+ errors: [
70
+ {
71
+ description: error.message,
72
+ filename: error.filename,
73
+ lineNumber: error.lineNumber,
74
+ },
75
+ ],
76
+ };
74
77
  } else if (error instanceof ResourceNotFoundError) {
75
78
  return {
76
79
  type: 'ResourceNotFoundError',
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ const CONTENT_TYPE = "application/json";
4
+ const SIZE_LIMIT = 100 * 1024 * 1024;
5
+ function parseJsonBody(req, options = {}) {
6
+ const { strict = true } = options;
7
+ return new Promise((resolve, reject) => {
8
+ if (strict) {
9
+ const contentType = req.headers["content-type"] || "";
10
+ if (!contentType.includes(CONTENT_TYPE)) {
11
+ reject(new Error(`Invalid content type, expected ${CONTENT_TYPE}`));
12
+ return;
13
+ }
14
+ }
15
+ let size = 0;
16
+ let data = "";
17
+ req.on("data", (chunk) => {
18
+ size += Buffer.byteLength(chunk);
19
+ if (size > SIZE_LIMIT) {
20
+ req.destroy();
21
+ reject(new Error("Request body size exceeds size limit (100MB)"));
22
+ return;
23
+ }
24
+ data += chunk;
25
+ });
26
+ req.on("end", () => {
27
+ try {
28
+ resolve(JSON.parse(data));
29
+ } catch (e) {
30
+ reject(e);
31
+ }
32
+ });
33
+ });
34
+ }
35
+ module.exports = parseJsonBody;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ * @oncall react_native
10
+ */
11
+
12
+ import type {IncomingMessage} from 'http';
13
+
14
+ const CONTENT_TYPE = 'application/json';
15
+ const SIZE_LIMIT = 100 * 1024 * 1024; // 100MB
16
+
17
+ /**
18
+ * Attempt to parse a request body as JSON.
19
+ */
20
+ function parseJsonBody(
21
+ req: IncomingMessage,
22
+ options: {strict?: boolean} = {},
23
+ ): Promise<$FlowFixMe> {
24
+ const {strict = true} = options;
25
+
26
+ return new Promise((resolve, reject) => {
27
+ if (strict) {
28
+ const contentType = req.headers['content-type'] || '';
29
+ if (!contentType.includes(CONTENT_TYPE)) {
30
+ reject(new Error(`Invalid content type, expected ${CONTENT_TYPE}`));
31
+ return;
32
+ }
33
+ }
34
+
35
+ let size = 0;
36
+ let data = '';
37
+
38
+ req.on('data', (chunk: string) => {
39
+ size += Buffer.byteLength(chunk);
40
+
41
+ if (size > SIZE_LIMIT) {
42
+ req.destroy();
43
+ reject(new Error('Request body size exceeds size limit (100MB)'));
44
+ return;
45
+ }
46
+
47
+ data += chunk;
48
+ });
49
+
50
+ req.on('end', () => {
51
+ try {
52
+ resolve(JSON.parse(data));
53
+ } catch (e) {
54
+ reject(e);
55
+ }
56
+ });
57
+ });
58
+ }
59
+
60
+ module.exports = parseJsonBody;