diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index e50166ce949..008c6091d49 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -661,18 +661,23 @@ func ResolveFailureErrorTextSuggestionNotes( if strings.HasPrefix(pkg, "node:") { pkg = pkg[5:] } + + var how string + switch logger.API { + case logger.CLIAPI: + how = "--platform=node" + case logger.JSAPI: + how = "platform: 'node'" + case logger.GoAPI: + how = "Platform: api.PlatformNode" + } + if resolver.BuiltInNodeModules[pkg] { - var how string - switch logger.API { - case logger.CLIAPI: - how = "--platform=node" - case logger.JSAPI: - how = "platform: 'node'" - case logger.GoAPI: - how = "Platform: api.PlatformNode" - } hint = fmt.Sprintf("The package %q wasn't found on the file system but is built into node. "+ "Are you trying to bundle for node? You can use %q to do that, which will remove this error.", path, how) + } else if strings.HasPrefix(pkg, "bun:") { + hint = fmt.Sprintf("The package %q wasn't found on the file system but is built into Bun. "+ + "Bun users should target Node.js with %q which will remove this error.", path, how) } } diff --git a/internal/bundler_tests/bundler_default_test.go b/internal/bundler_tests/bundler_default_test.go index a36a5d3a0c0..c62cc7381fb 100644 --- a/internal/bundler_tests/bundler_default_test.go +++ b/internal/bundler_tests/bundler_default_test.go @@ -2543,6 +2543,31 @@ func TestAutoExternalNode(t *testing.T) { }) } +func TestAutoExternalBun(t *testing.T) { + default_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/entry.js": ` + // These URLs should be external automatically + import {file} from "bun"; + import { Database } from "bun:sqlite"; + + await file("./test.txt"); + const db = new Database(":memory:"); + + // This should be external and should be tree-shaken because it's side-effect free + import "bun:ffi"; + import "bun"; + `, + }, + entryPaths: []string{"/entry.js"}, + options: config.Options{ + Mode: config.ModeBundle, + AbsOutputDir: "/out", + Platform: config.PlatformNode, + }, + }) +} + func TestExternalWithWildcard(t *testing.T) { default_suite.expectBundled(t, bundled{ files: map[string]string{ diff --git a/internal/bundler_tests/snapshots/snapshots_default.txt b/internal/bundler_tests/snapshots/snapshots_default.txt index d76d075f800..70bae8f7d0d 100644 --- a/internal/bundler_tests/snapshots/snapshots_default.txt +++ b/internal/bundler_tests/snapshots/snapshots_default.txt @@ -150,6 +150,15 @@ import "https://example.com/code.js"; import "//example.com/code.js"; import "data:application/javascript;base64,ZXhwb3J0IGRlZmF1bHQgMTIz"; +================================================================================ +TestAutoExternalBun +---------- /out/entry.js ---------- +// entry.js +import { file } from "bun"; +import { Database } from "bun:sqlite"; +await file("./test.txt"); +var db = new Database(":memory:"); + ================================================================================ TestAutoExternalNode ---------- /out/entry.js ---------- diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 77703b0d0ad..3e8e7cca208 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -441,7 +441,18 @@ func (res *Resolver) Resolve(sourceDir string, importPath string, kind ast.Impor if r.debugLogs != nil { r.debugLogs.addNote("Marking this path as implicitly external due to it being a node built-in") } + r.flushDebugLogs(flushDueToSuccess) + return &ResolveResult{ + PathPair: PathPair{Primary: logger.Path{Text: importPath}}, + IsExternal: true, + PrimarySideEffectsData: &SideEffectsData{}, // Mark this with "sideEffects: false" + }, debugMeta + } + if r.options.Platform == config.PlatformNode && (strings.HasPrefix(importPath, "bun:") || importPath == "bun") { + if r.debugLogs != nil { + r.debugLogs.addNote("Marking this path as implicitly external due to it being a Bun built-in") + } r.flushDebugLogs(flushDueToSuccess) return &ResolveResult{ PathPair: PathPair{Primary: logger.Path{Text: importPath}}, @@ -495,6 +506,22 @@ func (res *Resolver) Resolve(sourceDir string, importPath string, kind ast.Impor IsExternal: true, PrimarySideEffectsData: sideEffects, }, debugMeta + + } + + if r.options.Platform == config.PlatformNode && (strings.HasPrefix(importPath, "bun:") || importPath == "bun") { + if r.debugLogs != nil { + r.debugLogs.addNote("Marking this path as implicitly external due to it being a Bun built-in") + } + + // NodeColonPrefixImport check isn't necessary because the "bun:" prefix is always required + + r.flushDebugLogs(flushDueToSuccess) + return &ResolveResult{ + PathPair: PathPair{Primary: logger.Path{Text: importPath}}, + IsExternal: true, + PrimarySideEffectsData: &SideEffectsData{}, + }, debugMeta } if parsed, ok := ParseDataURL(importPath); ok {