diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000000..a21be158eb
--- /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/bench/hot-module-reloading/css-stress-test/src/index.tsx b/bench/hot-module-reloading/css-stress-test/src/index.tsx
index 5eefb43040..c8b470ec7a 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 83a874dc5f..4663dbfedf 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/oxlint.json b/oxlint.json
new file mode 100644
index 0000000000..f596253eb7
--- /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 b0cfe51737..4df6d400fe 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 0a8fdc7e6f..f07be12c50 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/api/sockets.classes.ts b/src/bun.js/api/sockets.classes.ts
index 7d30de0344..9b03d24a9d 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/js/internal/primordials.js b/src/js/internal/primordials.js
index 565a056a60..bd54b95070 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 a48c0027bc..f480b4212b 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 1fad065074..2e7e28e7e1 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/test/bundler/bundler_html.test.ts b/test/bundler/bundler_html.test.ts
index 6314971925..1a2d57334e 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/js/bun/css/css.test.ts b/test/js/bun/css/css.test.ts
index 2e4405893a..5c85ae7b07 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 39782240bf..93f1abbfc1 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 d2fa6fbb97..969449ffd0 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 83028ae12b..8f162b004a 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 800ad428d7..c693717466 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) {