usingnamespace @import("global.zig"); usingnamespace @import("./http.zig"); const std = @import("std"); const lex = @import("js_lexer.zig"); const logger = @import("logger.zig"); const alloc = @import("alloc.zig"); const options = @import("options.zig"); const js_parser = @import("js_parser.zig"); const json_parser = @import("json_parser.zig"); const js_printer = @import("js_printer.zig"); const js_ast = @import("js_ast.zig"); const linker = @import("linker.zig"); usingnamespace @import("ast/base.zig"); usingnamespace @import("defines.zig"); const panicky = @import("panic_handler.zig"); const sync = @import("./sync.zig"); const Api = @import("api/schema.zig").Api; const resolve_path = @import("./resolver/resolve_path.zig"); const configureTransformOptionsForBun = @import("./javascript/jsc/config.zig").configureTransformOptionsForBun; const clap = @import("clap"); const bundler = @import("bundler.zig"); const DotEnv = @import("./env_loader.zig"); const fs = @import("fs.zig"); const Router = @import("./router.zig"); const NodeModuleBundle = @import("./node_module_bundle.zig").NodeModuleBundle; const BunCommand = @import("./cli/bun_command.zig").BunCommand; const DevCommand = @import("./cli/dev_command.zig").DevCommand; const DiscordCommand = @import("./cli/discord_command.zig").DiscordCommand; const BuildCommand = @import("./cli/build_command.zig").BuildCommand; const CreateCommand = @import("./cli/create_command.zig").CreateCommand; const CreateListExamplesCommand = @import("./cli/create_command.zig").CreateListExamplesCommand; const RunCommand = @import("./cli/run_command.zig").RunCommand; const UpgradeCommand = @import("./cli/upgrade_command.zig").UpgradeCommand; var start_time: i128 = undefined; pub const Cli = struct { var wait_group: sync.WaitGroup = undefined; pub fn startTransform(allocator: *std.mem.Allocator, args: Api.TransformOptions, log: *logger.Log) anyerror!void {} pub fn start(allocator: *std.mem.Allocator, stdout: anytype, stderr: anytype, comptime MainPanicHandler: type) anyerror!void { start_time = std.time.nanoTimestamp(); var log = try allocator.create(logger.Log); log.* = logger.Log.init(allocator); var panicker = MainPanicHandler.init(log); MainPanicHandler.Singleton = &panicker; Command.start(allocator, log) catch |err| { switch (err) { error.MissingEntryPoint => { Output.prettyErrorln("MissingEntryPoint what do you want to bundle?\n\nExample:\n\n bun bun --use next\n\n bun bun ./src/index.ts ./src/file2.ts\n", .{}); Output.flush(); std.os.exit(1); }, else => { return err; }, } }; } }; const LoaderMatcher = strings.ExactSizeMatcher(4); const ColonListType = @import("./cli/colon_list_type.zig").ColonListType; pub const LoaderColonList = ColonListType(Api.Loader, Arguments.loader_resolver); pub const DefineColonList = ColonListType(string, Arguments.noop_resolver); pub const Arguments = struct { pub fn loader_resolver(in: string) !Api.Loader { const Matcher = strings.ExactSizeMatcher(4); switch (Matcher.match(in)) { Matcher.case("jsx") => return Api.Loader.jsx, Matcher.case("js") => return Api.Loader.js, Matcher.case("ts") => return Api.Loader.ts, Matcher.case("tsx") => return Api.Loader.tsx, Matcher.case("css") => return Api.Loader.css, Matcher.case("file") => return Api.Loader.file, Matcher.case("json") => return Api.Loader.json, else => { return error.InvalidLoader; }, } } pub fn noop_resolver(in: string) !string { return in; } pub fn fileReadError(err: anyerror, stderr: anytype, filename: string, kind: string) noreturn { stderr.writer().print("Error reading file \"{s}\" for {s}: {s}", .{ filename, kind, @errorName(err) }) catch {}; std.process.exit(1); } pub fn readFile( allocator: *std.mem.Allocator, cwd: string, filename: string, ) ![]u8 { var paths = [_]string{ cwd, filename }; const outpath = try std.fs.path.resolve(allocator, &paths); defer allocator.free(outpath); var file = try std.fs.openFileAbsolute(outpath, std.fs.File.OpenFlags{ .read = true, .write = false }); defer file.close(); const stats = try file.stat(); return try file.readToEndAlloc(allocator, stats.size); } pub fn resolve_jsx_runtime(str: string) !Api.JsxRuntime { if (strings.eqlComptime(str, "automatic")) { return Api.JsxRuntime.automatic; } else if (strings.eqlComptime(str, "fallback")) { return Api.JsxRuntime.classic; } else { return error.InvalidJSXRuntime; } } pub const ParamType = clap.Param(clap.Help); const public_params = [_]ParamType{ clap.parseParam("--use Choose a framework, e.g. \"--use next\". It checks first for a package named \"bun-framework-packagename\" and then \"packagename\".") catch unreachable, clap.parseParam("--bunfile Use a .bun file (default: node_modules.bun)") catch unreachable, clap.parseParam("--server-bunfile Use a .server.bun file (default: node_modules.server.bun)") catch unreachable, clap.parseParam("--cwd Absolute path to resolve files & entry points from. This just changes the process' cwd.") 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)") catch unreachable, clap.parseParam("--extension-order ... defaults to: .tsx,.ts,.jsx,.js,.json ") catch unreachable, clap.parseParam("--jsx-factory Changes the function called when compiling JSX elements using the classic JSX runtime") catch unreachable, clap.parseParam("--jsx-fragment Changes the function called when compiling JSX fragments using the classic JSX runtime") catch unreachable, clap.parseParam("--jsx-import-source 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 \"automatic\" (default) or \"classic\"") catch unreachable, clap.parseParam("--main-fields ... 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 \"browser\" or \"node\". Defaults to \"browser\"") catch unreachable, // clap.parseParam("--production   [not implemented] generate production code") catch unreachable, clap.parseParam("--public-dir Top-level directory for .html files, fonts or anything external. Defaults to \"/public\", to match create-react-app and Next.js") catch unreachable, clap.parseParam("--tsconfig-override Load tsconfig from path instead of cwd/tsconfig.json") catch unreachable, clap.parseParam("-d, --define ... Substitute K:V while parsing, e.g. --define process.env.NODE_ENV:\"development\". Values are parsed as JSON.") catch unreachable, clap.parseParam("-e, --external ... 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("-i, --inject ... Inject module at the top of every file") catch unreachable, clap.parseParam("-l, --loader ... Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: jsx, js, json, tsx, ts, css") catch unreachable, clap.parseParam("--origin Rewrite import paths to start with --origin. Default: \"\"") catch unreachable, clap.parseParam("--port Port to serve Bun's dev server on. Default: \"3000\"") catch unreachable, clap.parseParam("--silent Don't repeat the command for bun run") catch unreachable, // clap.parseParam("-o, --outdir Save output to directory (default: \"out\" if none provided and multiple entry points passed)") catch unreachable, // clap.parseParam("-r, --resolve Determine import/require behavior. \"disable\" ignores. \"dev\" bundles node_modules and builds everything else as independent entry points") catch unreachable, // clap.parseParam("-r, --resolve Determine import/require behavior. \"disable\" ignores. \"dev\" bundles node_modules and builds everything else as independent entry points") catch unreachable, clap.parseParam("... ") 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("--disable-bun.js Disable Bun.js from loading in the dev server") catch unreachable, }; const params = public_params ++ debug_params; fn printVersionAndExit() noreturn { @setCold(true); Output.writer().writeAll(Global.package_json_version ++ "\n") catch {}; Output.flush(); std.os.exit(0); } pub fn parse(allocator: *std.mem.Allocator, ctx: *Command.Context, comptime cmd: Command.Tag) !Api.TransformOptions { var diag = clap.Diagnostic{}; var args = clap.parse(clap.Help, ¶ms, .{ .diagnostic = &diag, .allocator = allocator, }) catch |err| { // Report useful error and exit diag.report(Output.errorWriter(), err) catch {}; return err; }; if (args.flag("--version")) { printVersionAndExit(); } var cwd_paths = [_]string{args.option("--cwd") orelse try std.process.getCwdAlloc(allocator)}; var cwd = try std.fs.path.resolve(allocator, &cwd_paths); var defines_tuple = try DefineColonList.resolve(allocator, args.options("--define")); var loader_tuple = try LoaderColonList.resolve(allocator, args.options("--define")); var externals = std.mem.zeroes([][]u8); if (args.options("--external").len > 0) { externals = try allocator.alloc([]u8, args.options("--external").len); for (args.options("--external")) |external, i| { externals[i] = constStrToU8(external); } } var opts = Api.TransformOptions{ .tsconfig_override = if (args.option("--tsconfig-override")) |ts| (Arguments.readFile(allocator, cwd, ts) catch |err| fileReadError(err, Output.errorStream(), ts, "tsconfig.json")) else null, .external = externals, .absolute_working_dir = cwd, .origin = args.option("--origin"), .define = .{ .keys = defines_tuple.keys, .values = defines_tuple.values, }, .loaders = .{ .extensions = loader_tuple.keys, .loaders = loader_tuple.values, }, .port = if (args.option("--port")) |port_str| std.fmt.parseInt(u16, port_str, 10) catch return error.InvalidPort else null, .serve = cmd == .DevCommand or (FeatureFlags.dev_only and cmd == .AutoCommand), .main_fields = args.options("--main-fields"), .generate_node_module_bundle = cmd == .BunCommand, .inject = args.options("--inject"), .extension_order = args.options("--extension-order"), .entry_points = undefined, .no_summary = args.flag("--no-summary"), .disable_hmr = args.flag("--disable-hmr"), }; ctx.positionals = args.positionals(); 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.?}); } const print_help = args.flag("--help"); if (print_help) { clap.help(Output.writer(), std.mem.span(params[0..public_params.len])) catch {}; Output.prettyln("\n-------\n\n", .{}); Output.flush(); HelpCommand.printWithReason(.explicit); Output.flush(); std.os.exit(0); } ctx.debug.dump_environment_variables = args.flag("--dump-environment-variables"); ctx.debug.fallback_only = args.flag("--disable-bun.js"); // var output_dir = args.option("--outdir"); var output_dir: ?string = null; var define_keys = defines_tuple.keys; var define_values = defines_tuple.values; var loader_keys = loader_tuple.keys; var loader_values = loader_tuple.values; var entry_points = args.positionals(); switch (comptime cmd) { .BunCommand => { if (entry_points.len > 0 and (strings.eqlComptime( entry_points[0], "bun", ))) { entry_points = entry_points[1..]; } }, .DevCommand => { if (entry_points.len > 0 and (strings.eqlComptime( entry_points[0], "dev", ) or strings.eqlComptime( entry_points[0], "d", ))) { entry_points = entry_points[1..]; } }, .BuildCommand => { if (entry_points.len > 0 and (strings.eqlComptime( entry_points[0], "build", ) or strings.eqlComptime( entry_points[0], "b", ))) { entry_points = entry_points[1..]; } }, .RunCommand => { if (entry_points.len > 0 and (strings.eqlComptime( entry_points[0], "run", ) or strings.eqlComptime( entry_points[0], "r", ))) { entry_points = entry_points[1..]; } }, else => {}, } const production = false; //args.flag("--production"); if (comptime cmd == .BuildCommand) { var write = entry_points.len > 1 or output_dir != null; if (write and output_dir == null) { var _paths = [_]string{ cwd, "out" }; output_dir = try std.fs.path.resolve(allocator, &_paths); } opts.write = write; } opts.entry_points = entry_points; 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") or production; const react_fast_refresh = switch (comptime cmd) { .BunCommand, .DevCommand => !(args.flag("--disable-react-fast-refresh") or jsx_production), else => true, }; opts.node_modules_bundle_path = args.option("--bunfile") 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 = args.option("--server-bunfile") 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; }; switch (comptime cmd) { .AutoCommand, .DevCommand, .BuildCommand, .BunCommand => { if (args.option("--public-dir")) |public_dir| { if (public_dir.len > 0) { opts.router = Api.RouteConfig{ .extensions = &.{}, .dir = &.{}, .static_dir = public_dir }; } } }, else => {}, } const ResolveMatcher = strings.ExactSizeMatcher(8); opts.resolve = Api.ResolveMode.lazy; switch (comptime cmd) { .BuildCommand => { // if (args.option("--resolve")) |_resolve| { // switch (ResolveMatcher.match(_resolve)) { // ResolveMatcher.case("disable") => { // opts.resolve = Api.ResolveMode.disable; // }, // ResolveMatcher.case("bundle") => { // opts.resolve = Api.ResolveMode.bundle; // }, // ResolveMatcher.case("dev") => { // opts.resolve = Api.ResolveMode.dev; // }, // ResolveMatcher.case("lazy") => { // opts.resolve = Api.ResolveMode.lazy; // }, // else => { // diag.name.long = "--resolve"; // diag.arg = _resolve; // try diag.report(Output.errorWriter(), error.InvalidResolveOption); // std.process.exit(1); // }, // } // } }, else => {}, } const PlatformMatcher = strings.ExactSizeMatcher(8); if (args.option("--platform")) |_platform| { switch (PlatformMatcher.match(_platform)) { PlatformMatcher.case("browser") => { opts.platform = Api.Platform.browser; }, PlatformMatcher.case("node") => { opts.platform = Api.Platform.node; }, PlatformMatcher.case("macro"), PlatformMatcher.case("bun") => { opts.platform = Api.Platform.bun; }, else => { diag.name.long = "--platform"; diag.arg = _platform; try diag.report(Output.errorWriter(), error.InvalidPlatform); std.process.exit(1); }, } } if (jsx_factory != null or jsx_fragment != null or jsx_import_source != null or jsx_runtime != null or jsx_production or !react_fast_refresh) { var default_factory = "".*; var default_fragment = "".*; var default_import_source = "".*; opts.jsx = Api.Jsx{ .factory = constStrToU8(jsx_factory orelse &default_factory), .fragment = constStrToU8(jsx_fragment orelse &default_fragment), .import_source = constStrToU8(jsx_import_source orelse &default_import_source), .runtime = if (jsx_runtime != null) try resolve_jsx_runtime(jsx_runtime.?) else Api.JsxRuntime.automatic, .development = !jsx_production, .react_fast_refresh = react_fast_refresh, }; } if (args.option("--use")) |entry| { opts.framework = Api.FrameworkConfig{ .package = entry, .development = !production, }; } if (cmd == .BunCommand or !FeatureFlags.dev_only) { if (entry_points.len == 0 and opts.framework == null and opts.node_modules_bundle_path == null) { return error.MissingEntryPoint; } } opts.output_dir = output_dir; return opts; } }; const AutoCommand = struct { pub fn exec(allocator: *std.mem.Allocator) !void { try HelpCommand.execWithReason(allocator, .invalid_command); } }; const InitCommand = struct { pub fn exec(allocator: *std.mem.Allocator) !void {} }; const HelpCommand = struct { pub fn exec(allocator: *std.mem.Allocator) !void { @setCold(true); execWithReason(allocator, .explicit); } pub const Reason = enum { explicit, invalid_command, }; pub fn printWithReason(comptime reason: Reason) void { var cwd_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; const cwd = std.os.getcwd(&cwd_buf) catch unreachable; const dirname = std.fs.path.basename(cwd); if (FeatureFlags.dev_only) { const fmt = \\> dev ./a.ts ./b.jsx Start a Bun Dev Server \\> bun ./a.ts ./b.jsx Bundle dependencies of input files into a .bun \\> run test Run a package.json script or executable \\> create next ./app Start a new project from a template (shorthand: c) \\> upgrade Get the latest version of Bun \\> discord Open Bun's Discord server \\> help Print this help menu \\ ; switch (reason) { .explicit => Output.pretty("Bun: a fast bundler, transpiler and task runner for web software.\n\n" ++ fmt, .{}), .invalid_command => Output.prettyError("Uh-oh not sure what to do with that command.\n\n" ++ fmt, .{}), } } else { const fmt = \\> init Setup Bun in \"{s}\" \\> dev ./a.ts ./b.jsx Start a Bun Dev Server \\* build ./a.ts ./b.jsx Make JavaScript-like code runnable & bundle CSS \\> create next ./app Start a new project from a template \\> bun ./a.ts ./b.jsx Bundle dependencies of input files into a .bun \\> run ./a.ts Run a JavaScript-like file with Bun.js \\> discord Open Bun's Discord server \\> upgrade Get the latest version of Bun \\> help Print this help menu \\ ; switch (reason) { .explicit => Output.pretty("Bun: a fast bundler, transpiler and task runner for web software.\n\n" ++ fmt, .{dirname}), .invalid_command => Output.prettyError("Uh-oh not sure what to do with that command.\n\n" ++ fmt, .{dirname}), } } Output.flush(); } pub fn execWithReason(allocator: *std.mem.Allocator, comptime reason: Reason) void { @setCold(true); printWithReason(reason); if (reason == .invalid_command) { std.process.exit(1); } } }; pub const PrintBundleCommand = struct { pub fn exec(ctx: Command.Context) !void { const entry_point = ctx.args.entry_points[0]; var out_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; var stdout = std.io.getStdOut(); var input = try std.fs.openFileAbsolute(try std.os.realpath(ctx.args.entry_points[0], &out_buffer), .{ .read = true }); const params = comptime [_]Arguments.ParamType{ clap.parseParam("--summary Peek inside the .bun") catch unreachable, }; var jsBundleArgs = clap.parse(clap.Help, ¶ms, .{ .allocator = ctx.allocator }) catch |err| { try NodeModuleBundle.printBundle(std.fs.File, input, @TypeOf(stdout), stdout); return; }; if (jsBundleArgs.flag("--summary")) { NodeModuleBundle.printSummaryFromDisk(std.fs.File, input, @TypeOf(stdout), stdout, ctx.allocator) catch {}; return; } try NodeModuleBundle.printBundle(std.fs.File, input, @TypeOf(stdout), stdout); } }; pub const Command = struct { pub const DebugOptions = struct { dump_environment_variables: bool = false, fallback_only: bool = false, silent: bool = false, }; pub const Context = struct { start_time: i128, args: Api.TransformOptions = std.mem.zeroes(Api.TransformOptions), log: *logger.Log, allocator: *std.mem.Allocator, positionals: []const string = &[_]string{}, debug: DebugOptions = DebugOptions{}, pub fn create(allocator: *std.mem.Allocator, log: *logger.Log, comptime command: Command.Tag) anyerror!Context { var ctx = Command.Context{ .args = std.mem.zeroes(Api.TransformOptions), .log = log, .start_time = start_time, .allocator = allocator, }; if (comptime command != Command.Tag.CreateCommand) { ctx.args = try Arguments.parse(allocator, &ctx, command); } return ctx; } }; pub fn which(allocator: *std.mem.Allocator) Tag { var args_iter = std.process.args(); // first one is the executable name const skipped = args_iter.skip(); if (!skipped) { return .AutoCommand; } var next_arg = (args_iter.next(allocator) orelse return .AutoCommand) catch unreachable; while (next_arg[0] == '-') { next_arg = (args_iter.next(allocator) orelse return .AutoCommand) catch unreachable; } const first_arg_name = std.mem.span(next_arg); const RootCommandMatcher = strings.ExactSizeMatcher(8); if (comptime FeatureFlags.dev_only) { return switch (RootCommandMatcher.match(first_arg_name)) { RootCommandMatcher.case("init") => .InitCommand, RootCommandMatcher.case("bun") => .BunCommand, RootCommandMatcher.case("discord") => .DiscordCommand, RootCommandMatcher.case("upgrade") => .UpgradeCommand, RootCommandMatcher.case("c"), RootCommandMatcher.case("create") => .CreateCommand, RootCommandMatcher.case("b"), RootCommandMatcher.case("build") => .BuildCommand, RootCommandMatcher.case("r"), RootCommandMatcher.case("run") => .RunCommand, RootCommandMatcher.case("d"), RootCommandMatcher.case("dev") => .DevCommand, RootCommandMatcher.case("help") => .HelpCommand, else => .AutoCommand, }; } else { return switch (RootCommandMatcher.match(first_arg_name)) { RootCommandMatcher.case("init") => .InitCommand, RootCommandMatcher.case("bun") => .BunCommand, RootCommandMatcher.case("discord") => .DiscordCommand, RootCommandMatcher.case("d"), RootCommandMatcher.case("dev") => .DevCommand, RootCommandMatcher.case("c"), RootCommandMatcher.case("create") => .CreateCommand, RootCommandMatcher.case("upgrade") => .UpgradeCommand, RootCommandMatcher.case("help") => .HelpCommand, else => .AutoCommand, }; } } pub fn start(allocator: *std.mem.Allocator, log: *logger.Log) !void { const tag = which(allocator); switch (tag) { .DiscordCommand => return try DiscordCommand.exec(allocator), .HelpCommand => return try HelpCommand.exec(allocator), .InitCommand => return try InitCommand.exec(allocator), else => {}, } switch (tag) { .BunCommand => { const ctx = try Command.Context.create(allocator, log, .BunCommand); try BunCommand.exec(ctx); }, .DevCommand => { const ctx = try Command.Context.create(allocator, log, .DevCommand); try DevCommand.exec(ctx); }, .BuildCommand => { const ctx = try Command.Context.create(allocator, log, .BuildCommand); try BuildCommand.exec(ctx); }, .CreateCommand => { const ctx = try Command.Context.create(allocator, log, .CreateCommand); var positionals: [2]string = undefined; var positional_i: usize = 0; var args = try std.process.argsAlloc(allocator); if (args.len > 2) { var remainder = args[2..]; var remainder_i: usize = 0; var i: usize = 0; while (remainder_i < remainder.len and positional_i < positionals.len) : (remainder_i += 1) { var slice = std.mem.trim(u8, std.mem.span(remainder[remainder_i]), " \t\n;"); if (slice.len > 0) { positionals[positional_i] = slice; positional_i += 1; } } } var positionals_ = positionals[0..positional_i]; try CreateCommand.exec(ctx, positionals_); return; }, .RunCommand => { const ctx = try Command.Context.create(allocator, log, .RunCommand); if (ctx.positionals.len > 0) { _ = try RunCommand.exec(ctx, false, true); } }, .UpgradeCommand => { const ctx = try Command.Context.create(allocator, log, .UpgradeCommand); try UpgradeCommand.exec(ctx); return; }, .AutoCommand => { var ctx = Command.Context.create(allocator, log, .AutoCommand) catch |e| { switch (e) { error.MissingEntryPoint => { HelpCommand.execWithReason(allocator, .explicit); return; }, else => { return e; }, } }; if (ctx.args.entry_points.len == 1 and std.mem.endsWith(u8, ctx.args.entry_points[0], ".bun")) { try PrintBundleCommand.exec(ctx); return; } if (ctx.positionals.len > 0 and (std.fs.path.extension(ctx.positionals[0]).len == 0)) { if (try RunCommand.exec(ctx, true, false)) { return; } } if (FeatureFlags.dev_only) { try DevCommand.exec(ctx); } else { try BuildCommand.exec(ctx); } }, else => unreachable, } } pub const Tag = enum { InitCommand, BunCommand, DevCommand, DiscordCommand, BuildCommand, RunCommand, AutoCommand, HelpCommand, CreateCommand, UpgradeCommand, }; };