aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/launch.json12
-rw-r--r--src/api/schema.peechy2
-rw-r--r--src/cli.zig4
-rw-r--r--src/global.zig1
-rw-r--r--src/install/install.zig488
-rw-r--r--src/js_ast.zig2
-rw-r--r--src/libarchive/libarchive.zig2
-rw-r--r--src/s2n.zig16
-rw-r--r--src/string_immutable.zig4
9 files changed, 340 insertions, 191 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json
index a9cf56a9f..002036fce 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -317,6 +317,18 @@
{
"type": "lldb",
"request": "launch",
+ "name": "Install",
+ "program": "bun-debug",
+ "args": ["install"],
+ "cwd": "/tmp/wow-such-npm",
+ "env": {
+ "GOMAXPROCS": "1"
+ },
+ "console": "internalConsole"
+ },
+ {
+ "type": "lldb",
+ "request": "launch",
"name": "Integration Test Dev (no hmr)",
"program": "bun-debug",
"args": ["--disable-hmr"],
diff --git a/src/api/schema.peechy b/src/api/schema.peechy
index 150bac007..88cd51a4f 100644
--- a/src/api/schema.peechy
+++ b/src/api/schema.peechy
@@ -546,7 +546,7 @@ struct Semver {
uint32 major;
uint32 minor;
uint32 patch;
- qualifier SemverQualifier;
+ SemverQualifier qualifier;
}
enum NPMPackageDataKind {
diff --git a/src/cli.zig b/src/cli.zig
index 44c6159ca..5e88dc3e9 100644
--- a/src/cli.zig
+++ b/src/cli.zig
@@ -664,7 +664,9 @@ pub const Command = struct {
return;
},
.InstallCommand => {
- try InstallCommand.exec(allocator);
+ const ctx = try Command.Context.create(allocator, log, .BuildCommand);
+
+ try InstallCommand.exec(ctx);
return;
},
.GetCompletionsCommand => {
diff --git a/src/global.zig b/src/global.zig
index 76b502b30..f8c802841 100644
--- a/src/global.zig
+++ b/src/global.zig
@@ -58,6 +58,7 @@ pub const Output = struct {
}
pub fn configureThread() void {
+ if (source_set) return;
std.debug.assert(stdout_stream_set);
source = Source.init(stdout_stream, stderr_stream);
}
diff --git a/src/install/install.zig b/src/install/install.zig
index 7b7a804ee..f2fda1d6e 100644
--- a/src/install/install.zig
+++ b/src/install/install.zig
@@ -31,6 +31,14 @@ var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var path_buf2: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const URL = @import("../query_string_map.zig").URL;
+threadlocal var initialized_store = false;
+pub fn initializeStore() void {
+ if (initialized_store) return;
+ initialized_store = true;
+ JSAst.Expr.Data.Store.create(default_allocator);
+ JSAst.Stmt.Data.Store.create(default_allocator);
+}
+
pub const URI = union(Tag) {
local: string,
remote: URL,
@@ -88,6 +96,7 @@ pub const ExternalStringMap = extern struct {
pub const Dependency = struct {
name: string,
+ name_hash: u32,
version: Dependency.Version,
from: PackageID = invalid_package_id,
resolution: PackageID = invalid_package_id,
@@ -146,11 +155,11 @@ pub const Dependency = struct {
pub fn infer(dependency: string) Tag {
switch (dependency[0]) {
// npm package
- '~', '0'...'9', '^', '*', '~', '|' => return Tag.npm,
+ '0'...'9', '^', '*', '~', '|' => return Tag.npm,
// MIGHT be semver, might not be.
'x', 'X' => {
- if (dependecy.len == 1) {
+ if (dependency.len == 1) {
return Tag.npm;
}
@@ -317,9 +326,7 @@ pub const Dependency = struct {
const dependency = std.mem.trimLeft(u8, dependency_, " \t\n\r");
if (dependency.len == 0) return null;
-
- const tag = Tag.infer(dependency);
-
+ const tag = Version.Tag.infer(dependency);
switch (tag) {
.npm => {
const version = Semver.Query.parse(allocator, dependency) catch |err| {
@@ -337,7 +344,7 @@ pub const Dependency = struct {
if (strings.startsWith(dependency, "file://")) {
return Version{ .tarball = URI{ .local = dependency[7..] } };
} else if (strings.startsWith(dependency, "https://") or strings.startsWith(dependency, "http://")) {
- return Version{ .tarball = URI{ .remote = dependency } };
+ return Version{ .tarball = URI{ .remote = URL.parse(dependency) } };
} else {
log.addErrorFmt(null, logger.Loc.Empty, allocator, "invalid dependency \"{s}\"", .{dependency}) catch unreachable;
return null;
@@ -385,6 +392,8 @@ pub const Package = struct {
preinstall_state: PreinstallState = PreinstallState.unknown,
+ const Version = Dependency.Version;
+
pub fn fromNPM(
allocator: *std.mem.Allocator,
package_id: PackageID,
@@ -398,7 +407,7 @@ pub const Package = struct {
package.name = manifest.name;
package.version = version;
- package.dependencies = package.createDependencyList(
+ package.dependencies = Package.createDependencyList(
allocator,
package_id,
log,
@@ -406,13 +415,49 @@ pub const Package = struct {
package_version.dependencies.name.get(manifest.external_strings),
package_version.dependencies.value.get(manifest.external_strings),
manifest.string_buf,
- );
-
- if (features.dev_dependencies) {}
+ true,
+ ) orelse Dependency.List{};
+
+ // if (features.dev_dependencies) {
+ // package.dependencies = Package.createDependencyList(
+ // allocator,
+ // package_id,
+ // log,
+ // &package.npm_count,
+ // package_version.dependencies.name.get(manifest.external_strings),
+ // package_version.dependencies.value.get(manifest.external_strings),
+ // manifest.string_buf,
+ // true,
+ // ) orelse Dependency.List{};
+ // }
+
+ // if (features.peer_dependencies) {
+ // package.dependencies = Package.createDependencyList(
+ // allocator,
+ // package_id,
+ // log,
+ // &package.npm_count,
+ // package_version.peer_dependencies.name.get(manifest.external_strings),
+ // package_version.peer_dependencies.value.get(manifest.external_strings),
+ // manifest.string_buf,
+ // true,
+ // ) orelse Dependency.List{};
+ // }
- if (features.peer_dependencies) {}
+ if (features.optional_dependencies) {
+ package.dependencies = Package.createDependencyList(
+ allocator,
+ package_id,
+ log,
+ &package.npm_count,
+ package_version.optional_dependencies.name.get(manifest.external_strings),
+ package_version.optional_dependencies.value.get(manifest.external_strings),
+ manifest.string_buf,
+ false,
+ ) orelse Dependency.List{};
+ }
- if (features.optional_dependencies) {}
+ return package;
}
fn createDependencyList(
@@ -425,29 +470,27 @@ pub const Package = struct {
string_buf: []const u8,
required: bool,
) ?Dependency.List {
- if (expr.data != .e_object) return null;
-
- const properties = expr.data.e_object.properties;
- if (properties.len == 0) return null;
+ if (names.len == 0 or names.len != values.len) return null;
var dependencies = Dependency.List{};
- dependencies.ensureTotalCapacity(allocator, properties.len) catch @panic("OOM while parsing dependencies?");
+ dependencies.ensureTotalCapacity(allocator, names.len) catch @panic("OOM while parsing dependencies?");
var npm_count = npm_count_.*;
defer npm_count_.* = npm_count;
- for (properties) |prop| {
- const name = prop.key.?.asString(allocator) orelse continue;
- const value = prop.value.?.asString(allocator) orelse continue;
+ for (names) |name_, i| {
+ const name = name_.slice(string_buf);
+ const value = values[i].slice(string_buf);
const version = Dependency.parse(allocator, value, log) orelse continue;
const name_hash = @truncate(u32, std.hash.Wyhash.hash(0, name));
const dependency = Dependency{
.name = name,
+ .name_hash = name_hash,
.version = version,
.from = package_id,
.required = required,
};
- var entry = dependencies.getOrPutAssumeCapacityContext(allocator, name_hash, ArrayIdentityContext{});
+ var entry = dependencies.getOrPutAssumeCapacityContext(name_hash, ArrayIdentityContext{});
entry.value_ptr.* = dependency;
npm_count += @as(u32, @boolToInt(@enumToInt(dependency.version) > @enumToInt(Version.Tag.npm))) * @as(u32, @boolToInt(!entry.found_existing));
@@ -479,7 +522,7 @@ pub const Package = struct {
pub fn determinePreinstallState(this: *Package, manager: *PackageManager) PreinstallState {
switch (this.preinstall_state) {
.unknown => {
- const folder_path = manager.cachedNPMPackageFolderName(this.name, this.version);
+ const folder_path = PackageManager.cachedNPMPackageFolderName(this.name, this.version);
if (manager.isFolderInCache(folder_path)) {
this.preinstall_state = .done;
return this.preinstall_state;
@@ -525,11 +568,12 @@ pub const Package = struct {
const name_hash = @truncate(u32, std.hash.Wyhash.hash(0, name));
const dependency = Dependency{
.name = name,
+ .name_hash = name_hash,
.version = version,
.from = package_id,
.required = required,
};
- var entry = dependencies.getOrPutAssumeCapacityContext(allocator, name_hash, ArrayIdentityContext{});
+ var entry = dependencies.getOrPutAssumeCapacityContext(name_hash, ArrayIdentityContext{});
entry.value_ptr.* = dependency;
npm_count += @as(u32, @boolToInt(@enumToInt(dependency.version) > @enumToInt(Version.Tag.npm))) * @as(u32, @boolToInt(!entry.found_existing));
@@ -544,6 +588,7 @@ pub const Package = struct {
source: logger.Source,
comptime features: Features,
) !Package {
+ initializeStore();
var json = json_parser.ParseJSON(&source, log, allocator) catch |err| {
if (Output.enable_ansi_colors) {
log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {};
@@ -555,6 +600,7 @@ pub const Package = struct {
};
var package = Package{
+ .id = package_id,
.origin = if (features.is_main) .local else .npm,
};
@@ -609,13 +655,11 @@ pub const Package = struct {
fn ObjectPool(comptime Type: type, comptime Init: (fn (allocator: *std.mem.Allocator) anyerror!Type)) type {
return struct {
const LinkedList = std.SinglyLinkedList(Type);
- var list: LinkedList = undefined;
- var loaded: bool = false;
- var lock: Lock = undefined;
+ // mimalloc crashes on realloc across threads
+ threadlocal var list: LinkedList = undefined;
+ threadlocal var loaded: bool = false;
pub fn get(allocator: *std.mem.Allocator) *LinkedList.Node {
if (loaded) {
- lock.lock();
- defer lock.unlock();
if (list.popFirst()) |node| {
node.data.reset();
return node;
@@ -634,15 +678,12 @@ fn ObjectPool(comptime Type: type, comptime Init: (fn (allocator: *std.mem.Alloc
pub fn release(node: *LinkedList.Node) void {
if (loaded) {
- lock.lock();
- defer lock.unlock();
list.prepend(node);
return;
}
list = LinkedList{ .first = node };
loaded = true;
- lock = Lock.init();
}
};
}
@@ -897,7 +938,7 @@ const Npm = struct {
pub fn findByDistTag(this: *const PackageManifest, tag: string) ?FindResult {
for (this.pkg.dist_tags.tags.get(this.external_strings)) |tag_str, i| {
- if (strings.eql(tag_str.slice(this.string_buffer), tag)) {
+ if (strings.eql(tag_str.slice(this.string_buf), tag)) {
return this.findByVersion(this.pkg.dist_tags.versions.get(this.versions)[i]);
}
}
@@ -912,8 +953,8 @@ const Npm = struct {
return this.findByVersion(left.version);
}
- const releases = this.pkg.releases.keys.get(this.package_versions);
- const prereleases = this.pkg.prereleases.keys.get(this.package_versions);
+ const releases = this.pkg.releases.keys.get(this.versions);
+ const prereleases = this.pkg.prereleases.keys.get(this.versions);
// // For now, this is the dumb way
for (releases) |version, i| {
@@ -922,7 +963,7 @@ const Npm = struct {
}
}
- for (prereleases) |version| {
+ for (prereleases) |version, i| {
if (group.satisfies(version)) {
return FindResult{ .version = version, .package = &this.pkg.prereleases.values.mut(this.package_versions)[i] };
}
@@ -942,6 +983,7 @@ const Npm = struct {
public_max_age: u32,
) !?PackageManifest {
const source = logger.Source.initPathString(expected_name, json_buffer);
+ initializeStore();
const json = json_parser.ParseJSON(&source, log, allocator) catch |err| {
return null;
};
@@ -1072,7 +1114,10 @@ const Npm = struct {
var extern_strings = all_extern_strings;
try string_builder.allocate(allocator);
- var string_buf = string_builder.ptr.?[0..string_builder.cap];
+ var string_buf: string = "";
+ if (string_builder.ptr) |ptr| {
+ string_buf = ptr[0..string_builder.cap];
+ }
if (json.asProperty("name")) |name_q| {
const name = name_q.expr.asString(allocator) orelse return null;
@@ -1279,8 +1324,13 @@ const Npm = struct {
}
}
- result.pkg.last_modified = string_slice.sub(string_builder.append(last_modified)).external();
- result.pkg.etag = string_slice.sub(string_builder.append(etag)).external();
+ if (last_modified.len > 0) {
+ result.pkg.last_modified = string_slice.sub(string_builder.append(last_modified)).external();
+ }
+
+ if (etag.len > 0) {
+ result.pkg.etag = string_slice.sub(string_builder.append(etag)).external();
+ }
result.pkg.releases.keys.len = @truncate(u32, release_versions_len);
result.pkg.releases.values.len = @truncate(u32, release_versions_len);
@@ -1318,45 +1368,6 @@ const Npm = struct {
};
};
-pub const DownloadPackageManifestTask = struct {
- ctx: *PackageManager,
- name: string,
-
- task: ThreadPool.Task,
-
- pub fn download(task: *ThreadPool.Task) void {
- var this: *DownloadPackageManifestTask = @fieldParentPtr(DownloadPackageManifestTask, "task", task);
- defer {
- var node = @fieldParentPtr(Pool.LinkedList.Node, "data", this);
- this.name = "";
- Pool.release(node);
- }
-
- var log = logger.Log.init(this.ctx.allocator);
- defer if (log.msgs.items.len > 0) this.ctx.mergeLog(log);
- var package_manifest = this.ctx.registry.getPackageMetadata(this.ctx.allocator, &log, this.name, "", "") catch |err| {
- log.addErrorFmt(null, logger.Loc.Empty, this.ctx.allocator, "Error fetching package manifest: {s}", .{@errorName(err)}) catch unreachable;
- return;
- };
- switch (package_manifest) {
- .not_found => {
- log.addErrorFmt(null, logger.Loc.Empty, this.ctx.allocator, "Package not found: {s}", this.name);
- return;
- },
- .fresh => |resolved| {
- this.ctx.appendPackageResolution(resolved);
- },
- else => unreachable,
- }
- }
-
- pub fn initFn(allocator: *std.mem.Allocator) !DownloadPackageManifestTask {
- return DownloadPackageManifestTask{ .ctx = undefined, .name = "", .task = .{ .callback = download } };
- }
-
- pub const Pool = ObjectPool(DownloadPackageManifestTask, initFn);
-};
-
pub const DependencyLevel = enum { dependency, dev, optional, peer };
pub const Dependents = std.EnumArray(DependencyLevel, std.ArrayListUnmanaged(PackageID));
@@ -1389,18 +1400,18 @@ const PackageList = struct {
allocator: *std.mem.Allocator = undefined,
- pub fn at(this: *const PackageList, index: PackageID) ?*Package {
+ pub fn at(this: *PackageList, index: PackageID) ?*Package {
if (index == invalid_package_id) return null;
const block_id = index >> 8;
std.debug.assert(this.block_i >= block_id);
return if (block_id == 0)
- &this.head[index]
+ &this.head.items[index]
else
- &this.blocks[block_id][index % comptime (PackageBlock.block_size - 1)];
+ &this.blocks[block_id].items[index % comptime (PackageBlock.block_size - 1)];
}
pub fn append(this: *PackageList, package: Package) !*Package {
- var block: *PackageBlock = this.tail.load(.Monotonic) orelse &this.head;
+ var block: *PackageBlock = this.blocks[this.block_i];
if (block.len.fetchMin(PackageBlock.block_size, .Monotonic) >= PackageBlock.block_size) {
// block.lock.lock();
@@ -1419,7 +1430,7 @@ const PackageList = struct {
}
pub fn reserveOne(this: *PackageList) !PackageID {
- var block: *PackageBlock = &this.blocks[block_i];
+ var block: *PackageBlock = this.blocks[this.block_i];
if (block.len.fetchMin(PackageBlock.block_size, .Monotonic) >= PackageBlock.block_size) {
// block.lock.lock();
@@ -1432,22 +1443,24 @@ const PackageList = struct {
this.blocks[this.block_i] = tail;
const result = this.block_i << 8;
this.block_i += 1;
- return result;
+ return @truncate(u32, result);
} else {
- return block.len.fetchAdd(1, .Monotonic) + (this.block_i << 8);
+ return @truncate(u32, block.len.fetchAdd(1, .Monotonic) + (this.block_i << 8));
}
}
};
-const IdentityContext = struct {
- pub fn hash(this: @This(), key: u32) u64 {
- return key;
- }
+pub fn IdentityContext(comptime Key: type) type {
+ return struct {
+ pub fn hash(this: @This(), key: Key) u64 {
+ return key;
+ }
- pub fn eql(this: @This(), a: u32, b: u32) bool {
- return a == b;
- }
-};
+ pub fn eql(this: @This(), a: Key, b: Key) bool {
+ return a == b;
+ }
+ };
+}
const ArrayIdentityContext = struct {
pub fn hash(this: @This(), key: u32) u32 {
@@ -1472,7 +1485,7 @@ const TarballDownload = struct {
defer Npm.Registry.BodyPool.release(body_node);
body_node.data.reset();
try this.download(&body_node.data);
- return this.extract(body_node.data.items);
+ return this.extract(body_node.data.list.items);
}
fn buildURL(allocator: *std.mem.Allocator, registry_: string, full_name: string, version: Semver.Version) !string {
@@ -1489,25 +1502,29 @@ const TarballDownload = struct {
if (!version.tag.hasPre() and !version.tag.hasBuild()) {
return try std.fmt.allocPrint(
- default_format ++ "{s}-{d}.{d}.{d}",
+ allocator,
+ default_format ++ "{s}-{d}.{d}.{d}.tgz",
.{ registry, full_name, name, version.major, version.minor, version.patch },
);
// TODO: tarball URLs for build/pre
} else if (version.tag.hasPre() and version.tag.hasBuild()) {
return try std.fmt.allocPrint(
- default_format ++ "{s}-{d}.{d}.{d}.{d}-{x}+{X}",
+ allocator,
+ default_format ++ "{s}-{d}.{d}.{d}-{x}+{X}.tgz",
.{ registry, full_name, name, version.major, version.minor, version.patch, version.tag.pre.hash, version.tag.build.hash },
);
// TODO: tarball URLs for build/pre
} else if (version.tag.hasPre()) {
return try std.fmt.allocPrint(
- default_format ++ "{s}-{d}.{d}.{d}.{d}-{x}",
+ allocator,
+ default_format ++ "{s}-{d}.{d}.{d}-{x}.tgz",
.{ registry, full_name, name, version.major, version.minor, version.patch, version.tag.pre.hash },
);
// TODO: tarball URLs for build/pre
} else if (version.tag.hasBuild()) {
return try std.fmt.allocPrint(
- default_format ++ "{s}-{d}.{d}.{d}.{d}+{X}",
+ allocator,
+ default_format ++ "{s}-{d}.{d}.{d}+{X}.tgz",
.{ registry, full_name, name, version.major, version.minor, version.patch, version.tag.build.hash },
);
} else {
@@ -1552,25 +1569,41 @@ const TarballDownload = struct {
}
}
- var tmpname = Fs.FileSystem.instance.tmpname(basename, &tmpname_buf, tgz_bytes.len);
+ var tmpname = try Fs.FileSystem.instance.tmpname(basename, &tmpname_buf, tgz_bytes.len);
- var cache_dir = tmpdir.makeOpenPath(tmpname) catch |err| {
+ var cache_dir = tmpdir.makeOpenPath(std.mem.span(tmpname), .{ .iterate = true }) catch |err| {
Output.panic("err: {s} when create temporary directory named {s} (while extracting {s})", .{ @errorName(err), tmpname, this.name });
};
- var temp_destination = std.os.getFdPath(cache_dir.handle, &abs_buf) catch |err| {
+ var temp_destination = std.os.getFdPath(cache_dir.fd, &abs_buf) catch |err| {
Output.panic("err: {s} when resolve path for temporary directory named {s} (while extracting {s})", .{ @errorName(err), tmpname, this.name });
};
cache_dir.close();
if (verbose_install) {
- Output.prettyErrorln("[{s}] Start extracting {s}<r>", .{this.name});
+ Output.prettyErrorln("[{s}] Start extracting {s}<r>", .{ this.name, tmpname });
Output.flush();
}
const Archive = @import("../libarchive/libarchive.zig").Archive;
+ const Zlib = @import("../zlib.zig");
+ var zlib_pool = Npm.Registry.BodyPool.get(default_allocator);
+ zlib_pool.data.reset();
+ defer Npm.Registry.BodyPool.release(zlib_pool);
+ var zlib_entry = try Zlib.ZlibReaderArrayList.init(tgz_bytes, &zlib_pool.data.list, default_allocator);
+ zlib_entry.readAll() catch |err| {
+ Output.prettyErrorln(
+ "<r><red>Error {s}<r> decompressing {s}",
+ .{
+ @errorName(err),
+ this.name,
+ },
+ );
+ Output.flush();
+ Global.crash();
+ };
const extracted_file_count = try Archive.extractToDisk(
- tgz_bytes,
+ zlib_pool.data.list.items,
temp_destination,
null,
void,
@@ -1618,12 +1651,12 @@ const TarballDownload = struct {
},
);
Output.flush();
- Output.crash();
+ Global.crash();
};
// We return a resolved absolute absolute file path to the cache dir.
// To get that directory, we open the directory again.
- var final_dir = PackageManager.instance.cache_directory.openDirZ(PackageManager.instance.cache_directory.fd, folder_name) catch |err| {
+ var final_dir = PackageManager.instance.cache_directory.openDirZ(folder_name, .{ .iterate = true }) catch |err| {
Output.prettyErrorln(
"<r><red>Error {s}<r> failed to verify cache dir for {s}",
.{
@@ -1632,12 +1665,13 @@ const TarballDownload = struct {
},
);
Output.flush();
- Output.crash();
+ Global.crash();
};
defer final_dir.close();
// and get the fd path
var final_path = std.os.getFdPath(
- final_dir,
+ final_dir.fd,
+ &abs_buf,
) catch |err| {
Output.prettyErrorln(
"<r><red>Error {s}<r> failed to verify cache dir for {s}",
@@ -1647,7 +1681,7 @@ const TarballDownload = struct {
},
);
Output.flush();
- Output.crash();
+ Global.crash();
};
return try Fs.FileSystem.instance.dirname_store.append(@TypeOf(final_path), final_path);
}
@@ -1661,6 +1695,7 @@ const Task = struct {
data: Data,
status: Status = Status.waiting,
threadpool_task: ThreadPool.Task = ThreadPool.Task{ .callback = callback },
+ log: logger.Log,
id: u64,
/// An ID that lets us register a callback without keeping the same pointer around
@@ -1685,9 +1720,52 @@ const Task = struct {
};
pub fn callback(task: *ThreadPool.Task) void {
+ Output.Source.configureThread();
+ defer Output.flush();
+
var this = @fieldParentPtr(Task, "threadpool_task", task);
- switch (this.tag) {}
+ switch (this.tag) {
+ .package_manifest => {
+ var allocator = PackageManager.instance.allocator;
+
+ const package_manifest = PackageManager.instance.registry.getPackageMetadata(allocator, &this.log, this.request.package_manifest, "", "") catch |err| {
+ this.status = Status.fail;
+ PackageManager.instance.resolve_tasks.writeItem(this.*) catch unreachable;
+ return;
+ };
+
+ this.data = .{ .package_manifest = .{ .name = "" } };
+
+ switch (package_manifest) {
+ .cached => unreachable,
+ .fresh => |manifest| {
+ this.data = .{ .package_manifest = manifest };
+ this.status = Status.success;
+ PackageManager.instance.resolve_tasks.writeItem(this.*) catch unreachable;
+ return;
+ },
+ .not_found => {
+ this.log.addErrorFmt(null, logger.Loc.Empty, allocator, "404 - GET {s}", .{this.request.package_manifest}) catch unreachable;
+ this.status = Status.fail;
+ PackageManager.instance.resolve_tasks.writeItem(this.*) catch unreachable;
+ return;
+ },
+ }
+ },
+ .tarball_download => {
+ const result = this.request.tarball_download.run() catch |err| {
+ this.status = Status.fail;
+ this.data = .{ .tarball_download = "" };
+ PackageManager.instance.resolve_tasks.writeItem(this.*) catch unreachable;
+ return;
+ };
+
+ this.data = .{ .tarball_download = result };
+ this.status = Status.success;
+ PackageManager.instance.resolve_tasks.writeItem(this.*) catch unreachable;
+ },
+ }
}
pub const Tag = enum(u4) {
@@ -1716,12 +1794,12 @@ const Task = struct {
const TaggedPointer = @import("../tagged_pointer.zig");
const TaskCallbackContext = TaggedPointer.TaggedPointerUnion(.{
- *Dependency,
- *Package,
+ Dependency,
+ Package,
});
const TaskCallbackList = std.ArrayListUnmanaged(TaskCallbackContext);
-const TaskDependencyQueue = std.HashMapUnmanaged(Task.Id, TaskCallbackList, IdentityContext, 80);
+const TaskDependencyQueue = std.HashMapUnmanaged(u64, TaskCallbackList, IdentityContext(u64), 80);
const TaskChannel = sync.Channel(Task, .{ .Static = 64 });
const ThreadPool = @import("../thread_pool.zig");
@@ -1755,11 +1833,11 @@ pub const PackageManager = struct {
task_queue: TaskDependencyQueue = TaskDependencyQueue{},
const PackageIndex = std.AutoHashMapUnmanaged(u64, *Package);
- const PackageManifestMap = std.HashMapUnmanaged(u32, Npm.PackageManifest, IdentityContext, 80);
+ const PackageManifestMap = std.HashMapUnmanaged(u32, Npm.PackageManifest, IdentityContext(u32), 80);
const PackageDedupeList = std.HashMapUnmanaged(
u32,
void,
- IdentityContext,
+ IdentityContext(u32),
80,
);
@@ -1779,19 +1857,19 @@ pub const PackageManager = struct {
} else if (version.tag.hasPre() and version.tag.hasBuild()) {
return std.fmt.bufPrintZ(
buf,
- "{s}@{d}.{d}.{d}.{d}-{x}+{X}",
+ "{s}@{d}.{d}.{d}-{x}+{X}",
.{ name, version.major, version.minor, version.patch, version.tag.pre.hash, version.tag.build.hash },
) catch unreachable;
} else if (version.tag.hasPre()) {
return std.fmt.bufPrintZ(
buf,
- "{s}@{d}.{d}.{d}.{d}-{x}",
+ "{s}@{d}.{d}.{d}-{x}",
.{ name, version.major, version.minor, version.patch, version.tag.pre.hash },
) catch unreachable;
} else if (version.tag.hasBuild()) {
return std.fmt.bufPrintZ(
buf,
- "{s}@{d}.{d}.{d}.{d}+{X}",
+ "{s}@{d}.{d}.{d}+{X}",
.{ name, version.major, version.minor, version.patch, version.tag.build.hash },
) catch unreachable;
} else {
@@ -1803,7 +1881,7 @@ pub const PackageManager = struct {
pub fn isFolderInCache(this: *PackageManager, folder_path: stringZ) bool {
// TODO: is this slow?
- var dir = this.cache_directory.openDirZ(folder_path) catch return false;
+ var dir = this.cache_directory.openDirZ(folder_path, .{ .iterate = true }) catch return false;
dir.close();
return true;
}
@@ -1828,13 +1906,14 @@ pub const PackageManager = struct {
switch (version) {
.npm, .dist_tag => {
// Resolve the version from the loaded NPM manifest
- const manifest = this.manifests.getPtr(name_hash) orelse return null; // manifest might still be dowlnoading
- const find_result = switch (version) {
+ const manifest = this.manifests.getPtr(name_hash) orelse return null; // manifest might still be downloading. This feels unreliable.
+ const find_result: Npm.PackageManifest.FindResult = switch (version) {
.dist_tag => manifest.findByDistTag(version.dist_tag),
.npm => manifest.findBestVersion(version.npm),
+ else => unreachable,
} orelse return switch (version) {
- .npm => error.DistTagNotFound,
- .dist_tag => error.NoMatchingVersion,
+ .npm => error.NoMatchingVersion,
+ .dist_tag => error.DistTagNotFound,
else => unreachable,
};
@@ -1843,7 +1922,7 @@ pub const PackageManager = struct {
// Was this package already allocated? Let's reuse the existing one.
if (resolved_package_entry.found_existing) {
resolution.* = resolved_package_entry.value_ptr.*.id;
- return ResolvedPackageResult{ .package = resolved_package_entry.value_ptr };
+ return ResolvedPackageResult{ .package = resolved_package_entry.value_ptr.* };
}
const id = package_list.reserveOne() catch unreachable;
@@ -1855,9 +1934,9 @@ pub const PackageManager = struct {
this.log,
manifest,
find_result.version,
- find_result.package_version,
+ find_result.package,
this.default_features,
- ) catch unreachable;
+ );
resolved_package_entry.value_ptr.* = ptr;
switch (ptr.determinePreinstallState(this)) {
@@ -1876,6 +1955,7 @@ pub const PackageManager = struct {
var task = try this.allocator.create(Task);
task.* = Task{
.id = task_id,
+ .log = logger.Log.init(this.allocator),
.tag = .tarball_download,
.data = undefined,
.request = .{
@@ -1889,7 +1969,7 @@ pub const PackageManager = struct {
},
};
- return ResolvedPackageResult{ .package = ptr, .task = task };
+ return ResolvedPackageResult{ .package = ptr, .task = &task.threadpool_task };
},
else => unreachable,
}
@@ -1915,18 +1995,20 @@ pub const PackageManager = struct {
) *ThreadPool.Task {
var task = this.allocator.create(Task) catch unreachable;
task.* = Task{
+ .log = logger.Log.init(this.allocator),
.tag = Task.Tag.package_manifest,
.request = .{ .package_manifest = name },
.id = task_id,
.data = undefined,
};
- return task;
+ return &task.threadpool_task;
}
- inline fn enqueueDependency(this: *PackageManager, dependency: *Dependency, required: bool) !?*ThreadPool.Task {
+ fn enqueueDependency(this: *PackageManager, dependency: *Dependency, required: bool) !?ThreadPool.Batch {
const name = dependency.name;
const name_hash = dependency.name_hash;
const version: Dependency.Version = dependency.version;
+ var batch = ThreadPool.Batch{};
switch (dependency.version) {
.npm, .dist_tag => {
const resolve_result = this.getOrPutResolvedPackage(name_hash, name, version, &dependency.resolution) catch |err| {
@@ -1974,13 +2056,26 @@ pub const PackageManager = struct {
const label: string = switch (version) {
.npm => version.npm.input,
.dist_tag => version.dist_tag,
+ else => unreachable,
};
- Output.prettyErrorln("Resolved \"{s}\": \"{s}\" -> {s}@{s}", .{ result.package.name, label });
+ Output.prettyErrorln("Resolved \"{s}\": \"{s}\" -> {s}@{}", .{ result.package.name, label, result.package.name, result.package.version });
}
}
- return result.task;
+ // First time?
+ if (result.task != null) {
+ // Resolve dependencies first
+ batch.push(this.enqueuePackages(result.package.dependencies, true));
+ if (this.default_features.peer_dependencies) batch.push(this.enqueuePackages(result.package.peer_dependencies, true));
+ if (this.default_features.optional_dependencies) batch.push(this.enqueuePackages(result.package.optional_dependencies, false));
+ batch.push(ThreadPool.Batch.from(result.task.?));
+ return batch;
+ }
+
+ if (batch.len > 0) {
+ return batch;
+ }
} else {
const task_id = Task.Id.forManifest(Task.Tag.package_manifest, name);
var manifest_download_queue_entry = try this.task_queue.getOrPutContext(this.allocator, task_id, .{});
@@ -1994,7 +2089,8 @@ pub const PackageManager = struct {
Output.prettyErrorln("Enqueue package manifest: {s}", .{name});
}
- return this.enqueueNpmPackage(task_id, name);
+ batch.push(ThreadPool.Batch.from(this.enqueueNpmPackage(task_id, name)));
+ return batch;
}
}
@@ -2002,6 +2098,7 @@ pub const PackageManager = struct {
},
else => {},
}
+ return null;
}
fn enqueuePackages(this: *PackageManager, dependencies: Dependency.List, required: bool) ThreadPool.Batch {
@@ -2011,16 +2108,9 @@ pub const PackageManager = struct {
const values = slice.items(.value);
const keys = slice.items(.key);
var i: usize = 0;
- var last_task: ?*ThreadPool.Task = null;
while (i < values.len) : (i += 1) {
- var task = try this.enqueueDependency(&values[i], required) orelse continue;
- if (last_task != null) {
- batch.tail.?.node.next = &task.node;
- batch.len += 1;
- } else {
- batch.* = ThreadPool.Batch.from(task);
- }
- batch.tail = &task;
+ var new_batch = (this.enqueueDependency(&values[i], required) catch null) orelse continue;
+ batch.push(new_batch);
}
if (verbose_install) Output.flush();
@@ -2029,20 +2119,20 @@ pub const PackageManager = struct {
}
pub fn enqueueDependencyList(this: *PackageManager, package: *const Package, features: Package.Features) u32 {
- this.task_queue.ensureUnusedCapacity(package.npm_count) catch unreachable;
+ this.task_queue.ensureUnusedCapacity(this.allocator, package.npm_count) catch unreachable;
var batch = this.enqueuePackages(package.dependencies, true);
if (features.dev_dependencies) {
- batch = batch.push(this.enqueuePackages(package.dev_dependencies, true));
+ batch.push(this.enqueuePackages(package.dev_dependencies, true));
}
if (features.peer_dependencies) {
- batch = batch.push(this.enqueuePackages(package.peer_dependencies, true));
+ batch.push(this.enqueuePackages(package.peer_dependencies, true));
}
if (features.optional_dependencies) {
- batch = batch.push(this.enqueuePackages(package.optional_dependencies, false));
+ batch.push(this.enqueuePackages(package.optional_dependencies, false));
}
this.thread_pool.schedule(batch);
@@ -2060,22 +2150,22 @@ pub const PackageManager = struct {
if (env_loader.map.get("BUN_INSTALL")) |dir| {
var parts = [_]string{ dir, "install/", "cache/" };
- return Fs.FileSystem.instance.absBuf(&parts);
+ return Fs.FileSystem.instance.abs(&parts);
}
if (env_loader.map.get("HOME")) |dir| {
var parts = [_]string{ dir, ".bun/", "install/", "cache/" };
- return Fs.FileSystem.instance.absBuf(&parts);
+ return Fs.FileSystem.instance.abs(&parts);
}
if (env_loader.map.get("XDG_CACHE_HOME")) |dir| {
var parts = [_]string{ dir, ".bun/", "install/", "cache/" };
- return Fs.FileSystem.instance.absBuf(&parts);
+ return Fs.FileSystem.instance.abs(&parts);
}
if (env_loader.map.get("TMPDIR")) |dir| {
var parts = [_]string{ dir, ".bun-cache" };
- return Fs.FileSystem.instance.absBuf(&parts);
+ return Fs.FileSystem.instance.abs(&parts);
}
return null;
@@ -2105,19 +2195,22 @@ pub const PackageManager = struct {
cwd_buf[parent.len + 1] = 0;
var chdir = cwd_buf[0..parent.len :0];
- std.os.chdirZ(chdir) catch break :brk null;
- std.fs.cwd().openFileZ("package.json", .{ .read = true, .write = true }) catch |err| {
+ std.os.chdirZ(chdir) catch |err| {
+ Output.prettyErrorln("Error {s} while chdir - {s}", .{ @errorName(err), chdir });
+ Output.flush();
+ return;
+ };
+
+ break :brk std.fs.cwd().openFileZ("package.json", .{ .read = true, .write = true }) catch |err| {
this_cwd = parent;
continue :outer;
};
}
- break :brk null;
+ Output.prettyErrorln("<r><green>No package.json<r> Nothing to install.", .{});
+ Output.flush();
+ return;
};
- } orelse {
- Output.prettyErrorln("<r><green>No package.json<r> Nothing to install.", .{});
- Output.flush();
- return;
};
fs.top_level_dir = try std.os.getcwd(&cwd_buf);
@@ -2127,7 +2220,7 @@ pub const PackageManager = struct {
std.mem.copy(u8, &package_json_cwd_buf, fs.top_level_dir);
std.mem.copy(u8, package_json_cwd_buf[fs.top_level_dir.len..], "package.json");
var package_json_contents = package_json_file.readToEndAlloc(ctx.allocator, std.math.maxInt(usize)) catch |err| {
- Output.prettyErrorln("<r><red>{s} reading package.json<r>!", .{@errorName(err)});
+ Output.prettyErrorln("<r><red>{s} reading package.json<r> :(", .{@errorName(err)});
Output.flush();
return;
};
@@ -2135,8 +2228,14 @@ pub const PackageManager = struct {
//
var package_json_source = logger.Source.initPathString(
package_json_cwd_buf[0 .. fs.top_level_dir.len + "package.json".len],
+ package_json_contents,
);
- package_list.items[0] = try Package.parse(
+
+ package_list.allocator = ctx.allocator;
+ package_list.blocks[0] = &package_list.head;
+
+ var root = try package_list.append(try Package.parse(
+ 0,
ctx.allocator,
ctx.log,
package_json_source,
@@ -2145,21 +2244,24 @@ pub const PackageManager = struct {
.dev_dependencies = true,
.is_main = true,
},
- );
- var root = &package_list.items[0];
- package_list.len = 1;
- var env_loader: DotEnv.Loader = brk: {
+ ));
+ var env_loader: *DotEnv.Loader = brk: {
var map = try ctx.allocator.create(DotEnv.Map);
map.* = DotEnv.Map.init(ctx.allocator);
- break :brk DotEnv.Loader.init(map, ctx.allocator);
+ var loader = try ctx.allocator.create(DotEnv.Loader);
+ loader.* = DotEnv.Loader.init(map, ctx.allocator);
+ break :brk loader;
};
- var entries_option = try fs.fs.readDirectory(fs.top_Level_dir, std.fs.cwd());
+ var entries_option = try fs.fs.readDirectory(fs.top_level_dir, null);
var enable_cache = false;
var cache_directory_path: string = "";
var cache_directory: std.fs.Dir = undefined;
- if (Install.fetchCacheDirectoryPath(ctx.allocator, env_loader, entries_option.dir)) |cache_dir_path| {
+ env_loader.loadProcess();
+ try env_loader.load(&fs.fs, &entries_option.entries, false);
+
+ if (PackageManager.fetchCacheDirectoryPath(ctx.allocator, env_loader, &entries_option.entries)) |cache_dir_path| {
enable_cache = true;
cache_directory_path = try fs.dirname_store.append(@TypeOf(cache_dir_path), cache_dir_path);
cache_directory = std.fs.cwd().makeOpenPath(cache_directory_path, .{ .iterate = true }) catch |err| brk: {
@@ -2174,25 +2276,36 @@ pub const PackageManager = struct {
Output.flush();
}
+ var cpu_count = @truncate(u32, (try std.Thread.getCpuCount()) + 1);
+
+ if (env_loader.map.get("GOMAXPROCS")) |max_procs| {
+ if (std.fmt.parseInt(u32, max_procs, 10)) |cpu_count_| {
+ cpu_count = @minimum(cpu_count, cpu_count_);
+ } else |err| {}
+ }
+
+ var manager = &instance;
// var progress = std.Progress{};
// var node = progress.start(name: []const u8, estimated_total_items: usize)
- manager = PackageManager{
+ manager.* = PackageManager{
.enable_cache = enable_cache,
.cache_directory_path = cache_directory_path,
.cache_directory = cache_directory,
.env_loader = env_loader,
.allocator = ctx.allocator,
.log = ctx.log,
- .root_dir = entries_option.dir,
+ .root_dir = &entries_option.entries,
.root_package = root,
- .thread_pool = ThreadPool.init(.{}),
- .resolve_tasks = TaskChannel{},
+ .thread_pool = ThreadPool.init(.{
+ .max_threads = cpu_count,
+ }),
+ .resolve_tasks = TaskChannel.init(),
// .progress
};
- package_list.allocator = ctx.allocator;
-
- var count = try manager.enqueueDependencyList(
- &package_list.items[0],
+ const S2N = @import("../s2n.zig");
+ S2N.boot(default_allocator);
+ var count = manager.enqueueDependencyList(
+ root,
Package.Features{
.optional_dependencies = true,
.dev_dependencies = true,
@@ -2201,9 +2314,17 @@ pub const PackageManager = struct {
);
var total_count = count;
var extracted_count: usize = 0;
- while (count > 0) : (count = @maximum(count, 1) - 1) {
+ while (count > 0) {
while (manager.resolve_tasks.tryReadItem() catch null) |task_| {
- var task: *Task = task_;
+ defer count = @maximum(count, 1) - 1;
+ var task: Task = task_;
+ if (task.log.msgs.items.len > 0) {
+ if (Output.enable_ansi_colors) {
+ try task.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true);
+ } else {
+ try task.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false);
+ }
+ }
switch (task.tag) {
.package_manifest => {
if (task.status == .fail) {
@@ -2212,20 +2333,23 @@ pub const PackageManager = struct {
continue;
}
const manifest = task.data.package_manifest;
- var entry = try manager.manifests.getOrPutValue(ctx.allocator, @truncate(u32, manifest.name.hash), manifest);
+ var entry = try manager.manifests.getOrPutValue(ctx.allocator, @truncate(u32, manifest.pkg.name.hash), manifest);
const dependency_list = manager.task_queue.get(task.id).?;
-
+ var batch = ThreadPool.Batch{};
for (dependency_list.items) |item| {
var dependency: *Dependency = TaskCallbackContext.get(item, Dependency).?;
- if (try manager.enqueueDependency(dependency, dependency.required)) |new_task| {
- count += 1;
- total_count += 1;
+ if (try manager.enqueueDependency(dependency, dependency.required)) |new_batch| {
+ batch.push(new_batch);
}
}
+ manager.thread_pool.schedule(batch);
+ count += @truncate(u32, batch.len);
},
.tarball_download => {
if (task.status == .fail) {
- Output.prettyErrorln("Failed to download package manifest for {s}", .{task.request.package_manifest});
+ Output.prettyErrorln("Failed to download tarball for {s}", .{
+ task.request.tarball_download.name,
+ });
Output.flush();
continue;
}
@@ -2240,6 +2364,12 @@ pub const PackageManager = struct {
Output.prettyErrorln("Preinstall complete.\n Extracted: {d} Tasks: {d}", .{ extracted_count, total_count });
}
+ if (Output.enable_ansi_colors) {
+ try manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true);
+ } else {
+ try manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false);
+ }
+
try manager.installDependencies();
}
};
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 7650a61a4..f92159104 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -1060,7 +1060,7 @@ pub const E = struct {
pub inline fn toU64(self: Number) u64 {
@setRuntimeSafety(false);
- return @floatToInt(u64, @maximum(@floor(self.value), 0));
+ return @floatToInt(u64, @maximum(@trunc(self.value), 0));
}
pub fn jsonStringify(self: *const Number, opts: anytype, o: anytype) !void {
diff --git a/src/libarchive/libarchive.zig b/src/libarchive/libarchive.zig
index ef8a2acfa..fda447c1d 100644
--- a/src/libarchive/libarchive.zig
+++ b/src/libarchive/libarchive.zig
@@ -540,7 +540,7 @@ pub const Archive = struct {
var archive = stream.archive;
var count: u32 = 0;
- const dir: std.fs.Dir = brk: {
+ var dir: std.fs.Dir = brk: {
const cwd = std.fs.cwd();
cwd.makePath(
root,
diff --git a/src/s2n.zig b/src/s2n.zig
index 6f7be069d..66eff6274 100644
--- a/src/s2n.zig
+++ b/src/s2n.zig
@@ -9,11 +9,17 @@ pub inline fn s2nassert(value: c_int) void {
std.debug.assert(value == 0);
}
+var booted_any: bool = false;
pub fn boot(allcoator: *std.mem.Allocator) void {
+ if (!booted_any) {
+ s2nassert(s2n_disable_atexit());
+ s2nassert(s2n_init());
+ Allocator.allocator = allcoator;
+ CacheStore.instance = CacheStore{ .allocator = allcoator, .map = @TypeOf(CacheStore.instance.map).init(allcoator) };
+ }
+ booted_any = true;
if (booted) return;
booted = true;
- Allocator.allocator = allcoator;
- CacheStore.instance = CacheStore{ .allocator = allcoator, .map = @TypeOf(CacheStore.instance.map).init(allcoator) };
// Important for any website using Cloudflare.
// Do to our modifications in the library, this must be called _first_
@@ -43,8 +49,6 @@ pub fn boot(allcoator: *std.mem.Allocator) void {
// Also, the implementation
// s2nassert(s2n_mem_set_callbacks(Allocator.initCallback, Allocator.deinitCallback, Allocator.mallocCallback, Allocator.freeCallback));
- s2nassert(s2n_disable_atexit());
- s2nassert(s2n_init());
global_s2n_config = s2n_fetch_default_config();
// s2nassert(s2n_config_set_verify_host_callback(global_s2n_config, verify_host_callback, null));
// s2nassert(s2n_config_set_check_stapled_ocsp_response(global_s2n_config, 0));
@@ -546,8 +550,8 @@ pub const s2n_offered_psk_list = struct_s2n_offered_psk_list;
pub const s2n_async_pkey_op = struct_s2n_async_pkey_op;
pub const s2n_offered_early_data = struct_s2n_offered_early_data;
-var booted = false;
-pub var global_s2n_config: *s2n_config = undefined;
+threadlocal var booted = false;
+pub threadlocal var global_s2n_config: *s2n_config = undefined;
const unexpectedErrno = std.os.unexpectedErrno;
const S2NError = error{ Closed, WouldBlock, Alert, Protocol, Internal, Usage };
pub inline fn s2nErrorNo(rc: c_int) S2NError!std.os.system.E {
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index 7c9b427c4..6657dc27e 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -257,8 +257,8 @@ pub inline fn endsWith(self: string, str: string) bool {
return str.len == 0 or @call(.{ .modifier = .always_inline }, std.mem.endsWith, .{ u8, self, str });
}
-pub inline fn endsWithComptime(self: string, comptime str: antype) bool {
- return self.len >= str.len and strings.eqlComptimeIgnoreLen(self[self.len - str.len .. self.len], comptime str);
+pub inline fn endsWithComptime(self: string, comptime str: anytype) bool {
+ return self.len >= str.len and eqlComptimeIgnoreLen(self[self.len - str.len .. self.len], comptime str);
}
pub inline fn startsWithChar(self: string, char: u8) bool {