From e146734596ab4a397169be9ad19870b0796d2964 Mon Sep 17 00:00:00 2001 From: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:45:26 -0800 Subject: [PATCH 1/6] `bun.lock` fixes (#15724) --- src/analytics/analytics_thread.zig | 1 + src/install/bun.lock.zig | 153 ++++++++++++++++++++++------- src/install/lockfile.zig | 6 +- 3 files changed, 124 insertions(+), 36 deletions(-) diff --git a/src/analytics/analytics_thread.zig b/src/analytics/analytics_thread.zig index 3c2c9bbe4d65e5..d8c77de1eda461 100644 --- a/src/analytics/analytics_thread.zig +++ b/src/analytics/analytics_thread.zig @@ -104,6 +104,7 @@ pub const Features = struct { pub var lifecycle_scripts: usize = 0; pub var loaders: usize = 0; pub var lockfile_migration_from_package_lock: usize = 0; + pub var text_lockfile: usize = 0; pub var macros: usize = 0; pub var no_avx2: usize = 0; pub var no_avx: usize = 0; diff --git a/src/install/bun.lock.zig b/src/install/bun.lock.zig index 9e09dcc99a8717..0084a35191114d 100644 --- a/src/install/bun.lock.zig +++ b/src/install/bun.lock.zig @@ -436,6 +436,9 @@ pub const Stringifier = struct { defer found_overrides.deinit(allocator); try found_overrides.ensureTotalCapacity(allocator, @truncate(lockfile.overrides.map.count())); + var optional_peers_buf = std.ArrayList(String).init(allocator); + defer optional_peers_buf.deinit(); + var _indent: u32 = 0; const indent = &_indent; try writer.writeAll("{\n"); @@ -458,6 +461,7 @@ pub const Stringifier = struct { buf, deps_buf, lockfile.workspace_versions, + &optional_peers_buf, ); for (0..pkgs.len) |pkg_id| { const res = pkg_resolution[pkg_id]; @@ -475,6 +479,7 @@ pub const Stringifier = struct { buf, deps_buf, lockfile.workspace_versions, + &optional_peers_buf, ); } } @@ -608,11 +613,38 @@ pub const Stringifier = struct { ); } + const DepSortCtx = struct { + string_buf: string, + deps_buf: []const Dependency, + + pub fn isLessThan(this: @This(), lhs: DependencyID, rhs: DependencyID) bool { + const l = this.deps_buf[lhs]; + const r = this.deps_buf[rhs]; + return strings.cmpStringsAsc({}, l.name.slice(this.string_buf), r.name.slice(this.string_buf)); + } + }; + + var deps_sort_buf: std.ArrayListUnmanaged(DependencyID) = .{}; + defer deps_sort_buf.deinit(allocator); + + var pkg_deps_sort_buf: std.ArrayListUnmanaged(DependencyID) = .{}; + defer pkg_deps_sort_buf.deinit(allocator); + try writeIndent(writer, indent); try writer.writeAll("\"packages\": {"); var first = true; while (pkgs_iter.next({})) |node| { - for (node.dependencies) |dep_id| { + deps_sort_buf.clearRetainingCapacity(); + try deps_sort_buf.appendSlice(allocator, node.dependencies); + + std.sort.pdq( + DependencyID, + deps_sort_buf.items, + DepSortCtx{ .string_buf = buf, .deps_buf = deps_buf }, + DepSortCtx.isLessThan, + ); + + for (deps_sort_buf.items) |dep_id| { const pkg_id = resolution_buf[dep_id]; if (pkg_id == invalid_package_id) continue; @@ -651,7 +683,20 @@ pub const Stringifier = struct { const pkg_name = pkg_names[pkg_id].slice(buf); const pkg_meta = pkg_metas[pkg_id]; - const pkg_deps = pkg_dep_lists[pkg_id].get(deps_buf); + const pkg_deps_list = pkg_dep_lists[pkg_id]; + + pkg_deps_sort_buf.clearRetainingCapacity(); + try pkg_deps_sort_buf.ensureUnusedCapacity(allocator, pkg_deps_list.len); + for (pkg_deps_list.begin()..pkg_deps_list.end()) |pkg_dep_id| { + pkg_deps_sort_buf.appendAssumeCapacity(@intCast(pkg_dep_id)); + } + + std.sort.pdq( + DependencyID, + pkg_deps_sort_buf.items, + DepSortCtx{ .string_buf = buf, .deps_buf = deps_buf }, + DepSortCtx.isLessThan, + ); // first index is resolution for all dependency types // npm -> [ "name@version", registry or "" (default), deps..., integrity, ... ] @@ -663,9 +708,6 @@ pub const Stringifier = struct { // git -> [ "name@git+repo", deps..., ... ] // github -> [ "name@github:user/repo", deps..., ... ] - var optional_peers_buf = std.ArrayList(String).init(allocator); - defer optional_peers_buf.deinit(); - switch (res.tag) { .root => { try writer.print("[\"{}@root:\"]", .{ @@ -679,9 +721,9 @@ pub const Stringifier = struct { bun.fmt.formatJSONStringUTF8(res.value.folder.slice(buf), .{ .quote = false }), }); - try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf); + try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); - try writer.print(", \"{}\"]", .{pkg_meta.integrity}); + try writer.writeByte(']'); }, .local_tarball => { try writer.print("[\"{s}@{}\", ", .{ @@ -689,9 +731,9 @@ pub const Stringifier = struct { bun.fmt.formatJSONStringUTF8(res.value.local_tarball.slice(buf), .{ .quote = false }), }); - try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf); + try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); - try writer.print(", \"{}\"]", .{pkg_meta.integrity}); + try writer.writeByte(']'); }, .remote_tarball => { try writer.print("[\"{s}@{}\", ", .{ @@ -699,9 +741,9 @@ pub const Stringifier = struct { bun.fmt.formatJSONStringUTF8(res.value.remote_tarball.slice(buf), .{ .quote = false }), }); - try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf); + try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); - try writer.print(", \"{}\"]", .{pkg_meta.integrity}); + try writer.writeByte(']'); }, .symlink => { try writer.print("[\"{s}@link:{}\", ", .{ @@ -709,9 +751,9 @@ pub const Stringifier = struct { bun.fmt.formatJSONStringUTF8(res.value.symlink.slice(buf), .{ .quote = false }), }); - try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf); + try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); - try writer.print(", \"{}\"]", .{pkg_meta.integrity}); + try writer.writeByte(']'); }, .npm => { try writer.print("[\"{s}@{}\", ", .{ @@ -727,9 +769,8 @@ pub const Stringifier = struct { res.value.npm.url.slice(buf), }); - try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf); + try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); - // TODO(dylan-conway): delete placeholder try writer.print(", \"{}\"]", .{ pkg_meta.integrity, }); @@ -742,7 +783,7 @@ pub const Stringifier = struct { bun.fmt.formatJSONStringUTF8(workspace_path, .{ .quote = false }), }); - try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf); + try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); try writer.writeByte(']'); }, @@ -753,9 +794,9 @@ pub const Stringifier = struct { repo.fmt(if (comptime tag == .git) "git+" else "github:", buf), }); - try writePackageDepsAndMeta(writer, pkg_deps, &pkg_meta, buf, &optional_peers_buf); + try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); - try writer.print(", \"{}\"]", .{pkg_meta.integrity}); + try writer.writeByte(']'); }, else => unreachable, } @@ -763,7 +804,7 @@ pub const Stringifier = struct { } if (!first) { - try writer.writeByte('\n'); + try writer.writeAll(",\n"); try decIndent(writer, indent); } try writer.writeAll("}\n"); @@ -779,7 +820,9 @@ pub const Stringifier = struct { /// { "devDependencies": { "one": "1.1.1", "two": "2.2.2" }, "os": "none" } fn writePackageDepsAndMeta( writer: anytype, - deps: []const Dependency, + _: DependencyID, + deps_buf: []const Dependency, + pkg_dep_ids: []const DependencyID, meta: *const Meta, buf: string, optional_peers_buf: *std.ArrayList(String), @@ -793,7 +836,8 @@ pub const Stringifier = struct { const group_name, const group_behavior = group; var first = true; - for (deps) |dep| { + for (pkg_dep_ids) |dep_id| { + const dep = deps_buf[dep_id]; if (!dep.behavior.includes(group_behavior)) continue; if (dep.behavior.isOptionalPeer()) { @@ -828,16 +872,16 @@ pub const Stringifier = struct { if (optional_peers_buf.items.len > 0) { bun.debugAssert(any); try writer.writeAll( - \\, "optionalPeers": [ + \\, "optionalPeerDependencies": [ ); for (optional_peers_buf.items, 0..) |optional_peer, i| { try writer.print( - \\"{s}{s}{s}", + \\{s}"{s}"{s} , .{ - if (i != 0) "," else "", + if (i != 0) " " else "", optional_peer.slice(buf), - if (i != optional_peers_buf.items.len) " " else "", + if (i != optional_peers_buf.items.len - 1) "," else "", }); } @@ -895,7 +939,9 @@ pub const Stringifier = struct { buf: string, deps_buf: []const Dependency, workspace_versions: BinaryLockfile.VersionHashMap, + optional_peers_buf: *std.ArrayList(String), ) OOM!void { + defer optional_peers_buf.clearRetainingCapacity(); // any - have any properties been written var any = false; @@ -931,6 +977,12 @@ pub const Stringifier = struct { for (pkg_deps[pkg_id].get(deps_buf)) |dep| { if (!dep.behavior.includes(group_behavior)) continue; + if (dep.behavior.isOptionalPeer()) { + if (group_behavior.isOptional()) continue; + + try optional_peers_buf.append(dep.name); + } + if (first) { if (any) { try writer.writeByte(','); @@ -957,16 +1009,39 @@ pub const Stringifier = struct { } if (!first) { - try writer.writeByte('\n'); + try writer.writeAll(",\n"); try decIndent(writer, indent); try writer.writeAll("}"); } } + if (optional_peers_buf.items.len > 0) { + bun.debugAssert(any); + try writer.writeAll( + \\, + \\ + ); + try writeIndent(writer, indent); + try writer.writeAll( + \\"optionalPeerDependencies": [ + \\ + ); + indent.* += 1; + for (optional_peers_buf.items) |optional_peer| { + try writeIndent(writer, indent); + try writer.print( + \\"{s}", + \\ + , .{optional_peer.slice(buf)}); + } + try decIndent(writer, indent); + try writer.writeByte(']'); + } + if (any) { - try writer.writeByte('\n'); + try writer.writeAll(",\n"); try decIndent(writer, indent); } - try writer.writeAll("}"); + try writer.writeAll("},"); } fn writeIndent(writer: anytype, indent: *const u32) OOM!void { @@ -999,8 +1074,8 @@ const dependency_groups = [3]struct { []const u8, Dependency.Behavior }{ const workspace_dependency_groups = [4]struct { []const u8, Dependency.Behavior }{ .{ "dependencies", Dependency.Behavior.normal }, .{ "devDependencies", Dependency.Behavior.dev }, - .{ "peerDependencies", Dependency.Behavior.peer }, .{ "optionalDependencies", Dependency.Behavior.optional }, + .{ "peerDependencies", Dependency.Behavior.peer }, }; const ParseError = OOM || error{ @@ -1369,7 +1444,7 @@ pub fn parseIntoBinaryLockfile( // integrity switch (res.tag) { - .npm, .git, .github => { + .npm => { const integrity_expr = pkg_info.at(i); i += 1; const integrity_str = integrity_expr.asString(allocator) orelse { @@ -1424,7 +1499,7 @@ pub fn parseIntoBinaryLockfile( _ = pkg_resolution_lists; { - // root pkg + // first the root dependencies are resolved pkg_resolutions[0] = Resolution.init(.{ .root = {} }); pkg_metas[0].origin = .local; @@ -1437,8 +1512,12 @@ pub fn parseIntoBinaryLockfile( lockfile.buffers.resolutions.items[dep_id] = dep_node.pkg_id; } } + + // TODO(dylan-conway) should we handle workspaces separately here for custom hoisting + } + // then each package dependency for (pkgs_expr.data.e_object.properties.slice()) |prop| { const key = prop.key.?; const value = prop.value.?; @@ -1463,9 +1542,6 @@ pub fn parseIntoBinaryLockfile( var curr: ?*PkgPath.Map.Node = pkg_map_entry; while (curr) |node| { if (node.nodes.getPtr(dep.name.slice(string_buf.bytes.items))) |dep_node| { - - // it doesn't matter which dependency is assigned to this node. the dependency - // id will only be used for getting the dependency name dep_node.dep_id = dep_id; lockfile.buffers.resolutions.items[dep_id] = dep_node.pkg_id; @@ -1519,7 +1595,7 @@ fn parseAppendDependencies( ) ParseError!struct { u32, u32 } { defer optional_peers_buf.clearRetainingCapacity(); - if (obj.get("optionalPeers")) |optional_peers| { + if (obj.get("optionalPeerDependencies")) |optional_peers| { if (!optional_peers.isArray()) { try log.addError(source, optional_peers.loc, "Expected an array"); return error.InvalidPackageInfo; @@ -1592,5 +1668,12 @@ fn parseAppendDependencies( } const end = lockfile.buffers.dependencies.items.len; + std.sort.pdq( + Dependency, + lockfile.buffers.dependencies.items[off..], + buf.bytes.items, + Dependency.isLessThan, + ); + return .{ @intCast(off), @intCast(end - off) }; } diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index eeb196159f6660..a1f7e748fc1aa3 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -348,6 +348,8 @@ pub fn loadFromDir( } }; + bun.Analytics.Features.text_lockfile += 1; + return .{ .ok = .{ .lockfile = this, @@ -379,6 +381,8 @@ pub fn loadFromDir( TextLockfile.parseIntoBinaryLockfile(this, allocator, json, &source, log, manager) catch |err| { Output.panic("failed to parse text lockfile converted from binary lockfile: {s}", .{@errorName(err)}); }; + + bun.Analytics.Features.text_lockfile += 1; } }, else => {}, @@ -425,7 +429,7 @@ pub const Tree = struct { id: Id = invalid_id, // Should not be used for anything other than name - // through `folderName()`. There is not guarentee a dependency + // through `folderName()`. There is not guarantee a dependency // id chosen for a tree node is the same behavior or has the // same version literal for packages hoisted. dependency_id: DependencyID = invalid_dependency_id, From e72692801a5769ce4379af759d59ec5bea3df80c Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Thu, 12 Dec 2024 17:48:53 -0800 Subject: [PATCH 2/6] [ci] Reduce number of environment variables we send (#15730) --- test/harness.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/harness.ts b/test/harness.ts index eb55b7b6820454..27a2bd0911eccf 100644 --- a/test/harness.ts +++ b/test/harness.ts @@ -43,17 +43,26 @@ export const bunEnv: NodeJS.ProcessEnv = { BUN_FEATURE_FLAG_EXPERIMENTAL_BAKE: "1", }; +const ciEnv = { ...bunEnv }; + if (isWindows) { bunEnv.SHELLOPTS = "igncr"; // Ignore carriage return } for (let key in bunEnv) { if (bunEnv[key] === undefined) { + delete ciEnv[key]; delete bunEnv[key]; } if (key.startsWith("BUN_DEBUG_") && key !== "BUN_DEBUG_QUIET_LOGS") { + delete ciEnv[key]; + delete bunEnv[key]; + } + + if (key.startsWith("BUILDKITE")) { delete bunEnv[key]; + delete process.env[key]; } } @@ -1322,6 +1331,7 @@ export function getSecret(name: string): string | undefined { const { exitCode, stdout } = spawnSync({ cmd: ["buildkite-agent", "secret", "get", name], stdout: "pipe", + env: ciEnv, stderr: "inherit", }); if (exitCode === 0) { From 8b3b1442fd262b0ebac3716e377fdaba9050e520 Mon Sep 17 00:00:00 2001 From: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Date: Thu, 12 Dec 2024 19:33:44 -0800 Subject: [PATCH 3/6] `bun.lock` workspace sorting and comma bugfix (#15739) --- src/install/bun.lock.zig | 42 ++++++++-- .../bun-install-registry.test.ts.snap | 69 ++++++++++++++++ .../registry/bun-install-registry.test.ts | 81 ++++++++++++++++++- 3 files changed, 184 insertions(+), 8 deletions(-) diff --git a/src/install/bun.lock.zig b/src/install/bun.lock.zig index 0084a35191114d..c399aecc759db3 100644 --- a/src/install/bun.lock.zig +++ b/src/install/bun.lock.zig @@ -413,7 +413,7 @@ pub const Stringifier = struct { const resolution_buf = lockfile.buffers.resolutions.items; const pkgs = lockfile.packages.slice(); const pkg_dep_lists: []DependencySlice = pkgs.items(.dependencies); - const pkg_resolution: []Resolution = pkgs.items(.resolution); + const pkg_resolutions: []Resolution = pkgs.items(.resolution); const pkg_names: []String = pkgs.items(.name); const pkg_name_hashes: []PackageNameHash = pkgs.items(.name_hash); const pkg_metas: []BinaryLockfile.Package.Meta = pkgs.items(.meta); @@ -463,15 +463,43 @@ pub const Stringifier = struct { lockfile.workspace_versions, &optional_peers_buf, ); - for (0..pkgs.len) |pkg_id| { - const res = pkg_resolution[pkg_id]; + + var workspace_sort_buf: std.ArrayListUnmanaged(PackageID) = .{}; + defer workspace_sort_buf.deinit(allocator); + + for (0..pkgs.len) |_pkg_id| { + const pkg_id: PackageID = @intCast(_pkg_id); + const res = pkg_resolutions[pkg_id]; if (res.tag != .workspace) continue; - try writer.writeAll(",\n"); + try workspace_sort_buf.append(allocator, pkg_id); + } + + const Sorter = struct { + string_buf: string, + res_buf: []const Resolution, + + pub fn isLessThan(this: @This(), l: PackageID, r: PackageID) bool { + const l_res = this.res_buf[l]; + const r_res = this.res_buf[r]; + return l_res.value.workspace.order(&r_res.value.workspace, this.string_buf, this.string_buf) == .lt; + } + }; + + std.sort.pdq( + PackageID, + workspace_sort_buf.items, + Sorter{ .string_buf = buf, .res_buf = pkg_resolutions }, + Sorter.isLessThan, + ); + + for (workspace_sort_buf.items) |workspace_pkg_id| { + const res = pkg_resolutions[workspace_pkg_id]; + try writer.writeAll("\n"); try writeIndent(writer, indent); try writeWorkspaceDeps( writer, indent, - @intCast(pkg_id), + @intCast(workspace_pkg_id), res.value.workspace, pkg_names, pkg_name_hashes, @@ -497,7 +525,7 @@ pub const Stringifier = struct { const pkg_name = pkg_names[pkg_id]; const pkg_name_hash = pkg_name_hashes[pkg_id]; - const res = pkg_resolution[pkg_id]; + const res = pkg_resolutions[pkg_id]; const dep = deps_buf[dep_id]; if (lockfile.patched_dependencies.count() > 0) { @@ -648,7 +676,7 @@ pub const Stringifier = struct { const pkg_id = resolution_buf[dep_id]; if (pkg_id == invalid_package_id) continue; - const res = pkg_resolution[pkg_id]; + const res = pkg_resolutions[pkg_id]; switch (res.tag) { .root, .npm, .folder, .local_tarball, .github, .git, .symlink, .workspace, .remote_tarball => {}, .uninitialized => continue, diff --git a/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap b/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap index f21ad002a6c578..3793083aef7382 100644 --- a/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap +++ b/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap @@ -134,3 +134,72 @@ exports[`auto-install symlinks (and junctions) are created correctly in the inst } " `; + +exports[`text lockfile workspace sorting 1`] = ` +"{ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "no-deps": "1.0.0", + }, + }, + "packages/b": { + "name": "b", + "dependencies": { + "no-deps": "1.0.0", + }, + }, + "packages/c": { + "name": "c", + "dependencies": { + "no-deps": "1.0.0", + }, + }, + }, + "packages": { + "b": ["b@workspace:packages/b", { "dependencies": { "no-deps": "1.0.0" } }], + "c": ["c@workspace:packages/c", { "dependencies": { "no-deps": "1.0.0" } }], + "no-deps": ["no-deps@1.0.0", "http://localhost:1234/no-deps/-/no-deps-1.0.0.tgz", {}, "sha512-v4w12JRjUGvfHDUP8vFDwu0gUWu04j0cv9hLb1Abf9VdaXu4XcrddYFTMVBVvmldKViGWH7jrb6xPJRF0wq6gw=="], + } +} +" +`; + +exports[`text lockfile workspace sorting 2`] = ` +"{ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "no-deps": "1.0.0", + }, + }, + "packages/a": { + "name": "a", + "dependencies": { + "no-deps": "1.0.0", + }, + }, + "packages/b": { + "name": "b", + "dependencies": { + "no-deps": "1.0.0", + }, + }, + "packages/c": { + "name": "c", + "dependencies": { + "no-deps": "1.0.0", + }, + }, + }, + "packages": { + "a": ["a@workspace:packages/a", { "dependencies": { "no-deps": "1.0.0" } }], + "b": ["b@workspace:packages/b", { "dependencies": { "no-deps": "1.0.0" } }], + "c": ["c@workspace:packages/c", { "dependencies": { "no-deps": "1.0.0" } }], + "no-deps": ["no-deps@1.0.0", "http://localhost:1234/no-deps/-/no-deps-1.0.0.tgz", {}, "sha512-v4w12JRjUGvfHDUP8vFDwu0gUWu04j0cv9hLb1Abf9VdaXu4XcrddYFTMVBVvmldKViGWH7jrb6xPJRF0wq6gw=="], + } +} +" +`; diff --git a/test/cli/install/registry/bun-install-registry.test.ts b/test/cli/install/registry/bun-install-registry.test.ts index 12b8a5ff1985fe..03677d7d264e1b 100644 --- a/test/cli/install/registry/bun-install-registry.test.ts +++ b/test/cli/install/registry/bun-install-registry.test.ts @@ -891,7 +891,7 @@ describe("whoami", async () => { expect(err).not.toContain("error:"); expect(await exited).toBe(0); }); - test("two .npmrc", async () => { + test("two .npmrc", async () => { const token = await generateRegistryUser("whoami-two-npmrc", "whoami-two-npmrc"); const packageNpmrc = `registry=http://localhost:${port}/`; const homeNpmrc = `//localhost:${port}/:_authToken=${token}`; @@ -1700,6 +1700,85 @@ describe("package.json indentation", async () => { }); }); +describe("text lockfile", () => { + test("workspace sorting", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + workspaces: ["packages/*"], + dependencies: { + "no-deps": "1.0.0", + }, + }), + ), + write( + join(packageDir, "packages", "b", "package.json"), + JSON.stringify({ + name: "b", + dependencies: { + "no-deps": "1.0.0", + }, + }), + ), + write( + join(packageDir, "packages", "c", "package.json"), + JSON.stringify({ + name: "c", + dependencies: { + "no-deps": "1.0.0", + }, + }), + ), + ]); + + let { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install", "--save-text-lockfile"], + cwd: packageDir, + stdout: "pipe", + stderr: "pipe", + env, + }); + + let out = await Bun.readableStreamToText(stdout); + let err = await Bun.readableStreamToText(stderr); + console.log({ out, err }); + expect(await exited).toBe(0); + + expect( + (await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll(/localhost:\d+/g, "localhost:1234"), + ).toMatchSnapshot(); + + // now add workspace 'a' + await write( + join(packageDir, "packages", "a", "package.json"), + JSON.stringify({ + name: "a", + dependencies: { + "no-deps": "1.0.0", + }, + }), + ); + + ({ stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "pipe", + stderr: "pipe", + env, + })); + + out = await Bun.readableStreamToText(stdout); + err = await Bun.readableStreamToText(stderr); + console.log({ out, err }); + expect(await exited).toBe(0); + + const lockfile = await Bun.file(join(packageDir, "bun.lock")).text(); + expect(lockfile.replaceAll(/localhost:\d+/g, "localhost:1234")).toMatchSnapshot(); + }); +}); + describe("optionalDependencies", () => { for (const optional of [true, false]) { test(`exit code is ${optional ? 0 : 1} when ${optional ? "optional" : ""} dependency tarball is missing`, async () => { From f64ca29c0eb3f35084537ab4873cb7fc578552e8 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Thu, 12 Dec 2024 20:53:02 -0800 Subject: [PATCH 4/6] Fix symbols test. Bump Webkit. (#15741) --- cmake/targets/BuildBun.cmake | 10 ++++++- cmake/tools/SetupWebKit.cmake | 2 +- .../webcrypto/CryptoAlgorithmHMACOpenSSL.cpp | 4 +-- .../bindings/workaround-missing-symbols.cpp | 30 +++++++++++-------- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/cmake/targets/BuildBun.cmake b/cmake/targets/BuildBun.cmake index a846750ffad0cb..84a99eaa58fc4f 100644 --- a/cmake/targets/BuildBun.cmake +++ b/cmake/targets/BuildBun.cmake @@ -891,13 +891,21 @@ if(LINUX) target_link_options(${bun} PUBLIC -Wl,--wrap=exp -Wl,--wrap=expf + -Wl,--wrap=fcntl64 -Wl,--wrap=log -Wl,--wrap=log2 -Wl,--wrap=log2f -Wl,--wrap=logf -Wl,--wrap=pow -Wl,--wrap=powf - -Wl,--wrap=fcntl64 + ) + else() + target_link_options(${bun} PUBLIC + -Wl,--wrap=exp + -Wl,--wrap=expf + -Wl,--wrap=log2f + -Wl,--wrap=logf + -Wl,--wrap=powf ) endif() endif() diff --git a/cmake/tools/SetupWebKit.cmake b/cmake/tools/SetupWebKit.cmake index 50c1e2c7b93224..47ad1b9e34f971 100644 --- a/cmake/tools/SetupWebKit.cmake +++ b/cmake/tools/SetupWebKit.cmake @@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use") option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading") if(NOT WEBKIT_VERSION) - set(WEBKIT_VERSION e17d16e0060b3d80ae40e78353d19575c9a8f3af) + set(WEBKIT_VERSION 58549ddc4d9e7164823fe9d4e86c46c003e46a19) endif() if(WEBKIT_LOCAL) diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMACOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMACOpenSSL.cpp index 78cbc6f8e86740..c7f5ba08cb761c 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMACOpenSSL.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMACOpenSSL.cpp @@ -95,7 +95,7 @@ ExceptionOr CryptoAlgorithmHMAC::platformVerifyWithAlgorithm(const CryptoK if (!expectedSignature) return Exception { OperationError }; // Using a constant time comparison to prevent timing attacks. - return signature.size() == expectedSignature->size() && !constantTimeMemcmp(expectedSignature->data(), signature.data(), expectedSignature->size()); + return signature.size() == expectedSignature->size() && !constantTimeMemcmp(expectedSignature->span(), signature.span()); } ExceptionOr CryptoAlgorithmHMAC::platformVerify(const CryptoKeyHMAC& key, const Vector& signature, const Vector& data) @@ -108,7 +108,7 @@ ExceptionOr CryptoAlgorithmHMAC::platformVerify(const CryptoKeyHMAC& key, if (!expectedSignature) return Exception { OperationError }; // Using a constant time comparison to prevent timing attacks. - return signature.size() == expectedSignature->size() && !constantTimeMemcmp(expectedSignature->data(), signature.data(), expectedSignature->size()); + return signature.size() == expectedSignature->size() && !constantTimeMemcmp(expectedSignature->span(), signature.span()); } } // namespace WebCore diff --git a/src/bun.js/bindings/workaround-missing-symbols.cpp b/src/bun.js/bindings/workaround-missing-symbols.cpp index 3cfb58408616d1..2ca4eaf3514a20 100644 --- a/src/bun.js/bindings/workaround-missing-symbols.cpp +++ b/src/bun.js/bindings/workaround-missing-symbols.cpp @@ -81,16 +81,20 @@ extern "C" int kill(int pid, int sig) #endif #if defined(__x86_64__) +__asm__(".symver exp,exp@GLIBC_2.2.5"); __asm__(".symver expf,expf@GLIBC_2.2.5"); +__asm__(".symver log2f,log2f@GLIBC_2.2.5"); +__asm__(".symver logf,logf@GLIBC_2.2.5"); +__asm__(".symver powf,powf@GLIBC_2.2.5"); #elif defined(__aarch64__) __asm__(".symver expf,expf@GLIBC_2.17"); -__asm__(".symver powf,powf@GLIBC_2.17"); -__asm__(".symver pow,pow@GLIBC_2.17"); -__asm__(".symver log,log@GLIBC_2.17"); __asm__(".symver exp,exp@GLIBC_2.17"); -__asm__(".symver logf,logf@GLIBC_2.17"); -__asm__(".symver log2f,log2f@GLIBC_2.17"); +__asm__(".symver log,log@GLIBC_2.17"); __asm__(".symver log2,log2@GLIBC_2.17"); +__asm__(".symver log2f,log2f@GLIBC_2.17"); +__asm__(".symver logf,logf@GLIBC_2.17"); +__asm__(".symver pow,pow@GLIBC_2.17"); +__asm__(".symver powf,powf@GLIBC_2.17"); #endif #if defined(__x86_64__) || defined(__aarch64__) @@ -101,16 +105,16 @@ __asm__(".symver log2,log2@GLIBC_2.17"); extern "C" { +double BUN_WRAP_GLIBC_SYMBOL(exp)(double); float BUN_WRAP_GLIBC_SYMBOL(expf)(float); +float BUN_WRAP_GLIBC_SYMBOL(log2f)(float); +float BUN_WRAP_GLIBC_SYMBOL(logf)(float); +float BUN_WRAP_GLIBC_SYMBOL(powf)(float, float); #if defined(__aarch64__) -float BUN_WRAP_GLIBC_SYMBOL(powf)(float, float); double BUN_WRAP_GLIBC_SYMBOL(pow)(double, double); double BUN_WRAP_GLIBC_SYMBOL(log)(double); -double BUN_WRAP_GLIBC_SYMBOL(exp)(double); -float BUN_WRAP_GLIBC_SYMBOL(logf)(float); -float BUN_WRAP_GLIBC_SYMBOL(log2f)(float); double BUN_WRAP_GLIBC_SYMBOL(log2)(double); int BUN_WRAP_GLIBC_SYMBOL(fcntl64)(int, int, ...); @@ -119,15 +123,15 @@ int BUN_WRAP_GLIBC_SYMBOL(fcntl64)(int, int, ...); #if defined(__x86_64__) || defined(__aarch64__) float __wrap_expf(float x) { return expf(x); } +float __wrap_powf(float x, float y) { return powf(x, y); } +float __wrap_logf(float x) { return logf(x); } +float __wrap_log2f(float x) { return log2f(x); } +double __wrap_exp(double x) { return exp(x); } #if defined(__aarch64__) -float __wrap_powf(float x, float y) { return powf(x, y); } double __wrap_pow(double x, double y) { return pow(x, y); } double __wrap_log(double x) { return log(x); } -double __wrap_exp(double x) { return exp(x); } -float __wrap_logf(float x) { return logf(x); } -float __wrap_log2f(float x) { return log2f(x); } double __wrap_log2(double x) { return log2(x); } #endif From bbb56acdf7535edd36fc1ddf819505a821b1b984 Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Thu, 12 Dec 2024 21:39:30 -0800 Subject: [PATCH 5/6] test(ws): do not create temporary `.sock` files in root repo directory (#15670) Co-authored-by: Don Isaac --- test/cli/inspect/inspect.test.ts | 35 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/test/cli/inspect/inspect.test.ts b/test/cli/inspect/inspect.test.ts index ce16df2320b0c3..5709101ebc70f9 100644 --- a/test/cli/inspect/inspect.test.ts +++ b/test/cli/inspect/inspect.test.ts @@ -1,8 +1,9 @@ import { Subprocess, spawn } from "bun"; -import { afterEach, expect, test, describe } from "bun:test"; -import { bunEnv, bunExe, isPosix, randomPort, tempDirWithFiles } from "harness"; +import { afterEach, expect, test, describe, beforeAll, afterAll } from "bun:test"; +import { bunEnv, bunExe, isPosix, randomPort, tempDirWithFiles, tmpdirSync } from "harness"; import { WebSocket } from "ws"; import { join } from "node:path"; +import fs from "fs"; let inspectee: Subprocess; import { SocketFramer } from "./socket-framer"; import { JUnitReporter, InspectorSession, connect } from "./junit-reporter"; @@ -10,6 +11,12 @@ import stripAnsi from "strip-ansi"; const anyPort = expect.stringMatching(/^\d+$/); const anyPathname = expect.stringMatching(/^\/[a-z0-9]+$/); +/** + * Get a function that creates a random `.sock` file in the specified temporary directory. + */ +const randomSocketPathFn = (tempdir: string) => (): string => + join(tempdir, Math.random().toString(36).substring(2, 15) + ".sock"); + describe("websocket", () => { const tests = [ { @@ -294,6 +301,20 @@ describe("websocket", () => { }); describe("unix domain socket without websocket", () => { + let tempdir: string; + let randomSocketPath: () => string; + + beforeAll(() => { + // Create .tmp in root repo directory to avoid long paths on Windows + tempdir = ".tmp"; + fs.mkdirSync(tempdir, { recursive: true }); + randomSocketPath = randomSocketPathFn(tempdir); + }); + + afterAll(() => { + fs.rmdirSync(tempdir, { recursive: true }); + }); + if (isPosix) { async function runTest(path: string, args: string[], env = bunEnv) { let { promise, resolve, reject } = Promise.withResolvers(); @@ -337,23 +358,23 @@ describe("unix domain socket without websocket", () => { } test("bun --inspect=unix://", async () => { - const path = Math.random().toString(36).substring(2, 15) + ".sock"; + const path = randomSocketPath(); const url = new URL(`unix://${path}`); await runTest(path, ["--inspect=" + url.href]); }); test("bun --inspect=unix:", async () => { - const path = Math.random().toString(36).substring(2, 15) + ".sock"; + const path = randomSocketPath(); await runTest(path, ["--inspect=unix:" + path]); }); test("BUN_INSPECT=' unix://' bun --inspect", async () => { - const path = Math.random().toString(36).substring(2, 15) + ".sock"; + const path = randomSocketPath(); await runTest(path, [], { ...bunEnv, BUN_INSPECT: "unix://" + path }); }); test("BUN_INSPECT='unix:' bun --inspect", async () => { - const path = Math.random().toString(36).substring(2, 15) + ".sock"; + const path = randomSocketPath(); await runTest(path, [], { ...bunEnv, BUN_INSPECT: "unix:" + path }); }); } @@ -362,7 +383,6 @@ describe("unix domain socket without websocket", () => { /// TODO: this test is flaky because the inspect may not send all messages before the process exit /// we need to implement a way/option so we wait every message from the inspector before exiting test.todo("junit reporter", async () => { - const path = Math.random().toString(36).substring(2, 15) + ".sock"; let reporter: JUnitReporter; let session: InspectorSession; @@ -386,6 +406,7 @@ test.todo("junit reporter", async () => { }); `, }); + const path = randomSocketPathFn(tempdir)(); let { resolve, reject, promise } = Promise.withResolvers(); const [socket, subprocess] = await Promise.all([ connect(`unix://${path}`, resolve), From bd1c5e98768b0b204b7cef24b3716206b7334f2c Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Thu, 12 Dec 2024 22:04:19 -0800 Subject: [PATCH 6/6] feat: add `JSObject` constructors (#15742) Co-authored-by: Don Isaac --- src/bake/FrameworkRouter.zig | 16 ++++---- src/bun.js/bindgen_test.zig | 4 +- src/bun.js/bindings/bindings.zig | 69 +++++++++++++++++++++++++++----- src/ini.zig | 12 +++--- src/js/private.d.ts | 37 +++++++++++++++++ src/js/tsconfig.json | 2 +- 6 files changed, 113 insertions(+), 27 deletions(-) diff --git a/src/bake/FrameworkRouter.zig b/src/bake/FrameworkRouter.zig index 49607db03c88b0..ed7b2cc258d1e4 100644 --- a/src/bake/FrameworkRouter.zig +++ b/src/bake/FrameworkRouter.zig @@ -1084,10 +1084,10 @@ pub const JSFrameworkRouter = struct { const validators = bun.JSC.Node.validators; pub fn getBindings(global: *JSC.JSGlobalObject) JSC.JSValue { - return global.createObjectFromStruct(.{ + return JSC.JSObject.create(.{ .parseRoutePattern = global.createHostFunction("parseRoutePattern", parseRoutePattern, 1), .FrameworkRouter = codegen.getConstructor(global), - }).toJS(); + }, global).toJS(); } pub fn constructor(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) !*JSFrameworkRouter { @@ -1165,7 +1165,7 @@ pub const JSFrameworkRouter = struct { var sfb = std.heap.stackFallback(4096, bun.default_allocator); const alloc = sfb.get(); - return global.createObjectFromStruct(.{ + return JSC.JSObject.create(.{ .params = if (params_out.params.len > 0) params: { const obj = JSValue.createEmptyObject(global, params_out.params.len); for (params_out.params.slice()) |param| { @@ -1176,7 +1176,7 @@ pub const JSFrameworkRouter = struct { break :params obj; } else .null, .route = try jsfr.routeToJsonInverse(global, index, alloc), - }).toJS(); + }, global).toJS(); } return .null; @@ -1193,7 +1193,7 @@ pub const JSFrameworkRouter = struct { fn routeToJson(jsfr: *JSFrameworkRouter, global: *JSGlobalObject, route_index: Route.Index, allocator: Allocator) !JSValue { const route = jsfr.router.routePtr(route_index); - return global.createObjectFromStruct(.{ + return JSC.JSObject.create(.{ .part = try partToJS(global, route.part, allocator), .page = jsfr.fileIdToJS(global, route.file_page), .layout = jsfr.fileIdToJS(global, route.file_layout), @@ -1212,12 +1212,12 @@ pub const JSFrameworkRouter = struct { } break :brk arr; }, - }).toJS(); + }, global).toJS(); } fn routeToJsonInverse(jsfr: *JSFrameworkRouter, global: *JSGlobalObject, route_index: Route.Index, allocator: Allocator) !JSValue { const route = jsfr.router.routePtr(route_index); - return global.createObjectFromStruct(.{ + return JSC.JSObject.create(.{ .part = try partToJS(global, route.part, allocator), .page = jsfr.fileIdToJS(global, route.file_page), .layout = jsfr.fileIdToJS(global, route.file_layout), @@ -1226,7 +1226,7 @@ pub const JSFrameworkRouter = struct { try routeToJsonInverse(jsfr, global, parent, allocator) else .null, - }).toJS(); + }, global).toJS(); } pub fn finalize(this: *JSFrameworkRouter) void { diff --git a/src/bun.js/bindgen_test.zig b/src/bun.js/bindgen_test.zig index 224ca58a0600fb..1d0d0d5e31216f 100644 --- a/src/bun.js/bindgen_test.zig +++ b/src/bun.js/bindgen_test.zig @@ -2,10 +2,10 @@ const gen = bun.gen.bindgen_test; pub fn getBindgenTestFunctions(global: *JSC.JSGlobalObject) JSC.JSValue { - return global.createObjectFromStruct(.{ + return JSC.JSObject.create(.{ .add = gen.createAddCallback(global), .requiredAndOptionalArg = gen.createRequiredAndOptionalArgCallback(global), - }).toJS(); + }, global).toJS(); } // This example should be kept in sync with bindgen's documentation diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 173add280230e9..ea29db478c0f13 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -32,6 +32,65 @@ pub const JSObject = extern struct { return JSValue.fromCell(obj); } + /// Marshall a struct instance into a JSObject, copying its properties. + /// + /// Each field will be encoded with `JSC.toJS`. Fields whose types have a + /// `toJS` method will have it called to encode. + /// + /// This method is equivalent to `Object.create(...)` + setting properties, + /// and is only intended for creating POJOs. + pub fn create(pojo: anytype, global: *JSGlobalObject) *JSObject { + return createFromStructWithPrototype(@TypeOf(pojo), pojo, global, false); + } + /// Marshall a struct into a JSObject, copying its properties. It's + /// `__proto__` will be `null`. + /// + /// Each field will be encoded with `JSC.toJS`. Fields whose types have a + /// `toJS` method will have it called to encode. + /// + /// This is roughly equivalent to creating an object with + /// `Object.create(null)` and adding properties to it. + pub fn createNullProto(pojo: anytype, global: *JSGlobalObject) *JSObject { + return createFromStructWithPrototype(@TypeOf(pojo), pojo, global, true); + } + + /// Marshall a struct instance into a JSObject. `pojo` is borrowed. + /// + /// Each field will be encoded with `JSC.toJS`. Fields whose types have a + /// `toJS` method will have it called to encode. + /// + /// This method is equivalent to `Object.create(...)` + setting properties, + /// and is only intended for creating POJOs. + /// + /// The object's prototype with either be `null` or `ObjectPrototype` + /// depending on whether `null_prototype` is set. Prefer using the object + /// prototype (`null_prototype = false`) unless you have a good reason not + /// to. + fn createFromStructWithPrototype(comptime T: type, pojo: T, global: *JSGlobalObject, comptime null_prototype: bool) *JSObject { + const info: std.builtin.Type.Struct = @typeInfo(T).Struct; + + const obj = obj: { + const val = if (comptime null_prototype) + JSValue.createEmptyObjectWithNullPrototype(global) + else + JSValue.createEmptyObject(global, comptime info.fields.len); + bun.debugAssert(val.isObject()); + break :obj val.uncheckedPtrCast(JSObject); + }; + + const cell = toJS(obj); + inline for (info.fields) |field| { + const property = @field(pojo, field.name); + cell.put( + global, + field.name, + JSC.toJS(global, @TypeOf(property), property, .temporary), + ); + } + + return obj; + } + pub inline fn put(obj: *JSObject, global: *JSGlobalObject, key: anytype, value: JSValue) !void { obj.toJS().put(global, key, value); } @@ -3491,16 +3550,6 @@ pub const JSGlobalObject = opaque { return default; } - pub inline fn createObjectFromStruct(global: *JSGlobalObject, properties: anytype) *JSObject { - const obj = JSValue.createEmptyObject(global, comptime std.meta.fields(@TypeOf(properties)).len).uncheckedPtrCast(JSObject); - obj.putAllFromStruct(global, properties) catch { - // empty object has no setters. this must be a low-allocation OOM, - // wherethrowing will be nearly impossible to handle correctly. - bun.outOfMemory(); - }; - return obj; - } - pub inline fn createHostFunction( global: *JSGlobalObject, comptime display_name: [:0]const u8, diff --git a/src/ini.zig b/src/ini.zig index 1d1dd142d14a4c..d13c34d696d53a 100644 --- a/src/ini.zig +++ b/src/ini.zig @@ -593,12 +593,12 @@ pub const IniTestingAPIs = struct { default_registry_password.deref(); } - return globalThis.createObjectFromStruct(.{ - .default_registry_url = default_registry_url.toJS(globalThis), - .default_registry_token = default_registry_token.toJS(globalThis), - .default_registry_username = default_registry_username.toJS(globalThis), - .default_registry_password = default_registry_password.toJS(globalThis), - }).toJS(); + return JSC.JSObject.create(.{ + .default_registry_url = default_registry_url, + .default_registry_token = default_registry_token, + .default_registry_username = default_registry_username, + .default_registry_password = default_registry_password, + }, globalThis).toJS(); } pub fn parse(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { diff --git a/src/js/private.d.ts b/src/js/private.d.ts index 825d4a68fd3c0e..aeb86e97d20984 100644 --- a/src/js/private.d.ts +++ b/src/js/private.d.ts @@ -161,7 +161,43 @@ interface CommonJSModuleRecord { require: typeof require; } +/** + * Call a native c++ binding, getting whatever it returns. + * + * This is more like a macro; it is replaced with a WebKit intrisic during + * codegen. Passing a template parameter will break codegen. Prefer `$cpp(...) + * as Foo` instead. + * + * Binding files are located in `src/bun.js/bindings` + * + * @see {@link $zig} for native zig bindings. + * @see `src/codegen/replacements.ts` for the script that performs replacement of this funciton. + * + * @param filename name of the c++ file containing the function. Do not pass a path. + * @param symbol The name of the binding function to call. Use `dot.notation` to access + * member symbols. + * + * @returns whatever the binding function returns. + */ declare function $cpp(filename: NativeFilenameCPP, symbol: string): T; +/** + * Call a native zig binding function, getting whatever it returns. + * + * This is more like a macro; it is replaced with a WebKit intrisic during + * codegen. Passing a template parameter will break codegen. Prefer `$zig(...) + * as Foo` instead. + * + * Binding files are located in `src/bun.js/bindings` + * + * @see {@link $cpp} for native c++ bindings. + * @see `src/codegen/replacements.ts` for the script that performs replacement of this funciton. + * + * @param filename name of the zig file containing the function. Do not pass a path. + * @param symbol The name of the binding function. Use `dot.notation` to access + * member symbols. + * + * @returns whatever the binding function returns. + */ declare function $zig(filename: NativeFilenameZig, symbol: string): T; declare function $newCppFunction any>( filename: NativeFilenameCPP, @@ -178,3 +214,4 @@ declare function $newZigFunction any>( * @param symbol - The name of the function to call. */ declare function $bindgenFn any>(filename: string, symbol: string): T; +// NOTE: $debug, $assert, and $isPromiseResolved omitted diff --git a/src/js/tsconfig.json b/src/js/tsconfig.json index a2fd51e7dcd8a7..5f4eb774baeaf0 100644 --- a/src/js/tsconfig.json +++ b/src/js/tsconfig.json @@ -4,7 +4,7 @@ // Path remapping "baseUrl": ".", "paths": { - "internal/*": ["./js/internal/*"] //deprecated + "internal/*": ["./internal/*"] //deprecated } }, "include": ["**/*.ts", "**/*.tsx", "./builtins.d.ts", "./private.d.ts"]