mirror of
https://github.com/oven-sh/bun
synced 2026-02-15 05:12:29 +00:00
### What does this PR do? fixes #7157, fixes #14662 migrates pnpm-workspace.yaml data to package.json & converts pnpm-lock.yml to bun.lock --- ### How did you verify your code works? manually, tests and real world examples --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
800 lines
37 KiB
Zig
800 lines
37 KiB
Zig
const dependency_groups = &.{
|
|
.{ "optionalDependencies", .{ .optional = true } },
|
|
.{ "devDependencies", .{ .dev = true } },
|
|
.{ "dependencies", .{ .prod = true } },
|
|
.{ "peerDependencies", .{ .peer = true } },
|
|
};
|
|
|
|
pub const EditOptions = struct {
|
|
exact_versions: bool = false,
|
|
add_trusted_dependencies: bool = false,
|
|
before_install: bool = false,
|
|
};
|
|
|
|
pub fn editPatchedDependencies(
|
|
manager: *PackageManager,
|
|
package_json: *Expr,
|
|
patch_key: []const u8,
|
|
patchfile_path: []const u8,
|
|
) !void {
|
|
|
|
// const pkg_to_patch = manager.
|
|
var patched_dependencies = brk: {
|
|
if (package_json.asProperty("patchedDependencies")) |query| {
|
|
if (query.expr.data == .e_object)
|
|
break :brk query.expr.data.e_object.*;
|
|
}
|
|
break :brk E.Object{};
|
|
};
|
|
|
|
const patchfile_expr = try Expr.init(
|
|
E.String,
|
|
E.String{
|
|
.data = patchfile_path,
|
|
},
|
|
logger.Loc.Empty,
|
|
).clone(manager.allocator);
|
|
|
|
try patched_dependencies.put(
|
|
manager.allocator,
|
|
patch_key,
|
|
patchfile_expr,
|
|
);
|
|
|
|
try package_json.data.e_object.put(
|
|
manager.allocator,
|
|
"patchedDependencies",
|
|
try Expr.init(E.Object, patched_dependencies, logger.Loc.Empty).clone(manager.allocator),
|
|
);
|
|
}
|
|
|
|
pub fn editTrustedDependencies(allocator: std.mem.Allocator, package_json: *Expr, names_to_add: []string) !void {
|
|
var len = names_to_add.len;
|
|
|
|
var original_trusted_dependencies = brk: {
|
|
if (package_json.asProperty(trusted_dependencies_string)) |query| {
|
|
if (query.expr.data == .e_array) {
|
|
break :brk query.expr.data.e_array.*;
|
|
}
|
|
}
|
|
break :brk E.Array{};
|
|
};
|
|
|
|
for (names_to_add, 0..) |name, i| {
|
|
for (original_trusted_dependencies.items.slice()) |item| {
|
|
if (item.data == .e_string) {
|
|
if (item.data.e_string.eql(string, name)) {
|
|
const temp = names_to_add[i];
|
|
names_to_add[i] = names_to_add[len - 1];
|
|
names_to_add[len - 1] = temp;
|
|
len -= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var trusted_dependencies: []Expr = &[_]Expr{};
|
|
if (package_json.asProperty(trusted_dependencies_string)) |query| {
|
|
if (query.expr.data == .e_array) {
|
|
trusted_dependencies = query.expr.data.e_array.items.slice();
|
|
}
|
|
}
|
|
|
|
const trusted_dependencies_to_add = len;
|
|
const new_trusted_deps: JSAst.ExprNodeList = brk: {
|
|
const deps = try allocator.alloc(Expr, trusted_dependencies.len + trusted_dependencies_to_add);
|
|
@memcpy(deps[0..trusted_dependencies.len], trusted_dependencies);
|
|
@memset(deps[trusted_dependencies.len..], Expr.empty);
|
|
|
|
for (names_to_add[0..len]) |name| {
|
|
if (comptime Environment.allow_assert) {
|
|
var has_missing = false;
|
|
for (deps) |dep| {
|
|
if (dep.data == .e_missing) has_missing = true;
|
|
}
|
|
bun.assert(has_missing);
|
|
}
|
|
|
|
var i = deps.len;
|
|
while (i > 0) {
|
|
i -= 1;
|
|
if (deps[i].data == .e_missing) {
|
|
deps[i] = try Expr.init(
|
|
E.String,
|
|
E.String{
|
|
.data = name,
|
|
},
|
|
logger.Loc.Empty,
|
|
).clone(allocator);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (comptime Environment.allow_assert) {
|
|
for (deps) |dep| bun.assert(dep.data != .e_missing);
|
|
}
|
|
|
|
break :brk .fromOwnedSlice(deps);
|
|
};
|
|
|
|
var needs_new_trusted_dependencies_list = true;
|
|
const trusted_dependencies_array: Expr = brk: {
|
|
if (package_json.asProperty(trusted_dependencies_string)) |query| {
|
|
if (query.expr.data == .e_array) {
|
|
needs_new_trusted_dependencies_list = false;
|
|
break :brk query.expr;
|
|
}
|
|
}
|
|
|
|
break :brk Expr.init(
|
|
E.Array,
|
|
E.Array{ .items = new_trusted_deps },
|
|
logger.Loc.Empty,
|
|
);
|
|
};
|
|
|
|
if (trusted_dependencies_to_add > 0 and new_trusted_deps.len > 0) {
|
|
trusted_dependencies_array.data.e_array.items = new_trusted_deps;
|
|
trusted_dependencies_array.data.e_array.alphabetizeStrings();
|
|
}
|
|
|
|
if (package_json.data != .e_object or package_json.data.e_object.properties.len == 0) {
|
|
const root_properties = try allocator.alloc(JSAst.G.Property, 1);
|
|
root_properties[0] = JSAst.G.Property{
|
|
.key = Expr.init(
|
|
E.String,
|
|
E.String{
|
|
.data = trusted_dependencies_string,
|
|
},
|
|
logger.Loc.Empty,
|
|
),
|
|
.value = trusted_dependencies_array,
|
|
};
|
|
|
|
package_json.* = Expr.init(
|
|
E.Object,
|
|
E.Object{
|
|
.properties = JSAst.G.Property.List.fromOwnedSlice(root_properties),
|
|
},
|
|
logger.Loc.Empty,
|
|
);
|
|
} else if (needs_new_trusted_dependencies_list) {
|
|
const root_properties = try allocator.alloc(G.Property, package_json.data.e_object.properties.len + 1);
|
|
@memcpy(root_properties[0..package_json.data.e_object.properties.len], package_json.data.e_object.properties.slice());
|
|
root_properties[root_properties.len - 1] = .{
|
|
.key = Expr.init(
|
|
E.String,
|
|
E.String{
|
|
.data = trusted_dependencies_string,
|
|
},
|
|
logger.Loc.Empty,
|
|
),
|
|
.value = trusted_dependencies_array,
|
|
};
|
|
package_json.* = Expr.init(
|
|
E.Object,
|
|
E.Object{
|
|
.properties = JSAst.G.Property.List.fromOwnedSlice(root_properties),
|
|
},
|
|
logger.Loc.Empty,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// When `bun update` is called without package names, all dependencies are updated.
|
|
/// This function will identify the current workspace and update all changed package
|
|
/// versions.
|
|
pub fn editUpdateNoArgs(
|
|
manager: *PackageManager,
|
|
current_package_json: *Expr,
|
|
options: EditOptions,
|
|
) !void {
|
|
// using data store is going to result in undefined memory issues as
|
|
// the store is cleared in some workspace situations. the solution
|
|
// is to always avoid the store
|
|
Expr.Disabler.disable();
|
|
defer Expr.Disabler.enable();
|
|
|
|
const allocator = manager.allocator;
|
|
|
|
inline for (dependency_groups) |group| {
|
|
const group_str = group[0];
|
|
|
|
if (current_package_json.asProperty(group_str)) |root| {
|
|
if (root.expr.data == .e_object) {
|
|
if (options.before_install) {
|
|
// set each npm dependency to latest
|
|
for (root.expr.data.e_object.properties.slice()) |*dep| {
|
|
const key = dep.key orelse continue;
|
|
if (key.data != .e_string) continue;
|
|
const value = dep.value orelse continue;
|
|
if (value.data != .e_string) continue;
|
|
|
|
const version_literal = try value.asStringCloned(allocator) orelse bun.outOfMemory();
|
|
var tag = Dependency.Version.Tag.infer(version_literal);
|
|
|
|
// only updating dependencies with npm versions, dist-tags if `--latest`, and catalog versions.
|
|
if (tag != .npm and (tag != .dist_tag or !manager.options.do.update_to_latest) and tag != .catalog) continue;
|
|
|
|
var alias_at_index: ?usize = null;
|
|
if (strings.hasPrefixComptime(strings.trim(version_literal, &strings.whitespace_chars), "npm:")) {
|
|
// negative because the real package might have a scope
|
|
// e.g. "dep": "npm:@foo/bar@1.2.3"
|
|
if (strings.lastIndexOfChar(version_literal, '@')) |at_index| {
|
|
tag = Dependency.Version.Tag.infer(version_literal[at_index + 1 ..]);
|
|
if (tag != .npm and (tag != .dist_tag or !manager.options.do.update_to_latest) and tag != .catalog) continue;
|
|
alias_at_index = at_index;
|
|
}
|
|
}
|
|
|
|
const key_str = try key.asStringCloned(allocator) orelse unreachable;
|
|
const entry = bun.handleOom(manager.updating_packages.getOrPut(allocator, key_str));
|
|
|
|
// If a dependency is present in more than one dependency group, only one of it's versions
|
|
// will be updated. The group is determined by the order of `dependency_groups`, the same
|
|
// order used to choose which version to install.
|
|
if (entry.found_existing) continue;
|
|
|
|
entry.value_ptr.* = .{
|
|
.original_version_literal = version_literal,
|
|
.is_alias = alias_at_index != null,
|
|
.original_version = null,
|
|
};
|
|
|
|
if (manager.options.do.update_to_latest) {
|
|
// is it an aliased package
|
|
const temp_version = if (alias_at_index) |at_index|
|
|
bun.handleOom(std.fmt.allocPrint(allocator, "{s}@latest", .{version_literal[0..at_index]}))
|
|
else
|
|
bun.handleOom(allocator.dupe(u8, "latest"));
|
|
|
|
dep.value = Expr.allocate(allocator, E.String, .{
|
|
.data = temp_version,
|
|
}, logger.Loc.Empty);
|
|
}
|
|
}
|
|
} else {
|
|
const lockfile = manager.lockfile;
|
|
const string_buf = lockfile.buffers.string_bytes.items;
|
|
const workspace_package_id = lockfile.getWorkspacePackageID(manager.workspace_name_hash);
|
|
const packages = lockfile.packages.slice();
|
|
const resolutions = packages.items(.resolution);
|
|
const deps = packages.items(.dependencies)[workspace_package_id];
|
|
const resolution_ids = packages.items(.resolutions)[workspace_package_id];
|
|
const workspace_deps: []const Dependency = deps.get(lockfile.buffers.dependencies.items);
|
|
const workspace_resolution_ids = resolution_ids.get(lockfile.buffers.resolutions.items);
|
|
|
|
for (root.expr.data.e_object.properties.slice()) |*dep| {
|
|
const key = dep.key orelse continue;
|
|
if (key.data != .e_string) continue;
|
|
const value = dep.value orelse continue;
|
|
if (value.data != .e_string) continue;
|
|
|
|
const key_str = key.asString(allocator) orelse bun.outOfMemory();
|
|
|
|
updated: {
|
|
// fetchSwapRemove because we want to update the first dependency with a matching
|
|
// name, or none at all
|
|
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| {
|
|
if (package_id == invalid_package_id) continue;
|
|
|
|
const resolution = resolutions[package_id];
|
|
if (resolution.tag != .npm) continue;
|
|
|
|
const workspace_dep_name = workspace_dep.name.slice(string_buf);
|
|
if (!strings.eqlLong(workspace_dep_name, dep_name, true)) continue;
|
|
|
|
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.
|
|
if (!manager.options.do.update_to_latest and npm_version.version.isExact()) break :updated;
|
|
}
|
|
|
|
const new_version = new_version: {
|
|
const version_fmt = resolution.value.npm.version.fmt(string_buf);
|
|
if (options.exact_versions) {
|
|
break :new_version try std.fmt.allocPrint(allocator, "{}", .{version_fmt});
|
|
}
|
|
|
|
const version_literal = version_literal: {
|
|
if (!is_alias) break :version_literal entry.value.original_version_literal;
|
|
if (strings.lastIndexOfChar(entry.value.original_version_literal, '@')) |at_index| {
|
|
break :version_literal entry.value.original_version_literal[at_index + 1 ..];
|
|
}
|
|
break :version_literal entry.value.original_version_literal;
|
|
};
|
|
|
|
const pinned_version = Semver.Version.whichVersionIsPinned(version_literal);
|
|
break :new_version try switch (pinned_version) {
|
|
.patch => std.fmt.allocPrint(allocator, "{}", .{version_fmt}),
|
|
.minor => std.fmt.allocPrint(allocator, "~{}", .{version_fmt}),
|
|
.major => std.fmt.allocPrint(allocator, "^{}", .{version_fmt}),
|
|
};
|
|
};
|
|
|
|
if (is_alias) {
|
|
const dep_literal = workspace_dep.version.literal.slice(string_buf);
|
|
|
|
// negative because the real package might have a scope
|
|
// e.g. "dep": "npm:@foo/bar@1.2.3"
|
|
if (strings.lastIndexOfChar(dep_literal, '@')) |at_index| {
|
|
dep.value = Expr.allocate(allocator, E.String, .{
|
|
.data = try std.fmt.allocPrint(allocator, "{s}@{s}", .{
|
|
dep_literal[0..at_index],
|
|
new_version,
|
|
}),
|
|
}, logger.Loc.Empty);
|
|
break :updated;
|
|
}
|
|
|
|
// fallthrough and replace entire version.
|
|
}
|
|
|
|
dep.value = Expr.allocate(allocator, E.String, .{
|
|
.data = new_version,
|
|
}, logger.Loc.Empty);
|
|
break :updated;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// edits dependencies and trusted dependencies
|
|
/// if options.add_trusted_dependencies is true, gets list from PackageManager.trusted_deps_to_add_to_package_json
|
|
pub fn edit(
|
|
manager: *PackageManager,
|
|
updates: *[]UpdateRequest,
|
|
current_package_json: *Expr,
|
|
dependency_list: string,
|
|
options: EditOptions,
|
|
) !void {
|
|
// using data store is going to result in undefined memory issues as
|
|
// the store is cleared in some workspace situations. the solution
|
|
// is to always avoid the store
|
|
Expr.Disabler.disable();
|
|
defer Expr.Disabler.enable();
|
|
|
|
const allocator = manager.allocator;
|
|
var remaining = updates.len;
|
|
var replacing: usize = 0;
|
|
const only_add_missing = manager.options.enable.only_missing;
|
|
|
|
// There are three possible scenarios here
|
|
// 1. There is no "dependencies" (or equivalent list) or it is empty
|
|
// 2. There is a "dependencies" (or equivalent list), but the package name already exists in a separate list
|
|
// 3. There is a "dependencies" (or equivalent list), and the package name exists in multiple lists
|
|
// Try to use the existing spot in the dependencies list if possible
|
|
{
|
|
var original_trusted_dependencies = brk: {
|
|
if (!options.add_trusted_dependencies) break :brk E.Array{};
|
|
if (current_package_json.asProperty(trusted_dependencies_string)) |query| {
|
|
if (query.expr.data == .e_array) {
|
|
// not modifying
|
|
break :brk query.expr.data.e_array.*;
|
|
}
|
|
}
|
|
break :brk E.Array{};
|
|
};
|
|
|
|
if (options.add_trusted_dependencies) {
|
|
// Iterate backwards to avoid index issues when removing items
|
|
var i: usize = manager.trusted_deps_to_add_to_package_json.items.len;
|
|
while (i > 0) {
|
|
i -= 1;
|
|
const trusted_package_name = manager.trusted_deps_to_add_to_package_json.items[i];
|
|
for (original_trusted_dependencies.items.slice()) |item| {
|
|
if (item.data == .e_string) {
|
|
if (item.data.e_string.eql(string, trusted_package_name)) {
|
|
allocator.free(manager.trusted_deps_to_add_to_package_json.swapRemove(i));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
{
|
|
var i: usize = 0;
|
|
loop: while (i < updates.len) {
|
|
var request = &updates.*[i];
|
|
inline for ([_]string{ "dependencies", "devDependencies", "optionalDependencies", "peerDependencies" }) |list| {
|
|
if (current_package_json.asProperty(list)) |query| {
|
|
if (query.expr.data == .e_object) {
|
|
const name = request.getName();
|
|
|
|
if (query.expr.asProperty(name)) |value| {
|
|
if (value.expr.data == .e_string) {
|
|
if (request.package_id != invalid_package_id and strings.eqlLong(list, dependency_list, true)) {
|
|
replacing += 1;
|
|
} else {
|
|
if (manager.subcommand == .update and options.before_install) add_packages_to_update: {
|
|
const version_literal = try value.expr.asStringCloned(allocator) orelse break :add_packages_to_update;
|
|
var tag = Dependency.Version.Tag.infer(version_literal);
|
|
|
|
if (tag != .npm and tag != .dist_tag) break :add_packages_to_update;
|
|
|
|
const entry = bun.handleOom(manager.updating_packages.getOrPut(allocator, name));
|
|
|
|
// first come, first serve
|
|
if (entry.found_existing) break :add_packages_to_update;
|
|
|
|
var is_alias = false;
|
|
if (strings.hasPrefixComptime(strings.trim(version_literal, &strings.whitespace_chars), "npm:")) {
|
|
if (strings.lastIndexOfChar(version_literal, '@')) |at_index| {
|
|
tag = Dependency.Version.Tag.infer(version_literal[at_index + 1 ..]);
|
|
if (tag != .npm and tag != .dist_tag) break :add_packages_to_update;
|
|
is_alias = true;
|
|
}
|
|
}
|
|
|
|
entry.value_ptr.* = .{
|
|
.original_version_literal = version_literal,
|
|
.is_alias = is_alias,
|
|
.original_version = null,
|
|
};
|
|
}
|
|
if (!only_add_missing) {
|
|
request.e_string = value.expr.data.e_string;
|
|
remaining -= 1;
|
|
} else {
|
|
if (i < updates.*.len - 1) {
|
|
updates.*[i] = updates.*[updates.*.len - 1];
|
|
}
|
|
|
|
updates.*.len -= 1;
|
|
remaining -= 1;
|
|
continue :loop;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
} else {
|
|
if (request.version.tag == .github or request.version.tag == .git) {
|
|
for (query.expr.data.e_object.properties.slice()) |item| {
|
|
if (item.value) |v| {
|
|
const url = request.version.literal.slice(request.version_buf);
|
|
if (v.data == .e_string and v.data.e_string.eql(string, url)) {
|
|
request.e_string = v.data.e_string;
|
|
remaining -= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
i += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (remaining != 0) {
|
|
var dependencies: []G.Property = &[_]G.Property{};
|
|
if (current_package_json.asProperty(dependency_list)) |query| {
|
|
if (query.expr.data == .e_object) {
|
|
dependencies = query.expr.data.e_object.properties.slice();
|
|
}
|
|
}
|
|
|
|
var new_dependencies = try std.ArrayListUnmanaged(G.Property)
|
|
.initCapacity(allocator, dependencies.len + remaining - replacing);
|
|
new_dependencies.expandToCapacity();
|
|
|
|
bun.copy(G.Property, new_dependencies.items, dependencies);
|
|
@memset(new_dependencies.items[dependencies.len..], G.Property{});
|
|
|
|
var trusted_dependencies: []Expr = &[_]Expr{};
|
|
if (options.add_trusted_dependencies) {
|
|
if (current_package_json.asProperty(trusted_dependencies_string)) |query| {
|
|
if (query.expr.data == .e_array) {
|
|
trusted_dependencies = query.expr.data.e_array.items.slice();
|
|
}
|
|
}
|
|
}
|
|
|
|
const trusted_dependencies_to_add = manager.trusted_deps_to_add_to_package_json.items.len;
|
|
const new_trusted_deps: JSAst.ExprNodeList = brk: {
|
|
if (!options.add_trusted_dependencies or trusted_dependencies_to_add == 0) break :brk .empty;
|
|
|
|
const deps = try allocator.alloc(Expr, trusted_dependencies.len + trusted_dependencies_to_add);
|
|
@memcpy(deps[0..trusted_dependencies.len], trusted_dependencies);
|
|
@memset(deps[trusted_dependencies.len..], Expr.empty);
|
|
|
|
for (manager.trusted_deps_to_add_to_package_json.items) |package_name| {
|
|
if (comptime Environment.allow_assert) {
|
|
var has_missing = false;
|
|
for (deps) |dep| {
|
|
if (dep.data == .e_missing) has_missing = true;
|
|
}
|
|
bun.assert(has_missing);
|
|
}
|
|
|
|
var i = deps.len;
|
|
while (i > 0) {
|
|
i -= 1;
|
|
if (deps[i].data == .e_missing) {
|
|
deps[i] = Expr.allocate(allocator, E.String, .{
|
|
.data = package_name,
|
|
}, logger.Loc.Empty);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (comptime Environment.allow_assert) {
|
|
for (deps) |dep| bun.assert(dep.data != .e_missing);
|
|
}
|
|
|
|
break :brk .fromOwnedSlice(deps);
|
|
};
|
|
|
|
for (updates.*) |*request| {
|
|
if (request.e_string != null) continue;
|
|
defer if (comptime Environment.allow_assert) bun.assert(request.e_string != null);
|
|
|
|
var k: usize = 0;
|
|
while (k < new_dependencies.items.len) : (k += 1) {
|
|
if (new_dependencies.items[k].key) |key| {
|
|
const name = request.getName();
|
|
if (!key.data.e_string.eql(string, name)) continue;
|
|
if (request.package_id == invalid_package_id) {
|
|
// Duplicate dependency (e.g., "react" in both "dependencies" and
|
|
// "optionalDependencies"). Remove the old dependency.
|
|
new_dependencies.items[k] = .{};
|
|
new_dependencies.items.len -= 1;
|
|
}
|
|
}
|
|
|
|
new_dependencies.items[k].key = JSAst.Expr.allocate(
|
|
allocator,
|
|
JSAst.E.String,
|
|
.{ .data = try allocator.dupe(u8, request.getResolvedName(manager.lockfile)) },
|
|
logger.Loc.Empty,
|
|
);
|
|
|
|
new_dependencies.items[k].value = JSAst.Expr.allocate(allocator, JSAst.E.String, .{
|
|
// we set it later
|
|
.data = "",
|
|
}, logger.Loc.Empty);
|
|
|
|
request.e_string = new_dependencies.items[k].value.?.data.e_string;
|
|
break;
|
|
}
|
|
}
|
|
|
|
var needs_new_dependency_list = true;
|
|
const dependencies_object: JSAst.Expr = brk: {
|
|
if (current_package_json.asProperty(dependency_list)) |query| {
|
|
if (query.expr.data == .e_object) {
|
|
needs_new_dependency_list = false;
|
|
|
|
break :brk query.expr;
|
|
}
|
|
}
|
|
|
|
break :brk JSAst.Expr.allocate(allocator, JSAst.E.Object, .{
|
|
.properties = .empty,
|
|
}, logger.Loc.Empty);
|
|
};
|
|
|
|
dependencies_object.data.e_object.properties = JSAst.G.Property.List.moveFromList(&new_dependencies);
|
|
if (dependencies_object.data.e_object.properties.len > 1)
|
|
dependencies_object.data.e_object.alphabetizeProperties();
|
|
|
|
var needs_new_trusted_dependencies_list = true;
|
|
const trusted_dependencies_array: Expr = brk: {
|
|
if (!options.add_trusted_dependencies or trusted_dependencies_to_add == 0) {
|
|
needs_new_trusted_dependencies_list = false;
|
|
break :brk Expr.empty;
|
|
}
|
|
if (current_package_json.asProperty(trusted_dependencies_string)) |query| {
|
|
if (query.expr.data == .e_array) {
|
|
needs_new_trusted_dependencies_list = false;
|
|
break :brk query.expr;
|
|
}
|
|
}
|
|
|
|
break :brk Expr.allocate(allocator, E.Array, .{
|
|
.items = new_trusted_deps,
|
|
}, logger.Loc.Empty);
|
|
};
|
|
|
|
if (options.add_trusted_dependencies and trusted_dependencies_to_add > 0) {
|
|
trusted_dependencies_array.data.e_array.items = new_trusted_deps;
|
|
if (new_trusted_deps.len > 1) {
|
|
trusted_dependencies_array.data.e_array.alphabetizeStrings();
|
|
}
|
|
}
|
|
|
|
if (current_package_json.data != .e_object or current_package_json.data.e_object.properties.len == 0) {
|
|
const root_properties = try allocator.alloc(JSAst.G.Property, if (options.add_trusted_dependencies) 2 else 1);
|
|
root_properties[0] = JSAst.G.Property{
|
|
.key = JSAst.Expr.allocate(allocator, JSAst.E.String, .{
|
|
.data = dependency_list,
|
|
}, logger.Loc.Empty),
|
|
.value = dependencies_object,
|
|
};
|
|
|
|
if (options.add_trusted_dependencies) {
|
|
root_properties[1] = JSAst.G.Property{
|
|
.key = Expr.allocate(allocator, E.String, .{
|
|
.data = trusted_dependencies_string,
|
|
}, logger.Loc.Empty),
|
|
.value = trusted_dependencies_array,
|
|
};
|
|
}
|
|
|
|
current_package_json.* = JSAst.Expr.allocate(allocator, JSAst.E.Object, .{
|
|
.properties = JSAst.G.Property.List.fromOwnedSlice(root_properties),
|
|
}, logger.Loc.Empty);
|
|
} else {
|
|
if (needs_new_dependency_list and needs_new_trusted_dependencies_list) {
|
|
const root_properties = try allocator.alloc(G.Property, current_package_json.data.e_object.properties.len + 2);
|
|
@memcpy(root_properties[0..current_package_json.data.e_object.properties.len], current_package_json.data.e_object.properties.slice());
|
|
root_properties[root_properties.len - 2] = .{
|
|
.key = Expr.allocate(allocator, E.String, E.String{
|
|
.data = dependency_list,
|
|
}, logger.Loc.Empty),
|
|
.value = dependencies_object,
|
|
};
|
|
root_properties[root_properties.len - 1] = .{
|
|
.key = Expr.allocate(allocator, E.String, .{
|
|
.data = trusted_dependencies_string,
|
|
}, logger.Loc.Empty),
|
|
.value = trusted_dependencies_array,
|
|
};
|
|
current_package_json.* = Expr.allocate(allocator, E.Object, .{
|
|
.properties = G.Property.List.fromOwnedSlice(root_properties),
|
|
}, logger.Loc.Empty);
|
|
} else if (needs_new_dependency_list or needs_new_trusted_dependencies_list) {
|
|
const root_properties = try allocator.alloc(JSAst.G.Property, current_package_json.data.e_object.properties.len + 1);
|
|
@memcpy(root_properties[0..current_package_json.data.e_object.properties.len], current_package_json.data.e_object.properties.slice());
|
|
root_properties[root_properties.len - 1] = .{
|
|
.key = JSAst.Expr.allocate(allocator, JSAst.E.String, .{
|
|
.data = if (needs_new_dependency_list) dependency_list else trusted_dependencies_string,
|
|
}, logger.Loc.Empty),
|
|
.value = if (needs_new_dependency_list) dependencies_object else trusted_dependencies_array,
|
|
};
|
|
current_package_json.* = JSAst.Expr.allocate(allocator, JSAst.E.Object, .{
|
|
.properties = JSAst.G.Property.List.fromOwnedSlice(root_properties),
|
|
}, logger.Loc.Empty);
|
|
}
|
|
}
|
|
}
|
|
|
|
const resolutions = if (!options.before_install) manager.lockfile.packages.items(.resolution) else &.{};
|
|
for (updates.*) |*request| {
|
|
if (request.e_string) |e_string| {
|
|
if (request.package_id >= resolutions.len or resolutions[request.package_id].tag == .uninitialized) {
|
|
e_string.data = uninitialized: {
|
|
if (manager.subcommand == .update and manager.options.do.update_to_latest) {
|
|
break :uninitialized try allocator.dupe(u8, "latest");
|
|
}
|
|
|
|
if (manager.subcommand != .update or !options.before_install or e_string.isBlank() or request.version.tag == .npm) {
|
|
break :uninitialized switch (request.version.tag) {
|
|
.uninitialized => try allocator.dupe(u8, "latest"),
|
|
else => try allocator.dupe(u8, request.version.literal.slice(request.version_buf)),
|
|
};
|
|
} else {
|
|
break :uninitialized e_string.data;
|
|
}
|
|
};
|
|
|
|
continue;
|
|
}
|
|
e_string.data = switch (resolutions[request.package_id].tag) {
|
|
.npm => npm: {
|
|
if (manager.subcommand == .update and (request.version.tag == .dist_tag or request.version.tag == .npm)) {
|
|
if (manager.updating_packages.fetchSwapRemove(request.name)) |entry| {
|
|
var alias_at_index: ?usize = null;
|
|
|
|
const new_version = new_version: {
|
|
const version_fmt = resolutions[request.package_id].value.npm.version.fmt(manager.lockfile.buffers.string_bytes.items);
|
|
if (options.exact_versions) {
|
|
break :new_version try std.fmt.allocPrint(allocator, "{}", .{version_fmt});
|
|
}
|
|
|
|
const version_literal = version_literal: {
|
|
if (!entry.value.is_alias) break :version_literal entry.value.original_version_literal;
|
|
if (strings.lastIndexOfChar(entry.value.original_version_literal, '@')) |at_index| {
|
|
alias_at_index = at_index;
|
|
break :version_literal entry.value.original_version_literal[at_index + 1 ..];
|
|
}
|
|
|
|
break :version_literal entry.value.original_version_literal;
|
|
};
|
|
|
|
const pinned_version = Semver.Version.whichVersionIsPinned(version_literal);
|
|
break :new_version try switch (pinned_version) {
|
|
.patch => std.fmt.allocPrint(allocator, "{}", .{version_fmt}),
|
|
.minor => std.fmt.allocPrint(allocator, "~{}", .{version_fmt}),
|
|
.major => std.fmt.allocPrint(allocator, "^{}", .{version_fmt}),
|
|
};
|
|
};
|
|
|
|
if (entry.value.is_alias) {
|
|
const dep_literal = entry.value.original_version_literal;
|
|
|
|
if (strings.lastIndexOfChar(dep_literal, '@')) |at_index| {
|
|
break :npm try std.fmt.allocPrint(allocator, "{s}@{s}", .{
|
|
dep_literal[0..at_index],
|
|
new_version,
|
|
});
|
|
}
|
|
}
|
|
|
|
break :npm new_version;
|
|
}
|
|
}
|
|
if (request.version.tag == .dist_tag or
|
|
(manager.subcommand == .update and request.version.tag == .npm and !request.version.value.npm.version.isExact()))
|
|
{
|
|
const new_version = try switch (options.exact_versions) {
|
|
inline else => |exact_versions| std.fmt.allocPrint(
|
|
allocator,
|
|
if (comptime exact_versions) "{}" else "^{}",
|
|
.{
|
|
resolutions[request.package_id].value.npm.version.fmt(request.version_buf),
|
|
},
|
|
),
|
|
};
|
|
|
|
if (request.version.tag == .npm and request.version.value.npm.is_alias) {
|
|
const dep_literal = request.version.literal.slice(request.version_buf);
|
|
if (strings.indexOfChar(dep_literal, '@')) |at_index| {
|
|
break :npm try std.fmt.allocPrint(allocator, "{s}@{s}", .{
|
|
dep_literal[0..at_index],
|
|
new_version,
|
|
});
|
|
}
|
|
}
|
|
|
|
break :npm new_version;
|
|
}
|
|
|
|
break :npm try allocator.dupe(u8, request.version.literal.slice(request.version_buf));
|
|
},
|
|
|
|
.workspace => try allocator.dupe(u8, "workspace:*"),
|
|
else => try allocator.dupe(u8, request.version.literal.slice(request.version_buf)),
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
const trusted_dependencies_string = "trustedDependencies";
|
|
|
|
const string = []const u8;
|
|
|
|
const std = @import("std");
|
|
|
|
const bun = @import("bun");
|
|
const Environment = bun.Environment;
|
|
const Semver = bun.Semver;
|
|
const logger = bun.logger;
|
|
const strings = bun.strings;
|
|
|
|
const JSAst = bun.ast;
|
|
const E = JSAst.E;
|
|
const Expr = JSAst.Expr;
|
|
const G = JSAst.G;
|
|
|
|
const Dependency = bun.install.Dependency;
|
|
const invalid_package_id = bun.install.invalid_package_id;
|
|
|
|
const PackageManager = bun.install.PackageManager;
|
|
const UpdateRequest = bun.install.PackageManager.UpdateRequest;
|