diff --git a/src/core/dependency-transformer.ts b/src/core/dependency-transformer.ts index 3cdb904..b3eb1fd 100644 --- a/src/core/dependency-transformer.ts +++ b/src/core/dependency-transformer.ts @@ -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 { + 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 { + private async parsePnpmWorkspaceFile(workspaceRoot: string): Promise { 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 { + 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