metro 0.82.0 → 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 +18 -18
- 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/IncrementalBundler.js.flow +1 -1
- package/src/Server/symbolicate.js.flow +1 -1
- package/src/Server.js +12 -3
- package/src/Server.js.flow +24 -7
- package/src/lib/TerminalReporter.js +8 -5
- package/src/lib/TerminalReporter.js.flow +16 -8
- package/src/lib/formatBundlingError.js +11 -8
- package/src/lib/formatBundlingError.js.flow +12 -9
- package/src/lib/getPrependedScripts.js.flow +1 -4
- package/src/lib/parseJsonBody.js +35 -0
- package/src/lib/parseJsonBody.js.flow +60 -0
- package/src/shared/types.flow.js.flow +1 -1
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",
|
|
@@ -65,14 +65,14 @@
|
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
|
|
67
67
|
"@babel/plugin-transform-modules-commonjs": "^7.24.8",
|
|
68
|
-
"@react-native/babel-preset": "0.
|
|
69
|
-
"@react-native/metro-babel-transformer": "0.
|
|
68
|
+
"@react-native/babel-preset": "0.78.0",
|
|
69
|
+
"@react-native/metro-babel-transformer": "0.78.0",
|
|
70
70
|
"babel-jest": "^29.7.0",
|
|
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<{
|
|
@@ -36,7 +36,7 @@ export opaque type RevisionId: string = string;
|
|
|
36
36
|
export type OutputGraph = Graph<>;
|
|
37
37
|
|
|
38
38
|
type OtherOptions = $ReadOnly<{
|
|
39
|
-
onProgress:
|
|
39
|
+
onProgress: DeltaBundlerOptions<>['onProgress'],
|
|
40
40
|
shallow: boolean,
|
|
41
41
|
lazy: boolean,
|
|
42
42
|
}>;
|
|
@@ -38,7 +38,7 @@ export type StackFrameOutput = $ReadOnly<{
|
|
|
38
38
|
...IntermediateStackFrame,
|
|
39
39
|
...
|
|
40
40
|
}>;
|
|
41
|
-
type ExplodedSourceMapModule =
|
|
41
|
+
type ExplodedSourceMapModule = ExplodedSourceMap[number];
|
|
42
42
|
type Position = {+line1Based: number, column0Based: number};
|
|
43
43
|
|
|
44
44
|
function createFunctionNameGetter(
|
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,
|
|
@@ -55,8 +55,7 @@ class TerminalReporter {
|
|
|
55
55
|
chalk.reset.dim(` ${path.dirname(localPath)}/`) +
|
|
56
56
|
chalk.bold(path.basename(localPath)) +
|
|
57
57
|
" " +
|
|
58
|
-
progress
|
|
59
|
-
"\n"
|
|
58
|
+
progress
|
|
60
59
|
);
|
|
61
60
|
}
|
|
62
61
|
_logBundleBuildDone(buildID) {
|
|
@@ -241,9 +240,13 @@ class TerminalReporter {
|
|
|
241
240
|
if (currentProgress == null) {
|
|
242
241
|
return;
|
|
243
242
|
}
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
243
|
+
const ratio = Math.min(
|
|
244
|
+
Math.max(
|
|
245
|
+
Math.pow(transformedFileCount / Math.max(totalFileCount, 10), 2),
|
|
246
|
+
currentProgress.ratio
|
|
247
|
+
),
|
|
248
|
+
0.999
|
|
249
|
+
);
|
|
247
250
|
Object.assign(currentProgress, {
|
|
248
251
|
ratio,
|
|
249
252
|
transformedFileCount,
|
|
@@ -146,8 +146,7 @@ class TerminalReporter {
|
|
|
146
146
|
chalk.reset.dim(` ${path.dirname(localPath)}/`) +
|
|
147
147
|
chalk.bold(path.basename(localPath)) +
|
|
148
148
|
' ' +
|
|
149
|
-
progress
|
|
150
|
-
'\n'
|
|
149
|
+
progress
|
|
151
150
|
);
|
|
152
151
|
}
|
|
153
152
|
|
|
@@ -355,9 +354,12 @@ class TerminalReporter {
|
|
|
355
354
|
}
|
|
356
355
|
|
|
357
356
|
/**
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
357
|
+
* Because we know the `totalFileCount` is going to progressively increase
|
|
358
|
+
* starting with 1:
|
|
359
|
+
* - We use Math.max(totalFileCount, 10) to prevent the ratio to raise too
|
|
360
|
+
* quickly when the total file count is low. (e.g 1/2 5/6)
|
|
361
|
+
* - We prevent the ratio from going backwards.
|
|
362
|
+
* - Instead, we use Math.pow(ratio, 2) to as a conservative measure of progress.
|
|
361
363
|
*/
|
|
362
364
|
_updateBundleProgress({
|
|
363
365
|
buildID,
|
|
@@ -373,9 +375,15 @@ class TerminalReporter {
|
|
|
373
375
|
if (currentProgress == null) {
|
|
374
376
|
return;
|
|
375
377
|
}
|
|
376
|
-
|
|
377
|
-
const
|
|
378
|
-
|
|
378
|
+
|
|
379
|
+
const ratio = Math.min(
|
|
380
|
+
Math.max(
|
|
381
|
+
Math.pow(transformedFileCount / Math.max(totalFileCount, 10), 2),
|
|
382
|
+
currentProgress.ratio,
|
|
383
|
+
),
|
|
384
|
+
0.999, // make sure not to go above 99.9% to not get rounded to 100%,
|
|
385
|
+
);
|
|
386
|
+
|
|
379
387
|
Object.assign(currentProgress, {
|
|
380
388
|
ratio,
|
|
381
389
|
transformedFileCount,
|
|
@@ -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',
|
|
@@ -26,10 +26,7 @@ const defaults = require('metro-config/src/defaults/defaults');
|
|
|
26
26
|
|
|
27
27
|
async function getPrependedScripts(
|
|
28
28
|
config: ConfigT,
|
|
29
|
-
options:
|
|
30
|
-
TransformInputOptions,
|
|
31
|
-
{type: $PropertyType<TransformInputOptions, 'type'>, ...},
|
|
32
|
-
>,
|
|
29
|
+
options: Omit<TransformInputOptions, 'type'>,
|
|
33
30
|
resolverOptions: ResolverInputOptions,
|
|
34
31
|
bundler: Bundler,
|
|
35
32
|
deltaBundler: DeltaBundler<>,
|
|
@@ -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;
|
|
@@ -98,7 +98,7 @@ export type SplitBundleOptions = {
|
|
|
98
98
|
+transformOptions: TransformInputOptions,
|
|
99
99
|
+serializerOptions: SerializerOptions,
|
|
100
100
|
+graphOptions: GraphOptions,
|
|
101
|
-
+onProgress:
|
|
101
|
+
+onProgress: DeltaBundlerOptions<>['onProgress'],
|
|
102
102
|
};
|
|
103
103
|
|
|
104
104
|
export type ModuleGroups = {
|