@lerna/publish
Version:
Publish packages in the current project
293 lines (292 loc) • 11.6 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var package_graph_exports = {};
__export(package_graph_exports, {
PackageGraph: () => PackageGraph
});
module.exports = __toCommonJS(package_graph_exports);
var import_npm_package_arg = __toESM(require("npm-package-arg"));
var import_validation_error = require("../validation-error");
var import_cyclic_package_graph_node = require("./cyclic-package-graph-node");
var import_package_graph_node = require("./package-graph-node");
var import_report_cycles = require("./report-cycles");
class PackageGraph extends Map {
/**
* @param {import("@lerna/package").Package[]} packages An array of Packages to build the graph out of.
* @param {'allDependencies'|'dependencies'} [graphType]
* Pass "dependencies" to create a graph of only dependencies,
* excluding the devDependencies that would normally be included.
* @param {boolean} [forceLocal] Force all local dependencies to be linked.
*/
constructor(packages, graphType = "allDependencies", forceLocal) {
super(packages.map((pkg) => [pkg.name, new import_package_graph_node.PackageGraphNode(pkg)]));
if (packages.length !== this.size) {
const seen = /* @__PURE__ */ new Map();
for (const { name, location } of packages) {
if (seen.has(name)) {
seen.get(name).push(location);
} else {
seen.set(name, [location]);
}
}
for (const [name, locations] of seen) {
if (locations.length > 1) {
throw new import_validation_error.ValidationError(
"ENAME",
[`Package name "${name}" used in multiple packages:`, ...locations].join("\n ")
);
}
}
}
this.forEach((currentNode, currentName) => {
const graphDependencies = graphType === "dependencies" ? Object.assign({}, currentNode.pkg.optionalDependencies, currentNode.pkg.dependencies) : Object.assign(
{},
currentNode.pkg.devDependencies,
currentNode.pkg.optionalDependencies,
currentNode.pkg.dependencies
);
Object.keys(graphDependencies).forEach((depName) => {
const depNode = this.get(depName);
let spec = graphDependencies[depName].replace(/^link:/, "file:");
const isWorkspaceSpec = /^workspace:/.test(spec);
let fullWorkspaceSpec;
let workspaceAlias;
if (isWorkspaceSpec) {
fullWorkspaceSpec = spec;
spec = spec.replace(/^workspace:/, "");
if (spec === "*" || spec === "^" || spec === "~") {
workspaceAlias = spec;
if (depNode?.version) {
const prefix = spec === "*" ? "" : spec;
const version = depNode.version;
spec = `${prefix}${version}`;
} else {
spec = "*";
}
}
}
const resolved = import_npm_package_arg.default.resolve(depName, spec, currentNode.location);
resolved.workspaceSpec = fullWorkspaceSpec;
resolved.workspaceAlias = workspaceAlias;
if (!depNode) {
return currentNode.externalDependencies.set(depName, resolved);
}
if (forceLocal || resolved.fetchSpec === depNode.location || depNode.satisfies(resolved)) {
currentNode.localDependencies.set(depName, resolved);
depNode.localDependents.set(currentName, currentNode);
} else {
if (isWorkspaceSpec) {
throw new import_validation_error.ValidationError(
"EWORKSPACE",
`Package specification "${depName}@${spec}" could not be resolved within the workspace. To reference a non-matching, remote version of a local dependency, remove the 'workspace:' prefix.`
);
}
currentNode.externalDependencies.set(depName, resolved);
}
});
});
}
get rawPackageList() {
return Array.from(this.values()).map((node) => node.pkg);
}
/**
* Takes a list of Packages and returns a list of those same Packages with any Packages
* they depend on. i.e if packageA depended on packageB `graph.addDependencies([packageA])`
* would return [packageA, packageB].
*
* @param filteredPackages The packages to include dependencies for.
*/
addDependencies(filteredPackages) {
return this.extendList(filteredPackages, "localDependencies");
}
/**
* Takes a list of Packages and returns a list of those same Packages with any Packages
* that depend on them. i.e if packageC depended on packageD `graph.addDependents([packageD])`
* would return [packageD, packageC].
*
* @param filteredPackages The packages to include dependents for.
*/
addDependents(filteredPackages) {
return this.extendList(filteredPackages, "localDependents");
}
/**
* Extends a list of packages by traversing on a given property, which must refer to a
* `PackageGraphNode` property that is a collection of `PackageGraphNode`s.
* Returns input packages with any additional packages found by traversing `nodeProp`.
*
* @param packageList The list of packages to extend
* @param nodeProp The property on `PackageGraphNode` used to traverse
*/
extendList(packageList, nodeProp) {
const search = new Set(packageList.map(({ name }) => this.get(name)));
const result = [];
search.forEach((currentNode) => {
result.push(currentNode);
currentNode[nodeProp].forEach((meta, depName) => {
const depNode = this.get(depName);
if (depNode !== currentNode && !search.has(depNode)) {
search.add(depNode);
}
});
});
return result.map((node) => node.pkg);
}
/**
* Return a tuple of cycle paths and nodes.
*
* @deprecated Use collapseCycles instead.
*
* @param rejectCycles Whether or not to reject cycles
*/
partitionCycles(rejectCycles) {
const cyclePaths = /* @__PURE__ */ new Set();
const cycleNodes = /* @__PURE__ */ new Set();
this.forEach((currentNode, currentName) => {
const seen = /* @__PURE__ */ new Set();
const visits = (walk) => (dependentNode, dependentName, siblingDependents) => {
const step = walk.concat(dependentName);
if (seen.has(dependentNode)) {
return;
}
seen.add(dependentNode);
if (dependentNode === currentNode) {
cycleNodes.add(currentNode);
cyclePaths.add(step);
return;
}
if (siblingDependents.has(currentName)) {
const cycleDependentName = Array.from(dependentNode.localDependencies.keys()).find(
(key) => (
// TODO: refactor to address type issues
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
currentNode.localDependents.has(key)
)
);
const pathToCycle = step.slice().reverse().concat(cycleDependentName);
cycleNodes.add(dependentNode);
cyclePaths.add(pathToCycle);
}
dependentNode.localDependents.forEach(visits(step));
};
currentNode.localDependents.forEach(visits([currentName]));
});
(0, import_report_cycles.reportCycles)(
// TODO: refactor to address type issues
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
Array.from(cyclePaths, (cycle) => cycle.join(" -> ")),
rejectCycles
);
return [cyclePaths, cycleNodes];
}
/**
* Returns the cycles of this graph. If two cycles share some elements, they will
* be returned as a single cycle.
*
* @param {boolean} rejectCycles Whether or not to reject cycles
* @returns {Set<CyclicPackageGraphNode>}
*/
collapseCycles(rejectCycles) {
const cyclePaths = [];
const nodeToCycle = /* @__PURE__ */ new Map();
const cycles = /* @__PURE__ */ new Set();
const walkStack = [];
const alreadyVisited = /* @__PURE__ */ new Set();
function visits(baseNode, dependentNode) {
if (nodeToCycle.has(baseNode)) {
return;
}
let topLevelDependent = dependentNode;
while (nodeToCycle.has(topLevelDependent)) {
topLevelDependent = nodeToCycle.get(topLevelDependent);
}
const identifier = `${baseNode.name}:${topLevelDependent.name}`;
if (alreadyVisited.has(identifier)) {
return;
}
alreadyVisited.add(identifier);
if (topLevelDependent === baseNode || topLevelDependent.isCycle && topLevelDependent.has(baseNode.name)) {
const cycle = new import_cyclic_package_graph_node.CyclicPackageGraphNode();
walkStack.forEach((nodeInCycle) => {
nodeToCycle.set(nodeInCycle, cycle);
cycle.insert(nodeInCycle);
cycles.delete(nodeInCycle);
});
cycles.add(cycle);
cyclePaths.push(cycle.toString());
return;
}
if (walkStack.indexOf(topLevelDependent) === -1) {
visitWithStack(baseNode, topLevelDependent);
}
}
function visitWithStack(baseNode, currentNode = baseNode) {
walkStack.push(currentNode);
currentNode.localDependents.forEach(visits.bind(null, baseNode));
walkStack.pop();
}
this.forEach((currentNode) => visitWithStack(currentNode));
cycles.forEach((collapsedNode) => visitWithStack(collapsedNode));
(0, import_report_cycles.reportCycles)(cyclePaths, rejectCycles);
return cycles;
}
/**
* Remove cycle nodes.
*
* @deprecated Spread set into prune() instead.
*/
pruneCycleNodes(cycleNodes) {
return this.prune(...cycleNodes);
}
/**
* Remove all candidate nodes.
*/
prune(...candidates) {
if (candidates.length === this.size) {
return this.clear();
}
candidates.forEach((node) => this.remove(node));
}
/**
* Delete by value (instead of key), as well as removing pointers
* to itself in the other node's internal collections.
* @param candidateNode instance to remove
*/
remove(candidateNode) {
this.delete(candidateNode.name);
this.forEach((node) => {
node.localDependencies.delete(candidateNode.name);
node.localDependents.delete(candidateNode.name);
});
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
PackageGraph
});