metro 0.70.3 → 0.71.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 (94) hide show
  1. package/package.json +23 -22
  2. package/src/Assets.js.flow +4 -4
  3. package/src/Bundler/util.js +1 -1
  4. package/src/Bundler/util.js.flow +2 -2
  5. package/src/Bundler.js +15 -10
  6. package/src/Bundler.js.flow +19 -14
  7. package/src/DeltaBundler/DeltaCalculator.js +13 -17
  8. package/src/DeltaBundler/DeltaCalculator.js.flow +15 -20
  9. package/src/DeltaBundler/Serializers/getAllFiles.js.flow +2 -2
  10. package/src/DeltaBundler/Serializers/getAssets.js.flow +2 -2
  11. package/src/DeltaBundler/Serializers/getExplodedSourceMap.js.flow +4 -4
  12. package/src/DeltaBundler/Serializers/getRamBundleInfo.js.flow +6 -6
  13. package/src/DeltaBundler/Serializers/helpers/getSourceMapInfo.js.flow +4 -4
  14. package/src/DeltaBundler/Serializers/helpers/processBytecodeModules.js.flow +2 -2
  15. package/src/DeltaBundler/Serializers/helpers/processModules.js.flow +2 -2
  16. package/src/DeltaBundler/Serializers/hmrJSBundle.js.flow +2 -2
  17. package/src/DeltaBundler/Serializers/sourceMapGenerator.js.flow +6 -6
  18. package/src/DeltaBundler/Serializers/sourceMapObject.js.flow +4 -4
  19. package/src/DeltaBundler/Serializers/sourceMapString.js.flow +2 -2
  20. package/src/DeltaBundler/Worker.flow.js +78 -0
  21. package/src/DeltaBundler/Worker.flow.js.flow +121 -0
  22. package/src/DeltaBundler/Worker.js +8 -66
  23. package/src/DeltaBundler/Worker.js.flow +8 -107
  24. package/src/DeltaBundler/WorkerFarm.js.flow +4 -4
  25. package/src/DeltaBundler/__fixtures__/hasteImpl.js +4 -0
  26. package/src/DeltaBundler/getTransformCacheKey.js.flow +2 -2
  27. package/src/DeltaBundler/graphOperations.js +641 -0
  28. package/src/DeltaBundler/graphOperations.js.flow +752 -0
  29. package/src/DeltaBundler/types.flow.js +6 -0
  30. package/src/DeltaBundler/types.flow.js.flow +43 -31
  31. package/src/DeltaBundler.js +12 -6
  32. package/src/DeltaBundler.js.flow +14 -10
  33. package/src/HmrServer.js +0 -2
  34. package/src/HmrServer.js.flow +7 -8
  35. package/src/IncrementalBundler.js +1 -1
  36. package/src/IncrementalBundler.js.flow +8 -8
  37. package/src/ModuleGraph/node-haste/ModuleCache.js +1 -1
  38. package/src/ModuleGraph/node-haste/ModuleCache.js.flow +1 -1
  39. package/src/ModuleGraph/node-haste/node-haste.flow.js +0 -1
  40. package/src/ModuleGraph/node-haste/node-haste.flow.js.flow +3 -4
  41. package/src/ModuleGraph/node-haste/node-haste.js +4 -4
  42. package/src/ModuleGraph/node-haste/node-haste.js.flow +13 -7
  43. package/src/ModuleGraph/output/indexed-ram-bundle.js.flow +2 -2
  44. package/src/ModuleGraph/output/plain-bundle.js.flow +2 -2
  45. package/src/ModuleGraph/output/reverse-dependency-map-references.js.flow +8 -8
  46. package/src/ModuleGraph/output/util.js.flow +2 -2
  47. package/src/ModuleGraph/types.flow.js.flow +37 -37
  48. package/src/ModuleGraph/worker/collectDependencies.js +215 -8
  49. package/src/ModuleGraph/worker/collectDependencies.js.flow +230 -13
  50. package/src/Server/symbolicate.js.flow +1 -1
  51. package/src/Server.js.flow +18 -18
  52. package/src/cli.js +5 -0
  53. package/src/cli.js.flow +5 -0
  54. package/src/commands/build.js +4 -3
  55. package/src/commands/build.js.flow +5 -3
  56. package/src/commands/serve.js +3 -3
  57. package/src/commands/serve.js.flow +5 -3
  58. package/src/index.flow.js +392 -0
  59. package/src/index.flow.js.flow +480 -0
  60. package/src/index.js +8 -366
  61. package/src/index.js.flow +8 -456
  62. package/src/lib/CountingSet.js +116 -0
  63. package/src/lib/CountingSet.js.flow +126 -0
  64. package/src/lib/JsonReporter.js +0 -2
  65. package/src/lib/JsonReporter.js.flow +1 -1
  66. package/src/lib/bundleToBytecode.js.flow +2 -2
  67. package/src/lib/bundleToString.js.flow +2 -2
  68. package/src/lib/getAppendScripts.js +10 -4
  69. package/src/lib/getAppendScripts.js.flow +6 -4
  70. package/src/lib/getPreludeCode.js +19 -1
  71. package/src/lib/getPreludeCode.js.flow +17 -2
  72. package/src/lib/getPrependedScripts.js +10 -2
  73. package/src/lib/getPrependedScripts.js.flow +11 -2
  74. package/src/lib/reporting.js +0 -2
  75. package/src/lib/reporting.js.flow +2 -1
  76. package/src/lib/transformHelpers.js.flow +2 -2
  77. package/src/node-haste/DependencyGraph/ModuleResolution.js +17 -4
  78. package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +20 -12
  79. package/src/node-haste/DependencyGraph/createHasteMap.js +80 -19
  80. package/src/node-haste/DependencyGraph/createHasteMap.js.flow +16 -14
  81. package/src/node-haste/DependencyGraph.js +31 -29
  82. package/src/node-haste/DependencyGraph.js.flow +44 -38
  83. package/src/node-haste/ModuleCache.js.flow +1 -1
  84. package/src/node-haste/lib/AssetPaths.js.flow +2 -2
  85. package/src/node-haste/lib/parsePlatformFilePath.js.flow +2 -2
  86. package/src/shared/output/RamBundle/as-indexed-file.js.flow +1 -1
  87. package/src/shared/output/RamBundle/buildSourcemapWithMetadata.js.flow +2 -2
  88. package/src/shared/types.flow.js.flow +14 -14
  89. package/src/DeltaBundler/computeDelta.js +0 -42
  90. package/src/DeltaBundler/computeDelta.js.flow +0 -47
  91. package/src/DeltaBundler/traverseDependencies.js +0 -470
  92. package/src/DeltaBundler/traverseDependencies.js.flow +0 -565
  93. package/src/node-haste/DependencyGraph/types.js +0 -10
  94. package/src/node-haste/DependencyGraph/types.js.flow +0 -88
package/src/index.js.flow CHANGED
@@ -10,461 +10,13 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- import type {Graph} from './DeltaBundler';
14
- import type {ServerOptions} from './Server';
15
- import type {OutputOptions, RequestOptions} from './shared/types.flow.js';
16
- import type {Server as HttpServer} from 'http';
17
- import type {Server as HttpsServer} from 'https';
18
- import type {
19
- ConfigT,
20
- InputConfigT,
21
- Middleware,
22
- } from 'metro-config/src/configTypes.flow';
23
- import type {CustomTransformOptions} from 'metro-transform-worker';
24
- import typeof Yargs from 'yargs';
13
+ /*::
14
+ export type * from './index.flow';
15
+ */
25
16
 
26
- const makeBuildCommand = require('./commands/build');
27
- const makeDependenciesCommand = require('./commands/dependencies');
28
- const makeServeCommand = require('./commands/serve');
29
- const MetroHmrServer = require('./HmrServer');
30
- const IncrementalBundler = require('./IncrementalBundler');
31
- const createWebsocketServer = require('./lib/createWebsocketServer');
32
- const MetroServer = require('./Server');
33
- const outputBundle = require('./shared/output/bundle');
34
- const chalk = require('chalk');
35
- const fs = require('fs');
36
- const http = require('http');
37
- const https = require('https');
38
- const {getDefaultConfig, loadConfig, mergeConfig} = require('metro-config');
39
- const {InspectorProxy} = require('metro-inspector-proxy');
40
- const {parse} = require('url');
41
- const ws = require('ws');
17
+ try {
18
+ // $FlowFixMe[untyped-import]
19
+ require('metro-babel-register').unstable_registerForMetroMonorepo();
20
+ } catch {}
42
21
 
43
- type MetroMiddleWare = {|
44
- attachHmrServer: (httpServer: HttpServer | HttpsServer) => void,
45
- end: () => void,
46
- metroServer: MetroServer,
47
- middleware: Middleware,
48
- |};
49
-
50
- export type RunMetroOptions = {
51
- ...ServerOptions,
52
- waitForBundler?: boolean,
53
- };
54
-
55
- export type RunServerOptions = {|
56
- hasReducedPerformance?: boolean,
57
- host?: string,
58
- onError?: (Error & {|code?: string|}) => void,
59
- onReady?: (server: HttpServer | HttpsServer) => void,
60
- runInspectorProxy?: boolean,
61
- secureServerOptions?: Object,
62
- secure?: boolean, // deprecated
63
- secureCert?: string, // deprecated
64
- secureKey?: string, // deprecated
65
- waitForBundler?: boolean,
66
- websocketEndpoints?: {
67
- [path: string]: typeof ws.Server,
68
- },
69
- |};
70
-
71
- type BuildGraphOptions = {|
72
- entries: $ReadOnlyArray<string>,
73
- customTransformOptions?: CustomTransformOptions,
74
- dev?: boolean,
75
- minify?: boolean,
76
- onProgress?: (transformedFileCount: number, totalFileCount: number) => void,
77
- platform?: string,
78
- type?: 'module' | 'script',
79
- |};
80
-
81
- export type RunBuildOptions = {|
82
- entry: string,
83
- dev?: boolean,
84
- out?: string,
85
- onBegin?: () => void,
86
- onComplete?: () => void,
87
- onProgress?: (transformedFileCount: number, totalFileCount: number) => void,
88
- minify?: boolean,
89
- output?: {
90
- build: (
91
- MetroServer,
92
- RequestOptions,
93
- ) => Promise<{
94
- code: string,
95
- map: string,
96
- ...
97
- }>,
98
- save: (
99
- {
100
- code: string,
101
- map: string,
102
- ...
103
- },
104
- OutputOptions,
105
- (...args: Array<string>) => void,
106
- ) => Promise<mixed>,
107
- ...
108
- },
109
- platform?: string,
110
- sourceMap?: boolean,
111
- sourceMapUrl?: string,
112
- |};
113
-
114
- type BuildCommandOptions = {||} | null;
115
- type ServeCommandOptions = {||} | null;
116
-
117
- async function getConfig(config: InputConfigT): Promise<ConfigT> {
118
- const defaultConfig = await getDefaultConfig(config.projectRoot);
119
- return mergeConfig(defaultConfig, config);
120
- }
121
-
122
- async function runMetro(
123
- config: InputConfigT,
124
- options?: RunMetroOptions,
125
- ): Promise<MetroServer> {
126
- const mergedConfig = await getConfig(config);
127
- const {
128
- reporter,
129
- server: {port},
130
- } = mergedConfig;
131
-
132
- reporter.update({
133
- hasReducedPerformance: options
134
- ? Boolean(options.hasReducedPerformance)
135
- : false,
136
- port,
137
- type: 'initialize_started',
138
- });
139
-
140
- const {waitForBundler = false, ...serverOptions} = options ?? {};
141
- const server = new MetroServer(mergedConfig, serverOptions);
142
-
143
- const readyPromise = server
144
- .ready()
145
- .then(() => {
146
- reporter.update({
147
- type: 'initialize_done',
148
- port,
149
- });
150
- })
151
- .catch(error => {
152
- reporter.update({
153
- type: 'initialize_failed',
154
- port,
155
- error,
156
- });
157
- });
158
- if (waitForBundler) {
159
- await readyPromise;
160
- }
161
-
162
- return server;
163
- }
164
-
165
- exports.runMetro = runMetro;
166
- exports.loadConfig = loadConfig;
167
-
168
- const createConnectMiddleware = async function (
169
- config: ConfigT,
170
- options?: RunMetroOptions,
171
- ): Promise<MetroMiddleWare> {
172
- const metroServer = await runMetro(config, options);
173
-
174
- let enhancedMiddleware = metroServer.processRequest;
175
-
176
- // Enhance the resulting middleware using the config options
177
- if (config.server.enhanceMiddleware) {
178
- enhancedMiddleware = config.server.enhanceMiddleware(
179
- enhancedMiddleware,
180
- metroServer,
181
- );
182
- }
183
-
184
- return {
185
- attachHmrServer(httpServer: HttpServer | HttpsServer): void {
186
- const wss = createWebsocketServer({
187
- websocketServer: new MetroHmrServer(
188
- metroServer.getBundler(),
189
- metroServer.getCreateModuleId(),
190
- config,
191
- ),
192
- });
193
- httpServer.on('upgrade', (request, socket, head) => {
194
- const {pathname} = parse(request.url);
195
- if (pathname === '/hot') {
196
- wss.handleUpgrade(request, socket, head, ws => {
197
- wss.emit('connection', ws, request);
198
- });
199
- } else {
200
- socket.destroy();
201
- }
202
- });
203
- },
204
- metroServer,
205
- middleware: enhancedMiddleware,
206
- end(): void {
207
- metroServer.end();
208
- },
209
- };
210
- };
211
- exports.createConnectMiddleware = createConnectMiddleware;
212
-
213
- exports.runServer = async (
214
- config: ConfigT,
215
- {
216
- hasReducedPerformance = false,
217
- host,
218
- onError,
219
- onReady,
220
- secureServerOptions,
221
- secure, //deprecated
222
- secureCert, // deprecated
223
- secureKey, // deprecated
224
- waitForBundler = false,
225
- websocketEndpoints = {},
226
- }: RunServerOptions,
227
- ): Promise<HttpServer | HttpsServer> => {
228
- if (secure != null || secureCert != null || secureKey != null) {
229
- // eslint-disable-next-line no-console
230
- console.warn(
231
- chalk.inverse.yellow.bold(' DEPRECATED '),
232
- 'The `secure`, `secureCert`, and `secureKey` options are now deprecated. ' +
233
- 'Please use the `secureServerOptions` object instead to pass options to ' +
234
- "Metro's https development server.",
235
- );
236
- }
237
- // Lazy require
238
- const connect = require('connect');
239
-
240
- const serverApp = connect();
241
-
242
- const {middleware, end, metroServer} = await createConnectMiddleware(config, {
243
- hasReducedPerformance,
244
- waitForBundler,
245
- });
246
-
247
- serverApp.use(middleware);
248
-
249
- let inspectorProxy: ?InspectorProxy = null;
250
- if (config.server.runInspectorProxy) {
251
- inspectorProxy = new InspectorProxy(config.projectRoot);
252
- }
253
-
254
- let httpServer;
255
-
256
- if (secure || secureServerOptions != null) {
257
- let options = secureServerOptions;
258
- if (typeof secureKey === 'string' && typeof secureCert === 'string') {
259
- options = Object.assign(
260
- {
261
- key: fs.readFileSync(secureKey),
262
- cert: fs.readFileSync(secureCert),
263
- },
264
- secureServerOptions,
265
- );
266
- }
267
- httpServer = https.createServer(options, serverApp);
268
- } else {
269
- httpServer = http.createServer(serverApp);
270
- }
271
-
272
- httpServer.on('error', error => {
273
- if (onError) {
274
- onError(error);
275
- }
276
- end();
277
- });
278
-
279
- return new Promise(
280
- (
281
- resolve: (result: HttpServer | HttpsServer) => void,
282
- reject: mixed => mixed,
283
- ) => {
284
- httpServer.listen(config.server.port, host, () => {
285
- if (onReady) {
286
- onReady(httpServer);
287
- }
288
-
289
- Object.assign(websocketEndpoints, {
290
- ...(inspectorProxy
291
- ? {...inspectorProxy.createWebSocketListeners(httpServer)}
292
- : {}),
293
- '/hot': createWebsocketServer({
294
- websocketServer: new MetroHmrServer(
295
- metroServer.getBundler(),
296
- metroServer.getCreateModuleId(),
297
- config,
298
- ),
299
- }),
300
- });
301
-
302
- httpServer.on('upgrade', (request, socket, head) => {
303
- const {pathname} = parse(request.url);
304
- if (pathname != null && websocketEndpoints[pathname]) {
305
- websocketEndpoints[pathname].handleUpgrade(
306
- request,
307
- socket,
308
- head,
309
- ws => {
310
- websocketEndpoints[pathname].emit('connection', ws, request);
311
- },
312
- );
313
- } else {
314
- socket.destroy();
315
- }
316
- });
317
-
318
- if (inspectorProxy) {
319
- // TODO(hypuk): Refactor inspectorProxy.processRequest into separate request handlers
320
- // so that we could provide routes (/json/list and /json/version) here.
321
- // Currently this causes Metro to give warning about T31407894.
322
- // $FlowFixMe[method-unbinding] added when improving typing for this parameters
323
- serverApp.use(inspectorProxy.processRequest.bind(inspectorProxy));
324
- }
325
-
326
- resolve(httpServer);
327
- });
328
-
329
- // Disable any kind of automatic timeout behavior for incoming
330
- // requests in case it takes the packager more than the default
331
- // timeout of 120 seconds to respond to a request.
332
- httpServer.timeout = 0;
333
-
334
- httpServer.on('error', error => {
335
- end();
336
- reject(error);
337
- });
338
-
339
- httpServer.on('close', () => {
340
- end();
341
- });
342
- },
343
- );
344
- };
345
-
346
- exports.runBuild = async (
347
- config: ConfigT,
348
- {
349
- dev = false,
350
- entry,
351
- onBegin,
352
- onComplete,
353
- onProgress,
354
- minify = true,
355
- output = outputBundle,
356
- out,
357
- platform = 'web',
358
- sourceMap = false,
359
- sourceMapUrl,
360
- }: RunBuildOptions,
361
- ): Promise<{
362
- code: string,
363
- map: string,
364
- ...
365
- }> => {
366
- const metroServer = await runMetro(config, {
367
- watch: false,
368
- });
369
-
370
- try {
371
- const requestOptions: RequestOptions = {
372
- dev,
373
- entryFile: entry,
374
- inlineSourceMap: sourceMap && !sourceMapUrl,
375
- minify,
376
- platform,
377
- sourceMapUrl: sourceMap === false ? undefined : sourceMapUrl,
378
- createModuleIdFactory: config.serializer.createModuleIdFactory,
379
- onProgress,
380
- };
381
-
382
- if (onBegin) {
383
- onBegin();
384
- }
385
-
386
- const metroBundle = await output.build(metroServer, requestOptions);
387
-
388
- if (onComplete) {
389
- onComplete();
390
- }
391
-
392
- if (out) {
393
- const bundleOutput = out.replace(/(\.js)?$/, '.js');
394
- const sourcemapOutput =
395
- sourceMap === false ? undefined : out.replace(/(\.js)?$/, '.map');
396
-
397
- const outputOptions: OutputOptions = {
398
- bundleOutput,
399
- sourcemapOutput,
400
- dev,
401
- platform,
402
- };
403
-
404
- // eslint-disable-next-line no-console
405
- await output.save(metroBundle, outputOptions, console.log);
406
- }
407
-
408
- return metroBundle;
409
- } finally {
410
- await metroServer.end();
411
- }
412
- };
413
-
414
- exports.buildGraph = async function (
415
- config: InputConfigT,
416
- {
417
- customTransformOptions = Object.create(null),
418
- dev = false,
419
- entries,
420
- minify = false,
421
- onProgress,
422
- platform = 'web',
423
- type = 'module',
424
- }: BuildGraphOptions,
425
- ): Promise<Graph<>> {
426
- const mergedConfig = await getConfig(config);
427
-
428
- const bundler = new IncrementalBundler(mergedConfig);
429
-
430
- try {
431
- return await bundler.buildGraphForEntries(entries, {
432
- ...MetroServer.DEFAULT_GRAPH_OPTIONS,
433
- customTransformOptions,
434
- dev,
435
- minify,
436
- platform,
437
- type,
438
- });
439
- } finally {
440
- bundler.end();
441
- }
442
- };
443
-
444
- exports.attachMetroCli = function (
445
- yargs: Yargs,
446
- {
447
- build = {},
448
- serve = {},
449
- dependencies = {},
450
- }: {
451
- build: BuildCommandOptions,
452
- serve: ServeCommandOptions,
453
- dependencies: any,
454
- ...
455
- } = {},
456
- ): Yargs {
457
- if (build) {
458
- const {command, description, builder, handler} = makeBuildCommand();
459
- yargs.command(command, description, builder, handler);
460
- }
461
- if (serve) {
462
- const {command, description, builder, handler} = makeServeCommand();
463
- yargs.command(command, description, builder, handler);
464
- }
465
- if (dependencies) {
466
- const {command, description, builder, handler} = makeDependenciesCommand();
467
- yargs.command(command, description, builder, handler);
468
- }
469
- return yargs;
470
- };
22
+ module.exports = require('./index.flow');
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true,
5
+ });
6
+ exports.default = void 0;
7
+
8
+ /**
9
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
10
+ *
11
+ * This source code is licensed under the MIT license found in the
12
+ * LICENSE file in the root directory of this source tree.
13
+ *
14
+ *
15
+ * @format
16
+ */
17
+
18
+ /**
19
+ * A Set that only deletes a given item when the number of delete(item) calls
20
+ * matches the number of add(item) calls. Iteration and `size` are in terms of
21
+ * *unique* items.
22
+ */
23
+ class CountingSet {
24
+ #map = new Map();
25
+
26
+ constructor(items) {
27
+ if (items) {
28
+ if (items instanceof CountingSet) {
29
+ this.#map = new Map(items.#map);
30
+ } else {
31
+ for (const item of items) {
32
+ this.add(item);
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ has(item) {
39
+ return this.#map.has(item);
40
+ }
41
+
42
+ add(item) {
43
+ const newCount = this.count(item) + 1;
44
+ this.#map.set(item, newCount);
45
+ }
46
+
47
+ delete(item) {
48
+ const newCount = this.count(item) - 1;
49
+
50
+ if (newCount <= 0) {
51
+ this.#map.delete(item);
52
+ } else {
53
+ this.#map.set(item, newCount);
54
+ }
55
+ }
56
+
57
+ keys() {
58
+ return this.#map.keys();
59
+ }
60
+
61
+ values() {
62
+ return this.#map.keys();
63
+ }
64
+
65
+ *entries() {
66
+ for (const item of this) {
67
+ yield [item, item];
68
+ }
69
+ } // Iterate over unique entries
70
+ // $FlowIssue[unsupported-syntax]
71
+
72
+ [Symbol.iterator]() {
73
+ return this.values();
74
+ }
75
+ /*::
76
+ // For Flow's benefit
77
+ @@iterator(): Iterator<T> {
78
+ return this.values();
79
+ }
80
+ */
81
+ // Number of unique entries
82
+ // $FlowIssue[unsafe-getters-setters]
83
+
84
+ get size() {
85
+ return this.#map.size;
86
+ }
87
+
88
+ count(item) {
89
+ var _this$map$get;
90
+
91
+ return (_this$map$get = this.#map.get(item)) !== null &&
92
+ _this$map$get !== void 0
93
+ ? _this$map$get
94
+ : 0;
95
+ }
96
+
97
+ clear() {
98
+ this.#map.clear();
99
+ }
100
+
101
+ forEach(callbackFn, thisArg) {
102
+ for (const item of this) {
103
+ callbackFn.call(thisArg, item, item, this);
104
+ }
105
+ } // For Jest purposes. Ideally a custom serializer would be enough, but in
106
+ // practice there is hardcoded magic for Set in toEqual (etc) that we cannot
107
+ // extend to custom collection classes. Instead let's assume values are
108
+ // sortable ( = strings) and make this look like an array with some stable
109
+ // order.
110
+
111
+ toJSON() {
112
+ return [...this].sort();
113
+ }
114
+ }
115
+
116
+ exports.default = CountingSet;
@@ -0,0 +1,126 @@
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
+ */
10
+
11
+ export interface ReadOnlyCountingSet<T> extends Iterable<T> {
12
+ has(item: T): boolean;
13
+ @@iterator(): Iterator<T>;
14
+ +size: number;
15
+ count(item: T): number;
16
+ forEach<ThisT>(
17
+ callbackFn: (
18
+ this: ThisT,
19
+ value: T,
20
+ key: T,
21
+ set: ReadOnlyCountingSet<T>,
22
+ ) => mixed,
23
+
24
+ // NOTE: Should be optional, but Flow seems happy to infer undefined here
25
+ // which is what we want.
26
+ thisArg: ThisT,
27
+ ): void;
28
+ }
29
+
30
+ /**
31
+ * A Set that only deletes a given item when the number of delete(item) calls
32
+ * matches the number of add(item) calls. Iteration and `size` are in terms of
33
+ * *unique* items.
34
+ */
35
+ export default class CountingSet<T> implements ReadOnlyCountingSet<T> {
36
+ #map: Map<T, number> = new Map();
37
+
38
+ constructor(items?: Iterable<T>) {
39
+ if (items) {
40
+ if (items instanceof CountingSet) {
41
+ this.#map = new Map(items.#map);
42
+ } else {
43
+ for (const item of items) {
44
+ this.add(item);
45
+ }
46
+ }
47
+ }
48
+ }
49
+
50
+ has(item: T): boolean {
51
+ return this.#map.has(item);
52
+ }
53
+
54
+ add(item: T): void {
55
+ const newCount = this.count(item) + 1;
56
+ this.#map.set(item, newCount);
57
+ }
58
+
59
+ delete(item: T): void {
60
+ const newCount = this.count(item) - 1;
61
+ if (newCount <= 0) {
62
+ this.#map.delete(item);
63
+ } else {
64
+ this.#map.set(item, newCount);
65
+ }
66
+ }
67
+
68
+ keys(): Iterator<T> {
69
+ return this.#map.keys();
70
+ }
71
+
72
+ values(): Iterator<T> {
73
+ return this.#map.keys();
74
+ }
75
+
76
+ *entries(): Iterator<[T, T]> {
77
+ for (const item of this) {
78
+ yield [item, item];
79
+ }
80
+ }
81
+
82
+ // Iterate over unique entries
83
+ // $FlowIssue[unsupported-syntax]
84
+ [Symbol.iterator](): Iterator<T> {
85
+ return this.values();
86
+ }
87
+
88
+ /*::
89
+ // For Flow's benefit
90
+ @@iterator(): Iterator<T> {
91
+ return this.values();
92
+ }
93
+ */
94
+
95
+ // Number of unique entries
96
+ // $FlowIssue[unsafe-getters-setters]
97
+ get size(): number {
98
+ return this.#map.size;
99
+ }
100
+
101
+ count(item: T): number {
102
+ return this.#map.get(item) ?? 0;
103
+ }
104
+
105
+ clear(): void {
106
+ this.#map.clear();
107
+ }
108
+
109
+ forEach<ThisT>(
110
+ callbackFn: (this: ThisT, value: T, key: T, set: CountingSet<T>) => mixed,
111
+ thisArg: ThisT,
112
+ ): void {
113
+ for (const item of this) {
114
+ callbackFn.call(thisArg, item, item, this);
115
+ }
116
+ }
117
+
118
+ // For Jest purposes. Ideally a custom serializer would be enough, but in
119
+ // practice there is hardcoded magic for Set in toEqual (etc) that we cannot
120
+ // extend to custom collection classes. Instead let's assume values are
121
+ // sortable ( = strings) and make this look like an array with some stable
122
+ // order.
123
+ toJSON(): mixed {
124
+ return [...this].sort();
125
+ }
126
+ }
@@ -9,8 +9,6 @@
9
9
  */
10
10
  "use strict";
11
11
 
12
- const { Writable } = require("stream");
13
-
14
12
  class JsonReporter {
15
13
  constructor(stream) {
16
14
  this._stream = stream;