Skip to content

Commit

Permalink
handle pnpx & pnpm dlx in package.json scripts (#16187)
Browse files Browse the repository at this point in the history
Co-authored-by: Jarred Sumner <[email protected]>
  • Loading branch information
pfgithub and Jarred-Sumner authored Jan 7, 2025
1 parent f299ef8 commit 1923509
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 123 deletions.
178 changes: 73 additions & 105 deletions src/cli/list-of-yarn-commands.zig
Original file line number Diff line number Diff line change
@@ -1,109 +1,77 @@
const std = @import("std");
const bun = @import("root").bun;

// yarn v2.3 commands
const yarn_v2 = [_][]const u8{
"add",
"bin",
"cache",
"config",
"dedupe",
"dlx",
"exec",
"explain",
"info",
"init",
"install",
"link",
"node",
"npm",
"pack",
"patch",
"plugin",
"rebuild",
"remove",
"run",
"set",
"unplug",
"up",
"why",
"workspace",
"workspaces",
};
pub const all_yarn_commands = bun.ComptimeStringMap(void, .{
// yarn v2.3 commands
.{"add"},
.{"bin"},
.{"cache"},
.{"config"},
.{"dedupe"},
.{"dlx"},
.{"exec"},
.{"explain"},
.{"info"},
.{"init"},
.{"install"},
.{"link"},
.{"node"},
.{"npm"},
.{"pack"},
.{"patch"},
.{"plugin"},
.{"rebuild"},
.{"remove"},
.{"run"},
.{"set"},
.{"unplug"},
.{"up"},
.{"why"},
.{"workspace"},
.{"workspaces"},

// yarn v1 commands
const yarn_v1 = [_][]const u8{
"access",
"add",
"audit",
"autoclean",
"bin",
"cache",
"check",
"config",
"create",
"exec",
"generate-lock-entry",
"generateLockEntry",
"global",
"help",
"import",
"info",
"init",
"install",
"licenses",
"link",
"list",
"login",
"logout",
"node",
"outdated",
"owner",
"pack",
"policies",
"publish",
"remove",
"run",
"tag",
"team",
"unlink",
"unplug",
"upgrade",
"upgrade-interactive",
"upgradeInteractive",
"version",
"versions",
"why",
"workspace",
"workspaces",
};

pub const all_yarn_commands = brk: {
@setEvalBranchQuota(9999);
var array: [yarn_v2.len + yarn_v1.len]u64 = undefined;
var array_i: usize = 0;
for (yarn_v2) |yarn| {
const hash = bun.hash(yarn);
@setEvalBranchQuota(9999);
if (std.mem.indexOfScalar(u64, array[0..array_i], hash) == null) {
@setEvalBranchQuota(9999);
array[array_i] = hash;
array_i += 1;
}
}

for (yarn_v1) |yarn| {
@setEvalBranchQuota(9999);

const hash = bun.hash(yarn);
if (std.mem.indexOfScalar(u64, array[0..array_i], hash) == null) {
@setEvalBranchQuota(9999);

array[array_i] = hash;
array_i += 1;
}
}

const final = array[0..array_i].*;
break :brk &final;
};
// yarn v1 commands
.{"access"},
.{"add"},
.{"audit"},
.{"autoclean"},
.{"bin"},
.{"cache"},
.{"check"},
.{"config"},
.{"create"},
.{"exec"},
.{"generate-lock-entry"},
.{"generateLockEntry"},
.{"global"},
.{"help"},
.{"import"},
.{"info"},
.{"init"},
.{"install"},
.{"licenses"},
.{"link"},
.{"list"},
.{"login"},
.{"logout"},
.{"node"},
.{"outdated"},
.{"owner"},
.{"pack"},
.{"policies"},
.{"publish"},
.{"remove"},
.{"run"},
.{"tag"},
.{"team"},
.{"unlink"},
.{"unplug"},
.{"upgrade"},
.{"upgrade-interactive"},
.{"upgradeInteractive"},
.{"version"},
.{"versions"},
.{"why"},
.{"workspace"},
.{"workspaces"},
});
16 changes: 14 additions & 2 deletions src/cli/run_command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const NpmArgs = struct {
pub const package_version: string = "npm_package_version";
};
const PackageJSON = @import("../resolver/package_json.zig").PackageJSON;
const yarn_commands: []const u64 = @import("./list-of-yarn-commands.zig").all_yarn_commands;
const yarn_commands = @import("./list-of-yarn-commands.zig").all_yarn_commands;

const ShellCompletions = @import("./shell_completions.zig");
const PosixSpawn = bun.posix.spawn;
Expand Down Expand Up @@ -179,7 +179,7 @@ pub const RunCommand = struct {
}

// implicit yarn commands
if (std.mem.indexOfScalar(u64, yarn_commands, bun.hash(yarn_cmd)) == null) {
if (!yarn_commands.has(yarn_cmd)) {
try copy_script.appendSlice(BUN_RUN);
try copy_script.append(' ');
try copy_script.appendSlice(yarn_cmd);
Expand Down Expand Up @@ -231,6 +231,18 @@ pub const RunCommand = struct {
delimiter = 0;
continue;
}
if (strings.hasPrefixComptime(script[start..], "pnpm dlx ")) {
try copy_script.appendSlice(BUN_BIN_NAME ++ " x ");
entry_i += "pnpm dlx ".len;
delimiter = 0;
continue;
}
if (strings.hasPrefixComptime(script[start..], "pnpx ")) {
try copy_script.appendSlice(BUN_BIN_NAME ++ " x ");
entry_i += "pnpx ".len;
delimiter = 0;
continue;
}
}

delimiter = 0;
Expand Down
63 changes: 47 additions & 16 deletions test/cli/install/bun-run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -599,22 +599,53 @@ it("should pass arguments correctly in scripts", async () => {
}
});

it("should run with bun instead of npm even with leading spaces", async () => {
const dir = tempDirWithFiles("test", {
"package.json": JSON.stringify({
workspaces: ["a", "b"],
scripts: { "root_script": " npm run other_script ", "other_script": " echo hi " },
}),
});
{
const { stdout, stderr, exitCode } = spawnSync({
cmd: [bunExe(), "run", "root_script"],
cwd: dir,
env: bunEnv,
});
const cases = [
["yarn run", "run"],
["yarn add", "passthrough"],
["yarn audit", "passthrough"],
["yarn -abcd run", "passthrough"],
["yarn info", "passthrough"],
["yarn generate-lock-entry", "passthrough"],
["yarn", "run"],
["npm run", "run"],
["npx", "x"],
["pnpm run", "run"],
["pnpm dlx", "x"],
["pnpx", "x"],
];
describe("should handle run case", () => {
for (const ccase of cases) {
it(ccase[0], async () => {
const dir = tempDirWithFiles("test", {
"package.json": JSON.stringify({
scripts: {
"root_script": ` ${ccase[0]} target_script% `,
"target_script%": " echo target_script ",
},
}),
});
{
const { stdout, stderr, exitCode } = spawnSync({
cmd: [bunExe(), "root_script"],
cwd: dir,
env: bunEnv,
});

expect(stderr.toString()).toMatch(/\$ bun(-debug)? run other_script \n\$ echo hi \n/);
expect(stdout.toString()).toEndWith("hi\n");
expect(exitCode).toBe(0);
if (ccase[1] === "run") {
expect(stderr.toString()).toMatch(
/^\$ bun(-debug)? run target_script% \n\$ echo target_script \n/,
);
expect(stdout.toString()).toEndWith("target_script\n");
expect(exitCode).toBe(0);
} else if (ccase[1] === "x") {
expect(stderr.toString()).toMatch(
/^\$ bun(-debug)? x target_script% \nerror: unrecognised dependency format: target_script%/,
);
expect(exitCode).toBe(1);
} else {
expect(stderr.toString()).toStartWith(`$ ${ccase[0]} target_script% \n`);
}
}
});
}
});

0 comments on commit 1923509

Please sign in to comment.