@parcel/core
Version:
625 lines (606 loc) • 26.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.propagateSymbols = propagateSymbols;
function _assert() {
const data = _interopRequireDefault(require("assert"));
_assert = function () {
return data;
};
return data;
}
function _nullthrows() {
const data = _interopRequireDefault(require("nullthrows"));
_nullthrows = function () {
return data;
};
return data;
}
function _utils() {
const data = require("@parcel/utils");
_utils = function () {
return data;
};
return data;
}
function _logger() {
const data = _interopRequireDefault(require("@parcel/logger"));
_logger = function () {
return data;
};
return data;
}
function _diagnostic() {
const data = require("@parcel/diagnostic");
_diagnostic = function () {
return data;
};
return data;
}
var _types = require("./types");
var _projectPath = require("./projectPath");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function propagateSymbols({
options,
assetGraph,
changedAssetsPropagation,
assetGroupsWithRemovedParents,
previousErrors
}) {
let changedAssets = new Set([...changedAssetsPropagation].map(id => assetGraph.getNodeIdByContentKey(id)));
// To reorder once at the end
let changedDeps = new Set();
// For the down traversal, the nodes with `usedSymbolsDownDirty = true` are exactly
// `changedAssetsPropagation` (= asset and therefore potentially dependencies changed) or the
// asset children of `assetGroupsWithRemovedParents` (= fewer incoming dependencies causing less
// used symbols).
//
// The up traversal has to consider all nodes that changed in the down traversal
// (`useSymbolsUpDirtyDown = true`) which are listed in `changedDepsUsedSymbolsUpDirtyDown`
// (more or less requested symbols) and in `changedAssetsPropagation` (changing an asset might
// change exports).
// The dependencies that changed in the down traversal causing an update in the up traversal.
let changedDepsUsedSymbolsUpDirtyDown = new Set();
// Propagate the requested symbols down from the root to the leaves
propagateSymbolsDown(assetGraph, changedAssets, assetGroupsWithRemovedParents, (assetNode, incomingDeps, outgoingDeps) => {
// exportSymbol -> identifier
let assetSymbols = assetNode.value.symbols;
// identifier -> exportSymbol
let assetSymbolsInverse;
if (assetSymbols) {
assetSymbolsInverse = new Map();
for (let [s, {
local
}] of assetSymbols) {
let set = assetSymbolsInverse.get(local);
if (!set) {
set = new Set();
assetSymbolsInverse.set(local, set);
}
set.add(s);
}
}
let hasNamespaceOutgoingDeps = outgoingDeps.some(d => {
var _d$value$symbols;
return ((_d$value$symbols = d.value.symbols) === null || _d$value$symbols === void 0 || (_d$value$symbols = _d$value$symbols.get('*')) === null || _d$value$symbols === void 0 ? void 0 : _d$value$symbols.local) === '*';
});
// 1) Determine what the incomingDeps requests from the asset
// ----------------------------------------------------------
let isEntry = false;
let addAll = false;
// Used symbols that are exported or reexported (symbol will be removed again later) by asset.
assetNode.usedSymbols = new Set();
// Symbols that have to be namespace reexported by outgoingDeps.
let namespaceReexportedSymbols = new Set();
if (incomingDeps.length === 0) {
// Root in the runtimes Graph
assetNode.usedSymbols.add('*');
namespaceReexportedSymbols.add('*');
} else {
for (let incomingDep of incomingDeps) {
if (incomingDep.value.symbols == null) {
if (incomingDep.value.sourceAssetId == null) {
// The root dependency on non-library builds
isEntry = true;
} else {
// A regular dependency with cleared symbols
addAll = true;
}
continue;
}
for (let exportSymbol of incomingDep.usedSymbolsDown) {
if (exportSymbol === '*') {
assetNode.usedSymbols.add('*');
namespaceReexportedSymbols.add('*');
}
if (!assetSymbols || assetSymbols.has(exportSymbol) || assetSymbols.has('*')) {
// An own symbol or a non-namespace reexport
assetNode.usedSymbols.add(exportSymbol);
}
// A namespace reexport
// (but only if we actually have namespace-exporting outgoing dependencies,
// This usually happens with a reexporting asset with many namespace exports which means that
// we cannot match up the correct asset with the used symbol at this level.)
else if (hasNamespaceOutgoingDeps && exportSymbol !== 'default') {
namespaceReexportedSymbols.add(exportSymbol);
}
}
}
}
// Incomding dependency with cleared symbols, add everything
if (addAll) {
assetSymbols === null || assetSymbols === void 0 || assetSymbols.forEach((_, exportSymbol) => assetNode.usedSymbols.add(exportSymbol));
}
// 2) Distribute the symbols to the outgoing dependencies
// ----------------------------------------------------------
for (let dep of outgoingDeps) {
let depUsedSymbolsDownOld = dep.usedSymbolsDown;
let depUsedSymbolsDown = new Set();
dep.usedSymbolsDown = depUsedSymbolsDown;
if (assetNode.value.sideEffects ||
// Incoming dependency with cleared symbols
addAll ||
// For entries, we still need to add dep.value.symbols of the entry (which are "used" but not according to the symbols data)
isEntry ||
// If not a single symbol is used, we can say the entire subgraph is not used.
// This is e.g. needed when some symbol is imported and then used for a export which isn't used (= "semi-weak" reexport)
// index.js: `import {bar} from "./lib"; ...`
// lib/index.js: `export * from "./foo.js"; export * from "./bar.js";`
// lib/foo.js: `import { data } from "./bar.js"; export const foo = data + " esm2";`
assetNode.usedSymbols.size > 0 || namespaceReexportedSymbols.size > 0) {
var _depSymbols$get;
let depSymbols = dep.value.symbols;
if (!depSymbols) continue;
if (((_depSymbols$get = depSymbols.get('*')) === null || _depSymbols$get === void 0 ? void 0 : _depSymbols$get.local) === '*') {
if (addAll) {
depUsedSymbolsDown.add('*');
} else {
for (let s of namespaceReexportedSymbols) {
// We need to propagate the namespaceReexportedSymbols to all namespace dependencies (= even wrong ones because we don't know yet)
depUsedSymbolsDown.add(s);
}
}
}
for (let [symbol, {
local
}] of depSymbols) {
var _depSymbols$get2;
// Was already handled above
if (local === '*') continue;
if (!assetSymbolsInverse || !((_depSymbols$get2 = depSymbols.get(symbol)) !== null && _depSymbols$get2 !== void 0 && _depSymbols$get2.isWeak)) {
// Bailout or non-weak symbol (= used in the asset itself = not a reexport)
depUsedSymbolsDown.add(symbol);
} else {
let reexportedExportSymbols = assetSymbolsInverse.get(local);
if (reexportedExportSymbols == null) {
// not reexported = used in asset itself
depUsedSymbolsDown.add(symbol);
} else if (assetNode.usedSymbols.has('*')) {
// we need everything
depUsedSymbolsDown.add(symbol);
[...reexportedExportSymbols].forEach(s => assetNode.usedSymbols.delete(s));
} else {
let usedReexportedExportSymbols = [...reexportedExportSymbols].filter(s => assetNode.usedSymbols.has(s));
if (usedReexportedExportSymbols.length > 0) {
// The symbol is indeed a reexport, so it's not used from the asset itself
depUsedSymbolsDown.add(symbol);
usedReexportedExportSymbols.forEach(s => assetNode.usedSymbols.delete(s));
}
}
}
}
} else {
depUsedSymbolsDown.clear();
}
if (!(0, _utils().setEqual)(depUsedSymbolsDownOld, depUsedSymbolsDown)) {
dep.usedSymbolsDownDirty = true;
dep.usedSymbolsUpDirtyDown = true;
changedDepsUsedSymbolsUpDirtyDown.add(dep.id);
}
if (dep.usedSymbolsUpDirtyDown) {
// Set on node creation
changedDepsUsedSymbolsUpDirtyDown.add(dep.id);
}
}
});
const logFallbackNamespaceInsertion = (assetNode, symbol, depNode1, depNode2) => {
if (options.logLevel === 'verbose') {
_logger().default.warn({
message: `${(0, _projectPath.fromProjectPathRelative)(assetNode.value.filePath)} reexports "${symbol}", which could be resolved either to the dependency "${depNode1.value.specifier}" or "${depNode2.value.specifier}" at runtime. Adding a namespace object to fall back on.`,
origin: '@parcel/core'
});
}
};
// Because namespace reexports introduce ambiguity, go up the graph from the leaves to the
// root and remove requested symbols that aren't actually exported
let errors = propagateSymbolsUp(assetGraph, changedAssets, changedDepsUsedSymbolsUpDirtyDown, previousErrors, (assetNode, incomingDeps, outgoingDeps) => {
let assetSymbols = assetNode.value.symbols;
let assetSymbolsInverse = null;
if (assetSymbols) {
assetSymbolsInverse = new Map();
for (let [s, {
local
}] of assetSymbols) {
let set = assetSymbolsInverse.get(local);
if (!set) {
set = new Set();
assetSymbolsInverse.set(local, set);
}
set.add(s);
}
}
// the symbols that are reexported (not used in `asset`) -> asset they resolved to
let reexportedSymbols = new Map();
// the symbols that are reexported (not used in `asset`) -> the corresponding outgoingDep(s)
// To generate the diagnostic when there are multiple dependencies with non-statically
// analyzable exports
let reexportedSymbolsSource = new Map();
for (let outgoingDep of outgoingDeps) {
var _outgoingDepSymbols$g;
let outgoingDepSymbols = outgoingDep.value.symbols;
if (!outgoingDepSymbols) continue;
let isExcluded = assetGraph.getNodeIdsConnectedFrom(assetGraph.getNodeIdByContentKey(outgoingDep.id)).length === 0;
// excluded, assume everything that is requested exists
if (isExcluded) {
outgoingDep.usedSymbolsDown.forEach((_, s) => outgoingDep.usedSymbolsUp.set(s, null));
}
if (((_outgoingDepSymbols$g = outgoingDepSymbols.get('*')) === null || _outgoingDepSymbols$g === void 0 ? void 0 : _outgoingDepSymbols$g.local) === '*') {
outgoingDep.usedSymbolsUp.forEach((sResolved, s) => {
if (s === 'default') {
return;
}
// If the symbol could come from multiple assets at runtime, assetNode's
// namespace will be needed at runtime to perform the lookup on.
if (reexportedSymbols.has(s)) {
if (!assetNode.usedSymbols.has('*')) {
logFallbackNamespaceInsertion(assetNode, s, (0, _nullthrows().default)(reexportedSymbolsSource.get(s)), outgoingDep);
}
assetNode.usedSymbols.add('*');
reexportedSymbols.set(s, {
asset: assetNode.id,
symbol: s
});
} else {
reexportedSymbols.set(s, sResolved);
reexportedSymbolsSource.set(s, outgoingDep);
}
});
}
for (let [s, sResolved] of outgoingDep.usedSymbolsUp) {
var _outgoingDepSymbols$g2, _assetSymbolsInverse;
if (!outgoingDep.usedSymbolsDown.has(s)) {
// usedSymbolsDown is a superset of usedSymbolsUp
continue;
}
let local = (_outgoingDepSymbols$g2 = outgoingDepSymbols.get(s)) === null || _outgoingDepSymbols$g2 === void 0 ? void 0 : _outgoingDepSymbols$g2.local;
if (local == null) {
// Caused by '*' => '*', already handled
continue;
}
let reexported = (_assetSymbolsInverse = assetSymbolsInverse) === null || _assetSymbolsInverse === void 0 ? void 0 : _assetSymbolsInverse.get(local);
if (reexported != null) {
reexported.forEach(s => {
// see same code above
if (reexportedSymbols.has(s)) {
if (!assetNode.usedSymbols.has('*')) {
logFallbackNamespaceInsertion(assetNode, s, (0, _nullthrows().default)(reexportedSymbolsSource.get(s)), outgoingDep);
}
assetNode.usedSymbols.add('*');
reexportedSymbols.set(s, {
asset: assetNode.id,
symbol: s
});
} else {
reexportedSymbols.set(s, sResolved);
reexportedSymbolsSource.set(s, outgoingDep);
}
});
}
}
}
let errors = [];
function usedSymbolsUpAmbiguous(old, current, s, value) {
if (old.has(s)) {
let valueOld = old.get(s);
if (valueOld !== value && !((valueOld === null || valueOld === void 0 ? void 0 : valueOld.asset) === value.asset && (valueOld === null || valueOld === void 0 ? void 0 : valueOld.symbol) === value.symbol)) {
// The dependency points to multiple assets (via an asset group).
current.set(s, undefined);
return;
}
}
current.set(s, value);
}
for (let incomingDep of incomingDeps) {
var _incomingDepSymbols$g;
let incomingDepUsedSymbolsUpOld = incomingDep.usedSymbolsUp;
incomingDep.usedSymbolsUp = new Map();
let incomingDepSymbols = incomingDep.value.symbols;
if (!incomingDepSymbols) continue;
let hasNamespaceReexport = ((_incomingDepSymbols$g = incomingDepSymbols.get('*')) === null || _incomingDepSymbols$g === void 0 ? void 0 : _incomingDepSymbols$g.local) === '*';
for (let s of incomingDep.usedSymbolsDown) {
if (assetSymbols == null ||
// Assume everything could be provided if symbols are cleared
assetNode.value.bundleBehavior === _types.BundleBehavior.isolated || assetNode.value.bundleBehavior === _types.BundleBehavior.inline || s === '*' || assetNode.usedSymbols.has(s)) {
usedSymbolsUpAmbiguous(incomingDepUsedSymbolsUpOld, incomingDep.usedSymbolsUp, s, {
asset: assetNode.id,
symbol: s
});
} else if (reexportedSymbols.has(s)) {
let reexport = reexportedSymbols.get(s);
let v =
// Forward a reexport only if the current asset is side-effect free and not external
!assetNode.value.sideEffects && reexport != null ? reexport : {
asset: assetNode.id,
symbol: s
};
usedSymbolsUpAmbiguous(incomingDepUsedSymbolsUpOld, incomingDep.usedSymbolsUp, s, v);
} else if (!hasNamespaceReexport) {
var _incomingDep$value$sy;
let loc = (_incomingDep$value$sy = incomingDep.value.symbols) === null || _incomingDep$value$sy === void 0 || (_incomingDep$value$sy = _incomingDep$value$sy.get(s)) === null || _incomingDep$value$sy === void 0 ? void 0 : _incomingDep$value$sy.loc;
let [resolutionNodeId] = assetGraph.getNodeIdsConnectedFrom(assetGraph.getNodeIdByContentKey(incomingDep.id));
let resolution = (0, _nullthrows().default)(assetGraph.getNode(resolutionNodeId));
(0, _assert().default)(resolution && (resolution.type === 'asset_group' || resolution.type === 'asset'));
errors.push({
message: (0, _diagnostic().md)`${(0, _projectPath.fromProjectPathRelative)(resolution.value.filePath)} does not export '${s}'`,
origin: '@parcel/core',
codeFrames: loc ? [{
filePath: (0, _projectPath.fromProjectPath)(options.projectRoot, loc === null || loc === void 0 ? void 0 : loc.filePath) ?? undefined,
language: incomingDep.value.sourceAssetType ?? undefined,
codeHighlights: [(0, _diagnostic().convertSourceLocationToHighlight)(loc)]
}] : undefined
});
}
}
if (!equalMap(incomingDepUsedSymbolsUpOld, incomingDep.usedSymbolsUp)) {
changedDeps.add(incomingDep);
incomingDep.usedSymbolsUpDirtyUp = true;
}
incomingDep.excluded = false;
if (incomingDep.value.symbols != null && incomingDep.usedSymbolsUp.size === 0) {
let assetGroups = assetGraph.getNodeIdsConnectedFrom(assetGraph.getNodeIdByContentKey(incomingDep.id));
if (assetGroups.length === 1) {
let [assetGroupId] = assetGroups;
let assetGroup = (0, _nullthrows().default)(assetGraph.getNode(assetGroupId));
if (assetGroup.type === 'asset_group' && assetGroup.value.sideEffects === false) {
incomingDep.excluded = true;
}
} else {
(0, _assert().default)(assetGroups.length === 0);
}
}
}
return errors;
});
// Sort usedSymbolsUp so they are a consistent order across builds.
// This ensures a consistent ordering of these symbols when packaging.
// See https://github.com/parcel-bundler/parcel/pull/8212
for (let dep of changedDeps) {
dep.usedSymbolsUp = new Map([...dep.usedSymbolsUp].sort(([a], [b]) => a.localeCompare(b)));
}
return errors;
}
function propagateSymbolsDown(assetGraph, changedAssets, assetGroupsWithRemovedParents, visit) {
if (changedAssets.size === 0 && assetGroupsWithRemovedParents.size === 0) {
return;
}
// We care about changed assets and their changed dependencies. So start with the first changed
// asset or dependency and continue while the symbols change. If the queue becomes empty,
// continue with the next unvisited changed asset.
//
// In the end, nodes, which are neither listed in changedAssets nor in
// assetGroupsWithRemovedParents nor reached via a dirty flag, don't have to be visited at all.
//
// In the worst case, some nodes have to be revisited because we don't want to sort the assets
// into topological order. For example in a diamond graph where the join point is visited twice
// via each parent (the numbers signifiying the order of re/visiting, `...` being unvisited).
// However, this only continues as long as there are changes in the used symbols that influence
// child nodes.
//
// |
// ...
// / \
// 1 4
// \ /
// 2+5
// |
// 3+6
// |
// ...
// |
//
let unreachedAssets = new Set([...changedAssets, ...assetGroupsWithRemovedParents]);
let queue = new Set([setPop(unreachedAssets)]);
while (queue.size > 0) {
let queuedNodeId = setPop(queue);
unreachedAssets.delete(queuedNodeId);
let outgoing = assetGraph.getNodeIdsConnectedFrom(queuedNodeId);
let node = (0, _nullthrows().default)(assetGraph.getNode(queuedNodeId));
let wasNodeDirty = false;
if (node.type === 'dependency' || node.type === 'asset_group') {
wasNodeDirty = node.usedSymbolsDownDirty;
node.usedSymbolsDownDirty = false;
} else if (node.type === 'asset' && node.usedSymbolsDownDirty) {
visit(node, assetGraph.getIncomingDependencies(node.value).map(d => {
let dep = assetGraph.getNodeByContentKey(d.id);
(0, _assert().default)(dep && dep.type === 'dependency');
return dep;
}), outgoing.map(dep => {
let depNode = (0, _nullthrows().default)(assetGraph.getNode(dep));
(0, _assert().default)(depNode.type === 'dependency');
return depNode;
}));
node.usedSymbolsDownDirty = false;
}
for (let child of outgoing) {
let childNode = (0, _nullthrows().default)(assetGraph.getNode(child));
let childDirty = false;
if ((childNode.type === 'asset' || childNode.type === 'asset_group') && wasNodeDirty) {
childNode.usedSymbolsDownDirty = true;
childDirty = true;
} else if (childNode.type === 'dependency') {
childDirty = childNode.usedSymbolsDownDirty;
}
if (childDirty) {
queue.add(child);
}
}
if (queue.size === 0 && unreachedAssets.size > 0) {
queue.add(setPop(unreachedAssets));
}
}
}
function propagateSymbolsUp(assetGraph, changedAssets, changedDepsUsedSymbolsUpDirtyDown, previousErrors, visit) {
// For graphs in general (so with cyclic dependencies), some nodes will have to be revisited. So
// run a regular queue-based BFS for anything that's still dirty.
//
// (Previously, there was first a recursive post-order DFS, with the idea that all children of a
// node should be processed first. With a tree, this would result in a minimal amount of work by
// processing every asset exactly once and then the remaining cycles would have been handled
// with the loop. This was slightly faster for initial builds but had O(project) instead of
// O(changes).)
let errors = previousErrors ?
// Some nodes might have been removed since the last build
new Map([...previousErrors].filter(([n]) => assetGraph.hasNode(n))) : new Map();
let changedDepsUsedSymbolsUpDirtyDownAssets = new Set([...[...changedDepsUsedSymbolsUpDirtyDown].reverse().flatMap(id => getDependencyResolution(assetGraph, id)), ...changedAssets]);
// Do a more efficient full traversal (less recomputations) if more than half of the assets
// changed.
let runFullPass =
// If there are n nodes in the graph, then the asset count is approximately
// n/6 (for every asset, there are ~4 dependencies and ~1 asset_group).
assetGraph.nodes.length * (1 / 6) * 0.5 < changedDepsUsedSymbolsUpDirtyDownAssets.size;
let dirtyDeps;
if (runFullPass) {
dirtyDeps = new Set();
let rootNodeId = (0, _nullthrows().default)(assetGraph.rootNodeId, 'A root node is required to traverse');
const nodeVisitor = nodeId => {
let node = (0, _nullthrows().default)(assetGraph.getNode(nodeId));
let outgoing = assetGraph.getNodeIdsConnectedFrom(nodeId);
for (let childId of outgoing) {
let child = (0, _nullthrows().default)(assetGraph.getNode(childId));
if (node.type === 'asset') {
(0, _assert().default)(child.type === 'dependency');
if (child.usedSymbolsUpDirtyUp) {
node.usedSymbolsUpDirty = true;
child.usedSymbolsUpDirtyUp = false;
}
}
}
if (node.type === 'asset') {
let incoming = assetGraph.getIncomingDependencies(node.value).map(d => {
let n = assetGraph.getNodeByContentKey(d.id);
(0, _assert().default)(n && n.type === 'dependency');
return n;
});
for (let dep of incoming) {
if (dep.usedSymbolsUpDirtyDown) {
dep.usedSymbolsUpDirtyDown = false;
node.usedSymbolsUpDirty = true;
}
}
if (node.usedSymbolsUpDirty) {
let e = visit(node, incoming, outgoing.map(depNodeId => {
let depNode = (0, _nullthrows().default)(assetGraph.getNode(depNodeId));
(0, _assert().default)(depNode.type === 'dependency');
return depNode;
}));
if (e.length > 0) {
node.usedSymbolsUpDirty = true;
errors.set(nodeId, e);
} else {
node.usedSymbolsUpDirty = false;
errors.delete(nodeId);
}
}
} else {
if (node.type === 'dependency') {
if (node.usedSymbolsUpDirtyUp) {
dirtyDeps.add(nodeId);
} else {
dirtyDeps.delete(nodeId);
}
}
}
};
assetGraph.postOrderDfsFast(nodeVisitor, rootNodeId);
}
let queue = dirtyDeps ?? changedDepsUsedSymbolsUpDirtyDownAssets;
while (queue.size > 0) {
let queuedNodeId = setPop(queue);
let node = (0, _nullthrows().default)(assetGraph.getNode(queuedNodeId));
if (node.type === 'asset') {
let incoming = assetGraph.getIncomingDependencies(node.value).map(dep => {
let depNode = assetGraph.getNodeByContentKey(dep.id);
(0, _assert().default)(depNode && depNode.type === 'dependency');
return depNode;
});
for (let dep of incoming) {
if (dep.usedSymbolsUpDirtyDown) {
dep.usedSymbolsUpDirtyDown = false;
node.usedSymbolsUpDirty = true;
}
}
let outgoing = assetGraph.getNodeIdsConnectedFrom(queuedNodeId).map(depNodeId => {
let depNode = (0, _nullthrows().default)(assetGraph.getNode(depNodeId));
(0, _assert().default)(depNode.type === 'dependency');
return depNode;
});
for (let dep of outgoing) {
if (dep.usedSymbolsUpDirtyUp) {
node.usedSymbolsUpDirty = true;
dep.usedSymbolsUpDirtyUp = false;
}
}
if (node.usedSymbolsUpDirty) {
let e = visit(node, incoming, outgoing);
if (e.length > 0) {
node.usedSymbolsUpDirty = true;
errors.set(queuedNodeId, e);
} else {
node.usedSymbolsUpDirty = false;
errors.delete(queuedNodeId);
}
}
for (let i of incoming) {
if (i.usedSymbolsUpDirtyUp) {
queue.add(assetGraph.getNodeIdByContentKey(i.id));
}
}
} else {
let connectedNodes = assetGraph.getNodeIdsConnectedTo(queuedNodeId);
if (connectedNodes.length > 0) {
queue.add(...connectedNodes);
}
}
}
return errors;
}
function getDependencyResolution(graph, depId) {
let depNodeId = graph.getNodeIdByContentKey(depId);
let connected = graph.getNodeIdsConnectedFrom(depNodeId);
(0, _assert().default)(connected.length <= 1);
let child = connected[0];
if (child) {
let childNode = (0, _nullthrows().default)(graph.getNode(child));
if (childNode.type === 'asset_group') {
return graph.getNodeIdsConnectedFrom(child);
} else {
return [child];
}
}
return [];
}
function equalMap(a, b) {
if (a.size !== b.size) return false;
for (let [k, v] of a) {
if (!b.has(k)) return false;
let vB = b.get(k);
if ((vB === null || vB === void 0 ? void 0 : vB.asset) !== (v === null || v === void 0 ? void 0 : v.asset) || (vB === null || vB === void 0 ? void 0 : vB.symbol) !== (v === null || v === void 0 ? void 0 : v.symbol)) return false;
}
return true;
}
function setPop(set) {
let v = (0, _nullthrows().default)(set.values().next().value);
set.delete(v);
return v;
}