UNPKG

@backstage/cli

Version:

CLI for developing Backstage plugins and apps

251 lines (244 loc) • 8.69 kB
'use strict'; 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