aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alex Lam S.L <alexlamsl@gmail.com> 2023-01-29 09:54:47 +0200
committerGravatar GitHub <noreply@github.com> 2023-01-28 23:54:47 -0800
commitd9c1a18776a9e692083b590a5d04b30efd9a4c03 (patch)
tree026899cd7e17a67b569fde2ec8c5a42e7a054db4
parentf087388ebc6314c2852d553f4f4ea3074369dfbe (diff)
downloadbun-d9c1a18776a9e692083b590a5d04b30efd9a4c03.tar.gz
bun-d9c1a18776a9e692083b590a5d04b30efd9a4c03.tar.zst
bun-d9c1a18776a9e692083b590a5d04b30efd9a4c03.zip
[bun add] fix more corner cases (#1930)
-rw-r--r--src/install/install.zig86
-rw-r--r--src/string_immutable.zig32
-rw-r--r--test/bun.js/install/bun-add.test.ts167
-rw-r--r--test/bun.js/install/bun-install.test.ts46
4 files changed, 235 insertions, 96 deletions
diff --git a/src/install/install.zig b/src/install/install.zig
index 585243163..d5ba59c4b 100644
--- a/src/install/install.zig
+++ b/src/install/install.zig
@@ -4332,10 +4332,10 @@ pub const PackageManager = struct {
new_dependencies[k].key = JSAst.Expr.init(
JSAst.E.String,
JSAst.E.String{
- .data = if (update.resolved_name.isEmpty())
+ .data = try allocator.dupe(u8, if (update.is_aliased or update.resolved_name.isEmpty())
update.name
else
- try allocator.dupe(u8, update.resolved_name.slice(update.version_buf)),
+ update.resolved_name.slice(update.version_buf)),
},
logger.Loc.Empty,
);
@@ -4422,11 +4422,11 @@ pub const PackageManager = struct {
else
null,
.uninitialized => switch (update.version.tag) {
- .uninitialized => latest,
+ .uninitialized => try allocator.dupe(u8, latest),
else => null,
},
else => null,
- } orelse update.version.literal.slice(update.version_buf);
+ } orelse try allocator.dupe(u8, update.version.literal.slice(update.version_buf));
}
}
};
@@ -5265,6 +5265,7 @@ pub const PackageManager = struct {
version_buf: []const u8 = "",
resolution: Resolution = .{},
resolved_name: String = .{},
+ is_aliased: bool = false,
missing_version: bool = false,
failed: bool = false,
// This must be cloned to handle when the AST store resets
@@ -5284,81 +5285,64 @@ pub const PackageManager = struct {
// remove
outer: for (positionals) |positional| {
var input = std.mem.trim(u8, positional, " \n\r\t");
- var value = input;
switch (op) {
- .link, .unlink => if (!strings.hasPrefixComptime(value, "link:")) {
- value = std.fmt.allocPrint(allocator, "link:{s}", .{value}) catch unreachable;
+ .link, .unlink => if (!strings.hasPrefixComptime(input, "link:")) {
+ input = std.fmt.allocPrint(allocator, "link:{s}", .{input}) catch unreachable;
},
else => {},
}
+ var value = input;
+ var alias: ?string = null;
+ if (strings.isNPMPackageName(input)) {
+ alias = input;
+ value = input[input.len..];
+ } else if (input.len > 1) {
+ if (strings.indexOfChar(input[1..], '@')) |at| {
+ const name = input[0 .. at + 1];
+ if (strings.isNPMPackageName(name)) {
+ alias = name;
+ value = input[at + 2 ..];
+ }
+ }
+ }
+
const placeholder = String.from("@@@");
var version = Dependency.parseWithOptionalTag(
allocator,
- placeholder,
+ if (alias) |name| String.init(input, name) else placeholder,
value,
null,
- &SlicedString.init(value, value),
+ &SlicedString.init(input, value),
log,
- ) orelse switch (op) {
- .link, .unlink => null,
- else => brk: {
- value = std.fmt.allocPrint(allocator, "npm:{s}", .{value}) catch unreachable;
- break :brk Dependency.parseWithOptionalTag(
- allocator,
- placeholder,
- value,
- null,
- &SlicedString.init(value, value),
- log,
- );
- },
- } orelse {
+ ) orelse {
Output.prettyErrorln("<r><red>error<r><d>:<r> unrecognised dependency format: {s}", .{
positional,
});
Global.exit(1);
};
if (switch (version.tag) {
- .dist_tag => version.value.dist_tag.name.eql(placeholder, value, value),
- .npm => version.value.npm.name.eql(placeholder, value, value),
+ .dist_tag => version.value.dist_tag.name.eql(placeholder, input, input),
+ .npm => version.value.npm.name.eql(placeholder, input, input),
else => false,
}) {
- value = std.fmt.allocPrint(allocator, "npm:{s}", .{value}) catch unreachable;
- version = Dependency.parseWithOptionalTag(
- allocator,
- placeholder,
- value,
- null,
- &SlicedString.init(value, value),
- log,
- ) orelse {
- Output.prettyErrorln("<r><red>error<r><d>:<r> unrecognised dependency format: {s}", .{
- positional,
- });
- Global.exit(1);
- };
- }
- switch (version.tag) {
- .dist_tag, .npm => version.literal = brk: {
- if (strings.lastIndexOfChar(value, '@')) |at| {
- if (at >= "npm:@".len) break :brk String.init(value, value[at + 1 ..]);
- }
- break :brk String.from("");
- },
- else => {},
+ Output.prettyErrorln("<r><red>error<r><d>:<r> unrecognised dependency format: {s}", .{
+ positional,
+ });
+ Global.exit(1);
}
var request = UpdateRequest{
- .name = allocator.dupe(u8, switch (version.tag) {
+ .name = allocator.dupe(u8, alias orelse switch (version.tag) {
.dist_tag => version.value.dist_tag.name,
.github => version.value.github.repo,
.npm => version.value.npm.name,
.symlink => version.value.symlink,
else => version.literal,
- }.slice(value)) catch unreachable,
+ }.slice(input)) catch unreachable,
+ .is_aliased = alias != null,
.version = version,
- .version_buf = value,
+ .version_buf = input,
};
request.name_hash = String.Builder.stringHash(request.name);
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index b02cf1249..a69802a1b 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -91,34 +91,28 @@ pub inline fn containsAny(in: anytype, target: string) bool {
/// a folder name. Therefore, the name can't contain any non-URL-safe
/// characters.
pub inline fn isNPMPackageName(target: string) bool {
- if (target.len >= 215) return false;
- switch (target[0]) {
- 'a'...'z',
- '0'...'9',
- '$',
- '@',
- '-',
- => {},
- else => return false,
- }
- if (target.len == 1) return true;
+ if (target.len == 0) return false;
+ if (target.len > 214) return false;
- var slash_count: usize = 0;
-
- for (target[1..]) |c| {
+ const scoped = switch (target[0]) {
+ 'a'...'z', '0'...'9', '$', '-' => false,
+ '@' => true,
+ else => return false,
+ };
+ var slash_index: usize = 0;
+ for (target[1..]) |c, i| {
switch (c) {
'A'...'Z', 'a'...'z', '0'...'9', '$', '-', '_', '.' => {},
'/' => {
- if (slash_count > 0) {
- return false;
- }
- slash_count += 1;
+ if (!scoped) return false;
+ if (slash_index > 0) return false;
+ slash_index = i + 1;
},
else => return false,
}
}
- return true;
+ return !scoped or slash_index > 0 and slash_index + 1 < target.len;
}
pub inline fn indexAny(in: anytype, target: string) ?usize {
diff --git a/test/bun.js/install/bun-add.test.ts b/test/bun.js/install/bun-add.test.ts
index cabc7820c..5094e541d 100644
--- a/test/bun.js/install/bun-add.test.ts
+++ b/test/bun.js/install/bun-add.test.ts
@@ -73,7 +73,7 @@ it("should add existing package", async () => {
]);
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
` + foo@${add_path}`,
"",
"",
@@ -279,7 +279,7 @@ it("should add dependency with specified semver", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"",
" installed baz@0.0.3 with binaries:",
" - baz-run",
@@ -361,7 +361,7 @@ it("should add dependency alongside workspaces", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + bar@workspace:packages/bar",
"",
" installed baz@0.0.3 with binaries:",
@@ -412,3 +412,164 @@ it("should add dependency alongside workspaces", async () => {
});
await access(join(package_dir, "bun.lockb"));
});
+
+it("should add aliased dependency (npm)", async () => {
+ const urls: string[] = [];
+ 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",
+ }),
+ );
+ const { stdout, stderr, exited } = spawn({
+ cmd: [bunExe(), "add", "bar@npm:baz@~0.0.2", "--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",
+ },
+ });
+ expect(await file(join(package_dir, "package.json")).json()).toEqual({
+ name: "foo",
+ version: "0.0.1",
+ dependencies: {
+ bar: "npm:baz@~0.0.2",
+ },
+ });
+ await access(join(package_dir, "bun.lockb"));
+});
+
+it("should add aliased dependency (GitHub)", async () => {
+ const urls: string[] = [];
+ setHandler(dummyRegistry(urls));
+ await writeFile(
+ join(package_dir, "package.json"),
+ JSON.stringify({
+ name: "foo",
+ version: "0.0.1",
+ }),
+ );
+ const { stdout, stderr, exited } = spawn({
+ cmd: [bunExe(), "add", "uglify@mishoo/UglifyJS#v3.14.1", "--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([
+ " + uglify@github:mishoo/UglifyJS#e219a9a",
+ "",
+ "",
+ "",
+ " 1 packages installed",
+ ]);
+ expect(await exited).toBe(0);
+ expect(urls).toEqual([]);
+ expect(requested).toBe(0);
+ expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([
+ ".bin",
+ ".cache",
+ "uglify",
+ ]);
+ expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toEqual([
+ "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", "uglify"))).toEqual([
+ ".bun-tag",
+ ".gitattributes",
+ ".github",
+ ".gitignore",
+ "CONTRIBUTING.md",
+ "LICENSE",
+ "README.md",
+ "bin",
+ "lib",
+ "package.json",
+ "test",
+ "tools",
+ ]);
+ const package_json = await file(join(
+ package_dir,
+ "node_modules",
+ "uglify",
+ "package.json",
+ )).json();
+ expect(package_json.name).toBe("uglify-js");
+ expect(package_json.version).toBe("3.14.1");
+ expect(await file(join(package_dir, "package.json")).json()).toEqual({
+ name: "foo",
+ version: "0.0.1",
+ dependencies: {
+ uglify: "mishoo/UglifyJS#v3.14.1",
+ },
+ });
+ await access(join(package_dir, "bun.lockb"));
+});
diff --git a/test/bun.js/install/bun-install.test.ts b/test/bun.js/install/bun-install.test.ts
index 7f5fe656e..7d127d4ed 100644
--- a/test/bun.js/install/bun-install.test.ts
+++ b/test/bun.js/install/bun-install.test.ts
@@ -155,7 +155,7 @@ it("should handle empty string in dependencies", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + bar@0.0.2",
"",
" 1 packages installed",
@@ -212,7 +212,7 @@ it("should handle workspaces", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + Bar@workspace:bar",
"",
" 1 packages installed",
@@ -273,7 +273,7 @@ it("should handle inter-dependency between workspaces", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + Bar@workspace:bar",
" + Baz@workspace:packages/baz",
"",
@@ -339,7 +339,7 @@ it("should handle inter-dependency between workspaces (devDependencies)", async
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + Bar@workspace:bar",
" + Baz@workspace:packages/baz",
"",
@@ -405,7 +405,7 @@ it("should handle inter-dependency between workspaces (optionalDependencies)", a
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + Bar@workspace:bar",
" + Baz@workspace:packages/baz",
"",
@@ -463,7 +463,7 @@ it("should ignore peerDependencies within workspaces", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + Baz@workspace:packages/baz",
"",
" 1 packages installed",
@@ -524,7 +524,7 @@ it("should handle life-cycle scripts within workspaces", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"[scripts:run] Bar",
" + Bar@workspace:bar",
"[scripts:run] Foo",
@@ -569,7 +569,7 @@ it("should handle ^0 in dependencies", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + bar@0.0.2",
"",
" 1 packages installed",
@@ -661,7 +661,7 @@ it("should handle ^0.0 in dependencies", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + bar@0.0.2",
"",
" 1 packages installed",
@@ -792,7 +792,7 @@ it("should handle ^0.0.2 in dependencies", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + bar@0.0.2",
"",
" 1 packages installed",
@@ -845,7 +845,7 @@ it("should handle ^0.0.2-rc in dependencies", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + bar@0.0.2-rc",
"",
" 1 packages installed",
@@ -898,7 +898,7 @@ it("should handle ^0.0.2-alpha.3+b4d in dependencies", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + bar@0.0.2-alpha.3",
"",
" 1 packages installed",
@@ -955,7 +955,7 @@ it("should handle dependency aliasing", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + Bar@0.0.3",
"",
" 1 packages installed",
@@ -1019,7 +1019,7 @@ it("should handle dependency aliasing (versioned)", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + Bar@0.0.3",
"",
" 1 packages installed",
@@ -1083,7 +1083,7 @@ it("should handle dependency aliasing (dist-tagged)", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + Bar@0.0.3",
"",
" 1 packages installed",
@@ -1151,7 +1151,7 @@ it("should not reinstall aliased dependencies", async () => {
expect(err1).toContain("Saved lockfile");
expect(stdout1).toBeDefined();
const out1 = await new Response(stdout1).text();
- expect(out1.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out1.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + Bar@0.0.3",
"",
" 1 packages installed",
@@ -1202,7 +1202,7 @@ it("should not reinstall aliased dependencies", async () => {
expect(err2).not.toContain("Saved lockfile");
expect(stdout2).toBeDefined();
const out2 = await new Response(stdout2).text();
- expect(out2.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out2.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"",
"Checked 1 installs across 2 packages (no changes)",
]);
@@ -1258,7 +1258,7 @@ it("should handle GitHub URL in dependencies (user/repo)", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
let out = await new Response(stdout).text();
- out = out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "");
+ out = out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "");
out = out.replace(/(github:[^#]+)#[a-f0-9]+/, "$1");
expect(out.split(/\r?\n/)).toEqual([
" + uglify@github:mishoo/UglifyJS",
@@ -1325,7 +1325,7 @@ it("should handle GitHub URL in dependencies (user/repo#commit-id)", async () =>
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + uglify@github:mishoo/UglifyJS#e219a9a",
"",
" 1 packages installed",
@@ -1410,7 +1410,7 @@ it("should handle GitHub URL in dependencies (user/repo#tag)", async () => {
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + uglify@github:mishoo/UglifyJS#e219a9a",
"",
" 1 packages installed",
@@ -1495,7 +1495,7 @@ it("should handle GitHub URL in dependencies (github:user/repo#tag)", async () =
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
- expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
" + uglify@github:mishoo/UglifyJS#e219a9a",
"",
" 1 packages installed",
@@ -1580,7 +1580,7 @@ it("should handle GitHub URL in dependencies (https://github.com/user/repo.git)"
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
let out = await new Response(stdout).text();
- out = out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "");
+ out = out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "");
out = out.replace(/(github:[^#]+)#[a-f0-9]+/, "$1");
expect(out.split(/\r?\n/)).toEqual([
" + uglify@github:mishoo/UglifyJS",
@@ -1643,7 +1643,7 @@ it("should handle GitHub URL in dependencies (git+https://github.com/user/repo.g
expect(err).toContain("Saved lockfile");
expect(stdout).toBeDefined();
let out = await new Response(stdout).text();
- out = out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "");
+ out = out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "");
out = out.replace(/(github:[^#]+)#[a-f0-9]+/, "$1");
expect(out.split(/\r?\n/)).toEqual([
" + uglify@github:mishoo/UglifyJS",