Socket
Socket
Sign inDemoInstall

metro

Package Overview
Dependencies
Maintainers
2
Versions
156
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

metro - npm Package Compare versions

Comparing version 0.80.2 to 0.80.3

src/DeltaBundler/buildSubgraph.js

34

package.json
{
"name": "metro",
"version": "0.80.2",
"version": "0.80.3",
"description": "🚇 The JavaScript bundler for React Native.",

@@ -31,3 +31,3 @@ "main": "src/index.js",

"graceful-fs": "^4.2.4",
"hermes-parser": "0.18.0",
"hermes-parser": "0.18.2",
"image-size": "^1.0.2",

@@ -38,15 +38,15 @@ "invariant": "^2.2.4",

"lodash.throttle": "^4.1.1",
"metro-babel-transformer": "0.80.2",
"metro-cache": "0.80.2",
"metro-cache-key": "0.80.2",
"metro-config": "0.80.2",
"metro-core": "0.80.2",
"metro-file-map": "0.80.2",
"metro-minify-terser": "0.80.2",
"metro-resolver": "0.80.2",
"metro-runtime": "0.80.2",
"metro-source-map": "0.80.2",
"metro-symbolicate": "0.80.2",
"metro-transform-plugins": "0.80.2",
"metro-transform-worker": "0.80.2",
"metro-babel-transformer": "0.80.3",
"metro-cache": "0.80.3",
"metro-cache-key": "0.80.3",
"metro-config": "0.80.3",
"metro-core": "0.80.3",
"metro-file-map": "0.80.3",
"metro-minify-terser": "0.80.3",
"metro-resolver": "0.80.3",
"metro-runtime": "0.80.3",
"metro-source-map": "0.80.3",
"metro-symbolicate": "0.80.3",
"metro-transform-plugins": "0.80.3",
"metro-transform-worker": "0.80.3",
"mime-types": "^2.1.27",

@@ -72,4 +72,4 @@ "node-fetch": "^2.2.0",

"jest-snapshot-serializer-raw": "^1.2.0",
"metro-babel-register": "0.80.2",
"metro-memory-fs": "0.80.2",
"metro-babel-register": "0.80.3",
"metro-memory-fs": "0.80.3",
"mock-req": "^0.2.0",

@@ -76,0 +76,0 @@ "mock-res": "^0.6.0",

@@ -110,3 +110,2 @@ /**

let result;
const numDependencies = this._graph.dependencies.size;
try {

@@ -122,9 +121,2 @@ result = await this._currentBuildPromise;

addedFiles.forEach((file) => this._addedFiles.add(file));
// If after an error the number of modules has changed, we could be in
// a weird state. As a safe net we clean the dependency modules to force
// a clean traversal of the graph next time.
if (this._graph.dependencies.size !== numDependencies) {
this._graph.dependencies.clear();
}
throw error;

@@ -131,0 +123,0 @@ } finally {

@@ -9,43 +9,3 @@ "use strict";

var _CountingSet = _interopRequireDefault(require("../lib/CountingSet"));
var path = _interopRequireWildcard(require("path"));
function _getRequireWildcardCache(nodeInterop) {
if (typeof WeakMap !== "function") return null;
var cacheBabelInterop = new WeakMap();
var cacheNodeInterop = new WeakMap();
return (_getRequireWildcardCache = function (nodeInterop) {
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
})(nodeInterop);
}
function _interopRequireWildcard(obj, nodeInterop) {
if (!nodeInterop && obj && obj.__esModule) {
return obj;
}
if (obj === null || (typeof obj !== "object" && typeof obj !== "function")) {
return { default: obj };
}
var cache = _getRequireWildcardCache(nodeInterop);
if (cache && cache.has(obj)) {
return cache.get(obj);
}
var newObj = {};
var hasPropertyDescriptor =
Object.defineProperty && Object.getOwnPropertyDescriptor;
for (var key in obj) {
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
var desc = hasPropertyDescriptor
? Object.getOwnPropertyDescriptor(obj, key)
: null;
if (desc && (desc.get || desc.set)) {
Object.defineProperty(newObj, key, desc);
} else {
newObj[key] = obj[key];
}
}
}
newObj.default = obj;
if (cache) {
cache.set(obj, newObj);
}
return newObj;
}
var _buildSubgraph = require("./buildSubgraph");
function _interopRequireDefault(obj) {

@@ -102,2 +62,6 @@ return obj && obj.__esModule ? obj : { default: obj };

}
function isWeakOrLazy(dependency, options) {
const asyncType = dependency.data.data.asyncType;
return asyncType === "weak" || (asyncType != null && options.lazy);
}
class Graph {

@@ -132,37 +96,102 @@ dependencies = new Map();

async traverseDependencies(paths, options) {
const delta = {
added: new Set(),
modified: new Set(),
deleted: new Set(),
earlyInverseDependencies: new Map(),
};
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)
);
// Record the paths that are part of the dependency graph before we start
// traversing - we'll use this to ensure we don't report modules modified
// that only exist as part of the graph mid-traversal, and to eliminate
// modules that end up in the same state that they started from the delta.
const originalModules = new Map();
for (const path of paths) {
const originalModule = this.dependencies.get(path);
if (originalModule) {
originalModules.set(path, originalModule);
// 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)))
);
}
}
for (const [path] of originalModules) {
// Traverse over modules that are part of the dependency graph.
//
// Note: A given path may not be part of the graph *at this time*, in
// particular it may have been removed since we started traversing, but
// in that case the path will be visited if and when we add it back to
// the graph in a subsequent iteration.
if (this.dependencies.has(path)) {
await this._traverseDependenciesForSingleFile(
path,
delta,
internalOptions
);
// 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 = {
added: delta.added,
deleted: delta.deleted,
touched: new Set(),
updatedModuleData: delta.baseModuleData,
baseModuleData: new Map(),
errors: new Map(),
};
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();

@@ -173,38 +202,9 @@ for (const path of delta.added) {

const modified = new Map();
// A path in delta.modified has been processed during this traversal,
// but may not actually differ, may be new, or may have been deleted after
// processing. The actually-modified modules are the intersection of
// delta.modified with the pre-existing paths, minus modules deleted.
for (const [path, originalModule] of originalModules) {
invariant(
!delta.added.has(path),
"delta.added has %s, but this path was already in the graph.",
path
);
if (delta.modified.has(path)) {
// It's expected that a module may be both modified and subsequently
// deleted - we'll only return it as deleted.
if (!delta.deleted.has(path)) {
// If a module existed before and has not been deleted, it must be
// in the dependencies map.
const newModule = nullthrows(this.dependencies.get(path));
if (
// Module.dependencies is mutable, so it's not obviously the case
// that referential equality implies no modification. However, we
// only mutate dependencies in two cases:
// 1. Within _processModule. In that case, we always mutate a new
// module and set a new reference in this.dependencies.
// 2. During _releaseModule, when recursively removing
// dependencies. In that case, we immediately discard the module
// object.
// TODO: Refactor for more explicit immutability
newModule !== originalModule ||
transfromOutputMayDiffer(newModule, originalModule) ||
// $FlowFixMe[incompatible-call]
!allDependenciesEqual(newModule, originalModule)
) {
modified.set(path, newModule);
}
}
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)));
}

@@ -219,8 +219,2 @@ }

async initialTraverseDependencies(options) {
const delta = {
added: new Set(),
modified: new Set(),
deleted: new Set(),
earlyInverseDependencies: new Map(),
};
const internalOptions = getInternalOptions(options);

@@ -238,7 +232,14 @@ invariant(

}
await Promise.all(
[...this.entryPoints].map((path) =>
this._traverseDependenciesForSingleFile(path, delta, internalOptions)
)
);
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({

@@ -253,34 +254,59 @@ shallow: options.shallow,

}
async _traverseDependenciesForSingleFile(path, delta, options) {
options.onDependencyAdd();
await this._processModule(path, delta, options);
options.onDependencyAdded();
async _buildDelta(pathsToVisit, options, moduleFilter) {
const subGraph = await (0, _buildSubgraph.buildSubgraph)(
pathsToVisit,
this.#resolvedContexts,
{
resolve: options.resolve,
transform: async (absolutePath, requireContext) => {
options.onDependencyAdd();
const result = await options.transform(absolutePath, requireContext);
options.onDependencyAdded();
return result;
},
shouldTraverse: (dependency) => {
if (options.shallow || isWeakOrLazy(dependency, options)) {
return false;
}
return moduleFilter == null || moduleFilter(dependency.absolutePath);
},
}
);
return {
added: new Set(),
touched: new Set(),
deleted: new Set(),
updatedModuleData: subGraph.moduleData,
baseModuleData: new Map(),
errors: subGraph.errors,
};
}
async _processModule(path, delta, options) {
const resolvedContext = this.#resolvedContexts.get(path);
// Transform the file via the given option.
// TODO: Unbind the transform method from options
const result = await options.transform(path, resolvedContext);
// Get the absolute path of all sub-dependencies (some of them could have been
// moved but maintain the same relative path).
const currentDependencies = this._resolveDependencies(
path,
result.dependencies,
options
_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 = nullthrows(
delta.updatedModuleData.get(path) ?? delta.baseModuleData.get(path)
);
const previousModule = this.dependencies.get(path);
const previousDependencies = previousModule?.dependencies ?? new Map();
const {
dependencies: currentDependencies,
resolvedContexts,
...transformResult
} = currentModule;
const nextModule = {
...(previousModule ?? {
inverseDependencies:
delta.earlyInverseDependencies.get(path) ??
new _CountingSet.default(),
inverseDependencies: new _CountingSet.default(),
path,
}),
...transformResult,
dependencies: new Map(previousDependencies),
getSource: result.getSource,
output: result.output,
unstable_transformResultKey: result.unstable_transformResultKey,
};

@@ -290,2 +316,13 @@

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

@@ -306,34 +343,46 @@ // Diff dependencies (1/2): remove dependencies that have changed or been removed.

// Diff dependencies (2/2): add dependencies that have changed or been added.
const addDependencyPromises = [];
for (const [key, curDependency] of currentDependencies) {
const prevDependency = previousDependencies.get(key);
if (
!prevDependency ||
!dependenciesEqual(prevDependency, curDependency, options)
) {
addDependencyPromises.push(
this._addDependency(nextModule, key, curDependency, 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
);
}
}
}
if (
previousModule &&
!transfromOutputMayDiffer(previousModule, nextModule) &&
previousModule != null &&
!transformOutputMayDiffer(previousModule, nextModule) &&
!dependenciesRemoved &&
addDependencyPromises.length === 0
!dependenciesAdded
) {
// We have not operated on nextModule, so restore previousModule
// to aid diffing.
// to aid diffing. Don't add this path to delta.touched.
this.dependencies.set(previousModule.path, previousModule);
return previousModule;
}
delta.modified.add(path);
await Promise.all(addDependencyPromises);
delta.touched.add(path);
// Replace dependencies with the correctly-ordered version. As long as all
// the above promises have resolved, this will be the same map but without
// the added nondeterminism of promise resolution order. Because this
// assignment does not add or remove edges, it does NOT invalidate any of the
// garbage collection state.
// 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.

@@ -344,6 +393,13 @@ invariant(

);
nextModule.dependencies = currentDependencies;
nextModule.dependencies = new Map(currentDependencies);
return nextModule;
}
async _addDependency(parentModule, key, dependency, delta, options) {
_addDependency(
parentModule,
key,
dependency,
requireContext,
delta,
options
) {
const path = dependency.absolutePath;

@@ -365,32 +421,33 @@

if (!module) {
// Add a new node to the graph.
const earlyInverseDependencies =
delta.earlyInverseDependencies.get(path);
if (earlyInverseDependencies) {
// This module is being transformed at the moment in parallel, so we
// should only mark its parent as an inverse dependency.
earlyInverseDependencies.add(parentModule.path);
} else {
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);
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);
}
}
delta.earlyInverseDependencies.set(path, new _CountingSet.default());
options.onDependencyAdd();
module = await this._processModule(path, delta, options);
options.onDependencyAdded();
this.dependencies.set(module.path, module);
throw error;
}
}
if (module) {
// We either added a new node to the graph, or we're updating an existing one.
module.inverseDependencies.add(parentModule.path);
this._markModuleInUse(module);
}
// 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 (requireContext) {
this.#resolvedContexts.set(path, requireContext);
} else {
// This dependency may have existed previously as a require.context -
// clean it up.
this.#resolvedContexts.delete(path);
}
// Always update the parent's dependency map.
// Update the parent's dependency map unless we failed to add a dependency.
// This means the parent's dependencies can get desynced from

@@ -455,71 +512,3 @@ // inverseDependencies and the other fields in the case of lazy edges.

}
_resolveDependencies(parentPath, dependencies, options) {
const maybeResolvedDeps = new Map();
for (const dep of dependencies) {
let resolvedDep;
// `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 = (0, _contextModule.deriveAbsolutePathFromContext)(
from,
contextParams
);
const resolvedContext = {
from,
mode: contextParams.mode,
recursive: contextParams.recursive,
filter: new RegExp(
contextParams.filter.pattern,
contextParams.filter.flags
),
};
this.#resolvedContexts.set(absolutePath, resolvedContext);
resolvedDep = {
absolutePath,
data: dep,
};
} else {
try {
resolvedDep = {
absolutePath: options.resolve(parentPath, dep).filePath,
data: dep,
};
// This dependency may have existed previously as a require.context -
// clean it up.
this.#resolvedContexts.delete(resolvedDep.absolutePath);
} 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;
}
}
}
const key = dep.data.key;
if (maybeResolvedDeps.has(key)) {
throw new Error(
`resolveDependencies: Found duplicate dependency key '${key}' in ${parentPath}`
);
}
maybeResolvedDeps.set(key, resolvedDep);
}
const resolvedDeps = new Map();
// Return just the dependencies we successfully resolved.
// FIXME: This has a bad bug affecting all dependencies *after* an unresolved
// optional dependency. We'll need to propagate the nulls all the way to the
// serializer and the require() runtime to keep the dependency map from being
// desynced from the contents of the module.
for (const [key, resolvedDep] of maybeResolvedDeps) {
if (resolvedDep) {
resolvedDeps.set(key, resolvedDep);
}
}
return resolvedDeps;
}
/**

@@ -603,4 +592,3 @@ * Re-traverse the dependency graph in DFS order to reorder the modules and

for (const dependency of module.dependencies.values()) {
const asyncType = dependency.data.data.asyncType;
if (asyncType === "weak" || (options.lazy && asyncType != null)) {
if (isWeakOrLazy(dependency, options)) {
continue;

@@ -611,2 +599,22 @@ }

}
_moduleSnapshot(module) {
const { dependencies, getSource, output, unstable_transformResultKey } =
module;
const resolvedContexts = new Map();
for (const [key, dependency] of dependencies) {
const resolvedContext = this.#resolvedContexts.get(
dependency.absolutePath
);
if (resolvedContext != null) {
resolvedContexts.set(key, resolvedContext);
}
}
return {
dependencies: new Map(dependencies),
resolvedContexts,
getSource,
output,
unstable_transformResultKey,
};
}

@@ -617,2 +625,12 @@ // Delete an unreachable module (and its outbound edges) from the graph

_releaseModule(module, delta, options) {
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) {

@@ -639,3 +657,2 @@ this._removeDependency(module, key, dependency, delta, options);

this.dependencies.delete(module.path);
delta.earlyInverseDependencies.delete(module.path);
this.#gc.possibleCycleRoots.delete(module.path);

@@ -648,3 +665,3 @@ this.#gc.color.delete(module.path);

_markAsPossibleCycleRoot(module) {
if (nullthrows(this.#gc.color.get(module.path)) !== "purple") {
if (this.#gc.color.get(module.path) !== "purple") {
this.#gc.color.set(module.path, "purple");

@@ -750,14 +767,2 @@ this.#gc.possibleCycleRoots.add(module.path);

}
function allDependenciesEqual(a, b, options) {
if (a.dependencies.size !== b.dependencies.size) {
return false;
}
for (const [key, depA] of a.dependencies) {
const depB = b.dependencies.get(key);
if (!depB || !dependenciesEqual(depA, depB, options)) {
return false;
}
}
return true;
}
function contextParamsEqual(a, b) {

@@ -775,3 +780,3 @@ return (

}
function transfromOutputMayDiffer(a, b) {
function transformOutputMayDiffer(a, b) {
return (

@@ -778,0 +783,0 @@ a.unstable_transformResultKey == null ||

@@ -324,2 +324,3 @@ /**

customResolverOptions,
dev,
}

@@ -326,0 +327,0 @@ );

@@ -22,2 +22,3 @@ /**

customResolverOptions: options.customResolverOptions,
dev: options.dev,
},

@@ -24,0 +25,0 @@ transformOptions: {

@@ -263,6 +263,3 @@ "use strict";

const resolverOptionsKey =
JSON.stringify(
resolverOptions.customResolverOptions ?? {},
canonicalize
) ?? "";
JSON.stringify(resolverOptions ?? {}, canonicalize) ?? "";
const originKey = isSensitiveToOriginFolder ? path.dirname(from) : "";

@@ -269,0 +266,0 @@ const targetKey = to;

@@ -56,3 +56,5 @@ /**

null,
/* resolverOptions */ {}
/* resolverOptions */ {
dev: false,
}
);

@@ -92,2 +94,3 @@ this._cachedEmptyModule = emptyModule;

assetExts,
dev: resolverOptions.dev,
disableHierarchicalLookup,

@@ -94,0 +97,0 @@ doesFileExist,

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc