From 5fa13625a1ca0ea1a3a1c5bb86d0880dcfac349f Mon Sep 17 00:00:00 2001 From: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Date: Wed, 21 Jun 2023 23:38:18 -0700 Subject: upgrade zig to `v0.11.0-dev.3737+9eb008717` (#3374) * progress * finish `@memset/@memcpy` update * Update build.zig * change `@enumToInt` to `@intFromEnum` and friends * update zig versions * it was 1 * add link to issue * add `compileError` reminder * fix merge * format * upgrade to llvm 16 * Revert "upgrade to llvm 16" This reverts commit cc930ceb1c5b4db9614a7638596948f704544ab8. --------- Co-authored-by: Jarred Sumner Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- src/resolver/package_json.zig | 18 +++++++++--------- src/resolver/resolve_path.zig | 4 ++-- src/resolver/resolver.zig | 13 ++++++------- src/resolver/tsconfig_json.zig | 2 +- 4 files changed, 18 insertions(+), 19 deletions(-) (limited to 'src/resolver') diff --git a/src/resolver/package_json.zig b/src/resolver/package_json.zig index 63bc5b20b..e759374e7 100644 --- a/src/resolver/package_json.zig +++ b/src/resolver/package_json.zig @@ -59,7 +59,7 @@ pub const PackageJSON = struct { pub fn generateHash(package_json: *PackageJSON) void { var hashy: [1024]u8 = undefined; - std.mem.set(u8, &hashy, 0); + @memset(&hashy, 0); var used: usize = 0; bun.copy(u8, &hashy, package_json.name); used = package_json.name.len; @@ -390,7 +390,7 @@ pub const PackageJSON = struct { var count: usize = 0; const items = array.items.slice(); for (items) |item| { - count += @boolToInt(item.data == .e_string and item.data.e_string.data.len > 0); + count += @intFromBool(item.data == .e_string and item.data.e_string.data.len > 0); } switch (count) { 0 => {}, @@ -888,10 +888,10 @@ pub const PackageJSON = struct { const dependency_groups = comptime brk: { var out_groups: [ - @as(usize, @boolToInt(features.dependencies)) + - @as(usize, @boolToInt(features.dev_dependencies)) + - @as(usize, @boolToInt(features.optional_dependencies)) + - @as(usize, @boolToInt(features.peer_dependencies)) + @as(usize, @intFromBool(features.dependencies)) + + @as(usize, @intFromBool(features.dev_dependencies)) + + @as(usize, @intFromBool(features.optional_dependencies)) + + @as(usize, @intFromBool(features.peer_dependencies)) ]DependencyGroup = undefined; var out_group_i: usize = 0; if (features.dependencies) { @@ -989,7 +989,7 @@ pub const PackageJSON = struct { const key = prop.key.?.asString(allocator) orelse continue; const value = prop.value.?.asString(allocator) orelse continue; - count += @as(usize, @boolToInt(key.len > 0 and value.len > 0)); + count += @as(usize, @intFromBool(key.len > 0 and value.len > 0)); } if (count == 0) break :read_scripts; @@ -1022,7 +1022,7 @@ pub const PackageJSON = struct { } pub fn hashModule(this: *const PackageJSON, module: string) u32 { - var hasher = std.hash.Wyhash.init(0); + var hasher = bun.Wyhash.init(0); hasher.update(std.mem.asBytes(&this.hash)); hasher.update(module); @@ -1148,7 +1148,7 @@ pub const ExportsMap = struct { // PATTERN_KEY_COMPARE which orders in descending order of specificity. const GlobLengthSorter: type = strings.NewGlobLengthSorter(Entry.Data.Map.MapEntry, "key"); var sorter = GlobLengthSorter{}; - std.sort.sort(Entry.Data.Map.MapEntry, expansion_keys, sorter, GlobLengthSorter.lessThan); + std.sort.block(Entry.Data.Map.MapEntry, expansion_keys, sorter, GlobLengthSorter.lessThan); return Entry{ .data = .{ diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig index c5ffdc626..e63318887 100644 --- a/src/resolver/resolve_path.zig +++ b/src/resolver/resolve_path.zig @@ -376,7 +376,7 @@ pub fn relativeToCommonPath( var out_slice: []u8 = buf[0..0]; if (normalized_from.len > 0) { - var i: usize = @intCast(usize, @boolToInt(normalized_from[0] == separator)) + 1 + last_common_separator; + var i: usize = @intCast(usize, @intFromBool(normalized_from[0] == separator)) + 1 + last_common_separator; while (i <= normalized_from.len) : (i += 1) { if (i == normalized_from.len or (normalized_from[i] == separator and i + 1 < normalized_from.len)) { @@ -549,7 +549,7 @@ pub fn normalizeStringGeneric(path: []const u8, buf: []u8, comptime allow_above_ const from = r; while (r < n and !isSeparator(path[r])) : (r += 1) {} const count = r - from; - @memcpy(buf[buf_i..].ptr, path[from..].ptr, count); + @memcpy(buf[buf_i..][0..count], path[from..][0..count]); buf_i += count; } diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index 8a6a2bc5e..14bc358d0 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -27,7 +27,6 @@ const CacheSet = cache.Set; const DataURL = @import("./data_url.zig").DataURL; pub const DirInfo = @import("./dir_info.zig"); const HTTPWatcher = if (Environment.isTest or Environment.isWasm) void else @import("../http.zig").Watcher; -const Wyhash = std.hash.Wyhash; const ResolvePath = @import("./resolve_path.zig"); const NodeFallbackModules = @import("../node_fallbacks.zig"); const Mutex = @import("../lock.zig").Lock; @@ -283,10 +282,10 @@ pub const Result = struct { if (strings.lastIndexOf(module, node_module_root)) |end_| { var end: usize = end_ + node_module_root.len; - return @truncate(u32, std.hash.Wyhash.hash(0, module[end..])); + return @truncate(u32, bun.hash(module[end..])); } - return @truncate(u32, std.hash.Wyhash.hash(0, this.path_pair.primary.text)); + return @truncate(u32, bun.hash(this.path_pair.primary.text)); } }; @@ -609,7 +608,7 @@ pub const Resolver = struct { if (r.debug_logs) |*debug| { if (flush_mode == DebugLogs.FlushMode.fail) { try r.log.addRangeDebugWithNotes(null, logger.Range{ .loc = logger.Loc{} }, debug.what, try debug.notes.toOwnedSlice()); - } else if (@enumToInt(r.log.level) <= @enumToInt(logger.Log.Level.verbose)) { + } else if (@intFromEnum(r.log.level) <= @intFromEnum(logger.Log.Level.verbose)) { try r.log.addVerboseWithNotes(null, logger.Loc.Empty, debug.what, try debug.notes.toOwnedSlice()); } } @@ -1265,7 +1264,7 @@ pub const Resolver = struct { if (NodeFallbackModules.Map.get(import_path_without_node_prefix)) |*fallback_module| { result.path_pair.primary = fallback_module.path; result.module_type = .cjs; - result.package_json = @intToPtr(*PackageJSON, @ptrToInt(fallback_module.package_json)); + result.package_json = @ptrFromInt(*PackageJSON, @intFromPtr(fallback_module.package_json)); result.is_from_node_modules = true; return .{ .success = result }; // "node:* @@ -1696,7 +1695,7 @@ pub const Resolver = struct { // check the global cache directory for a package.json file. var manager = r.getPackageManager(); var dependency_version: Dependency.Version = .{}; - var dependency_behavior = @intToEnum(Dependency.Behavior, Dependency.Behavior.normal); + var dependency_behavior = @enumFromInt(Dependency.Behavior, Dependency.Behavior.normal); // const initial_pending_tasks = manager.pending_tasks; var resolved_package_id: Install.PackageID = brk: { @@ -2629,7 +2628,7 @@ pub const Resolver = struct { // Directories must always end in a trailing slash or else various bugs can occur. // This covers "what happens when the trailing" - end += @intCast(usize, @boolToInt(safe_path.len > end and end > 0 and safe_path[end - 1] != std.fs.path.sep and safe_path[end] == std.fs.path.sep)); + end += @intCast(usize, @intFromBool(safe_path.len > end and end > 0 and safe_path[end - 1] != std.fs.path.sep and safe_path[end] == std.fs.path.sep)); break :brk safe_path[dir_path_i..end]; }; diff --git a/src/resolver/tsconfig_json.zig b/src/resolver/tsconfig_json.zig index 4f04c39ca..2ec402fd2 100644 --- a/src/resolver/tsconfig_json.zig +++ b/src/resolver/tsconfig_json.zig @@ -344,7 +344,7 @@ pub const TSConfigJSON = struct { // foo == 1 // foo.bar.baz == 3 // foo.bar.baz.bun == 4 - const parts_count = std.mem.count(u8, text, ".") + @as(usize, @boolToInt(text[text.len - 1] != '.')); + const parts_count = std.mem.count(u8, text, ".") + @as(usize, @intFromBool(text[text.len - 1] != '.')); var parts = std.ArrayList(string).initCapacity(allocator, parts_count) catch unreachable; if (parts_count == 1) { -- cgit v1.2.3 From ec3ed67bc9ad8cbb0e59234564d57265d5423fce Mon Sep 17 00:00:00 2001 From: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Date: Mon, 26 Jun 2023 08:12:37 -0700 Subject: implement `_nodeModulePaths` and `require.main.paths` (#3411) * tests in progress * add `require.main.paths`, add every dir up to root * remove imports --- src/bun.js/bindings/CommonJSModuleRecord.cpp | 31 +++++++++ src/bun.js/bindings/CommonJSModuleRecord.h | 1 + src/bun.js/modules/NodeModuleModule.cpp | 15 ++-- src/resolver/resolver.zig | 95 ++++++++++++++++++++++++++ test/js/node/module/node-module-module.test.js | 19 ++++++ 5 files changed, 150 insertions(+), 11 deletions(-) (limited to 'src/resolver') diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp index 8d4fe0a1e..3615db774 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.cpp +++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp @@ -295,6 +295,35 @@ JSC_DEFINE_CUSTOM_SETTER(setterPath, return true; } +extern "C" EncodedJSValue Resolver__propForRequireMainPaths(JSGlobalObject*); + +JSC_DEFINE_CUSTOM_GETTER(getterPaths, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) +{ + JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); + if (UNLIKELY(!thisObject)) { + return JSValue::encode(jsUndefined()); + } + + if (!thisObject->m_paths) { + JSValue paths = JSValue::decode(Resolver__propForRequireMainPaths(globalObject)); + thisObject->m_paths.set(globalObject->vm(), thisObject, paths); + } + + return JSValue::encode(thisObject->m_paths.get()); +} + +JSC_DEFINE_CUSTOM_SETTER(setterPaths, + (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, + JSC::EncodedJSValue value, JSC::PropertyName propertyName)) +{ + JSCommonJSModule* thisObject = jsDynamicCast(JSValue::decode(thisValue)); + if (!thisObject) + return false; + + thisObject->m_paths.set(globalObject->vm(), thisObject, JSValue::decode(value)); + return true; +} + JSC_DEFINE_CUSTOM_SETTER(setterFilename, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) @@ -340,6 +369,7 @@ static const struct HashTableValue JSCommonJSModulePrototypeTableValues[] = { { "loaded"_s, static_cast(PropertyAttribute::PropertyCallback | PropertyAttribute::DontEnum | 0), NoIntrinsic, { HashTableValue::LazyPropertyType, createLoaded } }, { "parent"_s, static_cast(PropertyAttribute::PropertyCallback | PropertyAttribute::DontEnum | 0), NoIntrinsic, { HashTableValue::LazyPropertyType, createParent } }, { "path"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, getterPath, setterPath } }, + { "paths"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, getterPaths, setterPaths } }, }; class JSCommonJSModulePrototype final : public JSC::JSNonFinalObject { @@ -675,6 +705,7 @@ void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor) visitor.append(thisObject->sourceCode); visitor.append(thisObject->m_filename); visitor.append(thisObject->m_dirname); + visitor.append(thisObject->m_paths); } DEFINE_VISIT_CHILDREN(JSCommonJSModule); diff --git a/src/bun.js/bindings/CommonJSModuleRecord.h b/src/bun.js/bindings/CommonJSModuleRecord.h index 48f14b39c..a96ab5f75 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.h +++ b/src/bun.js/bindings/CommonJSModuleRecord.h @@ -24,6 +24,7 @@ public: mutable JSC::WriteBarrier m_id; mutable JSC::WriteBarrier m_filename; mutable JSC::WriteBarrier m_dirname; + mutable JSC::WriteBarrier m_paths; mutable JSC::WriteBarrier sourceCode; static void destroy(JSC::JSCell*); diff --git a/src/bun.js/modules/NodeModuleModule.cpp b/src/bun.js/modules/NodeModuleModule.cpp index 8b278ddd8..34d45698f 100644 --- a/src/bun.js/modules/NodeModuleModule.cpp +++ b/src/bun.js/modules/NodeModuleModule.cpp @@ -26,15 +26,8 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire, scope, JSValue::encode(Zig::ImportMetaObject::createRequireFunction( vm, globalObject, val))); } -JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModulePaths, - (JSC::JSGlobalObject * globalObject, - JSC::CallFrame *callFrame)) { - return JSC::JSValue::encode(JSC::JSArray::create( - globalObject->vm(), - globalObject->arrayStructureForIndexingTypeDuringAllocation( - ArrayWithContiguous), - 0)); -} +extern "C" EncodedJSValue Resolver__nodeModulePathsForJS(JSGlobalObject *, + CallFrame *); JSC_DEFINE_HOST_FUNCTION(jsFunctionFindSourceMap, (JSGlobalObject * globalObject, @@ -114,7 +107,7 @@ void generateNodeModuleModule(JSC::JSGlobalObject *globalObject, vm, globalObject, 1, String("createRequire"_s), jsFunctionNodeModuleCreateRequire, ImplementationVisibility::Public)); exportValues.append(JSFunction::create(vm, globalObject, 1, String("paths"_s), - jsFunctionNodeModulePaths, + Resolver__nodeModulePathsForJS, ImplementationVisibility::Public)); exportValues.append(JSFunction::create( vm, globalObject, 1, String("findSourceMap"_s), jsFunctionFindSourceMap, @@ -143,7 +136,7 @@ void generateNodeModuleModule(JSC::JSGlobalObject *globalObject, exportNames.append(JSC::Identifier::fromString(vm, "_nodeModulePaths"_s)); exportValues.append(JSFunction::create( vm, globalObject, 0, String("_nodeModulePaths"_s), - jsFunctionNodeModulePaths, ImplementationVisibility::Public)); + Resolver__nodeModulePathsForJS, ImplementationVisibility::Public)); exportNames.append(JSC::Identifier::fromString(vm, "_cache"_s)); exportValues.append( diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index 14bc358d0..40b106f3a 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -94,6 +94,7 @@ const bufs = struct { threadlocal var remap_path_trailing_slash: [bun.MAX_PATH_BYTES]u8 = undefined; threadlocal var path_in_global_disk_cache: [bun.MAX_PATH_BYTES]u8 = undefined; threadlocal var abs_to_rel: [bun.MAX_PATH_BYTES]u8 = undefined; + threadlocal var node_modules_paths_buf: [bun.MAX_PATH_BYTES]u8 = undefined; pub inline fn bufs(comptime field: std.meta.DeclEnum(@This())) *@TypeOf(@field(@This(), @tagName(field))) { return &@field(@This(), @tagName(field)); @@ -3107,6 +3108,93 @@ pub const Resolver = struct { }; } + pub export fn Resolver__nodeModulePathsForJS(globalThis: *bun.JSC.JSGlobalObject, callframe: *bun.JSC.CallFrame) callconv(.C) bun.JSC.JSValue { + bun.JSC.markBinding(@src()); + const argument: bun.JSC.JSValue = callframe.argument(0); + + if (argument.isEmpty() or !argument.isString()) { + globalThis.throwInvalidArgumentType("nodeModulePaths", "path", "string"); + return .zero; + } + + const in_str = argument.toBunString(globalThis); + var r = &globalThis.bunVM().bundler.resolver; + return nodeModulePathsJSValue(r, in_str, globalThis); + } + + pub export fn Resolver__propForRequireMainPaths(globalThis: *bun.JSC.JSGlobalObject) callconv(.C) bun.JSC.JSValue { + bun.JSC.markBinding(@src()); + + const in_str = bun.String.create("."); + var r = &globalThis.bunVM().bundler.resolver; + return nodeModulePathsJSValue(r, in_str, globalThis); + } + + pub fn nodeModulePathsJSValue( + r: *ThisResolver, + in_str: bun.String, + globalObject: *bun.JSC.JSGlobalObject, + ) bun.JSC.JSValue { + var list = std.ArrayList(bun.String).init(bun.default_allocator); + defer list.deinit(); + + const sliced = in_str.toUTF8(bun.default_allocator); + defer sliced.deinit(); + + const str = brk: { + if (std.fs.path.isAbsolute(sliced.slice())) break :brk sliced.slice(); + var dir_path_buf = bufs(.node_modules_paths_buf); + break :brk r.fs.joinBuf(&[_]string{ r.fs.top_level_dir, sliced.slice() }, dir_path_buf); + }; + var arena = std.heap.ArenaAllocator.init(bun.default_allocator); + defer arena.deinit(); + var stack_fallback_allocator = std.heap.stackFallback(1024, arena.allocator()); + + if (r.readDirInfo(strings.withoutTrailingSlash(str)) catch null) |result| { + var dir_info = result; + + while (true) { + const path_without_trailing_slash = strings.withoutTrailingSlash(dir_info.abs_path); + const path_parts = brk: { + if (path_without_trailing_slash.len == 1 and path_without_trailing_slash[0] == '/') { + break :brk [2]string{ "", "/node_modules" }; + } + + break :brk [2]string{ path_without_trailing_slash, "/node_modules" }; + }; + list.append( + bun.String.create( + bun.strings.concat(stack_fallback_allocator.get(), &path_parts) catch unreachable, + ), + ) catch unreachable; + dir_info = (r.readDirInfo(std.fs.path.dirname(path_without_trailing_slash) orelse break) catch null) orelse break; + } + } else { + // does not exist + const full_path = std.fs.path.resolve(r.allocator, &[1][]const u8{str}) catch unreachable; + var path = full_path; + while (true) { + const path_without_trailing_slash = strings.withoutTrailingSlash(path); + + list.append( + bun.String.create( + bun.strings.concat( + stack_fallback_allocator.get(), + &[_]string{ + path_without_trailing_slash, + "/node_modules", + }, + ) catch unreachable, + ), + ) catch unreachable; + + path = path[0 .. strings.lastIndexOfChar(path, '/') orelse break]; + } + } + + return bun.String.toJSArray(globalObject, list.items); + } + pub fn loadAsIndex(r: *ThisResolver, dir_info: *DirInfo, extension_order: []const string) ?MatchResult { var rfs = &r.fs.fs; // Try the "index" file with extensions @@ -3892,3 +3980,10 @@ pub const GlobalCache = enum { }; } }; + +comptime { + if (!bun.JSC.is_bindgen) { + _ = Resolver.Resolver__nodeModulePathsForJS; + _ = Resolver.Resolver__propForRequireMainPaths; + } +} diff --git a/test/js/node/module/node-module-module.test.js b/test/js/node/module/node-module-module.test.js index 549b5e085..3ced63da1 100644 --- a/test/js/node/module/node-module-module.test.js +++ b/test/js/node/module/node-module-module.test.js @@ -1,5 +1,24 @@ import { expect, test } from "bun:test"; +import { _nodeModulePaths } from "module"; test("module.globalPaths exists", () => { expect(Array.isArray(require("module").globalPaths)).toBe(true); }); + +test("_nodeModulePaths() works", () => { + expect(() => { + _nodeModulePaths(); + }).toThrow(); + expect(_nodeModulePaths(".").length).toBeGreaterThan(0); + expect(_nodeModulePaths(".").pop()).toBe("/node_modules"); + expect(_nodeModulePaths("")).toEqual(_nodeModulePaths(".")); + expect(_nodeModulePaths("/")).toEqual(["/node_modules"]); + expect(_nodeModulePaths("/a/b/c/d")).toEqual([ + "/a/b/c/d/node_modules", + "/a/b/c/node_modules", + "/a/b/node_modules", + "/a/node_modules", + "/node_modules", + ]); + expect(_nodeModulePaths("/a/b/../d")).toEqual(["/a/d/node_modules", "/a/node_modules", "/node_modules"]); +}); -- cgit v1.2.3 From 0de5bb22af427dcad57731d7063f8678b13265e2 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 28 Jun 2023 11:20:59 +0300 Subject: [install] workaround run-time module loading issue (#3432) --- src/bun.js/module_loader.zig | 6 --- src/install/install.zig | 40 ++++++++++++++--- src/resolver/resolver.zig | 27 ++++++----- test/cli/install/bun-run.test.ts | 96 ++++++++++++++++++++++++++++++++++++++++ test/cli/install/bunx.test.ts | 80 +-------------------------------- 5 files changed, 143 insertions(+), 106 deletions(-) create mode 100644 test/cli/install/bun-run.test.ts (limited to 'src/resolver') diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index b25bb4b10..6fd4fef99 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -271,14 +271,8 @@ pub const ModuleLoader = struct { pub fn onPoll(this: *Queue) void { debug("onPoll", .{}); - var pm = this.vm().packageManager(); - this.runTasks(); - _ = pm.scheduleTasks(); - this.runTasks(); - this.pollModules(); - _ = pm.flushDependencyQueue(); } pub fn runTasks(this: *Queue) void { diff --git a/src/install/install.zig b/src/install/install.zig index 9465c4897..87f931291 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -1718,9 +1718,8 @@ pub const PackageManager = struct { } pub fn wake(this: *PackageManager) void { - if (this.onWake.context != null) { - this.onWake.getHandler()(this.onWake.context.?, this); - return; + if (this.onWake.context) |ctx| { + this.onWake.getHandler()(ctx, this); } _ = this.wait_count.fetchAdd(1, .Monotonic); @@ -1791,12 +1790,37 @@ pub const PackageManager = struct { return .{ .failure = err }; }; - const resolution_id = this.lockfile.buffers.resolutions.items[index]; + const resolution_id = switch (this.lockfile.buffers.resolutions.items[index]) { + invalid_package_id => brk: { + this.drainDependencyList(); + + switch (this.options.log_level) { + inline else => |log_level| { + if (log_level.showProgress()) this.startProgressBarIfNone(); + while (this.pending_tasks > 0) : (this.sleep()) { + this.runTasks( + void, + {}, + .{ + .onExtract = {}, + .onResolve = {}, + .onPackageManifestError = {}, + .onPackageDownloadError = {}, + }, + log_level, + ) catch |err| { + return .{ .failure = err }; + }; + } + }, + } - // check if we managed to synchronously resolve the dependency - if (resolution_id == invalid_package_id) return .{ .pending = index }; + break :brk this.lockfile.buffers.resolutions.items[index]; + }, + // we managed to synchronously resolve the dependency + else => |pkg_id| pkg_id, + }; - this.drainDependencyList(); return .{ .resolution = .{ .resolution = this.lockfile.packages.items(.resolution)[resolution_id], @@ -5310,6 +5334,8 @@ pub const PackageManager = struct { manager.progress.supports_ansi_escape_codes = Output.enable_ansi_colors_stderr; manager.root_progress_node = manager.progress.start("", 0); manager.root_download_node = manager.root_progress_node.start(ProgressStrings.download(), 0); + } else { + manager.options.log_level = .default_no_progress; } if (!manager.options.enable.cache) { diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index 40b106f3a..d9f4dc887 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -534,20 +534,19 @@ pub const Resolver = struct { dir_cache: *DirInfo.HashMap, pub fn getPackageManager(this: *Resolver) *PackageManager { - if (this.package_manager != null) { - return this.package_manager.?; - } - bun.HTTPThead.init() catch unreachable; - this.package_manager = PackageManager.initWithRuntime( - this.log, - this.opts.install, - this.allocator, - .{}, - this.env_loader.?, - ) catch @panic("Failed to initialize package manager"); - this.package_manager.?.onWake = this.onWakePackageManager; - - return this.package_manager.?; + return this.package_manager orelse brk: { + bun.HTTPThead.init() catch unreachable; + const pm = PackageManager.initWithRuntime( + this.log, + this.opts.install, + this.allocator, + .{}, + this.env_loader.?, + ) catch @panic("Failed to initialize package manager"); + pm.onWake = this.onWakePackageManager; + this.package_manager = pm; + break :brk pm; + }; } pub inline fn usePackageManager(self: *const ThisResolver) bool { diff --git a/test/cli/install/bun-run.test.ts b/test/cli/install/bun-run.test.ts new file mode 100644 index 000000000..fecbfc3d5 --- /dev/null +++ b/test/cli/install/bun-run.test.ts @@ -0,0 +1,96 @@ +import { file, spawn } from "bun"; +import { afterEach, beforeEach, expect, it } from "bun:test"; +import { bunExe, bunEnv as env } from "harness"; +import { mkdtemp, realpath, rm, writeFile } from "fs/promises"; +import { tmpdir } from "os"; +import { join } from "path"; +import { readdirSorted } from "./dummy.registry"; + +let run_dir: string; + +beforeEach(async () => { + run_dir = await realpath(await mkdtemp(join(tmpdir(), "bun-run.test"))); +}); +afterEach(async () => { + await rm(run_dir, { force: true, recursive: true }); +}); + +it("should download dependency to run local file", async () => { + await writeFile( + join(run_dir, "test.js"), + ` + const { minify } = require("uglify-js@3.17.4"); + + console.log(minify("print(6 * 7)").code); + `, + ); + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "test.js"], + cwd: run_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env: { + ...env, + BUN_INSTALL_CACHE_DIR: join(run_dir, ".cache"), + }, + }); + expect(stderr).toBeDefined(); + const err = await new Response(stderr).text(); + expect(err).toBe(""); + expect(stdout).toBeDefined(); + const out = await new Response(stdout).text(); + expect(out.split(/\r?\n/)).toEqual(["print(42);", ""]); + expect(await exited).toBe(0); + expect(await readdirSorted(run_dir)).toEqual([".cache", "test.js"]); +}); + +it("should download dependencies to run local file", async () => { + await writeFile( + join(run_dir, "test.js"), + ` + import { file } from "bun"; + import decompress from "decompress@4.2.1"; + + const buffer = await file("${join(import.meta.dir, "baz-0.0.3.tgz")}").arrayBuffer(); + for (const entry of await decompress(Buffer.from(buffer))) { + console.log(\`\${entry.type}: \${entry.path}\`); + } + `, + ); + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "test.js"], + cwd: run_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env: { + ...env, + BUN_INSTALL_CACHE_DIR: join(run_dir, ".cache"), + }, + }); + expect(stderr).toBeDefined(); + const err = await new Response(stderr).text(); + expect(err).toBe(""); + expect(await readdirSorted(run_dir)).toEqual([".cache", "test.js"]); + expect(await readdirSorted(join(run_dir, ".cache"))).toContain("decompress"); + expect(await readdirSorted(join(run_dir, ".cache", "decompress"))).toEqual(["4.2.1"]); + expect(await readdirSorted(join(run_dir, ".cache", "decompress", "4.2.1"))).toEqual([ + "index.js", + "license", + "package.json", + "readme.md", + ]); + expect(await file(join(run_dir, ".cache", "decompress", "4.2.1", "index.js")).text()).toContain( + "\nmodule.exports = ", + ); + expect(stdout).toBeDefined(); + const out = await new Response(stdout).text(); + expect(out.split(/\r?\n/)).toEqual([ + "directory: package/", + "file: package/index.js", + "file: package/package.json", + "", + ]); + expect(await exited).toBe(0); +}); diff --git a/test/cli/install/bunx.test.ts b/test/cli/install/bunx.test.ts index 3605f5b6b..70d7aac29 100644 --- a/test/cli/install/bunx.test.ts +++ b/test/cli/install/bunx.test.ts @@ -1,4 +1,4 @@ -import { file, spawn } from "bun"; +import { spawn } from "bun"; import { afterEach, beforeEach, expect, it } from "bun:test"; import { bunExe, bunEnv as env } from "harness"; import { mkdtemp, realpath, rm, writeFile } from "fs/promises"; @@ -109,84 +109,6 @@ it("should work for @scoped packages", async () => { expect(await cached.exited).toBe(0); }); -it("should download dependency to run local file", async () => { - await writeFile( - join(x_dir, "test.js"), - ` -const { minify } = require("uglify-js@3.17.4"); - -console.log(minify("print(6 * 7)").code); -`, - ); - const { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "test.js"], - cwd: x_dir, - stdout: null, - stdin: "pipe", - stderr: "pipe", - env: { - ...env, - BUN_INSTALL_CACHE_DIR: join(x_dir, ".cache"), - }, - }); - expect(stderr).toBeDefined(); - const err = await new Response(stderr).text(); - expect(err).toBe(""); - expect(stdout).toBeDefined(); - const out = await new Response(stdout).text(); - expect(out.split(/\r?\n/)).toEqual(["print(42);", ""]); - expect(await exited).toBe(0); - expect(await readdirSorted(x_dir)).toEqual([".cache", "test.js"]); -}); - -it("should download dependencies to run local file", async () => { - await writeFile( - join(x_dir, "test.js"), - ` -import { file } from "bun"; -import decompress from "decompress@4.2.1"; - -const buffer = await file("${join(import.meta.dir, "baz-0.0.3.tgz")}").arrayBuffer(); -for (const entry of await decompress(Buffer.from(buffer))) { - console.log(\`\${entry.type}: \${entry.path}\`); -} -`, - ); - const { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "test.js"], - cwd: x_dir, - stdout: null, - stdin: "pipe", - stderr: "pipe", - env: { - ...env, - BUN_INSTALL_CACHE_DIR: join(x_dir, ".cache"), - }, - }); - expect(stderr).toBeDefined(); - const err = await new Response(stderr).text(); - expect(err).toBe(""); - expect(await readdirSorted(x_dir)).toEqual([".cache", "test.js"]); - expect(await readdirSorted(join(x_dir, ".cache"))).toContain("decompress"); - expect(await readdirSorted(join(x_dir, ".cache", "decompress"))).toEqual(["4.2.1"]); - expect(await readdirSorted(join(x_dir, ".cache", "decompress", "4.2.1"))).toEqual([ - "index.js", - "license", - "package.json", - "readme.md", - ]); - expect(await file(join(x_dir, ".cache", "decompress", "4.2.1", "index.js")).text()).toContain("\nmodule.exports = "); - expect(stdout).toBeDefined(); - const out = await new Response(stdout).text(); - expect(out.split(/\r?\n/)).toEqual([ - "directory: package/", - "file: package/index.js", - "file: package/package.json", - "", - ]); - expect(await exited).toBe(0); -}); - it("should execute from current working directory", async () => { await writeFile( join(x_dir, "test.js"), -- cgit v1.2.3 From 3258bed1c03a7808b9f2e4970012668e984ed390 Mon Sep 17 00:00:00 2001 From: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Date: Wed, 28 Jun 2023 19:28:53 -0700 Subject: use main field over module for runtime (#3448) * use main field over module for runtime * move flag to `Resolver` * set `prefer_module_field` in `initWithModuleGraph` --- src/bun.js/javascript.zig | 2 + src/resolver/resolver.zig | 9 +- test/bundler/esbuild/default.test.ts | 200 +++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+), 1 deletion(-) (limited to 'src/resolver') diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 5c58eff60..cf6a65841 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -801,6 +801,7 @@ pub const VirtualMachine = struct { vm.bundler.macro_context = null; vm.bundler.resolver.store_fd = false; + vm.bundler.resolver.prefer_module_field = false; vm.bundler.resolver.onWakePackageManager = .{ .context = &vm.modules, @@ -898,6 +899,7 @@ pub const VirtualMachine = struct { vm.bundler.macro_context = null; vm.bundler.resolver.store_fd = store_fd; + vm.bundler.resolver.prefer_module_field = false; vm.bundler.resolver.onWakePackageManager = .{ .context = &vm.modules, diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index d9f4dc887..e1e83ba4f 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -533,6 +533,10 @@ pub const Resolver = struct { // all parent directories dir_cache: *DirInfo.HashMap, + /// This is set to false for the runtime. The runtime should choose "main" + /// over "module" in package.json + prefer_module_field: bool = true, + pub fn getPackageManager(this: *Resolver) *PackageManager { return this.package_manager orelse brk: { bun.HTTPThead.init() catch unreachable; @@ -3408,7 +3412,10 @@ pub const Resolver = struct { // with this same path. The goal of this code is to avoid having // both the "module" file and the "main" file in the bundle at the // same time. - if (kind != ast.ImportKind.require) { + // + // Additionally, if this is for the runtime, use the "main" field. + // If it doesn't exist, the "module" field will be used. + if (r.prefer_module_field and kind != ast.ImportKind.require) { if (r.debug_logs) |*debug| { debug.addNoteFmt("Resolved to \"{s}\" using the \"module\" field in \"{s}\"", .{ auto_main_result.path_pair.primary.text, pkg_json.source.key_path.text }); diff --git a/test/bundler/esbuild/default.test.ts b/test/bundler/esbuild/default.test.ts index 4c4cf87be..20856ecdf 100644 --- a/test/bundler/esbuild/default.test.ts +++ b/test/bundler/esbuild/default.test.ts @@ -6565,4 +6565,204 @@ describe("bundler", () => { api.expectFile("/out.js").not.toContain("data = 123"); }, }); + itBundled("default/BundlerUsesModuleFieldForEsm", { + files: { + "/entry.js": ` + import { foo } from 'foo'; + console.log(foo); + `, + "/node_modules/foo/package.json": ` + { + "name": "foo", + "version": "2.0.0", + "module": "index.esm.js", + "main": "index.cjs.js" + } + `, + "/node_modules/foo/index.cjs.js": ` + module.exports.foo = "hello index.cjs.js"; + `, + "/node_modules/foo/index.esm.js": ` + export const foo = "hello index.esm.js"; + `, + }, + run: { + stdout: "hello index.esm.js", + }, + }); + itBundled("default/BundlerUsesMainFieldForCjs", { + files: { + "/entry.js": ` + const { foo } = require('foo'); + console.log(foo); + `, + "/node_modules/foo/package.json": ` + { + "name": "foo", + "version": "2.0.0", + "module": "index.esm.js", + "main": "index.cjs.js" + } + `, + "/node_modules/foo/index.cjs.js": ` + module.exports.foo = "hello index.cjs.js"; + `, + "/node_modules/foo/index.esm.js": ` + export const foo = "hello index.esm.js"; + `, + }, + run: { + stdout: "hello index.cjs.js", + }, + }); + itBundled("default/RuntimeUsesMainFieldForCjs", { + files: { + "/entry.js": ` + const { foo } = require('foo'); + console.log(foo); + `, + "/node_modules/foo/package.json": ` + { + "name": "foo", + "version": "2.0.0", + "module": "index.esm.js", + "main": "index.cjs.js" + } + `, + "/node_modules/foo/index.cjs.js": ` + module.exports.foo = "hello index.cjs.js"; + `, + "/node_modules/foo/index.esm.js": ` + export const foo = "hello index.esm.js"; + `, + }, + bundling: false, + run: { + stdout: "hello index.cjs.js", + }, + }); + itBundled("default/RuntimeUsesMainFieldForEsm", { + files: { + "/entry.js": ` + import { foo } from 'foo'; + console.log(foo); + `, + "/node_modules/foo/package.json": ` + { + "name": "foo", + "version": "2.0.0", + "module": "index.esm.js", + "main": "index.cjs.js" + } + `, + "/node_modules/foo/index.cjs.js": ` + module.exports.foo = "hello index.cjs.js"; + `, + "/node_modules/foo/index.esm.js": ` + export const foo = "hello index.esm.js"; + `, + }, + bundling: false, + run: { + stdout: "hello index.cjs.js", + }, + }); + itBundled("default/BundlerUsesModuleFieldIfMainDoesNotExistCjs", { + files: { + "/entry.js": ` + const { foo } = require('foo'); + console.log(foo); + `, + "/node_modules/foo/package.json": ` + { + "name": "foo", + "version": "2.0.0", + "module": "index.esm.js" + } + `, + "/node_modules/foo/index.cjs.js": ` + module.exports.foo = "hello index.cjs.js"; + `, + "/node_modules/foo/index.esm.js": ` + export const foo = "hello index.esm.js"; + `, + }, + run: { + stdout: "hello index.esm.js", + }, + }); + itBundled("default/BundlerUsesModuleFieldIfMainDoesNotExistEsm", { + files: { + "/entry.js": ` + import { foo } from 'foo'; + console.log(foo); + `, + "/node_modules/foo/package.json": ` + { + "name": "foo", + "version": "2.0.0", + "module": "index.esm.js" + } + `, + "/node_modules/foo/index.cjs.js": ` + module.exports.foo = "hello index.cjs.js"; + `, + "/node_modules/foo/index.esm.js": ` + export const foo = "hello index.esm.js"; + `, + }, + run: { + stdout: "hello index.esm.js", + }, + }); + itBundled("default/RuntimeUsesModuleFieldIfMainDoesNotExistCjs", { + files: { + "/entry.js": ` + const { foo } = require('foo'); + console.log(foo); + `, + "/node_modules/foo/package.json": ` + { + "name": "foo", + "version": "2.0.0", + "module": "index.esm.js" + } + `, + "/node_modules/foo/index.cjs.js": ` + module.exports.foo = "hello index.cjs.js"; + `, + "/node_modules/foo/index.esm.js": ` + export const foo = "hello index.esm.js"; + `, + }, + bundling: false, + run: { + stdout: "hello index.esm.js", + }, + }); + itBundled("default/RuntimeUsesModuleFieldIfMainDoesNotExistEsm", { + files: { + "/entry.js": ` + import { foo } from 'foo'; + console.log(foo); + `, + "/node_modules/foo/package.json": ` + { + "name": "foo", + "version": "2.0.0", + "module": "index.esm.js" + } + `, + "/node_modules/foo/index.cjs.js": ` + module.exports.foo = "hello index.cjs.js"; + `, + "/node_modules/foo/index.esm.js": ` + export const foo = "hello index.esm.js"; + `, + }, + bundling: false, + run: { + stdout: "hello index.esm.js", + }, + }); }); -- cgit v1.2.3 From 3d0ffc48cb2608ed696581e10cb082c68b56c6b9 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 4 Jul 2023 12:09:58 +0300 Subject: [install] fix run-time module loading (#3510) - fix version buffer confusion - improve workaround to handle cached modules fixes #3507 --- src/install/install.zig | 84 +++++++++++++++--------------- src/js/out/modules/node/http.js | 2 + src/resolver/resolver.zig | 46 ++++++++--------- test/cli/install/bun-run.test.ts | 108 +++++++++++++++++++++++++++++++++------ 4 files changed, 154 insertions(+), 86 deletions(-) (limited to 'src/resolver') diff --git a/src/install/install.zig b/src/install/install.zig index 87f931291..f6133afce 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -1742,55 +1742,51 @@ pub const PackageManager = struct { pub fn enqueueDependencyToRoot( this: *PackageManager, name: []const u8, - version_buf: []const u8, version: *const Dependency.Version, + version_buf: []const u8, behavior: Dependency.Behavior, ) DependencyToEnqueue { - const str_buf = this.lockfile.buffers.string_bytes.items; - for (this.lockfile.buffers.dependencies.items, 0..) |dependency, dependency_id| { - if (!strings.eqlLong(dependency.name.slice(str_buf), name, true)) continue; - if (!dependency.version.eql(version, str_buf, version_buf)) continue; - return switch (this.lockfile.buffers.resolutions.items[dependency_id]) { - invalid_package_id => .{ - .pending = @truncate(DependencyID, dependency_id), - }, - else => |resolution_id| .{ - .resolution = .{ - .resolution = this.lockfile.packages.items(.resolution)[resolution_id], - .package_id = resolution_id, - }, - }, + const dep_id = @truncate(DependencyID, brk: { + const str_buf = this.lockfile.buffers.string_bytes.items; + for (this.lockfile.buffers.dependencies.items, 0..) |dep, id| { + if (!strings.eqlLong(dep.name.slice(str_buf), name, true)) continue; + if (!dep.version.eql(version, str_buf, version_buf)) continue; + break :brk id; + } + + var builder = this.lockfile.stringBuilder(); + const dummy = Dependency{ + .name = String.init(name, name), + .name_hash = String.Builder.stringHash(name), + .version = version.*, + .behavior = behavior, }; - } + dummy.countWithDifferentBuffers(name, version_buf, @TypeOf(&builder), &builder); - var builder = this.lockfile.stringBuilder(); - const dependency = Dependency{ - .name = String.init(name, name), - .name_hash = String.Builder.stringHash(name), - .version = version.*, - .behavior = behavior, - }; - dependency.countWithDifferentBuffers(name, version_buf, @TypeOf(&builder), &builder); - - builder.allocate() catch |err| return .{ .failure = err }; - - const cloned_dependency = dependency.cloneWithDifferentBuffers(name, version_buf, @TypeOf(&builder), &builder) catch unreachable; - builder.clamp(); - const index = @truncate(DependencyID, this.lockfile.buffers.dependencies.items.len); - this.lockfile.buffers.dependencies.append(this.allocator, cloned_dependency) catch unreachable; - this.lockfile.buffers.resolutions.append(this.allocator, invalid_package_id) catch unreachable; - if (comptime Environment.allow_assert) std.debug.assert(this.lockfile.buffers.dependencies.items.len == this.lockfile.buffers.resolutions.items.len); - this.enqueueDependencyWithMainAndSuccessFn( - index, - &cloned_dependency, - invalid_package_id, - assignRootResolution, - failRootResolution, - ) catch |err| { - return .{ .failure = err }; - }; + builder.allocate() catch |err| return .{ .failure = err }; + + const dep = dummy.cloneWithDifferentBuffers(name, version_buf, @TypeOf(&builder), &builder) catch unreachable; + builder.clamp(); + const index = this.lockfile.buffers.dependencies.items.len; + this.lockfile.buffers.dependencies.append(this.allocator, dep) catch unreachable; + this.lockfile.buffers.resolutions.append(this.allocator, invalid_package_id) catch unreachable; + if (comptime Environment.allow_assert) std.debug.assert(this.lockfile.buffers.dependencies.items.len == this.lockfile.buffers.resolutions.items.len); + break :brk index; + }); + + if (this.lockfile.buffers.resolutions.items[dep_id] == invalid_package_id) { + this.enqueueDependencyWithMainAndSuccessFn( + dep_id, + &this.lockfile.buffers.dependencies.items[dep_id], + invalid_package_id, + assignRootResolution, + failRootResolution, + ) catch |err| { + return .{ .failure = err }; + }; + } - const resolution_id = switch (this.lockfile.buffers.resolutions.items[index]) { + const resolution_id = switch (this.lockfile.buffers.resolutions.items[dep_id]) { invalid_package_id => brk: { this.drainDependencyList(); @@ -1815,7 +1811,7 @@ pub const PackageManager = struct { }, } - break :brk this.lockfile.buffers.resolutions.items[index]; + break :brk this.lockfile.buffers.resolutions.items[dep_id]; }, // we managed to synchronously resolve the dependency else => |pkg_id| pkg_id, diff --git a/src/js/out/modules/node/http.js b/src/js/out/modules/node/http.js index 955c83642..f07dcc2e0 100644 --- a/src/js/out/modules/node/http.js +++ b/src/js/out/modules/node/http.js @@ -1085,6 +1085,8 @@ var tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/, METHODS = [ debug(`${NODE_HTTP_WARNING}\n`, "setMaxIdleHTTPParsers() is a no-op"); }, globalAgent, + ClientRequest, + OutgoingMessage, [Symbol.for("CommonJS")]: 0 }, http_default = defaultObject; export { diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index e1e83ba4f..409df85af 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -1256,14 +1256,8 @@ pub const Resolver = struct { if (check_package) { if (r.opts.polyfill_node_globals) { - var import_path_without_node_prefix = import_path; - const had_node_prefix = import_path_without_node_prefix.len > "node:".len and - strings.eqlComptime(import_path_without_node_prefix[0.."node:".len], "node:"); - - import_path_without_node_prefix = if (had_node_prefix) - import_path_without_node_prefix["node:".len..] - else - import_path_without_node_prefix; + const had_node_prefix = strings.hasPrefixComptime(import_path, "node:"); + const import_path_without_node_prefix = if (had_node_prefix) import_path["node:".len..] else import_path; if (NodeFallbackModules.Map.get(import_path_without_node_prefix)) |*fallback_module| { result.path_pair.primary = fallback_module.path; @@ -1278,7 +1272,7 @@ pub const Resolver = struct { } else if (had_node_prefix or (strings.hasPrefixComptime(import_path_without_node_prefix, "fs") and (import_path_without_node_prefix.len == 2 or - import_path_without_node_prefix[3] == '/'))) + import_path_without_node_prefix[2] == '/'))) { result.path_pair.primary.namespace = "node"; result.path_pair.primary.text = import_path_without_node_prefix; @@ -1698,8 +1692,9 @@ pub const Resolver = struct { // If the source directory doesn't have a node_modules directory, we can // check the global cache directory for a package.json file. var manager = r.getPackageManager(); - var dependency_version: Dependency.Version = .{}; + var dependency_version = Dependency.Version{}; var dependency_behavior = @enumFromInt(Dependency.Behavior, Dependency.Behavior.normal); + var string_buf = esm.version; // const initial_pending_tasks = manager.pending_tasks; var resolved_package_id: Install.PackageID = brk: { @@ -1707,7 +1702,6 @@ pub const Resolver = struct { // and try to look up the dependency from there if (dir_info.package_json_for_dependencies) |package_json| { var dependencies_list: []const Dependency = &[_]Dependency{}; - var string_buf: []const u8 = ""; const resolve_from_lockfile = package_json.package_manager_package_id != Install.invalid_package_id; if (resolve_from_lockfile) { @@ -1723,24 +1717,21 @@ pub const Resolver = struct { } for (dependencies_list, 0..) |dependency, dependency_id| { - const dep_name = dependency.name.slice(string_buf); - if (dep_name.len == esm.name.len) { - if (!strings.eqlLong(dep_name, esm.name, false)) { - continue; - } + if (!strings.eqlLong(dependency.name.slice(string_buf), esm.name, true)) { + continue; + } - dependency_version = dependency.version; - dependency_behavior = dependency.behavior; + dependency_version = dependency.version; + dependency_behavior = dependency.behavior; - if (resolve_from_lockfile) { - const resolutions = &manager.lockfile.packages.items(.resolutions)[package_json.package_manager_package_id]; + if (resolve_from_lockfile) { + const resolutions = &manager.lockfile.packages.items(.resolutions)[package_json.package_manager_package_id]; - // found it! - break :brk resolutions.get(manager.lockfile.buffers.resolutions.items)[dependency_id]; - } - - break; + // found it! + break :brk resolutions.get(manager.lockfile.buffers.resolutions.items)[dependency_id]; } + + break; } } @@ -1770,6 +1761,7 @@ pub const Resolver = struct { if (esm_.?.version.len > 0 and dir_info.enclosing_package_json != null and global_cache.allowVersionSpecifier()) { return .{ .failure = error.VersionSpecifierNotAllowedHere }; } + string_buf = esm.version; dependency_version = Dependency.parse( r.allocator, Semver.String.init(esm.name, esm.name), @@ -1795,6 +1787,7 @@ pub const Resolver = struct { dependency_behavior, &resolved_package_id, dependency_version, + string_buf, )) { .resolution => |res| break :brk res, .pending => |pending| return .{ .pending = pending }, @@ -2073,6 +2066,7 @@ pub const Resolver = struct { behavior: Dependency.Behavior, input_package_id_: *Install.PackageID, version: Dependency.Version, + version_buf: []const u8, ) DependencyToResolve { if (r.debug_logs) |*debug| { debug.addNoteFmt("Enqueueing pending dependency \"{s}@{s}\"", .{ esm.name, esm.version }); @@ -2135,7 +2129,7 @@ pub const Resolver = struct { // All packages are enqueued to the root // because we download all the npm package dependencies - switch (pm.enqueueDependencyToRoot(esm.name, esm.version, &version, behavior)) { + switch (pm.enqueueDependencyToRoot(esm.name, &version, version_buf, behavior)) { .resolution => |result| { input_package_id_.* = result.package_id; return .{ .resolution = result.resolution }; diff --git a/test/cli/install/bun-run.test.ts b/test/cli/install/bun-run.test.ts index 9ab094f08..95f33ebb8 100644 --- a/test/cli/install/bun-run.test.ts +++ b/test/cli/install/bun-run.test.ts @@ -24,7 +24,37 @@ const { minify } = require("uglify-js@3.17.4"); console.log(minify("print(6 * 7)").code); `, ); - const { stdout, stderr, exited } = spawn({ + const { + stdout: stdout1, + stderr: stderr1, + exited: exited1, + } = spawn({ + cmd: [bunExe(), "run", "test.js"], + cwd: run_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env: { + ...env, + BUN_INSTALL_CACHE_DIR: join(run_dir, ".cache"), + }, + }); + expect(stderr1).toBeDefined(); + const err1 = await new Response(stderr1).text(); + expect(err1).toBe(""); + expect(await readdirSorted(run_dir)).toEqual([".cache", "test.js"]); + expect(await readdirSorted(join(run_dir, ".cache"))).toContain("uglify-js"); + expect(await readdirSorted(join(run_dir, ".cache", "uglify-js"))).toEqual(["3.17.4"]); + expect(stdout1).toBeDefined(); + const out1 = await new Response(stdout1).text(); + expect(out1.split(/\r?\n/)).toEqual(["print(42);", ""]); + expect(await exited1).toBe(0); + // Perform `bun test.js` with cached dependencies + const { + stdout: stdout2, + stderr: stderr2, + exited: exited2, + } = spawn({ cmd: [bunExe(), "test.js"], cwd: run_dir, stdout: null, @@ -35,14 +65,16 @@ console.log(minify("print(6 * 7)").code); BUN_INSTALL_CACHE_DIR: join(run_dir, ".cache"), }, }); - expect(stderr).toBeDefined(); - const err = await new Response(stderr).text(); - expect(err).toBe(""); - expect(stdout).toBeDefined(); - const out = await new Response(stdout).text(); - expect(out.split(/\r?\n/)).toEqual(["print(42);", ""]); - expect(await exited).toBe(0); + expect(stderr2).toBeDefined(); + const err2 = await new Response(stderr2).text(); + expect(err2).toBe(""); expect(await readdirSorted(run_dir)).toEqual([".cache", "test.js"]); + expect(await readdirSorted(join(run_dir, ".cache"))).toContain("uglify-js"); + expect(await readdirSorted(join(run_dir, ".cache", "uglify-js"))).toEqual(["3.17.4"]); + expect(stdout2).toBeDefined(); + const out2 = await new Response(stdout2).text(); + expect(out2.split(/\r?\n/)).toEqual(["print(42);", ""]); + expect(await exited2).toBe(0); }); it("should download dependencies to run local file", async () => { @@ -58,7 +90,11 @@ for (const entry of await decompress(Buffer.from(buffer))) { } `, ); - const { stdout, stderr, exited } = spawn({ + const { + stdout: stdout1, + stderr: stderr1, + exited: exited1, + } = spawn({ cmd: [bunExe(), "test.js"], cwd: run_dir, stdout: null, @@ -69,9 +105,49 @@ for (const entry of await decompress(Buffer.from(buffer))) { BUN_INSTALL_CACHE_DIR: join(run_dir, ".cache"), }, }); - expect(stderr).toBeDefined(); - const err = await new Response(stderr).text(); - expect(err).toBe(""); + expect(stderr1).toBeDefined(); + const err1 = await new Response(stderr1).text(); + expect(err1).toBe(""); + expect(await readdirSorted(run_dir)).toEqual([".cache", "test.js"]); + expect(await readdirSorted(join(run_dir, ".cache"))).toContain("decompress"); + expect(await readdirSorted(join(run_dir, ".cache", "decompress"))).toEqual(["4.2.1"]); + expect(await readdirSorted(join(run_dir, ".cache", "decompress", "4.2.1"))).toEqual([ + "index.js", + "license", + "package.json", + "readme.md", + ]); + expect(await file(join(run_dir, ".cache", "decompress", "4.2.1", "index.js")).text()).toContain( + "\nmodule.exports = ", + ); + expect(stdout1).toBeDefined(); + const out1 = await new Response(stdout1).text(); + expect(out1.split(/\r?\n/)).toEqual([ + "directory: package/", + "file: package/index.js", + "file: package/package.json", + "", + ]); + expect(await exited1).toBe(0); + // Perform `bun run test.js` with cached dependencies + const { + stdout: stdout2, + stderr: stderr2, + exited: exited2, + } = spawn({ + cmd: [bunExe(), "run", "test.js"], + cwd: run_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env: { + ...env, + BUN_INSTALL_CACHE_DIR: join(run_dir, ".cache"), + }, + }); + expect(stderr2).toBeDefined(); + const err2 = await new Response(stderr2).text(); + expect(err2).toBe(""); expect(await readdirSorted(run_dir)).toEqual([".cache", "test.js"]); expect(await readdirSorted(join(run_dir, ".cache"))).toContain("decompress"); expect(await readdirSorted(join(run_dir, ".cache", "decompress"))).toEqual(["4.2.1"]); @@ -84,13 +160,13 @@ for (const entry of await decompress(Buffer.from(buffer))) { expect(await file(join(run_dir, ".cache", "decompress", "4.2.1", "index.js")).text()).toContain( "\nmodule.exports = ", ); - expect(stdout).toBeDefined(); - const out = await new Response(stdout).text(); - expect(out.split(/\r?\n/)).toEqual([ + expect(stdout2).toBeDefined(); + const out2 = await new Response(stdout2).text(); + expect(out2.split(/\r?\n/)).toEqual([ "directory: package/", "file: package/index.js", "file: package/package.json", "", ]); - expect(await exited).toBe(0); + expect(await exited2).toBe(0); }); -- cgit v1.2.3