metro 0.84.3 → 0.84.4

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.84.3",
3
+ "version": "0.84.4",
4
4
  "description": "🚇 The JavaScript bundler for React Native.",
5
5
  "main": "src/index.js",
6
6
  "bin": "src/cli.js",
@@ -27,7 +27,6 @@
27
27
  "@babel/traverse": "^7.29.0",
28
28
  "@babel/types": "^7.29.0",
29
29
  "accepts": "^2.0.0",
30
- "chalk": "^4.0.0",
31
30
  "ci-info": "^2.0.0",
32
31
  "connect": "^3.6.5",
33
32
  "debug": "^4.4.0",
@@ -40,18 +39,18 @@
40
39
  "jest-worker": "^29.7.0",
41
40
  "jsc-safe-url": "^0.2.2",
42
41
  "lodash.throttle": "^4.1.1",
43
- "metro-babel-transformer": "0.84.3",
44
- "metro-cache": "0.84.3",
45
- "metro-cache-key": "0.84.3",
46
- "metro-config": "0.84.3",
47
- "metro-core": "0.84.3",
48
- "metro-file-map": "0.84.3",
49
- "metro-resolver": "0.84.3",
50
- "metro-runtime": "0.84.3",
51
- "metro-source-map": "0.84.3",
52
- "metro-symbolicate": "0.84.3",
53
- "metro-transform-plugins": "0.84.3",
54
- "metro-transform-worker": "0.84.3",
42
+ "metro-babel-transformer": "0.84.4",
43
+ "metro-cache": "0.84.4",
44
+ "metro-cache-key": "0.84.4",
45
+ "metro-config": "0.84.4",
46
+ "metro-core": "0.84.4",
47
+ "metro-file-map": "0.84.4",
48
+ "metro-resolver": "0.84.4",
49
+ "metro-runtime": "0.84.4",
50
+ "metro-source-map": "0.84.4",
51
+ "metro-symbolicate": "0.84.4",
52
+ "metro-transform-plugins": "0.84.4",
53
+ "metro-transform-worker": "0.84.4",
55
54
  "mime-types": "^3.0.1",
56
55
  "nullthrows": "^1.1.1",
57
56
  "serialize-error": "^2.1.0",
@@ -72,7 +71,7 @@
72
71
  "dedent": "^0.7.0",
73
72
  "jest-snapshot": "^29.7.0",
74
73
  "jest-snapshot-serializer-raw": "^1.2.0",
75
- "metro-babel-register": "0.84.3",
74
+ "metro-babel-register": "0.84.4",
76
75
  "metro-memory-fs": "*",
77
76
  "mock-req": "^0.2.0",
78
77
  "mock-res": "^0.6.0",
package/src/Assets.js CHANGED
@@ -221,26 +221,33 @@ async function getAsset(
221
221
  `'${relativePath}' cannot be loaded as its extension is not registered in assetExts`,
222
222
  );
223
223
  }
224
- if (fileExistsInFileMap != null) {
225
- if (!fileExistsInFileMap(absolutePath)) {
226
- throw new Error(
227
- `'${relativePath}' could not be found, because it is not within the projectRoot or watchFolders, or it is blocked via the resolver.blockList config`,
228
- );
229
- }
230
- } else {
231
- if (!pathBelongsToRoots(absolutePath, [projectRoot, ...watchFolders])) {
232
- throw new Error(
233
- `'${relativePath}' could not be found, because it cannot be found in the project root or any watch folder`,
234
- );
235
- }
224
+ if (
225
+ fileExistsInFileMap == null &&
226
+ !pathBelongsToRoots(absolutePath, [projectRoot, ...watchFolders])
227
+ ) {
228
+ throw new Error(
229
+ `'${relativePath}' could not be found, because it cannot be found in the project root or any watch folder`,
230
+ );
236
231
  }
237
232
  const record = await getAbsoluteAssetRecord(absolutePath, platform ?? null);
238
233
  for (let i = 0; i < record.scales.length; i++) {
239
234
  if (record.scales[i] >= assetData.resolution) {
235
+ if (
236
+ fileExistsInFileMap != null &&
237
+ !fileExistsInFileMap(record.files[i])
238
+ ) {
239
+ continue;
240
+ }
240
241
  return _fs.default.promises.readFile(record.files[i]);
241
242
  }
242
243
  }
243
- return _fs.default.promises.readFile(record.files[record.files.length - 1]);
244
+ const lastFile = record.files[record.files.length - 1];
245
+ if (fileExistsInFileMap != null && !fileExistsInFileMap(lastFile)) {
246
+ throw new Error(
247
+ `'${relativePath}' could not be found, because it is not within the projectRoot or watchFolders, or it is blocked via the resolver.blockList config`,
248
+ );
249
+ }
250
+ return _fs.default.promises.readFile(lastFile);
244
251
  }
245
252
  function pathBelongsToRoots(pathToCheck, roots) {
246
253
  for (const rootFolder of roots) {
@@ -299,29 +299,36 @@ export async function getAsset(
299
299
  }
300
300
 
301
301
  // NOTE: If fileExistsInFileMap is not provided, we fall back to pathBelongsToRoots for backward compatibility, as getAsset is part of the public API.
302
- if (fileExistsInFileMap != null) {
303
- if (!fileExistsInFileMap(absolutePath)) {
304
- throw new Error(
305
- `'${relativePath}' could not be found, because it is not within the projectRoot or watchFolders, or it is blocked via the resolver.blockList config`,
306
- );
307
- }
308
- } else {
309
- if (!pathBelongsToRoots(absolutePath, [projectRoot, ...watchFolders])) {
310
- throw new Error(
311
- `'${relativePath}' could not be found, because it cannot be found in the project root or any watch folder`,
312
- );
313
- }
302
+ if (
303
+ fileExistsInFileMap == null &&
304
+ !pathBelongsToRoots(absolutePath, [projectRoot, ...watchFolders])
305
+ ) {
306
+ throw new Error(
307
+ `'${relativePath}' could not be found, because it cannot be found in the project root or any watch folder`,
308
+ );
314
309
  }
315
310
 
316
311
  const record = await getAbsoluteAssetRecord(absolutePath, platform ?? null);
317
312
 
318
313
  for (let i = 0; i < record.scales.length; i++) {
319
314
  if (record.scales[i] >= assetData.resolution) {
315
+ if (
316
+ fileExistsInFileMap != null &&
317
+ !fileExistsInFileMap(record.files[i])
318
+ ) {
319
+ continue;
320
+ }
320
321
  return fs.promises.readFile(record.files[i]);
321
322
  }
322
323
  }
323
324
 
324
- return fs.promises.readFile(record.files[record.files.length - 1]);
325
+ const lastFile = record.files[record.files.length - 1];
326
+ if (fileExistsInFileMap != null && !fileExistsInFileMap(lastFile)) {
327
+ throw new Error(
328
+ `'${relativePath}' could not be found, because it is not within the projectRoot or watchFolders, or it is blocked via the resolver.blockList config`,
329
+ );
330
+ }
331
+ return fs.promises.readFile(lastFile);
325
332
  }
326
333
 
327
334
  function pathBelongsToRoots(
@@ -405,6 +405,9 @@ function isOptionalDependency(name, path, state) {
405
405
  if (!allowOptionalDependencies || isExcluded()) {
406
406
  return false;
407
407
  }
408
+ if (isInPromiseChainWithRejectionHandler(path)) {
409
+ return true;
410
+ }
408
411
  let sCount = 0;
409
412
  let p = path;
410
413
  while (p && sCount < 3) {
@@ -422,6 +425,55 @@ function isOptionalDependency(name, path, state) {
422
425
  }
423
426
  return false;
424
427
  }
428
+ function isInPromiseChainWithRejectionHandler(path) {
429
+ let current = path;
430
+ while (current.parentPath != null) {
431
+ const member = current.parentPath;
432
+ if (
433
+ member.node.type !== "MemberExpression" ||
434
+ member.node.object !== current.node ||
435
+ member.node.computed ||
436
+ member.node.property.type !== "Identifier" ||
437
+ member.parentPath == null
438
+ ) {
439
+ return false;
440
+ }
441
+ const call = member.parentPath;
442
+ if (
443
+ call.node.type !== "CallExpression" ||
444
+ call.node.callee !== member.node
445
+ ) {
446
+ return false;
447
+ }
448
+ const propertyName = member.node.property.name;
449
+ const args = call.node.arguments;
450
+ if (
451
+ propertyName === "catch" &&
452
+ args.length >= 1 &&
453
+ isNonNullishCallbackArg(args[0])
454
+ ) {
455
+ return true;
456
+ }
457
+ if (
458
+ propertyName === "then" &&
459
+ args.length >= 2 &&
460
+ isNonNullishCallbackArg(args[1])
461
+ ) {
462
+ return true;
463
+ }
464
+ current = call;
465
+ }
466
+ return false;
467
+ }
468
+ function isNonNullishCallbackArg(arg) {
469
+ if (arg.type === "NullLiteral") {
470
+ return false;
471
+ }
472
+ if (arg.type === "Identifier" && arg.name === "undefined") {
473
+ return false;
474
+ }
475
+ return true;
476
+ }
425
477
  function getModuleNameFromCallArgs(path) {
426
478
  const args = path.get("arguments");
427
479
  if (!Array.isArray(args) || args.length !== 1) {
@@ -630,6 +630,15 @@ function isOptionalDependency(
630
630
  return false;
631
631
  }
632
632
 
633
+ // Treat dynamic imports as optional when a rejection handler is attached
634
+ // close to the import call, e.g.
635
+ // import('x').catch(handler)
636
+ // import('x').then(handler, onReject)
637
+ // import('x').then(...).catch(handler)
638
+ if (isInPromiseChainWithRejectionHandler(path)) {
639
+ return true;
640
+ }
641
+
633
642
  // Valid statement stack for single-level try-block: expressionStatement -> blockStatement -> tryStatement
634
643
  let sCount = 0;
635
644
  let p: ?(NodePath<> | NodePath<BabelNode>) = path;
@@ -652,6 +661,64 @@ function isOptionalDependency(
652
661
  return false;
653
662
  }
654
663
 
664
+ // Walk up a chain of `.then(...)` / `.catch(...)` member calls starting from
665
+ // `path` (typically an `import()` CallExpression) and return true if any
666
+ // chained call provides a rejection handler — either `.catch(handler)` or
667
+ // `.then(_, handler)`. The chain must be unbroken: as soon as the parent is
668
+ // not a member call applied to the previous expression, we stop. This keeps
669
+ // the heuristic local to the import, matching the behaviour of the
670
+ // try/catch heuristic above.
671
+ function isInPromiseChainWithRejectionHandler(path: NodePath<>): boolean {
672
+ let current: NodePath<> = path;
673
+ while (current.parentPath != null) {
674
+ const member = current.parentPath;
675
+ if (
676
+ member.node.type !== 'MemberExpression' ||
677
+ member.node.object !== current.node ||
678
+ member.node.computed ||
679
+ member.node.property.type !== 'Identifier' ||
680
+ member.parentPath == null
681
+ ) {
682
+ return false;
683
+ }
684
+ const call = member.parentPath;
685
+ if (
686
+ call.node.type !== 'CallExpression' ||
687
+ call.node.callee !== member.node
688
+ ) {
689
+ return false;
690
+ }
691
+ const propertyName = member.node.property.name;
692
+ const args = call.node.arguments;
693
+ if (
694
+ propertyName === 'catch' &&
695
+ args.length >= 1 &&
696
+ isNonNullishCallbackArg(args[0])
697
+ ) {
698
+ return true;
699
+ }
700
+ if (
701
+ propertyName === 'then' &&
702
+ args.length >= 2 &&
703
+ isNonNullishCallbackArg(args[1])
704
+ ) {
705
+ return true;
706
+ }
707
+ current = call;
708
+ }
709
+ return false;
710
+ }
711
+
712
+ function isNonNullishCallbackArg(arg: BabelNode): boolean {
713
+ if (arg.type === 'NullLiteral') {
714
+ return false;
715
+ }
716
+ if (arg.type === 'Identifier' && arg.name === 'undefined') {
717
+ return false;
718
+ }
719
+ return true;
720
+ }
721
+
655
722
  function getModuleNameFromCallArgs(path: NodePath<CallExpression>): ?string {
656
723
  const args = path.get('arguments');
657
724
  if (!Array.isArray(args) || args.length !== 1) {
package/src/Server.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @noformat
8
8
  * @oncall react_native
9
- * @generated SignedSource<<161e77301d04ce6cc254f1dbf15ef06b>>
9
+ * @generated SignedSource<<03b526801403adb05b3b0f6c25b25ed5>>
10
10
  *
11
11
  * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
12
12
  * Original file: packages/metro/src/Server.js
@@ -225,6 +225,9 @@ declare class Server {
225
225
  _explodedSourceMapForBundleOptions(
226
226
  bundleOptions: BundleOptions,
227
227
  ): Promise<ExplodedSourceMap>;
228
+ _resolveWatchFolderPrefix(
229
+ filePath: string,
230
+ ): {rootDir: string; filePath: string} | null;
228
231
  _resolveRelativePath(
229
232
  filePath: string,
230
233
  $$PARAM_1$$: Readonly<{
package/src/Server.js CHANGED
@@ -1347,6 +1347,35 @@ class Server {
1347
1347
  },
1348
1348
  );
1349
1349
  }
1350
+ _resolveWatchFolderPrefix(filePath) {
1351
+ const watchFolderMatch = filePath.match(
1352
+ /^\.\/\[metro-watchFolders\]\/(\d+)\/(.*)/,
1353
+ );
1354
+ if (watchFolderMatch != null) {
1355
+ const index = parseInt(watchFolderMatch[1], 10);
1356
+ const watchFolder = this._config.watchFolders[index];
1357
+ if (watchFolder != null) {
1358
+ return {
1359
+ rootDir: _path.default.resolve(watchFolder),
1360
+ filePath:
1361
+ "." +
1362
+ _path.default.sep +
1363
+ watchFolderMatch[2].split("/").join(_path.default.sep),
1364
+ };
1365
+ }
1366
+ }
1367
+ const projectMatch = filePath.match(/^\.\/\[metro-project\]\/(.*)/);
1368
+ if (projectMatch != null) {
1369
+ return {
1370
+ rootDir: _path.default.resolve(this._config.projectRoot),
1371
+ filePath:
1372
+ "." +
1373
+ _path.default.sep +
1374
+ projectMatch[1].split("/").join(_path.default.sep),
1375
+ };
1376
+ }
1377
+ return null;
1378
+ }
1350
1379
  async _resolveRelativePath(
1351
1380
  filePath,
1352
1381
  { relativeTo, resolverOptions, transformOptions },
@@ -1356,14 +1385,18 @@ class Server {
1356
1385
  transformOptions.platform,
1357
1386
  resolverOptions,
1358
1387
  );
1388
+ const resolved = this._resolveWatchFolderPrefix(filePath);
1359
1389
  const rootDir =
1360
- relativeTo === "server"
1361
- ? this._getServerRootDir()
1362
- : this._config.projectRoot;
1390
+ resolved != null
1391
+ ? resolved.rootDir
1392
+ : relativeTo === "server"
1393
+ ? this._getServerRootDir()
1394
+ : this._config.projectRoot;
1395
+ const resolvedFilePath = resolved != null ? resolved.filePath : filePath;
1363
1396
  return resolutionFn(`${rootDir}/.`, {
1364
- name: filePath,
1397
+ name: resolvedFilePath,
1365
1398
  data: {
1366
- key: filePath,
1399
+ key: resolvedFilePath,
1367
1400
  locs: [],
1368
1401
  asyncType: null,
1369
1402
  isESMImport: false,
@@ -1403,6 +1436,10 @@ class Server {
1403
1436
  return this._config.server.unstable_serverRoot ?? this._config.projectRoot;
1404
1437
  }
1405
1438
  _getEntryPointAbsolutePath(entryFile) {
1439
+ const resolved = this._resolveWatchFolderPrefix(entryFile);
1440
+ if (resolved != null) {
1441
+ return _path.default.resolve(resolved.rootDir, resolved.filePath);
1442
+ }
1406
1443
  return _path.default.resolve(this._getServerRootDir(), entryFile);
1407
1444
  }
1408
1445
  async ready() {
@@ -1622,6 +1622,33 @@ export default class Server {
1622
1622
  );
1623
1623
  }
1624
1624
 
1625
+ _resolveWatchFolderPrefix(
1626
+ filePath: string,
1627
+ ): {rootDir: string, filePath: string} | null {
1628
+ const watchFolderMatch = filePath.match(
1629
+ /^\.\/\[metro-watchFolders\]\/(\d+)\/(.*)/,
1630
+ );
1631
+ if (watchFolderMatch != null) {
1632
+ const index = parseInt(watchFolderMatch[1], 10);
1633
+ const watchFolder = this._config.watchFolders[index];
1634
+ if (watchFolder != null) {
1635
+ return {
1636
+ rootDir: path.resolve(watchFolder),
1637
+ filePath:
1638
+ '.' + path.sep + watchFolderMatch[2].split('/').join(path.sep),
1639
+ };
1640
+ }
1641
+ }
1642
+ const projectMatch = filePath.match(/^\.\/\[metro-project\]\/(.*)/);
1643
+ if (projectMatch != null) {
1644
+ return {
1645
+ rootDir: path.resolve(this._config.projectRoot),
1646
+ filePath: '.' + path.sep + projectMatch[1].split('/').join(path.sep),
1647
+ };
1648
+ }
1649
+ return null;
1650
+ }
1651
+
1625
1652
  async _resolveRelativePath(
1626
1653
  filePath: string,
1627
1654
  {
@@ -1639,13 +1666,22 @@ export default class Server {
1639
1666
  transformOptions.platform,
1640
1667
  resolverOptions,
1641
1668
  );
1669
+ const resolved = this._resolveWatchFolderPrefix(filePath);
1642
1670
  const rootDir =
1643
- relativeTo === 'server'
1644
- ? this._getServerRootDir()
1645
- : this._config.projectRoot;
1671
+ resolved != null
1672
+ ? resolved.rootDir
1673
+ : relativeTo === 'server'
1674
+ ? this._getServerRootDir()
1675
+ : this._config.projectRoot;
1676
+ const resolvedFilePath = resolved != null ? resolved.filePath : filePath;
1646
1677
  return resolutionFn(`${rootDir}/.`, {
1647
- name: filePath,
1648
- data: {key: filePath, locs: [], asyncType: null, isESMImport: false},
1678
+ name: resolvedFilePath,
1679
+ data: {
1680
+ key: resolvedFilePath,
1681
+ locs: [],
1682
+ asyncType: null,
1683
+ isESMImport: false,
1684
+ },
1649
1685
  }).filePath;
1650
1686
  }
1651
1687
 
@@ -1706,6 +1742,10 @@ export default class Server {
1706
1742
  }
1707
1743
 
1708
1744
  _getEntryPointAbsolutePath(entryFile: string): string {
1745
+ const resolved = this._resolveWatchFolderPrefix(entryFile);
1746
+ if (resolved != null) {
1747
+ return path.resolve(resolved.rootDir, resolved.filePath);
1748
+ }
1709
1749
  return path.resolve(this._getServerRootDir(), entryFile);
1710
1750
  }
1711
1751
 
package/src/index.flow.js CHANGED
@@ -63,7 +63,6 @@ var _TerminalReporter = _interopRequireDefault(
63
63
  );
64
64
  var _Server = _interopRequireDefault(require("./Server"));
65
65
  var outputBundle = _interopRequireWildcard(require("./shared/output/bundle"));
66
- var _chalk = _interopRequireDefault(require("chalk"));
67
66
  var _fs = _interopRequireDefault(require("fs"));
68
67
  var _http = _interopRequireDefault(require("http"));
69
68
  var _https = _interopRequireDefault(require("https"));
@@ -71,6 +70,7 @@ var _metroConfig = require("metro-config");
71
70
  var _metroCore = require("metro-core");
72
71
  var _net = _interopRequireDefault(require("net"));
73
72
  var _nullthrows = _interopRequireDefault(require("nullthrows"));
73
+ var _util = _interopRequireDefault(require("util"));
74
74
  function _interopRequireWildcard(e, t) {
75
75
  if ("function" == typeof WeakMap)
76
76
  var r = new WeakMap(),
@@ -201,7 +201,7 @@ const runServer = async (
201
201
  await earlyPortCheck(host, config.server.port);
202
202
  if (secure != null || secureCert != null || secureKey != null) {
203
203
  console.warn(
204
- _chalk.default.inverse.yellow.bold(" DEPRECATED "),
204
+ _util.default.styleText(["inverse", "yellow", "bold"], " DEPRECATED "),
205
205
  "The `secure`, `secureCert`, and `secureKey` options are now deprecated. " +
206
206
  "Please use the `secureServerOptions` object instead to pass options to " +
207
207
  "Metro's https development server, or `config.server.tls` in Metro's configuration",
@@ -41,7 +41,6 @@ import JsonReporter from './lib/JsonReporter';
41
41
  import TerminalReporter from './lib/TerminalReporter';
42
42
  import MetroServer from './Server';
43
43
  import * as outputBundle from './shared/output/bundle';
44
- import chalk from 'chalk';
45
44
  import fs from 'fs';
46
45
  import http from 'http';
47
46
  import https from 'https';
@@ -54,6 +53,7 @@ import {
54
53
  import {Terminal} from 'metro-core';
55
54
  import net from 'net';
56
55
  import nullthrows from 'nullthrows';
56
+ import util from 'util';
57
57
 
58
58
  const DEFAULTS = MetroServer.DEFAULT_BUNDLE_OPTIONS;
59
59
 
@@ -296,7 +296,7 @@ export const runServer = async (
296
296
  if (secure != null || secureCert != null || secureKey != null) {
297
297
  // eslint-disable-next-line no-console
298
298
  console.warn(
299
- chalk.inverse.yellow.bold(' DEPRECATED '),
299
+ util.styleText(['inverse', 'yellow', 'bold'], ' DEPRECATED '),
300
300
  'The `secure`, `secureCert`, and `secureKey` options are now deprecated. ' +
301
301
  'Please use the `secureServerOptions` object instead to pass options to ' +
302
302
  "Metro's https development server, or `config.server.tls` in Metro's configuration",
@@ -7,10 +7,10 @@ exports.default = void 0;
7
7
  var _bundleProgressUtils = require("./bundleProgressUtils");
8
8
  var _logToConsole = _interopRequireDefault(require("./logToConsole"));
9
9
  var reporting = _interopRequireWildcard(require("./reporting"));
10
- var _chalk = _interopRequireDefault(require("chalk"));
11
10
  var _lodash = _interopRequireDefault(require("lodash.throttle"));
12
11
  var _metroCore = require("metro-core");
13
12
  var _path = _interopRequireDefault(require("path"));
13
+ var _util = _interopRequireDefault(require("util"));
14
14
  function _interopRequireWildcard(e, t) {
15
15
  if ("function" == typeof WeakMap)
16
16
  var r = new WeakMap(),
@@ -41,6 +41,7 @@ function _interopRequireWildcard(e, t) {
41
41
  function _interopRequireDefault(e) {
42
42
  return e && e.__esModule ? e : { default: e };
43
43
  }
44
+ const style = (format, text) => _util.default.styleText(format, text);
44
45
  const DARK_BLOCK_CHAR = "\u2593";
45
46
  const LIGHT_BLOCK_CHAR = "\u2591";
46
47
  const MAX_PROGRESS_BAR_CHAR_WIDTH = 16;
@@ -68,26 +69,24 @@ class TerminalReporter {
68
69
  const localPath = _path.default.relative(".", entryFile);
69
70
  const filledBar = Math.floor(ratio * MAX_PROGRESS_BAR_CHAR_WIDTH);
70
71
  const bundleTypeColor =
71
- phase === "done"
72
- ? _chalk.default.green
73
- : phase === "failed"
74
- ? _chalk.default.red
75
- : _chalk.default.yellow;
72
+ phase === "done" ? ["green"] : phase === "failed" ? ["red"] : ["yellow"];
76
73
  const progress =
77
74
  phase === "in_progress"
78
- ? _chalk.default.green.bgGreen(DARK_BLOCK_CHAR.repeat(filledBar)) +
79
- _chalk.default.bgWhite.white(
75
+ ? style(["green", "bgGreen"], DARK_BLOCK_CHAR.repeat(filledBar)) +
76
+ style(
77
+ ["bgWhite", "white"],
80
78
  LIGHT_BLOCK_CHAR.repeat(MAX_PROGRESS_BAR_CHAR_WIDTH - filledBar),
81
79
  ) +
82
- _chalk.default.bold(` ${Math.floor(100 * ratio)}% `) +
83
- _chalk.default.dim(`(${transformedFileCount}/${totalFileCount})`)
80
+ style(["bold"], ` ${Math.floor(100 * ratio)}% `) +
81
+ style(["dim"], `(${transformedFileCount}/${totalFileCount})`)
84
82
  : "";
85
83
  return (
86
- bundleTypeColor.inverse.bold(
84
+ style(
85
+ [...bundleTypeColor, "inverse", "bold"],
87
86
  ` ${isPrefetch === true ? "PREBUNDLE" : bundleType.toUpperCase()} `,
88
87
  ) +
89
- _chalk.default.reset.dim(` ${_path.default.dirname(localPath)}/`) +
90
- _chalk.default.bold(_path.default.basename(localPath)) +
88
+ style(["reset", "dim"], ` ${_path.default.dirname(localPath)}/`) +
89
+ style(["bold"], _path.default.basename(localPath)) +
91
90
  " " +
92
91
  progress
93
92
  );
@@ -132,38 +131,36 @@ class TerminalReporter {
132
131
  "",
133
132
  "",
134
133
  ];
135
- const color = hasReducedPerformance
136
- ? _chalk.default.red
137
- : _chalk.default.blue;
138
- this.terminal.log(color(logo.join("\n")));
134
+ const color = hasReducedPerformance ? ["red"] : ["blue"];
135
+ this.terminal.log(style(color, logo.join("\n")));
139
136
  }
140
137
  _logInitializingFailed(port, error) {
141
138
  if (error.code === "EADDRINUSE") {
142
139
  this.terminal.log(
143
- _chalk.default.bgRed.bold(" ERROR "),
144
- _chalk.default.red(
145
- "Metro can't listen on port",
146
- _chalk.default.bold(String(port)),
140
+ style(["bgRed", "bold"], " ERROR "),
141
+ style(
142
+ ["red"],
143
+ `Metro can't listen on port ${style(["bold"], String(port))}`,
147
144
  ),
148
145
  );
149
146
  this.terminal.log(
150
147
  "Most likely another process is already using this port",
151
148
  );
152
149
  this.terminal.log("Run the following command to find out which process:");
153
- this.terminal.log("\n ", _chalk.default.bold("lsof -i :" + port), "\n");
150
+ this.terminal.log("\n ", style(["bold"], "lsof -i :" + port), "\n");
154
151
  this.terminal.log("Then, you can either shut down the other process:");
155
- this.terminal.log("\n ", _chalk.default.bold("kill -9 <PID>"), "\n");
152
+ this.terminal.log("\n ", style(["bold"], "kill -9 <PID>"), "\n");
156
153
  this.terminal.log("or run Metro on different port.");
157
154
  } else {
158
155
  this.terminal.log(
159
- _chalk.default.bgRed.bold(" ERROR "),
160
- _chalk.default.red(error.message),
156
+ style(["bgRed", "bold"], " ERROR "),
157
+ style(["red"], error.message),
161
158
  );
162
159
  const errorAttributes = JSON.stringify(error);
163
160
  if (errorAttributes !== "{}") {
164
- this.terminal.log(_chalk.default.red(errorAttributes));
161
+ this.terminal.log(style(["red"], errorAttributes));
165
162
  }
166
- this.terminal.log(_chalk.default.red(error.stack));
163
+ this.terminal.log(style(["red"], String(error.stack)));
167
164
  }
168
165
  }
169
166
  _log(event) {
@@ -214,22 +211,21 @@ class TerminalReporter {
214
211
  logFn(this.terminal, String(format), ...args);
215
212
  break;
216
213
  case "dep_graph_loading":
217
- const color = event.hasReducedPerformance
218
- ? _chalk.default.red
219
- : _chalk.default.blue;
214
+ const color = event.hasReducedPerformance ? ["red"] : ["blue"];
220
215
  const version = "v" + require("../../package.json").version;
221
216
  this.terminal.log(
222
- color.bold(
223
- " ".repeat(19 - version.length / 2),
224
- "Welcome to Metro " + _chalk.default.white(version) + "\n",
225
- ) +
226
- _chalk.default.dim(
227
- " Fast - Scalable - Integrated\n\n",
228
- ),
217
+ style(
218
+ [...color, "bold"],
219
+ " ".repeat(19 - version.length / 2) +
220
+ " Welcome to Metro " +
221
+ style(["white"], version) +
222
+ "\n",
223
+ ) + style(["dim"], " Fast - Scalable - Integrated\n\n"),
229
224
  );
230
225
  if (event.hasReducedPerformance) {
231
226
  this.terminal.log(
232
- _chalk.default.red(
227
+ style(
228
+ ["red"],
233
229
  "Metro is operating with reduced performance.\n" +
234
230
  "Please fix the problem above and restart Metro.\n\n",
235
231
  ),
@@ -360,7 +356,7 @@ class TerminalReporter {
360
356
  case "success":
361
357
  if (this._prevHealthCheckResult) {
362
358
  this.terminal.log(
363
- _chalk.default.green(`Watcher ${watcherName} is now healthy.`),
359
+ style(["green"], `Watcher ${watcherName} is now healthy.`),
364
360
  );
365
361
  }
366
362
  break;
@@ -399,14 +395,16 @@ class TerminalReporter {
399
395
  break;
400
396
  case "watchman_slow_command":
401
397
  this.terminal.log(
402
- _chalk.default.dim(
398
+ style(
399
+ ["dim"],
403
400
  `Waiting for Watchman \`${status.command}\` (${Math.round(status.timeElapsed / 1000)}s)...`,
404
401
  ),
405
402
  );
406
403
  break;
407
404
  case "watchman_slow_command_complete":
408
405
  this.terminal.log(
409
- _chalk.default.green(
406
+ style(
407
+ ["green"],
410
408
  `Watchman \`${status.command}\` finished after ${(status.timeElapsed / 1000).toFixed(1)}s.`,
411
409
  ),
412
410
  );
@@ -12,15 +12,22 @@
12
12
  import type {BundleDetails, ReportableEvent} from './reporting';
13
13
  import type {Terminal} from 'metro-core';
14
14
  import type {HealthCheckResult, WatcherStatus} from 'metro-file-map';
15
+ import type {BackgroundColors, ForegroundColors, Modifiers} from 'util';
15
16
 
16
17
  import {calculateBundleProgressRatio} from './bundleProgressUtils';
17
18
  import logToConsole from './logToConsole';
18
19
  import * as reporting from './reporting';
19
- import chalk from 'chalk';
20
20
  // $FlowFixMe[untyped-import] lodash.throttle
21
21
  import throttle from 'lodash.throttle';
22
22
  import {AmbiguousModuleResolutionError} from 'metro-core';
23
23
  import path from 'path';
24
+ import util from 'util';
25
+
26
+ type StyleFormat = ReadonlyArray<
27
+ ForegroundColors | BackgroundColors | Modifiers,
28
+ >;
29
+ const style = (format: StyleFormat, text: string): string =>
30
+ util.styleText(format, text);
24
31
 
25
32
  type BundleProgress = {
26
33
  bundleDetails: BundleDetails,
@@ -121,28 +128,26 @@ export default class TerminalReporter {
121
128
  ): string {
122
129
  const localPath = path.relative('.', entryFile);
123
130
  const filledBar = Math.floor(ratio * MAX_PROGRESS_BAR_CHAR_WIDTH);
124
- const bundleTypeColor =
125
- phase === 'done'
126
- ? chalk.green
127
- : phase === 'failed'
128
- ? chalk.red
129
- : chalk.yellow;
131
+ const bundleTypeColor: StyleFormat =
132
+ phase === 'done' ? ['green'] : phase === 'failed' ? ['red'] : ['yellow'];
130
133
  const progress =
131
134
  phase === 'in_progress'
132
- ? chalk.green.bgGreen(DARK_BLOCK_CHAR.repeat(filledBar)) +
133
- chalk.bgWhite.white(
135
+ ? style(['green', 'bgGreen'], DARK_BLOCK_CHAR.repeat(filledBar)) +
136
+ style(
137
+ ['bgWhite', 'white'],
134
138
  LIGHT_BLOCK_CHAR.repeat(MAX_PROGRESS_BAR_CHAR_WIDTH - filledBar),
135
139
  ) +
136
- chalk.bold(` ${Math.floor(100 * ratio)}% `) +
137
- chalk.dim(`(${transformedFileCount}/${totalFileCount})`)
140
+ style(['bold'], ` ${Math.floor(100 * ratio)}% `) +
141
+ style(['dim'], `(${transformedFileCount}/${totalFileCount})`)
138
142
  : '';
139
143
 
140
144
  return (
141
- bundleTypeColor.inverse.bold(
145
+ style(
146
+ [...bundleTypeColor, 'inverse', 'bold'],
142
147
  ` ${isPrefetch === true ? 'PREBUNDLE' : bundleType.toUpperCase()} `,
143
148
  ) +
144
- chalk.reset.dim(` ${path.dirname(localPath)}/`) +
145
- chalk.bold(path.basename(localPath)) +
149
+ style(['reset', 'dim'], ` ${path.dirname(localPath)}/`) +
150
+ style(['bold'], path.basename(localPath)) +
146
151
  ' ' +
147
152
  progress
148
153
  );
@@ -191,31 +196,37 @@ export default class TerminalReporter {
191
196
  '',
192
197
  ];
193
198
 
194
- const color = hasReducedPerformance ? chalk.red : chalk.blue;
195
- this.terminal.log(color(logo.join('\n')));
199
+ const color: StyleFormat = hasReducedPerformance ? ['red'] : ['blue'];
200
+ this.terminal.log(style(color, logo.join('\n')));
196
201
  }
197
202
 
198
203
  _logInitializingFailed(port: number, error: SnippetError): void {
199
204
  if (error.code === 'EADDRINUSE') {
200
205
  this.terminal.log(
201
- chalk.bgRed.bold(' ERROR '),
202
- chalk.red("Metro can't listen on port", chalk.bold(String(port))),
206
+ style(['bgRed', 'bold'], ' ERROR '),
207
+ style(
208
+ ['red'],
209
+ `Metro can't listen on port ${style(['bold'], String(port))}`,
210
+ ),
203
211
  );
204
212
  this.terminal.log(
205
213
  'Most likely another process is already using this port',
206
214
  );
207
215
  this.terminal.log('Run the following command to find out which process:');
208
- this.terminal.log('\n ', chalk.bold('lsof -i :' + port), '\n');
216
+ this.terminal.log('\n ', style(['bold'], 'lsof -i :' + port), '\n');
209
217
  this.terminal.log('Then, you can either shut down the other process:');
210
- this.terminal.log('\n ', chalk.bold('kill -9 <PID>'), '\n');
218
+ this.terminal.log('\n ', style(['bold'], 'kill -9 <PID>'), '\n');
211
219
  this.terminal.log('or run Metro on different port.');
212
220
  } else {
213
- this.terminal.log(chalk.bgRed.bold(' ERROR '), chalk.red(error.message));
221
+ this.terminal.log(
222
+ style(['bgRed', 'bold'], ' ERROR '),
223
+ style(['red'], error.message),
224
+ );
214
225
  const errorAttributes = JSON.stringify(error);
215
226
  if (errorAttributes !== '{}') {
216
- this.terminal.log(chalk.red(errorAttributes));
227
+ this.terminal.log(style(['red'], errorAttributes));
217
228
  }
218
- this.terminal.log(chalk.red(error.stack));
229
+ this.terminal.log(style(['red'], String(error.stack)));
219
230
  }
220
231
  }
221
232
 
@@ -271,20 +282,26 @@ export default class TerminalReporter {
271
282
  logFn(this.terminal, String(format), ...args);
272
283
  break;
273
284
  case 'dep_graph_loading':
274
- const color = event.hasReducedPerformance ? chalk.red : chalk.blue;
285
+ const color: StyleFormat = event.hasReducedPerformance
286
+ ? ['red']
287
+ : ['blue'];
275
288
  // eslint-disable-next-line import/no-commonjs
276
289
  // $FlowFixMe[untyped-import] package.json
277
290
  const version = 'v' + require('../../package.json').version;
278
291
  this.terminal.log(
279
- color.bold(
280
- ' '.repeat(19 - version.length / 2),
281
- 'Welcome to Metro ' + chalk.white(version) + '\n',
282
- ) + chalk.dim(' Fast - Scalable - Integrated\n\n'),
292
+ style(
293
+ [...color, 'bold'],
294
+ ' '.repeat(19 - version.length / 2) +
295
+ ' Welcome to Metro ' +
296
+ style(['white'], version) +
297
+ '\n',
298
+ ) + style(['dim'], ' Fast - Scalable - Integrated\n\n'),
283
299
  );
284
300
 
285
301
  if (event.hasReducedPerformance) {
286
302
  this.terminal.log(
287
- chalk.red(
303
+ style(
304
+ ['red'],
288
305
  'Metro is operating with reduced performance.\n' +
289
306
  'Please fix the problem above and restart Metro.\n\n',
290
307
  ),
@@ -459,7 +476,7 @@ export default class TerminalReporter {
459
476
  // Only report success after a prior failure.
460
477
  if (this._prevHealthCheckResult) {
461
478
  this.terminal.log(
462
- chalk.green(`Watcher ${watcherName} is now healthy.`),
479
+ style(['green'], `Watcher ${watcherName} is now healthy.`),
463
480
  );
464
481
  }
465
482
  break;
@@ -499,7 +516,8 @@ export default class TerminalReporter {
499
516
  break;
500
517
  case 'watchman_slow_command':
501
518
  this.terminal.log(
502
- chalk.dim(
519
+ style(
520
+ ['dim'],
503
521
  `Waiting for Watchman \`${status.command}\` (${Math.round(
504
522
  status.timeElapsed / 1000,
505
523
  )}s)...`,
@@ -508,7 +526,8 @@ export default class TerminalReporter {
508
526
  break;
509
527
  case 'watchman_slow_command_complete':
510
528
  this.terminal.log(
511
- chalk.green(
529
+ style(
530
+ ['green'],
512
531
  `Watchman \`${status.command}\` finished after ${(
513
532
  status.timeElapsed / 1000
514
533
  ).toFixed(1)}s.`,
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true,
5
5
  });
6
6
  exports.default = void 0;
7
- var _chalk = _interopRequireDefault(require("chalk"));
8
7
  var _util = _interopRequireDefault(require("util"));
9
8
  function _interopRequireDefault(e) {
10
9
  return e && e.__esModule ? e : { default: e };
@@ -15,10 +14,10 @@ var _default = (terminal, level, ...data) => {
15
14
  const logFunction = console[level] && level !== "trace" ? level : "log";
16
15
  const color =
17
16
  level === "error"
18
- ? _chalk.default.inverse.red
17
+ ? ["inverse", "red"]
19
18
  : level === "warn"
20
- ? _chalk.default.inverse.yellow
21
- : _chalk.default.inverse.white;
19
+ ? ["inverse", "yellow"]
20
+ : ["inverse", "white"];
22
21
  if (level === "group") {
23
22
  groupStack.push(level);
24
23
  } else if (level === "groupCollapsed") {
@@ -27,7 +26,7 @@ var _default = (terminal, level, ...data) => {
27
26
  collapsedGuardTimer = setTimeout(() => {
28
27
  if (groupStack.includes("groupCollapsed")) {
29
28
  terminal.log(
30
- _chalk.default.inverse.yellow.bold(" WARN "),
29
+ _util.default.styleText(["inverse", "yellow", "bold"], " WARN "),
31
30
  "Expected `console.groupEnd` to be called after `console.groupCollapsed`.",
32
31
  );
33
32
  groupStack.length = 0;
@@ -47,8 +46,10 @@ var _default = (terminal, level, ...data) => {
47
46
  data[data.length - 1] = lastItem.trimEnd();
48
47
  }
49
48
  terminal.log(
50
- color.bold(` ${logFunction.toUpperCase()} `) +
51
- "".padEnd(groupStack.length * 2, " "),
49
+ _util.default.styleText(
50
+ [...color, "bold"],
51
+ ` ${logFunction.toUpperCase()} `,
52
+ ) + "".padEnd(groupStack.length * 2, " "),
52
53
  _util.default.format(...data),
53
54
  );
54
55
  }
@@ -11,8 +11,8 @@
11
11
  /* eslint-disable no-console */
12
12
 
13
13
  import type {Terminal} from 'metro-core';
14
+ import type {BackgroundColors, ForegroundColors, Modifiers} from 'util';
14
15
 
15
- import chalk from 'chalk';
16
16
  import util from 'util';
17
17
 
18
18
  const groupStack = [];
@@ -21,12 +21,12 @@ let collapsedGuardTimer;
21
21
  export default (terminal: Terminal, level: string, ...data: Array<unknown>) => {
22
22
  // $FlowFixMe[invalid-computed-prop]
23
23
  const logFunction = console[level] && level !== 'trace' ? level : 'log';
24
- const color =
24
+ const color: ReadonlyArray<ForegroundColors | BackgroundColors | Modifiers> =
25
25
  level === 'error'
26
- ? chalk.inverse.red
26
+ ? ['inverse', 'red']
27
27
  : level === 'warn'
28
- ? chalk.inverse.yellow
29
- : chalk.inverse.white;
28
+ ? ['inverse', 'yellow']
29
+ : ['inverse', 'white'];
30
30
 
31
31
  if (level === 'group') {
32
32
  groupStack.push(level);
@@ -37,7 +37,7 @@ export default (terminal: Terminal, level: string, ...data: Array<unknown>) => {
37
37
  collapsedGuardTimer = setTimeout(() => {
38
38
  if (groupStack.includes('groupCollapsed')) {
39
39
  terminal.log(
40
- chalk.inverse.yellow.bold(' WARN '),
40
+ util.styleText(['inverse', 'yellow', 'bold'], ' WARN '),
41
41
  'Expected `console.groupEnd` to be called after `console.groupCollapsed`.',
42
42
  );
43
43
  groupStack.length = 0;
@@ -60,7 +60,7 @@ export default (terminal: Terminal, level: string, ...data: Array<unknown>) => {
60
60
  }
61
61
 
62
62
  terminal.log(
63
- color.bold(` ${logFunction.toUpperCase()} `) +
63
+ util.styleText([...color, 'bold'], ` ${logFunction.toUpperCase()} `) +
64
64
  ''.padEnd(groupStack.length * 2, ' '),
65
65
  // `util.format` actually accepts any arguments.
66
66
  // If the first argument is a string, it tries to format it.
@@ -7,30 +7,39 @@ exports.logError = logError;
7
7
  exports.logInfo = logInfo;
8
8
  exports.logWarning = logWarning;
9
9
  exports.nullReporter = void 0;
10
- var _chalk = _interopRequireDefault(require("chalk"));
10
+ var _tty = _interopRequireDefault(require("tty"));
11
11
  var _util = _interopRequireDefault(require("util"));
12
12
  function _interopRequireDefault(e) {
13
13
  return e && e.__esModule ? e : { default: e };
14
14
  }
15
+ const supportsColor = () =>
16
+ process.stdout instanceof _tty.default.WriteStream &&
17
+ process.stdout.hasColors();
15
18
  function logWarning(terminal, format, ...args) {
16
19
  const str = _util.default.format(format, ...args);
17
- terminal.log("%s %s", _chalk.default.yellow.inverse.bold(" WARN "), str);
20
+ terminal.log(
21
+ "%s %s",
22
+ _util.default.styleText(["yellow", "inverse", "bold"], " WARN "),
23
+ str,
24
+ );
18
25
  }
19
26
  function logError(terminal, format, ...args) {
20
27
  terminal.log(
21
28
  "%s %s",
22
- _chalk.default.red.inverse.bold(" ERROR "),
29
+ _util.default.styleText(["red", "inverse", "bold"], " ERROR "),
23
30
  _util.default.format(
24
- _chalk.default.supportsColor
25
- ? format
26
- : _util.default.stripVTControlCharacters(format),
31
+ supportsColor() ? format : _util.default.stripVTControlCharacters(format),
27
32
  ...args,
28
33
  ),
29
34
  );
30
35
  }
31
36
  function logInfo(terminal, format, ...args) {
32
37
  const str = _util.default.format(format, ...args);
33
- terminal.log("%s %s", _chalk.default.cyan.inverse.bold(" INFO "), str);
38
+ terminal.log(
39
+ "%s %s",
40
+ _util.default.styleText(["cyan", "inverse", "bold"], " INFO "),
41
+ str,
42
+ );
34
43
  }
35
44
  const nullReporter = (exports.nullReporter = {
36
45
  update() {},
@@ -14,9 +14,12 @@ import type {HealthCheckResult, WatcherStatus} from 'metro-file-map';
14
14
  import type {CustomResolverOptions} from 'metro-resolver';
15
15
  import type {CustomTransformOptions} from 'metro-transform-worker';
16
16
 
17
- import chalk from 'chalk';
17
+ import tty from 'tty';
18
18
  import util from 'util';
19
19
 
20
+ const supportsColor = (): boolean =>
21
+ process.stdout instanceof tty.WriteStream && process.stdout.hasColors();
22
+
20
23
  export type BundleDetails = {
21
24
  bundleType: string,
22
25
  customResolverOptions: CustomResolverOptions,
@@ -190,7 +193,11 @@ export function logWarning(
190
193
  ...args: Array<unknown>
191
194
  ): void {
192
195
  const str = util.format(format, ...args);
193
- terminal.log('%s %s', chalk.yellow.inverse.bold(' WARN '), str);
196
+ terminal.log(
197
+ '%s %s',
198
+ util.styleText(['yellow', 'inverse', 'bold'], ' WARN '),
199
+ str,
200
+ );
194
201
  }
195
202
 
196
203
  /**
@@ -203,13 +210,13 @@ export function logError(
203
210
  ): void {
204
211
  terminal.log(
205
212
  '%s %s',
206
- chalk.red.inverse.bold(' ERROR '),
213
+ util.styleText(['red', 'inverse', 'bold'], ' ERROR '),
207
214
  // Syntax errors may have colors applied for displaying code frames
208
215
  // in various places outside of where Metro is currently running.
209
216
  // If the current terminal does not support color, we'll strip the colors
210
217
  // here.
211
218
  util.format(
212
- chalk.supportsColor ? format : util.stripVTControlCharacters(format),
219
+ supportsColor() ? format : util.stripVTControlCharacters(format),
213
220
  ...args,
214
221
  ),
215
222
  );
@@ -224,7 +231,11 @@ export function logInfo(
224
231
  ...args: Array<unknown>
225
232
  ): void {
226
233
  const str = util.format(format, ...args);
227
- terminal.log('%s %s', chalk.cyan.inverse.bold(' INFO '), str);
234
+ terminal.log(
235
+ '%s %s',
236
+ util.styleText(['cyan', 'inverse', 'bold'], ' INFO '),
237
+ str,
238
+ );
228
239
  }
229
240
 
230
241
  /**
@@ -80,7 +80,6 @@ function createFileMap(config, options) {
80
80
  dependencyPlugin = new _metroFileMap.DependencyPlugin({
81
81
  dependencyExtractor: config.resolver.dependencyExtractor,
82
82
  computeDependencies: true,
83
- rootDir: config.projectRoot,
84
83
  });
85
84
  plugins.push(dependencyPlugin);
86
85
  }
@@ -88,7 +88,6 @@ export default function createFileMap(
88
88
  dependencyPlugin = new DependencyPlugin({
89
89
  dependencyExtractor: config.resolver.dependencyExtractor,
90
90
  computeDependencies: true,
91
- rootDir: config.projectRoot,
92
91
  });
93
92
  plugins.push(dependencyPlugin);
94
93
  }