aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/install/install.zig54
-rw-r--r--src/js_ast.zig123
-rw-r--r--test/bun.js/install/bun-install.test.ts484
3 files changed, 507 insertions, 154 deletions
diff --git a/src/install/install.zig b/src/install/install.zig
index 99cb9213d..dc416005e 100644
--- a/src/install/install.zig
+++ b/src/install/install.zig
@@ -4270,17 +4270,25 @@ pub const PackageManager = struct {
// 3. There is a "dependencies" (or equivalent list), and the package name exists in multiple lists
ast_modifier: {
// Try to use the existing spot in the dependencies list if possible
- for (updates) |update, i| {
+ for (updates) |*update| {
outer: for (dependency_lists_to_check) |list| {
if (current_package_json.asProperty(list)) |query| {
if (query.expr.data == .e_object) {
if (query.expr.asProperty(update.name)) |value| {
if (value.expr.data == .e_string) {
if (update.resolved_name.isEmpty()) {
- updates[i].e_string = value.expr.data.e_string;
+ update.e_string = value.expr.data.e_string;
remaining -= 1;
} else {
replacing += 1;
+ update.e_string = (try JSAst.Expr.init(
+ JSAst.E.String,
+ JSAst.E.String{
+ // we set it later
+ .data = "",
+ },
+ logger.Loc.Empty,
+ ).clone(allocator)).data.e_string;
}
}
break :outer;
@@ -4290,13 +4298,16 @@ pub const PackageManager = struct {
}
}
- if (remaining == 0)
+ if (remaining == 0 and replacing == 0)
break :ast_modifier;
var dependencies: []G.Property = &[_]G.Property{};
+ var dependencies_obj: ?*JSAst.E.Object = null;
if (current_package_json.asProperty(dependency_list)) |query| {
if (query.expr.data == .e_object) {
- dependencies = query.expr.data.e_object.properties.slice();
+ dependencies_obj = query.expr.data.e_object;
+
+ dependencies = dependencies_obj.?.properties.slice();
}
}
@@ -4304,32 +4315,25 @@ pub const PackageManager = struct {
std.mem.copy(G.Property, new_dependencies, dependencies);
std.mem.set(G.Property, new_dependencies[dependencies.len..], G.Property{});
- outer: for (updates) |update, j| {
+ outer: for (updates) |*update| {
if (update.e_string != null) continue;
+ defer std.debug.assert(update.e_string != null);
var k: usize = 0;
-
while (k < new_dependencies.len) : (k += 1) {
- if (new_dependencies[k].key) |key| {
- if (key.data.e_string.eql(string, update.name)) {
- if (update.resolved_name.isEmpty()) {
- // This actually is a duplicate
- // like "react" appearing in both "dependencies" and "optionalDependencies"
- // For this case, we'll just swap remove it
- if (new_dependencies.len > 1) {
- new_dependencies[k] = new_dependencies[new_dependencies.len - 1];
- new_dependencies = new_dependencies[0 .. new_dependencies.len - 1];
- } else {
- new_dependencies = &[_]G.Property{};
- }
- continue;
+ if (dependencies_obj) |obj| {
+ if (obj.asProperty(update.name)) |prop| {
+ if (prop.expr.data == .e_string) {
+ var str = try prop.expr.clone(allocator);
+ str.data.e_string.* = try str.data.e_string.clone(allocator);
+ update.e_string = str.data.e_string;
+ continue :outer;
}
- new_dependencies[k].key = null;
}
}
if (new_dependencies[k].key == null) {
- new_dependencies[k].key = JSAst.Expr.init(
+ new_dependencies[k].key = try JSAst.Expr.init(
JSAst.E.String,
JSAst.E.String{
.data = try allocator.dupe(u8, if (update.is_aliased or update.resolved_name.isEmpty())
@@ -4338,17 +4342,17 @@ pub const PackageManager = struct {
update.resolved_name.slice(update.version_buf)),
},
logger.Loc.Empty,
- );
+ ).clone(allocator);
- new_dependencies[k].value = JSAst.Expr.init(
+ new_dependencies[k].value = try JSAst.Expr.init(
JSAst.E.String,
JSAst.E.String{
// we set it later
.data = "",
},
logger.Loc.Empty,
- );
- updates[j].e_string = new_dependencies[k].value.?.data.e_string;
+ ).clone(allocator);
+ update.e_string = new_dependencies[k].value.?.data.e_string;
continue :outer;
}
}
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 350bf6561..5b575eda5 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -2228,6 +2228,13 @@ pub const Expr = struct {
loc: logger.Loc,
data: Data,
+ pub fn clone(this: Expr, allocator: std.mem.Allocator) !Expr {
+ return .{
+ .loc = this.loc,
+ .data = try this.data.clone(allocator),
+ };
+ }
+
pub fn wrapInArrow(this: Expr, allocator: std.mem.Allocator) !Expr {
var stmts = try allocator.alloc(Stmt, 1);
stmts[0] = Stmt.alloc(S.Return, S.Return{ .value = this }, this.loc);
@@ -3608,6 +3615,122 @@ pub const Expr = struct {
// If it ends up in JSParser or JSPrinter, it is a bug.
inline_identifier: i32,
+ pub fn clone(this: Expr.Data, allocator: std.mem.Allocator) !Data {
+ return switch (this) {
+ .e_array => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_array)));
+ item.* = el.*;
+ return .{ .e_array = item };
+ },
+ .e_unary => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_unary)));
+ item.* = el.*;
+ return .{ .e_unary = item };
+ },
+ .e_binary => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_binary)));
+ item.* = el.*;
+ return .{ .e_binary = item };
+ },
+ .e_class => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_class)));
+ item.* = el.*;
+ return .{ .e_class = item };
+ },
+ .e_new => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_new)));
+ item.* = el.*;
+ return .{ .e_new = item };
+ },
+ .e_function => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_function)));
+ item.* = el.*;
+ return .{ .e_function = item };
+ },
+ .e_call => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_call)));
+ item.* = el.*;
+ return .{ .e_call = item };
+ },
+ .e_dot => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_dot)));
+ item.* = el.*;
+ return .{ .e_dot = item };
+ },
+ .e_index => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_index)));
+ item.* = el.*;
+ return .{ .e_index = item };
+ },
+ .e_arrow => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_arrow)));
+ item.* = el.*;
+ return .{ .e_arrow = item };
+ },
+ .e_jsx_element => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_jsx_element)));
+ item.* = el.*;
+ return .{ .e_jsx_element = item };
+ },
+ .e_object => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_object)));
+ item.* = el.*;
+ return .{ .e_object = item };
+ },
+ .e_spread => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_spread)));
+ item.* = el.*;
+ return .{ .e_spread = item };
+ },
+ .e_template_part => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_template_part)));
+ item.* = el.*;
+ return .{ .e_template_part = item };
+ },
+ .e_template => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_template)));
+ item.* = el.*;
+ return .{ .e_template = item };
+ },
+ .e_reg_exp => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_reg_exp)));
+ item.* = el.*;
+ return .{ .e_reg_exp = item };
+ },
+ .e_await => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_await)));
+ item.* = el.*;
+ return .{ .e_await = item };
+ },
+ .e_yield => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_yield)));
+ item.* = el.*;
+ return .{ .e_yield = item };
+ },
+ .e_if => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_if)));
+ item.* = el.*;
+ return .{ .e_if = item };
+ },
+ .e_import => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_import)));
+ item.* = el.*;
+ return .{ .e_import = item };
+ },
+ .e_big_int => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_big_int)));
+ item.* = el.*;
+ return .{ .e_big_int = item };
+ },
+ .e_string => |el| {
+ var item = try allocator.create(std.meta.Child(@TypeOf(this.e_string)));
+ item.* = el.*;
+ return .{ .e_string = item };
+ },
+ else => this,
+ };
+ }
+
pub fn canBeConstValue(this: Expr.Data) bool {
return switch (this) {
.e_number, .e_boolean, .e_null, .e_undefined => true,
diff --git a/test/bun.js/install/bun-install.test.ts b/test/bun.js/install/bun-install.test.ts
index 7d127d4ed..33a662dc2 100644
--- a/test/bun.js/install/bun-install.test.ts
+++ b/test/bun.js/install/bun-install.test.ts
@@ -9,12 +9,7 @@ import {
} from "bun:test";
import { bunExe } from "bunExe";
import { bunEnv as env } from "bunEnv";
-import {
- access,
- mkdir,
- readlink,
- writeFile,
-} from "fs/promises";
+import { access, mkdir, readlink, writeFile } from "fs/promises";
import { join } from "path";
import {
dummyAfterAll,
@@ -28,6 +23,7 @@ import {
root_url,
setHandler,
} from "./dummy.registry";
+import { rmSync } from "fs";
beforeAll(dummyBeforeAll);
afterAll(dummyAfterAll);
@@ -161,10 +157,7 @@ it("should handle empty string in dependencies", async () => {
" 1 packages installed",
]);
expect(await exited).toBe(0);
- expect(urls).toEqual([
- `${root_url}/bar`,
- `${root_url}/bar.tgz`,
- ]);
+ expect(urls).toEqual([`${root_url}/bar`, `${root_url}/bar.tgz`]);
expect(requested).toBe(2);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
".cache",
@@ -575,10 +568,7 @@ it("should handle ^0 in dependencies", async () => {
" 1 packages installed",
]);
expect(await exited).toBe(0);
- expect(urls).toEqual([
- `${root_url}/bar`,
- `${root_url}/bar.tgz`,
- ]);
+ expect(urls).toEqual([`${root_url}/bar`, `${root_url}/bar.tgz`]);
expect(requested).toBe(2);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
".cache",
@@ -667,10 +657,7 @@ it("should handle ^0.0 in dependencies", async () => {
" 1 packages installed",
]);
expect(await exited).toBe(0);
- expect(urls).toEqual([
- `${root_url}/bar`,
- `${root_url}/bar.tgz`,
- ]);
+ expect(urls).toEqual([`${root_url}/bar`, `${root_url}/bar.tgz`]);
expect(requested).toBe(2);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
".cache",
@@ -798,10 +785,7 @@ it("should handle ^0.0.2 in dependencies", async () => {
" 1 packages installed",
]);
expect(await exited).toBe(0);
- expect(urls).toEqual([
- `${root_url}/bar`,
- `${root_url}/bar.tgz`,
- ]);
+ expect(urls).toEqual([`${root_url}/bar`, `${root_url}/bar.tgz`]);
expect(requested).toBe(2);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
".cache",
@@ -851,10 +835,7 @@ it("should handle ^0.0.2-rc in dependencies", async () => {
" 1 packages installed",
]);
expect(await exited).toBe(0);
- expect(urls).toEqual([
- `${root_url}/bar`,
- `${root_url}/bar.tgz`,
- ]);
+ expect(urls).toEqual([`${root_url}/bar`, `${root_url}/bar.tgz`]);
expect(requested).toBe(2);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
".cache",
@@ -904,10 +885,7 @@ it("should handle ^0.0.2-alpha.3+b4d in dependencies", async () => {
" 1 packages installed",
]);
expect(await exited).toBe(0);
- expect(urls).toEqual([
- `${root_url}/bar`,
- `${root_url}/bar.tgz`,
- ]);
+ expect(urls).toEqual([`${root_url}/bar`, `${root_url}/bar.tgz`]);
expect(requested).toBe(2);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
".cache",
@@ -927,11 +905,13 @@ it("should handle ^0.0.2-alpha.3+b4d in dependencies", async () => {
it("should handle dependency aliasing", async () => {
const urls = [];
- setHandler(dummyRegistry(urls, "0.0.3", {
- bin: {
- "baz-run": "index.js",
- },
- }));
+ setHandler(
+ dummyRegistry(urls, "0.0.3", {
+ bin: {
+ "baz-run": "index.js",
+ },
+ }),
+ );
await writeFile(
join(package_dir, "package.json"),
JSON.stringify({
@@ -961,25 +941,25 @@ it("should handle dependency aliasing", async () => {
" 1 packages installed",
]);
expect(await exited).toBe(0);
- expect(urls).toEqual([
- `${root_url}/baz`,
- `${root_url}/baz.tgz`,
- ]);
+ expect(urls).toEqual([`${root_url}/baz`, `${root_url}/baz.tgz`]);
expect(requested).toBe(2);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
".bin",
".cache",
"Bar",
]);
- expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toEqual([
- "baz-run",
- ]);
- expect(await readlink(join(package_dir, "node_modules", ".bin", "baz-run"))).toBe(join("..", "Bar", "index.js"));
- expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual([
- "index.js",
- "package.json",
- ]);
- expect(await file(join(package_dir, "node_modules", "Bar", "package.json")).json()).toEqual({
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", ".bin")),
+ ).toEqual(["baz-run"]);
+ expect(
+ await readlink(join(package_dir, "node_modules", ".bin", "baz-run")),
+ ).toBe(join("..", "Bar", "index.js"));
+ expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual(
+ ["index.js", "package.json"],
+ );
+ expect(
+ await file(join(package_dir, "node_modules", "Bar", "package.json")).json(),
+ ).toEqual({
name: "baz",
version: "0.0.3",
bin: {
@@ -991,11 +971,13 @@ it("should handle dependency aliasing", async () => {
it("should handle dependency aliasing (versioned)", async () => {
const urls: string[] = [];
- setHandler(dummyRegistry(urls, "0.0.3", {
- bin: {
- "baz-run": "index.js",
- },
- }));
+ setHandler(
+ dummyRegistry(urls, "0.0.3", {
+ bin: {
+ "baz-run": "index.js",
+ },
+ }),
+ );
await writeFile(
join(package_dir, "package.json"),
JSON.stringify({
@@ -1025,25 +1007,25 @@ it("should handle dependency aliasing (versioned)", async () => {
" 1 packages installed",
]);
expect(await exited).toBe(0);
- expect(urls).toEqual([
- `${root_url}/baz`,
- `${root_url}/baz.tgz`,
- ]);
+ expect(urls).toEqual([`${root_url}/baz`, `${root_url}/baz.tgz`]);
expect(requested).toBe(2);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
".bin",
".cache",
"Bar",
]);
- expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toEqual([
- "baz-run",
- ]);
- expect(await readlink(join(package_dir, "node_modules", ".bin", "baz-run"))).toBe(join("..", "Bar", "index.js"));
- expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual([
- "index.js",
- "package.json",
- ]);
- expect(await file(join(package_dir, "node_modules", "Bar", "package.json")).json()).toEqual({
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", ".bin")),
+ ).toEqual(["baz-run"]);
+ expect(
+ await readlink(join(package_dir, "node_modules", ".bin", "baz-run")),
+ ).toBe(join("..", "Bar", "index.js"));
+ expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual(
+ ["index.js", "package.json"],
+ );
+ expect(
+ await file(join(package_dir, "node_modules", "Bar", "package.json")).json(),
+ ).toEqual({
name: "baz",
version: "0.0.3",
bin: {
@@ -1053,13 +1035,241 @@ it("should handle dependency aliasing (versioned)", async () => {
await access(join(package_dir, "bun.lockb"));
});
-it("should handle dependency aliasing (dist-tagged)", async () => {
+it("should handle ^0.0.2-rc in dependencies", async () => {
+ const urls: string[] = [];
+ setHandler(dummyRegistry(urls, "0.0.2-rc"));
+ await writeFile(
+ join(package_dir, "package.json"),
+ JSON.stringify({
+ name: "foo",
+ version: "0.0.1",
+ dependencies: {
+ bar: "^0.0.2-rc",
+ },
+ }),
+ );
+ const { stdout, stderr, exited } = spawn({
+ cmd: [bunExe(), "install", "--config", import.meta.dir + "/basic.toml"],
+ cwd: package_dir,
+ stdout: null,
+ stdin: "pipe",
+ stderr: "pipe",
+ env,
+ });
+ expect(stderr).toBeDefined();
+ const err = await new Response(stderr).text();
+ expect(err).toContain("Saved lockfile");
+ expect(stdout).toBeDefined();
+ const out = await new Response(stdout).text();
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
+ " + bar@0.0.2-rc",
+ "",
+ " 1 packages installed",
+ ]);
+ expect(await exited).toBe(0);
+ expect(urls).toEqual([`${root_url}/bar`, `${root_url}/bar.tgz`]);
+ expect(requested).toBe(2);
+ expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
+ ".cache",
+ "bar",
+ ]);
+ expect(await readdirSorted(join(package_dir, "node_modules", "bar"))).toEqual(
+ ["package.json"],
+ );
+ expect(
+ await file(join(package_dir, "node_modules", "bar", "package.json")).json(),
+ ).toEqual({
+ name: "bar",
+ version: "0.0.2",
+ });
+ await access(join(package_dir, "bun.lockb"));
+});
+
+it("should handle ^0.0.2-alpha.3+b4d in dependencies", async () => {
const urls: string[] = [];
- setHandler(dummyRegistry(urls, "0.0.3", {
+ setHandler(dummyRegistry(urls, "0.0.2-alpha.3"));
+ await writeFile(
+ join(package_dir, "package.json"),
+ JSON.stringify({
+ name: "foo",
+ version: "0.0.1",
+ dependencies: {
+ bar: "^0.0.2-alpha.3+b4d",
+ },
+ }),
+ );
+ const { stdout, stderr, exited } = spawn({
+ cmd: [bunExe(), "install", "--config", import.meta.dir + "/basic.toml"],
+ cwd: package_dir,
+ stdout: null,
+ stdin: "pipe",
+ stderr: "pipe",
+ env,
+ });
+ expect(stderr).toBeDefined();
+ const err = await new Response(stderr).text();
+ expect(err).toContain("Saved lockfile");
+ expect(stdout).toBeDefined();
+ const out = await new Response(stdout).text();
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
+ " + bar@0.0.2-alpha.3",
+ "",
+ " 1 packages installed",
+ ]);
+ expect(await exited).toBe(0);
+ expect(urls).toEqual([`${root_url}/bar`, `${root_url}/bar.tgz`]);
+ expect(requested).toBe(2);
+ expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
+ ".cache",
+ "bar",
+ ]);
+ expect(await readdirSorted(join(package_dir, "node_modules", "bar"))).toEqual(
+ ["package.json"],
+ );
+ expect(
+ await file(join(package_dir, "node_modules", "bar", "package.json")).json(),
+ ).toEqual({
+ name: "bar",
+ version: "0.0.2",
+ });
+ await access(join(package_dir, "bun.lockb"));
+});
+
+it("should handle dependency aliasing", async () => {
+ const urls = [];
+ setHandler(
+ dummyRegistry(urls, "0.0.3", {
+ bin: {
+ "baz-run": "index.js",
+ },
+ }),
+ );
+ await writeFile(
+ join(package_dir, "package.json"),
+ JSON.stringify({
+ name: "Foo",
+ version: "0.0.1",
+ dependencies: {
+ Bar: "npm:baz",
+ },
+ }),
+ );
+ const { stdout, stderr, exited } = spawn({
+ cmd: [bunExe(), "install", "--config", import.meta.dir + "/basic.toml"],
+ cwd: package_dir,
+ stdout: null,
+ stdin: "pipe",
+ stderr: "pipe",
+ env,
+ });
+ expect(stderr).toBeDefined();
+ const err = await new Response(stderr).text();
+ expect(err).toContain("Saved lockfile");
+ expect(stdout).toBeDefined();
+ const out = await new Response(stdout).text();
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
+ " + Bar@0.0.3",
+ "",
+ " 1 packages installed",
+ ]);
+ expect(await exited).toBe(0);
+ expect(urls).toEqual([`${root_url}/baz`, `${root_url}/baz.tgz`]);
+ expect(requested).toBe(2);
+ expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
+ ".bin",
+ ".cache",
+ "Bar",
+ ]);
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", ".bin")),
+ ).toEqual(["baz-run"]);
+ expect(
+ await readlink(join(package_dir, "node_modules", ".bin", "baz-run")),
+ ).toBe(join("..", "Bar", "index.js"));
+ expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual(
+ ["index.js", "package.json"],
+ );
+ expect(
+ await file(join(package_dir, "node_modules", "Bar", "package.json")).json(),
+ ).toEqual({
+ name: "baz",
+ version: "0.0.3",
bin: {
"baz-run": "index.js",
},
- }));
+ });
+ await access(join(package_dir, "bun.lockb"));
+});
+
+it("should let you add the same package twice", async () => {
+ const urls: string[] = [];
+ setHandler(dummyRegistry(urls, "0.0.3", {}));
+ await writeFile(
+ join(package_dir, "package.json"),
+ JSON.stringify({
+ name: "Foo",
+ version: "0.0.1",
+ dependencies: {},
+ }),
+ );
+ rmSync(`${root_url}/baz`, { recursive: true, force: true });
+ for (let i = 0; i < 2; i++) {
+ const { stdout, stderr, exited } = spawn({
+ cmd: [
+ bunExe(),
+ "install",
+ "baz@0.0.3",
+ "--config",
+ import.meta.dir + "/basic.toml",
+ ],
+ cwd: package_dir,
+ stdout: null,
+ stdin: "pipe",
+ stderr: "pipe",
+ env,
+ });
+ expect(stderr).toBeDefined();
+ const err = await new Response(stderr).text();
+ expect(err).toContain("Saved lockfile");
+ expect(stdout).toBeDefined();
+ const out = await new Response(stdout).text();
+ expect(out).toContain("installed baz@0.0.3");
+ if (i === 0) {
+ expect(out).toContain("1 packages installed");
+ } else {
+ expect(out).not.toContain("1 packages installed");
+ }
+ expect(await exited).toBe(0);
+ expect(urls).toEqual([`${root_url}/baz`, `${root_url}/baz.tgz`]);
+ expect(requested).toBe(2);
+ expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
+ ".cache",
+ "baz",
+ ]);
+ expect(
+ await file(
+ join(package_dir, "node_modules", "baz", "package.json"),
+ ).json(),
+ ).toEqual({
+ name: "baz",
+ version: "0.0.3",
+ bin: {
+ "baz-run": "index.js",
+ },
+ });
+ await access(join(package_dir, "bun.lockb"));
+ }
+});
+
+it("should handle dependency aliasing (dist-tagged)", async () => {
+ const urls: string[] = [];
+ setHandler(
+ dummyRegistry(urls, "0.0.3", {
+ bin: {
+ "baz-run": "index.js",
+ },
+ }),
+ );
await writeFile(
join(package_dir, "package.json"),
JSON.stringify({
@@ -1089,25 +1299,25 @@ it("should handle dependency aliasing (dist-tagged)", async () => {
" 1 packages installed",
]);
expect(await exited).toBe(0);
- expect(urls).toEqual([
- `${root_url}/baz`,
- `${root_url}/baz.tgz`,
- ]);
+ expect(urls).toEqual([`${root_url}/baz`, `${root_url}/baz.tgz`]);
expect(requested).toBe(2);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
".bin",
".cache",
"Bar",
]);
- expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toEqual([
- "baz-run",
- ]);
- expect(await readlink(join(package_dir, "node_modules", ".bin", "baz-run"))).toBe(join("..", "Bar", "index.js"));
- expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual([
- "index.js",
- "package.json",
- ]);
- expect(await file(join(package_dir, "node_modules", "Bar", "package.json")).json()).toEqual({
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", ".bin")),
+ ).toEqual(["baz-run"]);
+ expect(
+ await readlink(join(package_dir, "node_modules", ".bin", "baz-run")),
+ ).toBe(join("..", "Bar", "index.js"));
+ expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual(
+ ["index.js", "package.json"],
+ );
+ expect(
+ await file(join(package_dir, "node_modules", "Bar", "package.json")).json(),
+ ).toEqual({
name: "baz",
version: "0.0.3",
bin: {
@@ -1119,11 +1329,13 @@ it("should handle dependency aliasing (dist-tagged)", async () => {
it("should not reinstall aliased dependencies", async () => {
const urls = [];
- setHandler(dummyRegistry(urls, "0.0.3", {
- bin: {
- "baz-run": "index.js",
- },
- }));
+ setHandler(
+ dummyRegistry(urls, "0.0.3", {
+ bin: {
+ "baz-run": "index.js",
+ },
+ }),
+ );
await writeFile(
join(package_dir, "package.json"),
JSON.stringify({
@@ -1157,25 +1369,25 @@ it("should not reinstall aliased dependencies", async () => {
" 1 packages installed",
]);
expect(await exited1).toBe(0);
- expect(urls).toEqual([
- `${root_url}/baz`,
- `${root_url}/baz.tgz`,
- ]);
+ expect(urls).toEqual([`${root_url}/baz`, `${root_url}/baz.tgz`]);
expect(requested).toBe(2);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
".bin",
".cache",
"Bar",
]);
- expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toEqual([
- "baz-run",
- ]);
- expect(await readlink(join(package_dir, "node_modules", ".bin", "baz-run"))).toBe(join("..", "Bar", "index.js"));
- expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual([
- "index.js",
- "package.json",
- ]);
- expect(await file(join(package_dir, "node_modules", "Bar", "package.json")).json()).toEqual({
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", ".bin")),
+ ).toEqual(["baz-run"]);
+ expect(
+ await readlink(join(package_dir, "node_modules", ".bin", "baz-run")),
+ ).toBe(join("..", "Bar", "index.js"));
+ expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual(
+ ["index.js", "package.json"],
+ );
+ expect(
+ await file(join(package_dir, "node_modules", "Bar", "package.json")).json(),
+ ).toEqual({
name: "baz",
version: "0.0.3",
bin: {
@@ -1214,15 +1426,18 @@ it("should not reinstall aliased dependencies", async () => {
".cache",
"Bar",
]);
- expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toEqual([
- "baz-run",
- ]);
- expect(await readlink(join(package_dir, "node_modules", ".bin", "baz-run"))).toBe(join("..", "Bar", "index.js"));
- expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual([
- "index.js",
- "package.json",
- ]);
- expect(await file(join(package_dir, "node_modules", "Bar", "package.json")).json()).toEqual({
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", ".bin")),
+ ).toEqual(["baz-run"]);
+ expect(
+ await readlink(join(package_dir, "node_modules", ".bin", "baz-run")),
+ ).toBe(join("..", "Bar", "index.js"));
+ expect(await readdirSorted(join(package_dir, "node_modules", "Bar"))).toEqual(
+ ["index.js", "package.json"],
+ );
+ expect(
+ await file(join(package_dir, "node_modules", "Bar", "package.json")).json(),
+ ).toEqual({
name: "baz",
version: "0.0.3",
bin: {
@@ -1511,25 +1726,28 @@ it("should handle GitHub URL in dependencies (github:user/repo#tag)", async () =
expect(
await readdirSorted(join(package_dir, "node_modules", ".bin")),
).toEqual(["uglifyjs"]);
- expect(await readlink(join(package_dir, "node_modules", ".bin", "uglifyjs"))).toBe(join(
- "..",
- "uglify",
- "bin",
- "uglifyjs",
- ));
+ expect(
+ await readlink(join(package_dir, "node_modules", ".bin", "uglifyjs")),
+ ).toBe(join("..", "uglify", "bin", "uglifyjs"));
expect(
await readdirSorted(join(package_dir, "node_modules", ".cache")),
).toEqual(["@GH@mishoo-UglifyJS-e219a9a", "uglify"]);
- expect(await readdirSorted(join(package_dir, "node_modules", ".cache", "uglify"))).toEqual([
- "mishoo-UglifyJS-e219a9a",
- ]);
- expect(await readlink(join(
- package_dir,
- "node_modules",
- ".cache",
- "uglify",
- "mishoo-UglifyJS-e219a9a",
- ))).toBe(join(package_dir, "node_modules", ".cache", "@GH@mishoo-UglifyJS-e219a9a"));
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", ".cache", "uglify")),
+ ).toEqual(["mishoo-UglifyJS-e219a9a"]);
+ expect(
+ await readlink(
+ join(
+ package_dir,
+ "node_modules",
+ ".cache",
+ "uglify",
+ "mishoo-UglifyJS-e219a9a",
+ ),
+ ),
+ ).toBe(
+ join(package_dir, "node_modules", ".cache", "@GH@mishoo-UglifyJS-e219a9a"),
+ );
expect(
await readdirSorted(join(package_dir, "node_modules", "uglify")),
).toEqual([
@@ -1595,8 +1813,12 @@ it("should handle GitHub URL in dependencies (https://github.com/user/repo.git)"
".cache",
"uglify",
]);
- expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toEqual(["uglifyjs"]);
- expect(await readdirSorted(join(package_dir, "node_modules", "uglify"))).toEqual([
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", ".bin")),
+ ).toEqual(["uglifyjs"]);
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", "uglify")),
+ ).toEqual([
".bun-tag",
".gitattributes",
".github",
@@ -1658,8 +1880,12 @@ it("should handle GitHub URL in dependencies (git+https://github.com/user/repo.g
".cache",
"uglify",
]);
- expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toEqual(["uglifyjs"]);
- expect(await readdirSorted(join(package_dir, "node_modules", "uglify"))).toEqual([
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", ".bin")),
+ ).toEqual(["uglifyjs"]);
+ expect(
+ await readdirSorted(join(package_dir, "node_modules", "uglify")),
+ ).toEqual([
".bun-tag",
".gitattributes",
".github",