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

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,
}>;