cubx-dependency-resolver
Version:
Npm package to resolve the dependencies of a list of artifacts
864 lines (797 loc) • 30 kB
JavaScript
(function () {
'use strict';
/**
* Represents a tree of Dependencies. This is mainly used by the DependencyMgr to handle dependencies and their
* relation
*
* @global
* @class DependencyTree
* @constructor
*/
var DependencyTree = function () {
/**
* Holding all conflicts of type DependencyTree.conflictTypes.NAME
* @type {Array}
* @private
*/
this._nameConflicts = [];
/**
* Holding all conflicts of type DependencyTree.conflictTypes.VERSION
* @type {Array}
* @private
*/
this._versionConflicts = [];
/**
* Holding all conflicts
* @type {Array}
* @private
*/
this._conflicts = [];
/**
* The nodes Array holds all rootNodes of the dependencyTree. The order of root nodes is important when it comes
* to dependency conflict resolution.
* Note: In fact their could be multiple rootNodes. So indeed the dependencyTree is not a "real" tree
* @type {Array}
*/
this._rootNodes = [];
/**
* If true the removeDuplicate() method will take care to handle artifact version conflicts and resolves them.
* @type {boolean}
* @private
*/
this._enableACR = false;
};
/**
* Enum for possible conflictTypes
* @type {{NAME: string, VERSION: string, MIXED: string}}
*/
DependencyTree.conflictTypes = {
NAME: 'NAME_CONFLICT',
VERSION: 'VERSION_CONFLICT',
MIXED: 'MIXED_CONFLICT'
};
/**
* Enum for possible conflictTypes
* @type {{NAME: string, VERSION: string, MIXED: string}}
*/
DependencyTree.conflictTypes = {
NAME: 'NAME_CONFLICT',
VERSION: 'VERSION_CONFLICT',
MIXED: 'MIXED_CONFLICT'
};
/**
* Enum for characterizing different relationships between nodes
* @type {{ARTIFACT_VERSION_CONFLICT: number, ARTIFACT_NAME_CONFLICT: number, ARTIFACT_CUPLICATE: number}}
*/
DependencyTree.NodeRelationship = {
'DISTINCT_ARTIFACT': 0,
'ARTIFACT_VERSION_CONFLICT': 1,
'ARTIFACT_NAME_CONFLICT': 2,
'ARTIFACT_DUPLICATE': 3
};
/**
* Mark all descendants of given node as excluded.
* @param {object} node A DependencyTree.Node whose descendants should be marked excluded
* @returns {object} the DependencyTree itself
* @memberOf DependencyTree
* @private
*/
DependencyTree.prototype._markDescendantsAsExcluded = function (node) {
this.traverseSubtreeBF(node, function (currentNode) {
currentNode.excluded = true;
});
return this;
};
/**
* Mark all excludes of given node in it's descendents.
* @private
* @memberOf DependencyTree
* @return {object} DependencyTree
*/
DependencyTree.prototype._markExcludedNodesInSubtree = function (node) {
var excludes = node.data.dependencyExcludes;
// mark all artifacts that are excluded explicitly
this.traverseSubtreeBF(node, function (currentNode) {
excludes.forEach(function (exclude) {
if (exclude.webpackageId === currentNode.data.webpackageId &&
exclude.artifactId === currentNode.data.artifactId) {
currentNode.excluded = true;
this._markDescendantsAsExcluded(currentNode);
}
}.bind(this));
}.bind(this));
return this;
};
/**
* Removes a duplicate from the DependencyTree. When removing duplicates the excludes of duplicated Node and duplicate Node
* are merged. Only nodes, which are excluded in both nodes or their descendants. It also will be checked if duplicated
* and duplicate node are excluded or not.
* @memberOf DependencyTree
* @param {object} duplicated DependencyTree.Node that is duplicated
* @param {object} duplicate DependencyTree.Node that duplicates the duplicated node
* @private
*/
DependencyTree.prototype._removeDuplicate = function (duplicated, duplicate) {
var descendantsOfDuplicated = [];
var descendantsOfDuplicate = [];
// If duplicated node is excluded but duplicate node is not excluded we need to make exclude of duplicated node invalid
if (duplicated.excluded && !duplicate.excluded) duplicated.excluded = false;
// collect all nodes in subtree of duplicated node
this.traverseSubtreeBF(duplicated, function (node) {
descendantsOfDuplicated.push(node);
});
// collect all nodes in subtree of duplicate node
this.traverseSubtreeBF(duplicate, function (node) {
descendantsOfDuplicate.push(node);
});
// calculate the intersection of all excluded nodes. Only nodes that are excluded in both subtrees are removed
descendantsOfDuplicated.forEach(function (node) {
// collect all nodes in subtree of duplicate node
descendantsOfDuplicate.forEach(function (descOfDuplicate) {
if (node.equalsArtifact(descOfDuplicate)) {
// the exclude on duplicate node is invalid because the same node is not marked as excluded in duplicated subtree
if (!node.excluded && descOfDuplicate.excluded) {
descOfDuplicate.excluded = false;
}
// the exclude on duplicated node is invalid because the same node is not marked as excluded in duplicate subtree
if (node.excluded && !descOfDuplicate.excluded) {
node.excluded = false;
}
}
});
});
// if duplicate is not a root node we need to set usesExisting and usedBy of the duplicates parent
if (duplicate.parent != null) {
duplicate.parent.usesExisting.push(duplicated);
duplicated.usedBy.push(duplicate.parent);
}
duplicated.data.referrer = duplicated.data.referrer.concat(duplicate.data.referrer);
this.removeNode(duplicate);
};
/**
* Remove node which is in conflict with another node from DependencyTree.
* @memberOf DependencyTree
* @param {object} node Node which causes the conflict
* @param {object} node Node which is on conflict with give node and thus needs to be removed
*/
DependencyTree.prototype._removeConflictedNode = function (node, conflictedNode) {
// if conflictedNode is not a root node we need to set usesExisting and usedBy of node which will replace the conflictedNode
if (conflictedNode.parent != null) {
conflictedNode.parent.usesExisting.push(node);
node.usedBy.push(conflictedNode.parent);
}
node.data.referrer = node.data.referrer.concat(conflictedNode.data.referrer);
this.removeNode(conflictedNode);
};
/**
* Group given list of nodes by their webpackage name.
* @memberOf DependencyTree
* @param {array} nodes
* @return {object} nodes grouped by webpackage name as key
* @private
*/
DependencyTree.prototype._groupNodesByWebpackageName = function (nodes) {
var webpackageMapByName = {};
nodes.forEach(function (node) {
var webpackageName = node.data.webpackageId.split('@')[0];
if (webpackageMapByName.hasOwnProperty(webpackageName)) {
webpackageMapByName[webpackageName].push(node);
} else {
webpackageMapByName[webpackageName] = [node];
}
});
return webpackageMapByName;
};
/**
* Determine the relationship between two given nodes. For possible return values see DependencyTree.NodeRelationship enum.
* @param {object} nodeA
* @param {object} nodeB
* @memberOf DependencyTree
* @return {number} Relationship Type
* @private
*/
DependencyTree.prototype._determineNodeRelationship = function (nodeA, nodeB) {
var type = DependencyTree.NodeRelationship.DISTINCT_ARTIFACT;
if (nodeA.equalsArtifact(nodeB)) {
type = DependencyTree.NodeRelationship.ARTIFACT_DUPLICATE;
} else if (nodeA.equalsArtifactIgnoreVersion(nodeB)) {
type = DependencyTree.NodeRelationship.ARTIFACT_VERSION_CONFLICT;
} else if (nodeA.data.artifactId === nodeB.data.artifactId) {
type = DependencyTree.NodeRelationship.ARTIFACT_NAME_CONFLICT;
}
return type;
};
/**
* Get related node(s) to given node out out of given nodes array depending on the given type of relationship.
* @memberOf DependencyTree
* @param {object} node The given node to compare to
* @param {array} nodes A nodes array to compare against
* @param {number} relationship One of DependencyTree.NodeRelationship
* @returns {array}
* @private
*/
DependencyTree.prototype._getRelatedNodes = function (node, nodes, relationship) {
var relatedNodes;
relatedNodes = nodes.filter(function (currentNode) {
return (this._determineNodeRelationship(currentNode, node) === relationship);
}.bind(this));
return relatedNodes;
};
/**
* Helper method to create an item representing a conflict between two nodes
* @memberOf DependencyTree
* @private
* @param {DependencyTree.Node} node
* @param {DependencyTree.Node} conflictedNode
* @param {string} type One of DependencyTree.conflictTypes
* @param {boolean} resolved
* @returns {object}
*/
DependencyTree.prototype._createConflictItem = function (node, conflictedNode, type, resolved) {
return {
node: node,
conflictedNode: conflictedNode,
type: type,
resolved: resolved
};
};
/**
* Disable automatic conflict resolution.
* @memberOf DependencyTree
*/
DependencyTree.prototype.disableACR = function () {
this._enableACR = false;
};
/**
* Enable automatic conflict resolution. Note: This only applies to artifact version conflicts!
* @memberOf DependencyTree
*/
DependencyTree.prototype.enableACR = function () {
this._enableACR = true;
};
/**
* Get a list of all conflicted nodes including type of conflict. Note: this method only works
* when ACR is enabled and removeDuplicates() method was already called.
* @memberOf DependencyTree
* @returns {array}
*/
DependencyTree.prototype.getConflictedNodes = function () {
return this._conflicts;
};
/**
* Apply all excludes in DependencyTree. Note: this needs to be done before removeDuplicates() is called!
* @memberOf DependencyTree
* @returns {object} DependencyTree itself
*/
DependencyTree.prototype.applyExcludes = function () {
this.traverseBF(function (node) {
if (node.data.dependencyExcludes.length > 0) {
this._markExcludedNodesInSubtree(node);
}
}.bind(this));
return this;
};
/**
* Mark all appearances of given artifact in DependencyTree as excluded. This should be done before removeDuplicates()
* is called.
* @memberOf DependencyTree
* @param {string} webpackageId
* @param {string} artifactId
* @returns {object} DependencyTree itself
*/
DependencyTree.prototype.applyGlobalExclude = function (webpackageId, artifactId) {
this.traverseBF(function (node) {
if (node.data.artifactId === artifactId && node.data.webpackageId === webpackageId) {
node.excluded = true;
this._markDescendantsAsExcluded(node);
}
}.bind(this));
return this;
};
/**
* Check if the given node is in DependencyTree.
* @memberOf DependencyTree
* @param {object} node
* @returns {boolean} True if Node is in DependencyTree, false otherwise.
*/
DependencyTree.prototype.contains = function (node) {
// if we can reach any of the current rootNodes by using the parent reference given node is a member of DependencyTree
while (node.parent != null) {
node = node.parent;
};
return this._rootNodes.some(function (rootNode) {
return rootNode.equals(node);
});
};
/**
* Insert a node into dependency tree. If no parent is given, then the node will be added to rootNodes.
* If before is given then the nodes will be inserted right before this node in parents children. Otherwise it will
* be appended to the array of child nodes.
* @memberOf DependencyTree
* @param {object} node The node to be inserted. Needs to be an instance of DependencyTree.Node
* @param [object] parent An instance of DependencyTree.Node representing the parent for the node to be inserted
* @param [object] before An instance of DependencyTree.Node representing the direct successor of the
* inserted node in the children array
* @returns {object} The created and inserted node. This is instance of DependencyTree.Node
*/
DependencyTree.prototype.insertNode = function (node, parent, before) {
if (!(node instanceof DependencyTree.Node)) {
console.error('parameter \'node\' needs to be an instance of DependencyTree.Node');
return;
}
if (parent && !(parent instanceof DependencyTree.Node)) {
console.error('parameter \'parent\' needs to be an instance of DependencyTree.Node');
return;
}
if (before && !(before instanceof DependencyTree.Node)) {
console.error('parameter \'before\' needs to be an instance of DependencyTree.Node');
return;
}
var beforeIndex;
// if parent and before are given, insert node as new child of given parent as left neighbour of before in parents
if (parent && before) {
// find index of before node in parents children array
beforeIndex = -1;
parent.children.some(function (child, index) {
if (before.equals(child)) {
beforeIndex = index;
return true;
}
});
node.parent = parent;
if (beforeIndex < 0) {
console.warn('Could not find given before node in parents child nodes array. Append given node to end of child nodes array.');
parent.children.push(node);
} else {
parent.children.splice(beforeIndex, 0, node);
}
} else if (parent && !before) { // if only parent is given, append given node to the end of parents children array
node.parent = parent;
parent.children.push(node);
} else if (!parent && before) { // if only before is given insert node in rootNodes array as left neighbour of before node
beforeIndex = -1;
this._rootNodes.some(function (child, index) {
if (before.equals(child)) {
beforeIndex = index;
return true;
}
});
node.parent = null;
if (beforeIndex < 0) {
console.warn('Could not find given before node in rootNodes array. Append given node to end of rootNodes array.');
this._rootNodes.push(node);
} else {
this._rootNodes.splice(beforeIndex, 0, node);
}
} else { // neither parent nor before are given. So just append node to the end of rootNodes array
node.parent = null;
this._rootNodes.push(node);
}
return node;
};
/**
* Remove all duplicate Dependencies from DependencyTree. Nodes are considered equal if the assigned webpackageId
* and artifactId is equal. If automatic conflict resolution is enabled (per default it is) also artifact version
* conflicts will be resolved.
* Note: applyExcludes() needs to be called before removeDuplicates() is called!
* @memberOf DependencyTree
* @returns {object} The DependencyTree without duplicates
*/
DependencyTree.prototype.removeDuplicates = function () {
var nodesBF = []; // holds an array of all processed/visited nodes
this.traverseBF(function (node) {
// If current node is already removed from depTree skip iteration --> if this.contains(node) is false we don't do anything
if (this.contains(node)) {
var relatedNodes = {
duplicates: this._getRelatedNodes(node, nodesBF, DependencyTree.NodeRelationship.ARTIFACT_DUPLICATE),
versionConflicts: this._getRelatedNodes(node, nodesBF, DependencyTree.NodeRelationship.ARTIFACT_VERSION_CONFLICT),
nameConflicts: this._getRelatedNodes(node, nodesBF, DependencyTree.NodeRelationship.ARTIFACT_NAME_CONFLICT),
distinct: this._getRelatedNodes(node, nodesBF, DependencyTree.NodeRelationship.DISTINCT_ARTIFACT)
};
if (relatedNodes.duplicates.length === 1) {
this._removeDuplicate(relatedNodes.duplicates[0], node);
} else if (relatedNodes.versionConflicts.length === 1) {
var conflict = this._createConflictItem(relatedNodes.versionConflicts[0], node, DependencyTree.conflictTypes.VERSION, false);
if (this._enableACR) {
// remove conflicted node if ACR is enabled
this._removeConflictedNode(relatedNodes.versionConflicts[0], node);
conflict.resolved = true;
}
// push conflict to internal conflicts array for logging purposes
this._conflicts.push(conflict);
} else if (relatedNodes.distinct.length + relatedNodes.nameConflicts.length === nodesBF.length) {
nodesBF.push(node);
}
// check for naming conflicts. If there are any just keep them on internal conflicts array for logging purposes
if (relatedNodes.nameConflicts.length > 0) {
relatedNodes.nameConflicts.some(function (conflictNode) {
this._conflicts.push(this._createConflictItem(conflictNode, node, DependencyTree.conflictTypes.NAME, false));
}.bind(this));
}
}
}.bind(this));
return this;
};
/**
* Removes all nodes which are marked as excluded. Note: if a node is marked as excluded then it will be removed including all it's descandants!
* This should be called after excludes are applied (local and global ones) and duplicates are removed.
*
* @memberOf DependencyTree
* @returns {object} DependencyTree itself
*/
DependencyTree.prototype.removeExcludes = function () {
this.traverseBF(function (node) {
if (node.excluded) this.removeNode(node);
}.bind(this));
return this;
};
/**
* Remove a given node (including all of its descendants).
* @memberOf DependencyTree
* @param {object} node The node to be removed
* @returns {object} The removed node, null if the given node was not found in tree
*/
DependencyTree.prototype.removeNode = function (node) {
if (!(node instanceof DependencyTree.Node)) {
console.error('parameter \'node\' needs to be an instance of DependencyTree.Node');
return;
}
// holds children array from which the given node needs to be removed. If given node is rootNode
// children will be the rootNodes array
var children;
this.traverseBF(function (current) {
if (node.equals(current)) {
children = current.parent ? current.parent.children : this._rootNodes;
return false;
}
}.bind(this));
// find and remove node from children array
if (children) {
var index;
children.some(function (child, childIndex) {
if (node.equals(child)) {
index = childIndex;
return true;
}
});
if (index >= 0) {
node.parent = null;
children.splice(index, 1);
// remove node also in usesExisting array of all nodes referenced in node's usedBy array
node.usedBy.forEach(function (current) {
current.usesExisting = current.usesExisting.filter(function (element) {
return !node.equals(element);
});
});
return node;
} else {
return null;
}
} else {
return null;
}
};
/**
* Travers the DependencyTree in Depth First pre-order. The given callback will be called on each visited node given
* the node itself as the first parameter. If the callback returns false the traversal will be canceled.
* @memberOf DependencyTree
* @param {function} callback
*/
DependencyTree.prototype.traverseDF = function (callback) {
if (!callback || typeof callback !== 'function') {
console.error('Parameter \'callback\' needs to be of type function');
return;
};
this._rootNodes.some(function (node) {
(function processNode (node) {
if (callback(node) === false) {
return true;
}
node.children.some(function (child) {
return processNode(child);
});
})(node);
});
};
/**
* Travers the DependencyTree in Breadth First order. The given callback will be called on each visited node given
* the node itself as the first parameter. If the callback returns false the traversal will be canceled.
* @memberOf DependencyTree
* @param {function} callback
*/
DependencyTree.prototype.traverseBF = function (callback) {
if (!callback || typeof callback !== 'function') {
console.error('Parameter \'callback\' needs to be of type function');
return;
};
var queue = this._rootNodes.length > 0 ? this._rootNodes.slice() : [];
while (queue.length > 0) {
var node = queue.shift();
if (node.children.length > 0) {
node.children.forEach(function (child) {
queue.push(child);
});
}
if (callback(node) === false) {
return;
};
}
};
/**
* Traverse a subtree within the DependencyTree starting from the given node.
* @memberOf DependencyTree
* @param {object} rootNode A DependencyTree.Node within the DependendyTree acting as root for the subtree traversal
* @param {function} callback Callback function that will be called for each visited node
*/
DependencyTree.prototype.traverseSubtreeBF = function (rootNode, callback) {
if (!(rootNode instanceof DependencyTree.Node)) {
console.error('Parameter \'rootNode\' needs to be an instance of DependencyTree.Node');
return;
};
if (!callback || typeof callback !== 'function') {
console.error('Parameter \'callback\' needs to be of type function');
return;
};
var queue = [rootNode];
while (queue.length > 0) {
var node = queue.shift();
if (node.children.length > 0) {
node.children.forEach(function (child) {
queue.push(child);
});
}
if (callback(node) === false) {
return;
};
}
};
/**
* Returns a JSON object describing the dependency tree using following structure:
* {
* "rootNodes": [
* {
* "webpackageId": ...,
* "artifactId": ...,
* "resources": [...], //optional
* "children": [
* {
* "webpackageId": ...,
* "artifactId": ...,
* "resources": [...], //optional
* "children": [...]
* },
* ...
// * ],
* usesExisting: [...],
* usedBy: [...]
* },
* ...
* }
* }
* @memberOf DependencyTree
* @param {boolean} [includeResources=false] Indicates whether to include resources property.
* @returns {{}} JSON object describing the depTree
*/
DependencyTree.prototype.toJSON = function (includeResources) {
var rootNodes = [];
this._rootNodes.forEach(function (rootNode) {
rootNodes.push(rootNode.toJSON(includeResources));
});
return {
rootNodes: rootNodes
};
};
DependencyTree.prototype.clone = function () {
var hash = new WeakMap();
function clone (obj) {
if (!obj || typeof obj !== 'object') {
return obj;
}
if (hash.has(obj)) {
return hash.get(obj);
}
try {
var result = Array.isArray(obj) ? []
: obj.constructor ? new obj.constructor() : {};
} catch (e) { // The constructor failed, create without running it
result = Object.create(Object.getPrototypeOf(obj));
}
hash.set(obj, result);
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = clone(obj[key]);
}
}
return result;
}
return clone(this);
};
/**
* Internal Class representing a node of a DependencyTree
* @constructor
* @memberOf DependencyTree
* @static
*/
DependencyTree.Node = function () {
/**
* Holds a list of (ordered) children of the node.
* @type {Array}
*/
this.children = [];
/**
* Stores the data of the node. Normally this will be an instance of DependencyMgr.DepReference
* @type {object|misc}
*/
this.data = null;
/**
* True if node is excluded by any of it's ancestors
* @type {boolean}
*/
this.excluded = false;
/**
* References the parent node of this node.
* @type {object}
*/
this.parent = null;
/**
* Holds a list of all Nodes that references this node in their usesExisting property
* @type {Array}
*/
this.usedBy = [];
/**
* References other existing nodes from the DependencyTree. This is used to resolve redundant Dependency Nodes
* inside the DependencyTree.
* @type {object}
*/
this.usesExisting = [];
};
/**
* Check if given node equals this node.
* @memberOf DependencyTree.Node
* @param {object} node The node to compare to
* @returns {boolean} true if given node is equal to this node, otherwise else is returned
*/
DependencyTree.Node.prototype.equals = function (node) {
// for now only check if given node and this node represent the same instances
return this === node;
};
/**
* Checks if the given node references the same artifact.
* @memberOf DependencyTree.Node
* @param {object} node DependencyTree.Node to compare to
* @returns {boolean} true if artifactId and webpackageId are equal, false otherwise
*/
DependencyTree.Node.prototype.equalsArtifact = function (node) {
return node.data.getId() === this.data.getId();
};
/**
* Check if the given node references the same artifact but ignore the version number. This can be used to determine
* nodes with artifact version conflicts.
* @memberOf DependencyTree.Node
* @param {object} node DependencyTree.Node to compare to
* @return {boolean} true if artifactId and webpackageName and (optional) groupId are equal. False otherwise
*/
DependencyTree.Node.prototype.equalsArtifactIgnoreVersion = function (node) {
return (node.data.webpackageId.split('@')[0] + '/' + node.data.artifactId) === (this.data.webpackageId.split('@')[0] + '/' + this.data.artifactId);
};
/**
* Return the path starting from root for this node as string.
* @memberOf DependencyTree.Node
* @return {string}
*/
DependencyTree.Node.prototype.getPathAsString = function () {
var current = this;
var path = [current.data.getId()];
while (current.parent) {
current = current.parent;
path.splice(0, 0, current.data.getId());
}
return path.join(' > ');
};
/**
* Check if the given node is an ancestor of this node.
* Note: Edges defined by usesExisting arrays are also take into consideration!
* @memberOf DependencyTree.Node
* @param {object} node A DependencyTree.Node instance within te same tree
* @returns {boolean} true if given node is an ancestor of this node. False otherwise
*/
DependencyTree.Node.prototype.isAncestorOf = function (node) {
var isAncestor = false;
var children = this.children.concat(this.usesExisting);
while (children.length > 0) {
var current = children.shift();
if (current.equals(node)) {
isAncestor = true;
break;
} else {
current.children.some(function (child) {
children.push(child);
});
current.usesExisting.some(function (child) {
children.push(child);
});
}
}
return isAncestor;
};
/**
* Returns true if the node is a descendant of the given node. This also includes usedBy relations!
* @memberOf DependencyTree.Node
* @param {object} node A DependencyTree.Node
* @returns {boolean} true if the node is a descendent of the given node, false otherwise.
*/
DependencyTree.Node.prototype.isDescendantOf = function (node) {
var parents = this.parent ? this.usedBy.concat(this.parent) : this.usedBy;
while (parents.length > 0) {
var current = parents.shift();
if (current.equals(node)) return true;
current.usedBy.forEach(function (item) {
parents.push(item);
});
if (current.parent != null) parents.push(current.parent);
}
return false;
};
/**
* Returns a JSON object describing the node using the following structure:
* {
* "webpackageId": ...,
* "artifactId": ...,
* "resources": [...], //optional
* "children": [
* {
* "webpackageId": ...,
* "artifactId": ...,
* "resources": [...], //optional
* "children": [...],
* usesExisting: [...],
* usedBy: [...]
* },
* ...
* ],
* usesExisting: [...],
* usedBy: [...]
* }
* @memberOf DependencyTree
* @param {boolean} [includeResources=false] Indicates whether to include resources property.
* @returns {{}} JSON object describing the depTree
*/
DependencyTree.Node.prototype.toJSON = function (includeResources) {
var jsonObject = {};
var children = [];
var usedBy = [];
var usesExisting = [];
if (this.children.length > 0) {
this.children.forEach(function (child) {
children.push(child.toJSON(includeResources));
});
}
if (this.usedBy.length > 0) {
this.usedBy.forEach(function (child) {
usedBy.push({
webpackageId: child.data.webpackageId,
artifactId: child.data.artifactId
});
});
}
if (this.usesExisting.length > 0) {
this.usesExisting.forEach(function (child) {
usesExisting.push({
webpackageId: child.data.webpackageId,
artifactId: child.data.artifactId
});
});
}
jsonObject.webpackageId = this.data.webpackageId;
jsonObject.artifactId = this.data.artifactId;
jsonObject.children = children;
jsonObject.usesExisting = usesExisting;
jsonObject.usedBy = usedBy;
jsonObject.excluded = this.excluded;
if (includeResources) {
jsonObject.resources = this.data.resources;
}
return jsonObject;
};
module.exports = DependencyTree;
}());