Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@parcel/core

Package Overview
Dependencies
Maintainers
1
Versions
888
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@parcel/core - npm Package Compare versions

Comparing version 2.0.0-nightly.255 to 2.0.0-nightly.256

169

lib/AssetGraph.js

@@ -37,8 +37,7 @@ "use strict";

function nodeFromAssetGroup(assetGroup, deferred = false) {
function nodeFromAssetGroup(assetGroup) {
return {
id: (0, _utils.md5FromObject)(assetGroup),
type: 'asset_group',
value: assetGroup,
deferred
value: assetGroup
};

@@ -69,7 +68,4 @@ }

};
} // Types that are considered incomplete when they don't have a child node
}
const INCOMPLETE_TYPES = ['entry_specifier', 'entry_file', 'dependency', 'asset_group'];
class AssetGraph extends _Graph.default {

@@ -79,10 +75,4 @@ constructor(...args) {

_defineProperty(this, "onNodeAdded", void 0);
_defineProperty(this, "onNodeRemoved", void 0);
_defineProperty(this, "onIncompleteNode", void 0);
_defineProperty(this, "incompleteNodeIds", new Set());
_defineProperty(this, "hash", void 0);

@@ -96,3 +86,2 @@ }

res.incompleteNodeIds = opts.incompleteNodeIds;
res.hash = opts.hash;

@@ -106,3 +95,2 @@ return res;

return { ...super.serialize(),
incompleteNodeIds: this.incompleteNodeIds,
hash: this.hash

@@ -113,9 +101,5 @@ };

initOptions({
onNodeAdded,
onNodeRemoved,
onIncompleteNode
onNodeRemoved
} = {}) {
this.onNodeAdded = onNodeAdded;
this.onNodeRemoved = onNodeRemoved;
this.onIncompleteNode = onIncompleteNode;
}

@@ -149,9 +133,2 @@

this.hash = null;
let existingNode = this.getNode(node.id);
if (INCOMPLETE_TYPES.includes(node.type) && !node.complete && !node.deferred && (!existingNode || existingNode.deferred)) {
this.markIncomplete(node);
}
this.onNodeAdded && this.onNodeAdded(node);
return super.addNode(node);

@@ -162,3 +139,2 @@ }

this.hash = null;
this.incompleteNodeIds.delete(node.id);
this.onNodeRemoved && this.onNodeRemoved(node);

@@ -168,22 +144,11 @@ return super.removeNode(node);

markIncomplete(node) {
this.incompleteNodeIds.add(node.id);
if (this.onIncompleteNode) {
this.onIncompleteNode(node);
}
}
hasIncompleteNodes() {
return this.incompleteNodeIds.size > 0;
}
resolveEntry(entry, resolved) {
let entrySpecifierNode = nodeFromEntrySpecifier(entry);
resolveEntry(entry, resolved, correspondingRequest) {
let entrySpecifierNode = (0, _nullthrows.default)(this.getNode(nodeFromEntrySpecifier(entry).id));
(0, _assert.default)(entrySpecifierNode.type === 'entry_specifier');
entrySpecifierNode.correspondingRequest = correspondingRequest;
let entryFileNodes = resolved.map(file => nodeFromEntryFile(file));
this.replaceNodesConnectedTo(entrySpecifierNode, entryFileNodes);
this.incompleteNodeIds.delete(entrySpecifierNode.id);
}
resolveTargets(entry, targets) {
resolveTargets(entry, targets, correspondingRequest) {
let depNodes = targets.map(target => nodeFromDep((0, _Dependency.createDependency)({

@@ -196,28 +161,124 @@ moduleSpecifier: entry.filePath,

})));
let entryNode = nodeFromEntryFile(entry);
let entryNode = (0, _nullthrows.default)(this.getNode(nodeFromEntryFile(entry).id));
(0, _assert.default)(entryNode.type === 'entry_file');
entryNode.correspondingRequest = correspondingRequest;
if (this.hasNode(entryNode.id)) {
this.replaceNodesConnectedTo(entryNode, depNodes);
this.incompleteNodeIds.delete(entryNode.id);
}
}
resolveDependency(dependency, assetGroupNode) {
let depNode = this.nodes.get(dependency.id);
resolveDependency(dependency, assetGroup, correspondingRequest) {
let depNode = (0, _nullthrows.default)(this.nodes.get(dependency.id));
(0, _assert.default)(depNode.type === 'dependency');
if (!depNode) return;
this.incompleteNodeIds.delete(depNode.id);
depNode.correspondingRequest = correspondingRequest;
if (assetGroupNode) {
this.replaceNodesConnectedTo(depNode, [assetGroupNode]);
if (!assetGroup) {
return;
}
let assetGroupNode = nodeFromAssetGroup(assetGroup);
let existingAssetGroupNode = this.getNode(assetGroupNode.id);
this.replaceNodesConnectedTo(depNode, [existingAssetGroupNode !== null && existingAssetGroupNode !== void 0 ? existingAssetGroupNode : assetGroupNode]);
}
resolveAssetGroup(assetGroup, assets) {
shouldVisitChild(node, childNode) {
if (node.type !== 'dependency' || childNode.type !== 'asset_group' || childNode.deferred === false) {
return true;
}
let sideEffects = childNode.value.sideEffects;
let dependency = node.value;
let previouslyDeferred = childNode.deferred;
let defer = this.shouldDeferDependency(dependency, sideEffects);
dependency.isDeferred = defer;
node.hasDeferred = defer;
childNode.deferred = defer;
if (!previouslyDeferred && defer) {
this.markParentsWithHasDeferred(node);
} else if (previouslyDeferred && !defer) {
this.unmarkParentsWithHasDeferred(node);
}
return !defer;
}
markParentsWithHasDeferred(node) {
this.traverseAncestors(node, (_node, _, actions) => {
if (_node.type === 'asset') {
_node.hasDeferred = true;
} else if (_node.type === 'asset_group') {
_node.hasDeferred = true;
actions.skipChildren();
} else if (node !== _node) {
actions.skipChildren();
}
});
}
unmarkParentsWithHasDeferred(node) {
this.traverseAncestors(node, (_node, ctx, actions) => {
if (_node.type === 'asset') {
let hasDeferred = this.getNodesConnectedFrom(_node).some(_childNode => _childNode.hasDeferred == null ? false : _childNode.hasDeferred);
if (!hasDeferred) {
delete _node.hasDeferred;
}
return {
hasDeferred
};
} else if (_node.type === 'asset_group') {
if (!(ctx === null || ctx === void 0 ? void 0 : ctx.hasDeferred)) {
delete _node.hasDeferred;
}
actions.skipChildren();
} else if (node !== _node) {
actions.skipChildren();
}
});
} // Defer transforming this dependency if it is marked as weak, there are no side effects,
// no re-exported symbols are used by ancestor dependencies and the re-exporting asset isn't
// using a wildcard and isn't an entry (in library mode).
// This helps with performance building large libraries like `lodash-es`, which re-exports
// a huge number of functions since we can avoid even transforming the files that aren't used.
shouldDeferDependency(dependency, sideEffects) {
let defer = false;
if (dependency.isWeak && sideEffects === false && !dependency.symbols.has('*')) {
let depNode = this.getNode(dependency.id);
(0, _assert.default)(depNode);
let assets = this.getNodesConnectedTo(depNode);
let symbols = new Map([...dependency.symbols].map(([key, val]) => [val.local, key]));
(0, _assert.default)(assets.length === 1);
let firstAsset = assets[0];
(0, _assert.default)(firstAsset.type === 'asset');
let resolvedAsset = firstAsset.value;
let deps = this.getIncomingDependencies(resolvedAsset);
defer = deps.every(d => !(d.env.isLibrary && d.isEntry) && !d.symbols.has('*') && ![...d.symbols.keys()].some(symbol => {
var _resolvedAsset$symbol, _resolvedAsset$symbol2;
let assetSymbol = (_resolvedAsset$symbol = resolvedAsset.symbols) === null || _resolvedAsset$symbol === void 0 ? void 0 : (_resolvedAsset$symbol2 = _resolvedAsset$symbol.get(symbol)) === null || _resolvedAsset$symbol2 === void 0 ? void 0 : _resolvedAsset$symbol2.local;
return assetSymbol != null && symbols.has(assetSymbol);
}));
}
return defer;
}
resolveAssetGroup(assetGroup, assets, correspondingRequest) {
let assetGroupNode = nodeFromAssetGroup(assetGroup);
this.incompleteNodeIds.delete(assetGroupNode.id);
assetGroupNode = this.getNode(assetGroupNode.id);
if (!this.hasNode(assetGroupNode.id)) {
if (!assetGroupNode) {
return;
}
(0, _assert.default)(assetGroupNode.type === 'asset_group');
assetGroupNode.correspondingRequest = correspondingRequest;
let dependentAssetKeys = [];

@@ -224,0 +285,0 @@ let assetObjects = [];

@@ -12,2 +12,4 @@ "use strict";

var _assert = _interopRequireDefault(require("assert"));
var _path = _interopRequireDefault(require("path"));

@@ -49,4 +51,2 @@

const requestPriorities = [['entry_request'], ['target_request'], ['dep_path_request', 'asset_request']];
class AssetGraphBuilder extends _events.default {

@@ -94,2 +94,6 @@ constructor(...args) {

_defineProperty(this, "entries", void 0);
_defineProperty(this, "initialAssetGroups", void 0);
_defineProperty(this, "handle", void 0);

@@ -108,2 +112,4 @@ }

this.optionsRef = optionsRef;
this.entries = entries;
this.initialAssetGroups = assetRequests;
this.workerFarm = workerFarm;

@@ -144,4 +150,3 @@ this.assetRequests = []; // TODO: changing these should not throw away the entire graph.

this.assetGraph.initOptions({
onNodeRemoved: node => this.handleNodeRemovedFromAssetGraph(node),
onIncompleteNode: node => this.handleIncompleteNode(node)
onNodeRemoved: node => this.handleNodeRemovedFromAssetGraph(node)
});

@@ -215,36 +220,33 @@ let assetGraph = this.assetGraph;

this.rejected = new Map();
let lastQueueError;
let root = this.assetGraph.getRootNode();
for (let currPriorities of requestPriorities) {
if (!this.requestTracker.hasInvalidRequests()) {
break;
}
if (!root) {
throw new Error('A root node is required to traverse');
}
let promises = [];
let visited = new Set([root.id]);
for (let request of this.requestTracker.getInvalidRequests()) {
const visit = node => {
let request = this.getCorrespondingRequest(node);
if (!node.complete && !node.deferred && request != null && !this.requestTracker.hasValidResult((0, _nullthrows.default)(request).id)) {
// $FlowFixMe
if (currPriorities.includes(request.type)) {
promises.push(this.queueRequest(request, {
signal
}));
}
this.queueRequest(request, {
signal
}).then(() => visitChildren(node));
} else {
visitChildren(node);
}
};
if (lastQueueError) {
throw lastQueueError;
const visitChildren = node => {
for (let child of this.assetGraph.getNodesConnectedFrom(node)) {
if ((!visited.has(child.id) || child.hasDeferred) && this.assetGraph.shouldVisitChild(node, child)) {
visited.add(child.id);
visit(child);
}
}
};
this.queue.run().catch(e => {
lastQueueError = e;
});
await Promise.all(promises);
}
if (this.assetGraph.hasIncompleteNodes()) {
for (let id of this.assetGraph.incompleteNodeIds) {
this.processIncompleteAssetGraphNode((0, _nullthrows.default)(this.assetGraph.getNode(id)), signal);
}
}
visit(root);
await this.queue.run();

@@ -254,2 +256,3 @@ let errors = [];

for (let [requestId, error] of this.rejected) {
// ? Is this still needed?
if (this.requestTracker.isTracked(requestId)) {

@@ -284,3 +287,3 @@ errors.push(error);

configRef: this.configRef
})); // Skip sending validation requests if no validators were no validators configured
})); // Skip sending validation requests if no validators were configured

@@ -331,5 +334,6 @@ if (trackedRequestsDesc.length === 0) {

this.assetRequests.push(request);
let assetActuallyChanged = !this.requestTracker.hasValidResult(request.id);
let result = await this.assetRequestRunner.runRequest(request.request, runOpts);
if (result != null) {
if (assetActuallyChanged && result != null) {
for (let asset of result.assets) {

@@ -346,2 +350,9 @@ this.changedAssets.set(asset.id, asset);

getCorrespondingRequest(node) {
let requestNode = node.correspondingRequest != null ? this.requestGraph.getNode(node.correspondingRequest) : null;
if (requestNode != null) {
(0, _assert.default)(requestNode.type === 'request');
return requestNode.value;
}
switch (node.type) {

@@ -390,16 +401,2 @@ case 'entry_specifier':

processIncompleteAssetGraphNode(node, signal) {
let request = (0, _nullthrows.default)(this.getCorrespondingRequest(node));
if (!this.requestTracker.hasValidResult(request.id)) {
this.queueRequest(request, {
signal
});
}
}
handleIncompleteNode(node) {
this.processIncompleteAssetGraphNode(node);
}
handleNodeRemovedFromAssetGraph(node) {

@@ -406,0 +403,0 @@ let request = this.getCorrespondingRequest(node);

@@ -64,3 +64,3 @@ "use strict";

onComplete(request, result, api) {
this.assetGraph.resolveAssetGroup(request, result.assets);
this.assetGraph.resolveAssetGroup(request, result.assets, api.getId());
let {

@@ -67,0 +67,0 @@ assets,

@@ -8,6 +8,2 @@ "use strict";

var _assert = _interopRequireDefault(require("assert"));
var _AssetGraph = require("../AssetGraph");
var _RequestTracker = require("../RequestTracker");

@@ -51,79 +47,9 @@

if (!assetGroup) {
this.assetGraph.resolveDependency(dependency, null);
this.assetGraph.resolveDependency(dependency, null, api.getId());
return;
}
let defer = this.shouldDeferDependency(dependency, assetGroup.sideEffects);
dependency.isDeferred = defer;
let assetGroupNode = (0, _AssetGraph.nodeFromAssetGroup)(assetGroup, defer);
let existingAssetGroupNode = this.assetGraph.getNode(assetGroupNode.id);
this.assetGraph.resolveDependency(dependency, assetGroup, api.getId()); // ? Should this happen if asset is deferred?
if (existingAssetGroupNode) {
// Don't overwrite non-deferred asset groups with deferred ones
(0, _assert.default)(existingAssetGroupNode.type === 'asset_group');
assetGroupNode.deferred = existingAssetGroupNode.deferred && defer;
}
this.assetGraph.resolveDependency(dependency, assetGroupNode);
if (existingAssetGroupNode) {
// Node already existed, that asset might have deferred dependencies,
// recheck all dependencies of all assets of this asset group
this.assetGraph.traverse((node, parent, actions) => {
if (node == assetGroupNode) {
return;
}
if (node.type === 'dependency' && !node.value.isDeferred) {
actions.skipChildren();
return;
}
if (node.type == 'asset_group') {
(0, _assert.default)(parent && parent.type === 'dependency');
if (node.deferred && !this.shouldDeferDependency(parent.value, node.value.sideEffects)) {
parent.value.isDeferred = false;
node.deferred = false;
this.assetGraph.markIncomplete(node);
}
actions.skipChildren();
}
return node;
}, assetGroupNode);
} // ? Should this happen if asset is deferred?
api.invalidateOnFileDelete(assetGroup.filePath); // TODO: invalidate dep path requests that have failed and a file creation may fulfill the request
} // Defer transforming this dependency if it is marked as weak, there are no side effects,
// no re-exported symbols are used by ancestor dependencies and the re-exporting asset isn't
// using a wildcard and isn't an entry (in library mode).
// This helps with performance building large libraries like `lodash-es`, which re-exports
// a huge number of functions since we can avoid even transforming the files that aren't used.
shouldDeferDependency(dependency, sideEffects) {
let defer = false;
if (dependency.isWeak && sideEffects === false && !dependency.symbols.has('*')) {
let depNode = this.assetGraph.getNode(dependency.id);
(0, _assert.default)(depNode);
let assets = this.assetGraph.getNodesConnectedTo(depNode);
let symbols = new Map([...dependency.symbols].map(([key, val]) => [val.local, key]));
(0, _assert.default)(assets.length === 1);
let firstAsset = assets[0];
(0, _assert.default)(firstAsset.type === 'asset');
let resolvedAsset = firstAsset.value;
let deps = this.assetGraph.getIncomingDependencies(resolvedAsset);
defer = deps.every(d => !(d.env.isLibrary && d.isEntry) && !d.symbols.has('*') && ![...d.symbols.keys()].some(symbol => {
var _resolvedAsset$symbol, _resolvedAsset$symbol2;
let assetSymbol = (_resolvedAsset$symbol = resolvedAsset.symbols) === null || _resolvedAsset$symbol === void 0 ? void 0 : (_resolvedAsset$symbol2 = _resolvedAsset$symbol.get(symbol)) === null || _resolvedAsset$symbol2 === void 0 ? void 0 : _resolvedAsset$symbol2.local;
return assetSymbol != null && symbols.has(assetSymbol);
}));
}
return defer;
api.invalidateOnFileDelete(assetGroup.filePath);
}

@@ -130,0 +56,0 @@

@@ -34,3 +34,3 @@ "use strict";

onComplete(request, result, api) {
this.assetGraph.resolveEntry(request, result.entries); // Connect files like package.json that affect the entry
this.assetGraph.resolveEntry(request, result.entries, api.getId()); // Connect files like package.json that affect the entry
// resolution so we invalidate when they change.

@@ -37,0 +37,0 @@

@@ -34,3 +34,3 @@ "use strict";

onComplete(request, result, api) {
this.assetGraph.resolveTargets(request, result.targets); // Connect files like package.json that affect the target
this.assetGraph.resolveTargets(request, result.targets, api.getId()); // Connect files like package.json that affect the target
// resolution so we invalidate when they change.

@@ -37,0 +37,0 @@

@@ -415,3 +415,4 @@ "use strict";

this.tracker.storeResult(requestId, result);
}
},
getId: () => requestId
};

@@ -418,0 +419,0 @@ }

{
"name": "@parcel/core",
"version": "2.0.0-nightly.255+5175a66b",
"version": "2.0.0-nightly.256+e8d7d944",
"license": "MIT",

@@ -19,13 +19,13 @@ "publishConfig": {

"dependencies": {
"@parcel/cache": "2.0.0-nightly.257+5175a66b",
"@parcel/diagnostic": "2.0.0-nightly.257+5175a66b",
"@parcel/events": "2.0.0-nightly.257+5175a66b",
"@parcel/fs": "2.0.0-nightly.257+5175a66b",
"@parcel/logger": "2.0.0-nightly.257+5175a66b",
"@parcel/package-manager": "2.0.0-nightly.257+5175a66b",
"@parcel/plugin": "2.0.0-nightly.257+5175a66b",
"@parcel/cache": "2.0.0-nightly.258+e8d7d944",
"@parcel/diagnostic": "2.0.0-nightly.258+e8d7d944",
"@parcel/events": "2.0.0-nightly.258+e8d7d944",
"@parcel/fs": "2.0.0-nightly.258+e8d7d944",
"@parcel/logger": "2.0.0-nightly.258+e8d7d944",
"@parcel/package-manager": "2.0.0-nightly.258+e8d7d944",
"@parcel/plugin": "2.0.0-nightly.258+e8d7d944",
"@parcel/source-map": "2.0.0-alpha.4.9",
"@parcel/types": "2.0.0-nightly.257+5175a66b",
"@parcel/utils": "2.0.0-nightly.257+5175a66b",
"@parcel/workers": "2.0.0-nightly.257+5175a66b",
"@parcel/types": "2.0.0-nightly.258+e8d7d944",
"@parcel/utils": "2.0.0-nightly.258+e8d7d944",
"@parcel/workers": "2.0.0-nightly.258+e8d7d944",
"abortcontroller-polyfill": "^1.1.9",

@@ -46,3 +46,3 @@ "browserslist": "^4.6.6",

},
"gitHead": "5175a66b28cd2a6090b247204e550cc0264aabbe"
"gitHead": "e8d7d94484f887c9f61093fca0c63b499305b933"
}

@@ -13,3 +13,2 @@ // @flow strict-local

Entry,
NodeId,
Target,

@@ -27,4 +26,2 @@ } from './types';

...GraphOpts<AssetGraphNode>,
onIncompleteNode?: (node: AssetGraphNode) => mixed,
onNodeAdded?: (node: AssetGraphNode) => mixed,
onNodeRemoved?: (node: AssetGraphNode) => mixed,

@@ -52,6 +49,3 @@ |};

export function nodeFromAssetGroup(
assetGroup: AssetGroup,
deferred: boolean = false,
) {
export function nodeFromAssetGroup(assetGroup: AssetGroup) {
return {

@@ -61,3 +55,2 @@ id: md5FromObject(assetGroup),

value: assetGroup,
deferred,
};

@@ -90,15 +83,4 @@ }

// Types that are considered incomplete when they don't have a child node
const INCOMPLETE_TYPES = [
'entry_specifier',
'entry_file',
'dependency',
'asset_group',
];
export default class AssetGraph extends Graph<AssetGraphNode> {
onNodeAdded: ?(node: AssetGraphNode) => mixed;
onNodeRemoved: ?(node: AssetGraphNode) => mixed;
onIncompleteNode: ?(node: AssetGraphNode) => mixed;
incompleteNodeIds: Set<NodeId> = new Set();
hash: ?string;

@@ -111,3 +93,2 @@

// $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
res.incompleteNodeIds = opts.incompleteNodeIds;
res.hash = opts.hash;

@@ -122,3 +103,2 @@ return res;

...super.serialize(),
incompleteNodeIds: this.incompleteNodeIds,
hash: this.hash,

@@ -128,10 +108,4 @@ };

initOptions({
onNodeAdded,
onNodeRemoved,
onIncompleteNode,
}: AssetGraphOpts = {}) {
this.onNodeAdded = onNodeAdded;
initOptions({onNodeRemoved}: AssetGraphOpts = {}) {
this.onNodeRemoved = onNodeRemoved;
this.onIncompleteNode = onIncompleteNode;
}

@@ -159,12 +133,2 @@

this.hash = null;
let existingNode = this.getNode(node.id);
if (
INCOMPLETE_TYPES.includes(node.type) &&
!node.complete &&
!node.deferred &&
(!existingNode || existingNode.deferred)
) {
this.markIncomplete(node);
}
this.onNodeAdded && this.onNodeAdded(node);
return super.addNode(node);

@@ -175,3 +139,2 @@ }

this.hash = null;
this.incompleteNodeIds.delete(node.id);
this.onNodeRemoved && this.onNodeRemoved(node);

@@ -181,21 +144,21 @@ return super.removeNode(node);

markIncomplete(node: AssetGraphNode) {
this.incompleteNodeIds.add(node.id);
if (this.onIncompleteNode) {
this.onIncompleteNode(node);
}
}
hasIncompleteNodes() {
return this.incompleteNodeIds.size > 0;
}
resolveEntry(entry: string, resolved: Array<Entry>) {
let entrySpecifierNode = nodeFromEntrySpecifier(entry);
resolveEntry(
entry: string,
resolved: Array<Entry>,
correspondingRequest: string,
) {
let entrySpecifierNode = nullthrows(
this.getNode(nodeFromEntrySpecifier(entry).id),
);
invariant(entrySpecifierNode.type === 'entry_specifier');
entrySpecifierNode.correspondingRequest = correspondingRequest;
let entryFileNodes = resolved.map(file => nodeFromEntryFile(file));
this.replaceNodesConnectedTo(entrySpecifierNode, entryFileNodes);
this.incompleteNodeIds.delete(entrySpecifierNode.id);
}
resolveTargets(entry: Entry, targets: Array<Target>) {
resolveTargets(
entry: Entry,
targets: Array<Target>,
correspondingRequest: string,
) {
let depNodes = targets.map(target =>

@@ -213,6 +176,7 @@ nodeFromDep(

let entryNode = nodeFromEntryFile(entry);
let entryNode = nullthrows(this.getNode(nodeFromEntryFile(entry).id));
invariant(entryNode.type === 'entry_file');
entryNode.correspondingRequest = correspondingRequest;
if (this.hasNode(entryNode.id)) {
this.replaceNodesConnectedTo(entryNode, depNodes);
this.incompleteNodeIds.delete(entryNode.id);
}

@@ -223,19 +187,131 @@ }

dependency: Dependency,
assetGroupNode: AssetGroupNode | null,
assetGroup: AssetGroup | null,
correspondingRequest: string,
) {
let depNode = this.nodes.get(dependency.id);
let depNode = nullthrows(this.nodes.get(dependency.id));
invariant(depNode.type === 'dependency');
if (!depNode) return;
this.incompleteNodeIds.delete(depNode.id);
depNode.correspondingRequest = correspondingRequest;
if (assetGroupNode) {
this.replaceNodesConnectedTo(depNode, [assetGroupNode]);
if (!assetGroup) {
return;
}
let assetGroupNode = nodeFromAssetGroup(assetGroup);
let existingAssetGroupNode = this.getNode(assetGroupNode.id);
this.replaceNodesConnectedTo(depNode, [
existingAssetGroupNode ?? assetGroupNode,
]);
}
resolveAssetGroup(assetGroup: AssetGroup, assets: Array<Asset>) {
shouldVisitChild(node: AssetGraphNode, childNode: AssetGraphNode) {
if (
node.type !== 'dependency' ||
childNode.type !== 'asset_group' ||
childNode.deferred === false
) {
return true;
}
let sideEffects = childNode.value.sideEffects;
let dependency = node.value;
let previouslyDeferred = childNode.deferred;
let defer = this.shouldDeferDependency(dependency, sideEffects);
dependency.isDeferred = defer;
node.hasDeferred = defer;
childNode.deferred = defer;
if (!previouslyDeferred && defer) {
this.markParentsWithHasDeferred(node);
} else if (previouslyDeferred && !defer) {
this.unmarkParentsWithHasDeferred(node);
}
return !defer;
}
markParentsWithHasDeferred(node: DependencyNode) {
this.traverseAncestors(node, (_node, _, actions) => {
if (_node.type === 'asset') {
_node.hasDeferred = true;
} else if (_node.type === 'asset_group') {
_node.hasDeferred = true;
actions.skipChildren();
} else if (node !== _node) {
actions.skipChildren();
}
});
}
unmarkParentsWithHasDeferred(node: DependencyNode) {
this.traverseAncestors(node, (_node, ctx, actions) => {
if (_node.type === 'asset') {
let hasDeferred = this.getNodesConnectedFrom(_node).some(_childNode =>
_childNode.hasDeferred == null ? false : _childNode.hasDeferred,
);
if (!hasDeferred) {
delete _node.hasDeferred;
}
return {hasDeferred};
} else if (_node.type === 'asset_group') {
if (!ctx?.hasDeferred) {
delete _node.hasDeferred;
}
actions.skipChildren();
} else if (node !== _node) {
actions.skipChildren();
}
});
}
// Defer transforming this dependency if it is marked as weak, there are no side effects,
// no re-exported symbols are used by ancestor dependencies and the re-exporting asset isn't
// using a wildcard and isn't an entry (in library mode).
// This helps with performance building large libraries like `lodash-es`, which re-exports
// a huge number of functions since we can avoid even transforming the files that aren't used.
shouldDeferDependency(dependency: Dependency, sideEffects: ?boolean) {
let defer = false;
if (
dependency.isWeak &&
sideEffects === false &&
!dependency.symbols.has('*')
) {
let depNode = this.getNode(dependency.id);
invariant(depNode);
let assets = this.getNodesConnectedTo(depNode);
let symbols = new Map(
[...dependency.symbols].map(([key, val]) => [val.local, key]),
);
invariant(assets.length === 1);
let firstAsset = assets[0];
invariant(firstAsset.type === 'asset');
let resolvedAsset = firstAsset.value;
let deps = this.getIncomingDependencies(resolvedAsset);
defer = deps.every(
d =>
!(d.env.isLibrary && d.isEntry) &&
!d.symbols.has('*') &&
![...d.symbols.keys()].some(symbol => {
let assetSymbol = resolvedAsset.symbols?.get(symbol)?.local;
return assetSymbol != null && symbols.has(assetSymbol);
}),
);
}
return defer;
}
resolveAssetGroup(
assetGroup: AssetGroup,
assets: Array<Asset>,
correspondingRequest: string,
) {
let assetGroupNode = nodeFromAssetGroup(assetGroup);
this.incompleteNodeIds.delete(assetGroupNode.id);
if (!this.hasNode(assetGroupNode.id)) {
assetGroupNode = this.getNode(assetGroupNode.id);
if (!assetGroupNode) {
return;
}
invariant(assetGroupNode.type === 'asset_group');
assetGroupNode.correspondingRequest = correspondingRequest;

@@ -242,0 +318,0 @@ let dependentAssetKeys = [];

@@ -21,2 +21,3 @@ // @flow strict-local

import nullthrows from 'nullthrows';
import invariant from 'assert';
import path from 'path';

@@ -52,8 +53,2 @@ import {md5FromObject, md5FromString, PromiseQueue} from '@parcel/utils';

const requestPriorities: $ReadOnlyArray<$ReadOnlyArray<string>> = [
['entry_request'],
['target_request'],
['dep_path_request', 'asset_request'],
];
type AssetGraphBuildRequest =

@@ -86,2 +81,4 @@ | EntryRequest

cacheKey: string;
entries: ?Array<string>;
initialAssetGroups: ?Array<AssetRequestDesc>;

@@ -100,2 +97,4 @@ handle: Handle;

this.optionsRef = optionsRef;
this.entries = entries;
this.initialAssetGroups = assetRequests;
this.workerFarm = workerFarm;

@@ -129,3 +128,2 @@ this.assetRequests = [];

onNodeRemoved: node => this.handleNodeRemovedFromAssetGraph(node),
onIncompleteNode: node => this.handleIncompleteNode(node),
});

@@ -209,34 +207,40 @@

this.rejected = new Map();
let lastQueueError;
for (let currPriorities of requestPriorities) {
if (!this.requestTracker.hasInvalidRequests()) {
break;
}
let promises = [];
for (let request of this.requestTracker.getInvalidRequests()) {
// $FlowFixMe
let assetGraphBuildRequest: AssetGraphBuildRequest = (request: any);
if (currPriorities.includes(request.type)) {
promises.push(this.queueRequest(assetGraphBuildRequest, {signal}));
}
}
if (lastQueueError) {
throw lastQueueError;
}
this.queue.run().catch(e => {
lastQueueError = e;
});
await Promise.all(promises);
let root = this.assetGraph.getRootNode();
if (!root) {
throw new Error('A root node is required to traverse');
}
if (this.assetGraph.hasIncompleteNodes()) {
for (let id of this.assetGraph.incompleteNodeIds) {
this.processIncompleteAssetGraphNode(
nullthrows(this.assetGraph.getNode(id)),
let visited = new Set([root.id]);
const visit = node => {
let request = this.getCorrespondingRequest(node);
if (
!node.complete &&
!node.deferred &&
request != null &&
!this.requestTracker.hasValidResult(nullthrows(request).id)
) {
// $FlowFixMe
this.queueRequest(request, {
signal,
);
}).then(() => visitChildren(node));
} else {
visitChildren(node);
}
}
};
const visitChildren = node => {
for (let child of this.assetGraph.getNodesConnectedFrom(node)) {
if (
(!visited.has(child.id) || child.hasDeferred) &&
this.assetGraph.shouldVisitChild(node, child)
) {
visited.add(child.id);
visit(child);
}
}
};
visit(root);
await this.queue.run();

@@ -246,2 +250,3 @@

for (let [requestId, error] of this.rejected) {
// ? Is this still needed?
if (this.requestTracker.isTracked(requestId)) {

@@ -262,3 +267,2 @@ errors.push(error);

this.changedAssets = new Map();
return {assetGraph: this.assetGraph, changedAssets: changedAssets};

@@ -285,3 +289,3 @@ }

// Skip sending validation requests if no validators were no validators configured
// Skip sending validation requests if no validators were configured
if (trackedRequestsDesc.length === 0) {

@@ -311,2 +315,3 @@ return;

}
try {

@@ -330,2 +335,5 @@ await this.runRequest(request, runOpts);

this.assetRequests.push(request);
let assetActuallyChanged = !this.requestTracker.hasValidResult(
request.id,
);
let result = await this.assetRequestRunner.runRequest(

@@ -335,3 +343,3 @@ request.request,

);
if (result != null) {
if (assetActuallyChanged && result != null) {
for (let asset of result.assets) {

@@ -347,2 +355,10 @@ this.changedAssets.set(asset.id, asset);

getCorrespondingRequest(node: AssetGraphNode) {
let requestNode =
node.correspondingRequest != null
? this.requestGraph.getNode(node.correspondingRequest)
: null;
if (requestNode != null) {
invariant(requestNode.type === 'request');
return requestNode.value;
}
switch (node.type) {

@@ -384,15 +400,2 @@ case 'entry_specifier': {

processIncompleteAssetGraphNode(node: AssetGraphNode, signal: ?AbortSignal) {
let request = nullthrows(this.getCorrespondingRequest(node));
if (!this.requestTracker.hasValidResult(request.id)) {
this.queueRequest(request, {
signal,
});
}
}
handleIncompleteNode(node: AssetGraphNode) {
this.processIncompleteAssetGraphNode(node);
}
handleNodeRemovedFromAssetGraph(node: AssetGraphNode) {

@@ -399,0 +402,0 @@ let request = this.getCorrespondingRequest(node);

@@ -286,3 +286,2 @@ // @flow strict-local

await this.#assetGraphBuilder.validate();
return event;

@@ -289,0 +288,0 @@ } catch (e) {

@@ -73,3 +73,3 @@ // @flow strict-local

) {
this.assetGraph.resolveAssetGroup(request, result.assets);
this.assetGraph.resolveAssetGroup(request, result.assets, api.getId());

@@ -76,0 +76,0 @@ let {assets, configRequests} = result;

@@ -7,4 +7,2 @@ // @flow strict-local

import invariant from 'assert';
import {nodeFromAssetGroup} from '../AssetGraph';
import {RequestRunner} from '../RequestTracker';

@@ -57,91 +55,11 @@ import ResolverRunner from '../ResolverRunner';

if (!assetGroup) {
this.assetGraph.resolveDependency(dependency, null);
this.assetGraph.resolveDependency(dependency, null, api.getId());
return;
}
let defer = this.shouldDeferDependency(dependency, assetGroup.sideEffects);
dependency.isDeferred = defer;
this.assetGraph.resolveDependency(dependency, assetGroup, api.getId());
let assetGroupNode = nodeFromAssetGroup(assetGroup, defer);
let existingAssetGroupNode = this.assetGraph.getNode(assetGroupNode.id);
if (existingAssetGroupNode) {
// Don't overwrite non-deferred asset groups with deferred ones
invariant(existingAssetGroupNode.type === 'asset_group');
assetGroupNode.deferred = existingAssetGroupNode.deferred && defer;
}
this.assetGraph.resolveDependency(dependency, assetGroupNode);
if (existingAssetGroupNode) {
// Node already existed, that asset might have deferred dependencies,
// recheck all dependencies of all assets of this asset group
this.assetGraph.traverse((node, parent, actions) => {
if (node == assetGroupNode) {
return;
}
if (node.type === 'dependency' && !node.value.isDeferred) {
actions.skipChildren();
return;
}
if (node.type == 'asset_group') {
invariant(parent && parent.type === 'dependency');
if (
node.deferred &&
!this.shouldDeferDependency(parent.value, node.value.sideEffects)
) {
parent.value.isDeferred = false;
node.deferred = false;
this.assetGraph.markIncomplete(node);
}
actions.skipChildren();
}
return node;
}, assetGroupNode);
}
// ? Should this happen if asset is deferred?
api.invalidateOnFileDelete(assetGroup.filePath);
// TODO: invalidate dep path requests that have failed and a file creation may fulfill the request
}
// Defer transforming this dependency if it is marked as weak, there are no side effects,
// no re-exported symbols are used by ancestor dependencies and the re-exporting asset isn't
// using a wildcard and isn't an entry (in library mode).
// This helps with performance building large libraries like `lodash-es`, which re-exports
// a huge number of functions since we can avoid even transforming the files that aren't used.
shouldDeferDependency(dependency: Dependency, sideEffects: ?boolean) {
let defer = false;
if (
dependency.isWeak &&
sideEffects === false &&
!dependency.symbols.has('*')
) {
let depNode = this.assetGraph.getNode(dependency.id);
invariant(depNode);
let assets = this.assetGraph.getNodesConnectedTo(depNode);
let symbols = new Map(
[...dependency.symbols].map(([key, val]) => [val.local, key]),
);
invariant(assets.length === 1);
let firstAsset = assets[0];
invariant(firstAsset.type === 'asset');
let resolvedAsset = firstAsset.value;
let deps = this.assetGraph.getIncomingDependencies(resolvedAsset);
defer = deps.every(
d =>
!(d.env.isLibrary && d.isEntry) &&
!d.symbols.has('*') &&
![...d.symbols.keys()].some(symbol => {
let assetSymbol = resolvedAsset.symbols?.get(symbol)?.local;
return assetSymbol != null && symbols.has(assetSymbol);
}),
);
}
return defer;
}
}

@@ -42,3 +42,3 @@ // @flow strict-local

onComplete(request: FilePath, result: EntryResult, api: RequestRunnerAPI) {
this.assetGraph.resolveEntry(request, result.entries);
this.assetGraph.resolveEntry(request, result.entries, api.getId());

@@ -45,0 +45,0 @@ // Connect files like package.json that affect the entry

@@ -44,3 +44,3 @@ // @flow strict-local

) {
this.assetGraph.resolveTargets(request, result.targets);
this.assetGraph.resolveTargets(request, result.targets, api.getId());

@@ -47,0 +47,0 @@ // Connect files like package.json that affect the target

@@ -376,2 +376,3 @@ // @flow strict-local

storeResult: (result: mixed) => void,
getId: () => string,
|};

@@ -452,2 +453,3 @@

},
getId: () => requestId,
};

@@ -454,0 +456,0 @@

@@ -181,3 +181,8 @@ // @flow strict-local

export type AssetNode = {|id: string, +type: 'asset', value: Asset|};
export type AssetNode = {|
id: string,
+type: 'asset',
value: Asset,
hasDeferred?: boolean,
|};

@@ -189,2 +194,4 @@ export type DependencyNode = {|

complete?: boolean,
correspondingRequest?: string,
hasDeferred?: boolean,
|};

@@ -214,3 +221,5 @@

value: AssetGroup,
deferred: boolean,
deferred?: boolean,
correspondingRequest?: string,
hasDeferred?: boolean,
|};

@@ -234,2 +243,3 @@

value: ModuleSpecifier,
correspondingRequest?: string,
|};

@@ -246,2 +256,3 @@

value: Entry,
correspondingRequest?: string,
|};

@@ -248,0 +259,0 @@

// @flow
import assert from 'assert';
import nullthrows from 'nullthrows';
import AssetGraph, {

@@ -8,2 +9,3 @@ nodeFromAssetGroup,

nodeFromEntryFile,
nodeFromAsset,
} from '../src/AssetGraph';

@@ -14,2 +16,4 @@ import {createDependency} from '../src/Dependency';

const invariant = assert;
const DEFAULT_ENV = createEnvironment({

@@ -52,5 +56,7 @@ context: 'browser',

graph.resolveEntry('/path/to/index1', [
{filePath: '/path/to/index1/src/main.js'},
]);
graph.resolveEntry(
'/path/to/index1',
[{filePath: '/path/to/index1/src/main.js'}],
'123',
);

@@ -76,12 +82,24 @@ assert(

graph.resolveEntry('/path/to/index1', [
graph.resolveEntry(
'/path/to/index1',
[{filePath: '/path/to/index1/src/main.js'}],
'1',
);
graph.resolveEntry(
'/path/to/index2',
[{filePath: '/path/to/index2/src/main.js'}],
'2',
);
graph.resolveTargets(
{filePath: '/path/to/index1/src/main.js'},
]);
graph.resolveEntry('/path/to/index2', [
TARGETS,
'3',
);
graph.resolveTargets(
{filePath: '/path/to/index2/src/main.js'},
]);
TARGETS,
'4',
);
graph.resolveTargets({filePath: '/path/to/index1/src/main.js'}, TARGETS);
graph.resolveTargets({filePath: '/path/to/index2/src/main.js'}, TARGETS);
assert(

@@ -158,6 +176,12 @@ graph.nodes.has(

graph.resolveEntry('/path/to/index', [
graph.resolveEntry(
'/path/to/index',
[{filePath: '/path/to/index/src/main.js'}],
'1',
);
graph.resolveTargets(
{filePath: '/path/to/index/src/main.js'},
]);
graph.resolveTargets({filePath: '/path/to/index/src/main.js'}, TARGETS);
TARGETS,
'2',
);

@@ -172,3 +196,3 @@ let dep = createDependency({

graph.resolveDependency(dep, nodeFromAssetGroup(req));
graph.resolveDependency(dep, req, '3');
assert(graph.nodes.has(nodeFromAssetGroup(req).id));

@@ -178,3 +202,3 @@ assert(graph.hasEdge(dep.id, nodeFromAssetGroup(req).id));

let req2 = {filePath: '/index.jsx', env: DEFAULT_ENV};
graph.resolveDependency(dep, nodeFromAssetGroup(req2));
graph.resolveDependency(dep, req2, '4');
assert(!graph.nodes.has(nodeFromAssetGroup(req).id));

@@ -185,3 +209,3 @@ assert(graph.nodes.has(nodeFromAssetGroup(req2).id));

graph.resolveDependency(dep, nodeFromAssetGroup(req2));
graph.resolveDependency(dep, req2, '5');
assert(graph.nodes.has(nodeFromAssetGroup(req2).id));

@@ -198,6 +222,12 @@ assert(graph.hasEdge(dep.id, nodeFromAssetGroup(req2).id));

graph.resolveEntry('/path/to/index', [
graph.resolveEntry(
'/path/to/index',
[{filePath: '/path/to/index/src/main.js'}],
'1',
);
graph.resolveTargets(
{filePath: '/path/to/index/src/main.js'},
]);
graph.resolveTargets({filePath: '/path/to/index/src/main.js'}, TARGETS);
TARGETS,
'2',
);

@@ -213,3 +243,3 @@ let dep = createDependency({

let req = {filePath, env: DEFAULT_ENV};
graph.resolveDependency(dep, nodeFromAssetGroup(req));
graph.resolveDependency(dep, req, '3');
let sourcePath = filePath;

@@ -270,3 +300,3 @@ let assets = [

graph.resolveAssetGroup(req, assets);
graph.resolveAssetGroup(req, assets, '4');
assert(graph.nodes.has('1'));

@@ -317,3 +347,3 @@ assert(graph.nodes.has('2'));

graph.resolveAssetGroup(req, assets2);
graph.resolveAssetGroup(req, assets2, '5');
assert(graph.nodes.has('1'));

@@ -338,4 +368,12 @@ assert(graph.nodes.has('2'));

graph.resolveEntry('./index', [{filePath: '/path/to/index/src/main.js'}]);
graph.resolveTargets({filePath: '/path/to/index/src/main.js'}, TARGETS);
graph.resolveEntry(
'./index',
[{filePath: '/path/to/index/src/main.js'}],
'1',
);
graph.resolveTargets(
{filePath: '/path/to/index/src/main.js'},
TARGETS,
'2',
);

@@ -350,3 +388,3 @@ let dep = createDependency({

let req = {filePath, env: DEFAULT_ENV};
graph.resolveDependency(dep, nodeFromAssetGroup(req));
graph.resolveDependency(dep, req, '123');
let sourcePath = filePath;

@@ -397,3 +435,3 @@ let dep1 = createDependency({

graph.resolveAssetGroup(req, assets);
graph.resolveAssetGroup(req, assets, '3');
assert(graph.nodes.has('1'));

@@ -410,2 +448,45 @@ assert(graph.nodes.has('2'));

});
it('should support marking and unmarking parents with hasDeferred', () => {
let graph = new AssetGraph();
let assetGroup = {filePath: '/index.js', env: DEFAULT_ENV};
let assetGroupNode = nodeFromAssetGroup(assetGroup);
graph.initialize({assetGroups: [assetGroup]});
let dependency = createDependency({
moduleSpecifier: './utils',
env: DEFAULT_ENV,
sourcePath: '/index.js',
});
let depNode = nodeFromDep(dependency);
let asset = createAsset({
id: '1',
filePath: '/index.js',
type: 'js',
isSource: true,
hash: '#1',
stats,
dependencies: new Map([['utils', dependency]]),
env: DEFAULT_ENV,
includedFiles: new Map(),
});
let assetNode = nodeFromAsset(asset);
graph.resolveAssetGroup(assetGroup, [asset], '1');
graph.markParentsWithHasDeferred(depNode);
let node = nullthrows(graph.getNode(assetNode.id));
invariant(node.type === 'asset');
assert(node.hasDeferred);
node = nullthrows(graph.getNode(assetGroupNode.id));
invariant(node.type === 'asset_group');
assert(node.hasDeferred);
graph.unmarkParentsWithHasDeferred(depNode);
node = nullthrows(graph.getNode(assetNode.id));
invariant(node.type === 'asset');
assert(!node.hasDeferred);
node = nullthrows(graph.getNode(assetGroupNode.id));
invariant(node.type === 'asset_group');
assert(!node.hasDeferred);
});
});
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