aboutsummaryrefslogtreecommitdiff
path: root/src/install/dependency.zig
diff options
context:
space:
mode:
authorGravatar Dylan Conway <dylan.conway567@gmail.com> 2023-10-14 12:58:30 -0700
committerGravatar Dylan Conway <dylan.conway567@gmail.com> 2023-10-14 12:58:30 -0700
commitf9add8b6bea4df3cdbd56a21f17e4cab1a854e4e (patch)
tree8e5306104d81c67b771181337bba02cd9ec39453 /src/install/dependency.zig
parent81a1a58d66c598ea35c42453d0ba4c6341a940fc (diff)
parent9b5e66453b0879ed77b71dcdbe50e4efa184261e (diff)
downloadbun-f9add8b6bea4df3cdbd56a21f17e4cab1a854e4e.tar.gz
bun-f9add8b6bea4df3cdbd56a21f17e4cab1a854e4e.tar.zst
bun-f9add8b6bea4df3cdbd56a21f17e4cab1a854e4e.zip
Merge branch 'main' into sdlsdl
Diffstat (limited to 'src/install/dependency.zig')
-rw-r--r--src/install/dependency.zig160
1 files changed, 107 insertions, 53 deletions
diff --git a/src/install/dependency.zig b/src/install/dependency.zig
index cb73c04e1..ca0d702aa 100644
--- a/src/install/dependency.zig
+++ b/src/install/dependency.zig
@@ -49,10 +49,10 @@ version: Dependency.Version = .{},
/// - `peerDependencies`
/// Technically, having the same package name specified under multiple fields is invalid
/// But we don't want to allocate extra arrays for them. So we use a bitfield instead.
-behavior: Behavior = .uninitialized,
+behavior: Behavior = Behavior.uninitialized,
/// Sorting order for dependencies is:
-/// 1. [`dependencies`, `devDependencies`, `optionalDependencies`, `peerDependencies`]
+/// 1. [ `peerDependencies`, `optionalDependencies`, `devDependencies`, `dependencies` ]
/// 2. name ASC
/// "name" must be ASC so that later, when we rebuild the lockfile
/// we insert it back in reverse order without an extra sorting pass
@@ -147,7 +147,7 @@ pub fn toDependency(
return Dependency{
.name = name,
.name_hash = @as(u64, @bitCast(this[8..16].*)),
- .behavior = @as(Dependency.Behavior, @enumFromInt(this[16])),
+ .behavior = @bitCast(this[16]),
.version = Dependency.Version.toVersion(name, this[17..this.len].*, ctx),
};
}
@@ -156,7 +156,7 @@ pub fn toExternal(this: Dependency) External {
var bytes: External = undefined;
bytes[0..this.name.bytes.len].* = this.name.bytes;
bytes[8..16].* = @as([8]u8, @bitCast(this.name_hash));
- bytes[16] = @intFromEnum(this.behavior);
+ bytes[16] = @bitCast(this.behavior);
bytes[17..bytes.len].* = this.version.toExternal();
return bytes;
}
@@ -221,12 +221,16 @@ pub inline fn isGitHubRepoPath(dependency: string) bool {
return hash_index != dependency.len - 1 and first_slash_index > 0 and first_slash_index != dependency.len - 1;
}
-// Github allows for the following format of URL:
-// https://github.com/<org>/<repo>/tarball/<ref>
-// This is a legacy (but still supported) method of retrieving a tarball of an
-// entire source tree at some git reference. (ref = branch, tag, etc. Note: branch
-// can have arbitrary number of slashes)
+/// Github allows for the following format of URL:
+/// https://github.com/<org>/<repo>/tarball/<ref>
+/// This is a legacy (but still supported) method of retrieving a tarball of an
+/// entire source tree at some git reference. (ref = branch, tag, etc. Note: branch
+/// can have arbitrary number of slashes)
+///
+/// This also checks for a github url that ends with ".tar.gz"
pub inline fn isGitHubTarballPath(dependency: string) bool {
+ if (isTarball(dependency)) return true;
+
var parts = strings.split(dependency, "/");
var n_parts: usize = 0;
@@ -248,7 +252,7 @@ pub inline fn isTarball(dependency: string) bool {
}
pub const Version = struct {
- tag: Dependency.Version.Tag = .uninitialized,
+ tag: Tag = .uninitialized,
literal: String = .{},
value: Value = .{ .uninitialized = {} },
@@ -610,7 +614,7 @@ pub const Version = struct {
}
};
- const NpmInfo = struct {
+ pub const NpmInfo = struct {
name: String,
version: Semver.Query.Group,
@@ -619,7 +623,7 @@ pub const Version = struct {
}
};
- const TagInfo = struct {
+ pub const TagInfo = struct {
name: String,
tag: String,
@@ -628,7 +632,7 @@ pub const Version = struct {
}
};
- const TarballInfo = struct {
+ pub const TarballInfo = struct {
uri: URI,
package_name: String = .{},
@@ -670,7 +674,8 @@ pub inline fn parse(
sliced: *const SlicedString,
log: ?*logger.Log,
) ?Version {
- return parseWithOptionalTag(allocator, alias, dependency, null, sliced, log);
+ const dep = std.mem.trimLeft(u8, dependency, " \t\n\r");
+ return parseWithTag(allocator, alias, dep, Version.Tag.infer(dep), sliced, log);
}
pub fn parseWithOptionalTag(
@@ -888,6 +893,12 @@ pub fn parseWithTag(
.literal = sliced.value(),
.value = .{ .tarball = .{ .uri = .{ .local = sliced.sub(dependency[7..]).value() } } },
};
+ } else if (strings.hasPrefixComptime(dependency, "file:")) {
+ return .{
+ .tag = .tarball,
+ .literal = sliced.value(),
+ .value = .{ .tarball = .{ .uri = .{ .local = sliced.sub(dependency[5..]).value() } } },
+ };
} else if (strings.contains(dependency, "://")) {
if (log_) |log| log.addErrorFmt(null, logger.Loc.Empty, allocator, "invalid or unsupported dependency \"{s}\"", .{dependency}) catch unreachable;
return null;
@@ -950,78 +961,83 @@ pub fn parseWithTag(
}
}
-pub const Behavior = enum(u8) {
- uninitialized = 0,
- _,
+pub const Behavior = packed struct(u8) {
+ pub const uninitialized: Behavior = .{};
+
+ // these padding fields are to have compatibility
+ // with older versions of lockfile v2
+ _unused_1: u1 = 0,
+
+ normal: bool = false,
+ optional: bool = false,
+ dev: bool = false,
+ peer: bool = false,
+ workspace: bool = false,
+
+ _unused_2: u2 = 0,
- pub const normal: u8 = 1 << 1;
- pub const optional: u8 = 1 << 2;
- pub const dev: u8 = 1 << 3;
- pub const peer: u8 = 1 << 4;
- pub const workspace: u8 = 1 << 5;
+ pub const normal = Behavior{ .normal = true };
+ pub const optional = Behavior{ .optional = true };
+ pub const dev = Behavior{ .dev = true };
+ pub const peer = Behavior{ .peer = true };
+ pub const workspace = Behavior{ .workspace = true };
pub inline fn isNormal(this: Behavior) bool {
- return (@intFromEnum(this) & Behavior.normal) != 0;
+ return this.normal;
}
pub inline fn isOptional(this: Behavior) bool {
- return (@intFromEnum(this) & Behavior.optional) != 0 and !this.isPeer();
+ return this.optional and !this.isPeer();
}
pub inline fn isDev(this: Behavior) bool {
- return (@intFromEnum(this) & Behavior.dev) != 0;
+ return this.dev;
}
pub inline fn isPeer(this: Behavior) bool {
- return (@intFromEnum(this) & Behavior.peer) != 0;
+ return this.peer;
}
pub inline fn isWorkspace(this: Behavior) bool {
- return (@intFromEnum(this) & Behavior.workspace) != 0;
+ return this.workspace;
}
pub inline fn setNormal(this: Behavior, value: bool) Behavior {
- if (value) {
- return @as(Behavior, @enumFromInt(@intFromEnum(this) | Behavior.normal));
- } else {
- return @as(Behavior, @enumFromInt(@intFromEnum(this) & ~Behavior.normal));
- }
+ var b = this;
+ b.normal = value;
+ return b;
}
pub inline fn setOptional(this: Behavior, value: bool) Behavior {
- if (value) {
- return @as(Behavior, @enumFromInt(@intFromEnum(this) | Behavior.optional));
- } else {
- return @as(Behavior, @enumFromInt(@intFromEnum(this) & ~Behavior.optional));
- }
+ var b = this;
+ b.optional = value;
+ return b;
}
pub inline fn setDev(this: Behavior, value: bool) Behavior {
- if (value) {
- return @as(Behavior, @enumFromInt(@intFromEnum(this) | Behavior.dev));
- } else {
- return @as(Behavior, @enumFromInt(@intFromEnum(this) & ~Behavior.dev));
- }
+ var b = this;
+ b.dev = value;
+ return b;
}
pub inline fn setPeer(this: Behavior, value: bool) Behavior {
- if (value) {
- return @as(Behavior, @enumFromInt(@intFromEnum(this) | Behavior.peer));
- } else {
- return @as(Behavior, @enumFromInt(@intFromEnum(this) & ~Behavior.peer));
- }
+ var b = this;
+ b.peer = value;
+ return b;
}
pub inline fn setWorkspace(this: Behavior, value: bool) Behavior {
- if (value) {
- return @as(Behavior, @enumFromInt(@intFromEnum(this) | Behavior.workspace));
- } else {
- return @as(Behavior, @enumFromInt(@intFromEnum(this) & ~Behavior.workspace));
- }
+ var b = this;
+ b.workspace = value;
+ return b;
+ }
+
+ pub inline fn eq(lhs: Behavior, rhs: Behavior) bool {
+ return @as(u8, @bitCast(lhs)) == @as(u8, @bitCast(rhs));
}
pub inline fn cmp(lhs: Behavior, rhs: Behavior) std.math.Order {
- if (@intFromEnum(lhs) == @intFromEnum(rhs)) {
+ if (eq(lhs, rhs)) {
return .eq;
}
@@ -1074,4 +1090,42 @@ pub const Behavior = enum(u8) {
(features.peer_dependencies and this.isPeer()) or
this.isWorkspace();
}
+
+ pub fn format(self: Behavior, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
+ const fields = std.meta.fields(Behavior);
+ var num_fields: u8 = 0;
+ inline for (fields) |f| {
+ if (f.type == bool and @field(self, f.name)) {
+ num_fields += 1;
+ }
+ }
+ switch (num_fields) {
+ 0 => try writer.writeAll("Behavior.uninitialized"),
+ 1 => {
+ inline for (fields) |f| {
+ if (f.type == bool and @field(self, f.name)) {
+ try writer.writeAll("Behavior." ++ f.name);
+ break;
+ }
+ }
+ },
+ else => {
+ try writer.writeAll("Behavior{");
+ inline for (fields) |f| {
+ if (f.type == bool and @field(self, f.name)) {
+ try writer.writeAll(" " ++ f.name);
+ }
+ }
+ try writer.writeAll(" }");
+ },
+ }
+ }
+
+ comptime {
+ std.debug.assert(@as(u8, @bitCast(Behavior.normal)) == (1 << 1));
+ std.debug.assert(@as(u8, @bitCast(Behavior.optional)) == (1 << 2));
+ std.debug.assert(@as(u8, @bitCast(Behavior.dev)) == (1 << 3));
+ std.debug.assert(@as(u8, @bitCast(Behavior.peer)) == (1 << 4));
+ std.debug.assert(@as(u8, @bitCast(Behavior.workspace)) == (1 << 5));
+ }
};