@backstage/cli
Version:
CLI for developing Backstage plugins and apps
251 lines (244 loc) • 8.69 kB
JavaScript
var path = require('path');
var getPackages = require('@manypkg/get-packages');
var index = require('./index-d2845aa8.cjs.js');
var errors = require('@backstage/errors');
var child_process = require('child_process');
var util = require('util');
var Lockfile = require('./Lockfile-e5943b84.cjs.js');
require('minimatch');
require('chalk');
require('./yarn-8315d2ff.cjs.js');
require('./run-eac5f3ab.cjs.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
const execFile = util.promisify(child_process.execFile);
async function runGit(...args) {
var _a, _b;
try {
const { stdout } = await execFile("git", args, {
shell: true,
cwd: index.paths.targetRoot
});
return stdout.trim().split(/\r\n|\r|\n/);
} catch (error) {
errors.assertError(error);
if (error.stderr || typeof error.code === "number") {
const stderr = (_a = error.stderr) == null ? void 0 : _a.toString("utf8");
const msg = (_b = stderr == null ? void 0 : stderr.trim()) != null ? _b : `with exit code ${error.code}`;
throw new Error(`git ${args[0]} failed, ${msg}`);
}
throw new errors.ForwardedError("Unknown execution error", error);
}
}
async function listChangedFiles(ref) {
if (!ref) {
throw new Error("ref is required");
}
let diffRef = ref;
try {
const [base] = await runGit("merge-base", "HEAD", ref);
diffRef = base;
} catch {
}
const tracked = await runGit("diff", "--name-only", diffRef);
const untracked = await runGit("ls-files", "--others", "--exclude-standard");
return Array.from(/* @__PURE__ */ new Set([...tracked, ...untracked]));
}
async function readFileAtRef(path, ref) {
let showRef = ref;
try {
const [base] = await runGit("merge-base", "HEAD", ref);
showRef = base;
} catch {
}
const { stdout } = await execFile("git", ["show", `${showRef}:${path}`], {
shell: true,
cwd: index.paths.targetRoot,
maxBuffer: 1024 * 1024 * 50
});
return stdout;
}
class PackageGraph extends Map {
static async listTargetPackages() {
const { packages } = await getPackages.getPackages(index.paths.targetDir);
return packages;
}
static fromPackages(packages) {
const graph = new PackageGraph();
for (const pkg of packages) {
const name = pkg.packageJson.name;
const existingPkg = graph.get(name);
if (existingPkg) {
throw new Error(
`Duplicate package name '${name}' at ${pkg.dir} and ${existingPkg.dir}`
);
}
graph.set(name, {
name,
dir: pkg.dir,
packageJson: pkg.packageJson,
allLocalDependencies: /* @__PURE__ */ new Map(),
publishedLocalDependencies: /* @__PURE__ */ new Map(),
localDependencies: /* @__PURE__ */ new Map(),
localDevDependencies: /* @__PURE__ */ new Map(),
localOptionalDependencies: /* @__PURE__ */ new Map(),
allLocalDependents: /* @__PURE__ */ new Map(),
publishedLocalDependents: /* @__PURE__ */ new Map(),
localDependents: /* @__PURE__ */ new Map(),
localDevDependents: /* @__PURE__ */ new Map(),
localOptionalDependents: /* @__PURE__ */ new Map()
});
}
for (const node of graph.values()) {
for (const depName of Object.keys(node.packageJson.dependencies || {})) {
const depPkg = graph.get(depName);
if (depPkg) {
node.allLocalDependencies.set(depName, depPkg);
node.publishedLocalDependencies.set(depName, depPkg);
node.localDependencies.set(depName, depPkg);
depPkg.allLocalDependents.set(node.name, node);
depPkg.publishedLocalDependents.set(node.name, node);
depPkg.localDependents.set(node.name, node);
}
}
for (const depName of Object.keys(
node.packageJson.devDependencies || {}
)) {
const depPkg = graph.get(depName);
if (depPkg) {
node.allLocalDependencies.set(depName, depPkg);
node.localDevDependencies.set(depName, depPkg);
depPkg.allLocalDependents.set(node.name, node);
depPkg.localDevDependents.set(node.name, node);
}
}
for (const depName of Object.keys(
node.packageJson.optionalDependencies || {}
)) {
const depPkg = graph.get(depName);
if (depPkg) {
node.allLocalDependencies.set(depName, depPkg);
node.publishedLocalDependencies.set(depName, depPkg);
node.localOptionalDependencies.set(depName, depPkg);
depPkg.allLocalDependents.set(node.name, node);
depPkg.publishedLocalDependents.set(node.name, node);
depPkg.localOptionalDependents.set(node.name, node);
}
}
}
return graph;
}
/**
* Traverses the package graph and collects a set of package names.
*
* The traversal starts at the provided list names, and continues
* throughout all the names returned by the `collectFn`, which is
* called once for each seen package.
*/
collectPackageNames(startingPackageNames, collectFn) {
const targets = /* @__PURE__ */ new Set();
const searchNames = startingPackageNames.slice();
while (searchNames.length) {
const name = searchNames.pop();
if (targets.has(name)) {
continue;
}
const node = this.get(name);
if (!node) {
throw new Error(`Package '${name}' not found`);
}
targets.add(name);
const collected = collectFn(node);
if (collected) {
searchNames.push(...collected);
}
}
return targets;
}
async listChangedPackages(options) {
var _a, _b;
const changedFiles = await listChangedFiles(options.ref);
const dirMap = new Map(
Array.from(this.values()).map((pkg) => [
// relative from root, convert to posix, and add a / at the end
path__default["default"].relative(index.paths.targetRoot, pkg.dir).split(path__default["default"].sep).join(path__default["default"].posix.sep) + path__default["default"].posix.sep,
pkg
])
);
const packageDirs = Array.from(dirMap.keys());
const result = new Array();
let searchIndex = 0;
changedFiles.sort();
packageDirs.sort();
for (const packageDir of packageDirs) {
while (searchIndex < changedFiles.length && changedFiles[searchIndex] < packageDir) {
searchIndex += 1;
}
if ((_a = changedFiles[searchIndex]) == null ? void 0 : _a.startsWith(packageDir)) {
searchIndex += 1;
result.push(dirMap.get(packageDir));
while ((_b = changedFiles[searchIndex]) == null ? void 0 : _b.startsWith(packageDir)) {
searchIndex += 1;
}
}
}
if (changedFiles.includes("yarn.lock") && options.analyzeLockfile) {
let thisLockfile;
let otherLockfile;
try {
thisLockfile = await Lockfile.Lockfile.load(
index.paths.resolveTargetRoot("yarn.lock")
);
otherLockfile = Lockfile.Lockfile.parse(
await readFileAtRef("yarn.lock", options.ref)
);
} catch (error) {
console.warn(
`Failed to read lockfiles, assuming all packages have changed, ${error}`
);
return Array.from(this.values());
}
const diff = thisLockfile.diff(otherLockfile);
const graph = thisLockfile.createSimplifiedDependencyGraph();
{
const otherGraph = thisLockfile.createSimplifiedDependencyGraph();
for (const [name, dependencies] of otherGraph) {
const node = graph.get(name);
if (node) {
dependencies.forEach((d) => node.add(d));
} else {
graph.set(name, dependencies);
}
}
}
const changedPackages = new Set(
[...diff.added, ...diff.changed, ...diff.removed].map((e) => e.name)
);
let changed = false;
do {
changed = false;
for (const [name, dependencies] of graph) {
if (changedPackages.has(name)) {
continue;
}
for (const dep of dependencies) {
if (changedPackages.has(dep)) {
changed = true;
changedPackages.add(name);
break;
}
}
}
} while (changed);
for (const node of this.values()) {
if (changedPackages.has(node.name) && !result.includes(node)) {
result.push(node);
}
}
}
return result;
}
}
exports.PackageGraph = PackageGraph;
//# sourceMappingURL=PackageGraph-84e587f4.cjs.js.map
;