Skip to content

Commit 198a8af

Browse files
committed
feat(pgpm): add aliased npm package name lookup for CLI commands
Add support for using npm package names (e.g., @scope/my-module) as aliases for control file names (e.g., my-module) in deploy, revert, verify, and tag commands. Changes: - Add package-alias.ts utility with buildPackageAliasMap() and resolvePackageAlias() - Update deploy.ts, revert.ts, verify.ts, tag.ts to resolve package aliases - Update module-utils.ts selectPackage() to accept aliased names - Update deployed-changes.ts to accept aliased names with cwd parameter This is a CLI-only change that keeps core logic unchanged. Users can now use: pgpm deploy --package @some-scope/my-module instead of: pgpm deploy --package my-module Co-Authored-By: Dan Lynch <pyramation@gmail.com>
1 parent e4d5396 commit 198a8af

8 files changed

Lines changed: 137 additions & 21 deletions

File tree

packages/pgpm/src/commands/deploy.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
getSpawnEnvWithPg,
1010
} from 'pg-env';
1111

12-
import { getTargetDatabase } from '../utils';
12+
import { getTargetDatabase, resolvePackageAlias } from '../utils';
1313
import { selectPackage } from '../utils/module-utils';
1414

1515
const deployUsageText = `
@@ -154,9 +154,10 @@ export default async (
154154
} else if (packageName) {
155155
target = packageName;
156156
} else if (argv.package && argv.to) {
157-
target = `${argv.package}:${argv.to}`;
157+
const resolvedPackage = resolvePackageAlias(argv.package as string, cwd);
158+
target = `${resolvedPackage}:${argv.to}`;
158159
} else if (argv.package) {
159-
target = argv.package as string;
160+
target = resolvePackageAlias(argv.package as string, cwd);
160161
}
161162

162163
await project.deploy(

packages/pgpm/src/commands/revert.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Logger } from '@pgpmjs/logger';
44
import { CLIOptions, Inquirerer, Question } from 'inquirerer';
55
import { getPgEnvOptions } from 'pg-env';
66

7-
import { getTargetDatabase } from '../utils';
7+
import { getTargetDatabase, resolvePackageAlias } from '../utils';
88
import { cliExitWithError } from '../utils/cli-error';
99
import { selectDeployedChange, selectDeployedPackage } from '../utils/deployed-changes';
1010

@@ -84,7 +84,7 @@ export default async (
8484

8585
let packageName: string | undefined;
8686
if (recursive && argv.to !== true) {
87-
packageName = await selectDeployedPackage(database, argv, prompter, log, 'revert');
87+
packageName = await selectDeployedPackage(database, argv, prompter, log, 'revert', cwd);
8888
if (!packageName) {
8989
await cliExitWithError('No package found to revert');
9090
}
@@ -102,18 +102,19 @@ export default async (
102102
let target: string | undefined;
103103

104104
if (argv.to === true) {
105-
target = await selectDeployedChange(database, argv, prompter, log, 'revert');
105+
target = await selectDeployedChange(database, argv, prompter, log, 'revert', cwd);
106106
if (!target) {
107107
await cliExitWithError('No target selected, operation cancelled');
108108
}
109-
} else if (packageName && argv.to) {
109+
}else if (packageName && argv.to) {
110110
target = `${packageName}:${argv.to}`;
111111
} else if (packageName) {
112112
target = packageName;
113113
} else if (argv.package && argv.to) {
114-
target = `${argv.package}:${argv.to}`;
114+
const resolvedPackage = resolvePackageAlias(argv.package as string, cwd);
115+
target = `${resolvedPackage}:${argv.to}`;
115116
} else if (argv.package) {
116-
target = argv.package as string;
117+
target = resolvePackageAlias(argv.package as string, cwd);
117118
}
118119

119120
await pkg.revert(

packages/pgpm/src/commands/tag.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as path from 'path';
66

77
import { extractFirst } from '../utils/argv';
88
import { selectPackage } from '../utils/module-utils';
9+
import { resolvePackageAlias } from '../utils/package-alias';
910

1011
const log = new Logger('tag');
1112

@@ -61,7 +62,7 @@ export default async (
6162
let packageName: string | undefined;
6263

6364
if (argv.package) {
64-
packageName = argv.package as string;
65+
packageName = resolvePackageAlias(argv.package as string, cwd);
6566
log.info(`Using specified package: ${packageName}`);
6667
}
6768
else if (pkg.isInModule()) {

packages/pgpm/src/commands/verify.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Logger } from '@pgpmjs/logger';
44
import { CLIOptions, Inquirerer, Question } from 'inquirerer';
55
import { getPgEnvOptions } from 'pg-env';
66

7-
import { getTargetDatabase } from '../utils';
7+
import { getTargetDatabase, resolvePackageAlias } from '../utils';
88
import { cliExitWithError } from '../utils/cli-error';
99
import { selectDeployedChange, selectDeployedPackage } from '../utils/deployed-changes';
1010

@@ -62,7 +62,7 @@ export default async (
6262

6363
let packageName: string | undefined;
6464
if (recursive && argv.to !== true) {
65-
packageName = await selectDeployedPackage(database, argv, prompter, log, 'verify');
65+
packageName = await selectDeployedPackage(database, argv, prompter, log, 'verify', cwd);
6666
if (!packageName) {
6767
await cliExitWithError('No package found to verify');
6868
}
@@ -77,18 +77,19 @@ export default async (
7777
let target: string | undefined;
7878

7979
if (argv.to === true) {
80-
target = await selectDeployedChange(database, argv, prompter, log, 'verify');
80+
target = await selectDeployedChange(database, argv, prompter, log, 'verify', cwd);
8181
if (!target) {
8282
await cliExitWithError('No target selected, operation cancelled');
8383
}
84-
} else if (packageName && argv.to) {
84+
}else if (packageName && argv.to) {
8585
target = `${packageName}:${argv.to}`;
8686
} else if (packageName) {
8787
target = packageName;
8888
} else if (argv.package && argv.to) {
89-
target = `${argv.package}:${argv.to}`;
89+
const resolvedPackage = resolvePackageAlias(argv.package as string, cwd);
90+
target = `${resolvedPackage}:${argv.to}`;
9091
} else if (argv.package) {
91-
target = argv.package as string;
92+
target = resolvePackageAlias(argv.package as string, cwd);
9293
}
9394

9495
await project.verify(

packages/pgpm/src/utils/deployed-changes.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,23 @@ import { Logger } from '@pgpmjs/logger';
33
import { Inquirerer } from 'inquirerer';
44
import { getPgEnvOptions } from 'pg-env';
55

6+
import { resolvePackageAlias } from './package-alias';
7+
68
export async function selectDeployedChange(
79
database: string,
810
argv: Partial<Record<string, any>>,
911
prompter: Inquirerer,
1012
log: Logger,
11-
action: 'revert' | 'verify' = 'revert'
13+
action: 'revert' | 'verify' = 'revert',
14+
cwd: string = process.cwd()
1215
): Promise<string | undefined> {
1316
const pgEnv = getPgEnvOptions({ database });
1417
const client = new PgpmMigrate(pgEnv);
1518

1619
let selectedPackage: string;
1720

1821
if (argv.package) {
19-
selectedPackage = argv.package;
22+
selectedPackage = resolvePackageAlias(argv.package as string, cwd);
2023
} else {
2124
const packageStatuses = await client.status();
2225

@@ -66,10 +69,11 @@ export async function selectDeployedPackage(
6669
argv: Partial<Record<string, any>>,
6770
prompter: Inquirerer,
6871
log: Logger,
69-
action: 'revert' | 'verify' = 'revert'
72+
action: 'revert' | 'verify' = 'revert',
73+
cwd: string = process.cwd()
7074
): Promise<string | undefined> {
7175
if (argv.package) {
72-
return argv.package;
76+
return resolvePackageAlias(argv.package as string, cwd);
7377
}
7478

7579
const pgEnv = getPgEnvOptions({ database });

packages/pgpm/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ export * from './cli-error';
55
export * from './deployed-changes';
66
export * from './module-utils';
77
export * from './npm-version';
8+
export * from './package-alias';
89
export * from './update-check';
910
export * from './update-config';

packages/pgpm/src/utils/module-utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { errors } from '@pgpmjs/types';
44
import { Inquirerer } from 'inquirerer';
55
import { ParsedArgs } from 'minimist';
66

7+
import { resolvePackageAlias } from './package-alias';
8+
79
/**
810
* Handle package selection for operations that need a specific package
911
* Returns the selected package name, or undefined if validation fails or no packages exist
@@ -33,7 +35,8 @@ export async function selectPackage(
3335

3436
// If a specific package was provided, validate it
3537
if (argv.package) {
36-
const packageName = argv.package as string;
38+
const inputPackage = argv.package as string;
39+
const packageName = resolvePackageAlias(inputPackage, cwd);
3740
if (log) log.info(`Using specified package: ${packageName}`);
3841

3942
if (!moduleNames.includes(packageName)) {
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { PgpmPackage } from '@pgpmjs/core';
2+
import { existsSync, readFileSync } from 'fs';
3+
import { join } from 'path';
4+
5+
export interface PackageAliasMap {
6+
[npmName: string]: string;
7+
}
8+
9+
/**
10+
* Build a map of npm package names to control file names (extension names).
11+
* This allows users to reference packages by their npm name (e.g., @scope/my-module)
12+
* instead of the control file name (e.g., my-module).
13+
*/
14+
export function buildPackageAliasMap(cwd: string): PackageAliasMap {
15+
const aliasMap: PackageAliasMap = {};
16+
17+
try {
18+
const pkg = new PgpmPackage(cwd);
19+
const workspacePath = pkg.getWorkspacePath();
20+
21+
if (!workspacePath) {
22+
return aliasMap;
23+
}
24+
25+
const modules = pkg.getModuleMap();
26+
27+
for (const [controlName, moduleInfo] of Object.entries(modules)) {
28+
const modulePath = join(workspacePath, moduleInfo.path);
29+
const packageJsonPath = join(modulePath, 'package.json');
30+
31+
if (existsSync(packageJsonPath)) {
32+
try {
33+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
34+
const npmName = packageJson.name;
35+
36+
if (npmName && npmName !== controlName) {
37+
aliasMap[npmName] = controlName;
38+
}
39+
} catch {
40+
// Skip modules with invalid package.json
41+
}
42+
}
43+
}
44+
} catch {
45+
// Return empty map if we can't access workspace
46+
}
47+
48+
return aliasMap;
49+
}
50+
51+
/**
52+
* Resolve a package name that might be an npm alias to its control file name.
53+
* If the input is already a control file name or not found in aliases, returns as-is.
54+
*
55+
* @param input - The package name (could be npm name like @scope/pkg or control name)
56+
* @param cwd - The current working directory
57+
* @returns The resolved control file name
58+
*/
59+
export function resolvePackageAlias(input: string, cwd: string): string {
60+
if (!input) {
61+
return input;
62+
}
63+
64+
const aliasMap = buildPackageAliasMap(cwd);
65+
return aliasMap[input] ?? input;
66+
}
67+
68+
/**
69+
* Get the npm package name for a given control file name, if available.
70+
* Returns undefined if no npm alias exists.
71+
*
72+
* @param controlName - The control file name (extension name)
73+
* @param cwd - The current working directory
74+
* @returns The npm package name or undefined
75+
*/
76+
export function getNpmNameForControl(controlName: string, cwd: string): string | undefined {
77+
const aliasMap = buildPackageAliasMap(cwd);
78+
79+
for (const [npmName, ctrlName] of Object.entries(aliasMap)) {
80+
if (ctrlName === controlName) {
81+
return npmName;
82+
}
83+
}
84+
85+
return undefined;
86+
}
87+
88+
/**
89+
* Format a module name for display, showing both control name and npm alias if available.
90+
* Example: "my-module (@scope/my-module)" or just "my-module" if no alias
91+
*
92+
* @param controlName - The control file name
93+
* @param cwd - The current working directory
94+
* @returns Formatted display string
95+
*/
96+
export function formatModuleNameWithAlias(controlName: string, cwd: string): string {
97+
const npmName = getNpmNameForControl(controlName, cwd);
98+
99+
if (npmName) {
100+
return `${controlName} (${npmName})`;
101+
}
102+
103+
return controlName;
104+
}

0 commit comments

Comments
 (0)