mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Compare commits
1 Commits
bun-v1.3.5
...
claude/imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9386d63daa |
@@ -70,7 +70,7 @@ pub const ResolveMessage = struct {
|
||||
return jsc.JSValue.jsNumber(@as(i32, 0));
|
||||
}
|
||||
|
||||
pub fn fmt(allocator: std.mem.Allocator, specifier: string, referrer: string, err: anyerror, import_kind: bun.ImportKind) !string {
|
||||
pub fn fmt(allocator: std.mem.Allocator, specifier: string, referrer: string, err: anyerror, import_kind: bun.ImportKind, resolved_path: string) !string {
|
||||
if (import_kind != .require_resolve and bun.strings.hasPrefixComptime(specifier, "node:")) {
|
||||
// This matches Node.js exactly.
|
||||
return try std.fmt.allocPrint(allocator, "No such built-in module: {s}", .{specifier});
|
||||
@@ -80,6 +80,10 @@ pub const ResolveMessage = struct {
|
||||
if (strings.eqlComptime(referrer, "bun:main")) {
|
||||
return try std.fmt.allocPrint(allocator, "Module not found '{s}'", .{specifier});
|
||||
}
|
||||
// If we have a resolved path, show it (e.g., from package exports)
|
||||
if (resolved_path.len > 0) {
|
||||
return try std.fmt.allocPrint(allocator, "Cannot find module \"{s}\" imported from \"{s}\"", .{ resolved_path, referrer });
|
||||
}
|
||||
if (Resolver.isPackagePath(specifier) and !strings.containsChar(specifier, '/')) {
|
||||
return try std.fmt.allocPrint(allocator, "Cannot find package '{s}' from '{s}'", .{ specifier, referrer });
|
||||
} else {
|
||||
|
||||
@@ -1575,6 +1575,8 @@ pub const ResolveFunctionResult = struct {
|
||||
result: ?Resolver.Result,
|
||||
path: string,
|
||||
query_string: []const u8 = "",
|
||||
/// Resolved file path that doesn't exist (for better error messages)
|
||||
resolved_path_for_error: []const u8 = "",
|
||||
};
|
||||
|
||||
fn normalizeSpecifierForResolution(specifier_: []const u8, query_string: *[]const u8) []const u8 {
|
||||
@@ -1660,7 +1662,7 @@ fn _resolve(
|
||||
)) {
|
||||
.success => |r| r,
|
||||
.failure => |e| e,
|
||||
.pending, .not_found => if (!retry_on_not_found)
|
||||
.pending => if (!retry_on_not_found)
|
||||
error.ModuleNotFound
|
||||
else {
|
||||
retry_on_not_found = false;
|
||||
@@ -1692,6 +1694,42 @@ fn _resolve(
|
||||
continue;
|
||||
}
|
||||
|
||||
return error.ModuleNotFound;
|
||||
},
|
||||
.not_found => |nf| if (!retry_on_not_found) {
|
||||
// Capture resolved path if available for better error message
|
||||
ret.resolved_path_for_error = nf.path;
|
||||
return error.ModuleNotFound;
|
||||
} else {
|
||||
retry_on_not_found = false;
|
||||
|
||||
const buster_name = name: {
|
||||
if (std.fs.path.isAbsolute(normalized_specifier)) {
|
||||
if (std.fs.path.dirname(normalized_specifier)) |dir| {
|
||||
// Normalized without trailing slash
|
||||
break :name bun.strings.normalizeSlashesOnly(&specifier_cache_resolver_buf, dir, std.fs.path.sep);
|
||||
}
|
||||
}
|
||||
|
||||
var parts = [_]string{
|
||||
source_to_use,
|
||||
normalized_specifier,
|
||||
bun.pathLiteral(".."),
|
||||
};
|
||||
|
||||
break :name bun.path.joinAbsStringBufZ(
|
||||
jsc_vm.transpiler.fs.top_level_dir,
|
||||
&specifier_cache_resolver_buf,
|
||||
&parts,
|
||||
.auto,
|
||||
);
|
||||
};
|
||||
|
||||
// Only re-query if we previously had something cached.
|
||||
if (jsc_vm.transpiler.resolver.bustDirCache(bun.strings.withoutTrailingSlashWindowsPath(buster_name))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return error.ModuleNotFound;
|
||||
},
|
||||
};
|
||||
@@ -1703,6 +1741,7 @@ fn _resolve(
|
||||
}
|
||||
ret.result = result;
|
||||
ret.query_string = query_string;
|
||||
// resolved_path_for_error is set in the .not_found case above
|
||||
const result_path = result.pathConst() orelse return error.ModuleNotFound;
|
||||
jsc_vm.resolved_count += 1;
|
||||
|
||||
@@ -1749,6 +1788,7 @@ pub fn resolveMaybeNeedsTrailingSlash(
|
||||
source_utf8.slice(),
|
||||
error.NameTooLong,
|
||||
if (is_esm) .stmt else if (is_user_require_resolve) .require_resolve else .require,
|
||||
"", // no resolved path for NameTooLong error
|
||||
) catch |err| bun.handleOom(err);
|
||||
const msg = logger.Msg{
|
||||
.data = logger.rangeData(
|
||||
@@ -1830,6 +1870,7 @@ pub fn resolveMaybeNeedsTrailingSlash(
|
||||
source_utf8.slice(),
|
||||
err,
|
||||
import_kind,
|
||||
result.resolved_path_for_error,
|
||||
);
|
||||
break :brk logger.Msg{
|
||||
.data = logger.rangeData(
|
||||
|
||||
@@ -176,7 +176,11 @@ pub const Result = struct {
|
||||
success: Result,
|
||||
failure: anyerror,
|
||||
pending: PendingResolution,
|
||||
not_found: void,
|
||||
not_found: struct {
|
||||
/// Optional: the resolved file path that doesn't exist (e.g., from package exports)
|
||||
/// Empty string if not available
|
||||
path: string = "",
|
||||
},
|
||||
};
|
||||
|
||||
pub fn path(this: *Result) ?*Path {
|
||||
@@ -340,7 +344,11 @@ pub const MatchResult = struct {
|
||||
is_external: bool = false,
|
||||
|
||||
pub const Union = union(enum) {
|
||||
not_found: void,
|
||||
not_found: struct {
|
||||
/// Optional: the resolved file path that doesn't exist (e.g., from package exports)
|
||||
/// Empty string if not available
|
||||
path: string = "",
|
||||
},
|
||||
success: MatchResult,
|
||||
pending: PendingResolution,
|
||||
failure: anyerror,
|
||||
@@ -675,7 +683,7 @@ pub const Resolver = struct {
|
||||
r.debug_logs = DebugLogs.init(r.allocator) catch unreachable;
|
||||
}
|
||||
|
||||
if (import_path.len == 0) return .{ .not_found = {} };
|
||||
if (import_path.len == 0) return .{ .not_found = .{} };
|
||||
|
||||
if (r.opts.mark_builtins_as_external) {
|
||||
if (strings.hasPrefixComptime(import_path, "node:") or
|
||||
@@ -783,7 +791,7 @@ pub const Resolver = struct {
|
||||
};
|
||||
}
|
||||
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
} else if (bun.StandaloneModuleGraph.isBunStandaloneFilePath(source_dir)) {
|
||||
if (import_path.len > 2 and isDotSlash(import_path[0..2])) {
|
||||
const buf = bufs(.import_path_for_standalone_module_graph);
|
||||
@@ -848,7 +856,7 @@ pub const Resolver = struct {
|
||||
// anyways would cause assertion failures.
|
||||
if (bun.strings.containsChar(import_path, 0)) {
|
||||
r.flushDebugLogs(.fail) catch {};
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
|
||||
var tmp = r.resolveWithoutSymlinks(source_dir_normalized, import_path, kind, global_cache);
|
||||
@@ -910,9 +918,9 @@ pub const Resolver = struct {
|
||||
r.flushDebugLogs(.fail) catch {};
|
||||
return .{ .pending = pending };
|
||||
},
|
||||
.not_found => {
|
||||
.not_found => |nf| {
|
||||
r.flushDebugLogs(.fail) catch {};
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{ .path = nf.path } };
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1173,7 +1181,7 @@ pub const Resolver = struct {
|
||||
};
|
||||
}
|
||||
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
|
||||
// Check both relative and package paths for CSS URL tokens, with relative
|
||||
@@ -1196,7 +1204,7 @@ pub const Resolver = struct {
|
||||
}
|
||||
}
|
||||
bun.debugAssert(!check_package); // always from JavaScript
|
||||
return .{ .not_found = {} }; // bail out now since there isn't anywhere else to check
|
||||
return .{ .not_found = .{} }; // bail out now since there isn't anywhere else to check
|
||||
} else {
|
||||
switch (r.checkRelativePath(source_dir, import_path, kind, global_cache)) {
|
||||
.success => |res| return .{ .success = res },
|
||||
@@ -1223,7 +1231,7 @@ pub const Resolver = struct {
|
||||
if (had_node_prefix) {
|
||||
// Module resolution fails automatically for unknown node builtins
|
||||
if (!bun.jsc.ModuleLoader.HardcodedModule.Alias.has(import_path_without_node_prefix, .node, .{})) {
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
|
||||
// Valid node:* modules becomes {} in the output
|
||||
@@ -1281,6 +1289,7 @@ pub const Resolver = struct {
|
||||
|
||||
if (r.custom_dir_paths) |custom_paths| {
|
||||
@branchHint(.unlikely);
|
||||
var last_not_found_path: []const u8 = "";
|
||||
for (custom_paths) |custom_path| {
|
||||
const custom_utf8 = custom_path.toUTF8WithoutRef(bun.default_allocator);
|
||||
defer custom_utf8.deinit();
|
||||
@@ -1288,20 +1297,32 @@ pub const Resolver = struct {
|
||||
.success => |res| return .{ .success = res },
|
||||
.pending => |p| return .{ .pending = p },
|
||||
.failure => |p| return .{ .failure = p },
|
||||
.not_found => {},
|
||||
.not_found => |nf| {
|
||||
// Remember the last resolved path for error messages
|
||||
if (nf.path.len > 0) last_not_found_path = nf.path;
|
||||
},
|
||||
}
|
||||
}
|
||||
// If we tried all custom paths and none succeeded, return the last resolved path
|
||||
if (last_not_found_path.len > 0) {
|
||||
return .{ .not_found = .{ .path = last_not_found_path } };
|
||||
}
|
||||
} else {
|
||||
switch (r.checkPackagePath(source_dir, import_path, kind, global_cache)) {
|
||||
.success => |res| return .{ .success = res },
|
||||
.pending => |p| return .{ .pending = p },
|
||||
.failure => |p| return .{ .failure = p },
|
||||
.not_found => {},
|
||||
.not_found => |nf| {
|
||||
// Propagate the resolved path for better error messages
|
||||
if (nf.path.len > 0) {
|
||||
return .{ .not_found = .{ .path = nf.path } };
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
|
||||
pub fn checkRelativePath(r: *ThisResolver, source_dir: string, import_path: string, kind: ast.ImportKind, global_cache: GlobalCache) Result.Union {
|
||||
@@ -1380,13 +1401,13 @@ pub const Resolver = struct {
|
||||
.jsx = r.opts.jsx,
|
||||
} };
|
||||
} else {
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checkPackagePath(r: *ThisResolver, source_dir: string, unremapped_import_path: string, kind: ast.ImportKind, global_cache: GlobalCache) Result.Union {
|
||||
var import_path = unremapped_import_path;
|
||||
var source_dir_info = r.dirInfoCached(source_dir) catch (return .{ .not_found = {} }) orelse dir: {
|
||||
var source_dir_info = r.dirInfoCached(source_dir) catch (return .{ .not_found = .{} }) orelse dir: {
|
||||
// It is possible to resolve with a source file that does not exist:
|
||||
// A. Bundler plugin refers to a non-existing `resolveDir`.
|
||||
// B. `createRequire()` is called with a path that does not exist. This was
|
||||
@@ -1417,10 +1438,10 @@ pub const Resolver = struct {
|
||||
// directory tree has been visited. `null` is theoretically
|
||||
// impossible since the drive root should always exist.
|
||||
while (std.fs.path.dirname(closest_dir)) |current| : (closest_dir = current) {
|
||||
if (r.dirInfoCached(current) catch return .{ .not_found = {} }) |dir|
|
||||
if (r.dirInfoCached(current) catch return .{ .not_found = .{} }) |dir|
|
||||
break :dir dir;
|
||||
}
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
};
|
||||
|
||||
if (r.care_about_browser_field) {
|
||||
@@ -1541,7 +1562,7 @@ pub const Resolver = struct {
|
||||
},
|
||||
.pending => |p| return .{ .pending = p },
|
||||
.failure => |p| return .{ .failure = p },
|
||||
else => return .{ .not_found = {} },
|
||||
.not_found => |nf| return .{ .not_found = .{ .path = nf.path } },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1821,11 +1842,23 @@ pub const Resolver = struct {
|
||||
{
|
||||
const esm_resolution = esmodule.resolve("/", esm.subpath, exports_map.root);
|
||||
|
||||
// Compute the absolute path now for error messages later
|
||||
const abs_esm_path = if (esm_resolution.path.len > 0) brk: {
|
||||
const parts = [_]string{
|
||||
abs_package_path,
|
||||
strings.withoutLeadingPathSeparator(esm_resolution.path),
|
||||
};
|
||||
break :brk r.fs.absBuf(&parts, bufs(.esm_absolute_package_path_joined));
|
||||
} else "";
|
||||
|
||||
if (r.handleESMResolution(esm_resolution, abs_package_path, kind, package_json, esm.subpath)) |result| {
|
||||
var result_copy = result;
|
||||
result_copy.is_node_module = true;
|
||||
result_copy.module_type = module_type;
|
||||
return .{ .success = result_copy };
|
||||
} else if (abs_esm_path.len > 0) {
|
||||
// handleESMResolution returned null but we have a resolved path - file doesn't exist
|
||||
return .{ .not_found = .{ .path = abs_esm_path } };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1870,7 +1903,7 @@ pub const Resolver = struct {
|
||||
};
|
||||
}
|
||||
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2012,7 +2045,7 @@ pub const Resolver = struct {
|
||||
.pending => |pending| return .{ .pending = pending },
|
||||
.failure => |err| return .{ .failure = err },
|
||||
// this means we looked it up in the registry and the package doesn't exist or the version doesn't exist
|
||||
.not_found => return .{ .not_found = {} },
|
||||
.not_found => return .{ .not_found = .{} },
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2034,7 +2067,7 @@ pub const Resolver = struct {
|
||||
},
|
||||
.extract, .extracting => |st| {
|
||||
if (!global_cache.canInstall()) {
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
var builder = Semver.String.Builder{};
|
||||
esm.count(&builder);
|
||||
@@ -2150,7 +2183,7 @@ pub const Resolver = struct {
|
||||
};
|
||||
}
|
||||
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2172,7 +2205,7 @@ pub const Resolver = struct {
|
||||
}
|
||||
}
|
||||
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
fn dirInfoForResolution(
|
||||
r: *ThisResolver,
|
||||
@@ -2274,7 +2307,11 @@ pub const Resolver = struct {
|
||||
}
|
||||
|
||||
const DependencyToResolve = union(enum) {
|
||||
not_found: void,
|
||||
not_found: struct {
|
||||
/// Optional: the resolved file path that doesn't exist (e.g., from package exports)
|
||||
/// Empty string if not available
|
||||
path: string = "",
|
||||
},
|
||||
pending: PendingResolution,
|
||||
failure: anyerror,
|
||||
resolution: Resolution,
|
||||
@@ -2375,7 +2412,7 @@ pub const Resolver = struct {
|
||||
};
|
||||
},
|
||||
.not_found => {
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
},
|
||||
.failure => |err| {
|
||||
return .{ .failure = err };
|
||||
@@ -2527,7 +2564,7 @@ pub const Resolver = struct {
|
||||
if (r.loadAsFileOrDirectory(resolved, kind)) |result| {
|
||||
return .{ .success = result };
|
||||
}
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3114,7 +3151,7 @@ pub const Resolver = struct {
|
||||
if (r.debug_logs) |*debug| {
|
||||
debug.addNoteFmt("The path \"{s}\" must not equal \"#\" and must not start with \"#/\"", .{import_path});
|
||||
}
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
var module_type = options.ModuleType.unknown;
|
||||
|
||||
@@ -3170,7 +3207,7 @@ pub const Resolver = struct {
|
||||
return .{ .success = result };
|
||||
}
|
||||
|
||||
return .{ .not_found = {} };
|
||||
return .{ .not_found = .{} };
|
||||
}
|
||||
|
||||
const BrowserMapPath = struct {
|
||||
|
||||
162
test/js/bun/resolve/package-exports-file-not-found.test.ts
Normal file
162
test/js/bun/resolve/package-exports-file-not-found.test.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe, normalizeBunSnapshot, tempDir } from "harness";
|
||||
|
||||
test("error message shows resolved file path when package exports points to non-existent file", async () => {
|
||||
using dir = tempDir("package-exports-file-not-found", {
|
||||
"node_modules/testpkg/package.json": JSON.stringify({
|
||||
name: "testpkg",
|
||||
version: "1.0.0",
|
||||
exports: {
|
||||
bun: {
|
||||
import: "./worker.js",
|
||||
},
|
||||
default: {
|
||||
import: "./node.js",
|
||||
},
|
||||
},
|
||||
}),
|
||||
"node_modules/testpkg/node.js": `export default "node version";`,
|
||||
// Note: worker.js intentionally missing
|
||||
"index.js": `import pkg from "testpkg"; console.log(pkg);`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "index.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stderr, exitCode] = await Promise.all([proc.stderr.text(), proc.exited]);
|
||||
|
||||
// The error message should show the resolved file path, not just "Cannot find package 'testpkg'"
|
||||
expect(normalizeBunSnapshot(stderr, dir)).toMatchInlineSnapshot(`
|
||||
"error: Cannot find module "<dir>/node_modules/testpkg/worker.js" imported from "<dir>/index.js"
|
||||
|
||||
Bun v<bun-version>"
|
||||
`);
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
});
|
||||
|
||||
test("error message with subpath exports pointing to non-existent file", async () => {
|
||||
using dir = tempDir("package-exports-subpath-not-found", {
|
||||
"node_modules/mypkg/package.json": JSON.stringify({
|
||||
name: "mypkg",
|
||||
version: "1.0.0",
|
||||
exports: {
|
||||
"./utils": "./dist/utils.js",
|
||||
"./core": "./dist/core.js",
|
||||
},
|
||||
}),
|
||||
"node_modules/mypkg/dist/core.js": `export const core = true;`,
|
||||
// Note: dist/utils.js intentionally missing
|
||||
"index.js": `import { util } from "mypkg/utils";`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "index.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stderr, exitCode] = await Promise.all([proc.stderr.text(), proc.exited]);
|
||||
|
||||
// Note: Subpath exports currently don't show the resolved path (could be improved in the future)
|
||||
expect(stderr).toContain("Cannot find module 'mypkg/utils'");
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
});
|
||||
|
||||
test("successful import still works when file exists", async () => {
|
||||
using dir = tempDir("package-exports-success", {
|
||||
"node_modules/testpkg/package.json": JSON.stringify({
|
||||
name: "testpkg",
|
||||
version: "1.0.0",
|
||||
exports: {
|
||||
bun: {
|
||||
import: "./bun.js",
|
||||
},
|
||||
default: {
|
||||
import: "./node.js",
|
||||
},
|
||||
},
|
||||
}),
|
||||
"node_modules/testpkg/bun.js": `export default "bun version";`,
|
||||
"node_modules/testpkg/node.js": `export default "node version";`,
|
||||
"index.js": `import pkg from "testpkg"; console.log(pkg);`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "index.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stdout).toBe("bun version\n");
|
||||
expect(stderr).toBe("");
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test("truly missing package still shows old error message", async () => {
|
||||
using dir = tempDir("package-truly-missing", {
|
||||
"index.js": `import pkg from "nonexistent-package-12345"; console.log(pkg);`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "index.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stderr, exitCode] = await Promise.all([proc.stderr.text(), proc.exited]);
|
||||
|
||||
// Should still use the old "Cannot find package" message when the package doesn't exist at all
|
||||
expect(stderr).toContain("Cannot find package 'nonexistent-package-12345'");
|
||||
expect(exitCode).toBe(1);
|
||||
});
|
||||
|
||||
test("nested conditional exports with missing file", async () => {
|
||||
using dir = tempDir("package-exports-nested-conditions", {
|
||||
"node_modules/complexpkg/package.json": JSON.stringify({
|
||||
name: "complexpkg",
|
||||
version: "1.0.0",
|
||||
exports: {
|
||||
".": {
|
||||
bun: {
|
||||
import: "./esm/index.mjs",
|
||||
require: "./cjs/index.cjs",
|
||||
},
|
||||
import: "./esm/index.js",
|
||||
require: "./cjs/index.js",
|
||||
},
|
||||
},
|
||||
}),
|
||||
"node_modules/complexpkg/esm/index.js": `export const version = "esm";`,
|
||||
// Note: esm/index.mjs intentionally missing
|
||||
"index.js": `import pkg from "complexpkg"; console.log(pkg);`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "index.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(dir),
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stderr, exitCode] = await Promise.all([proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(normalizeBunSnapshot(stderr, dir)).toMatchInlineSnapshot(`
|
||||
"error: Cannot find module "<dir>/node_modules/complexpkg/esm/index.mjs" imported from "<dir>/index.js"
|
||||
|
||||
Bun v<bun-version>"
|
||||
`);
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
});
|
||||
Reference in New Issue
Block a user