Code size optimization opportunities
- Dominant language
- Go
- Stars
- 40.1k
- Forks
- 1.3k
- PR merge metrics
- No merged PRs in 30d
Description
Bun's bundler implementation is mostly a port of esbuild's so I wanted to share a couple notes:
- The CommonJS wrapper is expensive. If the CommonJS file doesn't require other CommonJS files, I think wrapping can be skipped so long as the exports object is not assigned to directly, exports isn't used other than for statically known property accesses, and no eval/with/etc. To make this work, Bun's implementation has a CommonJSExportIdentifier AST node and an extra symbol for each export. At print time, it chooses which to print as (either exports object or hoisted var). Also module scope function statements have to become function expressions with var decls and moved to the top of the scope. The exports kind is set to esm with dynamic fallback. If this same operation can be performed on multiple CommonJS files that require each other then those can be unwrapped too, but then you're left with the entire module namespace still bundled because most code assigns the result of the require() to another variable. I'm still figuring out a solution to this. Currently thinking (1) a hardcoded list of packages which this transform can fully apply to and (2) for decls which bind identifiers that assign require, replace it with an import statement with a namespace (as long as no try / catch is used). Then when the require calls are used, it will become an ImportIdentifier. There's probably some way to do this without the hardcoded list involving checking for cyclical require but I worry about adding more edgecases
- JS Files which have a single statement that is a module.exports = require("foo") or a export * from can be "redirected" earlier in the linker to reduce code size & indirection. This is important to make packages like react not wrapped (edit: ESM cannot be redirected without several complicated de-opts. CommonJS modules can be redirected, so far.)
- there's probably am okay perf optimization in using a bitset for storing is top level in the list of declared symbols. I tried doing this in bun but got stuck on memory bugs from using a sometimes stack allocated bitset
Contributor guide
No contributing guide indexed for this repository
Research direction
The issue discusses CommonJSExportIdentifier, linker redirection for module.exports = require("foo") and export *, and bitsets for top-level declared symbols, but names no files or tests. Start by locating those implementation areas and choose one optimization to scope; done should include a focused change with evidence that bundled code size or symbol handling improves.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, javascript
- Domain
- build-system, compilers, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100