aboutsummaryrefslogtreecommitdiff
path: root/src/cli/build_command.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli/build_command.zig')
-rw-r--r--src/cli/build_command.zig314
1 files changed, 192 insertions, 122 deletions
diff --git a/src/cli/build_command.zig b/src/cli/build_command.zig
index 92d2d5b3a..1b8e5c632 100644
--- a/src/cli/build_command.zig
+++ b/src/cli/build_command.zig
@@ -1,167 +1,237 @@
+const std = @import("std");
+const Command = @import("../cli.zig").Command;
const bun = @import("bun");
const string = bun.string;
const Output = bun.Output;
const Global = bun.Global;
const Environment = bun.Environment;
-const FeatureFlags = bun.FeatureFlags;
const strings = bun.strings;
const MutableString = bun.MutableString;
const stringZ = bun.stringZ;
const default_allocator = bun.default_allocator;
const C = bun.C;
-const std = @import("std");
const lex = bun.js_lexer;
const logger = @import("bun").logger;
const options = @import("../options.zig");
const js_parser = bun.js_parser;
+const json_parser = bun.JSON;
+const js_printer = bun.js_printer;
const js_ast = bun.JSAst;
const linker = @import("../linker.zig");
-const allocators = @import("../allocators.zig");
const sync = @import("../sync.zig");
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 bundler = bun.bundler;
const NodeModuleBundle = @import("../node_module_bundle.zig").NodeModuleBundle;
+const GenerateNodeModuleBundle = @import("../bundler/generate_node_modules_bundle.zig");
+const DotEnv = @import("../env_loader.zig");
+
const fs = @import("../fs.zig");
-const constStrToU8 = bun.constStrToU8;
+const Router = @import("../router.zig");
+const BundleV2 = @import("../bundler/bundle_v2.zig").BundleV2;
+var estimated_input_lines_of_code_: usize = undefined;
pub const BuildCommand = struct {
- pub fn exec(ctx: Command.Context) !void {
- var result: options.TransformResult = undefined;
- switch (ctx.args.resolve orelse Api.ResolveMode.dev) {
- .lazy => {
- result = try bundler.Bundler.bundle(
- ctx.allocator,
- ctx.log,
- ctx.args,
- );
- },
- else => {
- result = try bundler.Bundler.bundle(
- ctx.allocator,
- ctx.log,
- ctx.args,
- );
- },
- }
- var did_write = false;
+ pub fn exec(
+ ctx: Command.Context,
+ ) !void {
+ Global.configureAllocator(.{ .long_running = true });
+ var allocator = ctx.allocator;
+ var log = ctx.log;
+ estimated_input_lines_of_code_ = 0;
+
+ var this_bundler = try bundler.Bundler.init(allocator, log, ctx.args, null, null);
+ this_bundler.options.entry_names = ctx.bundler_options.entry_names;
+ this_bundler.resolver.opts.entry_names = ctx.bundler_options.entry_names;
+ this_bundler.options.output_dir = ctx.bundler_options.outdir;
+ this_bundler.resolver.opts.output_dir = ctx.bundler_options.outdir;
+ this_bundler.options.react_server_components = ctx.bundler_options.react_server_components;
+ this_bundler.resolver.opts.react_server_components = ctx.bundler_options.react_server_components;
+ this_bundler.options.code_splitting = ctx.bundler_options.code_splitting;
+ this_bundler.resolver.opts.code_splitting = ctx.bundler_options.code_splitting;
+
+ this_bundler.configureLinker();
+
+ // This step is optional
+ // If it fails for any reason, ignore it and continue bundling
+ // This is partially a workaround for the 'error.MissingRoutesDir' error
+ this_bundler.configureRouter(true) catch {
+ this_bundler.options.routes.routes_enabled = false;
+ this_bundler.options.framework = null;
+ if (this_bundler.router) |*router| {
+ router.config.routes_enabled = false;
+ router.config.single_page_app_routing = false;
+ router.config.static_dir_enabled = false;
+ this_bundler.router = null;
+ }
+ this_bundler.options.node_modules_bundle = null;
+ this_bundler.options.node_modules_bundle_pretty_path = "";
+ this_bundler.options.node_modules_bundle_url = "";
+ };
- defer Output.flush();
- var writer = Output.errorWriter();
- var err_writer = writer;
+ if (ctx.debug.macros) |macros| {
+ this_bundler.options.macro_remap = macros;
+ }
- var open_file_limit: usize = fs.FileSystem.RealFS.Limit.handles;
- if (ctx.args.write) |write| {
- if (write) {
- const root_dir = result.root_dir orelse unreachable;
+ // var env_loader = this_bundler.env;
- var all_paths = try ctx.allocator.alloc([]const u8, result.output_files.len);
- var max_path_len: usize = 0;
- for (result.output_files, 0..) |f, i| {
- all_paths[i] = f.input.text;
- }
+ if (ctx.debug.dump_environment_variables) {
+ this_bundler.dumpEnvironmentVariables();
+ return;
+ }
- var from_path = resolve_path.longestCommonPath(all_paths);
+ if (ctx.debug.dump_limits) {
+ fs.FileSystem.printLimits();
+ Global.exit(0);
+ return;
+ }
- for (result.output_files) |f| {
- max_path_len = std.math.max(
- std.math.max(from_path.len, f.input.text.len) + 2 - from_path.len,
- max_path_len,
- );
+ // var generated_server = false;
+ // if (this_bundler.options.framework) |*framework| {
+ // if (framework.toAPI(allocator, this_bundler.fs.top_level_dir) catch null) |_server_conf| {
+ // ServerBundleGeneratorThread.generate(
+ // log,
+ // env_loader,
+ // ctx,
+ // server_bundle_filepath,
+ // _server_conf,
+ // loaded_route_config,
+ // this_bundler.router,
+ // );
+ // generated_server = true;
+
+ // if (log.msgs.items.len > 0) {
+ // try log.printForLogLevel(Output.errorWriter());
+ // log.* = logger.Log.init(allocator);
+ // Output.flush();
+ // }
+ // }
+ // }
+
+ {
+
+ // Always generate the client-only bundle
+ // we can revisit this decision if people ask
+ const output_files = BundleV2.generate(
+ &this_bundler,
+ allocator,
+ &estimated_input_lines_of_code_,
+ ctx.debug.package_bundle_map,
+ bun.JSC.AnyEventLoop.init(ctx.allocator),
+ std.crypto.random.int(u64),
+ ctx.debug.hot_reload == .watch,
+ ) catch |err| {
+ if (log.msgs.items.len > 0) {
+ try log.printForLogLevel(Output.errorWriter());
+ } else {
+ try Output.errorWriter().print("error: {s}", .{@errorName(err)});
}
- did_write = true;
-
- // On posix, file handles automatically close on process exit by the OS
- // Closing files shows up in profiling.
- // So don't do that unless we actually need to.
- // const do_we_need_to_close = !FeatureFlags.store_file_descriptors or (@intCast(usize, root_dir.fd) + open_file_limit) < result.output_files.len;
-
- var filepath_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
- filepath_buf[0] = '.';
- filepath_buf[1] = '/';
-
- for (result.output_files) |f| {
- var rel_path: []const u8 = undefined;
- switch (f.value) {
- // easy mode: write the buffer
- .buffer => |value| {
- rel_path = resolve_path.relative(from_path, f.input.text);
-
- try root_dir.writeFile(rel_path, value);
- },
- .move => |value| {
- // const primary = f.input.text[from_path.len..];
- // bun.copy(u8, filepath_buf[2..], primary);
- // rel_path = filepath_buf[0 .. primary.len + 2];
- rel_path = value.pathname;
-
- // try f.moveTo(result.outbase, constStrToU8(rel_path), root_dir.fd);
- },
- .copy => |value| {
- rel_path = value.pathname;
-
- try f.copyTo(result.outbase, constStrToU8(rel_path), root_dir.fd);
- },
- .noop => {},
- .pending => unreachable,
+ Output.flush();
+ exitOrWatch(1, ctx.debug.hot_reload == .watch);
+ unreachable;
+ };
+
+ {
+ dump: {
+ defer Output.flush();
+ var writer = Output.errorWriter();
+ var output_dir = this_bundler.options.output_dir;
+ if (ctx.bundler_options.outfile.len > 0 and output_files.items.len == 1 and output_files.items[0].value == .buffer) {
+ output_dir = std.fs.path.dirname(ctx.bundler_options.outfile) orelse ".";
+ output_files.items[0].input.text = std.fs.path.basename(ctx.bundler_options.outfile);
}
- // Print summary
- _ = try writer.write("\n");
- const padding_count = 2 + (std.math.max(rel_path.len, max_path_len) - rel_path.len);
- try writer.writeByteNTimes(' ', 2);
- try writer.writeAll(rel_path);
- try writer.writeByteNTimes(' ', padding_count);
- const size = @intToFloat(f64, f.size) / 1000.0;
- try std.fmt.formatFloatDecimal(size, .{ .precision = 2 }, writer);
- try writer.writeAll(" KB\n");
- }
- }
- }
+ if (output_dir.len == 0 and output_files.items.len == 1 and output_files.items[0].value == .buffer) {
+ try writer.writeAll(output_files.items[0].value.buffer);
+ break :dump;
+ }
- if (Environment.isDebug) {
- err_writer.print("\nExpr count: {d}\n", .{js_ast.Expr.icount}) catch {};
- err_writer.print("Stmt count: {d}\n", .{js_ast.Stmt.icount}) catch {};
- err_writer.print("Binding count: {d}\n", .{js_ast.Binding.icount}) catch {};
- err_writer.print("File Descriptors: {d} / {d}\n", .{
- fs.FileSystem.max_fd,
- open_file_limit,
- }) catch {};
- }
+ const root_path = output_dir;
+ const root_dir = try std.fs.cwd().makeOpenPathIterable(root_path, .{});
+ var all_paths = try ctx.allocator.alloc([]const u8, output_files.items.len);
+ var max_path_len: usize = 0;
+ for (all_paths, output_files.items) |*dest, src| {
+ dest.* = src.input.text;
+ }
- if (Output.enable_ansi_colors) {
- for (result.errors) |err| {
- try err.writeFormat(err_writer, true);
- _ = try err_writer.write("\n");
- }
+ var from_path = resolve_path.longestCommonPath(all_paths);
- for (result.warnings) |err| {
- try err.writeFormat(err_writer, true);
- _ = try err_writer.write("\n");
- }
- } else {
- for (result.errors) |err| {
- try err.writeFormat(err_writer, false);
- _ = try err_writer.write("\n");
- }
+ for (output_files.items) |f| {
+ max_path_len = std.math.max(
+ std.math.max(from_path.len, f.input.text.len) + 2 - from_path.len,
+ max_path_len,
+ );
+ }
- for (result.warnings) |err| {
- try err.writeFormat(err_writer, false);
- _ = try err_writer.write("\n");
+ // On posix, file handles automatically close on process exit by the OS
+ // Closing files shows up in profiling.
+ // So don't do that unless we actually need to.
+ // const do_we_need_to_close = !FeatureFlags.store_file_descriptors or (@intCast(usize, root_dir.fd) + open_file_limit) < output_files.items.len;
+
+ var filepath_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
+ filepath_buf[0] = '.';
+ filepath_buf[1] = '/';
+
+ for (output_files.items) |f| {
+ var rel_path: []const u8 = undefined;
+ switch (f.value) {
+ // easy mode: write the buffer
+ .buffer => |value| {
+ rel_path = f.input.text;
+ if (f.input.text.len > from_path.len) {
+ rel_path = resolve_path.relative(from_path, f.input.text);
+ if (std.fs.path.dirname(rel_path)) |parent| {
+ if (parent.len > root_path.len) {
+ try root_dir.dir.makePath(parent);
+ }
+ }
+ }
+ try root_dir.dir.writeFile(rel_path, value);
+ },
+ .move => |value| {
+ // const primary = f.input.text[from_path.len..];
+ // bun.copy(u8, filepath_buf[2..], primary);
+ // rel_path = filepath_buf[0 .. primary.len + 2];
+ rel_path = value.pathname;
+
+ // try f.moveTo(result.outbase, constStrToU8(rel_path), root_dir.fd);
+ },
+ .copy => |value| {
+ rel_path = value.pathname;
+
+ try f.copyTo(root_path, bun.constStrToU8(rel_path), root_dir.dir.fd);
+ },
+ .noop => {},
+ .pending => unreachable,
+ }
+
+ // Print summary
+ _ = try writer.write("\n");
+ const padding_count = 2 + (std.math.max(rel_path.len, max_path_len) - rel_path.len);
+ try writer.writeByteNTimes(' ', 2);
+ try writer.writeAll(rel_path);
+ try writer.writeByteNTimes(' ', padding_count);
+ const size = @intToFloat(f64, f.size) / 1000.0;
+ try std.fmt.formatFloatDecimal(size, .{ .precision = 2 }, writer);
+ try writer.writeAll(" KB\n");
+ }
+ }
}
- }
-
- const duration = std.time.nanoTimestamp() - ctx.start_time;
-
- if (did_write and duration < @as(i128, @as(i128, std.time.ns_per_s) * @as(i128, 2))) {
- var elapsed = @divTrunc(duration, @as(i128, std.time.ns_per_ms));
- try err_writer.print("\nCompleted in {d}ms", .{elapsed});
+ try log.printForLogLevel(Output.errorWriter());
+ exitOrWatch(0, ctx.debug.hot_reload == .watch);
}
}
};
+
+fn exitOrWatch(code: u8, watch: bool) void {
+ if (watch) {
+ // the watcher thread will exit the process
+ std.time.sleep(std.math.maxInt(u64) - 1);
+ }
+ Global.exit(code);
+}