metro 0.76.1 → 0.76.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.
Files changed (40) hide show
  1. package/package.json +21 -23
  2. package/src/DeltaBundler/Serializers/baseJSBundle.js +1 -0
  3. package/src/DeltaBundler/Serializers/baseJSBundle.js.flow +1 -0
  4. package/src/DeltaBundler/Serializers/helpers/js.js +22 -6
  5. package/src/DeltaBundler/Serializers/helpers/js.js.flow +24 -6
  6. package/src/DeltaBundler/Serializers/helpers/processModules.js +2 -0
  7. package/src/DeltaBundler/Serializers/helpers/processModules.js.flow +3 -0
  8. package/src/DeltaBundler/Serializers/hmrJSBundle.js +1 -0
  9. package/src/DeltaBundler/Serializers/hmrJSBundle.js.flow +1 -0
  10. package/src/DeltaBundler/types.d.ts +4 -2
  11. package/src/Server/symbolicate.js +33 -5
  12. package/src/Server/symbolicate.js.flow +40 -9
  13. package/src/Server.js +5 -3
  14. package/src/Server.js.flow +4 -2
  15. package/src/index.d.ts +16 -6
  16. package/src/index.flow.js +9 -2
  17. package/src/index.flow.js.flow +23 -4
  18. package/src/node-haste/DependencyGraph.js +6 -1
  19. package/src/node-haste/DependencyGraph.js.flow +7 -1
  20. package/src/shared/types.flow.js.flow +0 -1
  21. package/types/Asset.d.ts +0 -25
  22. package/types/Bundler.d.ts +0 -39
  23. package/types/DeltaBundler/Graph.d.ts +0 -40
  24. package/types/DeltaBundler/Serializers/getRamBundleInfo.d.ts +0 -18
  25. package/types/DeltaBundler/Worker.d.ts +0 -47
  26. package/types/DeltaBundler/types.d.ts +0 -167
  27. package/types/DeltaBundler.d.ts +0 -58
  28. package/types/IncrementalBundler.d.ts +0 -97
  29. package/types/ModuleGraph/worker/collectDependencies.d.ts +0 -27
  30. package/types/Server/MultipartResponse.d.ts +0 -31
  31. package/types/Server.d.ts +0 -113
  32. package/types/index.d.ts +0 -151
  33. package/types/lib/CountingSet.d.ts +0 -48
  34. package/types/lib/TerminalReporter.d.ts +0 -27
  35. package/types/lib/contextModule.d.ts +0 -22
  36. package/types/lib/getGraphId.d.ts +0 -11
  37. package/types/lib/reporting.d.ts +0 -140
  38. package/types/node-haste/DependencyGraph.d.ts +0 -59
  39. package/types/shared/output/bundle.d.ts +0 -31
  40. package/types/shared/types.d.ts +0 -138
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metro",
3
- "version": "0.76.1",
3
+ "version": "0.76.2",
4
4
  "description": "🚇 The JavaScript bundler for React Native.",
5
5
  "main": "src/index.js",
6
6
  "bin": "src/cli.js",
@@ -34,22 +34,22 @@
34
34
  "invariant": "^2.2.4",
35
35
  "jest-worker": "^27.2.0",
36
36
  "lodash.throttle": "^4.1.1",
37
- "metro-babel-transformer": "0.76.1",
38
- "metro-cache": "0.76.1",
39
- "metro-cache-key": "0.76.1",
40
- "metro-config": "0.76.1",
41
- "metro-core": "0.76.1",
42
- "metro-file-map": "0.76.1",
43
- "metro-inspector-proxy": "0.76.1",
44
- "metro-minify-terser": "0.76.1",
45
- "metro-minify-uglify": "0.76.1",
46
- "metro-react-native-babel-preset": "0.76.1",
47
- "metro-resolver": "0.76.1",
48
- "metro-runtime": "0.76.1",
49
- "metro-source-map": "0.76.1",
50
- "metro-symbolicate": "0.76.1",
51
- "metro-transform-plugins": "0.76.1",
52
- "metro-transform-worker": "0.76.1",
37
+ "metro-babel-transformer": "0.76.2",
38
+ "metro-cache": "0.76.2",
39
+ "metro-cache-key": "0.76.2",
40
+ "metro-config": "0.76.2",
41
+ "metro-core": "0.76.2",
42
+ "metro-file-map": "0.76.2",
43
+ "metro-inspector-proxy": "0.76.2",
44
+ "metro-minify-terser": "0.76.2",
45
+ "metro-minify-uglify": "0.76.2",
46
+ "metro-react-native-babel-preset": "0.76.2",
47
+ "metro-resolver": "0.76.2",
48
+ "metro-runtime": "0.76.2",
49
+ "metro-source-map": "0.76.2",
50
+ "metro-symbolicate": "0.76.2",
51
+ "metro-transform-plugins": "0.76.2",
52
+ "metro-transform-worker": "0.76.2",
53
53
  "mime-types": "^2.1.27",
54
54
  "node-fetch": "^2.2.0",
55
55
  "nullthrows": "^1.1.1",
@@ -63,16 +63,14 @@
63
63
  },
64
64
  "devDependencies": {
65
65
  "@babel/plugin-transform-flow-strip-types": "^7.0.0",
66
- "@types/babel__code-frame": "^7.0.3",
67
- "@types/ws": "^8.5.4",
68
66
  "babel-jest": "^29.2.1",
69
67
  "dedent": "^0.7.0",
70
68
  "jest-snapshot": "^26.5.2",
71
69
  "jest-snapshot-serializer-raw": "^1.2.0",
72
- "metro-babel-register": "0.76.1",
73
- "metro-memory-fs": "0.76.1",
74
- "metro-react-native-babel-preset": "0.76.1",
75
- "metro-react-native-babel-transformer": "0.76.1",
70
+ "metro-babel-register": "0.76.2",
71
+ "metro-memory-fs": "0.76.2",
72
+ "metro-react-native-babel-preset": "0.76.2",
73
+ "metro-react-native-babel-transformer": "0.76.2",
76
74
  "mock-req": "^0.2.0",
77
75
  "mock-res": "^0.6.0",
78
76
  "stack-trace": "^0.0.10"
@@ -24,6 +24,7 @@ function baseJSBundle(entryPoint, preModules, graph, options) {
24
24
  includeAsyncPaths: options.includeAsyncPaths,
25
25
  projectRoot: options.projectRoot,
26
26
  serverRoot: options.serverRoot,
27
+ sourceUrl: options.sourceUrl,
27
28
  };
28
29
 
29
30
  // Do not prepend polyfills or the require runtime when only modules are requested
@@ -39,6 +39,7 @@ function baseJSBundle(
39
39
  includeAsyncPaths: options.includeAsyncPaths,
40
40
  projectRoot: options.projectRoot,
41
41
  serverRoot: options.serverRoot,
42
+ sourceUrl: options.sourceUrl,
42
43
  };
43
44
 
44
45
  // Do not prepend polyfills or the require runtime when only modules are requested
@@ -31,16 +31,32 @@ function getModuleParams(module, options) {
31
31
  const id = options.createModuleId(dependency.absolutePath);
32
32
  if (options.includeAsyncPaths && dependency.data.data.asyncType != null) {
33
33
  hasPaths = true;
34
+ invariant(
35
+ options.sourceUrl != null,
36
+ "sourceUrl is required when includeAsyncPaths is true"
37
+ );
38
+
39
+ // TODO: Only include path if the target is not in the bundle
40
+
41
+ // Construct a server-relative URL for the split bundle, propagating
42
+ // most parameters from the main bundle's URL.
43
+
44
+ const { searchParams } = new URL(options.sourceUrl);
45
+ searchParams.set("modulesOnly", "true");
46
+ searchParams.set("runModule", "false");
34
47
  const bundlePath = path.relative(
35
48
  options.serverRoot,
36
49
  dependency.absolutePath
37
50
  );
38
- // TODO: Eventually this slicing should be asyncRequire's responsibility
39
- // Strip the file extension
40
- paths[id] = path.join(
41
- path.dirname(bundlePath),
42
- path.basename(bundlePath, path.extname(bundlePath))
43
- );
51
+ paths[id] =
52
+ "/" +
53
+ path.join(
54
+ path.dirname(bundlePath),
55
+ // Strip the file extension
56
+ path.basename(bundlePath, path.extname(bundlePath))
57
+ ) +
58
+ ".bundle?" +
59
+ searchParams.toString();
44
60
  }
45
61
  return id;
46
62
  }
@@ -24,6 +24,7 @@ export type Options = $ReadOnly<{
24
24
  includeAsyncPaths: boolean,
25
25
  projectRoot: string,
26
26
  serverRoot: string,
27
+ sourceUrl: ?string,
27
28
  ...
28
29
  }>;
29
30
 
@@ -48,16 +49,33 @@ function getModuleParams(module: Module<>, options: Options): Array<mixed> {
48
49
  const id = options.createModuleId(dependency.absolutePath);
49
50
  if (options.includeAsyncPaths && dependency.data.data.asyncType != null) {
50
51
  hasPaths = true;
52
+ invariant(
53
+ options.sourceUrl != null,
54
+ 'sourceUrl is required when includeAsyncPaths is true',
55
+ );
56
+
57
+ // TODO: Only include path if the target is not in the bundle
58
+
59
+ // Construct a server-relative URL for the split bundle, propagating
60
+ // most parameters from the main bundle's URL.
61
+
62
+ const {searchParams} = new URL(options.sourceUrl);
63
+ searchParams.set('modulesOnly', 'true');
64
+ searchParams.set('runModule', 'false');
65
+
51
66
  const bundlePath = path.relative(
52
67
  options.serverRoot,
53
68
  dependency.absolutePath,
54
69
  );
55
- // TODO: Eventually this slicing should be asyncRequire's responsibility
56
- // Strip the file extension
57
- paths[id] = path.join(
58
- path.dirname(bundlePath),
59
- path.basename(bundlePath, path.extname(bundlePath)),
60
- );
70
+ paths[id] =
71
+ '/' +
72
+ path.join(
73
+ path.dirname(bundlePath),
74
+ // Strip the file extension
75
+ path.basename(bundlePath, path.extname(bundlePath)),
76
+ ) +
77
+ '.bundle?' +
78
+ searchParams.toString();
61
79
  }
62
80
  return id;
63
81
  },
@@ -21,6 +21,7 @@ function processModules(
21
21
  includeAsyncPaths,
22
22
  projectRoot,
23
23
  serverRoot,
24
+ sourceUrl,
24
25
  }
25
26
  ) {
26
27
  return [...modules]
@@ -34,6 +35,7 @@ function processModules(
34
35
  includeAsyncPaths,
35
36
  projectRoot,
36
37
  serverRoot,
38
+ sourceUrl,
37
39
  }),
38
40
  ]);
39
41
  }
@@ -24,6 +24,7 @@ function processModules(
24
24
  includeAsyncPaths,
25
25
  projectRoot,
26
26
  serverRoot,
27
+ sourceUrl,
27
28
  }: $ReadOnly<{
28
29
  filter?: (module: Module<>) => boolean,
29
30
  createModuleId: string => number,
@@ -31,6 +32,7 @@ function processModules(
31
32
  includeAsyncPaths: boolean,
32
33
  projectRoot: string,
33
34
  serverRoot: string,
35
+ sourceUrl: ?string,
34
36
  }>,
35
37
  ): $ReadOnlyArray<[Module<>, string]> {
36
38
  return [...modules]
@@ -44,6 +46,7 @@ function processModules(
44
46
  includeAsyncPaths,
45
47
  projectRoot,
46
48
  serverRoot,
49
+ sourceUrl,
47
50
  }),
48
51
  ]);
49
52
  }
@@ -50,6 +50,7 @@ function generateModules(sourceModules, graph, options) {
50
50
  function prepareModule(module, graph, options) {
51
51
  const code = wrapModule(module, {
52
52
  ...options,
53
+ sourceUrl: url.format(options.clientUrl),
53
54
  dev: true,
54
55
  });
55
56
  const inverseDependencies = getInverseDependencies(module.path, graph);
@@ -77,6 +77,7 @@ function prepareModule(
77
77
  ): string {
78
78
  const code = wrapModule(module, {
79
79
  ...options,
80
+ sourceUrl: url.format(options.clientUrl),
80
81
  dev: true,
81
82
  });
82
83
 
@@ -8,7 +8,6 @@
8
8
  * @oncall react_native
9
9
  */
10
10
 
11
- import type {SourceLocation} from '@babel/code-frame';
12
11
  import type {JsTransformOptions} from 'metro-transform-worker';
13
12
  import type {RequireContextParams} from '../ModuleGraph/worker/collectDependencies';
14
13
  import type {RequireContext} from '../lib/contextModule';
@@ -55,7 +54,10 @@ export interface TransformResultDependency {
55
54
  */
56
55
  readonly isOptional?: boolean;
57
56
 
58
- readonly locs: ReadonlyArray<SourceLocation>;
57
+ readonly locs: ReadonlyArray<{
58
+ readonly start: {readonly line: number; readonly column: number};
59
+ readonly end: {readonly line: number; readonly column: number};
60
+ }>;
59
61
 
60
62
  /** Context for requiring a collection of modules. */
61
63
  readonly contextParams?: RequireContextParams;
@@ -34,7 +34,7 @@ function createFunctionNameGetter(module) {
34
34
  source: "dummy",
35
35
  });
36
36
  }
37
- async function symbolicate(stack, maps, config) {
37
+ async function symbolicate(stack, maps, config, extraData) {
38
38
  const mapsByUrl = new Map();
39
39
  for (const [url, map] of maps) {
40
40
  mapsByUrl.set(url, map);
@@ -108,7 +108,9 @@ async function symbolicate(stack, maps, config) {
108
108
  function symbolicateFrame(frame) {
109
109
  const module = findModule(frame);
110
110
  if (!module) {
111
- return frame;
111
+ return {
112
+ ...frame,
113
+ };
112
114
  }
113
115
  if (!Array.isArray(module.map)) {
114
116
  throw new Error(
@@ -117,7 +119,9 @@ async function symbolicate(stack, maps, config) {
117
119
  }
118
120
  const originalPos = findOriginalPos(frame, module);
119
121
  if (!originalPos) {
120
- return frame;
122
+ return {
123
+ ...frame,
124
+ };
121
125
  }
122
126
  const methodName =
123
127
  findFunctionName(originalPos, module) ?? frame.methodName;
@@ -129,15 +133,39 @@ async function symbolicate(stack, maps, config) {
129
133
  column: originalPos.column0Based,
130
134
  };
131
135
  }
136
+
137
+ /**
138
+ * `customizeFrame` allows for custom modifications of the symbolicated frame in a stack.
139
+ * It can be used to collapse stack frames that are not relevant to users, pointing them
140
+ * to more relevant product code instead.
141
+ *
142
+ * An example usecase is a library throwing an error while sanitizing inputs from product code.
143
+ * In some cases, it's more useful to point the developer looking at the error towards the product code directly.
144
+ */
132
145
  async function customizeFrame(frame) {
133
146
  const customizations =
134
147
  (await config.symbolicator.customizeFrame(frame)) || {};
135
148
  return {
136
149
  ...frame,
137
- collapse: false,
138
150
  ...customizations,
139
151
  };
140
152
  }
141
- return Promise.all(stack.map(symbolicateFrame).map(customizeFrame));
153
+
154
+ /**
155
+ * `customizeStack` allows for custom modifications of a symbolicated stack.
156
+ * Where `customizeFrame` operates on individual frames, this hook can process the entire stack in context.
157
+ *
158
+ * Note: `customizeStack` has access to an `extraData` object which can be used to attach metadata
159
+ * to the error coming in, to be used by the customizeStack hook.
160
+ */
161
+ async function customizeStack(symbolicatedStack) {
162
+ return await config.symbolicator.customizeStack(
163
+ symbolicatedStack,
164
+ extraData
165
+ );
166
+ }
167
+ return Promise.all(stack.map(symbolicateFrame).map(customizeFrame)).then(
168
+ customizeStack
169
+ );
142
170
  }
143
171
  module.exports = symbolicate;
@@ -30,9 +30,13 @@ export type StackFrameInput = {
30
30
  +methodName: ?string,
31
31
  ...
32
32
  };
33
- export type StackFrameOutput = $ReadOnly<{
33
+ export type IntermediateStackFrame = {
34
34
  ...StackFrameInput,
35
- +collapse: boolean,
35
+ collapse?: boolean,
36
+ ...
37
+ };
38
+ export type StackFrameOutput = $ReadOnly<{
39
+ ...IntermediateStackFrame,
36
40
  ...
37
41
  }>;
38
42
  type ExplodedSourceMapModule = $ElementType<ExplodedSourceMap, number>;
@@ -63,6 +67,7 @@ async function symbolicate(
63
67
  stack: $ReadOnlyArray<StackFrameInput>,
64
68
  maps: Iterable<[string, ExplodedSourceMap]>,
65
69
  config: ConfigT,
70
+ extraData: mixed,
66
71
  ): Promise<$ReadOnlyArray<StackFrameOutput>> {
67
72
  const mapsByUrl = new Map<?string, ExplodedSourceMap>();
68
73
  for (const [url, map] of maps) {
@@ -157,10 +162,10 @@ async function symbolicate(
157
162
  return null;
158
163
  }
159
164
 
160
- function symbolicateFrame(frame: StackFrameInput): StackFrameInput {
165
+ function symbolicateFrame(frame: StackFrameInput): IntermediateStackFrame {
161
166
  const module = findModule(frame);
162
167
  if (!module) {
163
- return frame;
168
+ return {...frame};
164
169
  }
165
170
  if (!Array.isArray(module.map)) {
166
171
  throw new Error(
@@ -169,7 +174,7 @@ async function symbolicate(
169
174
  }
170
175
  const originalPos = findOriginalPos(frame, module);
171
176
  if (!originalPos) {
172
- return frame;
177
+ return {...frame};
173
178
  }
174
179
  const methodName =
175
180
  findFunctionName(originalPos, module) ?? frame.methodName;
@@ -182,15 +187,41 @@ async function symbolicate(
182
187
  };
183
188
  }
184
189
 
190
+ /**
191
+ * `customizeFrame` allows for custom modifications of the symbolicated frame in a stack.
192
+ * It can be used to collapse stack frames that are not relevant to users, pointing them
193
+ * to more relevant product code instead.
194
+ *
195
+ * An example usecase is a library throwing an error while sanitizing inputs from product code.
196
+ * In some cases, it's more useful to point the developer looking at the error towards the product code directly.
197
+ */
185
198
  async function customizeFrame(
186
- frame: StackFrameInput,
187
- ): Promise<StackFrameOutput> {
199
+ frame: IntermediateStackFrame,
200
+ ): Promise<IntermediateStackFrame> {
188
201
  const customizations =
189
202
  (await config.symbolicator.customizeFrame(frame)) || {};
190
- return {...frame, collapse: false, ...customizations};
203
+ return {...frame, ...customizations};
191
204
  }
192
205
 
193
- return Promise.all(stack.map(symbolicateFrame).map(customizeFrame));
206
+ /**
207
+ * `customizeStack` allows for custom modifications of a symbolicated stack.
208
+ * Where `customizeFrame` operates on individual frames, this hook can process the entire stack in context.
209
+ *
210
+ * Note: `customizeStack` has access to an `extraData` object which can be used to attach metadata
211
+ * to the error coming in, to be used by the customizeStack hook.
212
+ */
213
+ async function customizeStack(
214
+ symbolicatedStack: Array<IntermediateStackFrame>,
215
+ ): Promise<Array<IntermediateStackFrame>> {
216
+ return await config.symbolicator.customizeStack(
217
+ symbolicatedStack,
218
+ extraData,
219
+ );
220
+ }
221
+
222
+ return Promise.all(stack.map(symbolicateFrame).map(customizeFrame)).then(
223
+ customizeStack,
224
+ );
194
225
  }
195
226
 
196
227
  module.exports = symbolicate;
package/src/Server.js CHANGED
@@ -884,7 +884,8 @@ class Server {
884
884
  debug("Start symbolication");
885
885
  /* $FlowFixMe: where is `rawBody` defined? Is it added by the `connect` framework? */
886
886
  const body = await req.rawBody;
887
- const stack = JSON.parse(body).stack.map((frame) => {
887
+ const parsedBody = JSON.parse(body);
888
+ const stack = parsedBody.stack.map((frame) => {
888
889
  if (frame.file && frame.file.includes("://")) {
889
890
  return {
890
891
  ...frame,
@@ -913,10 +914,11 @@ class Server {
913
914
  Array.from(urls.values()).map(this._explodedSourceMapForURL, this)
914
915
  );
915
916
  debug("Performing fast symbolication");
916
- const symbolicatedStack = await await symbolicate(
917
+ const symbolicatedStack = await symbolicate(
917
918
  stack,
918
919
  zip(urls.values(), sourceMaps),
919
- this._config
920
+ this._config,
921
+ parsedBody.extraData ?? {}
920
922
  );
921
923
  debug("Symbolication done");
922
924
  res.end(
@@ -1094,7 +1094,8 @@ class Server {
1094
1094
  debug('Start symbolication');
1095
1095
  /* $FlowFixMe: where is `rawBody` defined? Is it added by the `connect` framework? */
1096
1096
  const body = await req.rawBody;
1097
- const stack = JSON.parse(body).stack.map(frame => {
1097
+ const parsedBody = JSON.parse(body);
1098
+ const stack = parsedBody.stack.map(frame => {
1098
1099
  if (frame.file && frame.file.includes('://')) {
1099
1100
  return {
1100
1101
  ...frame,
@@ -1126,10 +1127,11 @@ class Server {
1126
1127
  );
1127
1128
 
1128
1129
  debug('Performing fast symbolication');
1129
- const symbolicatedStack = await await symbolicate(
1130
+ const symbolicatedStack = await symbolicate(
1130
1131
  stack,
1131
1132
  zip(urls.values(), sourceMaps),
1132
1133
  this._config,
1134
+ parsedBody.extraData ?? {},
1133
1135
  );
1134
1136
 
1135
1137
  debug('Symbolication done');
package/src/index.d.ts CHANGED
@@ -14,22 +14,25 @@ export * from './ModuleGraph/worker/collectDependencies';
14
14
  export * from './Server';
15
15
  export * from './lib/reporting';
16
16
 
17
- import type {Server as HttpServer} from 'http';
17
+ import type {EventEmitter} from 'events';
18
+ import type {IncomingMessage, Server as HttpServer} from 'http';
18
19
  import type {Server as HttpsServer} from 'https';
19
20
  import type {
20
21
  ConfigT,
21
22
  InputConfigT,
22
- loadConfig,
23
23
  MetroConfig,
24
24
  Middleware,
25
25
  } from 'metro-config';
26
26
  import type {CustomTransformOptions} from 'metro-babel-transformer';
27
27
  import type {ReadOnlyGraph} from './DeltaBundler/types';
28
- import type {Server} from 'ws';
28
+ import type {Duplex} from 'stream';
29
29
  import Yargs = require('yargs');
30
30
  import type {default as MetroServer, ServerOptions} from './Server';
31
31
  import type {OutputOptions, RequestOptions} from './shared/types';
32
32
 
33
+ export {loadConfig, mergeConfig, resolveConfig} from 'metro-config';
34
+ export {Terminal} from 'metro-core';
35
+
33
36
  export {HttpServer, HttpsServer};
34
37
 
35
38
  interface MetroMiddleWare {
@@ -43,6 +46,15 @@ export interface RunMetroOptions extends ServerOptions {
43
46
  waitForBundler?: boolean;
44
47
  }
45
48
 
49
+ interface WebsocketServer extends EventEmitter {
50
+ handleUpgrade<T = WebsocketServer>(
51
+ request: IncomingMessage,
52
+ socket: Duplex,
53
+ upgradeHead: Buffer,
54
+ callback: (client: T, request: IncomingMessage) => void,
55
+ ): void;
56
+ }
57
+
46
58
  export interface RunServerOptions {
47
59
  hasReducedPerformance?: boolean;
48
60
  host?: string;
@@ -63,7 +75,7 @@ export interface RunServerOptions {
63
75
  waitForBundler?: boolean;
64
76
  watch?: boolean;
65
77
  websocketEndpoints?: {
66
- [path: string]: Server;
78
+ [path: string]: WebsocketServer;
67
79
  };
68
80
  }
69
81
 
@@ -114,8 +126,6 @@ export function runMetro(
114
126
  options?: RunMetroOptions,
115
127
  ): Promise<MetroServer>;
116
128
 
117
- export {loadConfig};
118
-
119
129
  export function createConnectMiddleWare(
120
130
  config: ConfigT,
121
131
  options?: RunMetroOptions,
package/src/index.flow.js CHANGED
@@ -23,11 +23,17 @@ const chalk = require("chalk");
23
23
  const fs = require("fs");
24
24
  const http = require("http");
25
25
  const https = require("https");
26
- const { getDefaultConfig, loadConfig, mergeConfig } = require("metro-config");
26
+ const {
27
+ getDefaultConfig,
28
+ loadConfig,
29
+ mergeConfig,
30
+ resolveConfig,
31
+ } = require("metro-config");
32
+ const { Terminal } = require("metro-core");
27
33
  const { InspectorProxy } = require("metro-inspector-proxy");
28
34
  const net = require("net");
29
35
  const { parse } = require("url");
30
- const ws = require("ws");
36
+ exports.Terminal = Terminal;
31
37
  async function getConfig(config) {
32
38
  const defaultConfig = await getDefaultConfig(config.projectRoot);
33
39
  return mergeConfig(defaultConfig, config);
@@ -70,6 +76,7 @@ async function runMetro(config, options) {
70
76
  exports.runMetro = runMetro;
71
77
  exports.loadConfig = loadConfig;
72
78
  exports.mergeConfig = mergeConfig;
79
+ exports.resolveConfig = resolveConfig;
73
80
  const createConnectMiddleware = async function (config, options) {
74
81
  const metroServer = await runMetro(config, options);
75
82
  let enhancedMiddleware = metroServer.processRequest;
@@ -15,7 +15,8 @@ import type {CustomResolverOptions} from 'metro-resolver';
15
15
  import type {ReadOnlyGraph} from './DeltaBundler';
16
16
  import type {ServerOptions} from './Server';
17
17
  import type {OutputOptions, RequestOptions} from './shared/types.flow.js';
18
- import type {Server as HttpServer} from 'http';
18
+ import type EventEmitter from 'events';
19
+ import type {IncomingMessage, Server as HttpServer} from 'http';
19
20
  import type {Server as HttpsServer} from 'https';
20
21
  import type {
21
22
  ConfigT,
@@ -24,6 +25,7 @@ import type {
24
25
  Middleware,
25
26
  } from 'metro-config/src/configTypes.flow';
26
27
  import type {CustomTransformOptions} from 'metro-transform-worker';
28
+ import type {Duplex} from 'stream';
27
29
  import typeof Yargs from 'yargs';
28
30
 
29
31
  const makeBuildCommand = require('./commands/build');
@@ -38,11 +40,16 @@ const chalk = require('chalk');
38
40
  const fs = require('fs');
39
41
  const http = require('http');
40
42
  const https = require('https');
41
- const {getDefaultConfig, loadConfig, mergeConfig} = require('metro-config');
43
+ const {
44
+ getDefaultConfig,
45
+ loadConfig,
46
+ mergeConfig,
47
+ resolveConfig,
48
+ } = require('metro-config');
49
+ const {Terminal} = require('metro-core');
42
50
  const {InspectorProxy} = require('metro-inspector-proxy');
43
51
  const net = require('net');
44
52
  const {parse} = require('url');
45
- const ws = require('ws');
46
53
 
47
54
  type MetroMiddleWare = {
48
55
  attachHmrServer: (httpServer: HttpServer | HttpsServer) => void,
@@ -56,6 +63,15 @@ export type RunMetroOptions = {
56
63
  waitForBundler?: boolean,
57
64
  };
58
65
 
66
+ interface WebsocketServer extends EventEmitter {
67
+ handleUpgrade<T = WebsocketServer>(
68
+ request: IncomingMessage,
69
+ socket: Duplex,
70
+ upgradeHead: Buffer,
71
+ callback: (client: T, request: IncomingMessage) => void,
72
+ ): void;
73
+ }
74
+
59
75
  export type RunServerOptions = $ReadOnly<{
60
76
  hasReducedPerformance?: boolean,
61
77
  host?: string,
@@ -69,7 +85,7 @@ export type RunServerOptions = $ReadOnly<{
69
85
  waitForBundler?: boolean,
70
86
  watch?: boolean,
71
87
  websocketEndpoints?: $ReadOnly<{
72
- [path: string]: typeof ws.Server,
88
+ [path: string]: WebsocketServer,
73
89
  }>,
74
90
  }>;
75
91
 
@@ -121,6 +137,8 @@ export type RunBuildOptions = {
121
137
  type BuildCommandOptions = {} | null;
122
138
  type ServeCommandOptions = {} | null;
123
139
 
140
+ exports.Terminal = Terminal;
141
+
124
142
  export type {MetroConfig};
125
143
 
126
144
  async function getConfig(config: InputConfigT): Promise<ConfigT> {
@@ -174,6 +192,7 @@ async function runMetro(
174
192
  exports.runMetro = runMetro;
175
193
  exports.loadConfig = loadConfig;
176
194
  exports.mergeConfig = mergeConfig;
195
+ exports.resolveConfig = resolveConfig;
177
196
 
178
197
  const createConnectMiddleware = async function (
179
198
  config: ConfigT,
@@ -105,8 +105,13 @@ class DependencyGraph extends EventEmitter {
105
105
  _getClosestPackage(filePath) {
106
106
  const parsedPath = path.parse(filePath);
107
107
  const root = parsedPath.root;
108
- let dir = parsedPath.dir;
108
+ let dir = path.join(parsedPath.dir, parsedPath.base);
109
109
  do {
110
+ // If we've hit a node_modules directory, the closest package was not
111
+ // found (`filePath` was likely nonexistent).
112
+ if (path.basename(dir) === "node_modules") {
113
+ return null;
114
+ }
110
115
  const candidate = path.join(dir, "package.json");
111
116
  if (this._fileSystem.exists(candidate)) {
112
117
  return candidate;
@@ -152,8 +152,14 @@ class DependencyGraph extends EventEmitter {
152
152
  _getClosestPackage(filePath: string): ?string {
153
153
  const parsedPath = path.parse(filePath);
154
154
  const root = parsedPath.root;
155
- let dir = parsedPath.dir;
155
+ let dir = path.join(parsedPath.dir, parsedPath.base);
156
+
156
157
  do {
158
+ // If we've hit a node_modules directory, the closest package was not
159
+ // found (`filePath` was likely nonexistent).
160
+ if (path.basename(dir) === 'node_modules') {
161
+ return null;
162
+ }
157
163
  const candidate = path.join(dir, 'package.json');
158
164
  if (this._fileSystem.exists(candidate)) {
159
165
  return candidate;