diff --git a/src/pkg/cli.zig b/src/pkg/cli.zig new file mode 100644 index 0000000..c7ef455 --- /dev/null +++ b/src/pkg/cli.zig @@ -0,0 +1,70 @@ +const std = @import("std"); +const root = @import("root.zig"); + +const ListCallback = + *const fn ([*:0]const u8, [*:0]const u8, ?*anyopaque) callconv(.c) void; + +const PkgDeps = struct { + deps: ?std.json.ObjectMap, + dev_deps: ?std.json.ObjectMap, + nm_path: []const u8, + arena: std.mem.Allocator, + parsed: std.json.Parsed(std.json.Value), + + pub fn deinit(self: *PkgDeps) void { + self.parsed.deinit(); + } + + pub fn count(self: *const PkgDeps) u32 { + var c: u32 = 0; + if (self.deps) |d| c += @intCast(d.count()); + if (self.dev_deps) |d| c += @intCast(d.count()); + return c; + } +}; + +fn emitPkg(pd: *PkgDeps, name: []const u8, cb: ListCallback, ud: ?*anyopaque) void { + const pkg_json = std.fmt.allocPrint(pd.arena, "{s}/{s}/package.json", .{pd.nm_path, name}) catch return; + const content = std.fs.cwd().readFileAlloc(pd.arena, pkg_json, 256 * 1024) catch return; + const parsed = std.json.parseFromSlice(std.json.Value, pd.arena, content, .{}) catch return; + defer parsed.deinit(); + + const ver = if (parsed.value.object.get("version")) |v| if (v == .string) v.string else "?" else "?"; + cb(pd.arena.dupeZ(u8, name) catch return, pd.arena.dupeZ(u8, ver) catch return, ud); +} + +pub fn get_dependencies(ctx: ?*root.PkgContext, base_path: ?[]const u8, include_dev: bool) ?PkgDeps { + const c = ctx orelse return null; + _ = c.arena_state.reset(.retain_capacity); + const arena = c.arena_state.allocator(); + + const pkg_json_path = if (base_path) |bp| std.fmt.allocPrint(arena, "{s}/package.json", .{bp}) catch return null + else arena.dupe(u8, "package.json") catch return null; + + const nm_path = if (base_path) |bp| std.fmt.allocPrint(arena, "{s}/node_modules", .{bp}) catch return null + else arena.dupe(u8, "node_modules") catch return null; + + const content = std.fs.cwd().readFileAlloc(arena, pkg_json_path, 1024 * 1024) catch return null; + const parsed = std.json.parseFromSlice(std.json.Value, arena, content, .{}) catch return null; + + const deps_val = parsed.value.object.get("dependencies"); + const deps = if (deps_val) |v| if (v == .object) v.object else null else null; + + const dev_deps = if (include_dev) blk: { + const dev_val = parsed.value.object.get("devDependencies"); + break :blk if (dev_val) |v| if (v == .object) v.object else null else null; + } else null; + + if (deps == null and dev_deps == null) { + var p = parsed; + p.deinit(); + return null; + } + + return .{ .deps = deps, .dev_deps = dev_deps, .nm_path = nm_path, .arena = arena, .parsed = parsed }; +} + +pub fn list_dependencies(pd: *PkgDeps, cb: ListCallback, ud: ?*anyopaque) void { + if (pd.deps) |d| for (d.keys()) |n| emitPkg(pd, n, cb, ud); + if (pd.dev_deps) |d| for (d.keys()) |n| emitPkg(pd, n, cb, ud); +} diff --git a/src/pkg/root.zig b/src/pkg/root.zig index 668711a..8bbe996 100644 --- a/src/pkg/root.zig +++ b/src/pkg/root.zig @@ -591,8 +591,16 @@ export fn pkg_get_added_package(ctx: ?*const PkgContext, index: u32, out: *Added } export fn pkg_count_installed(node_modules_path: [*:0]const u8) u32 { - var dir = std.fs.cwd().openDir(std.mem.span(node_modules_path), .{ .iterate = true }) catch return 0; - defer dir.close(); return cli.count_dir(dir, true); + const nm_path = std.mem.span(node_modules_path); + if (!std.mem.endsWith(u8, nm_path, "node_modules")) return 0; + + const base = nm_path[0 .. nm_path.len - "node_modules".len]; + var buf: [std.fs.max_path_bytes]u8 = undefined; + const lp = std.fmt.bufPrint(&buf, "{s}ant.lockb", .{base}) catch return 0; + + var lf = lockfile.Lockfile.open(lp) catch return 0; + defer lf.close(); + return lf.header.package_count; } export fn pkg_discover_lifecycle_scripts(