metro 0.81.0 → 0.81.1

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.81.0",
3
+ "version": "0.81.1",
4
4
  "description": "🚇 The JavaScript bundler for React Native.",
5
5
  "main": "src/index.js",
6
6
  "bin": "src/cli.js",
@@ -25,33 +25,31 @@
25
25
  "ci-info": "^2.0.0",
26
26
  "connect": "^3.6.5",
27
27
  "debug": "^2.2.0",
28
- "denodeify": "^1.2.1",
29
28
  "error-stack-parser": "^2.0.6",
30
29
  "flow-enums-runtime": "^0.0.6",
31
30
  "graceful-fs": "^4.2.4",
32
- "hermes-parser": "0.24.0",
31
+ "hermes-parser": "0.25.1",
33
32
  "image-size": "^1.0.2",
34
33
  "invariant": "^2.2.4",
35
34
  "jest-worker": "^29.6.3",
36
35
  "jsc-safe-url": "^0.2.2",
37
36
  "lodash.throttle": "^4.1.1",
38
- "metro-babel-transformer": "0.81.0",
39
- "metro-cache": "0.81.0",
40
- "metro-cache-key": "0.81.0",
41
- "metro-config": "0.81.0",
42
- "metro-core": "0.81.0",
43
- "metro-file-map": "0.81.0",
44
- "metro-resolver": "0.81.0",
45
- "metro-runtime": "0.81.0",
46
- "metro-source-map": "0.81.0",
47
- "metro-symbolicate": "0.81.0",
48
- "metro-transform-plugins": "0.81.0",
49
- "metro-transform-worker": "0.81.0",
37
+ "metro-babel-transformer": "0.81.1",
38
+ "metro-cache": "0.81.1",
39
+ "metro-cache-key": "0.81.1",
40
+ "metro-config": "0.81.1",
41
+ "metro-core": "0.81.1",
42
+ "metro-file-map": "0.81.1",
43
+ "metro-resolver": "0.81.1",
44
+ "metro-runtime": "0.81.1",
45
+ "metro-source-map": "0.81.1",
46
+ "metro-symbolicate": "0.81.1",
47
+ "metro-transform-plugins": "0.81.1",
48
+ "metro-transform-worker": "0.81.1",
50
49
  "mime-types": "^2.1.27",
51
50
  "nullthrows": "^1.1.1",
52
51
  "serialize-error": "^2.1.0",
53
52
  "source-map": "^0.5.6",
54
- "strip-ansi": "^6.0.0",
55
53
  "throat": "^5.0.0",
56
54
  "ws": "^7.5.10",
57
55
  "yargs": "^17.6.2"
@@ -65,8 +63,8 @@
65
63
  "dedent": "^0.7.0",
66
64
  "jest-snapshot": "^29.6.3",
67
65
  "jest-snapshot-serializer-raw": "^1.2.0",
68
- "metro-babel-register": "0.81.0",
69
- "metro-memory-fs": "0.81.0",
66
+ "metro-babel-register": "0.81.1",
67
+ "metro-memory-fs": "0.81.1",
70
68
  "mock-req": "^0.2.0",
71
69
  "mock-res": "^0.6.0",
72
70
  "stack-trace": "^0.0.10"
package/src/Assets.js CHANGED
@@ -2,12 +2,9 @@
2
2
 
3
3
  const AssetPaths = require("./node-haste/lib/AssetPaths");
4
4
  const crypto = require("crypto");
5
- const denodeify = require("denodeify");
6
5
  const fs = require("fs");
7
6
  const getImageSize = require("image-size");
8
7
  const path = require("path");
9
- const readDir = denodeify(fs.readdir);
10
- const readFile = denodeify(fs.readFile);
11
8
  function isAssetTypeAnImage(type) {
12
9
  return (
13
10
  [
@@ -37,22 +34,6 @@ function getAssetSize(type, content, filePath) {
37
34
  height,
38
35
  };
39
36
  }
40
- const hashFiles = denodeify(function hashFilesCb(files, hash, callback) {
41
- if (!files.length) {
42
- callback(null);
43
- return;
44
- }
45
- const file = files.shift();
46
- fs.readFile(file, (err, data) => {
47
- if (err) {
48
- callback(err);
49
- return;
50
- } else {
51
- hash.update(data);
52
- hashFilesCb(files, hash, callback);
53
- }
54
- });
55
- });
56
37
  function buildAssetMap(dir, files, platform) {
57
38
  const platforms = new Set(platform != null ? [platform] : []);
58
39
  const assets = files.map((file) => AssetPaths.tryParse(file, platforms));
@@ -93,7 +74,7 @@ function getAssetKey(assetName, platform) {
93
74
  async function getAbsoluteAssetRecord(assetPath, platform = null) {
94
75
  const filename = path.basename(assetPath);
95
76
  const dir = path.dirname(assetPath);
96
- const files = await readDir(dir);
77
+ const files = await fs.promises.readdir(dir);
97
78
  const assetData = AssetPaths.parse(
98
79
  filename,
99
80
  new Set(platform != null ? [platform] : [])
@@ -124,8 +105,11 @@ async function getAbsoluteAssetInfo(assetPath, platform = null) {
124
105
  const { name, type } = nameData;
125
106
  const { scales, files } = await getAbsoluteAssetRecord(assetPath, platform);
126
107
  const hasher = crypto.createHash("md5");
127
- if (files.length > 0) {
128
- await hashFiles(Array.from(files), hasher);
108
+ const fileData = await Promise.all(
109
+ files.map((file) => fs.promises.readFile(file))
110
+ );
111
+ for (const data of fileData) {
112
+ hasher.update(data);
129
113
  }
130
114
  return {
131
115
  files,
@@ -207,10 +191,10 @@ async function getAsset(
207
191
  const record = await getAbsoluteAssetRecord(absolutePath, platform);
208
192
  for (let i = 0; i < record.scales.length; i++) {
209
193
  if (record.scales[i] >= assetData.resolution) {
210
- return readFile(record.files[i]);
194
+ return fs.promises.readFile(record.files[i]);
211
195
  }
212
196
  }
213
- return readFile(record.files[record.files.length - 1]);
197
+ return fs.promises.readFile(record.files[record.files.length - 1]);
214
198
  }
215
199
  function pathBelongsToRoots(pathToCheck, roots) {
216
200
  for (const rootFolder of roots) {
@@ -15,14 +15,10 @@ import type {AssetPath} from './node-haste/lib/AssetPaths';
15
15
 
16
16
  const AssetPaths = require('./node-haste/lib/AssetPaths');
17
17
  const crypto = require('crypto');
18
- const denodeify = require('denodeify');
19
18
  const fs = require('fs');
20
19
  const getImageSize = require('image-size');
21
20
  const path = require('path');
22
21
 
23
- const readDir = denodeify(fs.readdir);
24
- const readFile = denodeify(fs.readFile);
25
-
26
22
  export type AssetInfo = {
27
23
  +files: Array<string>,
28
24
  +hash: string,
@@ -95,25 +91,6 @@ export type AssetDataPlugin = (
95
91
  assetData: AssetData,
96
92
  ) => AssetData | Promise<AssetData>;
97
93
 
98
- const hashFiles = denodeify(function hashFilesCb(files, hash, callback): void {
99
- if (!files.length) {
100
- callback(null);
101
- return;
102
- }
103
-
104
- const file = files.shift();
105
-
106
- fs.readFile(file, (err, data: Buffer) => {
107
- if (err) {
108
- callback(err);
109
- return;
110
- } else {
111
- hash.update(data);
112
- hashFilesCb(files, hash, callback);
113
- }
114
- });
115
- });
116
-
117
94
  function buildAssetMap(
118
95
  dir: string,
119
96
  files: $ReadOnlyArray<string>,
@@ -168,7 +145,7 @@ async function getAbsoluteAssetRecord(
168
145
  ): Promise<{files: Array<string>, scales: Array<number>}> {
169
146
  const filename = path.basename(assetPath);
170
147
  const dir = path.dirname(assetPath);
171
- const files = await readDir(dir);
148
+ const files = await fs.promises.readdir(dir);
172
149
 
173
150
  const assetData = AssetPaths.parse(
174
151
  filename,
@@ -210,8 +187,12 @@ async function getAbsoluteAssetInfo(
210
187
  const {scales, files} = await getAbsoluteAssetRecord(assetPath, platform);
211
188
  const hasher = crypto.createHash('md5');
212
189
 
213
- if (files.length > 0) {
214
- await hashFiles(Array.from(files), hasher);
190
+ const fileData = await Promise.all(
191
+ files.map(file => fs.promises.readFile(file)),
192
+ );
193
+
194
+ for (const data of fileData) {
195
+ hasher.update(data);
215
196
  }
216
197
 
217
198
  return {files, hash: hasher.digest('hex'), name, scales, type};
@@ -328,11 +309,11 @@ async function getAsset(
328
309
 
329
310
  for (let i = 0; i < record.scales.length; i++) {
330
311
  if (record.scales[i] >= assetData.resolution) {
331
- return readFile(record.files[i]);
312
+ return fs.promises.readFile(record.files[i]);
332
313
  }
333
314
  }
334
315
 
335
- return readFile(record.files[record.files.length - 1]);
316
+ return fs.promises.readFile(record.files[record.files.length - 1]);
336
317
  }
337
318
 
338
319
  function pathBelongsToRoots(
@@ -13,7 +13,7 @@
13
13
 
14
14
  import type {DeltaResult, Options} from './types.flow';
15
15
  import type {RootPerfLogger} from 'metro-config';
16
- import type {ChangeEventMetadata} from 'metro-file-map';
16
+ import type {ChangeEvent} from 'metro-file-map';
17
17
 
18
18
  import {Graph} from './Graph';
19
19
  import path from 'path';
@@ -173,9 +173,7 @@ class DeltaCalculator<T> extends EventEmitter {
173
173
  return this._graph;
174
174
  }
175
175
 
176
- /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
177
- * LTI update could not be added via codemod */
178
- _handleMultipleFileChanges = changeEvent => {
176
+ _handleMultipleFileChanges = (changeEvent: ChangeEvent) => {
179
177
  changeEvent.eventsQueue.forEach(eventInfo => {
180
178
  this._handleFileChange(eventInfo, changeEvent.logger);
181
179
  });
@@ -187,16 +185,7 @@ class DeltaCalculator<T> extends EventEmitter {
187
185
  * when the delta needs to be calculated.
188
186
  */
189
187
  _handleFileChange = (
190
- {
191
- type,
192
- filePath,
193
- metadata,
194
- }: {
195
- type: string,
196
- filePath: string,
197
- metadata: ChangeEventMetadata,
198
- ...
199
- },
188
+ {type, filePath, metadata}: ChangeEvent['eventsQueue'][number],
200
189
  logger: ?RootPerfLogger,
201
190
  ): mixed => {
202
191
  debug('Handling %s: %s (type: %s)', type, filePath, metadata.type);
@@ -10,7 +10,9 @@ function generateModules(sourceModules, graph, options) {
10
10
  for (const module of sourceModules) {
11
11
  if (isJsModule(module)) {
12
12
  const getURL = (extension) => {
13
- options.clientUrl.pathname = path.relative(
13
+ const moduleUrl = url.parse(url.format(options.clientUrl), true);
14
+ moduleUrl.search = "";
15
+ moduleUrl.pathname = path.relative(
14
16
  options.serverRoot ?? options.projectRoot,
15
17
  path.join(
16
18
  path.dirname(module.path),
@@ -19,7 +21,8 @@ function generateModules(sourceModules, graph, options) {
19
21
  extension
20
22
  )
21
23
  );
22
- return url.format(options.clientUrl);
24
+ delete moduleUrl.query.excludeSource;
25
+ return url.format(moduleUrl);
23
26
  };
24
27
  const sourceMappingURL = getURL("map");
25
28
  const sourceURL = jscSafeUrl.toJscSafeUrl(getURL("bundle"));
@@ -41,7 +41,12 @@ function generateModules(
41
41
  if (isJsModule(module)) {
42
42
  // Construct a bundle URL for this specific module only
43
43
  const getURL = (extension: 'bundle' | 'map') => {
44
- options.clientUrl.pathname = path.relative(
44
+ const moduleUrl = url.parse(url.format(options.clientUrl), true);
45
+ // the legacy url object is parsed with both "search" and "query" fields.
46
+ // for the "query" field to be used when formatting the object bach to string, the "search" field must be empty.
47
+ // https://nodejs.org/api/url.html#urlformaturlobject:~:text=If%20the%20urlObject.search%20property%20is%20undefined
48
+ moduleUrl.search = '';
49
+ moduleUrl.pathname = path.relative(
45
50
  options.serverRoot ?? options.projectRoot,
46
51
  path.join(
47
52
  path.dirname(module.path),
@@ -50,7 +55,8 @@ function generateModules(
50
55
  extension,
51
56
  ),
52
57
  );
53
- return url.format(options.clientUrl);
58
+ delete moduleUrl.query.excludeSource;
59
+ return url.format(moduleUrl);
54
60
  };
55
61
 
56
62
  const sourceMappingURL = getURL('map');
@@ -169,6 +169,9 @@ class HmrServer<TClient: Client> {
169
169
  runModule: runModule || 'false',
170
170
  shallow: 'true',
171
171
  };
172
+ // the legacy url object is parsed with both "search" and "query" fields.
173
+ // for the "query" field to be used when formatting the object bach to string, the "search" field must be empty.
174
+ // https://nodejs.org/api/url.html#urlformaturlobject:~:text=If%20the%20urlObject.search%20property%20is%20undefined
172
175
  clientUrl.search = '';
173
176
 
174
177
  clientGroup = {
package/src/index.flow.js CHANGED
@@ -264,7 +264,12 @@ exports.runBuild = async (
264
264
  dev,
265
265
  platform,
266
266
  };
267
- await output.save(metroBundle, outputOptions, console.log);
267
+ await output.save(metroBundle, outputOptions, (message) =>
268
+ config.reporter.update({
269
+ type: "bundle_save_log",
270
+ message,
271
+ })
272
+ );
268
273
  }
269
274
  return metroBundle;
270
275
  } finally {
@@ -98,7 +98,7 @@ export type RunBuildOptions = {
98
98
  onComplete?: () => void,
99
99
  onProgress?: (transformedFileCount: number, totalFileCount: number) => void,
100
100
  minify?: boolean,
101
- output?: {
101
+ output?: $ReadOnly<{
102
102
  build: (
103
103
  MetroServer,
104
104
  RequestOptions,
@@ -114,10 +114,10 @@ export type RunBuildOptions = {
114
114
  ...
115
115
  },
116
116
  OutputOptions,
117
- (...args: Array<string>) => void,
117
+ (logMessage: string) => void,
118
118
  ) => Promise<mixed>,
119
119
  ...
120
- },
120
+ }>,
121
121
  platform?: string,
122
122
  sourceMap?: boolean,
123
123
  sourceMapUrl?: string,
@@ -428,8 +428,12 @@ exports.runBuild = async (
428
428
  platform,
429
429
  };
430
430
 
431
- // eslint-disable-next-line no-console
432
- await output.save(metroBundle, outputOptions, console.log);
431
+ await output.save(metroBundle, outputOptions, message =>
432
+ config.reporter.update({
433
+ type: 'bundle_save_log',
434
+ message,
435
+ }),
436
+ );
433
437
  }
434
438
 
435
439
  return metroBundle;
@@ -11,7 +11,7 @@ module.exports = {
11
11
  },
12
12
  watchFolders: [path.resolve(__dirname, "../../../")],
13
13
  server: {
14
- port: 10028,
14
+ port: 0,
15
15
  },
16
16
  resolver: {
17
17
  blockList: [/excluded_from_file_map\.js$/],
@@ -136,6 +136,9 @@ class TerminalReporter {
136
136
  case "bundle_build_done":
137
137
  this._logBundleBuildDone(event.buildID);
138
138
  break;
139
+ case "bundle_save_log":
140
+ this.terminal.log("LOG:" + event.message);
141
+ break;
139
142
  case "bundle_build_failed":
140
143
  this._logBundleBuildFailed(event.buildID);
141
144
  break;
@@ -237,6 +237,9 @@ class TerminalReporter {
237
237
  case 'bundle_build_done':
238
238
  this._logBundleBuildDone(event.buildID);
239
239
  break;
240
+ case 'bundle_save_log':
241
+ this.terminal.log('LOG:' + event.message);
242
+ break;
240
243
  case 'bundle_build_failed':
241
244
  this._logBundleBuildFailed(event.buildID);
242
245
  break;
@@ -2,7 +2,7 @@
2
2
 
3
3
  const os = require("os");
4
4
  module.exports = (workers) => {
5
- const cores = os.cpus().length;
5
+ const cores = os.availableParallelism();
6
6
  return typeof workers === "number" && Number.isInteger(workers)
7
7
  ? Math.min(cores, workers > 0 ? workers : 1)
8
8
  : Math.max(1, Math.ceil(cores * (0.5 + 0.5 * Math.exp(-cores * 0.07)) - 1));
@@ -14,7 +14,8 @@
14
14
  const os = require('os');
15
15
 
16
16
  module.exports = (workers: ?number): number => {
17
- const cores = os.cpus().length;
17
+ // $FlowFixMe[prop-missing] Missing Flow lib def for availableParallelism
18
+ const cores = os.availableParallelism();
18
19
  return typeof workers === 'number' && Number.isInteger(workers)
19
20
  ? Math.min(cores, workers > 0 ? workers : 1)
20
21
  : Math.max(1, Math.ceil(cores * (0.5 + 0.5 * Math.exp(-cores * 0.07)) - 1));
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
 
3
3
  const chalk = require("chalk");
4
- const stripAnsi = require("strip-ansi");
5
4
  const util = require("util");
6
5
  function logWarning(terminal, format, ...args) {
7
6
  const str = util.format(format, ...args);
@@ -11,7 +10,10 @@ function logError(terminal, format, ...args) {
11
10
  terminal.log(
12
11
  "%s %s",
13
12
  chalk.red.inverse.bold(" ERROR "),
14
- util.format(chalk.supportsColor ? format : stripAnsi(format), ...args)
13
+ util.format(
14
+ chalk.supportsColor ? format : util.stripVTControlCharacters(format),
15
+ ...args
16
+ )
15
17
  );
16
18
  }
17
19
  function logInfo(terminal, format, ...args) {
@@ -17,7 +17,6 @@ import type {CustomResolverOptions} from 'metro-resolver';
17
17
  import type {CustomTransformOptions} from 'metro-transform-worker';
18
18
 
19
19
  const chalk = require('chalk');
20
- const stripAnsi = require('strip-ansi');
21
20
  const util = require('util');
22
21
 
23
22
  export type BundleDetails = {
@@ -62,6 +61,11 @@ export type ReportableEvent =
62
61
  type: 'bundle_build_failed',
63
62
  ...
64
63
  }
64
+ | {
65
+ type: 'bundle_save_log',
66
+ message: string,
67
+ ...
68
+ }
65
69
  | {
66
70
  buildID: string,
67
71
  bundleDetails: BundleDetails,
@@ -207,7 +211,10 @@ function logError(
207
211
  // in various places outside of where Metro is currently running.
208
212
  // If the current terminal does not support color, we'll strip the colors
209
213
  // here.
210
- util.format(chalk.supportsColor ? format : stripAnsi(format), ...args),
214
+ util.format(
215
+ chalk.supportsColor ? format : util.stripVTControlCharacters(format),
216
+ ...args,
217
+ ),
211
218
  );
212
219
  }
213
220
 
@@ -12,6 +12,7 @@ const baseIgnoredInlineRequires = [
12
12
  "react",
13
13
  "react/jsx-dev-runtime",
14
14
  "react/jsx-runtime",
15
+ "react-compiler-runtime",
15
16
  "react-native",
16
17
  ];
17
18
  async function calcTransformerOptions(
@@ -78,6 +79,10 @@ async function calcTransformerOptions(
78
79
  );
79
80
  return {
80
81
  ...baseOptions,
82
+ inlinePlatform:
83
+ transform?.unstable_inlinePlatform != null
84
+ ? transform.unstable_inlinePlatform
85
+ : true,
81
86
  inlineRequires: transform?.inlineRequires || false,
82
87
  experimentalImportSupport: transform?.experimentalImportSupport || false,
83
88
  unstable_disableES6Transforms:
@@ -41,6 +41,7 @@ const baseIgnoredInlineRequires = [
41
41
  'react',
42
42
  'react/jsx-dev-runtime',
43
43
  'react/jsx-runtime',
44
+ 'react-compiler-runtime',
44
45
  'react-native',
45
46
  ];
46
47
 
@@ -112,6 +113,10 @@ async function calcTransformerOptions(
112
113
 
113
114
  return {
114
115
  ...baseOptions,
116
+ inlinePlatform:
117
+ transform?.unstable_inlinePlatform != null
118
+ ? transform.unstable_inlinePlatform
119
+ : true,
115
120
  inlineRequires: transform?.inlineRequires || false,
116
121
  experimentalImportSupport: transform?.experimentalImportSupport || false,
117
122
  unstable_disableES6Transforms:
@@ -37,6 +37,7 @@ class DependencyGraph extends EventEmitter {
37
37
  hasReducedPerformance: !!hasReducedPerformance,
38
38
  });
39
39
  const fileMap = createFileMap(config, {
40
+ throwOnModuleCollision: false,
40
41
  watch,
41
42
  });
42
43
  fileMap.setMaxListeners(1000);
@@ -101,7 +101,10 @@ class DependencyGraph extends EventEmitter {
101
101
  type: 'dep_graph_loading',
102
102
  hasReducedPerformance: !!hasReducedPerformance,
103
103
  });
104
- const fileMap = createFileMap(config, {watch});
104
+ const fileMap = createFileMap(config, {
105
+ throwOnModuleCollision: false,
106
+ watch,
107
+ });
105
108
 
106
109
  // We can have a lot of graphs listening to Haste for changes.
107
110
  // Bump this up to silence the max listeners EventEmitter warning.
@@ -6,7 +6,7 @@ function writeSourcemap(fileName, contents, log) {
6
6
  return Promise.resolve();
7
7
  }
8
8
  log("Writing sourcemap output to:", fileName);
9
- const writeMap = writeFile(fileName, contents, null);
9
+ const writeMap = writeFile(fileName, contents);
10
10
  writeMap.then(() => log("Done writing sourcemap output"));
11
11
  return writeMap;
12
12
  }
@@ -22,7 +22,7 @@ function writeSourcemap(
22
22
  return Promise.resolve();
23
23
  }
24
24
  log('Writing sourcemap output to:', fileName);
25
- const writeMap = writeFile(fileName, contents, null);
25
+ const writeMap = writeFile(fileName, contents);
26
26
  // $FlowFixMe[unused-promise]
27
27
  writeMap.then(() => log('Done writing sourcemap output'));
28
28
  return writeMap;
@@ -37,7 +37,7 @@ async function saveBundleAndMap(bundle, options, log) {
37
37
  }
38
38
  writeFns.push(async () => {
39
39
  log(`Writing sourcemap output to: ${sourcemapOutput}`);
40
- await writeFile(sourcemapOutput, map, null);
40
+ await writeFile(sourcemapOutput, map);
41
41
  log("Done writing sourcemap output");
42
42
  });
43
43
  }
@@ -49,7 +49,7 @@ async function saveBundleAndMap(
49
49
  ...
50
50
  },
51
51
  options: OutputOptions,
52
- log: (...args: Array<string>) => void,
52
+ log: string => void,
53
53
  ): Promise<mixed> {
54
54
  const {
55
55
  bundleOutput,
@@ -76,7 +76,7 @@ async function saveBundleAndMap(
76
76
 
77
77
  writeFns.push(async () => {
78
78
  log(`Writing sourcemap output to: ${sourcemapOutput}`);
79
- await writeFile(sourcemapOutput, map, null);
79
+ await writeFile(sourcemapOutput, map);
80
80
  log('Done writing sourcemap output');
81
81
  });
82
82
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
 
3
- const denodeify = require("denodeify");
4
3
  const fs = require("fs");
5
4
  const throat = require("throat");
6
- const writeFile = throat(128, denodeify(fs.writeFile));
5
+ const writeFile = throat(128, fs.promises.writeFile);
7
6
  module.exports = writeFile;
@@ -4,22 +4,19 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @flow
7
+ * @flow strict-local
8
8
  * @format
9
9
  * @oncall react_native
10
10
  */
11
11
 
12
12
  'use strict';
13
13
 
14
- const denodeify = require('denodeify');
15
14
  const fs = require('fs');
16
15
  const throat = require('throat');
17
16
 
18
- type WriteFn = (
19
- file: string,
20
- data: string | Buffer,
21
- encoding?: ?string,
22
- ) => Promise<mixed>;
23
- const writeFile: WriteFn = throat(128, denodeify(fs.writeFile));
17
+ const writeFile: typeof fs.promises.writeFile = throat(
18
+ 128,
19
+ fs.promises.writeFile,
20
+ );
24
21
 
25
22
  module.exports = writeFile;