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 +16 -16
- package/src/DeltaBundler/Worker.flow.js +3 -0
- package/src/DeltaBundler/Worker.flow.js.flow +3 -0
- package/src/DeltaBundler/WorkerFarm.js +1 -0
- package/src/DeltaBundler/WorkerFarm.js.flow +6 -0
- package/src/DeltaBundler/types.flow.js.flow +2 -6
- package/src/Server.js +12 -3
- package/src/Server.js.flow +24 -7
- package/src/lib/formatBundlingError.js +11 -8
- package/src/lib/formatBundlingError.js.flow +12 -9
- package/src/lib/parseJsonBody.js +35 -0
- package/src/lib/parseJsonBody.js.flow +60 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "metro",
|
|
3
|
-
"version": "0.82.
|
|
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.
|
|
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.
|
|
46
|
-
"metro-cache": "0.82.
|
|
47
|
-
"metro-cache-key": "0.82.
|
|
48
|
-
"metro-config": "0.82.
|
|
49
|
-
"metro-core": "0.82.
|
|
50
|
-
"metro-file-map": "0.82.
|
|
51
|
-
"metro-resolver": "0.82.
|
|
52
|
-
"metro-runtime": "0.82.
|
|
53
|
-
"metro-source-map": "0.82.
|
|
54
|
-
"metro-symbolicate": "0.82.
|
|
55
|
-
"metro-transform-plugins": "0.82.
|
|
56
|
-
"metro-transform-worker": "0.82.
|
|
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.
|
|
75
|
-
"metro-memory-fs": "0.82.
|
|
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
|
|
|
@@ -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 =
|
|
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
|
-
|
|
978
|
-
|
|
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",
|
package/src/Server.js.flow
CHANGED
|
@@ -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
|
-
) =>
|
|
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
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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;
|