Skip to content

Exports

Bunup automatically generates and updates the exports field in your package.json file after each build.

Bunup handles mapping all entry points to their corresponding output files, including ESM/CJS formats and type declarations. The exports field stays perfectly in sync with your build configuration always - no manual updates needed when you make any change to config.

Usage

Enable exports generation in your Bunup configuration:

sh
bunup --exports
ts
import { defineConfig } from 'bunup';

export default defineConfig({
	exports: true,
});

This will automatically update your package.json with the correct exports field each time you build. For example:

package.json
json
{
	"name": "my-package",
	"version": "1.0.0",
	"type": "module",
	"files": [ 
		"dist"
	], 
	"module": "./dist/index.js", 
	"main": "./dist/index.cjs", 
	"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"
			} 
		} 
	} 
}

Custom Exports

The customExports option allows you to specify additional export fields that will be preserved alongside the automatically generated exports. This is useful when you need custom export conditions or paths that aren't automatically generated by the build process.

bunup.config.ts
ts
import { defineConfig } from 'bunup';

export default defineConfig({
	exports: {
		customExports: (ctx) => ({
			'./package.json': './package.json',
		})
	},
});

Exclude

The exclude option allows you to filter out specific export keys from the generated exports field in your package.json. This operates on the final export keys (like ".", "./utils", "./components") that appear in the exports object.

You can provide an array of strings (using exact export keys, wildcards, or a mix of both), or a function that returns such an array.

sh
# Single exclusion - exclude the "./internal" export key
bunup --exports.exclude=./internal

# Multiple exclusions
bunup --exports.exclude=./utils,./internal

# Using wildcards - exclude all exports under "./private"
bunup --exports.exclude="./private/*"

# Mix both - exact keys and wildcards
bunup --exports.exclude="./internal,./private/*"
ts
import { defineConfig } from 'bunup';

export default defineConfig({
	entry: ['src/index.ts', 'src/utils.ts', 'src/internal.ts'],
	exports: {
		// Exclude the "./internal" export key from package.json exports
		exclude: ['./internal']
	},
});

This will generate exports for "." and "./utils" but exclude "./internal" from the final package.json:

package.json
json
{
	"exports": {
		".": {
			"import": "./dist/index.js",
			"types": "./dist/index.d.ts"
		},
		"./utils": {
			"import": "./dist/utils.js",
			"types": "./dist/utils.d.ts"
		}
		// "./internal" is excluded
	}
}

For more dynamic control, you can use a function:

bunup.config.ts
ts
import { defineConfig } from 'bunup';

export default defineConfig({
	entry: ['src/index.ts', 'src/utils.ts', 'src/internal.ts'],
	exports: {
		exclude: (ctx) => {
			// Access build context information
			const { options, meta } = ctx;
			// Dynamically exclude export keys based on your logic
			return ['./internal', './debug'];
		}
	},
});

Exclude CLI

By default, CLI-related entry points are automatically excluded from the package exports field. This prevents binary/command-line tools from being exposed as importable package exports, which is the correct behavior in most cases since CLI entries are typically used via the bin field in package.json.

The plugin uses glob patterns to automatically detect and exclude common CLI entry point patterns:

  • Files or directories named cli (e.g., cli.ts, cli/index.ts)
  • Files or directories named bin (e.g., bin.ts, bin/index.ts)
  • CLI-related paths in any directory (e.g., src/cli.ts, tools/bin/index.ts)

If you want to include CLI entries in your exports (which is rarely needed), you can disable this behavior:

sh
bunup --no-exports.exclude-cli
ts
import { defineConfig } from 'bunup';

export default defineConfig({
	exports: {
		excludeCli: false // Include CLI entries in exports
	},
});

When disabled, CLI entries will be treated like any other entry point and included in the exports field.

Exclude CSS

When you use CSS files and import them in your JavaScript files, Bun will bundle the CSS and include it in the build output. As a result, these CSS files will be automatically added to the exports field with appropriate export keys.

The excludeCss option allows you to prevent CSS files from being included in the exports field if you prefer to handle CSS distribution manually or don't want to expose CSS files as part of your package's public API.

sh
bunup --exports.exclude-css
ts
import { defineConfig } from 'bunup';

export default defineConfig({
	exports: {
		excludeCss: true
	},
});

Include Package JSON

By default, exports generation automatically adds "./package.json": "./package.json" to your package's exports field. This export is useful for:

  • Package introspection: Allowing consumers to access your package's metadata programmatically
  • Tooling compatibility: Many development tools and package managers expect to be able to import package.json
  • Runtime information: Enabling your package to access its own version and metadata at runtime

The includePackageJson option allows you to control this behavior:

sh
bunup --no-exports.include-package-json
ts
import { defineConfig } from 'bunup';

export default defineConfig({
	exports: {
		includePackageJson: false // Disable package.json export
	},
});

When enabled (default), your exports field will include:

package.json
json
{
	"exports": {
		".": {
			"import": "./dist/index.js",
			"types": "./dist/index.d.ts"
		},
		"./package.json": "./package.json"
	}
}

All

The all option controls how open your package exports are. This affects what files consumers can import from your package.

When all: true, a wildcard subpath export is added that allows importing any file from your package:

sh
bunup --exports.all
ts
import { defineConfig } from 'bunup';

export default defineConfig({
	exports: {
		all: true
	},
});

This generates:

package.json
json
{
	"exports": {
		".": {
			"import": "./dist/index.js",
			"types": "./dist/index.d.ts"
		},
		"./*": "./*"
	}
}

With all: true, consumers can import any file that ends up in your published package.

WARNING

When using all: true, any file that ends up in your published tarball becomes importable. Control what you publish using the files field in package.json or .npmignore to avoid exposing internal files.

Export Keys and Output Structure

Bunup generates export keys based on the output file paths. In some cases, such as when using glob patterns like src/**/*.ts, the output structure may include the src/ directory, which affects the generated export keys.

In certain configurations, your output files may be nested under a src/ directory:

sh
bunup 'src/**/*.ts'
ts
export default defineConfig({
  entry: ['src/**/*.ts'],
});

Output structure:

dist/
└── src/
    ├── components/
    │   └── Button.js
    └── utils/
        └── format.js

When this happens, the generated export keys will include the ./src/ prefix:

json
{
  "exports": {
    "./src/components/Button": "./dist/src/components/Button.js",
    "./src/utils/format": "./dist/src/utils/format.js"
  }
}

If you don't want ./src/ in your export keys, you can set sourceBase to './src'. This tells Bunup to use src/ as the base directory, which removes src/ from the output structure:

sh
bunup 'src/**/*.ts' --source-base ./src
ts
export default defineConfig({
  entry: ['src/**/*.ts'],
  sourceBase: './src',
});

Output structure:

dist/
├── components/
│   └── Button.js
└── utils/
    └── format.js

As a result, the generated export keys no longer include the ./src/ prefix:

json
{
  "exports": {
    "./components/Button": "./dist/components/Button.js",
    "./utils/format": "./dist/utils/format.js"
  }
}

Key Takeaway

Bunup generates export keys based on the output file paths relative to your output directory. The sourceBase option controls the output structure, which directly affects the generated export keys. This is not specific to ./src, you can use sourceBase with any directory structure to control how paths appear in your export keys.

For more details on how sourceBase works, see the Source Base Directory option.

Released under the MIT License.