Skip to content

Commit

Permalink
more commas, sorting, and optional peers
Browse files Browse the repository at this point in the history
  • Loading branch information
dylan-conway committed Dec 12, 2024
1 parent 011a06c commit 0d4ff3e
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 26 deletions.
134 changes: 109 additions & 25 deletions src/install/bun.lock.zig
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ pub const Stringifier = struct {
defer found_overrides.deinit(allocator);
try found_overrides.ensureTotalCapacity(allocator, @truncate(lockfile.overrides.map.count()));

var optional_peers_buf = std.ArrayList(String).init(allocator);
defer optional_peers_buf.deinit();

var _indent: u32 = 0;
const indent = &_indent;
try writer.writeAll("{\n");
Expand All @@ -458,6 +461,7 @@ pub const Stringifier = struct {
buf,
deps_buf,
lockfile.workspace_versions,
&optional_peers_buf,
);
for (0..pkgs.len) |pkg_id| {
const res = pkg_resolution[pkg_id];
Expand All @@ -475,6 +479,7 @@ pub const Stringifier = struct {
buf,
deps_buf,
lockfile.workspace_versions,
&optional_peers_buf,
);
}
}
Expand Down Expand Up @@ -608,11 +613,38 @@ pub const Stringifier = struct {
);
}

const DepSortCtx = struct {
string_buf: string,
deps_buf: []const Dependency,

pub fn isLessThan(this: @This(), lhs: DependencyID, rhs: DependencyID) bool {
const l = this.deps_buf[lhs];
const r = this.deps_buf[rhs];
return strings.cmpStringsAsc({}, l.name.slice(this.string_buf), r.name.slice(this.string_buf));
}
};

var deps_sort_buf: std.ArrayListUnmanaged(DependencyID) = .{};
defer deps_sort_buf.deinit(allocator);

var pkg_deps_sort_buf: std.ArrayListUnmanaged(DependencyID) = .{};
defer pkg_deps_sort_buf.deinit(allocator);

try writeIndent(writer, indent);
try writer.writeAll("\"packages\": {");
var first = true;
while (pkgs_iter.next({})) |node| {
for (node.dependencies) |dep_id| {
deps_sort_buf.clearRetainingCapacity();
try deps_sort_buf.appendSlice(allocator, node.dependencies);

std.sort.pdq(
DependencyID,
deps_sort_buf.items,
DepSortCtx{ .string_buf = buf, .deps_buf = deps_buf },
DepSortCtx.isLessThan,
);

for (deps_sort_buf.items) |dep_id| {
const pkg_id = resolution_buf[dep_id];
if (pkg_id == invalid_package_id) continue;

Expand Down Expand Up @@ -651,7 +683,20 @@ pub const Stringifier = struct {

const pkg_name = pkg_names[pkg_id].slice(buf);
const pkg_meta = pkg_metas[pkg_id];
const pkg_deps = pkg_dep_lists[pkg_id].get(deps_buf);
const pkg_deps_list = pkg_dep_lists[pkg_id];

pkg_deps_sort_buf.clearRetainingCapacity();
try pkg_deps_sort_buf.ensureUnusedCapacity(allocator, pkg_deps_list.len);
for (pkg_deps_list.begin()..pkg_deps_list.end()) |pkg_dep_id| {
pkg_deps_sort_buf.appendAssumeCapacity(@intCast(pkg_dep_id));
}

std.sort.pdq(
DependencyID,
pkg_deps_sort_buf.items,
DepSortCtx{ .string_buf = buf, .deps_buf = deps_buf },
DepSortCtx.isLessThan,
);

// first index is resolution for all dependency types
// npm -> [ "name@version", registry or "" (default), deps..., integrity, ... ]
Expand All @@ -663,9 +708,6 @@ pub const Stringifier = struct {
// git -> [ "name@git+repo", deps..., ... ]
// github -> [ "name@github:user/repo", deps..., ... ]

var optional_peers_buf = std.ArrayList(String).init(allocator);
defer optional_peers_buf.deinit();

switch (res.tag) {
.root => {
try writer.print("[\"{}@root:\"]", .{
Expand All @@ -679,7 +721,7 @@ pub const Stringifier = struct {
bun.fmt.formatJSONStringUTF8(res.value.folder.slice(buf), .{ .quote = false }),
});

try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf);
try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf);

try writer.print(", \"{}\"]", .{pkg_meta.integrity});
},
Expand All @@ -689,7 +731,7 @@ pub const Stringifier = struct {
bun.fmt.formatJSONStringUTF8(res.value.local_tarball.slice(buf), .{ .quote = false }),
});

try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf);
try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf);

try writer.print(", \"{}\"]", .{pkg_meta.integrity});
},
Expand All @@ -699,7 +741,7 @@ pub const Stringifier = struct {
bun.fmt.formatJSONStringUTF8(res.value.remote_tarball.slice(buf), .{ .quote = false }),
});

try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf);
try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf);

try writer.print(", \"{}\"]", .{pkg_meta.integrity});
},
Expand All @@ -709,7 +751,7 @@ pub const Stringifier = struct {
bun.fmt.formatJSONStringUTF8(res.value.symlink.slice(buf), .{ .quote = false }),
});

try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf);
try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf);

try writer.print(", \"{}\"]", .{pkg_meta.integrity});
},
Expand All @@ -727,7 +769,7 @@ pub const Stringifier = struct {
res.value.npm.url.slice(buf),
});

try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf);
try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf);

// TODO(dylan-conway): delete placeholder
try writer.print(", \"{}\"]", .{
Expand All @@ -742,7 +784,7 @@ pub const Stringifier = struct {
bun.fmt.formatJSONStringUTF8(workspace_path, .{ .quote = false }),
});

try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf);
try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf);

try writer.writeByte(']');
},
Expand All @@ -753,7 +795,7 @@ pub const Stringifier = struct {
repo.fmt(if (comptime tag == .git) "git+" else "github:", buf),
});

try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf);
try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf);

try writer.print(", \"{}\"]", .{pkg_meta.integrity});
},
Expand All @@ -763,7 +805,7 @@ pub const Stringifier = struct {
}

if (!first) {
try writer.writeByte('\n');
try writer.writeAll(",\n");
try decIndent(writer, indent);
}
try writer.writeAll("}\n");
Expand All @@ -779,7 +821,9 @@ pub const Stringifier = struct {
/// { "devDependencies": { "one": "1.1.1", "two": "2.2.2" }, "os": "none" }
fn writePackageDepsAndMeta(
writer: anytype,
deps: []const Dependency,
_: DependencyID,
deps_buf: []const Dependency,
pkg_dep_ids: []const DependencyID,
meta: *const Meta,
buf: string,
optional_peers_buf: *std.ArrayList(String),
Expand All @@ -793,7 +837,8 @@ pub const Stringifier = struct {
const group_name, const group_behavior = group;

var first = true;
for (deps) |dep| {
for (pkg_dep_ids) |dep_id| {
const dep = deps_buf[dep_id];
if (!dep.behavior.includes(group_behavior)) continue;

if (dep.behavior.isOptionalPeer()) {
Expand Down Expand Up @@ -828,7 +873,7 @@ pub const Stringifier = struct {
if (optional_peers_buf.items.len > 0) {
bun.debugAssert(any);
try writer.writeAll(
\\, "optionalPeers": [
\\, "optionalPeerDependencies": [
);

for (optional_peers_buf.items, 0..) |optional_peer, i| {
Expand Down Expand Up @@ -895,7 +940,9 @@ pub const Stringifier = struct {
buf: string,
deps_buf: []const Dependency,
workspace_versions: BinaryLockfile.VersionHashMap,
optional_peers_buf: *std.ArrayList(String),
) OOM!void {
defer optional_peers_buf.clearRetainingCapacity();
// any - have any properties been written
var any = false;

Expand Down Expand Up @@ -931,6 +978,12 @@ pub const Stringifier = struct {
for (pkg_deps[pkg_id].get(deps_buf)) |dep| {
if (!dep.behavior.includes(group_behavior)) continue;

if (dep.behavior.isOptionalPeer()) {
if (group_behavior.isOptional()) continue;

try optional_peers_buf.append(dep.name);
}

if (first) {
if (any) {
try writer.writeByte(',');
Expand All @@ -957,16 +1010,39 @@ pub const Stringifier = struct {
}

if (!first) {
try writer.writeByte('\n');
try writer.writeAll(",\n");
try decIndent(writer, indent);
try writer.writeAll("}");
}
}
if (optional_peers_buf.items.len > 0) {
bun.debugAssert(any);
try writer.writeAll(
\\,
\\
);
try writeIndent(writer, indent);
try writer.writeAll(
\\"optionalPeerDependencies": [
\\
);
indent.* += 1;
for (optional_peers_buf.items) |optional_peer| {
try writeIndent(writer, indent);
try writer.print(
\\"{s}",
\\
, .{optional_peer.slice(buf)});
}
try decIndent(writer, indent);
try writer.writeByte(']');
}

if (any) {
try writer.writeByte('\n');
try writer.writeAll(",\n");
try decIndent(writer, indent);
}
try writer.writeAll("}");
try writer.writeAll("},");
}

fn writeIndent(writer: anytype, indent: *const u32) OOM!void {
Expand Down Expand Up @@ -999,8 +1075,8 @@ const dependency_groups = [3]struct { []const u8, Dependency.Behavior }{
const workspace_dependency_groups = [4]struct { []const u8, Dependency.Behavior }{
.{ "dependencies", Dependency.Behavior.normal },
.{ "devDependencies", Dependency.Behavior.dev },
.{ "peerDependencies", Dependency.Behavior.peer },
.{ "optionalDependencies", Dependency.Behavior.optional },
.{ "peerDependencies", Dependency.Behavior.peer },
};

const ParseError = OOM || error{
Expand Down Expand Up @@ -1424,7 +1500,7 @@ pub fn parseIntoBinaryLockfile(
_ = pkg_resolution_lists;

{
// root pkg
// first the root dependencies are resolved
pkg_resolutions[0] = Resolution.init(.{ .root = {} });
pkg_metas[0].origin = .local;

Expand All @@ -1437,8 +1513,12 @@ pub fn parseIntoBinaryLockfile(
lockfile.buffers.resolutions.items[dep_id] = dep_node.pkg_id;
}
}

// TODO(dylan-conway) should we handle workspaces separately here for custom hoisting

}

// then each package dependency
for (pkgs_expr.data.e_object.properties.slice()) |prop| {
const key = prop.key.?;
const value = prop.value.?;
Expand All @@ -1463,9 +1543,6 @@ pub fn parseIntoBinaryLockfile(
var curr: ?*PkgPath.Map.Node = pkg_map_entry;
while (curr) |node| {
if (node.nodes.getPtr(dep.name.slice(string_buf.bytes.items))) |dep_node| {

// it doesn't matter which dependency is assigned to this node. the dependency
// id will only be used for getting the dependency name
dep_node.dep_id = dep_id;
lockfile.buffers.resolutions.items[dep_id] = dep_node.pkg_id;

Expand Down Expand Up @@ -1519,7 +1596,7 @@ fn parseAppendDependencies(
) ParseError!struct { u32, u32 } {
defer optional_peers_buf.clearRetainingCapacity();

if (obj.get("optionalPeers")) |optional_peers| {
if (obj.get("optionalPeerDependencies")) |optional_peers| {
if (!optional_peers.isArray()) {
try log.addError(source, optional_peers.loc, "Expected an array");
return error.InvalidPackageInfo;
Expand Down Expand Up @@ -1592,5 +1669,12 @@ fn parseAppendDependencies(
}
const end = lockfile.buffers.dependencies.items.len;

std.sort.pdq(
Dependency,
lockfile.buffers.dependencies.items[off..],
buf.bytes.items,
Dependency.isLessThan,
);

return .{ @intCast(off), @intCast(end - off) };
}
2 changes: 1 addition & 1 deletion src/install/lockfile.zig
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ pub const Tree = struct {
id: Id = invalid_id,

// Should not be used for anything other than name
// through `folderName()`. There is not guarentee a dependency
// through `folderName()`. There is not guarantee a dependency
// id chosen for a tree node is the same behavior or has the
// same version literal for packages hoisted.
dependency_id: DependencyID = invalid_dependency_id,
Expand Down

0 comments on commit 0d4ff3e

Please sign in to comment.