mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix(install): change semver core numbers to u64 (#22889)
### What does this PR do? Sometimes packages will use very large numbers exceeding max u32 for major/minor/patch (usually patch). This pr changes each core number in bun to u64. Because we serialize package information to disk for the binary lockfile and package manifests, this pr bumps the version of each. We don't need to change anything other than the version for serialized package manifests because they will invalidate and save the new version. For old binary lockfiles, this pr adds logic for migrating to the new version. Even if there are no changes, migrating will always save the new lockfile. Unfortunately means there will be a one time invisible diff for binary lockfile users, but this is better than installs failing to work. fixes #22881 fixes #21793 fixes #16041 fixes #22891 resolves BUN-7MX, BUN-R4Q, BUN-WRB ### How did you verify your code works? Manually, and added a test for migrating from an older binary lockfile. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -782,7 +782,7 @@ pub fn installWithManager(
|
||||
(did_meta_hash_change or
|
||||
had_any_diffs or
|
||||
manager.update_requests.len > 0 or
|
||||
(load_result == .ok and load_result.ok.serializer_result.packages_need_update) or
|
||||
(load_result == .ok and (load_result.ok.serializer_result.packages_need_update or load_result.ok.serializer_result.migrated_from_lockb_v2)) or
|
||||
manager.lockfile.isEmpty() or
|
||||
manager.options.enable.force_save_lockfile));
|
||||
|
||||
|
||||
@@ -1585,9 +1585,11 @@ pub const FormatVersion = enum(u32) {
|
||||
// bun v0.1.7+
|
||||
// This change added tarball URLs to npm-resolved packages
|
||||
v2 = 2,
|
||||
// Changed semver major/minor/patch to each use u64 instead of u32
|
||||
v3 = 3,
|
||||
|
||||
_,
|
||||
pub const current = FormatVersion.v2;
|
||||
pub const current = FormatVersion.v3;
|
||||
};
|
||||
|
||||
pub const PackageIDSlice = ExternalSlice(PackageID);
|
||||
@@ -1607,7 +1609,7 @@ pub const Buffers = @import("./lockfile/Buffers.zig");
|
||||
pub const Serializer = @import("./lockfile/bun.lockb.zig");
|
||||
pub const CatalogMap = @import("./lockfile/CatalogMap.zig");
|
||||
pub const OverrideMap = @import("./lockfile/OverrideMap.zig");
|
||||
pub const Package = @import("./lockfile/Package.zig").Package;
|
||||
pub const Package = @import("./lockfile/Package.zig").Package(u64);
|
||||
pub const Tree = @import("./lockfile/Tree.zig");
|
||||
|
||||
pub fn deinit(this: *Lockfile) void {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -253,6 +253,7 @@ pub fn save(this: *Lockfile, verbose_log: bool, bytes: *std.ArrayList(u8), total
|
||||
|
||||
pub const SerializerLoadResult = struct {
|
||||
packages_need_update: bool = false,
|
||||
migrated_from_lockb_v2: bool = false,
|
||||
};
|
||||
|
||||
pub fn load(
|
||||
@@ -271,9 +272,20 @@ pub fn load(
|
||||
return error.InvalidLockfile;
|
||||
}
|
||||
|
||||
var migrate_from_v2 = false;
|
||||
const format = try reader.readInt(u32, .little);
|
||||
if (format != @intFromEnum(Lockfile.FormatVersion.current)) {
|
||||
return error.@"Outdated lockfile version";
|
||||
if (format > @intFromEnum(Lockfile.FormatVersion.current)) {
|
||||
return error.@"Unexpected lockfile version";
|
||||
}
|
||||
|
||||
if (format < @intFromEnum(Lockfile.FormatVersion.current)) {
|
||||
|
||||
// we only allow migrating from v2 to v3 or above
|
||||
if (format != @intFromEnum(Lockfile.FormatVersion.v2)) {
|
||||
return error.@"Outdated lockfile version";
|
||||
}
|
||||
|
||||
migrate_from_v2 = true;
|
||||
}
|
||||
|
||||
lockfile.format = Lockfile.FormatVersion.current;
|
||||
@@ -290,10 +302,13 @@ pub fn load(
|
||||
stream,
|
||||
total_buffer_size,
|
||||
allocator,
|
||||
migrate_from_v2,
|
||||
);
|
||||
|
||||
lockfile.packages = packages_load_result.list;
|
||||
|
||||
res.packages_need_update = packages_load_result.needs_update;
|
||||
res.migrated_from_lockb_v2 = migrate_from_v2;
|
||||
|
||||
lockfile.buffers = try Lockfile.Buffers.load(
|
||||
stream,
|
||||
|
||||
@@ -932,7 +932,8 @@ pub const PackageManifest = struct {
|
||||
// - v0.0.3: added serialization of registry url. it's used to invalidate when it changes
|
||||
// - v0.0.4: fixed bug with cpu & os tag not being added correctly
|
||||
// - v0.0.5: added bundled dependencies
|
||||
pub const version = "bun-npm-manifest-cache-v0.0.5\n";
|
||||
// - v0.0.6: changed semver major/minor/patch to each use u64 instead of u32
|
||||
pub const version = "bun-npm-manifest-cache-v0.0.6\n";
|
||||
const header_bytes: string = "#!/usr/bin/env bun\n" ++ version;
|
||||
|
||||
pub const sizes = blk: {
|
||||
|
||||
@@ -1,438 +1,445 @@
|
||||
pub const Resolution = extern struct {
|
||||
tag: Tag = .uninitialized,
|
||||
_padding: [7]u8 = .{0} ** 7,
|
||||
value: Value = .{ .uninitialized = {} },
|
||||
pub const Resolution = ResolutionType(u64);
|
||||
pub const OldV2Resolution = ResolutionType(u32);
|
||||
|
||||
/// Use like Resolution.init(.{ .npm = VersionedURL{ ... } })
|
||||
pub inline fn init(value: bun.meta.Tagged(Value, Tag)) Resolution {
|
||||
return Resolution{
|
||||
.tag = std.meta.activeTag(value),
|
||||
.value = Value.init(value),
|
||||
};
|
||||
}
|
||||
pub fn ResolutionType(comptime SemverIntType: type) type {
|
||||
return extern struct {
|
||||
tag: Tag = .uninitialized,
|
||||
_padding: [7]u8 = .{0} ** 7,
|
||||
value: Value = .{ .uninitialized = {} },
|
||||
|
||||
pub fn isGit(this: *const Resolution) bool {
|
||||
return this.tag.isGit();
|
||||
}
|
||||
const This = @This();
|
||||
|
||||
pub fn canEnqueueInstallTask(this: *const Resolution) bool {
|
||||
return this.tag.canEnqueueInstallTask();
|
||||
}
|
||||
|
||||
const FromTextLockfileError = OOM || error{
|
||||
UnexpectedResolution,
|
||||
InvalidSemver,
|
||||
};
|
||||
|
||||
pub fn fromTextLockfile(res_str: string, string_buf: *String.Buf) FromTextLockfileError!Resolution {
|
||||
if (strings.hasPrefixComptime(res_str, "root:")) {
|
||||
return Resolution.init(.{ .root = {} });
|
||||
}
|
||||
|
||||
if (strings.withoutPrefixIfPossibleComptime(res_str, "link:")) |link| {
|
||||
return Resolution.init(.{ .symlink = try string_buf.append(link) });
|
||||
}
|
||||
|
||||
if (strings.withoutPrefixIfPossibleComptime(res_str, "workspace:")) |workspace| {
|
||||
return Resolution.init(.{ .workspace = try string_buf.append(workspace) });
|
||||
}
|
||||
|
||||
if (strings.withoutPrefixIfPossibleComptime(res_str, "file:")) |folder| {
|
||||
return Resolution.init(.{ .folder = try string_buf.append(folder) });
|
||||
}
|
||||
|
||||
return switch (Dependency.Version.Tag.infer(res_str)) {
|
||||
.git => Resolution.init(.{ .git = try Repository.parseAppendGit(res_str, string_buf) }),
|
||||
.github => Resolution.init(.{ .github = try Repository.parseAppendGithub(res_str, string_buf) }),
|
||||
.tarball => {
|
||||
if (Dependency.isRemoteTarball(res_str)) {
|
||||
return Resolution.init(.{ .remote_tarball = try string_buf.append(res_str) });
|
||||
}
|
||||
|
||||
return Resolution.init(.{ .local_tarball = try string_buf.append(res_str) });
|
||||
},
|
||||
.npm => {
|
||||
const version_literal = try string_buf.append(res_str);
|
||||
const parsed = Semver.Version.parse(version_literal.sliced(string_buf.bytes.items));
|
||||
|
||||
if (!parsed.valid) {
|
||||
return error.UnexpectedResolution;
|
||||
}
|
||||
|
||||
if (parsed.version.major == null or parsed.version.minor == null or parsed.version.patch == null) {
|
||||
return error.UnexpectedResolution;
|
||||
}
|
||||
|
||||
return .{
|
||||
.tag = .npm,
|
||||
.value = .{
|
||||
.npm = .{
|
||||
.version = parsed.version.min(),
|
||||
|
||||
// will fill this later
|
||||
.url = .{},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
// covered above
|
||||
.workspace => error.UnexpectedResolution,
|
||||
.symlink => error.UnexpectedResolution,
|
||||
.folder => error.UnexpectedResolution,
|
||||
|
||||
// even though it's a dependency type, it's not
|
||||
// possible for 'catalog:' to be written to the
|
||||
// lockfile for any resolution because the install
|
||||
// will fail it it's not successfully replaced by
|
||||
// a version
|
||||
.catalog => error.UnexpectedResolution,
|
||||
|
||||
// should not happen
|
||||
.dist_tag => error.UnexpectedResolution,
|
||||
.uninitialized => error.UnexpectedResolution,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn order(
|
||||
lhs: *const Resolution,
|
||||
rhs: *const Resolution,
|
||||
lhs_buf: []const u8,
|
||||
rhs_buf: []const u8,
|
||||
) std.math.Order {
|
||||
if (lhs.tag != rhs.tag) {
|
||||
return std.math.order(@intFromEnum(lhs.tag), @intFromEnum(rhs.tag));
|
||||
}
|
||||
|
||||
return switch (lhs.tag) {
|
||||
.npm => lhs.value.npm.order(rhs.value.npm, lhs_buf, rhs_buf),
|
||||
.local_tarball => lhs.value.local_tarball.order(&rhs.value.local_tarball, lhs_buf, rhs_buf),
|
||||
.folder => lhs.value.folder.order(&rhs.value.folder, lhs_buf, rhs_buf),
|
||||
.remote_tarball => lhs.value.remote_tarball.order(&rhs.value.remote_tarball, lhs_buf, rhs_buf),
|
||||
.workspace => lhs.value.workspace.order(&rhs.value.workspace, lhs_buf, rhs_buf),
|
||||
.symlink => lhs.value.symlink.order(&rhs.value.symlink, lhs_buf, rhs_buf),
|
||||
.single_file_module => lhs.value.single_file_module.order(&rhs.value.single_file_module, lhs_buf, rhs_buf),
|
||||
.git => lhs.value.git.order(&rhs.value.git, lhs_buf, rhs_buf),
|
||||
.github => lhs.value.github.order(&rhs.value.github, lhs_buf, rhs_buf),
|
||||
else => .eq,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn count(this: *const Resolution, buf: []const u8, comptime Builder: type, builder: Builder) void {
|
||||
switch (this.tag) {
|
||||
.npm => this.value.npm.count(buf, Builder, builder),
|
||||
.local_tarball => builder.count(this.value.local_tarball.slice(buf)),
|
||||
.folder => builder.count(this.value.folder.slice(buf)),
|
||||
.remote_tarball => builder.count(this.value.remote_tarball.slice(buf)),
|
||||
.workspace => builder.count(this.value.workspace.slice(buf)),
|
||||
.symlink => builder.count(this.value.symlink.slice(buf)),
|
||||
.single_file_module => builder.count(this.value.single_file_module.slice(buf)),
|
||||
.git => this.value.git.count(buf, Builder, builder),
|
||||
.github => this.value.github.count(buf, Builder, builder),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone(this: *const Resolution, buf: []const u8, comptime Builder: type, builder: Builder) Resolution {
|
||||
return .{
|
||||
.tag = this.tag,
|
||||
.value = switch (this.tag) {
|
||||
.npm => Value.init(.{ .npm = this.value.npm.clone(buf, Builder, builder) }),
|
||||
.local_tarball => Value.init(.{
|
||||
.local_tarball = builder.append(String, this.value.local_tarball.slice(buf)),
|
||||
}),
|
||||
.folder => Value.init(.{
|
||||
.folder = builder.append(String, this.value.folder.slice(buf)),
|
||||
}),
|
||||
.remote_tarball => Value.init(.{
|
||||
.remote_tarball = builder.append(String, this.value.remote_tarball.slice(buf)),
|
||||
}),
|
||||
.workspace => Value.init(.{
|
||||
.workspace = builder.append(String, this.value.workspace.slice(buf)),
|
||||
}),
|
||||
.symlink => Value.init(.{
|
||||
.symlink = builder.append(String, this.value.symlink.slice(buf)),
|
||||
}),
|
||||
.single_file_module => Value.init(.{
|
||||
.single_file_module = builder.append(String, this.value.single_file_module.slice(buf)),
|
||||
}),
|
||||
.git => Value.init(.{
|
||||
.git = this.value.git.clone(buf, Builder, builder),
|
||||
}),
|
||||
.github => Value.init(.{
|
||||
.github = this.value.github.clone(buf, Builder, builder),
|
||||
}),
|
||||
.root => Value.init(.{ .root = {} }),
|
||||
else => {
|
||||
std.debug.panic("Internal error: unexpected resolution tag: {}", .{this.tag});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fmt(this: *const Resolution, string_bytes: []const u8, path_sep: bun.fmt.PathFormatOptions.Sep) Formatter {
|
||||
return Formatter{
|
||||
.resolution = this,
|
||||
.buf = string_bytes,
|
||||
.path_sep = path_sep,
|
||||
};
|
||||
}
|
||||
|
||||
const StorePathFormatter = struct {
|
||||
res: *const Resolution,
|
||||
string_buf: string,
|
||||
// opts: String.StorePathFormatter.Options,
|
||||
|
||||
pub fn format(this: StorePathFormatter, comptime _: string, _: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
|
||||
const string_buf = this.string_buf;
|
||||
const res = this.res.value;
|
||||
switch (this.res.tag) {
|
||||
.root => try writer.writeAll("root"),
|
||||
.npm => try writer.print("{}", .{res.npm.version.fmt(string_buf)}),
|
||||
.local_tarball => try writer.print("{}", .{res.local_tarball.fmtStorePath(string_buf)}),
|
||||
.remote_tarball => try writer.print("{}", .{res.remote_tarball.fmtStorePath(string_buf)}),
|
||||
.folder => try writer.print("{}", .{res.folder.fmtStorePath(string_buf)}),
|
||||
.git => try writer.print("{}", .{res.git.fmtStorePath("git+", string_buf)}),
|
||||
.github => try writer.print("{}", .{res.github.fmtStorePath("github+", string_buf)}),
|
||||
.workspace => try writer.print("{}", .{res.workspace.fmtStorePath(string_buf)}),
|
||||
.symlink => try writer.print("{}", .{res.symlink.fmtStorePath(string_buf)}),
|
||||
.single_file_module => try writer.print("{}", .{res.single_file_module.fmtStorePath(string_buf)}),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn fmtStorePath(this: *const Resolution, string_buf: string) StorePathFormatter {
|
||||
return .{
|
||||
.res = this,
|
||||
.string_buf = string_buf,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fmtURL(this: *const Resolution, string_bytes: []const u8) URLFormatter {
|
||||
return URLFormatter{ .resolution = this, .buf = string_bytes };
|
||||
}
|
||||
|
||||
pub fn fmtForDebug(this: *const Resolution, string_bytes: []const u8) DebugFormatter {
|
||||
return DebugFormatter{ .resolution = this, .buf = string_bytes };
|
||||
}
|
||||
|
||||
pub fn eql(
|
||||
lhs: *const Resolution,
|
||||
rhs: *const Resolution,
|
||||
lhs_string_buf: []const u8,
|
||||
rhs_string_buf: []const u8,
|
||||
) bool {
|
||||
if (lhs.tag != rhs.tag) return false;
|
||||
|
||||
return switch (lhs.tag) {
|
||||
.root => true,
|
||||
.npm => lhs.value.npm.eql(rhs.value.npm),
|
||||
.local_tarball => lhs.value.local_tarball.eql(
|
||||
rhs.value.local_tarball,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.folder => lhs.value.folder.eql(
|
||||
rhs.value.folder,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.remote_tarball => lhs.value.remote_tarball.eql(
|
||||
rhs.value.remote_tarball,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.workspace => lhs.value.workspace.eql(
|
||||
rhs.value.workspace,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.symlink => lhs.value.symlink.eql(
|
||||
rhs.value.symlink,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.single_file_module => lhs.value.single_file_module.eql(
|
||||
rhs.value.single_file_module,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.git => lhs.value.git.eql(
|
||||
&rhs.value.git,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.github => lhs.value.github.eql(
|
||||
&rhs.value.github,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub const URLFormatter = struct {
|
||||
resolution: *const Resolution,
|
||||
|
||||
buf: []const u8,
|
||||
|
||||
pub fn format(formatter: URLFormatter, comptime layout: []const u8, opts: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
|
||||
const buf = formatter.buf;
|
||||
const value = formatter.resolution.value;
|
||||
switch (formatter.resolution.tag) {
|
||||
.npm => try writer.writeAll(value.npm.url.slice(formatter.buf)),
|
||||
.local_tarball => try bun.fmt.fmtPath(u8, value.local_tarball.slice(buf), .{ .path_sep = .posix }).format("", {}, writer),
|
||||
.folder => try writer.writeAll(value.folder.slice(formatter.buf)),
|
||||
.remote_tarball => try writer.writeAll(value.remote_tarball.slice(formatter.buf)),
|
||||
.git => try value.git.formatAs("git+", formatter.buf, layout, opts, writer),
|
||||
.github => try value.github.formatAs("github:", formatter.buf, layout, opts, writer),
|
||||
.workspace => try std.fmt.format(writer, "workspace:{s}", .{value.workspace.slice(formatter.buf)}),
|
||||
.symlink => try std.fmt.format(writer, "link:{s}", .{value.symlink.slice(formatter.buf)}),
|
||||
.single_file_module => try std.fmt.format(writer, "module:{s}", .{value.single_file_module.slice(formatter.buf)}),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Formatter = struct {
|
||||
resolution: *const Resolution,
|
||||
buf: []const u8,
|
||||
path_sep: bun.fmt.PathFormatOptions.Sep,
|
||||
|
||||
pub fn format(formatter: Formatter, comptime layout: []const u8, opts: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
|
||||
const buf = formatter.buf;
|
||||
const value = formatter.resolution.value;
|
||||
switch (formatter.resolution.tag) {
|
||||
.npm => try value.npm.version.fmt(buf).format(layout, opts, writer),
|
||||
.local_tarball => try bun.fmt.fmtPath(u8, value.local_tarball.slice(buf), .{ .path_sep = formatter.path_sep }).format("", {}, writer),
|
||||
.folder => try bun.fmt.fmtPath(u8, value.folder.slice(buf), .{ .path_sep = formatter.path_sep }).format("", {}, writer),
|
||||
.remote_tarball => try writer.writeAll(value.remote_tarball.slice(buf)),
|
||||
.git => try value.git.formatAs("git+", buf, layout, opts, writer),
|
||||
.github => try value.github.formatAs("github:", buf, layout, opts, writer),
|
||||
.workspace => try std.fmt.format(writer, "workspace:{s}", .{bun.fmt.fmtPath(u8, value.workspace.slice(buf), .{
|
||||
.path_sep = formatter.path_sep,
|
||||
})}),
|
||||
.symlink => try std.fmt.format(writer, "link:{s}", .{bun.fmt.fmtPath(u8, value.symlink.slice(buf), .{
|
||||
.path_sep = formatter.path_sep,
|
||||
})}),
|
||||
.single_file_module => try std.fmt.format(writer, "module:{s}", .{value.single_file_module.slice(buf)}),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const DebugFormatter = struct {
|
||||
resolution: *const Resolution,
|
||||
buf: []const u8,
|
||||
|
||||
pub fn format(formatter: DebugFormatter, comptime layout: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
try writer.writeAll("Resolution{ .");
|
||||
try writer.writeAll(bun.tagName(Tag, formatter.resolution.tag) orelse "invalid");
|
||||
try writer.writeAll(" = ");
|
||||
switch (formatter.resolution.tag) {
|
||||
.npm => try formatter.resolution.value.npm.version.fmt(formatter.buf).format(layout, opts, writer),
|
||||
.local_tarball => try writer.writeAll(formatter.resolution.value.local_tarball.slice(formatter.buf)),
|
||||
.folder => try writer.writeAll(formatter.resolution.value.folder.slice(formatter.buf)),
|
||||
.remote_tarball => try writer.writeAll(formatter.resolution.value.remote_tarball.slice(formatter.buf)),
|
||||
.git => try formatter.resolution.value.git.formatAs("git+", formatter.buf, layout, opts, writer),
|
||||
.github => try formatter.resolution.value.github.formatAs("github:", formatter.buf, layout, opts, writer),
|
||||
.workspace => try std.fmt.format(writer, "workspace:{s}", .{formatter.resolution.value.workspace.slice(formatter.buf)}),
|
||||
.symlink => try std.fmt.format(writer, "link:{s}", .{formatter.resolution.value.symlink.slice(formatter.buf)}),
|
||||
.single_file_module => try std.fmt.format(writer, "module:{s}", .{formatter.resolution.value.single_file_module.slice(formatter.buf)}),
|
||||
else => try writer.writeAll("{}"),
|
||||
}
|
||||
try writer.writeAll(" }");
|
||||
}
|
||||
};
|
||||
|
||||
pub const Value = extern union {
|
||||
uninitialized: void,
|
||||
root: void,
|
||||
|
||||
npm: VersionedURL,
|
||||
|
||||
folder: String,
|
||||
|
||||
/// File path to a tarball relative to the package root
|
||||
local_tarball: String,
|
||||
|
||||
github: Repository,
|
||||
|
||||
git: Repository,
|
||||
|
||||
/// global link
|
||||
symlink: String,
|
||||
|
||||
workspace: String,
|
||||
|
||||
/// URL to a tarball.
|
||||
remote_tarball: String,
|
||||
|
||||
single_file_module: String,
|
||||
|
||||
/// To avoid undefined memory between union values, we must zero initialize the union first.
|
||||
pub fn init(field: bun.meta.Tagged(Value, Tag)) Value {
|
||||
return switch (field) {
|
||||
inline else => |v, t| @unionInit(Value, @tagName(t), v),
|
||||
/// Use like Resolution.init(.{ .npm = VersionedURL{ ... } })
|
||||
pub inline fn init(value: bun.meta.Tagged(Value, Tag)) This {
|
||||
return .{
|
||||
.tag = std.meta.activeTag(value),
|
||||
.value = Value.init(value),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Tag = enum(u8) {
|
||||
uninitialized = 0,
|
||||
root = 1,
|
||||
npm = 2,
|
||||
folder = 4,
|
||||
|
||||
local_tarball = 8,
|
||||
|
||||
github = 16,
|
||||
|
||||
git = 32,
|
||||
|
||||
symlink = 64,
|
||||
|
||||
workspace = 72,
|
||||
|
||||
remote_tarball = 80,
|
||||
|
||||
// This is a placeholder for now.
|
||||
// But the intent is to eventually support URL imports at the package manager level.
|
||||
//
|
||||
// There are many ways to do it, but perhaps one way to be maximally compatible is just removing the protocol part of the URL.
|
||||
//
|
||||
// For example, bun would transform this input:
|
||||
//
|
||||
// import _ from "https://github.com/lodash/lodash/lodash.min.js";
|
||||
//
|
||||
// Into:
|
||||
//
|
||||
// import _ from "github.com/lodash/lodash/lodash.min.js";
|
||||
//
|
||||
// github.com would become a package, with it's own package.json
|
||||
// This is similar to how Go does it, except it wouldn't clone the whole repo.
|
||||
// There are more efficient ways to do this, e.g. generate a .bun file just for all URL imports.
|
||||
// There are questions of determinism, but perhaps that's what Integrity would do.
|
||||
single_file_module = 100,
|
||||
|
||||
_,
|
||||
|
||||
pub fn isGit(this: Tag) bool {
|
||||
return this == .git or this == .github;
|
||||
pub fn isGit(this: *const This) bool {
|
||||
return this.tag.isGit();
|
||||
}
|
||||
|
||||
pub fn canEnqueueInstallTask(this: Tag) bool {
|
||||
return this == .npm or this == .local_tarball or this == .remote_tarball or this == .git or this == .github;
|
||||
pub fn canEnqueueInstallTask(this: *const This) bool {
|
||||
return this.tag.canEnqueueInstallTask();
|
||||
}
|
||||
|
||||
const FromTextLockfileError = OOM || error{
|
||||
UnexpectedResolution,
|
||||
InvalidSemver,
|
||||
};
|
||||
|
||||
pub fn fromTextLockfile(res_str: string, string_buf: *String.Buf) FromTextLockfileError!This {
|
||||
if (strings.hasPrefixComptime(res_str, "root:")) {
|
||||
return This.init(.{ .root = {} });
|
||||
}
|
||||
|
||||
if (strings.withoutPrefixIfPossibleComptime(res_str, "link:")) |link| {
|
||||
return This.init(.{ .symlink = try string_buf.append(link) });
|
||||
}
|
||||
|
||||
if (strings.withoutPrefixIfPossibleComptime(res_str, "workspace:")) |workspace| {
|
||||
return This.init(.{ .workspace = try string_buf.append(workspace) });
|
||||
}
|
||||
|
||||
if (strings.withoutPrefixIfPossibleComptime(res_str, "file:")) |folder| {
|
||||
return This.init(.{ .folder = try string_buf.append(folder) });
|
||||
}
|
||||
|
||||
return switch (Dependency.Version.Tag.infer(res_str)) {
|
||||
.git => This.init(.{ .git = try Repository.parseAppendGit(res_str, string_buf) }),
|
||||
.github => This.init(.{ .github = try Repository.parseAppendGithub(res_str, string_buf) }),
|
||||
.tarball => {
|
||||
if (Dependency.isRemoteTarball(res_str)) {
|
||||
return This.init(.{ .remote_tarball = try string_buf.append(res_str) });
|
||||
}
|
||||
|
||||
return This.init(.{ .local_tarball = try string_buf.append(res_str) });
|
||||
},
|
||||
.npm => {
|
||||
const version_literal = try string_buf.append(res_str);
|
||||
const parsed = Semver.Version.parse(version_literal.sliced(string_buf.bytes.items));
|
||||
|
||||
if (!parsed.valid) {
|
||||
return error.UnexpectedResolution;
|
||||
}
|
||||
|
||||
if (parsed.version.major == null or parsed.version.minor == null or parsed.version.patch == null) {
|
||||
return error.UnexpectedResolution;
|
||||
}
|
||||
|
||||
return .{
|
||||
.tag = .npm,
|
||||
.value = .{
|
||||
.npm = .{
|
||||
.version = parsed.version.min(),
|
||||
|
||||
// will fill this later
|
||||
.url = .{},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
// covered above
|
||||
.workspace => error.UnexpectedResolution,
|
||||
.symlink => error.UnexpectedResolution,
|
||||
.folder => error.UnexpectedResolution,
|
||||
|
||||
// even though it's a dependency type, it's not
|
||||
// possible for 'catalog:' to be written to the
|
||||
// lockfile for any resolution because the install
|
||||
// will fail it it's not successfully replaced by
|
||||
// a version
|
||||
.catalog => error.UnexpectedResolution,
|
||||
|
||||
// should not happen
|
||||
.dist_tag => error.UnexpectedResolution,
|
||||
.uninitialized => error.UnexpectedResolution,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn order(
|
||||
lhs: *const This,
|
||||
rhs: *const This,
|
||||
lhs_buf: []const u8,
|
||||
rhs_buf: []const u8,
|
||||
) std.math.Order {
|
||||
if (lhs.tag != rhs.tag) {
|
||||
return std.math.order(@intFromEnum(lhs.tag), @intFromEnum(rhs.tag));
|
||||
}
|
||||
|
||||
return switch (lhs.tag) {
|
||||
.npm => lhs.value.npm.order(rhs.value.npm, lhs_buf, rhs_buf),
|
||||
.local_tarball => lhs.value.local_tarball.order(&rhs.value.local_tarball, lhs_buf, rhs_buf),
|
||||
.folder => lhs.value.folder.order(&rhs.value.folder, lhs_buf, rhs_buf),
|
||||
.remote_tarball => lhs.value.remote_tarball.order(&rhs.value.remote_tarball, lhs_buf, rhs_buf),
|
||||
.workspace => lhs.value.workspace.order(&rhs.value.workspace, lhs_buf, rhs_buf),
|
||||
.symlink => lhs.value.symlink.order(&rhs.value.symlink, lhs_buf, rhs_buf),
|
||||
.single_file_module => lhs.value.single_file_module.order(&rhs.value.single_file_module, lhs_buf, rhs_buf),
|
||||
.git => lhs.value.git.order(&rhs.value.git, lhs_buf, rhs_buf),
|
||||
.github => lhs.value.github.order(&rhs.value.github, lhs_buf, rhs_buf),
|
||||
else => .eq,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn count(this: *const This, buf: []const u8, comptime Builder: type, builder: Builder) void {
|
||||
switch (this.tag) {
|
||||
.npm => this.value.npm.count(buf, Builder, builder),
|
||||
.local_tarball => builder.count(this.value.local_tarball.slice(buf)),
|
||||
.folder => builder.count(this.value.folder.slice(buf)),
|
||||
.remote_tarball => builder.count(this.value.remote_tarball.slice(buf)),
|
||||
.workspace => builder.count(this.value.workspace.slice(buf)),
|
||||
.symlink => builder.count(this.value.symlink.slice(buf)),
|
||||
.single_file_module => builder.count(this.value.single_file_module.slice(buf)),
|
||||
.git => this.value.git.count(buf, Builder, builder),
|
||||
.github => this.value.github.count(buf, Builder, builder),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone(this: *const This, buf: []const u8, comptime Builder: type, builder: Builder) This {
|
||||
return .{
|
||||
.tag = this.tag,
|
||||
.value = switch (this.tag) {
|
||||
.npm => Value.init(.{ .npm = this.value.npm.clone(buf, Builder, builder) }),
|
||||
.local_tarball => Value.init(.{
|
||||
.local_tarball = builder.append(String, this.value.local_tarball.slice(buf)),
|
||||
}),
|
||||
.folder => Value.init(.{
|
||||
.folder = builder.append(String, this.value.folder.slice(buf)),
|
||||
}),
|
||||
.remote_tarball => Value.init(.{
|
||||
.remote_tarball = builder.append(String, this.value.remote_tarball.slice(buf)),
|
||||
}),
|
||||
.workspace => Value.init(.{
|
||||
.workspace = builder.append(String, this.value.workspace.slice(buf)),
|
||||
}),
|
||||
.symlink => Value.init(.{
|
||||
.symlink = builder.append(String, this.value.symlink.slice(buf)),
|
||||
}),
|
||||
.single_file_module => Value.init(.{
|
||||
.single_file_module = builder.append(String, this.value.single_file_module.slice(buf)),
|
||||
}),
|
||||
.git => Value.init(.{
|
||||
.git = this.value.git.clone(buf, Builder, builder),
|
||||
}),
|
||||
.github => Value.init(.{
|
||||
.github = this.value.github.clone(buf, Builder, builder),
|
||||
}),
|
||||
.root => Value.init(.{ .root = {} }),
|
||||
else => {
|
||||
std.debug.panic("Internal error: unexpected resolution tag: {}", .{this.tag});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fmt(this: *const This, string_bytes: []const u8, path_sep: bun.fmt.PathFormatOptions.Sep) Formatter {
|
||||
return Formatter{
|
||||
.resolution = this,
|
||||
.buf = string_bytes,
|
||||
.path_sep = path_sep,
|
||||
};
|
||||
}
|
||||
|
||||
const StorePathFormatter = struct {
|
||||
res: *const This,
|
||||
string_buf: string,
|
||||
// opts: String.StorePathFormatter.Options,
|
||||
|
||||
pub fn format(this: StorePathFormatter, comptime _: string, _: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
|
||||
const string_buf = this.string_buf;
|
||||
const res = this.res.value;
|
||||
switch (this.res.tag) {
|
||||
.root => try writer.writeAll("root"),
|
||||
.npm => try writer.print("{}", .{res.npm.version.fmt(string_buf)}),
|
||||
.local_tarball => try writer.print("{}", .{res.local_tarball.fmtStorePath(string_buf)}),
|
||||
.remote_tarball => try writer.print("{}", .{res.remote_tarball.fmtStorePath(string_buf)}),
|
||||
.folder => try writer.print("{}", .{res.folder.fmtStorePath(string_buf)}),
|
||||
.git => try writer.print("{}", .{res.git.fmtStorePath("git+", string_buf)}),
|
||||
.github => try writer.print("{}", .{res.github.fmtStorePath("github+", string_buf)}),
|
||||
.workspace => try writer.print("{}", .{res.workspace.fmtStorePath(string_buf)}),
|
||||
.symlink => try writer.print("{}", .{res.symlink.fmtStorePath(string_buf)}),
|
||||
.single_file_module => try writer.print("{}", .{res.single_file_module.fmtStorePath(string_buf)}),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn fmtStorePath(this: *const This, string_buf: string) StorePathFormatter {
|
||||
return .{
|
||||
.res = this,
|
||||
.string_buf = string_buf,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fmtURL(this: *const This, string_bytes: []const u8) URLFormatter {
|
||||
return URLFormatter{ .resolution = this, .buf = string_bytes };
|
||||
}
|
||||
|
||||
pub fn fmtForDebug(this: *const This, string_bytes: []const u8) DebugFormatter {
|
||||
return DebugFormatter{ .resolution = this, .buf = string_bytes };
|
||||
}
|
||||
|
||||
pub fn eql(
|
||||
lhs: *const This,
|
||||
rhs: *const This,
|
||||
lhs_string_buf: []const u8,
|
||||
rhs_string_buf: []const u8,
|
||||
) bool {
|
||||
if (lhs.tag != rhs.tag) return false;
|
||||
|
||||
return switch (lhs.tag) {
|
||||
.root => true,
|
||||
.npm => lhs.value.npm.eql(rhs.value.npm),
|
||||
.local_tarball => lhs.value.local_tarball.eql(
|
||||
rhs.value.local_tarball,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.folder => lhs.value.folder.eql(
|
||||
rhs.value.folder,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.remote_tarball => lhs.value.remote_tarball.eql(
|
||||
rhs.value.remote_tarball,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.workspace => lhs.value.workspace.eql(
|
||||
rhs.value.workspace,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.symlink => lhs.value.symlink.eql(
|
||||
rhs.value.symlink,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.single_file_module => lhs.value.single_file_module.eql(
|
||||
rhs.value.single_file_module,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.git => lhs.value.git.eql(
|
||||
&rhs.value.git,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
.github => lhs.value.github.eql(
|
||||
&rhs.value.github,
|
||||
lhs_string_buf,
|
||||
rhs_string_buf,
|
||||
),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub const URLFormatter = struct {
|
||||
resolution: *const This,
|
||||
|
||||
buf: []const u8,
|
||||
|
||||
pub fn format(formatter: URLFormatter, comptime layout: []const u8, opts: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
|
||||
const buf = formatter.buf;
|
||||
const value = formatter.resolution.value;
|
||||
switch (formatter.resolution.tag) {
|
||||
.npm => try writer.writeAll(value.npm.url.slice(formatter.buf)),
|
||||
.local_tarball => try bun.fmt.fmtPath(u8, value.local_tarball.slice(buf), .{ .path_sep = .posix }).format("", {}, writer),
|
||||
.folder => try writer.writeAll(value.folder.slice(formatter.buf)),
|
||||
.remote_tarball => try writer.writeAll(value.remote_tarball.slice(formatter.buf)),
|
||||
.git => try value.git.formatAs("git+", formatter.buf, layout, opts, writer),
|
||||
.github => try value.github.formatAs("github:", formatter.buf, layout, opts, writer),
|
||||
.workspace => try std.fmt.format(writer, "workspace:{s}", .{value.workspace.slice(formatter.buf)}),
|
||||
.symlink => try std.fmt.format(writer, "link:{s}", .{value.symlink.slice(formatter.buf)}),
|
||||
.single_file_module => try std.fmt.format(writer, "module:{s}", .{value.single_file_module.slice(formatter.buf)}),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Formatter = struct {
|
||||
resolution: *const This,
|
||||
buf: []const u8,
|
||||
path_sep: bun.fmt.PathFormatOptions.Sep,
|
||||
|
||||
pub fn format(formatter: Formatter, comptime layout: []const u8, opts: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
|
||||
const buf = formatter.buf;
|
||||
const value = formatter.resolution.value;
|
||||
switch (formatter.resolution.tag) {
|
||||
.npm => try value.npm.version.fmt(buf).format(layout, opts, writer),
|
||||
.local_tarball => try bun.fmt.fmtPath(u8, value.local_tarball.slice(buf), .{ .path_sep = formatter.path_sep }).format("", {}, writer),
|
||||
.folder => try bun.fmt.fmtPath(u8, value.folder.slice(buf), .{ .path_sep = formatter.path_sep }).format("", {}, writer),
|
||||
.remote_tarball => try writer.writeAll(value.remote_tarball.slice(buf)),
|
||||
.git => try value.git.formatAs("git+", buf, layout, opts, writer),
|
||||
.github => try value.github.formatAs("github:", buf, layout, opts, writer),
|
||||
.workspace => try std.fmt.format(writer, "workspace:{s}", .{bun.fmt.fmtPath(u8, value.workspace.slice(buf), .{
|
||||
.path_sep = formatter.path_sep,
|
||||
})}),
|
||||
.symlink => try std.fmt.format(writer, "link:{s}", .{bun.fmt.fmtPath(u8, value.symlink.slice(buf), .{
|
||||
.path_sep = formatter.path_sep,
|
||||
})}),
|
||||
.single_file_module => try std.fmt.format(writer, "module:{s}", .{value.single_file_module.slice(buf)}),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const DebugFormatter = struct {
|
||||
resolution: *const This,
|
||||
buf: []const u8,
|
||||
|
||||
pub fn format(formatter: DebugFormatter, comptime layout: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
try writer.writeAll("Resolution{ .");
|
||||
try writer.writeAll(bun.tagName(Tag, formatter.resolution.tag) orelse "invalid");
|
||||
try writer.writeAll(" = ");
|
||||
switch (formatter.resolution.tag) {
|
||||
.npm => try formatter.resolution.value.npm.version.fmt(formatter.buf).format(layout, opts, writer),
|
||||
.local_tarball => try writer.writeAll(formatter.resolution.value.local_tarball.slice(formatter.buf)),
|
||||
.folder => try writer.writeAll(formatter.resolution.value.folder.slice(formatter.buf)),
|
||||
.remote_tarball => try writer.writeAll(formatter.resolution.value.remote_tarball.slice(formatter.buf)),
|
||||
.git => try formatter.resolution.value.git.formatAs("git+", formatter.buf, layout, opts, writer),
|
||||
.github => try formatter.resolution.value.github.formatAs("github:", formatter.buf, layout, opts, writer),
|
||||
.workspace => try std.fmt.format(writer, "workspace:{s}", .{formatter.resolution.value.workspace.slice(formatter.buf)}),
|
||||
.symlink => try std.fmt.format(writer, "link:{s}", .{formatter.resolution.value.symlink.slice(formatter.buf)}),
|
||||
.single_file_module => try std.fmt.format(writer, "module:{s}", .{formatter.resolution.value.single_file_module.slice(formatter.buf)}),
|
||||
else => try writer.writeAll("{}"),
|
||||
}
|
||||
try writer.writeAll(" }");
|
||||
}
|
||||
};
|
||||
|
||||
pub const Value = extern union {
|
||||
uninitialized: void,
|
||||
root: void,
|
||||
|
||||
npm: VersionedURLType(SemverIntType),
|
||||
|
||||
folder: String,
|
||||
|
||||
/// File path to a tarball relative to the package root
|
||||
local_tarball: String,
|
||||
|
||||
github: Repository,
|
||||
|
||||
git: Repository,
|
||||
|
||||
/// global link
|
||||
symlink: String,
|
||||
|
||||
workspace: String,
|
||||
|
||||
/// URL to a tarball.
|
||||
remote_tarball: String,
|
||||
|
||||
single_file_module: String,
|
||||
|
||||
/// To avoid undefined memory between union values, we must zero initialize the union first.
|
||||
pub fn init(field: bun.meta.Tagged(Value, Tag)) Value {
|
||||
return switch (field) {
|
||||
inline else => |v, t| @unionInit(Value, @tagName(t), v),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Tag = enum(u8) {
|
||||
uninitialized = 0,
|
||||
root = 1,
|
||||
npm = 2,
|
||||
folder = 4,
|
||||
|
||||
local_tarball = 8,
|
||||
|
||||
github = 16,
|
||||
|
||||
git = 32,
|
||||
|
||||
symlink = 64,
|
||||
|
||||
workspace = 72,
|
||||
|
||||
remote_tarball = 80,
|
||||
|
||||
// This is a placeholder for now.
|
||||
// But the intent is to eventually support URL imports at the package manager level.
|
||||
//
|
||||
// There are many ways to do it, but perhaps one way to be maximally compatible is just removing the protocol part of the URL.
|
||||
//
|
||||
// For example, bun would transform this input:
|
||||
//
|
||||
// import _ from "https://github.com/lodash/lodash/lodash.min.js";
|
||||
//
|
||||
// Into:
|
||||
//
|
||||
// import _ from "github.com/lodash/lodash/lodash.min.js";
|
||||
//
|
||||
// github.com would become a package, with it's own package.json
|
||||
// This is similar to how Go does it, except it wouldn't clone the whole repo.
|
||||
// There are more efficient ways to do this, e.g. generate a .bun file just for all URL imports.
|
||||
// There are questions of determinism, but perhaps that's what Integrity would do.
|
||||
single_file_module = 100,
|
||||
|
||||
_,
|
||||
|
||||
pub fn isGit(this: Tag) bool {
|
||||
return this == .git or this == .github;
|
||||
}
|
||||
|
||||
pub fn canEnqueueInstallTask(this: Tag) bool {
|
||||
return this == .npm or this == .local_tarball or this == .remote_tarball or this == .git or this == .github;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const string = []const u8;
|
||||
|
||||
const std = @import("std");
|
||||
const Repository = @import("./repository.zig").Repository;
|
||||
const VersionedURL = @import("./versioned_url.zig").VersionedURL;
|
||||
const VersionedURLType = @import("./versioned_url.zig").VersionedURLType;
|
||||
|
||||
const bun = @import("bun");
|
||||
const OOM = bun.OOM;
|
||||
|
||||
@@ -1,27 +1,42 @@
|
||||
pub const VersionedURL = extern struct {
|
||||
url: String,
|
||||
version: Semver.Version,
|
||||
pub const VersionedURL = VersionedURLType(u64);
|
||||
pub const OldV2VersionedURL = VersionedURLType(u32);
|
||||
|
||||
pub fn eql(this: VersionedURL, other: VersionedURL) bool {
|
||||
return this.version.eql(other.version);
|
||||
}
|
||||
pub fn VersionedURLType(comptime SemverIntType: type) type {
|
||||
return extern struct {
|
||||
url: String,
|
||||
version: Semver.VersionType(SemverIntType),
|
||||
|
||||
pub fn order(this: VersionedURL, other: VersionedURL, lhs_buf: []const u8, rhs_buf: []const u8) @import("std").math.Order {
|
||||
return this.version.order(other.version, lhs_buf, rhs_buf);
|
||||
}
|
||||
pub fn eql(this: @This(), other: @This()) bool {
|
||||
return this.version.eql(other.version);
|
||||
}
|
||||
|
||||
pub fn count(this: VersionedURL, buf: []const u8, comptime Builder: type, builder: Builder) void {
|
||||
this.version.count(buf, comptime Builder, builder);
|
||||
builder.count(this.url.slice(buf));
|
||||
}
|
||||
pub fn order(this: @This(), other: @This(), lhs_buf: []const u8, rhs_buf: []const u8) @import("std").math.Order {
|
||||
return this.version.order(other.version, lhs_buf, rhs_buf);
|
||||
}
|
||||
|
||||
pub fn clone(this: VersionedURL, buf: []const u8, comptime Builder: type, builder: Builder) VersionedURL {
|
||||
return VersionedURL{
|
||||
.version = this.version.append(buf, Builder, builder),
|
||||
.url = builder.append(String, this.url.slice(buf)),
|
||||
};
|
||||
}
|
||||
};
|
||||
pub fn count(this: @This(), buf: []const u8, comptime Builder: type, builder: Builder) void {
|
||||
this.version.count(buf, comptime Builder, builder);
|
||||
builder.count(this.url.slice(buf));
|
||||
}
|
||||
|
||||
pub fn clone(this: @This(), buf: []const u8, comptime Builder: type, builder: Builder) @This() {
|
||||
return @This(){
|
||||
.version = this.version.append(buf, Builder, builder),
|
||||
.url = builder.append(String, this.url.slice(buf)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn migrate(this: @This()) VersionedURLType(u64) {
|
||||
if (comptime SemverIntType != u32) {
|
||||
@compileError("unexpected SemverIntType");
|
||||
}
|
||||
return .{
|
||||
.url = this.url,
|
||||
.version = this.version.migrate(),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const bun = @import("bun");
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
pub const String = @import("./semver/SemverString.zig").String;
|
||||
pub const ExternalString = @import("./semver/ExternalString.zig").ExternalString;
|
||||
pub const Version = @import("./semver/Version.zig").Version;
|
||||
pub const VersionType = @import("./semver/Version.zig").VersionType;
|
||||
|
||||
pub const SlicedString = @import("./semver/SlicedString.zig");
|
||||
pub const Range = @import("./semver/SemverRange.zig");
|
||||
|
||||
@@ -425,9 +425,9 @@ pub const Token = struct {
|
||||
.right = .{
|
||||
.op = .lte,
|
||||
.version = .{
|
||||
.major = std.math.maxInt(u32),
|
||||
.minor = std.math.maxInt(u32),
|
||||
.patch = std.math.maxInt(u32),
|
||||
.major = std.math.maxInt(u64),
|
||||
.minor = std.math.maxInt(u64),
|
||||
.patch = std.math.maxInt(u64),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -437,8 +437,8 @@ pub const Token = struct {
|
||||
.op = .lte,
|
||||
.version = .{
|
||||
.major = version.major orelse 0,
|
||||
.minor = std.math.maxInt(u32),
|
||||
.patch = std.math.maxInt(u32),
|
||||
.minor = std.math.maxInt(u64),
|
||||
.patch = std.math.maxInt(u64),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -458,8 +458,8 @@ pub const Token = struct {
|
||||
.op = .gt,
|
||||
.version = .{
|
||||
.major = version.major orelse 0,
|
||||
.minor = std.math.maxInt(u32),
|
||||
.patch = std.math.maxInt(u32),
|
||||
.minor = std.math.maxInt(u64),
|
||||
.patch = std.math.maxInt(u64),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -483,7 +483,7 @@ pub const Token = struct {
|
||||
.version = .{
|
||||
.major = version.major orelse 0,
|
||||
.minor = version.minor orelse 0,
|
||||
.patch = std.math.maxInt(u32),
|
||||
.patch = std.math.maxInt(u64),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -504,7 +504,7 @@ pub const Token = struct {
|
||||
.version = .{
|
||||
.major = version.major orelse 0,
|
||||
.minor = version.minor orelse 0,
|
||||
.patch = std.math.maxInt(u32),
|
||||
.patch = std.math.maxInt(u64),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: * 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": 2,
|
||||
@@ -125,7 +125,7 @@ exports[`dependency on workspace without version in package.json: version: * 1`]
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: *.*.* 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": 2,
|
||||
@@ -248,7 +248,7 @@ exports[`dependency on workspace without version in package.json: version: *.*.*
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: =* 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": 2,
|
||||
@@ -371,7 +371,7 @@ exports[`dependency on workspace without version in package.json: version: =* 1`
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: kjwoehcojrgjoj 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": 2,
|
||||
@@ -494,7 +494,7 @@ exports[`dependency on workspace without version in package.json: version: kjwoe
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: *.1.* 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": 2,
|
||||
@@ -617,7 +617,7 @@ exports[`dependency on workspace without version in package.json: version: *.1.*
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: *-pre 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": 2,
|
||||
@@ -740,7 +740,7 @@ exports[`dependency on workspace without version in package.json: version: *-pre
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: 1 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": [
|
||||
@@ -896,7 +896,7 @@ exports[`dependency on workspace without version in package.json: version: 1 1`]
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: 1.* 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": [
|
||||
@@ -1052,7 +1052,7 @@ exports[`dependency on workspace without version in package.json: version: 1.* 1
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: 1.1.* 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": [
|
||||
@@ -1208,7 +1208,7 @@ exports[`dependency on workspace without version in package.json: version: 1.1.*
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: 1.1.0 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": [
|
||||
@@ -1364,7 +1364,7 @@ exports[`dependency on workspace without version in package.json: version: 1.1.0
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: *-pre+build 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": [
|
||||
@@ -1520,7 +1520,7 @@ exports[`dependency on workspace without version in package.json: version: *-pre
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: *+build 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": [
|
||||
@@ -1676,7 +1676,7 @@ exports[`dependency on workspace without version in package.json: version: *+bui
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: latest 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": [
|
||||
@@ -1832,7 +1832,7 @@ exports[`dependency on workspace without version in package.json: version: lates
|
||||
|
||||
exports[`dependency on workspace without version in package.json: version: 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": [
|
||||
@@ -1988,7 +1988,7 @@ exports[`dependency on workspace without version in package.json: version: 1`]
|
||||
|
||||
exports[`dependency on same name as workspace and dist-tag: with version 1`] = `
|
||||
"{
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"no-deps": [
|
||||
|
||||
BIN
test/cli/install/fixtures/bun.lockb.v2
Executable file
BIN
test/cli/install/fixtures/bun.lockb.v2
Executable file
Binary file not shown.
63
test/cli/install/migrate-bun-lockb-v2.test.ts
Normal file
63
test/cli/install/migrate-bun-lockb-v2.test.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { file, spawn } from "bun";
|
||||
import { install_test_helpers } from "bun:internal-for-testing";
|
||||
import { expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe, tempDir } from "harness";
|
||||
import { cp } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
const { parseLockfile } = install_test_helpers;
|
||||
|
||||
test("old binary lockfile migrates successfully", async () => {
|
||||
const oldLockfileContents = await file(join(import.meta.dir, "fixtures/bun.lockb.v2")).text();
|
||||
using testDir = tempDir("migrate-bun-lockb-v2", {
|
||||
"bunfig.toml": "install.saveTextLockfile = false",
|
||||
"package.json": JSON.stringify({
|
||||
name: "migrate-bun-lockb-v2",
|
||||
dependencies: {
|
||||
jquery: "~3.7.1",
|
||||
"is-even": "^1.0.0",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await cp(join(import.meta.dir, "fixtures/bun.lockb.v2"), join(testDir, "bun.lockb"));
|
||||
|
||||
const oldLockfile = parseLockfile(testDir);
|
||||
|
||||
let { stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: testDir,
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
let err = await stderr.text();
|
||||
|
||||
expect(await exited).toBe(0);
|
||||
expect(err).toContain("Saved lockfile");
|
||||
|
||||
const newLockfileContents = await file(join(testDir, "bun.lockb")).bytes();
|
||||
const newLockfile = parseLockfile(testDir);
|
||||
|
||||
// contents should be different due to semver numbers changing size
|
||||
expect(newLockfileContents).not.toEqual(oldLockfileContents);
|
||||
// but parse result should be the same
|
||||
expect(newLockfile).toEqual(oldLockfile);
|
||||
|
||||
// another install should not change the lockfile
|
||||
({ stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: testDir,
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
}));
|
||||
|
||||
expect(await exited).toBe(0);
|
||||
expect(await stderr.text()).not.toContain("Saved lockfile");
|
||||
|
||||
const newLockfileContents2 = await file(join(testDir, "bun.lockb")).bytes();
|
||||
const newLockfile2 = parseLockfile(testDir);
|
||||
expect(newLockfileContents2).toEqual(newLockfileContents);
|
||||
expect(newLockfile2).toEqual(newLockfile);
|
||||
});
|
||||
@@ -13596,7 +13596,7 @@ exports[`ssr works for 100-ish requests 1`] = `
|
||||
"package_id": null,
|
||||
},
|
||||
],
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"@alloc/quick-lru": 1,
|
||||
|
||||
@@ -13596,7 +13596,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = `
|
||||
"package_id": null,
|
||||
},
|
||||
],
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"@alloc/quick-lru": 1,
|
||||
|
||||
@@ -13596,7 +13596,7 @@ exports[`next build works: bun 1`] = `
|
||||
"package_id": null,
|
||||
},
|
||||
],
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"@alloc/quick-lru": 1,
|
||||
@@ -39534,7 +39534,7 @@ exports[`next build works: node 1`] = `
|
||||
"package_id": null,
|
||||
},
|
||||
],
|
||||
"format": "v2",
|
||||
"format": "v3",
|
||||
"meta_hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"package_index": {
|
||||
"@alloc/quick-lru": 1,
|
||||
|
||||
Reference in New Issue
Block a user