cli-node: refactor to move getDependencyHash to Lockfile
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -92,7 +92,7 @@ export function isMonoRepo(): Promise<boolean>;
|
||||
export class Lockfile {
|
||||
createSimplifiedDependencyGraph(): Map<string, Set<string>>;
|
||||
diff(otherLockfile: Lockfile): LockfileDiff;
|
||||
getVersions(name: string): string[];
|
||||
getDependencyTreeHash(startName: string): string;
|
||||
static load(path: string): Promise<Lockfile>;
|
||||
static parse(content: string): Lockfile;
|
||||
}
|
||||
@@ -117,7 +117,6 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
|
||||
collectFn: (pkg: PackageGraphNode) => Iterable<string> | undefined,
|
||||
): Set<string>;
|
||||
static fromPackages(packages: Package[]): PackageGraph;
|
||||
getDependencyHash(name: string): Promise<string>;
|
||||
listChangedPackages(options: {
|
||||
ref: string;
|
||||
analyzeLockfile?: boolean;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { parseSyml } from '@yarnpkg/parsers';
|
||||
import crypto from 'node:crypto';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
const ENTRY_PATTERN = /^((?:@[^/]+\/)?[^@/]+)@(.+)$/;
|
||||
@@ -133,14 +134,6 @@ export class Lockfile {
|
||||
private readonly data: LockfileData,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Returns all versions of a package in the lockfile.
|
||||
*/
|
||||
getVersions(name: string): string[] {
|
||||
const queries = this.packages.get(name);
|
||||
return queries ? queries.map(q => q.version) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a simplified dependency graph from the lockfile data, where each
|
||||
* key is a package, and the value is a set of all packages that it depends on
|
||||
@@ -223,4 +216,63 @@ export class Lockfile {
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a sha1 hex hash of the dependency graph for a package.
|
||||
*/
|
||||
getDependencyTreeHash(startName: string): string {
|
||||
if (!this.packages.has(startName)) {
|
||||
throw new Error(`Package '${startName}' not found in lockfile`);
|
||||
}
|
||||
|
||||
const hash = crypto.createHash('sha1');
|
||||
|
||||
const queue = [startName];
|
||||
const seen = new Set<string>();
|
||||
|
||||
while (queue.length > 0) {
|
||||
const name = queue.pop()!;
|
||||
|
||||
if (seen.has(name)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(name);
|
||||
|
||||
const entries = this.packages.get(name);
|
||||
if (!entries) {
|
||||
continue; // In case of missing optional peer dependencies
|
||||
}
|
||||
|
||||
hash.update(`pkg:${name}`);
|
||||
hash.update('\0');
|
||||
|
||||
// TODO(Rugvip): This uses the same simplified lookup as createSimplifiedDependencyGraph()
|
||||
// we could match version queries to make the resulting tree a bit smaller.
|
||||
const deps = new Array<string>();
|
||||
for (const entry of entries) {
|
||||
// We're not being particular about stable ordering here. If the lockfile ordering changes, so will likely hash.
|
||||
hash.update(entry.version);
|
||||
|
||||
const data = this.data[entry.dataKey];
|
||||
if (!data) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const checksum = data.checksum || data.integrity;
|
||||
if (checksum) {
|
||||
hash.update('#');
|
||||
hash.update(checksum);
|
||||
}
|
||||
|
||||
hash.update(' ');
|
||||
|
||||
deps.push(...Object.keys(data.dependencies ?? {}));
|
||||
deps.push(...Object.keys(data.peerDependencies ?? {}));
|
||||
}
|
||||
|
||||
queue.push(...new Set(deps));
|
||||
}
|
||||
|
||||
return hash.digest('hex');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import crypto from 'node:crypto';
|
||||
import { getPackages, Package } from '@manypkg/get-packages';
|
||||
import { paths } from '../paths';
|
||||
import { PackageRole } from '../roles';
|
||||
@@ -284,43 +283,6 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
|
||||
return targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a sha1 hex hash of the dependency graph for a package.
|
||||
*/
|
||||
async getDependencyHash(name: string): Promise<string> {
|
||||
const pkg = this.get(name);
|
||||
if (!pkg) {
|
||||
throw new Error(`Package '${name}' not found`);
|
||||
}
|
||||
|
||||
const lockfile = await this.#getLockfile();
|
||||
const depGraph = lockfile.createSimplifiedDependencyGraph();
|
||||
|
||||
const seen = new Set<string>();
|
||||
const queue = [name];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const deps = depGraph.get(queue.pop()!);
|
||||
if (deps) {
|
||||
for (const dep of deps) {
|
||||
if (!seen.has(dep)) {
|
||||
seen.add(dep);
|
||||
queue.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hash = crypto.createHash('sha1');
|
||||
for (const dep of Array.from(seen).sort()) {
|
||||
hash.update(dep);
|
||||
hash.update('\0');
|
||||
hash.update(lockfile.getVersions(dep).join(' '));
|
||||
hash.update('\0');
|
||||
}
|
||||
return hash.digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all packages that have changed since a given git ref.
|
||||
*
|
||||
@@ -380,7 +342,9 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
|
||||
let thisLockfile: Lockfile;
|
||||
let otherLockfile: Lockfile;
|
||||
try {
|
||||
thisLockfile = await this.#getLockfile();
|
||||
thisLockfile = await Lockfile.load(
|
||||
paths.resolveTargetRoot('yarn.lock'),
|
||||
);
|
||||
otherLockfile = Lockfile.parse(
|
||||
await GitUtils.readFileAtRef('yarn.lock', options.ref),
|
||||
);
|
||||
@@ -446,13 +410,4 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#lockfilePromise?: Promise<Lockfile>;
|
||||
#getLockfile(): Promise<Lockfile> {
|
||||
if (this.#lockfilePromise) {
|
||||
return this.#lockfilePromise;
|
||||
}
|
||||
this.#lockfilePromise = Lockfile.load(paths.resolveTargetRoot('yarn.lock'));
|
||||
return this.#lockfilePromise;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user