- Notifications
You must be signed in to change notification settings - Fork32
Zero config bundler for npm packages
huozhi/bunchee
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Zero-config bundler for JS/TS packages.
bunchee is a zero-configuration bundler designed to streamline package building by adhering to theexports
field in yourpackage.json. Powered by Rollup and SWC ⚡️, it generates output based on your config, supporting both CommonJS and ESModules.
By using the standardexports
configuration as the single source of truth,bunchee automatically aligns entry file conventions with your exports, ensuring seamless and efficient builds.
npm install --save-dev bunchee typescript
Create entry files of your library andpackage.json
.
cd ./coffeemkdir src&& touch ./src/index.ts&& touch package.json
Add the exports inpackage.json
.
{"name":"coffee","type":"module","main":"./dist/index.js","scripts":{"build":"bunchee"}}
npm run build
Then files insrc
folders will be treated as entry files and match the export names in package.json.Simply like Node.js module resolution, each export name will match the file insrc/
directory.
Here's a example of entry files and exports configuration:
File | Exports Name |
---|---|
src/index.ts | "." (default export) |
src/lite.ts | "./lite" |
src/react/index.ts | "./react" |
{"name":"coffee","scripts":{"build: "bunchee",},"type":"module","exports":{// entry: ./src/index.ts".":{"import":"./dist/index.js","require":"./dist/index.cjs",},// entry: ./src/lite.ts"./lite":"./dist/lite.js",// entry: ./src/react/index.ts"./react":"./dist/react.js",},}
bunchee detects the format of each entry-point based on export condition type or the file extension. It supports the following output formats:
package.json Field | Output format |
---|---|
main | Default |
types | TypeScript declaration |
exports | Default |
exports.require | CommonJS |
exports.import | Default |
exports.types | TypeScript declaration of export |
bin | Default |
bin.<name> | Default |
TheDefault output format is determined by the file extension:
File Extension | Output format |
---|---|
.js | Determined bypackage.json#type , CommonJS by default |
.cjs | CommonJS |
.mjs | ECMAScript Modules |
Thedependencies
andpeerDependencies
will be marked as externalized and wont be included in the bundle. If you want to include them in the bundle, you can use the--no-external
option. Or you can import thedevDependencies
in your source code to bundle them.
{// Externalized"dependencies":{/* ... */},"peerDependencies":{/* ... */},// Bundled"devDependencies":{/* ... */},}
For exports condition likereact-native
,react-server
andedge-light
as they're special platforms, they could have different exports or different code conditions. In this case bunchee provides an override input source file convention if you want to build them as different code bundle.
For instance:
{"exports":{"react-server":"./dist/react-server.mjs","edge-light":"./dist/edge-light.mjs","import":"./dist/index.mjs",},}
bunchee
supports both TypeScriptpaths
config and Node.jsimports field
inpackage.json
for path aliasing. It will resolve the path alias to the correct file path. If you're using modern TypeScript versions, you can also directly configure theimports
field inpackage.json
and it will work as a charm.
// package.json{"imports":{"#util":"./src/utils.ts",},}
To build executable files with thebin
field in package.json,bunchee
requires you to create thebin
directory undersrc
directory. The source file matching will be same as the entry files convention.
For example:
|- src/|- bin/|- index.ts
This will match thebin
field in package.json as:
{"bin":"./dist/bin.js",}
If you have multiple binaries, you can create multiple files under thebin
directory. Check the below example for more details.
Multiple Binaries
For named executable files, you can create multiple files under thebin
directory.
|- src/|- bin/
This will match thebin
field in package.json as:
{"bin":{"foo":"./dist/bin/a.js","bar":"./dist/bin/b.js",},}
Note: For multiple
bin
files, the filename should match the key name in thebin
field.
bunchee supports building React Server Components and Server Actions with directives like"use client"
or"use server"
. It generates separate chunks for the server or client boundaries. When integrated to framework like Next.js, it can correctly handles the boundaries with the split chunks.
Sometimes, you may want to share a chunk across multiple bundles without promoting it to separate entries or exports, such as single instance of React context module, shared utils, etc. In these cases,shared modules will help you achieve the goal. Files or directoriesprefixed with an underscore (_<name>.<ext>
or_<name>/**
) will be treated asshared modules.
These conventions are kept private and are not going to be treat as shared modules or entry points. For example, test or mock files like_foo/a.test.ts
will be ignored and not included as shared modules.
Shared Utils Example
// src/_util.jsexportfunctionsharedUtil(){/* ... */}
You can then use them in different entry files:
// src/index.jsimport{sharedUtil}from'./_util'
// src/lite.jsimport{sharedUtil}from'./_util'
bunchee
will bundle the shared module into a separate chunk, keeping it private and ensuring it's referenced by multiple entry bundles.
For scenarios involving multiple runtime bundles, such asdefault
andreact-server
, modules that need to be shared and remain as a single instance across different runtime bundles can also follow this convention. The leading underscore (_
) ensures that these modules are private to your application while facilitating reuse.
Shared Runtime Module Example
'use client'// src/_app-context.jsexportconstAppContext=React.createContext(null)
These modules can be imported in various runtime entry files:
// src/index.jsimport{AppContext}from'./_app-context'
// src/index.react-server.jsimport{AppContext}from'./_app-context'
The_app-context
module will be bundled into a shared chunk that exists as a single instance across different runtime bundles.
This convention keeps shared modules private while enabling efficient bundling and reuse across your codebase.
bunchee
CLI provides few options to create different bundles or generating types. Callbunchee --help
to see the help information in the terminal.
Here are the available options for the CLI:
cd<project-root-dir># Build based on the package.json configurationbunchee --runtime node -o ./dist/bundle.jsbunchee -f esm -o --target es2022 ./dist/bundle.esm.js# Specify the input source filebunchee ./src/foo.ts -o ./dist/foo.js
bunchee --external=dep1,dep2,dep3
Replacedep1
,dep2
, anddep3
with the names of the dependencies you want to exclude from the bundle.
To bundle your library without external dependencies, use the--no-external
option:
bunchee --no-external
This will include all dependencies within your output bundle.
A command to be executed after a build is successful can be specified using the--success
option, which is useful for development watching mode:
bunchee --watch --success"node ./dist/index.js"
# Use bunchee to prepare package.json configurationnpmexec bunchee prepare# "If you're using other package manager such as pnpm"# pnpm bunchee prepare# "Or use with npx"# npx bunchee@latest prepare
Or you can checkout the following cases to configure your package.json.
JavaScript ESModule
Then use use theexports field in package.json to configure different conditions and leverage the same functionality as other bundlers, such as webpack. The exports field allows you to define multiple conditions.
{"files":["dist"],"type":"module","exports":{".":"./dist/es/index.js","./react":"./dist/es/react.js",},"scripts":{"build":"bunchee",},}
TypeScript
If you're building a TypeScript library, separate the types from the main entry file and specify the types path in package.json. Types exports need to stay on the top of each export withtypes
condition, and you can usedefault
condition for the JS bundle file.
{"files":["dist"],"type":"module","main":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js",},"./react":{"types":"./dist/react/index.d.ts","default":"./dist/react/index.js",},},"scripts":{"build":"bunchee",},}
Hybrid (CJS & ESM) Module Resolution with TypeScript
If you're using TypeScript with Node 10 and Node 16 module resolution, you can use the `types` field in package.json to specify the types path. Then `bunchee` will generate the types file with the same extension as the main entry file.NOTE: When you're using.mjs
or.cjs
extensions with TypeScript and modern module resolution (above node16), TypeScript will require specific type declaration files like.d.mts
or.d.cts
to match the extension.bunchee
can automatically generate them to match the types to match the condition and extensions.
{"files":["dist"],"type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js",},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs",},},"scripts":{"build":"bunchee",},}
lint
command will check the package.json configuration is valid or not, it can valid few things like:
- if the entry files are matched with the exports conditions.
- if the entry files are matched with the exports paths.
# Use bunchee to lint if the package.json configuration is validnpmexec bunchee lint
To pass environment variables to your bundled code, use the --env option followed by a comma-separated list of environment variable names:
bunchee --env=ENV1,ENV2,ENV3
ReplaceENV1
,ENV2
, andENV3
with the names of the environment variables you want to include in your bundled code. These environment variables will be inlined during the bundling process.
You can useindex.<export-type>.<ext>
to override the input source file for specific export name. Or using<export-path>/index.<export-type>.<ext>
also works. Such as:
|- src/ |- index/.ts |- index.react-server.ts |- index.edge-light.ts
This will match the export name"react-server"
and"edge-light"
then use the corresponding input source file to build the bundle.
process.env.NODE_ENV
is injected by default if present that you don't need to manually inject yourself. If you need to separate the development build and production build,bunchee
provides different export conditions for development and production mode withdevelopment
andproduction
export conditions.
{"exports":{"development":'./dist/index.development.js',"production":'./dist/index.production.js',},}
Then you can usebunchee
to build the development bundle and production bundle automatically.
bunchee
has basic CSS support for pure CSS file imports. It will be bundled into js bundle and insert the style tag into the document head when the bundle is loaded by browser.
/* src/style.css */.foo {color: orange;}
// src/index.tsximport'./style.css'exportconstFoo=()=><divclassName="foo">foo</div>
If you just want to import a file as string content, you can name the extension as.txt
or.data
and it will be bundled as string content.
For example:
src/index.ts
importdatafrom'./data.txt'exportdefaultdata
src/data.txt
hello world
output
exportdefault'hello world'
importpathfrom'path'import{bundle,typeBundleConfig}from'bunchee'// The definition of these options can be found in help informationawaitbundle(path.resolve('./src/index.ts'),{dts:false,// Booleanwatch:false,// Booleanminify:false,// Booleansourcemap:false,// Booleanexternal:[],// string[]format:'esm',// 'esm' | 'cjs'target:'es2015',// ES syntax targetruntime:'nodejs',// 'browser' | 'nodejs'cwd:process.cwd(),// stringclean:true,// booleantsconfig:'tsconfig.json',// string})
Bunchee offers a convenient watch mode for rebuilding your library whenever changes are made to the source files. To enable this feature, use either-w
or--watch
.
If you specifytarget
option intsconfig.json
, then you don't have to pass it again through CLI.To target a range of browsers, you can use thebrowserslist
field inpackage.json
, bunchee will use it to determine the target browsers for the output bundle.
For example:
{"browserslist":["last 1 version","> 1%","maintained node versions","not dead",],}
bunchee
has support for checking the package bundles are matched with package exports configuration.
MIT
About
Zero config bundler for npm packages