aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-03-01 16:17:47 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-03-01 16:17:47 -0800
commit6bc075e377a1f4dcb49ea27c9224117c6cbada70 (patch)
treee29d748c346624e7e3c667d6b80c479fed051f1b
parentba0706939d1c3b26121d7e39edf0c1a7a3f3a4da (diff)
downloadbun-6bc075e377a1f4dcb49ea27c9224117c6cbada70.tar.gz
bun-6bc075e377a1f4dcb49ea27c9224117c6cbada70.tar.zst
bun-6bc075e377a1f4dcb49ea27c9224117c6cbada70.zip
Revert "Update clap (#2238)"
This reverts commit 7b9a17f9d7106ffd8e553a5192aba60d14ea5e9c.
-rw-r--r--.gitmodules3
-rw-r--r--misctools/fetch.zig16
-rw-r--r--misctools/http_bench.zig22
-rw-r--r--src/cli.zig292
-rw-r--r--src/cli/create_command.zig44
m---------src/deps/zig-clap0
-rw-r--r--src/deps/zig-clap/.gitignore1
-rw-r--r--src/deps/zig-clap/LICENSE24
-rw-r--r--src/deps/zig-clap/build.zig55
-rw-r--r--src/deps/zig-clap/clap.zig546
-rw-r--r--src/deps/zig-clap/clap/args.zig337
-rw-r--r--src/deps/zig-clap/clap/comptime.zig194
-rw-r--r--src/deps/zig-clap/clap/streaming.zig430
-rw-r--r--src/deps/zig-clap/gyro.zzz14
-rw-r--r--src/deps/zig-clap/zig.mod5
-rw-r--r--src/install/install.zig147
16 files changed, 1840 insertions, 290 deletions
diff --git a/.gitmodules b/.gitmodules
index a86b743b7..c22446cbd 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -65,6 +65,3 @@ fetchRecurseSubmodules = false
[submodule "src/deps/c-ares"]
path = src/deps/c-ares
url = https://github.com/c-ares/c-ares.git
-[submodule "src/deps/zig-clap"]
- path = src/deps/zig-clap
- url = https://github.com/Hejsil/zig-clap.git
diff --git a/misctools/fetch.zig b/misctools/fetch.zig
index 85f21c3b7..cdefd29d4 100644
--- a/misctools/fetch.zig
+++ b/misctools/fetch.zig
@@ -78,7 +78,7 @@ pub const Arguments = struct {
pub fn parse(allocator: std.mem.Allocator) !Arguments {
var diag = clap.Diagnostic{};
- var res = clap.parse(clap.Help, &params, clap.parsers.default, .{
+ var args = clap.parse(clap.Help, &params, .{
.diagnostic = &diag,
.allocator = allocator,
}) catch |err| {
@@ -87,7 +87,7 @@ pub const Arguments = struct {
return err;
};
- var positionals = res.positionals;
+ var positionals = args.positionals();
var raw_args: std.ArrayListUnmanaged(string) = undefined;
if (positionals.len > 0) {
@@ -96,16 +96,16 @@ pub const Arguments = struct {
raw_args = .{};
}
- if (res.args.version) {
+ if (args.flag("--version")) {
try Output.writer().writeAll(VERSION);
Global.exit(0);
}
var method = Method.GET;
var url: URL = .{};
- var body_string: string = res.args.body orelse "";
+ var body_string: string = args.option("--body") orelse "";
- if (res.args.file) |file_path| {
+ if (args.option("--file")) |file_path| {
if (file_path.len > 0) {
var cwd = try std.process.getCwd(&cwd_buf);
var parts = [_]string{file_path};
@@ -154,12 +154,12 @@ pub const Arguments = struct {
return Arguments{
.url = url,
.method = method,
- .verbose = res.args.verbose,
+ .verbose = args.flag("--verbose"),
.headers = .{},
.headers_buf = "",
.body = body_string,
- .turbo = res.args.turbo,
- .quiet = res.args.quiet,
+ .turbo = args.flag("--turbo"),
+ .quiet = args.flag("--quiet"),
};
}
};
diff --git a/misctools/http_bench.zig b/misctools/http_bench.zig
index d4d27b56e..5e12f0157 100644
--- a/misctools/http_bench.zig
+++ b/misctools/http_bench.zig
@@ -82,7 +82,7 @@ pub const Arguments = struct {
pub fn parse(allocator: std.mem.Allocator) !Arguments {
var diag = clap.Diagnostic{};
- var res = clap.parse(clap.Help, &params, clap.parsers.default, .{
+ var args = clap.parse(clap.Help, &params, .{
.diagnostic = &diag,
.allocator = allocator,
}) catch |err| {
@@ -91,7 +91,7 @@ pub const Arguments = struct {
return err;
};
- var positionals = res.positionals;
+ var positionals = args.positionals();
var raw_args: std.ArrayListUnmanaged(string) = undefined;
if (positionals.len > 0) {
@@ -100,16 +100,16 @@ pub const Arguments = struct {
raw_args = .{};
}
- if (res.args.version) {
+ if (args.flag("--version")) {
try Output.writer().writeAll(VERSION);
Global.exit(0);
}
var method = Method.GET;
var url: URL = .{};
- var body_string: string = res.args.body orelse "";
+ var body_string: string = args.option("--body") orelse "";
- if (res.args.file) |file_path| {
+ if (args.option("--file")) |file_path| {
if (file_path.len > 0) {
var cwd = try std.process.getCwd(&cwd_buf);
var parts = [_]string{file_path};
@@ -158,18 +158,18 @@ pub const Arguments = struct {
return Arguments{
.url = url,
.method = method,
- .verbose = res.args.verbose,
+ .verbose = args.flag("--verbose"),
.headers = .{},
.headers_buf = "",
.body = body_string,
- // .keep_alive = !res.args.@"--no-keep-alive",
- .concurrency = std.fmt.parseInt(u16, res.args.@"max-concurrency" orelse "32", 10) catch 32,
- .turbo = res.args.turbo,
- .timeout = std.fmt.parseInt(usize, res.args.timeout orelse "0", 10) catch |err| {
+ // .keep_alive = !args.flag("--no-keep-alive"),
+ .concurrency = std.fmt.parseInt(u16, args.option("--max-concurrency") orelse "32", 10) catch 32,
+ .turbo = args.flag("--turbo"),
+ .timeout = std.fmt.parseInt(usize, args.option("--timeout") orelse "0", 10) catch |err| {
Output.prettyErrorln("<r><red>{s}<r> parsing timeout", .{@errorName(err)});
Global.exit(1);
},
- .count = std.fmt.parseInt(usize, res.args.count orelse "10", 10) catch |err| {
+ .count = std.fmt.parseInt(usize, args.option("--count") orelse "10", 10) catch |err| {
Output.prettyErrorln("<r><red>{s}<r> parsing count", .{@errorName(err)});
Global.exit(1);
},
diff --git a/src/cli.zig b/src/cli.zig
index e44c75d43..e05bd113d 100644
--- a/src/cli.zig
+++ b/src/cli.zig
@@ -160,70 +160,59 @@ pub const Arguments = struct {
}
}
- pub const parser = .{
- .STR = clap.parsers.string,
- .PATH = clap.parsers.string,
- .POS = clap.parsers.string,
- };
-
pub const ParamType = clap.Param(clap.Help);
- const public_params = clap.parseParamsComptime(
- \\--use <STR> Choose a framework, e.g. "--use next". It checks first for a package named "bun-framework-packagename" and then "packagename".
- \\-b, --bun Force a script or package to use Bun.js instead of Node.js (via symlinking node)
- \\--bunfile <STR> Use a .bun file (default: node_modules.bun)
- \\--server-bunfile <STR> Use a .server.bun file (default: node_modules.server.bun)
- \\--cwd <STR> Absolute path to resolve files & entry points from. This just changes the process' cwd.
- \\-c, --config <PATH> Config file to load bun from (e.g. -c bunfig.toml)
- \\--disable-react-fast-refresh Disable React Fast Refresh
- \\--disable-hmr Disable Hot Module Reloading (disables fast refresh too) in bun dev
- \\--extension-order <STR>... defaults to: .tsx,.ts,.jsx,.js,.json
- \\--jsx-factory <STR> Changes the function called when compiling JSX elements using the classic JSX runtime
- \\--jsx-fragment <STR> Changes the function called when compiling JSX fragments
- \\--jsx-import-source <STR> Declares the module specifier to be used for importing the jsx and jsxs factory functions. Default: "react"
- \\--jsx-production Use jsx instead of jsxDEV (default) for the automatic runtime
- \\--jsx-runtime <STR> "automatic" (default) or "classic"
- \\-r, --preload <STR>... Import a module before other modules are loaded") catch unreachable,
- \\--main-fields <STR>... Main fields to lookup in package.json. Defaults to --platform dependent
- \\--no-summary Don't print a summary (when generating .bun
- \\-v, --version Print version and exit
- \\--platform <STR> "bun" or "browser" or "node", used when building or bundling
- \\--public-dir <STR> Top-level directory for .html files, fonts or anything external. Defaults to "<cwd>/public", to match create-react-app and Next.js
- \\--tsconfig-override <STR> Load tsconfig from path instead of cwd/tsconfig.json
- \\-d, --define <STR>... Substitute K:V while parsing, e.g. --define process.env.NODE_ENV:"development". Values are parsed as JSON.
- \\-e, --external <STR>... Exclude module from transpilation (can use * wildcards). ex: -e react
- \\-h, --help Display this help and exit.
- \\-l, --loader <STR>... Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: jsx, js, json, tsx, ts, css
- \\-u, --origin <STR> Rewrite import URLs to start with --origin. Default: ""
- \\-p, --port <STR> Port to serve bun's dev server on. Default: "3000"
- \\--hot Enable auto reload in bun's JavaScript runtime
- \\--no-install Disable auto install in bun's JavaScript runtime
- \\-i, --install-fallback Automatically install dependencies and use global cache in bun's runtime, equivalent to --install=fallback
- \\--install <STR> Install dependencies automatically when no node_modules are present, default: "auto". "force" to ignore node_modules, fallback to install any missing
- \\--prefer-offline Skip staleness checks for packages in bun's JavaScript runtime and resolve from disk
- \\--prefer-latest Use the latest matching versions of packages in bun's JavaScript runtime, always checking npm
- \\--silent Don't repeat the command for bun run
- \\<POS>...
- );
-
- const debug_params = clap.parseParamsComptime(
- \\--dump-environment-variables Dump environment variables from .env and process as JSON and quit. Useful for debugging
- \\--dump-limits Dump system limits. Useful for debugging
- )
- // clap can't handle parsing the '.'
- ++ .{
- clap.Param(clap.Help){
- .id = .{ .val = "disable-bun.js", .desc = "Disable bun.js from loading in the dev server" },
- .names = .{ .long = "disable-bun.js" },
- }
+ const public_params = [_]ParamType{
+ clap.parseParam("--use <STR> Choose a framework, e.g. \"--use next\". It checks first for a package named \"bun-framework-packagename\" and then \"packagename\".") catch unreachable,
+ clap.parseParam("-b, --bun Force a script or package to use Bun.js instead of Node.js (via symlinking node)") catch unreachable,
+ clap.parseParam("--bunfile <STR> Use a .bun file (default: node_modules.bun)") catch unreachable,
+ clap.parseParam("--server-bunfile <STR> Use a .server.bun file (default: node_modules.server.bun)") catch unreachable,
+ clap.parseParam("--cwd <STR> Absolute path to resolve files & entry points from. This just changes the process' cwd.") catch unreachable,
+ clap.parseParam("-c, --config <PATH>? Config file to load bun from (e.g. -c bunfig.toml") catch unreachable,
+ clap.parseParam("--disable-react-fast-refresh Disable React Fast Refresh") catch unreachable,
+ clap.parseParam("--disable-hmr Disable Hot Module Reloading (disables fast refresh too) in bun dev") catch unreachable,
+ clap.parseParam("--extension-order <STR>... defaults to: .tsx,.ts,.jsx,.js,.json ") catch unreachable,
+ clap.parseParam("--jsx-factory <STR> Changes the function called when compiling JSX elements using the classic JSX runtime") catch unreachable,
+ clap.parseParam("--jsx-fragment <STR> Changes the function called when compiling JSX fragments") catch unreachable,
+ clap.parseParam("--jsx-import-source <STR> Declares the module specifier to be used for importing the jsx and jsxs factory functions. Default: \"react\"") catch unreachable,
+ clap.parseParam("--jsx-production Use jsx instead of jsxDEV (default) for the automatic runtime") catch unreachable,
+ clap.parseParam("--jsx-runtime <STR> \"automatic\" (default) or \"classic\"") catch unreachable,
+ clap.parseParam("-r, --preload <STR>... Import a module before other modules are loaded") catch unreachable,
+ clap.parseParam("--main-fields <STR>... Main fields to lookup in package.json. Defaults to --platform dependent") catch unreachable,
+ clap.parseParam("--no-summary Don't print a summary (when generating .bun") catch unreachable,
+ clap.parseParam("-v, --version Print version and exit") catch unreachable,
+ clap.parseParam("--platform <STR> \"bun\" or \"browser\" or \"node\", used when building or bundling") catch unreachable,
+ // clap.parseParam("--production [not implemented] generate production code") catch unreachable,
+ clap.parseParam("--public-dir <STR> Top-level directory for .html files, fonts or anything external. Defaults to \"<cwd>/public\", to match create-react-app and Next.js") catch unreachable,
+ clap.parseParam("--tsconfig-override <STR> Load tsconfig from path instead of cwd/tsconfig.json") catch unreachable,
+ clap.parseParam("-d, --define <STR>... Substitute K:V while parsing, e.g. --define process.env.NODE_ENV:\"development\". Values are parsed as JSON.") catch unreachable,
+ clap.parseParam("-e, --external <STR>... Exclude module from transpilation (can use * wildcards). ex: -e react") catch unreachable,
+ clap.parseParam("-h, --help Display this help and exit. ") catch unreachable,
+ clap.parseParam("-l, --loader <STR>... Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: jsx, js, json, tsx, ts, css") catch unreachable,
+ clap.parseParam("-u, --origin <STR> Rewrite import URLs to start with --origin. Default: \"\"") catch unreachable,
+ clap.parseParam("-p, --port <STR> Port to serve bun's dev server on. Default: \"3000\"") catch unreachable,
+ clap.parseParam("--hot Enable auto reload in bun's JavaScript runtime") catch unreachable,
+ clap.parseParam("--no-install Disable auto install in bun's JavaScript runtime") catch unreachable,
+ clap.parseParam("-i Automatically install dependencies and use global cache in bun's runtime, equivalent to --install=fallback") catch unreachable,
+ clap.parseParam("--install <STR> Install dependencies automatically when no node_modules are present, default: \"auto\". \"force\" to ignore node_modules, fallback to install any missing") catch unreachable,
+ clap.parseParam("--prefer-offline Skip staleness checks for packages in bun's JavaScript runtime and resolve from disk") catch unreachable,
+ clap.parseParam("--prefer-latest Use the latest matching versions of packages in bun's JavaScript runtime, always checking npm") catch unreachable,
+ clap.parseParam("--silent Don't repeat the command for bun run") catch unreachable,
+ clap.parseParam("<POS>... ") catch unreachable,
+ };
+
+ const debug_params = [_]ParamType{
+ clap.parseParam("--dump-environment-variables Dump environment variables from .env and process as JSON and quit. Useful for debugging") catch unreachable,
+ clap.parseParam("--dump-limits Dump system limits. Useful for debugging") catch unreachable,
+ clap.parseParam("--disable-bun.js Disable bun.js from loading in the dev server") catch unreachable,
};
pub const params = public_params ++ debug_params;
- const build_only_params = clap.parseParamsComptime(
- \\--sourcemap <STR> Build with sourcemaps - "inline", "external", or "none"
- \\--outdir <STR> Default to "dist" if multiple files
- );
+ const build_only_params = [_]ParamType{
+ clap.parseParam("--sourcemap <STR>? Build with sourcemaps - 'inline', 'external', or 'none'") catch unreachable,
+ clap.parseParam("--outdir <STR> Default to \"dist\" if multiple files") catch unreachable,
+ };
const build_params_public = public_params ++ build_only_params;
pub const build_params = build_params_public ++ debug_params;
@@ -333,78 +322,40 @@ pub const Arguments = struct {
try loadConfigPath(allocator, auto_loaded, config_path, ctx, comptime cmd);
}
+ pub fn loadConfigWithCmdArgs(
+ comptime cmd: Command.Tag,
+ allocator: std.mem.Allocator,
+ args: clap.Args(clap.Help, cmd.params()),
+ ctx: *Command.Context,
+ ) !void {
+ return try loadConfig(allocator, args.option("--config"), ctx, comptime cmd);
+ }
+
pub fn parse(allocator: std.mem.Allocator, ctx: *Command.Context, comptime cmd: Command.Tag) !Api.TransformOptions {
var diag = clap.Diagnostic{};
+ const params_to_use = comptime cmd.params();
- return parseImpl(allocator, ctx, cmd, &diag) catch |err| {
+ var args = clap.parse(clap.Help, params_to_use, .{
+ .diagnostic = &diag,
+ .allocator = allocator,
+ .stop_after_positional_at = if (cmd == .RunCommand) 2 else if (cmd == .AutoCommand)
+ 1
+ else
+ 0,
+ }) catch |err| {
// Report useful error and exit
- clap.help(Output.errorWriter(), clap.Help, cmd.params(), .{}) catch {};
+ clap.help(Output.errorWriter(), params_to_use) catch {};
Output.errorWriter().writeAll("\n") catch {};
diag.report(Output.errorWriter(), err) catch {};
Global.exit(1);
};
- }
- // This implementation is separated into a function so that we can use try at
- // various key points knowing that the public parse fn will catch and handle
- // those errors for us.
- fn parseImpl(allocator: std.mem.Allocator, ctx: *Command.Context, comptime cmd: Command.Tag, diag: *clap.Diagnostic) !Api.TransformOptions {
- const params_to_use = comptime cmd.params();
-
- // We'll need to work around clap a bit to support this pattern:
- // bun run --bun-arg app.js --app-arg
- //
- // 1) Collect all args into a slice so that we can iterate them multiple times.
- // 2) Count positionals until we hit the second one (in the example above,
- // could vary based on command).
- // 3) Call clap.parse with only the slice up to and including that positional.
- // 4) All remaining args become passthroughs
- const full_args = try std.process.argsAlloc(allocator);
- //NOTE: we won't free full_args because we'll make slices available to
- // the running script and they need to remain available for the entire run
- const args = full_args[1..]; // skip exe path
-
- // 2: Find our parse stopping point
- const passthrough_after_nth_positional = comptime if (cmd == .RunCommand) 2
- else if (cmd == .AutoCommand) 1
- else 0;
- var it = clap.args.SliceIterator{ .args = args[0..] };
- var streaming_clap = clap.streaming.Clap(clap.Help, clap.args.SliceIterator){
- .params = params_to_use,
- .iter = &it,
- .diagnostic = diag,
- };
- var args_to_parse: usize = 0;
- if (comptime passthrough_after_nth_positional > 0) {
- var num_positionals_seen: usize = 0;
- while (try streaming_clap.next()) |arg| : (args_to_parse += 1) {
- if (arg.param.names.longest().kind == .positional) {
- num_positionals_seen += 1;
-
- if (num_positionals_seen == passthrough_after_nth_positional) {
- args_to_parse += 1;
- break;
- }
- }
- }
- } else
- args_to_parse = args.len;
-
- // 3: parse but only up to args_to_parse
- it = clap.args.SliceIterator{ .args = args[0..args_to_parse] };
- var res = try clap.parseEx(clap.Help, params_to_use, Arguments.parser, &it, .{
- .diagnostic = diag,
- .allocator = allocator,
- });
- //defer res.deinit();
-
-
- if (res.args.version) {
+ if (args.flag("--version")) {
printVersionAndExit();
}
var cwd: []u8 = undefined;
- if (res.args.cwd) |cwd_| {
+ if (args.option("--cwd")) |cwd_| {
cwd = brk: {
var outbuf: [bun.MAX_PATH_BYTES]u8 = undefined;
const out = std.os.realpath(cwd_, &outbuf) catch |err| {
@@ -418,17 +369,15 @@ pub const Arguments = struct {
}
ctx.args.absolute_working_dir = cwd;
- ctx.positionals = res.positionals;
- // All remaining args should be passed through
- ctx.passthrough = args[args_to_parse..];
+ ctx.positionals = args.positionals();
if (comptime Command.Tag.loads_config.get(cmd)) {
- try loadConfig(allocator, res.args.config, ctx, cmd);
+ try loadConfigWithCmdArgs(cmd, allocator, args, ctx);
}
var opts: Api.TransformOptions = ctx.args;
- var defines_tuple = try DefineColonList.resolve(allocator, res.args.define);
+ var defines_tuple = try DefineColonList.resolve(allocator, args.options("--define"));
if (defines_tuple.keys.len > 0) {
opts.define = .{
@@ -437,7 +386,7 @@ pub const Arguments = struct {
};
}
- var loader_tuple = try LoaderColonList.resolve(allocator, res.args.loader);
+ var loader_tuple = try LoaderColonList.resolve(allocator, args.options("--loader"));
if (loader_tuple.keys.len > 0) {
opts.loaders = .{
@@ -446,39 +395,40 @@ pub const Arguments = struct {
};
}
- if (res.args.external.len > 0) {
- var externals = try allocator.alloc([]u8, res.args.external.len);
- for (res.args.external, 0..) |external, i| {
+ if (args.options("--external").len > 0) {
+ var externals = try allocator.alloc([]u8, args.options("--external").len);
+ for (args.options("--external"), 0..) |external, i| {
externals[i] = constStrToU8(external);
}
opts.external = externals;
}
- opts.tsconfig_override = if (res.args.@"tsconfig-override") |ts|
+ opts.tsconfig_override = if (args.option("--tsconfig-override")) |ts|
(Arguments.readFile(allocator, cwd, ts) catch |err| fileReadError(err, Output.errorStream(), ts, "tsconfig.json"))
else
null;
- if (res.args.origin) |origin| {
+ if (args.option("--origin")) |origin| {
opts.origin = origin;
}
- if (res.args.port) |port_str| {
+ if (args.option("--port")) |port_str| {
opts.port = std.fmt.parseInt(u16, port_str, 10) catch return error.InvalidPort;
}
opts.serve = cmd == .DevCommand;
- opts.main_fields = res.args.@"main-fields";
+ opts.main_fields = args.options("--main-fields");
opts.generate_node_module_bundle = cmd == .BunCommand;
// we never actually supported inject.
- // opts.inject = res.args.inject;
- opts.extension_order = res.args.@"extension-order";
- ctx.debug.hot_reload = res.args.hot;
+ // opts.inject = args.options("--inject");
+ opts.extension_order = args.options("--extension-order");
+ ctx.debug.hot_reload = args.flag("--hot");
+ ctx.passthrough = args.remaining();
- opts.no_summary = res.args.@"no-summary";
- opts.disable_hmr = res.args.@"disable-hmr";
+ opts.no_summary = args.flag("--no-summary");
+ opts.disable_hmr = args.flag("--disable-hmr");
if (cmd != .DevCommand) {
- const preloads = res.args.preload;
+ const preloads = args.options("--preload");
if (ctx.preloads.len > 0 and preloads.len > 0) {
var all = std.ArrayList(string).initCapacity(ctx.allocator, ctx.preloads.len + preloads.len) catch unreachable;
all.appendSliceAssumeCapacity(ctx.preloads);
@@ -489,36 +439,37 @@ pub const Arguments = struct {
}
}
- ctx.debug.silent = res.args.@"silent";
+ ctx.debug.silent = args.flag("--silent");
if (opts.port != null and opts.origin == null) {
opts.origin = try std.fmt.allocPrint(allocator, "http://localhost:{d}/", .{opts.port.?});
}
- if (res.args.help) {
+ const print_help = args.flag("--help");
+ if (print_help) {
const params_len = if (cmd == .BuildCommand) build_params_public.len else public_params.len;
- clap.help(Output.writer(), clap.Help, params_to_use[0..params_len], .{}) catch {};
+ clap.help(Output.writer(), params_to_use[0..params_len]) catch {};
Output.prettyln("\n-------\n\n", .{});
Output.flush();
HelpCommand.printWithReason(.explicit);
Global.exit(0);
}
- ctx.debug.dump_environment_variables = res.args.@"dump-environment-variables";
- ctx.debug.fallback_only = ctx.debug.fallback_only or res.args.@"disable-bun.js";
- ctx.debug.dump_limits = res.args.@"dump-limits";
+ ctx.debug.dump_environment_variables = args.flag("--dump-environment-variables");
+ ctx.debug.fallback_only = ctx.debug.fallback_only or args.flag("--disable-bun.js");
+ ctx.debug.dump_limits = args.flag("--dump-limits");
- ctx.debug.offline_mode_setting = if (res.args.@"prefer-offline")
+ ctx.debug.offline_mode_setting = if (args.flag("--prefer-offline"))
Bunfig.OfflineMode.offline
- else if (res.args.@"prefer-latest")
+ else if (args.flag("--prefer-latest"))
Bunfig.OfflineMode.latest
else
Bunfig.OfflineMode.online;
- if (res.args.@"no-install") {
+ if (args.flag("--no-install")) {
ctx.debug.global_cache = .disable;
- } else if (res.args.@"install-fallback") {
+ } else if (args.flag("-i")) {
ctx.debug.global_cache = .fallback;
- } else if (res.args.install) |enum_value| {
+ } else if (args.option("--install")) |enum_value| {
// -i=auto --install=force, --install=disable
if (options.GlobalCache.Map.get(enum_value)) |result| {
ctx.debug.global_cache = result;
@@ -531,18 +482,18 @@ pub const Arguments = struct {
}
}
- // var output_dir = res.args.outdir;
+ // var output_dir = args.option("--outdir");
var output_dir: ?string = null;
const production = false;
- if (comptime cmd == .BuildCommand) {
- if (res.args.outdir) |outdir| {
+ if (cmd == .BuildCommand) {
+ if (args.option("--outdir")) |outdir| {
if (outdir.len > 0) {
output_dir = outdir;
}
}
- if (res.args.sourcemap) |setting| {
+ if (args.option("--sourcemap")) |setting| {
if (setting.len == 0 or strings.eqlComptime(setting, "inline")) {
opts.source_map = Api.SourceMapMode.inline_into_file;
} else if (strings.eqlComptime(setting, "none")) {
@@ -614,24 +565,24 @@ pub const Arguments = struct {
opts.entry_points = entry_points;
}
- var jsx_factory = res.args.@"jsx-factory";
- var jsx_fragment = res.args.@"jsx-fragment";
- var jsx_import_source = res.args.@"jsx-import-source";
- var jsx_runtime = res.args.@"jsx-runtime";
- var jsx_production = res.args.@"jsx-production";
+ var jsx_factory = args.option("--jsx-factory");
+ var jsx_fragment = args.option("--jsx-fragment");
+ var jsx_import_source = args.option("--jsx-import-source");
+ var jsx_runtime = args.option("--jsx-runtime");
+ var jsx_production = args.flag("--jsx-production");
const react_fast_refresh = switch (comptime cmd) {
- .BunCommand, .DevCommand => !(res.args.@"disable-react-fast-refresh" or jsx_production),
+ .BunCommand, .DevCommand => !(args.flag("--disable-react-fast-refresh") or jsx_production),
else => true,
};
if (comptime Command.Tag.cares_about_bun_file.get(cmd)) {
- opts.node_modules_bundle_path = res.args.bunfile orelse opts.node_modules_bundle_path orelse brk: {
+ opts.node_modules_bundle_path = args.option("--bunfile") orelse opts.node_modules_bundle_path orelse brk: {
const node_modules_bundle_path_absolute = resolve_path.joinAbs(cwd, .auto, "node_modules.bun");
break :brk std.fs.realpathAlloc(allocator, node_modules_bundle_path_absolute) catch null;
};
- opts.node_modules_bundle_path_server = res.args.@"server-bunfile" orelse opts.node_modules_bundle_path_server orelse brk: {
+ opts.node_modules_bundle_path_server = args.option("--server-bunfile") orelse opts.node_modules_bundle_path_server orelse brk: {
const node_modules_bundle_path_absolute = resolve_path.joinAbs(cwd, .auto, "node_modules.server.bun");
break :brk std.fs.realpathAlloc(allocator, node_modules_bundle_path_absolute) catch null;
@@ -640,7 +591,7 @@ pub const Arguments = struct {
switch (comptime cmd) {
.AutoCommand, .DevCommand, .BuildCommand, .BunCommand => {
- if (res.args.@"public-dir") |public_dir| {
+ if (args.option("--public-dir")) |public_dir| {
if (public_dir.len > 0) {
opts.router = Api.RouteConfig{ .extensions = &.{}, .dir = &.{}, .static_dir = public_dir };
}
@@ -655,7 +606,7 @@ pub const Arguments = struct {
switch (comptime cmd) {
.BuildCommand => {
- // if (res.args.resolve) |_resolve| {
+ // if (args.option("--resolve")) |_resolve| {
// switch (ResolveMatcher.match(_resolve)) {
// ResolveMatcher.case("disable") => {
// opts.resolve = Api.ResolveMode.disable;
@@ -683,19 +634,19 @@ pub const Arguments = struct {
const PlatformMatcher = strings.ExactSizeMatcher(8);
- if (res.args.platform) |_platform| {
+ if (args.option("--platform")) |_platform| {
opts.platform = opts.platform orelse switch (PlatformMatcher.match(_platform)) {
PlatformMatcher.case("browser") => Api.Platform.browser,
PlatformMatcher.case("node") => Api.Platform.node,
PlatformMatcher.case("macro") => if (cmd == .BuildCommand) Api.Platform.bun_macro else Api.Platform.bun,
PlatformMatcher.case("bun") => Api.Platform.bun,
- else => invalidPlatform(diag, _platform),
+ else => invalidPlatform(&diag, _platform),
};
ctx.debug.run_in_bun = opts.platform.? == .bun;
}
- ctx.debug.run_in_bun = res.args.bun or ctx.debug.run_in_bun;
+ ctx.debug.run_in_bun = args.flag("--bun") or ctx.debug.run_in_bun;
if (jsx_factory != null or
jsx_fragment != null or
@@ -727,7 +678,7 @@ pub const Arguments = struct {
}
}
- if (res.args.use) |entry| {
+ if (args.option("--use")) |entry| {
opts.framework = Api.FrameworkConfig{
.package = entry,
.development = !production,
@@ -868,17 +819,16 @@ pub const PrintBundleCommand = struct {
var stdout = std.io.getStdOut();
var input = try std.fs.openFileAbsolute(try std.os.realpath(entry_point, &out_buffer), .{ .mode = .read_only });
- const params = comptime clap.parseParamsComptime(
- \\--summary Peek inside the .bun"
- );
+ const params = comptime [_]Arguments.ParamType{
+ clap.parseParam("--summary Peek inside the .bun") catch unreachable,
+ };
- var jsBundleArgs = clap.parse(clap.Help, &params, Arguments.parser, .{ .allocator = ctx.allocator }) catch {
+ var jsBundleArgs = clap.parse(clap.Help, &params, .{ .allocator = ctx.allocator }) catch {
try NodeModuleBundle.printBundle(std.fs.File, input, @TypeOf(stdout), stdout);
return;
};
- defer jsBundleArgs.deinit();
- if (jsBundleArgs.args.summary) {
+ if (jsBundleArgs.flag("--summary")) {
NodeModuleBundle.printSummaryFromDisk(std.fs.File, input, @TypeOf(stdout), stdout, ctx.allocator) catch {};
return;
}
diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig
index c21ccf21d..0c76a9c01 100644
--- a/src/cli/create_command.zig
+++ b/src/cli/create_command.zig
@@ -25,7 +25,6 @@ const Api = @import("../api/schema.zig").Api;
const resolve_path = @import("../resolver/resolve_path.zig");
const configureTransformOptionsForBun = @import("../bun.js/config.zig").configureTransformOptionsForBun;
const Command = @import("../cli.zig").Command;
-const BunArguments = @import("../cli.zig").Arguments;
const bundler = bun.bundler;
const NodeModuleBundle = @import("../node_module_bundle.zig").NodeModuleBundle;
const fs = @import("../fs.zig");
@@ -188,34 +187,35 @@ const CreateOptions = struct {
verbose: bool = false,
open: bool = false,
- const params = clap.parseParamsComptime(
- \\--help Print this menu
- \\--force Overwrite existing files
- \\--no-install Don't install node_modules
- \\--no-git Don't create a git repository
- \\--verbose Too many logs
- \\--no-package-json Disable package.json transforms
- \\--open On finish, start bun & open in-browser
- \\<POS>...
- );
+ const params = [_]clap.Param(clap.Help){
+ clap.parseParam("--help Print this menu") catch unreachable,
+ clap.parseParam("--force Overwrite existing files") catch unreachable,
+ clap.parseParam("--no-install Don't install node_modules") catch unreachable,
+ clap.parseParam("--no-git Don't create a git repository") catch unreachable,
+ clap.parseParam("--verbose Too many logs") catch unreachable,
+ clap.parseParam("--no-package-json Disable package.json transforms") catch unreachable,
+ clap.parseParam("--open On finish, start bun & open in-browser") catch unreachable,
+ clap.parseParam("<POS>... ") catch unreachable,
+ };
pub fn parse(ctx: Command.Context, comptime print_flags_only: bool) !CreateOptions {
var diag = clap.Diagnostic{};
- var res = clap.parse(clap.Help, &params, BunArguments.parser, .{ .diagnostic = &diag, .allocator = ctx.allocator }) catch |err| {
+
+ var args = clap.parse(clap.Help, &params, .{ .diagnostic = &diag, .allocator = ctx.allocator }) catch |err| {
// Report useful error and exit
diag.report(Output.errorWriter(), err) catch {};
return err;
};
- if (res.args.help or comptime print_flags_only) {
+ if (args.flag("--help") or comptime print_flags_only) {
if (comptime print_flags_only) {
- clap.help(Output.writer(), clap.Help, params[1..], .{}) catch {};
+ clap.help(Output.writer(), params[1..]) catch {};
return undefined;
}
Output.prettyln("<r><b>bun create<r>\n\n flags:\n", .{});
Output.flush();
- clap.help(Output.writer(), clap.Help, params[1..], .{}) catch {};
+ clap.help(Output.writer(), params[1..]) catch {};
Output.pretty("\n", .{});
Output.prettyln("<r> environment variables:\n\n", .{});
Output.prettyln(" GITHUB_ACCESS_TOKEN<r> Downloading code from GitHub with a higher rate limit", .{});
@@ -226,19 +226,19 @@ const CreateOptions = struct {
Global.exit(0);
}
- var opts = CreateOptions{ .positionals = res.positionals };
+ var opts = CreateOptions{ .positionals = args.positionals() };
if (opts.positionals.len >= 1 and (strings.eqlComptime(opts.positionals[0], "c") or strings.eqlComptime(opts.positionals[0], "create"))) {
opts.positionals = opts.positionals[1..];
}
- opts.skip_package_json = res.args.@"no-package-json";
+ opts.skip_package_json = args.flag("--no-package-json");
- opts.verbose = res.args.verbose;
- opts.open = res.args.open;
- opts.skip_install = res.args.@"no-install";
- opts.skip_git = res.args.@"no-git";
- opts.overwrite = res.args.force;
+ opts.verbose = args.flag("--verbose");
+ opts.open = args.flag("--open");
+ opts.skip_install = args.flag("--no-install");
+ opts.skip_git = args.flag("--no-git");
+ opts.overwrite = args.flag("--force");
return opts;
}
diff --git a/src/deps/zig-clap b/src/deps/zig-clap
deleted file mode 160000
-Subproject 9c3ac846121a03934c7460cc54989059b1f66b2
diff --git a/src/deps/zig-clap/.gitignore b/src/deps/zig-clap/.gitignore
new file mode 100644
index 000000000..2040c29db
--- /dev/null
+++ b/src/deps/zig-clap/.gitignore
@@ -0,0 +1 @@
+zig-cache
diff --git a/src/deps/zig-clap/LICENSE b/src/deps/zig-clap/LICENSE
new file mode 100644
index 000000000..cf1ab25da
--- /dev/null
+++ b/src/deps/zig-clap/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/src/deps/zig-clap/build.zig b/src/deps/zig-clap/build.zig
new file mode 100644
index 000000000..5ab66da8a
--- /dev/null
+++ b/src/deps/zig-clap/build.zig
@@ -0,0 +1,55 @@
+const builtin = @import("builtin");
+const std = @import("std");
+
+const Builder = std.build.Builder;
+const Mode = std.builtin.Mode;
+
+pub fn build(b: *Builder) void {
+ const mode = b.standardReleaseOptions();
+ const target = b.standardTargetOptions(.{});
+
+ const test_all_step = b.step("test", "Run all tests in all modes.");
+ inline for ([_]Mode{ Mode.Debug, Mode.ReleaseFast, Mode.ReleaseSafe, Mode.ReleaseSmall }) |test_mode| {
+ const mode_str = comptime modeToString(test_mode);
+
+ const tests = b.addTest("clap.zig");
+ tests.setBuildMode(test_mode);
+ tests.setTarget(target);
+ tests.setNamePrefix(mode_str ++ " ");
+
+ const test_step = b.step("test-" ++ mode_str, "Run all tests in " ++ mode_str ++ ".");
+ test_step.dependOn(&tests.step);
+ test_all_step.dependOn(test_step);
+ }
+
+ const example_step = b.step("examples", "Build examples");
+ inline for ([_][]const u8{
+ "simple",
+ "simple-ex",
+ //"simple-error",
+ "streaming-clap",
+ "help",
+ "usage",
+ }) |example_name| {
+ const example = b.addExecutable(example_name, "example/" ++ example_name ++ ".zig");
+ example.addPackagePath("clap", "clap.zig");
+ example.setBuildMode(mode);
+ example.setTarget(target);
+ example.install();
+ example_step.dependOn(&example.step);
+ }
+
+ const all_step = b.step("all", "Build everything and runs all tests");
+ all_step.dependOn(test_all_step);
+
+ b.default_step.dependOn(all_step);
+}
+
+fn modeToString(mode: Mode) []const u8 {
+ return switch (mode) {
+ Mode.Debug => "debug",
+ Mode.ReleaseFast => "release-fast",
+ Mode.ReleaseSafe => "release-safe",
+ Mode.ReleaseSmall => "release-small",
+ };
+}
diff --git a/src/deps/zig-clap/clap.zig b/src/deps/zig-clap/clap.zig
new file mode 100644
index 000000000..a21a1cb1a
--- /dev/null
+++ b/src/deps/zig-clap/clap.zig
@@ -0,0 +1,546 @@
+const std = @import("std");
+
+const debug = std.debug;
+const heap = std.heap;
+const io = std.io;
+const mem = std.mem;
+const testing = std.testing;
+
+pub const args = @import("clap/args.zig");
+
+test "clap" {
+ testing.refAllDecls(@This());
+}
+
+pub const ComptimeClap = @import("clap/comptime.zig").ComptimeClap;
+pub const StreamingClap = @import("clap/streaming.zig").StreamingClap;
+
+/// The names a ::Param can have.
+pub const Names = struct {
+ /// '-' prefix
+ short: ?u8 = null,
+
+ /// '--' prefix
+ long: ?[]const u8 = null,
+};
+
+/// Whether a param takes no value (a flag), one value, or can be specified multiple times.
+pub const Values = enum {
+ none,
+ one,
+ many,
+ one_optional,
+};
+
+/// Represents a parameter for the command line.
+/// Parameters come in three kinds:
+/// * Short ("-a"): Should be used for the most commonly used parameters in your program.
+/// * They can take a value three different ways.
+/// * "-a value"
+/// * "-a=value"
+/// * "-avalue"
+/// * They chain if they don't take values: "-abc".
+/// * The last given parameter can take a value in the same way that a single parameter can:
+/// * "-abc value"
+/// * "-abc=value"
+/// * "-abcvalue"
+/// * Long ("--long-param"): Should be used for less common parameters, or when no single character
+/// can describe the paramter.
+/// * They can take a value two different ways.
+/// * "--long-param value"
+/// * "--long-param=value"
+/// * Positional: Should be used as the primary parameter of the program, like a filename or
+/// an expression to parse.
+/// * Positional parameters have both names.long and names.short == null.
+/// * Positional parameters must take a value.
+pub fn Param(comptime Id: type) type {
+ return struct {
+ id: Id = std.mem.zeroes(Id),
+ names: Names = std.mem.zeroes(Names),
+ takes_value: Values = .none,
+ };
+}
+
+/// Takes a string and parses it to a Param(Help).
+/// This is the reverse of 'help' but for at single parameter only.
+pub fn parseParam(line: []const u8) !Param(Help) {
+ @setEvalBranchQuota(999999);
+
+ var found_comma = false;
+ var it = mem.tokenize(u8, line, " \t");
+ var param_str = it.next() orelse return error.NoParamFound;
+
+ const short_name = if (!mem.startsWith(u8, param_str, "--") and
+ mem.startsWith(u8, param_str, "-"))
+ blk: {
+ found_comma = param_str[param_str.len - 1] == ',';
+ if (found_comma)
+ param_str = param_str[0 .. param_str.len - 1];
+
+ if (param_str.len != 2)
+ return error.InvalidShortParam;
+
+ const short_name = param_str[1];
+ if (!found_comma) {
+ var res = parseParamRest(it.rest());
+ res.names.short = short_name;
+ return res;
+ }
+
+ param_str = it.next() orelse return error.NoParamFound;
+ break :blk short_name;
+ } else null;
+
+ _ = if (mem.startsWith(u8, param_str, "--")) {
+ if (param_str[param_str.len - 1] == ',')
+ return error.TrailingComma;
+ } else if (found_comma) {
+ return error.TrailingComma;
+ } else if (short_name == null) {
+ return parseParamRest(mem.trimLeft(u8, line, " \t"));
+ } else null;
+
+ var res = parseParamRest(it.rest());
+ res.names.long = param_str[2..];
+ res.names.short = short_name;
+ return res;
+}
+
+fn parseParamRest(line: []const u8) Param(Help) {
+ if (mem.startsWith(u8, line, "<")) blk: {
+ const len = mem.indexOfScalar(u8, line, '>') orelse break :blk;
+ const takes_many = mem.startsWith(u8, line[len + 1 ..], "...");
+ const takes_one_optional = mem.startsWith(u8, line[len + 1 ..], "?");
+ const help_start = len + 1 + @as(usize, 3) * @boolToInt(takes_many) + (@as(usize, 1) * @boolToInt(takes_one_optional));
+ return .{
+ .takes_value = if (takes_many) Values.many else if (takes_one_optional) Values.one_optional else Values.one,
+ .id = .{
+ .msg = mem.trim(u8, line[help_start..], " \t"),
+ .value = line[1..len],
+ },
+ };
+ }
+
+ return .{ .id = .{ .msg = mem.trim(u8, line, " \t") } };
+}
+
+fn expectParam(expect: Param(Help), actual: Param(Help)) void {
+ testing.expectEqualStrings(expect.id.msg, actual.id.msg);
+ testing.expectEqualStrings(expect.id.value, actual.id.value);
+ testing.expectEqual(expect.names.short, actual.names.short);
+ testing.expectEqual(expect.takes_value, actual.takes_value);
+ if (expect.names.long) |long| {
+ testing.expectEqualStrings(long, actual.names.long.?);
+ } else {
+ testing.expectEqual(@as(?[]const u8, null), actual.names.long);
+ }
+}
+
+test "parseParam" {
+ expectParam(Param(Help){
+ .id = .{ .msg = "Help text", .value = "value" },
+ .names = .{ .short = 's', .long = "long" },
+ .takes_value = .one,
+ }, try parseParam("-s, --long <value> Help text"));
+
+ expectParam(Param(Help){
+ .id = .{ .msg = "Help text", .value = "value" },
+ .names = .{ .short = 's', .long = "long" },
+ .takes_value = .many,
+ }, try parseParam("-s, --long <value>... Help text"));
+
+ expectParam(Param(Help){
+ .id = .{ .msg = "Help text", .value = "value" },
+ .names = .{ .long = "long" },
+ .takes_value = .one,
+ }, try parseParam("--long <value> Help text"));
+
+ expectParam(Param(Help){
+ .id = .{ .msg = "Help text", .value = "value" },
+ .names = .{ .short = 's' },
+ .takes_value = .one,
+ }, try parseParam("-s <value> Help text"));
+
+ expectParam(Param(Help){
+ .id = .{ .msg = "Help text" },
+ .names = .{ .short = 's', .long = "long" },
+ }, try parseParam("-s, --long Help text"));
+
+ expectParam(Param(Help){
+ .id = .{ .msg = "Help text" },
+ .names = .{ .short = 's' },
+ }, try parseParam("-s Help text"));
+
+ expectParam(Param(Help){
+ .id = .{ .msg = "Help text" },
+ .names = .{ .long = "long" },
+ }, try parseParam("--long Help text"));
+
+ expectParam(Param(Help){
+ .id = .{ .msg = "Help text", .value = "A | B" },
+ .names = .{ .long = "long" },
+ .takes_value = .one,
+ }, try parseParam("--long <A | B> Help text"));
+
+ expectParam(Param(Help){
+ .id = .{ .msg = "Help text", .value = "A" },
+ .names = .{},
+ .takes_value = .one,
+ }, try parseParam("<A> Help text"));
+
+ expectParam(Param(Help){
+ .id = .{ .msg = "Help text", .value = "A" },
+ .names = .{},
+ .takes_value = .many,
+ }, try parseParam("<A>... Help text"));
+
+ testing.expectError(error.TrailingComma, parseParam("--long, Help"));
+ testing.expectError(error.TrailingComma, parseParam("-s, Help"));
+ testing.expectError(error.InvalidShortParam, parseParam("-ss Help"));
+ testing.expectError(error.InvalidShortParam, parseParam("-ss <value> Help"));
+ testing.expectError(error.InvalidShortParam, parseParam("- Help"));
+}
+
+/// Optional diagnostics used for reporting useful errors
+pub const Diagnostic = struct {
+ arg: []const u8 = "",
+ name: Names = Names{},
+
+ /// Default diagnostics reporter when all you want is English with no colors.
+ /// Use this as a reference for implementing your own if needed.
+ pub fn report(diag: Diagnostic, stream: anytype, err: anyerror) !void {
+ const Arg = struct {
+ prefix: []const u8,
+ name: []const u8,
+ };
+ const a = if (diag.name.short) |*c|
+ Arg{ .prefix = "-", .name = @as(*const [1]u8, c)[0..] }
+ else if (diag.name.long) |l|
+ Arg{ .prefix = "--", .name = l }
+ else
+ Arg{ .prefix = "", .name = diag.arg };
+
+ switch (err) {
+ error.DoesntTakeValue => try stream.print("The argument '{s}{s}' does not take a value\n", .{ a.prefix, a.name }),
+ error.MissingValue => try stream.print("The argument '{s}{s}' requires a value but none was supplied\n", .{ a.prefix, a.name }),
+ error.InvalidArgument => if (a.prefix.len > 0 and a.name.len > 0)
+ try stream.print("Invalid argument '{s}{s}'\n", .{ a.prefix, a.name })
+ else
+ try stream.print("Failed to parse argument due to unexpected single dash\n", .{}),
+ else => try stream.print("Error while parsing arguments: {s}\n", .{@errorName(err)}),
+ }
+ }
+};
+
+fn testDiag(diag: Diagnostic, err: anyerror, expected: []const u8) void {
+ var buf: [1024]u8 = undefined;
+ var slice_stream = io.fixedBufferStream(&buf);
+ diag.report(slice_stream.writer(), err) catch unreachable;
+ testing.expectEqualStrings(expected, slice_stream.getWritten());
+}
+
+pub fn Args(comptime Id: type, comptime params: []const Param(Id)) type {
+ return struct {
+ arena: std.heap.ArenaAllocator,
+ clap: ComptimeClap(Id, params),
+ exe_arg: ?[]const u8,
+
+ pub fn deinit(a: *@This()) void {
+ a.arena.deinit();
+ }
+
+ pub fn flag(a: @This(), comptime name: []const u8) bool {
+ return a.clap.flag(name);
+ }
+
+ pub fn option(a: @This(), comptime name: []const u8) ?[]const u8 {
+ return a.clap.option(name);
+ }
+
+ pub fn options(a: @This(), comptime name: []const u8) []const []const u8 {
+ return a.clap.options(name);
+ }
+
+ pub fn positionals(a: @This()) []const []const u8 {
+ return a.clap.positionals();
+ }
+
+ pub fn remaining(a: @This()) []const []const u8 {
+ return a.clap.remaining();
+ }
+
+ pub fn hasFlag(comptime name: []const u8) bool {
+ return ComptimeClap(Id, params).hasFlag(name);
+ }
+ };
+}
+
+/// Options that can be set to customize the behavior of parsing.
+pub const ParseOptions = struct {
+ /// The allocator used for all memory allocations. Defaults to the `heap.page_allocator`.
+ /// Note: You should probably override this allocator if you are calling `parseEx`. Unlike
+ /// `parse`, `parseEx` does not wrap the allocator so the heap allocator can be
+ /// quite expensive. (TODO: Can we pick a better default? For `parse`, this allocator
+ /// is fine, as it wraps it in an arena)
+ allocator: mem.Allocator = heap.page_allocator,
+ diagnostic: ?*Diagnostic = null,
+ stop_after_positional_at: usize = 0,
+};
+
+/// Same as `parseEx` but uses the `args.OsIterator` by default.
+pub fn parse(
+ comptime Id: type,
+ comptime params: []const Param(Id),
+ opt: ParseOptions,
+) !Args(Id, params) {
+ var iter = args.OsIterator.init(opt.allocator);
+ var res = Args(Id, params){
+ .arena = iter.arena,
+ .exe_arg = iter.exe_arg,
+ .clap = undefined,
+ };
+
+ // Let's reuse the arena from the `OSIterator` since we already have
+ // it.
+ res.clap = try parseEx(Id, params, &iter, .{
+ .allocator = res.arena.allocator(),
+ .diagnostic = opt.diagnostic,
+ .stop_after_positional_at = opt.stop_after_positional_at,
+ });
+ return res;
+}
+
+/// Parses the command line arguments passed into the program based on an
+/// array of `Param`s.
+pub fn parseEx(
+ comptime Id: type,
+ comptime params: []const Param(Id),
+ iter: anytype,
+ opt: ParseOptions,
+) !ComptimeClap(Id, params) {
+ const Clap = ComptimeClap(Id, params);
+ return try Clap.parse(iter, opt);
+}
+
+/// Will print a help message in the following format:
+/// -s, --long <valueText> helpText
+/// -s, helpText
+/// -s <valueText> helpText
+/// --long helpText
+/// --long <valueText> helpText
+pub fn helpFull(
+ stream: anytype,
+ comptime Id: type,
+ params: []const Param(Id),
+ comptime Error: type,
+ context: anytype,
+ helpText: fn (@TypeOf(context), Param(Id)) Error![]const u8,
+ valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8,
+) !void {
+ const max_spacing = blk: {
+ var res: usize = 0;
+ for (params) |param| {
+ var cs = io.countingWriter(io.null_writer);
+ try printParam(cs.writer(), Id, param, Error, context, valueText);
+ if (res < cs.bytes_written)
+ res = @intCast(usize, cs.bytes_written);
+ }
+
+ break :blk res;
+ };
+
+ for (params) |param| {
+ if (param.names.short == null and param.names.long == null)
+ continue;
+
+ var cs = io.countingWriter(stream);
+ try stream.print("\t", .{});
+ try printParam(cs.writer(), Id, param, Error, context, valueText);
+ try stream.writeByteNTimes(' ', max_spacing - @intCast(usize, cs.bytes_written));
+ try stream.print("\t{s}\n", .{try helpText(context, param)});
+ }
+}
+
+fn printParam(
+ stream: anytype,
+ comptime Id: type,
+ param: Param(Id),
+ comptime Error: type,
+ context: anytype,
+ valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8,
+) !void {
+ if (param.names.short) |s| {
+ try stream.print("-{c}", .{s});
+ } else {
+ try stream.print(" ", .{});
+ }
+ if (param.names.long) |l| {
+ if (param.names.short) |_| {
+ try stream.print(", ", .{});
+ } else {
+ try stream.print(" ", .{});
+ }
+
+ try stream.print("--{s}", .{l});
+ }
+
+ switch (param.takes_value) {
+ .none => {},
+ .one => try stream.print(" <{s}>", .{try valueText(context, param)}),
+ .one_optional => try stream.print(" <{s}>?", .{try valueText(context, param)}),
+ .many => try stream.print(" <{s}>...", .{try valueText(context, param)}),
+ }
+}
+
+/// A wrapper around helpFull for simple helpText and valueText functions that
+/// cant return an error or take a context.
+pub fn helpEx(
+ stream: anytype,
+ comptime Id: type,
+ params: []const Param(Id),
+ helpText: *const fn (Param(Id)) []const u8,
+ valueText: *const fn (Param(Id)) []const u8,
+) !void {
+ const Context = struct {
+ helpText: *const fn (Param(Id)) []const u8,
+ valueText: *const fn (Param(Id)) []const u8,
+
+ pub fn help(c: @This(), p: Param(Id)) error{}![]const u8 {
+ return c.helpText(p);
+ }
+
+ pub fn value(c: @This(), p: Param(Id)) error{}![]const u8 {
+ return c.valueText(p);
+ }
+ };
+
+ return helpFull(
+ stream,
+ Id,
+ params,
+ error{},
+ Context{
+ .helpText = helpText,
+ .valueText = valueText,
+ },
+ Context.help,
+ Context.value,
+ );
+}
+
+pub const Help = struct {
+ msg: []const u8 = "",
+ value: []const u8 = "",
+};
+
+/// A wrapper around helpEx that takes a Param(Help).
+pub fn help(stream: anytype, params: []const Param(Help)) !void {
+ try helpEx(stream, Help, params, getHelpSimple, getValueSimple);
+}
+
+fn getHelpSimple(param: Param(Help)) []const u8 {
+ return param.id.msg;
+}
+
+fn getValueSimple(param: Param(Help)) []const u8 {
+ return param.id.value;
+}
+
+/// Will print a usage message in the following format:
+/// [-abc] [--longa] [-d <valueText>] [--longb <valueText>] <valueText>
+///
+/// First all none value taking parameters, which have a short name are
+/// printed, then non positional parameters and finally the positinal.
+pub fn usageFull(
+ stream: anytype,
+ comptime Id: type,
+ params: []const Param(Id),
+ comptime Error: type,
+ context: anytype,
+ valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8,
+) !void {
+ var cos = io.countingWriter(stream);
+ const cs = cos.writer();
+ for (params) |param| {
+ const name = param.names.short orelse continue;
+ if (param.takes_value != .none)
+ continue;
+
+ if (cos.bytes_written == 0)
+ try stream.writeAll("[-");
+ try cs.writeByte(name);
+ }
+ if (cos.bytes_written != 0)
+ try cs.writeByte(']');
+
+ var positional: ?Param(Id) = null;
+ for (params) |param| {
+ if (param.takes_value == .none and param.names.short != null)
+ continue;
+
+ const prefix = if (param.names.short) |_| "-" else "--";
+
+ // Seems the zig compiler is being a little wierd. I doesn't allow me to write
+ // @as(*const [1]u8, s) VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ const name = if (param.names.short) |*s| @ptrCast([*]const u8, s)[0..1] else param.names.long orelse {
+ positional = param;
+ continue;
+ };
+ if (cos.bytes_written != 0)
+ try cs.writeByte(' ');
+
+ try cs.print("[{s}{s}", .{ prefix, name });
+ switch (param.takes_value) {
+ .none => {},
+ .one => try cs.print(" <{s}>", .{try valueText(context, param)}),
+ .one_optional => try cs.print(" <{s}>?", .{try valueText(context, param)}),
+ .many => try cs.print(" <{s}>...", .{try valueText(context, param)}),
+ }
+
+ try cs.writeByte(']');
+ }
+
+ if (positional) |p| {
+ if (cos.bytes_written != 0)
+ try cs.writeByte(' ');
+ try cs.print("<{s}>", .{try valueText(context, p)});
+ }
+}
+
+/// A wrapper around usageFull for a simple valueText functions that
+/// cant return an error or take a context.
+pub fn usageEx(
+ stream: anytype,
+ comptime Id: type,
+ params: []const Param(Id),
+ valueText: fn (Param(Id)) []const u8,
+) !void {
+ const Context = struct {
+ valueText: fn (Param(Id)) []const u8,
+
+ pub fn value(c: @This(), p: Param(Id)) error{}![]const u8 {
+ return c.valueText(p);
+ }
+ };
+
+ return usageFull(
+ stream,
+ Id,
+ params,
+ error{},
+ Context{ .valueText = valueText },
+ Context.value,
+ );
+}
+
+/// A wrapper around usageEx that takes a Param(Help).
+pub fn usage(stream: anytype, params: []const Param(Help)) !void {
+ try usageEx(stream, Help, params, getValueSimple);
+}
+
+fn testUsage(expected: []const u8, params: []const Param(Help)) !void {
+ var buf: [1024]u8 = undefined;
+ var fbs = io.fixedBufferStream(&buf);
+ try usage(fbs.writer(), params);
+ testing.expectEqualStrings(expected, fbs.getWritten());
+}
diff --git a/src/deps/zig-clap/clap/args.zig b/src/deps/zig-clap/clap/args.zig
new file mode 100644
index 000000000..a1fa3773a
--- /dev/null
+++ b/src/deps/zig-clap/clap/args.zig
@@ -0,0 +1,337 @@
+const std = @import("std");
+
+const builtin = @import("builtin");
+const debug = std.debug;
+const heap = std.heap;
+const mem = std.mem;
+const process = std.process;
+const testing = std.testing;
+
+/// An example of what methods should be implemented on an arg iterator.
+pub const ExampleArgIterator = struct {
+ const Error = error{};
+
+ pub fn next(_: *ExampleArgIterator) Error!?[]const u8 {
+ return "2";
+ }
+};
+
+/// An argument iterator which iterates over a slice of arguments.
+/// This implementation does not allocate.
+pub const SliceIterator = struct {
+ const Error = error{};
+
+ args: []const []const u8,
+ index: usize = 0,
+
+ pub fn next(iter: *SliceIterator) Error!?[]const u8 {
+ if (iter.args.len <= iter.index)
+ return null;
+
+ defer iter.index += 1;
+ return iter.args[iter.index];
+ }
+};
+
+test "SliceIterator" {
+ const args = &[_][]const u8{ "A", "BB", "CCC" };
+ var iter = SliceIterator{ .args = args };
+
+ for (args) |a| {
+ const b = try iter.next();
+ debug.assert(mem.eql(u8, a, b.?));
+ }
+}
+
+/// An argument iterator which wraps the ArgIterator in ::std.
+/// On windows, this iterator allocates.
+pub const OsIterator = struct {
+ const Error = process.ArgIterator.InitError;
+
+ arena: heap.ArenaAllocator,
+ args: process.ArgIterator,
+
+ /// The executable path (this is the first argument passed to the program)
+ /// TODO: Is it the right choice for this to be null? Maybe `init` should
+ /// return an error when we have no exe.
+ exe_arg: ?[:0]const u8,
+
+ pub fn init(allocator: mem.Allocator) OsIterator {
+ var res = OsIterator{
+ .arena = heap.ArenaAllocator.init(allocator),
+ .args = process.args(),
+ .exe_arg = undefined,
+ };
+ res.exe_arg = res.next();
+ return res;
+ }
+
+ pub fn deinit(iter: *OsIterator) void {
+ iter.arena.deinit();
+ }
+
+ pub fn next(iter: *OsIterator) ?[:0]const u8 {
+ return iter.args.next();
+ }
+};
+
+/// An argument iterator that takes a string and parses it into arguments, simulating
+/// how shells split arguments.
+pub const ShellIterator = struct {
+ const Error = error{
+ DanglingEscape,
+ QuoteNotClosed,
+ } || mem.Allocator.Error;
+
+ arena: heap.ArenaAllocator,
+ str: []const u8,
+
+ pub fn init(allocator: mem.Allocator, str: []const u8) ShellIterator {
+ return .{
+ .arena = heap.ArenaAllocator.init(allocator),
+ .str = str,
+ };
+ }
+
+ pub fn deinit(iter: *ShellIterator) void {
+ iter.arena.deinit();
+ }
+
+ pub fn next(iter: *ShellIterator) Error!?[]const u8 {
+ // Whenever possible, this iterator will return slices into `str` instead of
+ // allocating. Sometimes this is not possible, for example, escaped characters
+ // have be be unescape, so we need to allocate in this case.
+ var list = std.ArrayList(u8).init(&iter.arena.allocator);
+ var start: usize = 0;
+ var state: enum {
+ skip_whitespace,
+ no_quote,
+ no_quote_escape,
+ single_quote,
+ double_quote,
+ double_quote_escape,
+ after_quote,
+ } = .skip_whitespace;
+
+ for (iter.str, 0..) |c, i| {
+ switch (state) {
+ // The state that skips the initial whitespace.
+ .skip_whitespace => switch (c) {
+ ' ', '\t', '\n' => {},
+ '\'' => {
+ start = i + 1;
+ state = .single_quote;
+ },
+ '"' => {
+ start = i + 1;
+ state = .double_quote;
+ },
+ '\\' => {
+ start = i + 1;
+ state = .no_quote_escape;
+ },
+ else => {
+ start = i;
+ state = .no_quote;
+ },
+ },
+
+ // The state that parses the none quoted part of a argument.
+ .no_quote => switch (c) {
+ // We're done parsing a none quoted argument when we hit a
+ // whitespace.
+ ' ', '\t', '\n' => {
+ defer iter.str = iter.str[i..];
+ return iter.result(start, i, &list);
+ },
+
+ // Slicing is not possible if a quote starts while parsing none
+ // quoted args.
+ // Example:
+ // ab'cd' -> abcd
+ '\'' => {
+ try list.appendSlice(iter.str[start..i]);
+ start = i + 1;
+ state = .single_quote;
+ },
+ '"' => {
+ try list.appendSlice(iter.str[start..i]);
+ start = i + 1;
+ state = .double_quote;
+ },
+
+ // Slicing is not possible if we need to escape a character.
+ // Example:
+ // ab\"d -> ab"d
+ '\\' => {
+ try list.appendSlice(iter.str[start..i]);
+ start = i + 1;
+ state = .no_quote_escape;
+ },
+ else => {},
+ },
+
+ // We're in this state after having parsed the quoted part of an
+ // argument. This state works mostly the same as .no_quote, but
+ // is aware, that the last character seen was a quote, which should
+ // not be part of the argument. This is why you will see `i - 1` here
+ // instead of just `i` when `iter.str` is sliced.
+ .after_quote => switch (c) {
+ ' ', '\t', '\n' => {
+ defer iter.str = iter.str[i..];
+ return iter.result(start, i - 1, &list);
+ },
+ '\'' => {
+ try list.appendSlice(iter.str[start .. i - 1]);
+ start = i + 1;
+ state = .single_quote;
+ },
+ '"' => {
+ try list.appendSlice(iter.str[start .. i - 1]);
+ start = i + 1;
+ state = .double_quote;
+ },
+ '\\' => {
+ try list.appendSlice(iter.str[start .. i - 1]);
+ start = i + 1;
+ state = .no_quote_escape;
+ },
+ else => {
+ try list.appendSlice(iter.str[start .. i - 1]);
+ start = i;
+ state = .no_quote;
+ },
+ },
+
+ // The states that parse the quoted part of arguments. The only differnece
+ // between single and double quoted arguments is that single quoted
+ // arguments ignore escape sequences, while double quoted arguments
+ // does escaping.
+ .single_quote => switch (c) {
+ '\'' => state = .after_quote,
+ else => {},
+ },
+ .double_quote => switch (c) {
+ '"' => state = .after_quote,
+ '\\' => {
+ try list.appendSlice(iter.str[start..i]);
+ start = i + 1;
+ state = .double_quote_escape;
+ },
+ else => {},
+ },
+
+ // The state we end up when after the escape character (`\`). All these
+ // states do is transition back into the previous state.
+ // TODO: Are there any escape sequences that does transform the second
+ // character into something else? For example, in Zig, `\n` is
+ // transformed into the line feed ascii character.
+ .no_quote_escape => switch (c) {
+ else => state = .no_quote,
+ },
+ .double_quote_escape => switch (c) {
+ else => state = .double_quote,
+ },
+ }
+ }
+
+ defer iter.str = iter.str[iter.str.len..];
+ switch (state) {
+ .skip_whitespace => return null,
+ .no_quote => return iter.result(start, iter.str.len, &list),
+ .after_quote => return iter.result(start, iter.str.len - 1, &list),
+ .no_quote_escape => return Error.DanglingEscape,
+ .single_quote,
+ .double_quote,
+ .double_quote_escape,
+ => return Error.QuoteNotClosed,
+ }
+ }
+
+ fn result(iter: *ShellIterator, start: usize, end: usize, list: *std.ArrayList(u8)) Error!?[]const u8 {
+ const res = iter.str[start..end];
+
+ // If we already have something in `list` that means that we could not
+ // parse the argument without allocation. We therefor need to just append
+ // the rest we have to the list and return that.
+ if (list.items.len != 0) {
+ try list.appendSlice(res);
+ return try list.toOwnedSlice();
+ }
+ return res;
+ }
+};
+
+fn testShellIteratorOk(str: []const u8, allocations: usize, expect: []const []const u8) void {
+ var allocator = testing.FailingAllocator.init(testing.allocator, allocations);
+ var it = ShellIterator.init(&allocator.allocator, str);
+ defer it.deinit();
+
+ for (expect) |e| {
+ if (it.next()) |actual| {
+ testing.expect(actual != null);
+ testing.expectEqualStrings(e, actual.?);
+ } else |err| testing.expectEqual(@as(anyerror![]const u8, e), err);
+ }
+
+ if (it.next()) |actual| {
+ testing.expectEqual(@as(?[]const u8, null), actual);
+ testing.expectEqual(allocations, allocator.allocations);
+ } else |err| testing.expectEqual(@as(anyerror!void, {}), err);
+}
+
+fn testShellIteratorErr(str: []const u8, expect: anyerror) void {
+ var it = ShellIterator.init(testing.allocator, str);
+ defer it.deinit();
+
+ while (it.next() catch |err| {
+ testing.expectError(expect, @as(anyerror!void, err));
+ return;
+ }) |_| {}
+
+ testing.expectError(expect, @as(anyerror!void, {}));
+}
+
+test "ShellIterator" {
+ testShellIteratorOk("a", 0, &[_][]const u8{"a"});
+ testShellIteratorOk("'a'", 0, &[_][]const u8{"a"});
+ testShellIteratorOk("\"a\"", 0, &[_][]const u8{"a"});
+ testShellIteratorOk("a b", 0, &[_][]const u8{ "a", "b" });
+ testShellIteratorOk("'a' b", 0, &[_][]const u8{ "a", "b" });
+ testShellIteratorOk("\"a\" b", 0, &[_][]const u8{ "a", "b" });
+ testShellIteratorOk("a 'b'", 0, &[_][]const u8{ "a", "b" });
+ testShellIteratorOk("a \"b\"", 0, &[_][]const u8{ "a", "b" });
+ testShellIteratorOk("'a b'", 0, &[_][]const u8{"a b"});
+ testShellIteratorOk("\"a b\"", 0, &[_][]const u8{"a b"});
+ testShellIteratorOk("\"a\"\"b\"", 1, &[_][]const u8{"ab"});
+ testShellIteratorOk("'a''b'", 1, &[_][]const u8{"ab"});
+ testShellIteratorOk("'a'b", 1, &[_][]const u8{"ab"});
+ testShellIteratorOk("a'b'", 1, &[_][]const u8{"ab"});
+ testShellIteratorOk("a\\ b", 1, &[_][]const u8{"a b"});
+ testShellIteratorOk("\"a\\ b\"", 1, &[_][]const u8{"a b"});
+ testShellIteratorOk("'a\\ b'", 0, &[_][]const u8{"a\\ b"});
+ testShellIteratorOk(" a b ", 0, &[_][]const u8{ "a", "b" });
+ testShellIteratorOk("\\ \\ ", 0, &[_][]const u8{ " ", " " });
+
+ testShellIteratorOk(
+ \\printf 'run\nuninstall\n'
+ , 0, &[_][]const u8{ "printf", "run\\nuninstall\\n" });
+ testShellIteratorOk(
+ \\setsid -f steam "steam://$action/$id"
+ , 0, &[_][]const u8{ "setsid", "-f", "steam", "steam://$action/$id" });
+ testShellIteratorOk(
+ \\xargs -I% rg --no-heading --no-line-number --only-matching
+ \\ --case-sensitive --multiline --text --byte-offset '(?-u)%' $@
+ \\
+ , 0, &[_][]const u8{
+ "xargs", "-I%", "rg", "--no-heading",
+ "--no-line-number", "--only-matching", "--case-sensitive", "--multiline",
+ "--text", "--byte-offset", "(?-u)%", "$@",
+ });
+
+ testShellIteratorErr("'a", error.QuoteNotClosed);
+ testShellIteratorErr("'a\\", error.QuoteNotClosed);
+ testShellIteratorErr("\"a", error.QuoteNotClosed);
+ testShellIteratorErr("\"a\\", error.QuoteNotClosed);
+ testShellIteratorErr("a\\", error.DanglingEscape);
+}
diff --git a/src/deps/zig-clap/clap/comptime.zig b/src/deps/zig-clap/clap/comptime.zig
new file mode 100644
index 000000000..3dcd4f7d7
--- /dev/null
+++ b/src/deps/zig-clap/clap/comptime.zig
@@ -0,0 +1,194 @@
+const clap = @import("../clap.zig");
+const std = @import("std");
+
+const debug = std.debug;
+const heap = std.heap;
+const mem = std.mem;
+const testing = std.testing;
+
+/// Deprecated: Use `parseEx` instead
+pub fn ComptimeClap(
+ comptime Id: type,
+ comptime params: []const clap.Param(Id),
+) type {
+ var _flags: usize = 0;
+ var _single_options: usize = 0;
+ var _multi_options: usize = 0;
+ var _converted_params: []const clap.Param(usize) = &[_]clap.Param(usize){};
+ for (params) |param| {
+ var index: usize = 0;
+ if (param.names.long != null or param.names.short != null) {
+ const ptr = switch (param.takes_value) {
+ .none => &_flags,
+ .one_optional, .one => &_single_options,
+ .many => &_multi_options,
+ };
+ index = ptr.*;
+ ptr.* += 1;
+ }
+
+ const converted = clap.Param(usize){
+ .id = index,
+ .names = param.names,
+ .takes_value = param.takes_value,
+ };
+ _converted_params = _converted_params ++ [_]clap.Param(usize){converted};
+ }
+ const flags = _flags;
+ const single_options = _single_options;
+ const multi_options = _multi_options;
+ const converted_params = _converted_params;
+
+ return struct {
+ single_options: [single_options]?[]const u8,
+ multi_options: [multi_options][]const []const u8,
+ flags: [flags]bool,
+ pos: []const []const u8,
+ passthrough_positionals: []const []const u8,
+ allocator: mem.Allocator,
+
+ pub fn parse(iter: anytype, opt: clap.ParseOptions) !@This() {
+ const allocator = opt.allocator;
+ var multis = [_]std.ArrayList([]const u8){undefined} ** multi_options;
+ for (&multis) |*multi| {
+ multi.* = std.ArrayList([]const u8).init(allocator);
+ }
+
+ var pos = std.ArrayList([]const u8).init(allocator);
+ var passthrough_positionals = std.ArrayList([]const u8).init(allocator);
+
+ var res = @This(){
+ .single_options = [_]?[]const u8{null} ** single_options,
+ .multi_options = [_][]const []const u8{undefined} ** multi_options,
+ .flags = [_]bool{false} ** flags,
+ .pos = undefined,
+ .allocator = allocator,
+ .passthrough_positionals = undefined,
+ };
+
+ var stream = clap.StreamingClap(usize, @typeInfo(@TypeOf(iter)).Pointer.child){
+ .params = converted_params,
+ .iter = iter,
+ };
+
+ while (try stream.next()) |arg| {
+ const param = arg.param;
+ if (param.names.long == null and param.names.short == null) {
+ try pos.append(arg.value.?);
+ if (opt.stop_after_positional_at > 0 and pos.items.len >= opt.stop_after_positional_at) {
+ const bun = @import("bun");
+ if (comptime bun.Environment.isWindows) @compileError(
+ "TODO: implement stop_after_positional_at on windows",
+ );
+
+ var remaining_ = std.os.argv[@min(std.os.argv.len, stream.iter.args.inner.index)..];
+ const first: []const u8 = if (remaining_.len > 0) bun.span(remaining_[0]) else "";
+ if (first.len > 0 and std.mem.eql(u8, first, "--")) {
+ remaining_ = remaining_[1..];
+ }
+
+ try passthrough_positionals.ensureTotalCapacityPrecise(remaining_.len);
+ for (remaining_) |arg_| {
+ // use bun.span due to the optimization for long strings
+ passthrough_positionals.appendAssumeCapacity(bun.span(arg_));
+ }
+ break;
+ }
+ } else if (param.takes_value == .one or param.takes_value == .one_optional) {
+ debug.assert(res.single_options.len != 0);
+ if (res.single_options.len != 0)
+ res.single_options[param.id] = arg.value orelse "";
+ } else if (param.takes_value == .many) {
+ debug.assert(multis.len != 0);
+ if (multis.len != 0)
+ try multis[param.id].append(arg.value.?);
+ } else {
+ debug.assert(res.flags.len != 0);
+ if (res.flags.len != 0)
+ res.flags[param.id] = true;
+ }
+ }
+
+ for (&multis, 0..) |*multi, i|
+ res.multi_options[i] = try multi.toOwnedSlice();
+ res.pos = try pos.toOwnedSlice();
+ res.passthrough_positionals = try passthrough_positionals.toOwnedSlice();
+ return res;
+ }
+
+ pub fn deinit(parser: @This()) void {
+ for (parser.multi_options) |o|
+ parser.allocator.free(o);
+ parser.allocator.free(parser.pos);
+ }
+
+ pub fn flag(parser: @This(), comptime name: []const u8) bool {
+ const param = comptime findParam(name);
+ if (param.takes_value != .none and param.takes_value != .one_optional)
+ @compileError(name ++ " is an option and not a flag.");
+
+ return parser.flags[param.id];
+ }
+
+ pub fn option(parser: @This(), comptime name: []const u8) ?[]const u8 {
+ const param = comptime findParam(name);
+ if (param.takes_value == .none)
+ @compileError(name ++ " is a flag and not an option.");
+ if (param.takes_value == .many)
+ @compileError(name ++ " takes many options, not one.");
+ return parser.single_options[param.id];
+ }
+
+ pub fn options(parser: @This(), comptime name: []const u8) []const []const u8 {
+ const param = comptime findParam(name);
+ if (param.takes_value == .none)
+ @compileError(name ++ " is a flag and not an option.");
+ if (param.takes_value == .one or param.takes_value == .one_optional)
+ @compileError(name ++ " takes one option, not multiple.");
+
+ return parser.multi_options[param.id];
+ }
+
+ pub fn positionals(parser: @This()) []const []const u8 {
+ return parser.pos;
+ }
+
+ pub fn remaining(parser: @This()) []const []const u8 {
+ return parser.passthrough_positionals;
+ }
+
+ pub fn hasFlag(comptime name: []const u8) bool {
+ comptime {
+ for (converted_params) |param| {
+ if (param.names.short) |s| {
+ if (mem.eql(u8, name, "-" ++ [_]u8{s}))
+ return true;
+ }
+ if (param.names.long) |l| {
+ if (mem.eql(u8, name, "--" ++ l))
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ fn findParam(comptime name: []const u8) clap.Param(usize) {
+ comptime {
+ for (converted_params) |param| {
+ if (param.names.short) |s| {
+ if (mem.eql(u8, name, "-" ++ [_]u8{s}))
+ return param;
+ }
+ if (param.names.long) |l| {
+ if (mem.eql(u8, name, "--" ++ l))
+ return param;
+ }
+ }
+
+ @compileError(name ++ " is not a parameter.");
+ }
+ }
+ };
+}
diff --git a/src/deps/zig-clap/clap/streaming.zig b/src/deps/zig-clap/clap/streaming.zig
new file mode 100644
index 000000000..e3948e33b
--- /dev/null
+++ b/src/deps/zig-clap/clap/streaming.zig
@@ -0,0 +1,430 @@
+const builtin = @import("builtin");
+const clap = @import("../clap.zig");
+const std = @import("std");
+
+const args = clap.args;
+const debug = std.debug;
+const heap = std.heap;
+const io = std.io;
+const mem = std.mem;
+const os = std.os;
+const testing = std.testing;
+
+/// The result returned from StreamingClap.next
+pub fn Arg(comptime Id: type) type {
+ return struct {
+ const Self = @This();
+
+ param: *const clap.Param(Id),
+ value: ?[]const u8 = null,
+ };
+}
+
+/// A command line argument parser which, given an ArgIterator, will parse arguments according
+/// to the params. StreamingClap parses in an iterating manner, so you have to use a loop together with
+/// StreamingClap.next to parse all the arguments of your program.
+pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
+ return struct {
+ const State = union(enum) {
+ normal,
+ chaining: Chaining,
+ rest_are_positional,
+
+ const Chaining = struct {
+ arg: []const u8,
+ index: usize,
+ };
+ };
+
+ params: []const clap.Param(Id),
+ iter: *ArgIterator,
+ state: State = .normal,
+ positional: ?*const clap.Param(Id) = null,
+ diagnostic: ?*clap.Diagnostic = null,
+
+ /// Get the next Arg that matches a Param.
+ pub fn next(parser: *@This()) !?Arg(Id) {
+ switch (parser.state) {
+ .normal => return try parser.normal(),
+ .chaining => |state| return try parser.chainging(state),
+ .rest_are_positional => {
+ const param = parser.positionalParam() orelse unreachable;
+ const value = parser.iter.next() orelse return null;
+ return Arg(Id){ .param = param, .value = value };
+ },
+ }
+ }
+
+ fn normal(parser: *@This()) !?Arg(Id) {
+ const ArgType = Arg(Id);
+ const arg_info = (try parser.parseNextArg()) orelse return null;
+ const arg = arg_info.arg;
+
+ switch (arg_info.kind) {
+ .long => {
+ const eql_index = mem.indexOfScalar(u8, arg, '=');
+ const name = if (eql_index) |i| arg[0..i] else arg;
+ const maybe_value = if (eql_index) |i| arg[i + 1 ..] else null;
+
+ for (parser.params) |*param| {
+ const match = param.names.long orelse continue;
+
+ if (!mem.eql(u8, name, match))
+ continue;
+ if (param.takes_value == .none) {
+ if (maybe_value != null)
+ return parser.err(arg, .{ .long = name }, error.DoesntTakeValue);
+
+ return ArgType{ .param = param };
+ }
+
+ const value = blk: {
+ if (maybe_value) |v|
+ break :blk v;
+
+ break :blk parser.iter.next() orelse brk2: {
+ if (param.takes_value != .one_optional)
+ return parser.err(arg, .{ .long = name }, error.MissingValue);
+
+ break :brk2 "";
+ };
+ };
+
+ return ArgType{ .param = param, .value = value };
+ }
+
+ return null;
+ },
+ .short => return try parser.chainging(.{
+ .arg = arg,
+ .index = 0,
+ }),
+ .positional => if (parser.positionalParam()) |param| {
+ // If we find a positional with the value `--` then we
+ // interpret the rest of the arguments as positional
+ // arguments.
+ if (mem.eql(u8, arg, "--")) {
+ parser.state = .rest_are_positional;
+ const value = parser.iter.next() orelse return null;
+ return Arg(Id){ .param = param, .value = value };
+ }
+
+ return Arg(Id){ .param = param, .value = arg };
+ } else {
+ return parser.err(arg, .{}, error.InvalidArgument);
+ },
+ }
+ }
+
+ fn chainging(parser: *@This(), state: State.Chaining) !?Arg(Id) {
+ const arg = state.arg;
+ const index = state.index;
+ const next_index = index + 1;
+
+ for (parser.params) |*param| {
+ const short = param.names.short orelse continue;
+ if (short != arg[index])
+ continue;
+
+ // Before we return, we have to set the new state of the clap
+ defer {
+ if (arg.len <= next_index or param.takes_value != .none) {
+ parser.state = .normal;
+ } else {
+ parser.state = .{
+ .chaining = .{
+ .arg = arg,
+ .index = next_index,
+ },
+ };
+ }
+ }
+
+ const next_is_eql = if (next_index < arg.len) arg[next_index] == '=' else false;
+ if (param.takes_value == .none or param.takes_value == .one_optional) {
+ if (next_is_eql and param.takes_value == .none)
+ return parser.err(arg, .{ .short = short }, error.DoesntTakeValue);
+ return Arg(Id){ .param = param };
+ }
+
+ if (arg.len <= next_index) {
+ const value = parser.iter.next() orelse
+ return parser.err(arg, .{ .short = short }, error.MissingValue);
+
+ return Arg(Id){ .param = param, .value = value };
+ }
+
+ if (next_is_eql)
+ return Arg(Id){ .param = param, .value = arg[next_index + 1 ..] };
+
+ return Arg(Id){ .param = param, .value = arg[next_index..] };
+ }
+
+ return parser.err(arg, .{ .short = arg[index] }, error.InvalidArgument);
+ }
+
+ fn positionalParam(parser: *@This()) ?*const clap.Param(Id) {
+ if (parser.positional) |p|
+ return p;
+
+ for (parser.params) |*param| {
+ if (param.names.long) |_|
+ continue;
+ if (param.names.short) |_|
+ continue;
+
+ parser.positional = param;
+ return param;
+ }
+
+ return null;
+ }
+
+ const ArgInfo = struct {
+ arg: []const u8,
+ kind: enum {
+ long,
+ short,
+ positional,
+ },
+ };
+
+ fn parseNextArg(parser: *@This()) !?ArgInfo {
+ const full_arg = parser.iter.next() orelse return null;
+ if (mem.eql(u8, full_arg, "--") or mem.eql(u8, full_arg, "-"))
+ return ArgInfo{ .arg = full_arg, .kind = .positional };
+ if (mem.startsWith(u8, full_arg, "--"))
+ return ArgInfo{ .arg = full_arg[2..], .kind = .long };
+ if (mem.startsWith(u8, full_arg, "-"))
+ return ArgInfo{ .arg = full_arg[1..], .kind = .short };
+
+ return ArgInfo{ .arg = full_arg, .kind = .positional };
+ }
+
+ fn err(parser: @This(), arg: []const u8, names: clap.Names, _err: anytype) @TypeOf(_err) {
+ if (parser.diagnostic) |d|
+ d.* = .{ .arg = arg, .name = names };
+ return _err;
+ }
+ };
+}
+
+fn testNoErr(params: []const clap.Param(u8), args_strings: []const []const u8, results: []const Arg(u8)) void {
+ var iter = args.SliceIterator{ .args = args_strings };
+ var c = StreamingClap(u8, args.SliceIterator){
+ .params = params,
+ .iter = &iter,
+ };
+
+ for (results) |res| {
+ const arg = (c.next() catch unreachable) orelse unreachable;
+ testing.expectEqual(res.param, arg.param);
+ const expected_value = res.value orelse {
+ testing.expectEqual(@as(@TypeOf(arg.value), null), arg.value);
+ continue;
+ };
+ const actual_value = arg.value orelse unreachable;
+ testing.expectEqualSlices(u8, expected_value, actual_value);
+ }
+
+ if (c.next() catch unreachable) |_|
+ unreachable;
+}
+
+fn testErr(params: []const clap.Param(u8), args_strings: []const []const u8, expected: []const u8) void {
+ var diag = clap.Diagnostic{};
+ var iter = args.SliceIterator{ .args = args_strings };
+ var c = StreamingClap(u8, args.SliceIterator){
+ .params = params,
+ .iter = &iter,
+ .diagnostic = &diag,
+ };
+ while (c.next() catch |err| {
+ var buf: [1024]u8 = undefined;
+ var fbs = io.fixedBufferStream(&buf);
+ diag.report(fbs.writer(), err) catch unreachable;
+ testing.expectEqualStrings(expected, fbs.getWritten());
+ return;
+ }) |_| {}
+
+ testing.expect(false);
+}
+
+test "short params" {
+ const params = [_]clap.Param(u8){
+ .{ .id = 0, .names = .{ .short = 'a' } },
+ .{ .id = 1, .names = .{ .short = 'b' } },
+ .{
+ .id = 2,
+ .names = .{ .short = 'c' },
+ .takes_value = .one,
+ },
+ .{
+ .id = 3,
+ .names = .{ .short = 'd' },
+ .takes_value = .many,
+ },
+ };
+
+ const a = &params[0];
+ const b = &params[1];
+ const c = &params[2];
+ const d = &params[3];
+
+ testNoErr(
+ &params,
+ &[_][]const u8{
+ "-a", "-b", "-ab", "-ba",
+ "-c", "0", "-c=0", "-ac",
+ "0", "-ac=0", "-d=0",
+ },
+ &[_]Arg(u8){
+ .{ .param = a },
+ .{ .param = b },
+ .{ .param = a },
+ .{ .param = b },
+ .{ .param = b },
+ .{ .param = a },
+ .{ .param = c, .value = "0" },
+ .{ .param = c, .value = "0" },
+ .{ .param = a },
+ .{ .param = c, .value = "0" },
+ .{ .param = a },
+ .{ .param = c, .value = "0" },
+ .{ .param = d, .value = "0" },
+ },
+ );
+}
+
+test "long params" {
+ const params = [_]clap.Param(u8){
+ .{ .id = 0, .names = .{ .long = "aa" } },
+ .{ .id = 1, .names = .{ .long = "bb" } },
+ .{
+ .id = 2,
+ .names = .{ .long = "cc" },
+ .takes_value = .one,
+ },
+ .{
+ .id = 3,
+ .names = .{ .long = "dd" },
+ .takes_value = .many,
+ },
+ };
+
+ const aa = &params[0];
+ const bb = &params[1];
+ const cc = &params[2];
+ const dd = &params[3];
+
+ testNoErr(
+ &params,
+ &[_][]const u8{
+ "--aa", "--bb",
+ "--cc", "0",
+ "--cc=0", "--dd=0",
+ },
+ &[_]Arg(u8){
+ .{ .param = aa },
+ .{ .param = bb },
+ .{ .param = cc, .value = "0" },
+ .{ .param = cc, .value = "0" },
+ .{ .param = dd, .value = "0" },
+ },
+ );
+}
+
+test "positional params" {
+ const params = [_]clap.Param(u8){.{
+ .id = 0,
+ .takes_value = .one,
+ }};
+
+ testNoErr(
+ &params,
+ &[_][]const u8{ "aa", "bb" },
+ &[_]Arg(u8){
+ .{ .param = &params[0], .value = "aa" },
+ .{ .param = &params[0], .value = "bb" },
+ },
+ );
+}
+
+test "all params" {
+ const params = [_]clap.Param(u8){
+ .{
+ .id = 0,
+ .names = .{ .short = 'a', .long = "aa" },
+ },
+ .{
+ .id = 1,
+ .names = .{ .short = 'b', .long = "bb" },
+ },
+ .{
+ .id = 2,
+ .names = .{ .short = 'c', .long = "cc" },
+ .takes_value = .one,
+ },
+ .{ .id = 3, .takes_value = .one },
+ };
+
+ const aa = &params[0];
+ const bb = &params[1];
+ const cc = &params[2];
+ const positional = &params[3];
+
+ testNoErr(
+ &params,
+ &[_][]const u8{
+ "-a", "-b", "-ab", "-ba",
+ "-c", "0", "-c=0", "-ac",
+ "0", "-ac=0", "--aa", "--bb",
+ "--cc", "0", "--cc=0", "something",
+ "-", "--", "--cc=0", "-a",
+ },
+ &[_]Arg(u8){
+ .{ .param = aa },
+ .{ .param = bb },
+ .{ .param = aa },
+ .{ .param = bb },
+ .{ .param = bb },
+ .{ .param = aa },
+ .{ .param = cc, .value = "0" },
+ .{ .param = cc, .value = "0" },
+ .{ .param = aa },
+ .{ .param = cc, .value = "0" },
+ .{ .param = aa },
+ .{ .param = cc, .value = "0" },
+ .{ .param = aa },
+ .{ .param = bb },
+ .{ .param = cc, .value = "0" },
+ .{ .param = cc, .value = "0" },
+ .{ .param = positional, .value = "something" },
+ .{ .param = positional, .value = "-" },
+ .{ .param = positional, .value = "--cc=0" },
+ .{ .param = positional, .value = "-a" },
+ },
+ );
+}
+
+test "errors" {
+ const params = [_]clap.Param(u8){
+ .{
+ .id = 0,
+ .names = .{ .short = 'a', .long = "aa" },
+ },
+ .{
+ .id = 1,
+ .names = .{ .short = 'c', .long = "cc" },
+ .takes_value = .one,
+ },
+ };
+ testErr(&params, &[_][]const u8{"q"}, "Invalid argument 'q'\n");
+ testErr(&params, &[_][]const u8{"-q"}, "Invalid argument '-q'\n");
+ testErr(&params, &[_][]const u8{"--q"}, "Invalid argument '--q'\n");
+ testErr(&params, &[_][]const u8{"--q=1"}, "Invalid argument '--q'\n");
+ testErr(&params, &[_][]const u8{"-a=1"}, "The argument '-a' does not take a value\n");
+ testErr(&params, &[_][]const u8{"--aa=1"}, "The argument '--aa' does not take a value\n");
+ testErr(&params, &[_][]const u8{"-c"}, "The argument '-c' requires a value but none was supplied\n");
+ testErr(&params, &[_][]const u8{"--cc"}, "The argument '--cc' requires a value but none was supplied\n");
+}
diff --git a/src/deps/zig-clap/gyro.zzz b/src/deps/zig-clap/gyro.zzz
new file mode 100644
index 000000000..3853db049
--- /dev/null
+++ b/src/deps/zig-clap/gyro.zzz
@@ -0,0 +1,14 @@
+pkgs:
+ clap:
+ version: 0.3.0
+ license: Unlicense
+ description: Simple command line argument parsing library
+ source_url: "https://github.com/Hejsil/zig-clap"
+ root: clap.zig
+ files:
+ README.md
+ LICENSE
+ build.zig
+ clap/*.zig
+ example/*.zig
+
diff --git a/src/deps/zig-clap/zig.mod b/src/deps/zig-clap/zig.mod
new file mode 100644
index 000000000..00c1a690d
--- /dev/null
+++ b/src/deps/zig-clap/zig.mod
@@ -0,0 +1,5 @@
+id: aoe2l16htluewam6bfwvv0khsbbno8g8jd7suonifg74u7kd
+name: clap
+main: clap.zig
+license: Unlicense
+dependencies:
diff --git a/src/install/install.zig b/src/install/install.zig
index 5399a6fd3..0c280ccaa 100644
--- a/src/install/install.zig
+++ b/src/install/install.zig
@@ -5271,58 +5271,56 @@ pub const PackageManager = struct {
else
"Possible values: \"hardlink\" (default), \"symlink\", \"copyfile\"";
- pub const install_params_ = clap.parseParamsComptime(
- \\-c, --config <STR>? Load config (bunfig.toml)
- \\-y, --yarn Write a yarn.lock file (yarn v1)
- \\-p, --production Don't install devDependencies
- \\--no-save Don't save a lockfile
- \\--dry-run Don't install anything
- \\--lockfile <PATH> Store & load a lockfile at a specific filepath
- \\-f, --force Always request the latest versions from the registry & reinstall all dependencies
- \\--cache-dir <PATH> Store & load cached data from a specific directory path
- \\--no-cache Ignore manifest cache entirely
- \\--silent Don't log anything
- \\--verbose Excessively verbose logging
- \\--no-progress Disable the progress bar
- \\--no-summary Don't print a summary
- \\--no-verify Skip verifying integrity of newly downloaded packages
- \\--ignore-scripts Skip lifecycle scripts in the project's package.json (dependency scripts are never run)
- \\-g, --global Install globally
- \\--cwd <STR> Set a specific cwd
- \\--backend <STR> Platform-specific optimizations for installing dependencies.
- \\
- ++ platform_specific_backend_label ++
- \\
- \\--link-native-bins <STR>... Link "bin" from a matching platform-specific "optionalDependencies" instead. Default: esbuild, turbo
- \\--help Print this help menu
- );
- // clap.parseParam("--omit <str>... Skip installing dependencies of a certain type. \"dev\", \"optional\", or \"peer\"") catch unreachable,
+ pub const install_params_ = [_]ParamType{
+ clap.parseParam("-c, --config <STR>? Load config (bunfig.toml)") catch unreachable,
+ clap.parseParam("-y, --yarn Write a yarn.lock file (yarn v1)") catch unreachable,
+ clap.parseParam("-p, --production Don't install devDependencies") catch unreachable,
+ clap.parseParam("--no-save Don't save a lockfile") catch unreachable,
+ clap.parseParam("--dry-run Don't install anything") catch unreachable,
+ clap.parseParam("--lockfile <PATH> Store & load a lockfile at a specific filepath") catch unreachable,
+ clap.parseParam("-f, --force Always request the latest versions from the registry & reinstall all dependencies") catch unreachable,
+ clap.parseParam("--cache-dir <PATH> Store & load cached data from a specific directory path") catch unreachable,
+ clap.parseParam("--no-cache Ignore manifest cache entirely") catch unreachable,
+ clap.parseParam("--silent Don't log anything") catch unreachable,
+ clap.parseParam("--verbose Excessively verbose logging") catch unreachable,
+ clap.parseParam("--no-progress Disable the progress bar") catch unreachable,
+ clap.parseParam("--no-summary Don't print a summary") catch unreachable,
+ clap.parseParam("--no-verify Skip verifying integrity of newly downloaded packages") catch unreachable,
+ clap.parseParam("--ignore-scripts Skip lifecycle scripts in the project's package.json (dependency scripts are never run)") catch unreachable,
+ clap.parseParam("-g, --global Install globally") catch unreachable,
+ clap.parseParam("--cwd <STR> Set a specific cwd") catch unreachable,
+ clap.parseParam("--backend <STR> Platform-specific optimizations for installing dependencies. " ++ platform_specific_backend_label) catch unreachable,
+ clap.parseParam("--link-native-bins <STR>... Link \"bin\" from a matching platform-specific \"optionalDependencies\" instead. Default: esbuild, turbo") catch unreachable,
+
+ // clap.parseParam("--omit <STR>... Skip installing dependencies of a certain type. \"dev\", \"optional\", or \"peer\"") catch unreachable,
// clap.parseParam("--no-dedupe Disable automatic downgrading of dependencies that would otherwise cause unnecessary duplicate package versions ($BUN_CONFIG_NO_DEDUPLICATE)") catch unreachable,
+ clap.parseParam("--help Print this help menu") catch unreachable,
+ };
- pub const install_params = install_params_ ++ clap.parseParamsComptime(
- \\<STR> ...
- );
+ pub const install_params = install_params_ ++ [_]ParamType{
+ clap.parseParam("<POS> ... ") catch unreachable,
+ };
- pub const add_params = install_params_ ++ clap.parseParamsComptime(
- \\-d, --development Add dependency to "devDependencies"
- \\--optional Add dependency to "optionalDependencies"
- \\<STR> ... "name" or "name@version" of packages to install
- );
+ pub const add_params = install_params_ ++ [_]ParamType{
+ clap.parseParam("-d, --development Add dependency to \"devDependencies\"") catch unreachable,
+ clap.parseParam("--optional Add dependency to \"optionalDependencies\"") catch unreachable,
+ clap.parseParam("<POS> ... \"name\" or \"name@version\" of packages to install") catch unreachable,
+ };
- pub const remove_params = install_params_ ++ clap.parseParamsComptime(
- \\<STR> ... "name" of packages to remove from package.json
- );
+ pub const remove_params = install_params_ ++ [_]ParamType{
+ clap.parseParam("<POS> ... \"name\" of packages to remove from package.json") catch unreachable,
+ };
- pub const link_params = install_params_ ++ clap.parseParamsComptime(
- \\--save Save to package.json
- \\<STR> ... "name" install package as a link
- );
+ pub const link_params = install_params_ ++ [_]ParamType{
+ clap.parseParam("--save Save to package.json") catch unreachable,
+ clap.parseParam("<POS> ... \"name\" install package as a link") catch unreachable,
+ };
- pub const unlink_params = install_params_ ++ clap.parseParamsComptime(
- \\--save Save to package.json
- \\<STR> ... "name" uninstall package as a link
- );
+ pub const unlink_params = install_params_ ++ [_]ParamType{
+ clap.parseParam("--save Save to package.json") catch unreachable,
+ clap.parseParam("<POS> ... \"name\" uninstall package as a link") catch unreachable,
+ };
pub const CommandLineArguments = struct {
registry: string = "",
@@ -5379,63 +5377,62 @@ pub const PackageManager = struct {
) !CommandLineArguments {
var diag = clap.Diagnostic{};
- var res = clap.parse(clap.Help, params, BunArguments.parser, .{
+ var args = clap.parse(clap.Help, params, .{
.diagnostic = &diag,
.allocator = allocator,
}) catch |err| {
- clap.help(Output.errorWriter(), clap.Help, params, .{}) catch {};
+ clap.help(Output.errorWriter(), params) catch {};
Output.errorWriter().writeAll("\n") catch {};
diag.report(Output.errorWriter(), err) catch {};
return err;
};
- if (res.args.help) {
+ if (args.flag("--help")) {
Output.prettyln("\n<b><magenta>bun<r> (package manager) flags:<r>\n\n", .{});
Output.flush();
- clap.help(Output.writer(), clap.Help, params, .{}) catch {};
+ clap.help(Output.writer(), params) catch {};
Global.exit(0);
}
var cli = CommandLineArguments{};
- cli.yarn = res.args.yarn;
- cli.production = res.args.production;
- cli.no_save = res.args.@"no-save";
- cli.no_progress = res.args.@"no-progress";
- cli.dry_run = res.args.@"dry-run";
- cli.global = res.args.global;
- cli.force = res.args.force;
- cli.no_verify = res.args.@"no-verify";
- // cli.no_dedupe = res.args.no_dedupe;
- cli.no_cache = res.args.@"no-cache";
- cli.silent = res.args.silent;
- cli.verbose = res.args.verbose;
- cli.ignore_scripts = res.args.@"ignore-scripts";
- cli.no_summary = res.args.@"no-summary";
-
- if (comptime @hasDecl(@TypeOf(res.args), "save")) {
+ cli.yarn = args.flag("--yarn");
+ cli.production = args.flag("--production");
+ cli.no_save = args.flag("--no-save");
+ cli.no_progress = args.flag("--no-progress");
+ cli.dry_run = args.flag("--dry-run");
+ cli.global = args.flag("--global");
+ cli.force = args.flag("--force");
+ cli.no_verify = args.flag("--no-verify");
+ // cli.no_dedupe = args.flag("--no-dedupe");
+ cli.no_cache = args.flag("--no-cache");
+ cli.silent = args.flag("--silent");
+ cli.verbose = args.flag("--verbose");
+ cli.ignore_scripts = args.flag("--ignore-scripts");
+ cli.no_summary = args.flag("--no-summary");
+ if (comptime @TypeOf(args).hasFlag("--save")) {
cli.no_save = true;
- if (res.args.save) {
+ if (args.flag("--save")) {
cli.no_save = false;
}
}
- if (res.args.config) |opt| {
+ if (args.option("--config")) |opt| {
cli.config = opt;
}
try BunArguments.loadConfig(allocator, cli.config, ctx, .InstallCommand);
- cli.link_native_bins = res.args.@"link-native-bins";
+ cli.link_native_bins = args.options("--link-native-bins");
if (comptime params.len == add_params.len) {
- cli.development = res.args.development;
- cli.optional = res.args.optional;
+ cli.development = args.flag("--development");
+ cli.optional = args.flag("--optional");
}
- // for (res.args.omit) |omit| {
+ // for (args.options("--omit")) |omit| {
// if (strings.eqlComptime(omit, "dev")) {
// cli.omit.dev = true;
// } else if (strings.eqlComptime(omit, "optional")) {
@@ -5448,11 +5445,11 @@ pub const PackageManager = struct {
// }
// }
- if (res.args.lockfile) |lockfile| {
+ if (args.option("--lockfile")) |lockfile| {
cli.lockfile = lockfile;
}
- if (res.args.cwd) |cwd_| {
+ if (args.option("--cwd")) |cwd_| {
var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
var buf2: [bun.MAX_PATH_BYTES]u8 = undefined;
var final_path: [:0]u8 = undefined;
@@ -5471,7 +5468,7 @@ pub const PackageManager = struct {
}
const specified_backend: ?PackageInstall.Method = brk: {
- if (res.args.backend) |backend_| {
+ if (args.option("--backend")) |backend_| {
break :brk PackageInstall.Method.map.get(backend_);
}
break :brk null;
@@ -5483,7 +5480,7 @@ pub const PackageManager = struct {
}
}
- cli.positionals = res.positionals;
+ cli.positionals = args.positionals();
return cli;
}