This commit is contained in:
Jarred Sumner
2021-10-15 19:44:53 -07:00
parent 7e159cb5cd
commit 10696680ff
13 changed files with 2912 additions and 213 deletions

View File

@@ -548,20 +548,30 @@ Any command that starts with `"bun "` will be run without npm, relying on the fi
When you run `bun create ${template} ${destination}`, here's what happens:
IF remote template
1. GET `registry.npmjs.org/@bun-examples/${template}/latest` and parse it
2. GET `registry.npmjs.org/@bun-examples/${template}/-/${template}-${latestVersion}.tgz`
3. Decompress & extract `${template}-${latestVersion}.tgz` into `${destination}`
- If there are files that would overwrite, warn and exit unless `--force` is passed
4. Parse the `package.json` (again!), update `name` to be `${basename(destination)}`, remove the `bun-create` section from the `package.json` and save updated `package.json` to disk
ELSE IF local template
1. Open local template folder
2. Delete destination directory recursively
3. Copy files recursively using the fastest system calls available (on macOS `fcopyfile` and Linux, `copy_file_range`). Do not copy or traverse into `node_modules` folder if exists (this alone makes it faster than `cp`)
4. Parse the `package.json` (again!), update `name` to be `${basename(destination)}`, remove the `bun-create` section from the `package.json` and save the updated `package.json` to disk.
5. Auto-detect the npm client, preferring `pnpm`, `yarn` (v1), and lastly `npm`
6. Run any tasks defined in `"bun-create": { "preinstall" }` with the npm client
7. Run `${npmClient} install`
7. Run `${npmClient} install` unless `--no-install` is passed OR no dependencies are in package.json
8. Run any tasks defined in `"bun-create": { "preinstall" }` with the npm client
9. Run `git init; git add -A .; git commit -am "Initial Commit";`.
9. Run `git init; git add -A .; git commit -am "Initial Commit";`
- Rename `gitignore` to `.gitignore`. NPM automatically removes `.gitignore` files from appearing in packages.
- If there are dependencies, this runs in a separate thread concurrently while node_modules are being installed
- Using libgit2 if available was tested and performed roughly 3x slower in microbenchmarks
10. Done

View File

@@ -165,21 +165,21 @@ pub fn main() anyerror!void {
var client = HTTPClient.init(default_allocator, args.method, args.url, args.headers, args.headers_buf);
client.verbose = args.verbose;
client.disable_shutdown = args.turbo;
var body_out_str = try MutableString.init(default_allocator, 1024);
var response = try client.send(args.body, &body_out_str);
switch (response.status_code) {
200, 302 => {},
else => {
if (!client.verbose) {
Output.prettyErrorln("{}", .{response});
}
},
}
Output.flush();
Output.disableBuffering();
try Output.writer().writeAll(body_out_str.list.items);
Output.enableBuffering();
switch (response.status_code) {
200, 302 => {},
else => {
if (!client.verbose) {
// Output.flush();
// Output.prettyErrorln("Response: {}", .{response});
}
},
}
}

View File

@@ -272,6 +272,11 @@ pub fn BSSStringList(comptime _count: usize, comptime _item_length: usize) type
return constStrToU8(slice);
}
pub fn appendMutable(self: *Self, comptime AppendType: type, _value: AppendType) ![]u8 {
const appended = try @call(.{ .modifier = .always_inline }, append, .{ self, AppendType, _value });
return constStrToU8(appended);
}
pub fn append(self: *Self, comptime AppendType: type, _value: AppendType) ![]const u8 {
mutex.lock();
defer mutex.unlock();
@@ -302,7 +307,7 @@ pub fn BSSStringList(comptime _count: usize, comptime _item_length: usize) type
) ![]const u8 {
const value_len: usize = brk: {
switch (comptime AppendType) {
[]const u8, []u8 => {
[]const u8, []u8, [:0]const u8, [:0]u8 => {
break :brk _value.len;
},
else => {
@@ -322,7 +327,7 @@ pub fn BSSStringList(comptime _count: usize, comptime _item_length: usize) type
backing_buf_used += value_len;
switch (AppendType) {
[]const u8, []u8 => {
[]const u8, []u8, [:0]const u8, [:0]u8 => {
std.mem.copy(u8, backing_buf[start .. backing_buf_used - 1], _value);
backing_buf[backing_buf_used - 1] = 0;
},
@@ -341,7 +346,7 @@ pub fn BSSStringList(comptime _count: usize, comptime _item_length: usize) type
var value_buf = try self.allocator.alloc(u8, value_len);
switch (comptime AppendType) {
[]const u8, []u8 => {
[]const u8, []u8, [:0]const u8, [:0]u8 => {
std.mem.copy(u8, value_buf, _value);
},
else => {

View File

@@ -17,8 +17,6 @@ const ImportRecord = import_record.ImportRecord;
pub const FsCacheEntry = struct {
contents: string,
fd: StoredFileDescriptorType = 0,
// Null means its not usable
mod_key: ?fs.FileSystem.Implementation.ModKey = null,
pub fn deinit(entry: *FsCacheEntry, allocator: *std.mem.Allocator) void {
if (entry.contents.len > 0) {
@@ -66,11 +64,10 @@ pub const Fs = struct {
) !Entry {
var rfs = _fs.fs;
var file_handle: std.fs.File = if (_file_handle) |__file| std.fs.File{ .handle = __file } else undefined;
if (_file_handle == null) {
file_handle = try std.fs.openFileAbsoluteZ(path, .{ .read = true });
}
const file_handle: std.fs.File = if (_file_handle) |__file|
std.fs.File{ .handle = __file }
else
try std.fs.openFileAbsoluteZ(path, .{ .read = true });
defer {
if (rfs.needToCloseFiles() and _file_handle == null) {
@@ -78,42 +75,15 @@ pub const Fs = struct {
}
}
// If the file's modification key hasn't changed since it was cached, assume
// the contents of the file are also the same and skip reading the file.
var mod_key: ?fs.FileSystem.Implementation.ModKey = rfs.modKeyWithFile(path, file_handle) catch |err| handler: {
switch (err) {
error.FileNotFound, error.AccessDenied => {
return err;
},
else => {
if (isDebug) {
Output.printError("modkey error: {s}", .{@errorName(err)});
}
break :handler null;
},
const file = rfs.readFileWithHandle(path, null, file_handle, true, shared) catch |err| {
if (comptime isDebug) {
Output.printError("{s}: readFile error -- {s}", .{ path, @errorName(err) });
}
return err;
};
var file: fs.File = undefined;
if (mod_key) |modk| {
file = rfs.readFileWithHandle(path, modk.size, file_handle, true, shared) catch |err| {
if (isDebug) {
Output.printError("{s}: readFile error -- {s}", .{ path, @errorName(err) });
}
return err;
};
} else {
file = rfs.readFileWithHandle(path, null, file_handle, true, shared) catch |err| {
if (isDebug) {
Output.printError("{s}: readFile error -- {s}", .{ path, @errorName(err) });
}
return err;
};
}
return Entry{
.contents = file.contents,
.mod_key = mod_key,
.fd = if (FeatureFlags.store_file_descriptors) file_handle.handle else 0,
};
}
@@ -156,42 +126,15 @@ pub const Fs = struct {
}
}
// If the file's modification key hasn't changed since it was cached, assume
// the contents of the file are also the same and skip reading the file.
var mod_key: ?fs.FileSystem.Implementation.ModKey = rfs.modKeyWithFile(path, file_handle) catch |err| handler: {
switch (err) {
error.FileNotFound, error.AccessDenied => {
return err;
},
else => {
if (isDebug) {
Output.printError("modkey error: {s}", .{@errorName(err)});
}
break :handler null;
},
const file = rfs.readFileWithHandle(path, null, file_handle, use_shared_buffer, &c.shared_buffer) catch |err| {
if (isDebug) {
Output.printError("{s}: readFile error -- {s}", .{ path, @errorName(err) });
}
return err;
};
var file: fs.File = undefined;
if (mod_key) |modk| {
file = rfs.readFileWithHandle(path, modk.size, file_handle, use_shared_buffer, &c.shared_buffer) catch |err| {
if (isDebug) {
Output.printError("{s}: readFile error -- {s}", .{ path, @errorName(err) });
}
return err;
};
} else {
file = rfs.readFileWithHandle(path, null, file_handle, use_shared_buffer, &c.shared_buffer) catch |err| {
if (isDebug) {
Output.printError("{s}: readFile error -- {s}", .{ path, @errorName(err) });
}
return err;
};
}
return Entry{
.contents = file.contents,
.mod_key = mod_key,
.fd = if (FeatureFlags.store_file_descriptors) file_handle.handle else 0,
};
}

View File

@@ -30,10 +30,112 @@ const DotEnv = @import("../env_loader.zig");
const NPMClient = @import("../which_npm_client.zig").NPMClient;
const which = @import("../which.zig").which;
const clap = @import("clap");
const Lock = @import("../lock.zig").Lock;
const CopyFile = @import("../copy_file.zig");
var bun_path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const libgit2_basename = switch (std.builtin.os.tag) {
.linux => "libgit2.so.26",
.macos => "libgit2.dylib",
.windows => "libgit2.dll",
else => "libgit2.so",
};
const global_libgit2_paths = switch (std.builtin.os.tag) {
.macos => if (Environment.isAarch64)
&[][:0]const u8{
"/opt/homebrew/lib/libgit2.dylib",
"/opt/homebrew/libgit2/lib/libgit2.dylib",
"/usr/local/lib/libgit2.dylib",
"/usr/local/opt/libgit2/lib/libgit2.dylib",
}
else
&[_][:0]const u8{
"/usr/local/lib/libgit2.dylib",
"/usr/local/opt/libgit2/lib/libgit2.dylib",
},
else => &[_][:0]const u8{},
};
// the standard library function for this returns false when statically linking
fn getSelfExeSharedLibPaths(allocator: *std.mem.Allocator) error{OutOfMemory}![][:0]u8 {
const List = std.ArrayList([:0]u8);
const os = std.os;
const Allocator = std.mem.Allocator;
const builtin = std.builtin;
const mem = std.mem;
switch (builtin.os.tag) {
.linux,
.freebsd,
.netbsd,
.dragonfly,
.openbsd,
=> {
var paths = List.init(allocator);
errdefer {
const slice = paths.toOwnedSlice();
for (slice) |item| {
allocator.free(item);
}
allocator.free(slice);
}
try os.dl_iterate_phdr(&paths, error{OutOfMemory}, struct {
fn callback(info: *os.dl_phdr_info, size: usize, list: *List) !void {
_ = size;
const name = info.dlpi_name orelse return;
if (name[0] == '/') {
const item = try list.allocator.dupeZ(u8, mem.spanZ(name));
errdefer list.allocator.free(item);
try list.append(item);
}
}
}.callback);
return paths.toOwnedSlice();
},
.macos, .ios, .watchos, .tvos => {
var paths = List.init(allocator);
errdefer {
const slice = paths.toOwnedSlice();
for (slice) |item| {
allocator.free(item);
}
allocator.free(slice);
}
const img_count = std.c._dyld_image_count();
var i: u32 = 0;
while (i < img_count) : (i += 1) {
const name = std.c._dyld_get_image_name(i);
const item = try allocator.dupeZ(u8, mem.spanZ(name));
errdefer allocator.free(item);
try paths.append(item);
}
return paths.toOwnedSlice();
},
// revisit if Haiku implements dl_iterat_phdr (https://dev.haiku-os.org/ticket/15743)
.haiku => {
var paths = List.init(allocator);
errdefer {
const slice = paths.toOwnedSlice();
for (slice) |item| {
allocator.free(item);
}
allocator.free(slice);
}
var b = "/boot/system/runtime_loader";
const item = try allocator.dupeZ(u8, mem.spanZ(b));
errdefer allocator.free(item);
try paths.append(item);
return paths.toOwnedSlice();
},
else => @compileError("getSelfExeSharedLibPaths unimplemented for this target"),
}
}
const skip_dirs = &[_]string{ "node_modules", ".git" };
const skip_files = &[_]string{
"package-lock.json",
@@ -136,6 +238,7 @@ const CreateOptions = struct {
skip_install: bool = false,
overwrite: bool = false,
skip_git: bool = false,
verbose: bool = false,
const params = [_]clap.Param(clap.Help){
clap.parseParam("--help Print this menu") catch unreachable,
@@ -145,6 +248,7 @@ const CreateOptions = struct {
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("<POS>... ") catch unreachable,
};
@@ -183,17 +287,10 @@ const CreateOptions = struct {
opts.npm_client = NPMClient.Tag.pnpm;
}
if (args.flag("--no-install")) {
opts.skip_install = true;
}
if (args.flag("--no-git")) {
opts.skip_git = true;
}
if (args.flag("--force")) {
opts.overwrite = true;
}
opts.verbose = args.flag("--verbose");
opts.skip_install = args.flag("--no-install");
opts.skip_git = args.flag("--no-git");
opts.overwrite = args.flag("--force");
return opts;
}
@@ -228,6 +325,9 @@ pub const CreateCommand = struct {
home_dir_buf[outdir_path.len] = 0;
var outdir_path_ = home_dir_buf[0..outdir_path.len :0];
std.fs.accessAbsoluteZ(outdir_path_, .{}) catch break :outer;
if (create_options.verbose) {
Output.prettyErrorln("reading from {s}", .{outdir_path});
}
break :brk outdir_path;
}
}
@@ -238,6 +338,9 @@ pub const CreateCommand = struct {
home_dir_buf[outdir_path.len] = 0;
var outdir_path_ = home_dir_buf[0..outdir_path.len :0];
std.fs.accessAbsoluteZ(outdir_path_, .{}) catch break :outer;
if (create_options.verbose) {
Output.prettyErrorln("reading from {s}", .{outdir_path});
}
break :brk outdir_path;
}
@@ -248,6 +351,9 @@ pub const CreateCommand = struct {
home_dir_buf[outdir_path.len] = 0;
var outdir_path_ = home_dir_buf[0..outdir_path.len :0];
std.fs.accessAbsoluteZ(outdir_path_, .{}) catch break :outer;
if (create_options.verbose) {
Output.prettyErrorln("reading from {s}", .{outdir_path});
}
break :brk outdir_path;
}
}
@@ -266,6 +372,10 @@ pub const CreateCommand = struct {
// alacritty is fast
if (env_loader.map.get("ALACRITTY_LOG") != null) {
progress.refresh_rate_ns = std.time.ns_per_ms * 8;
if (create_options.verbose) {
Output.prettyErrorln("your using alacritty", .{});
}
}
defer {
@@ -277,6 +387,10 @@ pub const CreateCommand = struct {
const is_remote_template = !std.fs.path.isAbsolute(template);
if (create_options.verbose) {
Output.prettyErrorln("is_remote_template {d}", .{@boolToInt(is_remote_template)});
}
if (is_remote_template) {
var tarball_bytes: MutableString = Example.fetch(ctx, template, &progress, node) catch |err| {
switch (err) {
@@ -288,7 +402,8 @@ pub const CreateCommand = struct {
template,
});
Output.flush();
const examples = try Example.fetchAllLocalAndRemote(ctx, &env_loader, filesystem);
const examples = try Example.fetchAllLocalAndRemote(ctx, null, &env_loader, filesystem);
Example.print(examples.items, dirname);
Output.flush();
std.os.exit(1);
@@ -325,6 +440,7 @@ pub const CreateCommand = struct {
var archive_context = Archive.Context{
.pluckers = &pluckers,
.all_files = undefined,
.overwrite_list = std.StringArrayHashMap(void).init(ctx.allocator),
};
@@ -348,14 +464,15 @@ pub const CreateCommand = struct {
// Thank you create-react-app for this copy (and idea)
Output.prettyErrorln(
"<r><red>error<r><d>: <r>The directory <b><green>{s}<r> contains files that could conflict:",
"<r><red>error<r><d>: <r>The directory <b><blue>{s}<r>/ contains files that could conflict:\n\n",
.{
std.fs.path.basename(destination),
},
);
for (archive_context.overwrite_list.keys()) |path| {
if (strings.endsWith(path, std.fs.path.sep_str)) {
Output.prettyErrorln("<r> <cyan>{s}<r>", .{path});
Output.prettyError("<r> <blue>{s}<r>", .{path[0 .. std.math.max(path.len, 1) - 1]});
Output.prettyErrorln(std.fs.path.sep_str, .{});
} else {
Output.prettyErrorln("<r> {s}", .{path});
}
@@ -369,6 +486,8 @@ pub const CreateCommand = struct {
tarball_buf_list.items,
destination,
&archive_context,
void,
void{},
1,
false,
false,
@@ -416,48 +535,63 @@ pub const CreateCommand = struct {
};
const Walker = @import("../walker_skippable.zig");
var walker = try Walker.walk(template_dir, ctx.allocator, skip_files, skip_dirs);
defer walker.deinit();
var walker_ = try Walker.walk(template_dir, ctx.allocator, skip_files, skip_dirs);
defer walker_.deinit();
var count: usize = 0;
while (try walker.next()) |entry| {
// TODO: make this not walk these folders entirely
// rather than checking each file path.....
if (entry.kind != .File) continue;
var outfile = destination_dir.createFile(entry.path, .{}) catch brk: {
if (std.fs.path.dirname(entry.path)) |entry_dirname| {
destination_dir.makePath(entry_dirname) catch {};
const FileCopier = struct {
pub fn copy(
destination_dir_: std.fs.Dir,
walker: *Walker,
node_: *std.Progress.Node,
progress_: *std.Progress,
) !void {
while (try walker.next()) |entry| {
// TODO: make this not walk these folders entirely
// rather than checking each file path.....
if (entry.kind != .File) continue;
var outfile = destination_dir_.createFile(entry.path, .{}) catch brk: {
if (std.fs.path.dirname(entry.path)) |entry_dirname| {
destination_dir_.makePath(entry_dirname) catch {};
}
break :brk destination_dir_.createFile(entry.path, .{}) catch |err| {
node_.end();
progress_.refresh();
Output.prettyErrorln("<r><red>{s}<r>: copying file {s}", .{ @errorName(err), entry.path });
Output.flush();
std.os.exit(1);
};
};
defer outfile.close();
defer node_.completeOne();
var infile = try entry.dir.openFile(entry.basename, .{ .read = true });
defer infile.close();
// Assumption: you only really care about making sure something that was executable is still executable
const stat = infile.stat() catch continue;
_ = C.fchmod(outfile.handle, stat.mode);
CopyFile.copy(infile.handle, outfile.handle) catch {
entry.dir.copyFile(entry.basename, destination_dir_, entry.path, .{}) catch |err| {
node_.end();
progress_.refresh();
Output.prettyErrorln("<r><red>{s}<r>: copying file {s}", .{ @errorName(err), entry.path });
Output.flush();
std.os.exit(1);
};
};
}
break :brk destination_dir.createFile(entry.path, .{}) catch |err| {
node.end();
}
};
progress.refresh();
Output.prettyErrorln("<r><red>{s}<r>: copying file {s}", .{ @errorName(err), entry.path });
Output.flush();
std.os.exit(1);
};
};
defer outfile.close();
defer node.completeOne();
var infile = try entry.dir.openFile(entry.basename, .{ .read = true });
defer infile.close();
CopyFile.copy(infile.handle, outfile.handle) catch {
entry.dir.copyFile(entry.basename, destination_dir, entry.path, .{}) catch |err| {
node.end();
progress.refresh();
Output.prettyErrorln("<r><red>{s}<r>: copying file {s}", .{ @errorName(err), entry.path });
Output.flush();
std.os.exit(1);
};
};
var stat = outfile.stat() catch continue;
_ = C.fchmod(outfile.handle, stat.mode);
}
try FileCopier.copy(destination_dir, &walker_, node, &progress);
package_json_file = destination_dir.openFile("package.json", .{ .read = true, .write = true }) catch |err| {
node.end();
@@ -547,6 +681,8 @@ pub const CreateCommand = struct {
var preinstall_tasks = std.mem.zeroes(std.ArrayListUnmanaged([]const u8));
var postinstall_tasks = std.mem.zeroes(std.ArrayListUnmanaged([]const u8));
var has_dependencies: bool = false;
{
var i: usize = 0;
var property_i: usize = 0;
@@ -554,6 +690,13 @@ pub const CreateCommand = struct {
const property = package_json_expr.data.e_object.properties[i];
const key = property.key.?.asString(ctx.allocator).?;
has_dependencies = has_dependencies or
((strings.eqlComptime(key, "dependencies") or
strings.eqlComptime(key, "devDependencies") or
std.mem.indexOf(u8, key, "optionalDependencies") != null or
strings.eqlComptime(key, "peerDependencies")) and
(property.value.?.data == .e_object and property.value.?.data.e_object.properties.len > 0));
if (key.len == 0 or !strings.eqlComptime(key, "bun-create")) {
package_json_expr.data.e_object.properties[property_i] = property;
property_i += 1;
@@ -605,6 +748,11 @@ pub const CreateCommand = struct {
}
}
}
package_json_expr.data.e_object.properties = package_json_expr.data.e_object.properties[0..property_i];
}
if (create_options.verbose) {
Output.prettyErrorln("Has dependencies? {d}", .{@boolToInt(has_dependencies)});
}
var package_json_writer = JSPrinter.NewFileWriter(package_json_file);
@@ -615,9 +763,38 @@ pub const CreateCommand = struct {
std.os.exit(1);
};
{
var parent_dir = try std.fs.openDirAbsolute(destination, .{});
defer parent_dir.close();
std.os.linkat(parent_dir.fd, "gitignore", parent_dir.fd, ".gitignore", 0) catch {};
std.os.unlinkat(
parent_dir.fd,
"gitignore",
0,
) catch {};
std.os.unlinkat(
parent_dir.fd,
".npmignore",
0,
) catch {};
}
const PATH = env_loader.map.get("PATH") orelse "";
var npm_client_: ?NPMClient = null;
create_options.skip_install = create_options.skip_install or !has_dependencies;
if (!create_options.skip_git) {
if (!create_options.skip_install) {
GitHandler.spawn(destination, PATH, create_options.verbose);
} else {
if (create_options.verbose) {
create_options.skip_git = GitHandler.run(destination, PATH, true) catch false;
} else {
create_options.skip_git = GitHandler.run(destination, PATH, false) catch false;
}
}
}
if (!create_options.skip_install) {
if (env_loader.map.get("NPM_CLIENT")) |npm_client_bin| {
@@ -694,39 +871,8 @@ pub const CreateCommand = struct {
}
}
{
var parent_dir = try std.fs.openDirAbsolute(destination, .{});
std.os.linkat(parent_dir.fd, "gitignore", parent_dir.fd, ".gitignore", 0) catch {};
std.os.unlinkat(
parent_dir.fd,
"gitignore",
0,
) catch {};
parent_dir.close();
}
if (!create_options.skip_git) {
if (which(&bun_path_buf, PATH, destination, "git")) |git| {
const git_commands = .{
&[_]string{ std.mem.span(git), "init", "--quiet" },
&[_]string{ std.mem.span(git), "add", "-A", destination, "--ignore-errors" },
&[_]string{ std.mem.span(git), "commit", "-am", "\"Initial Commit\"", "--quiet" },
};
// same names, just comptime known values
inline for (comptime std.meta.fieldNames(@TypeOf(Commands))) |command_field| {
const command: []const string = @field(git_commands, command_field);
var process = try std.ChildProcess.init(command, ctx.allocator);
process.cwd = destination;
process.stdin_behavior = .Inherit;
process.stdout_behavior = .Inherit;
process.stderr_behavior = .Inherit;
defer process.deinit();
var term = try process.spawnAndWait();
_ = process.kill() catch undefined;
}
}
if (!create_options.skip_install and !create_options.skip_git) {
create_options.skip_git = !GitHandler.wait();
}
Output.printError("\n", .{});
@@ -734,11 +880,13 @@ pub const CreateCommand = struct {
Output.prettyErrorln(" <r><d>bun create {s}<r>", .{template});
Output.flush();
Output.pretty(
\\
\\<r><d>-----<r>
\\
, .{});
if (!create_options.skip_install) {
Output.pretty(
\\
\\<r><d>-----<r>
\\
, .{});
}
if (!create_options.skip_git and !create_options.skip_install) {
Output.pretty(
@@ -800,7 +948,7 @@ const PackageDownloadThread = struct {
std.Thread.Futex.wake(&this.done, 1);
}
pub fn spawn(allocator: *std.mem.Allocator, tarball_url: string) !*PackageDownloadThread {
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,
@@ -811,6 +959,10 @@ const PackageDownloadThread = struct {
.thread = undefined,
};
if (Output.enable_ansi_colors) {
download.client.progress_node = progress_node;
}
download.thread = try std.Thread.spawn(.{}, threadHandler, .{download});
return download;
@@ -853,8 +1005,9 @@ pub const Example = struct {
}
}
pub fn fetchAllLocalAndRemote(ctx: Command.Context, env_loader: *DotEnv.Loader, filesystem: *fs.FileSystem) !std.ArrayList(Example) {
const remote_examples = try Example.fetchAll(ctx);
pub fn fetchAllLocalAndRemote(ctx: Command.Context, node: ?*std.Progress.Node, env_loader: *DotEnv.Loader, filesystem: *fs.FileSystem) !std.ArrayList(Example) {
const remote_examples = try Example.fetchAll(ctx, node);
if (node) |node_| node_.end();
var examples = std.ArrayList(Example).fromOwnedSlice(ctx.allocator, remote_examples);
{
@@ -936,6 +1089,7 @@ pub const Example = struct {
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);
switch (response.status_code) {
@@ -1007,23 +1161,11 @@ pub const Example = struct {
progress.name = "Downloading tarball";
refresher.refresh();
var thread: *PackageDownloadThread = try PackageDownloadThread.spawn(ctx.allocator, tarball_url);
std.Thread.Futex.wait(&thread.done, 1, std.time.ns_per_ms * 100) catch {};
progress.setEstimatedTotalItems(thread.client.body_size);
progress.setCompletedItems(thread.client.read_count);
var thread: *PackageDownloadThread = try PackageDownloadThread.spawn(ctx.allocator, tarball_url, progress);
refresher.maybeRefresh();
if (thread.done.load(.Acquire) == 0) {
while (true) {
std.Thread.Futex.wait(&thread.done, 1, std.time.ns_per_ms * 100) catch {};
progress.setEstimatedTotalItems(thread.client.body_size);
progress.setCompletedItems(thread.client.read_count);
refresher.maybeRefresh();
if (thread.done.load(.Acquire) == 1) {
break;
}
}
while (thread.done.load(.Acquire) == 0) {
std.Thread.Futex.wait(&thread.done, 1, std.time.ns_per_ms * 10000) catch {};
}
refresher.maybeRefresh();
@@ -1042,10 +1184,15 @@ pub const Example = struct {
return thread.buffer;
}
pub fn fetchAll(ctx: Command.Context) ![]Example {
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;
if (Output.enable_ansi_colors) {
client.progress_node = progress_node;
}
var mutable: MutableString = try MutableString.init(ctx.allocator, 1024);
var response = client.send("", &mutable) catch |err| {
switch (err) {
@@ -1139,9 +1286,13 @@ pub const CreateListExamplesCommand = struct {
env_loader.loadProcess();
const time = std.time.nanoTimestamp();
const examples = try Example.fetchAllLocalAndRemote(ctx, &env_loader, filesystem);
var progress = std.Progress{};
var node = try progress.start("Fetching manifest", 0);
progress.refresh();
const examples = try Example.fetchAllLocalAndRemote(ctx, node, &env_loader, filesystem);
Output.printStartEnd(time, std.time.nanoTimestamp());
Output.prettyln(" <d>Fetched examples<r>", .{});
Output.prettyln(" <d>Fetched manifest<r>", .{});
Output.prettyln("Welcome to Bun! Create a new project by pasting any of the following:\n\n", .{});
Output.flush();
@@ -1162,3 +1313,288 @@ pub const CreateListExamplesCommand = struct {
Output.flush();
}
};
const GitHandler = struct {
var success: std.atomic.Atomic(u32) = undefined;
var thread: std.Thread = undefined;
pub fn spawn(
destination: string,
PATH: string,
verbose: bool,
) void {
success = std.atomic.Atomic(u32).init(0);
thread = std.Thread.spawn(.{}, spawnThread, .{ destination, PATH, verbose }) catch |err| {
Output.prettyErrorln("<r><red>{s}<r>", .{@errorName(err)});
Output.flush();
std.os.exit(1);
};
}
fn spawnThread(
destination: string,
PATH: string,
verbose: bool,
) void {
Output.Source.configureThread();
std.Thread.setName(thread, "git") catch {};
defer Output.flush();
const outcome = if (verbose)
run(destination, PATH, true) catch false
else
run(destination, PATH, false) catch false;
@fence(.Acquire);
success.store(
if (outcome)
1
else
2,
.Release,
);
std.Thread.Futex.wake(&success, 1);
}
pub fn wait() bool {
@fence(.Release);
while (success.load(.Acquire) == 0) {
std.Thread.Futex.wait(&success, 0, 1000) catch continue;
}
const outcome = success.load(.Acquire) == 1;
thread.join();
return outcome;
}
pub fn run(
destination: string,
PATH: string,
comptime verbose: bool,
) !bool {
const git_start = std.time.nanoTimestamp();
// This feature flag is disabled.
// using libgit2 is slower than the CLI.
// [481.00ms] git
// [89.00ms] git
// if (comptime FeatureFlags.use_libgit2) {
// try_to_use_libgit2: {
// if (comptime Environment.isWindows) @compileError("TODO win32");
// // since we link libc, it always goes through dlopen unless windows
// const DlDynlib = std.DynLib;
// var libgit2_dylib: std.DynLib = brk: {
// for (global_libgit2_paths) |lib_path| {
// break :brk DlDynlib.openZ(lib_path) catch continue;
// }
// var lib_paths = getSelfExeSharedLibPaths(default_allocator) catch break :try_to_use_libgit2;
// // this is a list of every dynamically loaded library on the user's computer?
// for (lib_paths) |lib_path| {
// const basename = std.fs.path.basename(std.mem.span(lib_path));
// if (basename.len == (comptime libgit2_basename.len) and
// std.hash.Wyhash.hash(10, basename) == comptime std.hash.Wyhash.hash(10, libgit2_basename))
// {
// break :brk DlDynlib.openZ(lib_path) catch continue;
// }
// }
// break :try_to_use_libgit2;
// };
// const libgit2 = @import("../deps/libgit2.zig");
// var repo: ?*libgit2.git_repository = null;
// // https://libgit2.org/docs/examples/init/
// // Note: git_threads_init was renamed to git_libgit2_init
// // https://mail.gnome.org/archives/commits-list/2015-January/msg03703.html
// // ...in 2015 and that doc is still out of date
// const git_repository_init = libgit2_dylib.lookup(libgit2.git_repository_init, "git_repository_init") orelse break :try_to_use_libgit2;
// const git_repository_free = libgit2_dylib.lookup(libgit2.git_repository_free, "git_repository_free") orelse break :try_to_use_libgit2;
// const git_signature_free = libgit2_dylib.lookup(libgit2.git_signature_free, "git_signature_free") orelse break :try_to_use_libgit2;
// const git_signature_default = libgit2_dylib.lookup(libgit2.git_signature_default, "git_signature_default") orelse break :try_to_use_libgit2;
// const git_repository_index = libgit2_dylib.lookup(libgit2.git_repository_index, "git_repository_index") orelse break :try_to_use_libgit2;
// const git_index_read_tree = libgit2_dylib.lookup(libgit2.git_index_read_tree, "git_index_read_tree") orelse break :try_to_use_libgit2;
// const git_index_write_tree = libgit2_dylib.lookup(libgit2.git_index_write_tree, "git_index_write_tree") orelse break :try_to_use_libgit2;
// const git_index_write = libgit2_dylib.lookup(libgit2.git_index_write, "git_index_write") orelse break :try_to_use_libgit2;
// const git_index_add_all = libgit2_dylib.lookup(libgit2.git_index_add_all, "git_index_add_all") orelse break :try_to_use_libgit2;
// const git_tree_lookup = libgit2_dylib.lookup(libgit2.git_tree_lookup, "git_tree_lookup") orelse break :try_to_use_libgit2;
// const git_commit_create_v = libgit2_dylib.lookup(libgit2.git_commit_create_v, "git_commit_create_v") orelse break :try_to_use_libgit2;
// const git_index_free = libgit2_dylib.lookup(libgit2.git_index_free, "git_index_free") orelse break :try_to_use_libgit2;
// const git_tree_free = libgit2_dylib.lookup(libgit2.git_tree_free, "git_tree_free") orelse break :try_to_use_libgit2;
// const git_index_add_bypath = libgit2_dylib.lookup(libgit2.git_index_add_bypath, "git_index_add_bypath") orelse break :try_to_use_libgit2;
// const git_status_should_ignore = libgit2_dylib.lookup(libgit2.git_status_should_ignore, "git_status_should_ignore") orelse break :try_to_use_libgit2;
// var sig: ?*libgit2.git_signature = null;
// // var destination_z = destination.ptr[0..destination.len :0];
// // const original_cwd = std.os.getcwd(&bun_path_buf) catch break :try_to_use_libgit2;
// // bun_path_buf[original_cwd.len] = 0;
// // std.os.chdirZ(destination_z) catch {};
// // var origin_z = bun_path_buf[0..original_cwd.len :0];
// // defer std.os.chdirZ(origin_z) catch {};
// if (libgit2_dylib.lookup(libgit2.git_libgit2_init, "git_libgit2_init")) |git_libgit2_init| {
// std.debug.assert(git_libgit2_init() > -1);
// }
// var rc: c_int = 0;
// rc = git_repository_init(&repo, destination.ptr, 0);
// if (rc < 0) break :try_to_use_libgit2;
// var repo_ = repo orelse break :try_to_use_libgit2;
// // defer git_repository_free(repo_);
// rc = git_signature_default(&sig, repo_);
// // this fails if they never ran git config
// // the child process will fail too, so we just skip
// if (rc < 0) {
// return false;
// }
// // defer git_signature_free(sig.?);
// var index: ?*libgit2.git_index = null;
// var tree_id: libgit2.git_oid = undefined;
// var commit_id: libgit2.git_oid = undefined;
// var second_commit_id: libgit2.git_oid = undefined;
// var git_tree: *libgit2.git_tree = undefined;
// rc = git_repository_index(&index, repo_);
// if (rc < 0) break :try_to_use_libgit2;
// // // add gitignore file first
// // // technically, we should add every gitignore first but i'll leave that as a future TODO
// // if (files_list.containsAdapted(comptime std.hash.Wyhash.hash(0, "gitignore"), Archive.Context.U64Context{})) {
// // rc = git_index_add_bypath(index.?, ".gitignore");
// // if (rc < 0) break :try_to_use_libgit2;
// // } else if (files_list.containsAdapted(comptime std.hash.Wyhash.hash(0, ".gitignore"), Archive.Context.U64Context{})) {
// // rc = git_index_add_bypath(index.?, ".gitignore");
// // if (rc < 0) break :try_to_use_libgit2;
// // }
// var files = files_list.values();
// // without git
// // [6.00ms] bun create /Users/jarred/.bun-create/react2
// // with git:
// // [44.00ms] bun create /Users/jarred/.bun-create/react2
// var strs = std.mem.zeroes(libgit2.git_strarray);
// // var star = [_]u8{ '*', 0 };
// // var star_ptr: [*c]u8 = &star;
// strs.strings = files.ptr;
// strs.count = files.len;
// rc = git_index_add_all(index, &strs, libgit2.GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH, null, null);
// if (rc < 0) break :try_to_use_libgit2;
// // if (comptime verbose) {
// // for (files) |file| {
// // var ignored: c_int = 0;
// // rc = git_status_should_ignore(&ignored, repo_, file);
// // if (rc < 0) break :try_to_use_libgit2;
// // if (ignored > 0) {
// // Output.prettyErrorln("[libgit2]: ignored {s}", .{std.mem.sliceTo(file, 0)});
// // continue;
// // }
// // // this can fail for a variety of reasons
// // // if it does, better to get most of the files than stop
// // rc = git_index_add_bypath(index.?, file);
// // switch (rc) {
// // libgit2.GIT_OK => {
// // Output.prettyErrorln("[libgit2]: Added {s}", .{std.mem.sliceTo(file, 0)});
// // },
// // libgit2.GIT_ENOTFOUND => {
// // Output.prettyErrorln("[libgit2]: not found {s}", .{std.mem.sliceTo(file, 0)});
// // },
// // else => {
// // Output.prettyErrorln("[libgit2]: error {d} for {s}", .{ rc, std.mem.sliceTo(file, 0) });
// // },
// // }
// // }
// // } else {
// // for (files) |file| {
// // var ignored: c_int = 0;
// // rc = git_status_should_ignore(&ignored, repo_, file);
// // if (rc < 0) break :try_to_use_libgit2;
// // if (ignored > 0) continue;
// // // this can fail for a variety of reasons
// // // if it does, better to get most of the files than stop
// // _ = git_index_add_bypath(index.?, file);
// // }
// // }
// // if you don't call git_index_write, git status will look very strange.
// rc = git_index_write(index.?);
// if (rc < 0) break :try_to_use_libgit2;
// rc = git_index_write_tree(&tree_id, index);
// if (rc < 0) break :try_to_use_libgit2;
// rc = git_tree_lookup(&git_tree, repo_, &tree_id);
// if (rc < 0) break :try_to_use_libgit2;
// // defer git_tree_free(git_tree);
// rc = git_commit_create_v(
// &commit_id,
// repo_,
// "HEAD",
// sig.?,
// sig.?,
// null,
// "Initial commit (via bun create)",
// git_tree,
// 0,
// );
// if (rc < 0) break :try_to_use_libgit2;
// if (comptime verbose) {
// Output.prettyErrorln("git backend: libgit2", .{});
// }
// Output.prettyError("\n", .{});
// Output.printStartEnd(git_start, std.time.nanoTimestamp());
// Output.prettyError(" <d>git<r>\n", .{});
// // success!
// return true;
// }
// }
if (which(&bun_path_buf, PATH, destination, "git")) |git| {
const git_commands = .{
&[_]string{ std.mem.span(git), "init", "--quiet" },
&[_]string{ std.mem.span(git), "add", destination, "--ignore-errors" },
&[_]string{ std.mem.span(git), "commit", "-am", "\"Initial commit (via bun create)\"", "--quiet" },
};
if (comptime verbose) {
Output.prettyErrorln("git backend: {s}", .{git});
}
// same names, just comptime known values
inline for (comptime std.meta.fieldNames(@TypeOf(Commands))) |command_field| {
const command: []const string = @field(git_commands, command_field);
var process = try std.ChildProcess.init(command, default_allocator);
process.cwd = destination;
process.stdin_behavior = .Inherit;
process.stdout_behavior = .Inherit;
process.stderr_behavior = .Inherit;
defer process.deinit();
var term = try process.spawnAndWait();
_ = process.kill() catch undefined;
}
Output.prettyError("\n", .{});
Output.printStartEnd(git_start, std.time.nanoTimestamp());
Output.prettyError(" <d>git<r>\n", .{});
return true;
}
return false;
}
};

View File

@@ -21,7 +21,7 @@ pub fn copy(fd_in: os.fd_t, fd_out: os.fd_t) CopyFileError!void {
}
}
if (std.Target.current.os.tag == .linux) {
if (comptime std.Target.current.os.tag == .linux) {
// Try copy_file_range first as that works at the FS level and is the
// most efficient method (if available).
var offset: u64 = 0;

2209
src/deps/libgit2.zig Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -79,3 +79,20 @@ pub const include_filename_in_jsx = false;
pub const verbose_analytics = false;
pub const disable_compression_in_http_client = false;
// Not sure why...
// But this is slower!
// ~/Build/throw
// hyperfine "bun create react3 app --force --no-install" --prepare="rm -rf app"
// Benchmark #1: bun create react3 app --force --no-install
// Time (mean ± σ): 974.6 ms ± 6.8 ms [User: 170.5 ms, System: 798.3 ms]
// Range (min … max): 960.8 ms … 984.6 ms 10 runs
// mv /usr/local/opt/libgit2/lib/libgit2.dylib /usr/local/opt/libgit2/lib/libgit2.dylib.1
// ~/Build/throw
// hyperfine "bun create react3 app --force --no-install" --prepare="rm -rf app"
// Benchmark #1: bun create react3 app --force --no-install
// Time (mean ± σ): 306.7 ms ± 6.1 ms [User: 31.7 ms, System: 269.8 ms]
// Range (min … max): 299.5 ms … 318.8 ms 10 runs
pub const use_libgit2 = true;

View File

@@ -47,6 +47,7 @@ remaining_redirect_count: i8 = 127,
redirect_buf: [2048]u8 = undefined,
disable_shutdown: bool = false,
timeout: u32 = 0,
progress_node: ?*std.Progress.Node = null,
pub fn init(allocator: *std.mem.Allocator, method: Method, url: URL, header_entries: Headers.Entries, header_buf: string) HTTPClient {
return HTTPClient{
@@ -306,12 +307,23 @@ pub fn sendHTTP(this: *HTTPClient, body: []const u8, body_out_str: *MutableStrin
var client_reader = this.tcp_client.reader(SOCKET_FLAGS);
return this.processResponse(
false,
@TypeOf(client_reader),
client_reader,
body_out_str,
);
if (this.progress_node == null) {
return this.processResponse(
false,
false,
@TypeOf(client_reader),
client_reader,
body_out_str,
);
} else {
return this.processResponse(
false,
true,
@TypeOf(client_reader),
client_reader,
body_out_str,
);
}
}
const ZlibPool = struct {
@@ -353,7 +365,7 @@ const ZlibPool = struct {
}
};
pub fn processResponse(this: *HTTPClient, comptime is_https: bool, comptime Client: type, client: Client, body_out_str: *MutableString) !picohttp.Response {
pub fn processResponse(this: *HTTPClient, comptime is_https: bool, comptime report_progress: bool, comptime Client: type, client: Client, body_out_str: *MutableString) !picohttp.Response {
var response: picohttp.Response = undefined;
var read_length: usize = 0;
{
@@ -365,6 +377,11 @@ pub fn processResponse(this: *HTTPClient, comptime is_https: bool, comptime Clie
restart: while (req_buf_read != 0) {
req_buf_read = try client.read(http_req_buf[read_length..]);
read_length += req_buf_read;
if (comptime report_progress) {
this.progress_node.?.activate();
this.progress_node.?.setCompletedItems(read_length);
this.progress_node.?.context.maybeRefresh();
}
var request_buffer = http_req_buf[0..read_length];
read_headers_up_to = if (read_headers_up_to > read_length) read_length else read_headers_up_to;
@@ -520,6 +537,12 @@ pub fn processResponse(this: *HTTPClient, comptime is_https: bool, comptime Clie
if (pret == -1) return error.ChunkedEncodingParseError;
total_size += rsize;
if (comptime report_progress) {
this.progress_node.?.activate();
this.progress_node.?.setCompletedItems(total_size);
this.progress_node.?.context.maybeRefresh();
}
}
buffer.list.shrinkRetainingCapacity(total_size);
@@ -541,6 +564,12 @@ pub fn processResponse(this: *HTTPClient, comptime is_https: bool, comptime Clie
else => {},
}
if (comptime report_progress) {
this.progress_node.?.activate();
this.progress_node.?.setCompletedItems(body_out_str.list.items.len);
this.progress_node.?.context.maybeRefresh();
}
this.body_size = @intCast(u32, body_out_str.list.items.len);
return response;
}
@@ -585,6 +614,18 @@ pub fn processResponse(this: *HTTPClient, comptime is_https: bool, comptime Clie
body_size += size;
remaining_content_length -= size;
if (comptime report_progress) {
this.progress_node.?.activate();
this.progress_node.?.setCompletedItems(body_size);
this.progress_node.?.context.maybeRefresh();
}
}
if (comptime report_progress) {
this.progress_node.?.activate();
this.progress_node.?.setCompletedItems(body_size);
this.progress_node.?.context.maybeRefresh();
}
buffer.list.shrinkRetainingCapacity(body_size);
@@ -607,6 +648,12 @@ pub fn processResponse(this: *HTTPClient, comptime is_https: bool, comptime Clie
}
}
if (comptime report_progress) {
this.progress_node.?.activate();
this.progress_node.?.setCompletedItems(body_out_str.list.items.len);
this.progress_node.?.context.maybeRefresh();
}
return response;
}
@@ -643,7 +690,11 @@ pub fn sendHTTPS(this: *HTTPClient, body_str: []const u8, body_out_str: *Mutable
try client_writer.writeAll(body);
}
return try this.processResponse(true, @TypeOf(&client), &client, body_out_str);
if (this.progress_node == null) {
return try this.processResponse(true, false, @TypeOf(&client), &client, body_out_str);
} else {
return try this.processResponse(true, true, @TypeOf(&client), &client, body_out_str);
}
}
// zig test src/http_client.zig --test-filter "sendHTTP - only" -lc -lc++ /Users/jarred/Code/bun/src/deps/zlib/libz.a /Users/jarred/Code/bun/src/deps/picohttpparser.o --cache-dir /Users/jarred/Code/bun/zig-cache --global-cache-dir /Users/jarred/.cache/zig --name bun --pkg-begin clap /Users/jarred/Code/bun/src/deps/zig-clap/clap.zig --pkg-end --pkg-begin picohttp /Users/jarred/Code/bun/src/deps/picohttp.zig --pkg-end --pkg-begin iguanaTLS /Users/jarred/Code/bun/src/deps/iguanaTLS/src/main.zig --pkg-end -I /Users/jarred/Code/bun/src/deps -I /Users/jarred/Code/bun/src/deps/mimalloc -I /usr/local/opt/icu4c/include -L src/deps/mimalloc -L /usr/local/opt/icu4c/lib --main-pkg-path /Users/jarred/Code/bun --enable-cache -femit-bin=zig-out/bin/test --test-no-exec

View File

@@ -408,6 +408,17 @@ pub const Archive = struct {
pub const Context = struct {
pluckers: []Plucker = &[_]Plucker{},
overwrite_list: std.StringArrayHashMap(void),
all_files: EntryMap,
pub const EntryMap = std.ArrayHashMap(u64, [*c]u8, U64Context, false);
pub const U64Context = struct {
pub fn hash(ctx: @This(), k: u64) u32 {
return @truncate(u32, k);
}
pub fn eql(ctx: @This(), a: u64, b: u64) bool {
return a == b;
}
};
};
pub const Plucker = struct {
@@ -512,6 +523,8 @@ pub const Archive = struct {
file_buffer: []const u8,
root: []const u8,
ctx: ?*Archive.Context,
comptime FilePathAppender: type,
appender: FilePathAppender,
comptime depth_to_skip: usize,
comptime close_handles: bool,
comptime log: bool,
@@ -552,17 +565,19 @@ pub const Archive = struct {
var pathname: [:0]const u8 = std.mem.sliceTo(lib.archive_entry_pathname(entry).?, 0);
var tokenizer = std.mem.tokenize(u8, std.mem.span(pathname), std.fs.path.sep_str);
comptime var depth_i: usize = 0;
inline while (depth_i < depth_to_skip) : (depth_i += 1) {
if (tokenizer.next() == null) continue :loop;
}
var pathname_ = tokenizer.rest();
pathname = std.mem.sliceTo(pathname_.ptr[0..pathname_.len :0], 0);
const dirname = std.fs.path.dirname(std.mem.span(pathname)) orelse "";
pathname = @intToPtr([*]const u8, @ptrToInt(pathname_.ptr))[0..pathname_.len :0];
const mask = lib.archive_entry_filetype(entry);
const size = @intCast(usize, std.math.max(lib.archive_entry_size(entry), 0));
if (size > 0) {
const slice = std.mem.span(pathname);
if (comptime log) {
Output.prettyln(" {s}", .{pathname});
}
@@ -570,7 +585,7 @@ pub const Archive = struct {
const file = dir.createFileZ(pathname, .{ .truncate = true }) catch |err| brk: {
switch (err) {
error.FileNotFound => {
dir.makePath(dirname) catch {};
try dir.makePath(std.fs.path.dirname(slice) orelse return err);
break :brk try dir.createFileZ(pathname, .{ .truncate = true });
},
else => {
@@ -584,10 +599,17 @@ pub const Archive = struct {
if (ctx) |ctx_| {
const hash: u64 = if (ctx_.pluckers.len > 0)
std.hash.Wyhash.hash(0, std.mem.span(pathname))
std.hash.Wyhash.hash(0, slice)
else
@as(u64, 0);
if (comptime FilePathAppender != void) {
var result = ctx.?.all_files.getOrPutAdapted(hash, Context.U64Context{}) catch unreachable;
if (!result.found_existing) {
result.value_ptr.* = (try appender.appendMutable(@TypeOf(slice), slice)).ptr;
}
}
for (ctx_.pluckers) |*plucker_| {
if (plucker_.filename_hash == hash) {
try plucker_.contents.inflate(size);

View File

@@ -1 +1 @@
35057197d4ad54bc
4865dbe641dca1de

View File

@@ -189,9 +189,11 @@ if (typeof window !== "undefined") {
let count = 0;
let match: CSSHMRInsertionPoint = null;
const adoptedStyles = document.adoptedStyleSheets;
if (this.updateMethod === CSSUpdateMethod.cssObjectModel) {
if (document.adoptedStyleSheets.length > 0) {
count = document.adoptedStyleSheets.length;
if (adoptedStyles.length > 0) {
count = adoptedStyles.length;
for (let i = 0; i < count && match === null; i++) {
let cssRules: CSSRuleList;
@@ -200,7 +202,7 @@ if (typeof window !== "undefined") {
// Non-same origin stylesheets will potentially throw "Security error"
// We will ignore those stylesheets and look at others.
try {
sheet = document.adoptedStyleSheets[i];
sheet = adoptedStyles[i];
cssRules = sheet.rules;
ruleCount = sheet.rules.length;
} catch (exception) {

View File

@@ -58,6 +58,10 @@ pub fn next(self: *Walker) !?WalkerEntry {
dirname_len += 1;
}
try self.name_buffer.appendSlice(base.name);
const cur_len = self.name_buffer.items.len;
try self.name_buffer.append(0);
self.name_buffer.shrinkRetainingCapacity(cur_len);
if (base.kind == .Directory) {
var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
error.NameTooLong => unreachable, // no path sep in base.name