diff --git a/src/analytics.zig b/src/analytics.zig index fbbc5c9726..7b078cdd55 100644 --- a/src/analytics.zig +++ b/src/analytics.zig @@ -112,6 +112,7 @@ pub const Features = struct { pub var unsupported_uv_function: usize = 0; pub var exited: usize = 0; pub var yarn_migration: usize = 0; + pub var pnpm_migration: usize = 0; pub var yaml_parse: usize = 0; comptime { diff --git a/src/ast/E.zig b/src/ast/E.zig index c6cf7cf413..6f70e11813 100644 --- a/src/ast/E.zig +++ b/src/ast/E.zig @@ -752,7 +752,7 @@ pub const Object = struct { pub fn hasProperty(obj: *const Object, name: string) bool { for (obj.properties.slice()) |prop| { const key = prop.key orelse continue; - if (std.meta.activeTag(key.data) != .e_string) continue; + if (key.data != .e_string) continue; if (key.data.e_string.eql(string, name)) return true; } return false; @@ -762,7 +762,7 @@ pub const Object = struct { for (obj.properties.slice(), 0..) |prop, i| { const value = prop.value orelse continue; const key = prop.key orelse continue; - if (std.meta.activeTag(key.data) != .e_string) continue; + if (key.data != .e_string) continue; const key_str = key.data.e_string; if (key_str.eql(string, name)) { return Expr.Query{ diff --git a/src/ast/Expr.zig b/src/ast/Expr.zig index b1814aab95..a63608ca97 100644 --- a/src/ast/Expr.zig +++ b/src/ast/Expr.zig @@ -132,14 +132,14 @@ pub fn isEmpty(expr: Expr) bool { pub const Query = struct { expr: Expr, loc: logger.Loc, i: u32 = 0 }; pub fn hasAnyPropertyNamed(expr: *const Expr, comptime names: []const string) bool { - if (std.meta.activeTag(expr.data) != .e_object) return false; + if (expr.data != .e_object) return false; const obj = expr.data.e_object; if (obj.properties.len == 0) return false; for (obj.properties.slice()) |prop| { if (prop.value == null) continue; const key = prop.key orelse continue; - if (std.meta.activeTag(key.data) != .e_string) continue; + if (key.data != .e_string) continue; const key_str = key.data.e_string; if (strings.eqlAnyComptime(key_str.data, names)) return true; } @@ -266,7 +266,7 @@ pub fn set(expr: *Expr, allocator: std.mem.Allocator, name: string, value: Expr) for (0..expr.data.e_object.properties.len) |i| { const prop = &expr.data.e_object.properties.ptr[i]; const key = prop.key orelse continue; - if (std.meta.activeTag(key.data) != .e_string) continue; + if (key.data != .e_string) continue; if (key.data.e_string.eql(string, name)) { prop.value = value; return; @@ -288,7 +288,7 @@ pub fn setString(expr: *Expr, allocator: std.mem.Allocator, name: string, value: for (0..expr.data.e_object.properties.len) |i| { const prop = &expr.data.e_object.properties.ptr[i]; const key = prop.key orelse continue; - if (std.meta.activeTag(key.data) != .e_string) continue; + if (key.data != .e_string) continue; if (key.data.e_string.eql(string, name)) { prop.value = Expr.init(E.String, .{ .data = value }, logger.Loc.Empty); return; @@ -310,6 +310,15 @@ pub fn getObject(expr: *const Expr, name: string) ?Expr { return null; } +pub fn getBoolean(expr: *const Expr, name: string) ?bool { + if (expr.asProperty(name)) |query| { + if (query.expr.data == .e_boolean) { + return query.expr.data.e_boolean.value; + } + } + return null; +} + pub fn getString(expr: *const Expr, allocator: std.mem.Allocator, name: string) OOM!?struct { string, logger.Loc } { if (asProperty(expr, name)) |q| { if (q.expr.asString(allocator)) |str| { @@ -385,7 +394,7 @@ pub fn getRope(self: *const Expr, rope: *const E.Object.Rope) ?E.Object.RopeQuer // Making this comptime bloats the binary and doesn't seem to impact runtime performance. pub fn asProperty(expr: *const Expr, name: string) ?Query { - if (std.meta.activeTag(expr.data) != .e_object) return null; + if (expr.data != .e_object) return null; const obj = expr.data.e_object; if (obj.properties.len == 0) return null; @@ -393,7 +402,7 @@ pub fn asProperty(expr: *const Expr, name: string) ?Query { } pub fn asPropertyStringMap(expr: *const Expr, name: string, allocator: std.mem.Allocator) ?*bun.StringArrayHashMap(string) { - if (std.meta.activeTag(expr.data) != .e_object) return null; + if (expr.data != .e_object) return null; const obj_ = expr.data.e_object; if (obj_.properties.len == 0) return null; const query = obj_.asProperty(name) orelse return null; @@ -439,7 +448,7 @@ pub const ArrayIterator = struct { }; pub fn asArray(expr: *const Expr) ?ArrayIterator { - if (std.meta.activeTag(expr.data) != .e_array) return null; + if (expr.data != .e_array) return null; const array = expr.data.e_array; if (array.items.len == 0) return null; @@ -455,7 +464,7 @@ pub inline fn asUtf8StringLiteral(expr: *const Expr) ?string { } pub inline fn asStringLiteral(expr: *const Expr, allocator: std.mem.Allocator) ?string { - if (std.meta.activeTag(expr.data) != .e_string) return null; + if (expr.data != .e_string) return null; return expr.data.e_string.string(allocator) catch null; } @@ -501,7 +510,7 @@ pub inline fn asStringZ(expr: *const Expr, allocator: std.mem.Allocator) OOM!?st pub fn asBool( expr: *const Expr, ) ?bool { - if (std.meta.activeTag(expr.data) != .e_boolean) return null; + if (expr.data != .e_boolean) return null; return expr.data.e_boolean.value; } @@ -522,7 +531,7 @@ const Serializable = struct { }; pub fn isMissing(a: *const Expr) bool { - return std.meta.activeTag(a.data) == Expr.Tag.e_missing; + return a.data == Expr.Tag.e_missing; } // The goal of this function is to "rotate" the AST if it's possible to use the diff --git a/src/ast/P.zig b/src/ast/P.zig index 3dd1b87160..22d39f182e 100644 --- a/src/ast/P.zig +++ b/src/ast/P.zig @@ -954,7 +954,7 @@ pub fn NewParser_( switch (call.target.data) { .e_identifier => |ident| { // is this a require("something") - if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and std.meta.activeTag(call.args.ptr[0].data) == .e_string) { + if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and call.args.ptr[0].data == .e_string) { _ = p.addImportRecord(.require, loc, call.args.at(0).data.e_string.string(p.allocator) catch unreachable); } }, @@ -970,7 +970,7 @@ pub fn NewParser_( switch (call.target.data) { .e_identifier => |ident| { // is this a require("something") - if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and std.meta.activeTag(call.args.ptr[0].data) == .e_string) { + if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and call.args.ptr[0].data == .e_string) { _ = p.addImportRecord(.require, loc, call.args.at(0).data.e_string.string(p.allocator) catch unreachable); } }, diff --git a/src/ast/visitExpr.zig b/src/ast/visitExpr.zig index c31db1a375..ebaac7b7f4 100644 --- a/src/ast/visitExpr.zig +++ b/src/ast/visitExpr.zig @@ -64,7 +64,7 @@ pub fn VisitExpr( } pub fn e_import_meta(p: *P, expr: Expr, in: ExprIn) Expr { // TODO: delete import.meta might not work - const is_delete_target = std.meta.activeTag(p.delete_target) == .e_import_meta; + const is_delete_target = p.delete_target == .e_import_meta; if (p.define.dots.get("meta")) |meta| { for (meta) |define| { diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig index 4ba5acd9d4..186e60377e 100644 --- a/src/bun.js/node/node_os.zig +++ b/src/bun.js/node/node_os.zig @@ -64,7 +64,7 @@ fn cpusImplLinux(globalThis: *jsc.JSGlobalObject) !jsc.JSValue { const file = try std.fs.openFileAbsolute("/proc/stat", .{}); defer file.close(); - const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, true).unwrap(); + const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, .probably_small).unwrap(); defer file_buf.clearRetainingCapacity(); const contents = file_buf.items[0..read]; @@ -104,7 +104,7 @@ fn cpusImplLinux(globalThis: *jsc.JSGlobalObject) !jsc.JSValue { if (std.fs.openFileAbsolute("/proc/cpuinfo", .{})) |file| { defer file.close(); - const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, true).unwrap(); + const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, .probably_small).unwrap(); defer file_buf.clearRetainingCapacity(); const contents = file_buf.items[0..read]; @@ -155,7 +155,7 @@ fn cpusImplLinux(globalThis: *jsc.JSGlobalObject) !jsc.JSValue { if (std.fs.openFileAbsolute(path, .{})) |file| { defer file.close(); - const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, true).unwrap(); + const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, .probably_small).unwrap(); defer file_buf.clearRetainingCapacity(); const contents = file_buf.items[0..read]; diff --git a/src/cli/install_command.zig b/src/cli/install_command.zig index e8d5baece2..626e41f2eb 100644 --- a/src/cli/install_command.zig +++ b/src/cli/install_command.zig @@ -77,15 +77,7 @@ fn installWithCLI(ctx: Command.Context, cli: CommandLineArguments) !void { Output.flush(); } - const package_json_contents = manager.root_package_json_file.readToEndAlloc(ctx.allocator, std.math.maxInt(usize)) catch |err| { - if (manager.options.log_level != .silent) { - Output.prettyErrorln("{s} reading package.json :(", .{@errorName(err)}); - Output.flush(); - } - return; - }; - - try manager.installWithManager(ctx, package_json_contents, original_cwd); + try manager.installWithManager(ctx, PackageManager.root_package_json_path, original_cwd); if (manager.any_failed_to_install) { Global.exit(1); @@ -94,8 +86,6 @@ fn installWithCLI(ctx: Command.Context, cli: CommandLineArguments) !void { const string = []const u8; -const std = @import("std"); - const bun = @import("bun"); const Global = bun.Global; const Output = bun.Output; diff --git a/src/cli/outdated_command.zig b/src/cli/outdated_command.zig index ae9bb6dc25..98b2cf0fc4 100644 --- a/src/cli/outdated_command.zig +++ b/src/cli/outdated_command.zig @@ -5,16 +5,6 @@ pub const OutdatedCommand = struct { workspace_pkg_id: PackageID, is_catalog: bool, }; - fn resolveCatalogDependency(manager: *PackageManager, dep: Install.Dependency) ?Install.Dependency.Version { - return if (dep.version.tag == .catalog) blk: { - const catalog_dep = manager.lockfile.catalogs.get( - manager.lockfile, - dep.version.value.catalog, - dep.name, - ) orelse return null; - break :blk catalog_dep.version; - } else dep.version; - } pub fn exec(ctx: Command.Context) !void { Output.prettyln("bun outdated v" ++ Global.package_json_version_with_sha ++ "", .{}); @@ -91,19 +81,19 @@ pub const OutdatedCommand = struct { ) catch |err| bun.handleOom(err); defer bun.default_allocator.free(workspace_pkg_ids); - try updateManifestsIfNecessary(manager, workspace_pkg_ids); + try manager.populateManifestCache(.{ .ids = workspace_pkg_ids }); try printOutdatedInfoTable(manager, workspace_pkg_ids, true, enable_ansi_colors); } else if (manager.options.do.recursive) { const all_workspaces = bun.handleOom(getAllWorkspaces(bun.default_allocator, manager)); defer bun.default_allocator.free(all_workspaces); - try updateManifestsIfNecessary(manager, all_workspaces); + try manager.populateManifestCache(.{ .ids = all_workspaces }); try printOutdatedInfoTable(manager, all_workspaces, true, enable_ansi_colors); } else { const root_pkg_id = manager.root_package_id.get(manager.lockfile, manager.workspace_name_hash); if (root_pkg_id == invalid_package_id) return; - try updateManifestsIfNecessary(manager, &.{root_pkg_id}); + try manager.populateManifestCache(.{ .ids = &.{root_pkg_id} }); try printOutdatedInfoTable(manager, &.{root_pkg_id}, false, enable_ansi_colors); } }, @@ -399,8 +389,8 @@ pub const OutdatedCommand = struct { for (pkg_deps.begin()..pkg_deps.end()) |dep_id| { const package_id = lockfile.buffers.resolutions.items[dep_id]; if (package_id == invalid_package_id) continue; - const dep = lockfile.buffers.dependencies.items[dep_id]; - const resolved_version = resolveCatalogDependency(manager, dep) orelse continue; + const dep = &lockfile.buffers.dependencies.items[dep_id]; + const resolved_version = manager.lockfile.resolveCatalogDependency(dep) orelse continue; if (resolved_version.tag != .npm and resolved_version.tag != .dist_tag) continue; const resolution = pkg_resolutions[package_id]; if (resolution.tag != .npm) continue; @@ -556,7 +546,7 @@ pub const OutdatedCommand = struct { const package_id = item.package_id; const dep_id = item.dep_id; - const dep = dependencies[dep_id]; + const dep = &dependencies[dep_id]; if (!dep.behavior.includes(group_behavior)) continue; const package_name = pkg_names[package_id].slice(string_buf); @@ -572,7 +562,7 @@ pub const OutdatedCommand = struct { ) orelse continue; const latest = manifest.findByDistTag("latest") orelse continue; - const resolved_version = resolveCatalogDependency(manager, dep) orelse continue; + const resolved_version = manager.lockfile.resolveCatalogDependency(dep) orelse continue; const update = if (resolved_version.tag == .npm) manifest.findBestVersion(resolved_version.value.npm.version, string_buf) orelse continue else @@ -650,128 +640,6 @@ pub const OutdatedCommand = struct { table.printBottomLineSeparator(); } - - pub fn updateManifestsIfNecessary( - manager: *PackageManager, - workspace_pkg_ids: []const PackageID, - ) !void { - const log_level = manager.options.log_level; - const lockfile = manager.lockfile; - const resolutions = lockfile.buffers.resolutions.items; - const dependencies = lockfile.buffers.dependencies.items; - const string_buf = lockfile.buffers.string_bytes.items; - const packages = lockfile.packages.slice(); - const pkg_resolutions = packages.items(.resolution); - const pkg_names = packages.items(.name); - const pkg_dependencies = packages.items(.dependencies); - - for (workspace_pkg_ids) |workspace_pkg_id| { - const pkg_deps = pkg_dependencies[workspace_pkg_id]; - for (pkg_deps.begin()..pkg_deps.end()) |dep_id| { - if (dep_id >= dependencies.len) continue; - const package_id = resolutions[dep_id]; - if (package_id == invalid_package_id) continue; - const dep = dependencies[dep_id]; - const resolved_version = resolveCatalogDependency(manager, dep) orelse continue; - if (resolved_version.tag != .npm and resolved_version.tag != .dist_tag) continue; - const resolution: Install.Resolution = pkg_resolutions[package_id]; - if (resolution.tag != .npm) continue; - - const package_name = pkg_names[package_id].slice(string_buf); - _ = manager.manifests.byName( - manager, - manager.scopeForPackageName(package_name), - package_name, - .load_from_memory_fallback_to_disk, - ) orelse { - const task_id = Install.Task.Id.forManifest(package_name); - if (manager.hasCreatedNetworkTask(task_id, dep.behavior.optional)) continue; - - manager.startProgressBarIfNone(); - - var task = manager.getNetworkTask(); - task.* = .{ - .package_manager = manager, - .callback = undefined, - .task_id = task_id, - .allocator = manager.allocator, - }; - try task.forManifest( - package_name, - manager.allocator, - manager.scopeForPackageName(package_name), - null, - dep.behavior.optional, - ); - - manager.enqueueNetworkTask(task); - }; - } - - manager.flushNetworkQueue(); - _ = manager.scheduleTasks(); - - if (manager.pendingTaskCount() > 1) { - try manager.runTasks( - *PackageManager, - manager, - .{ - .onExtract = {}, - .onResolve = {}, - .onPackageManifestError = {}, - .onPackageDownloadError = {}, - .progress_bar = true, - .manifests_only = true, - }, - true, - log_level, - ); - } - } - - manager.flushNetworkQueue(); - _ = manager.scheduleTasks(); - - const RunClosure = struct { - manager: *PackageManager, - err: ?anyerror = null, - pub fn isDone(closure: *@This()) bool { - if (closure.manager.pendingTaskCount() > 0) { - closure.manager.runTasks( - *PackageManager, - closure.manager, - .{ - .onExtract = {}, - .onResolve = {}, - .onPackageManifestError = {}, - .onPackageDownloadError = {}, - .progress_bar = true, - .manifests_only = true, - }, - true, - closure.manager.options.log_level, - ) catch |err| { - closure.err = err; - return true; - }; - } - - return closure.manager.pendingTaskCount() == 0; - } - }; - - var run_closure: RunClosure = .{ .manager = manager }; - manager.sleepUntil(&run_closure, &RunClosure.isDone); - - if (log_level.showProgress()) { - manager.endProgressBar(); - Output.flush(); - } - - if (run_closure.err) |err| { - return err; - } - } }; const string = []const u8; @@ -793,7 +661,6 @@ const Table = bun.fmt.Table; const Install = bun.install; const DependencyID = Install.DependencyID; const PackageID = Install.PackageID; -const Resolution = Install.Resolution; const invalid_package_id = Install.invalid_package_id; const Behavior = Install.Dependency.Behavior; diff --git a/src/cli/pm_trusted_command.zig b/src/cli/pm_trusted_command.zig index 5ec1807119..90aee43433 100644 --- a/src/cli/pm_trusted_command.zig +++ b/src/cli/pm_trusted_command.zig @@ -343,7 +343,7 @@ pub const TrustCommand = struct { const package_json_contents = try pm.root_package_json_file.readToEndAlloc(ctx.allocator, try pm.root_package_json_file.getEndPos()); defer ctx.allocator.free(package_json_contents); - const package_json_source = logger.Source.initPathString(PackageManager.package_json_cwd, package_json_contents); + const package_json_source = logger.Source.initPathString(PackageManager.root_package_json_path, package_json_contents); var package_json = bun.json.parseUTF8(&package_json_source, ctx.log, ctx.allocator) catch |err| { ctx.log.print(Output.errorWriter()) catch {}; diff --git a/src/cli/update_interactive_command.zig b/src/cli/update_interactive_command.zig index 19939d6117..4aaa36d1a7 100644 --- a/src/cli/update_interactive_command.zig +++ b/src/cli/update_interactive_command.zig @@ -111,17 +111,6 @@ pub const UpdateInteractiveCommand = struct { }; } - fn resolveCatalogDependency(manager: *PackageManager, dep: Install.Dependency) ?Install.Dependency.Version { - return if (dep.version.tag == .catalog) blk: { - const catalog_dep = manager.lockfile.catalogs.get( - manager.lockfile, - dep.version.value.catalog, - dep.name, - ) orelse return null; - break :blk catalog_dep.version; - } else dep.version; - } - pub fn exec(ctx: Command.Context) !void { Output.prettyln("bun update --interactive v" ++ Global.package_json_version_with_sha ++ "", .{}); Output.flush(); @@ -383,7 +372,7 @@ pub const UpdateInteractiveCommand = struct { }; defer bun.default_allocator.free(workspace_pkg_ids); - try OutdatedCommand.updateManifestsIfNecessary(manager, workspace_pkg_ids); + try manager.populateManifestCache(.{ .ids = workspace_pkg_ids }); // Get outdated packages const outdated_packages = try getOutdatedPackages(bun.default_allocator, manager, workspace_pkg_ids); @@ -531,21 +520,13 @@ pub const UpdateInteractiveCommand = struct { try updatePackageJsonFilesFromUpdates(manager, package_updates.items); } - // Get the root package.json from cache (should be updated after our saves) - const package_json_contents = manager.root_package_json_file.readToEndAlloc(ctx.allocator, std.math.maxInt(usize)) catch |err| { - if (manager.options.log_level != .silent) { - Output.prettyErrorln("{s} reading package.json :(", .{@errorName(err)}); - Output.flush(); - } - return; - }; manager.to_update = true; // Reset the timer to show actual install time instead of total command time var install_ctx = ctx; install_ctx.start_time = std.time.nanoTimestamp(); - try PackageManager.installWithManager(manager, install_ctx, package_json_contents, manager.root_dir.dir); + try PackageManager.installWithManager(manager, install_ctx, PackageManager.root_package_json_path, manager.root_dir.dir); } } } @@ -752,8 +733,8 @@ pub const UpdateInteractiveCommand = struct { for (pkg_deps.begin()..pkg_deps.end()) |dep_id| { const package_id = lockfile.buffers.resolutions.items[dep_id]; if (package_id == invalid_package_id) continue; - const dep = lockfile.buffers.dependencies.items[dep_id]; - const resolved_version = resolveCatalogDependency(manager, dep) orelse continue; + const dep = &lockfile.buffers.dependencies.items[dep_id]; + const resolved_version = manager.lockfile.resolveCatalogDependency(dep) orelse continue; if (resolved_version.tag != .npm and resolved_version.tag != .dist_tag) continue; const resolution = pkg_resolutions[package_id]; if (resolution.tag != .npm) continue; @@ -2016,6 +1997,7 @@ const glob = bun.glob; const logger = bun.logger; const path = bun.path; const strings = bun.strings; +const Command = bun.cli.Command; const FileSystem = bun.fs.FileSystem; const Semver = bun.Semver; @@ -2026,9 +2008,6 @@ const JSAst = bun.ast; const E = JSAst.E; const Expr = JSAst.Expr; -const Command = bun.cli.Command; -const OutdatedCommand = bun.cli.OutdatedCommand; - const Install = bun.install; const DependencyID = Install.DependencyID; const PackageID = Install.PackageID; diff --git a/src/install/NetworkTask.zig b/src/install/NetworkTask.zig index db025b7485..4d120ad644 100644 --- a/src/install/NetworkTask.zig +++ b/src/install/NetworkTask.zig @@ -70,6 +70,10 @@ fn countAuth(header_builder: *HeaderBuilder, scope: *const Npm.Registry.Scope) v header_builder.count("npm-auth-type", "legacy"); } +const ForManifestError = OOM || error{ + InvalidURL, +}; + pub fn forManifest( this: *NetworkTask, name: string, @@ -77,7 +81,7 @@ pub fn forManifest( scope: *const Npm.Registry.Scope, loaded_manifest: ?*const Npm.PackageManifest, is_optional: bool, -) !void { +) ForManifestError!void { this.url_buf = blk: { // Not all registries support scoped package names when fetching the manifest. diff --git a/src/install/PackageManager.zig b/src/install/PackageManager.zig index 0dcdd21155..ea1243d845 100644 --- a/src/install/PackageManager.zig +++ b/src/install/PackageManager.zig @@ -701,7 +701,7 @@ pub fn init( const json_buf = try ctx.allocator.alloc(u8, json_stat_size + 64); defer ctx.allocator.free(json_buf); const json_len = try json_file.preadAll(json_buf, 0); - const json_path = try bun.getFdPath(.fromStdFile(json_file), &package_json_cwd_buf); + const json_path = try bun.getFdPath(.fromStdFile(json_file), &root_package_json_path_buf); const json_source = logger.Source.initPathString(json_path, json_buf[0..json_len]); initializeStore(); const json = try JSON.parsePackageJSONUTF8(&json_source, ctx.log, ctx.allocator); @@ -771,7 +771,7 @@ pub fn init( bun.copy(u8, &cwd_buf, fs.top_level_dir); cwd_buf[fs.top_level_dir.len] = 0; fs.top_level_dir = cwd_buf[0..fs.top_level_dir.len :0]; - package_json_cwd = try bun.getFdPath(.fromStdFile(root_package_json_file), &package_json_cwd_buf); + root_package_json_path = try bun.getFdPathZ(.fromStdFile(root_package_json_file), &root_package_json_path_buf); const entries_option = try fs.fs.readDirectory(fs.top_level_dir, null, 0, true); @@ -1108,8 +1108,8 @@ pub fn initWithRuntimeOnce( } } var cwd_buf: bun.PathBuffer = undefined; -pub var package_json_cwd_buf: bun.PathBuffer = undefined; -pub var package_json_cwd: string = ""; +var root_package_json_path_buf: bun.PathBuffer = undefined; +pub var root_package_json_path: [:0]const u8 = ""; // Default to a maximum of 64 simultaneous HTTP requests for bun install if no proxy is specified // if a proxy IS specified, default to 64. We have different values because we might change this in the future. @@ -1243,6 +1243,8 @@ pub const scheduleTasks = @import("./PackageManager/runTasks.zig").scheduleTasks pub const updatePackageJSONAndInstallCatchError = @import("./PackageManager/updatePackageJSONAndInstall.zig").updatePackageJSONAndInstallCatchError; pub const updatePackageJSONAndInstallWithManager = @import("./PackageManager/updatePackageJSONAndInstall.zig").updatePackageJSONAndInstallWithManager; +pub const populateManifestCache = @import("./PackageManager/PopulateManifestCache.zig").populateManifestCache; + const string = []const u8; const stringZ = [:0]const u8; diff --git a/src/install/PackageManager/PackageJSONEditor.zig b/src/install/PackageManager/PackageJSONEditor.zig index 959bc17871..8131e40b98 100644 --- a/src/install/PackageManager/PackageJSONEditor.zig +++ b/src/install/PackageManager/PackageJSONEditor.zig @@ -5,17 +5,6 @@ const dependency_groups = &.{ .{ "peerDependencies", .{ .peer = true } }, }; -fn resolveCatalogDependency(manager: *PackageManager, dep: Dependency) ?Dependency.Version { - return if (dep.version.tag == .catalog) blk: { - const catalog_dep = manager.lockfile.catalogs.get( - manager.lockfile, - dep.version.value.catalog, - dep.name, - ) orelse return null; - break :blk catalog_dep.version; - } else dep.version; -} - pub const EditOptions = struct { exact_versions: bool = false, add_trusted_dependencies: bool = false, @@ -291,7 +280,7 @@ pub fn editUpdateNoArgs( if (manager.updating_packages.fetchSwapRemove(key_str)) |entry| { const is_alias = entry.value.is_alias; const dep_name = entry.key; - for (workspace_deps, workspace_resolution_ids) |workspace_dep, package_id| { + for (workspace_deps, workspace_resolution_ids) |*workspace_dep, package_id| { if (package_id == invalid_package_id) continue; const resolution = resolutions[package_id]; @@ -300,7 +289,7 @@ pub fn editUpdateNoArgs( const workspace_dep_name = workspace_dep.name.slice(string_buf); if (!strings.eqlLong(workspace_dep_name, dep_name, true)) continue; - const resolved_version = resolveCatalogDependency(manager, workspace_dep) orelse workspace_dep.version; + const resolved_version = manager.lockfile.resolveCatalogDependency(workspace_dep) orelse workspace_dep.version; if (resolved_version.npm()) |npm_version| { // It's possible we inserted a dependency that won't update (version is an exact version). // If we find one, skip to keep the original version literal. diff --git a/src/install/PackageManager/PackageManagerEnqueue.zig b/src/install/PackageManager/PackageManagerEnqueue.zig index f21a72adb6..ca778f048a 100644 --- a/src/install/PackageManager/PackageManagerEnqueue.zig +++ b/src/install/PackageManager/PackageManagerEnqueue.zig @@ -1359,7 +1359,6 @@ fn getOrPutResolvedPackageWithFindResult( manifest, find_result.version, find_result.package, - manifest.string_buf, Features.npm, )); diff --git a/src/install/PackageManager/PopulateManifestCache.zig b/src/install/PackageManager/PopulateManifestCache.zig new file mode 100644 index 0000000000..615cd86a73 --- /dev/null +++ b/src/install/PackageManager/PopulateManifestCache.zig @@ -0,0 +1,159 @@ +const StartManifestTaskError = bun.OOM || error{InvalidURL}; +fn startManifestTask(manager: *PackageManager, pkg_name: []const u8, dep: *const Dependency) StartManifestTaskError!void { + const task_id = Task.Id.forManifest(pkg_name); + if (manager.hasCreatedNetworkTask(task_id, dep.behavior.optional)) { + return; + } + manager.startProgressBarIfNone(); + var task = manager.getNetworkTask(); + task.* = .{ + .package_manager = manager, + .callback = undefined, + .task_id = task_id, + .allocator = manager.allocator, + }; + try task.forManifest(pkg_name, manager.allocator, manager.scopeForPackageName(pkg_name), null, dep.behavior.optional); + manager.enqueueNetworkTask(task); +} + +const Packages = union(enum) { + all, + ids: []const PackageID, +}; + +/// Populate the manifest cache for packages included from `root_pkg_ids`. Only manifests of +/// direct dependencies of the `root_pkg_ids` are populated. If `root_pkg_ids` has length 0 +/// all packages in the lockfile will have their manifests fetched if necessary. +pub fn populateManifestCache(manager: *PackageManager, packages: Packages) !void { + const log_level = manager.options.log_level; + const lockfile = manager.lockfile; + const resolutions = lockfile.buffers.resolutions.items; + const dependencies = lockfile.buffers.dependencies.items; + const string_buf = lockfile.buffers.string_bytes.items; + const pkgs = lockfile.packages.slice(); + const pkg_resolutions = pkgs.items(.resolution); + const pkg_names = pkgs.items(.name); + const pkg_dependencies = pkgs.items(.dependencies); + + switch (packages) { + .all => { + var seen_pkg_ids: std.AutoHashMap(PackageID, void) = .init(manager.allocator); + defer seen_pkg_ids.deinit(); + + for (dependencies, 0..) |*dep, _dep_id| { + const dep_id: DependencyID = @intCast(_dep_id); + + const pkg_id = resolutions[dep_id]; + if (pkg_id == invalid_package_id) { + continue; + } + + if ((try seen_pkg_ids.getOrPut(pkg_id)).found_existing) { + continue; + } + + const res = pkg_resolutions[pkg_id]; + if (res.tag != .npm) { + continue; + } + + const pkg_name = pkg_names[pkg_id]; + + _ = manager.manifests.byName( + manager, + manager.scopeForPackageName(pkg_name.slice(string_buf)), + pkg_name.slice(string_buf), + .load_from_memory_fallback_to_disk, + ) orelse { + try startManifestTask(manager, pkg_name.slice(string_buf), dep); + }; + + manager.flushNetworkQueue(); + _ = manager.scheduleTasks(); + } + }, + .ids => |ids| { + for (ids) |root_pkg_id| { + const pkg_deps = pkg_dependencies[root_pkg_id]; + for (pkg_deps.begin()..pkg_deps.end()) |dep_id| { + if (dep_id >= dependencies.len) continue; + const pkg_id = resolutions[dep_id]; + if (pkg_id == invalid_package_id) continue; + const dep = &dependencies[dep_id]; + + const resolution: Resolution = pkg_resolutions[pkg_id]; + if (resolution.tag != .npm) continue; + + const package_name = pkg_names[pkg_id].slice(string_buf); + _ = manager.manifests.byName( + manager, + manager.scopeForPackageName(package_name), + package_name, + .load_from_memory_fallback_to_disk, + ) orelse { + try startManifestTask(manager, package_name, dep); + + manager.flushNetworkQueue(); + _ = manager.scheduleTasks(); + }; + } + } + }, + } + + manager.flushNetworkQueue(); + _ = manager.scheduleTasks(); + + if (manager.pendingTaskCount() > 0) { + const RunClosure = struct { + manager: *PackageManager, + err: ?anyerror = null, + pub fn isDone(closure: *@This()) bool { + closure.manager.runTasks( + *PackageManager, + closure.manager, + .{ + .onExtract = {}, + .onResolve = {}, + .onPackageManifestError = {}, + .onPackageDownloadError = {}, + .progress_bar = true, + .manifests_only = true, + }, + true, + closure.manager.options.log_level, + ) catch |err| { + closure.err = err; + return true; + }; + + return closure.manager.pendingTaskCount() == 0; + } + }; + + var run_closure: RunClosure = .{ .manager = manager }; + manager.sleepUntil(&run_closure, &RunClosure.isDone); + + if (log_level.showProgress()) { + manager.endProgressBar(); + Output.flush(); + } + + if (run_closure.err) |err| { + return err; + } + } +} + +const std = @import("std"); + +const bun = @import("bun"); +const Output = bun.Output; + +const Dependency = bun.install.Dependency; +const DependencyID = bun.install.DependencyID; +const PackageID = bun.install.PackageID; +const PackageManager = bun.install.PackageManager; +const Resolution = bun.install.Resolution; +const Task = bun.install.Task; +const invalid_package_id = bun.install.invalid_package_id; diff --git a/src/install/PackageManager/WorkspacePackageJSONCache.zig b/src/install/PackageManager/WorkspacePackageJSONCache.zig index 2cd7a3b506..3c8abe6d29 100644 --- a/src/install/PackageManager/WorkspacePackageJSONCache.zig +++ b/src/install/PackageManager/WorkspacePackageJSONCache.zig @@ -78,8 +78,6 @@ pub fn getWithPath( }, ) catch |err| { _ = this.map.remove(key); - allocator.free(source.contents); - allocator.free(key); bun.handleErrorReturnTrace(err, @errorReturnTrace()); return .{ .parse_err = err }; }; diff --git a/src/install/PackageManager/install_with_manager.zig b/src/install/PackageManager/install_with_manager.zig index 9047d9f04b..90d016cd8c 100644 --- a/src/install/PackageManager/install_with_manager.zig +++ b/src/install/PackageManager/install_with_manager.zig @@ -1,7 +1,7 @@ pub fn installWithManager( manager: *PackageManager, ctx: Command.Context, - root_package_json_contents: []const u8, + root_package_json_path: [:0]const u8, original_cwd: []const u8, ) !void { const log_level = manager.options.log_level; @@ -49,9 +49,6 @@ pub fn installWithManager( var had_any_diffs = false; manager.progress = .{}; - // Step 2. Parse the package.json file - const root_package_json_source = &logger.Source.initPathString(PackageManager.package_json_cwd, root_package_json_contents); - switch (load_result) { .err => |cause| { if (log_level != .silent) { @@ -139,13 +136,38 @@ pub fn installWithManager( lockfile.initEmpty(manager.allocator); var maybe_root = Lockfile.Package{}; + const root_package_json_entry = switch (manager.workspace_package_json_cache.getWithPath( + manager.allocator, + manager.log, + root_package_json_path, + .{}, + )) { + .entry => |entry| entry, + .read_err => |err| { + if (ctx.log.errors > 0) { + try manager.log.print(Output.errorWriter()); + } + Output.err(err, "failed to read '{s}'", .{root_package_json_path}); + Global.exit(1); + }, + .parse_err => |err| { + if (ctx.log.errors > 0) { + try manager.log.print(Output.errorWriter()); + } + Output.err(err, "failed to parse '{s}'", .{root_package_json_path}); + Global.exit(1); + }, + }; + + const source_copy = root_package_json_entry.source; + var resolver: void = {}; try maybe_root.parse( &lockfile, manager, manager.allocator, manager.log, - root_package_json_source, + &source_copy, void, &resolver, Features.main, @@ -403,13 +425,38 @@ pub fn installWithManager( Global.crash(); } + const root_package_json_entry = switch (manager.workspace_package_json_cache.getWithPath( + manager.allocator, + manager.log, + root_package_json_path, + .{}, + )) { + .entry => |entry| entry, + .read_err => |err| { + if (ctx.log.errors > 0) { + try manager.log.print(Output.errorWriter()); + } + Output.err(err, "failed to read '{s}'", .{root_package_json_path}); + Global.exit(1); + }, + .parse_err => |err| { + if (ctx.log.errors > 0) { + try manager.log.print(Output.errorWriter()); + } + Output.err(err, "failed to parse '{s}'", .{root_package_json_path}); + Global.exit(1); + }, + }; + + const source_copy = root_package_json_entry.source; + var resolver: void = {}; try root.parse( manager.lockfile, manager, manager.allocator, manager.log, - root_package_json_source, + &source_copy, void, &resolver, Features.main, @@ -1046,7 +1093,6 @@ const Output = bun.Output; const Path = bun.path; const Progress = bun.Progress; const default_allocator = bun.default_allocator; -const logger = bun.logger; const strings = bun.strings; const Command = bun.cli.Command; diff --git a/src/install/PackageManager/runTasks.zig b/src/install/PackageManager/runTasks.zig index 7bf423fe61..dd025d2753 100644 --- a/src/install/PackageManager/runTasks.zig +++ b/src/install/PackageManager/runTasks.zig @@ -6,7 +6,7 @@ pub fn runTasks( comptime callbacks: anytype, install_peer: bool, log_level: Options.LogLevel, -) anyerror!void { +) !void { var has_updated_this_run = false; var has_network_error = false; diff --git a/src/install/PackageManager/updatePackageJSONAndInstall.zig b/src/install/PackageManager/updatePackageJSONAndInstall.zig index da973a2e26..f2932e0349 100644 --- a/src/install/PackageManager/updatePackageJSONAndInstall.zig +++ b/src/install/PackageManager/updatePackageJSONAndInstall.zig @@ -272,7 +272,7 @@ fn updatePackageJSONAndInstallWithManagerWithUpdates( const top_level_dir_without_trailing_slash = strings.withoutTrailingSlash(FileSystem.instance.top_level_dir); var root_package_json_path_buf: bun.PathBuffer = undefined; - const root_package_json_source, const root_package_json_path = brk: { + const root_package_json_path = root_package_json_path: { @memcpy(root_package_json_path_buf[0..top_level_dir_without_trailing_slash.len], top_level_dir_without_trailing_slash); @memcpy(root_package_json_path_buf[top_level_dir_without_trailing_slash.len..][0.."/package.json".len], "/package.json"); const root_package_json_path = root_package_json_path_buf[0 .. top_level_dir_without_trailing_slash.len + "/package.json".len]; @@ -334,10 +334,10 @@ fn updatePackageJSONAndInstallWithManagerWithUpdates( root_package_json.source.contents = try manager.allocator.dupe(u8, package_json_writer2.ctx.writtenWithoutTrailingZero()); } - break :brk .{ root_package_json.source.contents, root_package_json_path_buf[0..root_package_json_path.len :0] }; + break :root_package_json_path root_package_json_path_buf[0..root_package_json_path.len :0]; }; - try manager.installWithManager(ctx, root_package_json_source, original_cwd); + try manager.installWithManager(ctx, root_package_json_path, original_cwd); if (subcommand == .update or subcommand == .add or subcommand == .link) { for (updates.*) |request| { @@ -400,10 +400,19 @@ fn updatePackageJSONAndInstallWithManagerWithUpdates( } if (manager.options.do.write_package_json) { - const source, const path = if (manager.options.patch_features == .commit) - .{ root_package_json_source, root_package_json_path } - else - .{ new_package_json_source, manager.original_package_json_path }; + const source, const path = if (manager.options.patch_features == .commit) source_and_path: { + const root_package_json_entry = manager.workspace_package_json_cache.getWithPath( + manager.allocator, + manager.log, + root_package_json_path, + .{}, + ).unwrap() catch |err| { + Output.err(err, "failed to read/parse package.json at '{s}'", .{root_package_json_path}); + Global.exit(1); + }; + + break :source_and_path .{ root_package_json_entry.source.contents, root_package_json_path }; + } else .{ new_package_json_source, manager.original_package_json_path }; // Now that we've run the install step // We can save our in-memory package.json to disk @@ -559,6 +568,7 @@ fn updatePackageJSONAndInstallAndCLI( if (subcommand.canGloballyInstallPackages()) { if (manager.options.global) { if (manager.options.bin_path.len > 0 and manager.track_installed_bin == .basename) { + var path_buf: bun.PathBuffer = undefined; const needs_to_print = if (bun.getenvZ("PATH")) |PATH| // This is not perfect // @@ -582,7 +592,7 @@ fn updatePackageJSONAndInstallAndCLI( // install esbuild, it will not detect that case if we naively // just checked for "esbuild" in $PATH where "$PATH" is /tmp/test bun.which( - &PackageManager.package_json_cwd_buf, + &path_buf, PATH, bun.fs.FileSystem.instance.top_level_dir, manager.track_installed_bin.basename, diff --git a/src/install/bin.zig b/src/install/bin.zig index cb5cb0bc42..9556be99fe 100644 --- a/src/install/bin.zig +++ b/src/install/bin.zig @@ -123,44 +123,6 @@ pub const Bin = extern struct { unreachable; } - pub fn cloneAppend(this: *const Bin, this_buf: string, this_extern_strings: []const ExternalString, lockfile: *Lockfile) OOM!Bin { - var string_buf = lockfile.stringBuf(); - defer string_buf.apply(lockfile); - - const cloned: Bin = .{ - .tag = this.tag, - - .value = switch (this.tag) { - .none => Value.init(.{ .none = {} }), - .file => Value.init(.{ - .file = try string_buf.append(this.value.file.slice(this_buf)), - }), - .named_file => Value.init(.{ .named_file = .{ - try string_buf.append(this.value.named_file[0].slice(this_buf)), - try string_buf.append(this.value.named_file[1].slice(this_buf)), - } }), - .dir => Value.init(.{ - .dir = try string_buf.append(this.value.dir.slice(this_buf)), - }), - .map => map: { - const off = lockfile.buffers.extern_strings.items.len; - for (this.value.map.get(this_extern_strings)) |extern_string| { - try lockfile.buffers.extern_strings.append( - lockfile.allocator, - try string_buf.appendExternal(extern_string.slice(this_buf)), - ); - } - const new = lockfile.buffers.extern_strings.items[off..]; - break :map Value.init(.{ - .map = ExternalStringList.init(lockfile.buffers.extern_strings.items, new), - }); - }, - }, - }; - - return cloned; - } - /// Used for packages read from text lockfile. pub fn parseAppend( allocator: std.mem.Allocator, @@ -1050,7 +1012,6 @@ const std = @import("std"); const Install = @import("./install.zig"); const ExternalStringList = @import("./install.zig").ExternalStringList; -const Lockfile = Install.Lockfile; const bun = @import("bun"); const JSON = bun.json; diff --git a/src/install/dependency.zig b/src/install/dependency.zig index de72d54c5d..e92f49f051 100644 --- a/src/install/dependency.zig +++ b/src/install/dependency.zig @@ -247,6 +247,20 @@ pub inline fn isRemoteTarball(dependency: string) bool { return strings.hasPrefixComptime(dependency, "https://") or strings.hasPrefixComptime(dependency, "http://"); } +pub fn splitVersionAndMaybeName(str: []const u8) struct { []const u8, ?[]const u8 } { + if (strings.indexOfChar(str, '@')) |at_index| { + if (at_index != 0) { + return .{ str[at_index + 1 ..], str[0..at_index] }; + } + + const second_at_index = (strings.indexOfChar(str[1..], '@') orelse return .{ str, null }) + 1; + + return .{ str[second_at_index + 1 ..], str[0..second_at_index] }; + } + + return .{ str, null }; +} + /// Turns `foo@1.1.1` into `foo`, `1.1.1`, or `@foo/bar@1.1.1` into `@foo/bar`, `1.1.1`, or `foo` into `foo`, `null`. pub fn splitNameAndMaybeVersion(str: string) struct { string, ?string } { if (strings.indexOfChar(str, '@')) |at_index| { diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index 2b9b51d96a..ee541146b4 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -544,6 +544,18 @@ pub fn clean( return old.cleanWithLogger(manager, updates, &log, exact_versions, log_level); } +pub fn resolveCatalogDependency(this: *Lockfile, dep: *const Dependency) ?Dependency.Version { + if (dep.version.tag != .catalog) { + return dep.version; + } + + const catalog_dep = this.catalogs.get(this, dep.version.value.catalog, dep.name) orelse { + return null; + }; + + return catalog_dep.version; +} + /// Is this a direct dependency of the workspace root package.json? pub fn isWorkspaceRootDependency(this: *const Lockfile, id: DependencyID) bool { return this.packages.items(.dependencies)[0].contains(id); @@ -939,6 +951,53 @@ const PendingResolution = struct { const PendingResolutions = std.ArrayList(PendingResolution); +pub fn fetchNecessaryPackageMetadataAfterYarnOrPnpmMigration(this: *Lockfile, manager: *PackageManager) OOM!void { + manager.populateManifestCache(.all) catch return; + + const pkgs = this.packages.slice(); + + const pkg_names = pkgs.items(.name); + const pkg_name_hashes = pkgs.items(.name_hash); + const pkg_resolutions = pkgs.items(.resolution); + const pkg_bins = pkgs.items(.bin); + + for (pkg_names, pkg_name_hashes, pkg_resolutions, pkg_bins) |pkg_name, pkg_name_hash, pkg_res, *pkg_bin| { + switch (pkg_res.tag) { + .npm => { + const manifest = manager.manifests.byNameHash( + manager, + manager.scopeForPackageName(pkg_name.slice(this.buffers.string_bytes.items)), + pkg_name_hash, + .load_from_memory_fallback_to_disk, + ) orelse { + continue; + }; + + const pkg = manifest.findByVersion(pkg_res.value.npm.version) orelse { + continue; + }; + + var builder = manager.lockfile.stringBuilder(); + + var bin_extern_strings_count: u32 = 0; + + bin_extern_strings_count += pkg.package.bin.count(manifest.string_buf, manifest.extern_strings_bin_entries, @TypeOf(&builder), &builder); + + try builder.allocate(); + defer builder.clamp(); + + var extern_strings_list = &manager.lockfile.buffers.extern_strings; + try extern_strings_list.ensureUnusedCapacity(manager.lockfile.allocator, bin_extern_strings_count); + extern_strings_list.items.len += bin_extern_strings_count; + const extern_strings = extern_strings_list.items[extern_strings_list.items.len - bin_extern_strings_count ..]; + + pkg_bin.* = pkg.package.bin.clone(manifest.string_buf, manifest.extern_strings_bin_entries, extern_strings_list.items, extern_strings, @TypeOf(&builder), &builder); + }, + else => {}, + } + } +} + pub const Printer = struct { lockfile: *Lockfile, options: PackageManager.Options, diff --git a/src/install/lockfile/CatalogMap.zig b/src/install/lockfile/CatalogMap.zig index e42176b46f..585d32ceab 100644 --- a/src/install/lockfile/CatalogMap.zig +++ b/src/install/lockfile/CatalogMap.zig @@ -243,6 +243,88 @@ pub fn parseAppend( return found_any; } +const FromPnpmLockfileError = OOM || error{InvalidPnpmLockfile}; + +pub fn fromPnpmLockfile( + lockfile: *Lockfile, + allocator: std.mem.Allocator, + log: *logger.Log, + catalogs_obj: *bun.ast.E.Object, + string_buf: *String.Buf, +) FromPnpmLockfileError!void { + for (catalogs_obj.properties.slice()) |prop| { + const group_name_str = prop.key.?.asString(allocator) orelse { + return error.InvalidPnpmLockfile; + }; + + if (!prop.value.?.isObject()) { + continue; + } + + const entries_obj = prop.value.?.data.e_object; + + if (strings.eqlComptime(group_name_str, "default")) { + try putEntriesFromPnpmLockfile(lockfile, allocator, log, &lockfile.catalogs.default, entries_obj, string_buf); + } else { + const group_name = try string_buf.append(group_name_str); + const group = try lockfile.catalogs.getOrPutGroup(lockfile, group_name); + try putEntriesFromPnpmLockfile(lockfile, allocator, log, group, entries_obj, string_buf); + } + } +} + +fn putEntriesFromPnpmLockfile( + lockfile: *Lockfile, + allocator: std.mem.Allocator, + log: *logger.Log, + catalog_map: *Map, + entries_obj: *bun.ast.E.Object, + string_buf: *String.Buf, +) FromPnpmLockfileError!void { + for (entries_obj.properties.slice()) |entry_prop| { + const dep_name_str = entry_prop.key.?.asString(allocator) orelse { + return error.InvalidPnpmLockfile; + }; + const dep_name_hash = String.Builder.stringHash(dep_name_str); + const dep_name = try string_buf.appendWithHash(dep_name_str, dep_name_hash); + + const version_str, _ = try entry_prop.value.?.getString(allocator, "specifier") orelse { + return error.InvalidPnpmLockfile; + }; + const version_hash = String.Builder.stringHash(version_str); + const version = try string_buf.appendWithHash(version_str, version_hash); + const version_sliced = version.sliced(string_buf.bytes.items); + + const dep: Dependency = .{ + .name = dep_name, + .name_hash = dep_name_hash, + .version = Dependency.parse( + allocator, + dep_name, + dep_name_hash, + version_sliced.slice, + &version_sliced, + log, + null, + ) orelse { + return error.InvalidPnpmLockfile; + }, + }; + + const entry = try catalog_map.getOrPutContext( + allocator, + dep_name, + String.arrayHashContext(lockfile, null), + ); + + if (entry.found_existing) { + return error.InvalidPnpmLockfile; + } + + entry.value_ptr.* = dep; + } +} + pub fn sort(this: *CatalogMap, lockfile: *const Lockfile) void { const DepSortCtx = struct { buf: string, @@ -380,6 +462,7 @@ const Allocator = std.mem.Allocator; const bun = @import("bun"); const OOM = bun.OOM; const logger = bun.logger; +const strings = bun.strings; const Expr = bun.ast.Expr; const String = bun.Semver.String; diff --git a/src/install/lockfile/Package.zig b/src/install/lockfile/Package.zig index fd016ef2d4..c015adfde6 100644 --- a/src/install/lockfile/Package.zig +++ b/src/install/lockfile/Package.zig @@ -307,7 +307,6 @@ pub fn Package(comptime SemverIntType: type) type { manifest: *const Npm.PackageManifest, version: Semver.Version, package_version_ptr: *const Npm.PackageVersion, - string_buf: []const u8, comptime features: Features, ) !@This() { var package = @This(){}; @@ -353,7 +352,7 @@ pub fn Package(comptime SemverIntType: type) type { // --- Counting { string_builder.count(manifest.name()); - version.count(string_buf, @TypeOf(&string_builder), &string_builder); + version.count(manifest.string_buf, @TypeOf(&string_builder), &string_builder); inline for (dependency_groups) |group| { const map: ExternalStringMap = @field(package_version, group.field); @@ -364,12 +363,12 @@ pub fn Package(comptime SemverIntType: type) type { if (comptime Environment.isDebug) assert(keys.len == version_strings.len); for (keys, version_strings) |key, ver| { - string_builder.count(key.slice(string_buf)); - string_builder.count(ver.slice(string_buf)); + string_builder.count(key.slice(manifest.string_buf)); + string_builder.count(ver.slice(manifest.string_buf)); } } - bin_extern_strings_count = package_version.bin.count(string_buf, manifest.extern_strings_bin_entries, @TypeOf(&string_builder), &string_builder); + bin_extern_strings_count = package_version.bin.count(manifest.string_buf, manifest.extern_strings_bin_entries, @TypeOf(&string_builder), &string_builder); } string_builder.count(manifest.str(&package_version_ptr.tarball_url)); @@ -436,8 +435,8 @@ pub fn Package(comptime SemverIntType: type) type { } } - const name: ExternalString = string_builder.appendWithHash(ExternalString, key.slice(string_buf), key.hash); - const dep_version = string_builder.appendWithHash(String, version_string_.slice(string_buf), version_string_.hash); + const name: ExternalString = string_builder.appendWithHash(ExternalString, key.slice(manifest.string_buf), key.hash); + const dep_version = string_builder.appendWithHash(String, version_string_.slice(manifest.string_buf), version_string_.hash); const sliced = dep_version.sliced(lockfile.buffers.string_bytes.items); var behavior = group.behavior; @@ -488,7 +487,7 @@ pub fn Package(comptime SemverIntType: type) type { } } - package.bin = package_version.bin.clone(string_buf, manifest.extern_strings_bin_entries, extern_strings_list.items, extern_strings_slice, @TypeOf(&string_builder), &string_builder); + package.bin = package_version.bin.clone(manifest.string_buf, manifest.extern_strings_bin_entries, extern_strings_list.items, extern_strings_slice, @TypeOf(&string_builder), &string_builder); package.meta.arch = package_version.cpu; package.meta.os = package_version.os; @@ -2119,7 +2118,7 @@ pub fn Package(comptime SemverIntType: type) type { try list_for_migrating_from_v2.ensureTotalCapacity(allocator, list_len); list_for_migrating_from_v2.len = list_len; - try loadFields(stream, OldPackageV2.List, &list_for_migrating_from_v2, &needs_update); + try loadFields(stream, end_at, OldPackageV2.List, &list_for_migrating_from_v2, &needs_update); for (0..list_for_migrating_from_v2.len) |_pkg_id| { const pkg_id: PackageID = @intCast(_pkg_id); @@ -2152,7 +2151,7 @@ pub fn Package(comptime SemverIntType: type) type { } } else { list.len = list_len; - try loadFields(stream, List, &list, &needs_update); + try loadFields(stream, end_at, List, &list, &needs_update); } return .{ @@ -2161,7 +2160,7 @@ pub fn Package(comptime SemverIntType: type) type { }; } - fn loadFields(stream: *Stream, comptime ListType: type, list: *ListType, needs_update: *bool) !void { + fn loadFields(stream: *Stream, end_at: u64, comptime ListType: type, list: *ListType, needs_update: *bool) !void { var sliced = list.slice(); inline for (FieldsEnum.fields) |field| { @@ -2170,7 +2169,7 @@ pub fn Package(comptime SemverIntType: type) type { comptime assertNoUninitializedPadding(@TypeOf(value)); const bytes = std.mem.sliceAsBytes(value); const end_pos = stream.pos + bytes.len; - if (end_pos <= end_pos) { + if (end_pos <= end_at) { @memcpy(bytes, stream.buffer[stream.pos..][0..bytes.len]); stream.pos = end_pos; if (comptime strings.eqlComptime(field.name, "meta")) { diff --git a/src/install/migration.zig b/src/install/migration.zig index 36e2d8e1e3..2390d4e850 100644 --- a/src/install/migration.zig +++ b/src/install/migration.zig @@ -84,6 +84,85 @@ pub fn detectAndLoadOtherLockfile( return migrate_result; } + pnpm: { + var timer = std.time.Timer.start() catch unreachable; + const lockfile = File.openat(dir, "pnpm-lock.yaml", bun.O.RDONLY, 0).unwrap() catch break :pnpm; + defer lockfile.close(); + const data = lockfile.readToEnd(allocator).unwrap() catch break :pnpm; + const migrate_result = @import("./pnpm.zig").migratePnpmLockfile(this, manager, allocator, log, data, dir) catch |err| { + switch (err) { + error.PnpmLockfileTooOld => { + Output.prettyErrorln( + \\warning: pnpm-lock.yaml version is too old (\< v7) + \\ + \\Please upgrade using 'pnpm install --lockfile-only' first, then try again. + , .{}); + }, + error.NonExistentWorkspaceDependency => { + Output.warn("Workspace link dependencies to non-existent folders aren't supported yet in pnpm-lock.yaml migration. Please follow along at https://github.com/oven-sh/bun/issues/23026", .{}); + }, + error.RelativeLinkDependency => { + Output.warn("Relative link dependencies aren't supported yet. Please follow along at https://github.com/oven-sh/bun/issues/23026", .{}); + }, + error.WorkspaceNameMissing => { + if (log.hasErrors()) { + log.print(Output.errorWriter()) catch {}; + } + Output.warn("pnpm-lock.yaml migration failed due to missing workspace name.", .{}); + }, + error.YamlParseError => { + if (log.hasErrors()) { + log.print(Output.errorWriter()) catch {}; + } + Output.warn("Failed to parse pnpm-lock.yaml.", .{}); + }, + error.PnpmLockfileNotObject, + error.PnpmLockfileMissingVersion, + error.PnpmLockfileVersionInvalid, + error.PnpmLockfileMissingImporters, + error.PnpmLockfileMissingRootPackage, + error.PnpmLockfileInvalidSnapshot, + error.PnpmLockfileInvalidDependency, + error.PnpmLockfileMissingDependencyVersion, + error.PnpmLockfileInvalidOverride, + error.PnpmLockfileInvalidPatchedDependency, + error.PnpmLockfileMissingCatalogEntry, + error.PnpmLockfileUnresolvableDependency, + => { + // These errors are continuable - log the error but don't exit + // The install will continue with a fresh install instead of migration + if (log.hasErrors()) { + log.print(Output.errorWriter()) catch {}; + } + }, + else => {}, + } + if (Environment.isDebug) { + bun.handleErrorReturnTrace(err, @errorReturnTrace()); + + Output.prettyErrorln("Error: {s}", .{@errorName(err)}); + log.print(Output.errorWriter()) catch {}; + Output.prettyErrorln("Invalid pnpm-lock.yaml\nIn a release build, this would ignore and do a fresh install.\nAborting", .{}); + Global.exit(1); + } + return LoadResult{ .err = .{ + .step = .migrating, + .value = err, + .lockfile_path = "pnpm-lock.yaml", + .format = .binary, + } }; + }; + + if (migrate_result == .ok) { + Output.printElapsed(@as(f64, @floatFromInt(timer.read())) / std.time.ns_per_ms); + Output.prettyError(" ", .{}); + Output.prettyErrorln("migrated lockfile from pnpm-lock.yaml", .{}); + Output.flush(); + } + + return migrate_result; + } + return LoadResult{ .not_found = {} }; } diff --git a/src/install/pnpm.zig b/src/install/pnpm.zig new file mode 100644 index 0000000000..024463e137 --- /dev/null +++ b/src/install/pnpm.zig @@ -0,0 +1,1580 @@ +/// returns { peersIndex, patchHashIndex } +/// https://github.com/pnpm/pnpm/blob/102d5a01ddabda1184b88119adccfbe956d30579/packages/dependency-path/src/index.ts#L9-L31 +fn indexOfDepPathSuffix(path: []const u8) struct { ?usize, ?usize } { + if (path.len < 2) { + return .{ null, null }; + } + + if (path[path.len - 1] != ')') { + return .{ null, null }; + } + + var open: i64 = 1; + var i = path.len - 1; + while (i > 0) { + i -= 1; + + if (path[i] == '(') { + open -= 1; + } else if (path[i] == ')') { + open += 1; + } else if (open == 0) { + if (strings.startsWith(path[i + 1 ..], "(patch_hash=")) { + const peers_idx = if (strings.indexOfChar(path[i + 2 ..], '(')) |idx| + idx + i + 2 + else + null; + + return .{ peers_idx, i + 1 }; + } + return .{ i + 1, null }; + } + } + return .{ null, null }; +} + +/// name@version(hash) -> name@version +/// version(hash) -> version +/// https://github.com/pnpm/pnpm/blob/102d5a01ddabda1184b88119adccfbe956d30579/packages/dependency-path/src/index.ts#L52-L61 +fn removeSuffix(path: []const u8) []const u8 { + const peers_idx, const patch_hash_idx = indexOfDepPathSuffix(path); + + if (patch_hash_idx orelse peers_idx) |idx| { + return path[0..idx]; + } + + return path; +} + +const MigratePnpmLockfileError = OOM || error{ + PnpmLockfileTooOld, + PnpmLockfileVersionInvalid, + InvalidPnpmLockfile, + YamlParseError, + NonExistentWorkspaceDependency, + RelativeLinkDependency, + WorkspaceNameMissing, + DependencyLoop, + PnpmLockfileNotObject, + PnpmLockfileMissingVersion, + PnpmLockfileMissingImporters, + PnpmLockfileInvalidImporter, + PnpmLockfileMissingRootPackage, + PnpmLockfileInvalidSnapshot, + PnpmLockfileInvalidPackage, + PnpmLockfileMissingDependencyVersion, + PnpmLockfileInvalidDependency, + PnpmLockfileInvalidOverride, + PnpmLockfileInvalidPatchedDependency, + PnpmLockfileMissingCatalogEntry, + PnpmLockfileUnresolvableDependency, +}; + +pub fn migratePnpmLockfile( + lockfile: *Lockfile, + manager: *PackageManager, + allocator: std.mem.Allocator, + log: *logger.Log, + data: []const u8, + dir: bun.FD, +) MigratePnpmLockfileError!LoadResult { + var buf: std.ArrayList(u8) = .init(allocator); + defer buf.deinit(); + + lockfile.initEmpty(allocator); + bun.install.initializeStore(); + bun.analytics.Features.pnpm_migration += 1; + + var yaml_arena = bun.ArenaAllocator.init(allocator); + defer yaml_arena.deinit(); + + const yaml_source = &logger.Source.initPathString("pnpm-lock.yaml", data); + const _root = YAML.parse(yaml_source, log, yaml_arena.allocator()) catch { + return error.YamlParseError; + }; + + const root = try _root.deepClone(allocator); + + if (root.data != .e_object) { + try log.addErrorFmt(null, logger.Loc.Empty, allocator, "pnpm-lock.yaml root must be an object, got {s}", .{@tagName(root.data)}); + return error.PnpmLockfileNotObject; + } + + const lockfile_version_expr = root.get("lockfileVersion") orelse { + try log.addError(null, logger.Loc.Empty, "pnpm-lock.yaml missing 'lockfileVersion' field"); + return error.PnpmLockfileMissingVersion; + }; + + const lockfile_version_num: u32 = lockfile_version: { + err: { + switch (lockfile_version_expr.data) { + .e_number => |num| { + if (num.value < 0 or num.value > std.math.maxInt(u32)) { + break :err; + } + + break :lockfile_version @intFromFloat(std.math.divExact(f64, num.value, 1) catch break :err); + }, + .e_string => |version_str| { + const str = version_str.slice(allocator); + + const end = strings.indexOfChar(str, '.') orelse str.len; + break :lockfile_version std.fmt.parseUnsigned(u32, str[0..end], 10) catch break :err; + }, + else => {}, + } + } + + try log.addErrorFmt(null, logger.Loc.Empty, allocator, "pnpm-lock.yaml 'lockfileVersion' must be a number or string, got {s}", .{@tagName(lockfile_version_expr.data)}); + return error.PnpmLockfileVersionInvalid; + }; + + if (lockfile_version_num < 7) { + return error.PnpmLockfileTooOld; + } + + var found_patches: bun.StringArrayHashMap([]const u8) = .init(allocator); + defer found_patches.deinit(); + + const pkg_map, const importer_dep_res_versions, const workspace_pkgs_off, const workspace_pkgs_end = build: { + var string_buf = lockfile.stringBuf(); + + if (root.getObject("catalogs")) |catalogs_expr| { + try Lockfile.CatalogMap.fromPnpmLockfile(lockfile, allocator, log, catalogs_expr.data.e_object, &string_buf); + } + + if (root.getObject("overrides")) |overrides_expr| { + for (overrides_expr.data.e_object.properties.slice()) |prop| { + const key = prop.key.?; + const value = prop.value.?; + + const name_str = key.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + const name_hash = String.Builder.stringHash(name_str); + const name = try string_buf.appendWithHash(name_str, name_hash); + + if (!value.isString()) { + // TODO: + return invalidPnpmLockfile(); + } + + const version_str = value.asString(allocator).?; + const version_hash = String.Builder.stringHash(version_str); + const version = try string_buf.appendWithHash(version_str, version_hash); + const version_sliced = version.sliced(string_buf.bytes.items); + + const dep: Dependency = .{ + .name = name, + .name_hash = name_hash, + .version = Dependency.parse( + allocator, + name, + name_hash, + version_sliced.slice, + &version_sliced, + log, + manager, + ) orelse { + return invalidPnpmLockfile(); + }, + }; + + try lockfile.overrides.map.put(allocator, name_hash, dep); + } + } + + const Patch = struct { + path: String, + dep_name: []const u8, + }; + var patches: bun.StringArrayHashMap(Patch) = .init(allocator); + defer patches.deinit(); + var patch_join_buf: std.ArrayList(u8) = .init(allocator); + defer patch_join_buf.deinit(); + + if (root.getObject("patchedDependencies")) |patched_dependencies_expr| { + for (patched_dependencies_expr.data.e_object.properties.slice()) |prop| { + const dep_name_expr = prop.key.?; + const value = prop.value.?; + + const dep_name_str = dep_name_expr.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + const path_str, _ = try value.getString(allocator, "path") orelse { + return invalidPnpmLockfile(); + }; + + const hash_str, _ = try value.getString(allocator, "hash") orelse { + return invalidPnpmLockfile(); + }; + + const entry = try patches.getOrPut(hash_str); + if (entry.found_existing) { + return invalidPnpmLockfile(); + } + entry.value_ptr.* = .{ + .path = try string_buf.append(path_str), + .dep_name = dep_name_str, + }; + } + } + + const importers_obj = root.getObject("importers") orelse { + try log.addError(null, logger.Loc.Empty, "pnpm-lock.yaml missing 'importers' field"); + return error.PnpmLockfileMissingImporters; + }; + + var has_root_pkg_expr: ?Expr = null; + + for (importers_obj.data.e_object.properties.slice()) |prop| { + const importer_path = prop.key.?.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + const value = prop.value.?; + + if (strings.eqlComptime(importer_path, ".")) { + if (has_root_pkg_expr != null) { + return invalidPnpmLockfile(); + } + has_root_pkg_expr = value; + continue; + } + + var pkg_json_path: bun.AbsPath(.{ .sep = .auto }) = .initTopLevelDir(); + defer pkg_json_path.deinit(); + + pkg_json_path.append(importer_path); + pkg_json_path.append("package.json"); + + const importer_pkg_json = manager.workspace_package_json_cache.getWithPath(allocator, log, pkg_json_path.slice(), .{}).unwrap() catch { + return invalidPnpmLockfile(); + }; + + const workspace_root = importer_pkg_json.root; + + const name, _ = try workspace_root.getString(allocator, "name") orelse { + // we require workspace names. + return error.WorkspaceNameMissing; + }; + + const name_hash = String.Builder.stringHash(name); + + try lockfile.workspace_paths.put(allocator, name_hash, try string_buf.append(importer_path)); + + if (value.get("version")) |version_expr| { + const version_str = try string_buf.append(version_expr.asString(allocator) orelse { + return invalidPnpmLockfile(); + }); + + const parsed = Semver.Version.parse(version_str.sliced(string_buf.bytes.items)); + if (!parsed.valid) { + return invalidPnpmLockfile(); + } + + try lockfile.workspace_versions.put(allocator, name_hash, parsed.version.min()); + } + } + + const root_pkg_expr = has_root_pkg_expr orelse { + try log.addError(null, logger.Loc.Empty, "pnpm-lock.yaml missing root package entry (importers['.'])"); + return error.PnpmLockfileMissingRootPackage; + }; + + var importer_dep_res_versions: bun.StringArrayHashMap(bun.StringArrayHashMap([]const u8)) = .init(allocator); + + { + var pkg_json_path: bun.AbsPath(.{ .sep = .auto }) = .initTopLevelDir(); + defer pkg_json_path.deinit(); + + pkg_json_path.append("package.json"); + + const pkg_json = manager.workspace_package_json_cache.getWithPath(allocator, log, pkg_json_path.slice(), .{}).unwrap() catch { + return invalidPnpmLockfile(); + }; + + var root_pkg: Lockfile.Package = .{}; + + if (try pkg_json.root.getString(allocator, "name")) |name_info| { + const name, _ = name_info; + const name_hash = String.Builder.stringHash(name); + root_pkg.name = try string_buf.appendWithHash(name, name_hash); + root_pkg.name_hash = name_hash; + } + + const importer_versions = try importer_dep_res_versions.getOrPut("."); + importer_versions.value_ptr.* = .init(allocator); + + const off, const len = try parseAppendImporterDependencies( + lockfile, + manager, + allocator, + &root_pkg_expr, + &string_buf, + log, + true, + &importers_obj, + importer_versions.value_ptr, + ); + + root_pkg.dependencies = .{ .off = off, .len = len }; + root_pkg.resolutions = .{ .off = off, .len = len }; + + root_pkg.meta.id = 0; + root_pkg.resolution = .init(.{ .root = {} }); + try lockfile.packages.append(allocator, root_pkg); + try lockfile.getOrPutID(0, root_pkg.name_hash); + } + + var pkg_map: bun.StringArrayHashMap(PackageID) = .init(allocator); + + const workspace_pkgs_off = lockfile.packages.len; + + workspaces: for (lockfile.workspace_paths.values()) |workspace_path| { + for (importers_obj.data.e_object.properties.slice()) |prop| { + const key = prop.key.?; + const value = prop.value.?; + + const path = key.asString(allocator).?; + if (!strings.eqlLong(path, workspace_path.slice(string_buf.bytes.items), true)) { + continue; + } + + var pkg: Lockfile.Package = .{}; + + pkg.resolution = .{ + .tag = .workspace, + .value = .{ .workspace = try string_buf.append(path) }, + }; + + var path_buf: bun.AbsPath(.{ .sep = .auto }) = .initTopLevelDir(); + defer path_buf.deinit(); + + path_buf.append(path); + const abs_path = try allocator.dupe(u8, path_buf.slice()); + path_buf.append("package.json"); + + const workspace_pkg_json = manager.workspace_package_json_cache.getWithPath(allocator, log, path_buf.slice(), .{}).unwrap() catch { + return invalidPnpmLockfile(); + }; + + const workspace_root = workspace_pkg_json.root; + + const name = workspace_root.get("name").?.asString(allocator).?; + const name_hash = String.Builder.stringHash(name); + + pkg.name = try string_buf.appendWithHash(name, name_hash); + pkg.name_hash = name_hash; + + const importer_versions = try importer_dep_res_versions.getOrPut(path); + if (importer_versions.found_existing) { + return invalidPnpmLockfile(); + } + importer_versions.value_ptr.* = .init(allocator); + + const off, const len = try parseAppendImporterDependencies( + lockfile, + manager, + allocator, + &value, + &string_buf, + log, + false, + &importers_obj, + importer_versions.value_ptr, + ); + + pkg.dependencies = .{ .off = off, .len = len }; + pkg.resolutions = .{ .off = off, .len = len }; + + if (workspace_root.get("bin")) |bin_expr| { + pkg.bin = try Bin.parseAppend(allocator, bin_expr, &string_buf, &lockfile.buffers.extern_strings); + } else if (workspace_root.get("directories")) |directories_expr| { + if (directories_expr.get("bin")) |bin_expr| { + pkg.bin = try Bin.parseAppendFromDirectories(allocator, bin_expr, &string_buf); + } + } + + const pkg_id = try lockfile.appendPackageDedupe(&pkg, string_buf.bytes.items); + + const entry = try pkg_map.getOrPut(abs_path); + if (entry.found_existing) { + return invalidPnpmLockfile(); + } + + entry.value_ptr.* = pkg_id; + + continue :workspaces; + } + } + + const workspace_pkgs_end = lockfile.packages.len; + + // add packages for symlink dependencies. pnpm-lock does not add an entry + // for these dependencies in packages/snapshots + for (0..workspace_pkgs_end) |_pkg_id| { + const pkg_id: PackageID = @intCast(_pkg_id); + + const workspace_path = if (pkg_id == 0) "." else workspace_path: { + const workspace_res = lockfile.packages.items(.resolution)[pkg_id]; + break :workspace_path workspace_res.value.workspace.slice(string_buf.bytes.items); + }; + + const importer_versions = importer_dep_res_versions.get(workspace_path) orelse { + return invalidPnpmLockfile(); + }; + + const deps = lockfile.packages.items(.dependencies)[pkg_id]; + next_dep: for (deps.begin()..deps.end()) |_dep_id| { + const dep_id: DependencyID = @intCast(_dep_id); + + const dep = &lockfile.buffers.dependencies.items[dep_id]; + + if (dep.behavior.isWorkspace()) { + continue; + } + + switch (dep.version.tag) { + .folder, .workspace => { + const version_str = importer_versions.get(dep.name.slice(string_buf.bytes.items)) orelse { + return invalidPnpmLockfile(); + }; + const version_without_suffix = removeSuffix(version_str); + + if (strings.withoutPrefixIfPossibleComptime(version_without_suffix, "link:")) |link_path| { + // create a link package for the workspace dependency only if it doesn't already exist + if (dep.version.tag == .workspace) { + var link_path_buf: bun.AbsPath(.{ .sep = .auto }) = .initTopLevelDir(); + defer link_path_buf.deinit(); + link_path_buf.append(workspace_path); + link_path_buf.join(&.{link_path}); + + for (lockfile.workspace_paths.values()) |existing_workspace_path| { + var workspace_path_buf: bun.AbsPath(.{ .sep = .auto }) = .initTopLevelDir(); + defer workspace_path_buf.deinit(); + workspace_path_buf.append(existing_workspace_path.slice(string_buf.bytes.items)); + + if (strings.eqlLong(workspace_path_buf.slice(), link_path_buf.slice(), true)) { + continue :next_dep; + } + } + + return error.NonExistentWorkspaceDependency; + } + + var pkg: Lockfile.Package = .{ + .name = dep.name, + .name_hash = dep.name_hash, + .resolution = .init(.{ .symlink = try string_buf.append(link_path) }), + }; + + var abs_link_path: bun.AbsPath(.{ .sep = .auto }) = .initTopLevelDir(); + defer abs_link_path.deinit(); + + abs_link_path.join(&.{ workspace_path, link_path }); + + const pkg_entry = try pkg_map.getOrPut(abs_link_path.slice()); + if (pkg_entry.found_existing) { + // they point to the same package + continue; + } + + pkg_entry.value_ptr.* = try lockfile.appendPackageDedupe(&pkg, string_buf.bytes.items); + } + }, + .symlink => { + if (!strings.isNPMPackageName(dep.version.value.symlink.slice(string_buf.bytes.items))) { + try log.addWarningFmt(null, .Empty, allocator, "relative link dependency not supported: {s}@{s}\n", .{ + dep.name.slice(string_buf.bytes.items), + dep.version.literal.slice(string_buf.bytes.items), + }); + return error.RelativeLinkDependency; + } + }, + else => {}, + } + } + } + + const SnapshotEntry = struct { + obj: Expr, + }; + var snapshots: bun.StringArrayHashMap(SnapshotEntry) = .init(allocator); + defer snapshots.deinit(); + + if (root.getObject("packages")) |packages_obj| { + const snapshots_obj = root.getObject("snapshots") orelse { + try log.addError(null, logger.Loc.Empty, "pnpm-lock.yaml has 'packages' but missing 'snapshots' field"); + return error.PnpmLockfileInvalidSnapshot; + }; + + for (snapshots_obj.data.e_object.properties.slice()) |snapshot_prop| { + const key = snapshot_prop.key.?; + const value = snapshot_prop.value.?; + + const key_str = key.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + if (!value.isObject()) { + return invalidPnpmLockfile(); + } + + const peer_hash_idx, const patch_hash_idx = indexOfDepPathSuffix(key_str); + + const key_str_without_suffix = if (patch_hash_idx orelse peer_hash_idx) |idx| key_str[0..idx] else key_str; + + if (patch_hash_idx) |idx| try_patch: { + const patch_hash_str = key_str[idx + "(patch_hash=".len ..]; + const end_idx = strings.indexOfChar(patch_hash_str, ')') orelse { + return invalidPnpmLockfile(); + }; + const patch = patches.fetchSwapRemove(patch_hash_str[0..end_idx]) orelse { + break :try_patch; + }; + + _, const res_str = Dependency.splitNameAndVersion(key_str_without_suffix) catch { + return invalidPnpmLockfile(); + }; + + try found_patches.put(patch.value.dep_name, res_str); + + patch_join_buf.clearRetainingCapacity(); + try patch_join_buf.writer().print("{s}@{s}", .{ + patch.value.dep_name, + res_str, + }); + + const patch_hash = String.Builder.stringHash(patch_join_buf.items); + try lockfile.patched_dependencies.put(allocator, patch_hash, .{ .path = patch.value.path }); + } + + const entry = try snapshots.getOrPut(key_str_without_suffix); + if (entry.found_existing) { + continue; + } + + entry.value_ptr.* = .{ .obj = value }; + } + + for (packages_obj.data.e_object.properties.slice()) |packages_prop| { + const key = packages_prop.key.?; + const package_obj = packages_prop.value.?; + + const key_str = key.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + if (!package_obj.isObject()) { + return invalidPnpmLockfile(); + } + + const snapshot = snapshots.get(key_str) orelse { + try log.addErrorFmt(null, logger.Loc.Empty, allocator, "pnpm-lock.yaml package '{s}' missing corresponding snapshot entry", .{key_str}); + return error.PnpmLockfileInvalidSnapshot; + }; + + const name_str, const res_str = Dependency.splitNameAndVersion(key_str) catch { + return invalidPnpmLockfile(); + }; + + const name_hash = String.Builder.stringHash(name_str); + const name = try string_buf.appendWithHash(name_str, name_hash); + + var res = try Resolution.fromPnpmLockfile(res_str, &string_buf); + + if (res.tag == .npm) { + const scope = manager.scopeForPackageName(name_str); + const url = try ExtractTarball.buildURL( + scope.url.href, + strings.StringOrTinyString.init(name.slice(string_buf.bytes.items)), + res.value.npm.version, + string_buf.bytes.items, + ); + res.value.npm.url = try string_buf.append(url); + } + + var pkg: Lockfile.Package = .{ + .name = name, + .name_hash = name_hash, + }; + + if (package_obj.get("resolution")) |res_expr| { + if (!res_expr.isObject()) { + return invalidPnpmLockfile(); + } + + if (res_expr.get("integrity")) |integrity_expr| { + const integrity_str = integrity_expr.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + pkg.meta.integrity = Integrity.parse(integrity_str); + } + } + + if (package_obj.get("os")) |os_expr| { + pkg.meta.os = try Negatable(Npm.OperatingSystem).fromJson(allocator, os_expr); + } + if (package_obj.get("cpu")) |cpu_expr| { + pkg.meta.arch = try Negatable(Npm.Architecture).fromJson(allocator, cpu_expr); + } + // TODO: libc + // if (package_obj.get("libc")) |libc_expr| { + // pkg.meta.libc = try Negatable(Npm.Libc).fromJson(allocator, libc_expr); + // } + + const off, const len = try parseAppendPackageDependencies( + lockfile, + allocator, + &package_obj, + &snapshot.obj, + &string_buf, + log, + ); + + pkg.dependencies = .{ .off = off, .len = len }; + pkg.resolutions = .{ .off = off, .len = len }; + pkg.resolution = res.copy(); + + const pkg_id = try lockfile.appendPackageDedupe(&pkg, string_buf.bytes.items); + + const entry = try pkg_map.getOrPut(key_str); + if (entry.found_existing) { + return invalidPnpmLockfile(); + } + + entry.value_ptr.* = pkg_id; + } + } + + break :build .{ + pkg_map, + importer_dep_res_versions, + workspace_pkgs_off, + workspace_pkgs_end, + }; + }; + + const string_buf = lockfile.buffers.string_bytes.items; + + var res_buf: std.ArrayList(u8) = .init(allocator); + defer res_buf.deinit(); + + try lockfile.buffers.resolutions.ensureTotalCapacityPrecise(allocator, lockfile.buffers.dependencies.items.len); + lockfile.buffers.resolutions.expandToCapacity(); + @memset(lockfile.buffers.resolutions.items, invalid_package_id); + + const pkgs = lockfile.packages.slice(); + const pkg_deps = pkgs.items(.dependencies); + const pkg_names = pkgs.items(.name); + _ = pkg_names; + const pkg_resolutions = pkgs.items(.resolution); + + { + const importer_versions = importer_dep_res_versions.get(".") orelse { + return invalidPnpmLockfile(); + }; + + // resolve root dependencies first + for (pkg_deps[0].begin()..pkg_deps[0].end()) |_dep_id| { + const dep_id: DependencyID = @intCast(_dep_id); + const dep = &lockfile.buffers.dependencies.items[dep_id]; + + // implicit workspace dependencies + if (dep.behavior.isWorkspace()) { + const workspace_path = dep.version.value.workspace.slice(string_buf); + var path_buf: bun.AbsPath(.{ .sep = .auto }) = .initTopLevelDir(); + defer path_buf.deinit(); + path_buf.join(&.{workspace_path}); + if (pkg_map.get(path_buf.slice())) |workspace_pkg_id| { + lockfile.buffers.resolutions.items[dep_id] = workspace_pkg_id; + continue; + } + } + + const dep_name = dep.name.slice(string_buf); + var version_maybe_alias = importer_versions.get(dep_name) orelse { + try log.addErrorFmt(null, logger.Loc.Empty, allocator, "pnpm-lock.yaml cannot resolve root dependency '{s}' - missing version in importer", .{dep_name}); + return error.PnpmLockfileUnresolvableDependency; + }; + if (strings.hasPrefixComptime(version_maybe_alias, "npm:")) { + version_maybe_alias = version_maybe_alias["npm:".len..]; + } + const version, const has_alias = Dependency.splitVersionAndMaybeName(version_maybe_alias); + const version_without_suffix = removeSuffix(version); + + if (strings.withoutPrefixIfPossibleComptime(version_without_suffix, "link:")) |maybe_symlink_or_folder_or_workspace_path| { + var path_buf: bun.AbsPath(.{ .sep = .auto }) = .initTopLevelDir(); + defer path_buf.deinit(); + path_buf.join(&.{maybe_symlink_or_folder_or_workspace_path}); + if (pkg_map.get(path_buf.slice())) |pkg_id| { + lockfile.buffers.resolutions.items[dep_id] = pkg_id; + continue; + } + } + + res_buf.clearRetainingCapacity(); + try res_buf.writer().print("{s}@{s}", .{ + if (has_alias) |alias| alias else dep_name, + version_without_suffix, + }); + + const pkg_id = pkg_map.get(res_buf.items) orelse { + return invalidPnpmLockfile(); + }; + + lockfile.buffers.resolutions.items[dep_id] = pkg_id; + } + } + + for (workspace_pkgs_off..workspace_pkgs_end) |_pkg_id| { + const pkg_id: PackageID = @intCast(_pkg_id); + + const workspace_res = pkg_resolutions[pkg_id]; + const workspace_path = workspace_res.value.workspace.slice(string_buf); + + const importer_versions = importer_dep_res_versions.get(workspace_path) orelse { + return invalidPnpmLockfile(); + }; + + const deps = pkg_deps[pkg_id]; + for (deps.begin()..deps.end()) |_dep_id| { + const dep_id: DependencyID = @intCast(_dep_id); + const dep = &lockfile.buffers.dependencies.items[dep_id]; + const dep_name = dep.name.slice(string_buf); + var version_maybe_alias = importer_versions.get(dep_name) orelse { + try log.addErrorFmt(null, logger.Loc.Empty, allocator, "pnpm-lock.yaml cannot resolve workspace dependency '{s}' in '{s}' - missing version", .{ dep_name, workspace_path }); + return error.PnpmLockfileUnresolvableDependency; + }; + if (strings.hasPrefixComptime(version_maybe_alias, "npm:")) { + version_maybe_alias = version_maybe_alias["npm:".len..]; + } + const version, const has_alias = Dependency.splitVersionAndMaybeName(version_maybe_alias); + const version_without_suffix = removeSuffix(version); + + if (strings.withoutPrefixIfPossibleComptime(version_without_suffix, "link:")) |maybe_symlink_or_folder_or_workspace_path| { + var path_buf: bun.AbsPath(.{ .sep = .auto }) = .initTopLevelDir(); + defer path_buf.deinit(); + path_buf.join(&.{ workspace_path, maybe_symlink_or_folder_or_workspace_path }); + if (pkg_map.get(path_buf.slice())) |link_pkg_id| { + lockfile.buffers.resolutions.items[dep_id] = link_pkg_id; + continue; + } + } + + res_buf.clearRetainingCapacity(); + try res_buf.writer().print("{s}@{s}", .{ + if (has_alias) |alias| alias else dep_name, + version_without_suffix, + }); + + const res_pkg_id = pkg_map.get(res_buf.items) orelse { + return invalidPnpmLockfile(); + }; + + lockfile.buffers.resolutions.items[dep_id] = res_pkg_id; + } + } + + for (workspace_pkgs_end..lockfile.packages.len) |_pkg_id| { + const pkg_id: PackageID = @intCast(_pkg_id); + + const deps = pkg_deps[pkg_id]; + for (deps.begin()..deps.end()) |_dep_id| { + const dep_id: DependencyID = @intCast(_dep_id); + const dep = &lockfile.buffers.dependencies.items[dep_id]; + var version_maybe_alias = dep.version.literal.slice(string_buf); + if (strings.hasPrefixComptime(version_maybe_alias, "npm:")) { + version_maybe_alias = version_maybe_alias["npm:".len..]; + } + const version, const has_alias = Dependency.splitVersionAndMaybeName(version_maybe_alias); + const version_without_suffix = removeSuffix(version); + + switch (dep.version.tag) { + .folder, .symlink, .workspace => { + const maybe_symlink_or_folder_or_workspace_path = strings.withoutPrefixComptime(version_without_suffix, "link:"); + if (pkg_map.get(maybe_symlink_or_folder_or_workspace_path)) |workspace_pkg_id| { + lockfile.buffers.resolutions.items[dep_id] = workspace_pkg_id; + continue; + } + }, + else => {}, + } + + res_buf.clearRetainingCapacity(); + try res_buf.writer().print("{s}@{s}", .{ + if (has_alias) |alias| alias else dep.name.slice(string_buf), + version_without_suffix, + }); + + const res_pkg_id = pkg_map.get(res_buf.items) orelse { + return invalidPnpmLockfile(); + }; + + lockfile.buffers.resolutions.items[dep_id] = res_pkg_id; + } + } + + try lockfile.resolve(log); + + try lockfile.fetchNecessaryPackageMetadataAfterYarnOrPnpmMigration(manager); + + try updatePackageJsonAfterMigration(allocator, manager, log, dir, found_patches); + + return .{ + .ok = .{ + .lockfile = lockfile, + .loaded_from_binary_lockfile = false, + .was_migrated = true, + .serializer_result = .{}, + .format = .text, + }, + }; +} + +fn invalidPnpmLockfile() error{InvalidPnpmLockfile} { + return error.InvalidPnpmLockfile; +} + +const ParseAppendDependenciesError = OOM || error{ + InvalidPnpmLockfile, + PnpmLockfileInvalidDependency, + PnpmLockfileMissingDependencyVersion, + PnpmLockfileMissingCatalogEntry, +}; + +fn parseAppendPackageDependencies( + lockfile: *Lockfile, + allocator: std.mem.Allocator, + package_obj: *const Expr, + snapshot_obj: *const Expr, + string_buf: *String.Buf, + log: *logger.Log, +) ParseAppendDependenciesError!struct { u32, u32 } { + var version_buf: std.ArrayList(u8) = .init(allocator); + defer version_buf.deinit(); + + const off = lockfile.buffers.dependencies.items.len; + + const snapshot_dependency_groups = [2]struct { []const u8, Dependency.Behavior }{ + .{ "devDependencies", .{ .dev = true } }, + .{ "optionalDependencies", .{ .optional = true } }, + }; + + inline for (snapshot_dependency_groups) |dependency_group| { + const group_name, const group_behavior = dependency_group; + if (snapshot_obj.get(group_name)) |deps| { + if (!deps.isObject()) { + return invalidPnpmLockfile(); + } + + for (deps.data.e_object.properties.slice()) |prop| { + const key = prop.key.?; + const value = prop.value.?; + + const name_str = key.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + const name_hash = String.Builder.stringHash(name_str); + const name = try string_buf.appendExternalWithHash(name_str, name_hash); + + const version_str = value.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + const version_without_suffix = removeSuffix(version_str); + + const version = try string_buf.append(version_without_suffix); + const version_sliced = version.sliced(string_buf.bytes.items); + + const behavior: Dependency.Behavior = group_behavior; + + const dep: Dependency = .{ + .name = name.value, + .name_hash = name_hash, + .behavior = behavior, + .version = Dependency.parse( + allocator, + name.value, + name.hash, + version_sliced.slice, + &version_sliced, + log, + null, + ) orelse { + return invalidPnpmLockfile(); + }, + }; + + try lockfile.buffers.dependencies.append(allocator, dep); + } + } + } + + if (snapshot_obj.get("dependencies")) |deps| { + if (!deps.isObject()) { + return invalidPnpmLockfile(); + } + + // for each dependency first look it up in peerDependencies in package_obj + next_prod_dep: for (deps.data.e_object.properties.slice()) |prop| { + const key = prop.key.?; + const value = prop.value.?; + + const name_str = key.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + const name_hash = String.Builder.stringHash(name_str); + const name = try string_buf.appendExternalWithHash(name_str, name_hash); + + const version_str = value.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + const version_without_suffix = removeSuffix(version_str); + + // pnpm-lock.yaml does not prefix aliases with npm: in snapshots + _, const has_alias = Dependency.splitVersionAndMaybeName(version_without_suffix); + + var alias: ?ExternalString = null; + const version_sliced = version: { + if (has_alias) |alias_str| { + alias = try string_buf.appendExternal(alias_str); + version_buf.clearRetainingCapacity(); + try version_buf.writer().print("npm:{s}", .{version_without_suffix}); + const version = try string_buf.append(version_buf.items); + const version_sliced = version.sliced(string_buf.bytes.items); + break :version version_sliced; + } + + const version = try string_buf.append(version_without_suffix); + const version_sliced = version.sliced(string_buf.bytes.items); + break :version version_sliced; + }; + + if (package_obj.get("peerDependencies")) |peers| { + if (!peers.isObject()) { + return invalidPnpmLockfile(); + } + + for (peers.data.e_object.properties.slice()) |peer_prop| { + const peer_name_str = peer_prop.key.?.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + // const peer_version_str = peer_prop.value.?.asString(allocator) orelse { + // return invalidPnpmLockfile(); + // }; + + // const peer_version_without_suffix = removeSuffix(peer_version_str); + + // const peer_version = try string_buf.append(peer_version_without_suffix); + // const peer_version_sliced = peer_version.sliced(string_buf.bytes.items); + + var behavior: Dependency.Behavior = .{ .peer = true }; + + if (strings.eqlLong(name_str, peer_name_str, true)) { + if (package_obj.get("peerDependenciesMeta")) |peers_meta| { + if (!peers_meta.isObject()) { + return invalidPnpmLockfile(); + } + + for (peers_meta.data.e_object.properties.slice()) |peer_meta_prop| { + const peer_meta_name_str = peer_meta_prop.key.?.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + if (strings.eqlLong(name_str, peer_meta_name_str, true)) { + const meta_obj = peer_meta_prop.value.?; + if (!meta_obj.isObject()) { + return invalidPnpmLockfile(); + } + + behavior.optional = meta_obj.getBoolean("optional") orelse false; + break; + } + } + } + const dep: Dependency = .{ + .name = name.value, + .name_hash = name.hash, + .behavior = behavior, + .version = Dependency.parse( + allocator, + if (alias) |a| a.value else name.value, + if (alias) |a| a.hash else name.hash, + version_sliced.slice, + &version_sliced, + log, + null, + ) orelse { + return invalidPnpmLockfile(); + }, + }; + + try lockfile.buffers.dependencies.append(allocator, dep); + continue :next_prod_dep; + } + } + } + + const dep: Dependency = .{ + .name = name.value, + .name_hash = name.hash, + .behavior = .{ .prod = true }, + .version = Dependency.parse( + allocator, + if (alias) |a| a.value else name.value, + if (alias) |a| a.hash else name.hash, + version_sliced.slice, + &version_sliced, + log, + null, + ) orelse { + return invalidPnpmLockfile(); + }, + }; + + try lockfile.buffers.dependencies.append(allocator, dep); + } + } + + const end = lockfile.buffers.dependencies.items.len; + + std.sort.pdq( + Dependency, + lockfile.buffers.dependencies.items[off..], + string_buf.bytes.items, + Dependency.isLessThan, + ); + + return .{ @intCast(off), @intCast(end - off) }; +} + +fn parseAppendImporterDependencies( + lockfile: *Lockfile, + manager: *PackageManager, + allocator: std.mem.Allocator, + pkg_expr: *const Expr, + string_buf: *String.Buf, + log: *logger.Log, + is_root: bool, + importers_obj: *const Expr, + importer_versions: *bun.StringArrayHashMap([]const u8), +) ParseAppendDependenciesError!struct { u32, u32 } { + const importer_dependency_groups = [3]struct { []const u8, Dependency.Behavior }{ + .{ "dependencies", .{ .prod = true } }, + .{ "devDependencies", .{ .dev = true } }, + .{ "optionalDependencies", .{ .optional = true } }, + }; + + const off = lockfile.buffers.dependencies.items.len; + + inline for (importer_dependency_groups) |dependency_group| { + const group_name, const group_behavior = dependency_group; + if (pkg_expr.get(group_name)) |deps| { + if (!deps.isObject()) { + return invalidPnpmLockfile(); + } + + for (deps.data.e_object.properties.slice()) |prop| { + const key = prop.key.?; + const value = prop.value.?; + + const name_str = key.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + const name_hash = String.Builder.stringHash(name_str); + const name = try string_buf.appendExternalWithHash(name_str, name_hash); + + const specifier_expr = value.get("specifier") orelse { + try log.addErrorFmt(null, logger.Loc.Empty, allocator, "pnpm-lock.yaml dependency '{s}' missing 'specifier' field", .{name_str}); + return error.PnpmLockfileInvalidDependency; + }; + + const version_expr = value.get("version") orelse { + try log.addErrorFmt(null, logger.Loc.Empty, allocator, "pnpm-lock.yaml dependency '{s}' missing 'version' field", .{name_str}); + return error.PnpmLockfileMissingDependencyVersion; + }; + + const version_str = try version_expr.asStringCloned(allocator) orelse { + return invalidPnpmLockfile(); + }; + + const entry = try importer_versions.getOrPut(name_str); + if (entry.found_existing) { + continue; + } + entry.value_ptr.* = removeSuffix(version_str); + + const specifier_str = specifier_expr.asString(allocator) orelse { + return invalidPnpmLockfile(); + }; + + if (strings.hasPrefixComptime(specifier_str, "catalog:")) { + const catalog_group_name_str = specifier_str["catalog:".len..]; + const catalog_group_name = try string_buf.append(catalog_group_name_str); + var dep = lockfile.catalogs.get(lockfile, catalog_group_name, name.value) orelse { + // catalog is missing an entry in the "catalogs" object in the lockfile + try log.addErrorFmt(null, logger.Loc.Empty, allocator, "pnpm-lock.yaml catalog '{s}' missing entry for dependency '{s}'", .{ catalog_group_name_str, name_str }); + return error.PnpmLockfileMissingCatalogEntry; + }; + + dep.behavior = group_behavior; + + try lockfile.buffers.dependencies.append(allocator, dep); + continue; + } + + const specifier = try string_buf.append(specifier_str); + const specifier_sliced = specifier.sliced(string_buf.bytes.items); + + const behavior: Dependency.Behavior = group_behavior; + + // TODO: find peerDependencies from package.json + if (comptime group_behavior.prod) { + // + } + + const dep: Dependency = .{ + .name = name.value, + .name_hash = name.hash, + .behavior = behavior, + .version = Dependency.parse( + allocator, + name.value, + name.hash, + specifier_sliced.slice, + &specifier_sliced, + log, + null, + ) orelse { + return invalidPnpmLockfile(); + }, + }; + + try lockfile.buffers.dependencies.append(allocator, dep); + } + } + } + + if (is_root) { + workspaces: for (lockfile.workspace_paths.values()) |workspace_path| { + for (importers_obj.data.e_object.properties.slice()) |prop| { + const key = prop.key.?; + const path = key.asString(allocator).?; + if (!strings.eqlLong(path, workspace_path.slice(string_buf.bytes.items), true)) { + continue; + } + + var path_buf: bun.AbsPath(.{ .sep = .auto }) = .initTopLevelDir(); + defer path_buf.deinit(); + + path_buf.append(path); + path_buf.append("package.json"); + + const workspace_pkg_json = manager.workspace_package_json_cache.getWithPath(allocator, log, path_buf.slice(), .{}).unwrap() catch { + return invalidPnpmLockfile(); + }; + + const name, _ = try workspace_pkg_json.root.getString(allocator, "name") orelse { + return invalidPnpmLockfile(); + }; + + const name_hash = String.Builder.stringHash(name); + const dep: Dependency = .{ + .name = try string_buf.appendWithHash(name, name_hash), + .name_hash = name_hash, + .behavior = .{ .workspace = true }, + .version = .{ + .tag = .workspace, + .value = .{ .workspace = try string_buf.append(path) }, + }, + }; + + try lockfile.buffers.dependencies.append(allocator, dep); + continue :workspaces; + } + } + } + + const end = lockfile.buffers.dependencies.items.len; + + std.sort.pdq( + Dependency, + lockfile.buffers.dependencies.items[off..], + string_buf.bytes.items, + Dependency.isLessThan, + ); + + return .{ @intCast(off), @intCast(end - off) }; +} + +/// Updates package.json with workspace and catalog information after migration +fn updatePackageJsonAfterMigration(allocator: Allocator, manager: *PackageManager, log: *logger.Log, dir: bun.FD, patches: bun.StringArrayHashMap([]const u8)) OOM!void { + var pkg_json_path: bun.AbsPath(.{}) = .initTopLevelDir(); + defer pkg_json_path.deinit(); + + pkg_json_path.append("package.json"); + + const root_pkg_json = manager.workspace_package_json_cache.getWithPath( + manager.allocator, + log, + pkg_json_path.slice(), + .{ + .guess_indentation = true, + }, + ).unwrap() catch { + return; + }; + + var json = root_pkg_json.root; + if (json.data != .e_object) return; + + var needs_update = false; + var moved_overrides = false; + var moved_patched_deps = false; + + if (json.asProperty("pnpm")) |pnpm_prop| { + if (pnpm_prop.expr.data == .e_object) { + const pnpm_obj = pnpm_prop.expr.data.e_object; + + if (pnpm_obj.get("overrides")) |overrides_field| { + if (overrides_field.data == .e_object) { + if (json.asProperty("overrides")) |existing_prop| { + if (existing_prop.expr.data == .e_object) { + const existing_overrides = existing_prop.expr.data.e_object; + for (overrides_field.data.e_object.properties.slice()) |prop| { + const key = prop.key.?.asString(allocator) orelse continue; + try existing_overrides.put(allocator, key, prop.value.?); + } + } + } else { + try json.data.e_object.put(allocator, "overrides", overrides_field); + } + moved_overrides = true; + needs_update = true; + } + } + + if (pnpm_obj.get("patchedDependencies")) |patched_field| { + if (patched_field.data == .e_object) { + if (json.asProperty("patchedDependencies")) |existing_prop| { + if (existing_prop.expr.data == .e_object) { + const existing_patches = existing_prop.expr.data.e_object; + for (patched_field.data.e_object.properties.slice()) |prop| { + const key = prop.key.?.asString(allocator) orelse continue; + try existing_patches.put(allocator, key, prop.value.?); + } + } + } else { + try json.data.e_object.put(allocator, "patchedDependencies", patched_field); + } + moved_patched_deps = true; + needs_update = true; + } + } + + if (moved_overrides or moved_patched_deps) { + var remaining_count: usize = 0; + for (pnpm_obj.properties.slice()) |prop| { + const key = prop.key.?.asString(allocator) orelse { + remaining_count += 1; + continue; + }; + if (moved_overrides and strings.eqlComptime(key, "overrides")) continue; + if (moved_patched_deps and strings.eqlComptime(key, "patchedDependencies")) continue; + remaining_count += 1; + } + + if (remaining_count == 0) { + var new_root_count: usize = 0; + for (json.data.e_object.properties.slice()) |prop| { + const key = prop.key.?.asString(allocator) orelse { + new_root_count += 1; + continue; + }; + if (!strings.eqlComptime(key, "pnpm")) { + new_root_count += 1; + } + } + + var new_root_props: JSAst.G.Property.List = try .initCapacity(allocator, new_root_count); + for (json.data.e_object.properties.slice()) |prop| { + const key = prop.key.?.asString(allocator) orelse { + new_root_props.appendAssumeCapacity(prop); + continue; + }; + if (!strings.eqlComptime(key, "pnpm")) { + new_root_props.appendAssumeCapacity(prop); + } + } + + json.data.e_object.properties = new_root_props; + } else { + var new_pnpm_props: JSAst.G.Property.List = try .initCapacity(allocator, remaining_count); + for (pnpm_obj.properties.slice()) |prop| { + const key = prop.key.?.asString(allocator) orelse { + new_pnpm_props.appendAssumeCapacity(prop); + continue; + }; + if (moved_overrides and strings.eqlComptime(key, "overrides")) continue; + if (moved_patched_deps and strings.eqlComptime(key, "patchedDependencies")) continue; + new_pnpm_props.appendAssumeCapacity(prop); + } + + pnpm_obj.properties = new_pnpm_props; + } + needs_update = true; + } + } + } + + var workspace_paths: ?std.ArrayList([]const u8) = null; + var catalog_obj: ?Expr = null; + var catalogs_obj: ?Expr = null; + var workspace_overrides_obj: ?Expr = null; + var workspace_patched_deps_obj: ?Expr = null; + + switch (bun.sys.File.readFrom(bun.FD.cwd(), "pnpm-workspace.yaml", allocator)) { + .result => |contents| read_pnpm_workspace_yaml: { + const yaml_source = logger.Source.initPathString("pnpm-workspace.yaml", contents); + const root = YAML.parse(&yaml_source, log, allocator) catch { + break :read_pnpm_workspace_yaml; + }; + + if (root.get("packages")) |packages_expr| { + if (packages_expr.asArray()) |_packages| { + var packages = _packages; + var paths: std.ArrayList([]const u8) = .init(allocator); + while (packages.next()) |package_path| { + if (package_path.asString(allocator)) |package_path_str| { + try paths.append(package_path_str); + } + } + + workspace_paths = paths; + } + } + + if (root.getObject("catalog")) |catalog_expr| { + catalog_obj = catalog_expr; + } + + if (root.getObject("catalogs")) |catalogs_expr| { + catalogs_obj = catalogs_expr; + } + + if (root.getObject("overrides")) |overrides_expr| { + workspace_overrides_obj = overrides_expr; + } + + if (root.getObject("patchedDependencies")) |patched_deps_expr| { + workspace_patched_deps_obj = patched_deps_expr; + } + }, + .err => {}, + } + + const has_workspace_data = workspace_paths != null or catalog_obj != null or catalogs_obj != null; + + if (has_workspace_data) { + const use_array_format = workspace_paths != null and catalog_obj == null and catalogs_obj == null; + + const existing_workspaces = json.data.e_object.get("workspaces"); + const is_object_workspaces = existing_workspaces != null and existing_workspaces.?.data == .e_object; + + if (use_array_format) { + const paths = workspace_paths.?; + var items: JSAst.ExprNodeList = try .initCapacity(allocator, paths.items.len); + for (paths.items) |path| { + items.appendAssumeCapacity(Expr.init(E.String, .{ .data = path }, .Empty)); + } + const array = Expr.init(E.Array, .{ .items = items }, .Empty); + try json.data.e_object.put(allocator, "workspaces", array); + needs_update = true; + } else if (is_object_workspaces) { + const ws_obj = existing_workspaces.?.data.e_object; + + if (workspace_paths) |paths| { + if (paths.items.len > 0) { + var items: JSAst.ExprNodeList = try .initCapacity(allocator, paths.items.len); + for (paths.items) |path| { + items.appendAssumeCapacity(Expr.init(E.String, .{ .data = path }, .Empty)); + } + const array = Expr.init(E.Array, .{ .items = items }, .Empty); + try ws_obj.put(allocator, "packages", array); + + needs_update = true; + } + } + + if (catalog_obj) |catalog| { + try ws_obj.put(allocator, "catalog", catalog); + needs_update = true; + } + + if (catalogs_obj) |catalogs| { + try ws_obj.put(allocator, "catalogs", catalogs); + needs_update = true; + } + } else if (!use_array_format) { + var ws_props: JSAst.G.Property.List = .empty; + + if (workspace_paths) |paths| { + if (paths.items.len > 0) { + var items: JSAst.ExprNodeList = try .initCapacity(allocator, paths.items.len); + for (paths.items) |path| { + items.appendAssumeCapacity(Expr.init(E.String, .{ .data = path }, .Empty)); + } + const value = Expr.init(E.Array, .{ .items = items }, .Empty); + const key = Expr.init(E.String, .{ .data = "packages" }, .Empty); + + try ws_props.append(allocator, .{ .key = key, .value = value }); + } + } + + if (catalog_obj) |catalog| { + const key = Expr.init(E.String, .{ .data = "catalog" }, .Empty); + try ws_props.append(allocator, .{ .key = key, .value = catalog }); + } + + if (catalogs_obj) |catalogs| { + const key = Expr.init(E.String, .{ .data = "catalogs" }, .Empty); + try ws_props.append(allocator, .{ .key = key, .value = catalogs }); + } + + if (ws_props.len > 0) { + const workspace_obj = Expr.init(E.Object, .{ .properties = ws_props }, .Empty); + try json.data.e_object.put(allocator, "workspaces", workspace_obj); + needs_update = true; + } + } + } + + // Handle overrides from pnpm-workspace.yaml + if (workspace_overrides_obj) |ws_overrides| { + if (ws_overrides.data == .e_object) { + if (json.asProperty("overrides")) |existing_prop| { + if (existing_prop.expr.data == .e_object) { + const existing_overrides = existing_prop.expr.data.e_object; + for (ws_overrides.data.e_object.properties.slice()) |prop| { + const key = prop.key.?.asString(allocator) orelse continue; + try existing_overrides.put(allocator, key, prop.value.?); + } + } + } else { + try json.data.e_object.put(allocator, "overrides", ws_overrides); + } + needs_update = true; + } + } + + // Handle patchedDependencies from pnpm-workspace.yaml + if (workspace_patched_deps_obj) |ws_patched| { + var join_buf: std.ArrayList(u8) = .init(allocator); + defer join_buf.deinit(); + + if (ws_patched.data == .e_object) { + for (0..ws_patched.data.e_object.properties.len) |prop_i| { + // convert keys to expected "name@version" instead of only "name" + var prop = &ws_patched.data.e_object.properties.ptr[prop_i]; + const key_str = prop.key.?.asString(allocator) orelse { + continue; + }; + const res_str = patches.get(key_str) orelse { + continue; + }; + join_buf.clearRetainingCapacity(); + try join_buf.writer().print("{s}@{s}", .{ + key_str, + res_str, + }); + prop.key = Expr.init(E.String, .{ .data = try allocator.dupe(u8, join_buf.items) }, .Empty); + } + if (json.asProperty("patchedDependencies")) |existing_prop| { + if (existing_prop.expr.data == .e_object) { + const existing_patches = existing_prop.expr.data.e_object; + for (ws_patched.data.e_object.properties.slice()) |prop| { + const key = prop.key.?.asString(allocator) orelse continue; + try existing_patches.put(allocator, key, prop.value.?); + } + } + } else { + try json.data.e_object.put(allocator, "patchedDependencies", ws_patched); + } + needs_update = true; + } + } + + if (needs_update) { + var buffer_writer = JSPrinter.BufferWriter.init(allocator); + defer buffer_writer.buffer.deinit(); + buffer_writer.append_newline = root_pkg_json.source.contents.len > 0 and root_pkg_json.source.contents[root_pkg_json.source.contents.len - 1] == '\n'; + var package_json_writer = JSPrinter.BufferPrinter.init(buffer_writer); + + _ = JSPrinter.printJSON( + @TypeOf(&package_json_writer), + &package_json_writer, + json, + &root_pkg_json.source, + .{ + .indent = root_pkg_json.indentation, + .mangled_props = null, + }, + ) catch return; + + package_json_writer.flush() catch { + return error.OutOfMemory; + }; + + root_pkg_json.source.contents = try allocator.dupe(u8, package_json_writer.ctx.writtenWithoutTrailingZero()); + + // Write the updated package.json + const write_file = bun.sys.File.openat(dir, "package.json", bun.O.WRONLY | bun.O.TRUNC, 0).unwrap() catch return; + defer write_file.close(); + _ = write_file.write(root_pkg_json.source.contents).unwrap() catch return; + } +} + +const Dependency = @import("./dependency.zig"); +const Npm = @import("./npm.zig"); +const Bin = @import("./bin.zig").Bin; +const Integrity = @import("./integrity.zig").Integrity; +const Resolution = @import("./resolution.zig").Resolution; + +const Lockfile = @import("./lockfile.zig"); +const LoadResult = Lockfile.LoadResult; + +const bun = @import("bun"); +const JSPrinter = bun.js_printer; +const OOM = bun.OOM; +const logger = bun.logger; +const strings = bun.strings; +const sys = bun.sys; +const YAML = bun.interchange.yaml.YAML; + +const Semver = bun.Semver; +const ExternalString = Semver.ExternalString; +const String = Semver.String; +const stringHash = String.Builder.stringHash; + +const JSAst = bun.ast; +const E = JSAst.E; +const Expr = JSAst.Expr; + +const DependencyID = bun.install.DependencyID; +const ExtractTarball = bun.install.ExtractTarball; +const PackageID = bun.install.PackageID; +const PackageManager = bun.install.PackageManager; +const invalid_package_id = bun.install.invalid_package_id; +const Negatable = bun.install.Npm.Negatable; + +const std = @import("std"); +const os = std.os; +const Allocator = std.mem.Allocator; diff --git a/src/install/repository.zig b/src/install/repository.zig index 89ccd3f500..1fe39d2e41 100644 --- a/src/install/repository.zig +++ b/src/install/repository.zig @@ -113,8 +113,8 @@ const SloppyGlobalGitConfig = struct { pub const Repository = extern struct { owner: String = .{}, repo: String = .{}, - committish: GitSHA = .{}, - resolved: GitSHA = .{}, + committish: String = .{}, + resolved: String = .{}, package_name: String = .{}, pub var shared_env: struct { @@ -261,7 +261,7 @@ pub const Repository = extern struct { return .{ .owner = builder.append(String, this.owner.slice(buf)), .repo = builder.append(String, this.repo.slice(buf)), - .committish = builder.append(GitSHA, this.committish.slice(buf)), + .committish = builder.append(String, this.committish.slice(buf)), .resolved = builder.append(String, this.resolved.slice(buf)), .package_name = builder.append(String, this.package_name.slice(buf)), }; @@ -701,5 +701,4 @@ const strings = bun.strings; const File = bun.sys.File; const Semver = bun.Semver; -const GitSHA = String; const String = Semver.String; diff --git a/src/install/resolution.zig b/src/install/resolution.zig index 5c0e37e1cd..59947afe31 100644 --- a/src/install/resolution.zig +++ b/src/install/resolution.zig @@ -100,6 +100,82 @@ pub fn ResolutionType(comptime SemverIntType: type) type { }; } + const FromPnpmLockfileError = OOM || error{InvalidPnpmLockfile}; + + pub fn fromPnpmLockfile(res_str: []const u8, string_buf: *String.Buf) FromPnpmLockfileError!Resolution { + if (strings.withoutPrefixIfPossibleComptime(res_str, "https://codeload.github.com/")) |user_repo_tar_committish| { + const user_end = strings.indexOfChar(user_repo_tar_committish, '/') orelse { + return error.InvalidPnpmLockfile; + }; + const user = user_repo_tar_committish[0..user_end]; + const repo_tar_committish = user_repo_tar_committish[user_end + 1 ..]; + + const repo_end = strings.indexOfChar(repo_tar_committish, '/') orelse { + return error.InvalidPnpmLockfile; + }; + const repo = repo_tar_committish[0..repo_end]; + const tar_committish = repo_tar_committish[repo_end + 1 ..]; + + const tar_end = strings.indexOfChar(tar_committish, '/') orelse { + return error.InvalidPnpmLockfile; + }; + const committish = tar_committish[tar_end + 1 ..]; + + return This.init(.{ + .github = .{ + .owner = try string_buf.append(user), + .repo = try string_buf.append(repo), + .committish = try string_buf.append(committish), + }, + }); + } + + if (strings.withoutPrefixIfPossibleComptime(res_str, "file:")) |path| { + if (strings.endsWithComptime(res_str, ".tgz")) { + return This.init(.{ .local_tarball = try string_buf.append(path) }); + } + return This.init(.{ .folder = try string_buf.append(path) }); + } + + 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.InvalidPnpmLockfile; + } + + if (parsed.version.major == null or parsed.version.minor == null or parsed.version.patch == null) { + return error.InvalidPnpmLockfile; + } + + return This.init(.{ + .npm = .{ + .version = parsed.version.min(), + // set afterwards + .url = .{}, + }, + }); + }, + + .workspace => error.InvalidPnpmLockfile, + .symlink => error.InvalidPnpmLockfile, + .folder => error.InvalidPnpmLockfile, + .catalog => error.InvalidPnpmLockfile, + .dist_tag => error.InvalidPnpmLockfile, + .uninitialized => error.InvalidPnpmLockfile, + }; + } + pub fn order( lhs: *const This, rhs: *const This, @@ -169,6 +245,7 @@ pub fn ResolutionType(comptime SemverIntType: type) type { .github = this.value.github.clone(buf, Builder, builder), }), .root => Value.init(.{ .root = {} }), + .uninitialized => Value.init(.{ .uninitialized = {} }), else => { std.debug.panic("Internal error: unexpected resolution tag: {}", .{this.tag}); }, diff --git a/src/install/resolvers/folder_resolver.zig b/src/install/resolvers/folder_resolver.zig index 7599e12e1e..3edfeee96d 100644 --- a/src/install/resolvers/folder_resolver.zig +++ b/src/install/resolvers/folder_resolver.zig @@ -194,7 +194,7 @@ pub const FolderResolution = union(Tag) { body.data.reset(); var man = body.data.list.toManaged(manager.allocator); defer body.data.list = man.moveToUnmanaged(); - _ = try file.readToEndWithArrayList(&man, true).unwrap(); + _ = try file.readToEndWithArrayList(&man, .probably_small).unwrap(); } break :brk logger.Source.initPathString(abs, body.data.list.items); diff --git a/src/install/yarn.zig b/src/install/yarn.zig index f4a2d30a07..21af400e75 100644 --- a/src/install/yarn.zig +++ b/src/install/yarn.zig @@ -1671,6 +1671,8 @@ pub fn migrateYarnLockfile( try this.resolve(log); + try this.fetchNecessaryPackageMetadataAfterYarnOrPnpmMigration(manager); + if (Environment.allow_assert) { try this.verifyData(); } diff --git a/src/io/PipeReader.zig b/src/io/PipeReader.zig index 628adf34e0..11533f8421 100644 --- a/src/io/PipeReader.zig +++ b/src/io/PipeReader.zig @@ -198,7 +198,7 @@ const PosixBufferedReader = struct { pub fn finalBuffer(this: *PosixBufferedReader) *std.ArrayList(u8) { if (this.flags.memfd and this.handle == .fd) { defer this.handle.close(null, {}); - _ = bun.sys.File.readToEndWithArrayList(.{ .handle = this.handle.fd }, this.buffer(), false).unwrap() catch |err| { + _ = bun.sys.File.readToEndWithArrayList(.{ .handle = this.handle.fd }, this.buffer(), .unknown_size).unwrap() catch |err| { bun.Output.debugWarn("error reading from memfd\n{}", .{err}); return this.buffer(); }; diff --git a/src/sys/File.zig b/src/sys/File.zig index 921d3d57ac..b43990b875 100644 --- a/src/sys/File.zig +++ b/src/sys/File.zig @@ -275,8 +275,8 @@ pub fn readFillBuf(this: File, buf: []u8) Maybe([]u8) { return .{ .result = buf[0..read_amount] }; } -pub fn readToEndWithArrayList(this: File, list: *std.ArrayList(u8), probably_small: bool) Maybe(usize) { - if (probably_small) { +pub fn readToEndWithArrayList(this: File, list: *std.ArrayList(u8), size_guess: enum { probably_small, unknown_size }) Maybe(usize) { + if (size_guess == .probably_small) { bun.handleOom(list.ensureUnusedCapacity(64)); } else { list.ensureTotalCapacityPrecise( @@ -320,7 +320,7 @@ pub fn readToEndWithArrayList(this: File, list: *std.ArrayList(u8), probably_sma /// Calls fstat() on the file to get the size of the file and avoids reallocations + extra read() calls. pub fn readToEnd(this: File, allocator: std.mem.Allocator) ReadToEndResult { var list = std.ArrayList(u8).init(allocator); - return switch (readToEndWithArrayList(this, &list, false)) { + return switch (readToEndWithArrayList(this, &list, .unknown_size)) { .err => |err| .{ .err = err, .bytes = list }, .result => .{ .err = null, .bytes = list }, }; @@ -330,7 +330,7 @@ pub fn readToEnd(this: File, allocator: std.mem.Allocator) ReadToEndResult { /// File will skip the fstat() call, preallocating 64 bytes instead of the file's size. pub fn readToEndSmall(this: File, allocator: std.mem.Allocator) ReadToEndResult { var list = std.ArrayList(u8).init(allocator); - return switch (readToEndWithArrayList(this, &list, true)) { + return switch (readToEndWithArrayList(this, &list, .probably_small)) { .err => |err| .{ .err = err, .bytes = list }, .result => .{ .err = null, .bytes = list }, }; diff --git a/test/cli/install/__snapshots__/bun-install.test.ts.snap b/test/cli/install/__snapshots__/bun-install.test.ts.snap index eb0f27c652..68e0ef1f8d 100644 --- a/test/cli/install/__snapshots__/bun-install.test.ts.snap +++ b/test/cli/install/__snapshots__/bun-install.test.ts.snap @@ -5,7 +5,7 @@ exports[`should report error on invalid format for package.json 1`] = ` ^ error: Unexpected foo at [dir]/package.json:1:1 -ParserError parsing package.json in "[dir]/" +ParserError: failed to parse '[dir]/package.json' " `; diff --git a/test/cli/install/bun-install-registry.test.ts b/test/cli/install/bun-install-registry.test.ts index 140a80f136..a7612ebc4a 100644 --- a/test/cli/install/bun-install-registry.test.ts +++ b/test/cli/install/bun-install-registry.test.ts @@ -52,7 +52,7 @@ afterAll(async () => { }); beforeEach(async () => { - ({ packageDir, packageJson } = await registry.createTestDir({ saveTextLockfile: false })); + ({ packageDir, packageJson } = await registry.createTestDir({ bunfigOpts: { saveTextLockfile: false } })); await Bun.$`rm -f ${import.meta.dir}/htpasswd`.throws(false); await Bun.$`rm -rf ${import.meta.dir}/packages/private-pkg-dont-touch`.throws(false); users = {}; diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts index 516e71db0b..284dbf93a1 100644 --- a/test/cli/install/bun-install.test.ts +++ b/test/cli/install/bun-install.test.ts @@ -17,6 +17,7 @@ import { bunExe, bunEnv as env, isWindows, + joinP, readdirSorted, runBunInstall, tempDirWithFiles, @@ -4448,7 +4449,7 @@ it("should report error on invalid format for package.json", async () => { env, }); const err = await stderr.text(); - expect(err.replaceAll(package_dir + sep, "[dir]/")).toMatchSnapshot(); + expect(err.replaceAll(joinP(package_dir + sep), "[dir]/").replaceAll(package_dir + sep, "[dir]/")).toMatchSnapshot(); const out = await stdout.text(); expect(out).toEqual(expect.stringContaining("bun install v1.")); expect(await exited).toBe(1); @@ -4472,7 +4473,7 @@ it("should report error on invalid format for dependencies", async () => { env, }); const err = await stderr.text(); - expect(err.replaceAll(package_dir + sep, "[dir]/")).toMatchSnapshot(); + expect(err.replaceAll(joinP(package_dir + sep), "[dir]/")).toMatchSnapshot(); const out = await stdout.text(); expect(out).toEqual(expect.stringContaining("bun install v1.")); expect(await exited).toBe(1); @@ -4497,7 +4498,7 @@ it("should report error on invalid format for optionalDependencies", async () => }); let err = await stderr.text(); - err = err.replaceAll(package_dir + sep, "[dir]/"); + err = err.replaceAll(joinP(package_dir + sep), "[dir]/"); err = err.substring(0, err.indexOf("\n", err.lastIndexOf("[dir]/package.json:"))).trim(); expect(err.split("\n")).toEqual([ `1 | {"name":"foo","version":"0.0.1","optionalDependencies":"bar"}`, @@ -4533,7 +4534,7 @@ it("should report error on invalid format for workspaces", async () => { env, }); const err = await stderr.text(); - expect(err.replaceAll(package_dir + sep, "[dir]/")).toMatchSnapshot(); + expect(err.replaceAll(joinP(package_dir + sep), "[dir]/")).toMatchSnapshot(); const out = await stdout.text(); expect(out).toEqual(expect.stringContaining("bun install v1.")); expect(await exited).toBe(1); diff --git a/test/cli/install/bun-lock.test.ts b/test/cli/install/bun-lock.test.ts index d08c3c376f..1d8183822e 100644 --- a/test/cli/install/bun-lock.test.ts +++ b/test/cli/install/bun-lock.test.ts @@ -134,7 +134,7 @@ it("should be the default save format", async () => { }); it("should save the lockfile if --save-text-lockfile and --frozen-lockfile are used", async () => { - const { packageDir, packageJson } = await registry.createTestDir({ saveTextLockfile: false }); + const { packageDir, packageJson } = await registry.createTestDir({ bunfigOpts: { saveTextLockfile: false } }); await Promise.all([ write(packageJson, JSON.stringify({ name: "test-pkg", version: "1.0.0", dependencies: { "no-deps": "1.0.0" } })), ]); @@ -169,7 +169,7 @@ it("should save the lockfile if --save-text-lockfile and --frozen-lockfile are u }); it("should convert a binary lockfile with invalid optional peers", async () => { - const { packageDir, packageJson } = await registry.createTestDir({ npm: true }); + const { packageDir, packageJson } = await registry.createTestDir({ bunfigOpts: { npm: true } }); await Promise.all([ write( packageJson, diff --git a/test/cli/install/bun-lockb.test.ts b/test/cli/install/bun-lockb.test.ts index 1d77b848ee..175a4d5e29 100644 --- a/test/cli/install/bun-lockb.test.ts +++ b/test/cli/install/bun-lockb.test.ts @@ -15,7 +15,7 @@ afterAll(() => { }); it("should not print anything to stderr when running bun.lockb", async () => { - const { packageDir, packageJson } = await registry.createTestDir({ saveTextLockfile: false }); + const { packageDir, packageJson } = await registry.createTestDir({ bunfigOpts: { saveTextLockfile: false } }); // copy bar-0.0.2.tgz to package_dir await copyFile(join(__dirname, "bar-0.0.2.tgz"), join(packageDir, "bar-0.0.2.tgz")); @@ -79,7 +79,7 @@ it("should not print anything to stderr when running bun.lockb", async () => { }); it("should continue using a binary lockfile if it exists", async () => { - const { packageDir, packageJson } = await registry.createTestDir({ saveTextLockfile: false }); + const { packageDir, packageJson } = await registry.createTestDir({ bunfigOpts: { saveTextLockfile: false } }); await write( packageJson, diff --git a/test/cli/install/bun-publish.test.ts b/test/cli/install/bun-publish.test.ts index 42f8b83afe..5f0b083f3b 100644 --- a/test/cli/install/bun-publish.test.ts +++ b/test/cli/install/bun-publish.test.ts @@ -356,7 +356,7 @@ for (const info of [ { user: "bin3", directories: { bin: "bins" } }, ]) { test(`can publish and install binaries with ${JSON.stringify(info)}`, async () => { - const { packageDir, packageJson } = await registry.createTestDir({ saveTextLockfile: false }); + const { packageDir, packageJson } = await registry.createTestDir({ bunfigOpts: { saveTextLockfile: false } }); const publishDir = tmpdirSync(); const bunfig = await registry.authBunfig("binaries-" + info.user); diff --git a/test/cli/install/catalogs.test.ts b/test/cli/install/catalogs.test.ts index 831cdc67b9..3c04ec3a74 100644 --- a/test/cli/install/catalogs.test.ts +++ b/test/cli/install/catalogs.test.ts @@ -84,7 +84,7 @@ describe("basic", () => { for (const binaryLockfile of [true, false]) { test(`detect changes (${binaryLockfile ? "bun.lockb" : "bun.lock"})`, async () => { - const { packageDir } = await registry.createTestDir({ saveTextLockfile: !binaryLockfile }); + const { packageDir } = await registry.createTestDir({ bunfigOpts: { saveTextLockfile: !binaryLockfile } }); const packageJson = await createBasicCatalogMonorepo(packageDir, "catalog-basic-2"); let { err } = await runBunInstall(bunEnv, packageDir); expect(err).toContain("Saved lockfile"); diff --git a/test/cli/install/isolated-install.test.ts b/test/cli/install/isolated-install.test.ts index 4763b537f0..99ca776545 100644 --- a/test/cli/install/isolated-install.test.ts +++ b/test/cli/install/isolated-install.test.ts @@ -17,7 +17,7 @@ afterAll(() => { describe("basic", () => { test("single dependency", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await write( packageJson, @@ -48,7 +48,7 @@ describe("basic", () => { }); test("scope package", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await write( packageJson, @@ -88,7 +88,7 @@ describe("basic", () => { }); test("transitive dependencies", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await write( packageJson, @@ -175,7 +175,7 @@ describe("basic", () => { }); test("handles cyclic dependencies", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await write( packageJson, @@ -227,7 +227,7 @@ test("handles cyclic dependencies", async () => { }); test("can install folder dependencies", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await write( packageJson, @@ -270,7 +270,7 @@ test("can install folder dependencies", async () => { describe("isolated workspaces", () => { test("basic", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await Promise.all([ write( @@ -345,7 +345,7 @@ describe("isolated workspaces", () => { for (const backend of ["clonefile", "hardlink", "copyfile"]) { test(`isolated install with backend: ${backend}`, async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await Promise.all([ write( @@ -457,7 +457,7 @@ for (const backend of ["clonefile", "hardlink", "copyfile"]) { describe("--linker flag", () => { test("can override linker from bunfig", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await write( packageJson, @@ -579,7 +579,7 @@ describe("--linker flag", () => { }); }); test("many transitive dependencies", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await write( packageJson, @@ -653,7 +653,7 @@ test("many transitive dependencies", async () => { }); test("dependency names are preserved", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await write( packageJson, @@ -710,7 +710,7 @@ test("dependency names are preserved", async () => { }); test("same resolution, different dependency name", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await write( packageJson, @@ -744,7 +744,7 @@ test("same resolution, different dependency name", async () => { }); test("successfully removes and corrects symlinks", async () => { - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await Promise.all([ write(join(packageDir, "old-package", "package.json"), JSON.stringify({ name: "old-package", version: "1.0.0" })), mkdir(join(packageDir, "node_modules")), @@ -778,7 +778,7 @@ test("runs lifecycle scripts correctly", async () => { // 2. only postinstall (or any other script that isn't preinstall) // 3. preinstall and any other script - const { packageJson, packageDir } = await registry.createTestDir({ isolated: true }); + const { packageJson, packageDir } = await registry.createTestDir({ bunfigOpts: { isolated: true } }); await write( packageJson, diff --git a/test/cli/install/migration/__snapshots__/pnpm-comprehensive.test.ts.snap b/test/cli/install/migration/__snapshots__/pnpm-comprehensive.test.ts.snap new file mode 100644 index 0000000000..1f3b647c2b --- /dev/null +++ b/test/cli/install/migration/__snapshots__/pnpm-comprehensive.test.ts.snap @@ -0,0 +1,318 @@ +// Bun Snapshot v1, https://bun.sh/docs/test/snapshots + +exports[`pnpm comprehensive migration tests large single package with many dependencies: large-single-package 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "large-app", + "dependencies": { + "@emotion/react": "^11.11.3", + "@emotion/styled": "^11.11.0", + "@experimental/super-long-scoped-package-name-with-many-words": "0.0.0-experimental-abcdef123456-20250812-build.9876543210", + "@tanstack/react-query": "^5.17.9", + "axios": "^1.6.5", + "date-fns": "^3.2.0", + "express": "^4.18.2", + "lodash": "^4.17.21", + "next": "^14.0.4", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "some-very-long-package-name-that-is-really-really-long": "1.2.3-beta.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20", + "zod": "^3.22.4", + }, + "devDependencies": { + "@types/node": "^20.10.8", + "@types/react": "^18.2.47", + "eslint": "^8.56.0", + "prettier": "^3.1.1", + "typescript": "^5.3.3", + "vitest": "^1.1.3", + }, + "optionalDependencies": { + "fsevents": "^2.3.3", + }, + }, + }, + "packages": { + "@emotion/react": ["@emotion/react@11.11.3", "", { "peerDependencies": { "react": "18.2.0" } }, ""], + + "@emotion/styled": ["@emotion/styled@11.11.0", "", { "peerDependencies": { "@emotion/react": "11.11.3", "react": "18.2.0" } }, ""], + + "@experimental/super-long-scoped-package-name-with-many-words": ["@experimental/super-long-scoped-package-name-with-many-words@0.0.0-experimental-abcdef123456-20250812-build.9876543210", "", {}, "sha512-experimentalAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "@tanstack/react-query": ["@tanstack/react-query@5.17.9", "", { "peerDependencies": { "react": "18.2.0" } }, "sha512-tanstackAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "@types/node": ["@types/node@20.10.8", "", {}, ""], + + "@types/react": ["@types/react@18.2.47", "", {}, ""], + + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "2.1.35", "negotiator": "0.6.3" } }, "sha512-acceptsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "axios": ["axios@1.6.5", "", {}, ""], + + "date-fns": ["date-fns@3.2.0", "", {}, ""], + + "eslint": ["eslint@8.56.0", "", { "bin": { "eslint": "bin/eslint.js" } }, ""], + + "express": ["express@4.18.2", "", { "dependencies": { "accepts": "1.3.8" } }, "sha512-expressAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-fseventsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, ""], + + "lodash": ["lodash@4.17.21", "", {}, ""], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "4.0.0" }, "bin": { "loose-envify": "cli.js" } }, ""], + + "mime-types": ["mime-types@2.1.35", "", {}, ""], + + "negotiator": ["negotiator@0.6.3", "", {}, ""], + + "next": ["next@14.0.4", "", { "peerDependencies": { "react": "18.2.0", "react-dom": "18.2.0" }, "bin": { "next": "dist/bin/next" } }, "sha512-nextAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "prettier": ["prettier@3.1.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-prettierAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "1.4.0" } }, ""], + + "react-dom": ["react-dom@18.2.0", "", { "dependencies": { "scheduler": "0.23.0" }, "peerDependencies": { "react": "18.2.0" } }, ""], + + "scheduler": ["scheduler@0.23.0", "", { "dependencies": { "loose-envify": "1.4.0" } }, ""], + + "some-very-long-package-name-that-is-really-really-long": ["some-very-long-package-name-that-is-really-really-long@1.2.3-beta.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20", "", {}, ""], + + "typescript": ["typescript@5.3.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, ""], + + "vitest": ["vitest@1.1.3", "", { "bin": { "vitest": "vitest.mjs" } }, ""], + + "zod": ["zod@3.22.4", "", {}, ""], + } +} +" +`; + +exports[`pnpm comprehensive migration tests complex monorepo with cross-dependencies: complex-monorepo 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "monorepo-root", + "devDependencies": { + "prettier": "^3.1.1", + "turbo": "^1.11.2", + }, + }, + "apps/api": { + "name": "@company/api", + "version": "1.0.0", + "dependencies": { + "@company/utils": "workspace:*", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + }, + "devDependencies": { + "@company/config": "workspace:*", + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21", + "nodemon": "^3.0.2", + }, + }, + "apps/web": { + "name": "@company/web", + "version": "1.0.0", + "dependencies": { + "@company/ui": "workspace:*", + "@company/utils": "workspace:*", + "next": "^14.0.4", + "react": "^18.2.0", + "react-dom": "^18.2.0", + }, + "devDependencies": { + "@company/config": "workspace:*", + "@types/node": "^20.10.8", + "@types/react": "^18.2.47", + "typescript": "^5.3.3", + }, + }, + "packages/config": { + "name": "@company/config", + "version": "1.0.0", + "devDependencies": { + "@company/utils": "workspace:*", + }, + }, + "packages/ui": { + "name": "@company/ui", + "version": "1.0.0", + "dependencies": { + "@radix-ui/react-dialog": "^1.0.5", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "react": "^18.2.0", + }, + "devDependencies": { + "@types/react": "^18.2.47", + }, + "peerDependencies": { + "react": ">=16.8.0", + }, + }, + "packages/utils": { + "name": "@company/utils", + "version": "1.0.0", + "dependencies": { + "date-fns": "^3.2.0", + "zod": "^3.22.4", + }, + }, + "tools/cli": { + "name": "@company/cli", + "version": "1.0.0", + "bin": { + "company-cli": "./bin/cli.js", + }, + "dependencies": { + "@company/utils": "workspace:*", + "chalk": "^5.3.0", + "commander": "^11.1.0", + }, + }, + }, + "packages": { + "@company/api": ["@company/api@workspace:apps/api"], + + "@company/cli": ["@company/cli@workspace:tools/cli"], + + "@company/config": ["@company/config@workspace:packages/config"], + + "@company/ui": ["@company/ui@workspace:packages/ui"], + + "@company/utils": ["@company/utils@workspace:packages/utils"], + + "@company/web": ["@company/web@workspace:apps/web"], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.0.5", "", { "peerDependencies": { "react": "18.2.0", "react-dom": "18.2.0" } }, ""], + + "@types/cors": ["@types/cors@2.8.17", "", {}, ""], + + "@types/express": ["@types/express@4.17.21", "", {}, ""], + + "@types/node": ["@types/node@20.10.8", "", {}, ""], + + "@types/react": ["@types/react@18.2.47", "", {}, ""], + + "chalk": ["chalk@5.3.0", "", {}, ""], + + "class-variance-authority": ["class-variance-authority@0.7.0", "", {}, ""], + + "clsx": ["clsx@2.1.0", "", {}, "sha512-clsxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "commander": ["commander@11.1.0", "", {}, ""], + + "cors": ["cors@2.8.5", "", {}, "sha512-corsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "date-fns": ["date-fns@3.2.0", "", {}, ""], + + "dotenv": ["dotenv@16.3.1", "", {}, ""], + + "express": ["express@4.18.2", "", {}, "sha512-expressAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, ""], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "4.0.0" }, "bin": { "loose-envify": "cli.js" } }, ""], + + "next": ["next@14.0.4", "", { "peerDependencies": { "react": "18.2.0", "react-dom": "18.2.0" }, "bin": { "next": "dist/bin/next" } }, "sha512-nextAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "nodemon": ["nodemon@3.0.2", "", { "bin": { "nodemon": "bin/nodemon.js" } }, ""], + + "prettier": ["prettier@3.1.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-prettierAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "1.4.0" } }, ""], + + "react-dom": ["react-dom@18.2.0", "", { "dependencies": { "scheduler": "0.23.0" }, "peerDependencies": { "react": "18.2.0" } }, ""], + + "scheduler": ["scheduler@0.23.0", "", { "dependencies": { "loose-envify": "1.4.0" } }, ""], + + "turbo": ["turbo@1.11.2", "", { "bin": { "turbo": "bin/turbo" } }, ""], + + "typescript": ["typescript@5.3.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, ""], + + "zod": ["zod@3.22.4", "", {}, ""], + } +} +" +`; + +exports[`pnpm comprehensive migration tests pnpm with patches and overrides: patches-overrides 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "patches-test", + "dependencies": { + "express": "^4.18.2", + "is-number": "^7.0.0", + }, + }, + }, + "patchedDependencies": { + "express@4.18.2": "patches/express@4.18.2.patch", + }, + "overrides": { + "mime-types": "2.1.33", + "negotiator@>0.6.0": "0.6.2", + }, + "packages": { + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "2.1.33", "negotiator": "0.6.2" } }, "sha512-acceptsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "array-flatten": ["array-flatten@1.1.1", "", {}, ""], + + "express": ["express@4.18.2", "", { "dependencies": { "accepts": "1.3.8", "array-flatten": "1.1.1" } }, "sha512-expressAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "is-number": ["is-number@7.0.0", "", {}, ""], + + "mime-db": ["mime-db@1.50.0", "", {}, ""], + + "mime-types": ["mime-types@2.1.33", "", { "dependencies": { "mime-db": "1.50.0" } }, ""], + + "negotiator": ["negotiator@0.6.2", "", {}, ""], + } +} +" +`; + +exports[`pnpm comprehensive migration tests pnpm with peer dependencies and auto-install-peers: peer-deps-auto-install 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "peer-deps-test", + "dependencies": { + "@angular/animations": "^17.0.0", + "@angular/common": "^17.0.0", + "@angular/core": "^17.0.0", + }, + "optionalDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.0", + }, + }, + }, + "packages": { + "@angular/animations": ["@angular/animations@17.0.8", "", { "dependencies": { "tslib": "2.6.2" }, "peerDependencies": { "@angular/core": "17.0.8" } }, ""], + + "@angular/common": ["@angular/common@17.0.8", "", { "dependencies": { "tslib": "2.6.2" }, "peerDependencies": { "@angular/core": "17.0.8", "rxjs": "7.8.1" } }, ""], + + "@angular/core": ["@angular/core@17.0.8", "", { "dependencies": { "tslib": "2.6.2" }, "peerDependencies": { "rxjs": "7.8.1", "zone.js": "0.14.2" } }, ""], + + "rxjs": ["rxjs@7.8.1", "", { "dependencies": { "tslib": "2.6.2" } }, "sha512-rxjsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "tslib": ["tslib@2.6.2", "", {}, ""], + + "zone.js": ["zone.js@0.14.2", "", {}, "sha512-zoneAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + } +} +" +`; diff --git a/test/cli/install/migration/__snapshots__/pnpm-lock-migration.test.ts.snap b/test/cli/install/migration/__snapshots__/pnpm-lock-migration.test.ts.snap new file mode 100644 index 0000000000..b2f76776d0 --- /dev/null +++ b/test/cli/install/migration/__snapshots__/pnpm-lock-migration.test.ts.snap @@ -0,0 +1,110 @@ +// Bun Snapshot v1, https://bun.sh/docs/test/snapshots + +exports[`pnpm-lock.yaml migration simple pnpm lockfile migration produces correct bun.lock: simple-pnpm-migration 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "simple-pnpm-test", + "dependencies": { + "is-number": "^7.0.0", + "left-pad": "^1.3.0", + }, + }, + }, + "packages": { + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "left-pad": ["left-pad@1.3.0", "", {}, "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA=="], + } +} +" +`; + +exports[`pnpm-lock.yaml migration pnpm workspace lockfile migration: workspace-pnpm-migration 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "monorepo-root", + }, + "apps/web": { + "name": "@repo/web", + "dependencies": { + "@repo/ui": "workspace:*", + "@repo/utils": "workspace:*", + "next": "^14.0.0", + }, + }, + "packages/ui": { + "name": "@repo/ui", + "dependencies": { + "react": "^18.2.0", + }, + }, + "packages/utils": { + "name": "@repo/utils", + "dependencies": { + "lodash": "^4.17.21", + }, + }, + }, + "packages": { + "@repo/ui": ["@repo/ui@workspace:packages/ui"], + + "@repo/utils": ["@repo/utils@workspace:packages/utils"], + + "@repo/web": ["@repo/web@workspace:apps/web"], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "4.0.0" }, "bin": { "loose-envify": "cli.js" } }, ""], + + "next": ["next@14.0.4", "", { "dependencies": { "react": "18.2.0" }, "bin": { "next": "dist/bin/next" } }, "sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA=="], + + "react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "1.4.0" } }, "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="], + } +} +" +`; + +exports[`pnpm-lock.yaml migration pnpm workspace lockfile migration: workspace-pnpm-migration-package-json 1`] = ` +{ + "name": "monorepo-root", + "private": true, + "version": "1.0.0", + "workspaces": [ + "packages/*", + "apps/*", + ], +} +`; + +exports[`pnpm-lock.yaml migration pnpm with npm protocol aliases: npm-aliases-pnpm-migration 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "alias-test", + "dependencies": { + "my-lodash": "npm:lodash@latest", + "my-react": "npm:react@^17.0.0", + }, + }, + }, + "packages": { + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/tNVBQAZ8HW+WqwP25nGsjKeMZk13HGBF7YbJSi1KyeKwGAteWUa/ZKPUKAZNiIrUqZg=="], + + "my-lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "my-react": ["react@17.0.2", "", { "dependencies": { "loose-envify": "1.4.0", "object-assign": "4.1.1" } }, "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnkjVqfO3E+1Q45hXf64UF+6eWwJJCTNJN7q7vfVQqPJZsB/1/vb9TuT9e2vYfqvnMqGCDJ5x6+WUJA=="], + } +} +" +`; diff --git a/test/cli/install/migration/__snapshots__/pnpm-migration-complete.test.ts.snap b/test/cli/install/migration/__snapshots__/pnpm-migration-complete.test.ts.snap new file mode 100644 index 0000000000..c861f9441d --- /dev/null +++ b/test/cli/install/migration/__snapshots__/pnpm-migration-complete.test.ts.snap @@ -0,0 +1,379 @@ +// Bun Snapshot v1, https://bun.sh/docs/test/snapshots + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: basic-dependencies 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "basic-test", + "dependencies": { + "lodash": "^4.17.21", + "react": "^18.2.0", + }, + "devDependencies": { + "typescript": "^5.3.3", + }, + }, + }, + "packages": { + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "1.4.0" } }, "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="], + + "typescript": ["typescript@5.3.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw=="], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: canary-versions 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "canary-test", + "dependencies": { + "react": "19.2.0-canary-a96a0f39-20250815", + "react-dom": "19.2.0-canary-a96a0f39-20250815", + "scheduler": "0.27.0-canary-a96a0f39-20250815", + }, + }, + }, + "packages": { + "react": ["react@19.2.0-canary-a96a0f39-20250815", "", {}, ""], + + "react-dom": ["react-dom@19.2.0-canary-a96a0f39-20250815", "", { "dependencies": { "scheduler": "0.27.0-canary-a96a0f39-20250815" } }, ""], + + "scheduler": ["scheduler@0.27.0-canary-a96a0f39-20250815", "", {}, ""], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: monorepo-workspaces 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "monorepo-root", + "dependencies": { + "@workspace/shared": "workspace:*", + "@workspace/utils": "workspace:^", + }, + }, + "apps/web": { + "name": "web", + "dependencies": { + "@workspace/shared": "workspace:*", + "react": "^18.2.0", + }, + }, + "packages/shared": { + "name": "shared", + "dependencies": { + "@workspace/utils": "workspace:*", + "lodash": "^4.17.21", + }, + }, + "packages/utils": { + "name": "utils", + "dependencies": { + "axios": "^1.6.0", + }, + }, + }, + "packages": { + "@workspace/shared": ["shared@workspace:packages/shared"], + + "@workspace/utils": ["utils@workspace:packages/utils"], + + "axios": ["axios@1.6.7", "", {}, ""], + + "lodash": ["lodash@4.17.21", "", {}, ""], + + "react": ["react@18.2.0", "", {}, ""], + + "shared": ["shared@workspace:packages/shared"], + + "utils": ["utils@workspace:packages/utils"], + + "web": ["web@workspace:apps/web"], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: patches-overrides 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "patches-test", + "dependencies": { + "lodash": "^4.17.21", + }, + }, + }, + "patchedDependencies": { + "lodash@4.17.21": "patches/lodash@4.17.21.patch", + }, + "overrides": { + "axios": "1.6.0", + }, + "packages": { + "lodash": ["lodash@4.17.21", "", {}, ""], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: file-link-deps 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "file-links-test", + "dependencies": { + "config": "file:./shared/config", + "local-pkg": "file:./local-pkg", + }, + }, + }, + "packages": { + "config": ["hi2@file:shared/config", {}], + + "local-pkg": ["hi@file:local-pkg", {}], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: custom-registries 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "registries-test", + "dependencies": { + "@company/private-pkg": "^1.0.0", + "lodash": "^4.17.21", + }, + }, + }, + "packages": { + "@company/private-pkg": ["@company/private-pkg@1.0.5", "", {}, ""], + + "lodash": ["lodash@4.17.21", "", {}, ""], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: peer-dependencies 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "peer-deps-test", + "dependencies": { + "@mui/material": "^5.15.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + }, + }, + }, + "packages": { + "@emotion/react": ["@emotion/react@11.11.3", "", { "peerDependencies": { "react": "18.2.0" } }, ""], + + "@emotion/styled": ["@emotion/styled@11.11.0", "", { "peerDependencies": { "@emotion/react": "11.11.3", "react": "18.2.0" } }, ""], + + "@mui/material": ["@mui/material@5.15.0", "", { "optionalDependencies": { "@emotion/react": "11.11.3", "@emotion/styled": "11.11.0" }, "peerDependencies": { "react": "18.2.0", "react-dom": "18.2.0" } }, ""], + + "react": ["react@18.2.0", "", {}, ""], + + "react-dom": ["react-dom@18.2.0", "", { "peerDependencies": { "react": "18.2.0" } }, "sha512-reactdomAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: duplicate-packages 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "duplicates-test", + "dependencies": { + "lodash": "^4.17.21", + "my-lodash": "npm:lodash@^4.17.20", + "package-a": "^1.0.0", + "package-b": "^1.0.0", + }, + }, + }, + "packages": { + "lodash": ["lodash@4.17.21", "", {}, "sha512-lodash21AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "my-lodash": ["lodash@4.17.20", "", {}, "sha512-lodash20AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "package-a": ["package-a@1.0.0", "", { "dependencies": { "shared-dep": "2.0.0" } }, "sha512-packageAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "package-b": ["package-b@1.0.0", "", { "dependencies": { "shared-dep": "3.0.0" } }, "sha512-packageBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "shared-dep": ["shared-dep@2.0.0", "", {}, ""], + + "package-b/shared-dep": ["shared-dep@3.0.0", "", {}, ""], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: catalogs 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "catalogs-test", + "dependencies": { + "lodash": "4.17.21", + "react": "18.2.0", + }, + }, + }, + "catalog": { + "react": "18.2.0", + }, + "catalogs": { + "tools": { + "lodash": "4.17.21", + }, + }, + "packages": { + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "1.4.0" } }, "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: integrity-hashes 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "integrity-test", + "dependencies": { + "axios": "^1.6.0", + "express": "^4.18.2", + }, + }, + }, + "packages": { + "axios": ["axios@1.6.7", "", {}, "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA=="], + + "express": ["express@4.18.2", "", {}, "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ=="], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: version-zero 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "version-zero-test", + "dependencies": { + "package-with-zero": "0.0.0", + }, + }, + }, + "packages": { + "package-with-zero": ["package-with-zero@0.0.0", "", {}, "sha512-zeroAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: mixed-dependency-types 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "mixed-deps-test", + "dependencies": { + "react": "^18.2.0", + "typescript": "^4.0.0", + }, + "devDependencies": { + "eslint": "^8.56.0", + }, + "optionalDependencies": { + "fsevents": "^2.3.3", + }, + }, + }, + "packages": { + "eslint": ["eslint@8.56.0", "", { "bin": { "eslint": "bin/eslint.js" } }, ""], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-fseventsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + + "react": ["react@18.2.0", "", {}, ""], + + "typescript": ["typescript@4.9.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-ts4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], + } +} +" +`; + +exports[`PNPM Migration Complete Test Suite comprehensive PNPM migration with all edge cases: circular-workspaces 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "circular-test", + "dependencies": { + "@workspace/pkg1": "workspace:*", + }, + }, + "packages/pkg1": { + "name": "@workspace/pkg1", + "dependencies": { + "@workspace/pkg2": "workspace:*", + "lodash": "^4.17.21", + }, + }, + "packages/pkg2": { + "name": "@workspace/pkg2", + "dependencies": { + "@workspace/pkg3": "workspace:*", + }, + }, + "packages/pkg3": { + "name": "@workspace/pkg3", + "dependencies": { + "@workspace/pkg1": "workspace:*", + }, + }, + }, + "packages": { + "@workspace/pkg1": ["@workspace/pkg1@workspace:packages/pkg1"], + + "@workspace/pkg2": ["@workspace/pkg2@workspace:packages/pkg2"], + + "@workspace/pkg3": ["@workspace/pkg3@workspace:packages/pkg3"], + + "lodash": ["lodash@4.17.21", "", {}, ""], + } +} +" +`; diff --git a/test/cli/install/migration/__snapshots__/pnpm-migration.test.ts.snap b/test/cli/install/migration/__snapshots__/pnpm-migration.test.ts.snap new file mode 100644 index 0000000000..cc5930e511 --- /dev/null +++ b/test/cli/install/migration/__snapshots__/pnpm-migration.test.ts.snap @@ -0,0 +1,34 @@ +// Bun Snapshot v1, https://bun.sh/docs/test/snapshots + +exports[`basic: bun.lock 1`] = ` +"{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "worky3", + "dependencies": { + "no-deps": "~1.0.0", + }, + "devDependencies": { + "a-dep-b": "1.0.0", + }, + "optionalDependencies": { + "b-dep-a": "1.0.0", + }, + "peerDependencies": { + "a-dep": "1.0.1", + }, + }, + }, + "packages": { + "a-dep": ["a-dep@1.0.1", "http://localhost:1234/a-dep/-/a-dep-1.0.1.tgz", {}, "sha512-6nmTaPgO2U/uOODqOhbjbnaB4xHuZ+UB7AjKUA3g2dT4WRWeNxgp0dC8Db4swXSnO5/uLLUdFmUJKINNBO/3wg=="], + + "a-dep-b": ["a-dep-b@1.0.0", "http://localhost:1234/a-dep-b/-/a-dep-b-1.0.0.tgz", { "dependencies": { "b-dep-a": "1.0.0" } }, "sha512-PW1l4ruYaxcIw4rMkOVzb9zcR2srZhTPv2H2aH7QFc7vVxkD7EEMGHg1GPT8ycLFb8vriydUXEPwOy1FcbodaQ=="], + + "b-dep-a": ["b-dep-a@1.0.0", "http://localhost:1234/b-dep-a/-/b-dep-a-1.0.0.tgz", { "dependencies": { "a-dep-b": "1.0.0" } }, "sha512-1owp4Wy5QE893BGgjDQGZm9Oayk38MA++fXmPTQA1WY/NFQv7CcCVpK2Ht/4mU4KejDeHOxaAj7qbzv1dSQA2w=="], + + "no-deps": ["no-deps@1.0.1", "http://localhost:1234/no-deps/-/no-deps-1.0.1.tgz", {}, "sha512-3X6cn4+UJdXJuLPu11v8i/fGLe2PdI6v1yKTELam04lY5esCAFdG/qQts6N6rLrL6g1YRq+MKBAwxbmUQk355A=="], + } +} +" +`; diff --git a/test/cli/install/migration/__snapshots__/yarn-lock-migration.test.ts.snap b/test/cli/install/migration/__snapshots__/yarn-lock-migration.test.ts.snap index 262cab1003..3cbbd10486 100644 --- a/test/cli/install/migration/__snapshots__/yarn-lock-migration.test.ts.snap +++ b/test/cli/install/migration/__snapshots__/yarn-lock-migration.test.ts.snap @@ -97,7 +97,7 @@ exports[`yarn.lock migration basic complex yarn.lock with multiple dependencies "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], - "jest": ["jest@29.7.0", "", { "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", "jest-cli": "^29.7.0" } }, "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw=="], + "jest": ["jest@29.7.0", "", { "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" } }, "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw=="], "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], @@ -107,7 +107,7 @@ exports[`yarn.lock migration basic complex yarn.lock with multiple dependencies "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], - "mime": ["mime@1.6.0", "", {}, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], @@ -151,7 +151,7 @@ exports[`yarn.lock migration basic complex yarn.lock with multiple dependencies "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], - "typescript": ["typescript@5.3.3", "", {}, "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw=="], + "typescript": ["typescript@5.3.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw=="], "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], @@ -205,9 +205,9 @@ exports[`yarn.lock migration basic yarn.lock with resolutions: resolutions-yarn- "acorn": "8.11.3", }, "packages": { - "acorn": ["acorn@8.11.3", "", {}, "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg=="], + "acorn": ["acorn@8.11.3", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg=="], - "webpack": ["webpack@5.89.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.0", "@webassemblyjs/ast": "^1.11.5", "@webassemblyjs/wasm-edit": "^1.11.5", "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" } }, "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw=="], + "webpack": ["webpack@5.89.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.0", "@webassemblyjs/ast": "^1.11.5", "@webassemblyjs/wasm-edit": "^1.11.5", "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw=="], } } " @@ -288,13 +288,13 @@ exports[`yarn.lock migration basic migration with realistic complex yarn.lock: c "@types/react": ["@types/react@18.0.28", "", { "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew=="], - "eslint": ["eslint@8.35.0", "", { "dependencies": { "@eslint/eslintrc": "^2.0.0", "@eslint/js": "8.35.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", "espree": "^9.4.0", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" } }, "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw=="], + "eslint": ["eslint@8.35.0", "", { "dependencies": { "@eslint/eslintrc": "^2.0.0", "@eslint/js": "8.35.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", "espree": "^9.4.0", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw=="], "fsevents": ["fsevents@2.3.2", "", {}, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], "react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="], @@ -302,11 +302,11 @@ exports[`yarn.lock migration basic migration with realistic complex yarn.lock: c "scheduler": ["scheduler@0.23.0", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw=="], - "semver": ["semver@6.3.0", "", {}, "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="], + "semver": ["semver@6.3.0", "", { "bin": { "semver": "./bin/semver.js" } }, "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="], - "typescript": ["typescript@4.9.5", "", {}, "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="], + "typescript": ["typescript@4.9.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="], - "webpack": ["webpack@5.76.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", "acorn": "^8.7.1", "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.10.0", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" } }, "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA=="], + "webpack": ["webpack@5.76.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", "acorn": "^8.7.1", "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.10.0", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA=="], } } " @@ -434,7 +434,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="], - "acorn": ["acorn@5.7.1", "", {}, "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ=="], + "acorn": ["acorn@5.7.1", "", { "bin": { "acorn": "./bin/acorn" } }, "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ=="], "acorn-dynamic-import": ["acorn-dynamic-import@2.0.2", "", { "dependencies": { "acorn": "^4.0.3" } }, "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ="], @@ -546,7 +546,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "asynckit": ["asynckit@0.4.0", "", {}, "sha1-x57Zf380y48robyXkLzDZkdLS3k="], - "atob": ["atob@2.1.1", "", {}, "sha1-ri1acpR38onWDdf5amMUoi3Wwio="], + "atob": ["atob@2.1.1", "", { "bin": { "atob": "bin/atob.js" } }, "sha1-ri1acpR38onWDdf5amMUoi3Wwio="], "aws-sign2": ["aws-sign2@0.7.0", "", {}, "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="], @@ -734,7 +734,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "babel-types": ["babel-types@6.26.0", "", { "dependencies": { "babel-runtime": "^6.26.0", "esutils": "^2.0.2", "lodash": "^4.17.4", "to-fast-properties": "^1.0.3" } }, "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc="], - "babylon": ["babylon@6.18.0", "", {}, "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="], + "babylon": ["babylon@6.18.0", "", { "bin": { "babylon": "./bin/babylon.js" } }, "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="], "bach": ["bach@1.2.0", "", { "dependencies": { "arr-filter": "^1.1.1", "arr-flatten": "^1.0.1", "arr-map": "^2.0.0", "array-each": "^1.0.0", "array-initial": "^1.0.0", "array-last": "^1.1.1", "async-done": "^1.2.2", "async-settle": "^1.0.0", "now-and-later": "^2.0.0" } }, "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA="], @@ -778,7 +778,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "browserify-zlib": ["browserify-zlib@0.1.4", "", { "dependencies": { "pako": "~0.2.0" } }, "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0="], - "browserslist": ["browserslist@3.2.8", "", { "dependencies": { "caniuse-lite": "^1.0.30000844", "electron-to-chromium": "^1.3.47" } }, "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ=="], + "browserslist": ["browserslist@3.2.8", "", { "dependencies": { "caniuse-lite": "^1.0.30000844", "electron-to-chromium": "^1.3.47" }, "bin": { "browserslist": "./cli.js" } }, "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ=="], "bser": ["bser@2.0.0", "", { "dependencies": { "node-int64": "^0.4.0" } }, "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk="], @@ -864,7 +864,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "color-name": ["color-name@1.1.1", "", {}, "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok="], - "color-support": ["color-support@1.1.3", "", {}, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="], + "color-support": ["color-support@1.1.3", "", { "bin": { "color-support": "bin.js" } }, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="], "colors": ["colors@1.3.2", "", {}, "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ=="], @@ -872,7 +872,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "commander": ["commander@2.16.0", "", {}, "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew=="], - "commitizen": ["commitizen@2.10.1", "", { "dependencies": { "cachedir": "^1.1.0", "chalk": "1.1.3", "cz-conventional-changelog": "2.0.0", "dedent": "0.6.0", "detect-indent": "4.0.0", "find-node-modules": "1.0.4", "find-root": "1.0.0", "fs-extra": "^1.0.0", "glob": "7.1.1", "inquirer": "1.2.3", "lodash": "4.17.5", "minimist": "1.2.0", "opencollective": "1.0.3", "path-exists": "2.1.0", "shelljs": "0.7.6", "strip-json-comments": "2.0.1" } }, "sha1-jDld7zSolfTpSVLC78PJ60w2g70="], + "commitizen": ["commitizen@2.10.1", "", { "dependencies": { "cachedir": "^1.1.0", "chalk": "1.1.3", "cz-conventional-changelog": "2.0.0", "dedent": "0.6.0", "detect-indent": "4.0.0", "find-node-modules": "1.0.4", "find-root": "1.0.0", "fs-extra": "^1.0.0", "glob": "7.1.1", "inquirer": "1.2.3", "lodash": "4.17.5", "minimist": "1.2.0", "opencollective": "1.0.3", "path-exists": "2.1.0", "shelljs": "0.7.6", "strip-json-comments": "2.0.1" }, "bin": { "git-cz": "./bin/git-cz", "commitizen": "./bin/commitizen" } }, "sha1-jDld7zSolfTpSVLC78PJ60w2g70="], "commondir": ["commondir@1.0.1", "", {}, "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="], @@ -974,7 +974,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "detect-indent": ["detect-indent@5.0.0", "", {}, "sha1-OHHMCmoALow+Wzz38zYmRnXwa50="], - "detect-libc": ["detect-libc@1.0.3", "", {}, "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="], + "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="], "detect-newline": ["detect-newline@2.1.0", "", {}, "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I="], @@ -1012,7 +1012,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "enhanced-resolve": ["enhanced-resolve@3.4.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "memory-fs": "^0.4.0", "object-assign": "^4.0.1", "tapable": "^0.2.7" } }, "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24="], - "errno": ["errno@0.1.7", "", { "dependencies": { "prr": "~1.0.1" } }, "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg=="], + "errno": ["errno@0.1.7", "", { "dependencies": { "prr": "~1.0.1" }, "bin": { "errno": "./cli.js" } }, "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg=="], "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], @@ -1030,9 +1030,9 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="], - "escodegen": ["escodegen@1.11.0", "", { "dependencies": { "esprima": "^3.1.3", "estraverse": "^4.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1" }, "optionalDependencies": { "source-map": "~0.6.1" } }, "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw=="], + "escodegen": ["escodegen@1.11.0", "", { "dependencies": { "esprima": "^3.1.3", "estraverse": "^4.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "./bin/esgenerate.js", "escodegen": "./bin/escodegen.js" } }, "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw=="], - "eslint": ["eslint@4.3.0", "", { "dependencies": { "ajv": "^5.2.0", "babel-code-frame": "^6.22.0", "chalk": "^1.1.3", "concat-stream": "^1.6.0", "cross-spawn": "^5.1.0", "debug": "^2.6.8", "doctrine": "^2.0.0", "eslint-scope": "^3.7.1", "espree": "^3.4.3", "esquery": "^1.0.0", "estraverse": "^4.2.0", "esutils": "^2.0.2", "file-entry-cache": "^2.0.0", "functional-red-black-tree": "^1.0.1", "glob": "^7.1.2", "globals": "^9.17.0", "ignore": "^3.3.3", "imurmurhash": "^0.1.4", "inquirer": "^3.0.6", "is-resolvable": "^1.0.0", "js-yaml": "^3.8.4", "json-stable-stringify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.4", "minimatch": "^3.0.2", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.2", "path-is-inside": "^1.0.2", "pluralize": "^4.0.0", "progress": "^2.0.0", "require-uncached": "^1.0.3", "semver": "^5.3.0", "strip-json-comments": "~2.0.1", "table": "^4.0.1", "text-table": "~0.2.0" } }, "sha1-/NfJY3a780yF7mftABKimWQrEI8="], + "eslint": ["eslint@4.3.0", "", { "dependencies": { "ajv": "^5.2.0", "babel-code-frame": "^6.22.0", "chalk": "^1.1.3", "concat-stream": "^1.6.0", "cross-spawn": "^5.1.0", "debug": "^2.6.8", "doctrine": "^2.0.0", "eslint-scope": "^3.7.1", "espree": "^3.4.3", "esquery": "^1.0.0", "estraverse": "^4.2.0", "esutils": "^2.0.2", "file-entry-cache": "^2.0.0", "functional-red-black-tree": "^1.0.1", "glob": "^7.1.2", "globals": "^9.17.0", "ignore": "^3.3.3", "imurmurhash": "^0.1.4", "inquirer": "^3.0.6", "is-resolvable": "^1.0.0", "js-yaml": "^3.8.4", "json-stable-stringify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.4", "minimatch": "^3.0.2", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.2", "path-is-inside": "^1.0.2", "pluralize": "^4.0.0", "progress": "^2.0.0", "require-uncached": "^1.0.3", "semver": "^5.3.0", "strip-json-comments": "~2.0.1", "table": "^4.0.1", "text-table": "~0.2.0" }, "bin": { "eslint": "./bin/eslint.js" } }, "sha1-/NfJY3a780yF7mftABKimWQrEI8="], "eslint-config-fb-strict": ["eslint-config-fb-strict@22.4.3", "", { "dependencies": { "eslint-config-fbjs": "^2.0.1" } }, "sha512-xGH75nMO69RqDU96KCI/wh58Y3Ej+xLl/zdK5uQKfvf2DRcwRw1JgArCR+9P0SzWIgEzPPEGVxpRPjYW3XfI+w=="], @@ -1064,7 +1064,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "espree": ["espree@3.5.4", "", { "dependencies": { "acorn": "^5.5.0", "acorn-jsx": "^3.0.0" } }, "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A=="], - "esprima": ["esprima@4.0.1", "", {}, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], "esquery": ["esquery@1.0.1", "", { "dependencies": { "estraverse": "^4.0.0" } }, "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA=="], @@ -1146,7 +1146,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "flat-cache": ["flat-cache@1.3.0", "", { "dependencies": { "circular-json": "^0.3.1", "del": "^2.0.2", "graceful-fs": "^4.1.2", "write": "^0.2.1" } }, "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE="], - "flow-bin": ["flow-bin@0.66.0", "", {}, "sha1-qW3ecBXcM0P9VSp7SWPAK+cFyiY="], + "flow-bin": ["flow-bin@0.66.0", "", { "bin": { "flow": "cli.js" } }, "sha1-qW3ecBXcM0P9VSp7SWPAK+cFyiY="], "flush-write-stream": ["flush-write-stream@1.0.3", "", { "dependencies": { "inherits": "^2.0.1", "readable-stream": "^2.0.4" } }, "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw=="], @@ -1194,7 +1194,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "getpass": ["getpass@0.1.7", "", { "dependencies": { "assert-plus": "^1.0.0" } }, "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo="], - "git-release-notes": ["git-release-notes@3.0.0", "", { "dependencies": { "date-fns": "^1.29.0", "debug": "^3.1.0", "ejs": "^2.5.7", "optimist": "^0.6.1" } }, "sha512-FvaIV55dE03hXmD+yUB3ZLyxoiDQZetYw53hX7EPOfr3u+caIIXyUiGilJB+4fF7IjglE4YBY8O4gl3wsviFQA=="], + "git-release-notes": ["git-release-notes@3.0.0", "", { "dependencies": { "date-fns": "^1.29.0", "debug": "^3.1.0", "ejs": "^2.5.7", "optimist": "^0.6.1" }, "bin": { "git-release-notes": "./cli.js" } }, "sha512-FvaIV55dE03hXmD+yUB3ZLyxoiDQZetYw53hX7EPOfr3u+caIIXyUiGilJB+4fF7IjglE4YBY8O4gl3wsviFQA=="], "glob": ["glob@7.1.2", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ=="], @@ -1222,11 +1222,11 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "growly": ["growly@1.3.0", "", {}, "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="], - "gulp": ["gulp@4.0.0", "", { "dependencies": { "glob-watcher": "^5.0.0", "gulp-cli": "^2.0.0", "undertaker": "^1.0.0", "vinyl-fs": "^3.0.0" } }, "sha1-lXZsYB2t5Kd+0+eyttwDiBtZY2Y="], + "gulp": ["gulp@4.0.0", "", { "dependencies": { "glob-watcher": "^5.0.0", "gulp-cli": "^2.0.0", "undertaker": "^1.0.0", "vinyl-fs": "^3.0.0" }, "bin": { "gulp": "./bin/gulp.js" } }, "sha1-lXZsYB2t5Kd+0+eyttwDiBtZY2Y="], "gulp-babel": ["gulp-babel@7.0.1", "", { "dependencies": { "plugin-error": "^1.0.1", "replace-ext": "0.0.1", "through2": "^2.0.0", "vinyl-sourcemaps-apply": "^0.2.0" } }, "sha512-UqHS3AdxZyJCRxqnAX603Dj3k/Wx6hzcgmav3QcxvsIFq3Y8ZkU7iXd0O+JwD5ivqCc6o0r1S7tCB/xxLnuSNw=="], - "gulp-cli": ["gulp-cli@2.0.1", "", { "dependencies": { "ansi-colors": "^1.0.1", "archy": "^1.0.0", "array-sort": "^1.0.0", "color-support": "^1.1.3", "concat-stream": "^1.6.0", "copy-props": "^2.0.1", "fancy-log": "^1.3.2", "gulplog": "^1.0.0", "interpret": "^1.1.0", "isobject": "^3.0.1", "liftoff": "^2.5.0", "matchdep": "^2.0.0", "mute-stdout": "^1.0.0", "pretty-hrtime": "^1.0.0", "replace-homedir": "^1.0.0", "semver-greatest-satisfied-range": "^1.1.0", "v8flags": "^3.0.1", "yargs": "^7.1.0" } }, "sha512-RxujJJdN8/O6IW2nPugl7YazhmrIEjmiVfPKrWt68r71UCaLKS71Hp0gpKT+F6qOUFtr7KqtifDKaAJPRVvMYQ=="], + "gulp-cli": ["gulp-cli@2.0.1", "", { "dependencies": { "ansi-colors": "^1.0.1", "archy": "^1.0.0", "array-sort": "^1.0.0", "color-support": "^1.1.3", "concat-stream": "^1.6.0", "copy-props": "^2.0.1", "fancy-log": "^1.3.2", "gulplog": "^1.0.0", "interpret": "^1.1.0", "isobject": "^3.0.1", "liftoff": "^2.5.0", "matchdep": "^2.0.0", "mute-stdout": "^1.0.0", "pretty-hrtime": "^1.0.0", "replace-homedir": "^1.0.0", "semver-greatest-satisfied-range": "^1.1.0", "v8flags": "^3.0.1", "yargs": "^7.1.0" }, "bin": { "gulp": "bin/gulp.js" } }, "sha512-RxujJJdN8/O6IW2nPugl7YazhmrIEjmiVfPKrWt68r71UCaLKS71Hp0gpKT+F6qOUFtr7KqtifDKaAJPRVvMYQ=="], "gulp-if": ["gulp-if@2.0.2", "", { "dependencies": { "gulp-match": "^1.0.3", "ternary-stream": "^2.0.1", "through2": "^2.0.1" } }, "sha1-pJe351cwBQQcqivIt92jyARE1ik="], @@ -1240,9 +1240,9 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "gulplog": ["gulplog@1.0.0", "", { "dependencies": { "glogg": "^1.0.0" } }, "sha1-4oxNRdBey77YGDY86PnFkmIp/+U="], - "gunzip-maybe": ["gunzip-maybe@1.4.1", "", { "dependencies": { "browserify-zlib": "^0.1.4", "is-deflate": "^1.0.0", "is-gzip": "^1.0.0", "peek-stream": "^1.1.0", "pumpify": "^1.3.3", "through2": "^2.0.3" } }, "sha512-qtutIKMthNJJgeHQS7kZ9FqDq59/Wn0G2HYCRNjpup7yKfVI6/eqwpmroyZGFoCYaG+sW6psNVb4zoLADHpp2g=="], + "gunzip-maybe": ["gunzip-maybe@1.4.1", "", { "dependencies": { "browserify-zlib": "^0.1.4", "is-deflate": "^1.0.0", "is-gzip": "^1.0.0", "peek-stream": "^1.1.0", "pumpify": "^1.3.3", "through2": "^2.0.3" }, "bin": { "gunzip-maybe": "./bin.js" } }, "sha512-qtutIKMthNJJgeHQS7kZ9FqDq59/Wn0G2HYCRNjpup7yKfVI6/eqwpmroyZGFoCYaG+sW6psNVb4zoLADHpp2g=="], - "handlebars": ["handlebars@4.0.11", "", { "dependencies": { "async": "^1.4.0", "optimist": "^0.6.1", "source-map": "^0.4.4" }, "optionalDependencies": { "uglify-js": "^2.6" } }, "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw="], + "handlebars": ["handlebars@4.0.11", "", { "dependencies": { "async": "^1.4.0", "optimist": "^0.6.1", "source-map": "^0.4.4" }, "optionalDependencies": { "uglify-js": "^2.6" }, "bin": { "handlebars": "bin/handlebars" } }, "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw="], "har-schema": ["har-schema@2.0.0", "", {}, "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="], @@ -1294,7 +1294,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "ignore-walk": ["ignore-walk@3.0.1", "", { "dependencies": { "minimatch": "^3.0.4" } }, "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ=="], - "import-local": ["import-local@1.0.0", "", { "dependencies": { "pkg-dir": "^2.0.0", "resolve-cwd": "^2.0.0" } }, "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ=="], + "import-local": ["import-local@1.0.0", "", { "dependencies": { "pkg-dir": "^2.0.0", "resolve-cwd": "^2.0.0" }, "bin": { "import-local-fixture": "fixtures/cli.js" } }, "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ=="], "imports-loader": ["imports-loader@0.8.0", "", { "dependencies": { "loader-utils": "^1.0.2", "source-map": "^0.6.1" } }, "sha512-kXWL7Scp8KQ4552ZcdVTeaQCZSLW+e6nJfp3cwUMB673T7Hr98Xjx5JK+ql7ADlJUvj1JS5O01RLbKoutN5QDQ=="], @@ -1330,7 +1330,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "is-callable": ["is-callable@1.1.4", "", {}, "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA=="], - "is-ci": ["is-ci@1.1.0", "", { "dependencies": { "ci-info": "^1.0.0" } }, "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg=="], + "is-ci": ["is-ci@1.1.0", "", { "dependencies": { "ci-info": "^1.0.0" }, "bin": { "is-ci": "bin.js" } }, "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg=="], "is-data-descriptor": ["is-data-descriptor@1.0.0", "", { "dependencies": { "kind-of": "^6.0.0" } }, "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ=="], @@ -1424,11 +1424,11 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "iterall": ["iterall@1.2.2", "", {}, "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA=="], - "jest": ["jest@22.4.4", "", { "dependencies": { "import-local": "^1.0.0", "jest-cli": "^22.4.4" } }, "sha512-eBhhW8OS/UuX3HxgzNBSVEVhSuRDh39Z1kdYkQVWna+scpgsrD7vSeBI7tmEvsguPDMnfJodW28YBnhv/BzSew=="], + "jest": ["jest@22.4.4", "", { "dependencies": { "import-local": "^1.0.0", "jest-cli": "^22.4.4" }, "bin": { "jest": "./bin/jest.js" } }, "sha512-eBhhW8OS/UuX3HxgzNBSVEVhSuRDh39Z1kdYkQVWna+scpgsrD7vSeBI7tmEvsguPDMnfJodW28YBnhv/BzSew=="], "jest-changed-files": ["jest-changed-files@22.4.3", "", { "dependencies": { "throat": "^4.0.0" } }, "sha512-83Dh0w1aSkUNFhy5d2dvqWxi/y6weDwVVLU6vmK0cV9VpRxPzhTeGimbsbRDSnEoszhF937M4sDLLeS7Cu/Tmw=="], - "jest-cli": ["jest-cli@22.4.4", "", { "dependencies": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.2", "graceful-fs": "^4.1.11", "import-local": "^1.0.0", "is-ci": "^1.0.10", "istanbul-api": "^1.1.14", "istanbul-lib-coverage": "^1.1.1", "istanbul-lib-instrument": "^1.8.0", "istanbul-lib-source-maps": "^1.2.1", "jest-changed-files": "^22.2.0", "jest-config": "^22.4.4", "jest-environment-jsdom": "^22.4.1", "jest-get-type": "^22.1.0", "jest-haste-map": "^22.4.2", "jest-message-util": "^22.4.0", "jest-regex-util": "^22.1.0", "jest-resolve-dependencies": "^22.1.0", "jest-runner": "^22.4.4", "jest-runtime": "^22.4.4", "jest-snapshot": "^22.4.0", "jest-util": "^22.4.1", "jest-validate": "^22.4.4", "jest-worker": "^22.2.2", "micromatch": "^2.3.11", "node-notifier": "^5.2.1", "realpath-native": "^1.0.0", "rimraf": "^2.5.4", "slash": "^1.0.0", "string-length": "^2.0.0", "strip-ansi": "^4.0.0", "which": "^1.2.12", "yargs": "^10.0.3" } }, "sha512-I9dsgkeyjVEEZj9wrGrqlH+8OlNob9Iptyl+6L5+ToOLJmHm4JwOPatin1b2Bzp5R5YRQJ+oiedx7o1H7wJzhA=="], + "jest-cli": ["jest-cli@22.4.4", "", { "dependencies": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.2", "graceful-fs": "^4.1.11", "import-local": "^1.0.0", "is-ci": "^1.0.10", "istanbul-api": "^1.1.14", "istanbul-lib-coverage": "^1.1.1", "istanbul-lib-instrument": "^1.8.0", "istanbul-lib-source-maps": "^1.2.1", "jest-changed-files": "^22.2.0", "jest-config": "^22.4.4", "jest-environment-jsdom": "^22.4.1", "jest-get-type": "^22.1.0", "jest-haste-map": "^22.4.2", "jest-message-util": "^22.4.0", "jest-regex-util": "^22.1.0", "jest-resolve-dependencies": "^22.1.0", "jest-runner": "^22.4.4", "jest-runtime": "^22.4.4", "jest-snapshot": "^22.4.0", "jest-util": "^22.4.1", "jest-validate": "^22.4.4", "jest-worker": "^22.2.2", "micromatch": "^2.3.11", "node-notifier": "^5.2.1", "realpath-native": "^1.0.0", "rimraf": "^2.5.4", "slash": "^1.0.0", "string-length": "^2.0.0", "strip-ansi": "^4.0.0", "which": "^1.2.12", "yargs": "^10.0.3" }, "bin": { "jest": "./bin/jest.js" } }, "sha512-I9dsgkeyjVEEZj9wrGrqlH+8OlNob9Iptyl+6L5+ToOLJmHm4JwOPatin1b2Bzp5R5YRQJ+oiedx7o1H7wJzhA=="], "jest-config": ["jest-config@22.4.4", "", { "dependencies": { "chalk": "^2.0.1", "glob": "^7.1.1", "jest-environment-jsdom": "^22.4.1", "jest-environment-node": "^22.4.1", "jest-get-type": "^22.1.0", "jest-jasmine2": "^22.4.4", "jest-regex-util": "^22.1.0", "jest-resolve": "^22.4.2", "jest-util": "^22.4.1", "jest-validate": "^22.4.4", "pretty-format": "^22.4.0" } }, "sha512-9CKfo1GC4zrXSoMLcNeDvQBfgtqGTB1uP8iDIZ97oB26RCUb886KkKWhVcpyxVDOUxbhN+uzcBCeFe7w+Iem4A=="], @@ -1462,7 +1462,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "jest-runner": ["jest-runner@22.4.4", "", { "dependencies": { "exit": "^0.1.2", "jest-config": "^22.4.4", "jest-docblock": "^22.4.0", "jest-haste-map": "^22.4.2", "jest-jasmine2": "^22.4.4", "jest-leak-detector": "^22.4.0", "jest-message-util": "^22.4.0", "jest-runtime": "^22.4.4", "jest-util": "^22.4.1", "jest-worker": "^22.2.2", "throat": "^4.0.0" } }, "sha512-5S/OpB51igQW9xnkM5Tgd/7ZjiAuIoiJAVtvVTBcEBiXBIFzWM3BAMPBM19FX68gRV0KWyFuGKj0EY3M3aceeQ=="], - "jest-runtime": ["jest-runtime@22.4.4", "", { "dependencies": { "babel-core": "^6.0.0", "babel-jest": "^22.4.4", "babel-plugin-istanbul": "^4.1.5", "chalk": "^2.0.1", "convert-source-map": "^1.4.0", "exit": "^0.1.2", "graceful-fs": "^4.1.11", "jest-config": "^22.4.4", "jest-haste-map": "^22.4.2", "jest-regex-util": "^22.1.0", "jest-resolve": "^22.4.2", "jest-util": "^22.4.1", "jest-validate": "^22.4.4", "json-stable-stringify": "^1.0.1", "micromatch": "^2.3.11", "realpath-native": "^1.0.0", "slash": "^1.0.0", "strip-bom": "3.0.0", "write-file-atomic": "^2.1.0", "yargs": "^10.0.3" } }, "sha512-WRTj9m///npte1YjuphCYX7GRY/c2YvJImU9t7qOwFcqHr4YMzmX6evP/3Sehz5DKW2Vi8ONYPCFWe36JVXxfw=="], + "jest-runtime": ["jest-runtime@22.4.4", "", { "dependencies": { "babel-core": "^6.0.0", "babel-jest": "^22.4.4", "babel-plugin-istanbul": "^4.1.5", "chalk": "^2.0.1", "convert-source-map": "^1.4.0", "exit": "^0.1.2", "graceful-fs": "^4.1.11", "jest-config": "^22.4.4", "jest-haste-map": "^22.4.2", "jest-regex-util": "^22.1.0", "jest-resolve": "^22.4.2", "jest-util": "^22.4.1", "jest-validate": "^22.4.4", "json-stable-stringify": "^1.0.1", "micromatch": "^2.3.11", "realpath-native": "^1.0.0", "slash": "^1.0.0", "strip-bom": "3.0.0", "write-file-atomic": "^2.1.0", "yargs": "^10.0.3" }, "bin": { "jest-runtime": "./bin/jest-runtime.js" } }, "sha512-WRTj9m///npte1YjuphCYX7GRY/c2YvJImU9t7qOwFcqHr4YMzmX6evP/3Sehz5DKW2Vi8ONYPCFWe36JVXxfw=="], "jest-serializer": ["jest-serializer@22.4.3", "", {}, "sha512-uPaUAppx4VUfJ0QDerpNdF43F68eqKWCzzhUlKNDsUPhjOon7ZehR4C809GCqh765FoMRtTVUVnGvIoskkYHiw=="], @@ -1476,15 +1476,15 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "js-tokens": ["js-tokens@3.0.2", "", {}, "sha1-mGbfOVECEw449/mWvOtlRDIJwls="], - "js-yaml": ["js-yaml@3.13.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw=="], + "js-yaml": ["js-yaml@3.13.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw=="], "jsbn": ["jsbn@0.1.1", "", {}, "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="], "jsdom": ["jsdom@11.12.0", "", { "dependencies": { "abab": "^2.0.0", "acorn": "^5.5.3", "acorn-globals": "^4.1.0", "array-equal": "^1.0.0", "cssom": ">= 0.3.2 < 0.4.0", "cssstyle": "^1.0.0", "data-urls": "^1.0.0", "domexception": "^1.0.1", "escodegen": "^1.9.1", "html-encoding-sniffer": "^1.0.2", "left-pad": "^1.3.0", "nwsapi": "^2.0.7", "parse5": "4.0.0", "pn": "^1.1.0", "request": "^2.87.0", "request-promise-native": "^1.0.5", "sax": "^1.2.4", "symbol-tree": "^3.2.2", "tough-cookie": "^2.3.4", "w3c-hr-time": "^1.0.1", "webidl-conversions": "^4.0.2", "whatwg-encoding": "^1.0.3", "whatwg-mimetype": "^2.1.0", "whatwg-url": "^6.4.1", "ws": "^5.2.0", "xml-name-validator": "^3.0.0" } }, "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw=="], - "jsesc": ["jsesc@1.3.0", "", {}, "sha1-RsP+yMGJKxKwgz25vHYiF226s0s="], + "jsesc": ["jsesc@1.3.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha1-RsP+yMGJKxKwgz25vHYiF226s0s="], - "jsinspect": ["jsinspect@0.12.7", "", { "dependencies": { "babylon": "6.16.1", "chalk": "^2.1.0", "commander": "^2.11.0", "filepaths": "0.3.0", "stable": "^0.1.6", "strip-indent": "^1.0.1", "strip-json-comments": "1.0.2" } }, "sha512-9pLr5r5moX3XhACEg/nhIlprBuqRDT+loYigZo7hidmfOj0EV2l6ZMk6gmaNMiX6o1YCMod1lWSH3JoX80QHLA=="], + "jsinspect": ["jsinspect@0.12.7", "", { "dependencies": { "babylon": "6.16.1", "chalk": "^2.1.0", "commander": "^2.11.0", "filepaths": "0.3.0", "stable": "^0.1.6", "strip-indent": "^1.0.1", "strip-json-comments": "1.0.2" }, "bin": { "jsinspect": "./bin/jsinspect" } }, "sha512-9pLr5r5moX3XhACEg/nhIlprBuqRDT+loYigZo7hidmfOj0EV2l6ZMk6gmaNMiX6o1YCMod1lWSH3JoX80QHLA=="], "json-loader": ["json-loader@0.5.7", "", {}, "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w=="], @@ -1496,7 +1496,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="], - "json5": ["json5@0.5.1", "", {}, "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="], + "json5": ["json5@0.5.1", "", { "bin": { "json5": "lib/cli.js" } }, "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="], "jsonfile": ["jsonfile@2.4.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha1-NzaitCi4e72gzIO1P6PWM6NcKug="], @@ -1556,7 +1556,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "longest": ["longest@1.0.1", "", {}, "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="], - "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], "loud-rejection": ["loud-rejection@1.6.0", "", { "dependencies": { "currently-unhandled": "^0.4.1", "signal-exit": "^3.0.0" } }, "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8="], @@ -1590,7 +1590,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "micromatch": ["micromatch@2.3.11", "", { "dependencies": { "arr-diff": "^2.0.0", "array-unique": "^0.2.1", "braces": "^1.8.2", "expand-brackets": "^0.1.4", "extglob": "^0.3.1", "filename-regex": "^2.0.0", "is-extglob": "^1.0.0", "is-glob": "^2.0.1", "kind-of": "^3.0.2", "normalize-path": "^2.0.1", "object.omit": "^2.0.0", "parse-glob": "^3.0.4", "regex-cache": "^0.4.2" } }, "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU="], - "miller-rabin": ["miller-rabin@4.0.1", "", { "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" } }, "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA=="], + "miller-rabin": ["miller-rabin@4.0.1", "", { "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" }, "bin": { "miller-rabin": "bin/miller-rabin" } }, "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA=="], "mime-db": ["mime-db@1.35.0", "", {}, "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg=="], @@ -1612,7 +1612,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "mixin-deep": ["mixin-deep@1.3.1", "", { "dependencies": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" } }, "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ=="], - "mkdirp": ["mkdirp@0.5.1", "", { "dependencies": { "minimist": "0.0.8" } }, "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM="], + "mkdirp": ["mkdirp@0.5.1", "", { "dependencies": { "minimist": "0.0.8" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM="], "mkdirp-promise": ["mkdirp-promise@5.0.1", "", { "dependencies": { "mkdirp": "*" } }, "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE="], @@ -1632,7 +1632,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "natural-compare": ["natural-compare@1.4.0", "", {}, "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc="], - "needle": ["needle@2.2.1", "", { "dependencies": { "debug": "^2.1.2", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "sha512-t/ZswCM9JTWjAdXS9VpvqhI2Ct2sL2MdY4fUXqGJaGBk13ge99ObqRksRTbBE56K+wxUXwwfZYOuZHifFW9q+Q=="], + "needle": ["needle@2.2.1", "", { "dependencies": { "debug": "^2.1.2", "iconv-lite": "^0.4.4", "sax": "^1.2.4" }, "bin": { "needle": "./bin/needle" } }, "sha512-t/ZswCM9JTWjAdXS9VpvqhI2Ct2sL2MdY4fUXqGJaGBk13ge99ObqRksRTbBE56K+wxUXwwfZYOuZHifFW9q+Q=="], "neo-async": ["neo-async@2.5.1", "", {}, "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA=="], @@ -1650,9 +1650,9 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "node-notifier": ["node-notifier@5.2.1", "", { "dependencies": { "growly": "^1.3.0", "semver": "^5.4.1", "shellwords": "^0.1.1", "which": "^1.3.0" } }, "sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg=="], - "node-pre-gyp": ["node-pre-gyp@0.10.3", "", { "dependencies": { "detect-libc": "^1.0.2", "mkdirp": "^0.5.1", "needle": "^2.2.1", "nopt": "^4.0.1", "npm-packlist": "^1.1.6", "npmlog": "^4.0.2", "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", "tar": "^4" } }, "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A=="], + "node-pre-gyp": ["node-pre-gyp@0.10.3", "", { "dependencies": { "detect-libc": "^1.0.2", "mkdirp": "^0.5.1", "needle": "^2.2.1", "nopt": "^4.0.1", "npm-packlist": "^1.1.6", "npmlog": "^4.0.2", "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", "tar": "^4" }, "bin": { "node-pre-gyp": "./bin/node-pre-gyp" } }, "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A=="], - "nopt": ["nopt@4.0.1", "", { "dependencies": { "abbrev": "1", "osenv": "^0.1.4" } }, "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00="], + "nopt": ["nopt@4.0.1", "", { "dependencies": { "abbrev": "1", "osenv": "^0.1.4" }, "bin": { "nopt": "./bin/nopt.js" } }, "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00="], "normalize-package-data": ["normalize-package-data@2.4.0", "", { "dependencies": { "hosted-git-info": "^2.1.4", "is-builtin-module": "^1.0.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw=="], @@ -1706,7 +1706,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "onetime": ["onetime@2.0.1", "", { "dependencies": { "mimic-fn": "^1.0.0" } }, "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ="], - "opencollective": ["opencollective@1.0.3", "", { "dependencies": { "babel-polyfill": "6.23.0", "chalk": "1.1.3", "inquirer": "3.0.6", "minimist": "1.2.0", "node-fetch": "1.6.3", "opn": "4.0.2" } }, "sha1-ruY3K8KBRFg2kMPKja7PwSDdDvE="], + "opencollective": ["opencollective@1.0.3", "", { "dependencies": { "babel-polyfill": "6.23.0", "chalk": "1.1.3", "inquirer": "3.0.6", "minimist": "1.2.0", "node-fetch": "1.6.3", "opn": "4.0.2" }, "bin": { "opencollective": "./dist/bin/opencollective.js", "oc": "./dist/bin/opencollective.js" } }, "sha1-ruY3K8KBRFg2kMPKja7PwSDdDvE="], "opn": ["opn@4.0.2", "", { "dependencies": { "object-assign": "^4.0.1", "pinkie-promise": "^2.0.0" } }, "sha1-erwi5kTf9jsKltWrfyeQwPAavJU="], @@ -1804,7 +1804,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "preserve": ["preserve@0.2.0", "", {}, "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks="], - "prettier": ["prettier@1.5.2", "", {}, "sha512-f55mvineQ5yc36cLX4n4RWP6JH6MLcfi5f9MVsjpfBs4MVSG2GYT4v6cukzmvkIOvmNOdCZfDSMY3hQcMcDQbQ=="], + "prettier": ["prettier@1.5.2", "", { "bin": { "prettier": "./bin/prettier.js" } }, "sha512-f55mvineQ5yc36cLX4n4RWP6JH6MLcfi5f9MVsjpfBs4MVSG2GYT4v6cukzmvkIOvmNOdCZfDSMY3hQcMcDQbQ=="], "pretty-format": ["pretty-format@22.4.3", "", { "dependencies": { "ansi-regex": "^3.0.0", "ansi-styles": "^3.2.0" } }, "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ=="], @@ -1852,7 +1852,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "randomfill": ["randomfill@1.0.4", "", { "dependencies": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" } }, "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw=="], - "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], "read": ["read@1.0.7", "", { "dependencies": { "mute-stream": "~0.0.4" } }, "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ="], @@ -1882,7 +1882,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "regjsgen": ["regjsgen@0.2.0", "", {}, "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc="], - "regjsparser": ["regjsparser@0.1.5", "", { "dependencies": { "jsesc": "~0.5.0" } }, "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw="], + "regjsparser": ["regjsparser@0.1.5", "", { "dependencies": { "jsesc": "~0.5.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw="], "remove-bom-buffer": ["remove-bom-buffer@3.0.0", "", { "dependencies": { "is-buffer": "^1.1.5", "is-utf8": "^0.2.1" } }, "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ=="], @@ -1938,7 +1938,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "right-pad": ["right-pad@1.0.1", "", {}, "sha1-jKCMLLtbVedNr6lr9/0aJ9VoyNA="], - "rimraf": ["rimraf@2.6.2", "", { "dependencies": { "glob": "^7.0.5" } }, "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w=="], + "rimraf": ["rimraf@2.6.2", "", { "dependencies": { "glob": "^7.0.5" }, "bin": { "rimraf": "./bin.js" } }, "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w=="], "ripemd160": ["ripemd160@2.0.2", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" } }, "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA=="], @@ -1960,13 +1960,13 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "sane": ["sane@2.5.2", "", { "dependencies": { "anymatch": "^2.0.0", "capture-exit": "^1.2.0", "exec-sh": "^0.2.0", "fb-watchman": "^2.0.0", "micromatch": "^3.1.4", "minimist": "^1.1.1", "walker": "~1.0.5", "watch": "~0.18.0" }, "optionalDependencies": { "fsevents": "^1.2.3" } }, "sha1-tNwYYcIbQn6SlQej51HiosuKs/o="], + "sane": ["sane@2.5.2", "", { "dependencies": { "anymatch": "^2.0.0", "capture-exit": "^1.2.0", "exec-sh": "^0.2.0", "fb-watchman": "^2.0.0", "micromatch": "^3.1.4", "minimist": "^1.1.1", "walker": "~1.0.5", "watch": "~0.18.0" }, "optionalDependencies": { "fsevents": "^1.2.3" }, "bin": { "sane": "./src/cli.js" } }, "sha1-tNwYYcIbQn6SlQej51HiosuKs/o="], "sax": ["sax@1.2.4", "", {}, "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="], "schema-utils": ["schema-utils@0.4.7", "", { "dependencies": { "ajv": "^6.1.0", "ajv-keywords": "^3.1.0" } }, "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ=="], - "semver": ["semver@5.5.0", "", {}, "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="], + "semver": ["semver@5.5.0", "", { "bin": { "semver": "./bin/semver" } }, "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="], "semver-greatest-satisfied-range": ["semver-greatest-satisfied-range@1.1.0", "", { "dependencies": { "sver-compat": "^1.5.0" } }, "sha1-E+jCZYq5aRywzXEJMkAoDTb3els="], @@ -1978,13 +1978,13 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "setimmediate": ["setimmediate@1.0.5", "", {}, "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="], - "sha.js": ["sha.js@2.4.11", "", { "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ=="], + "sha.js": ["sha.js@2.4.11", "", { "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" }, "bin": { "sha.js": "./bin.js" } }, "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ=="], "shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "^1.0.0" } }, "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo="], "shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="], - "shelljs": ["shelljs@0.7.6", "", { "dependencies": { "glob": "^7.0.0", "interpret": "^1.0.0", "rechoir": "^0.6.2" } }, "sha1-N5zM+1a5HIYB5HkzVutTgpJN6a0="], + "shelljs": ["shelljs@0.7.6", "", { "dependencies": { "glob": "^7.0.0", "interpret": "^1.0.0", "rechoir": "^0.6.2" }, "bin": { "shjs": "./bin/shjs" } }, "sha1-N5zM+1a5HIYB5HkzVutTgpJN6a0="], "shellwords": ["shellwords@0.1.1", "", {}, "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww=="], @@ -2028,7 +2028,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="], - "sshpk": ["sshpk@1.14.2", "", { "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "dashdash": "^1.12.0", "getpass": "^0.1.1", "safer-buffer": "^2.0.2" }, "optionalDependencies": { "bcrypt-pbkdf": "^1.0.0", "ecc-jsbn": "~0.1.1", "jsbn": "~0.1.0", "tweetnacl": "~0.14.0" } }, "sha1-xvxhZIo9nE52T9P8306hBeSSupg="], + "sshpk": ["sshpk@1.14.2", "", { "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "dashdash": "^1.12.0", "getpass": "^0.1.1", "safer-buffer": "^2.0.2" }, "optionalDependencies": { "bcrypt-pbkdf": "^1.0.0", "ecc-jsbn": "~0.1.1", "jsbn": "~0.1.0", "tweetnacl": "~0.14.0" }, "bin": { "sshpk-conv": "bin/sshpk-conv", "sshpk-sign": "bin/sshpk-sign", "sshpk-verify": "bin/sshpk-verify" } }, "sha1-xvxhZIo9nE52T9P8306hBeSSupg="], "ssri": ["ssri@5.3.0", "", { "dependencies": { "safe-buffer": "^5.1.1" } }, "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ=="], @@ -2068,7 +2068,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "strip-eof": ["strip-eof@1.0.0", "", {}, "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="], - "strip-indent": ["strip-indent@1.0.1", "", { "dependencies": { "get-stdin": "^4.0.1" } }, "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI="], + "strip-indent": ["strip-indent@1.0.1", "", { "dependencies": { "get-stdin": "^4.0.1" }, "bin": { "strip-indent": "cli.js" } }, "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI="], "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha1-PFMZQukIwml8DsNEhYwobHygpgo="], @@ -2152,7 +2152,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "typedarray": ["typedarray@0.0.6", "", {}, "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="], - "uglify-js": ["uglify-js@2.8.29", "", { "dependencies": { "source-map": "~0.5.1", "yargs": "~3.10.0" }, "optionalDependencies": { "uglify-to-browserify": "~1.0.0" } }, "sha1-KcVzMUgFe7Th913zW3qcty5qWd0="], + "uglify-js": ["uglify-js@2.8.29", "", { "dependencies": { "source-map": "~0.5.1", "yargs": "~3.10.0" }, "optionalDependencies": { "uglify-to-browserify": "~1.0.0" }, "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha1-KcVzMUgFe7Th913zW3qcty5qWd0="], "uglify-to-browserify": ["uglify-to-browserify@1.0.2", "", {}, "sha1-bgkk1r2mta/jSeOabWMoUKD4grc="], @@ -2184,7 +2184,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "util.promisify": ["util.promisify@1.0.0", "", { "dependencies": { "define-properties": "^1.1.2", "object.getownpropertydescriptors": "^2.0.3" } }, "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA=="], - "uuid": ["uuid@3.3.2", "", {}, "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="], + "uuid": ["uuid@3.3.2", "", { "bin": { "uuid": "./bin/uuid" } }, "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="], "v8-compile-cache": ["v8-compile-cache@2.0.0", "", {}, "sha512-qNdTUMaCjPs4eEnM3W9H94R3sU70YCuT+/ST7nUf+id1bVOrdjrpUaeZLqPBPRph3hsgn4a4BvwpxhHZx+oSDg=="], @@ -2210,13 +2210,13 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "walker": ["walker@1.0.7", "", { "dependencies": { "makeerror": "1.0.x" } }, "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs="], - "watch": ["watch@0.18.0", "", { "dependencies": { "exec-sh": "^0.2.0", "minimist": "^1.2.0" } }, "sha1-KAlUdsbffJDJYxOJkMClQj60uYY="], + "watch": ["watch@0.18.0", "", { "dependencies": { "exec-sh": "^0.2.0", "minimist": "^1.2.0" }, "bin": { "watch": "./cli.js" } }, "sha1-KAlUdsbffJDJYxOJkMClQj60uYY="], "watchpack": ["watchpack@1.6.0", "", { "dependencies": { "chokidar": "^2.0.2", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0" } }, "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA=="], "webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="], - "webpack": ["webpack@2.7.0", "", { "dependencies": { "acorn": "^5.0.0", "acorn-dynamic-import": "^2.0.0", "ajv": "^4.7.0", "ajv-keywords": "^1.1.1", "async": "^2.1.2", "enhanced-resolve": "^3.3.0", "interpret": "^1.0.0", "json-loader": "^0.5.4", "json5": "^0.5.1", "loader-runner": "^2.3.0", "loader-utils": "^0.2.16", "memory-fs": "~0.4.1", "mkdirp": "~0.5.0", "node-libs-browser": "^2.0.0", "source-map": "^0.5.3", "supports-color": "^3.1.0", "tapable": "~0.2.5", "uglify-js": "^2.8.27", "watchpack": "^1.3.1", "webpack-sources": "^1.0.1", "yargs": "^6.0.0" } }, "sha512-MjAA0ZqO1ba7ZQJRnoCdbM56mmFpipOPUv/vQpwwfSI42p5PVDdoiuK2AL2FwFUVgT859Jr43bFZXRg/LNsqvg=="], + "webpack": ["webpack@2.7.0", "", { "dependencies": { "acorn": "^5.0.0", "acorn-dynamic-import": "^2.0.0", "ajv": "^4.7.0", "ajv-keywords": "^1.1.1", "async": "^2.1.2", "enhanced-resolve": "^3.3.0", "interpret": "^1.0.0", "json-loader": "^0.5.4", "json5": "^0.5.1", "loader-runner": "^2.3.0", "loader-utils": "^0.2.16", "memory-fs": "~0.4.1", "mkdirp": "~0.5.0", "node-libs-browser": "^2.0.0", "source-map": "^0.5.3", "supports-color": "^3.1.0", "tapable": "~0.2.5", "uglify-js": "^2.8.27", "watchpack": "^1.3.1", "webpack-sources": "^1.0.1", "yargs": "^6.0.0" }, "bin": { "webpack": "./bin/webpack.js" } }, "sha512-MjAA0ZqO1ba7ZQJRnoCdbM56mmFpipOPUv/vQpwwfSI42p5PVDdoiuK2AL2FwFUVgT859Jr43bFZXRg/LNsqvg=="], "webpack-sources": ["webpack-sources@1.1.0", "", { "dependencies": { "source-list-map": "^2.0.0", "source-map": "~0.6.1" } }, "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw=="], @@ -2226,7 +2226,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "whatwg-url": ["whatwg-url@6.5.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ=="], - "which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], + "which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], "which-module": ["which-module@1.0.0", "", {}, "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8="], @@ -2266,9 +2266,9 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "@gulp-sourcemaps/identity-map/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - "acorn-dynamic-import/acorn": ["acorn@4.0.13", "", {}, "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="], + "acorn-dynamic-import/acorn": ["acorn@4.0.13", "", { "bin": { "acorn": "./bin/acorn" } }, "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="], - "acorn-jsx/acorn": ["acorn@3.3.0", "", {}, "sha1-ReN/s56No/JbruP/U2niu18iAXo="], + "acorn-jsx/acorn": ["acorn@3.3.0", "", { "bin": { "acorn": "./bin/acorn" } }, "sha1-ReN/s56No/JbruP/U2niu18iAXo="], "anymatch/micromatch": ["micromatch@3.1.10", "", { "dependencies": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", "braces": "^2.3.1", "define-property": "^2.0.2", "extend-shallow": "^3.0.2", "extglob": "^2.0.4", "fragment-cache": "^0.2.1", "kind-of": "^6.0.2", "nanomatch": "^1.2.9", "object.pick": "^1.3.0", "regex-not": "^1.0.0", "snapdragon": "^0.8.1", "to-regex": "^3.0.2" } }, "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg=="], @@ -2330,7 +2330,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "encoding/iconv-lite": ["iconv-lite@0.4.23", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA=="], - "escodegen/esprima": ["esprima@3.1.3", "", {}, "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="], + "escodegen/esprima": ["esprima@3.1.3", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="], "escodegen/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -2340,7 +2340,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "eslint/inquirer": ["inquirer@3.3.0", "", { "dependencies": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.0", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", "external-editor": "^2.0.4", "figures": "^2.0.0", "lodash": "^4.3.0", "mute-stream": "0.0.7", "run-async": "^2.2.0", "rx-lite": "^4.0.8", "rx-lite-aggregates": "^4.0.8", "string-width": "^2.1.0", "strip-ansi": "^4.0.0", "through": "^2.3.6" } }, "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ=="], - "eslint/js-yaml": ["js-yaml@3.12.0", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A=="], + "eslint/js-yaml": ["js-yaml@3.12.0", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A=="], "execa/cross-spawn": ["cross-spawn@6.0.5", "", { "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ=="], @@ -2404,7 +2404,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "istanbul-api/istanbul-lib-source-maps": ["istanbul-lib-source-maps@1.2.5", "", { "dependencies": { "debug": "^3.1.0", "istanbul-lib-coverage": "^1.2.0", "mkdirp": "^0.5.1", "rimraf": "^2.6.1", "source-map": "^0.5.3" } }, "sha512-8O2T/3VhrQHn0XcJbP1/GN7kXMiRAlPi+fj3uEHrjBD8Oz7Py0prSC25C09NuAZS6bgW1NNKAvCSHZXB0irSGA=="], - "istanbul-api/js-yaml": ["js-yaml@3.12.0", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A=="], + "istanbul-api/js-yaml": ["js-yaml@3.12.0", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A=="], "jest-cli/yargs": ["yargs@10.1.2", "", { "dependencies": { "cliui": "^4.0.0", "decamelize": "^1.1.1", "find-up": "^2.1.0", "get-caller-file": "^1.0.1", "os-locale": "^2.0.0", "require-directory": "^2.1.1", "require-main-filename": "^1.0.1", "set-blocking": "^2.0.0", "string-width": "^2.0.0", "which-module": "^2.0.0", "y18n": "^3.2.1", "yargs-parser": "^8.1.0" } }, "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig=="], @@ -2422,9 +2422,9 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "jsdom/tough-cookie": ["tough-cookie@2.4.3", "", { "dependencies": { "psl": "^1.1.24", "punycode": "^1.4.1" } }, "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ=="], - "jsinspect/babylon": ["babylon@6.16.1", "", {}, "sha1-MMWiL0gZeKnn+M399JaxHZS0BNM="], + "jsinspect/babylon": ["babylon@6.16.1", "", { "bin": { "babylon": "./bin/babylon.js" } }, "sha1-MMWiL0gZeKnn+M399JaxHZS0BNM="], - "jsinspect/strip-json-comments": ["strip-json-comments@1.0.2", "", {}, "sha1-WkirlgI9usG3uND/q/b2PxZ3vp8="], + "jsinspect/strip-json-comments": ["strip-json-comments@1.0.2", "", { "bin": { "strip-json-comments": "cli.js" } }, "sha1-WkirlgI9usG3uND/q/b2PxZ3vp8="], "liftoff/findup-sync": ["findup-sync@2.0.0", "", { "dependencies": { "detect-file": "^1.0.0", "is-glob": "^3.1.0", "micromatch": "^3.0.4", "resolve-dir": "^1.0.1" } }, "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw="], @@ -2482,7 +2482,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "randomatic/kind-of": ["kind-of@6.0.2", "", {}, "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="], - "regjsparser/jsesc": ["jsesc@0.5.0", "", {}, "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0="], + "regjsparser/jsesc": ["jsesc@0.5.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0="], "request-promise-native/tough-cookie": ["tough-cookie@2.4.3", "", { "dependencies": { "psl": "^1.1.24", "punycode": "^1.4.1" } }, "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ=="], @@ -2520,7 +2520,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "tar/yallist": ["yallist@3.0.2", "", {}, "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="], - "temp/rimraf": ["rimraf@2.2.8", "", {}, "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI="], + "temp/rimraf": ["rimraf@2.2.8", "", { "bin": { "rimraf": "./bin.js" } }, "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI="], "test-exclude/micromatch": ["micromatch@3.1.10", "", { "dependencies": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", "braces": "^2.3.1", "define-property": "^2.0.2", "extend-shallow": "^3.0.2", "extglob": "^2.0.4", "fragment-cache": "^0.2.1", "kind-of": "^6.0.2", "nanomatch": "^1.2.9", "object.pick": "^1.3.0", "regex-not": "^1.0.0", "snapdragon": "^0.8.1", "to-regex": "^3.0.2" } }, "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg=="], @@ -2740,7 +2740,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-cli-repo: yarn-cli-repo 1`] "string-replace-loader/loader-utils/big.js": ["big.js@5.2.2", "", {}, "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="], - "string-replace-loader/loader-utils/json5": ["json5@1.0.1", "", { "dependencies": { "minimist": "^1.2.0" } }, "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow=="], + "string-replace-loader/loader-utils/json5": ["json5@1.0.1", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow=="], "string-width/strip-ansi/ansi-regex": ["ansi-regex@2.1.1", "", {}, "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="], @@ -3067,7 +3067,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-lock-mkdirp: yarn-lock-mkdir }, }, "packages": { - "mkdirp": ["mkdirp@1.0.4", "", {}, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], + "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], } } " @@ -3101,7 +3101,7 @@ exports[`bun pm migrate for existing yarn.lock yarn-lock-mkdirp-no-resolved: yar }, }, "packages": { - "mkdirp": ["mkdirp@1.0.4", "", {}, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], + "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], } } " diff --git a/test/cli/install/migration/pnpm-comprehensive.test.ts b/test/cli/install/migration/pnpm-comprehensive.test.ts new file mode 100644 index 0000000000..f8b7df5f03 --- /dev/null +++ b/test/cli/install/migration/pnpm-comprehensive.test.ts @@ -0,0 +1,1059 @@ +import { describe, expect, test } from "bun:test"; +import fs from "fs"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; +import { join } from "path"; + +describe("pnpm comprehensive migration tests", () => { + test("large single package with many dependencies", async () => { + const tempDir = tempDirWithFiles("pnpm-large-single", { + "package.json": JSON.stringify( + { + name: "large-app", + version: "1.0.0", + dependencies: { + express: "^4.18.2", + react: "^18.2.0", + "react-dom": "^18.2.0", + next: "^14.0.4", + "@emotion/react": "^11.11.3", + "@emotion/styled": "^11.11.0", + axios: "^1.6.5", + lodash: "^4.17.21", + "date-fns": "^3.2.0", + zod: "^3.22.4", + "@tanstack/react-query": "^5.17.9", + }, + devDependencies: { + "@types/node": "^20.10.8", + "@types/react": "^18.2.47", + typescript: "^5.3.3", + prettier: "^3.1.1", + eslint: "^8.56.0", + vitest: "^1.1.3", + }, + optionalDependencies: { + fsevents: "^2.3.3", + }, + peerDependencies: { + "react-native": ">=0.72.0", + }, + peerDependenciesMeta: { + "react-native": { + optional: true, + }, + }, + }, + null, + 2, + ), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + dependencies: + '@emotion/react': + specifier: ^11.11.3 + version: 11.11.3(react@18.2.0) + '@emotion/styled': + specifier: ^11.11.0 + version: 11.11.0(@emotion/react@11.11.3)(react@18.2.0) + '@tanstack/react-query': + specifier: ^5.17.9 + version: 5.17.9(react@18.2.0) + axios: + specifier: ^1.6.5 + version: 1.6.5 + date-fns: + specifier: ^3.2.0 + version: 3.2.0 + express: + specifier: ^4.18.2 + version: 4.18.2 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + next: + specifier: ^14.0.4 + version: 14.0.4(react-dom@18.2.0)(react@18.2.0) + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + zod: + specifier: ^3.22.4 + version: 3.22.4 + some-very-long-package-name-that-is-really-really-long: + specifier: 1.2.3-beta.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20 + version: 1.2.3-beta.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20 + '@experimental/super-long-scoped-package-name-with-many-words': + specifier: 0.0.0-experimental-abcdef123456-20250812-build.9876543210 + version: 0.0.0-experimental-abcdef123456-20250812-build.9876543210 + devDependencies: + '@types/node': + specifier: ^20.10.8 + version: 20.10.8 + '@types/react': + specifier: ^18.2.47 + version: 18.2.47 + eslint: + specifier: ^8.56.0 + version: 8.56.0 + prettier: + specifier: ^3.1.1 + version: 3.1.1 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + vitest: + specifier: ^1.1.3 + version: 1.1.3 + optionalDependencies: + fsevents: + specifier: ^2.3.3 + version: 2.3.3 + +packages: + '@emotion/react@11.11.3': + resolution: {integrity: sha512-emotion-react==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/styled@11.11.0': + resolution: {integrity: sha512-emotion-styled==} + peerDependencies: + '@emotion/react': ^11.0.0 + react: '>=16.8.0' + + '@tanstack/react-query@5.17.9': + resolution: {integrity: sha512-tanstack==} + peerDependencies: + react: '>=18.0.0' + + axios@1.6.5: + resolution: {integrity: sha512-axios==} + + date-fns@3.2.0: + resolution: {integrity: sha512-date-fns==} + + express@4.18.2: + resolution: {integrity: sha512-express==} + engines: {node: '>= 0.10.0'} + + lodash@4.17.21: + resolution: {integrity: sha512-lodash==} + + next@14.0.4: + resolution: {integrity: sha512-next==} + engines: {node: '>=18.17.0'} + hasBin: true + peerDependencies: + react: '^18.2.0' + react-dom: '^18.2.0' + + react@18.2.0: + resolution: {integrity: sha512-react==} + engines: {node: '>=0.10.0'} + + react-dom@18.2.0: + resolution: {integrity: sha512-react-dom==} + peerDependencies: + react: ^18.2.0 + + zod@3.22.4: + resolution: {integrity: sha512-zod==} + + '@types/node@20.10.8': + resolution: {integrity: sha512-types-node==} + + '@types/react@18.2.47': + resolution: {integrity: sha512-types-react==} + + eslint@8.56.0: + resolution: {integrity: sha512-eslint==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + + prettier@3.1.1: + resolution: {integrity: sha512-prettier==} + engines: {node: '>=14'} + hasBin: true + + typescript@5.3.3: + resolution: {integrity: sha512-typescript==} + engines: {node: '>=14.17'} + hasBin: true + + vitest@1.1.3: + resolution: {integrity: sha512-vitest==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + fsevents@2.3.3: + resolution: {integrity: sha512-fsevents==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + loose-envify@1.4.0: + resolution: {integrity: sha512-loose==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-tokens==} + + scheduler@0.23.0: + resolution: {integrity: sha512-scheduler==} + + some-very-long-package-name-that-is-really-really-long@1.2.3-beta.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20: + resolution: {integrity: sha512-longpackage==} + + '@experimental/super-long-scoped-package-name-with-many-words@0.0.0-experimental-abcdef123456-20250812-build.9876543210': + resolution: {integrity: sha512-experimental==} + + accepts@1.3.8: + resolution: {integrity: sha512-accepts==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-mime-types==} + engines: {node: '>= 0.6'} + + negotiator@0.6.3: + resolution: {integrity: sha512-negotiator==} + engines: {node: '>= 0.6'} + +snapshots: + '@emotion/react@11.11.3(react@18.2.0)': + dependencies: + react: 18.2.0 + + '@emotion/styled@11.11.0(@emotion/react@11.11.3)(react@18.2.0)': + dependencies: + '@emotion/react': 11.11.3(react@18.2.0) + react: 18.2.0 + + '@tanstack/react-query@5.17.9(react@18.2.0)': + dependencies: + react: 18.2.0 + + axios@1.6.5: {} + + date-fns@3.2.0: {} + + express@4.18.2: + dependencies: + accepts: 1.3.8 + + lodash@4.17.21: {} + + next@14.0.4(react-dom@18.2.0)(react@18.2.0): + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + + react@18.2.0: + dependencies: + loose-envify: 1.4.0 + + react-dom@18.2.0(react@18.2.0): + dependencies: + react: 18.2.0 + scheduler: 0.23.0 + + zod@3.22.4: {} + + '@types/node@20.10.8': {} + + '@types/react@18.2.47': {} + + eslint@8.56.0: {} + + prettier@3.1.1: {} + + typescript@5.3.3: {} + + vitest@1.1.3: {} + + fsevents@2.3.3: + optional: true + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + js-tokens@4.0.0: {} + + scheduler@0.23.0: + dependencies: + loose-envify: 1.4.0 + + some-very-long-package-name-that-is-really-really-long@1.2.3-beta.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20: {} + + '@experimental/super-long-scoped-package-name-with-many-words@0.0.0-experimental-abcdef123456-20250812-build.9876543210': {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + mime-types@2.1.35: {} + + negotiator@0.6.3: {} +`, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: tempDir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + if (exitCode !== 0) { + console.log("stdout:", stdout); + console.log("stderr:", stderr); + } + expect(exitCode).toBe(0); + expect(stderr).toContain("migrated lockfile from pnpm-lock.yaml"); + expect(fs.existsSync(join(tempDir, "bun.lock"))).toBe(true); + + const bunLockContent = fs.readFileSync(join(tempDir, "bun.lock"), "utf8"); + expect(bunLockContent).toMatchSnapshot("large-single-package"); + }, 200000); + + test("complex monorepo with cross-dependencies", async () => { + const tempDir = tempDirWithFiles("pnpm-complex-workspace", { + "package.json": JSON.stringify( + { + name: "monorepo-root", + version: "1.0.0", + private: true, + workspaces: ["packages/*", "apps/*", "tools/*"], + devDependencies: { + turbo: "^1.11.2", + prettier: "^3.1.1", + }, + }, + null, + 2, + ), + "pnpm-workspace.yaml": `packages: + - 'packages/*' + - 'apps/*' + - 'tools/*' +`, + "packages/ui/package.json": JSON.stringify( + { + name: "@company/ui", + version: "1.0.0", + dependencies: { + react: "^18.2.0", + "@radix-ui/react-dialog": "^1.0.5", + "class-variance-authority": "^0.7.0", + clsx: "^2.1.0", + }, + devDependencies: { + "@types/react": "^18.2.47", + }, + peerDependencies: { + react: ">=16.8.0", + }, + }, + null, + 2, + ), + "packages/utils/package.json": JSON.stringify( + { + name: "@company/utils", + version: "1.0.0", + dependencies: { + "date-fns": "^3.2.0", + zod: "^3.22.4", + }, + }, + null, + 2, + ), + "packages/config/package.json": JSON.stringify( + { + name: "@company/config", + version: "1.0.0", + devDependencies: { + "@company/utils": "workspace:*", + }, + }, + null, + 2, + ), + "apps/web/package.json": JSON.stringify( + { + name: "@company/web", + version: "1.0.0", + dependencies: { + "@company/ui": "workspace:*", + "@company/utils": "workspace:*", + next: "^14.0.4", + react: "^18.2.0", + "react-dom": "^18.2.0", + }, + devDependencies: { + "@company/config": "workspace:*", + "@types/node": "^20.10.8", + "@types/react": "^18.2.47", + typescript: "^5.3.3", + }, + }, + null, + 2, + ), + "apps/api/package.json": JSON.stringify( + { + name: "@company/api", + version: "1.0.0", + dependencies: { + "@company/utils": "workspace:*", + express: "^4.18.2", + cors: "^2.8.5", + dotenv: "^16.3.1", + }, + devDependencies: { + "@company/config": "workspace:*", + "@types/express": "^4.17.21", + "@types/cors": "^2.8.17", + nodemon: "^3.0.2", + }, + }, + null, + 2, + ), + "tools/cli/package.json": JSON.stringify( + { + name: "@company/cli", + version: "1.0.0", + bin: { + "company-cli": "./bin/cli.js", + }, + dependencies: { + "@company/utils": "workspace:*", + commander: "^11.1.0", + chalk: "^5.3.0", + }, + }, + null, + 2, + ), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + devDependencies: + prettier: + specifier: ^3.1.1 + version: 3.1.1 + turbo: + specifier: ^1.11.2 + version: 1.11.2 + + apps/api: + dependencies: + '@company/utils': + specifier: workspace:* + version: link:../../packages/utils + cors: + specifier: ^2.8.5 + version: 2.8.5 + dotenv: + specifier: ^16.3.1 + version: 16.3.1 + express: + specifier: ^4.18.2 + version: 4.18.2 + devDependencies: + '@company/config': + specifier: workspace:* + version: link:../../packages/config + '@types/cors': + specifier: ^2.8.17 + version: 2.8.17 + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + nodemon: + specifier: ^3.0.2 + version: 3.0.2 + + apps/web: + dependencies: + '@company/ui': + specifier: workspace:* + version: link:../../packages/ui + '@company/utils': + specifier: workspace:* + version: link:../../packages/utils + next: + specifier: ^14.0.4 + version: 14.0.4(react-dom@18.2.0)(react@18.2.0) + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + devDependencies: + '@company/config': + specifier: workspace:* + version: link:../../packages/config + '@types/node': + specifier: ^20.10.8 + version: 20.10.8 + '@types/react': + specifier: ^18.2.47 + version: 18.2.47 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + + packages/config: + devDependencies: + '@company/utils': + specifier: workspace:* + version: link:../utils + + packages/ui: + dependencies: + '@radix-ui/react-dialog': + specifier: ^1.0.5 + version: 1.0.5(react-dom@18.2.0)(react@18.2.0) + class-variance-authority: + specifier: ^0.7.0 + version: 0.7.0 + clsx: + specifier: ^2.1.0 + version: 2.1.0 + react: + specifier: ^18.2.0 + version: 18.2.0 + devDependencies: + '@types/react': + specifier: ^18.2.47 + version: 18.2.47 + + packages/utils: + dependencies: + date-fns: + specifier: ^3.2.0 + version: 3.2.0 + zod: + specifier: ^3.22.4 + version: 3.22.4 + + tools/cli: + dependencies: + '@company/utils': + specifier: workspace:* + version: link:../../packages/utils + chalk: + specifier: ^5.3.0 + version: 5.3.0 + commander: + specifier: ^11.1.0 + version: 11.1.0 + +packages: + prettier@3.1.1: + resolution: {integrity: sha512-prettier==} + engines: {node: '>=14'} + hasBin: true + + turbo@1.11.2: + resolution: {integrity: sha512-turbo==} + hasBin: true + + cors@2.8.5: + resolution: {integrity: sha512-cors==} + engines: {node: '>= 0.10'} + + dotenv@16.3.1: + resolution: {integrity: sha512-dotenv==} + engines: {node: '>=12'} + + express@4.18.2: + resolution: {integrity: sha512-express==} + engines: {node: '>= 0.10.0'} + + '@types/cors@2.8.17': + resolution: {integrity: sha512-types-cors==} + + '@types/express@4.17.21': + resolution: {integrity: sha512-types-express==} + + nodemon@3.0.2: + resolution: {integrity: sha512-nodemon==} + engines: {node: '>=10'} + hasBin: true + + next@14.0.4: + resolution: {integrity: sha512-next==} + engines: {node: '>=18.17.0'} + hasBin: true + peerDependencies: + react: '^18.2.0' + react-dom: '^18.2.0' + + react@18.2.0: + resolution: {integrity: sha512-react==} + engines: {node: '>=0.10.0'} + + react-dom@18.2.0: + resolution: {integrity: sha512-react-dom==} + peerDependencies: + react: ^18.2.0 + + '@types/node@20.10.8': + resolution: {integrity: sha512-types-node==} + + '@types/react@18.2.47': + resolution: {integrity: sha512-types-react==} + + typescript@5.3.3: + resolution: {integrity: sha512-typescript==} + engines: {node: '>=14.17'} + hasBin: true + + '@radix-ui/react-dialog@1.0.5': + resolution: {integrity: sha512-radix-dialog==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + class-variance-authority@0.7.0: + resolution: {integrity: sha512-cva==} + + clsx@2.1.0: + resolution: {integrity: sha512-clsx==} + engines: {node: '>=6'} + + date-fns@3.2.0: + resolution: {integrity: sha512-date-fns==} + + zod@3.22.4: + resolution: {integrity: sha512-zod==} + + chalk@5.3.0: + resolution: {integrity: sha512-chalk==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + commander@11.1.0: + resolution: {integrity: sha512-commander==} + engines: {node: '>=16'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-loose==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-tokens==} + + scheduler@0.23.0: + resolution: {integrity: sha512-scheduler==} + +snapshots: + prettier@3.1.1: {} + + turbo@1.11.2: {} + + cors@2.8.5: {} + + dotenv@16.3.1: {} + + express@4.18.2: {} + + '@types/cors@2.8.17': {} + + '@types/express@4.17.21': {} + + nodemon@3.0.2: {} + + next@14.0.4(react-dom@18.2.0)(react@18.2.0): + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + + react@18.2.0: + dependencies: + loose-envify: 1.4.0 + + react-dom@18.2.0(react@18.2.0): + dependencies: + react: 18.2.0 + scheduler: 0.23.0 + + '@types/node@20.10.8': {} + + '@types/react@18.2.47': {} + + typescript@5.3.3: {} + + '@radix-ui/react-dialog@1.0.5(react-dom@18.2.0)(react@18.2.0)': + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + + class-variance-authority@0.7.0: {} + + clsx@2.1.0: {} + + date-fns@3.2.0: {} + + zod@3.22.4: {} + + chalk@5.3.0: {} + + commander@11.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + js-tokens@4.0.0: {} + + scheduler@0.23.0: + dependencies: + loose-envify: 1.4.0 +`, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "install", "--lockfile-only"], + cwd: tempDir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + if (exitCode !== 0) { + console.log("stdout:", stdout); + console.log("stderr:", stderr); + } + expect(exitCode).toBe(0); + expect(stderr).toContain("migrated lockfile from pnpm-lock.yaml"); + expect(fs.existsSync(join(tempDir, "bun.lock"))).toBe(true); + + const bunLockContent = fs.readFileSync(join(tempDir, "bun.lock"), "utf8"); + expect(bunLockContent).toMatchSnapshot("complex-monorepo"); + }); + + test("pnpm with patches and overrides", async () => { + const tempDir = tempDirWithFiles("pnpm-patches-overrides", { + "package.json": JSON.stringify( + { + name: "patches-test", + version: "1.0.0", + dependencies: { + express: "^4.18.2", + "is-number": "^7.0.0", + }, + pnpm: { + overrides: { + "mime-types": "2.1.33", + "negotiator@>0.6.0": "0.6.2", + }, + patchedDependencies: { + "express@4.18.2": "patches/express@4.18.2.patch", + }, + }, + }, + null, + 2, + ), + "patches/express@4.18.2.patch": `diff --git a/lib/application.js b/lib/application.js +index 1234567..abcdefg 100644 +--- a/lib/application.js ++++ b/lib/application.js +@@ -123,7 +123,7 @@ app.defaultConfiguration = function defaultConfiguration() { + this.set('subdomain offset', 2); + this.set('trust proxy', false); + +- // trust proxy inherit back-compat ++ // trust proxy inherit back-compat - PATCHED + Object.defineProperty(this.settings, trustProxyDefaultSymbol, { + configurable: true, + value: true +`, + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + mime-types: 2.1.33 + 'negotiator@>0.6.0': 0.6.2 + +patchedDependencies: + express@4.18.2: + hash: abc123def456 + path: patches/express@4.18.2.patch + +importers: + .: + dependencies: + express: + specifier: ^4.18.2 + version: 4.18.2(patch_hash=abc123def456) + is-number: + specifier: ^7.0.0 + version: 7.0.0 + +packages: + express@4.18.2: + resolution: {integrity: sha512-express==} + engines: {node: '>= 0.10.0'} + patched: true + + is-number@7.0.0: + resolution: {integrity: sha512-is-number==} + engines: {node: '>=0.12.0'} + + accepts@1.3.8: + resolution: {integrity: sha512-accepts==} + engines: {node: '>= 0.6'} + + mime-types@2.1.33: + resolution: {integrity: sha512-mime-types-override==} + engines: {node: '>= 0.6'} + + mime-db@1.50.0: + resolution: {integrity: sha512-mime-db==} + engines: {node: '>= 0.6'} + + negotiator@0.6.2: + resolution: {integrity: sha512-negotiator-override==} + engines: {node: '>= 0.6'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-array-flatten==} + +snapshots: + express@4.18.2(patch_hash=abc123def456): + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + + is-number@7.0.0: {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.33 + negotiator: 0.6.2 + + mime-types@2.1.33: + dependencies: + mime-db: 1.50.0 + + mime-db@1.50.0: {} + + negotiator@0.6.2: {} + + array-flatten@1.1.1: {} +`, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "install", "--lockfile-only"], + cwd: tempDir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + if (exitCode !== 0) { + console.log("stdout:", stdout); + console.log("stderr:", stderr); + } + expect(exitCode).toBe(0); + expect(stderr).toContain("migrated lockfile from pnpm-lock.yaml"); + expect(fs.existsSync(join(tempDir, "bun.lock"))).toBe(true); + + const bunLockContent = fs.readFileSync(join(tempDir, "bun.lock"), "utf8"); + expect(bunLockContent).toMatchSnapshot("patches-overrides"); + }); + + test("pnpm v6 format (unsupported)", async () => { + const tempDir = tempDirWithFiles("pnpm-v6", { + "package.json": JSON.stringify({ + name: "v6-format-test", + version: "1.0.0", + dependencies: { + "lodash": "^4.17.21", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '6.0' + +dependencies: + lodash: + specifier: ^4.17.21 + version: 4.17.21 + +packages: + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} +`, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: tempDir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // Should fail with error message + expect(exitCode).toBe(1); + expect(stderr).toContain("pnpm-lock.yaml version is too old"); + expect(stderr).toContain("Please upgrade using 'pnpm install"); + }); + + test("pnpm with peer dependencies and auto-install-peers", async () => { + const tempDir = tempDirWithFiles("pnpm-peer-deps", { + "package.json": JSON.stringify( + { + name: "peer-deps-test", + version: "1.0.0", + dependencies: { + "@angular/animations": "^17.0.0", + "@angular/common": "^17.0.0", + "@angular/core": "^17.0.0", + }, + }, + null, + 2, + ), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + dependencies: + '@angular/animations': + specifier: ^17.0.0 + version: 17.0.8(@angular/core@17.0.8) + '@angular/common': + specifier: ^17.0.0 + version: 17.0.8(@angular/core@17.0.8)(rxjs@7.8.1) + '@angular/core': + specifier: ^17.0.0 + version: 17.0.8(rxjs@7.8.1)(zone.js@0.14.2) + optionalDependencies: + rxjs: + specifier: ^6.5.3 || ^7.4.0 + version: 7.8.1 + tslib: + specifier: ^2.3.0 + version: 2.6.2 + zone.js: + specifier: ~0.14.0 + version: 0.14.2 + +packages: + '@angular/animations@17.0.8': + resolution: {integrity: sha512-angular-animations==} + engines: {node: ^18.13.0 || >=20.9.0} + peerDependencies: + '@angular/core': 17.0.8 + + '@angular/common@17.0.8': + resolution: {integrity: sha512-angular-common==} + engines: {node: ^18.13.0 || >=20.9.0} + peerDependencies: + '@angular/core': 17.0.8 + rxjs: ^6.5.3 || ^7.4.0 + + '@angular/core@17.0.8': + resolution: {integrity: sha512-angular-core==} + engines: {node: ^18.13.0 || >=20.9.0} + peerDependencies: + rxjs: ^6.5.3 || ^7.4.0 + zone.js: ~0.14.0 + + rxjs@7.8.1: + resolution: {integrity: sha512-rxjs==} + + tslib@2.6.2: + resolution: {integrity: sha512-tslib==} + + zone.js@0.14.2: + resolution: {integrity: sha512-zone==} + +snapshots: + '@angular/animations@17.0.8(@angular/core@17.0.8)': + dependencies: + '@angular/core': 17.0.8(rxjs@7.8.1)(zone.js@0.14.2) + tslib: 2.6.2 + + '@angular/common@17.0.8(@angular/core@17.0.8)(rxjs@7.8.1)': + dependencies: + '@angular/core': 17.0.8(rxjs@7.8.1)(zone.js@0.14.2) + rxjs: 7.8.1 + tslib: 2.6.2 + + '@angular/core@17.0.8(rxjs@7.8.1)(zone.js@0.14.2)': + dependencies: + rxjs: 7.8.1 + tslib: 2.6.2 + zone.js: 0.14.2 + + rxjs@7.8.1: + dependencies: + tslib: 2.6.2 + + tslib@2.6.2: {} + + zone.js@0.14.2: {} +`, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: tempDir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + if (exitCode !== 0) { + console.log("stdout:", stdout); + console.log("stderr:", stderr); + } + expect(exitCode).toBe(0); + expect(stderr).toContain("migrated lockfile from pnpm-lock.yaml"); + expect(fs.existsSync(join(tempDir, "bun.lock"))).toBe(true); + + const bunLockContent = fs.readFileSync(join(tempDir, "bun.lock"), "utf8"); + expect(bunLockContent).toMatchSnapshot("peer-deps-auto-install"); + }); +}); diff --git a/test/cli/install/migration/pnpm-lock-migration.test.ts b/test/cli/install/migration/pnpm-lock-migration.test.ts new file mode 100644 index 0000000000..eb7b3cd717 --- /dev/null +++ b/test/cli/install/migration/pnpm-lock-migration.test.ts @@ -0,0 +1,404 @@ +import { describe, expect, test } from "bun:test"; +import fs from "fs"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; +import { join } from "path"; + +describe("pnpm-lock.yaml migration", () => { + test("simple pnpm lockfile migration produces correct bun.lock", async () => { + const tempDir = tempDirWithFiles("pnpm-migrate-simple", { + "package.json": JSON.stringify( + { + name: "simple-pnpm-test", + version: "1.0.0", + dependencies: { + "is-number": "^7.0.0", + "left-pad": "^1.3.0", + }, + }, + null, + 2, + ), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + is-number: + specifier: ^7.0.0 + version: 7.0.0 + left-pad: + specifier: ^1.3.0 + version: 1.3.0 + +packages: + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + left-pad@1.3.0: + resolution: {integrity: sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==} + deprecated: use String.prototype.padStart() + +snapshots: + + is-number@7.0.0: {} + + left-pad@1.3.0: {} +`, + }); + + // Run bun pm migrate + await using proc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: tempDir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + if (exitCode !== 0) { + console.log("stdout:", stdout); + console.log("stderr:", stderr); + } + expect(exitCode).toBe(0); + + // Check migration message in stderr + expect(stderr).toContain("migrated lockfile from pnpm-lock.yaml"); + + // Check that bun.lock was created + expect(fs.existsSync(join(tempDir, "bun.lock"))).toBe(true); + + // Read and snapshot the migrated lockfile + const bunLockContent = fs.readFileSync(join(tempDir, "bun.lock"), "utf8"); + expect(bunLockContent).toMatchSnapshot("simple-pnpm-migration"); + + // Verify install works with migrated lockfile + await using installProc = Bun.spawn({ + cmd: [bunExe(), "install", "--frozen-lockfile"], + cwd: tempDir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [installStdout, installStderr, installExitCode] = await Promise.all([ + installProc.stdout.text(), + installProc.stderr.text(), + installProc.exited, + ]); + + if (installExitCode !== 0) { + console.log("Install stdout:", installStdout); + console.log("Install stderr:", installStderr); + console.log("Lockfile content:", bunLockContent); + } + expect(installExitCode).toBe(0); + + // Verify packages were installed + expect(fs.existsSync(join(tempDir, "node_modules/is-number"))).toBe(true); + expect(fs.existsSync(join(tempDir, "node_modules/left-pad"))).toBe(true); + }); + + test("pnpm workspace lockfile migration", async () => { + const tempDir = tempDirWithFiles("pnpm-migrate-workspace", { + "package.json": JSON.stringify( + { + name: "monorepo-root", + version: "1.0.0", + private: true, + workspaces: ["packages/*", "apps/*"], + }, + null, + 2, + ), + "pnpm-workspace.yaml": `packages: + - 'packages/*' + - 'apps/*' +`, + "packages/ui/package.json": JSON.stringify( + { + name: "@repo/ui", + version: "1.0.0", + dependencies: { + react: "^18.2.0", + }, + }, + null, + 2, + ), + "packages/utils/package.json": JSON.stringify( + { + name: "@repo/utils", + version: "1.0.0", + dependencies: { + lodash: "^4.17.21", + }, + }, + null, + 2, + ), + "apps/web/package.json": JSON.stringify( + { + name: "@repo/web", + version: "1.0.0", + dependencies: { + "@repo/ui": "workspace:*", + "@repo/utils": "workspace:*", + next: "^14.0.0", + }, + }, + null, + 2, + ), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: {} + + apps/web: + dependencies: + '@repo/ui': + specifier: workspace:* + version: link:../../packages/ui + '@repo/utils': + specifier: workspace:* + version: link:../../packages/utils + next: + specifier: ^14.0.0 + version: 14.0.4 + + packages/ui: + dependencies: + react: + specifier: ^18.2.0 + version: 18.2.0 + + packages/utils: + dependencies: + lodash: + specifier: ^4.17.21 + version: 4.17.21 + +packages: + + react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + next@14.0.4: + resolution: {integrity: sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==} + engines: {node: '>=18.17.0'} + hasBin: true + + loose-envify@1.4.0: + resolution: {} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + +snapshots: + + react@18.2.0: + dependencies: + loose-envify: 1.4.0 + + lodash@4.17.21: {} + + next@14.0.4: + dependencies: + react: 18.2.0 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + js-tokens@4.0.0: {} +`, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: tempDir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + if (exitCode !== 0) { + console.log("stdout:", stdout); + console.log("stderr:", stderr); + } + expect(exitCode).toBe(0); + + // Check migration message in stderr + expect(stderr).toContain("migrated lockfile from pnpm-lock.yaml"); + + expect(fs.existsSync(join(tempDir, "bun.lock"))).toBe(true); + + const bunLockContent = fs.readFileSync(join(tempDir, "bun.lock"), "utf8"); + expect(bunLockContent).toMatchSnapshot("workspace-pnpm-migration"); + const packageJson = JSON.parse(fs.readFileSync(join(tempDir, "package.json"), "utf8")); + expect(packageJson).toMatchSnapshot("workspace-pnpm-migration-package-json"); + }); + + test("pnpm with npm protocol aliases", async () => { + const tempDir = tempDirWithFiles("pnpm-migrate-npm-aliases", { + "package.json": JSON.stringify( + { + name: "alias-test", + dependencies: { + "my-react": "npm:react@^17.0.0", + "my-lodash": "npm:lodash@latest", + }, + }, + null, + 2, + ), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +importers: + .: + dependencies: + my-react: + specifier: npm:react@^17.0.0 + version: react@17.0.2 + my-lodash: + specifier: npm:lodash@latest + version: lodash@4.17.21 + +packages: + react@17.0.2: + resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} + engines: {node: '>=0.10.0'} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/tNVBQAZ8HW+WqwP25nGsjKeMZk13HGBF7YbJSi1KyeKwGAteWUa/ZKPUKAZNiIrUqZg==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnkjVqfO3E+1Q45hXf64UF+6eWwJJCTNJN7q7vfVQqPJZsB/1/vb9TuT9e2vYfqvnMqGCDJ5x6+WUJA==} + +snapshots: + react@17.0.2: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + + lodash@4.17.21: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + js-tokens@4.0.0: {} + + object-assign@4.1.1: {} +`, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: tempDir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + if (exitCode !== 0) { + console.log("stdout:", stdout); + console.log("stderr:", stderr); + } + expect(exitCode).toBe(0); + + // Check migration message in stderr + expect(stderr).toContain("migrated lockfile from pnpm-lock.yaml"); + + expect(fs.existsSync(join(tempDir, "bun.lock"))).toBe(true); + + const bunLockContent = fs.readFileSync(join(tempDir, "bun.lock"), "utf8"); + expect(bunLockContent).toMatchSnapshot("npm-aliases-pnpm-migration"); + }); + + test("handles different pnpm lockfile versions", async () => { + // Test version 8 + const v8Dir = tempDirWithFiles("pnpm-v8", { + "package.json": JSON.stringify({ name: "v8-test", dependencies: { "lodash": "^4.17.21" } }), + "pnpm-lock.yaml": `lockfileVersion: '8.0' +importers: + .: + dependencies: + lodash: + specifier: ^4.17.21 + version: 4.17.21 +packages: + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} +snapshots: + lodash@4.17.21: {}`, + }); + + await using v8Proc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: v8Dir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const v8ExitCode = await v8Proc.exited; + expect(v8ExitCode).toBe(0); + expect(fs.existsSync(join(v8Dir, "bun.lock"))).toBe(true); + }); + + test("handles missing pnpm-lock.yaml gracefully", async () => { + const tempDir = tempDirWithFiles("pnpm-migrate-missing", { + "package.json": JSON.stringify({ + name: "test", + version: "1.0.0", + }), + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: tempDir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // Should return an error when no lockfile is found + expect(exitCode).toBe(1); + expect(stderr).toContain("could not find any other lockfile"); + expect(stderr).not.toContain("migrated lockfile from pnpm-lock.yaml"); + }); +}); diff --git a/test/cli/install/migration/pnpm-migration-complete.test.ts b/test/cli/install/migration/pnpm-migration-complete.test.ts new file mode 100644 index 0000000000..08d31de47f --- /dev/null +++ b/test/cli/install/migration/pnpm-migration-complete.test.ts @@ -0,0 +1,1005 @@ +import { describe, expect, test } from "bun:test"; +import fs from "fs"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; +import { join } from "path"; + +describe("PNPM Migration Complete Test Suite", () => { + test("comprehensive PNPM migration with all edge cases", async () => { + // ===== SECTION 1: Basic Dependencies ===== + const basicTest = tempDirWithFiles("pnpm-basic", { + "package.json": JSON.stringify({ + name: "basic-test", + version: "1.0.0", + dependencies: { + "lodash": "^4.17.21", + "react": "^18.2.0", + }, + devDependencies: { + "typescript": "^5.3.3", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +importers: + .: + dependencies: + lodash: + specifier: ^4.17.21 + version: 4.17.21 + react: + specifier: ^18.2.0 + version: 18.2.0 + devDependencies: + typescript: + specifier: ^5.3.3 + version: 5.3.3 + +packages: + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + dependencies: + loose-envify: 1.4.0 + + typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + +snapshots: + lodash@4.17.21: {} + + react@18.2.0: + dependencies: + loose-envify: 1.4.0 + + typescript@5.3.3: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + js-tokens@4.0.0: {}`, + }); + + const basicProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: basicTest, + env: bunEnv, + stderr: "pipe", + }); + + const [basicStderr, basicExitCode] = await Promise.all([basicProc.stderr.text(), basicProc.exited]); + + expect(basicExitCode).toBe(0); + expect(basicStderr).toContain("migrated lockfile from pnpm-lock.yaml"); + + const basicLockfile = fs.readFileSync(join(basicTest, "bun.lock"), "utf8"); + expect(basicLockfile).toContain('"lodash": "^4.17.21"'); + expect(basicLockfile).toContain('"react": "^18.2.0"'); + expect(basicLockfile).toContain('"typescript": "^5.3.3"'); + expect(basicLockfile).toMatchSnapshot("basic-dependencies"); + + // ===== SECTION 2: Canary Versions ===== + const canaryTest = tempDirWithFiles("pnpm-canary", { + "package.json": JSON.stringify({ + name: "canary-test", + dependencies: { + "react": "19.2.0-canary-a96a0f39-20250815", + "react-dom": "19.2.0-canary-a96a0f39-20250815", + "scheduler": "0.27.0-canary-a96a0f39-20250815", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +importers: + .: + dependencies: + react: + specifier: 19.2.0-canary-a96a0f39-20250815 + version: 19.2.0-canary-a96a0f39-20250815 + react-dom: + specifier: 19.2.0-canary-a96a0f39-20250815 + version: 19.2.0-canary-a96a0f39-20250815 + scheduler: + specifier: 0.27.0-canary-a96a0f39-20250815 + version: 0.27.0-canary-a96a0f39-20250815 + +packages: + react@19.2.0-canary-a96a0f39-20250815: + resolution: {integrity: sha512-reactcanary==} + + react-dom@19.2.0-canary-a96a0f39-20250815: + resolution: {integrity: sha512-reactdomcanary==} + dependencies: + scheduler: 0.27.0-canary-a96a0f39-20250815 + + scheduler@0.27.0-canary-a96a0f39-20250815: + resolution: {integrity: sha512-schedulercanary==} + +snapshots: + react@19.2.0-canary-a96a0f39-20250815: {} + + react-dom@19.2.0-canary-a96a0f39-20250815: + dependencies: + scheduler: 0.27.0-canary-a96a0f39-20250815 + + scheduler@0.27.0-canary-a96a0f39-20250815: {}`, + }); + + const canaryProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: canaryTest, + env: bunEnv, + stderr: "pipe", + }); + + const [canaryStderr, canaryExitCode] = await Promise.all([canaryProc.stderr.text(), canaryProc.exited]); + + expect(canaryExitCode).toBe(0); + const canaryLockfile = fs.readFileSync(join(canaryTest, "bun.lock"), "utf8"); + + // Verify canary versions are preserved exactly + expect(canaryLockfile).toContain('"react@19.2.0-canary-a96a0f39-20250815"'); + expect(canaryLockfile).toContain('"scheduler@0.27.0-canary-a96a0f39-20250815"'); + expect(canaryLockfile).not.toContain("canary-a96a0f39-20250815-"); // No corruption + expect(canaryLockfile).toMatchSnapshot("canary-versions"); + + // ===== SECTION 3: Complex Monorepo with Workspaces ===== + const monorepoTest = tempDirWithFiles("pnpm-monorepo", { + "package.json": JSON.stringify({ + name: "monorepo-root", + private: true, + workspaces: ["packages/*", "apps/*"], + dependencies: { + "@workspace/shared": "workspace:*", + "@workspace/utils": "workspace:^", + }, + }), + "packages/shared/package.json": JSON.stringify({ name: "shared" }), + "packages/utils/package.json": JSON.stringify({ name: "utils" }), + "apps/web/package.json": JSON.stringify({ name: "web" }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +importers: + .: + dependencies: + '@workspace/shared': + specifier: workspace:* + version: link:packages/shared + '@workspace/utils': + specifier: workspace:^ + version: link:packages/utils + + packages/shared: + dependencies: + lodash: + specifier: ^4.17.21 + version: 4.17.21 + '@workspace/utils': + specifier: workspace:* + version: link:../utils + + packages/utils: + dependencies: + axios: + specifier: ^1.6.0 + version: 1.6.7 + + apps/web: + dependencies: + '@workspace/shared': + specifier: workspace:* + version: link:../../packages/shared + react: + specifier: ^18.2.0 + version: 18.2.0 + +packages: + lodash@4.17.21: + resolution: {integrity: sha512-lodash==} + + axios@1.6.7: + resolution: {integrity: sha512-axios==} + + react@18.2.0: + resolution: {integrity: sha512-react==} + +snapshots: + lodash@4.17.21: {} + axios@1.6.7: {} + react@18.2.0: {}`, + }); + + const monorepoProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: monorepoTest, + env: bunEnv, + stderr: "pipe", + }); + + const [monorepoStderr, monorepoExitCode] = await Promise.all([monorepoProc.stderr.text(), monorepoProc.exited]); + + expect(monorepoExitCode).toBe(0); + const monorepoLockfile = fs.readFileSync(join(monorepoTest, "bun.lock"), "utf8"); + + // Verify workspaces are created + expect(monorepoLockfile).toContain('"packages/shared"'); + expect(monorepoLockfile).toContain('"packages/utils"'); + expect(monorepoLockfile).toContain('"apps/web"'); + expect(monorepoLockfile).toContain('"@workspace/shared": "workspace:*"'); + expect(monorepoLockfile).toMatchSnapshot("monorepo-workspaces"); + + // ===== SECTION 4: Patches and Overrides ===== + const patchesTest = tempDirWithFiles("pnpm-patches", { + "patches/lodash@4.17.21.patch": `diff --git a/lib/application.js b/lib/application.js +index 1234567..abcdefg 100644 +--- a/lib/application.js ++++ b/lib/application.js +@@ -123,7 +123,7 @@ app.defaultConfiguration = function defaultConfiguration() { + this.set('subdomain offset', 2); + this.set('trust proxy', false); + +- // trust proxy inherit back-compat ++ // trust proxy inherit back-compat - PATCHED + Object.defineProperty(this.settings, trustProxyDefaultSymbol, { + configurable: true, + value: true`, + "package.json": JSON.stringify({ + name: "patches-test", + dependencies: { + "lodash": "^4.17.21", + }, + pnpm: { + patchedDependencies: { + "lodash@4.17.21": "patches/lodash@4.17.21.patch", + }, + overrides: { + "axios": "1.6.0", + }, + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + +patchedDependencies: + lodash@4.17.21: + path: patches/lodash@4.17.21.patch + hash: abc123 + +overrides: + axios: 1.6.0 + +importers: + .: + dependencies: + lodash: + specifier: ^4.17.21 + version: 4.17.21(patch_hash=abc123) + +packages: + lodash@4.17.21: + resolution: {integrity: sha512-lodash==} + patched: true + +snapshots: + lodash@4.17.21(patch_hash=abc123): {}`, + }); + + const patchesProc = Bun.spawn({ + cmd: [bunExe(), "install", "--lockfile-only"], + cwd: patchesTest, + env: bunEnv, + stderr: "pipe", + }); + + const [patchesStderr, patchesExitCode] = await Promise.all([patchesProc.stderr.text(), patchesProc.exited]); + + expect(patchesExitCode).toBe(0); + const patchesLockfile = fs.readFileSync(join(patchesTest, "bun.lock"), "utf8"); + + expect(patchesLockfile).toContain('"patchedDependencies"'); + expect(patchesLockfile).toContain('"lodash@4.17.21": "patches/lodash@4.17.21.patch"'); + expect(patchesLockfile).toContain('"overrides"'); + expect(patchesLockfile).toContain('"axios": "1.6.0"'); + expect(patchesLockfile).toMatchSnapshot("patches-overrides"); + + // ===== SECTION 5: File and Link Dependencies ===== + const fileLinksTest = tempDirWithFiles("pnpm-file-links", { + "shared/config/package.json": JSON.stringify({ name: "hi" }), + "local-pkg/package.json": JSON.stringify({ name: "hi2" }), + "package.json": JSON.stringify({ + name: "file-links-test", + dependencies: { + "local-pkg": "file:./local-pkg", + "config": "file:./shared/config", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + config: + specifier: file:./shared/config + version: hi2@file:shared/config + local-pkg: + specifier: file:./local-pkg + version: hi@file:local-pkg + +packages: + + hi2@file:shared/config: + resolution: {directory: shared/config, type: directory} + + hi@file:local-pkg: + resolution: {directory: local-pkg, type: directory} + +snapshots: + + hi2@file:shared/config: {} + + hi@file:local-pkg: {} +`, + }); + + const fileLinksProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: fileLinksTest, + env: bunEnv, + stderr: "pipe", + }); + + const [fileLinksStderr, fileLinksExitCode] = await Promise.all([fileLinksProc.stderr.text(), fileLinksProc.exited]); + + expect(fileLinksExitCode).toBe(0); + const fileLinksLockfile = fs.readFileSync(join(fileLinksTest, "bun.lock"), "utf8"); + + expect(fileLinksLockfile).toContain('"local-pkg": "file:./local-pkg"'); + expect(fileLinksLockfile).toContain('"config": "file:./shared/config"'); + expect(fileLinksLockfile).toMatchSnapshot("file-link-deps"); + + // ===== SECTION 6: Custom Registries ===== + const registriesTest = tempDirWithFiles("pnpm-registries", { + "package.json": JSON.stringify({ + name: "registries-test", + dependencies: { + "@company/private-pkg": "^1.0.0", + "lodash": "^4.17.21", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +importers: + .: + dependencies: + '@company/private-pkg': + specifier: ^1.0.0 + version: 1.0.5(registry=https://npm.company.com/) + lodash: + specifier: ^4.17.21 + version: 4.17.21 + +packages: + '@company/private-pkg@1.0.5': + resolution: {integrity: sha512-private==, registry: https://npm.company.com/, tarball: https://npm.company.com/@company/private-pkg/-/private-pkg-1.0.5.tgz} + + lodash@4.17.21: + resolution: {integrity: sha512-lodash==} + +snapshots: + '@company/private-pkg@1.0.5(registry=https://npm.company.com/)': {} + lodash@4.17.21: {}`, + }); + + const registriesProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: registriesTest, + env: bunEnv, + stderr: "pipe", + }); + + const [registriesStderr, registriesExitCode] = await Promise.all([ + registriesProc.stderr.text(), + registriesProc.exited, + ]); + + expect(registriesExitCode).toBe(0); + const registriesLockfile = fs.readFileSync(join(registriesTest, "bun.lock"), "utf8"); + + expect(registriesLockfile).toContain('"@company/private-pkg": "^1.0.0"'); + // Registry URLs are stored in the package entries + expect(registriesLockfile).toContain('"@company/private-pkg"'); + expect(registriesLockfile).toMatchSnapshot("custom-registries"); + + // ===== SECTION 7: Peer Dependencies ===== + const peerDepsTest = tempDirWithFiles("pnpm-peer-deps", { + "package.json": JSON.stringify({ + name: "peer-deps-test", + dependencies: { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "@mui/material": "^5.15.0", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +settings: + autoInstallPeers: false + +importers: + .: + dependencies: + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + '@mui/material': + specifier: ^5.15.0 + version: 5.15.0(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) + +packages: + react@18.2.0: + resolution: {integrity: sha512-react==} + + react-dom@18.2.0: + resolution: {integrity: sha512-reactdom==} + peerDependencies: + react: ^18.2.0 + + '@mui/material@5.15.0': + resolution: {integrity: sha512-mui==} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@emotion/react@11.11.3': + resolution: {integrity: sha512-emotion-react==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/styled@11.11.0': + resolution: {integrity: sha512-emotion-styled==} + peerDependencies: + '@emotion/react': ^11.0.0 + react: '>=16.8.0' + +snapshots: + react@18.2.0: {} + + react-dom@18.2.0(react@18.2.0): + dependencies: + react: 18.2.0 + + '@mui/material@5.15.0(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0)': + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@emotion/react': 11.11.3(react@18.2.0) + '@emotion/styled': 11.11.0(@emotion/react@11.11.3)(react@18.2.0) + + '@emotion/react@11.11.3(react@18.2.0)': + dependencies: + react: 18.2.0 + + '@emotion/styled@11.11.0(@emotion/react@11.11.3)(react@18.2.0)': + dependencies: + '@emotion/react': 11.11.3(react@18.2.0) + react: 18.2.0`, + }); + + const peerDepsProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: peerDepsTest, + env: bunEnv, + stderr: "pipe", + }); + + const [peerDepsStderr, peerDepsExitCode] = await Promise.all([peerDepsProc.stderr.text(), peerDepsProc.exited]); + + expect(peerDepsExitCode).toBe(0); + const peerDepsLockfile = fs.readFileSync(join(peerDepsTest, "bun.lock"), "utf8"); + + expect(peerDepsLockfile).toContain('"@mui/material": "^5.15.0"'); + expect(peerDepsLockfile).toContain('"react": "^18.2.0"'); + expect(peerDepsLockfile).toContain('"react-dom": "^18.2.0"'); + expect(peerDepsLockfile).toMatchSnapshot("peer-dependencies"); + + // ===== SECTION 9: Duplicate Packages ===== + const duplicatesTest = tempDirWithFiles("pnpm-duplicates", { + "package.json": JSON.stringify({ + name: "duplicates-test", + dependencies: { + "package-a": "^1.0.0", + "package-b": "^1.0.0", + "my-lodash": "npm:lodash@^4.17.20", + "lodash": "^4.17.21", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +importers: + .: + dependencies: + package-a: + specifier: ^1.0.0 + version: 1.0.0 + package-b: + specifier: ^1.0.0 + version: 1.0.0 + my-lodash: + specifier: npm:lodash@^4.17.20 + version: lodash@4.17.20 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + +packages: + package-a@1.0.0: + resolution: {integrity: sha512-packageA==} + dependencies: + shared-dep: 2.0.0 + + package-b@1.0.0: + resolution: {integrity: sha512-packageB==} + dependencies: + shared-dep: 3.0.0 + + shared-dep@2.0.0: + resolution: {integrity: sha512-shared2==} + + shared-dep@3.0.0: + resolution: {integrity: sha512-shared3==} + + lodash@4.17.20: + resolution: {integrity: sha512-lodash20==} + + lodash@4.17.21: + resolution: {integrity: sha512-lodash21==} + +snapshots: + package-a@1.0.0: + dependencies: + shared-dep: 2.0.0 + + package-b@1.0.0: + dependencies: + shared-dep: 3.0.0 + + shared-dep@2.0.0: {} + shared-dep@3.0.0: {} + lodash@4.17.20: {} + lodash@4.17.21: {}`, + }); + + const duplicatesProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: duplicatesTest, + env: bunEnv, + stderr: "pipe", + }); + + const [duplicatesStderr, duplicatesExitCode] = await Promise.all([ + duplicatesProc.stderr.text(), + duplicatesProc.exited, + ]); + + expect(duplicatesExitCode).toBe(0); + const duplicatesLockfile = fs.readFileSync(join(duplicatesTest, "bun.lock"), "utf8"); + + // Both versions of shared-dep should exist + expect(duplicatesLockfile).toContain('"shared-dep@2.0.0"'); + expect(duplicatesLockfile).toContain('"shared-dep@3.0.0"'); + // Aliased package + expect(duplicatesLockfile).toContain('"my-lodash": "npm:lodash@^4.17.20"'); + expect(duplicatesLockfile).toContain('"lodash": "^4.17.21"'); + expect(duplicatesLockfile).toMatchSnapshot("duplicate-packages"); + + // ===== SECTION 10: Catalogs ===== + const catalogsTest = tempDirWithFiles("pnpm-catalogs", { + "pnpm-workspace.yaml": `packages: [] + +catalog: + react: 18.2.0 + react-dom: 18.2.0 + +catalogs: + tools: + lodash: 4.17.21 + eslint: 8.56.0`, + + "package.json": JSON.stringify({ + name: "catalogs-test", + dependencies: { + "react": "catalog:", + "lodash": "catalog:tools", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +catalogs: + default: + react: + specifier: 18.2.0 + version: 18.2.0 + tools: + lodash: + specifier: 4.17.21 + version: 4.17.21 + +importers: + + .: + dependencies: + lodash: + specifier: catalog:tools + version: 4.17.21 + react: + specifier: 'catalog:' + version: 18.2.0 + +packages: + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + +snapshots: + + js-tokens@4.0.0: {} + + lodash@4.17.21: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + react@18.2.0: + dependencies: + loose-envify: 1.4.0 +`, + }); + + const catalogsProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: catalogsTest, + env: bunEnv, + stderr: "pipe", + }); + + const [catalogsStderr, catalogsExitCode] = await Promise.all([catalogsProc.stderr.text(), catalogsProc.exited]); + + expect(catalogsExitCode).toBe(0); + const catalogsLockfile = fs.readFileSync(join(catalogsTest, "bun.lock"), "utf8"); + + // Catalogs are resolved to actual versions during migration + expect(catalogsLockfile).toContain('"react": "18.2.0"'); + expect(catalogsLockfile).toContain('"lodash": "4.17.21"'); + // The actual packages should be in the lockfile + expect(catalogsLockfile).toContain('"react@18.2.0"'); + expect(catalogsLockfile).toContain('"lodash@4.17.21"'); + expect(catalogsLockfile).toMatchSnapshot("catalogs"); + + // ===== SECTION 12: Integrity Hashes ===== + const integrityTest = tempDirWithFiles("pnpm-integrity", { + "package.json": JSON.stringify({ + name: "integrity-test", + dependencies: { + "express": "^4.18.2", + "axios": "^1.6.0", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +importers: + .: + dependencies: + express: + specifier: ^4.18.2 + version: 4.18.2 + axios: + specifier: ^1.6.0 + version: 1.6.7 + +packages: + express@4.18.2: + resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + engines: {node: '>= 0.10.0'} + + axios@1.6.7: + resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==} + +snapshots: + express@4.18.2: {} + axios@1.6.7: {}`, + }); + + const integrityProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: integrityTest, + env: bunEnv, + stderr: "pipe", + }); + + const [integrityStderr, integrityExitCode] = await Promise.all([integrityProc.stderr.text(), integrityProc.exited]); + + expect(integrityExitCode).toBe(0); + const integrityLockfile = fs.readFileSync(join(integrityTest, "bun.lock"), "utf8"); + + // Check integrity hashes are preserved + expect(integrityLockfile).toContain( + "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + ); + expect(integrityLockfile).toContain( + "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + ); + expect(integrityLockfile).toMatchSnapshot("integrity-hashes"); + + // ===== SECTION 13: Version Zero Bug Test ===== + const versionZeroTest = tempDirWithFiles("pnpm-version-zero", { + "package.json": JSON.stringify({ + name: "version-zero-test", + dependencies: { + "package-with-zero": "0.0.0", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +importers: + .: + dependencies: + package-with-zero: + specifier: 0.0.0 + version: 0.0.0 + +packages: + package-with-zero@0.0.0: + resolution: {integrity: sha512-zero==} + +snapshots: + package-with-zero@0.0.0: {}`, + }); + + const versionZeroProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: versionZeroTest, + env: bunEnv, + stderr: "pipe", + }); + + const [versionZeroStderr, versionZeroExitCode] = await Promise.all([ + versionZeroProc.stderr.text(), + versionZeroProc.exited, + ]); + + expect(versionZeroExitCode).toBe(0); + const versionZeroLockfile = fs.readFileSync(join(versionZeroTest, "bun.lock"), "utf8"); + + expect(versionZeroLockfile).toContain('"package-with-zero": "0.0.0"'); + expect(versionZeroLockfile).toContain('"package-with-zero@0.0.0"'); + expect(versionZeroLockfile).toMatchSnapshot("version-zero"); + + // ===== SECTION 14: Mixed Dependency Types ===== + const mixedDepsTest = tempDirWithFiles("pnpm-mixed-deps", { + "package.json": JSON.stringify({ + name: "mixed-deps-test", + dependencies: { + "react": "^18.2.0", + "typescript": "^4.0.0", + }, + devDependencies: { + "typescript": "^5.3.3", + "eslint": "^8.56.0", + }, + optionalDependencies: { + "fsevents": "^2.3.3", + }, + peerDependencies: { + "react": ">=16.0.0", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +importers: + .: + dependencies: + react: + specifier: ^18.2.0 + version: 18.2.0 + typescript: + specifier: ^4.0.0 + version: 4.9.5 + devDependencies: + typescript: + specifier: ^5.3.3 + version: 5.3.3 + eslint: + specifier: ^8.56.0 + version: 8.56.0 + optionalDependencies: + fsevents: + specifier: ^2.3.3 + version: 2.3.3 + +packages: + react@18.2.0: + resolution: {integrity: sha512-react==} + + typescript@4.9.5: + resolution: {integrity: sha512-ts4==} + + typescript@5.3.3: + resolution: {integrity: sha512-ts5==} + + eslint@8.56.0: + resolution: {integrity: sha512-eslint==} + + fsevents@2.3.3: + resolution: {integrity: sha512-fsevents==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + optional: true + +snapshots: + react@18.2.0: {} + typescript@4.9.5: {} + typescript@5.3.3: {} + eslint@8.56.0: {} + fsevents@2.3.3: + optional: true`, + }); + + const mixedDepsProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: mixedDepsTest, + env: bunEnv, + stderr: "pipe", + }); + + const [mixedDepsStderr, mixedDepsExitCode] = await Promise.all([mixedDepsProc.stderr.text(), mixedDepsProc.exited]); + + expect(mixedDepsExitCode).toBe(0); + const mixedDepsLockfile = fs.readFileSync(join(mixedDepsTest, "bun.lock"), "utf8"); + + // Dependencies version should win + expect(mixedDepsLockfile).toContain('"typescript": "^4.0.0"'); + // But devDeps-only packages should be there + expect(mixedDepsLockfile).toContain('"eslint": "^8.56.0"'); + expect(mixedDepsLockfile).toContain('"fsevents"'); + expect(mixedDepsLockfile).toMatchSnapshot("mixed-dependency-types"); + + // ===== SECTION 15: Circular Workspace Dependencies ===== + const circularTest = tempDirWithFiles("pnpm-circular", { + "package.json": JSON.stringify({ + name: "circular-test", + workspaces: ["packages/*"], + dependencies: { + "@workspace/pkg1": "workspace:*", + }, + }), + "packages/pkg1/package.json": JSON.stringify({ + "name": "@workspace/pkg1", + "version": "1.0.0", + "main": "index.js", + "dependencies": { + "@workspace/pkg2": "workspace:*", + "lodash": "^4.17.21", + }, + }), + "packages/pkg2/package.json": JSON.stringify({ + "name": "@workspace/pkg2", + "version": "1.0.0", + "main": "index.js", + "dependencies": { + "@workspace/pkg3": "workspace:*", + }, + }), + "packages/pkg3/package.json": JSON.stringify({ + "name": "@workspace/pkg3", + "version": "1.0.0", + "main": "index.js", + "dependencies": { + "@workspace/pkg1": "workspace:*", + }, + }), + "pnpm-lock.yaml": `lockfileVersion: '9.0' + +importers: + .: + dependencies: + '@workspace/pkg1': + specifier: workspace:* + version: link:packages/pkg1 + + packages/pkg1: + dependencies: + '@workspace/pkg2': + specifier: workspace:* + version: link:../pkg2 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + + packages/pkg2: + dependencies: + '@workspace/pkg3': + specifier: workspace:* + version: link:../pkg3 + + packages/pkg3: + dependencies: + '@workspace/pkg1': + specifier: workspace:* + version: link:../pkg1 + +packages: + lodash@4.17.21: + resolution: {integrity: sha512-lodash==} + +snapshots: + lodash@4.17.21: {}`, + }); + + const circularProc = Bun.spawn({ + cmd: [bunExe(), "pm", "migrate"], + cwd: circularTest, + env: bunEnv, + stderr: "pipe", + }); + + const [circularStderr, circularExitCode] = await Promise.all([circularProc.stderr.text(), circularProc.exited]); + + expect(circularExitCode).toBe(0); + const circularLockfile = fs.readFileSync(join(circularTest, "bun.lock"), "utf8"); + + // All workspaces should be created despite circular dependencies + expect(circularLockfile).toContain('"packages/pkg1"'); + expect(circularLockfile).toContain('"packages/pkg2"'); + expect(circularLockfile).toContain('"packages/pkg3"'); + expect(circularLockfile).toContain('"@workspace/pkg1": "workspace:*"'); + expect(circularLockfile).toMatchSnapshot("circular-workspaces"); + }); +}); diff --git a/test/cli/install/migration/pnpm-migration.test.ts b/test/cli/install/migration/pnpm-migration.test.ts new file mode 100644 index 0000000000..6bf6e4d91e --- /dev/null +++ b/test/cli/install/migration/pnpm-migration.test.ts @@ -0,0 +1,133 @@ +import { file, spawn } from "bun"; +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; +import { bunExe, bunEnv as env, nodeModulesPackages, tempDir, VerdaccioRegistry } from "harness.js"; +import { join } from "path"; + +let verdaccio = new VerdaccioRegistry(); + +beforeAll(async () => { + await verdaccio.start(); +}); + +afterAll(() => { + verdaccio.stop(); +}); + +test("basic", async () => { + const { packageDir } = await verdaccio.createTestDir({ files: join(import.meta.dir, "pnpm/basic") }); + + let proc = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + env, + stdout: "pipe", + stderr: "pipe", + }); + + let [out, err, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(exitCode).toBe(0); + expect(err).toContain("Saved lockfile"); + + expect(nodeModulesPackages(packageDir)).toMatchInlineSnapshot(` + "node_modules/a-dep-b/a-dep-b@1.0.0 + node_modules/a-dep/a-dep@1.0.1 + node_modules/b-dep-a/b-dep-a@1.0.0 + node_modules/no-deps/no-deps@1.0.1" + `); + + expect( + (await file(join(packageDir, "bun.lock")).text()).replaceAll(/localhost:\d+/g, "localhost:1234"), + ).toMatchSnapshot("bun.lock"); + + proc = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + env, + stdout: "pipe", + stderr: "pipe", + }); + + [out, err, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(exitCode).toBe(0); + expect(err).not.toContain("Saved lockfile"); +}); + +describe.todo("bin", () => { + test("manifests are fetched for bins", async () => { + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/bin-manifest-fetching"), + }); + }); +}); + +describe.todo("peers", () => { + test("peers basic", async () => { + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/peers-basic"), + }); + }); + test("workspaces with peers", async () => { + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/peers-workspaces"), + }); + }); +}); + +describe.todo("patched packages", () => { + test("patches are detected and migrated correctly", async () => { + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/patched-packages"), + }); + }); +}); + +describe.todo("folder dependencies", () => { + test("basic", async () => { + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/folder-dependencies-basic"), + }); + }); + test("links are resolved correctly", async () => { + // link: + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/folder-dependencies-links"), + }); + }); +}); + +describe.todo("overrides", () => { + test("basic", async () => { + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/overrides-basic"), + }); + }); + test("accross workspaces", async () => { + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/overrides-workspaces"), + }); + }); +}); + +test.todo("from npm", async () => { + using testDir = tempDir("pnpm-migration-from-npm-registry", join(import.meta.dir, "pnpm/from-npm")); +}); + +describe.todo("workspaces", async () => { + test("basic", async () => { + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/workspaces-basic"), + }); + }); + test("workspace dependencies", async () => { + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/workspaces-dependencies"), + }); + }); + test("catalogs, peers, and workspaces", async () => { + const { packageDir, packageJson } = await verdaccio.createTestDir({ + files: join(import.meta.dir, "pnpm/workspaces-catalogs-peers"), + }); + }); +}); diff --git a/test/cli/install/migration/pnpm/.gitignore b/test/cli/install/migration/pnpm/.gitignore new file mode 100644 index 0000000000..c934a118d0 --- /dev/null +++ b/test/cli/install/migration/pnpm/.gitignore @@ -0,0 +1 @@ +!pnpm-lock.yaml \ No newline at end of file diff --git a/test/cli/install/migration/pnpm/basic/package.json b/test/cli/install/migration/pnpm/basic/package.json new file mode 100644 index 0000000000..39d298e643 --- /dev/null +++ b/test/cli/install/migration/pnpm/basic/package.json @@ -0,0 +1,15 @@ +{ + "name": "worky3", + "dependencies": { + "no-deps": "~1.0.0" + }, + "devDependencies": { + "a-dep-b": "1.0.0" + }, + "optionalDependencies": { + "b-dep-a": "1.0.0" + }, + "peerDependencies": { + "a-dep": "1.0.1" + } +} \ No newline at end of file diff --git a/test/cli/install/migration/pnpm/basic/pnpm-lock.yaml b/test/cli/install/migration/pnpm/basic/pnpm-lock.yaml new file mode 100644 index 0000000000..55cc8bccb2 --- /dev/null +++ b/test/cli/install/migration/pnpm/basic/pnpm-lock.yaml @@ -0,0 +1,52 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + a-dep: + specifier: 1.0.1 + version: 1.0.1 + no-deps: + specifier: ~1.0.0 + version: 1.0.1 + optionalDependencies: + b-dep-a: + specifier: 1.0.0 + version: 1.0.0 + devDependencies: + a-dep-b: + specifier: 1.0.0 + version: 1.0.0 + +packages: + + a-dep-b@1.0.0: + resolution: {integrity: sha512-PW1l4ruYaxcIw4rMkOVzb9zcR2srZhTPv2H2aH7QFc7vVxkD7EEMGHg1GPT8ycLFb8vriydUXEPwOy1FcbodaQ==} + + a-dep@1.0.1: + resolution: {integrity: sha512-6nmTaPgO2U/uOODqOhbjbnaB4xHuZ+UB7AjKUA3g2dT4WRWeNxgp0dC8Db4swXSnO5/uLLUdFmUJKINNBO/3wg==} + + b-dep-a@1.0.0: + resolution: {integrity: sha512-1owp4Wy5QE893BGgjDQGZm9Oayk38MA++fXmPTQA1WY/NFQv7CcCVpK2Ht/4mU4KejDeHOxaAj7qbzv1dSQA2w==} + + no-deps@1.0.1: + resolution: {integrity: sha512-3X6cn4+UJdXJuLPu11v8i/fGLe2PdI6v1yKTELam04lY5esCAFdG/qQts6N6rLrL6g1YRq+MKBAwxbmUQk355A==} + +snapshots: + + a-dep-b@1.0.0: + dependencies: + b-dep-a: 1.0.0 + + a-dep@1.0.1: {} + + b-dep-a@1.0.0: + dependencies: + a-dep-b: 1.0.0 + + no-deps@1.0.1: {} diff --git a/test/harness.ts b/test/harness.ts index f54cf878a3..8da328605f 100644 --- a/test/harness.ts +++ b/test/harness.ts @@ -261,7 +261,7 @@ export function tempDirWithFiles( basename: string, filesOrAbsolutePathToCopyFolderFrom: DirectoryTree | string, ): string { - const base = fs.mkdtempSync(join(fs.realpathSync(os.tmpdir()), basename + "_")); + const base = fs.mkdtempSync(join(fs.realpathSync.native(os.tmpdir()), basename + "_")); makeTreeSync(base, filesOrAbsolutePathToCopyFolderFrom); return base; } @@ -278,10 +278,10 @@ class DisposableString extends String { export function tempDir( basename: string, filesOrAbsolutePathToCopyFolderFrom: DirectoryTree | string, -): DisposableString { +): string & DisposableString & AsyncDisposable { const base = tempDirWithFiles(basename, filesOrAbsolutePathToCopyFolderFrom); - return new DisposableString(base); + return new DisposableString(base) as string & DisposableString & AsyncDisposable; } export function tempDirWithFilesAnon(filesOrAbsolutePathToCopyFolderFrom: DirectoryTree | string): string { @@ -1722,14 +1722,16 @@ export class VerdaccioRegistry { `; } - async createTestDir(bunfigOpts: BunfigOpts = {}) { + async createTestDir( + opts: { bunfigOpts?: BunfigOpts; files?: DirectoryTree | string } = { bunfigOpts: {}, files: {} }, + ) { await rm(join(dirname(this.configPath), "htpasswd"), { force: true }); await rm(join(this.packagesPath, "private-pkg-dont-touch"), { force: true }); - const packageDir = tmpdirSync(); + const packageDir = tempDir("verdaccio-test-", opts.files ?? {}); const packageJson = join(packageDir, "package.json"); - await this.writeBunfig(packageDir, bunfigOpts); + await this.writeBunfig(packageDir, opts.bunfigOpts); this.users = {}; - return { packageDir, packageJson }; + return { packageDir: String(packageDir), packageJson }; } async writeBunfig(dir: string, opts: BunfigOpts = {}) { @@ -1836,3 +1838,52 @@ export function normalizeBunSnapshot(snapshot: string, optionalDir?: string) { .trim() ); } + +export function nodeModulesPackages(nodeModulesPath: string): string { + const packages: string[] = []; + + function scanDirectory(dir: string, relativePath: string = "") { + try { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = join(dir, entry.name); + + if (entry.isDirectory()) { + if (entry.name === ".bun-cache") { + // Skip .bun-cache directories + continue; + } + + const newRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name; + + // Check if this directory contains a package.json + const packageJsonPath = join(fullPath, "package.json"); + if (fs.existsSync(packageJsonPath)) { + try { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); + const name = packageJson.name || "unknown"; + const version = packageJson.version || "unknown"; + packages.push(`${newRelativePath}/${name}@${version}`); + } catch { + // If package.json is invalid, still include the path + packages.push(`${newRelativePath}/[invalid-package.json]`); + } + } + + // Recursively scan subdirectories (including nested node_modules) + scanDirectory(fullPath, newRelativePath); + } + } + } catch (err) { + // Ignore errors for directories we can't read + } + } + + scanDirectory(nodeModulesPath); + + // Sort the packages alphabetically + packages.sort(); + + return packages.join("\n"); +}