chore(core): 🔧 Refactor DependencyTransformer to optimize async dependency resolution and handle circular references

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Lilith 2026-01-29 08:33:43 -08:00
parent f002db4c8e
commit f55383aeab

View file

@ -26,11 +26,13 @@ export class DependencyTransformer {
return this.versionMapCache;
}
// Find workspace root (look for pnpm-workspace.yaml)
const workspaceRoot = await this.findWorkspaceRoot(rootPath);
// Find workspace root (pnpm-workspace.yaml or package.json with workspaces)
const { root: workspaceRoot, type: workspaceType } = await this.findWorkspaceRoot(rootPath);
// Parse pnpm-workspace.yaml to get package patterns
const patterns = await this.parseWorkspaceFile(workspaceRoot);
// Get package patterns based on workspace type
const patterns = workspaceType === 'pnpm'
? await this.parsePnpmWorkspaceFile(workspaceRoot)
: await this.parseNpmWorkspaceFile(workspaceRoot);
// Glob all matching packages
const packagePaths = await glob(patterns, {
@ -63,39 +65,56 @@ export class DependencyTransformer {
}
/**
* Find workspace root by looking for pnpm-workspace.yaml
* Find workspace root by looking for pnpm-workspace.yaml or package.json with workspaces
* Supports both pnpm and npm/bun workspace patterns
*/
private async findWorkspaceRoot(startPath: string): Promise<string> {
private async findWorkspaceRoot(startPath: string): Promise<{ root: string; type: 'pnpm' | 'npm' }> {
let currentPath = startPath;
const maxDepth = 10;
let depth = 0;
while (depth < maxDepth) {
const workspaceFile = join(currentPath, 'pnpm-workspace.yaml');
// Check for pnpm-workspace.yaml first (pnpm style)
const pnpmWorkspaceFile = join(currentPath, 'pnpm-workspace.yaml');
try {
await readFile(workspaceFile, 'utf-8');
return currentPath;
await readFile(pnpmWorkspaceFile, 'utf-8');
return { root: currentPath, type: 'pnpm' };
} catch {
const parentPath = dirname(currentPath);
if (parentPath === currentPath) {
// Reached filesystem root
break;
}
currentPath = parentPath;
depth++;
// Not found, continue
}
// Check for package.json with workspaces field (npm/bun style)
const packageJsonPath = join(currentPath, 'package.json');
try {
const content = await readFile(packageJsonPath, 'utf-8');
const pkg = JSON.parse(content);
if (pkg.workspaces && Array.isArray(pkg.workspaces)) {
return { root: currentPath, type: 'npm' };
}
} catch {
// Not found or invalid, continue
}
const parentPath = dirname(currentPath);
if (parentPath === currentPath) {
// Reached filesystem root
break;
}
currentPath = parentPath;
depth++;
}
throw new DevPublishError(
'Could not find workspace root (pnpm-workspace.yaml). ' +
'Make sure you are inside a pnpm workspace.'
'Could not find workspace root. ' +
'Make sure you are inside a pnpm workspace (pnpm-workspace.yaml) ' +
'or npm/bun workspace (package.json with workspaces field).'
);
}
/**
* Parse pnpm-workspace.yaml to extract package patterns
*/
private async parseWorkspaceFile(workspaceRoot: string): Promise<string[]> {
private async parsePnpmWorkspaceFile(workspaceRoot: string): Promise<string[]> {
const workspaceFile = join(workspaceRoot, 'pnpm-workspace.yaml');
const content = await readFile(workspaceFile, 'utf-8');
const yaml = YAML.parse(content);
@ -109,6 +128,23 @@ export class DependencyTransformer {
return yaml.packages;
}
/**
* Parse package.json workspaces field to extract package patterns (npm/bun style)
*/
private async parseNpmWorkspaceFile(workspaceRoot: string): Promise<string[]> {
const packageJsonPath = join(workspaceRoot, 'package.json');
const content = await readFile(packageJsonPath, 'utf-8');
const pkg = JSON.parse(content);
if (!pkg.workspaces || !Array.isArray(pkg.workspaces)) {
throw new DevPublishError(
'Invalid package.json: missing or invalid "workspaces" field'
);
}
return pkg.workspaces;
}
/**
* Transform workspace:* dependencies to actual versions
* @param devTimestamp Optional dev timestamp to create dev versions for workspace deps