Compare commits

...

2 Commits

Author SHA1 Message Date
Claude Bot
a9e588a045 Use tempDirWithFiles harness utility in test
- Remove manual mkdtempSync/writeFileSync usage
- Use tempDirWithFiles for automatic cleanup
- Follows harness testing guidelines
2025-10-10 06:44:27 +00:00
Claude Bot
34a75d1b77 Fix: preserve bun: prefix in errors for invalid modules
Previously, importing an invalid bun: module like `import foo from "bun:apskdaposkdpok"`
would strip the prefix and show a confusing error:
  error: Cannot find package 'apskdaposkdpok' from '<file>'

This was misleading since the user wrote `bun:apskdaposkdpok`, not `apskdaposkdpok`.

The code now validates that a bun: module exists in HardcodedModule.map before
stripping the prefix during transpilation/linking. Invalid modules preserve their
full name, producing clearer errors:
  error: Cannot resolve invalid URL 'bun:apskdaposkdpok' from '<file>'

Implementation:
- Added validation check using HardcodedModule.map.has() before stripping prefix
- Applied consistently across ModuleLoader.zig, linker.zig, and bundle_v2.zig
- Uses ComptimeStringMap for O(1) validation with no runtime overhead
- Clear comments explain the validation purpose

Testing:
- Added regression test: test/regression/issue/invalid-bun-module-prefix.test.ts
- Verified existing test passes: test/js/node/missing-module.test.js
- Confirmed valid bun: modules still work correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 06:22:14 +00:00
4 changed files with 57 additions and 21 deletions

View File

@@ -2554,12 +2554,18 @@ pub const RuntimeTranspilerStore = struct {
}
if (strings.hasPrefixComptime(import_record.path.text, "bun:")) {
import_record.path = Fs.Path.init(import_record.path.text["bun:".len..]);
import_record.path.namespace = "bun";
import_record.is_external_without_side_effects = true;
// Validate and strip the bun: prefix for builtin modules.
// Invalid modules (e.g., "bun:invalid") are left unchanged so the
// resolver can report an error with the full "bun:invalid" name instead
// of the confusing "Cannot find package 'invalid'".
if (HardcodedModule.map.has(import_record.path.text)) {
import_record.path = Fs.Path.init(import_record.path.text["bun:".len..]);
import_record.path.namespace = "bun";
import_record.is_external_without_side_effects = true;
if (strings.eqlComptime(import_record.path.text, "test")) {
import_record.tag = .bun_test;
if (strings.eqlComptime(import_record.path.text, "test")) {
import_record.tag = .bun_test;
}
}
}
}

View File

@@ -3175,17 +3175,21 @@ pub const BundleV2 = struct {
}
if (strings.hasPrefixComptime(import_record.path.text, "bun:")) {
import_record.path = Fs.Path.init(import_record.path.text["bun:".len..]);
import_record.path.namespace = "bun";
import_record.source_index = Index.invalid;
import_record.is_external_without_side_effects = true;
// Validate and strip the bun: prefix for builtin modules.
// Invalid modules are left unchanged for better error messages.
if (jsc.ModuleLoader.HardcodedModule.map.has(import_record.path.text)) {
import_record.path = Fs.Path.init(import_record.path.text["bun:".len..]);
import_record.path.namespace = "bun";
import_record.source_index = Index.invalid;
import_record.is_external_without_side_effects = true;
if (strings.eqlComptime(import_record.path.text, "test")) {
import_record.tag = .bun_test;
if (strings.eqlComptime(import_record.path.text, "test")) {
import_record.tag = .bun_test;
}
// don't link bun
continue;
}
// don't link bun
continue;
}
}

View File

@@ -176,15 +176,19 @@ pub const Linker = struct {
}
if (strings.hasPrefixComptime(import_record.path.text, "bun:")) {
import_record.path = Fs.Path.init(import_record.path.text["bun:".len..]);
import_record.path.namespace = "bun";
// Validate and strip the bun: prefix for builtin modules.
// Invalid modules are left unchanged for better error messages.
if (jsc.ModuleLoader.HardcodedModule.map.has(import_record.path.text)) {
import_record.path = Fs.Path.init(import_record.path.text["bun:".len..]);
import_record.path.namespace = "bun";
if (strings.eqlComptime(import_record.path.text, "test")) {
import_record.tag = .bun_test;
if (strings.eqlComptime(import_record.path.text, "test")) {
import_record.tag = .bun_test;
}
// don't link bun
continue;
}
// don't link bun
continue;
}
// Resolve dynamic imports lazily for perf

View File

@@ -0,0 +1,22 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
test("invalid bun: module should preserve prefix in error", async () => {
const dir = tempDirWithFiles("bun-invalid-module", {
"test.js": 'import foo from "bun:apskdaposkdpok"',
});
const { stderr } = Bun.spawnSync({
cmd: [bunExe(), "test.js"],
cwd: dir,
env: bunEnv,
stderr: "pipe",
});
const output = stderr.toString();
// Error should mention "bun:apskdaposkdpok" not just "apskdaposkdpok"
expect(output).toContain("bun:apskdaposkdpok");
// The error could be "Cannot find package" or "Cannot resolve invalid URL"
// The key is that it shows the full "bun:..." prefix
expect(output).toMatch(/Cannot (find package|resolve invalid URL)/);
});