From 2fb79b7145ee7d9fa62c88fec96c024db2a452c9 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Mon, 23 Mar 2026 22:55:41 +0000 Subject: [PATCH] feat: throw error on duplicate module names in workspace Similar to how pnpm/yarn workspaces error when multiple packages share the same name, pgpm now throws a DUPLICATE_MODULE error when getModules() or listModules() encounters multiple modules with the same .control file name. Previously getModules() returned all matches (causing duplicate entries in pgpm deploy's interactive prompt) and listModules() silently picked the shortest path. Both now throw a descriptive error listing the conflicting paths. --- pgpm/core/src/core/class/pgpm.ts | 28 +++++++++++++++++++--------- pgpm/types/src/error-factory.ts | 7 +++++++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/pgpm/core/src/core/class/pgpm.ts b/pgpm/core/src/core/class/pgpm.ts index 74937c70a..9b6565ceb 100644 --- a/pgpm/core/src/core/class/pgpm.ts +++ b/pgpm/core/src/core/class/pgpm.ts @@ -284,16 +284,29 @@ export class PgpmPackage { if (!this.workspacePath || !this.config) return []; const dirs = this.loadAllowedDirs(); - const results: PgpmPackage[] = []; + const seen = new Map(); for (const dir of dirs) { const proj = new PgpmPackage(dir); if (proj.isInModule()) { - results.push(proj); + const name = proj.getModuleName(); + if (!seen.has(name)) { + seen.set(name, []); + } + seen.get(name)!.push(proj); + } + } + + // Error on duplicate module names, similar to pnpm/yarn workspace duplicate package errors + const duplicates = Array.from(seen.entries()).filter(([, projs]) => projs.length > 1); + if (duplicates.length > 0) { + for (const [name, projs] of duplicates) { + const paths = projs.map(p => ` - ${p.cwd}`).join('\n'); + throw errors.DUPLICATE_MODULE({ name, paths }); } } - return results; + return Array.from(seen.values()).map(projs => projs[0]); } /** @@ -318,17 +331,14 @@ export class PgpmPackage { filesByName.get(moduleName)!.push(file); }); - // For each module name, pick the shortest path in case of collisions + // Error on duplicate module names, similar to pnpm/yarn workspace duplicate package errors const selectedFiles = new Map(); filesByName.forEach((files, moduleName) => { if (files.length === 1) { selectedFiles.set(moduleName, files[0]); } else { - // Multiple files with same name - pick shortest path - const shortestFile = files.reduce((shortest, current) => - current.length < shortest.length ? current : shortest - ); - selectedFiles.set(moduleName, shortestFile); + const paths = files.map(f => ` - ${path.dirname(f)}`).join('\n'); + throw errors.DUPLICATE_MODULE({ name: moduleName, paths }); } }); diff --git a/pgpm/types/src/error-factory.ts b/pgpm/types/src/error-factory.ts index 117505c52..a2e22cec4 100644 --- a/pgpm/types/src/error-factory.ts +++ b/pgpm/types/src/error-factory.ts @@ -145,6 +145,13 @@ export const errors = { `${type ? `${type} file` : 'File'} not found: ${filePath}`, 404 ), + + DUPLICATE_MODULE: makeError( + 'DUPLICATE_MODULE', + ({ name, paths }: { name: string, paths: string }) => + `Multiple modules share the name "${name}". Each module in a workspace must have a unique name.\n Found in:\n${paths}`, + 400 + ), }; // throw errors.MODULE_NOT_FOUND({ name: 'auth' });