diff --git a/docs/Caching.md b/docs/Caching.md
index 9275efe5eb..bfda4f658e 100644
--- a/docs/Caching.md
+++ b/docs/Caching.md
@@ -20,7 +20,7 @@ The main option for configuring the Metro cache is [`cacheStores`](./Configurati
Metro provides a number of built-in cache store implementations for use with the [`cacheStores`](./Configuration.md#cachestores) config option:
* **`FileStore({root: string})`** will store cache entries as files under the directory specified by `root`.
-* **`AutoCleanFileStore()`** is a `FileStore` that periodically cleans up old entries. It accepts the same options as `FileStore` plus the following:
+* **`AutoCleanFileStore()`**
Deprecated
is a `FileStore` that periodically cleans up old entries. It accepts the same options as `FileStore` plus the following:
* **`options.intervalMs: number`** is the time in milliseconds between cleanup attempts. Defaults to 10 minutes.
* **`options.cleanupThresholdMs: number`** is the minimum time in milliseconds since the last modification of an entry before it can be deleted. Defaults to 3 days.
* **`HttpStore(options)`** is a bare-bones remote cache client that reads (`GET`) and writes (`PUT`) compressed cache artifacts over HTTP or HTTPS.
diff --git a/packages/metro-cache/package.json b/packages/metro-cache/package.json
index de252800a7..1f1101dd47 100644
--- a/packages/metro-cache/package.json
+++ b/packages/metro-cache/package.json
@@ -23,7 +23,7 @@
"metro-core": "0.83.1"
},
"devDependencies": {
- "metro-memory-fs": "*"
+ "memfs": "^4.38.2"
},
"license": "MIT",
"engines": {
diff --git a/packages/metro-cache/src/Cache.js b/packages/metro-cache/src/Cache.js
index bb15396116..2c70a311e2 100644
--- a/packages/metro-cache/src/Cache.js
+++ b/packages/metro-cache/src/Cache.js
@@ -9,7 +9,7 @@
* @oncall react_native
*/
-import type {CacheStore} from 'metro-cache';
+import type {CacheStore} from './types';
import {Logger} from 'metro-core';
@@ -21,17 +21,15 @@ import {Logger} from 'metro-core';
* All get/set operations are logged via Metro's logger.
*/
export default class Cache {
- _stores: $ReadOnlyArray>;
-
- _hits: WeakMap>;
+ +#stores: $ReadOnlyArray>;
+ +#hits: WeakMap> = new WeakMap();
constructor(stores: $ReadOnlyArray>) {
- this._hits = new WeakMap();
- this._stores = stores;
+ this.#stores = stores;
}
async get(key: Buffer): Promise {
- const stores = this._stores;
+ const stores = this.#stores;
const length = stores.length;
for (let i = 0; i < length; i++) {
@@ -74,7 +72,7 @@ export default class Cache {
);
if (value != null) {
- this._hits.set(key, store);
+ this.#hits.set(key, store);
return value;
}
@@ -85,8 +83,8 @@ export default class Cache {
}
async set(key: Buffer, value: T): Promise {
- const stores = this._stores;
- const stop = this._hits.get(key);
+ const stores = this.#stores;
+ const stop = this.#hits.get(key);
const length = stores.length;
const promises = [];
const writeErrors = [];
@@ -133,6 +131,6 @@ export default class Cache {
// writing to the cache is a no-op and reading from the cache will always
// return null.
get isDisabled(): boolean {
- return this._stores.length === 0;
+ return this.#stores.length === 0;
}
}
diff --git a/packages/metro-cache/src/index.js b/packages/metro-cache/src/index.js
index 46a756f8f4..df7c8f143e 100644
--- a/packages/metro-cache/src/index.js
+++ b/packages/metro-cache/src/index.js
@@ -29,6 +29,15 @@ export {
stableHash,
};
+export interface MetroCache {
+ +AutoCleanFileStore: typeof AutoCleanFileStore;
+ +Cache: typeof Cache;
+ +FileStore: typeof FileStore;
+ +HttpGetStore: typeof HttpGetStore;
+ +HttpStore: typeof HttpStore;
+ +stableHash: typeof stableHash;
+}
+
/**
* Backwards-compatibility with CommonJS consumers using interopRequireDefault.
* Do not add to this list.
@@ -42,4 +51,4 @@ export default {
HttpGetStore,
HttpStore,
stableHash,
-};
+} as MetroCache;
diff --git a/packages/metro-cache/src/stores/AutoCleanFileStore.js b/packages/metro-cache/src/stores/AutoCleanFileStore.js
index effde7df9b..8ebc34bcbc 100644
--- a/packages/metro-cache/src/stores/AutoCleanFileStore.js
+++ b/packages/metro-cache/src/stores/AutoCleanFileStore.js
@@ -4,8 +4,9 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
+ * @flow strict-local
* @format
- * @flow
+ * @oncall react_native
*/
import type {Options} from './FileStore';
@@ -14,90 +15,72 @@ import FileStore from './FileStore';
import fs from 'fs';
import path from 'path';
-type CleanOptions = {
+type CleanOptions = $ReadOnly<{
...Options,
intervalMs?: number,
cleanupThresholdMs?: number,
- ...
-};
-
-type FileList = {
- path: string,
- stats: fs.Stats,
- ...
-};
-
-// List all files in a directory in Node.js recursively in a synchronous fashion
-const walkSync = function (
- dir: string,
- filelist: Array,
-): Array {
- const files = fs.readdirSync(dir);
- filelist = filelist || [];
- files.forEach(function (file) {
- const fullPath = path.join(dir, file);
- const stats = fs.statSync(fullPath);
- if (stats.isDirectory()) {
- filelist = walkSync(fullPath + path.sep, filelist);
- } else {
- filelist.push({path: fullPath, stats});
- }
- });
- return filelist;
-};
-
-function get(property: ?T, defaultValue: T): T {
- if (property == null) {
- return defaultValue;
- }
-
- return property;
-}
+}>;
/**
- * A FileStore that cleans itself up in a given interval
+ * A FileStore that, at a given interval, stats the content of the cache root
+ * and deletes any file last modified a set threshold in the past.
+ *
+ * @deprecated This is not efficiently implemented and may cause significant
+ * redundant I/O when caches are large. Prefer your own cleanup scripts, or a
+ * custom Metro cache that uses watches, hooks get/set, and/or implements LRU.
*/
export default class AutoCleanFileStore extends FileStore {
- _intervalMs: number;
- _cleanupThresholdMs: number;
- _root: string;
+ +#intervalMs: number;
+ +#cleanupThresholdMs: number;
+ +#root: string;
constructor(opts: CleanOptions) {
super({root: opts.root});
- this._intervalMs = get(opts.intervalMs, 10 * 60 * 1000); // 10 minutes
- this._cleanupThresholdMs = get(
- opts.cleanupThresholdMs,
- 3 * 24 * 60 * 60 * 1000, // 3 days
- );
+ this.#root = opts.root;
+ this.#intervalMs = opts.intervalMs ?? 10 * 60 * 1000; // 10 minutes
+ this.#cleanupThresholdMs =
+ opts.cleanupThresholdMs ?? 3 * 24 * 60 * 60 * 1000; // 3 days
- this._scheduleCleanup();
+ this.#scheduleCleanup();
}
- _scheduleCleanup() {
- // $FlowFixMe[method-unbinding] added when improving typing for this parameters
- setTimeout(this._doCleanup.bind(this), this._intervalMs);
+ #scheduleCleanup() {
+ setTimeout(() => this.#doCleanup(), this.#intervalMs);
}
- _doCleanup() {
- const files = walkSync(this._root, []);
+ #doCleanup() {
+ const dirents = fs.readdirSync(this.#root, {
+ recursive: true,
+ withFileTypes: true,
+ });
let warned = false;
- files.forEach(file => {
- if (file.stats.mtimeMs < Date.now() - this._cleanupThresholdMs) {
+ const minModifiedTime = Date.now() - this.#cleanupThresholdMs;
+ dirents
+ .filter(dirent => dirent.isFile())
+ .forEach(dirent => {
+ const absolutePath = path.join(
+ // $FlowFixMe[prop-missing] - dirent.parentPath added in Node 20.12
+ dirent.parentPath,
+ dirent.name.toString(),
+ );
try {
- fs.unlinkSync(file.path);
+ if (fs.statSync(absolutePath).mtimeMs < minModifiedTime) {
+ fs.unlinkSync(absolutePath);
+ }
} catch (e) {
if (!warned) {
console.warn(
- 'Problem cleaning up cache for ' + file.path + ': ' + e.message,
+ 'Problem cleaning up cache for ' +
+ absolutePath +
+ ': ' +
+ e.message,
);
warned = true;
}
}
- }
- });
-
- this._scheduleCleanup();
+ });
+ this.#scheduleCleanup();
}
}
diff --git a/packages/metro-cache/src/stores/FileStore.js b/packages/metro-cache/src/stores/FileStore.js
index fc37b558da..3394eee0fe 100644
--- a/packages/metro-cache/src/stores/FileStore.js
+++ b/packages/metro-cache/src/stores/FileStore.js
@@ -4,8 +4,9 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @format
* @flow
+ * @format
+ * @oncall react_native
*/
import fs from 'fs';
@@ -14,20 +15,20 @@ import path from 'path';
const NULL_BYTE = 0x00;
const NULL_BYTE_BUFFER = Buffer.from([NULL_BYTE]);
-export type Options = {
+export type Options = $ReadOnly<{
root: string,
-};
+}>;
export default class FileStore {
- _root: string;
+ +#root: string;
constructor(options: Options) {
- this._root = options.root;
+ this.#root = options.root;
}
async get(key: Buffer): Promise {
try {
- const data = await fs.promises.readFile(this._getFilePath(key));
+ const data = await fs.promises.readFile(this.#getFilePath(key));
if (data[0] === NULL_BYTE) {
return (data.slice(1): any);
@@ -44,20 +45,20 @@ export default class FileStore {
}
async set(key: Buffer, value: T): Promise {
- const filePath = this._getFilePath(key);
+ const filePath = this.#getFilePath(key);
try {
- await this._set(filePath, value);
+ await this.#set(filePath, value);
} catch (err) {
if (err.code === 'ENOENT') {
fs.mkdirSync(path.dirname(filePath), {recursive: true});
- await this._set(filePath, value);
+ await this.#set(filePath, value);
} else {
throw err;
}
}
}
- async _set(filePath: string, value: T): Promise {
+ async #set(filePath: string, value: T): Promise {
let content;
if (value instanceof Buffer) {
content = Buffer.concat([NULL_BYTE_BUFFER, value]);
@@ -68,20 +69,20 @@ export default class FileStore {
}
clear() {
- this._removeDirs();
+ this.#removeDirs();
}
- _getFilePath(key: Buffer): string {
+ #getFilePath(key: Buffer): string {
return path.join(
- this._root,
+ this.#root,
key.slice(0, 1).toString('hex'),
key.slice(1).toString('hex'),
);
}
- _removeDirs() {
+ #removeDirs() {
for (let i = 0; i < 256; i++) {
- fs.rmSync(path.join(this._root, ('0' + i.toString(16)).slice(-2)), {
+ fs.rmSync(path.join(this.#root, ('0' + i.toString(16)).slice(-2)), {
force: true,
recursive: true,
});
diff --git a/packages/metro-cache/src/stores/HttpError.js b/packages/metro-cache/src/stores/HttpError.js
index 4baf14a5d0..aaf9a974ef 100644
--- a/packages/metro-cache/src/stores/HttpError.js
+++ b/packages/metro-cache/src/stores/HttpError.js
@@ -4,8 +4,8 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @format
* @flow strict
+ * @format
*/
export default class HttpError extends Error {
diff --git a/packages/metro-cache/src/stores/HttpGetStore.js b/packages/metro-cache/src/stores/HttpGetStore.js
index 7608b7801a..560e0071ab 100644
--- a/packages/metro-cache/src/stores/HttpGetStore.js
+++ b/packages/metro-cache/src/stores/HttpGetStore.js
@@ -4,24 +4,25 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @format
* @flow strict-local
+ * @format
+ * @oncall react_native
*/
import type HttpError from './HttpError';
+import type {Options as HttpOptions} from './HttpStore';
import type NetworkError from './NetworkError';
-import type {HttpOptions} from 'metro-cache';
import HttpStore from './HttpStore';
import {Logger} from 'metro-core';
export default class HttpGetStore extends HttpStore {
- _warned: boolean;
+ #warned: boolean;
constructor(options: HttpOptions) {
super(options);
- this._warned = false;
+ this.#warned = false;
}
async get(key: Buffer): Promise {
@@ -35,18 +36,16 @@ export default class HttpGetStore extends HttpStore {
throw err;
}
- this._warn(err);
+ this.#warn(err);
return null;
}
}
- set(): Promise {
- return Promise.resolve(undefined);
- }
+ async set(_key: Buffer, _value: T): Promise {}
- _warn(err: HttpError | NetworkError) {
- if (!this._warned) {
+ #warn(err: HttpError | NetworkError) {
+ if (!this.#warned) {
process.emitWarning(
[
'Could not connect to the HTTP cache.',
@@ -60,7 +59,7 @@ export default class HttpGetStore extends HttpStore {
log_entry_label: `${err.message} (${err.code})`,
}),
);
- this._warned = true;
+ this.#warned = true;
}
}
}
diff --git a/packages/metro-cache/src/stores/HttpStore.js b/packages/metro-cache/src/stores/HttpStore.js
index 8481b2bf49..590a3eaa86 100644
--- a/packages/metro-cache/src/stores/HttpStore.js
+++ b/packages/metro-cache/src/stores/HttpStore.js
@@ -4,8 +4,9 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @format
* @flow
+ * @format
+ * @oncall react_native
*/
import type {HttpsProxyAgentOptions} from 'https-proxy-agent';
@@ -78,19 +79,19 @@ export default class HttpStore {
static HttpError: typeof HttpError = HttpError;
static NetworkError: typeof NetworkError = NetworkError;
- _getEndpoint: Endpoint;
- _setEndpoint: Endpoint;
+ #getEndpoint: Endpoint;
+ #setEndpoint: Endpoint;
constructor(options: Options) {
- this._getEndpoint = this.createEndpointConfig(
+ this.#getEndpoint = this.#createEndpointConfig(
options.getOptions != null ? options.getOptions : options,
);
- this._setEndpoint = this.createEndpointConfig(
+ this.#setEndpoint = this.#createEndpointConfig(
options.setOptions != null ? options.setOptions : options,
);
}
- createEndpointConfig(options: EndpointOptions): Endpoint {
+ #createEndpointConfig(options: EndpointOptions): Endpoint {
const agentConfig: http$agentOptions & HttpsProxyAgentOptions = {
family: options.family,
keepAlive: true,
@@ -151,32 +152,32 @@ export default class HttpStore {
}
get(key: Buffer): Promise {
- return this.#withRetries(() => this.#getOnce(key), this._getEndpoint);
+ return this.#withRetries(() => this.#getOnce(key), this.#getEndpoint);
}
#getOnce(key: Buffer): Promise {
return new Promise((resolve, reject) => {
- let searchParamsString = this._getEndpoint.params.toString();
+ let searchParamsString = this.#getEndpoint.params.toString();
if (searchParamsString != '') {
searchParamsString = '?' + searchParamsString;
}
const options = {
- agent: this._getEndpoint.agent,
- headers: this._getEndpoint.headers,
- host: this._getEndpoint.host,
+ agent: this.#getEndpoint.agent,
+ headers: this.#getEndpoint.headers,
+ host: this.#getEndpoint.host,
method: 'GET',
- path: `${this._getEndpoint.path}/${key.toString(
+ path: `${this.#getEndpoint.path}/${key.toString(
'hex',
)}${searchParamsString}`,
- port: this._getEndpoint.port,
- timeout: this._getEndpoint.timeout,
+ port: this.#getEndpoint.port,
+ timeout: this.#getEndpoint.timeout,
};
// $FlowFixMe[incompatible-type]
/* $FlowFixMe[missing-local-annot](>=0.101.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.101 was deployed. To see the error, delete
* this comment and run Flow. */
- const req = this._getEndpoint.module.request(options, res => {
+ const req = this.#getEndpoint.module.request(options, res => {
const code = res.statusCode;
const data = [];
@@ -187,9 +188,9 @@ export default class HttpStore {
return;
} else if (
code !== 200 &&
- !this._getEndpoint.additionalSuccessStatuses.has(code)
+ !this.#getEndpoint.additionalSuccessStatuses.has(code)
) {
- if (this._getEndpoint.debug) {
+ if (this.#getEndpoint.debug) {
res.on('data', chunk => {
data.push(chunk);
});
@@ -275,7 +276,7 @@ export default class HttpStore {
set(key: Buffer, value: T): Promise {
return this.#withRetries(
() => this.#setOnce(key, value),
- this._setEndpoint,
+ this.#setEndpoint,
);
}
@@ -283,35 +284,35 @@ export default class HttpStore {
return new Promise((resolve, reject) => {
const gzip = zlib.createGzip(ZLIB_OPTIONS);
- let searchParamsString = this._setEndpoint.params.toString();
+ let searchParamsString = this.#setEndpoint.params.toString();
if (searchParamsString != '') {
searchParamsString = '?' + searchParamsString;
}
const options = {
- agent: this._setEndpoint.agent,
- headers: this._setEndpoint.headers,
- host: this._setEndpoint.host,
+ agent: this.#setEndpoint.agent,
+ headers: this.#setEndpoint.headers,
+ host: this.#setEndpoint.host,
method: 'PUT',
- path: `${this._setEndpoint.path}/${key.toString(
+ path: `${this.#setEndpoint.path}/${key.toString(
'hex',
)}${searchParamsString}`,
- port: this._setEndpoint.port,
- timeout: this._setEndpoint.timeout,
+ port: this.#setEndpoint.port,
+ timeout: this.#setEndpoint.timeout,
};
// $FlowFixMe[incompatible-type]
/* $FlowFixMe[missing-local-annot](>=0.101.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.101 was deployed. To see the error, delete
* this comment and run Flow. */
- const req = this._setEndpoint.module.request(options, res => {
+ const req = this.#setEndpoint.module.request(options, res => {
const code = res.statusCode;
if (
(code < 200 || code > 299) &&
- !this._setEndpoint.additionalSuccessStatuses.has(code)
+ !this.#setEndpoint.additionalSuccessStatuses.has(code)
) {
- if (this._setEndpoint.debug) {
+ if (this.#setEndpoint.debug) {
const data = [];
res.on('data', chunk => {
data.push(chunk);
@@ -395,13 +396,13 @@ export default class HttpStore {
return backOff(fn, {
jitter: 'full',
maxDelay: 30000,
- numOfAttempts: this._getEndpoint.maxAttempts || Number.POSITIVE_INFINITY,
+ numOfAttempts: this.#getEndpoint.maxAttempts || Number.POSITIVE_INFINITY,
retry: (e: Error) => {
if (e instanceof HttpError) {
- return this._getEndpoint.retryStatuses.has(e.code);
+ return this.#getEndpoint.retryStatuses.has(e.code);
}
return (
- e instanceof NetworkError && this._getEndpoint.retryNetworkErrors
+ e instanceof NetworkError && this.#getEndpoint.retryNetworkErrors
);
},
});
diff --git a/packages/metro-cache/src/stores/NetworkError.js b/packages/metro-cache/src/stores/NetworkError.js
index b3ec18cc16..4372b3ddc7 100644
--- a/packages/metro-cache/src/stores/NetworkError.js
+++ b/packages/metro-cache/src/stores/NetworkError.js
@@ -4,8 +4,8 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @format
* @flow strict
+ * @format
*/
export default class NetworkError extends Error {
diff --git a/packages/metro-cache/src/stores/__tests__/AutoCleanFileStore-test.js b/packages/metro-cache/src/stores/__tests__/AutoCleanFileStore-test.js
index af314226b8..c4a9380ee2 100644
--- a/packages/metro-cache/src/stores/__tests__/AutoCleanFileStore-test.js
+++ b/packages/metro-cache/src/stores/__tests__/AutoCleanFileStore-test.js
@@ -9,7 +9,7 @@
* @oncall react_native
*/
-'use strict';
+import {memfs} from 'memfs';
describe('AutoCleanFileStore', () => {
let AutoCleanFileStore;
@@ -19,22 +19,23 @@ describe('AutoCleanFileStore', () => {
jest
.resetModules()
.resetAllMocks()
- .mock('fs', () => new (require('metro-memory-fs'))());
-
+ .mock('fs', () => memfs().fs);
AutoCleanFileStore = require('../AutoCleanFileStore').default;
fs = require('fs');
+ jest.spyOn(fs, 'statSync');
jest.spyOn(fs, 'unlinkSync');
});
test('sets and writes into the cache', async () => {
- // $FlowFixMe[underconstrained-implicit-instantiation]
- const fileStore = new AutoCleanFileStore({
+ const fileStore = new AutoCleanFileStore({
root: '/root',
intervalMs: 49,
- cleanupThresholdMs: 0,
+ cleanupThresholdMs: 90,
});
const cache = Buffer.from([0xfa, 0xce, 0xb0, 0x0c]);
+ expect(fs.statSync).toHaveBeenCalledTimes(0);
+
await fileStore.set(cache, {foo: 42});
expect(await fileStore.get(cache)).toEqual({foo: 42});
@@ -43,17 +44,28 @@ describe('AutoCleanFileStore', () => {
expect(await fileStore.get(cache)).toEqual({foo: 42});
+ // And there should have been no cleanup
+ expect(fs.statSync).not.toHaveBeenCalled();
+
// Run to 50ms so that we've exceeded the 49ms cleanup interval
jest.advanceTimersByTime(20);
- // mtime doesn't work very well in in-memory-store, so we couldn't test that
- // functionality
+ expect(fs.statSync).toHaveBeenCalledTimes(1);
+
+ // At 50ms we should have checked the file, but it's still fresh enough
+ expect(await fileStore.get(cache)).toEqual({foo: 42});
+ expect(fs.unlinkSync).not.toHaveBeenCalled();
+
+ jest.advanceTimersByTime(50);
+
+ // After another 50ms, we should have checked the file again and deleted it
+ expect(fs.statSync).toHaveBeenCalledTimes(2);
+ expect(fs.unlinkSync).toHaveBeenCalledTimes(1);
expect(await fileStore.get(cache)).toEqual(null);
});
test('returns null when reading a non-existing file', async () => {
- // $FlowFixMe[underconstrained-implicit-instantiation]
- const fileStore = new AutoCleanFileStore({root: '/root'});
+ const fileStore = new AutoCleanFileStore({root: '/root'});
const cache = Buffer.from([0xfa, 0xce, 0xb0, 0x0c]);
expect(await fileStore.get(cache)).toEqual(null);
diff --git a/packages/metro-cache/src/stores/__tests__/FileStore-test.js b/packages/metro-cache/src/stores/__tests__/FileStore-test.js
index 027e7d3eb4..5f7a70a420 100644
--- a/packages/metro-cache/src/stores/__tests__/FileStore-test.js
+++ b/packages/metro-cache/src/stores/__tests__/FileStore-test.js
@@ -4,13 +4,12 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
+ * @flow strict-local
* @format
* @oncall react_native
*/
-'use strict';
-
-const {dirname} = require('path');
+import {memfs} from 'memfs';
describe('FileStore', () => {
let FileStore;
@@ -20,7 +19,7 @@ describe('FileStore', () => {
jest
.resetModules()
.resetAllMocks()
- .mock('fs', () => new (require('metro-memory-fs'))());
+ .mock('fs', () => memfs().fs);
FileStore = require('../FileStore').default;
fs = require('fs');
@@ -28,7 +27,7 @@ describe('FileStore', () => {
});
test('sets and writes into the cache', async () => {
- const fileStore = new FileStore({root: '/root'});
+ const fileStore = new FileStore({root: '/root'});
const cache = Buffer.from([0xfa, 0xce, 0xb0, 0x0c]);
await fileStore.set(cache, {foo: 42});
@@ -36,23 +35,22 @@ describe('FileStore', () => {
});
test('returns null when reading a non-existing file', async () => {
- const fileStore = new FileStore({root: '/root'});
+ const fileStore = new FileStore({root: '/root'});
const cache = Buffer.from([0xfa, 0xce, 0xb0, 0x0c]);
expect(await fileStore.get(cache)).toEqual(null);
});
test('returns null when reading a empty file', async () => {
- const fileStore = new FileStore({root: '/root'});
+ const fileStore = new FileStore({root: '/root'});
const cache = Buffer.from([0xfa, 0xce, 0xb0, 0x0c]);
- const filePath = fileStore._getFilePath(cache);
- fs.mkdirSync(dirname(filePath), {recursive: true});
- fs.writeFileSync(filePath, '');
+ jest.spyOn(fs.promises, 'readFile').mockImplementation(async () => '');
expect(await fileStore.get(cache)).toEqual(null);
+ expect(fs.promises.readFile).toHaveBeenCalledWith(expect.any(String));
});
test('writes into cache if folder is missing', async () => {
- const fileStore = new FileStore({root: '/root'});
+ const fileStore = new FileStore({root: '/root'});
const cache = Buffer.from([0xfa, 0xce, 0xb0, 0x0c]);
const data = Buffer.from([0xca, 0xc4, 0xe5]);
@@ -62,7 +60,7 @@ describe('FileStore', () => {
});
test('reads and writes binary data', async () => {
- const fileStore = new FileStore({root: '/root'});
+ const fileStore = new FileStore({root: '/root'});
const cache = Buffer.from([0xfa, 0xce, 0xb0, 0x0c]);
const data = Buffer.from([0xca, 0xc4, 0xe5]);
diff --git a/packages/metro-cache/types/Cache.d.ts b/packages/metro-cache/types/Cache.d.ts
index f97c4f41b7..f95aa554b3 100644
--- a/packages/metro-cache/types/Cache.d.ts
+++ b/packages/metro-cache/types/Cache.d.ts
@@ -8,8 +8,7 @@
* @oncall react_native
*/
-import {CacheStore} from './types';
-
+import type {CacheStore} from './types';
/**
* Main cache class. Receives an array of cache instances, and sequentially
* traverses them to return a previously stored value. It also ensures setting
@@ -17,9 +16,10 @@ import {CacheStore} from './types';
*
* All get/set operations are logged via Metro's logger.
*/
-export default class Cache {
+declare class Cache {
constructor(stores: ReadonlyArray>);
- get(key: Buffer): Promise;
+ get(key: Buffer): Promise;
set(key: Buffer, value: T): Promise;
get isDisabled(): boolean;
}
+export default Cache;
diff --git a/packages/metro-cache/types/index.d.ts b/packages/metro-cache/types/index.d.ts
index 417518ded1..f676d9adb4 100644
--- a/packages/metro-cache/types/index.d.ts
+++ b/packages/metro-cache/types/index.d.ts
@@ -26,16 +26,14 @@ export {
HttpStore,
stableHash,
};
-
export interface MetroCache {
- AutoCleanFileStore: typeof AutoCleanFileStore;
- Cache: typeof Cache;
- FileStore: typeof FileStore;
- HttpGetStore: typeof HttpGetStore;
- HttpStore: typeof HttpStore;
- stableHash: typeof stableHash;
+ readonly AutoCleanFileStore: typeof AutoCleanFileStore;
+ readonly Cache: typeof Cache;
+ readonly FileStore: typeof FileStore;
+ readonly HttpGetStore: typeof HttpGetStore;
+ readonly HttpStore: typeof HttpStore;
+ readonly stableHash: typeof stableHash;
}
-
/**
* Backwards-compatibility with CommonJS consumers using interopRequireDefault.
* Do not add to this list.
@@ -43,4 +41,6 @@ export interface MetroCache {
* @deprecated Default import from 'metro-cache' is deprecated, use named exports.
*/
declare const $$EXPORT_DEFAULT_DECLARATION$$: MetroCache;
+declare type $$EXPORT_DEFAULT_DECLARATION$$ =
+ typeof $$EXPORT_DEFAULT_DECLARATION$$;
export default $$EXPORT_DEFAULT_DECLARATION$$;
diff --git a/packages/metro-cache/types/stableHash.d.ts b/packages/metro-cache/types/stableHash.d.ts
index a50001f301..6e9a72292f 100644
--- a/packages/metro-cache/types/stableHash.d.ts
+++ b/packages/metro-cache/types/stableHash.d.ts
@@ -8,4 +8,5 @@
* @oncall react_native
*/
-export default function stableHash(value: unknown): Buffer;
+declare function stableHash(value: unknown): Buffer;
+export default stableHash;
diff --git a/packages/metro-cache/types/stores/AutoCleanFileStore.d.ts b/packages/metro-cache/types/stores/AutoCleanFileStore.d.ts
index 693b81a6f4..22aa4ab6bd 100644
--- a/packages/metro-cache/types/stores/AutoCleanFileStore.d.ts
+++ b/packages/metro-cache/types/stores/AutoCleanFileStore.d.ts
@@ -8,6 +8,25 @@
* @oncall react_native
*/
-import type FileStore from './FileStore';
+import type {Options} from './FileStore';
-export default class AutoCleanFileStore extends FileStore {}
+import FileStore from './FileStore';
+
+type CleanOptions = Readonly<
+ Omit & {
+ intervalMs?: number;
+ cleanupThresholdMs?: number;
+ }
+>;
+/**
+ * A FileStore that, at a given interval, stats the content of the cache root
+ * and deletes any file last modified a set threshold in the past.
+ *
+ * @deprecated This is not efficiently implemented and may cause significant
+ * redundant I/O when caches are large. Prefer your own cleanup scripts, or a
+ * custom Metro cache that uses watches, hooks get/set, and/or implements LRU.
+ */
+declare class AutoCleanFileStore extends FileStore {
+ constructor(opts: CleanOptions);
+}
+export default AutoCleanFileStore;
diff --git a/packages/metro-cache/types/stores/FileStore.d.ts b/packages/metro-cache/types/stores/FileStore.d.ts
index 238814a30f..8a62ac3d7a 100644
--- a/packages/metro-cache/types/stores/FileStore.d.ts
+++ b/packages/metro-cache/types/stores/FileStore.d.ts
@@ -8,13 +8,11 @@
* @oncall react_native
*/
-export interface Options {
- root: string;
-}
-
-export default class FileStore {
+export type Options = Readonly<{root: string}>;
+declare class FileStore {
constructor(options: Options);
- get(key: Buffer): Promise;
+ get(key: Buffer): Promise;
set(key: Buffer, value: T): Promise;
clear(): void;
}
+export default FileStore;
diff --git a/packages/metro-cache/types/stores/HttpError.d.ts b/packages/metro-cache/types/stores/HttpError.d.ts
new file mode 100644
index 0000000000..5837c66df6
--- /dev/null
+++ b/packages/metro-cache/types/stores/HttpError.d.ts
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ */
+
+declare class HttpError extends Error {
+ code: number;
+ constructor(message: string, code: number);
+}
+export default HttpError;
diff --git a/packages/metro-cache/types/stores/HttpGetStore.d.ts b/packages/metro-cache/types/stores/HttpGetStore.d.ts
index 52c3a59e35..70eaa16e79 100644
--- a/packages/metro-cache/types/stores/HttpGetStore.d.ts
+++ b/packages/metro-cache/types/stores/HttpGetStore.d.ts
@@ -8,11 +8,13 @@
* @oncall react_native
*/
-import type {Options} from './HttpStore';
+import type {Options as HttpOptions} from './HttpStore';
-export default class HttpGetStore {
- constructor(options: Options);
- get(key: Buffer): Promise;
- set(key: Buffer, value: T): Promise;
- clear(): void;
+import HttpStore from './HttpStore';
+
+declare class HttpGetStore extends HttpStore {
+ constructor(options: HttpOptions);
+ get(key: Buffer): Promise;
+ set(_key: Buffer, _value: T): Promise;
}
+export default HttpGetStore;
diff --git a/packages/metro-cache/types/stores/HttpStore.d.ts b/packages/metro-cache/types/stores/HttpStore.d.ts
index ac1bd54f62..bfc235d597 100644
--- a/packages/metro-cache/types/stores/HttpStore.d.ts
+++ b/packages/metro-cache/types/stores/HttpStore.d.ts
@@ -8,18 +8,41 @@
* @oncall react_native
*/
-export interface Options {
+import HttpError from './HttpError';
+import NetworkError from './NetworkError';
+
+export type Options =
+ | EndpointOptions
+ | {getOptions: EndpointOptions; setOptions: EndpointOptions};
+type EndpointOptions = {
endpoint: string;
family?: 4 | 6;
timeout?: number;
key?: string | ReadonlyArray | Buffer | ReadonlyArray;
cert?: string | ReadonlyArray | Buffer | ReadonlyArray;
ca?: string | ReadonlyArray | Buffer | ReadonlyArray;
-}
-
-export default class HttpStore {
+ params?: URLSearchParams;
+ headers?: {[$$Key$$: string]: string};
+ additionalSuccessStatuses?: ReadonlyArray;
+ /**
+ * Whether to include additional debug information in error messages.
+ */
+ debug?: boolean;
+ /**
+ * Retry configuration
+ */
+ maxAttempts?: number;
+ retryNetworkErrors?: boolean;
+ retryStatuses?: ReadonlySet;
+ socketPath?: string;
+ proxy?: string;
+};
+declare class HttpStore {
+ static HttpError: typeof HttpError;
+ static NetworkError: typeof NetworkError;
constructor(options: Options);
- get(key: Buffer): Promise;
+ get(key: Buffer): Promise;
set(key: Buffer, value: T): Promise;
clear(): void;
}
+export default HttpStore;
diff --git a/packages/metro-cache/types/stores/NetworkError.d.ts b/packages/metro-cache/types/stores/NetworkError.d.ts
new file mode 100644
index 0000000000..3984acfb7f
--- /dev/null
+++ b/packages/metro-cache/types/stores/NetworkError.d.ts
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ */
+
+declare class NetworkError extends Error {
+ code: string;
+ constructor(message: string, code: string);
+}
+export default NetworkError;
diff --git a/packages/metro-cache/types/types.d.ts b/packages/metro-cache/types/types.d.ts
index f54c7a912c..e747c9fd49 100644
--- a/packages/metro-cache/types/types.d.ts
+++ b/packages/metro-cache/types/types.d.ts
@@ -9,7 +9,8 @@
*/
export interface CacheStore {
- get(key: Buffer): T | undefined | Promise | Promise;
+ name?: string;
+ get(key: Buffer): (null | undefined | T) | Promise;
set(key: Buffer, value: T): void | Promise;
clear(): void | Promise;
}
diff --git a/packages/metro-config/src/types.js b/packages/metro-config/src/types.js
index 8465242cfa..134c39edf3 100644
--- a/packages/metro-config/src/types.js
+++ b/packages/metro-config/src/types.js
@@ -10,8 +10,7 @@
*/
import type {HandleFunction, Server} from 'connect';
-import type {CacheStore} from 'metro-cache';
-import typeof * as MetroCache from 'metro-cache';
+import type {CacheStore, MetroCache} from 'metro-cache';
import type {CacheManagerFactory} from 'metro-file-map';
import type {CustomResolver} from 'metro-resolver';
import type {JsTransformerConfig} from 'metro-transform-worker';
diff --git a/packages/metro-config/types/defaults/createModuleIdFactory.d.ts b/packages/metro-config/types/defaults/createModuleIdFactory.d.ts
new file mode 100644
index 0000000000..e9570aa491
--- /dev/null
+++ b/packages/metro-config/types/defaults/createModuleIdFactory.d.ts
@@ -0,0 +1,12 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+declare function createModuleIdFactory(): (path: string) => number;
+export default createModuleIdFactory;
diff --git a/packages/metro-config/types/defaults/defaults.d.ts b/packages/metro-config/types/defaults/defaults.d.ts
new file mode 100644
index 0000000000..d1f90bafb2
--- /dev/null
+++ b/packages/metro-config/types/defaults/defaults.d.ts
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {RootPerfLogger} from '../types';
+
+export {default as defaultCreateModuleIdFactory} from './createModuleIdFactory';
+export declare const assetExts: Array;
+export declare type assetExts = typeof assetExts;
+export declare const assetResolutions: Array;
+export declare type assetResolutions = typeof assetResolutions;
+export declare const sourceExts: Array;
+export declare type sourceExts = typeof sourceExts;
+export declare const additionalExts: Array;
+export declare type additionalExts = typeof additionalExts;
+export declare const moduleSystem: string;
+export declare type moduleSystem = typeof moduleSystem;
+export declare const platforms: Array;
+export declare type platforms = typeof platforms;
+export declare const DEFAULT_METRO_MINIFIER_PATH: 'metro-minify-terser';
+export declare type DEFAULT_METRO_MINIFIER_PATH =
+ typeof DEFAULT_METRO_MINIFIER_PATH;
+export declare const noopPerfLoggerFactory: () => RootPerfLogger;
+export declare type noopPerfLoggerFactory = typeof noopPerfLoggerFactory;
diff --git a/packages/metro-config/types/defaults/exclusionList.d.ts b/packages/metro-config/types/defaults/exclusionList.d.ts
new file mode 100644
index 0000000000..8c9a30b664
--- /dev/null
+++ b/packages/metro-config/types/defaults/exclusionList.d.ts
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+declare function exclusionList(
+ additionalExclusions?: ReadonlyArray,
+): RegExp;
+export default exclusionList;
diff --git a/packages/metro-config/types/defaults/getMaxWorkers.d.ts b/packages/metro-config/types/defaults/getMaxWorkers.d.ts
new file mode 100644
index 0000000000..af248faded
--- /dev/null
+++ b/packages/metro-config/types/defaults/getMaxWorkers.d.ts
@@ -0,0 +1,12 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+declare function getMaxWorkers(workers: null | undefined | number): number;
+export default getMaxWorkers;
diff --git a/packages/metro-config/types/defaults/index.d.ts b/packages/metro-config/types/defaults/index.d.ts
index 197af1891e..972c1041da 100644
--- a/packages/metro-config/types/defaults/index.d.ts
+++ b/packages/metro-config/types/defaults/index.d.ts
@@ -10,10 +10,10 @@
import type {ConfigT} from '../types';
-interface getDefaultConfig {
- (rootPath: string | null): Promise;
- getDefaultValues: (rootPath: string | null) => ConfigT;
-}
-
-declare const getDefaultConfig: getDefaultConfig;
-export default getDefaultConfig;
+declare const $$EXPORT_DEFAULT_DECLARATION$$: {
+ (rootPath?: string): Promise;
+ getDefaultValues: (rootPath?: string) => ConfigT;
+};
+declare type $$EXPORT_DEFAULT_DECLARATION$$ =
+ typeof $$EXPORT_DEFAULT_DECLARATION$$;
+export default $$EXPORT_DEFAULT_DECLARATION$$;
diff --git a/packages/metro-config/types/defaults/validConfig.d.ts b/packages/metro-config/types/defaults/validConfig.d.ts
new file mode 100644
index 0000000000..e08113e571
--- /dev/null
+++ b/packages/metro-config/types/defaults/validConfig.d.ts
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {InputConfigT} from '../types';
+
+declare function validConfig(): Promise;
+export default validConfig;
diff --git a/packages/metro-config/types/index.d.ts b/packages/metro-config/types/index.d.ts
index 3641391c6b..b20564ec10 100644
--- a/packages/metro-config/types/index.d.ts
+++ b/packages/metro-config/types/index.d.ts
@@ -13,7 +13,6 @@ import getDefaultConfig from './defaults';
import {loadConfig, mergeConfig, resolveConfig} from './loadConfig';
export {getDefaultConfig, loadConfig, mergeConfig, resolveConfig};
-
/**
* Backwards-compatibility with CommonJS consumers using interopRequireDefault.
* Do not add to this list.
diff --git a/packages/metro-config/types/loadConfig.d.ts b/packages/metro-config/types/loadConfig.d.ts
index ce30f2815d..d0415fd459 100644
--- a/packages/metro-config/types/loadConfig.d.ts
+++ b/packages/metro-config/types/loadConfig.d.ts
@@ -10,26 +10,33 @@
import type {ConfigT, InputConfigT, YargArguments} from './types';
-export interface CosmiConfigResult {
+type ResolveConfigResult = {
filepath: string;
isEmpty: boolean;
config:
- | ((partialConfig: ConfigT) => Promise)
- | ((partialConfig: ConfigT) => ConfigT)
+ | (($$PARAM_0$$: ConfigT) => Promise)
+ | (($$PARAM_0$$: ConfigT) => ConfigT)
| InputConfigT;
-}
-
-export function loadConfig(
- argv?: YargArguments,
- defaultConfigOverrides?: InputConfigT,
-): Promise;
-
-export function resolveConfig(
+};
+declare function resolveConfig(
filePath?: string,
cwd?: string,
-): Promise;
-
-export function mergeConfig(
- defaultConfig: InputConfigT,
- ...configs: InputConfigT[]
-): ConfigT;
+): Promise;
+declare function mergeConfig>(
+ defaultConfig: T,
+ ...configs: Array
+): T;
+/**
+ * Load the metro configuration from disk
+ * @param {object} argv Arguments coming from the CLI, can be empty
+ * @param {object} defaultConfigOverrides A configuration that can override the default config
+ * @return {object} Configuration returned
+ */
+declare function loadConfig(
+ argvInput?: YargArguments,
+ defaultConfigOverrides?: InputConfigT,
+): Promise;
+export declare function loadConfigFile(
+ absolutePath: string,
+): Promise;
+export {loadConfig, resolveConfig, mergeConfig};
diff --git a/packages/metro-config/types/types.d.ts b/packages/metro-config/types/types.d.ts
index a5c84143c9..072b2537ce 100644
--- a/packages/metro-config/types/types.d.ts
+++ b/packages/metro-config/types/types.d.ts
@@ -10,6 +10,7 @@
import type {HandleFunction, Server} from 'connect';
import type {CacheStore, MetroCache} from 'metro-cache';
+import type {CacheManagerFactory} from 'metro-file-map';
import type {CustomResolver} from 'metro-resolver';
import type {JsTransformerConfig} from 'metro-transform-worker';
import type {
@@ -21,59 +22,52 @@ import type {
} from 'metro/private/DeltaBundler/types';
import type {Reporter} from 'metro/private/lib/reporting';
import type MetroServer from 'metro/private/Server';
+import type {IntermediateStackFrame} from 'metro/private/Server/symbolicate';
-export interface ExtraTransformOptions {
- readonly preloadedModules: Readonly<{[path: string]: true}> | false;
- readonly ramGroups: ReadonlyArray;
- readonly transform: Readonly<{
- experimentalImportSupport: boolean;
- inlineRequires:
- | Readonly<{blockList: Readonly<{[path: string]: true}>}>
+export type ExtraTransformOptions = Readonly<{
+ preloadedModules?: Readonly<{[path: string]: true}> | false;
+ ramGroups?: ReadonlyArray;
+ transform?: Readonly<{
+ experimentalImportSupport?: boolean;
+ inlineRequires?:
+ | Readonly<{blockList: Readonly<{[$$Key$$: string]: true}>}>
| boolean;
nonInlinedRequires?: ReadonlyArray;
unstable_disableES6Transforms?: boolean;
unstable_memoizeInlineRequires?: boolean;
+ unstable_nonMemoizedInlineRequires?: ReadonlyArray;
}>;
-}
-
-export interface GetTransformOptionsOpts {
+}>;
+export type GetTransformOptionsOpts = {
dev: boolean;
- hot: boolean;
- platform?: string;
-}
-
+ /**
+ * @deprecated Always true
+ */
+ hot: true;
+ platform: null | undefined | string;
+};
export type GetTransformOptions = (
entryPoints: ReadonlyArray,
options: GetTransformOptionsOpts,
- getDependenciesOf: (filePath: string) => Promise,
+ getDependenciesOf: ($$PARAM_0$$: string) => Promise>,
) => Promise>;
-
export type Middleware = HandleFunction;
-
-export type PerfAnnotations = Partial<{
- string: {[key: string]: string};
- int: {[key: string]: number};
- double: {[key: string]: number};
- bool: {[key: string]: boolean};
- string_array: {[key: string]: string[]};
- int_array: {[key: string]: number[]};
- double_array: {[key: string]: number[]};
- bool_array: {[key: string]: boolean[]};
-}>;
-
-export type PerfLoggerPointOptions = Readonly<{
- /**
- * The time this event point occurred, if it differs from the time the point was logged.
- */
- timestamp?: number;
+type PerfAnnotations = Partial<{
+ string: Readonly<{[key: string]: string}>;
+ int: Readonly<{[key: string]: number}>;
+ double: Readonly<{[key: string]: number}>;
+ bool: Readonly<{[key: string]: boolean}>;
+ string_array: Readonly<{[key: string]: ReadonlyArray}>;
+ int_array: Readonly<{[key: string]: ReadonlyArray}>;
+ double_array: Readonly<{[key: string]: ReadonlyArray}>;
+ bool_array: Readonly<{[key: string]: ReadonlyArray}>;
}>;
-
+type PerfLoggerPointOptions = Readonly<{timestamp?: number}>;
export interface PerfLogger {
point(name: string, opts?: PerfLoggerPointOptions): void;
annotate(annotations: PerfAnnotations): void;
subSpan(label: string): PerfLogger;
}
-
export interface RootPerfLogger extends PerfLogger {
start(opts?: PerfLoggerPointOptions): void;
end(
@@ -81,30 +75,25 @@ export interface RootPerfLogger extends PerfLogger {
opts?: PerfLoggerPointOptions,
): void;
}
-
-export type PerfLoggerFactoryOptions = Readonly<{
- key?: number;
-}>;
-
+export type PerfLoggerFactoryOptions = Readonly<{key?: number}>;
export type PerfLoggerFactory = (
type: 'START_UP' | 'BUNDLING_REQUEST' | 'HMR',
opts?: PerfLoggerFactoryOptions,
) => RootPerfLogger;
-
-export interface ResolverConfigT {
+type ResolverConfigT = {
assetExts: ReadonlyArray;
assetResolutions: ReadonlyArray;
- blacklistRE?: RegExp | RegExp[];
- blockList: RegExp | RegExp[];
- dependencyExtractor?: string;
+ blacklistRE?: RegExp | Array;
+ blockList: RegExp | Array;
disableHierarchicalLookup: boolean;
- extraNodeModules: {[name: string]: string};
+ dependencyExtractor: null | undefined | string;
emptyModulePath: string;
enableGlobalPackages: boolean;
- hasteImplModulePath?: string;
+ extraNodeModules: {[name: string]: string};
+ hasteImplModulePath: null | undefined | string;
nodeModulesPaths: ReadonlyArray;
platforms: ReadonlyArray;
- resolveRequest?: CustomResolver;
+ resolveRequest: null | undefined | CustomResolver;
resolverMainFields: ReadonlyArray;
sourceExts: ReadonlyArray;
unstable_conditionNames: ReadonlyArray;
@@ -114,144 +103,161 @@ export interface ResolverConfigT {
unstable_enablePackageExports: boolean;
useWatchman: boolean;
requireCycleIgnorePatterns: ReadonlyArray;
-}
-
-export interface SerializerConfigT {
+};
+type SerializerConfigT = {
createModuleIdFactory: () => (path: string) => number;
customSerializer:
+ | null
+ | undefined
| ((
entryPoint: string,
preModules: ReadonlyArray,
graph: ReadOnlyGraph,
options: SerializerOptions,
- ) => Promise)
- | null;
+ ) => Promise);
experimentalSerializerHook: (
graph: ReadOnlyGraph,
delta: DeltaResult,
) => unknown;
- getModulesRunBeforeMainModule: (entryFilePath: string) => string[];
- getPolyfills: (options: {platform: string | null}) => ReadonlyArray;
+ getModulesRunBeforeMainModule: (entryFilePath: string) => Array;
+ getPolyfills: ($$PARAM_0$$: {
+ platform: null | undefined | string;
+ }) => ReadonlyArray;
getRunModuleStatement: (
moduleId: number | string,
globalPrefix: string,
) => string;
polyfillModuleNames: ReadonlyArray;
processModuleFilter: (modules: Module) => boolean;
- isThirdPartyModule: (module: {readonly path: string}) => boolean;
-}
-
-export interface TransformerConfigT extends JsTransformerConfig {
+ isThirdPartyModule: (module: Readonly<{path: string}>) => boolean;
+};
+type TransformerConfigT = Omit<
+ JsTransformerConfig,
+ keyof {
+ getTransformOptions: GetTransformOptions;
+ transformVariants: {
+ readonly [name: string]: Partial;
+ };
+ publicPath: string;
+ unstable_workerThreads: boolean;
+ }
+> & {
getTransformOptions: GetTransformOptions;
- transformVariants: Readonly<{[name: string]: Partial}>;
+ transformVariants: {
+ readonly [name: string]: Partial;
+ };
publicPath: string;
-}
-
-export interface MetalConfigT {
+ unstable_workerThreads: boolean;
+};
+type MetalConfigT = {
cacheVersion: string;
fileMapCacheDirectory?: string;
- /** Deprecated, alias of fileMapCacheDirectory */
hasteMapCacheDirectory?: string;
+ unstable_fileMapCacheManagerFactory?: CacheManagerFactory;
maxWorkers: number;
- unstable_perfLoggerFactory?: PerfLoggerFactory | null;
+ unstable_perfLoggerFactory?: null | undefined | PerfLoggerFactory;
projectRoot: string;
stickyWorkers: boolean;
transformerPath: string;
reporter: Reporter;
resetCache: boolean;
watchFolders: ReadonlyArray;
-}
-
-export interface ServerConfigT {
+};
+type CacheStoresConfigT = ReadonlyArray>;
+type ServerConfigT = {
/** @deprecated */
enhanceMiddleware: (
- metroMiddleware: Middleware,
- metroServer: MetroServer,
+ $$PARAM_0$$: Middleware,
+ $$PARAM_1$$: MetroServer,
) => Middleware | Server;
forwardClientLogs: boolean;
port: number;
- rewriteRequestUrl: (url: string) => string;
- unstable_serverRoot: string | null;
+ rewriteRequestUrl: ($$PARAM_0$$: string) => string;
+ unstable_serverRoot: null | undefined | string;
useGlobalHotkey: boolean;
verifyConnections: boolean;
-}
-
-export interface SymbolicatorConfigT {
- customizeFrame: (frame: {
- readonly file?: string;
- readonly lineNumber?: number;
- readonly column?: number;
- readonly methodName?: string;
+};
+type SymbolicatorConfigT = {
+ customizeFrame: ($$PARAM_0$$: {
+ readonly file: null | undefined | string;
+ readonly lineNumber: null | undefined | number;
+ readonly column: null | undefined | number;
+ readonly methodName: null | undefined | string;
}) =>
- | {readonly collapse?: boolean}
- | undefined
- | Promise<{readonly collapse?: boolean}>
- | Promise;
-}
-
-export interface WatcherConfigT {
+ | (null | undefined | {readonly collapse?: boolean})
+ | Promise;
+ customizeStack: (
+ $$PARAM_0$$: Array,
+ $$PARAM_1$$: unknown,
+ ) => Array | Promise>;
+};
+type WatcherConfigT = {
additionalExts: ReadonlyArray;
- watchman: {
- deferStates: ReadonlyArray;
- };
- healthCheck: {
+ healthCheck: Readonly<{
enabled: boolean;
interval: number;
timeout: number;
filePrefix: string;
- };
- unstable_autoSaveCache: {
- enabled: boolean;
- debounceMs?: number;
- };
-}
-
-export interface WatcherInputConfigT
- extends Partial<
- Omit
- > {
- healthCheck?: Partial;
- unstable_autoSaveCache?: Partial;
-}
-
-export interface InputConfigT extends Partial {
- readonly cacheStores?:
- | ReadonlyArray>
- | ((metroCache: MetroCache) => ReadonlyArray>);
- readonly resolver?: Partial;
- readonly server?: Partial;
- readonly serializer?: Partial;
- readonly symbolicator?: Partial;
- readonly transformer?: Partial;
- readonly watcher?: Partial;
-}
-
+ }>;
+ unstable_autoSaveCache: Readonly<{enabled: boolean; debounceMs?: number}>;
+ unstable_lazySha1: boolean;
+ unstable_workerThreads: boolean;
+ watchman: Readonly<{deferStates: ReadonlyArray}>;
+};
+export type InputConfigT = Partial<
+ Readonly<
+ MetalConfigT & {
+ cacheStores:
+ | CacheStoresConfigT
+ | (($$PARAM_0$$: MetroCache) => CacheStoresConfigT);
+ resolver: Readonly>;
+ server: Readonly>;
+ serializer: Readonly>;
+ symbolicator: Readonly>;
+ transformer: Readonly>;
+ watcher: Partial<
+ Readonly<
+ Omit<
+ WatcherConfigT,
+ 'healthCheck' | 'unstable_autoSaveCache' | 'watchman'
+ > & {
+ healthCheck: Partial>;
+ unstable_autoSaveCache: Partial<
+ Readonly
+ >;
+ watchman: Partial>;
+ }
+ >
+ >;
+ }
+ >
+>;
export type MetroConfig = InputConfigT;
-
-export interface ConfigT extends Readonly {
- readonly cacheStores: ReadonlyArray>;
- readonly resolver: Readonly;
- readonly server: Readonly;
- readonly serializer: Readonly;
- readonly symbolicator: Readonly;
- readonly transformer: Readonly;
- readonly watcher: Readonly;
-}
-
-export interface YargArguments {
+export type ConfigT = Readonly<
+ MetalConfigT & {
+ cacheStores: CacheStoresConfigT;
+ resolver: Readonly;
+ server: Readonly;
+ serializer: Readonly;
+ symbolicator: Readonly;
+ transformer: Readonly;
+ watcher: Readonly;
+ }
+>;
+export type YargArguments = Readonly<{
config?: string;
cwd?: string;
port?: string | number;
host?: string;
projectRoot?: string;
- watchFolders?: string[];
- assetExts?: string[];
- sourceExts?: string[];
- platforms?: string[];
+ watchFolders?: Array;
+ assetExts?: Array;
+ sourceExts?: Array;
+ platforms?: Array;
'max-workers'?: string | number;
maxWorkers?: string | number;
transformer?: string;
'reset-cache'?: boolean;
resetCache?: boolean;
verbose?: boolean;
-}
+}>;
diff --git a/packages/metro-core/src/Terminal.js b/packages/metro-core/src/Terminal.js
index f1b1b74049..3ccf5ba26d 100644
--- a/packages/metro-core/src/Terminal.js
+++ b/packages/metro-core/src/Terminal.js
@@ -89,32 +89,32 @@ function getTTYStream(stream: UnderlyingStream): ?tty.WriteStream {
* single responsibility of handling status messages.
*/
export default class Terminal {
- _logLines: Array;
- _nextStatusStr: string;
- _statusStr: string;
- _stream: UnderlyingStream;
- _ttyStream: ?tty.WriteStream;
- _updatePromise: Promise | null;
- _isUpdating: boolean;
- _isPendingUpdate: boolean;
- _shouldFlush: boolean;
- _writeStatusThrottled: string => void;
+ #logLines: Array;
+ #nextStatusStr: string;
+ #statusStr: string;
+ #stream: UnderlyingStream;
+ #ttyStream: ?tty.WriteStream;
+ #updatePromise: Promise | null;
+ #isUpdating: boolean;
+ #isPendingUpdate: boolean;
+ #shouldFlush: boolean;
+ #writeStatusThrottled: string => void;
constructor(
stream: UnderlyingStream,
{ttyPrint = true}: {ttyPrint?: boolean} = {},
) {
- this._logLines = [];
- this._nextStatusStr = '';
- this._statusStr = '';
- this._stream = stream;
- this._ttyStream = ttyPrint ? getTTYStream(stream) : null;
- this._updatePromise = null;
- this._isUpdating = false;
- this._isPendingUpdate = false;
- this._shouldFlush = false;
- this._writeStatusThrottled = throttle(
- status => this._stream.write(status),
+ this.#logLines = [];
+ this.#nextStatusStr = '';
+ this.#statusStr = '';
+ this.#stream = stream;
+ this.#ttyStream = ttyPrint ? getTTYStream(stream) : null;
+ this.#updatePromise = null;
+ this.#isUpdating = false;
+ this.#isPendingUpdate = false;
+ this.#shouldFlush = false;
+ this.#writeStatusThrottled = throttle(
+ status => this.#stream.write(status),
3500,
);
}
@@ -125,28 +125,28 @@ export default class Terminal {
* If there are two updates scheduled, do nothing, as the second update will
* take care of the latest status and log lines.
*/
- _scheduleUpdate() {
- if (this._isUpdating) {
- this._isPendingUpdate = true;
+ #scheduleUpdate() {
+ if (this.#isUpdating) {
+ this.#isPendingUpdate = true;
return;
}
- this._isUpdating = true;
- this._updatePromise = this._update().then(async () => {
- while (this._isPendingUpdate) {
- if (!this._shouldFlush) {
+ this.#isUpdating = true;
+ this.#updatePromise = this.#update().then(async () => {
+ while (this.#isPendingUpdate) {
+ if (!this.#shouldFlush) {
await new Promise(resolve => setTimeout(resolve, 33));
}
- this._isPendingUpdate = false;
- await this._update();
+ this.#isPendingUpdate = false;
+ await this.#update();
}
- this._isUpdating = false;
- this._shouldFlush = false;
+ this.#isUpdating = false;
+ this.#shouldFlush = false;
});
}
async waitForUpdates(): Promise {
- await (this._updatePromise || Promise.resolve());
+ await (this.#updatePromise || Promise.resolve());
}
/**
@@ -155,12 +155,12 @@ export default class Terminal {
* update starts writing to stream after a delay.
*/
async flush(): Promise {
- if (this._isUpdating) {
- this._shouldFlush = true;
+ if (this.#isUpdating) {
+ this.#shouldFlush = true;
}
await this.waitForUpdates();
// $FlowFixMe[prop-missing]
- this._writeStatusThrottled.flush();
+ this.#writeStatusThrottled.flush();
}
/**
@@ -169,16 +169,16 @@ export default class Terminal {
* `status()`) prevents us from repeatedly rewriting the status in case
* `terminal.log()` is called several times.
*/
- async _update(): Promise {
- const ttyStream = this._ttyStream;
+ async #update(): Promise {
+ const ttyStream = this.#ttyStream;
- const nextStatusStr = this._nextStatusStr;
- const statusStr = this._statusStr;
- const logLines = this._logLines;
+ const nextStatusStr = this.#nextStatusStr;
+ const statusStr = this.#statusStr;
+ const logLines = this.#logLines;
// reset these here to not have them changed while updating
- this._statusStr = nextStatusStr;
- this._logLines = [];
+ this.#statusStr = nextStatusStr;
+ this.#logLines = [];
if (statusStr === nextStatusStr && logLines.length === 0) {
return;
@@ -192,15 +192,15 @@ export default class Terminal {
}
if (logLines.length > 0) {
- await streamWrite(this._stream, logLines.join('\n') + '\n');
+ await streamWrite(this.#stream, logLines.join('\n') + '\n');
}
if (ttyStream) {
if (nextStatusStr.length > 0) {
- await streamWrite(this._stream, nextStatusStr + '\n');
+ await streamWrite(this.#stream, nextStatusStr + '\n');
}
} else {
- this._writeStatusThrottled(
+ this.#writeStatusThrottled(
nextStatusStr.length > 0 ? nextStatusStr + '\n' : '',
);
}
@@ -214,16 +214,16 @@ export default class Terminal {
* file, then we don't care too much about having a progress bar.
*/
status(format: string, ...args: Array): string {
- const {_nextStatusStr} = this;
+ const nextStatusStr = this.#nextStatusStr;
const statusStr = util.format(format, ...args);
- this._nextStatusStr = this._ttyStream
- ? chunkString(statusStr, this._ttyStream.columns).join('\n')
+ this.#nextStatusStr = this.#ttyStream
+ ? chunkString(statusStr, this.#ttyStream.columns).join('\n')
: statusStr;
- this._scheduleUpdate();
+ this.#scheduleUpdate();
- return _nextStatusStr;
+ return nextStatusStr;
}
/**
@@ -232,8 +232,8 @@ export default class Terminal {
* `console.log`.
*/
log(format: string, ...args: Array): void {
- this._logLines.push(util.format(format, ...args));
- this._scheduleUpdate();
+ this.#logLines.push(util.format(format, ...args));
+ this.#scheduleUpdate();
}
/**
@@ -241,7 +241,7 @@ export default class Terminal {
* status was the last one of a series of updates.
*/
persistStatus(): void {
- this.log(this._nextStatusStr);
- this._nextStatusStr = '';
+ this.log(this.#nextStatusStr);
+ this.#nextStatusStr = '';
}
}
diff --git a/packages/metro-core/types/Logger.d.ts b/packages/metro-core/types/Logger.d.ts
new file mode 100644
index 0000000000..6493ac4f88
--- /dev/null
+++ b/packages/metro-core/types/Logger.d.ts
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {BundleOptions} from 'metro/private/shared/types';
+
+export type ActionLogEntryData = {
+ action_name: string;
+ log_entry_label?: string;
+};
+export type ActionStartLogEntry = {
+ action_name?: string;
+ action_phase?: string;
+ log_entry_label: string;
+ log_session?: string;
+ start_timestamp?: [number, number];
+};
+export type LogEntry = {
+ action_name?: string;
+ action_phase?: string;
+ action_result?: string;
+ duration_ms?: number;
+ entry_point?: string;
+ file_name?: string;
+ log_entry_label: string;
+ log_session?: string;
+ start_timestamp?: [number, number];
+ outdated_modules?: number;
+ bundle_size?: number;
+ bundle_options?: BundleOptions;
+ bundle_hash?: string;
+ build_id?: string;
+ error_message?: string;
+ error_stack?: string;
+};
+declare function on(event: string, handler: (logEntry: LogEntry) => void): void;
+declare function createEntry(data: LogEntry | string): LogEntry;
+declare function createActionStartEntry(
+ data: ActionLogEntryData | string,
+): LogEntry;
+declare function createActionEndEntry(
+ logEntry: ActionStartLogEntry,
+ error?: null | undefined | Error,
+): LogEntry;
+declare function log(logEntry: LogEntry): LogEntry;
+export {on, createEntry, createActionStartEntry, createActionEndEntry, log};
diff --git a/packages/metro-core/types/Terminal.d.ts b/packages/metro-core/types/Terminal.d.ts
index ad33e25b46..9c28a446b9 100644
--- a/packages/metro-core/types/Terminal.d.ts
+++ b/packages/metro-core/types/Terminal.d.ts
@@ -8,13 +8,44 @@
* @oncall react_native
*/
-import * as net from 'net';
-import * as stream from 'stream';
-
-export type UnderlyingStream = net.Socket | stream.Writable;
-
-export class Terminal {
- constructor(stream: UnderlyingStream);
+type UnderlyingStream = net$Socket | stream$Writable;
+/**
+ * We don't just print things to the console, sometimes we also want to show
+ * and update progress. This utility just ensures the output stays neat: no
+ * missing newlines, no mangled log lines.
+ *
+ * const terminal = Terminal.default;
+ * terminal.status('Updating... 38%');
+ * terminal.log('warning: Something happened.');
+ * terminal.status('Updating, done.');
+ * terminal.persistStatus();
+ *
+ * The final output:
+ *
+ * warning: Something happened.
+ * Updating, done.
+ *
+ * Without the status feature, we may get a mangled output:
+ *
+ * Updating... 38%warning: Something happened.
+ * Updating, done.
+ *
+ * This is meant to be user-readable and TTY-oriented. We use stdout by default
+ * because it's more about status information than diagnostics/errors (stderr).
+ *
+ * Do not add any higher-level functionality in this class such as "warning" and
+ * "error" printers, as it is not meant for formatting/reporting. It has the
+ * single responsibility of handling status messages.
+ */
+declare class Terminal {
+ constructor(stream: UnderlyingStream, $$PARAM_1$$?: {ttyPrint?: boolean});
+ waitForUpdates(): Promise;
+ /**
+ * Useful for calling console/stdout directly after terminal logs
+ * Otherwise, you could end up with mangled output when the queued
+ * update starts writing to stream after a delay.
+ */
+ flush(): Promise;
/**
* Shows some text that is meant to be overriden later. Return the previous
* status that was shown and is no more. Calling `status()` with no argument
@@ -22,17 +53,17 @@ export class Terminal {
* non-interactive terminal: for example, if the output is redirected to a
* file, then we don't care too much about having a progress bar.
*/
- status(format: string, ...args: unknown[]): string;
+ status(format: string, ...args: Array): string;
/**
* Similar to `console.log`, except it moves the status/progress text out of
* the way correctly. In non-interactive terminals this is the same as
* `console.log`.
*/
- log(format: string, ...args: unknown[]): void;
+ log(format: string, ...args: Array): void;
/**
* Log the current status and start from scratch. This is useful if the last
* status was the last one of a series of updates.
*/
persistStatus(): void;
- flush(): void;
}
+export default Terminal;
diff --git a/packages/metro-core/types/canonicalize.d.ts b/packages/metro-core/types/canonicalize.d.ts
new file mode 100644
index 0000000000..6edfe16ad2
--- /dev/null
+++ b/packages/metro-core/types/canonicalize.d.ts
@@ -0,0 +1,12 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+declare function canonicalize(key: string, value: unknown): unknown;
+export default canonicalize;
diff --git a/packages/metro-core/types/errors.d.ts b/packages/metro-core/types/errors.d.ts
new file mode 100644
index 0000000000..55b7f5f7df
--- /dev/null
+++ b/packages/metro-core/types/errors.d.ts
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import AmbiguousModuleResolutionError from './errors/AmbiguousModuleResolutionError';
+import PackageResolutionError from './errors/PackageResolutionError';
+
+export {AmbiguousModuleResolutionError, PackageResolutionError};
diff --git a/packages/metro-core/types/errors/AmbiguousModuleResolutionError.d.ts b/packages/metro-core/types/errors/AmbiguousModuleResolutionError.d.ts
new file mode 100644
index 0000000000..1966412a61
--- /dev/null
+++ b/packages/metro-core/types/errors/AmbiguousModuleResolutionError.d.ts
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {DuplicateHasteCandidatesError} from 'metro-file-map';
+
+declare class AmbiguousModuleResolutionError extends Error {
+ fromModulePath: string;
+ hasteError: DuplicateHasteCandidatesError;
+ constructor(
+ fromModulePath: string,
+ hasteError: DuplicateHasteCandidatesError,
+ );
+}
+export default AmbiguousModuleResolutionError;
diff --git a/packages/metro-core/types/errors/PackageResolutionError.d.ts b/packages/metro-core/types/errors/PackageResolutionError.d.ts
new file mode 100644
index 0000000000..84d59f7417
--- /dev/null
+++ b/packages/metro-core/types/errors/PackageResolutionError.d.ts
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {InvalidPackageError} from 'metro-resolver';
+
+declare class PackageResolutionError extends Error {
+ originModulePath: string;
+ packageError: InvalidPackageError;
+ targetModuleName: string;
+ constructor(opts: {
+ readonly originModulePath: string;
+ readonly packageError: InvalidPackageError;
+ readonly targetModuleName: string;
+ });
+}
+export default PackageResolutionError;
diff --git a/packages/metro-core/types/index.d.ts b/packages/metro-core/types/index.d.ts
index 70b94ce115..73f3d39071 100644
--- a/packages/metro-core/types/index.d.ts
+++ b/packages/metro-core/types/index.d.ts
@@ -8,10 +8,17 @@
* @oncall react_native
*/
-import {Terminal} from './Terminal';
-
-export {Terminal};
+import AmbiguousModuleResolutionError from './errors/AmbiguousModuleResolutionError';
+import PackageResolutionError from './errors/PackageResolutionError';
+import * as Logger from './Logger';
+import Terminal from './Terminal';
+export {
+ AmbiguousModuleResolutionError,
+ Logger,
+ PackageResolutionError,
+ Terminal,
+};
/**
* Backwards-compatibility with CommonJS consumers using interopRequireDefault.
* Do not add to this list.
@@ -19,6 +26,9 @@ export {Terminal};
* @deprecated Default import from 'metro-core' is deprecated, use named exports.
*/
declare const $$EXPORT_DEFAULT_DECLARATION$$: {
+ AmbiguousModuleResolutionError: typeof AmbiguousModuleResolutionError;
+ Logger: typeof Logger;
+ PackageResolutionError: typeof PackageResolutionError;
Terminal: typeof Terminal;
};
declare type $$EXPORT_DEFAULT_DECLARATION$$ =
diff --git a/packages/metro-resolver/types/PackageExportsResolve.d.ts b/packages/metro-resolver/types/PackageExportsResolve.d.ts
new file mode 100644
index 0000000000..033ec0be26
--- /dev/null
+++ b/packages/metro-resolver/types/PackageExportsResolve.d.ts
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {ExportsField, FileResolution, ResolutionContext} from './types';
+/**
+ * Resolve a package subpath based on the entry points defined in the package's
+ * "exports" field. If there is no match for the given subpath (which may be
+ * augmented by resolution of conditional exports for the passed `context`),
+ * throws a `PackagePathNotExportedError`.
+ *
+ * Implements modern package resolution behaviour based on the [Package Entry
+ * Points spec](https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points).
+ *
+ * @throws {InvalidPackageConfigurationError} Raised if configuration specified
+ * by `exportsField` is invalid.
+ * @throws {InvalidModuleSpecifierError} Raised if the resolved module specifier
+ * is invalid.
+ * @throws {PackagePathNotExportedError} Raised when the requested subpath is
+ * not exported.
+ */
+export declare function resolvePackageTargetFromExports(
+ context: ResolutionContext,
+ packagePath: string,
+ modulePath: string,
+ packageRelativePath: string,
+ exportsField: ExportsField,
+ platform: string | null,
+): FileResolution;
diff --git a/packages/metro-resolver/types/PackageImportsResolve.d.ts b/packages/metro-resolver/types/PackageImportsResolve.d.ts
new file mode 100644
index 0000000000..124f531778
--- /dev/null
+++ b/packages/metro-resolver/types/PackageImportsResolve.d.ts
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {ExportsLikeMap, FileResolution, ResolutionContext} from './types';
+/**
+ * Resolve a package subpath based on the entry points defined in the package's
+ * "imports" field. If there is no match for the given subpath (which may be
+ * augmented by resolution of conditional exports for the passed `context`),
+ * throws a `PackagePathNotExportedError`.
+ *
+ * Implementation of PACKAGE_IMPORTS_RESOLVE described in https://nodejs.org/api/esm.html
+ *
+ * @throws {InvalidPackageConfigurationError} Raised if configuration specified
+ * by `importsMap` is invalid.
+ */
+export declare function resolvePackageTargetFromImports(
+ context: ResolutionContext,
+ packagePath: string,
+ importPath: string,
+ importsMap: ExportsLikeMap,
+ platform: string | null,
+): FileResolution;
diff --git a/packages/metro-resolver/types/PackageResolve.d.ts b/packages/metro-resolver/types/PackageResolve.d.ts
new file mode 100644
index 0000000000..c4e674ccd4
--- /dev/null
+++ b/packages/metro-resolver/types/PackageResolve.d.ts
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {PackageInfo, ResolutionContext} from './types';
+/**
+ * Resolve the main entry point subpath for a package.
+ *
+ * Implements legacy (non-exports) package resolution behaviour based on the
+ * ["browser" field spec](https://github.com/defunctzombie/package-browser-field-spec).
+ */
+export declare function getPackageEntryPoint(
+ context: ResolutionContext,
+ packageInfo: PackageInfo,
+ platform: string | null,
+): string;
+/**
+ * Get the resolved file path for the given import specifier based on any
+ * `package.json` rules. Returns `false` if the module should be
+ * [ignored](https://github.com/defunctzombie/package-browser-field-spec#ignore-a-module),
+ * and returns the original path if no `package.json` mapping is matched. Does
+ * not test file existence.
+ *
+ * Implements legacy (non-exports) package resolution behaviour based on the
+ * ["browser" field spec](https://github.com/defunctzombie/package-browser-field-spec).
+ */
+export declare function redirectModulePath(
+ context: Readonly<{
+ getPackageForModule: ResolutionContext['getPackageForModule'];
+ mainFields: ResolutionContext['mainFields'];
+ originModulePath: ResolutionContext['originModulePath'];
+ }>,
+ modulePath: string,
+): string | false;
diff --git a/packages/metro-resolver/types/createDefaultContext.d.ts b/packages/metro-resolver/types/createDefaultContext.d.ts
new file mode 100644
index 0000000000..86096cec2b
--- /dev/null
+++ b/packages/metro-resolver/types/createDefaultContext.d.ts
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {ResolutionContext} from './types';
+import type {TransformResultDependency} from 'metro/private/DeltaBundler/types';
+
+type PartialContext = Readonly<
+ Omit<
+ ResolutionContext,
+ keyof {redirectModulePath?: ResolutionContext['redirectModulePath']}
+ > & {redirectModulePath?: ResolutionContext['redirectModulePath']}
+>;
+/**
+ * Helper used by the `metro` package to create the `ResolutionContext` object.
+ * As context values can be overridden by callers, this occurs externally to
+ * `resolve.js`.
+ */
+declare function createDefaultContext(
+ context: PartialContext,
+ dependency: TransformResultDependency,
+): ResolutionContext;
+export default createDefaultContext;
diff --git a/packages/metro-resolver/types/errors/FailedToResolveNameError.d.ts b/packages/metro-resolver/types/errors/FailedToResolveNameError.d.ts
new file mode 100644
index 0000000000..e114242cbc
--- /dev/null
+++ b/packages/metro-resolver/types/errors/FailedToResolveNameError.d.ts
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+declare class FailedToResolveNameError extends Error {
+ dirPaths: ReadonlyArray;
+ extraPaths: ReadonlyArray;
+ constructor(
+ dirPaths: ReadonlyArray,
+ extraPaths: ReadonlyArray,
+ );
+}
+export default FailedToResolveNameError;
diff --git a/packages/metro-resolver/types/errors/FailedToResolvePathError.d.ts b/packages/metro-resolver/types/errors/FailedToResolvePathError.d.ts
new file mode 100644
index 0000000000..ec865d28be
--- /dev/null
+++ b/packages/metro-resolver/types/errors/FailedToResolvePathError.d.ts
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {FileAndDirCandidates} from '../types';
+
+declare class FailedToResolvePathError extends Error {
+ candidates: FileAndDirCandidates;
+ constructor(candidates: FileAndDirCandidates);
+}
+export default FailedToResolvePathError;
diff --git a/packages/metro-resolver/types/errors/FailedToResolveUnsupportedError.d.ts b/packages/metro-resolver/types/errors/FailedToResolveUnsupportedError.d.ts
new file mode 100644
index 0000000000..2eec91a8da
--- /dev/null
+++ b/packages/metro-resolver/types/errors/FailedToResolveUnsupportedError.d.ts
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+declare class FailedToResolveUnsupportedError extends Error {
+ constructor(message: string);
+}
+export default FailedToResolveUnsupportedError;
diff --git a/packages/metro-resolver/types/errors/InvalidPackageConfigurationError.d.ts b/packages/metro-resolver/types/errors/InvalidPackageConfigurationError.d.ts
new file mode 100644
index 0000000000..a5e6995a60
--- /dev/null
+++ b/packages/metro-resolver/types/errors/InvalidPackageConfigurationError.d.ts
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+/**
+ * Raised when a package contains an invalid `package.json` configuration.
+ */
+declare class InvalidPackageConfigurationError extends Error {
+ /**
+ * The description of the error cause.
+ */
+ reason: string;
+ /**
+ * Absolute path of the package being resolved.
+ */
+ packagePath: string;
+ constructor(opts: Readonly<{reason: string; packagePath: string}>);
+}
+export default InvalidPackageConfigurationError;
diff --git a/packages/metro-resolver/types/errors/InvalidPackageError.d.ts b/packages/metro-resolver/types/errors/InvalidPackageError.d.ts
new file mode 100644
index 0000000000..51b42b178b
--- /dev/null
+++ b/packages/metro-resolver/types/errors/InvalidPackageError.d.ts
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {FileCandidates} from '../types';
+
+declare class InvalidPackageError extends Error {
+ /**
+ * The file candidates we tried to find to resolve the `main` field of the
+ * package. Ex. `/js/foo/beep(.js|.json)?` if `main` is specifying `./beep`
+ * as the entry point.
+ */
+ fileCandidates: FileCandidates;
+ /**
+ * The 'index' file candidates we tried to find to resolve the `main` field of
+ * the package. Ex. `/js/foo/beep/index(.js|.json)?` if `main` is specifying
+ * `./beep` as the entry point.
+ */
+ indexCandidates: FileCandidates;
+ /**
+ * The full path to the main module that was attempted.
+ */
+ mainModulePath: string;
+ /**
+ * Full path the package we were trying to resolve.
+ * Ex. `/js/foo/package.json`.
+ */
+ packageJsonPath: string;
+ constructor(opts: {
+ readonly fileCandidates: FileCandidates;
+ readonly indexCandidates: FileCandidates;
+ readonly mainModulePath: string;
+ readonly packageJsonPath: string;
+ });
+}
+export default InvalidPackageError;
diff --git a/packages/metro-resolver/types/errors/PackageImportNotResolvedError.d.ts b/packages/metro-resolver/types/errors/PackageImportNotResolvedError.d.ts
new file mode 100644
index 0000000000..8ac2c5b863
--- /dev/null
+++ b/packages/metro-resolver/types/errors/PackageImportNotResolvedError.d.ts
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+/**
+ * Raised when package imports do not define or permit a target subpath in the
+ * package for the given import specifier.
+ */
+declare class PackageImportNotResolvedError extends Error {
+ /**
+ * Either the import specifier read, or the absolute path of the module being
+ * resolved (used when import specifier is externally remapped).
+ */
+ readonly importSpecifier: string;
+ /**
+ * The description of the error cause.
+ */
+ readonly reason: string;
+ constructor(opts: Readonly<{importSpecifier: string; reason: string}>);
+}
+export default PackageImportNotResolvedError;
diff --git a/packages/metro-resolver/types/errors/PackagePathNotExportedError.d.ts b/packages/metro-resolver/types/errors/PackagePathNotExportedError.d.ts
new file mode 100644
index 0000000000..591e4297db
--- /dev/null
+++ b/packages/metro-resolver/types/errors/PackagePathNotExportedError.d.ts
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+/**
+ * Raised when package exports do not define or permit a target subpath in the
+ * package for the given module.
+ */
+declare class PackagePathNotExportedError extends Error {}
+export default PackagePathNotExportedError;
diff --git a/packages/metro-resolver/types/errors/formatFileCandidates.d.ts b/packages/metro-resolver/types/errors/formatFileCandidates.d.ts
new file mode 100644
index 0000000000..48d518d0e6
--- /dev/null
+++ b/packages/metro-resolver/types/errors/formatFileCandidates.d.ts
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {FileCandidates} from '../types';
+
+declare function formatFileCandidates(candidates: FileCandidates): string;
+export default formatFileCandidates;
diff --git a/packages/metro-resolver/types/index.d.ts b/packages/metro-resolver/types/index.d.ts
index 3f60b23551..d363312715 100644
--- a/packages/metro-resolver/types/index.d.ts
+++ b/packages/metro-resolver/types/index.d.ts
@@ -8,16 +8,36 @@
* @oncall react_native
*/
-export * from './types';
-
-import {Resolution, ResolutionContext} from './types';
-
-export function resolve(
- context: ResolutionContext,
- moduleName: string,
- platform: string | null,
-): Resolution;
+export type {
+ AssetFileResolution,
+ CustomResolutionContext,
+ CustomResolver,
+ CustomResolverOptions,
+ DoesFileExist,
+ FileAndDirCandidates,
+ FileCandidates,
+ FileResolution,
+ FileSystemLookup,
+ ResolutionContext,
+ Resolution,
+ ResolveAsset,
+ Result,
+} from './types';
+import FailedToResolveNameError from './errors/FailedToResolveNameError';
+import FailedToResolvePathError from './errors/FailedToResolvePathError';
+import FailedToResolveUnsupportedError from './errors/FailedToResolveUnsupportedError';
+import formatFileCandidates from './errors/formatFileCandidates';
+import InvalidPackageError from './errors/InvalidPackageError';
+import resolve from './resolve';
+export {
+ FailedToResolveNameError,
+ FailedToResolvePathError,
+ FailedToResolveUnsupportedError,
+ formatFileCandidates,
+ InvalidPackageError,
+ resolve,
+};
/**
* Backwards-compatibility with CommonJS consumers using interopRequireDefault.
* Do not add to this list.
@@ -25,6 +45,11 @@ export function resolve(
* @deprecated Default import from 'metro-resolver' is deprecated, use named exports.
*/
declare const $$EXPORT_DEFAULT_DECLARATION$$: {
+ FailedToResolveNameError: typeof FailedToResolveNameError;
+ FailedToResolvePathError: typeof FailedToResolvePathError;
+ FailedToResolveUnsupportedError: typeof FailedToResolveUnsupportedError;
+ formatFileCandidates: typeof formatFileCandidates;
+ InvalidPackageError: typeof InvalidPackageError;
resolve: typeof resolve;
};
declare type $$EXPORT_DEFAULT_DECLARATION$$ =
diff --git a/packages/metro-resolver/types/resolve.d.ts b/packages/metro-resolver/types/resolve.d.ts
new file mode 100644
index 0000000000..574056539d
--- /dev/null
+++ b/packages/metro-resolver/types/resolve.d.ts
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {Resolution, ResolutionContext} from './types';
+
+declare function resolve(
+ context: ResolutionContext,
+ moduleName: string,
+ platform: string | null,
+): Resolution;
+export default resolve;
diff --git a/packages/metro-resolver/types/resolveAsset.d.ts b/packages/metro-resolver/types/resolveAsset.d.ts
new file mode 100644
index 0000000000..e3b3ff4f0d
--- /dev/null
+++ b/packages/metro-resolver/types/resolveAsset.d.ts
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {AssetResolution, ResolutionContext} from './types';
+/**
+ * Resolve a file path as an asset. Returns the set of files found after
+ * expanding asset resolutions (e.g. `icon@2x.png`). Users may override this
+ * behaviour via `context.resolveAsset`.
+ */
+declare function resolveAsset(
+ context: ResolutionContext,
+ filePath: string,
+): AssetResolution | null;
+export default resolveAsset;
diff --git a/packages/metro-resolver/types/types.d.ts b/packages/metro-resolver/types/types.d.ts
index 0b2a82db34..315b88c945 100644
--- a/packages/metro-resolver/types/types.d.ts
+++ b/packages/metro-resolver/types/types.d.ts
@@ -8,14 +8,12 @@
* @oncall react_native
*/
-import {TransformResultDependency} from 'metro';
+import type {TransformResultDependency} from 'metro/private/DeltaBundler/types';
export type Result =
| {readonly type: 'resolved'; readonly resolution: TResolution}
| {readonly type: 'failed'; readonly candidates: TCandidates};
-
-export type Resolution = FileResolution | Readonly<{type: 'empty'}>;
-
+export type Resolution = FileResolution | {readonly type: 'empty'};
export type SourceFileResolution = Readonly<{
type: 'sourceFile';
filePath: string;
@@ -26,55 +24,65 @@ export type AssetResolution = Readonly<{
filePaths: AssetFileResolution;
}>;
export type FileResolution = AssetResolution | SourceFileResolution;
-
-export interface FileAndDirCandidates {
- readonly dir: FileCandidates;
- readonly file: FileCandidates;
-}
-
+export type FileAndDirCandidates = {
+ readonly dir: null | undefined | FileCandidates;
+ readonly file: null | undefined | FileCandidates;
+};
/**
* This is a way to describe what files we tried to look for when resolving
* a module name as file. This is mainly used for error reporting, so that
* we can explain why we cannot resolve a module.
*/
export type FileCandidates =
- // We only tried to resolve a specific asset.
| {readonly type: 'asset'; readonly name: string}
- // We attempted to resolve a name as being a source file (ex. JavaScript,
- // JSON...), in which case there can be several extensions we tried, for
- // example `/js/foo.ios.js`, `/js/foo.js`, etc. for a single prefix '/js/foo'.
| {
readonly type: 'sourceFile';
filePathPrefix: string;
readonly candidateExts: ReadonlyArray;
};
-
-export type ExportMap = Readonly<{
- [subpathOrCondition: string]: ExportMap | string | null;
+export type ExportsLikeMap = Readonly<{
+ [subpathOrCondition: string]: string | ExportsLikeMap | null;
}>;
-
-export interface PackageJson {
- readonly name?: string;
- readonly main?: string;
- readonly exports?: string | ExportMap;
-}
-
-export interface PackageInfo {
- readonly packageJson: PackageJson;
- readonly rootPath: string;
-}
-
-export interface PackageForModule extends PackageInfo {
- /* A system-separated subpath (with no './' prefix) that reflects the subpath
- of the given candidate relative to the returned rootPath. */
- readonly packageRelativePath: string;
-}
-
+/** "exports" mapping where values may be legacy Node.js <13.7 array format. */
+export type ExportMapWithFallbacks = Readonly<{
+ [subpath: string]:
+ | ExportsLikeMap[keyof ExportsLikeMap]
+ | ExportValueWithFallback;
+}>;
+/** "exports" subpath value when in legacy Node.js <13.7 array format. */
+export type ExportValueWithFallback =
+ | ReadonlyArray
+ | ReadonlyArray>;
+export type ExportsField =
+ | string
+ | ReadonlyArray
+ | ExportValueWithFallback
+ | ExportsLikeMap
+ | ExportMapWithFallbacks;
+export type FlattenedExportMap = ReadonlyMap;
+export type NormalizedExportsLikeMap = Map<
+ string,
+ null | string | ExportsLikeMap
+>;
+export type PackageJson = Readonly<{
+ name?: string;
+ main?: string;
+ exports?: ExportsField;
+ imports?: ExportsLikeMap;
+}>;
+export type PackageInfo = Readonly<{
+ packageJson: PackageJson;
+ rootPath: string;
+}>;
+export type PackageForModule = Readonly<
+ Omit & {
+ packageRelativePath: string;
+ }
+>;
/**
* Check existence of a single file.
*/
export type DoesFileExist = (filePath: string) => boolean;
-export type IsAssetFile = (fileName: string) => boolean;
/**
* Performs a lookup against an absolute or project-relative path to determine
* whether it exists as a file or directory. Follows any symlinks, and returns
@@ -83,7 +91,6 @@ export type IsAssetFile = (fileName: string) => boolean;
export type FileSystemLookup = (
absoluteOrProjectRelativePath: string,
) => {exists: false} | {exists: true; type: 'f' | 'd'; realPath: string};
-
/**
* Given a directory path and the base asset name, return a list of all the
* asset file names that match the given base name in that directory. Return
@@ -94,27 +101,25 @@ export type ResolveAsset = (
dirPath: string,
assetName: string,
extension: string,
-) => ReadonlyArray | undefined;
-
-export interface ResolutionContext {
- readonly assetExts: ReadonlyArray;
- readonly allowHaste: boolean;
- readonly customResolverOptions: CustomResolverOptions;
- readonly disableHierarchicalLookup: boolean;
-
+) => null | undefined | ReadonlyArray;
+export type ResolutionContext = Readonly<{
+ allowHaste: boolean;
+ assetExts: ReadonlySet;
+ customResolverOptions: CustomResolverOptions;
+ disableHierarchicalLookup: boolean;
/**
* Determine whether a regular file exists at the given path.
*
* @deprecated, prefer `fileSystemLookup`
*/
- readonly doesFileExist: DoesFileExist;
- readonly extraNodeModules?: {[key: string]: string};
-
+ doesFileExist: DoesFileExist;
+ extraNodeModules: null | undefined | {[$$Key$$: string]: string};
+ /** Is resolving for a development bundle. */
+ dev: boolean;
/**
* Get the parsed contents of the specified `package.json` file.
*/
- readonly getPackage: (packageJsonPath: string) => PackageJson | null;
-
+ getPackage: (packageJsonPath: string) => null | undefined | PackageJson;
/**
* Get the closest package scope, parsed `package.json` and relative subpath
* for a given absolute candidate path (which need not exist), or null if
@@ -122,17 +127,15 @@ export interface ResolutionContext {
*
* @deprecated See https://github.com/facebook/metro/commit/29c77bff31e2475a086bc3f04073f485da8f9ff0
*/
- readonly getPackageForModule: (
+ getPackageForModule: (
absoluteModulePath: string,
- ) => PackageForModule | null;
-
+ ) => null | undefined | PackageForModule;
/**
* The dependency descriptor, within the origin module, corresponding to the
* current resolution request. This is provided for diagnostic purposes ONLY
* and may not be used for resolution purposes.
*/
- readonly dependency?: TransformResultDependency;
-
+ dependency?: TransformResultDependency;
/**
* Whether the dependency to be resolved was declared with an ESM import,
* ("import x from 'y'" or "await import('z')"), or a CommonJS "require".
@@ -142,66 +145,59 @@ export interface ResolutionContext {
* Always equal to dependency.data.isESMImport where dependency is provided,
* but may be used for resolution.
*/
- readonly isESMImport?: boolean;
-
+ isESMImport?: boolean;
/**
* Synchonously returns information about a given absolute path, including
* whether it exists, whether it is a file or directory, and its absolute
* real path.
*/
- readonly fileSystemLookup: FileSystemLookup;
-
+ fileSystemLookup: FileSystemLookup;
/**
* The ordered list of fields to read in `package.json` to resolve a main
* entry point based on the "browser" field spec.
*/
- readonly mainFields: ReadonlyArray;
-
+ mainFields: ReadonlyArray;
/**
* Full path of the module that is requiring or importing the module to be
* resolved. This may not be the only place this dependency was found,
* as resolutions can be cached.
*/
- readonly originModulePath: string;
-
- readonly nodeModulesPaths: ReadonlyArray;
- readonly preferNativePlatform: boolean;
- readonly resolveAsset: ResolveAsset;
- readonly redirectModulePath: (modulePath: string) => string | false;
-
+ originModulePath: string;
+ nodeModulesPaths: ReadonlyArray;
+ preferNativePlatform: boolean;
+ resolveAsset: ResolveAsset;
+ redirectModulePath: (modulePath: string) => string | false;
/**
* Given a name, this should return the full path to the file that provides
* a Haste module of that name. Ex. for `Foo` it may return `/smth/Foo.js`.
*/
- readonly resolveHasteModule: (name: string) => string | undefined;
-
+ resolveHasteModule: (name: string) => null | undefined | string;
/**
* Given a name, this should return the full path to the package manifest that
* provides a Haste package of that name. Ex. for `Foo` it may return
* `/smth/Foo/package.json`.
*/
- readonly resolveHastePackage: (name: string) => string | undefined;
-
- readonly resolveRequest?: CustomResolver;
- readonly sourceExts: ReadonlyArray;
+ resolveHastePackage: (name: string) => null | undefined | string;
+ resolveRequest?: null | undefined | CustomResolver;
+ sourceExts: ReadonlyArray;
unstable_conditionNames: ReadonlyArray;
unstable_conditionsByPlatform: Readonly<{
[platform: string]: ReadonlyArray;
}>;
unstable_enablePackageExports: boolean;
unstable_logWarning: (message: string) => void;
-}
-
-export interface CustomResolutionContext extends ResolutionContext {
- readonly resolveRequest: CustomResolver;
-}
-
+}>;
+export type CustomResolutionContext = Readonly<
+ Omit & {
+ resolveRequest: CustomResolver;
+ }
+>;
export type CustomResolver = (
context: CustomResolutionContext,
moduleName: string,
platform: string | null,
) => Resolution;
-
-export type CustomResolverOptions = Readonly<{
- [option: string]: unknown;
-}>;
+export type CustomResolverOptions = {
+ __proto__: null;
+ readonly [$$Key$$: string]: unknown;
+};
diff --git a/packages/metro-resolver/types/utils/isAssetFile.d.ts b/packages/metro-resolver/types/utils/isAssetFile.d.ts
new file mode 100644
index 0000000000..f09d7bd8d8
--- /dev/null
+++ b/packages/metro-resolver/types/utils/isAssetFile.d.ts
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+/**
+ * Determine if a file path should be considered an asset file based on the
+ * given `assetExts`.
+ */
+declare function isAssetFile(
+ filePath: string,
+ assetExts: ReadonlySet,
+): boolean;
+export default isAssetFile;
diff --git a/packages/metro-resolver/types/utils/isSubpathDefinedInExportsLike.d.ts b/packages/metro-resolver/types/utils/isSubpathDefinedInExportsLike.d.ts
new file mode 100644
index 0000000000..15f71e28f7
--- /dev/null
+++ b/packages/metro-resolver/types/utils/isSubpathDefinedInExportsLike.d.ts
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+/**
+ * Identifies whether the given subpath is defined in the given "exports"-like
+ * mapping. Does not reduce exports conditions (therefore does not identify
+ * whether the subpath is mapped to a value).
+ */
+import type {NormalizedExportsLikeMap} from '../types';
+
+export declare function isSubpathDefinedInExportsLike(
+ exportsLikeMap: NormalizedExportsLikeMap,
+ subpath: string,
+): boolean;
diff --git a/packages/metro-resolver/types/utils/matchSubpathFromExportsLike.d.ts b/packages/metro-resolver/types/utils/matchSubpathFromExportsLike.d.ts
new file mode 100644
index 0000000000..8e3e95a8b8
--- /dev/null
+++ b/packages/metro-resolver/types/utils/matchSubpathFromExportsLike.d.ts
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {NormalizedExportsLikeMap, ResolutionContext} from '../types';
+/**
+ * Get the mapped replacement for the given subpath.
+ *
+ * Implements modern package resolution behaviour based on the [Package Entry
+ * Points spec](https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points).
+ */
+export declare function matchSubpathFromExportsLike(
+ context: ResolutionContext,
+ subpath: string,
+ exportsLikeMap: NormalizedExportsLikeMap,
+ platform: string | null,
+ createConfigError: (reason: string) => Error,
+): Readonly<{target: string | null; patternMatch: string | null}>;
diff --git a/packages/metro-resolver/types/utils/matchSubpathPattern.d.ts b/packages/metro-resolver/types/utils/matchSubpathPattern.d.ts
new file mode 100644
index 0000000000..b4d2e774e3
--- /dev/null
+++ b/packages/metro-resolver/types/utils/matchSubpathPattern.d.ts
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+/**
+ * If a subpath pattern expands to the passed subpath, return the subpath match
+ * (value to substitute for '*'). Otherwise, return `null`.
+ *
+ * See https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns.
+ */
+export declare function matchSubpathPattern(
+ subpathPattern: string,
+ subpath: string,
+): string | null;
diff --git a/packages/metro-resolver/types/utils/reduceExportsLikeMap.d.ts b/packages/metro-resolver/types/utils/reduceExportsLikeMap.d.ts
new file mode 100644
index 0000000000..4add966aa9
--- /dev/null
+++ b/packages/metro-resolver/types/utils/reduceExportsLikeMap.d.ts
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+/**
+ * Reduce an "exports"-like mapping to a flat subpath mapping after resolving
+ * conditional exports.
+ */
+import type {FlattenedExportMap, NormalizedExportsLikeMap} from '../types';
+
+export declare function reduceExportsLikeMap(
+ exportsLikeMap: NormalizedExportsLikeMap,
+ conditionNames: ReadonlySet,
+ createConfigError: (reason: string) => Error,
+): FlattenedExportMap;
diff --git a/packages/metro-resolver/types/utils/toPosixPath.d.ts b/packages/metro-resolver/types/utils/toPosixPath.d.ts
new file mode 100644
index 0000000000..2f87e35ad7
--- /dev/null
+++ b/packages/metro-resolver/types/utils/toPosixPath.d.ts
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+/**
+ * Replace path separators in the passed string to coerce to a POSIX path. This
+ * is a no-op on POSIX systems.
+ */
+declare function toPosixPath(relativePathOrSpecifier: string): string;
+export default toPosixPath;
diff --git a/packages/metro-source-map/src/Consumer/constants.js b/packages/metro-source-map/src/Consumer/constants.js
index 57277a730c..d4c2adb62a 100644
--- a/packages/metro-source-map/src/Consumer/constants.js
+++ b/packages/metro-source-map/src/Consumer/constants.js
@@ -24,7 +24,12 @@ export opaque type LookupBias = 'GREATEST_LOWER_BOUND' | 'LEAST_UPPER_BOUND';
const GREATEST_LOWER_BOUND: LookupBias = 'GREATEST_LOWER_BOUND';
const LEAST_UPPER_BOUND: LookupBias = 'LEAST_UPPER_BOUND';
-const EMPTY_POSITION = Object.freeze({
+const EMPTY_POSITION: $ReadOnly<{
+ source: null,
+ name: null,
+ line: null,
+ column: null,
+}> = Object.freeze({
source: null,
name: null,
line: null,
diff --git a/packages/metro-source-map/src/encode.js b/packages/metro-source-map/src/encode.js
index 3b0f415e54..a1da7dfb8b 100644
--- a/packages/metro-source-map/src/encode.js
+++ b/packages/metro-source-map/src/encode.js
@@ -100,7 +100,11 @@ function toVLQSigned(value: number) {
* DON'T ADD MORE COMMENTS TO THIS FUNCTION TO KEEP ITS LENGTH SHORT ENOUGH FOR
* V8 OPTIMIZATION!
*/
-function encode(value: number, buffer: Buffer, position: number): number {
+export default function encode(
+ value: number,
+ buffer: Buffer,
+ position: number,
+): number {
let vlq = toVLQSigned(value);
let digit;
do {
@@ -116,5 +120,3 @@ function encode(value: number, buffer: Buffer, position: number): number {
return position;
}
-
-export default encode;
diff --git a/packages/metro-source-map/types/B64Builder.d.ts b/packages/metro-source-map/types/B64Builder.d.ts
new file mode 100644
index 0000000000..b6402525ee
--- /dev/null
+++ b/packages/metro-source-map/types/B64Builder.d.ts
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+/**
+ * Efficient builder for base64 VLQ mappings strings.
+ *
+ * This class uses a buffer that is preallocated with one megabyte and is
+ * reallocated dynamically as needed, doubling its size.
+ *
+ * Encoding never creates any complex value types (strings, objects), and only
+ * writes character values to the buffer.
+ *
+ * For details about source map terminology and specification, check
+ * https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
+ */
+declare class B64Builder {
+ buffer: Buffer;
+ pos: number;
+ hasSegment: boolean;
+ constructor();
+ /**
+ * Adds `n` markers for generated lines to the mappings.
+ */
+ markLines(n: number): this;
+ /**
+ * Starts a segment at the specified column offset in the current line.
+ */
+ startSegment(column: number): this;
+ /**
+ * Appends a single number to the mappings.
+ */
+ append(value: number): this;
+ /**
+ * Returns the string representation of the mappings.
+ */
+ toString(): string;
+ _writeByte(byte: number): void;
+ _realloc(): void;
+}
+export default B64Builder;
diff --git a/packages/metro-source-map/types/BundleBuilder.d.ts b/packages/metro-source-map/types/BundleBuilder.d.ts
new file mode 100644
index 0000000000..d63cc0ad89
--- /dev/null
+++ b/packages/metro-source-map/types/BundleBuilder.d.ts
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {IndexMap, IndexMapSection, MixedSourceMap} from './source-map';
+/**
+ * Builds a source-mapped bundle by concatenating strings and their
+ * corresponding source maps (if any).
+ *
+ * Usage:
+ *
+ * const builder = new BundleBuilder('bundle.js');
+ * builder
+ * .append('foo\n', fooMap)
+ * .append('bar\n')
+ * // ...
+ * const code = builder.getCode();
+ * const map = builder.getMap();
+ */
+export declare class BundleBuilder {
+ _file: string;
+ _sections: Array;
+ _line: number;
+ _column: number;
+ _code: string;
+ _afterMappedContent: boolean;
+ constructor(file: string);
+ _pushMapSection(map: MixedSourceMap): void;
+ _endMappedContent(): void;
+ append(code: string, map: null | undefined | MixedSourceMap): this;
+ getMap(): MixedSourceMap;
+ getCode(): string;
+}
+export declare function createIndexMap(
+ file: string,
+ sections: Array,
+): IndexMap;
diff --git a/packages/metro-source-map/types/Consumer/AbstractConsumer.d.ts b/packages/metro-source-map/types/Consumer/AbstractConsumer.d.ts
new file mode 100644
index 0000000000..c51c71a7d1
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/AbstractConsumer.d.ts
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {
+ GeneratedPositionLookup,
+ IConsumer,
+ IterationOrder,
+ Mapping,
+ SourcePosition,
+} from './types';
+
+declare class AbstractConsumer implements IConsumer {
+ _sourceMap: {readonly file?: string};
+ constructor(sourceMap: {readonly file?: string});
+ originalPositionFor(
+ generatedPosition: GeneratedPositionLookup,
+ ): SourcePosition;
+ generatedMappings(): Iterable;
+ eachMapping(
+ callback: (mapping: Mapping) => unknown,
+ context?: unknown,
+ order?: IterationOrder,
+ ): void;
+ get file(): null | undefined | string;
+ sourceContentFor(
+ source: string,
+ nullOnMissing: true,
+ ): null | undefined | string;
+}
+export default AbstractConsumer;
diff --git a/packages/metro-source-map/types/Consumer/DelegatingConsumer.d.ts b/packages/metro-source-map/types/Consumer/DelegatingConsumer.d.ts
new file mode 100644
index 0000000000..40381a996f
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/DelegatingConsumer.d.ts
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {MixedSourceMap} from '../source-map';
+import type {LookupBias} from './constants.js';
+import type {
+ GeneratedPositionLookup,
+ IConsumer,
+ IterationOrder,
+ Mapping,
+ SourcePosition,
+} from './types';
+/**
+ * A source map consumer that supports both "basic" and "indexed" source maps.
+ * Uses `MappingsConsumer` and `SectionsConsumer` under the hood (via
+ * `createConsumer`).
+ */
+declare class DelegatingConsumer implements IConsumer {
+ static readonly GENERATED_ORDER: IterationOrder;
+ static readonly ORIGINAL_ORDER: IterationOrder;
+ static readonly GREATEST_LOWER_BOUND: LookupBias;
+ static readonly LEAST_UPPER_BOUND: LookupBias;
+ _rootConsumer: IConsumer;
+ constructor(sourceMap: MixedSourceMap);
+ originalPositionFor(
+ generatedPosition: GeneratedPositionLookup,
+ ): SourcePosition;
+ generatedMappings(): Iterable;
+ eachMapping(
+ callback: (mapping: Mapping) => unknown,
+ context?: unknown,
+ order?: IterationOrder,
+ ): void;
+ get file(): null | undefined | string;
+ sourceContentFor(
+ source: string,
+ nullOnMissing: true,
+ ): null | undefined | string;
+}
+export default DelegatingConsumer;
diff --git a/packages/metro-source-map/types/Consumer/MappingsConsumer.d.ts b/packages/metro-source-map/types/Consumer/MappingsConsumer.d.ts
new file mode 100644
index 0000000000..dfadbaa5a8
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/MappingsConsumer.d.ts
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {BasicSourceMap} from '../source-map';
+import type {
+ GeneratedPositionLookup,
+ IConsumer,
+ Mapping,
+ SourcePosition,
+} from './types';
+import type {Number0} from 'ob1';
+
+import AbstractConsumer from './AbstractConsumer';
+/**
+ * A source map consumer that supports "basic" source maps (that have a
+ * `mappings` field and no sections).
+ */
+declare class MappingsConsumer extends AbstractConsumer implements IConsumer {
+ _sourceMap: BasicSourceMap;
+ _decodedMappings: null | undefined | ReadonlyArray;
+ _normalizedSources: null | undefined | ReadonlyArray;
+ constructor(sourceMap: BasicSourceMap);
+ originalPositionFor(
+ generatedPosition: GeneratedPositionLookup,
+ ): SourcePosition;
+ _decodeMappings(): Generator;
+ _normalizeAndCacheSources(): ReadonlyArray;
+ _decodeAndCacheMappings(): ReadonlyArray;
+ generatedMappings(): Iterable;
+ _indexOfSource(source: string): null | undefined | Number0;
+ sourceContentFor(
+ source: string,
+ nullOnMissing: true,
+ ): null | undefined | string;
+}
+export default MappingsConsumer;
diff --git a/packages/metro-source-map/types/Consumer/SectionsConsumer.d.ts b/packages/metro-source-map/types/Consumer/SectionsConsumer.d.ts
new file mode 100644
index 0000000000..865199acd4
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/SectionsConsumer.d.ts
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {IndexMap} from '../source-map';
+import type {
+ GeneratedOffset,
+ GeneratedPositionLookup,
+ IConsumer,
+ Mapping,
+ SourcePosition,
+} from './types';
+
+import AbstractConsumer from './AbstractConsumer';
+/**
+ * A source map consumer that supports "indexed" source maps (that have a
+ * `sections` field and no top-level mappings).
+ */
+declare class SectionsConsumer extends AbstractConsumer implements IConsumer {
+ _consumers: ReadonlyArray<[GeneratedOffset, IConsumer]>;
+ constructor(sourceMap: IndexMap);
+ originalPositionFor(
+ generatedPosition: GeneratedPositionLookup,
+ ): SourcePosition;
+ generatedMappings(): Iterable;
+ _consumerForPosition(
+ generatedPosition: GeneratedPositionLookup,
+ ): null | undefined | [GeneratedOffset, IConsumer];
+ sourceContentFor(
+ source: string,
+ nullOnMissing: true,
+ ): null | undefined | string;
+}
+export default SectionsConsumer;
diff --git a/packages/metro-source-map/types/Consumer/constants.d.ts b/packages/metro-source-map/types/Consumer/constants.d.ts
new file mode 100644
index 0000000000..145c66c6c8
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/constants.d.ts
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {Number0, Number1} from 'ob1';
+
+declare const FIRST_COLUMN: Number0;
+declare const FIRST_LINE: Number1;
+export declare type IterationOrder = symbol & {__IterationOrder__: string};
+declare const GENERATED_ORDER: IterationOrder;
+declare const ORIGINAL_ORDER: IterationOrder;
+export declare type LookupBias = symbol & {__LookupBias__: string};
+declare const GREATEST_LOWER_BOUND: LookupBias;
+declare const LEAST_UPPER_BOUND: LookupBias;
+declare const EMPTY_POSITION: Readonly<{
+ source: null;
+ name: null;
+ line: null;
+ column: null;
+}>;
+declare function iterationOrderToString(x: IterationOrder): string;
+declare function lookupBiasToString(x: LookupBias): string;
+export {
+ FIRST_COLUMN,
+ FIRST_LINE,
+ GENERATED_ORDER,
+ ORIGINAL_ORDER,
+ GREATEST_LOWER_BOUND,
+ LEAST_UPPER_BOUND,
+ EMPTY_POSITION,
+ iterationOrderToString,
+ lookupBiasToString,
+};
diff --git a/packages/metro-source-map/types/Consumer/createConsumer.d.ts b/packages/metro-source-map/types/Consumer/createConsumer.d.ts
new file mode 100644
index 0000000000..47e9856133
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/createConsumer.d.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {MixedSourceMap} from '../source-map';
+import type {IConsumer} from './types';
+
+declare function createConsumer(sourceMap: MixedSourceMap): IConsumer;
+export default createConsumer;
diff --git a/packages/metro-source-map/types/Consumer/index.d.ts b/packages/metro-source-map/types/Consumer/index.d.ts
new file mode 100644
index 0000000000..bb85e48e1a
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/index.d.ts
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import DelegatingConsumer from './DelegatingConsumer';
+
+declare const $$EXPORT_DEFAULT_DECLARATION$$: typeof DelegatingConsumer;
+declare type $$EXPORT_DEFAULT_DECLARATION$$ =
+ typeof $$EXPORT_DEFAULT_DECLARATION$$;
+export default $$EXPORT_DEFAULT_DECLARATION$$;
diff --git a/packages/metro-source-map/types/Consumer/normalizeSourcePath.d.ts b/packages/metro-source-map/types/Consumer/normalizeSourcePath.d.ts
new file mode 100644
index 0000000000..cd6deb84e4
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/normalizeSourcePath.d.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+declare function normalizeSourcePath(
+ sourceInput: string,
+ map: {readonly sourceRoot?: null | undefined | string},
+): string;
+export default normalizeSourcePath;
diff --git a/packages/metro-source-map/types/Consumer/positionMath.d.ts b/packages/metro-source-map/types/Consumer/positionMath.d.ts
new file mode 100644
index 0000000000..6af98d4c34
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/positionMath.d.ts
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {GeneratedOffset} from './types';
+import type {Number0, Number1} from 'ob1';
+
+export declare function shiftPositionByOffset<
+ T extends {
+ readonly line: null | undefined | Number1;
+ readonly column: null | undefined | Number0;
+ },
+>(pos: T, offset: GeneratedOffset): T;
+export declare function subtractOffsetFromPosition<
+ T extends {
+ readonly line: null | undefined | Number1;
+ readonly column: null | undefined | Number0;
+ },
+>(pos: T, offset: GeneratedOffset): T;
diff --git a/packages/metro-source-map/types/Consumer/search.d.ts b/packages/metro-source-map/types/Consumer/search.d.ts
new file mode 100644
index 0000000000..c23db5c75c
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/search.d.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+export declare function greatestLowerBound(
+ elements: ReadonlyArray,
+ target: U,
+ comparator: ($$PARAM_0$$: U, $$PARAM_1$$: T) => number,
+): null | undefined | number;
diff --git a/packages/metro-source-map/types/Consumer/types.d.ts b/packages/metro-source-map/types/Consumer/types.d.ts
new file mode 100644
index 0000000000..88132dab6f
--- /dev/null
+++ b/packages/metro-source-map/types/Consumer/types.d.ts
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {IterationOrder, LookupBias} from './constants';
+import type {Number0, Number1} from 'ob1';
+
+export type {IterationOrder, LookupBias};
+export type GeneratedOffset = {
+ readonly lines: Number0;
+ readonly columns: Number0;
+};
+export type SourcePosition = {
+ source: null | undefined | string;
+ line: null | undefined | Number1;
+ column: null | undefined | Number0;
+ name: null | undefined | string;
+};
+export type GeneratedPosition = {
+ readonly line: Number1;
+ readonly column: Number0;
+};
+export type GeneratedPositionLookup = {
+ readonly line: null | undefined | Number1;
+ readonly column: null | undefined | Number0;
+ readonly bias?: LookupBias;
+};
+export type Mapping = Readonly<{
+ source: null | undefined | string;
+ generatedLine: Number1;
+ generatedColumn: Number0;
+ originalLine: null | undefined | Number1;
+ originalColumn: null | undefined | Number0;
+ name: null | undefined | string;
+}>;
+export interface IConsumer {
+ originalPositionFor(
+ generatedPosition: GeneratedPositionLookup,
+ ): SourcePosition;
+ generatedMappings(): Iterable;
+ eachMapping(
+ callback: (mapping: Mapping) => unknown,
+ context?: unknown,
+ order?: IterationOrder,
+ ): void;
+ get file(): null | undefined | string;
+ sourceContentFor(
+ source: string,
+ nullOnMissing: true,
+ ): null | undefined | string;
+}
diff --git a/packages/metro-source-map/types/Generator.d.ts b/packages/metro-source-map/types/Generator.d.ts
new file mode 100644
index 0000000000..2aa73e7761
--- /dev/null
+++ b/packages/metro-source-map/types/Generator.d.ts
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {
+ BasicSourceMap,
+ FBSourceFunctionMap,
+ FBSourceMetadata,
+} from './source-map';
+
+import B64Builder from './B64Builder';
+
+type FileFlags = Readonly<{addToIgnoreList?: boolean}>;
+/**
+ * Generates a source map from raw mappings.
+ *
+ * Raw mappings are a set of 2, 4, or five elements:
+ *
+ * - line and column number in the generated source
+ * - line and column number in the original source
+ * - symbol name in the original source
+ *
+ * Mappings have to be passed in the order appearance in the generated source.
+ */
+declare class Generator {
+ builder: B64Builder;
+ last: {
+ generatedColumn: number;
+ generatedLine: number;
+ name: number;
+ source: number;
+ sourceColumn: number;
+ sourceLine: number;
+ };
+ names: IndexedSet;
+ source: number;
+ sources: Array;
+ sourcesContent: Array;
+ x_facebook_sources: Array;
+ x_google_ignoreList: Array;
+ constructor();
+ /**
+ * Mark the beginning of a new source file.
+ */
+ startFile(
+ file: string,
+ code: string,
+ functionMap: null | undefined | FBSourceFunctionMap,
+ flags?: FileFlags,
+ ): void;
+ /**
+ * Mark the end of the current source file
+ */
+ endFile(): void;
+ /**
+ * Adds a mapping for generated code without a corresponding source location.
+ */
+ addSimpleMapping(generatedLine: number, generatedColumn: number): void;
+ /**
+ * Adds a mapping for generated code with a corresponding source location.
+ */
+ addSourceMapping(
+ generatedLine: number,
+ generatedColumn: number,
+ sourceLine: number,
+ sourceColumn: number,
+ ): void;
+ /**
+ * Adds a mapping for code with a corresponding source location + symbol name.
+ */
+ addNamedSourceMapping(
+ generatedLine: number,
+ generatedColumn: number,
+ sourceLine: number,
+ sourceColumn: number,
+ name: string,
+ ): void;
+ /**
+ * Return the source map as object.
+ */
+ toMap(file?: string, options?: {excludeSource?: boolean}): BasicSourceMap;
+ /**
+ * Return the source map as string.
+ *
+ * This is ~2.5x faster than calling `JSON.stringify(generator.toMap())`
+ */
+ toString(file?: string, options?: {excludeSource?: boolean}): string;
+ /**
+ * Determine whether we need to write the `x_facebook_sources` field.
+ * If the metadata is all `null`s, we can omit the field entirely.
+ */
+ hasSourcesMetadata(): boolean;
+}
+export default Generator;
+declare class IndexedSet {
+ map: Map;
+ nextIndex: number;
+ constructor();
+ indexFor(x: string): number;
+ items(): Array;
+}
diff --git a/packages/metro-source-map/types/composeSourceMaps.d.ts b/packages/metro-source-map/types/composeSourceMaps.d.ts
new file mode 100644
index 0000000000..bf5002f055
--- /dev/null
+++ b/packages/metro-source-map/types/composeSourceMaps.d.ts
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {MixedSourceMap} from './source-map';
+
+declare function composeSourceMaps(
+ maps: ReadonlyArray,
+): MixedSourceMap;
+export default composeSourceMaps;
diff --git a/packages/metro-source-map/types/encode.d.ts b/packages/metro-source-map/types/encode.d.ts
new file mode 100644
index 0000000000..900abed5cc
--- /dev/null
+++ b/packages/metro-source-map/types/encode.d.ts
@@ -0,0 +1,25 @@
+/**
+ * Portions Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+/**
+ * Encodes a number to base64 VLQ format and appends it to the passed-in buffer
+ *
+ * DON'T USE COMPOUND OPERATORS (eg `>>>=`) ON `let`-DECLARED VARIABLES!
+ * V8 WILL DEOPTIMIZE THIS FUNCTION AND MAP CREATION WILL BE 25% SLOWER!
+ *
+ * DON'T ADD MORE COMMENTS TO THIS FUNCTION TO KEEP ITS LENGTH SHORT ENOUGH FOR
+ * V8 OPTIMIZATION!
+ */
+declare function encode(
+ value: number,
+ buffer: Buffer,
+ position: number,
+): number;
+export default encode;
diff --git a/packages/metro-source-map/types/generateFunctionMap.d.ts b/packages/metro-source-map/types/generateFunctionMap.d.ts
new file mode 100644
index 0000000000..bdf39c7b77
--- /dev/null
+++ b/packages/metro-source-map/types/generateFunctionMap.d.ts
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @oncall react_native
+ */
+
+import type {FBSourceFunctionMap} from './source-map';
+import type {PluginObj} from '@babel/core';
+
+type Position = {line: number; column: number};
+type RangeMapping = {name: string; start: Position};
+export type Context = {filename?: null | undefined | string};
+/**
+ * Generate a map of source positions to function names. The names are meant to
+ * describe the stack frame in an error trace and may contain more contextual
+ * information than just the actual name of the function.
+ *
+ * The output is encoded for use in a source map. For details about the format,
+ * see MappingEncoder below.
+ */
+declare function generateFunctionMap(
+ ast: BabelNode,
+ context?: Context,
+): FBSourceFunctionMap;
+/**
+ * Same as generateFunctionMap, but returns the raw array of mappings instead
+ * of encoding it for use in a source map.
+ *
+ * Lines are 1-based and columns are 0-based.
+ */
+declare function generateFunctionMappingsArray(
+ ast: BabelNode,
+ context?: Context,
+): ReadonlyArray;
+declare function functionMapBabelPlugin(): PluginObj;
+export {
+ functionMapBabelPlugin,
+ generateFunctionMap,
+ generateFunctionMappingsArray,
+};
diff --git a/packages/metro-source-map/types/source-map.d.ts b/packages/metro-source-map/types/source-map.d.ts
index a855bdcd47..6d1c21f5a5 100644
--- a/packages/metro-source-map/types/source-map.d.ts
+++ b/packages/metro-source-map/types/source-map.d.ts
@@ -8,54 +8,143 @@
* @oncall react_native
*/
-export type GeneratedCodeMapping = [number, number];
-export type SourceMapping = [number, number, number, number];
-export type SourceMappingWithName = [number, number, number, number, string];
+import type {IConsumer} from './Consumer/types';
+import type {BabelSourceMapSegment} from '@babel/generator';
+import {BundleBuilder, createIndexMap} from './BundleBuilder';
+import composeSourceMaps from './composeSourceMaps';
+import Consumer from './Consumer';
+import normalizeSourcePath from './Consumer/normalizeSourcePath';
+import {
+ functionMapBabelPlugin,
+ generateFunctionMap,
+} from './generateFunctionMap';
+import Generator from './Generator';
+
+export type {IConsumer};
+type GeneratedCodeMapping = [number, number];
+type SourceMapping = [number, number, number, number];
+type SourceMappingWithName = [number, number, number, number, string];
export type MetroSourceMapSegmentTuple =
| SourceMappingWithName
| SourceMapping
| GeneratedCodeMapping;
-
-export interface HermesFunctionOffsets {
- [id: number]: ReadonlyArray;
-}
-
-export type FBSourcesArray = ReadonlyArray;
-export type FBSourceMetadata = [FBSourceFunctionMap | null];
-export interface FBSourceFunctionMap {
+export type HermesFunctionOffsets = {
+ [$$Key$$: number]: ReadonlyArray;
+};
+export type FBSourcesArray = ReadonlyArray;
+export type FBSourceMetadata = [null | undefined | FBSourceFunctionMap];
+export type FBSourceFunctionMap = {
readonly names: ReadonlyArray;
readonly mappings: string;
-}
-
-export interface FBSegmentMap {
- [id: string]: MixedSourceMap;
-}
-
-export interface BasicSourceMap {
+};
+export type FBSegmentMap = {[id: string]: MixedSourceMap};
+export type BasicSourceMap = {
readonly file?: string;
readonly mappings: string;
- readonly names: string[];
+ readonly names: Array;
readonly sourceRoot?: string;
- readonly sources: string[];
- readonly sourcesContent?: Array;
+ readonly sources: Array;
+ readonly sourcesContent?: Array