FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

90
node_modules/metro/src/Assets.d.ts generated vendored Normal file
View File

@@ -0,0 +1,90 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<a58b1d0332fe424196b6d106eacdc2fe>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/Assets.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
export type AssetInfo = {
readonly files: Array<string>;
readonly hash: string;
readonly name: string;
readonly scales: Array<number>;
readonly type: string;
};
export type AssetDataWithoutFiles = {
readonly __packager_asset: boolean;
readonly fileSystemLocation: string;
readonly hash: string;
readonly height: null | undefined | number;
readonly httpServerLocation: string;
readonly name: string;
readonly scales: Array<number>;
readonly type: string;
readonly width: null | undefined | number;
};
export type AssetDataFiltered = {
readonly __packager_asset: boolean;
readonly hash: string;
readonly height: null | undefined | number;
readonly httpServerLocation: string;
readonly name: string;
readonly scales: Array<number>;
readonly type: string;
readonly width: null | undefined | number;
};
export declare function isAssetTypeAnImage(type: string): boolean;
export declare function getAssetSize(
type: string,
content: Buffer,
filePath: string,
): null | undefined | {readonly width: number; readonly height: number};
export type AssetData = AssetDataWithoutFiles & {
readonly files: Array<string>;
};
export type AssetDataPlugin = (
assetData: AssetData,
) => AssetData | Promise<AssetData>;
export declare function getAssetData(
assetPath: string,
localPath: string,
assetDataPlugins: ReadonlyArray<string>,
platform: null | undefined | string,
publicPath: string,
): Promise<AssetData>;
/**
* Returns all the associated files (for different resolutions) of an asset.
**/
export declare function getAssetFiles(
assetPath: string,
platform?: null | undefined | string,
): Promise<Array<string>>;
/**
* Return a buffer with the actual image given a request for an image by path.
* The relativePath can contain a resolution postfix, in this case we need to
* find that image (or the closest one to it's resolution) in one of the
* project roots:
*
* 1. We first parse the directory of the asset
* 2. We then build a map of all assets and their scales in this directory
* 3. Then try to pick platform-specific asset records
* 4. Then pick the closest resolution (rounding up) to the requested one
*/
export declare function getAsset(
relativePath: string,
projectRoot: string,
watchFolders: ReadonlyArray<string>,
platform: null | undefined | string,
assetExts: ReadonlyArray<string>,
fileExistsInFileMap?: (absolutePath: string) => boolean,
): Promise<Buffer>;

252
node_modules/metro/src/Assets.js generated vendored Normal file
View File

@@ -0,0 +1,252 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.getAsset = getAsset;
exports.getAssetData = getAssetData;
exports.getAssetFiles = getAssetFiles;
exports.getAssetSize = getAssetSize;
exports.isAssetTypeAnImage = isAssetTypeAnImage;
var _pathUtils = require("./lib/pathUtils");
var AssetPaths = _interopRequireWildcard(
require("./node-haste/lib/AssetPaths"),
);
var _crypto = _interopRequireDefault(require("crypto"));
var _fs = _interopRequireDefault(require("fs"));
var _imageSize = _interopRequireDefault(require("image-size"));
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function _interopRequireWildcard(e, t) {
if ("function" == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ("object" != typeof e && "function" != typeof e))
return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
"default" !== t &&
{}.hasOwnProperty.call(e, t) &&
((i =
(o = Object.defineProperty) &&
Object.getOwnPropertyDescriptor(e, t)) &&
(i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
function isAssetTypeAnImage(type) {
return (
[
"png",
"jpg",
"jpeg",
"bmp",
"gif",
"webp",
"psd",
"svg",
"tiff",
"ktx",
].indexOf(type) !== -1
);
}
function getAssetSize(type, content, filePath) {
if (!isAssetTypeAnImage(type)) {
return null;
}
if (content.length === 0) {
throw new Error(`Image asset \`${filePath}\` cannot be an empty file.`);
}
const { width, height } = (0, _imageSize.default)(content);
return {
width,
height,
};
}
function buildAssetMap(dir, files, platform) {
const platforms = new Set(platform != null ? [platform] : []);
const assets = files.map((file) => AssetPaths.tryParse(file, platforms));
const map = new Map();
assets.forEach(function (asset, i) {
if (asset == null) {
return;
}
const file = files[i];
const assetKey = getAssetKey(asset.assetName, asset.platform);
let record = map.get(assetKey);
if (!record) {
record = {
scales: [],
files: [],
};
map.set(assetKey, record);
}
let insertIndex;
const length = record.scales.length;
for (insertIndex = 0; insertIndex < length; insertIndex++) {
if (asset.resolution < record.scales[insertIndex]) {
break;
}
}
record.scales.splice(insertIndex, 0, asset.resolution);
record.files.splice(insertIndex, 0, _path.default.join(dir, file));
});
return map;
}
function getAssetKey(assetName, platform) {
if (platform != null) {
return `${assetName} : ${platform}`;
} else {
return assetName;
}
}
async function getAbsoluteAssetRecord(assetPath, platform = null) {
const filename = _path.default.basename(assetPath);
const dir = _path.default.dirname(assetPath);
const files = await _fs.default.promises.readdir(dir);
const assetData = AssetPaths.parse(
filename,
new Set(platform != null ? [platform] : []),
);
const map = buildAssetMap(dir, files, platform);
let record;
if (platform != null) {
record =
map.get(getAssetKey(assetData.assetName, platform)) ||
map.get(assetData.assetName);
} else {
record = map.get(assetData.assetName);
}
if (!record) {
throw new Error(
`Asset not found: ${assetPath} for platform: ${platform ?? "(unspecified)"}`,
);
}
return record;
}
async function getAbsoluteAssetInfo(assetPath, platform = null) {
const nameData = AssetPaths.parse(
assetPath,
new Set(platform != null ? [platform] : []),
);
const { name, type } = nameData;
const { scales, files } = await getAbsoluteAssetRecord(assetPath, platform);
const hasher = _crypto.default.createHash("md5");
const fileData = await Promise.all(
files.map((file) => _fs.default.promises.readFile(file)),
);
for (const data of fileData) {
hasher.update(data);
}
return {
files,
hash: hasher.digest("hex"),
name,
scales,
type,
};
}
async function getAssetData(
assetPath,
localPath,
assetDataPlugins,
platform,
publicPath,
) {
let assetUrlPath = localPath.startsWith("..")
? publicPath.replace(/\/$/, "") + "/" + _path.default.dirname(localPath)
: _path.default.join(publicPath, _path.default.dirname(localPath));
assetUrlPath = (0, _pathUtils.normalizePathSeparatorsToPosix)(assetUrlPath);
const isImage = isAssetTypeAnImage(_path.default.extname(assetPath).slice(1));
const assetInfo = await getAbsoluteAssetInfo(assetPath, platform ?? null);
const isImageInput = assetInfo.files[0].includes(".zip/")
? _fs.default.readFileSync(assetInfo.files[0])
: assetInfo.files[0];
const dimensions = isImage ? (0, _imageSize.default)(isImageInput) : null;
const scale = assetInfo.scales[0];
const assetData = {
__packager_asset: true,
fileSystemLocation: _path.default.dirname(assetPath),
httpServerLocation: assetUrlPath,
width: dimensions ? dimensions.width / scale : undefined,
height: dimensions ? dimensions.height / scale : undefined,
scales: assetInfo.scales,
files: assetInfo.files,
hash: assetInfo.hash,
name: assetInfo.name,
type: assetInfo.type,
};
return await applyAssetDataPlugins(assetDataPlugins, assetData);
}
async function applyAssetDataPlugins(assetDataPlugins, assetData) {
if (!assetDataPlugins.length) {
return assetData;
}
const [currentAssetPlugin, ...remainingAssetPlugins] = assetDataPlugins;
const assetPluginFunction = require(currentAssetPlugin);
const resultAssetData = await assetPluginFunction(assetData);
return await applyAssetDataPlugins(remainingAssetPlugins, resultAssetData);
}
async function getAssetFiles(assetPath, platform = null) {
const assetData = await getAbsoluteAssetRecord(assetPath, platform);
return assetData.files;
}
async function getAsset(
relativePath,
projectRoot,
watchFolders,
platform,
assetExts,
fileExistsInFileMap,
) {
const assetData = AssetPaths.parse(
relativePath,
new Set(platform != null ? [platform] : []),
);
const absolutePath = _path.default.resolve(projectRoot, relativePath);
if (!assetExts.includes(assetData.type)) {
throw new Error(
`'${relativePath}' cannot be loaded as its extension is not registered in assetExts`,
);
}
if (fileExistsInFileMap != null) {
if (!fileExistsInFileMap(absolutePath)) {
throw new Error(
`'${relativePath}' could not be found, because it is not within the projectRoot or watchFolders, or it is blocked via the resolver.blockList config`,
);
}
} else {
if (!pathBelongsToRoots(absolutePath, [projectRoot, ...watchFolders])) {
throw new Error(
`'${relativePath}' could not be found, because it cannot be found in the project root or any watch folder`,
);
}
}
const record = await getAbsoluteAssetRecord(absolutePath, platform ?? null);
for (let i = 0; i < record.scales.length; i++) {
if (record.scales[i] >= assetData.resolution) {
return _fs.default.promises.readFile(record.files[i]);
}
}
return _fs.default.promises.readFile(record.files[record.files.length - 1]);
}
function pathBelongsToRoots(pathToCheck, roots) {
for (const rootFolder of roots) {
if (pathToCheck.startsWith(_path.default.resolve(rootFolder))) {
return true;
}
}
return false;
}

338
node_modules/metro/src/Assets.js.flow generated vendored Normal file
View File

@@ -0,0 +1,338 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {AssetPath} from './node-haste/lib/AssetPaths';
import {normalizePathSeparatorsToPosix} from './lib/pathUtils';
import * as AssetPaths from './node-haste/lib/AssetPaths';
import crypto from 'crypto';
import fs from 'fs';
// $FlowFixMe[untyped-import] image-size
import getImageSize from 'image-size';
import path from 'path';
export type AssetInfo = {
+files: Array<string>,
+hash: string,
+name: string,
+scales: Array<number>,
+type: string,
};
export type AssetDataWithoutFiles = {
+__packager_asset: boolean,
+fileSystemLocation: string,
+hash: string,
+height: ?number,
+httpServerLocation: string,
+name: string,
+scales: Array<number>,
+type: string,
+width: ?number,
...
};
export type AssetDataFiltered = {
+__packager_asset: boolean,
+hash: string,
+height: ?number,
+httpServerLocation: string,
+name: string,
+scales: Array<number>,
+type: string,
+width: ?number,
...
};
// Test extension against all types supported by image-size module.
// If it's not one of these, we won't treat it as an image.
export function isAssetTypeAnImage(type: string): boolean {
return (
[
'png',
'jpg',
'jpeg',
'bmp',
'gif',
'webp',
'psd',
'svg',
'tiff',
'ktx',
].indexOf(type) !== -1
);
}
export function getAssetSize(
type: string,
content: Buffer,
filePath: string,
): ?{+width: number, +height: number} {
if (!isAssetTypeAnImage(type)) {
return null;
}
if (content.length === 0) {
throw new Error(`Image asset \`${filePath}\` cannot be an empty file.`);
}
const {width, height} = getImageSize(content);
return {width, height};
}
export type AssetData = AssetDataWithoutFiles & {+files: Array<string>, ...};
export type AssetDataPlugin = (
assetData: AssetData,
) => AssetData | Promise<AssetData>;
function buildAssetMap(
dir: string,
files: ReadonlyArray<string>,
platform: ?string,
): Map<string, {files: Array<string>, scales: Array<number>}> {
const platforms = new Set(platform != null ? [platform] : []);
const assets = files.map((file: string) =>
AssetPaths.tryParse(file, platforms),
);
const map = new Map<string, {files: Array<string>, scales: Array<number>}>();
assets.forEach(function (asset: ?AssetPath, i: number) {
if (asset == null) {
return;
}
const file = files[i];
const assetKey = getAssetKey(asset.assetName, asset.platform);
let record = map.get(assetKey);
if (!record) {
record = {
scales: [],
files: [],
};
map.set(assetKey, record);
}
let insertIndex;
const length = record.scales.length;
for (insertIndex = 0; insertIndex < length; insertIndex++) {
if (asset.resolution < record.scales[insertIndex]) {
break;
}
}
record.scales.splice(insertIndex, 0, asset.resolution);
record.files.splice(insertIndex, 0, path.join(dir, file));
});
return map;
}
function getAssetKey(assetName: string, platform: ?string): string {
if (platform != null) {
return `${assetName} : ${platform}`;
} else {
return assetName;
}
}
async function getAbsoluteAssetRecord(
assetPath: string,
platform: ?string = null,
): Promise<{files: Array<string>, scales: Array<number>}> {
const filename = path.basename(assetPath);
const dir = path.dirname(assetPath);
const files = await fs.promises.readdir(dir);
const assetData = AssetPaths.parse(
filename,
new Set(platform != null ? [platform] : []),
);
const map = buildAssetMap(dir, files, platform);
let record;
if (platform != null) {
record =
map.get(getAssetKey(assetData.assetName, platform)) ||
map.get(assetData.assetName);
} else {
record = map.get(assetData.assetName);
}
if (!record) {
throw new Error(
`Asset not found: ${assetPath} for platform: ${
platform ?? '(unspecified)'
}`,
);
}
return record;
}
async function getAbsoluteAssetInfo(
assetPath: string,
platform: ?string = null,
): Promise<AssetInfo> {
const nameData = AssetPaths.parse(
assetPath,
new Set(platform != null ? [platform] : []),
);
const {name, type} = nameData;
const {scales, files} = await getAbsoluteAssetRecord(assetPath, platform);
const hasher = crypto.createHash('md5');
const fileData = await Promise.all(
files.map(file => fs.promises.readFile(file)),
);
for (const data of fileData) {
hasher.update(data);
}
return {files, hash: hasher.digest('hex'), name, scales, type};
}
export async function getAssetData(
assetPath: string,
localPath: string,
assetDataPlugins: ReadonlyArray<string>,
platform: ?string,
publicPath: string,
): Promise<AssetData> {
// If the path of the asset is outside of the projectRoot, we don't want to
// use `path.join` since this will generate an incorrect URL path. In that
// case we just concatenate the publicPath with the relative path.
let assetUrlPath = localPath.startsWith('..')
? publicPath.replace(/\/$/, '') + '/' + path.dirname(localPath)
: path.join(publicPath, path.dirname(localPath));
// On Windows, change backslashes to slashes to get proper URL path from file path.
assetUrlPath = normalizePathSeparatorsToPosix(assetUrlPath);
const isImage = isAssetTypeAnImage(path.extname(assetPath).slice(1));
const assetInfo = await getAbsoluteAssetInfo(assetPath, platform ?? null);
const isImageInput = assetInfo.files[0].includes('.zip/')
? fs.readFileSync(assetInfo.files[0])
: assetInfo.files[0];
const dimensions = isImage ? getImageSize(isImageInput) : null;
const scale = assetInfo.scales[0];
const assetData = {
__packager_asset: true,
fileSystemLocation: path.dirname(assetPath),
httpServerLocation: assetUrlPath,
width: dimensions ? dimensions.width / scale : undefined,
height: dimensions ? dimensions.height / scale : undefined,
scales: assetInfo.scales,
files: assetInfo.files,
hash: assetInfo.hash,
name: assetInfo.name,
type: assetInfo.type,
};
return await applyAssetDataPlugins(assetDataPlugins, assetData);
}
async function applyAssetDataPlugins(
assetDataPlugins: ReadonlyArray<string>,
assetData: AssetData,
): Promise<AssetData> {
if (!assetDataPlugins.length) {
return assetData;
}
const [currentAssetPlugin, ...remainingAssetPlugins] = assetDataPlugins;
// $FlowFixMe[unsupported-syntax]: impossible to type a dynamic require.
const assetPluginFunction: AssetDataPlugin = require(currentAssetPlugin);
const resultAssetData = await assetPluginFunction(assetData);
return await applyAssetDataPlugins(remainingAssetPlugins, resultAssetData);
}
/**
* Returns all the associated files (for different resolutions) of an asset.
**/
export async function getAssetFiles(
assetPath: string,
platform: ?string = null,
): Promise<Array<string>> {
const assetData = await getAbsoluteAssetRecord(assetPath, platform);
return assetData.files;
}
/**
* Return a buffer with the actual image given a request for an image by path.
* The relativePath can contain a resolution postfix, in this case we need to
* find that image (or the closest one to it's resolution) in one of the
* project roots:
*
* 1. We first parse the directory of the asset
* 2. We then build a map of all assets and their scales in this directory
* 3. Then try to pick platform-specific asset records
* 4. Then pick the closest resolution (rounding up) to the requested one
*/
export async function getAsset(
relativePath: string,
projectRoot: string,
watchFolders: ReadonlyArray<string>,
platform: ?string,
assetExts: ReadonlyArray<string>,
fileExistsInFileMap?: (absolutePath: string) => boolean,
): Promise<Buffer> {
const assetData = AssetPaths.parse(
relativePath,
new Set(platform != null ? [platform] : []),
);
const absolutePath = path.resolve(projectRoot, relativePath);
if (!assetExts.includes(assetData.type)) {
throw new Error(
`'${relativePath}' cannot be loaded as its extension is not registered in assetExts`,
);
}
// NOTE: If fileExistsInFileMap is not provided, we fall back to pathBelongsToRoots for backward compatibility, as getAsset is part of the public API.
if (fileExistsInFileMap != null) {
if (!fileExistsInFileMap(absolutePath)) {
throw new Error(
`'${relativePath}' could not be found, because it is not within the projectRoot or watchFolders, or it is blocked via the resolver.blockList config`,
);
}
} else {
if (!pathBelongsToRoots(absolutePath, [projectRoot, ...watchFolders])) {
throw new Error(
`'${relativePath}' could not be found, because it cannot be found in the project root or any watch folder`,
);
}
}
const record = await getAbsoluteAssetRecord(absolutePath, platform ?? null);
for (let i = 0; i < record.scales.length; i++) {
if (record.scales[i] >= assetData.resolution) {
return fs.promises.readFile(record.files[i]);
}
}
return fs.promises.readFile(record.files[record.files.length - 1]);
}
function pathBelongsToRoots(
pathToCheck: string,
roots: ReadonlyArray<string>,
): boolean {
for (const rootFolder of roots) {
if (pathToCheck.startsWith(path.resolve(rootFolder))) {
return true;
}
}
return false;
}

45
node_modules/metro/src/Bundler.d.ts generated vendored Normal file
View File

@@ -0,0 +1,45 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<3d5664309abdece0f247fcd0c53c3aaf>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/Bundler.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {TransformResultWithSource} from './DeltaBundler';
import type {TransformOptions} from './DeltaBundler/Worker';
import type EventEmitter from 'events';
import type {ConfigT} from 'metro-config';
import Transformer from './DeltaBundler/Transformer';
import DependencyGraph from './node-haste/DependencyGraph';
export type BundlerOptions = Readonly<{
hasReducedPerformance?: boolean;
watch?: boolean;
}>;
declare class Bundler {
_depGraph: DependencyGraph;
_initializedPromise: Promise<void>;
_transformer: Transformer;
constructor(config: ConfigT, options?: BundlerOptions);
getWatcher(): EventEmitter;
end(): Promise<void>;
getDependencyGraph(): Promise<DependencyGraph>;
transformFile(
filePath: string,
transformOptions: TransformOptions,
fileBuffer?: Buffer,
): Promise<TransformResultWithSource>;
ready(): Promise<void>;
}
export default Bundler;

65
node_modules/metro/src/Bundler.js generated vendored Normal file
View File

@@ -0,0 +1,65 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _Transformer = _interopRequireDefault(
require("./DeltaBundler/Transformer"),
);
var _DependencyGraph = _interopRequireDefault(
require("./node-haste/DependencyGraph"),
);
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
class Bundler {
constructor(config, options) {
this._depGraph = new _DependencyGraph.default(config, options);
this._initializedPromise = this._depGraph
.ready()
.then(() => {
config.reporter.update({
type: "transformer_load_started",
});
this._transformer = new _Transformer.default(config, {
getOrComputeSha1: (filePath) =>
this._depGraph.getOrComputeSha1(filePath),
});
config.reporter.update({
type: "transformer_load_done",
});
})
.catch((error) => {
console.error("Failed to construct transformer: ", error);
config.reporter.update({
type: "transformer_load_failed",
error,
});
});
}
getWatcher() {
return this._depGraph.getWatcher();
}
async end() {
await this.ready();
await this._transformer.end();
await this._depGraph.end();
}
async getDependencyGraph() {
await this.ready();
return this._depGraph;
}
async transformFile(filePath, transformOptions, fileBuffer) {
await this.ready();
return this._transformer.transformFile(
filePath,
transformOptions,
fileBuffer,
);
}
async ready() {
await this._initializedPromise;
}
}
exports.default = Bundler;

90
node_modules/metro/src/Bundler.js.flow generated vendored Normal file
View File

@@ -0,0 +1,90 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {TransformResultWithSource} from './DeltaBundler';
import type {TransformOptions} from './DeltaBundler/Worker';
import type EventEmitter from 'events';
import type {ConfigT} from 'metro-config';
import Transformer from './DeltaBundler/Transformer';
import DependencyGraph from './node-haste/DependencyGraph';
export type BundlerOptions = Readonly<{
hasReducedPerformance?: boolean,
watch?: boolean,
}>;
export default class Bundler {
_depGraph: DependencyGraph;
_initializedPromise: Promise<void>;
_transformer: Transformer;
constructor(config: ConfigT, options?: BundlerOptions) {
this._depGraph = new DependencyGraph(config, options);
this._initializedPromise = this._depGraph
.ready()
.then(() => {
config.reporter.update({type: 'transformer_load_started'});
this._transformer = new Transformer(config, {
getOrComputeSha1: filePath =>
this._depGraph.getOrComputeSha1(filePath),
});
config.reporter.update({type: 'transformer_load_done'});
})
.catch(error => {
console.error('Failed to construct transformer: ', error);
config.reporter.update({
type: 'transformer_load_failed',
error,
});
});
}
getWatcher(): EventEmitter {
return this._depGraph.getWatcher();
}
async end(): Promise<void> {
await this.ready();
await this._transformer.end();
await this._depGraph.end();
}
async getDependencyGraph(): Promise<DependencyGraph> {
await this.ready();
return this._depGraph;
}
async transformFile(
filePath: string,
transformOptions: TransformOptions,
/** Optionally provide the file contents, this can be used to provide virtual contents for a file. */
fileBuffer?: Buffer,
): Promise<TransformResultWithSource<>> {
// We need to be sure that the DependencyGraph has been initialized.
// TODO: Remove this ugly hack!
await this.ready();
return this._transformer.transformFile(
filePath,
transformOptions,
fileBuffer,
);
}
// Waits for the bundler to become ready.
async ready(): Promise<void> {
await this._initializedPromise;
}
}

34
node_modules/metro/src/Bundler/util.d.ts generated vendored Normal file
View File

@@ -0,0 +1,34 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<adf0e819f93597b57ff231cca9c918ef>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/Bundler/util.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {AssetDataWithoutFiles} from '../Assets';
import type {ModuleTransportLike} from '../shared/types';
import type {File} from '@babel/types';
type SubTree<T extends ModuleTransportLike> = (
moduleTransport: T,
moduleTransportsByPath: Map<string, T>,
) => Iterable<number>;
export declare function generateAssetCodeFileAst(
assetRegistryPath: string,
assetDescriptor: AssetDataWithoutFiles,
): File;
export declare function createRamBundleGroups<T extends ModuleTransportLike>(
ramGroups: ReadonlyArray<string>,
groupableModules: ReadonlyArray<T>,
subtree: SubTree<T>,
): Map<number, Set<number>>;

122
node_modules/metro/src/Bundler/util.js generated vendored Normal file
View File

@@ -0,0 +1,122 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.createRamBundleGroups = createRamBundleGroups;
exports.generateAssetCodeFileAst = generateAssetCodeFileAst;
var babylon = _interopRequireWildcard(require("@babel/parser"));
var _template = _interopRequireDefault(require("@babel/template"));
var babelTypes = _interopRequireWildcard(require("@babel/types"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function _interopRequireWildcard(e, t) {
if ("function" == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ("object" != typeof e && "function" != typeof e))
return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
"default" !== t &&
{}.hasOwnProperty.call(e, t) &&
((i =
(o = Object.defineProperty) &&
Object.getOwnPropertyDescriptor(e, t)) &&
(i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
const assetPropertyBlockList = new Set(["files", "fileSystemLocation", "path"]);
function generateAssetCodeFileAst(assetRegistryPath, assetDescriptor) {
const properDescriptor = filterObject(
assetDescriptor,
assetPropertyBlockList,
);
const descriptorAst = babylon.parseExpression(
JSON.stringify(properDescriptor),
);
const t = babelTypes;
const buildRequire = _template.default.statement(`
module.exports = require(ASSET_REGISTRY_PATH).registerAsset(DESCRIPTOR_AST)
`);
return t.file(
t.program([
buildRequire({
ASSET_REGISTRY_PATH: t.stringLiteral(assetRegistryPath),
DESCRIPTOR_AST: descriptorAst,
}),
]),
);
}
function filterObject(object, blockList) {
const copied = {
...object,
};
for (const key of blockList) {
delete copied[key];
}
return copied;
}
function createRamBundleGroups(ramGroups, groupableModules, subtree) {
const byPath = new Map();
const byId = new Map();
groupableModules.forEach((m) => {
byPath.set(m.sourcePath, m);
byId.set(m.id, m.sourcePath);
});
const result = new Map(
ramGroups.map((modulePath) => {
const root = byPath.get(modulePath);
if (root == null) {
throw Error(`Group root ${modulePath} is not part of the bundle`);
}
return [root.id, new Set(subtree(root, byPath))];
}),
);
if (ramGroups.length > 1) {
const all = new ArrayMap();
for (const [parent, children] of result) {
for (const module of children) {
all.get(module).push(parent);
}
}
const doubles = filter(all, ([, parents]) => parents.length > 1);
for (const [moduleId, parents] of doubles) {
const parentNames = parents.map(byId.get, byId);
const lastName = parentNames.pop();
throw new Error(
`Module ${byId.get(moduleId) || moduleId} belongs to groups ${parentNames.join(", ")}, and ${String(lastName)}. Ensure that each module is only part of one group.`,
);
}
}
return result;
}
function* filter(iterator, predicate) {
for (const value of iterator) {
if (predicate(value)) {
yield value;
}
}
}
class ArrayMap extends Map {
get(key) {
let array = super.get(key);
if (!array) {
array = [];
this.set(key, array);
}
return array;
}
}

146
node_modules/metro/src/Bundler/util.js.flow generated vendored Normal file
View File

@@ -0,0 +1,146 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {AssetDataFiltered, AssetDataWithoutFiles} from '../Assets';
import type {ModuleTransportLike} from '../shared/types';
import type {File} from '@babel/types';
import * as babylon from '@babel/parser';
import template from '@babel/template';
import * as babelTypes from '@babel/types';
type SubTree<T extends ModuleTransportLike> = (
moduleTransport: T,
moduleTransportsByPath: Map<string, T>,
) => Iterable<number>;
const assetPropertyBlockList = new Set(['files', 'fileSystemLocation', 'path']);
export function generateAssetCodeFileAst(
assetRegistryPath: string,
assetDescriptor: AssetDataWithoutFiles,
): File {
const properDescriptor = filterObject(
assetDescriptor,
assetPropertyBlockList,
);
// {...}
const descriptorAst = babylon.parseExpression(
JSON.stringify(properDescriptor),
);
const t = babelTypes;
// require('AssetRegistry').registerAsset({...})
const buildRequire = template.statement(`
module.exports = require(ASSET_REGISTRY_PATH).registerAsset(DESCRIPTOR_AST)
`);
return t.file(
t.program([
buildRequire({
ASSET_REGISTRY_PATH: t.stringLiteral(assetRegistryPath),
DESCRIPTOR_AST: descriptorAst,
}),
]),
);
}
function filterObject(
object: AssetDataWithoutFiles,
blockList: Set<string>,
): AssetDataFiltered {
const copied = {...object};
for (const key of blockList) {
// $FlowFixMe[prop-missing]
delete copied[key];
}
return copied;
}
export function createRamBundleGroups<T extends ModuleTransportLike>(
ramGroups: ReadonlyArray<string>,
groupableModules: ReadonlyArray<T>,
subtree: SubTree<T>,
): Map<number, Set<number>> {
// build two maps that allow to lookup module data
// by path or (numeric) module id;
const byPath: Map<string, T> = new Map();
const byId: Map<number, string> = new Map();
groupableModules.forEach((m: T) => {
byPath.set(m.sourcePath, m);
byId.set(m.id, m.sourcePath);
});
// build a map of group root IDs to an array of module IDs in the group
const result: Map<number, Set<number>> = new Map(
ramGroups.map((modulePath: string) => {
const root = byPath.get(modulePath);
if (root == null) {
throw Error(`Group root ${modulePath} is not part of the bundle`);
}
return [
root.id,
// `subtree` yields the IDs of all transitive dependencies of a module
new Set(subtree(root, byPath)),
];
}),
);
if (ramGroups.length > 1) {
// build a map of all grouped module IDs to an array of group root IDs
const all = new ArrayMap<number, number>();
for (const [parent, children] of result) {
for (const module of children) {
all.get(module).push(parent);
}
}
// find all module IDs that are part of more than one group
const doubles = filter(all, ([, parents]) => parents.length > 1);
for (const [moduleId, parents] of doubles) {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
const parentNames = parents.map(byId.get, byId);
const lastName = parentNames.pop();
throw new Error(
`Module ${
byId.get(moduleId) || moduleId
} belongs to groups ${parentNames.join(', ')}, and ${String(
lastName,
)}. Ensure that each module is only part of one group.`,
);
}
}
return result;
}
function* filter<A extends number, B extends number>(
iterator: ArrayMap<A, B>,
predicate: ([A, Array<B>]) => boolean,
): Generator<[A, Array<B>], void, void> {
for (const value of iterator) {
if (predicate(value)) {
yield value;
}
}
}
class ArrayMap<K, V> extends Map<K, Array<V>> {
get(key: K): Array<V> {
let array = super.get(key);
if (!array) {
array = [];
this.set(key, array);
}
return array;
}
}

67
node_modules/metro/src/DeltaBundler.d.ts generated vendored Normal file
View File

@@ -0,0 +1,67 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<db564db653a99b07f3ad7585b2bed707>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {
DeltaResult,
Graph,
MixedOutput,
Options,
ReadOnlyGraph,
} from './DeltaBundler/types';
import type EventEmitter from 'events';
import DeltaCalculator from './DeltaBundler/DeltaCalculator';
export type {
DeltaResult,
Graph,
Dependencies,
MixedOutput,
Module,
ReadOnlyGraph,
TransformFn,
TransformResult,
TransformResultDependency,
TransformResultWithSource,
} from './DeltaBundler/types';
/**
* `DeltaBundler` uses the `DeltaTransformer` to build bundle deltas. This
* module handles all the transformer instances so it can support multiple
* concurrent clients requesting their own deltas. This is done through the
* `clientId` param (which maps a client to a specific delta transformer).
*/
declare class DeltaBundler<T = MixedOutput> {
_changeEventSource: EventEmitter;
_deltaCalculators: Map<Graph<T>, DeltaCalculator<T>>;
constructor(changeEventSource: EventEmitter);
end(): void;
getDependencies(
entryPoints: ReadonlyArray<string>,
options: Options<T>,
): Promise<ReadOnlyGraph<T>['dependencies']>;
buildGraph(
entryPoints: ReadonlyArray<string>,
options: Options<T>,
): Promise<Graph<T>>;
getDelta(
graph: Graph<T>,
$$PARAM_1$$: {reset: boolean; shallow: boolean},
): Promise<DeltaResult<T>>;
listen(graph: Graph<T>, callback: () => Promise<void>): () => void;
endGraph(graph: Graph<T>): void;
}
export default DeltaBundler;

79
node_modules/metro/src/DeltaBundler.js generated vendored Normal file
View File

@@ -0,0 +1,79 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _DeltaCalculator = _interopRequireDefault(
require("./DeltaBundler/DeltaCalculator"),
);
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
class DeltaBundler {
_deltaCalculators = new Map();
constructor(changeEventSource) {
this._changeEventSource = changeEventSource;
}
end() {
this._deltaCalculators.forEach((deltaCalculator) => deltaCalculator.end());
this._deltaCalculators = new Map();
}
async getDependencies(entryPoints, options) {
const deltaCalculator = new _DeltaCalculator.default(
new Set(entryPoints),
this._changeEventSource,
options,
);
await deltaCalculator.getDelta({
reset: true,
shallow: options.shallow,
});
const graph = deltaCalculator.getGraph();
deltaCalculator.end();
return graph.dependencies;
}
async buildGraph(entryPoints, options) {
const deltaCalculator = new _DeltaCalculator.default(
new Set(entryPoints),
this._changeEventSource,
options,
);
await deltaCalculator.getDelta({
reset: true,
shallow: options.shallow,
});
const graph = deltaCalculator.getGraph();
this._deltaCalculators.set(graph, deltaCalculator);
return graph;
}
async getDelta(graph, { reset, shallow }) {
const deltaCalculator = this._deltaCalculators.get(graph);
if (!deltaCalculator) {
throw new Error("Graph not found");
}
return await deltaCalculator.getDelta({
reset,
shallow,
});
}
listen(graph, callback) {
const deltaCalculator = this._deltaCalculators.get(graph);
if (!deltaCalculator) {
throw new Error("Graph not found");
}
deltaCalculator.on("change", callback);
return () => {
deltaCalculator.removeListener("change", callback);
};
}
endGraph(graph) {
const deltaCalculator = this._deltaCalculators.get(graph);
if (!deltaCalculator) {
throw new Error("Graph not found");
}
deltaCalculator.end();
this._deltaCalculators.delete(graph);
}
}
exports.default = DeltaBundler;

140
node_modules/metro/src/DeltaBundler.js.flow generated vendored Normal file
View File

@@ -0,0 +1,140 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {
DeltaResult,
Graph,
// eslint-disable-next-line no-unused-vars
MixedOutput,
Options,
ReadOnlyGraph,
} from './DeltaBundler/types';
import type EventEmitter from 'events';
import DeltaCalculator from './DeltaBundler/DeltaCalculator';
export type {
DeltaResult,
Graph,
Dependencies,
MixedOutput,
Module,
ReadOnlyGraph,
TransformFn,
TransformResult,
TransformResultDependency,
TransformResultWithSource,
} from './DeltaBundler/types';
/**
* `DeltaBundler` uses the `DeltaTransformer` to build bundle deltas. This
* module handles all the transformer instances so it can support multiple
* concurrent clients requesting their own deltas. This is done through the
* `clientId` param (which maps a client to a specific delta transformer).
*/
export default class DeltaBundler<T = MixedOutput> {
_changeEventSource: EventEmitter;
_deltaCalculators: Map<Graph<T>, DeltaCalculator<T>> = new Map();
constructor(changeEventSource: EventEmitter) {
this._changeEventSource = changeEventSource;
}
end(): void {
this._deltaCalculators.forEach((deltaCalculator: DeltaCalculator<T>) =>
deltaCalculator.end(),
);
this._deltaCalculators = new Map();
}
async getDependencies(
entryPoints: ReadonlyArray<string>,
options: Options<T>,
): Promise<ReadOnlyGraph<T>['dependencies']> {
const deltaCalculator = new DeltaCalculator(
new Set(entryPoints),
this._changeEventSource,
options,
);
await deltaCalculator.getDelta({reset: true, shallow: options.shallow});
const graph = deltaCalculator.getGraph();
deltaCalculator.end();
return graph.dependencies;
}
// Note: the graph returned by this function needs to be ended when finished
// so that we don't leak graphs that are not reachable.
// To get just the dependencies, use getDependencies which will not leak graphs.
async buildGraph(
entryPoints: ReadonlyArray<string>,
options: Options<T>,
): Promise<Graph<T>> {
const deltaCalculator = new DeltaCalculator(
new Set(entryPoints),
this._changeEventSource,
options,
);
await deltaCalculator.getDelta({reset: true, shallow: options.shallow});
const graph = deltaCalculator.getGraph();
this._deltaCalculators.set(graph, deltaCalculator);
return graph;
}
async getDelta(
graph: Graph<T>,
{
reset,
shallow,
}: {
reset: boolean,
shallow: boolean,
...
},
): Promise<DeltaResult<T>> {
const deltaCalculator = this._deltaCalculators.get(graph);
if (!deltaCalculator) {
throw new Error('Graph not found');
}
return await deltaCalculator.getDelta({reset, shallow});
}
listen(graph: Graph<T>, callback: () => Promise<void>): () => void {
const deltaCalculator = this._deltaCalculators.get(graph);
if (!deltaCalculator) {
throw new Error('Graph not found');
}
deltaCalculator.on('change', callback);
return () => {
deltaCalculator.removeListener('change', callback);
};
}
endGraph(graph: Graph<T>): void {
const deltaCalculator = this._deltaCalculators.get(graph);
if (!deltaCalculator) {
throw new Error('Graph not found');
}
deltaCalculator.end();
this._deltaCalculators.delete(graph);
}
}

View File

@@ -0,0 +1,68 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<d06b53dd09157df95aeb941035d4ebf0>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/DeltaCalculator.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {DeltaResult, Options} from './types';
import type {ChangeEvent} from 'metro-file-map';
import {Graph} from './Graph';
import EventEmitter from 'events';
/**
* This class is in charge of calculating the delta of changed modules that
* happen between calls. To do so, it subscribes to file changes, so it can
* traverse the files that have been changed between calls and avoid having to
* traverse the whole dependency tree for trivial small changes.
*/
declare class DeltaCalculator<T> extends EventEmitter {
_changeEventSource: EventEmitter;
_options: Options<T>;
_currentBuildPromise: null | undefined | Promise<DeltaResult<T>>;
_deletedFiles: Set<string>;
_modifiedFiles: Set<string>;
_addedFiles: Set<string>;
_requiresReset: boolean;
_graph: Graph<T>;
constructor(
entryPoints: ReadonlySet<string>,
changeEventSource: EventEmitter,
options: Options<T>,
);
/**
* Stops listening for file changes and clears all the caches.
*/
end(): void;
/**
* Main method to calculate the delta of modules. It returns a DeltaResult,
* which contain the modified/added modules and the removed modules.
*/
getDelta($$PARAM_0$$: {
reset: boolean;
shallow: boolean;
}): Promise<DeltaResult<T>>;
/**
* Returns the graph with all the dependencies. Each module contains the
* needed information to do the traversing (dependencies, inverseDependencies)
* plus some metadata.
*/
getGraph(): Graph<T>;
_handleMultipleFileChanges: (changeEvent: ChangeEvent) => void;
_getChangedDependencies(
modifiedFiles: Set<string>,
deletedFiles: Set<string>,
addedFiles: Set<string>,
): Promise<DeltaResult<T>>;
}
export default DeltaCalculator;

218
node_modules/metro/src/DeltaBundler/DeltaCalculator.js generated vendored Normal file
View File

@@ -0,0 +1,218 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _Graph = require("./Graph");
var _crypto = _interopRequireDefault(require("crypto"));
var _events = _interopRequireDefault(require("events"));
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const debug = require("debug")("Metro:DeltaCalculator");
const changeEventIds = new WeakMap();
class DeltaCalculator extends _events.default {
_deletedFiles = new Set();
_modifiedFiles = new Set();
_addedFiles = new Set();
_requiresReset = false;
constructor(entryPoints, changeEventSource, options) {
super();
this._options = options;
this._changeEventSource = changeEventSource;
this._graph = new _Graph.Graph({
entryPoints,
transformOptions: this._options.transformOptions,
});
this._changeEventSource.on("change", this._handleMultipleFileChanges);
}
end() {
this._changeEventSource.removeListener(
"change",
this._handleMultipleFileChanges,
);
this.removeAllListeners();
this._graph = new _Graph.Graph({
entryPoints: this._graph.entryPoints,
transformOptions: this._options.transformOptions,
});
this._modifiedFiles = new Set();
this._deletedFiles = new Set();
this._addedFiles = new Set();
}
async getDelta({ reset, shallow }) {
debug("Calculating delta (reset: %s, shallow: %s)", reset, shallow);
if (this._currentBuildPromise) {
await this._currentBuildPromise;
}
const modifiedFiles = this._modifiedFiles;
this._modifiedFiles = new Set();
const deletedFiles = this._deletedFiles;
this._deletedFiles = new Set();
const addedFiles = this._addedFiles;
this._addedFiles = new Set();
const requiresReset = this._requiresReset;
this._requiresReset = false;
if (requiresReset) {
const markModified = (file) => {
if (!addedFiles.has(file) && !deletedFiles.has(file)) {
modifiedFiles.add(file);
}
};
this._graph.dependencies.forEach((_, key) => markModified(key));
this._graph.entryPoints.forEach(markModified);
}
this._currentBuildPromise = this._getChangedDependencies(
modifiedFiles,
deletedFiles,
addedFiles,
);
let result;
try {
result = await this._currentBuildPromise;
} catch (error) {
modifiedFiles.forEach((file) => this._modifiedFiles.add(file));
deletedFiles.forEach((file) => this._deletedFiles.add(file));
addedFiles.forEach((file) => this._addedFiles.add(file));
throw error;
} finally {
this._currentBuildPromise = null;
}
if (reset) {
this._graph.reorderGraph({
shallow,
});
return {
added: this._graph.dependencies,
deleted: new Set(),
modified: new Map(),
reset: true,
};
}
return result;
}
getGraph() {
return this._graph;
}
#shouldReset(canonicalPath, metadata) {
if (metadata.isSymlink) {
return true;
}
if (
this._options.unstable_enablePackageExports &&
(canonicalPath === "package.json" ||
canonicalPath.endsWith(_path.default.sep + "package.json"))
) {
return true;
}
return false;
}
_handleMultipleFileChanges = (changeEvent) => {
const { changes, logger, rootDir } = changeEvent;
for (const [canonicalPath, metadata] of changes.addedFiles) {
debug("Handling add: %s", canonicalPath);
if (this.#shouldReset(canonicalPath, metadata)) {
this._requiresReset = true;
}
const absolutePath = _path.default.join(rootDir, canonicalPath);
if (this._deletedFiles.has(absolutePath)) {
this._deletedFiles.delete(absolutePath);
this._modifiedFiles.add(absolutePath);
} else {
this._addedFiles.add(absolutePath);
this._modifiedFiles.delete(absolutePath);
}
}
for (const [canonicalPath, metadata] of changes.modifiedFiles) {
debug("Handling change: %s", canonicalPath);
if (this.#shouldReset(canonicalPath, metadata)) {
this._requiresReset = true;
}
const absolutePath = _path.default.join(rootDir, canonicalPath);
if (!this._addedFiles.has(absolutePath)) {
this._modifiedFiles.add(absolutePath);
}
this._deletedFiles.delete(absolutePath);
}
for (const [canonicalPath, metadata] of changes.removedFiles) {
debug("Handling delete: %s", canonicalPath);
if (this.#shouldReset(canonicalPath, metadata)) {
this._requiresReset = true;
}
const absolutePath = _path.default.resolve(rootDir, canonicalPath);
if (this._addedFiles.has(absolutePath)) {
this._addedFiles.delete(absolutePath);
} else {
this._deletedFiles.add(absolutePath);
this._modifiedFiles.delete(absolutePath);
}
}
let changeId = changeEventIds.get(changeEvent);
if (changeId == null) {
changeId = _crypto.default.randomUUID();
changeEventIds.set(changeEvent, changeId);
}
this.emit("change", {
logger,
changeId,
});
};
async _getChangedDependencies(modifiedFiles, deletedFiles, addedFiles) {
if (!this._graph.dependencies.size) {
const { added } = await this._graph.initialTraverseDependencies(
this._options,
);
return {
added,
deleted: new Set(),
modified: new Map(),
reset: true,
};
}
deletedFiles.forEach((filePath) => {
for (const modifiedModulePath of this._graph.getModifiedModulesForDeletedPath(
filePath,
)) {
if (!deletedFiles.has(modifiedModulePath)) {
modifiedFiles.add(modifiedModulePath);
}
}
});
if (this._options.unstable_allowRequireContext) {
addedFiles.forEach((filePath) => {
this._graph.markModifiedContextModules(filePath, modifiedFiles);
});
}
const modifiedDependencies = Array.from(modifiedFiles).filter((filePath) =>
this._graph.dependencies.has(filePath),
);
if (modifiedDependencies.length === 0) {
return {
added: new Map(),
deleted: new Set(),
modified: new Map(),
reset: false,
};
}
debug("Traversing dependencies for %s paths", modifiedDependencies.length);
const { added, modified, deleted } = await this._graph.traverseDependencies(
modifiedDependencies,
this._options,
);
debug(
"Calculated graph delta {added: %s, modified: %d, deleted: %d}",
added.size,
modified.size,
deleted.size,
);
return {
added,
deleted,
modified,
reset: false,
};
}
}
exports.default = DeltaCalculator;

View File

@@ -0,0 +1,336 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {DeltaResult, Options} from './types';
import type {ChangeEvent} from 'metro-file-map';
import {Graph} from './Graph';
import crypto from 'crypto';
import EventEmitter from 'events';
import path from 'path';
// eslint-disable-next-line import/no-commonjs
const debug = require('debug')('Metro:DeltaCalculator');
/**
* Assigns a unique, stable `changeId` to each `ChangeEvent` from the file
* watcher. Since all `DeltaCalculator` instances share the same
* `ChangeEvent` object reference per file system change, the `WeakMap`
* ensures each gets the same `changeId`.
*/
const changeEventIds: WeakMap<ChangeEvent, string> = new WeakMap();
/**
* This class is in charge of calculating the delta of changed modules that
* happen between calls. To do so, it subscribes to file changes, so it can
* traverse the files that have been changed between calls and avoid having to
* traverse the whole dependency tree for trivial small changes.
*/
export default class DeltaCalculator<T> extends EventEmitter {
_changeEventSource: EventEmitter;
_options: Options<T>;
_currentBuildPromise: ?Promise<DeltaResult<T>>;
_deletedFiles: Set<string> = new Set();
_modifiedFiles: Set<string> = new Set();
_addedFiles: Set<string> = new Set();
_requiresReset: boolean = false;
_graph: Graph<T>;
constructor(
entryPoints: ReadonlySet<string>,
changeEventSource: EventEmitter,
options: Options<T>,
) {
super();
this._options = options;
this._changeEventSource = changeEventSource;
this._graph = new Graph({
entryPoints,
transformOptions: this._options.transformOptions,
});
this._changeEventSource.on('change', this._handleMultipleFileChanges);
}
/**
* Stops listening for file changes and clears all the caches.
*/
end(): void {
this._changeEventSource.removeListener(
'change',
this._handleMultipleFileChanges,
);
this.removeAllListeners();
// Clean up all the cache data structures to deallocate memory.
this._graph = new Graph({
entryPoints: this._graph.entryPoints,
transformOptions: this._options.transformOptions,
});
this._modifiedFiles = new Set();
this._deletedFiles = new Set();
this._addedFiles = new Set();
}
/**
* Main method to calculate the delta of modules. It returns a DeltaResult,
* which contain the modified/added modules and the removed modules.
*/
async getDelta({
reset,
shallow,
}: {
reset: boolean,
shallow: boolean,
...
}): Promise<DeltaResult<T>> {
debug('Calculating delta (reset: %s, shallow: %s)', reset, shallow);
// If there is already a build in progress, wait until it finish to start
// processing a new one (delta server doesn't support concurrent builds).
if (this._currentBuildPromise) {
await this._currentBuildPromise;
}
// We don't want the modified files Set to be modified while building the
// bundle, so we isolate them by using the current instance for the bundling
// and creating a new instance for the file watcher.
const modifiedFiles = this._modifiedFiles;
this._modifiedFiles = new Set();
const deletedFiles = this._deletedFiles;
this._deletedFiles = new Set();
const addedFiles = this._addedFiles;
this._addedFiles = new Set();
const requiresReset = this._requiresReset;
this._requiresReset = false;
// Revisit all files if changes require a graph reset - resolutions may be
// invalidated but we don't yet know which. This should be optimized in the
// future.
if (requiresReset) {
const markModified = (file: string) => {
if (!addedFiles.has(file) && !deletedFiles.has(file)) {
modifiedFiles.add(file);
}
};
this._graph.dependencies.forEach((_, key) => markModified(key));
this._graph.entryPoints.forEach(markModified);
}
// Concurrent requests should reuse the same bundling process. To do so,
// this method stores the promise as an instance variable, and then it's
// removed after it gets resolved.
this._currentBuildPromise = this._getChangedDependencies(
modifiedFiles,
deletedFiles,
addedFiles,
);
let result;
try {
result = await this._currentBuildPromise;
} catch (error) {
// In case of error, we don't want to mark the modified files as
// processed (since we haven't actually created any delta). If we do not
// do so, asking for a delta after an error will produce an empty Delta,
// which is not correct.
modifiedFiles.forEach((file: string) => this._modifiedFiles.add(file));
deletedFiles.forEach((file: string) => this._deletedFiles.add(file));
addedFiles.forEach((file: string) => this._addedFiles.add(file));
throw error;
} finally {
this._currentBuildPromise = null;
}
// Return all the modules if the client requested a reset delta.
if (reset) {
this._graph.reorderGraph({shallow});
return {
added: this._graph.dependencies,
deleted: new Set(),
modified: new Map(),
reset: true,
};
}
return result;
}
/**
* Returns the graph with all the dependencies. Each module contains the
* needed information to do the traversing (dependencies, inverseDependencies)
* plus some metadata.
*/
getGraph(): Graph<T> {
return this._graph;
}
#shouldReset(
canonicalPath: string,
metadata: {+isSymlink: boolean, ...},
): boolean {
if (metadata.isSymlink) {
return true;
}
if (
this._options.unstable_enablePackageExports &&
(canonicalPath === 'package.json' ||
canonicalPath.endsWith(path.sep + 'package.json'))
) {
return true;
}
return false;
}
_handleMultipleFileChanges = (changeEvent: ChangeEvent) => {
const {changes, logger, rootDir} = changeEvent;
// Process added files: deleted+added = modified, otherwise added
for (const [canonicalPath, metadata] of changes.addedFiles) {
debug('Handling add: %s', canonicalPath);
if (this.#shouldReset(canonicalPath, metadata)) {
this._requiresReset = true;
}
const absolutePath = path.join(rootDir, canonicalPath);
if (this._deletedFiles.has(absolutePath)) {
this._deletedFiles.delete(absolutePath);
this._modifiedFiles.add(absolutePath);
} else {
this._addedFiles.add(absolutePath);
this._modifiedFiles.delete(absolutePath);
}
}
// Process modified files: added+modified stays added, otherwise modified
for (const [canonicalPath, metadata] of changes.modifiedFiles) {
debug('Handling change: %s', canonicalPath);
if (this.#shouldReset(canonicalPath, metadata)) {
this._requiresReset = true;
}
const absolutePath = path.join(rootDir, canonicalPath);
if (!this._addedFiles.has(absolutePath)) {
this._modifiedFiles.add(absolutePath);
}
this._deletedFiles.delete(absolutePath);
}
// Process removed files: added+deleted = no change, otherwise deleted
for (const [canonicalPath, metadata] of changes.removedFiles) {
debug('Handling delete: %s', canonicalPath);
if (this.#shouldReset(canonicalPath, metadata)) {
this._requiresReset = true;
}
const absolutePath = path.resolve(rootDir, canonicalPath);
if (this._addedFiles.has(absolutePath)) {
this._addedFiles.delete(absolutePath);
} else {
this._deletedFiles.add(absolutePath);
this._modifiedFiles.delete(absolutePath);
}
}
let changeId = changeEventIds.get(changeEvent);
if (changeId == null) {
changeId = crypto.randomUUID();
changeEventIds.set(changeEvent, changeId);
}
this.emit('change', {logger, changeId});
};
async _getChangedDependencies(
modifiedFiles: Set<string>,
deletedFiles: Set<string>,
addedFiles: Set<string>,
): Promise<DeltaResult<T>> {
if (!this._graph.dependencies.size) {
const {added} = await this._graph.initialTraverseDependencies(
this._options,
);
return {
added,
deleted: new Set(),
modified: new Map(),
reset: true,
};
}
// If a file has been deleted, we want to invalidate any other file that
// depends on it, so we can process it and correctly return an error.
deletedFiles.forEach((filePath: string) => {
for (const modifiedModulePath of this._graph.getModifiedModulesForDeletedPath(
filePath,
)) {
// Only mark the inverse dependency as modified if it's not already
// marked as deleted (in that case we can just ignore it).
if (!deletedFiles.has(modifiedModulePath)) {
modifiedFiles.add(modifiedModulePath);
}
}
});
// NOTE(EvanBacon): This check adds extra complexity so we feature gate it
// to enable users to opt out.
if (this._options.unstable_allowRequireContext) {
// Check if any added or removed files are matched in a context module.
// We only need to do this for added files because (1) deleted files will have a context
// module as an inverse dependency, (2) modified files don't invalidate the contents
// of the context module.
addedFiles.forEach(filePath => {
this._graph.markModifiedContextModules(filePath, modifiedFiles);
});
}
// We only want to process files that are in the bundle.
const modifiedDependencies = Array.from(modifiedFiles).filter(
(filePath: string) => this._graph.dependencies.has(filePath),
);
// No changes happened. Return empty delta.
if (modifiedDependencies.length === 0) {
return {
added: new Map(),
deleted: new Set(),
modified: new Map(),
reset: false,
};
}
debug('Traversing dependencies for %s paths', modifiedDependencies.length);
const {added, modified, deleted} = await this._graph.traverseDependencies(
modifiedDependencies,
this._options,
);
debug(
'Calculated graph delta {added: %s, modified: %d, deleted: %d}',
added.size,
modified.size,
deleted.size,
);
return {
added,
deleted,
modified,
reset: false,
};
}
}

174
node_modules/metro/src/DeltaBundler/Graph.d.ts generated vendored Normal file
View File

@@ -0,0 +1,174 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<2af7aa3b61c2afa4d8794e127666c226>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Graph.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
/**
* Portions of this code are based on the Synchronous Cycle Collection
* algorithm described in:
*
* David F. Bacon and V. T. Rajan. 2001. Concurrent Cycle Collection in
* Reference Counted Systems. In Proceedings of the 15th European Conference on
* Object-Oriented Programming (ECOOP '01). Springer-Verlag, Berlin,
* Heidelberg, 207235.
*
* Notable differences from the algorithm in the paper:
* 1. Our implementation uses the inverseDependencies set (which we already
* have to maintain) instead of a separate refcount variable. A module's
* reference count is equal to the size of its inverseDependencies set, plus
* 1 if it's an entry point of the graph.
* 2. We keep the "root buffer" (possibleCycleRoots) free of duplicates by
* making it a Set, instead of storing a "buffered" flag on each node.
* 3. On top of tracking edges between nodes, we also count references between
* nodes and entries in the importBundleNodes set.
*/
import type {RequireContext} from '../lib/contextModule';
import type {
Dependencies,
Dependency,
GraphInputOptions,
MixedOutput,
Module,
ModuleData,
Options,
ResolvedDependency,
TransformInputOptions,
} from './types';
import CountingSet from '../lib/CountingSet';
export type Result<T> = {
added: Map<string, Module<T>>;
modified: Map<string, Module<T>>;
deleted: Set<string>;
};
type Delta<T> = Readonly<{
added: Set<string>;
touched: Set<string>;
deleted: Set<string>;
updatedModuleData: ReadonlyMap<string, ModuleData<T>>;
baseModuleData: Map<string, ModuleData<T>>;
errors: ReadonlyMap<string, Error>;
}>;
type InternalOptions<T> = Readonly<{
lazy: boolean;
onDependencyAdd: () => unknown;
onDependencyAdded: () => unknown;
resolve: Options<T>['resolve'];
transform: Options<T>['transform'];
shallow: boolean;
}>;
export declare class Graph<T = MixedOutput> {
readonly entryPoints: ReadonlySet<string>;
readonly transformOptions: TransformInputOptions;
readonly dependencies: Dependencies<T>;
constructor(options: GraphInputOptions);
/**
* Dependency Traversal logic for the Delta Bundler. This method calculates
* the modules that should be included in the bundle by traversing the
* dependency graph.
* Instead of traversing the whole graph each time, it just calculates the
* difference between runs by only traversing the added/removed dependencies.
* To do so, it uses the passed graph dependencies and it mutates it.
* The paths parameter contains the absolute paths of the root files that the
* method should traverse. Normally, these paths should be the modified files
* since the last traversal.
*/
traverseDependencies(
paths: ReadonlyArray<string>,
options: Options<T>,
): Promise<Result<T>>;
initialTraverseDependencies(options: Options<T>): Promise<Result<T>>;
_buildDelta(
pathsToVisit: ReadonlySet<string>,
options: InternalOptions<T>,
moduleFilter?: (path: string) => boolean,
): Promise<Delta<T>>;
_recursivelyCommitModule(
path: string,
delta: Delta<T>,
options: InternalOptions<T>,
commitOptions?: Readonly<{onlyRemove: boolean}>,
): Module<T>;
_addDependency(
parentModule: Module<T>,
key: string,
dependency: Dependency,
requireContext: null | undefined | RequireContext,
delta: Delta<T>,
options: InternalOptions<T>,
): void;
_removeDependency(
parentModule: Module<T>,
key: string,
dependency: Dependency,
delta: Delta<T>,
options: InternalOptions<T>,
): void;
/**
* Collect a list of context modules which include a given file.
*/
markModifiedContextModules(
filePath: string,
modifiedPaths: Set<string> | CountingSet<string>,
): void;
/**
* Gets the list of modules affected by the deletion of a given file. The
* caller is expected to mark these modules as modified in the next call to
* traverseDependencies. Note that the list may contain duplicates.
*/
getModifiedModulesForDeletedPath(filePath: string): Iterable<string>;
/**
* Re-traverse the dependency graph in DFS order to reorder the modules and
* guarantee the same order between runs. This method mutates the passed graph.
*/
reorderGraph(options: {shallow: boolean}): void;
_reorderDependencies(
module: Module<T>,
orderedDependencies: Map<string, Module<T>>,
options: {shallow: boolean},
): void;
/** Garbage collection functions */
_incrementImportBundleReference(
dependency: ResolvedDependency,
parentModule: Module<T>,
): void;
_decrementImportBundleReference(
dependency: ResolvedDependency,
parentModule: Module<T>,
): void;
_markModuleInUse(module: Module<T>): void;
_children(
module: Module<T>,
options: InternalOptions<T>,
): Iterator<Module<T>>;
_moduleSnapshot(module: Module<T>): ModuleData<T>;
_releaseModule(
module: Module<T>,
delta: Delta<T>,
options: InternalOptions<T>,
): void;
_freeModule(module: Module<T>, delta: Delta<T>): void;
_markAsPossibleCycleRoot(module: Module<T>): void;
_collectCycles(delta: Delta<T>, options: InternalOptions<T>): void;
_markGray(module: Module<T>, options: InternalOptions<T>): void;
_scan(module: Module<T>, options: InternalOptions<T>): void;
_scanBlack(module: Module<T>, options: InternalOptions<T>): void;
_collectWhite(module: Module<T>, delta: Delta<T>): void;
/** End of garbage collection functions */
}

612
node_modules/metro/src/DeltaBundler/Graph.js generated vendored Normal file
View File

@@ -0,0 +1,612 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.Graph = void 0;
var _contextModule = require("../lib/contextModule");
var _CountingSet = _interopRequireDefault(require("../lib/CountingSet"));
var _isResolvedDependency = require("../lib/isResolvedDependency");
var _buildSubgraph = require("./buildSubgraph");
var _invariant = _interopRequireDefault(require("invariant"));
var _nullthrows = _interopRequireDefault(require("nullthrows"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function getInternalOptions({ transform, resolve, onProgress, lazy, shallow }) {
let numProcessed = 0;
let total = 0;
return {
lazy,
onDependencyAdd: () => onProgress && onProgress(numProcessed, ++total),
onDependencyAdded: () => onProgress && onProgress(++numProcessed, total),
resolve,
shallow,
transform,
};
}
function isWeakOrLazy(dependency, options) {
const asyncType = dependency.data.data.asyncType;
return asyncType === "weak" || (asyncType != null && options.lazy);
}
class Graph {
dependencies = new Map();
#importBundleNodes = new Map();
#gc = {
color: new Map(),
possibleCycleRoots: new Set(),
};
#resolvedContexts = new Map();
constructor(options) {
this.entryPoints = options.entryPoints;
this.transformOptions = options.transformOptions;
}
async traverseDependencies(paths, options) {
const internalOptions = getInternalOptions(options);
const modifiedPathsInBaseGraph = new Set(
paths.filter((path) => this.dependencies.has(path)),
);
const allModifiedPaths = new Set(paths);
const delta = await this._buildDelta(
modifiedPathsInBaseGraph,
internalOptions,
(absolutePath) =>
!this.dependencies.has(absolutePath) ||
allModifiedPaths.has(absolutePath),
);
if (delta.errors.size > 0) {
for (const modified of modifiedPathsInBaseGraph) {
delta.baseModuleData.set(
modified,
this._moduleSnapshot(
(0, _nullthrows.default)(this.dependencies.get(modified)),
),
);
}
}
for (const modified of modifiedPathsInBaseGraph) {
if (delta.errors.has(modified)) {
continue;
}
const module = this.dependencies.get(modified);
if (module == null) {
continue;
}
this._recursivelyCommitModule(modified, delta, internalOptions, {
onlyRemove: true,
});
}
this._collectCycles(delta, internalOptions);
try {
for (const modified of modifiedPathsInBaseGraph) {
const module = this.dependencies.get(modified);
if (module == null) {
continue;
}
this._recursivelyCommitModule(modified, delta, internalOptions);
}
} catch (error) {
const rollbackDelta = {
added: delta.added,
baseModuleData: new Map(),
deleted: delta.deleted,
errors: new Map(),
touched: new Set(),
updatedModuleData: delta.baseModuleData,
};
for (const modified of modifiedPathsInBaseGraph) {
const module = this.dependencies.get(modified);
if (module == null) {
continue;
}
this._recursivelyCommitModule(modified, rollbackDelta, internalOptions);
}
this._collectCycles(delta, internalOptions);
(0, _invariant.default)(
rollbackDelta.added.size === 0 && rollbackDelta.deleted.size === 0,
"attempted to roll back a graph commit but there were still changes",
);
throw error;
}
const added = new Map();
for (const path of delta.added) {
added.set(path, (0, _nullthrows.default)(this.dependencies.get(path)));
}
const modified = new Map();
for (const path of modifiedPathsInBaseGraph) {
if (
delta.touched.has(path) &&
!delta.deleted.has(path) &&
!delta.added.has(path)
) {
modified.set(
path,
(0, _nullthrows.default)(this.dependencies.get(path)),
);
}
}
return {
added,
deleted: delta.deleted,
modified,
};
}
async initialTraverseDependencies(options) {
const internalOptions = getInternalOptions(options);
(0, _invariant.default)(
this.dependencies.size === 0,
"initialTraverseDependencies called on nonempty graph",
);
this.#gc.color.clear();
this.#gc.possibleCycleRoots.clear();
this.#importBundleNodes.clear();
for (const path of this.entryPoints) {
this.#gc.color.set(path, "black");
}
const delta = await this._buildDelta(this.entryPoints, internalOptions);
if (delta.errors.size > 0) {
throw delta.errors.values().next().value;
}
for (const path of this.entryPoints) {
this._recursivelyCommitModule(path, delta, internalOptions);
}
this.reorderGraph({
shallow: options.shallow,
});
return {
added: this.dependencies,
deleted: new Set(),
modified: new Map(),
};
}
async _buildDelta(pathsToVisit, options, moduleFilter) {
const subGraph = await (0, _buildSubgraph.buildSubgraph)(
pathsToVisit,
this.#resolvedContexts,
{
resolve: options.resolve,
shouldTraverse: (dependency) => {
if (options.shallow || isWeakOrLazy(dependency, options)) {
return false;
}
return moduleFilter == null || moduleFilter(dependency.absolutePath);
},
transform: async (absolutePath, requireContext) => {
options.onDependencyAdd();
const result = await options.transform(absolutePath, requireContext);
options.onDependencyAdded();
return result;
},
},
);
return {
added: new Set(),
baseModuleData: new Map(),
deleted: new Set(),
errors: subGraph.errors,
touched: new Set(),
updatedModuleData: subGraph.moduleData,
};
}
_recursivelyCommitModule(
path,
delta,
options,
commitOptions = {
onlyRemove: false,
},
) {
if (delta.errors.has(path)) {
throw delta.errors.get(path);
}
const previousModule = this.dependencies.get(path);
const currentModule = (0, _nullthrows.default)(
delta.updatedModuleData.get(path) ?? delta.baseModuleData.get(path),
);
const previousDependencies = previousModule?.dependencies ?? new Map();
const {
dependencies: currentDependencies,
resolvedContexts,
...transformResult
} = currentModule;
const nextModule = {
...(previousModule ?? {
inverseDependencies: new _CountingSet.default(),
path,
}),
...transformResult,
dependencies: new Map(previousDependencies),
};
this.dependencies.set(nextModule.path, nextModule);
if (previousModule == null) {
if (delta.deleted.has(path)) {
delta.deleted.delete(path);
} else {
delta.added.add(path);
}
}
let dependenciesRemoved = false;
for (const [key, prevDependency] of previousDependencies) {
const curDependency = currentDependencies.get(key);
if (
!curDependency ||
!dependenciesEqual(prevDependency, curDependency, options)
) {
dependenciesRemoved = true;
this._removeDependency(nextModule, key, prevDependency, delta, options);
}
}
let dependenciesAdded = false;
if (!commitOptions.onlyRemove) {
for (const [key, curDependency] of currentDependencies) {
const prevDependency = previousDependencies.get(key);
if (
!prevDependency ||
!dependenciesEqual(prevDependency, curDependency, options)
) {
dependenciesAdded = true;
this._addDependency(
nextModule,
key,
curDependency,
resolvedContexts.get(key),
delta,
options,
);
}
}
}
const previousDependencyKeys = [...previousDependencies.keys()];
const dependencyKeysChangedOrReordered =
currentDependencies.size !== previousDependencies.size ||
[...currentDependencies.keys()].some(
(currentKey, index) => currentKey !== previousDependencyKeys[index],
);
if (
previousModule != null &&
!transformOutputMayDiffer(previousModule, nextModule) &&
!dependenciesRemoved &&
!dependenciesAdded &&
!dependencyKeysChangedOrReordered
) {
this.dependencies.set(previousModule.path, previousModule);
return previousModule;
}
delta.touched.add(path);
if (commitOptions.onlyRemove) {
return nextModule;
}
(0, _invariant.default)(
nextModule.dependencies.size === currentDependencies.size,
"Failed to add the correct dependencies",
);
nextModule.dependencies = new Map(currentDependencies);
return nextModule;
}
_addDependency(
parentModule,
key,
dependency,
requireContext,
delta,
options,
) {
if (options.shallow) {
} else if (!(0, _isResolvedDependency.isResolvedDependency)(dependency)) {
} else if (dependency.data.data.asyncType === "weak") {
} else if (options.lazy && dependency.data.data.asyncType != null) {
this._incrementImportBundleReference(dependency, parentModule);
} else {
const path = dependency.absolutePath;
let module = this.dependencies.get(path);
if (!module) {
try {
module = this._recursivelyCommitModule(path, delta, options);
} catch (error) {
const module = this.dependencies.get(path);
if (module) {
if (module.inverseDependencies.size > 0) {
this._markAsPossibleCycleRoot(module);
} else {
this._releaseModule(module, delta, options);
}
}
throw error;
}
}
module.inverseDependencies.add(parentModule.path);
this._markModuleInUse(module);
}
if ((0, _isResolvedDependency.isResolvedDependency)(dependency)) {
const path = dependency.absolutePath;
if (requireContext) {
this.#resolvedContexts.set(path, requireContext);
} else {
this.#resolvedContexts.delete(path);
}
}
parentModule.dependencies.set(key, dependency);
}
_removeDependency(parentModule, key, dependency, delta, options) {
parentModule.dependencies.delete(key);
if (
!(0, _isResolvedDependency.isResolvedDependency)(dependency) ||
dependency.data.data.asyncType === "weak"
) {
return;
}
const { absolutePath } = dependency;
const module = this.dependencies.get(absolutePath);
if (options.lazy && dependency.data.data.asyncType != null) {
this._decrementImportBundleReference(dependency, parentModule);
} else if (module) {
module.inverseDependencies.delete(parentModule.path);
}
if (!module) {
return;
}
if (
module.inverseDependencies.size > 0 ||
this.entryPoints.has(absolutePath)
) {
this._markAsPossibleCycleRoot(module);
} else {
this._releaseModule(module, delta, options);
}
}
markModifiedContextModules(filePath, modifiedPaths) {
for (const [absolutePath, context] of this.#resolvedContexts) {
if (
!modifiedPaths.has(absolutePath) &&
(0, _contextModule.fileMatchesContext)(filePath, context)
) {
modifiedPaths.add(absolutePath);
}
}
}
*getModifiedModulesForDeletedPath(filePath) {
yield* this.dependencies.get(filePath)?.inverseDependencies ?? [];
yield* this.#importBundleNodes.get(filePath)?.inverseDependencies ?? [];
}
reorderGraph(options) {
const orderedDependencies = new Map();
this.entryPoints.forEach((entryPoint) => {
const mainModule = this.dependencies.get(entryPoint);
if (!mainModule) {
throw new ReferenceError(
"Module not registered in graph: " + entryPoint,
);
}
this._reorderDependencies(mainModule, orderedDependencies, options);
});
this.dependencies.clear();
for (const [key, dep] of orderedDependencies) {
this.dependencies.set(key, dep);
}
}
_reorderDependencies(module, orderedDependencies, options) {
if (module.path) {
if (orderedDependencies.has(module.path)) {
return;
}
orderedDependencies.set(module.path, module);
}
module.dependencies.forEach((dependency) => {
const path = dependency.absolutePath;
if (path == null) {
return;
}
const childModule = this.dependencies.get(path);
if (!childModule) {
if (dependency.data.data.asyncType != null || options.shallow) {
return;
} else {
throw new ReferenceError("Module not registered in graph: " + path);
}
}
this._reorderDependencies(childModule, orderedDependencies, options);
});
}
_incrementImportBundleReference(dependency, parentModule) {
const { absolutePath } = dependency;
const importBundleNode = this.#importBundleNodes.get(absolutePath) ?? {
inverseDependencies: new _CountingSet.default(),
};
importBundleNode.inverseDependencies.add(parentModule.path);
this.#importBundleNodes.set(absolutePath, importBundleNode);
}
_decrementImportBundleReference(dependency, parentModule) {
const { absolutePath } = dependency;
const importBundleNode = (0, _nullthrows.default)(
this.#importBundleNodes.get(absolutePath),
);
(0, _invariant.default)(
importBundleNode.inverseDependencies.has(parentModule.path),
"lazy: import bundle inverse references",
);
importBundleNode.inverseDependencies.delete(parentModule.path);
if (importBundleNode.inverseDependencies.size === 0) {
this.#importBundleNodes.delete(absolutePath);
}
}
_markModuleInUse(module) {
this.#gc.color.set(module.path, "black");
}
*_children(module, options) {
for (const dependency of module.dependencies.values()) {
if (
!(0, _isResolvedDependency.isResolvedDependency)(dependency) ||
isWeakOrLazy(dependency, options)
) {
continue;
}
yield (0, _nullthrows.default)(
this.dependencies.get(dependency.absolutePath),
);
}
}
_moduleSnapshot(module) {
const { dependencies, getSource, output, unstable_transformResultKey } =
module;
const resolvedContexts = new Map();
for (const [key, dependency] of dependencies) {
if (!(0, _isResolvedDependency.isResolvedDependency)(dependency)) {
continue;
}
const resolvedContext = this.#resolvedContexts.get(
dependency.absolutePath,
);
if (resolvedContext != null) {
resolvedContexts.set(key, resolvedContext);
}
}
return {
dependencies: new Map(dependencies),
getSource,
output,
resolvedContexts,
unstable_transformResultKey,
};
}
_releaseModule(module, delta, options) {
if (
!delta.updatedModuleData.has(module.path) &&
!delta.baseModuleData.has(module.path)
) {
delta.baseModuleData.set(module.path, this._moduleSnapshot(module));
}
for (const [key, dependency] of module.dependencies) {
if (!(0, _isResolvedDependency.isResolvedDependency)(dependency)) {
continue;
}
this._removeDependency(module, key, dependency, delta, options);
}
this.#gc.color.set(module.path, "black");
this._freeModule(module, delta);
}
_freeModule(module, delta) {
if (delta.added.has(module.path)) {
delta.added.delete(module.path);
} else {
delta.deleted.add(module.path);
}
this.dependencies.delete(module.path);
this.#gc.possibleCycleRoots.delete(module.path);
this.#gc.color.delete(module.path);
this.#resolvedContexts.delete(module.path);
}
_markAsPossibleCycleRoot(module) {
if (this.#gc.color.get(module.path) !== "purple") {
this.#gc.color.set(module.path, "purple");
this.#gc.possibleCycleRoots.add(module.path);
}
}
_collectCycles(delta, options) {
for (const path of this.#gc.possibleCycleRoots) {
const module = (0, _nullthrows.default)(this.dependencies.get(path));
const color = (0, _nullthrows.default)(this.#gc.color.get(path));
if (color === "purple") {
this._markGray(module, options);
} else {
this.#gc.possibleCycleRoots.delete(path);
if (
color === "black" &&
module.inverseDependencies.size === 0 &&
!this.entryPoints.has(path)
) {
this._freeModule(module, delta);
}
}
}
for (const path of this.#gc.possibleCycleRoots) {
const module = (0, _nullthrows.default)(this.dependencies.get(path));
this._scan(module, options);
}
for (const path of this.#gc.possibleCycleRoots) {
this.#gc.possibleCycleRoots.delete(path);
const module = (0, _nullthrows.default)(this.dependencies.get(path));
this._collectWhite(module, delta);
}
}
_markGray(module, options) {
const color = (0, _nullthrows.default)(this.#gc.color.get(module.path));
if (color !== "gray") {
this.#gc.color.set(module.path, "gray");
for (const childModule of this._children(module, options)) {
childModule.inverseDependencies.delete(module.path);
this._markGray(childModule, options);
}
}
}
_scan(module, options) {
const color = (0, _nullthrows.default)(this.#gc.color.get(module.path));
if (color === "gray") {
if (
module.inverseDependencies.size > 0 ||
this.entryPoints.has(module.path)
) {
this._scanBlack(module, options);
} else {
this.#gc.color.set(module.path, "white");
for (const childModule of this._children(module, options)) {
this._scan(childModule, options);
}
}
}
}
_scanBlack(module, options) {
this.#gc.color.set(module.path, "black");
for (const childModule of this._children(module, options)) {
childModule.inverseDependencies.add(module.path);
const childColor = (0, _nullthrows.default)(
this.#gc.color.get(childModule.path),
);
if (childColor !== "black") {
this._scanBlack(childModule, options);
}
}
}
_collectWhite(module, delta) {
const color = (0, _nullthrows.default)(this.#gc.color.get(module.path));
if (color === "white" && !this.#gc.possibleCycleRoots.has(module.path)) {
this.#gc.color.set(module.path, "black");
for (const dependency of module.dependencies.values()) {
if (!(0, _isResolvedDependency.isResolvedDependency)(dependency)) {
continue;
}
const childModule = this.dependencies.get(dependency.absolutePath);
if (childModule) {
this._collectWhite(childModule, delta);
}
}
this._freeModule(module, delta);
}
}
}
exports.Graph = Graph;
function dependenciesEqual(a, b, options) {
return (
a === b ||
(a.absolutePath === b.absolutePath &&
(!options.lazy || a.data.data.asyncType === b.data.data.asyncType) &&
contextParamsEqual(a.data.data.contextParams, b.data.data.contextParams))
);
}
function contextParamsEqual(a, b) {
return (
a === b ||
(a == null && b == null) ||
(a != null &&
b != null &&
a.recursive === b.recursive &&
a.filter.pattern === b.filter.pattern &&
a.filter.flags === b.filter.flags &&
a.mode === b.mode)
);
}
function transformOutputMayDiffer(a, b) {
return (
a.unstable_transformResultKey == null ||
a.unstable_transformResultKey !== b.unstable_transformResultKey
);
}

970
node_modules/metro/src/DeltaBundler/Graph.js.flow generated vendored Normal file
View File

@@ -0,0 +1,970 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
/**
* Portions of this code are based on the Synchronous Cycle Collection
* algorithm described in:
*
* David F. Bacon and V. T. Rajan. 2001. Concurrent Cycle Collection in
* Reference Counted Systems. In Proceedings of the 15th European Conference on
* Object-Oriented Programming (ECOOP '01). Springer-Verlag, Berlin,
* Heidelberg, 207235.
*
* Notable differences from the algorithm in the paper:
* 1. Our implementation uses the inverseDependencies set (which we already
* have to maintain) instead of a separate refcount variable. A module's
* reference count is equal to the size of its inverseDependencies set, plus
* 1 if it's an entry point of the graph.
* 2. We keep the "root buffer" (possibleCycleRoots) free of duplicates by
* making it a Set, instead of storing a "buffered" flag on each node.
* 3. On top of tracking edges between nodes, we also count references between
* nodes and entries in the importBundleNodes set.
*/
import type {RequireContext} from '../lib/contextModule';
import type {RequireContextParams} from '../ModuleGraph/worker/collectDependencies';
import type {
Dependencies,
Dependency,
GraphInputOptions,
MixedOutput,
Module,
ModuleData,
Options,
ResolvedDependency,
TransformInputOptions,
} from './types';
import {fileMatchesContext} from '../lib/contextModule';
import CountingSet from '../lib/CountingSet';
import {isResolvedDependency} from '../lib/isResolvedDependency';
import {buildSubgraph} from './buildSubgraph';
import invariant from 'invariant';
import nullthrows from 'nullthrows';
// TODO: Convert to a Flow enum
type NodeColor =
// In use or free
| 'black'
// Possible member of cycle
| 'gray'
// Member of garbage cycle
| 'white'
// Possible root of cycle
| 'purple'
// Inherently acyclic node (Not currently used)
| 'green';
export type Result<T> = {
added: Map<string, Module<T>>,
modified: Map<string, Module<T>>,
deleted: Set<string>,
};
/*
* Internal data structure that the traversal logic uses to know which of the
* files have been modified. This allows to return the added modules before the
* modified ones (which is useful for things like Hot Module Reloading).
*/
type Delta<T> = Readonly<{
// `added` and `deleted` are mutually exclusive.
// Internally, a module can be in both `touched` and (either) `added` or
// `deleted`. Before returning the result, we'll calculate
// modified = touched - added - deleted.
added: Set<string>,
touched: Set<string>,
deleted: Set<string>,
updatedModuleData: ReadonlyMap<string, ModuleData<T>>,
baseModuleData: Map<string, ModuleData<T>>,
errors: ReadonlyMap<string, Error>,
}>;
type InternalOptions<T> = Readonly<{
lazy: boolean,
onDependencyAdd: () => unknown,
onDependencyAdded: () => unknown,
resolve: Options<T>['resolve'],
transform: Options<T>['transform'],
shallow: boolean,
}>;
function getInternalOptions<T>({
transform,
resolve,
onProgress,
lazy,
shallow,
}: Options<T>): InternalOptions<T> {
let numProcessed = 0;
let total = 0;
return {
lazy,
onDependencyAdd: () => onProgress && onProgress(numProcessed, ++total),
onDependencyAdded: () => onProgress && onProgress(++numProcessed, total),
resolve,
shallow,
transform,
};
}
function isWeakOrLazy<T>(
dependency: ResolvedDependency,
options: InternalOptions<T>,
): boolean {
const asyncType = dependency.data.data.asyncType;
return asyncType === 'weak' || (asyncType != null && options.lazy);
}
export class Graph<T = MixedOutput> {
+entryPoints: ReadonlySet<string>;
+transformOptions: TransformInputOptions;
+dependencies: Dependencies<T> = new Map();
+#importBundleNodes: Map<
string,
Readonly<{
inverseDependencies: CountingSet<string>,
}>,
> = new Map();
/// GC state for nodes in the graph (this.dependencies)
+#gc: {
+color: Map<string, NodeColor>,
+possibleCycleRoots: Set<string>,
} = {
color: new Map(),
possibleCycleRoots: new Set(),
};
/** Resolved context parameters from `require.context`. */
#resolvedContexts: Map<string, RequireContext> = new Map();
constructor(options: GraphInputOptions) {
this.entryPoints = options.entryPoints;
this.transformOptions = options.transformOptions;
}
/**
* Dependency Traversal logic for the Delta Bundler. This method calculates
* the modules that should be included in the bundle by traversing the
* dependency graph.
* Instead of traversing the whole graph each time, it just calculates the
* difference between runs by only traversing the added/removed dependencies.
* To do so, it uses the passed graph dependencies and it mutates it.
* The paths parameter contains the absolute paths of the root files that the
* method should traverse. Normally, these paths should be the modified files
* since the last traversal.
*/
async traverseDependencies(
paths: ReadonlyArray<string>,
options: Options<T>,
): Promise<Result<T>> {
const internalOptions = getInternalOptions(options);
const modifiedPathsInBaseGraph = new Set(
paths.filter(path => this.dependencies.has(path)),
);
const allModifiedPaths = new Set(paths);
const delta = await this._buildDelta(
modifiedPathsInBaseGraph,
internalOptions,
// Traverse new or modified paths
absolutePath =>
!this.dependencies.has(absolutePath) ||
allModifiedPaths.has(absolutePath),
);
// If we have errors we might need to roll back any changes - take
// snapshots of all modified modules at the base state. We'll also snapshot
// unmodified modules that become unreachable as they are released, so that
// we have everything we need to restore the graph to base.
if (delta.errors.size > 0) {
for (const modified of modifiedPathsInBaseGraph) {
delta.baseModuleData.set(
modified,
this._moduleSnapshot(nullthrows(this.dependencies.get(modified))),
);
}
}
// Commit changes in a subtractive pass and then an additive pass - this
// ensures that any errors encountered on the additive pass would also be
// encountered on a fresh build (implying legitimate errors in the graph,
// rather than an error in a module that's no longer reachable).
for (const modified of modifiedPathsInBaseGraph) {
// Skip this module if it has errors. Hopefully it will be removed -
// if not, we'll throw during the additive pass.
if (delta.errors.has(modified)) {
continue;
}
const module = this.dependencies.get(modified);
// The module may have already been released from the graph - we'll readd
// it if necessary later.
if (module == null) {
continue;
}
// Process the transform result and dependency removals. This should
// never encounter an error.
this._recursivelyCommitModule(modified, delta, internalOptions, {
onlyRemove: true,
});
}
// Ensure we have released any unreachable modules before the additive
// pass.
this._collectCycles(delta, internalOptions);
// Additive pass - any errors we encounter here should be thrown after
// rolling back the commit.
try {
for (const modified of modifiedPathsInBaseGraph) {
const module = this.dependencies.get(modified);
// The module may have already been released from the graph (it may yet
// be readded via another dependency).
if (module == null) {
continue;
}
this._recursivelyCommitModule(modified, delta, internalOptions);
}
} catch (error) {
// Roll back to base before re-throwing.
const rollbackDelta: Delta<T> = {
added: delta.added,
baseModuleData: new Map(),
deleted: delta.deleted,
errors: new Map(),
touched: new Set(),
updatedModuleData: delta.baseModuleData,
};
for (const modified of modifiedPathsInBaseGraph) {
const module = this.dependencies.get(modified);
// The module may have already been released from the graph (it may yet
// be readded via another dependency).
if (module == null) {
continue;
}
// Set the module and descendants back to base state.
this._recursivelyCommitModule(modified, rollbackDelta, internalOptions);
}
// Collect cycles again after rolling back. There's no need if we're
// not rolling back, because we have not removed any edges.
this._collectCycles(delta, internalOptions);
// Cheap check to validate the rollback.
invariant(
rollbackDelta.added.size === 0 && rollbackDelta.deleted.size === 0,
'attempted to roll back a graph commit but there were still changes',
);
// Re-throw the transform or resolution error originally seen by
// `buildSubgraph`.
throw error;
}
const added = new Map<string, Module<T>>();
for (const path of delta.added) {
added.set(path, nullthrows(this.dependencies.get(path)));
}
const modified = new Map<string, Module<T>>();
for (const path of modifiedPathsInBaseGraph) {
if (
delta.touched.has(path) &&
!delta.deleted.has(path) &&
!delta.added.has(path)
) {
modified.set(path, nullthrows(this.dependencies.get(path)));
}
}
return {
added,
deleted: delta.deleted,
modified,
};
}
async initialTraverseDependencies(options: Options<T>): Promise<Result<T>> {
const internalOptions = getInternalOptions(options);
invariant(
this.dependencies.size === 0,
'initialTraverseDependencies called on nonempty graph',
);
this.#gc.color.clear();
this.#gc.possibleCycleRoots.clear();
this.#importBundleNodes.clear();
for (const path of this.entryPoints) {
// Each entry point implicitly has a refcount of 1, so mark them all black.
this.#gc.color.set(path, 'black');
}
const delta = await this._buildDelta(this.entryPoints, internalOptions);
if (delta.errors.size > 0) {
// If we encountered any errors during traversal, throw one of them.
// Since errors are encountered in a non-deterministic order, even on
// fresh builds, it's valid to arbitrarily pick the first.
throw delta.errors.values().next().value;
}
for (const path of this.entryPoints) {
// We have already thrown on userland errors in the delta, so any error
// encountered here would be exceptional and fatal.
this._recursivelyCommitModule(path, delta, internalOptions);
}
this.reorderGraph({
shallow: options.shallow,
});
return {
added: this.dependencies,
deleted: new Set(),
modified: new Map(),
};
}
async _buildDelta(
pathsToVisit: ReadonlySet<string>,
options: InternalOptions<T>,
moduleFilter?: (path: string) => boolean,
): Promise<Delta<T>> {
const subGraph = await buildSubgraph(pathsToVisit, this.#resolvedContexts, {
resolve: options.resolve,
shouldTraverse: (dependency: ResolvedDependency) => {
if (options.shallow || isWeakOrLazy(dependency, options)) {
return false;
}
return moduleFilter == null || moduleFilter(dependency.absolutePath);
},
transform: async (absolutePath, requireContext) => {
options.onDependencyAdd();
const result = await options.transform(absolutePath, requireContext);
options.onDependencyAdded();
return result;
},
});
return {
added: new Set(),
baseModuleData: new Map(),
deleted: new Set(),
errors: subGraph.errors,
touched: new Set(),
updatedModuleData: subGraph.moduleData,
};
}
_recursivelyCommitModule(
path: string,
delta: Delta<T>,
options: InternalOptions<T>,
commitOptions: Readonly<{
onlyRemove: boolean,
}> = {onlyRemove: false},
): Module<T> {
if (delta.errors.has(path)) {
throw delta.errors.get(path);
}
const previousModule = this.dependencies.get(path);
const currentModule: ModuleData<T> = nullthrows(
delta.updatedModuleData.get(path) ?? delta.baseModuleData.get(path),
);
const previousDependencies = previousModule?.dependencies ?? new Map();
const {
dependencies: currentDependencies,
resolvedContexts,
...transformResult
} = currentModule;
const nextModule = {
...(previousModule ?? {
inverseDependencies: new CountingSet(),
path,
}),
...transformResult,
dependencies: new Map(previousDependencies),
};
// Update the module information.
this.dependencies.set(nextModule.path, nextModule);
if (previousModule == null) {
// If the module is not currently in the graph, it is either new or was
// released earlier in the commit.
if (delta.deleted.has(path)) {
// Mark the addition by clearing a prior deletion.
delta.deleted.delete(path);
} else {
// Mark the addition in the added set.
delta.added.add(path);
}
}
// Diff dependencies (1/3): remove dependencies that have changed or been removed.
let dependenciesRemoved = false;
for (const [key, prevDependency] of previousDependencies) {
const curDependency = currentDependencies.get(key);
if (
!curDependency ||
!dependenciesEqual(prevDependency, curDependency, options)
) {
dependenciesRemoved = true;
this._removeDependency(nextModule, key, prevDependency, delta, options);
}
}
// Diff dependencies (2/3): add dependencies that have changed or been added.
let dependenciesAdded = false;
if (!commitOptions.onlyRemove) {
for (const [key, curDependency] of currentDependencies) {
const prevDependency = previousDependencies.get(key);
if (
!prevDependency ||
!dependenciesEqual(prevDependency, curDependency, options)
) {
dependenciesAdded = true;
this._addDependency(
nextModule,
key,
curDependency,
resolvedContexts.get(key),
delta,
options,
);
}
}
}
// Diff dependencies (3/3): detect changes in the ordering of dependency
// keys, which must be committed even if no other changes were made.
const previousDependencyKeys = [...previousDependencies.keys()];
const dependencyKeysChangedOrReordered =
currentDependencies.size !== previousDependencies.size ||
[...currentDependencies.keys()].some(
(currentKey, index) => currentKey !== previousDependencyKeys[index],
);
if (
previousModule != null &&
!transformOutputMayDiffer(previousModule, nextModule) &&
!dependenciesRemoved &&
!dependenciesAdded &&
!dependencyKeysChangedOrReordered
) {
// We have not operated on nextModule, so restore previousModule
// to aid diffing. Don't add this path to delta.touched.
this.dependencies.set(previousModule.path, previousModule);
return previousModule;
}
delta.touched.add(path);
// Replace dependencies with the correctly-ordered version, matching the
// transform output. Because this assignment does not add or remove edges,
// it does NOT invalidate any of the garbage collection state.
// A subtractive pass only partially commits modules, so our dependencies
// are not generally complete yet. We'll address ordering in the next pass
// after processing additions.
if (commitOptions.onlyRemove) {
return nextModule;
}
// Catch obvious errors with a cheap assertion.
invariant(
nextModule.dependencies.size === currentDependencies.size,
'Failed to add the correct dependencies',
);
nextModule.dependencies = new Map(currentDependencies);
return nextModule;
}
_addDependency(
parentModule: Module<T>,
key: string,
dependency: Dependency,
requireContext: ?RequireContext,
delta: Delta<T>,
options: InternalOptions<T>,
): void {
if (options.shallow) {
// Don't add a node for the module if the graph is shallow (single-module).
} else if (!isResolvedDependency(dependency)) {
// If the dependency is a missing optional dependency, it has no node of
// its own. We just need to add it to the parent's dependency map.
} else if (dependency.data.data.asyncType === 'weak') {
// Exclude weak dependencies from the bundle.
} else if (options.lazy && dependency.data.data.asyncType != null) {
// Don't add a node for the module if we are traversing async dependencies
// lazily (and this is an async dependency). Instead, record it in
// importBundleNodes.
this._incrementImportBundleReference(dependency, parentModule);
} else {
// The module may already exist, in which case we just need to update some
// bookkeeping instead of adding a new node to the graph.
const path = dependency.absolutePath;
let module = this.dependencies.get(path);
if (!module) {
try {
module = this._recursivelyCommitModule(path, delta, options);
} catch (error) {
// If we couldn't add this module but it was added to the graph
// before failing on a sub-dependency, it may be orphaned. Mark it as
// a possible garbage root.
const module = this.dependencies.get(path);
if (module) {
if (module.inverseDependencies.size > 0) {
this._markAsPossibleCycleRoot(module);
} else {
this._releaseModule(module, delta, options);
}
}
throw error;
}
}
// We either added a new node to the graph, or we're updating an existing one.
module.inverseDependencies.add(parentModule.path);
this._markModuleInUse(module);
}
if (isResolvedDependency(dependency)) {
const path = dependency.absolutePath;
if (requireContext) {
this.#resolvedContexts.set(path, requireContext);
} else {
// This dependency may have existed previously as a require.context -
// clean it up.
this.#resolvedContexts.delete(path);
}
}
// Update the parent's dependency map unless we failed to add a dependency.
// This means the parent's dependencies can get desynced from
// inverseDependencies and the other fields in the case of lazy edges.
// Not an optimal representation :(
parentModule.dependencies.set(key, dependency);
}
_removeDependency(
parentModule: Module<T>,
key: string,
dependency: Dependency,
delta: Delta<T>,
options: InternalOptions<T>,
): void {
parentModule.dependencies.delete(key);
if (
!isResolvedDependency(dependency) ||
dependency.data.data.asyncType === 'weak'
) {
// Weak and unresolved dependencies are excluded from the bundle.
return;
}
const {absolutePath} = dependency;
const module = this.dependencies.get(absolutePath);
if (options.lazy && dependency.data.data.asyncType != null) {
this._decrementImportBundleReference(dependency, parentModule);
} else if (module) {
// Decrement inverseDependencies only if the dependency is not async,
// mirroring the increment conditions in _addDependency.
module.inverseDependencies.delete(parentModule.path);
}
if (!module) {
return;
}
if (
module.inverseDependencies.size > 0 ||
this.entryPoints.has(absolutePath)
) {
// The reference count has decreased, but not to zero.
// NOTE: Each entry point implicitly has a refcount of 1.
this._markAsPossibleCycleRoot(module);
} else {
// The reference count has decreased to zero.
this._releaseModule(module, delta, options);
}
}
/**
* Collect a list of context modules which include a given file.
*/
markModifiedContextModules(
filePath: string,
modifiedPaths: Set<string> | CountingSet<string>,
) {
for (const [absolutePath, context] of this.#resolvedContexts) {
if (
!modifiedPaths.has(absolutePath) &&
fileMatchesContext(filePath, context)
) {
modifiedPaths.add(absolutePath);
}
}
}
/**
* Gets the list of modules affected by the deletion of a given file. The
* caller is expected to mark these modules as modified in the next call to
* traverseDependencies. Note that the list may contain duplicates.
*/
*getModifiedModulesForDeletedPath(filePath: string): Iterable<string> {
yield* this.dependencies.get(filePath)?.inverseDependencies ?? [];
yield* this.#importBundleNodes.get(filePath)?.inverseDependencies ?? [];
}
/**
* Re-traverse the dependency graph in DFS order to reorder the modules and
* guarantee the same order between runs. This method mutates the passed graph.
*/
reorderGraph(options: {shallow: boolean, ...}): void {
const orderedDependencies = new Map<string, Module<T>>();
this.entryPoints.forEach((entryPoint: string) => {
const mainModule = this.dependencies.get(entryPoint);
if (!mainModule) {
throw new ReferenceError(
'Module not registered in graph: ' + entryPoint,
);
}
this._reorderDependencies(mainModule, orderedDependencies, options);
});
this.dependencies.clear();
for (const [key, dep] of orderedDependencies) {
this.dependencies.set(key, dep);
}
}
_reorderDependencies(
module: Module<T>,
orderedDependencies: Map<string, Module<T>>,
options: {shallow: boolean, ...},
): void {
if (module.path) {
if (orderedDependencies.has(module.path)) {
return;
}
orderedDependencies.set(module.path, module);
}
module.dependencies.forEach(dependency => {
const path = dependency.absolutePath;
if (path == null) {
// If the dependency is not a missing optional dependency, it has no children to reorder.
return;
}
const childModule = this.dependencies.get(path);
if (!childModule) {
if (dependency.data.data.asyncType != null || options.shallow) {
return;
} else {
throw new ReferenceError('Module not registered in graph: ' + path);
}
}
this._reorderDependencies(childModule, orderedDependencies, options);
});
}
/** Garbage collection functions */
// Add an entry to importBundleNodes (or record an inverse dependency of an existing one)
_incrementImportBundleReference(
dependency: ResolvedDependency,
parentModule: Module<T>,
) {
const {absolutePath} = dependency;
const importBundleNode = this.#importBundleNodes.get(absolutePath) ?? {
inverseDependencies: new CountingSet(),
};
importBundleNode.inverseDependencies.add(parentModule.path);
this.#importBundleNodes.set(absolutePath, importBundleNode);
}
// Decrease the reference count of an entry in importBundleNodes (and delete it if necessary)
_decrementImportBundleReference(
dependency: ResolvedDependency,
parentModule: Module<T>,
) {
const {absolutePath} = dependency;
const importBundleNode = nullthrows(
this.#importBundleNodes.get(absolutePath),
);
invariant(
importBundleNode.inverseDependencies.has(parentModule.path),
'lazy: import bundle inverse references',
);
importBundleNode.inverseDependencies.delete(parentModule.path);
if (importBundleNode.inverseDependencies.size === 0) {
this.#importBundleNodes.delete(absolutePath);
}
}
// Mark a module as in use (ref count >= 1)
_markModuleInUse(module: Module<T>) {
this.#gc.color.set(module.path, 'black');
}
// Iterate "children" of the given module - i.e. non-weak / async
// dependencies having a corresponding inverse dependency.
*_children(
module: Module<T>,
options: InternalOptions<T>,
): Iterator<Module<T>> {
for (const dependency of module.dependencies.values()) {
if (
!isResolvedDependency(dependency) ||
isWeakOrLazy(dependency, options)
) {
continue;
}
yield nullthrows(this.dependencies.get(dependency.absolutePath));
}
}
_moduleSnapshot(module: Module<T>): ModuleData<T> {
const {dependencies, getSource, output, unstable_transformResultKey} =
module;
const resolvedContexts: Map<string, RequireContext> = new Map();
for (const [key, dependency] of dependencies) {
if (!isResolvedDependency(dependency)) {
continue;
}
const resolvedContext = this.#resolvedContexts.get(
dependency.absolutePath,
);
if (resolvedContext != null) {
resolvedContexts.set(key, resolvedContext);
}
}
return {
dependencies: new Map(dependencies),
getSource,
output,
resolvedContexts,
unstable_transformResultKey,
};
}
// Delete an unreachable module (and its outbound edges) from the graph
// immediately.
// Called when the reference count of a module has reached 0.
_releaseModule(
module: Module<T>,
delta: Delta<T>,
options: InternalOptions<T>,
) {
if (
!delta.updatedModuleData.has(module.path) &&
!delta.baseModuleData.has(module.path)
) {
// Before releasing a module, take a snapshot of the data we might need
// to reintroduce it to the graph later in this commit. As it is not
// already present in updatedModuleData we can infer it has not been modified,
// so the transform output and dependencies we copy here are current.
delta.baseModuleData.set(module.path, this._moduleSnapshot(module));
}
for (const [key, dependency] of module.dependencies) {
if (!isResolvedDependency(dependency)) {
// If the dependency is not a missing optional dependency, it has no children to remove.
continue;
}
this._removeDependency(module, key, dependency, delta, options);
}
this.#gc.color.set(module.path, 'black');
this._freeModule(module, delta);
}
// Delete an unreachable module from the graph.
_freeModule(module: Module<T>, delta: Delta<T>) {
if (delta.added.has(module.path)) {
// Mark the deletion by clearing a prior addition.
delta.added.delete(module.path);
} else {
// Mark the deletion in the deleted set.
delta.deleted.add(module.path);
}
// This module is not used anywhere else! We can clear it from the bundle.
// Clean up all the state associated with this module in order to correctly
// re-add it if we encounter it again.
this.dependencies.delete(module.path);
this.#gc.possibleCycleRoots.delete(module.path);
this.#gc.color.delete(module.path);
this.#resolvedContexts.delete(module.path);
}
// Mark a module as a possible cycle root
_markAsPossibleCycleRoot(module: Module<T>) {
if (this.#gc.color.get(module.path) !== 'purple') {
this.#gc.color.set(module.path, 'purple');
this.#gc.possibleCycleRoots.add(module.path);
}
}
// Collect any unreachable cycles in the graph.
_collectCycles(delta: Delta<T>, options: InternalOptions<T>) {
// Mark recursively from roots (trial deletion)
for (const path of this.#gc.possibleCycleRoots) {
const module = nullthrows(this.dependencies.get(path));
const color = nullthrows(this.#gc.color.get(path));
if (color === 'purple') {
this._markGray(module, options);
} else {
this.#gc.possibleCycleRoots.delete(path);
if (
color === 'black' &&
module.inverseDependencies.size === 0 &&
!this.entryPoints.has(path)
) {
this._freeModule(module, delta);
}
}
}
// Scan recursively from roots (undo unsuccessful trial deletions)
for (const path of this.#gc.possibleCycleRoots) {
const module = nullthrows(this.dependencies.get(path));
this._scan(module, options);
}
// Collect recursively from roots (free unreachable cycles)
for (const path of this.#gc.possibleCycleRoots) {
this.#gc.possibleCycleRoots.delete(path);
const module = nullthrows(this.dependencies.get(path));
this._collectWhite(module, delta);
}
}
_markGray(module: Module<T>, options: InternalOptions<T>) {
const color = nullthrows(this.#gc.color.get(module.path));
if (color !== 'gray') {
this.#gc.color.set(module.path, 'gray');
for (const childModule of this._children(module, options)) {
// The inverse dependency will be restored during the scan phase if this module remains live.
childModule.inverseDependencies.delete(module.path);
this._markGray(childModule, options);
}
}
}
_scan(module: Module<T>, options: InternalOptions<T>) {
const color = nullthrows(this.#gc.color.get(module.path));
if (color === 'gray') {
if (
module.inverseDependencies.size > 0 ||
this.entryPoints.has(module.path)
) {
this._scanBlack(module, options);
} else {
this.#gc.color.set(module.path, 'white');
for (const childModule of this._children(module, options)) {
this._scan(childModule, options);
}
}
}
}
_scanBlack(module: Module<T>, options: InternalOptions<T>) {
this.#gc.color.set(module.path, 'black');
for (const childModule of this._children(module, options)) {
// The inverse dependency must have been deleted during the mark phase.
childModule.inverseDependencies.add(module.path);
const childColor = nullthrows(this.#gc.color.get(childModule.path));
if (childColor !== 'black') {
this._scanBlack(childModule, options);
}
}
}
_collectWhite(module: Module<T>, delta: Delta<T>) {
const color = nullthrows(this.#gc.color.get(module.path));
if (color === 'white' && !this.#gc.possibleCycleRoots.has(module.path)) {
this.#gc.color.set(module.path, 'black');
for (const dependency of module.dependencies.values()) {
if (!isResolvedDependency(dependency)) {
continue;
}
const childModule = this.dependencies.get(dependency.absolutePath);
// The child may already have been collected.
if (childModule) {
this._collectWhite(childModule, delta);
}
}
this._freeModule(module, delta);
}
}
/** End of garbage collection functions */
}
function dependenciesEqual(
a: Dependency,
b: Dependency,
options: Readonly<{lazy: boolean, ...}>,
): boolean {
return (
a === b ||
(a.absolutePath === b.absolutePath &&
(!options.lazy || a.data.data.asyncType === b.data.data.asyncType) &&
contextParamsEqual(a.data.data.contextParams, b.data.data.contextParams))
);
}
function contextParamsEqual(
a: ?RequireContextParams,
b: ?RequireContextParams,
): boolean {
return (
a === b ||
(a == null && b == null) ||
(a != null &&
b != null &&
a.recursive === b.recursive &&
a.filter.pattern === b.filter.pattern &&
a.filter.flags === b.filter.flags &&
a.mode === b.mode)
);
}
function transformOutputMayDiffer<T>(a: Module<T>, b: Module<T>): boolean {
return (
a.unstable_transformResultKey == null ||
a.unstable_transformResultKey !== b.unstable_transformResultKey
);
}

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<e21aeb433f191f32b37d05733b0b76bd>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {Module, ReadOnlyGraph, SerializerOptions} from '../types';
import type {Bundle} from 'metro-runtime/src/modules/types';
declare function baseJSBundle(
entryPoint: string,
preModules: ReadonlyArray<Module>,
graph: ReadOnlyGraph,
options: SerializerOptions,
): Bundle;
export default baseJSBundle;

View File

@@ -0,0 +1,67 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = baseJSBundle;
var _getAppendScripts = _interopRequireDefault(
require("../../lib/getAppendScripts"),
);
var _processModules = _interopRequireDefault(
require("./helpers/processModules"),
);
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function baseJSBundle(entryPoint, preModules, graph, options) {
for (const module of graph.dependencies.values()) {
options.createModuleId(module.path);
}
const processModulesOptions = {
filter: options.processModuleFilter,
createModuleId: options.createModuleId,
dev: options.dev,
includeAsyncPaths: options.includeAsyncPaths,
projectRoot: options.projectRoot,
serverRoot: options.serverRoot,
sourceUrl: options.sourceUrl,
};
if (options.modulesOnly) {
preModules = [];
}
const preCode = (0, _processModules.default)(
preModules,
processModulesOptions,
)
.map(([_, code]) => code)
.join("\n");
const modules = [...graph.dependencies.values()].sort(
(a, b) => options.createModuleId(a.path) - options.createModuleId(b.path),
);
const postCode = (0, _processModules.default)(
(0, _getAppendScripts.default)(entryPoint, [...preModules, ...modules], {
asyncRequireModulePath: options.asyncRequireModulePath,
createModuleId: options.createModuleId,
getRunModuleStatement: options.getRunModuleStatement,
globalPrefix: options.globalPrefix,
inlineSourceMap: options.inlineSourceMap,
runBeforeMainModule: options.runBeforeMainModule,
runModule: options.runModule,
shouldAddToIgnoreList: options.shouldAddToIgnoreList,
sourceMapUrl: options.sourceMapUrl,
sourceUrl: options.sourceUrl,
getSourceUrl: options.getSourceUrl,
}),
processModulesOptions,
)
.map(([_, code]) => code)
.join("\n");
return {
pre: preCode,
post: postCode,
modules: (0, _processModules.default)(
[...graph.dependencies.values()],
processModulesOptions,
).map(([module, code]) => [options.createModuleId(module.path), code]),
};
}

View File

@@ -0,0 +1,84 @@
/**
* 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.
*
* @flow
* @format
* @oncall react_native
*/
import type {
MixedOutput,
Module,
ReadOnlyGraph,
SerializerOptions,
} from '../types';
import type {Bundle} from 'metro-runtime/src/modules/types';
import getAppendScripts from '../../lib/getAppendScripts';
import processModules from './helpers/processModules';
export default function baseJSBundle(
entryPoint: string,
preModules: ReadonlyArray<Module<>>,
graph: ReadOnlyGraph<>,
options: SerializerOptions,
): Bundle {
for (const module of graph.dependencies.values()) {
options.createModuleId(module.path);
}
const processModulesOptions = {
filter: options.processModuleFilter,
createModuleId: options.createModuleId,
dev: options.dev,
includeAsyncPaths: options.includeAsyncPaths,
projectRoot: options.projectRoot,
serverRoot: options.serverRoot,
sourceUrl: options.sourceUrl,
};
// Do not prepend polyfills or the require runtime when only modules are requested
if (options.modulesOnly) {
preModules = [];
}
const preCode = processModules(preModules, processModulesOptions)
.map(([_, code]) => code)
.join('\n');
const modules = [...graph.dependencies.values()].sort(
(a: Module<MixedOutput>, b: Module<MixedOutput>) =>
options.createModuleId(a.path) - options.createModuleId(b.path),
);
const postCode = processModules(
getAppendScripts(entryPoint, [...preModules, ...modules], {
asyncRequireModulePath: options.asyncRequireModulePath,
createModuleId: options.createModuleId,
getRunModuleStatement: options.getRunModuleStatement,
globalPrefix: options.globalPrefix,
inlineSourceMap: options.inlineSourceMap,
runBeforeMainModule: options.runBeforeMainModule,
runModule: options.runModule,
shouldAddToIgnoreList: options.shouldAddToIgnoreList,
sourceMapUrl: options.sourceMapUrl,
sourceUrl: options.sourceUrl,
getSourceUrl: options.getSourceUrl,
}),
processModulesOptions,
)
.map(([_, code]) => code)
.join('\n');
return {
pre: preCode,
post: postCode,
modules: processModules(
[...graph.dependencies.values()],
processModulesOptions,
).map(([module, code]) => [options.createModuleId(module.path), code]),
};
}

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<1d044a890d1eebbef947f78609d7c58f>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/getAllFiles.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {Module, ReadOnlyGraph} from '../types';
type Options = {
platform: null | undefined | string;
readonly processModuleFilter: (module: Module) => boolean;
};
declare function getAllFiles(
pre: ReadonlyArray<Module>,
graph: ReadOnlyGraph,
options: Options,
): Promise<ReadonlyArray<string>>;
export default getAllFiles;

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = getAllFiles;
var _Assets = require("../../Assets");
var _js = require("./helpers/js");
async function getAllFiles(pre, graph, options) {
const modules = graph.dependencies;
const { processModuleFilter } = options;
const promises = [];
for (const module of pre) {
if (processModuleFilter(module)) {
promises.push([module.path]);
}
}
for (const module of modules.values()) {
if (!(0, _js.isJsModule)(module) || !processModuleFilter(module)) {
continue;
}
if ((0, _js.getJsOutput)(module).type === "js/module/asset") {
promises.push((0, _Assets.getAssetFiles)(module.path, options.platform));
} else {
promises.push([module.path]);
}
}
const dependencies = await Promise.all(promises);
const output = [];
for (const dependencyArray of dependencies) {
output.push(...dependencyArray);
}
return output;
}

View File

@@ -0,0 +1,58 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {Module, ReadOnlyGraph} from '../types';
import {getAssetFiles} from '../../Assets';
import {getJsOutput, isJsModule} from './helpers/js';
type Options = {
platform: ?string,
+processModuleFilter: (module: Module<>) => boolean,
};
export default async function getAllFiles(
pre: ReadonlyArray<Module<>>,
graph: ReadOnlyGraph<>,
options: Options,
): Promise<ReadonlyArray<string>> {
const modules = graph.dependencies;
const {processModuleFilter} = options;
const promises: Array<Promise<Array<string>> | Array<string>> = [];
for (const module of pre) {
if (processModuleFilter(module)) {
promises.push([module.path]);
}
}
for (const module of modules.values()) {
if (!isJsModule(module) || !processModuleFilter(module)) {
continue;
}
if (getJsOutput(module).type === 'js/module/asset') {
promises.push(getAssetFiles(module.path, options.platform));
} else {
promises.push([module.path]);
}
}
const dependencies = await Promise.all(promises);
const output: Array<string> = [];
for (const dependencyArray of dependencies) {
output.push(...dependencyArray);
}
return output;
}

View File

@@ -0,0 +1,32 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<0a49d828c4a80d52ccab4d4766b84c86>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/getAssets.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {AssetData} from '../../Assets';
import type {Module, ReadOnlyDependencies} from '../types';
type Options = {
readonly processModuleFilter: (module: Module) => boolean;
assetPlugins: ReadonlyArray<string>;
platform: null | undefined | string;
projectRoot: string;
publicPath: string;
};
declare function getAssets(
dependencies: ReadOnlyDependencies,
options: Options,
): Promise<ReadonlyArray<AssetData>>;
export default getAssets;

View File

@@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = getAssets;
var _Assets = require("../../Assets");
var _js = require("./helpers/js");
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
async function getAssets(dependencies, options) {
const promises = [];
const { processModuleFilter } = options;
for (const module of dependencies.values()) {
if (
(0, _js.isJsModule)(module) &&
processModuleFilter(module) &&
(0, _js.getJsOutput)(module).type === "js/module/asset" &&
_path.default.relative(options.projectRoot, module.path) !==
"package.json"
) {
promises.push(
(0, _Assets.getAssetData)(
module.path,
_path.default.relative(options.projectRoot, module.path),
options.assetPlugins,
options.platform,
options.publicPath,
),
);
}
}
return await Promise.all(promises);
}

View File

@@ -0,0 +1,54 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {AssetData} from '../../Assets';
import type {Module, ReadOnlyDependencies} from '../types';
import {getAssetData} from '../../Assets';
import {getJsOutput, isJsModule} from './helpers/js';
import path from 'path';
type Options = {
+processModuleFilter: (module: Module<>) => boolean,
assetPlugins: ReadonlyArray<string>,
platform: ?string,
projectRoot: string,
publicPath: string,
};
export default async function getAssets(
dependencies: ReadOnlyDependencies<>,
options: Options,
): Promise<ReadonlyArray<AssetData>> {
const promises = [];
const {processModuleFilter} = options;
for (const module of dependencies.values()) {
if (
isJsModule(module) &&
processModuleFilter(module) &&
getJsOutput(module).type === 'js/module/asset' &&
path.relative(options.projectRoot, module.path) !== 'package.json'
) {
promises.push(
getAssetData(
module.path,
path.relative(options.projectRoot, module.path),
options.assetPlugins,
options.platform,
options.publicPath,
),
);
}
}
return await Promise.all(promises);
}

View File

@@ -0,0 +1,33 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<623892927b76c4f68802bb69f19d9974>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/getExplodedSourceMap.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {Module} from '../types';
import type {
FBSourceFunctionMap,
MetroSourceMapSegmentTuple,
} from 'metro-source-map';
export type ExplodedSourceMap = ReadonlyArray<{
readonly map: Array<MetroSourceMapSegmentTuple>;
readonly firstLine1Based: number;
readonly functionMap: null | undefined | FBSourceFunctionMap;
readonly path: string;
}>;
export declare function getExplodedSourceMap(
modules: ReadonlyArray<Module>,
options: {readonly processModuleFilter: (module: Module) => boolean},
): ExplodedSourceMap;

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.getExplodedSourceMap = getExplodedSourceMap;
var _js = require("./helpers/js");
function getExplodedSourceMap(modules, options) {
const modulesToProcess = modules
.filter(_js.isJsModule)
.filter(options.processModuleFilter);
const result = [];
let firstLine1Based = 1;
for (const module of modulesToProcess) {
const { path } = module;
const { lineCount, functionMap, map } = (0, _js.getJsOutput)(module).data;
result.push({
firstLine1Based,
functionMap,
path,
map,
});
firstLine1Based += lineCount;
}
return result;
}

View File

@@ -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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {Module} from '../types';
import type {
FBSourceFunctionMap,
MetroSourceMapSegmentTuple,
} from 'metro-source-map';
import {getJsOutput, isJsModule} from './helpers/js';
export type ExplodedSourceMap = ReadonlyArray<{
+map: Array<MetroSourceMapSegmentTuple>,
+firstLine1Based: number,
+functionMap: ?FBSourceFunctionMap,
+path: string,
}>;
export function getExplodedSourceMap(
modules: ReadonlyArray<Module<>>,
options: {
+processModuleFilter: (module: Module<>) => boolean,
},
): ExplodedSourceMap {
const modulesToProcess = modules
.filter(isJsModule)
.filter(options.processModuleFilter);
const result = [];
let firstLine1Based = 1;
for (const module of modulesToProcess) {
const {path} = module;
const {lineCount, functionMap, map} = getJsOutput(module).data;
result.push({firstLine1Based, functionMap, path, map});
firstLine1Based += lineCount;
}
return result;
}

View File

@@ -0,0 +1,55 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<b01899b6156596978e6b8e7ba93e97a4>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {ModuleTransportLike} from '../../shared/types';
import type {Module, ReadOnlyGraph, SerializerOptions} from '../types';
import type {SourceMapGeneratorOptions} from './sourceMapGenerator';
import type {GetTransformOptions} from 'metro-config';
type Options = Readonly<
Omit<
SerializerOptions,
| keyof SourceMapGeneratorOptions
| keyof {
getTransformOptions: null | undefined | GetTransformOptions;
platform: null | undefined | string;
}
> &
Omit<
SourceMapGeneratorOptions,
keyof {
getTransformOptions: null | undefined | GetTransformOptions;
platform: null | undefined | string;
}
> & {
getTransformOptions: null | undefined | GetTransformOptions;
platform: null | undefined | string;
}
>;
export type RamBundleInfo = {
getDependencies: ($$PARAM_0$$: string) => Set<string>;
startupModules: ReadonlyArray<ModuleTransportLike>;
lazyModules: ReadonlyArray<ModuleTransportLike>;
groups: Map<number, Set<number>>;
};
declare function getRamBundleInfo(
entryPoint: string,
pre: ReadonlyArray<Module>,
graph: ReadOnlyGraph,
options: Options,
): Promise<RamBundleInfo>;
export default getRamBundleInfo;

View File

@@ -0,0 +1,121 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = getRamBundleInfo;
var _util = require("../../Bundler/util");
var _getAppendScripts = _interopRequireDefault(
require("../../lib/getAppendScripts"),
);
var _getTransitiveDependencies = _interopRequireDefault(
require("./helpers/getTransitiveDependencies"),
);
var _js = require("./helpers/js");
var _sourceMapObject = require("./sourceMapObject");
var _nullthrows = _interopRequireDefault(require("nullthrows"));
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
async function getRamBundleInfo(entryPoint, pre, graph, options) {
let modules = [...pre, ...graph.dependencies.values()];
modules = modules.concat(
(0, _getAppendScripts.default)(entryPoint, modules, options),
);
modules.forEach((module) => options.createModuleId(module.path));
const ramModules = modules
.filter(_js.isJsModule)
.filter(options.processModuleFilter)
.map((module) => ({
id: options.createModuleId(module.path),
code: (0, _js.wrapModule)(module, options),
map: (0, _sourceMapObject.sourceMapObject)([module], {
excludeSource: options.excludeSource,
processModuleFilter: options.processModuleFilter,
shouldAddToIgnoreList: options.shouldAddToIgnoreList,
getSourceUrl: options.getSourceUrl,
}),
name: _path.default.basename(module.path),
sourcePath: module.path,
source: module.getSource().toString(),
type: (0, _nullthrows.default)(
module.output.find(({ type }) => type.startsWith("js")),
).type,
}));
const { preloadedModules, ramGroups } = await _getRamOptions(
entryPoint,
{
dev: options.dev,
platform: options.platform,
},
(filePath) => (0, _getTransitiveDependencies.default)(filePath, graph),
options.getTransformOptions,
);
const startupModules = [];
const lazyModules = [];
ramModules.forEach((module) => {
if (preloadedModules.hasOwnProperty(module.sourcePath)) {
startupModules.push(module);
return;
}
if (module.type.startsWith("js/script")) {
startupModules.push(module);
return;
}
if (module.type.startsWith("js/module")) {
lazyModules.push(module);
}
});
const groups = (0, _util.createRamBundleGroups)(
ramGroups,
lazyModules,
(module, dependenciesByPath) => {
const deps = (0, _getTransitiveDependencies.default)(
module.sourcePath,
graph,
);
const output = new Set();
for (const dependency of deps) {
const module = dependenciesByPath.get(dependency);
if (module) {
output.add(module.id);
}
}
return output;
},
);
return {
getDependencies: (filePath) =>
(0, _getTransitiveDependencies.default)(filePath, graph),
groups,
lazyModules,
startupModules,
};
}
async function _getRamOptions(
entryFile,
options,
getDependencies,
getTransformOptions,
) {
if (getTransformOptions == null) {
return {
preloadedModules: {},
ramGroups: [],
};
}
const { preloadedModules, ramGroups } = await getTransformOptions(
[entryFile],
{
dev: options.dev,
hot: true,
platform: options.platform,
},
async (x) => Array.from(getDependencies),
);
return {
preloadedModules: preloadedModules || {},
ramGroups: ramGroups || [],
};
}

View File

@@ -0,0 +1,171 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {ModuleTransportLike, RamModuleTransport} from '../../shared/types';
import type {Module, ReadOnlyGraph, SerializerOptions} from '../types';
import type {SourceMapGeneratorOptions} from './sourceMapGenerator';
import type {GetTransformOptions} from 'metro-config';
import {createRamBundleGroups} from '../../Bundler/util';
import getAppendScripts from '../../lib/getAppendScripts';
import getTransitiveDependencies from './helpers/getTransitiveDependencies';
import {isJsModule, wrapModule} from './helpers/js';
import {sourceMapObject} from './sourceMapObject';
import nullthrows from 'nullthrows';
import path from 'path';
type Options = Readonly<{
...SerializerOptions,
...SourceMapGeneratorOptions,
getTransformOptions: ?GetTransformOptions,
platform: ?string,
}>;
export type RamBundleInfo = {
getDependencies: string => Set<string>,
startupModules: ReadonlyArray<ModuleTransportLike>,
lazyModules: ReadonlyArray<ModuleTransportLike>,
groups: Map<number, Set<number>>,
};
export default async function getRamBundleInfo(
entryPoint: string,
pre: ReadonlyArray<Module<>>,
graph: ReadOnlyGraph<>,
options: Options,
): Promise<RamBundleInfo> {
let modules: ReadonlyArray<Module<>> = [
...pre,
...graph.dependencies.values(),
];
modules = modules.concat(getAppendScripts(entryPoint, modules, options));
modules.forEach((module: Module<>) => options.createModuleId(module.path));
const ramModules: Array<RamModuleTransport> = modules
.filter(isJsModule)
.filter(options.processModuleFilter)
.map((module: Module<>): RamModuleTransport => ({
id: options.createModuleId(module.path),
code: wrapModule(module, options),
map: sourceMapObject([module], {
excludeSource: options.excludeSource,
processModuleFilter: options.processModuleFilter,
shouldAddToIgnoreList: options.shouldAddToIgnoreList,
getSourceUrl: options.getSourceUrl,
}),
name: path.basename(module.path),
sourcePath: module.path,
source: module.getSource().toString(),
type: nullthrows(module.output.find(({type}) => type.startsWith('js')))
.type,
}));
const {preloadedModules, ramGroups} = await _getRamOptions(
entryPoint,
{
dev: options.dev,
platform: options.platform,
},
(filePath: string) => getTransitiveDependencies(filePath, graph),
options.getTransformOptions,
);
const startupModules = [];
const lazyModules = [];
ramModules.forEach((module: RamModuleTransport) => {
if (preloadedModules.hasOwnProperty(module.sourcePath)) {
startupModules.push(module);
return;
}
if (module.type.startsWith('js/script')) {
startupModules.push(module);
return;
}
if (module.type.startsWith('js/module')) {
lazyModules.push(module);
}
});
const groups = createRamBundleGroups(
ramGroups,
lazyModules,
(
module: ModuleTransportLike,
dependenciesByPath: Map<string, ModuleTransportLike>,
): Set<number> => {
const deps = getTransitiveDependencies(module.sourcePath, graph);
const output = new Set<number>();
for (const dependency of deps) {
const module = dependenciesByPath.get(dependency);
if (module) {
output.add(module.id);
}
}
return output;
},
);
return {
getDependencies: (filePath: string): Set<string> =>
getTransitiveDependencies(filePath, graph),
groups,
lazyModules,
startupModules,
};
}
/**
* Returns the options needed to create a RAM bundle.
*/
async function _getRamOptions(
entryFile: string,
options: {
dev: boolean,
platform: ?string,
...
},
getDependencies: string => Iterable<string>,
getTransformOptions: ?GetTransformOptions,
): Promise<
Readonly<{
preloadedModules: Readonly<{[string]: true, ...}>,
ramGroups: ReadonlyArray<string>,
}>,
> {
if (getTransformOptions == null) {
return {
preloadedModules: {},
ramGroups: [],
};
}
const {preloadedModules, ramGroups} = await getTransformOptions(
[entryFile],
{dev: options.dev, hot: true, platform: options.platform},
/* $FlowFixMe[incompatible-type](>=0.99.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.99 was deployed. To see the error, delete this
* comment and run Flow. */
async (x: string) => Array.from(getDependencies),
);
return {
// $FlowFixMe[sketchy-null-bool]
preloadedModules: preloadedModules || {},
ramGroups: ramGroups || [],
};
}

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<f9cd017db02091bdd4f6d114e4744087>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/helpers/getInlineSourceMappingURL.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
declare function getInlineSourceMappingURL(sourceMap: string): string;
export default getInlineSourceMappingURL;

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = getInlineSourceMappingURL;
function getInlineSourceMappingURL(sourceMap) {
const base64 = Buffer.from(sourceMap).toString("base64");
return `data:application/json;charset=utf-8;base64,${base64}`;
}

View File

@@ -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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
export default function getInlineSourceMappingURL(sourceMap: string): string {
const base64 = Buffer.from(sourceMap).toString('base64');
return `data:application/json;charset=utf-8;base64,${base64}`;
}

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<cd7e523b4fdfbff33e663b21c4529401>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/helpers/getSourceMapInfo.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {Module} from '../../types';
import type {
FBSourceFunctionMap,
MetroSourceMapSegmentTuple,
} from 'metro-source-map';
declare function getSourceMapInfo(
module: Module,
options: {
readonly excludeSource: boolean;
readonly shouldAddToIgnoreList: ($$PARAM_0$$: Module) => boolean;
getSourceUrl: null | undefined | ((module: Module) => string);
},
): {
readonly map: Array<MetroSourceMapSegmentTuple>;
readonly functionMap: null | undefined | FBSourceFunctionMap;
readonly code: string;
readonly path: string;
readonly source: string;
readonly lineCount: number;
readonly isIgnored: boolean;
};
export default getSourceMapInfo;

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = getSourceMapInfo;
var _js = require("./js");
function getSourceMapInfo(module, options) {
return {
...(0, _js.getJsOutput)(module).data,
isIgnored: options.shouldAddToIgnoreList(module),
path: options?.getSourceUrl?.(module) ?? module.path,
source: options.excludeSource ? "" : getModuleSource(module),
};
}
function getModuleSource(module) {
if ((0, _js.getJsOutput)(module).type === "js/module/asset") {
return "";
}
return module.getSource().toString();
}

View File

@@ -0,0 +1,50 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {Module} from '../../types';
import type {
FBSourceFunctionMap,
MetroSourceMapSegmentTuple,
} from 'metro-source-map';
import {getJsOutput} from './js';
export default function getSourceMapInfo(
module: Module<>,
options: {
+excludeSource: boolean,
+shouldAddToIgnoreList: (Module<>) => boolean,
getSourceUrl: ?(module: Module<>) => string,
},
): {
+map: Array<MetroSourceMapSegmentTuple>,
+functionMap: ?FBSourceFunctionMap,
+code: string,
+path: string,
+source: string,
+lineCount: number,
+isIgnored: boolean,
} {
return {
...getJsOutput(module).data,
isIgnored: options.shouldAddToIgnoreList(module),
path: options?.getSourceUrl?.(module) ?? module.path,
source: options.excludeSource ? '' : getModuleSource(module),
};
}
function getModuleSource(module: Module<>): string {
if (getJsOutput(module).type === 'js/module/asset') {
return '';
}
return module.getSource().toString();
}

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<55bd91c160900bb31ffe72e2ddfad85d>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/helpers/getTransitiveDependencies.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {ReadOnlyGraph} from '../../types';
declare function getTransitiveDependencies<T>(
path: string,
graph: ReadOnlyGraph<T>,
): Set<string>;
export default getTransitiveDependencies;

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = getTransitiveDependencies;
var _isResolvedDependency = require("../../../lib/isResolvedDependency");
function getTransitiveDependencies(path, graph) {
const dependencies = _getDeps(path, graph, new Set());
dependencies.delete(path);
return dependencies;
}
function _getDeps(path, graph, deps) {
if (deps.has(path)) {
return deps;
}
const module = graph.dependencies.get(path);
if (!module) {
return deps;
}
deps.add(path);
for (const dependency of module.dependencies.values()) {
if ((0, _isResolvedDependency.isResolvedDependency)(dependency)) {
_getDeps(dependency.absolutePath, graph, deps);
}
}
return deps;
}

View File

@@ -0,0 +1,53 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {ReadOnlyGraph} from '../../types';
import {isResolvedDependency} from '../../../lib/isResolvedDependency';
export default function getTransitiveDependencies<T>(
path: string,
graph: ReadOnlyGraph<T>,
): Set<string> {
const dependencies = _getDeps(path, graph, new Set());
// Remove the main entry point, since this method only returns the
// dependencies.
dependencies.delete(path);
return dependencies;
}
function _getDeps<T>(
path: string,
graph: ReadOnlyGraph<T>,
deps: Set<string>,
): Set<string> {
if (deps.has(path)) {
return deps;
}
const module = graph.dependencies.get(path);
if (!module) {
return deps;
}
deps.add(path);
for (const dependency of module.dependencies.values()) {
if (isResolvedDependency(dependency)) {
_getDeps(dependency.absolutePath, graph, deps);
}
}
return deps;
}

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<f61e17deebe7e34585ad214ae287e704>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/helpers/js.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {MixedOutput, Module} from '../../types';
import type {JsOutput} from 'metro-transform-worker';
export type Options = Readonly<{
createModuleId: ($$PARAM_0$$: string) => number | string;
dev: boolean;
includeAsyncPaths: boolean;
projectRoot: string;
serverRoot: string;
sourceUrl: null | undefined | string;
}>;
export declare function wrapModule(module: Module, options: Options): string;
export declare function getModuleParams(
module: Module,
options: Options,
): Array<unknown>;
export declare function getJsOutput(
module: Readonly<{output: ReadonlyArray<MixedOutput>; path?: string}>,
): JsOutput;
export declare function isJsModule(module: Module): boolean;

View File

@@ -0,0 +1,133 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.getJsOutput = getJsOutput;
exports.getModuleParams = getModuleParams;
exports.isJsModule = isJsModule;
exports.wrapModule = wrapModule;
var _isResolvedDependency = require("../../../lib/isResolvedDependency");
var _pathUtils = require("../../../lib/pathUtils");
var _invariant = _interopRequireDefault(require("invariant"));
var jscSafeUrl = _interopRequireWildcard(require("jsc-safe-url"));
var _metroTransformPlugins = require("metro-transform-plugins");
var _path = _interopRequireDefault(require("path"));
function _interopRequireWildcard(e, t) {
if ("function" == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ("object" != typeof e && "function" != typeof e))
return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
"default" !== t &&
{}.hasOwnProperty.call(e, t) &&
((i =
(o = Object.defineProperty) &&
Object.getOwnPropertyDescriptor(e, t)) &&
(i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function wrapModule(module, options) {
const output = getJsOutput(module);
if (output.type.startsWith("js/script")) {
return output.data.code;
}
const params = getModuleParams(module, options);
return (0, _metroTransformPlugins.addParamsToDefineCall)(
output.data.code,
...params,
);
}
function getModuleParams(module, options) {
const moduleId = options.createModuleId(module.path);
const paths = {};
let hasPaths = false;
const dependencyMapArray = Array.from(module.dependencies.values()).map(
(dependency) => {
if (!(0, _isResolvedDependency.isResolvedDependency)(dependency)) {
return null;
}
const id = options.createModuleId(dependency.absolutePath);
if (options.includeAsyncPaths && dependency.data.data.asyncType != null) {
hasPaths = true;
(0, _invariant.default)(
options.sourceUrl != null,
"sourceUrl is required when includeAsyncPaths is true",
);
const { searchParams } = new URL(
jscSafeUrl.toNormalUrl(options.sourceUrl),
);
searchParams.set("modulesOnly", "true");
searchParams.set("runModule", "false");
const bundlePath = _path.default.relative(
options.serverRoot,
dependency.absolutePath,
);
paths[id] =
"/" +
_path.default.join(
_path.default.dirname(bundlePath),
_path.default.basename(
bundlePath,
_path.default.extname(bundlePath),
),
) +
".bundle?" +
searchParams.toString();
}
return id;
},
);
const params = [
moduleId,
hasPaths
? {
...dependencyMapArray,
paths,
}
: dependencyMapArray,
];
if (options.dev) {
params.push(
(0, _pathUtils.normalizePathSeparatorsToPosix)(
_path.default.relative(options.projectRoot, module.path),
),
);
}
return params;
}
function getJsOutput(module) {
const jsModules = module.output.filter(({ type }) => type.startsWith("js/"));
(0, _invariant.default)(
jsModules.length === 1,
`Modules must have exactly one JS output, but ${module.path ?? "unknown module"} has ${jsModules.length} JS outputs.`,
);
const jsOutput = jsModules[0];
(0, _invariant.default)(
Number.isFinite(jsOutput.data.lineCount),
`JS output must populate lineCount, but ${module.path ?? "unknown module"} has ${jsOutput.type} output with lineCount '${jsOutput.data.lineCount}'`,
);
return jsOutput;
}
function isJsModule(module) {
return module.output.filter(isJsOutput).length > 0;
}
function isJsOutput(output) {
return output.type.startsWith("js/");
}

View File

@@ -0,0 +1,154 @@
/**
* 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.
*
* @flow
* @format
* @oncall react_native
*/
import type {MixedOutput, Module} from '../../types';
import type {JsOutput} from 'metro-transform-worker';
import {isResolvedDependency} from '../../../lib/isResolvedDependency';
import {normalizePathSeparatorsToPosix} from '../../../lib/pathUtils';
import invariant from 'invariant';
import * as jscSafeUrl from 'jsc-safe-url';
import {addParamsToDefineCall} from 'metro-transform-plugins';
import path from 'path';
export type Options = Readonly<{
createModuleId: string => number | string,
dev: boolean,
includeAsyncPaths: boolean,
projectRoot: string,
serverRoot: string,
sourceUrl: ?string,
...
}>;
export function wrapModule(module: Module<>, options: Options): string {
const output = getJsOutput(module);
if (output.type.startsWith('js/script')) {
return output.data.code;
}
const params = getModuleParams(module, options);
return addParamsToDefineCall(output.data.code, ...params);
}
export function getModuleParams(
module: Module<>,
options: Options,
): Array<unknown> {
const moduleId = options.createModuleId(module.path);
const paths: {[moduleID: number | string]: unknown} = {};
let hasPaths = false;
const dependencyMapArray = Array.from(module.dependencies.values()).map(
dependency => {
if (!isResolvedDependency(dependency)) {
// An unresolved dependency, which should cause a runtime error
// when required.
return null;
}
const id = options.createModuleId(dependency.absolutePath);
if (options.includeAsyncPaths && dependency.data.data.asyncType != null) {
hasPaths = true;
invariant(
options.sourceUrl != null,
'sourceUrl is required when includeAsyncPaths is true',
);
// TODO: Only include path if the target is not in the bundle
// Construct a server-relative URL for the split bundle, propagating
// most parameters from the main bundle's URL.
const {searchParams} = new URL(
jscSafeUrl.toNormalUrl(options.sourceUrl),
);
searchParams.set('modulesOnly', 'true');
searchParams.set('runModule', 'false');
const bundlePath = path.relative(
options.serverRoot,
dependency.absolutePath,
);
paths[id] =
'/' +
path.join(
// TODO: This is not the proper Metro URL encoding of a file path
path.dirname(bundlePath),
// Strip the file extension
path.basename(bundlePath, path.extname(bundlePath)),
) +
'.bundle?' +
searchParams.toString();
}
return id;
},
);
const params = [
moduleId,
hasPaths
? {
// $FlowFixMe[not-an-object] Intentionally spreading an array into an object
...dependencyMapArray,
paths,
}
: dependencyMapArray,
];
if (options.dev) {
// Add the relative path of the module to make debugging easier.
// This is mapped to `module.verboseName` in `require.js`.
params.push(
normalizePathSeparatorsToPosix(
path.relative(options.projectRoot, module.path),
),
);
}
return params;
}
export function getJsOutput(
module: Readonly<{
output: ReadonlyArray<MixedOutput>,
path?: string,
...
}>,
): JsOutput {
const jsModules = module.output.filter(({type}) => type.startsWith('js/'));
invariant(
jsModules.length === 1,
`Modules must have exactly one JS output, but ${
module.path ?? 'unknown module'
} has ${jsModules.length} JS outputs.`,
);
const jsOutput: JsOutput = jsModules[0] as any;
invariant(
Number.isFinite(jsOutput.data.lineCount),
`JS output must populate lineCount, but ${
module.path ?? 'unknown module'
} has ${jsOutput.type} output with lineCount '${jsOutput.data.lineCount}'`,
);
return jsOutput;
}
export function isJsModule(module: Module<>): boolean {
return module.output.filter(isJsOutput).length > 0;
}
function isJsOutput(output: MixedOutput): boolean {
return output.type.startsWith('js/');
}

View File

@@ -0,0 +1,32 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<1c5fe56fba9dbedcde1dbaeb5a486467>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/helpers/processModules.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {Module} from '../../types';
declare function processModules(
modules: ReadonlyArray<Module>,
$$PARAM_1$$: Readonly<{
filter?: (module: Module) => boolean;
createModuleId: ($$PARAM_0$$: string) => number;
dev: boolean;
includeAsyncPaths: boolean;
projectRoot: string;
serverRoot: string;
sourceUrl: null | undefined | string;
}>,
): ReadonlyArray<[Module, string]>;
export default processModules;

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = processModules;
var _js = require("./js");
function processModules(
modules,
{
filter = () => true,
createModuleId,
dev,
includeAsyncPaths,
projectRoot,
serverRoot,
sourceUrl,
},
) {
return [...modules]
.filter(_js.isJsModule)
.filter(filter)
.map((module) => [
module,
(0, _js.wrapModule)(module, {
createModuleId,
dev,
includeAsyncPaths,
projectRoot,
serverRoot,
sourceUrl,
}),
]);
}

View File

@@ -0,0 +1,50 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {Module} from '../../types';
import {isJsModule, wrapModule} from './js';
export default function processModules(
modules: ReadonlyArray<Module<>>,
{
filter = () => true,
createModuleId,
dev,
includeAsyncPaths,
projectRoot,
serverRoot,
sourceUrl,
}: Readonly<{
filter?: (module: Module<>) => boolean,
createModuleId: string => number,
dev: boolean,
includeAsyncPaths: boolean,
projectRoot: string,
serverRoot: string,
sourceUrl: ?string,
}>,
): ReadonlyArray<[Module<>, string]> {
return [...modules]
.filter(isJsModule)
.filter(filter)
.map((module: Module<>) => [
module,
wrapModule(module, {
createModuleId,
dev,
includeAsyncPaths,
projectRoot,
serverRoot,
sourceUrl,
}),
]);
}

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<d8ccae61344526a4f6da61987b3dad9b>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {DeltaResult, ReadOnlyGraph} from '../types';
import type {HmrModule} from 'metro-runtime/src/modules/types';
type Options = Readonly<{
clientUrl: URL;
createModuleId: ($$PARAM_0$$: string) => number;
includeAsyncPaths: boolean;
projectRoot: string;
serverRoot: string;
}>;
declare function hmrJSBundle(
delta: DeltaResult,
graph: ReadOnlyGraph,
options: Options,
): {
readonly added: ReadonlyArray<HmrModule>;
readonly deleted: ReadonlyArray<number>;
readonly modified: ReadonlyArray<HmrModule>;
};
export default hmrJSBundle;

View File

@@ -0,0 +1,128 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = hmrJSBundle;
var _js = require("./helpers/js");
var jscSafeUrl = _interopRequireWildcard(require("jsc-safe-url"));
var _metroTransformPlugins = require("metro-transform-plugins");
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function _interopRequireWildcard(e, t) {
if ("function" == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ("object" != typeof e && "function" != typeof e))
return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
"default" !== t &&
{}.hasOwnProperty.call(e, t) &&
((i =
(o = Object.defineProperty) &&
Object.getOwnPropertyDescriptor(e, t)) &&
(i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
const debug = require("debug")("Metro:HMR");
function generateModules(sourceModules, graph, options) {
const modules = [];
for (const module of sourceModules) {
if ((0, _js.isJsModule)(module)) {
const getPathname = (extension) => {
return _path.default
.relative(
options.serverRoot ?? options.projectRoot,
_path.default.join(
_path.default.dirname(module.path),
_path.default.basename(
module.path,
_path.default.extname(module.path),
) +
"." +
extension,
),
)
.split(_path.default.sep)
.map((segment) => encodeURIComponent(segment))
.join("/");
};
const clientUrl = new URL(options.clientUrl);
clientUrl.searchParams.delete("excludeSource");
clientUrl.pathname = getPathname("map");
const sourceMappingURL = clientUrl.toString();
clientUrl.pathname = getPathname("bundle");
const sourceURL = jscSafeUrl.toJscSafeUrl(clientUrl.toString());
debug(
"got sourceMappingURL: %s\nand sourceURL: %s\nfor module: %s",
sourceMappingURL,
sourceURL,
module.path,
);
const code =
prepareModule(module, graph, options) +
`\n//# sourceMappingURL=${sourceMappingURL}\n` +
`//# sourceURL=${sourceURL}\n`;
modules.push({
module: [options.createModuleId(module.path), code],
sourceMappingURL,
sourceURL,
});
}
}
return modules;
}
function prepareModule(module, graph, options) {
const code = (0, _js.wrapModule)(module, {
...options,
sourceUrl: options.clientUrl.toString(),
dev: true,
});
const inverseDependencies = getInverseDependencies(module.path, graph);
const inverseDependenciesById = Object.create(null);
Object.keys(inverseDependencies).forEach((path) => {
inverseDependenciesById[options.createModuleId(path)] = inverseDependencies[
path
].map(options.createModuleId);
});
return (0, _metroTransformPlugins.addParamsToDefineCall)(
code,
inverseDependenciesById,
);
}
function getInverseDependencies(path, graph, inverseDependencies = {}) {
if (path in inverseDependencies) {
return inverseDependencies;
}
const module = graph.dependencies.get(path);
if (!module) {
return inverseDependencies;
}
inverseDependencies[path] = [];
for (const inverse of module.inverseDependencies) {
inverseDependencies[path].push(inverse);
getInverseDependencies(inverse, graph, inverseDependencies);
}
return inverseDependencies;
}
function hmrJSBundle(delta, graph, options) {
return {
added: generateModules(delta.added.values(), graph, options),
modified: generateModules(delta.modified.values(), graph, options),
deleted: [...delta.deleted].map((path) => options.createModuleId(path)),
};
}

View File

@@ -0,0 +1,162 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {DeltaResult, Module, ReadOnlyGraph} from '../types';
import type {HmrModule} from 'metro-runtime/src/modules/types';
import {isJsModule, wrapModule} from './helpers/js';
import * as jscSafeUrl from 'jsc-safe-url';
import {addParamsToDefineCall} from 'metro-transform-plugins';
import path from 'path';
// eslint-disable-next-line import/no-commonjs
const debug = require('debug')('Metro:HMR');
type Options = Readonly<{
clientUrl: URL,
createModuleId: string => number,
includeAsyncPaths: boolean,
projectRoot: string,
serverRoot: string,
...
}>;
function generateModules(
sourceModules: Iterable<Module<>>,
graph: ReadOnlyGraph<>,
options: Options,
): ReadonlyArray<HmrModule> {
const modules = [];
for (const module of sourceModules) {
if (isJsModule(module)) {
const getPathname = (extension: 'bundle' | 'map') => {
return (
path
.relative(
options.serverRoot ?? options.projectRoot,
path.join(
path.dirname(module.path),
path.basename(module.path, path.extname(module.path)) +
'.' +
extension,
),
)
.split(path.sep)
// using this Metro particular convention for encoding file paths as URL paths.
.map(segment => encodeURIComponent(segment))
.join('/')
);
};
const clientUrl = new URL(options.clientUrl);
clientUrl.searchParams.delete('excludeSource');
clientUrl.pathname = getPathname('map');
const sourceMappingURL = clientUrl.toString();
clientUrl.pathname = getPathname('bundle');
const sourceURL = jscSafeUrl.toJscSafeUrl(clientUrl.toString());
debug(
'got sourceMappingURL: %s\nand sourceURL: %s\nfor module: %s',
sourceMappingURL,
sourceURL,
module.path,
);
const code =
prepareModule(module, graph, options) +
`\n//# sourceMappingURL=${sourceMappingURL}\n` +
`//# sourceURL=${sourceURL}\n`;
modules.push({
module: [options.createModuleId(module.path), code],
sourceMappingURL,
sourceURL,
});
}
}
return modules;
}
function prepareModule(
module: Module<>,
graph: ReadOnlyGraph<>,
options: Options,
): string {
const code = wrapModule(module, {
...options,
sourceUrl: options.clientUrl.toString(),
dev: true,
});
const inverseDependencies = getInverseDependencies(module.path, graph);
// Transform the inverse dependency paths to ids.
const inverseDependenciesById = Object.create(null);
Object.keys(inverseDependencies).forEach((path: string) => {
// $FlowFixMe[prop-missing]
// $FlowFixMe[invalid-computed-prop]
inverseDependenciesById[options.createModuleId(path)] = inverseDependencies[
path
].map(options.createModuleId);
});
return addParamsToDefineCall(code, inverseDependenciesById);
}
/**
* Instead of adding the whole inverseDependncies object into each changed
* module (which can be really huge if the dependency graph is big), we only
* add the needed inverseDependencies for each changed module (we do this by
* traversing upwards the dependency graph).
*/
function getInverseDependencies(
path: string,
graph: ReadOnlyGraph<>,
inverseDependencies: {[key: string]: Array<string>, ...} = {},
): {[key: string]: Array<string>, ...} {
// Dependency alredy traversed.
if (path in inverseDependencies) {
return inverseDependencies;
}
const module = graph.dependencies.get(path);
if (!module) {
return inverseDependencies;
}
inverseDependencies[path] = [];
for (const inverse of module.inverseDependencies) {
inverseDependencies[path].push(inverse);
getInverseDependencies(inverse, graph, inverseDependencies);
}
return inverseDependencies;
}
export default function hmrJSBundle(
delta: DeltaResult<>,
graph: ReadOnlyGraph<>,
options: Options,
): {
+added: ReadonlyArray<HmrModule>,
+deleted: ReadonlyArray<number>,
+modified: ReadonlyArray<HmrModule>,
} {
return {
added: generateModules(delta.added.values(), graph, options),
modified: generateModules(delta.modified.values(), graph, options),
deleted: [...delta.deleted].map((path: string) =>
options.createModuleId(path),
),
};
}

View File

@@ -0,0 +1,36 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<49bc83c20821024a7b77f5d5c3168d62>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/sourceMapGenerator.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {Module} from '../types';
import {fromRawMappings, fromRawMappingsNonBlocking} from 'metro-source-map';
export type SourceMapGeneratorOptions = Readonly<{
excludeSource: boolean;
processModuleFilter: (module: Module) => boolean;
shouldAddToIgnoreList: (module: Module) => boolean;
getSourceUrl: null | undefined | ((module: Module) => string);
}>;
declare function sourceMapGenerator(
modules: ReadonlyArray<Module>,
options: SourceMapGeneratorOptions,
): ReturnType<typeof fromRawMappings>;
declare function sourceMapGeneratorNonBlocking(
modules: ReadonlyArray<Module>,
options: SourceMapGeneratorOptions,
): ReturnType<typeof fromRawMappingsNonBlocking>;
export {sourceMapGenerator, sourceMapGeneratorNonBlocking};

View File

@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.sourceMapGenerator = sourceMapGenerator;
exports.sourceMapGeneratorNonBlocking = sourceMapGeneratorNonBlocking;
var _getSourceMapInfo = _interopRequireDefault(
require("./helpers/getSourceMapInfo"),
);
var _js = require("./helpers/js");
var _metroSourceMap = require("metro-source-map");
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function getSourceMapInfosImpl(isBlocking, onDone, modules, options) {
const sourceMapInfos = [];
const modulesToProcess = modules
.filter(_js.isJsModule)
.filter(options.processModuleFilter);
function processNextModule() {
if (modulesToProcess.length === 0) {
return true;
}
const mod = modulesToProcess.shift();
const info = (0, _getSourceMapInfo.default)(mod, {
excludeSource: options.excludeSource,
shouldAddToIgnoreList: options.shouldAddToIgnoreList,
getSourceUrl: options.getSourceUrl,
});
sourceMapInfos.push(info);
return false;
}
function workLoop() {
const time = process.hrtime();
while (true) {
const isDone = processNextModule();
if (isDone) {
onDone(sourceMapInfos);
break;
}
if (!isBlocking) {
const diff = process.hrtime(time);
const NS_IN_MS = 1000000;
if (diff[1] > 50 * NS_IN_MS) {
setImmediate(workLoop);
break;
}
}
}
}
workLoop();
}
function sourceMapGenerator(modules, options) {
let sourceMapInfos;
getSourceMapInfosImpl(
true,
(infos) => {
sourceMapInfos = infos;
},
modules,
options,
);
if (sourceMapInfos == null) {
throw new Error(
"Expected getSourceMapInfosImpl() to finish synchronously.",
);
}
return (0, _metroSourceMap.fromRawMappings)(sourceMapInfos);
}
async function sourceMapGeneratorNonBlocking(modules, options) {
const sourceMapInfos = await new Promise((resolve) => {
getSourceMapInfosImpl(false, resolve, modules, options);
});
return (0, _metroSourceMap.fromRawMappingsNonBlocking)(sourceMapInfos);
}

View File

@@ -0,0 +1,111 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {Module} from '../types';
import getSourceMapInfo from './helpers/getSourceMapInfo';
import {isJsModule} from './helpers/js';
import {fromRawMappings, fromRawMappingsNonBlocking} from 'metro-source-map';
export type SourceMapGeneratorOptions = Readonly<{
excludeSource: boolean,
processModuleFilter: (module: Module<>) => boolean,
shouldAddToIgnoreList: (module: Module<>) => boolean,
getSourceUrl: ?(module: Module<>) => string,
}>;
function getSourceMapInfosImpl(
isBlocking: boolean,
onDone: (ReadonlyArray<ReturnType<typeof getSourceMapInfo>>) => void,
modules: ReadonlyArray<Module<>>,
options: SourceMapGeneratorOptions,
): void {
const sourceMapInfos = [];
const modulesToProcess = modules
.filter(isJsModule)
.filter(options.processModuleFilter);
function processNextModule() {
if (modulesToProcess.length === 0) {
return true;
}
const mod = modulesToProcess.shift();
// $FlowFixMe[incompatible-type]
const info = getSourceMapInfo(mod, {
excludeSource: options.excludeSource,
shouldAddToIgnoreList: options.shouldAddToIgnoreList,
getSourceUrl: options.getSourceUrl,
});
sourceMapInfos.push(info);
return false;
}
function workLoop() {
const time = process.hrtime();
while (true) {
const isDone = processNextModule();
if (isDone) {
onDone(sourceMapInfos);
break;
}
if (!isBlocking) {
// Keep the loop running but try to avoid blocking
// for too long because this is not in a worker yet.
const diff = process.hrtime(time);
const NS_IN_MS = 1000000;
if (diff[1] > 50 * NS_IN_MS) {
// We've blocked for more than 50ms.
// This code currently runs on the main thread,
// so let's give Metro an opportunity to handle requests.
setImmediate(workLoop);
break;
}
}
}
}
workLoop();
}
function sourceMapGenerator(
modules: ReadonlyArray<Module<>>,
options: SourceMapGeneratorOptions,
): ReturnType<typeof fromRawMappings> {
let sourceMapInfos;
getSourceMapInfosImpl(
true,
infos => {
sourceMapInfos = infos;
},
modules,
options,
);
if (sourceMapInfos == null) {
throw new Error(
'Expected getSourceMapInfosImpl() to finish synchronously.',
);
}
return fromRawMappings(sourceMapInfos);
}
async function sourceMapGeneratorNonBlocking(
modules: ReadonlyArray<Module<>>,
options: SourceMapGeneratorOptions,
): ReturnType<typeof fromRawMappingsNonBlocking> {
const sourceMapInfos = await new Promise<
ReadonlyArray<ReturnType<typeof getSourceMapInfo>>,
>(resolve => {
getSourceMapInfosImpl(false, resolve, modules, options);
});
return fromRawMappingsNonBlocking(sourceMapInfos);
}
export {sourceMapGenerator, sourceMapGeneratorNonBlocking};

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<cb907f1a9aa40efd505a19826a21be6d>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/sourceMapObject.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {Module} from '../types';
import type {SourceMapGeneratorOptions} from './sourceMapGenerator';
import type {MixedSourceMap} from 'metro-source-map';
declare function sourceMapObject(
modules: ReadonlyArray<Module>,
options: SourceMapGeneratorOptions,
): MixedSourceMap;
declare function sourceMapObjectNonBlocking(
modules: ReadonlyArray<Module>,
options: SourceMapGeneratorOptions,
): Promise<MixedSourceMap>;
export {sourceMapObject, sourceMapObjectNonBlocking};

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.sourceMapObject = sourceMapObject;
exports.sourceMapObjectNonBlocking = sourceMapObjectNonBlocking;
var _sourceMapGenerator = require("./sourceMapGenerator");
function sourceMapObject(modules, options) {
const generator = (0, _sourceMapGenerator.sourceMapGenerator)(
modules,
options,
);
return generator.toMap(undefined, {
excludeSource: options.excludeSource,
});
}
async function sourceMapObjectNonBlocking(modules, options) {
const generator = await (0,
_sourceMapGenerator.sourceMapGeneratorNonBlocking)(modules, options);
return generator.toMap(undefined, {
excludeSource: options.excludeSource,
});
}

View File

@@ -0,0 +1,41 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {Module} from '../types';
import type {SourceMapGeneratorOptions} from './sourceMapGenerator';
import type {MixedSourceMap} from 'metro-source-map';
import {
sourceMapGenerator,
sourceMapGeneratorNonBlocking,
} from './sourceMapGenerator';
function sourceMapObject(
modules: ReadonlyArray<Module<>>,
options: SourceMapGeneratorOptions,
): MixedSourceMap {
const generator = sourceMapGenerator(modules, options);
return generator.toMap(undefined, {
excludeSource: options.excludeSource,
});
}
async function sourceMapObjectNonBlocking(
modules: ReadonlyArray<Module<>>,
options: SourceMapGeneratorOptions,
): Promise<MixedSourceMap> {
const generator = await sourceMapGeneratorNonBlocking(modules, options);
return generator.toMap(undefined, {
excludeSource: options.excludeSource,
});
}
export {sourceMapObject, sourceMapObjectNonBlocking};

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<578dd38524928420df15b0aba8f32e77>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Serializers/sourceMapString.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {Module} from '../types';
import type {SourceMapGeneratorOptions} from './sourceMapGenerator';
declare function sourceMapString(
modules: ReadonlyArray<Module>,
options: SourceMapGeneratorOptions,
): string;
declare function sourceMapStringNonBlocking(
modules: ReadonlyArray<Module>,
options: SourceMapGeneratorOptions,
): Promise<string>;
export {sourceMapString, sourceMapStringNonBlocking};

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.sourceMapString = sourceMapString;
exports.sourceMapStringNonBlocking = sourceMapStringNonBlocking;
var _sourceMapGenerator = require("./sourceMapGenerator");
function sourceMapString(modules, options) {
return (0, _sourceMapGenerator.sourceMapGenerator)(modules, options).toString(
undefined,
{
excludeSource: options.excludeSource,
},
);
}
async function sourceMapStringNonBlocking(modules, options) {
const generator = await (0,
_sourceMapGenerator.sourceMapGeneratorNonBlocking)(modules, options);
return generator.toString(undefined, {
excludeSource: options.excludeSource,
});
}

View File

@@ -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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {Module} from '../types';
import type {SourceMapGeneratorOptions} from './sourceMapGenerator';
import {
sourceMapGenerator,
sourceMapGeneratorNonBlocking,
} from './sourceMapGenerator';
function sourceMapString(
modules: ReadonlyArray<Module<>>,
options: SourceMapGeneratorOptions,
): string {
return sourceMapGenerator(modules, options).toString(undefined, {
excludeSource: options.excludeSource,
});
}
async function sourceMapStringNonBlocking(
modules: ReadonlyArray<Module<>>,
options: SourceMapGeneratorOptions,
): Promise<string> {
const generator = await sourceMapGeneratorNonBlocking(modules, options);
return generator.toString(undefined, {
excludeSource: options.excludeSource,
});
}
export {sourceMapString, sourceMapStringNonBlocking};

45
node_modules/metro/src/DeltaBundler/Transformer.d.ts generated vendored Normal file
View File

@@ -0,0 +1,45 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<15f603afc860c64c7acc5a6cfe2a6717>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Transformer.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {TransformResult, TransformResultWithSource} from '../DeltaBundler';
import type {TransformOptions} from './Worker';
import type {ConfigT} from 'metro-config';
import WorkerFarm from './WorkerFarm';
import {Cache} from 'metro-cache';
type GetOrComputeSha1Fn = (
$$PARAM_0$$: string,
) => Promise<Readonly<{content?: Buffer; sha1: string}>>;
declare class Transformer {
_config: ConfigT;
_cache: Cache<TransformResult>;
_baseHash: string;
_getSha1: GetOrComputeSha1Fn;
_workerFarm: WorkerFarm;
constructor(
config: ConfigT,
opts: Readonly<{getOrComputeSha1: GetOrComputeSha1Fn}>,
);
transformFile(
filePath: string,
transformerOptions: TransformOptions,
fileBuffer?: Buffer,
): Promise<TransformResultWithSource>;
end(): Promise<void>;
}
export default Transformer;

159
node_modules/metro/src/DeltaBundler/Transformer.js generated vendored Normal file
View File

@@ -0,0 +1,159 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _pathUtils = require("../lib/pathUtils");
var _getTransformCacheKey = _interopRequireDefault(
require("./getTransformCacheKey"),
);
var _WorkerFarm = _interopRequireDefault(require("./WorkerFarm"));
var _assert = _interopRequireDefault(require("assert"));
var _crypto = _interopRequireDefault(require("crypto"));
var _fs = _interopRequireDefault(require("fs"));
var _metroCache = require("metro-cache");
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const debug = require("debug")("Metro:Transformer");
class Transformer {
constructor(config, opts) {
this._config = config;
this._config.watchFolders.forEach(verifyRootExists);
this._cache = new _metroCache.Cache(config.cacheStores);
this._getSha1 = opts.getOrComputeSha1;
const {
getTransformOptions: _getTransformOptions,
transformVariants: _transformVariants,
unstable_workerThreads: _workerThreads,
...transformerConfig
} = this._config.transformer;
const transformerOptions = {
transformerPath: this._config.transformerPath,
transformerConfig,
};
this._workerFarm = new _WorkerFarm.default(config, transformerOptions);
const globalCacheKey = this._cache.isDisabled
? ""
: (0, _getTransformCacheKey.default)({
cacheVersion: this._config.cacheVersion,
projectRoot: this._config.projectRoot,
transformerConfig: transformerOptions,
});
const baseHashBuffer = (0, _metroCache.stableHash)([globalCacheKey]);
this._baseHash = baseHashBuffer.toString("binary");
debug("Base hash: %s", baseHashBuffer.toString("hex"));
}
async transformFile(filePath, transformerOptions, fileBuffer) {
const cache = this._cache;
const {
customTransformOptions,
dev,
experimentalImportSupport,
inlinePlatform,
inlineRequires,
minify,
nonInlinedRequires,
platform,
type,
unstable_transformProfile,
unstable_memoizeInlineRequires,
unstable_nonMemoizedInlineRequires,
...extra
} = transformerOptions;
for (const key in extra) {
if (hasOwnProperty.call(extra, key)) {
throw new Error(
"Extra keys detected: " + Object.keys(extra).join(", "),
);
}
}
const localPath = _path.default.relative(
this._config.projectRoot,
filePath,
);
const partialKey = (0, _metroCache.stableHash)([
this._baseHash,
(0, _pathUtils.normalizePathSeparatorsToPosix)(localPath),
customTransformOptions,
dev,
experimentalImportSupport,
inlinePlatform,
inlineRequires,
minify,
nonInlinedRequires,
platform,
type,
unstable_memoizeInlineRequires,
unstable_nonMemoizedInlineRequires,
unstable_transformProfile,
]);
let sha1;
let content;
if (fileBuffer) {
sha1 = _crypto.default
.createHash("sha1")
.update(fileBuffer)
.digest("hex");
content = fileBuffer;
} else {
const result = await this._getSha1(filePath);
sha1 = result.sha1;
if (result.content) {
content = result.content;
}
}
let fullKey = Buffer.concat([partialKey, Buffer.from(sha1, "hex")]);
let result;
try {
result = await cache.get(fullKey);
} catch (error) {
this._config.reporter.update({
type: "cache_read_error",
error,
});
throw error;
}
const data = result
? {
result,
sha1,
}
: await this._workerFarm.transform(
localPath,
transformerOptions,
content,
);
if (sha1 !== data.sha1) {
fullKey = Buffer.concat([partialKey, Buffer.from(data.sha1, "hex")]);
}
cache.set(fullKey, data.result).catch((error) => {
this._config.reporter.update({
type: "cache_write_error",
error,
});
});
return {
...data.result,
unstable_transformResultKey: fullKey.toString(),
getSource() {
if (fileBuffer) {
return fileBuffer;
}
return _fs.default.readFileSync(filePath);
},
};
}
async end() {
await this._workerFarm.kill();
}
}
exports.default = Transformer;
function verifyRootExists(root) {
(0, _assert.default)(
_fs.default.statSync(root).isDirectory(),
"Root has to be a valid directory",
);
}

209
node_modules/metro/src/DeltaBundler/Transformer.js.flow generated vendored Normal file
View File

@@ -0,0 +1,209 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {TransformResult, TransformResultWithSource} from '../DeltaBundler';
import type {TransformerConfig, TransformOptions} from './Worker';
import type {ConfigT} from 'metro-config';
import {normalizePathSeparatorsToPosix} from '../lib/pathUtils';
import getTransformCacheKey from './getTransformCacheKey';
import WorkerFarm from './WorkerFarm';
import assert from 'assert';
import crypto from 'crypto';
import fs from 'fs';
import {Cache, stableHash} from 'metro-cache';
import path from 'path';
// eslint-disable-next-line import/no-commonjs
const debug = require('debug')('Metro:Transformer');
type GetOrComputeSha1Fn = string => Promise<
Readonly<{content?: Buffer, sha1: string}>,
>;
export default class Transformer {
_config: ConfigT;
_cache: Cache<TransformResult<>>;
_baseHash: string;
_getSha1: GetOrComputeSha1Fn;
_workerFarm: WorkerFarm;
constructor(
config: ConfigT,
opts: Readonly<{getOrComputeSha1: GetOrComputeSha1Fn}>,
) {
this._config = config;
this._config.watchFolders.forEach(verifyRootExists);
this._cache = new Cache(config.cacheStores);
this._getSha1 = opts.getOrComputeSha1;
// Remove the transformer config params that we don't want to pass to the
// transformer. We should change the config object and move them away so we
// can treat the transformer config params as opaque.
const {
getTransformOptions: _getTransformOptions,
transformVariants: _transformVariants,
unstable_workerThreads: _workerThreads,
...transformerConfig
} = this._config.transformer;
const transformerOptions: TransformerConfig = {
transformerPath: this._config.transformerPath,
transformerConfig,
};
this._workerFarm = new WorkerFarm(config, transformerOptions);
const globalCacheKey = this._cache.isDisabled
? ''
: getTransformCacheKey({
cacheVersion: this._config.cacheVersion,
projectRoot: this._config.projectRoot,
transformerConfig: transformerOptions,
});
const baseHashBuffer = stableHash([globalCacheKey]);
this._baseHash = baseHashBuffer.toString('binary');
debug('Base hash: %s', baseHashBuffer.toString('hex'));
}
async transformFile(
filePath: string,
transformerOptions: TransformOptions,
fileBuffer?: Buffer,
): Promise<TransformResultWithSource<>> {
const cache = this._cache;
const {
customTransformOptions,
dev,
experimentalImportSupport,
inlinePlatform,
inlineRequires,
minify,
nonInlinedRequires,
platform,
type,
unstable_transformProfile,
unstable_memoizeInlineRequires,
unstable_nonMemoizedInlineRequires,
...extra
} = transformerOptions;
for (const key in extra) {
// $FlowFixMe[cannot-resolve-name]
if (hasOwnProperty.call(extra, key)) {
throw new Error(
'Extra keys detected: ' + Object.keys(extra).join(', '),
);
}
}
const localPath = path.relative(this._config.projectRoot, filePath);
const partialKey = stableHash([
// This is the hash related to the global Bundler config.
this._baseHash,
// Project-relative, posix-separated path for portability. Necessary in
// addition to content hash because transformers receive path as an
// input, and may apply e.g. extension-based logic.
normalizePathSeparatorsToPosix(localPath),
customTransformOptions,
dev,
experimentalImportSupport,
inlinePlatform,
inlineRequires,
minify,
nonInlinedRequires,
platform,
type,
unstable_memoizeInlineRequires,
unstable_nonMemoizedInlineRequires,
unstable_transformProfile,
]);
let sha1: string;
let content: ?Buffer;
if (fileBuffer) {
// Shortcut for virtual modules which provide the contents with the filename.
sha1 = crypto.createHash('sha1').update(fileBuffer).digest('hex');
content = fileBuffer;
} else {
const result = await this._getSha1(filePath);
sha1 = result.sha1;
if (result.content) {
content = result.content;
}
}
let fullKey = Buffer.concat([partialKey, Buffer.from(sha1, 'hex')]);
let result;
try {
result = await cache.get(fullKey);
} catch (error) {
this._config.reporter.update({
type: 'cache_read_error',
error,
});
throw error;
}
// A valid result from the cache is used directly; otherwise we call into
// the transformer to computed the corresponding result.
const data: Readonly<{
result: TransformResult<>,
sha1: string,
}> = result
? {result, sha1}
: await this._workerFarm.transform(
localPath,
transformerOptions,
content,
);
// Only re-compute the full key if the SHA-1 changed. This is because
// references are used by the cache implementation in a weak map to keep
// track of the cache that returned the result.
if (sha1 !== data.sha1) {
fullKey = Buffer.concat([partialKey, Buffer.from(data.sha1, 'hex')]);
}
// Fire-and-forget cache set promise.
cache.set(fullKey, data.result).catch(error => {
this._config.reporter.update({
type: 'cache_write_error',
error,
});
});
return {
...data.result,
unstable_transformResultKey: fullKey.toString(),
getSource(): Buffer {
if (fileBuffer) {
return fileBuffer;
}
return fs.readFileSync(filePath);
},
};
}
async end(): Promise<void> {
await this._workerFarm.kill();
}
}
function verifyRootExists(root: string): void {
// Verify that the root exists.
assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory');
}

44
node_modules/metro/src/DeltaBundler/Worker.d.ts generated vendored Normal file
View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<36640ae81894592fbdc160fac081bdbf>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/Worker.flow.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {TransformResult} from './types';
import type {LogEntry} from 'metro-core/private/Logger';
import type {
JsTransformerConfig,
JsTransformOptions,
} from 'metro-transform-worker';
export type {JsTransformOptions as TransformOptions} from 'metro-transform-worker';
export type TransformerConfig = {
transformerPath: string;
transformerConfig: JsTransformerConfig;
};
type Data = Readonly<{
result: TransformResult;
sha1: string;
transformFileStartLogEntry: LogEntry;
transformFileEndLogEntry: LogEntry;
}>;
export declare const transform: (
filename: string,
transformOptions: JsTransformOptions,
projectRoot: string,
transformerConfig: TransformerConfig,
fileBuffer?: Buffer,
) => Promise<Data>;
export declare type transform = typeof transform;
export type Worker = {readonly transform: typeof transform};

96
node_modules/metro/src/DeltaBundler/Worker.flow.js generated vendored Normal file
View File

@@ -0,0 +1,96 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.transform = void 0;
var _traverse = _interopRequireDefault(require("@babel/traverse"));
var _crypto = _interopRequireDefault(require("crypto"));
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function asDeserializedBuffer(value) {
if (Buffer.isBuffer(value)) {
return value;
}
if (value && value.type === "Buffer") {
return Buffer.from(value.data);
}
if (ArrayBuffer.isView(value)) {
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
}
return null;
}
const transform = (
filename,
transformOptions,
projectRoot,
transformerConfig,
fileBuffer,
) => {
let data;
const fileBufferObject = asDeserializedBuffer(fileBuffer);
if (fileBufferObject) {
data = fileBufferObject;
} else {
data = _fs.default.readFileSync(
_path.default.resolve(projectRoot, filename),
);
}
return transformFile(
filename,
data,
transformOptions,
projectRoot,
transformerConfig,
);
};
exports.transform = transform;
async function transformFile(
filename,
data,
transformOptions,
projectRoot,
transformerConfig,
) {
const Transformer = require.call(null, transformerConfig.transformerPath);
const transformFileStartLogEntry = {
action_name: "Transforming file",
action_phase: "start",
file_name: filename,
log_entry_label: "Transforming file",
start_timestamp: process.hrtime(),
};
const sha1 = _crypto.default.createHash("sha1").update(data).digest("hex");
const result = await Transformer.transform(
transformerConfig.transformerConfig,
projectRoot,
filename,
data,
transformOptions,
);
_traverse.default.cache.clear();
const transformFileEndLogEntry = getEndLogEntry(
transformFileStartLogEntry,
filename,
);
return {
result,
sha1,
transformFileStartLogEntry,
transformFileEndLogEntry,
};
}
function getEndLogEntry(startLogEntry, filename) {
const timeDelta = process.hrtime(startLogEntry.start_timestamp);
const duration_ms = Math.round((timeDelta[0] * 1e9 + timeDelta[1]) / 1e6);
return {
action_name: "Transforming file",
action_phase: "end",
file_name: filename,
duration_ms,
log_entry_label: "Transforming file",
};
}

160
node_modules/metro/src/DeltaBundler/Worker.flow.js.flow generated vendored Normal file
View File

@@ -0,0 +1,160 @@
/**
* 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.
*
* @flow
* @format
* @oncall react_native
*/
import type {TransformResult} from './types';
import type {LogEntry} from 'metro-core/private/Logger';
import type {
JsTransformerConfig,
JsTransformOptions,
} from 'metro-transform-worker';
import traverse from '@babel/traverse';
import crypto from 'crypto';
import fs from 'fs';
import path from 'path';
export type {JsTransformOptions as TransformOptions} from 'metro-transform-worker';
type TransformerInterface = {
transform(
JsTransformerConfig,
string,
string,
Buffer,
JsTransformOptions,
): Promise<TransformResult<>>,
};
export type TransformerConfig = {
transformerPath: string,
transformerConfig: JsTransformerConfig,
...
};
type Data = Readonly<{
result: TransformResult<>,
sha1: string,
transformFileStartLogEntry: LogEntry,
transformFileEndLogEntry: LogEntry,
}>;
/**
* When the `Buffer` is sent over the worker thread it gets serialized into a JSON object.
* This helper method will deserialize it if needed.
*
* @returns `Buffer` representation of the JSON object.
* @returns `null` if the given object is nullish or not a serialized `Buffer` object.
*/
function asDeserializedBuffer(value: any): Buffer | null {
if (Buffer.isBuffer(value)) {
return value;
}
if (value && value.type === 'Buffer') {
return Buffer.from(value.data);
}
if (ArrayBuffer.isView(value)) {
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
}
return null;
}
export const transform = (
filename: string,
transformOptions: JsTransformOptions,
projectRoot: string,
transformerConfig: TransformerConfig,
fileBuffer?: Buffer,
): Promise<Data> => {
let data;
const fileBufferObject = asDeserializedBuffer(fileBuffer);
if (fileBufferObject) {
data = fileBufferObject;
} else {
data = fs.readFileSync(path.resolve(projectRoot, filename));
}
return transformFile(
filename,
data,
transformOptions,
projectRoot,
transformerConfig,
);
};
export type Worker = {
+transform: typeof transform,
};
async function transformFile(
filename: string,
data: Buffer,
transformOptions: JsTransformOptions,
projectRoot: string,
transformerConfig: TransformerConfig,
): Promise<Data> {
// eslint-disable-next-line no-useless-call
const Transformer: TransformerInterface = require.call(
null,
transformerConfig.transformerPath,
);
const transformFileStartLogEntry: LogEntry = {
action_name: 'Transforming file',
action_phase: 'start',
file_name: filename,
log_entry_label: 'Transforming file',
start_timestamp: process.hrtime(),
};
const sha1 = crypto.createHash('sha1').update(data).digest('hex');
const result = await Transformer.transform(
transformerConfig.transformerConfig,
projectRoot,
filename,
data,
transformOptions,
);
// The babel cache caches scopes and pathes for already traversed AST nodes.
// Clearing the cache here since the nodes of the transformed file are no longer referenced.
// This isn't stritcly necessary since the cache uses a WeakMap. However, WeakMap only permit
// that unreferenced keys are collected but the values still hold references to the Scope and NodePaths.
// Manually clearing the cache allows the GC to collect the Scope and NodePaths without checking if there
// exist any other references to the keys.
traverse.cache.clear();
const transformFileEndLogEntry = getEndLogEntry(
transformFileStartLogEntry,
filename,
);
return {
result,
sha1,
transformFileStartLogEntry,
transformFileEndLogEntry,
};
}
function getEndLogEntry(startLogEntry: LogEntry, filename: string): LogEntry {
const timeDelta = process.hrtime(startLogEntry.start_timestamp);
const duration_ms = Math.round((timeDelta[0] * 1e9 + timeDelta[1]) / 1e6);
return {
action_name: 'Transforming file',
action_phase: 'end',
file_name: filename,
duration_ms,
log_entry_label: 'Transforming file',
};
}

6
node_modules/metro/src/DeltaBundler/Worker.js generated vendored Normal file
View File

@@ -0,0 +1,6 @@
"use strict";
try {
require("metro-babel-register").unstable_registerForMetroMonorepo();
} catch {}
module.exports = require("./Worker.flow");

24
node_modules/metro/src/DeltaBundler/Worker.js.flow generated vendored Normal file
View File

@@ -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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
/* eslint-disable import/no-commonjs */
'use strict';
/*::
export type * from './Worker.flow';
*/
try {
require('metro-babel-register').unstable_registerForMetroMonorepo();
} catch {}
module.exports = require('./Worker.flow');

77
node_modules/metro/src/DeltaBundler/WorkerFarm.d.ts generated vendored Normal file
View File

@@ -0,0 +1,77 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<bbd52f1dc5e4a9253455034b585115ac>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/WorkerFarm.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {TransformResult} from '../DeltaBundler';
import type {TransformerConfig, TransformOptions, Worker} from './Worker';
import type {ConfigT} from 'metro-config';
import type {Readable} from 'stream';
type WorkerInterface = Readonly<
Omit<
Worker,
keyof {
end(): void | Promise<void>;
getStdout(): Readable;
getStderr(): Readable;
}
> & {
end(): void | Promise<void>;
getStdout(): Readable;
getStderr(): Readable;
}
>;
type TransformerResult = Readonly<{result: TransformResult; sha1: string}>;
declare class WorkerFarm {
_config: ConfigT;
_transformerConfig: TransformerConfig;
_worker: WorkerInterface | Worker;
constructor(config: ConfigT, transformerConfig: TransformerConfig);
kill(): Promise<void>;
transform(
filename: string,
options: TransformOptions,
fileBuffer?: Buffer,
): Promise<TransformerResult>;
_makeFarm(
absoluteWorkerPath: string,
exposedMethods: ReadonlyArray<string>,
numWorkers: number,
): WorkerInterface;
_computeWorkerKey(
method: string,
filename: string,
): null | undefined | string;
_formatGenericError(
err: Readonly<{message: string; stack?: string}>,
filename: string,
): TransformError;
_formatBabelError(
err: Readonly<{
message: string;
stack?: string;
type?: string;
codeFrame?: unknown;
loc: {line?: number; column?: number};
}>,
filename: string,
): TransformError;
}
export default WorkerFarm;
declare class TransformError extends SyntaxError {
type: string;
constructor(message: string);
}

116
node_modules/metro/src/DeltaBundler/WorkerFarm.js generated vendored Normal file
View File

@@ -0,0 +1,116 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _jestWorker = require("jest-worker");
var _metroCore = require("metro-core");
class WorkerFarm {
constructor(config, transformerConfig) {
this._config = config;
this._transformerConfig = transformerConfig;
const absoluteWorkerPath = require.resolve("./Worker.js");
if (this._config.maxWorkers > 1) {
const worker = this._makeFarm(
absoluteWorkerPath,
["transform"],
this._config.maxWorkers,
);
worker.getStdout().on("data", (chunk) => {
this._config.reporter.update({
type: "worker_stdout_chunk",
chunk: chunk.toString("utf8"),
});
});
worker.getStderr().on("data", (chunk) => {
this._config.reporter.update({
type: "worker_stderr_chunk",
chunk: chunk.toString("utf8"),
});
});
this._worker = worker;
} else {
this._worker = require("./Worker");
}
}
async kill() {
if (this._worker && typeof this._worker.end === "function") {
await this._worker.end();
}
}
async transform(filename, options, fileBuffer) {
try {
const data = await this._worker.transform(
filename,
options,
this._config.projectRoot,
this._transformerConfig,
fileBuffer,
);
_metroCore.Logger.log(data.transformFileStartLogEntry);
_metroCore.Logger.log(data.transformFileEndLogEntry);
return {
result: data.result,
sha1: data.sha1,
};
} catch (err) {
if (err.loc) {
throw this._formatBabelError(err, filename);
} else {
throw this._formatGenericError(err, filename);
}
}
}
_makeFarm(absoluteWorkerPath, exposedMethods, numWorkers) {
const env = {
...process.env,
FORCE_COLOR: 1,
};
return new _jestWorker.Worker(absoluteWorkerPath, {
computeWorkerKey: this._config.stickyWorkers
? this._computeWorkerKey
: undefined,
exposedMethods,
enableWorkerThreads: this._config.transformer.unstable_workerThreads,
forkOptions: {
env,
},
numWorkers,
workerSchedulingPolicy: "in-order",
});
}
_computeWorkerKey(method, filename) {
if (method === "transform") {
return filename;
}
return null;
}
_formatGenericError(err, filename) {
const error = new TransformError(`${filename}: ${err.message}`);
return Object.assign(error, {
stack: (err.stack || "").split("\n").slice(0, -1).join("\n"),
lineNumber: 0,
});
}
_formatBabelError(err, filename) {
const error = new TransformError(
`${err.type ?? "Error"}${err.message.includes(filename) ? "" : " in " + filename}: ${err.message}`,
);
return Object.assign(error, {
stack: err.stack,
snippet: err.codeFrame,
lineNumber: err.loc.line,
column: err.loc.column,
filename,
});
}
}
exports.default = WorkerFarm;
class TransformError extends SyntaxError {
type = "TransformError";
constructor(message) {
super(message);
Error.captureStackTrace && Error.captureStackTrace(this, TransformError);
}
}

195
node_modules/metro/src/DeltaBundler/WorkerFarm.js.flow generated vendored Normal file
View File

@@ -0,0 +1,195 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {TransformResult} from '../DeltaBundler';
import type {TransformerConfig, TransformOptions, Worker} from './Worker';
import type {ConfigT} from 'metro-config';
import type {Readable} from 'stream';
import {Worker as JestWorker} from 'jest-worker';
import {Logger} from 'metro-core';
type WorkerInterface = Readonly<{
...Worker,
end(): void | Promise<void>,
getStdout(): Readable,
getStderr(): Readable,
}>;
type TransformerResult = Readonly<{
result: TransformResult<>,
sha1: string,
}>;
export default class WorkerFarm {
_config: ConfigT;
_transformerConfig: TransformerConfig;
_worker: WorkerInterface | Worker;
constructor(config: ConfigT, transformerConfig: TransformerConfig) {
this._config = config;
this._transformerConfig = transformerConfig;
const absoluteWorkerPath = require.resolve('./Worker.js');
if (this._config.maxWorkers > 1) {
const worker = this._makeFarm(
absoluteWorkerPath,
['transform'],
this._config.maxWorkers,
);
worker.getStdout().on('data', chunk => {
this._config.reporter.update({
type: 'worker_stdout_chunk',
chunk: chunk.toString('utf8'),
});
});
worker.getStderr().on('data', chunk => {
this._config.reporter.update({
type: 'worker_stderr_chunk',
chunk: chunk.toString('utf8'),
});
});
this._worker = worker;
} else {
// eslint-disable-next-line import/no-commonjs
this._worker = require('./Worker') as Worker;
}
}
async kill(): Promise<void> {
if (this._worker && typeof this._worker.end === 'function') {
await this._worker.end();
}
}
async transform(
filename: string,
options: TransformOptions,
fileBuffer?: Buffer,
): Promise<TransformerResult> {
try {
const data = await this._worker.transform(
filename,
options,
this._config.projectRoot,
this._transformerConfig,
fileBuffer,
);
Logger.log(data.transformFileStartLogEntry);
Logger.log(data.transformFileEndLogEntry);
return {
result: data.result,
sha1: data.sha1,
};
} catch (err) {
if (err.loc) {
throw this._formatBabelError(err, filename);
} else {
throw this._formatGenericError(err, filename);
}
}
}
_makeFarm(
absoluteWorkerPath: string,
exposedMethods: ReadonlyArray<string>,
numWorkers: number,
): WorkerInterface {
const env = {
...process.env,
// Force color to print syntax highlighted code frames.
FORCE_COLOR: 1,
};
return new JestWorker<Worker>(absoluteWorkerPath, {
computeWorkerKey: this._config.stickyWorkers
? // $FlowFixMe[method-unbinding] added when improving typing for this parameters
// $FlowFixMe[incompatible-type]
this._computeWorkerKey
: undefined,
exposedMethods,
enableWorkerThreads: this._config.transformer.unstable_workerThreads,
forkOptions: {env},
numWorkers,
// Prefer using lower numbered available workers repeatedly rather than
// spreading jobs over the whole pool evenly (round-robin). This is more
// likely to use faster, hot workers earlier in graph traversal, when
// we want to be able to fan out as quickly as possible, and therefore
// gets us to full speed faster.
workerSchedulingPolicy: 'in-order',
});
}
_computeWorkerKey(method: string, filename: string): ?string {
// Only when transforming a file we want to stick to the same worker; and
// we'll shard by file path. If not; we return null, which tells the worker
// to pick the first available one.
if (method === 'transform') {
return filename;
}
return null;
}
_formatGenericError(
err: Readonly<{message: string, stack?: string, ...}>,
filename: string,
): TransformError {
const error = new TransformError(`${filename}: ${err.message}`);
// $FlowFixMe[unsafe-object-assign]
return Object.assign(error, {
stack: (err.stack || '').split('\n').slice(0, -1).join('\n'),
lineNumber: 0,
});
}
_formatBabelError(
err: Readonly<{
message: string,
stack?: string,
type?: string,
codeFrame?: unknown,
loc: {line?: number, column?: number, ...},
...
}>,
filename: string,
): TransformError {
const error = new TransformError(
`${err.type ?? 'Error'}${
err.message.includes(filename) ? '' : ' in ' + filename
}: ${err.message}`,
);
// $FlowFixMe[prop-missing]
// $FlowExpectedError[unsafe-object-assign] : TODO(t67543470): Change this to properly extend the error.
return Object.assign(error, {
stack: err.stack,
snippet: err.codeFrame,
lineNumber: err.loc.line,
column: err.loc.column,
filename,
});
}
}
class TransformError extends SyntaxError {
type: string = 'TransformError';
constructor(message: string) {
super(message);
Error.captureStackTrace && Error.captureStackTrace(this, TransformError);
}
}

View File

@@ -0,0 +1,17 @@
"use strict";
const fs = require("fs");
module.exports = {
getHasteName(filename) {
const matches = fs
.readFileSync(filename, "utf8")
.match(/@providesModule ([^\n]+)/);
if (!matches) {
return undefined;
}
return matches[1];
},
getCacheKey() {
return "hasteImplFixture";
},
};

37
node_modules/metro/src/DeltaBundler/buildSubgraph.d.ts generated vendored Normal file
View File

@@ -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.
*
* @noformat
* @generated SignedSource<<1e334cd36bb429700b82654f1ddab0a0>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/buildSubgraph.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {RequireContext} from '../lib/contextModule';
import type {
ModuleData,
ResolvedDependency,
ResolveFn,
TransformFn,
} from './types';
type Parameters<T> = Readonly<{
resolve: ResolveFn;
transform: TransformFn<T>;
shouldTraverse: ($$PARAM_0$$: ResolvedDependency) => boolean;
}>;
export declare function buildSubgraph<T>(
entryPaths: ReadonlySet<string>,
resolvedContexts: ReadonlyMap<string, null | undefined | RequireContext>,
$$PARAM_2$$: Parameters<T>,
): Promise<{
moduleData: Map<string, ModuleData<T>>;
errors: Map<string, Error>;
}>;

116
node_modules/metro/src/DeltaBundler/buildSubgraph.js generated vendored Normal file
View File

@@ -0,0 +1,116 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.buildSubgraph = buildSubgraph;
var _contextModule = require("../lib/contextModule");
var _isResolvedDependency = require("../lib/isResolvedDependency");
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function resolveDependencies(parentPath, dependencies, resolve) {
const maybeResolvedDeps = new Map();
const resolvedContexts = new Map();
for (const dep of dependencies) {
let maybeResolvedDep;
const key = dep.data.key;
const { contextParams } = dep.data;
if (contextParams) {
const from = _path.default.join(parentPath, "..", dep.name);
const absolutePath = (0, _contextModule.deriveAbsolutePathFromContext)(
from,
contextParams,
);
const resolvedContext = {
filter: new RegExp(
contextParams.filter.pattern,
contextParams.filter.flags,
),
from,
mode: contextParams.mode,
recursive: contextParams.recursive,
};
resolvedContexts.set(key, resolvedContext);
maybeResolvedDep = {
absolutePath,
data: dep,
};
} else {
try {
maybeResolvedDep = {
absolutePath: resolve(parentPath, dep).filePath,
data: dep,
};
} catch (error) {
if (dep.data.isOptional !== true) {
throw error;
}
maybeResolvedDep = {
data: dep,
};
}
}
if (maybeResolvedDeps.has(key)) {
throw new Error(
`resolveDependencies: Found duplicate dependency key '${key}' in ${parentPath}`,
);
}
maybeResolvedDeps.set(key, maybeResolvedDep);
}
return {
dependencies: maybeResolvedDeps,
resolvedContexts,
};
}
async function buildSubgraph(
entryPaths,
resolvedContexts,
{ resolve, transform, shouldTraverse },
) {
const moduleData = new Map();
const errors = new Map();
const visitedPaths = new Set();
async function visit(absolutePath, requireContext) {
if (visitedPaths.has(absolutePath)) {
return;
}
visitedPaths.add(absolutePath);
const transformResult = await transform(absolutePath, requireContext);
const resolutionResult = resolveDependencies(
absolutePath,
transformResult.dependencies,
resolve,
);
moduleData.set(absolutePath, {
...transformResult,
...resolutionResult,
});
await Promise.all(
[...resolutionResult.dependencies.values()]
.filter(
(dependency) =>
(0, _isResolvedDependency.isResolvedDependency)(dependency) &&
shouldTraverse(dependency),
)
.map((dependency) =>
visit(
dependency.absolutePath,
resolutionResult.resolvedContexts.get(dependency.data.data.key),
).catch((error) => errors.set(dependency.absolutePath, error)),
),
);
}
await Promise.all(
[...entryPaths].map((absolutePath) =>
visit(absolutePath, resolvedContexts.get(absolutePath)).catch((error) =>
errors.set(absolutePath, error),
),
),
);
return {
errors,
moduleData,
};
}

View File

@@ -0,0 +1,161 @@
/**
* 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.
*
* @flow strict-local
* @format
*/
import type {RequireContext} from '../lib/contextModule';
import type {
Dependency,
ModuleData,
ResolvedDependency,
ResolveFn,
TransformFn,
TransformResultDependency,
} from './types';
import {deriveAbsolutePathFromContext} from '../lib/contextModule';
import {isResolvedDependency} from '../lib/isResolvedDependency';
import path from 'path';
type Parameters<T> = Readonly<{
resolve: ResolveFn,
transform: TransformFn<T>,
shouldTraverse: ResolvedDependency => boolean,
}>;
function resolveDependencies(
parentPath: string,
dependencies: ReadonlyArray<TransformResultDependency>,
resolve: ResolveFn,
): {
dependencies: Map<string, Dependency>,
resolvedContexts: Map<string, RequireContext>,
} {
const maybeResolvedDeps = new Map<string, Dependency>();
const resolvedContexts = new Map<string, RequireContext>();
for (const dep of dependencies) {
let maybeResolvedDep: Dependency;
const key = dep.data.key;
// `require.context`
const {contextParams} = dep.data;
if (contextParams) {
// Ensure the filepath has uniqueness applied to ensure multiple `require.context`
// statements can be used to target the same file with different properties.
const from = path.join(parentPath, '..', dep.name);
const absolutePath = deriveAbsolutePathFromContext(from, contextParams);
const resolvedContext: RequireContext = {
filter: new RegExp(
contextParams.filter.pattern,
contextParams.filter.flags,
),
from,
mode: contextParams.mode,
recursive: contextParams.recursive,
};
resolvedContexts.set(key, resolvedContext);
maybeResolvedDep = {
absolutePath,
data: dep,
};
} else {
try {
maybeResolvedDep = {
absolutePath: resolve(parentPath, dep).filePath,
data: dep,
};
} catch (error) {
// Ignore unavailable optional dependencies. They are guarded
// with a try-catch block and will be handled during runtime.
if (dep.data.isOptional !== true) {
throw error;
}
maybeResolvedDep = {
data: dep,
};
}
}
if (maybeResolvedDeps.has(key)) {
throw new Error(
`resolveDependencies: Found duplicate dependency key '${key}' in ${parentPath}`,
);
}
maybeResolvedDeps.set(key, maybeResolvedDep);
}
return {
dependencies: maybeResolvedDeps,
resolvedContexts,
};
}
export async function buildSubgraph<T>(
entryPaths: ReadonlySet<string>,
resolvedContexts: ReadonlyMap<string, ?RequireContext>,
{resolve, transform, shouldTraverse}: Parameters<T>,
): Promise<{
moduleData: Map<string, ModuleData<T>>,
errors: Map<string, Error>,
}> {
const moduleData: Map<string, ModuleData<T>> = new Map();
const errors: Map<string, Error> = new Map();
const visitedPaths: Set<string> = new Set();
async function visit(
absolutePath: string,
requireContext: ?RequireContext,
): Promise<void> {
if (visitedPaths.has(absolutePath)) {
return;
}
visitedPaths.add(absolutePath);
const transformResult = await transform(absolutePath, requireContext);
// Get the absolute path of all sub-dependencies (some of them could have been
// moved but maintain the same relative path).
const resolutionResult = resolveDependencies(
absolutePath,
transformResult.dependencies,
resolve,
);
moduleData.set(absolutePath, {
...transformResult,
...resolutionResult,
});
await Promise.all(
[...resolutionResult.dependencies.values()]
.filter(
dependency =>
isResolvedDependency(dependency) && shouldTraverse(dependency),
)
.map(dependency =>
visit(
dependency.absolutePath,
resolutionResult.resolvedContexts.get(dependency.data.data.key),
).catch(error => errors.set(dependency.absolutePath, error)),
),
);
}
await Promise.all(
[...entryPaths].map(absolutePath =>
visit(absolutePath, resolvedContexts.get(absolutePath)).catch(error =>
errors.set(absolutePath, error),
),
),
);
return {errors, moduleData};
}

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<1a82c2238a94514c16e7bb93bf49d8c9>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/getTransformCacheKey.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {TransformerConfig} from './Worker';
declare function getTransformCacheKey(opts: {
readonly cacheVersion: string;
readonly projectRoot: string;
readonly transformerConfig: TransformerConfig;
}): string;
export default getTransformCacheKey;

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = getTransformCacheKey;
var _crypto = _interopRequireDefault(require("crypto"));
var _metroCacheKey = require("metro-cache-key");
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const VERSION = require("../../package.json").version;
function getTransformCacheKey(opts) {
const { transformerPath, transformerConfig } = opts.transformerConfig;
const Transformer = require.call(null, transformerPath);
const transformerKey = Transformer.getCacheKey
? Transformer.getCacheKey(transformerConfig, {
projectRoot: opts.projectRoot,
})
: "";
return _crypto.default
.createHash("sha1")
.update(
[
"metro-cache",
VERSION,
opts.cacheVersion,
(0, _metroCacheKey.getCacheKey)([require.resolve(transformerPath)]),
transformerKey,
transformerConfig.globalPrefix,
].join("$"),
)
.digest("hex");
}

View File

@@ -0,0 +1,56 @@
/**
* 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.
*
* @flow
* @format
* @oncall react_native
*/
import type {TransformerConfig} from './Worker';
import type {JsTransformerConfig} from 'metro-transform-worker';
import crypto from 'crypto';
import {getCacheKey} from 'metro-cache-key';
// eslint-disable-next-line import/no-commonjs
const VERSION = require('../../package.json').version;
type CacheKeyProvider = {
getCacheKey?: (
config: JsTransformerConfig,
opts?: Readonly<{projectRoot: string}>,
) => string,
};
export default function getTransformCacheKey(opts: {
+cacheVersion: string,
+projectRoot: string,
+transformerConfig: TransformerConfig,
}): string {
const {transformerPath, transformerConfig} = opts.transformerConfig;
// eslint-disable-next-line no-useless-call
const Transformer: CacheKeyProvider = require.call(null, transformerPath);
const transformerKey = Transformer.getCacheKey
? Transformer.getCacheKey(transformerConfig, {
projectRoot: opts.projectRoot,
})
: '';
return crypto
.createHash('sha1')
.update(
[
'metro-cache',
VERSION,
opts.cacheVersion,
getCacheKey([require.resolve(transformerPath)]),
transformerKey,
transformerConfig.globalPrefix,
].join('$'),
)
.digest('hex');
}

24
node_modules/metro/src/DeltaBundler/mergeDeltas.d.ts generated vendored Normal file
View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<46981e9bc1ef3945b99b147cbdf9ec5d>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/mergeDeltas.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {DeltaBundle} from 'metro-runtime/src/modules/types';
declare function mergeDeltas(
delta1: DeltaBundle,
delta2: DeltaBundle,
): DeltaBundle;
export default mergeDeltas;

56
node_modules/metro/src/DeltaBundler/mergeDeltas.js generated vendored Normal file
View File

@@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = mergeDeltas;
function mergeDeltas(delta1, delta2) {
const added1 = new Map(delta1.added);
const modified1 = new Map(delta1.modified);
const deleted1 = new Set(delta1.deleted);
const added2 = new Map(delta2.added);
const modified2 = new Map(delta2.modified);
const deleted2 = new Set(delta2.deleted);
const added = new Map();
const modified = new Map();
const deleted = new Set();
for (const [id, code] of added1) {
if (!deleted2.has(id) && !modified2.has(id)) {
added.set(id, code);
}
}
for (const [id, code] of modified1) {
if (!deleted2.has(id) && !modified2.has(id)) {
modified.set(id, code);
}
}
for (const id of deleted1) {
if (!added2.has(id)) {
deleted.add(id);
}
}
for (const [id, code] of added2) {
if (deleted1.has(id)) {
modified.set(id, code);
} else {
added.set(id, code);
}
}
for (const [id, code] of modified2) {
if (added1.has(id)) {
added.set(id, code);
} else {
modified.set(id, code);
}
}
for (const id of deleted2) {
if (!added1.has(id)) {
deleted.add(id);
}
}
return {
added: [...added.entries()],
modified: [...modified.entries()],
deleted: [...deleted],
};
}

View File

@@ -0,0 +1,73 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {DeltaBundle} from 'metro-runtime/src/modules/types';
export default function mergeDeltas(
delta1: DeltaBundle,
delta2: DeltaBundle,
): DeltaBundle {
const added1 = new Map(delta1.added);
const modified1 = new Map(delta1.modified);
const deleted1 = new Set(delta1.deleted);
const added2 = new Map(delta2.added);
const modified2 = new Map(delta2.modified);
const deleted2 = new Set(delta2.deleted);
const added = new Map<number, string>();
const modified = new Map<number, string>();
const deleted = new Set<number>();
for (const [id, code] of added1) {
if (!deleted2.has(id) && !modified2.has(id)) {
added.set(id, code);
}
}
for (const [id, code] of modified1) {
if (!deleted2.has(id) && !modified2.has(id)) {
modified.set(id, code);
}
}
for (const id of deleted1) {
if (!added2.has(id)) {
deleted.add(id);
}
}
for (const [id, code] of added2) {
if (deleted1.has(id)) {
modified.set(id, code);
} else {
added.set(id, code);
}
}
for (const [id, code] of modified2) {
if (added1.has(id)) {
added.set(id, code);
} else {
modified.set(id, code);
}
}
for (const id of deleted2) {
if (!added1.has(id)) {
deleted.add(id);
}
}
return {
added: [...added.entries()],
modified: [...modified.entries()],
deleted: [...deleted],
};
}

170
node_modules/metro/src/DeltaBundler/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,170 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<feddf9cb00d4a097d348531773c9f0bd>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/DeltaBundler/types.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {RequireContext} from '../lib/contextModule';
import type {RequireContextParams} from '../ModuleGraph/worker/collectDependencies';
import type {ReadonlySourceLocation} from '../shared/types';
import type {Graph} from './Graph';
import type {JsTransformOptions} from 'metro-transform-worker';
import CountingSet from '../lib/CountingSet';
export type MixedOutput = {readonly data: unknown; readonly type: string};
export type AsyncDependencyType = 'async' | 'maybeSync' | 'prefetch' | 'weak';
export type TransformResultDependency = Readonly<{
/**
* The literal name provided to a require or import call. For example 'foo' in
* case of `require('foo')`.
*/
name: string;
/**
* Extra data returned by the dependency extractor.
*/
data: Readonly<{
/**
* A locally unique key for this dependency within the current module.
*/
key: string;
/**
* If not null, this dependency is due to a dynamic `import()` or `__prefetchImport()` call.
*/
asyncType: AsyncDependencyType | null;
/**
* True if the dependency is declared with a static "import x from 'y'" or
* an import() call.
*/
isESMImport: boolean;
/**
* The dependency is enclosed in a try/catch block.
*/
isOptional?: boolean;
locs: ReadonlyArray<ReadonlySourceLocation>;
/** Context for requiring a collection of modules. */
contextParams?: RequireContextParams;
}>;
}>;
export type ResolvedDependency = Readonly<{
absolutePath: string;
data: TransformResultDependency;
}>;
export type Dependency =
| ResolvedDependency
| Readonly<{data: TransformResultDependency}>;
export type Module<T = MixedOutput> = Readonly<{
dependencies: Map<string, Dependency>;
inverseDependencies: CountingSet<string>;
output: ReadonlyArray<T>;
path: string;
getSource: () => Buffer;
unstable_transformResultKey?: null | undefined | string;
}>;
export type ModuleData<T = MixedOutput> = Readonly<{
dependencies: ReadonlyMap<string, Dependency>;
resolvedContexts: ReadonlyMap<string, RequireContext>;
output: ReadonlyArray<T>;
getSource: () => Buffer;
unstable_transformResultKey?: null | undefined | string;
}>;
export type Dependencies<T = MixedOutput> = Map<string, Module<T>>;
export type ReadOnlyDependencies<T = MixedOutput> = ReadonlyMap<
string,
Module<T>
>;
export type TransformInputOptions = Omit<
JsTransformOptions,
'inlinePlatform' | 'inlineRequires'
>;
export type GraphInputOptions = Readonly<{
entryPoints: ReadonlySet<string>;
transformOptions: TransformInputOptions;
}>;
export interface ReadOnlyGraph<T = MixedOutput> {
readonly entryPoints: ReadonlySet<string>;
readonly transformOptions: Readonly<TransformInputOptions>;
readonly dependencies: ReadOnlyDependencies<T>;
}
export type {Graph};
export type TransformResult<T = MixedOutput> = Readonly<{
dependencies: ReadonlyArray<TransformResultDependency>;
output: ReadonlyArray<T>;
unstable_transformResultKey?: null | undefined | string;
}>;
export type TransformResultWithSource<T = MixedOutput> = Readonly<
Omit<TransformResult<T>, keyof {getSource: () => Buffer}> & {
getSource: () => Buffer;
}
>;
export type TransformFn<T = MixedOutput> = (
$$PARAM_0$$: string,
$$PARAM_1$$: null | undefined | RequireContext,
) => Promise<TransformResultWithSource<T>>;
export type ResolveFn = (
from: string,
dependency: TransformResultDependency,
) => BundlerResolution;
export type AllowOptionalDependenciesWithOptions = {
readonly exclude: Array<string>;
};
export type AllowOptionalDependencies =
| boolean
| AllowOptionalDependenciesWithOptions;
export type BundlerResolution = Readonly<{
type: 'sourceFile';
filePath: string;
}>;
export type Options<T = MixedOutput> = Readonly<{
resolve: ResolveFn;
transform: TransformFn<T>;
transformOptions: TransformInputOptions;
onProgress:
| null
| undefined
| ((numProcessed: number, total: number) => unknown);
lazy: boolean;
unstable_allowRequireContext: boolean;
unstable_enablePackageExports: boolean;
unstable_incrementalResolution: boolean;
shallow: boolean;
}>;
export type DeltaResult<T = MixedOutput> = {
readonly added: Map<string, Module<T>>;
readonly modified: Map<string, Module<T>>;
readonly deleted: Set<string>;
readonly reset: boolean;
};
export type SerializerOptions = Readonly<{
asyncRequireModulePath: string;
createModuleId: ($$PARAM_0$$: string) => number;
dev: boolean;
getRunModuleStatement: (
moduleId: number | string,
globalPrefix: string,
) => string;
globalPrefix: string;
includeAsyncPaths: boolean;
inlineSourceMap: null | undefined | boolean;
modulesOnly: boolean;
processModuleFilter: (module: Module) => boolean;
projectRoot: string;
runBeforeMainModule: ReadonlyArray<string>;
runModule: boolean;
serverRoot: string;
shouldAddToIgnoreList: ($$PARAM_0$$: Module) => boolean;
sourceMapUrl: null | undefined | string;
sourceUrl: null | undefined | string;
getSourceUrl: null | undefined | (($$PARAM_0$$: Module) => string);
}>;

6
node_modules/metro/src/DeltaBundler/types.js generated vendored Normal file
View File

@@ -0,0 +1,6 @@
"use strict";
var _CountingSet = _interopRequireDefault(require("../lib/CountingSet"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}

190
node_modules/metro/src/DeltaBundler/types.js.flow generated vendored Normal file
View File

@@ -0,0 +1,190 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {RequireContext} from '../lib/contextModule';
import type {RequireContextParams} from '../ModuleGraph/worker/collectDependencies';
import type {ReadonlySourceLocation} from '../shared/types';
import type {Graph} from './Graph';
import type {JsTransformOptions} from 'metro-transform-worker';
import CountingSet from '../lib/CountingSet';
export type MixedOutput = {
+data: unknown,
+type: string,
};
export type AsyncDependencyType = 'async' | 'maybeSync' | 'prefetch' | 'weak';
export type TransformResultDependency = Readonly<{
/**
* The literal name provided to a require or import call. For example 'foo' in
* case of `require('foo')`.
*/
name: string,
/**
* Extra data returned by the dependency extractor.
*/
data: Readonly<{
/**
* A locally unique key for this dependency within the current module.
*/
key: string,
/**
* If not null, this dependency is due to a dynamic `import()` or `__prefetchImport()` call.
*/
asyncType: AsyncDependencyType | null,
/**
* True if the dependency is declared with a static "import x from 'y'" or
* an import() call.
*/
isESMImport: boolean,
/**
* The dependency is enclosed in a try/catch block.
*/
isOptional?: boolean,
locs: ReadonlyArray<ReadonlySourceLocation>,
/** Context for requiring a collection of modules. */
contextParams?: RequireContextParams,
}>,
}>;
export type ResolvedDependency = Readonly<{
absolutePath: string,
data: TransformResultDependency,
}>;
export type Dependency =
| ResolvedDependency
| Readonly<{
data: TransformResultDependency,
}>;
export type Module<T = MixedOutput> = Readonly<{
dependencies: Map<string, Dependency>,
inverseDependencies: CountingSet<string>,
output: ReadonlyArray<T>,
path: string,
getSource: () => Buffer,
unstable_transformResultKey?: ?string,
}>;
export type ModuleData<T = MixedOutput> = Readonly<{
dependencies: ReadonlyMap<string, Dependency>,
resolvedContexts: ReadonlyMap<string, RequireContext>,
output: ReadonlyArray<T>,
getSource: () => Buffer,
unstable_transformResultKey?: ?string,
}>;
export type Dependencies<T = MixedOutput> = Map<string, Module<T>>;
export type ReadOnlyDependencies<T = MixedOutput> = ReadonlyMap<
string,
Module<T>,
>;
export type TransformInputOptions = Omit<
JsTransformOptions,
'inlinePlatform' | 'inlineRequires',
>;
export type GraphInputOptions = Readonly<{
entryPoints: ReadonlySet<string>,
// Unused in core but useful for custom serializers / experimentalSerializerHook
transformOptions: TransformInputOptions,
}>;
export interface ReadOnlyGraph<T = MixedOutput> {
+entryPoints: ReadonlySet<string>;
// Unused in core but useful for custom serializers / experimentalSerializerHook
+transformOptions: Readonly<TransformInputOptions>;
+dependencies: ReadOnlyDependencies<T>;
}
export type {Graph};
export type TransformResult<T = MixedOutput> = Readonly<{
dependencies: ReadonlyArray<TransformResultDependency>,
output: ReadonlyArray<T>,
unstable_transformResultKey?: ?string,
}>;
export type TransformResultWithSource<T = MixedOutput> = Readonly<{
...TransformResult<T>,
getSource: () => Buffer,
}>;
export type TransformFn<T = MixedOutput> = (
string,
?RequireContext,
) => Promise<TransformResultWithSource<T>>;
export type ResolveFn = (
from: string,
dependency: TransformResultDependency,
) => BundlerResolution;
export type AllowOptionalDependenciesWithOptions = {
+exclude: Array<string>,
};
export type AllowOptionalDependencies =
| boolean
| AllowOptionalDependenciesWithOptions;
export type BundlerResolution = Readonly<{
type: 'sourceFile',
filePath: string,
}>;
export type Options<T = MixedOutput> = Readonly<{
resolve: ResolveFn,
transform: TransformFn<T>,
transformOptions: TransformInputOptions,
onProgress: ?(numProcessed: number, total: number) => unknown,
lazy: boolean,
unstable_allowRequireContext: boolean,
unstable_enablePackageExports: boolean,
unstable_incrementalResolution: boolean,
shallow: boolean,
}>;
export type DeltaResult<T = MixedOutput> = {
+added: Map<string, Module<T>>,
+modified: Map<string, Module<T>>,
+deleted: Set<string>,
+reset: boolean,
};
export type SerializerOptions = Readonly<{
asyncRequireModulePath: string,
createModuleId: string => number,
dev: boolean,
getRunModuleStatement: (
moduleId: number | string,
globalPrefix: string,
) => string,
globalPrefix: string,
includeAsyncPaths: boolean,
inlineSourceMap: ?boolean,
modulesOnly: boolean,
processModuleFilter: (module: Module<>) => boolean,
projectRoot: string,
runBeforeMainModule: ReadonlyArray<string>,
runModule: boolean,
serverRoot: string,
shouldAddToIgnoreList: (Module<>) => boolean,
sourceMapUrl: ?string,
sourceUrl: ?string,
getSourceUrl: ?(Module<>) => string,
}>;

98
node_modules/metro/src/HmrServer.d.ts generated vendored Normal file
View File

@@ -0,0 +1,98 @@
/**
* 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.
*
* @noformat
* @generated SignedSource<<ab4c245134631e14db114a9d49da79d1>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/HmrServer.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {
RevisionId,
default as IncrementalBundler,
} from './IncrementalBundler';
import type {GraphOptions} from './shared/types';
import type {ConfigT, RootPerfLogger} from 'metro-config';
import type {
HmrErrorMessage,
HmrUpdateMessage,
} from 'metro-runtime/src/modules/types';
export type Client = {
optedIntoHMR: boolean;
revisionIds: Array<RevisionId>;
readonly sendFn: ($$PARAM_0$$: string) => void;
};
type ClientGroup = {
readonly clients: Set<Client>;
clientUrl: URL;
revisionId: RevisionId;
readonly unlisten: () => void;
readonly graphOptions: GraphOptions;
};
/**
* The HmrServer (Hot Module Reloading) implements a lightweight interface
* to communicate easily to the logic in the React Native repository (which
* is the one that handles the Web Socket connections).
*
* This interface allows the HmrServer to hook its own logic to WS clients
* getting connected, disconnected or having errors (through the
* `onClientConnect`, `onClientDisconnect` and `onClientError` methods).
*/
declare class HmrServer<TClient extends Client> {
_config: ConfigT;
_bundler: IncrementalBundler;
_createModuleId: (path: string) => number;
_clientGroups: Map<RevisionId, ClientGroup>;
constructor(
bundler: IncrementalBundler,
createModuleId: (path: string) => number,
config: ConfigT,
);
onClientConnect: (
requestUrl: string,
sendFn: (data: string) => void,
) => Promise<Client>;
_registerEntryPoint(
client: Client,
originalRequestUrl: string,
sendFn: (data: string) => void,
): Promise<void>;
onClientMessage: (
client: TClient,
message: string | Buffer | ArrayBuffer | Array<Buffer>,
sendFn: (data: string) => void,
) => Promise<void>;
onClientError: (client: TClient, e: Error) => void;
onClientDisconnect: (client: TClient) => void;
_handleFileChange(
group: ClientGroup,
options: {isInitialUpdate: boolean},
changeEvent:
| null
| undefined
| {
readonly logger: null | undefined | RootPerfLogger;
readonly changeId?: string;
},
): Promise<void>;
_prepareMessage(
group: ClientGroup,
options: {isInitialUpdate: boolean},
changeEvent:
| null
| undefined
| {
readonly logger: null | undefined | RootPerfLogger;
readonly changeId?: string;
},
): Promise<HmrUpdateMessage | HmrErrorMessage>;
}
export default HmrServer;

346
node_modules/metro/src/HmrServer.js generated vendored Normal file
View File

@@ -0,0 +1,346 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _hmrJSBundle = _interopRequireDefault(
require("./DeltaBundler/Serializers/hmrJSBundle"),
);
var _GraphNotFoundError = _interopRequireDefault(
require("./IncrementalBundler/GraphNotFoundError"),
);
var _RevisionNotFoundError = _interopRequireDefault(
require("./IncrementalBundler/RevisionNotFoundError"),
);
var _debounceAsyncQueue = _interopRequireDefault(
require("./lib/debounceAsyncQueue"),
);
var _formatBundlingError = _interopRequireDefault(
require("./lib/formatBundlingError"),
);
var _getGraphId = _interopRequireDefault(require("./lib/getGraphId"));
var _parseBundleOptionsFromBundleRequestUrl = _interopRequireDefault(
require("./lib/parseBundleOptionsFromBundleRequestUrl"),
);
var _splitBundleOptions = _interopRequireDefault(
require("./lib/splitBundleOptions"),
);
var transformHelpers = _interopRequireWildcard(
require("./lib/transformHelpers"),
);
var _metroCore = require("metro-core");
var _nullthrows = _interopRequireDefault(require("nullthrows"));
function _interopRequireWildcard(e, t) {
if ("function" == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ("object" != typeof e && "function" != typeof e))
return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
"default" !== t &&
{}.hasOwnProperty.call(e, t) &&
((i =
(o = Object.defineProperty) &&
Object.getOwnPropertyDescriptor(e, t)) &&
(i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const debug = require("debug")("Metro:HMR");
const { createActionStartEntry, createActionEndEntry, log } = _metroCore.Logger;
function send(sendFns, message) {
const strMessage = JSON.stringify(message);
sendFns.forEach((sendFn) => sendFn(strMessage));
}
class HmrServer {
constructor(bundler, createModuleId, config) {
this._config = config;
this._bundler = bundler;
this._createModuleId = createModuleId;
this._clientGroups = new Map();
}
onClientConnect = async (requestUrl, sendFn) => {
return {
sendFn,
revisionIds: [],
optedIntoHMR: false,
};
};
async _registerEntryPoint(client, originalRequestUrl, sendFn) {
debug("Registering entry point: %s", originalRequestUrl);
const requestUrl =
this._config.server.rewriteRequestUrl(originalRequestUrl);
debug("Rewritten as: %s", requestUrl);
const { bundleType: _bundleType, ...options } = (0,
_parseBundleOptionsFromBundleRequestUrl.default)(
requestUrl,
new Set(this._config.resolver.platforms),
);
const { entryFile, resolverOptions, transformOptions, graphOptions } = (0,
_splitBundleOptions.default)(options);
const resolutionFn = await transformHelpers.getResolveDependencyFn(
this._bundler.getBundler(),
transformOptions.platform,
resolverOptions,
);
const resolvedEntryFilePath = resolutionFn(
(this._config.server.unstable_serverRoot ?? this._config.projectRoot) +
"/.",
{
name: entryFile,
data: {
key: entryFile,
asyncType: null,
isESMImport: false,
locs: [],
},
},
).filePath;
const graphId = (0, _getGraphId.default)(
resolvedEntryFilePath,
transformOptions,
{
resolverOptions,
shallow: graphOptions.shallow,
lazy: graphOptions.lazy,
unstable_allowRequireContext:
this._config.transformer.unstable_allowRequireContext,
},
);
const revPromise = this._bundler.getRevisionByGraphId(graphId);
if (!revPromise) {
send([sendFn], {
type: "error",
body: (0, _formatBundlingError.default)(
new _GraphNotFoundError.default(graphId),
),
});
return;
}
const { graph, id } = await revPromise;
client.revisionIds.push(id);
let clientGroup = this._clientGroups.get(id);
if (clientGroup != null) {
clientGroup.clients.add(client);
} else {
const clientUrl = new URL(requestUrl);
clientUrl.protocol = "http";
const clientQuery = clientUrl.searchParams;
clientQuery.delete("bundleEntry");
clientQuery.set("dev", clientQuery.get("dev") || "true");
clientQuery.set("minify", clientQuery.get("minify") || "false");
clientQuery.set("modulesOnly", "true");
clientQuery.set("runModule", clientQuery.get("runModule") || "false");
clientQuery.set("shallow", "true");
clientGroup = {
clients: new Set([client]),
clientUrl: new URL(clientUrl),
revisionId: id,
graphOptions,
unlisten: () => unlisten(),
};
this._clientGroups.set(id, clientGroup);
let latestChangeEvent = null;
const debounceCallHandleFileChange = (0, _debounceAsyncQueue.default)(
async () => {
await this._handleFileChange(
(0, _nullthrows.default)(clientGroup),
{
isInitialUpdate: false,
},
latestChangeEvent,
);
},
50,
);
const unlisten = this._bundler
.getDeltaBundler()
.listen(graph, async (changeEvent) => {
latestChangeEvent = changeEvent;
await debounceCallHandleFileChange();
});
}
await this._handleFileChange(clientGroup, {
isInitialUpdate: true,
});
send([sendFn], {
type: "bundle-registered",
});
}
onClientMessage = async (client, message, sendFn) => {
let data;
try {
data = JSON.parse(String(message));
} catch (error) {
send([sendFn], {
type: "error",
body: (0, _formatBundlingError.default)(error),
});
return Promise.resolve();
}
if (data && data.type) {
switch (data.type) {
case "register-entrypoints":
return Promise.all(
data.entryPoints.map((entryPoint) =>
this._registerEntryPoint(client, entryPoint, sendFn),
),
);
case "log":
if (this._config.server.forwardClientLogs) {
this._config.reporter.update({
type: "client_log",
level: data.level,
data: data.data,
mode: data.mode,
});
}
break;
case "log-opt-in":
client.optedIntoHMR = true;
break;
case "heartbeat":
debug("Heartbeat received");
sendFn(String(message));
break;
default:
break;
}
}
return Promise.resolve();
};
onClientError = (client, e) => {
this._config.reporter.update({
type: "hmr_client_error",
error: e,
});
this.onClientDisconnect(client);
};
onClientDisconnect = (client) => {
client.revisionIds.forEach((revisionId) => {
const group = this._clientGroups.get(revisionId);
if (group != null) {
if (group.clients.size === 1) {
this._clientGroups.delete(revisionId);
group.unlisten();
} else {
group.clients.delete(client);
}
}
});
};
async _handleFileChange(group, options, changeEvent) {
const logger = !options.isInitialUpdate ? changeEvent?.logger : null;
if (logger) {
logger.point("fileChange_end");
logger.point("hmrPrepareAndSendMessage_start");
}
const optedIntoHMR = [...group.clients].some(
(client) => client.optedIntoHMR,
);
const processingHmrChange = log(
createActionStartEntry({
action_name: optedIntoHMR
? "Processing HMR change"
: "Processing HMR change (no client opt-in)",
}),
);
const sendFns = [...group.clients].map((client) => client.sendFn);
send(sendFns, {
type: "update-start",
body: options,
});
const message = await this._prepareMessage(group, options, changeEvent);
send(sendFns, message);
send(sendFns, {
type: "update-done",
body: {
changeId: changeEvent?.changeId,
},
});
log({
...createActionEndEntry(processingHmrChange),
outdated_modules:
message.type === "update"
? message.body.added.length + message.body.modified.length
: undefined,
});
if (logger) {
logger.point("hmrPrepareAndSendMessage_end");
logger.end("SUCCESS");
}
}
async _prepareMessage(group, options, changeEvent) {
const logger = !options.isInitialUpdate ? changeEvent?.logger : null;
try {
const revPromise = this._bundler.getRevision(group.revisionId);
if (!revPromise) {
return {
type: "error",
body: (0, _formatBundlingError.default)(
new _RevisionNotFoundError.default(group.revisionId),
),
};
}
logger?.point("updateGraph_start");
const { revision, delta } = await this._bundler.updateGraph(
await revPromise,
false,
);
logger?.point("updateGraph_end");
this._clientGroups.delete(group.revisionId);
group.revisionId = revision.id;
for (const client of group.clients) {
client.revisionIds = client.revisionIds.filter(
(revisionId) => revisionId !== group.revisionId,
);
client.revisionIds.push(revision.id);
}
this._clientGroups.set(group.revisionId, group);
logger?.point("serialize_start");
const hmrUpdate = (0, _hmrJSBundle.default)(delta, revision.graph, {
clientUrl: new URL(group.clientUrl),
createModuleId: this._createModuleId,
includeAsyncPaths: group.graphOptions.lazy,
projectRoot: this._config.projectRoot,
serverRoot:
this._config.server.unstable_serverRoot ?? this._config.projectRoot,
});
logger?.point("serialize_end");
return {
type: "update",
body: {
revisionId: revision.id,
isInitialUpdate: options.isInitialUpdate,
...hmrUpdate,
},
};
} catch (error) {
const formattedError = (0, _formatBundlingError.default)(error);
this._config.reporter.update({
type: "bundling_error",
error,
});
return {
type: "error",
body: formattedError,
};
}
}
}
exports.default = HmrServer;

404
node_modules/metro/src/HmrServer.js.flow generated vendored Normal file
View File

@@ -0,0 +1,404 @@
/**
* 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.
*
* @flow strict-local
* @format
*/
import type {
RevisionId,
default as IncrementalBundler,
} from './IncrementalBundler';
import type {GraphOptions} from './shared/types';
import type {ConfigT, RootPerfLogger} from 'metro-config';
import type {
HmrClientMessage,
HmrErrorMessage,
HmrMessage,
HmrUpdateMessage,
} from 'metro-runtime/src/modules/types';
import hmrJSBundle from './DeltaBundler/Serializers/hmrJSBundle';
import GraphNotFoundError from './IncrementalBundler/GraphNotFoundError';
import RevisionNotFoundError from './IncrementalBundler/RevisionNotFoundError';
import debounceAsyncQueue from './lib/debounceAsyncQueue';
import formatBundlingError from './lib/formatBundlingError';
import getGraphId from './lib/getGraphId';
import parseBundleOptionsFromBundleRequestUrl from './lib/parseBundleOptionsFromBundleRequestUrl';
import splitBundleOptions from './lib/splitBundleOptions';
import * as transformHelpers from './lib/transformHelpers';
import {Logger} from 'metro-core';
import nullthrows from 'nullthrows';
// eslint-disable-next-line import/no-commonjs
const debug = require('debug')('Metro:HMR');
const {createActionStartEntry, createActionEndEntry, log} = Logger;
export type Client = {
optedIntoHMR: boolean,
revisionIds: Array<RevisionId>,
+sendFn: string => void,
};
type ClientGroup = {
+clients: Set<Client>,
clientUrl: URL,
revisionId: RevisionId,
+unlisten: () => void,
+graphOptions: GraphOptions,
};
function send(sendFns: Array<(string) => void>, message: HmrMessage): void {
const strMessage = JSON.stringify(message);
sendFns.forEach((sendFn: string => void) => sendFn(strMessage));
}
/**
* The HmrServer (Hot Module Reloading) implements a lightweight interface
* to communicate easily to the logic in the React Native repository (which
* is the one that handles the Web Socket connections).
*
* This interface allows the HmrServer to hook its own logic to WS clients
* getting connected, disconnected or having errors (through the
* `onClientConnect`, `onClientDisconnect` and `onClientError` methods).
*/
export default class HmrServer<TClient extends Client> {
_config: ConfigT;
_bundler: IncrementalBundler;
_createModuleId: (path: string) => number;
_clientGroups: Map<RevisionId, ClientGroup>;
constructor(
bundler: IncrementalBundler,
createModuleId: (path: string) => number,
config: ConfigT,
) {
this._config = config;
this._bundler = bundler;
this._createModuleId = createModuleId;
this._clientGroups = new Map();
}
onClientConnect: (
requestUrl: string,
sendFn: (data: string) => void,
) => Promise<Client> = async (requestUrl, sendFn) => {
return {
sendFn,
revisionIds: [],
optedIntoHMR: false,
};
};
async _registerEntryPoint(
client: Client,
originalRequestUrl: string,
sendFn: (data: string) => void,
): Promise<void> {
debug('Registering entry point: %s', originalRequestUrl);
const requestUrl =
this._config.server.rewriteRequestUrl(originalRequestUrl);
debug('Rewritten as: %s', requestUrl);
const {bundleType: _bundleType, ...options} =
parseBundleOptionsFromBundleRequestUrl(
requestUrl,
new Set(this._config.resolver.platforms),
);
const {entryFile, resolverOptions, transformOptions, graphOptions} =
splitBundleOptions(options);
/**
* `entryFile` is relative to projectRoot, we need to use resolution function
* to find the appropriate file with supported extensions.
*/
const resolutionFn = await transformHelpers.getResolveDependencyFn(
this._bundler.getBundler(),
transformOptions.platform,
resolverOptions,
);
const resolvedEntryFilePath = resolutionFn(
(this._config.server.unstable_serverRoot ?? this._config.projectRoot) +
'/.',
{
name: entryFile,
data: {
key: entryFile,
asyncType: null,
isESMImport: false,
locs: [],
},
},
).filePath;
const graphId = getGraphId(resolvedEntryFilePath, transformOptions, {
resolverOptions,
shallow: graphOptions.shallow,
lazy: graphOptions.lazy,
unstable_allowRequireContext:
this._config.transformer.unstable_allowRequireContext,
});
const revPromise = this._bundler.getRevisionByGraphId(graphId);
if (!revPromise) {
send([sendFn], {
type: 'error',
body: formatBundlingError(new GraphNotFoundError(graphId)),
});
return;
}
const {graph, id} = await revPromise;
client.revisionIds.push(id);
let clientGroup: ?ClientGroup = this._clientGroups.get(id);
if (clientGroup != null) {
clientGroup.clients.add(client);
} else {
const clientUrl = new URL(requestUrl);
// Prepare the clientUrl to be used as sourceUrl in HMR updates.
clientUrl.protocol = 'http';
const clientQuery = clientUrl.searchParams;
clientQuery.delete('bundleEntry');
clientQuery.set('dev', clientQuery.get('dev') || 'true');
clientQuery.set('minify', clientQuery.get('minify') || 'false');
clientQuery.set('modulesOnly', 'true');
clientQuery.set('runModule', clientQuery.get('runModule') || 'false');
clientQuery.set('shallow', 'true');
clientGroup = {
clients: new Set([client]),
clientUrl: new URL(clientUrl),
revisionId: id,
graphOptions,
unlisten: (): void => unlisten(),
};
this._clientGroups.set(id, clientGroup);
let latestChangeEvent: ?{
+logger: ?RootPerfLogger,
+changeId: string,
} = null;
const debounceCallHandleFileChange = debounceAsyncQueue(async () => {
await this._handleFileChange(
nullthrows(clientGroup),
{isInitialUpdate: false},
latestChangeEvent,
);
}, 50);
const unlisten = this._bundler
.getDeltaBundler()
.listen(graph, async changeEvent => {
latestChangeEvent = changeEvent;
await debounceCallHandleFileChange();
});
}
await this._handleFileChange(clientGroup, {isInitialUpdate: true});
send([sendFn], {type: 'bundle-registered'});
}
onClientMessage: (
client: TClient,
message: string | Buffer | ArrayBuffer | Array<Buffer>,
sendFn: (data: string) => void,
) => Promise<void> = async (client, message, sendFn) => {
let data: HmrClientMessage;
try {
data = JSON.parse(String(message));
} catch (error) {
send([sendFn], {
type: 'error',
body: formatBundlingError(error),
});
return Promise.resolve();
}
if (data && data.type) {
switch (data.type) {
case 'register-entrypoints':
return Promise.all(
data.entryPoints.map(entryPoint =>
this._registerEntryPoint(client, entryPoint, sendFn),
),
);
case 'log':
if (this._config.server.forwardClientLogs) {
this._config.reporter.update({
type: 'client_log',
level: data.level,
data: data.data,
mode: data.mode,
});
}
break;
case 'log-opt-in':
client.optedIntoHMR = true;
break;
case 'heartbeat':
debug('Heartbeat received');
sendFn(String(message));
break;
default:
break;
}
}
return Promise.resolve();
};
onClientError: (client: TClient, e: Error) => void = (client, e) => {
this._config.reporter.update({
type: 'hmr_client_error',
error: e,
});
this.onClientDisconnect(client);
};
onClientDisconnect: (client: TClient) => void = client => {
client.revisionIds.forEach(revisionId => {
const group = this._clientGroups.get(revisionId);
if (group != null) {
if (group.clients.size === 1) {
this._clientGroups.delete(revisionId);
group.unlisten();
} else {
group.clients.delete(client);
}
}
});
};
async _handleFileChange(
group: ClientGroup,
options: {isInitialUpdate: boolean},
changeEvent: ?{
+logger: ?RootPerfLogger,
+changeId?: string,
},
): Promise<void> {
const logger = !options.isInitialUpdate ? changeEvent?.logger : null;
if (logger) {
logger.point('fileChange_end');
logger.point('hmrPrepareAndSendMessage_start');
}
const optedIntoHMR = [...group.clients].some(
(client: Client) => client.optedIntoHMR,
);
const processingHmrChange = log(
createActionStartEntry({
// Even when HMR is disabled on the client, this function still
// runs so we can stash updates while it's off and apply them later.
// However, this would mess up our internal analytics because we track
// HMR as being used even for people who have it disabled.
// As a workaround, we use a different event name for clients
// that didn't explicitly opt into HMR.
action_name: optedIntoHMR
? 'Processing HMR change'
: 'Processing HMR change (no client opt-in)',
}),
);
const sendFns = [...group.clients].map((client: Client) => client.sendFn);
send(sendFns, {
type: 'update-start',
body: options,
});
const message = await this._prepareMessage(group, options, changeEvent);
send(sendFns, message);
send(sendFns, {
type: 'update-done',
body: {changeId: changeEvent?.changeId},
});
log({
...createActionEndEntry(processingHmrChange),
outdated_modules:
message.type === 'update'
? message.body.added.length + message.body.modified.length
: undefined,
});
if (logger) {
logger.point('hmrPrepareAndSendMessage_end');
logger.end('SUCCESS');
}
}
async _prepareMessage(
group: ClientGroup,
options: {isInitialUpdate: boolean},
changeEvent: ?{
+logger: ?RootPerfLogger,
+changeId?: string,
},
): Promise<HmrUpdateMessage | HmrErrorMessage> {
const logger = !options.isInitialUpdate ? changeEvent?.logger : null;
try {
const revPromise = this._bundler.getRevision(group.revisionId);
if (!revPromise) {
return {
type: 'error',
body: formatBundlingError(
new RevisionNotFoundError(group.revisionId),
),
};
}
logger?.point('updateGraph_start');
const {revision, delta} = await this._bundler.updateGraph(
await revPromise,
false,
);
logger?.point('updateGraph_end');
this._clientGroups.delete(group.revisionId);
group.revisionId = revision.id;
for (const client of group.clients) {
client.revisionIds = client.revisionIds.filter(
revisionId => revisionId !== group.revisionId,
);
client.revisionIds.push(revision.id);
}
this._clientGroups.set(group.revisionId, group);
logger?.point('serialize_start');
const hmrUpdate = hmrJSBundle(delta, revision.graph, {
clientUrl: new URL(group.clientUrl),
createModuleId: this._createModuleId,
includeAsyncPaths: group.graphOptions.lazy,
projectRoot: this._config.projectRoot,
serverRoot:
this._config.server.unstable_serverRoot ?? this._config.projectRoot,
});
logger?.point('serialize_end');
return {
type: 'update',
body: {
revisionId: revision.id,
isInitialUpdate: options.isInitialUpdate,
...hmrUpdate,
},
};
} catch (error) {
const formattedError = formatBundlingError(error);
this._config.reporter.update({type: 'bundling_error', error});
return {type: 'error', body: formattedError};
}
}
}

103
node_modules/metro/src/IncrementalBundler.d.ts generated vendored Normal file
View File

@@ -0,0 +1,103 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<0ec72971869a882d97b381e3f1baa922>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/IncrementalBundler.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {DeltaResult, Graph, Module} from './DeltaBundler';
import type {
Options as DeltaBundlerOptions,
ReadOnlyDependencies,
TransformInputOptions,
} from './DeltaBundler/types';
import type {GraphId} from './lib/getGraphId';
import type {ResolverInputOptions} from './shared/types';
import type {ConfigT} from 'metro-config';
import Bundler from './Bundler';
import DeltaBundler from './DeltaBundler';
export declare type RevisionId = string;
export type OutputGraph = Graph;
type OtherOptions = Readonly<{
onProgress: DeltaBundlerOptions['onProgress'];
shallow: boolean;
lazy: boolean;
}>;
export type GraphRevision = {
readonly id: RevisionId;
readonly date: Date;
readonly graphId: GraphId;
readonly graph: OutputGraph;
readonly prepend: ReadonlyArray<Module>;
};
export type IncrementalBundlerOptions = Readonly<{
hasReducedPerformance?: boolean;
watch?: boolean;
}>;
declare class IncrementalBundler {
_config: ConfigT;
_bundler: Bundler;
_deltaBundler: DeltaBundler;
_revisionsById: Map<RevisionId, Promise<GraphRevision>>;
_revisionsByGraphId: Map<GraphId, Promise<GraphRevision>>;
static revisionIdFromString: (str: string) => RevisionId;
constructor(config: ConfigT, options?: IncrementalBundlerOptions);
end(): Promise<void>;
getBundler(): Bundler;
getDeltaBundler(): DeltaBundler;
getRevision(
revisionId: RevisionId,
): null | undefined | Promise<GraphRevision>;
getRevisionByGraphId(
graphId: GraphId,
): null | undefined | Promise<GraphRevision>;
buildGraphForEntries(
entryFiles: ReadonlyArray<string>,
transformOptions: TransformInputOptions,
resolverOptions: ResolverInputOptions,
otherOptions?: OtherOptions,
): Promise<OutputGraph>;
getDependencies(
entryFiles: ReadonlyArray<string>,
transformOptions: TransformInputOptions,
resolverOptions: ResolverInputOptions,
otherOptions?: OtherOptions,
): Promise<ReadOnlyDependencies>;
buildGraph(
entryFile: string,
transformOptions: TransformInputOptions,
resolverOptions: ResolverInputOptions,
otherOptions?: OtherOptions,
): Promise<{
readonly graph: OutputGraph;
readonly prepend: ReadonlyArray<Module>;
}>;
initializeGraph(
entryFile: string,
transformOptions: TransformInputOptions,
resolverOptions: ResolverInputOptions,
otherOptions?: OtherOptions,
): Promise<{delta: DeltaResult; revision: GraphRevision}>;
updateGraph(
revision: GraphRevision,
reset: boolean,
): Promise<{delta: DeltaResult; revision: GraphRevision}>;
endGraph(graphId: GraphId): Promise<void>;
_getAbsoluteEntryFiles(
entryFiles: ReadonlyArray<string>,
): Promise<ReadonlyArray<string>>;
ready(): Promise<void>;
}
export default IncrementalBundler;

313
node_modules/metro/src/IncrementalBundler.js generated vendored Normal file
View File

@@ -0,0 +1,313 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _Bundler = _interopRequireDefault(require("./Bundler"));
var _DeltaBundler = _interopRequireDefault(require("./DeltaBundler"));
var _ResourceNotFoundError = _interopRequireDefault(
require("./IncrementalBundler/ResourceNotFoundError"),
);
var _getGraphId = _interopRequireDefault(require("./lib/getGraphId"));
var _getPrependedScripts = _interopRequireDefault(
require("./lib/getPrependedScripts"),
);
var transformHelpers = _interopRequireWildcard(
require("./lib/transformHelpers"),
);
var _crypto = _interopRequireDefault(require("crypto"));
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
function _interopRequireWildcard(e, t) {
if ("function" == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ("object" != typeof e && "function" != typeof e))
return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
"default" !== t &&
{}.hasOwnProperty.call(e, t) &&
((i =
(o = Object.defineProperty) &&
Object.getOwnPropertyDescriptor(e, t)) &&
(i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function createRevisionId() {
return _crypto.default.randomBytes(8).toString("hex");
}
function revisionIdFromString(str) {
return str;
}
class IncrementalBundler {
_revisionsById = new Map();
_revisionsByGraphId = new Map();
static revisionIdFromString = revisionIdFromString;
constructor(config, options) {
this._config = config;
this._bundler = new _Bundler.default(config, options);
this._deltaBundler = new _DeltaBundler.default(this._bundler.getWatcher());
}
async end() {
this._deltaBundler.end();
await this._bundler.end();
}
getBundler() {
return this._bundler;
}
getDeltaBundler() {
return this._deltaBundler;
}
getRevision(revisionId) {
return this._revisionsById.get(revisionId);
}
getRevisionByGraphId(graphId) {
return this._revisionsByGraphId.get(graphId);
}
async buildGraphForEntries(
entryFiles,
transformOptions,
resolverOptions,
otherOptions = {
onProgress: null,
shallow: false,
lazy: false,
},
) {
const absoluteEntryFiles = await this._getAbsoluteEntryFiles(entryFiles);
const graph = await this._deltaBundler.buildGraph(absoluteEntryFiles, {
resolve: await transformHelpers.getResolveDependencyFn(
this._bundler,
transformOptions.platform,
resolverOptions,
),
transform: await transformHelpers.getTransformFn(
absoluteEntryFiles,
this._bundler,
this._deltaBundler,
this._config,
transformOptions,
resolverOptions,
),
transformOptions,
onProgress: otherOptions.onProgress,
lazy: otherOptions.lazy,
unstable_allowRequireContext:
this._config.transformer.unstable_allowRequireContext,
unstable_enablePackageExports:
this._config.resolver.unstable_enablePackageExports,
unstable_incrementalResolution:
this._config.resolver.unstable_incrementalResolution,
shallow: otherOptions.shallow,
});
this._config.serializer.experimentalSerializerHook(graph, {
added: graph.dependencies,
modified: new Map(),
deleted: new Set(),
reset: true,
});
return graph;
}
async getDependencies(
entryFiles,
transformOptions,
resolverOptions,
otherOptions = {
onProgress: null,
shallow: false,
lazy: false,
},
) {
const absoluteEntryFiles = await this._getAbsoluteEntryFiles(entryFiles);
const dependencies = await this._deltaBundler.getDependencies(
absoluteEntryFiles,
{
resolve: await transformHelpers.getResolveDependencyFn(
this._bundler,
transformOptions.platform,
resolverOptions,
),
transform: await transformHelpers.getTransformFn(
absoluteEntryFiles,
this._bundler,
this._deltaBundler,
this._config,
transformOptions,
resolverOptions,
),
transformOptions,
onProgress: otherOptions.onProgress,
lazy: otherOptions.lazy,
unstable_allowRequireContext:
this._config.transformer.unstable_allowRequireContext,
unstable_enablePackageExports:
this._config.resolver.unstable_enablePackageExports,
unstable_incrementalResolution:
this._config.resolver.unstable_incrementalResolution,
shallow: otherOptions.shallow,
},
);
return dependencies;
}
async buildGraph(
entryFile,
transformOptions,
resolverOptions,
otherOptions = {
onProgress: null,
shallow: false,
lazy: false,
},
) {
const graph = await this.buildGraphForEntries(
[entryFile],
transformOptions,
resolverOptions,
otherOptions,
);
const { type: _, ...transformOptionsWithoutType } = transformOptions;
const prepend = await (0, _getPrependedScripts.default)(
this._config,
transformOptionsWithoutType,
resolverOptions,
this._bundler,
this._deltaBundler,
);
return {
prepend,
graph,
};
}
async initializeGraph(
entryFile,
transformOptions,
resolverOptions,
otherOptions = {
onProgress: null,
shallow: false,
lazy: false,
},
) {
const graphId = (0, _getGraphId.default)(entryFile, transformOptions, {
resolverOptions,
shallow: otherOptions.shallow,
lazy: otherOptions.lazy,
unstable_allowRequireContext:
this._config.transformer.unstable_allowRequireContext,
});
const revisionId = createRevisionId();
const revisionPromise = (async () => {
const { graph, prepend } = await this.buildGraph(
entryFile,
transformOptions,
resolverOptions,
otherOptions,
);
return {
id: revisionId,
date: new Date(),
graphId,
graph,
prepend,
};
})();
this._revisionsById.set(revisionId, revisionPromise);
this._revisionsByGraphId.set(graphId, revisionPromise);
try {
const revision = await revisionPromise;
const delta = {
added: revision.graph.dependencies,
modified: new Map(),
deleted: new Set(),
reset: true,
};
return {
revision,
delta,
};
} catch (err) {
this._revisionsById.delete(revisionId);
this._revisionsByGraphId.delete(graphId);
throw err;
}
}
async updateGraph(revision, reset) {
const delta = await this._deltaBundler.getDelta(revision.graph, {
reset,
shallow: false,
});
this._config.serializer.experimentalSerializerHook(revision.graph, delta);
if (
delta.added.size > 0 ||
delta.modified.size > 0 ||
delta.deleted.size > 0
) {
this._revisionsById.delete(revision.id);
revision = {
...revision,
id: _crypto.default.randomBytes(8).toString("hex"),
date: new Date(),
};
const revisionPromise = Promise.resolve(revision);
this._revisionsById.set(revision.id, revisionPromise);
this._revisionsByGraphId.set(revision.graphId, revisionPromise);
}
return {
revision,
delta,
};
}
async endGraph(graphId) {
const revPromise = this._revisionsByGraphId.get(graphId);
if (!revPromise) {
return;
}
const revision = await revPromise;
this._deltaBundler.endGraph(revision.graph);
this._revisionsByGraphId.delete(graphId);
this._revisionsById.delete(revision.id);
}
async _getAbsoluteEntryFiles(entryFiles) {
const absoluteEntryFiles = entryFiles.map((entryFile) =>
_path.default.resolve(
this._config.server.unstable_serverRoot ?? this._config.projectRoot,
entryFile,
),
);
await Promise.all(
absoluteEntryFiles.map(
(entryFile) =>
new Promise((resolve, reject) => {
_fs.default.realpath(entryFile, (err) => {
if (err) {
reject(new _ResourceNotFoundError.default(entryFile));
} else {
resolve();
}
});
}),
),
);
return absoluteEntryFiles;
}
async ready() {
await this._bundler.ready();
}
}
exports.default = IncrementalBundler;

370
node_modules/metro/src/IncrementalBundler.js.flow generated vendored Normal file
View File

@@ -0,0 +1,370 @@
/**
* 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.
*
* @flow
* @format
* @oncall react_native
*/
import type {DeltaResult, Graph, MixedOutput, Module} from './DeltaBundler';
import type {
Options as DeltaBundlerOptions,
ReadOnlyDependencies,
TransformInputOptions,
} from './DeltaBundler/types';
import type {GraphId} from './lib/getGraphId';
import type {ResolverInputOptions} from './shared/types';
import type {ConfigT} from 'metro-config';
import Bundler from './Bundler';
import DeltaBundler from './DeltaBundler';
import ResourceNotFoundError from './IncrementalBundler/ResourceNotFoundError';
import getGraphId from './lib/getGraphId';
import getPrependedScripts from './lib/getPrependedScripts';
import * as transformHelpers from './lib/transformHelpers';
import crypto from 'crypto';
import fs from 'fs';
import path from 'path';
export opaque type RevisionId: string = string;
export type OutputGraph = Graph<>;
type OtherOptions = Readonly<{
onProgress: DeltaBundlerOptions<>['onProgress'],
shallow: boolean,
lazy: boolean,
}>;
export type GraphRevision = {
// Identifies the last computed revision.
+id: RevisionId,
+date: Date,
+graphId: GraphId,
+graph: OutputGraph,
+prepend: ReadonlyArray<Module<>>,
};
export type IncrementalBundlerOptions = Readonly<{
hasReducedPerformance?: boolean,
watch?: boolean,
}>;
function createRevisionId(): RevisionId {
return crypto.randomBytes(8).toString('hex');
}
function revisionIdFromString(str: string): RevisionId {
return str;
}
export default class IncrementalBundler {
_config: ConfigT;
_bundler: Bundler;
_deltaBundler: DeltaBundler<>;
_revisionsById: Map<RevisionId, Promise<GraphRevision>> = new Map();
_revisionsByGraphId: Map<GraphId, Promise<GraphRevision>> = new Map();
static revisionIdFromString: (str: string) => RevisionId =
revisionIdFromString;
constructor(config: ConfigT, options?: IncrementalBundlerOptions) {
this._config = config;
this._bundler = new Bundler(config, options);
this._deltaBundler = new DeltaBundler(this._bundler.getWatcher());
}
async end(): Promise<void> {
this._deltaBundler.end();
await this._bundler.end();
}
getBundler(): Bundler {
return this._bundler;
}
getDeltaBundler(): DeltaBundler<> {
return this._deltaBundler;
}
getRevision(revisionId: RevisionId): ?Promise<GraphRevision> {
return this._revisionsById.get(revisionId);
}
getRevisionByGraphId(graphId: GraphId): ?Promise<GraphRevision> {
return this._revisionsByGraphId.get(graphId);
}
async buildGraphForEntries(
entryFiles: ReadonlyArray<string>,
transformOptions: TransformInputOptions,
resolverOptions: ResolverInputOptions,
otherOptions?: OtherOptions = {
onProgress: null,
shallow: false,
lazy: false,
},
): Promise<OutputGraph> {
const absoluteEntryFiles = await this._getAbsoluteEntryFiles(entryFiles);
const graph = await this._deltaBundler.buildGraph(absoluteEntryFiles, {
resolve: await transformHelpers.getResolveDependencyFn(
this._bundler,
transformOptions.platform,
resolverOptions,
),
transform: await transformHelpers.getTransformFn(
absoluteEntryFiles,
this._bundler,
this._deltaBundler,
this._config,
transformOptions,
resolverOptions,
),
transformOptions,
onProgress: otherOptions.onProgress,
lazy: otherOptions.lazy,
unstable_allowRequireContext:
this._config.transformer.unstable_allowRequireContext,
unstable_enablePackageExports:
this._config.resolver.unstable_enablePackageExports,
unstable_incrementalResolution:
this._config.resolver.unstable_incrementalResolution,
shallow: otherOptions.shallow,
});
this._config.serializer.experimentalSerializerHook(graph, {
added: graph.dependencies,
modified: new Map(),
deleted: new Set(),
reset: true,
});
return graph;
}
async getDependencies(
entryFiles: ReadonlyArray<string>,
transformOptions: TransformInputOptions,
resolverOptions: ResolverInputOptions,
otherOptions?: OtherOptions = {
onProgress: null,
shallow: false,
lazy: false,
},
): Promise<ReadOnlyDependencies<>> {
const absoluteEntryFiles = await this._getAbsoluteEntryFiles(entryFiles);
const dependencies = await this._deltaBundler.getDependencies(
absoluteEntryFiles,
{
resolve: await transformHelpers.getResolveDependencyFn(
this._bundler,
transformOptions.platform,
resolverOptions,
),
transform: await transformHelpers.getTransformFn(
absoluteEntryFiles,
this._bundler,
this._deltaBundler,
this._config,
transformOptions,
resolverOptions,
),
transformOptions,
onProgress: otherOptions.onProgress,
lazy: otherOptions.lazy,
unstable_allowRequireContext:
this._config.transformer.unstable_allowRequireContext,
unstable_enablePackageExports:
this._config.resolver.unstable_enablePackageExports,
unstable_incrementalResolution:
this._config.resolver.unstable_incrementalResolution,
shallow: otherOptions.shallow,
},
);
return dependencies;
}
async buildGraph(
entryFile: string,
transformOptions: TransformInputOptions,
resolverOptions: ResolverInputOptions,
otherOptions?: OtherOptions = {
onProgress: null,
shallow: false,
lazy: false,
},
): Promise<{+graph: OutputGraph, +prepend: ReadonlyArray<Module<>>}> {
const graph = await this.buildGraphForEntries(
[entryFile],
transformOptions,
resolverOptions,
otherOptions,
);
const {type: _, ...transformOptionsWithoutType} = transformOptions;
const prepend = await getPrependedScripts(
this._config,
transformOptionsWithoutType,
resolverOptions,
this._bundler,
this._deltaBundler,
);
return {
prepend,
graph,
};
}
// TODO T34760750 (alexkirsz) Eventually, I'd like to get to a point where
// this class exposes only initializeGraph and updateGraph.
async initializeGraph(
entryFile: string,
transformOptions: TransformInputOptions,
resolverOptions: ResolverInputOptions,
otherOptions?: OtherOptions = {
onProgress: null,
shallow: false,
lazy: false,
},
): Promise<{
delta: DeltaResult<>,
revision: GraphRevision,
...
}> {
const graphId = getGraphId(entryFile, transformOptions, {
resolverOptions,
shallow: otherOptions.shallow,
lazy: otherOptions.lazy,
unstable_allowRequireContext:
this._config.transformer.unstable_allowRequireContext,
});
const revisionId = createRevisionId();
const revisionPromise = (async () => {
const {graph, prepend} = await this.buildGraph(
entryFile,
transformOptions,
resolverOptions,
otherOptions,
);
return {
id: revisionId,
date: new Date(),
graphId,
graph,
prepend,
};
})();
this._revisionsById.set(revisionId, revisionPromise);
this._revisionsByGraphId.set(graphId, revisionPromise);
try {
const revision = await revisionPromise;
const delta = {
added: revision.graph.dependencies,
modified: new Map<string, Module<MixedOutput>>(),
deleted: new Set<string>(),
reset: true,
};
return {
revision,
delta,
};
} catch (err) {
// Evict a bad revision from the cache since otherwise
// we'll keep getting it even after the build is fixed.
this._revisionsById.delete(revisionId);
this._revisionsByGraphId.delete(graphId);
throw err;
}
}
async updateGraph(
revision: GraphRevision,
reset: boolean,
): Promise<{
delta: DeltaResult<>,
revision: GraphRevision,
...
}> {
const delta = await this._deltaBundler.getDelta(revision.graph, {
reset,
shallow: false,
});
this._config.serializer.experimentalSerializerHook(revision.graph, delta);
if (
delta.added.size > 0 ||
delta.modified.size > 0 ||
delta.deleted.size > 0
) {
this._revisionsById.delete(revision.id);
revision = {
...revision,
// Generate a new revision id, to be used to verify the next incremental
// request.
id: crypto.randomBytes(8).toString('hex'),
date: new Date(),
};
const revisionPromise = Promise.resolve(revision);
this._revisionsById.set(revision.id, revisionPromise);
this._revisionsByGraphId.set(revision.graphId, revisionPromise);
}
return {revision, delta};
}
async endGraph(graphId: GraphId): Promise<void> {
const revPromise = this._revisionsByGraphId.get(graphId);
if (!revPromise) {
return;
}
const revision = await revPromise;
this._deltaBundler.endGraph(revision.graph);
this._revisionsByGraphId.delete(graphId);
this._revisionsById.delete(revision.id);
}
async _getAbsoluteEntryFiles(
entryFiles: ReadonlyArray<string>,
): Promise<ReadonlyArray<string>> {
const absoluteEntryFiles = entryFiles.map((entryFile: string) =>
path.resolve(
this._config.server.unstable_serverRoot ?? this._config.projectRoot,
entryFile,
),
);
await Promise.all(
absoluteEntryFiles.map(
(entryFile: string) =>
new Promise((resolve: void => void, reject: unknown => unknown) => {
// This should throw an error if the file doesn't exist.
// Using this instead of fs.exists to account for SimLinks.
fs.realpath(entryFile, err => {
if (err) {
reject(new ResourceNotFoundError(entryFile));
} else {
resolve();
}
});
}),
),
);
return absoluteEntryFiles;
}
// Wait for the bundler to become ready.
async ready(): Promise<void> {
await this._bundler.ready();
}
}

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<850ba6867e140fb0973cd13d0fd1bc60>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/IncrementalBundler/GraphNotFoundError.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {GraphId} from '../lib/getGraphId';
declare class GraphNotFoundError extends Error {
graphId: GraphId;
constructor(graphId: GraphId);
}
export default GraphNotFoundError;

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
class GraphNotFoundError extends Error {
constructor(graphId) {
super(`The graph \`${graphId}\` was not found.`);
this.graphId = graphId;
}
}
exports.default = GraphNotFoundError;

View File

@@ -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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {GraphId} from '../lib/getGraphId';
export default class GraphNotFoundError extends Error {
graphId: GraphId;
constructor(graphId: GraphId) {
super(`The graph \`${graphId}\` was not found.`);
this.graphId = graphId;
}
}

View File

@@ -0,0 +1,22 @@
/**
* 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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<6a9d75bc74b654362c3563ec8babda0b>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/IncrementalBundler/ResourceNotFoundError.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
declare class ResourceNotFoundError extends Error {
resourcePath: string;
constructor(resourcePath: string);
}
export default ResourceNotFoundError;

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
class ResourceNotFoundError extends Error {
constructor(resourcePath) {
super(`The resource \`${resourcePath}\` was not found.`);
this.resourcePath = resourcePath;
}
}
exports.default = ResourceNotFoundError;

View File

@@ -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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
export default class ResourceNotFoundError extends Error {
resourcePath: string;
constructor(resourcePath: string) {
super(`The resource \`${resourcePath}\` was not found.`);
this.resourcePath = resourcePath;
}
}

View File

@@ -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.
*
* @noformat
* @oncall react_native
* @generated SignedSource<<2c2d4a1a2d357eb73806a68bba897795>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/IncrementalBundler/RevisionNotFoundError.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {RevisionId} from '../IncrementalBundler';
declare class RevisionNotFoundError extends Error {
revisionId: RevisionId;
constructor(revisionId: RevisionId);
}
export default RevisionNotFoundError;

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
class RevisionNotFoundError extends Error {
constructor(revisionId) {
super(`The revision \`${revisionId}\` was not found.`);
this.revisionId = revisionId;
}
}
exports.default = RevisionNotFoundError;

View File

@@ -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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {RevisionId} from '../IncrementalBundler';
export default class RevisionNotFoundError extends Error {
revisionId: RevisionId;
constructor(revisionId: RevisionId) {
super(`The revision \`${revisionId}\` was not found.`);
this.revisionId = revisionId;
}
}

View File

@@ -0,0 +1,36 @@
/**
* 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.
*
* @noformat
* @generated SignedSource<<ef37054bf63dff008ccc8b58a2411597>>
*
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
* Original file: packages/metro/src/ModuleGraph/worker/JsFileWrapping.js
* To regenerate, run:
* js1 build metro-ts-defs (internal) OR
* yarn run build-ts-defs (OSS)
*/
import type {File as BabelNodeFile} from '@babel/types';
declare const WRAP_NAME: '$$_REQUIRE';
declare function wrapModule(
fileAst: BabelNodeFile,
importDefaultName: string,
importAllName: string,
dependencyMapName: string,
globalPrefix: string,
skipRequireRename: boolean,
$$PARAM_6$$?: Readonly<{unstable_useStaticHermesModuleFactory?: boolean}>,
): {ast: BabelNodeFile; requireName: string};
declare function wrapPolyfill(fileAst: BabelNodeFile): BabelNodeFile;
declare function jsonToCommonJS(source: string): string;
declare function wrapJson(
source: string,
globalPrefix: string,
unstable_useStaticHermesModuleFactory?: boolean,
): string;
export {WRAP_NAME, wrapJson, jsonToCommonJS, wrapModule, wrapPolyfill};

Some files were not shown because too many files have changed in this diff Show More