Perfect build tool for libraries, powered by esbuild
- Automatic entry points
- Support for ESM and CommonJS
- Support TypeScript
NodeNext
moduleResolution - Support multple & complex entries by Node.js's Conditional Exports
- Support Import Maps with Node.js's Subpath Imports rule
- Optimize esbuild options to maximize concurrency
- Only configuration you need is
package.json
(and optionallytsconfig.json
)
See feature comparison for more detail.
You don't need any config files or passing the entry paths. But only you need to have proper package.json
(and tsconfig.json
)
{
"main": "./lib/index.js",
"scripts": {
"build": "nanobundle build"
}
}
That's it, then just run yarn build
or npm run build
. What a magic ✨
nanobundle is smart enough to automatically determine the location of the appropriate source files from the entries specified in your package.json
.
It searches based on the --root-dir
and --out-dir
on the CLI flags (defaults to src
and lib
) but respects tsconfig.json
if present.
More interestingly, it supports all of Node.js' notoriously complex Conditional Exports rules.
The ESM-only approach
{
"type": "module",
"main": "./lib/index.js", // => src/index.ts
"module": "./lib/index.js", // => src/index.ts
"exports": "./lib/index.js" // => src/index.ts
}
Dual-package exports
{
"exports": {
".": {
"types": "./lib/index.d.ts", // => src/index.ts
"require": "./lib/index.js", // => src/index.ts
"import": "./lib/index.mjs" // => src/index.mts or src/index.ts
},
"./package.json": "./package.json" // => package.json
}
}
Mutliple platform support
{
"exports": {
".": {
"node": {
"require": "./lib/index.node.cjs", // => src/index.cts or src/index.ts
"import": "./lib/index.node.mjs" // => src/index.mts or src/index.ts
},
"deno": "./lib/index.deno.mjs", // => src/index.mts or src/index.ts
"browser": "./lib/index.browser.mjs", // => src/index.mts or src/index.ts
"default": "./lib/index.js" // => src/index.ts
},
"./package.json": "./package.json" // => package.json
}
}
Server/Client submodules
{
"exports": {
".": "./lib/common.js", // => src/common.ts
"./server": {
"types": "./lib/server.d.ts", // => src/server.ts
"require": "./lib/server.cjs", // => src/server.cts or src/server.ts
"import": "./lib/server.mjs" // => src/server.mts or src/server.ts
},
"./client": {
"types": "./lib/client.d.ts", // => src/client.ts
"require": "./lib/client.min.cjs", // => src/client.cts or src/client.ts, output will be minified:sparkles:
"import": "./lib/client.min.mjs" // => src/client.mts or src/client.ts, output will be minified
},
"./package.json": "./package.json"
}
}
Development-only code for debugging
{
"exports": {
"development": "./dev.js", // => src/dev.ts
"production": "./index.min.js" // => src/index.ts, output will be minified
}
}
Full CLI options
Usage
$ nanobundle <command> [options]
Available Commands
build Build once and exit
clean Remove outputs
Options
--version Display current version
--cwd Use an alternative working directory
--clean Clean outputs before build
--tsconfig Specify the path to a custom tsconfig.json
--import-maps Specify import map file path (default: package.json)
--root-dir Specify the path to resolve source entry (default: ./src)
This also can be configured by tsconfig.json
--out-dir Specify the path to resolve source entry (default: ./lib)
This also can be configured by tsconfig.json
--platform Specify bundle target platform (default: "netural")
One of "netural", "browser", "node" is allowed
--standalone Embed external dependencies into the bundle (default: false)
--external Specify external dependencies to exclude from the bundle
--jsx Specify JSX mode. One of "transform", "preserve", "automatic" is allowed
This also can be configufeature comparisonred by tsconfig.json
--jsx-factory Specify JSX factory (default: "React.createElement")
This also can be configured by tsconfig.json
--jsx-fragment Specify JSX <Fragment> factory (default: "Fragment")
This also can be configured by tsconfig.json
--jsx-import-source Specify JSX import source (default: "react")
This also can be configured by tsconfig.json
--no-sourcemap Disable source map generation
--no-legal-comments Disable legal comments generation
--no-bundle Disable ESBuild bundle and other files build
--no-dts Disable TypeScript .d.ts build
--verbose Set to report build result more verbosely
--help Display this message
Nanobundle believes the package.json
today is expressive enough for most module use cases.
So attempting to turn users' attention back to the Node's package spec like ES Modules and Import Maps which are already supported by Node.js, rather than adding another customizing options.
You don't need to pass or set entry points in any configuration file, only you have to do is provide correct exports
in your package.json
.
nanobundle will automatically search for entry files in the rootDir
and outDir
you have. (defaults are src
and lib
, or respectively configurable by tsconfig.json
or CLI arguments)
{
"main": "./lib/index.js", // => search src/index.cts, src/index.ts, etc
"module": "./lib/index.mjs", // => search src/index.mts, src/index.ts, etc
"exports": {
"./feature": "./lib/feature.js" // => search src/feature.cts, src/feature.ts, etc
}
}
nanobundle expects you to write a Web-compatible(netural) package.
If you use any Node.js APIs, you need to tell it explicitly via:.
- Pass
--platform=node
flag - Set the entry point with
node
condition.
Without engines
field in package.json
, the default Node.js version will be v14.
You can specify multiple/conditional entry points in your package.json
.
See Node.js docs for more detail.
{
"type": "module",
"main": "./main.js", // Legacy entry
"exports": {
".": "./main.js",
"./feature": {
"node": "./feature-node.js", // conditional entry
"default": "./feature.js"
}
}
}
You can use conditional exports for dealing with Dual Package Hazard.
E.g. for supporting both CommonJS and ESM package.
{
"exports": {
"require": "./lib/index.cjs",
"import": "./lib/index.mjs"
}
}
nanobundle supports Import Maps
You can specify import alias by your package.json
, or by a separated json file with the --import-map
option.
{
"imports": {
"~/": "./",
"@util/": "./src/utils/",
}
}
nanobundle also handles Node.js's Subpath Imports rules.
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
}
}
nanobundle by default does nothing about external like dependencies
and peerDependencies
.
However, if the --standalone
flag is set, it will try to embed all external dependencies into the bundle.
Dependencies specified with --external
and Node.js internal APIs are always excluded.
Given a tsconfig.json
file in the cwd or --tsconfig
option, nanobundle looks for options for TypeScript and JSX.
nanobundle automatically generate TypeScript declaration as you specify types
entries in the package.json
, or you can disable it passing --no-dts
flag.
Any entires with .min.(c|m)?js
will generate minified output.
{
"exports": "./index.min.js" // will be minifies output
}
Conditional entries with Node.js community condition production
or development
will be built with injected process.env.NODE_ENV
as its value.
{
"exports": {
".": {
"development": "./dev.js", // process.env.NODE_ENV === 'development'
"production": "./prod.min.js" // process.env.NODE_ENV === 'production'
}
}
}
Build tool | 0 Config | Respect package.json |
TypeScript .d.ts generation |
Concurrency | Multiple Entries | Conditional Exports | Import Maps | CSS Support | Plugins | Dev(watch) mode |
---|---|---|---|---|---|---|---|---|---|---|
nanobundle | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✖️ (planned) |
✖️ (planned) |
microbundle | ✔️ | ✔️ | ✔️ | ✔️ | ✖️ | 🟡 (only flat) |
✖️ | ✔️ | ✖️ | ✔️ |
tsup | 🟡 (mostly by custom file) |
✖️ | ✔️ | ✔️ | ✔️ | ✖️ | 🟡 (with plugin) |
🟡 (experimental) |
✔️ | ✔️ |
estrella | ✖️ | ✖️ | ✔️ | ✔️ | ✖️ | ✖️ | ✖️ | ✖️ | ✖️ | ✔️ |
esbuild | ✖️ | ✖️ | ✖️ | ✔️ | ✔️ | ✖️ | ✖️ | ✔️ | ✔️ | ✔️ |
Rollup | ✖️ | ✖️ | 🟡 (with plugin) |
✔️ | ✔️ | 🟡 (by code) |
🟡 (with plugin) |
✔️ | ✔️ | ✔️ |
Vite (lib mode) | ✖️ | ✖️ | 🟡 (with plugin) |
✔️ | ✔️ | 🟡 (by code) |
🟡 (with plugin) |
✔️ | ✔️ | ✔️ |
Parcel (lib mode) | ✔️ | ✔️ | ✔️ | ✔️ | ✖️ | ✖️ | ✖️ | ✔️ | ✖️ | ✔️ |
Thanks goes to these wonderful people (emoji key):
Hyeseong Kim 💻 🚧 |
Anton Petrov 💻 |
jinho park |
Manuel Thalmann 💻 |
This project follows the all-contributors specification. Contributions of any kind welcome!
MIT