I remember when I first used ESBuild in 2020. At the time I was still writing webpack config files by hand, and the results were… slow.
ESBuild wasn’t the most compatible thing out there, but it was fast. Seeing a 20x performance improvement with ESBuild still made one thing clear: speed has value in itself. Fast tools change how we work, how we iterate, and ultimately how we build software.
ESBuild was written in Go, and it was the start of a speed race, where JS tools weren’t necessarily written in JS anymore. Since then, the JavaScript ecosystem has been quietly migrating to Rust:
- Biome: Rust-based formatter and linter
- OXC: A collection of JavaScript tools like a linter and formatter written in Rust
- Rolldown & Turbopack: Next-generation bundlers aiming to replace Webpack and Rollup
- Turborepo: Monorepo tooling with intelligent caching
- Deno: A JavaScript runtime that’s an anagram of Node, also written in Rust
What’s particularly interesting is that these aren’t just side projects anymore. Void Zero, a company that encompasses Vite, Rolldown, and OXC, recently raised $4.6 million in seed funding. That is serious, well-funded infrastructure with proper governance and support.
Building with OXC: a real-world example
At Endform, we use OXC extensively for dependency analysis in our CLI. When users write Playwright tests, those files aren’t self-contained - they have dependencies that need to be resolved, potentially transpiled, and packaged for our test runners.
Here’s how we use OXC’s visitor pattern to extract dependencies from JavaScript files:
struct FileDependencyVisitor {
dependencies: Vec<String>,
}
impl<'a> Visit<'a> for FileDependencyVisitor {
// Handle ES6 imports: import X from 'Y'
fn visit_import_declaration(&mut self, it: &oxc_ast::ast::ImportDeclaration<'a>) {
self.dependencies.push(it.source.value.to_string());
}
// Handle CommonJS: require('module') and nested calls
fn visit_call_expression(&mut self, it: &oxc_ast::ast::CallExpression<'a>) {
// First, recursively visit nested expressions
self.visit_expression(&it.callee);
for arg in it.arguments.iter() {
if let Some(expr) = arg.as_expression() {
self.visit_expression(expr);
}
}
// Check if this is a require() call
if let Some(require) = it.common_js_require() {
let req_str = require.value.to_string();
self.dependencies.push(req_str);
}
}
// Handle re-exports: export * from 'module'
fn visit_export_all_declaration(&mut self, it: &oxc_ast::ast::ExportAllDeclaration<'a>) {
self.dependencies.push(it.source.value.to_string());
}
}
The visitor pattern makes it straightforward to traverse JavaScript’s abstract syntax tree and perform custom operations. We use similar approaches for:
- An environment variable analyzer that detects
process.env
usage - OXC’s built-in parser handles TypeScript to JavaScript transpilation
- File resolution that maps import paths to actual files in monorepos
- Finding test cases in user code
Compared to JavaScript-based tools like Vercel’s NFT (Node File Trace), OXC is not only more performant but also had much nicer ergonomics. I have tried using NFT in projects, and it doesn’t feel as stable, or like something I would have wanted to build Endform on top of. It’s really an ecosystem that you feel like you can build on top of.
The TypeScript elephant in the room
While written-in-rust tools seem to be conquering parsing, linting, bundling, and even runtimes, there’s one major piece missing: type checking. TypeScript remains a tricky part of the puzzle, that has the potential to hamper a complete Rust takeover.
People are finding ways around this. Biome v2 just announced support for the first type aware linting rules that don’t require a TypeScript compiler.
Then came the surprise announcement: Microsoft is rewriting TypeScript… in Go.
Not Rust. Go. This came as a bit of a surprise, especially given Microsoft’s heavy investment in Rust elsewhere. Their reasoning:
- Structural similarity: Go and TS codebases could be structured similarly - which would make it easier to port
- Garbage Collection: For TypeScript’s use case, Go’s GC wouldn’t be a significant drawback
I think that this could make it significantly harder for JS to become a language fully written in Rust in the long term. It will also make it harder for Rust-based JS tooling to make cooler type-aware features that use the type system.
More challenges ahead
Extensibility
One major hurdle is community extensibility. Linters are more powerful when developers can write custom rules. But requiring Rust knowledge for something like a simple company-internal custom lint rule creates a significant barrier for the JavaScript community.
Different projects are taking different approaches:
- Biome: Uses Grit, a query language that compiles to AST operations
- Rolldown: Exploring shared memory communication between Rust and JavaScript extensions
Dependency sharing
Another challenge is sharing dependencies. At Endform, our CLI compiles OXC crates + our other dependencies into a 15MB binary. If Turborepo also uses OXC tools, or if our users used OXC for linting, it would be great to share those dependencies somehow - but there’s no standard for this today.
Unlike npm’s package.json system, we can’t easily declare and share common Rust dependencies across JavaScript tools.
Rust vs. built-in Rust popularity contest
As a fun little break in this whole analysis - here are a few stats on GitHub stars of some popular Rust and JS projects.

- Turborepo is more popular than Axum
- Deno, a JS runtime written in Rust, is almost as popular as the Rust language itself
Just goes to show how massive the JavaScript ecosystem is and how much impact these Rust tools are having.
The future: total Rust takeover?
I’m not sure that we will see a complete Rust takeover, primarily because TypeScript isn’t going that direction. But we’re definitely seeing Rust become the backbone of JavaScript tooling infrastructure.
The value proposition is clear:
- Performance: 10-100x improvements aren’t uncommon
- Stability: These tools are becoming production-grade with proper funding and governance
- Developer experience: Growing ability to extend / build on top of the tooling
We’re probably at the beginning of this journey, not the end. More companies are leveraging Rust to improve existing ecosystems, and the JavaScript community is one of the biggest beneficiaries.
We are one of those companies here at Endform! We use Rust to make Playwright end-to-end tests faster. Without OXC, we wouldn’t have been able to build the product we have today.
Even if it won’t be a slam dunk, it’s a super interesting space to be watching.