MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add bulk add API and update CLI to install multiple packages

+210 -69
+14
include/pkg.h
··· 72 72 bool dev 73 73 ); 74 74 75 + pkg_error_t pkg_add_many( 76 + pkg_context_t *ctx, 77 + const char *package_json_path, 78 + const char *const *package_specs, 79 + uint32_t count, 80 + bool dev 81 + ); 82 + 75 83 pkg_error_t pkg_remove( 76 84 pkg_context_t *ctx, 77 85 const char *package_json_path, ··· 302 310 pkg_error_t pkg_add_global( 303 311 pkg_context_t *ctx, 304 312 const char *package_spec 313 + ); 314 + 315 + pkg_error_t pkg_add_global_many( 316 + pkg_context_t *ctx, 317 + const char *const *package_specs, 318 + uint32_t count 305 319 ); 306 320 307 321 pkg_error_t pkg_remove_global(
+41 -54
src/cli/pkg.c
··· 184 184 return global_dir; 185 185 } 186 186 187 - static int cmd_add_global(const char *package_spec) { 187 + static int cmd_add_global(const char *const *package_specs, int count) { 188 188 print_install_header("add -g"); 189 189 190 + char resolve_msg[64]; 191 + snprintf(resolve_msg, sizeof(resolve_msg), "๐Ÿ” Resolving [%d/%d]", count, count); 192 + 190 193 progress_t progress; 191 - if (!pkg_verbose) progress_start(&progress, "๐Ÿ” Resolving [1/1]"); 194 + if (!pkg_verbose) progress_start(&progress, resolve_msg); 192 195 193 196 pkg_options_t opts = { 194 197 .progress_callback = pkg_verbose ? NULL : progress_callback, ··· 202 205 return EXIT_FAILURE; 203 206 } 204 207 205 - pkg_error_t err = pkg_add_global(ctx, package_spec); 208 + pkg_error_t err = pkg_add_global_many(ctx, package_specs, (uint32_t)count); 206 209 if (!pkg_verbose) progress_stop(&progress); 207 210 208 211 if (err != PKG_OK) { ··· 213 216 214 217 pkg_install_result_t result; 215 218 if (pkg_get_install_result(ctx, &result) == PKG_OK) { 216 - printf("\n%sinstalled globally%s %s%s%s\n", 217 - C_GREEN, C_RESET, C_BOLD, package_spec, C_RESET); 219 + for (int i = 0; i < count; i++) { 220 + printf("\n%sinstalled globally%s %s%s%s\n", 221 + C_GREEN, C_RESET, C_BOLD, package_specs[i], C_RESET); 222 + } 218 223 printf(" %s(binaries linked to ~/.ant/bin)%s\n", C_DIM, C_RESET); 219 224 printf("\n%s[%s", C_DIM, C_RESET); 220 225 print_elapsed(result.elapsed_ms); ··· 368 373 return EXIT_SUCCESS; 369 374 } 370 375 371 - static int cmd_add(const char *package_spec, bool dev) { 376 + static int cmd_add(const char *const *package_specs, int count, bool dev) { 372 377 print_install_header(dev ? "add -D" : "add"); 373 378 374 - char pkg_name[256]; 375 - const char *at_pos = strchr(package_spec, '@'); 376 - if (at_pos == package_spec) { 377 - at_pos = strchr(package_spec + 1, '@'); 378 - } 379 - if (at_pos) { 380 - size_t name_len = (size_t)(at_pos - package_spec); 381 - if (name_len >= sizeof(pkg_name)) name_len = sizeof(pkg_name) - 1; 382 - memcpy(pkg_name, package_spec, name_len); 383 - pkg_name[name_len] = '\0'; 384 - } else { 385 - strncpy(pkg_name, package_spec, sizeof(pkg_name) - 1); 386 - pkg_name[sizeof(pkg_name) - 1] = '\0'; 387 - } 379 + char resolve_msg[64]; 380 + snprintf(resolve_msg, sizeof(resolve_msg), "๐Ÿ” Resolving [%d/%d]", count, count); 388 381 389 382 progress_t progress; 390 - 391 383 if (!pkg_verbose) { 392 - progress_start(&progress, "๐Ÿ” Resolving [1/1]"); 384 + progress_start(&progress, resolve_msg); 393 385 } 394 386 395 387 pkg_options_t opts = { ··· 403 395 return EXIT_FAILURE; 404 396 } 405 397 406 - pkg_error_t err = pkg_add(ctx, "package.json", package_spec, dev); 398 + pkg_error_t err = pkg_add_many(ctx, "package.json", package_specs, (uint32_t)count, dev); 407 399 if (err != PKG_OK) { 408 400 if (!pkg_verbose) { progress_stop(&progress); } 409 401 fprintf(stderr, "Error: %s\n", pkg_error_string(ctx)); ··· 420 412 } 421 413 422 414 if (!pkg_verbose) progress_stop(&progress); 423 - 424 - pkg_added_package_t added_pkg = {0}; 425 - uint32_t added_count = pkg_get_added_count(ctx); 426 - for (uint32_t i = 0; i < added_count; i++) { 427 - pkg_added_package_t pkg; 428 - if (pkg_get_added_package(ctx, i, &pkg) == PKG_OK && pkg.direct) { 429 - if (strcmp(pkg.name, pkg_name) == 0) { 430 - added_pkg = pkg; break; 431 - } 432 - } 433 - } 434 415 435 416 pkg_install_result_t result; 436 417 if (pkg_get_install_result(ctx, &result) == PKG_OK) { 437 418 printf("\n"); 438 419 439 - if (added_pkg.name) { 440 - int bin_count = pkg_list_package_bins("node_modules", added_pkg.name, NULL, NULL); 441 - 442 - if (bin_count > 0) { 443 - printf("%sinstalled%s %s%s@%s%s with binaries:\n", 444 - C_GREEN, C_RESET, 445 - C_BOLD, added_pkg.name, added_pkg.version, C_RESET); 446 - pkg_list_package_bins("node_modules", added_pkg.name, print_bin_callback, NULL); 447 - } else { 448 - printf("%sinstalled%s %s%s@%s%s\n", 449 - C_GREEN, C_RESET, 450 - C_BOLD, added_pkg.name, added_pkg.version, C_RESET); 420 + uint32_t added_count = pkg_get_added_count(ctx); 421 + for (uint32_t i = 0; i < added_count; i++) { 422 + pkg_added_package_t pkg; 423 + if (pkg_get_added_package(ctx, i, &pkg) == PKG_OK && pkg.direct) { 424 + int bin_count = pkg_list_package_bins("node_modules", pkg.name, NULL, NULL); 425 + if (bin_count > 0) { 426 + printf("%sinstalled%s %s%s@%s%s with binaries:\n", 427 + C_GREEN, C_RESET, 428 + C_BOLD, pkg.name, pkg.version, C_RESET); 429 + pkg_list_package_bins("node_modules", pkg.name, print_bin_callback, NULL); 430 + } else { 431 + printf("%sinstalled%s %s%s@%s%s\n", 432 + C_GREEN, C_RESET, 433 + C_BOLD, pkg.name, pkg.version, C_RESET); 434 + } 451 435 } 452 436 } 453 437 454 - printf("\n%s[%s", C_DIM, C_RESET); 438 + printf("\n%s%u%s package%s installed %s[%s", 439 + C_GREEN, result.packages_installed, C_RESET, 440 + result.packages_installed == 1 ? "" : "s", 441 + C_DIM, C_RESET); 455 442 print_elapsed(result.elapsed_ms); 456 - printf("%s]%s done\n", C_DIM, C_RESET); 443 + printf("%s]%s\n", C_DIM, C_RESET); 457 444 } 458 445 459 446 pkg_free(ctx); ··· 796 783 exitcode = cmd_install(); 797 784 } else { 798 785 bool is_dev = dev->count > 0; 799 - for (int i = 0; i < pkgs->count && exitcode == EXIT_SUCCESS; i++) { 800 - exitcode = global->count > 0 ? cmd_add_global(pkgs->sval[i]) : cmd_add(pkgs->sval[i], is_dev); 801 - } 786 + exitcode = global->count > 0 787 + ? cmd_add_global(pkgs->sval, pkgs->count) 788 + : cmd_add(pkgs->sval, pkgs->count, is_dev); 802 789 } 803 790 804 791 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0])); ··· 826 813 exitcode = EXIT_FAILURE; 827 814 } else { 828 815 bool is_dev = dev->count > 0; 829 - for (int i = 0; i < pkgs->count && exitcode == EXIT_SUCCESS; i++) { 830 - exitcode = global->count > 0 ? cmd_add_global(pkgs->sval[i]) : cmd_add(pkgs->sval[i], is_dev); 831 - } 816 + exitcode = global->count > 0 817 + ? cmd_add_global(pkgs->sval, pkgs->count) 818 + : cmd_add(pkgs->sval, pkgs->count, is_dev); 832 819 } 833 820 834 821 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
+155 -15
src/pkg/root.zig
··· 1568 1568 return .ok; 1569 1569 } 1570 1570 1571 + export fn pkg_add_many( 1572 + ctx: ?*PkgContext, 1573 + package_json_path: [*:0]const u8, 1574 + package_specs: [*]const [*:0]const u8, 1575 + count: u32, 1576 + dev: bool, 1577 + ) PkgError { 1578 + const c = ctx orelse return .invalid_argument; 1579 + _ = c.arena_state.reset(.retain_capacity); 1580 + const arena_alloc = c.arena_state.allocator(); 1581 + 1582 + const pkg_json_str = std.mem.span(package_json_path); 1583 + 1584 + const http = c.http orelse return .network_error; 1585 + var res = resolver.Resolver.init( 1586 + arena_alloc, 1587 + c.allocator, 1588 + &c.string_pool, 1589 + http, 1590 + c.cache_db, 1591 + if (c.options.registry_url) |url| std.mem.span(url) else "https://registry.npmjs.org", 1592 + &c.metadata_cache, 1593 + ); defer res.deinit(); 1594 + 1595 + const ResolvedEntry = struct { 1596 + name: []const u8, 1597 + version_str: []const u8, 1598 + }; 1599 + 1600 + const resolved = arena_alloc.alloc(ResolvedEntry, count) catch return .out_of_memory; 1601 + 1602 + for (0..count) |i| { 1603 + const spec_str = std.mem.span(package_specs[i]); 1604 + var pkg_name: []const u8 = spec_str; 1605 + var version_constraint: []const u8 = "latest"; 1606 + 1607 + if (std.mem.indexOf(u8, spec_str, "@")) |at_idx| { 1608 + if (at_idx == 0) { 1609 + if (std.mem.indexOfPos(u8, spec_str, 1, "@")) |second_at| { 1610 + pkg_name = spec_str[0..second_at]; 1611 + version_constraint = spec_str[second_at + 1 ..]; 1612 + } 1613 + } else { 1614 + pkg_name = spec_str[0..at_idx]; 1615 + version_constraint = spec_str[at_idx + 1 ..]; 1616 + } 1617 + } 1618 + 1619 + const resolved_pkg = res.resolve(pkg_name, version_constraint, 0) catch |err| { 1620 + c.setErrorFmt("Failed to resolve {s}: {}", .{ pkg_name, err }); 1621 + return .resolve_error; 1622 + }; 1623 + 1624 + const version_str = resolved_pkg.version.format(arena_alloc) catch return .out_of_memory; 1625 + resolved[i] = .{ .name = pkg_name, .version_str = version_str }; 1626 + } 1627 + 1628 + const content = blk: { 1629 + const file = std.fs.cwd().openFile(pkg_json_str, .{ .mode = .read_only }) catch |err| { 1630 + if (err == error.FileNotFound) break :blk "{}"; 1631 + c.setError("Failed to open package.json"); 1632 + return .io_error; 1633 + }; 1634 + defer file.close(); 1635 + break :blk file.readToEndAlloc(arena_alloc, 10 * 1024 * 1024) catch { 1636 + c.setError("Failed to read package.json"); 1637 + return .io_error; 1638 + }; 1639 + }; 1640 + 1641 + const parsed = std.json.parseFromSlice(std.json.Value, arena_alloc, content, .{}) catch { 1642 + c.setError("Failed to parse package.json"); 1643 + return .invalid_argument; 1644 + }; defer parsed.deinit(); 1645 + 1646 + if (parsed.value != .object) { 1647 + c.setError("Invalid package.json format"); 1648 + return .invalid_argument; 1649 + } 1650 + 1651 + const target_key = if (dev) "devDependencies" else "dependencies"; 1652 + 1653 + var deps = if (parsed.value.object.get(target_key)) |d| 1654 + if (d == .object) d.object else std.json.ObjectMap.init(arena_alloc) 1655 + else std.json.ObjectMap.init(arena_alloc); 1656 + 1657 + for (resolved) |entry| { 1658 + const version_with_caret = std.fmt.allocPrint(arena_alloc, "^{s}", .{entry.version_str}) catch return .out_of_memory; 1659 + deps.put(entry.name, .{ .string = version_with_caret }) catch return .out_of_memory; 1660 + } 1661 + 1662 + var writer = json.JsonWriter.init() catch return .out_of_memory; 1663 + defer writer.deinit(); 1664 + 1665 + const root_obj = writer.createObject(); 1666 + writer.setRoot(root_obj); 1667 + 1668 + var found_target = false; 1669 + for (parsed.value.object.keys(), parsed.value.object.values()) |key, value| { 1670 + if (std.mem.eql(u8, key, target_key)) { 1671 + found_target = true; 1672 + const deps_obj = writer.createObject(); 1673 + var dep_iter = deps.iterator(); 1674 + while (dep_iter.next()) |entry| { 1675 + if (entry.value_ptr.* == .string) { 1676 + writer.objectAdd(deps_obj, entry.key_ptr.*, writer.createString(entry.value_ptr.string)); 1677 + } 1678 + } writer.objectAdd(root_obj, key, deps_obj); 1679 + } else { 1680 + const json_val = jsonValueToMut(&writer, value) catch continue; 1681 + writer.objectAdd(root_obj, key, json_val); 1682 + } 1683 + } 1684 + 1685 + if (!found_target) { 1686 + const deps_obj = writer.createObject(); 1687 + for (resolved) |entry| { 1688 + const version_with_caret = std.fmt.allocPrint(arena_alloc, "^{s}", .{entry.version_str}) catch return .out_of_memory; 1689 + writer.objectAdd(deps_obj, entry.name, writer.createString(version_with_caret)); 1690 + } 1691 + writer.objectAdd(root_obj, target_key, deps_obj); 1692 + } 1693 + 1694 + const pkg_json_z = arena_alloc.dupeZ(u8, pkg_json_str) catch return .out_of_memory; 1695 + 1696 + writer.writeToFile(pkg_json_z) catch { 1697 + c.setError("Failed to write package.json"); 1698 + return .io_error; 1699 + }; 1700 + 1701 + return .ok; 1702 + } 1703 + 1571 1704 fn jsonValueToMut(writer: *json.JsonWriter, value: std.json.Value) !*json.yyjson.yyjson_mut_val { 1572 1705 return switch (value) { 1573 1706 .null => writer.createNull(), ··· 2655 2788 ctx: ?*PkgContext, 2656 2789 package_spec: [*:0]const u8, 2657 2790 ) PkgError { 2791 + const specs = [_][*:0]const u8{package_spec}; 2792 + return pkg_add_global_many(ctx, &specs, 1); 2793 + } 2794 + 2795 + export fn pkg_add_global_many( 2796 + ctx: ?*PkgContext, 2797 + package_specs: [*]const [*:0]const u8, 2798 + count: u32, 2799 + ) PkgError { 2658 2800 const c = ctx orelse return .invalid_argument; 2659 2801 const allocator = c.allocator; 2660 2802 ··· 2676 2818 const nm_path = std.fmt.allocPrintSentinel(allocator, "{s}/node_modules", .{global_dir}, 0) catch return .out_of_memory; 2677 2819 defer allocator.free(nm_path); 2678 2820 2679 - const spec_str = std.mem.span(package_spec); 2680 - var pkg_name: []const u8 = spec_str; 2681 - 2682 - if (std.mem.indexOf(u8, spec_str, "@")) |at_idx| { 2683 - if (at_idx == 0) { 2684 - if (std.mem.indexOfPos(u8, spec_str, 1, "@")) |second_at| { 2685 - pkg_name = spec_str[0..second_at]; 2686 - } 2687 - } else { 2688 - pkg_name = spec_str[0..at_idx]; 2689 - } 2690 - } 2691 - 2692 - const add_result = pkg_add(c, pkg_json_path.ptr, package_spec, false); 2821 + const add_result = pkg_add_many(c, pkg_json_path.ptr, package_specs, count, false); 2693 2822 if (add_result != .ok) return add_result; 2694 2823 2695 2824 const install_result = pkg_resolve_and_install(c, pkg_json_path.ptr, lockfile_path.ptr, nm_path.ptr); 2696 2825 if (install_result != .ok) return install_result; 2697 2826 2698 - linkGlobalBins(allocator, nm_path, pkg_name); 2827 + for (0..count) |i| { 2828 + const spec_str = std.mem.span(package_specs[i]); 2829 + var pkg_name: []const u8 = spec_str; 2830 + 2831 + if (std.mem.indexOf(u8, spec_str, "@")) |at_idx| { 2832 + if (at_idx == 0) { 2833 + if (std.mem.indexOfPos(u8, spec_str, 1, "@")) |second_at| pkg_name = spec_str[0..second_at]; 2834 + } else pkg_name = spec_str[0..at_idx]; 2835 + } 2836 + 2837 + linkGlobalBins(allocator, nm_path, pkg_name); 2838 + } 2699 2839 2700 2840 return .ok; 2701 2841 }