diff options
author | 2021-12-18 20:02:33 -0800 | |
---|---|---|
committer | 2021-12-18 20:02:33 -0800 | |
commit | 501fab0befb92ac5d35048b959a11befaa3e3e5e (patch) | |
tree | 9ed032a372f9518e200be193122724aa2d6f63f4 /src/cli/create_command.zig | |
parent | 1f1c337eb6b1181c1e09adc204c4acefa28fc0e2 (diff) | |
download | bun-501fab0befb92ac5d35048b959a11befaa3e3e5e.tar.gz bun-501fab0befb92ac5d35048b959a11befaa3e3e5e.tar.zst bun-501fab0befb92ac5d35048b959a11befaa3e3e5e.zip |
2x - 5x faster `bun create`
Diffstat (limited to 'src/cli/create_command.zig')
-rw-r--r-- | src/cli/create_command.zig | 162 |
1 files changed, 49 insertions, 113 deletions
diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index e48745f69..2ac8005db 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -21,7 +21,8 @@ const bundler = @import("../bundler.zig"); const NodeModuleBundle = @import("../node_module_bundle.zig").NodeModuleBundle; const fs = @import("../fs.zig"); const URL = @import("../query_string_map.zig").URL; -const HTTPClient = @import("../http_client.zig"); +const HTTP = @import("http"); +const NetworkThread = @import("network_thread"); const ParseJSON = @import("../json_parser.zig").ParseJSON; const Archive = @import("../libarchive/libarchive.zig").Archive; const Zlib = @import("../zlib.zig"); @@ -186,9 +187,6 @@ const CreateOptions = struct { const params = [_]clap.Param(clap.Help){ clap.parseParam("--help Print this menu") catch unreachable, - clap.parseParam("--npm Use npm for tasks & install") catch unreachable, - clap.parseParam("--yarn Use yarn for tasks & install") catch unreachable, - clap.parseParam("--pnpm Use pnpm for tasks & install") 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, @@ -232,18 +230,6 @@ const CreateOptions = struct { opts.positionals = opts.positionals[1..]; } - if (args.flag("--npm")) { - opts.npm_client = NPMClient.Tag.npm; - } - - if (args.flag("--yarn")) { - opts.npm_client = NPMClient.Tag.yarn; - } - - if (args.flag("--pnpm")) { - opts.npm_client = NPMClient.Tag.pnpm; - } - opts.skip_package_json = args.flag("--no-package-json"); opts.verbose = args.flag("--verbose"); @@ -263,6 +249,7 @@ pub const CreateCommand = struct { pub fn exec(ctx: Command.Context, positionals_: []const []const u8) !void { Global.configureAllocator(.{ .long_running = false }); + try NetworkThread.init(); var create_options = try CreateOptions.parse(ctx, false); const positionals = create_options.positionals; @@ -785,11 +772,8 @@ pub const CreateCommand = struct { .{ "@parcel/core", void{} }, .{ "@swc/cli", void{} }, .{ "@swc/core", void{} }, - .{ "@vitejs/plugin-react", void{} }, .{ "@webpack/cli", void{} }, .{ "react-scripts", void{} }, - .{ "swc", void{} }, - .{ "vite", void{} }, .{ "webpack-cli", void{} }, .{ "webpack", void{} }, @@ -1504,22 +1488,10 @@ pub const CreateCommand = struct { } if (!create_options.skip_install) { - if (env_loader.map.get("NPM_CLIENT")) |npm_client_bin| { - npm_client_ = NPMClient{ .tag = .npm, .bin = npm_client_bin }; - } else if (PATH.len > 0) { - var realpath_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; - - if (create_options.npm_client) |tag| { - if (which(&realpath_buf, PATH, destination, @tagName(tag))) |bin| { - npm_client_ = NPMClient{ .tag = tag, .bin = try ctx.allocator.dupe(u8, bin) }; - } - } else if (try NPMClient.detect(ctx.allocator, &realpath_buf, PATH, destination, true)) |npmclient| { - npm_client_ = NPMClient{ - .bin = try ctx.allocator.dupe(u8, npmclient.bin), - .tag = npmclient.tag, - }; - } - } + npm_client_ = NPMClient{ + .tag = .bun, + .bin = try std.fs.selfExePathAlloc(ctx.allocator), + }; } if (npm_client_ != null and preinstall_tasks.items.len > 0) { @@ -1530,13 +1502,7 @@ pub const CreateCommand = struct { if (npm_client_) |npm_client| { const start_time = std.time.nanoTimestamp(); - const install_args_ = [_]string{ npm_client.bin, "install", "--loglevel=error", "--no-fund", "--no-audit" }; - const len: usize = switch (npm_client.tag) { - .npm => install_args_.len, - else => 2, - }; - - const install_args = install_args_[0..len]; + const install_args = &[_]string{ npm_client.bin, "install" }; Output.flush(); Output.pretty("\n<r><d>$ <b><cyan>{s}<r><d> install", .{@tagName(npm_client.tag)}); var writer = Output.writer(); @@ -1568,8 +1534,6 @@ pub const CreateCommand = struct { var term = try process.spawnAndWait(); _ = process.kill() catch undefined; - } else if (!create_options.skip_install) { - progress.log("Failed to detect npm client. Tried pnpm, yarn, and npm.\n", .{}); } if (postinstall_tasks.items.len > 0) { @@ -1709,43 +1673,6 @@ const Commands = .{ }; const picohttp = @import("picohttp"); -const PackageDownloadThread = struct { - thread: std.Thread, - client: HTTPClient, - tarball_url: string, - allocator: *std.mem.Allocator, - buffer: MutableString, - done: std.atomic.Atomic(u32), - response: picohttp.Response = undefined, - - pub fn threadHandler(this: *PackageDownloadThread) !void { - this.done.store(0, .Release); - this.response = try this.client.send("", &this.buffer); - this.done.store(1, .Release); - Futex.wake(&this.done, 1); - } - - pub fn spawn(allocator: *std.mem.Allocator, tarball_url: string, progress_node: *std.Progress.Node) !*PackageDownloadThread { - var download = try allocator.create(PackageDownloadThread); - download.* = PackageDownloadThread{ - .allocator = allocator, - .client = HTTPClient.init(allocator, .GET, URL.parse(tarball_url), .{}, ""), - .tarball_url = tarball_url, - .buffer = try MutableString.init(allocator, 1024), - .done = std.atomic.Atomic(u32).init(0), - .thread = undefined, - }; - - if (Output.enable_ansi_colors) { - download.client.progress_node = progress_node; - } - - download.thread = try std.Thread.spawn(.{}, threadHandler, .{download}); - - return download; - } -}; - pub const DownloadedExample = struct { tarball_bytes: MutableString, example: Example, @@ -1764,7 +1691,6 @@ pub const Example = struct { local_folder, }; - var client: HTTPClient = undefined; const examples_url: string = "https://registry.npmjs.org/bun-examples-all/latest"; var url: URL = undefined; pub const timeout: u32 = 6000; @@ -1873,7 +1799,6 @@ pub const Example = struct { var owner_i = std.mem.indexOfScalar(u8, name, '/').?; var owner = name[0..owner_i]; var repository = name[owner_i + 1 ..]; - var mutable = try MutableString.init(ctx.allocator, 8096); if (std.mem.indexOfScalar(u8, repository, '/')) |i| { repository = repository[0..i]; @@ -1919,17 +1844,16 @@ pub const Example = struct { } } - client = HTTPClient.init( - ctx.allocator, - .GET, - api_url, - header_entries, - headers_buf, - ); - client.timeout = timeout; - client.progress_node = progress; + var mutable = try ctx.allocator.create(MutableString); + mutable.* = try MutableString.init(ctx.allocator, 8096); - var response = try client.send("", &mutable); + var request_body = try MutableString.init(ctx.allocator, 0); + + // ensure very stable memory address + var async_http: *HTTP.AsyncHTTP = ctx.allocator.create(HTTP.AsyncHTTP) catch unreachable; + async_http.* = try HTTP.AsyncHTTP.init(ctx.allocator, .GET, api_url, header_entries, headers_buf, mutable, &request_body, 60 * std.time.ns_per_min); + async_http.client.progress_node = progress; + const response = try async_http.sendSync(); switch (response.status_code) { 404 => return error.GitHubRepositoryNotFound, @@ -1977,7 +1901,7 @@ pub const Example = struct { Global.crash(); } - return mutable; + return mutable.*; } pub fn fetch(ctx: Command.Context, name: string, refresher: *std.Progress, progress: *std.Progress.Node) !MutableString { @@ -1986,13 +1910,17 @@ pub const Example = struct { const example_start = std.time.nanoTimestamp(); var url_buf: [1024]u8 = undefined; - var mutable = try MutableString.init(ctx.allocator, 2048); + var mutable = try ctx.allocator.create(MutableString); + mutable.* = try MutableString.init(ctx.allocator, 2048); + var request_body = try MutableString.init(ctx.allocator, 0); url = URL.parse(try std.fmt.bufPrint(&url_buf, "https://registry.npmjs.org/@bun-examples/{s}/latest", .{name})); - client = HTTPClient.init(ctx.allocator, .GET, url, .{}, ""); - client.timeout = timeout; - client.progress_node = progress; - var response = try client.send("", &mutable); + + // ensure very stable memory address + var async_http: *HTTP.AsyncHTTP = ctx.allocator.create(HTTP.AsyncHTTP) catch unreachable; + async_http.* = try HTTP.AsyncHTTP.init(ctx.allocator, .GET, url, .{}, "", mutable, &request_body, 60 * std.time.ns_per_min); + async_http.client.progress_node = progress; + var response = try async_http.sendSync(); switch (response.status_code) { 404 => return error.ExampleNotFound, @@ -2044,7 +1972,7 @@ pub const Example = struct { if (q.expr.asProperty("tarball")) |p| { if (p.expr.asString(ctx.allocator)) |s| { if (s.len > 0 and (strings.startsWith(s, "https://") or strings.startsWith(s, "http://"))) { - break :brk s; + break :brk ctx.allocator.dupe(u8, s) catch unreachable; } } } @@ -2061,40 +1989,48 @@ pub const Example = struct { progress.name = "Downloading tarball"; refresher.refresh(); - var thread: *PackageDownloadThread = try PackageDownloadThread.spawn(ctx.allocator, tarball_url, progress); + // reuse mutable buffer + // safe because the only thing we care about is the tarball url + mutable.reset(); + + // ensure very stable memory address + async_http.* = try HTTP.AsyncHTTP.init(ctx.allocator, .GET, URL.parse(tarball_url), .{}, "", mutable, &request_body, 60 * std.time.ns_per_min); + async_http.client.progress_node = progress; + refresher.maybeRefresh(); - while (thread.done.load(.Acquire) == 0) { - Futex.wait(&thread.done, 1, std.time.ns_per_ms * 10000) catch {}; - } + response = try async_http.sendSync(); refresher.maybeRefresh(); - if (thread.response.status_code != 200) { + if (response.status_code != 200) { progress.end(); refresher.refresh(); - Output.prettyErrorln("Error fetching tarball: <r><red>{d}<r>", .{thread.response.status_code}); + Output.prettyErrorln("Error fetching tarball: <r><red>{d}<r>", .{response.status_code}); Output.flush(); std.os.exit(1); } refresher.refresh(); - thread.thread.join(); - return thread.buffer; + return mutable.*; } pub fn fetchAll(ctx: Command.Context, progress_node: ?*std.Progress.Node) ![]Example { url = URL.parse(examples_url); - client = HTTPClient.init(ctx.allocator, .GET, url, .{}, ""); - client.timeout = timeout; + + var async_http: *HTTP.AsyncHTTP = ctx.allocator.create(HTTP.AsyncHTTP) catch unreachable; + var request_body = try MutableString.init(ctx.allocator, 0); + var mutable = try ctx.allocator.create(MutableString); + mutable.* = try MutableString.init(ctx.allocator, 2048); + + async_http.* = try HTTP.AsyncHTTP.init(ctx.allocator, .GET, url, .{}, "", mutable, &request_body, 60 * std.time.ns_per_min); if (Output.enable_ansi_colors) { - client.progress_node = progress_node; + async_http.client.progress_node = progress_node; } - var mutable: MutableString = try MutableString.init(ctx.allocator, 1024); - var response = client.send("", &mutable) catch |err| { + const response = async_http.sendSync() catch |err| { switch (err) { error.WouldBlock => { Output.prettyErrorln("Request timed out while trying to fetch examples list. Please try again", .{}); |