Skip to content

Commit f50d296

Browse files
Sahilb315Copilot
andauthored
add e2e and alias for npx, pnpx (#105)
* add e2e and alias for npx, pnpx * Update .github/workflows/pmg-e2e.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> --------- Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent ca224f5 commit f50d296

File tree

4 files changed

+81
-6
lines changed

4 files changed

+81
-6
lines changed

.github/workflows/pmg-e2e.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,56 @@ jobs:
217217
test -d node_modules/lodash
218218
cd .. && rm -rf yarn-test
219219
220+
- name: Test NPX - Package Execution
221+
run: |
222+
echo "Testing NPX package execution..."
223+
mkdir npx-test && cd npx-test
224+
225+
echo "Testing npx with a simple package..."
226+
pmg npx cowsay@1.6.0 "Hello from pmg npx" | tee npx-output.txt
227+
228+
# Verification: cowsay output contains our message
229+
grep -q "Hello from pmg npx" npx-output.txt
230+
231+
echo "Testing npx with --package flag..."
232+
pmg npx --package cowsay@1.6.0 -- cowsay "Hello with package flag" | tee npx-pkg-output.txt
233+
234+
# Verification: package flag execution produces expected output
235+
grep -q "Hello with package flag" npx-pkg-output.txt
236+
237+
echo "Testing npx dry-run mode..."
238+
pmg --dry-run npx cowsay@1.6.0 "This should not execute" | tee npx-dry-output.txt
239+
240+
# Verification: dry-run should NOT produce cowsay ASCII art (cow face ^__^ should not appear)
241+
! grep -q '\^__\^' npx-dry-output.txt
242+
243+
cd .. && rm -rf npx-test
244+
245+
- name: Test PNPX - Package Execution
246+
run: |
247+
echo "Testing PNPX package execution..."
248+
mkdir pnpx-test && cd pnpx-test
249+
250+
echo "Testing pnpx with a simple package..."
251+
pmg pnpx cowsay@1.6.0 "Hello from pmg pnpx" | tee pnpx-output.txt
252+
253+
# Verification: cowsay output contains our message
254+
grep -q "Hello from pmg pnpx" pnpx-output.txt
255+
256+
echo "Testing pnpx with --package flag..."
257+
pmg pnpx --package cowsay@1.6.0 -- cowsay "Hello with package flag" | tee pnpx-pkg-output.txt
258+
259+
# Verification: package flag execution produces expected output
260+
grep -q "Hello with package flag" pnpx-pkg-output.txt
261+
262+
echo "Testing pnpx dry-run mode..."
263+
pmg --dry-run pnpx cowsay@1.6.0 "This should not execute" | tee pnpx-dry-output.txt
264+
265+
# Verification: dry-run should NOT produce cowsay ASCII art (cow face ^__^ should not appear)
266+
! grep -q '\^__\^' pnpx-dry-output.txt
267+
268+
cd .. && rm -rf pnpx-test
269+
220270
- name: Test Pip - Single Package & Manifest
221271
run: |
222272
echo "Testing Pip single package installation..."

internal/alias/alias.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func DefaultConfig() AliasConfig {
9595

9696
return AliasConfig{
9797
RcFileName: ".pmg.rc",
98-
PackageManagers: []string{"npm", "pip", "pip3", "pnpm", "bun", "uv", "yarn", "poetry"},
98+
PackageManagers: []string{"npm", "pip", "pip3", "pnpm", "bun", "uv", "yarn", "poetry", "npx", "pnpx"},
9999
Shells: shells,
100100
}
101101
}

packagemanager/npm_executor.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,11 @@ func (n *npmPackageExecutor) ParseCommand(args []string) (*ParsedCommand, error)
8282
}
8383
}
8484

85-
// Unlike npx, pnpx does not separate package and binary;
86-
// the first arg is always an install target.
87-
if n.Config.CommandName == "pnpx" && len(flagSet.Args()) > 0 {
85+
// For both npx and pnpx, the first positional argument is typically
86+
// the package to execute (e.g., `npx cowsay@1.6.0` or `pnpx cowsay@1.6.0`).
87+
// However, if -p/--package flags are provided, the first positional arg
88+
// is the binary to run, not the package (e.g., `npx -p typescript tsc`).
89+
if len(flagSet.Args()) > 0 && len(packages) == 0 {
8890
pkg := flagSet.Args()[0]
8991
if !slices.Contains(packages, pkg) {
9092
packages = append(packages, pkg)

packagemanager/npm_executor_test.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,37 @@ func TestNpxExecutorParseCommand(t *testing.T) {
4343
},
4444
},
4545
{
46-
name: "non-package npx command",
46+
name: "package as first positional arg",
4747
command: "npx create-react-app my-app",
4848
assert: func(t *testing.T, parsed *ParsedCommand, err error) {
4949
assert.NoError(t, err)
50-
assert.Equal(t, 0, len(parsed.InstallTargets))
50+
assert.Equal(t, 1, len(parsed.InstallTargets))
51+
assert.Equal(t, "create-react-app", parsed.InstallTargets[0].PackageVersion.Package.Name)
5152
assert.Equal(t, []string{"create-react-app", "my-app"}, parsed.Command.Args)
5253
},
5354
},
55+
{
56+
name: "package with version as first positional arg",
57+
command: "npx cowsay@1.6.0 hello",
58+
assert: func(t *testing.T, parsed *ParsedCommand, err error) {
59+
assert.NoError(t, err)
60+
assert.Equal(t, 1, len(parsed.InstallTargets))
61+
assert.Equal(t, "cowsay", parsed.InstallTargets[0].PackageVersion.Package.Name)
62+
assert.Equal(t, "1.6.0", parsed.InstallTargets[0].PackageVersion.Version)
63+
},
64+
},
65+
{
66+
name: "package via -p flag with different binary",
67+
command: "npx -p typescript tsc --version",
68+
assert: func(t *testing.T, parsed *ParsedCommand, err error) {
69+
assert.NoError(t, err)
70+
assert.Equal(t, 1, len(parsed.InstallTargets))
71+
assert.Equal(t, "typescript", parsed.InstallTargets[0].PackageVersion.Package.Name)
72+
assert.Empty(t, parsed.InstallTargets[0].PackageVersion.Version)
73+
// tsc is the binary, not a package
74+
assert.Equal(t, []string{"-p", "typescript", "tsc", "--version"}, parsed.Command.Args)
75+
},
76+
},
5477
{
5578
name: "single package using -p flag npx command with binary",
5679
command: "npx -p tsx my-app",

0 commit comments

Comments
 (0)