diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 00000000000000..a21be158eba425
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,26 @@
+name: Lint
+
+on:
+ pull_request:
+ workflow_dispatch:
+
+env:
+ BUN_VERSION: "1.1.38"
+ OXLINT_VERSION: "0.15.0"
+
+jobs:
+ lint-js:
+ name: "Lint JavaScript"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Bun
+ uses: ./.github/actions/setup-bun
+ with:
+ bun-version: ${{ env.BUN_VERSION }}
+ - name: Lint
+ run: bunx oxlint --config oxlint.json --quiet --format github
+
+
+
+
diff --git a/.github/workflows/typos.yml b/.github/workflows/typos.yml
new file mode 100644
index 00000000000000..74c2aaec89a2c6
--- /dev/null
+++ b/.github/workflows/typos.yml
@@ -0,0 +1,19 @@
+name: Typos
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ docs:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Spellcheck
+ uses: crate-ci/typos@v1.29.4
+ with:
+ files: docs/**/*
diff --git a/.typos.toml b/.typos.toml
new file mode 100644
index 00000000000000..7819bc056fe4a8
--- /dev/null
+++ b/.typos.toml
@@ -0,0 +1,2 @@
+[type.md]
+extend-ignore-words-re = ["^ba"]
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e1341d8149381b..03a2b4d663fc78 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -63,7 +63,7 @@ $ brew install llvm@18
```bash#Ubuntu/Debian
$ # LLVM has an automatic installation script that is compatible with all versions of Ubuntu
-$ wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 16 all
+$ wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 18 all
```
```bash#Arch
@@ -71,23 +71,21 @@ $ sudo pacman -S llvm clang lld
```
```bash#Fedora
-$ sudo dnf install 'dnf-command(copr)'
-$ sudo dnf copr enable -y @fedora-llvm-team/llvm17
-$ sudo dnf install llvm16 clang16 lld16-devel
+$ sudo dnf install llvm18 clang18 lld18-devel
```
```bash#openSUSE Tumbleweed
-$ sudo zypper install clang16 lld16 llvm16
+$ sudo zypper install clang18 lld18 llvm18
```
{% /codetabs %}
-If none of the above solutions apply, you will have to install it [manually](https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.6).
+If none of the above solutions apply, you will have to install it [manually](https://github.com/llvm/llvm-project/releases/tag/llvmorg-18.1.8).
Make sure Clang/LLVM 18 is in your path:
```bash
-$ which clang-16
+$ which clang-18
```
If not, run this to manually add it:
@@ -96,13 +94,13 @@ If not, run this to manually add it:
```bash#macOS (Homebrew)
# use fish_add_path if you're using fish
-# use path+="$(brew --prefix llvm@16)/bin" if you are using zsh
-$ export PATH="$(brew --prefix llvm@16)/bin:$PATH"
+# use path+="$(brew --prefix llvm@18)/bin" if you are using zsh
+$ export PATH="$(brew --prefix llvm@18)/bin:$PATH"
```
```bash#Arch
# use fish_add_path if you're using fish
-$ export PATH="$PATH:/usr/lib/llvm16/bin"
+$ export PATH="$PATH:/usr/lib/llvm18/bin"
```
{% /codetabs %}
@@ -163,7 +161,7 @@ The binary will be located at `./build/release/bun` and `./build/release/bun-pro
### Download release build from pull requests
-To save you time spent building a release build locally, we provide a way to run release builds from pull requests. This is useful for manully testing changes in a release build before they are merged.
+To save you time spent building a release build locally, we provide a way to run release builds from pull requests. This is useful for manually testing changes in a release build before they are merged.
To run a release build from a pull request, you can use the `bun-pr` npm package:
@@ -240,7 +238,7 @@ The issue may manifest when initially running `bun setup` as Clang being unable
```
The C++ compiler
- "/usr/bin/clang++-16"
+ "/usr/bin/clang++-18"
is not able to compile a simple test program.
```
diff --git a/bench/hot-module-reloading/css-stress-test/src/index.tsx b/bench/hot-module-reloading/css-stress-test/src/index.tsx
index 5eefb430406aaa..c8b470ec7aa7ea 100644
--- a/bench/hot-module-reloading/css-stress-test/src/index.tsx
+++ b/bench/hot-module-reloading/css-stress-test/src/index.tsx
@@ -1,7 +1,7 @@
import ReactDOM from "react-dom";
import { Main } from "./main";
-const Base = ({}) => {
+const Base = () => {
const name = typeof location !== "undefined" ? decodeURIComponent(location.search.substring(1)) : null;
return ;
};
diff --git a/bench/snippets/urlsearchparams.mjs b/bench/snippets/urlsearchparams.mjs
index 83a874dc5f191a..4663dbfedf2c33 100644
--- a/bench/snippets/urlsearchparams.mjs
+++ b/bench/snippets/urlsearchparams.mjs
@@ -10,7 +10,6 @@ bench("new URLSearchParams(obj)", () => {
"Content-Length": "123",
"User-Agent": "node-fetch/1.0",
"Accept-Encoding": "gzip,deflate",
- "Content-Length": "0",
"Content-Range": "bytes 0-9/10",
});
});
diff --git a/docs/api/s3.md b/docs/api/s3.md
index 46484e7e552539..f467cd21723a8d 100644
--- a/docs/api/s3.md
+++ b/docs/api/s3.md
@@ -367,7 +367,7 @@ If the `S3_*` environment variable is not set, Bun will also check for the `AWS_
These environment variables are read from [`.env` files](/docs/runtime/env) or from the process environment at initialization time (`process.env` is not used for this).
-These defaults are overriden by the options you pass to `s3(credentials)`, `new Bun.S3Client(credentials)`, or any of the methods that accept credentials. So if, for example, you use the same credentials for different buckets, you can set the credentials once in your `.env` file and then pass `bucket: "my-bucket"` to the `s3()` helper function without having to specify all the credentials again.
+These defaults are overridden by the options you pass to `s3(credentials)`, `new Bun.S3Client(credentials)`, or any of the methods that accept credentials. So if, for example, you use the same credentials for different buckets, you can set the credentials once in your `.env` file and then pass `bucket: "my-bucket"` to the `s3()` helper function without having to specify all the credentials again.
### `S3Client` objects
diff --git a/docs/api/sqlite.md b/docs/api/sqlite.md
index 49c84136bb6de9..f9a707d27c30da 100644
--- a/docs/api/sqlite.md
+++ b/docs/api/sqlite.md
@@ -82,7 +82,7 @@ const strict = new Database(
// throws error because of the typo:
const query = strict
.query("SELECT $message;")
- .all({ messag: "Hello world" });
+ .all({ message: "Hello world" });
const notStrict = new Database(
":memory:"
@@ -90,7 +90,7 @@ const notStrict = new Database(
// does not throw error:
notStrict
.query("SELECT $message;")
- .all({ messag: "Hello world" });
+ .all({ message: "Hello world" });
```
### Load via ES module import
diff --git a/docs/api/utils.md b/docs/api/utils.md
index 8c96472f01be70..979e4068511d4b 100644
--- a/docs/api/utils.md
+++ b/docs/api/utils.md
@@ -121,7 +121,7 @@ const id = randomUUIDv7();
A UUID v7 is a 128-bit value that encodes the current timestamp, a random value, and a counter. The timestamp is encoded using the lowest 48 bits, and the random value and counter are encoded using the remaining bits.
-The `timestamp` parameter defaults to the current time in milliseconds. When the timestamp changes, the counter is reset to a psuedo-random integer wrapped to 4096. This counter is atomic and threadsafe, meaning that using `Bun.randomUUIDv7()` in many Workers within the same process running at the same timestamp will not have colliding counter values.
+The `timestamp` parameter defaults to the current time in milliseconds. When the timestamp changes, the counter is reset to a pseudo-random integer wrapped to 4096. This counter is atomic and threadsafe, meaning that using `Bun.randomUUIDv7()` in many Workers within the same process running at the same timestamp will not have colliding counter values.
The final 8 bytes of the UUID are a cryptographically secure random value. It uses the same random number generator used by `crypto.randomUUID()` (which comes from BoringSSL, which in turn comes from the platform-specific system random number generator usually provided by the underlying hardware).
diff --git a/docs/cli/filter.md b/docs/cli/filter.md
index 38a497548b5ab3..62c53658a3da1f 100644
--- a/docs/cli/filter.md
+++ b/docs/cli/filter.md
@@ -41,7 +41,7 @@ $ bun outdated --filter 'pkg-*'
$ bun outdated --filter './'
```
-For more infomation on both these commands, see [`bun install`](https://bun.sh/docs/cli/install) and [`bun outdated`](https://bun.sh/docs/cli/outdated).
+For more information on both these commands, see [`bun install`](https://bun.sh/docs/cli/install) and [`bun outdated`](https://bun.sh/docs/cli/outdated).
## Running scripts with `--filter`
diff --git a/docs/install/lockfile.md b/docs/install/lockfile.md
index 72e11c89442974..88b858bd5361f6 100644
--- a/docs/install/lockfile.md
+++ b/docs/install/lockfile.md
@@ -100,7 +100,7 @@ $ head -n3 bun.lock
"workspaces": {
```
-Once `bun.lock` is generated, Bun will use it for all subsequent installs and updates through commands that read and modify the lockfile. If both lockfiles exist, `bun.lock` will be choosen over `bun.lockb`.
+Once `bun.lock` is generated, Bun will use it for all subsequent installs and updates through commands that read and modify the lockfile. If both lockfiles exist, `bun.lock` will be chosen over `bun.lockb`.
Bun v1.2.0 will switch the default lockfile format to `bun.lock`.
diff --git a/docs/runtime/shell.md b/docs/runtime/shell.md
index 4423746831636c..99983bc12f8e68 100644
--- a/docs/runtime/shell.md
+++ b/docs/runtime/shell.md
@@ -102,7 +102,7 @@ The default handling of non-zero exit codes can be configured by calling `.nothr
import { $ } from "bun";
// shell promises will not throw, meaning you will have to
// check for `exitCode` manually on every shell command.
-$.nothrow(); // equivilent to $.throws(false)
+$.nothrow(); // equivalent to $.throws(false)
// default behavior, non-zero exit codes will throw an error
$.throws(true);
diff --git a/oxlint.json b/oxlint.json
new file mode 100644
index 00000000000000..f596253eb7ed58
--- /dev/null
+++ b/oxlint.json
@@ -0,0 +1,51 @@
+{
+ "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/refs/heads/main/npm/oxlint/configuration_schema.json",
+ "categories": {
+ "correctness": "warn" // TODO: gradually fix bugs and turn this to error
+ },
+ "rules": {
+ "const-comparisons": "off", // TODO: there's a bug when comparing private identifiers. Re-enable once it's fixed.
+ "no-cond-assign": "error",
+ "no-const-assign": "error",
+ "no-debugger": "error",
+ "no-dupe-class-members": "error",
+ "no-dupe-keys": "error",
+ "no-empty-pattern": "error",
+ "import/no-duplicates": "error",
+
+ "no-useless-escape": "off" // there's a lot of these. Should be fixed eventually.
+ },
+ "ignorePatterns": [
+ "vendor",
+ "build",
+ "test/snapshots/**",
+ "bench/react-hello-world/*.js",
+
+ "test/js/node/**/parallel/**",
+ "test/js/node/test/fixtures", // full of JS with intentional syntax errors
+ "test/snippets/**",
+ "test/regression/issue/14477/*.tsx",
+ "test/js/**/*bad.js",
+ "test/bundler/transpiler/decorators.test.ts", // uses `arguments` as decorator
+ "test/bundler/native-plugin.test.ts", // parser doesn't handle import metadata
+ "test/bundler/transpiler/with-statement-works.js" // parser doesn't allow `with` statement
+ ],
+ "overrides": [
+ {
+ "files": ["test/**", "examples/**", "packages/bun-internal/test/runners/**"],
+ "rules": {
+ "no-unused-vars": "off",
+ "no-unused-private-class-members": "off",
+ "no-unnecessary-await": "off"
+ }
+ },
+ {
+ "files": ["test/**", "bench/**"],
+ "rules": {
+ "no-shadow-restricted-names": "off",
+ "no-empty-file": "off",
+ "no-unnecessary-await": "off"
+ }
+ }
+ ]
+}
diff --git a/package.json b/package.json
index b0cfe5173790f7..4df6d400fe0717 100644
--- a/package.json
+++ b/package.json
@@ -49,8 +49,8 @@
"fmt": "bun run prettier",
"fmt:cpp": "bun run clang-format",
"fmt:zig": "bun run zig-format",
- "lint": "eslint './**/*.d.ts' --cache",
- "lint:fix": "eslint './**/*.d.ts' --cache --fix",
+ "lint": "oxlint --config oxlint.json",
+ "lint:fix": "oxlint --config oxlint.json --fix",
"test": "node scripts/runner.node.mjs --exec-path ./build/debug/bun-debug",
"test:release": "node scripts/runner.node.mjs --exec-path ./build/release/bun",
"banned": "bun packages/bun-internal-test/src/linter.ts",
diff --git a/packages/bun-wasm/index.ts b/packages/bun-wasm/index.ts
index 0a8fdc7e6f5870..f07be12c5017d3 100644
--- a/packages/bun-wasm/index.ts
+++ b/packages/bun-wasm/index.ts
@@ -66,25 +66,20 @@ const Wasi = {
return Date.now();
},
environ_sizes_get() {
- debugger;
return 0;
},
environ_get(__environ: unknown, environ_buf: unknown) {
- debugger;
return 0;
},
fd_close(fd: number) {
- debugger;
return 0;
},
proc_exit() {},
fd_seek(fd: number, offset_bigint: bigint, whence: unknown, newOffset: unknown) {
- debugger;
},
fd_write(fd: unknown, iov: unknown, iovcnt: unknown, pnum: unknown) {
- debugger;
},
};
diff --git a/src/bun.js/RuntimeTranspilerCache.zig b/src/bun.js/RuntimeTranspilerCache.zig
index ad40bc73998cf7..3eac90cbf6aeae 100644
--- a/src/bun.js/RuntimeTranspilerCache.zig
+++ b/src/bun.js/RuntimeTranspilerCache.zig
@@ -9,7 +9,8 @@
/// Version 10: Constant folding for ''.charCodeAt(n)
/// Version 11: Fix \uFFFF printing regression
/// Version 12: "use strict"; makes it CommonJS if we otherwise don't know which one to pick.
-const expected_version = 12;
+/// Version 13: Hoist `import.meta.require` definition, see #15738
+const expected_version = 13;
const bun = @import("root").bun;
const std = @import("std");
diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig
index 7eb446bea22b80..77d642b3f94652 100644
--- a/src/bun.js/api/JSTranspiler.zig
+++ b/src/bun.js/api/JSTranspiler.zig
@@ -462,10 +462,6 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
}
transpiler.runtime.allow_runtime = false;
- transpiler.runtime.use_import_meta_require = switch (transpiler.transform.target orelse .browser) {
- .bun, .bun_macro => true,
- else => false,
- };
if (try object.getTruthy(globalThis, "macro")) |macros| {
macros: {
diff --git a/src/bun.js/api/sockets.classes.ts b/src/bun.js/api/sockets.classes.ts
index 7d30de0344487a..9b03d24a9d5729 100644
--- a/src/bun.js/api/sockets.classes.ts
+++ b/src/bun.js/api/sockets.classes.ts
@@ -182,9 +182,6 @@ function generate(ssl) {
fn: "reload",
length: 1,
},
- bytesWritten: {
- getter: "getBytesWritten",
- },
setServername: {
fn: "setServername",
length: 1,
diff --git a/src/bun.js/webcore/S3Stat.zig b/src/bun.js/webcore/S3Stat.zig
index 56353074772bbd..53deb25bcb4c94 100644
--- a/src/bun.js/webcore/S3Stat.zig
+++ b/src/bun.js/webcore/S3Stat.zig
@@ -1,5 +1,5 @@
const bun = @import("../../bun.zig");
-const JSC = @import("../../JSC.zig");
+const JSC = @import("../../jsc.zig");
pub const S3Stat = struct {
const log = bun.Output.scoped(.S3Stat, false);
diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig
index 660cff180ddbd9..99d7e0a03a7c27 100644
--- a/src/bundler/bundle_v2.zig
+++ b/src/bundler/bundle_v2.zig
@@ -327,10 +327,15 @@ fn genericPathWithPrettyInitialized(path: Fs.Path, target: options.Target, top_l
// TODO: outbase
var buf: bun.PathBuffer = undefined;
+ const is_node = bun.strings.eqlComptime(path.namespace, "node");
+ if (is_node and bun.strings.hasPrefixComptime(path.text, NodeFallbackModules.import_path)) {
+ return path;
+ }
+
// "file" namespace should use the relative file path for its display name.
// the "node" namespace is also put through this code path so that the
// "node:" prefix is not emitted.
- if (path.isFile() or bun.strings.eqlComptime(path.namespace, "node")) {
+ if (path.isFile() or is_node) {
const rel = bun.path.relativePlatform(top_level_dir, path.text, .loose, false);
var path_clone = path;
// stack-allocated temporary is not leaked because dupeAlloc on the path will
@@ -3464,10 +3469,12 @@ pub const ParseTask = struct {
// and then that is either replaced with the module itself, or an import to the
// runtime here.
const runtime_require = switch (target) {
- // __require is intentionally not implemented here, as we
- // always inline 'import.meta.require' and 'import.meta.require.resolve'
- // Omitting it here acts as an extra assertion.
- .bun, .bun_macro => "",
+ // Previously, Bun inlined `import.meta.require` at all usages. This broke
+ // code that called `fn.toString()` and parsed the code outside a module
+ // context.
+ .bun, .bun_macro =>
+ \\export var __require = import.meta.require;
+ ,
.node =>
\\import { createRequire } from "node:module";
@@ -4406,10 +4413,9 @@ pub const ParseTask = struct {
opts.macro_context = &this.data.macro_context;
opts.package_version = task.package_version;
- opts.features.auto_polyfill_require = output_format == .esm and !target.isBun();
+ opts.features.auto_polyfill_require = output_format == .esm;
opts.features.allow_runtime = !source.index.isRuntime();
opts.features.unwrap_commonjs_to_esm = output_format == .esm and FeatureFlags.unwrap_commonjs_to_esm;
- opts.features.use_import_meta_require = target.isBun();
opts.features.top_level_await = output_format == .esm or output_format == .internal_bake_dev;
opts.features.auto_import_jsx = task.jsx.parse and transpiler.options.auto_import_jsx;
opts.features.trim_unused_imports = loader.isTypeScript() or (transpiler.options.trim_unused_imports orelse false);
@@ -7000,18 +7006,17 @@ pub const LinkerContext = struct {
try this.graph.generateSymbolImportAndUse(source_index, 0, module_ref, 1, Index.init(source_index));
// If this is a .napi addon and it's not node, we need to generate a require() call to the runtime
- if (expr.data == .e_call and expr.data.e_call.target.data == .e_require_call_target and
+ if (expr.data == .e_call and
+ expr.data.e_call.target.data == .e_require_call_target and
// if it's commonjs, use require()
- this.options.output_format != .cjs and
- // if it's esm and bun, use import.meta.require(). the code for __require is not injected into the bundle.
- !this.options.target.isBun())
+ this.options.output_format != .cjs)
{
- this.graph.generateRuntimeSymbolImportAndUse(
+ try this.graph.generateRuntimeSymbolImportAndUse(
source_index,
Index.part(1),
"__require",
1,
- ) catch {};
+ );
}
},
else => {
@@ -7752,11 +7757,9 @@ pub const LinkerContext = struct {
continue;
} else {
-
// We should use "__require" instead of "require" if we're not
// generating a CommonJS output file, since it won't exist otherwise.
- // Disabled for target bun because `import.meta.require` will be inlined.
- if (shouldCallRuntimeRequire(output_format) and !this.resolver.opts.target.isBun()) {
+ if (shouldCallRuntimeRequire(output_format)) {
runtime_require_uses += 1;
}
@@ -9386,7 +9389,7 @@ pub const LinkerContext = struct {
var runtime_members = &runtime_scope.members;
const toCommonJSRef = c.graph.symbols.follow(runtime_members.get("__toCommonJS").?.ref);
const toESMRef = c.graph.symbols.follow(runtime_members.get("__toESM").?.ref);
- const runtimeRequireRef = if (c.resolver.opts.target.isBun()) null else c.graph.symbols.follow(runtime_members.get("__require").?.ref);
+ const runtimeRequireRef = if (c.options.output_format == .cjs) null else c.graph.symbols.follow(runtime_members.get("__require").?.ref);
const result = c.generateCodeForFileInChunkJS(
&buffer_writer,
@@ -9855,10 +9858,11 @@ pub const LinkerContext = struct {
var runtime_members = &runtime_scope.members;
const toCommonJSRef = c.graph.symbols.follow(runtime_members.get("__toCommonJS").?.ref);
const toESMRef = c.graph.symbols.follow(runtime_members.get("__toESM").?.ref);
- const runtimeRequireRef = if (c.resolver.opts.target.isBun() or c.options.output_format == .cjs) null else c.graph.symbols.follow(runtime_members.get("__require").?.ref);
+ const runtimeRequireRef = if (c.options.output_format == .cjs) null else c.graph.symbols.follow(runtime_members.get("__require").?.ref);
{
const print_options = js_printer.Options{
+ .bundling = true,
.indent = .{},
.has_run_symbol_renamer = true,
@@ -10015,7 +10019,7 @@ pub const LinkerContext = struct {
switch (c.options.output_format) {
.internal_bake_dev => {
- const start = bun.bake.getHmrRuntime(if (c.options.target.isBun()) .server else .client);
+ const start = bun.bake.getHmrRuntime(if (c.options.target.isServerSide()) .server else .client);
j.pushStatic(start);
line_offset.advance(start);
},
@@ -12513,6 +12517,7 @@ pub const LinkerContext = struct {
};
const print_options = js_printer.Options{
+ .bundling = true,
// TODO: IIFE
.indent = .{},
.commonjs_named_exports = ast.commonjs_named_exports,
@@ -14099,7 +14104,7 @@ pub const LinkerContext = struct {
},
) catch unreachable;
}
- } else if (c.resolver.opts.target == .browser and JSC.HardcodedModule.Aliases.has(next_source.path.pretty, .browser)) {
+ } else if (c.resolver.opts.target == .browser and bun.strings.hasPrefixComptime(next_source.path.text, NodeFallbackModules.import_path)) {
c.log.addRangeErrorFmtWithNote(
source,
r,
diff --git a/src/js/internal/primordials.js b/src/js/internal/primordials.js
index 565a056a60de3f..bd54b95070707b 100644
--- a/src/js/internal/primordials.js
+++ b/src/js/internal/primordials.js
@@ -112,7 +112,6 @@ export default {
DatePrototypeToString: uncurryThis(Date.prototype.toString),
ErrorCaptureStackTrace,
ErrorPrototypeToString: uncurryThis(Error.prototype.toString),
- FunctionPrototypeToString: uncurryThis(Function.prototype.toString),
JSONStringify: JSON.stringify,
MapPrototypeGetSize: getGetter(Map, "size"),
MapPrototypeEntries: uncurryThis(Map.prototype.entries),
diff --git a/src/js/node/http2.ts b/src/js/node/http2.ts
index a48c0027bcfa7c..f480b4212bf40f 100644
--- a/src/js/node/http2.ts
+++ b/src/js/node/http2.ts
@@ -2419,7 +2419,7 @@ class ServerHttp2Session extends Http2Session {
// throwNotImplemented("ServerHttp2Stream.prototype.origin()");
}
- constructor(socket: TLSSocket | Socket, options?: Http2ConnectOptions, server: Http2Server) {
+ constructor(socket: TLSSocket | Socket, options?: Http2ConnectOptions, server?: Http2Server) {
super();
this[kServer] = server;
this.#connected = true;
diff --git a/src/js/node/net.ts b/src/js/node/net.ts
index 1fad0650748a0a..2e7e28e7e1f260 100644
--- a/src/js/node/net.ts
+++ b/src/js/node/net.ts
@@ -1342,7 +1342,6 @@ class Server extends EventEmitter {
});
} else {
this._handle = Bun.listen({
- exclusive,
port,
hostname,
tls,
diff --git a/src/js_ast.zig b/src/js_ast.zig
index fe7456598e30ba..4263c9a60c8517 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -6917,8 +6917,6 @@ pub const Ast = struct {
wrapper_ref: Ref = Ref.None,
require_ref: Ref = Ref.None,
- prepend_part: ?Part = null,
-
// These are used when bundling. They are filled in during the parser pass
// since we already have to traverse the AST then anyway and the parser pass
// is conveniently fully parallelized.
diff --git a/src/js_parser.zig b/src/js_parser.zig
index 9d3803f7519ea9..62e6515a8eb5d0 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -18863,22 +18863,20 @@ fn NewParser_(
}
},
.e_import_meta => {
- // Make `import.meta.url` side effect free.
- if (strings.eqlComptime(name, "url")) {
- return p.newExpr(
- E.Dot{
- .target = target,
- .name = name,
- .name_loc = name_loc,
- .can_be_removed_if_unused = true,
- },
- target.loc,
- );
- }
-
if (strings.eqlComptime(name, "main")) {
return p.valueForImportMetaMain(false, target.loc);
}
+
+ // Make all property accesses on `import.meta.url` side effect free.
+ return p.newExpr(
+ E.Dot{
+ .target = target,
+ .name = name,
+ .name_loc = name_loc,
+ .can_be_removed_if_unused = true,
+ },
+ target.loc,
+ );
},
.e_require_call_target => {
if (strings.eqlComptime(name, "main")) {
@@ -23749,8 +23747,11 @@ fn NewParser_(
.force_cjs_to_esm = p.unwrap_all_requires or exports_kind == .esm_with_dynamic_fallback_from_cjs,
.uses_module_ref = p.symbols.items[p.module_ref.inner_index].use_count_estimate > 0,
.uses_exports_ref = p.symbols.items[p.exports_ref.inner_index].use_count_estimate > 0,
- .uses_require_ref = p.runtime_imports.__require != null and
- p.symbols.items[p.runtime_imports.__require.?.inner_index].use_count_estimate > 0,
+ .uses_require_ref = if (p.options.bundle)
+ p.runtime_imports.__require != null and
+ p.symbols.items[p.runtime_imports.__require.?.inner_index].use_count_estimate > 0
+ else
+ p.symbols.items[p.require_ref.inner_index].use_count_estimate > 0,
.commonjs_module_exports_assigned_deoptimized = p.commonjs_module_exports_assigned_deoptimized,
.top_level_await_keyword = p.top_level_await_keyword,
.commonjs_named_exports = p.commonjs_named_exports,
diff --git a/src/js_printer.zig b/src/js_printer.zig
index 3116a92532ef40..fe3e6d4cd44909 100644
--- a/src/js_printer.zig
+++ b/src/js_printer.zig
@@ -434,6 +434,7 @@ pub const SourceMapHandler = struct {
};
pub const Options = struct {
+ bundling: bool = false,
transform_imports: bool = true,
to_commonjs_ref: Ref = Ref.None,
to_esm_ref: Ref = Ref.None,
@@ -475,22 +476,6 @@ pub const Options = struct {
// const_values: Ast.ConstValuesMap = .{},
ts_enums: Ast.TsEnumsMap = .{},
- // TODO: remove this
- // The reason for this is:
- // 1. You're bundling a React component
- // 2. jsx auto imports are prepended to the list of parts
- // 3. The AST modification for bundling only applies to the final part
- // 4. This means that it will try to add a toplevel part which is not wrapped in the arrow function, which is an error
- // TypeError: $30851277 is not a function. (In '$30851277()', '$30851277' is undefined)
- // at (anonymous) (0/node_modules.server.e1b5ffcd183e9551.jsb:1463:21)
- // at #init_react/jsx-dev-runtime.js (0/node_modules.server.e1b5ffcd183e9551.jsb:1309:8)
- // at (esm) (0/node_modules.server.e1b5ffcd183e9551.jsb:1480:30)
-
- // The temporary fix here is to tag a stmts ptr as the one we want to prepend to
- // Then, when we're JUST about to print it, we print the body of prepend_part_value first
- prepend_part_key: ?*anyopaque = null,
- prepend_part_value: ?*js_ast.Part = null,
-
// If we're writing out a source map, this table of line start indices lets
// us do binary search on to figure out what line a given AST node came from
line_offset_tables: ?SourceMap.LineOffsetTable.List = null,
@@ -1836,9 +1821,7 @@ fn NewPrinter(
p.print("(");
}
- if (module_type == .esm and is_bun_platform) {
- p.print("import.meta.require");
- } else if (p.options.require_ref) |ref| {
+ if (p.options.require_ref) |ref| {
p.printSymbol(ref);
} else {
p.print("require");
@@ -2072,7 +2055,9 @@ fn NewPrinter(
//
// This is currently only used in Bun's runtime for CommonJS modules
// referencing import.meta
- if (comptime Environment.allow_assert)
+ //
+ // TODO: This assertion trips when using `import.meta` with `--format=cjs`
+ if (comptime Environment.isDebug)
bun.assert(p.options.module_type == .cjs);
p.printSymbol(p.options.import_meta_ref);
@@ -2277,9 +2262,7 @@ fn NewPrinter(
p.printSpaceBeforeIdentifier();
p.addSourceMapping(expr.loc);
- if (p.options.module_type == .esm and is_bun_platform) {
- p.print("import.meta.require.main");
- } else if (p.options.require_ref) |require_ref| {
+ if (p.options.require_ref) |require_ref| {
p.printSymbol(require_ref);
p.print(".main");
} else {
@@ -2290,9 +2273,7 @@ fn NewPrinter(
p.printSpaceBeforeIdentifier();
p.addSourceMapping(expr.loc);
- if (p.options.module_type == .esm and is_bun_platform) {
- p.print("import.meta.require");
- } else if (p.options.require_ref) |require_ref| {
+ if (p.options.require_ref) |require_ref| {
p.printSymbol(require_ref);
} else {
p.print("require");
@@ -2302,9 +2283,7 @@ fn NewPrinter(
p.printSpaceBeforeIdentifier();
p.addSourceMapping(expr.loc);
- if (p.options.module_type == .esm and is_bun_platform) {
- p.print("import.meta.require.resolve");
- } else if (p.options.require_ref) |require_ref| {
+ if (p.options.require_ref) |require_ref| {
p.printSymbol(require_ref);
p.print(".resolve");
} else {
@@ -2331,9 +2310,7 @@ fn NewPrinter(
p.printSpaceBeforeIdentifier();
- if (p.options.module_type == .esm and is_bun_platform) {
- p.print("import.meta.require.resolve");
- } else if (p.options.require_ref) |require_ref| {
+ if (p.options.require_ref) |require_ref| {
p.printSymbol(require_ref);
p.print(".resolve");
} else {
@@ -2550,15 +2527,6 @@ fn NewPrinter(
p.printWhitespacer(ws(" => "));
var wasPrinted = false;
-
- // This is more efficient than creating a new Part just for the JSX auto imports when bundling
- if (comptime rewrite_esm_to_cjs) {
- if (@intFromPtr(p.options.prepend_part_key) > 0 and @intFromPtr(e.body.stmts.ptr) == @intFromPtr(p.options.prepend_part_key)) {
- p.printTwoBlocksInOne(e.body.loc, e.body.stmts, p.options.prepend_part_value.?.stmts);
- wasPrinted = true;
- }
- }
-
if (e.body.stmts.len == 1 and e.prefer_expr) {
switch (e.body.stmts[0].data) {
.s_return => {
@@ -5803,14 +5771,24 @@ pub fn printAst(
defer {
imported_module_ids_list = printer.imported_module_ids;
}
- if (tree.prepend_part) |part| {
- for (part.stmts) |stmt| {
- try printer.printStmt(stmt);
- if (printer.writer.getError()) {} else |err| {
- return err;
- }
- printer.printSemicolonIfNeeded();
- }
+
+ if (!opts.bundling and
+ tree.uses_require_ref and
+ tree.exports_kind == .esm and
+ opts.target == .bun)
+ {
+ // Hoist the `var {require}=import.meta;` declaration. Previously,
+ // `import.meta.require` was inlined into transpiled files, which
+ // meant calling `func.toString()` on a function with `require`
+ // would observe `import.meta.require` inside of the source code.
+ // Normally, Bun doesn't guarantee `Function.prototype.toString`
+ // will match the untranspiled source code, but in this case the new
+ // code is not valid outside of an ES module (eg, in `new Function`)
+ // https://github.com/oven-sh/bun/issues/15738#issuecomment-2574283514
+ //
+ // This is never a symbol collision because `uses_require_ref` means
+ // `require` must be an unbound variable.
+ printer.print("var {require}=import.meta;");
}
for (tree.parts.slice()) |part| {
@@ -6089,15 +6067,6 @@ pub fn printCommonJS(
imported_module_ids_list = printer.imported_module_ids;
}
- if (tree.prepend_part) |part| {
- for (part.stmts) |stmt| {
- try printer.printStmt(stmt);
- if (printer.writer.getError()) {} else |err| {
- return err;
- }
- printer.printSemicolonIfNeeded();
- }
- }
for (tree.parts.slice()) |part| {
for (part.stmts) |stmt| {
try printer.printStmt(stmt);
diff --git a/src/runtime.zig b/src/runtime.zig
index c7309a280e9474..3585fe11b04fed 100644
--- a/src/runtime.zig
+++ b/src/runtime.zig
@@ -191,10 +191,6 @@ pub const Runtime = struct {
trim_unused_imports: bool = false,
- /// Use `import.meta.require()` instead of require()?
- /// This is only supported with --target=bun
- use_import_meta_require: bool = false,
-
/// Allow runtime usage of require(), converting `require` into `__require`
auto_polyfill_require: bool = false,
@@ -240,7 +236,6 @@ pub const Runtime = struct {
.dead_code_elimination,
.set_breakpoint_on_first_line,
.trim_unused_imports,
- .use_import_meta_require,
.dont_bundle_twice,
.commonjs_at_runtime,
.emit_decorator_metadata,
diff --git a/src/transpiler.zig b/src/transpiler.zig
index b0078da1938f88..ead4afecc53187 100644
--- a/src/transpiler.zig
+++ b/src/transpiler.zig
@@ -1083,6 +1083,7 @@ pub const Transpiler = struct {
source,
false,
.{
+ .bundling = false,
.runtime_imports = ast.runtime_imports,
.require_ref = ast.require_ref,
.css_import_behavior = transpiler.options.cssImportBehavior(),
@@ -1105,6 +1106,7 @@ pub const Transpiler = struct {
source,
false,
.{
+ .bundling = false,
.runtime_imports = ast.runtime_imports,
.require_ref = ast.require_ref,
.source_map_handler = source_map_context,
@@ -1128,6 +1130,7 @@ pub const Transpiler = struct {
source,
is_bun,
.{
+ .bundling = false,
.runtime_imports = ast.runtime_imports,
.require_ref = ast.require_ref,
.css_import_behavior = transpiler.options.cssImportBehavior(),
@@ -1362,7 +1365,6 @@ pub const Transpiler = struct {
opts.features.allow_runtime = transpiler.options.allow_runtime;
opts.features.set_breakpoint_on_first_line = this_parse.set_breakpoint_on_first_line;
opts.features.trim_unused_imports = transpiler.options.trim_unused_imports orelse loader.isTypeScript();
- opts.features.use_import_meta_require = target.isBun();
opts.features.no_macros = transpiler.options.no_macros;
opts.features.runtime_transpiler_cache = this_parse.runtime_transpiler_cache;
opts.transform_only = transpiler.options.transform_only;
diff --git a/test/bundler/bundler_html.test.ts b/test/bundler/bundler_html.test.ts
index 6314971925e8f0..1a2d57334e5d9e 100644
--- a/test/bundler/bundler_html.test.ts
+++ b/test/bundler/bundler_html.test.ts
@@ -813,7 +813,6 @@ body {
// Test that sourcemap comments are not included in HTML and CSS files
itBundled("html/no-sourcemap-comments", {
outdir: "out/",
- sourceMap: "linked",
files: {
"/index.html": `
diff --git a/test/bundler/esbuild/default.test.ts b/test/bundler/esbuild/default.test.ts
index 39847ec22af728..785a5eca752696 100644
--- a/test/bundler/esbuild/default.test.ts
+++ b/test/bundler/esbuild/default.test.ts
@@ -1,4 +1,5 @@
import assert from "assert";
+import path from "path";
import { describe, expect } from "bun:test";
import { osSlashes } from "harness";
import { dedent, ESBUILD_PATH, itBundled } from "../expectBundled";
@@ -2797,20 +2798,25 @@ describe("bundler", () => {
},
bundling: false,
});
- itBundled("default/ImportMetaCommonJS", {
+ itBundled("default/ImportMetaCommonJS", ({ root }) => ({
+ // Currently Bun emits `import.meta` instead of correctly
+ // polyfilling its properties.
+ todo: true,
files: {
"/entry.js": `
- import fs from "fs";
- import { fileURLToPath } from "url";
- console.log(fs.existsSync(fileURLToPath(import.meta.url)), fs.existsSync(import.meta.path));
+ import fs from "fs";
+ import { fileURLToPath } from "url";
+ console.log(fileURLToPath(import.meta.url) === ${JSON.stringify(path.join(root, "out.cjs"))});
`,
},
+ outfile: "out.cjs",
format: "cjs",
target: "node",
run: {
+ runtime: "node",
stdout: "true true",
},
- });
+ }));
itBundled("default/ImportMetaES6", {
files: {
"/entry.js": `console.log(import.meta.url, import.meta.path)`,
diff --git a/test/bundler/esbuild/hello.ts b/test/bundler/esbuild/hello.ts
new file mode 100644
index 00000000000000..483b7660f686de
--- /dev/null
+++ b/test/bundler/esbuild/hello.ts
@@ -0,0 +1,3 @@
+import fs from "fs";
+import { fileURLToPath } from "url";
+console.log(fs.existsSync(fileURLToPath(import.meta.url)), fs.existsSync(import.meta.path));
diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts
index 36c52021f3397a..f229a603670b3f 100644
--- a/test/bundler/expectBundled.ts
+++ b/test/bundler/expectBundled.ts
@@ -1589,6 +1589,11 @@ for (const [key, blob] of build.outputs) {
// no idea why this logs. ¯\_(ツ)_/¯
result = result.replace(/\[Event_?Loop\] enqueueTaskConcurrent\(RuntimeTranspilerStore\)\n/gi, "");
+ // when the inspector runs (can be due to VSCode extension), there is
+ // a bug that in debug modes the console logs extra stuff
+ if (name === "stderr" && process.env.BUN_INSPECT_CONNECT_TO) {
+ result = result.replace(/(?:^|\n)\/[^\n]*: CONSOLE LOG[^\n]*(\n|$)/g, "$1").trim();
+ }
if (typeof expected === "string") {
expected = dedent(expected).trim();
diff --git a/test/bundler/native-plugin.test.ts b/test/bundler/native-plugin.test.ts
index 8a09905eaf7601..942461228f6896 100644
--- a/test/bundler/native-plugin.test.ts
+++ b/test/bundler/native-plugin.test.ts
@@ -74,6 +74,11 @@ values;`,
await Bun.$`${bunExe()} i && ${bunExe()} build:napi`.env(bunEnv).cwd(tempdir);
});
+ beforeEach(() => {
+ const tempdir2 = tempDirWithFiles("native-plugins", {});
+ process.chdir(tempdir2);
+ });
+
afterEach(async () => {
await Bun.$`rm -rf ${outdir}`;
process.chdir(cwd);
@@ -672,6 +677,7 @@ console.log(JSON.stringify(json))
},
},
],
+ throw: true,
});
expect(result.success).toBeTrue();
diff --git a/test/bundler/transpiler/function-tostring-require.test.ts b/test/bundler/transpiler/function-tostring-require.test.ts
new file mode 100644
index 00000000000000..2ea355f1a3ca51
--- /dev/null
+++ b/test/bundler/transpiler/function-tostring-require.test.ts
@@ -0,0 +1,13 @@
+import { test, expect } from "bun:test";
+
+test("toString doesnt observe import.meta.require", () => {
+ function hello() {
+ return typeof require("fs") === "string" ? "from eval" : "main function";
+ }
+ const newFunctionBody = `return ${hello.toString()}`;
+ const loadFakeModule = new Function("require", newFunctionBody)(id => `fake require ${id}`);
+ expect(hello()).toBe("main function");
+ expect(loadFakeModule()).toBe("from eval");
+});
+
+export {};
diff --git a/test/js/bun/css/css.test.ts b/test/js/bun/css/css.test.ts
index 2e4405893a4234..5c85ae7b07d998 100644
--- a/test/js/bun/css/css.test.ts
+++ b/test/js/bun/css/css.test.ts
@@ -2,9 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-import { describe, expect, test } from "bun:test";
+import { describe, test } from "bun:test";
import "harness";
-import path from "path";
import { attrTest, cssTest, indoc, minify_test, minifyTest, prefix_test } from "./util";
describe("css tests", () => {
diff --git a/test/js/bun/ffi/ffi.test.js b/test/js/bun/ffi/ffi.test.js
index 39782240bfadb6..93f1abbfc19dd9 100644
--- a/test/js/bun/ffi/ffi.test.js
+++ b/test/js/bun/ffi/ffi.test.js
@@ -927,14 +927,6 @@ const libSymbols = {
returns: "int",
args: ["ptr", "ptr", "usize"],
},
- pthread_attr_getguardsize: {
- returns: "int",
- args: ["ptr", "ptr"],
- },
- pthread_attr_setguardsize: {
- returns: "int",
- args: ["ptr", "usize"],
- },
login_tty: {
returns: "int",
args: ["int"],
diff --git a/test/js/bun/resolve/resolve.test.ts b/test/js/bun/resolve/resolve.test.ts
index d2fa6fbb97f561..969449ffd01e82 100644
--- a/test/js/bun/resolve/resolve.test.ts
+++ b/test/js/bun/resolve/resolve.test.ts
@@ -1,12 +1,8 @@
import { it, expect } from "bun:test";
import { mkdirSync, writeFileSync } from "fs";
-import { join } from "path";
+import { join, sep } from "path";
import { bunExe, bunEnv, tempDirWithFiles, isWindows } from "harness";
import { pathToFileURL } from "bun";
-import { expect, it } from "bun:test";
-import { mkdirSync, writeFileSync } from "fs";
-import { bunEnv, bunExe, tempDirWithFiles } from "harness";
-import { join, sep } from "path";
it("spawn test file", () => {
writePackageJSONImportsFixture();
diff --git a/test/js/web/fetch/fetch-gzip.test.ts b/test/js/web/fetch/fetch-gzip.test.ts
index 83028ae12bac47..8f162b004a8f00 100644
--- a/test/js/web/fetch/fetch-gzip.test.ts
+++ b/test/js/web/fetch/fetch-gzip.test.ts
@@ -210,8 +210,7 @@ it("fetch() with a gzip response works (multiple chunks, TCP server)", async don
await write("\r\n");
socket.flush();
- },
- drain(socket) {},
+ }
},
});
await 1;
diff --git a/test/snippets/react-context-value-func.tsx b/test/snippets/react-context-value-func.tsx
index 800ad428d7c134..c693717466c499 100644
--- a/test/snippets/react-context-value-func.tsx
+++ b/test/snippets/react-context-value-func.tsx
@@ -10,7 +10,7 @@ const ContextProvider = ({ children }) => {
return {children(foo)};
};
-const ContextValue = ({}) => (
+const ContextValue = () => (
{foo => {
if (foo) {