A TypeScript-first bundler designed specifically for library packages that delivers fast builds, type safety, and modern JavaScript output with minimal configuration.
✅ TypeScript-first - Built with TypeScript for maximum type safety
✅ Dual Output - Generate both ESM and CommonJS formats automatically
✅ Automatic Renaming - Handles duplicate declarations intelligently
✅ Fast Builds - Optimized for library packages with minimal overhead
✅ Package.json Management - Automatic updates to package.json fields based on the build results
✅ Plugin System - Extend functionality with custom plugins
✅ CLI & Programmatic API - Use as a CLI tool or integrate directly
Install susee as a development dependency in your project:
npm i -D suseeThis method is recommended for library projects as it ensures the bundler version is locked to the project and available for CI/CD pipelines.
For system-wide availability of the susee CLI:
npm install -g suseeGlobal installation enables running susee directly from any directory without the npx prefix.
After installation, verify the package is available by checking the version command:
npx susee --versionThe easiest way to start is using the built-in initialization command which generates a configuration template at your project root.This command creates a susee.config.ts, susee.config.js, or susee.config.mjs file.
npx susee initBuild your project by running:
npx suseeYou can trigger the build process within a TypeScript/JavaScript script using the build() function.
import { build } from "susee";
await build({
entryPoints: [
{
entry: "src/index.ts",
exportPath: ".",
format: ["esm", "commonjs"],
renameDuplicates: true,
},
],
outDir: "dist",
allowUpdatePackageJson: true,
});Build a single entry directly without a config file.This method uses default values for options not explicitly provided.
npx susee build src/index.ts --outdir dist --format esmWhen contributing to this repository, use npm to keep installs aligned with package-lock.json and npm-based scripts.
npm install
npm run hooks:installThis installs project dependencies and configures local git hooks for commit workflow checks.
Please report vulnerabilities privately and follow the disclosure process in SECURITY.md.
Do not open public issues for security reports.
| Surface | Command / API | Purpose | Defaults |
|---|---|---|---|
| Programmatic | build(options?) |
Build from provided options or discovered config file | Exits with code 1 when options and config are both missing |
| CLI | susee |
Build using susee.config.ts/js/mjs in project root |
Uses resolved config |
| CLI | susee init |
Create config template in project root | Prompts for TypeScript project |
| CLI | susee build <entry> [options] |
Build a single entry directly from CLI args | --outdir dist, --format esm, --rename true, --allow-update false, --minify false, --warning false |
| Config | entryPoints[].format |
Output module format(s) | ["esm"] |
| Config | entryPoints[].renameDuplicates |
Rename duplicate declarations | true |
| Config | entryPoints[].tsconfigFilePath |
Custom tsconfig path | undefined |
| Config | entryPoints[].plugins |
Post-process plugin list | [] |
| Config | outDir |
Root output directory | "dist" |
| Config | allowUpdatePackageJson |
Update package fields based on output | false |
Susee CLI.
Usage:
susee Build using susee.config.{ts,js,mjs}
susee init Generate susee.config.{ts,js,mjs}
susee --version | -v Check susee version
susee --help | -h Show this message
susee build <entry> [options] Build from a single entry file--entry <path> Entry file (optional if provided as positional <entry>)
--outdir <path> Output directory (default: dist)
--format <cjs|commonjs|esm> Output format (default: esm)
--tsconfig <path> Custom tsconfig path
--rename[=true|false] Rename duplicate declarations (default: true)
--allow-update[=true|false] Allow package.json updates (default: false)
--minify[=true|false] Minify output (default: false)
--warning[=true|false] Enable warnings (default: false)npx susee build src/index.ts --outdir dist
npx susee build src/index.ts --format commonjs
npx susee build --entry src/index.ts --format esm --minifySupported config filenames at project root:
susee.config.tssusee.config.jssusee.config.mjs
type OutputFormat = ("commonjs" | "esm")[];
interface EntryPoint {
entry: string;
exportPath: "." | `./${string}`;
format?: OutputFormat; // default: ["esm"]
tsconfigFilePath?: string | undefined; // default: undefined
renameDuplicates?: boolean; // default: true
plugins?: unknown[]; // default: []
warning?: boolean; // default: false
}
interface SuSeeConfig {
entryPoints: EntryPoint[];
outDir?: string; // default: "dist"
allowUpdatePackageJson?: boolean; // default: false
}import type { SuSeeConfig } from "susee";
const config: SuSeeConfig = {
entryPoints: [
{
entry: "src/index.ts",
exportPath: ".",
format: ["esm", "commonjs"],
},
],
outDir: "dist",
allowUpdatePackageJson: false,
};
export default config;Signature:
function build(options?: SuSeeConfig): Promise<void>;Parameters:
options(optional): Build options passed directly from code.
Returns:
Promise<void>that resolves when compilation completes.
Runtime behavior:
- If
optionsis provided, Susee builds from that object. - If
optionsis omitted, Susee tries to load config from project root. - If both are missing, Susee logs an error and exits with code
1.
import { build, type SuSeeConfig } from "susee";
const options: SuSeeConfig = {
entryPoints: [
{
entry: "src/index.ts",
exportPath: ".",
format: ["esm", "commonjs"],
},
],
};
await build(options);For an entry like src/index.ts with both formats enabled, output includes:
- ESM:
dist/index.mjs - CommonJS:
dist/index.cjs - Sourcemaps:
.mjs.mapand.cjs.map
Declaration files are emitted by the compiler when available.
| Input | Output Directory Rule | ESM Files | CommonJS Files |
|---|---|---|---|
entry: "src/index.ts", exportPath: "." |
<outDir> |
index.mjs, index.mjs.map, index.d.mts |
index.cjs, index.cjs.map, index.d.cts |
entry: "src/foo.ts", exportPath: "./foo" |
<outDir>/foo |
foo.mjs, foo.mjs.map, foo.d.mts |
foo.cjs, foo.cjs.map, foo.d.cts |
Notes:
- Default
outDirisdistwhen not set. - For subpath exports, output directory is computed as
outDir + exportPath.slice(1). - Declarations (
.d.mts/.d.cts) are emitted when provided by the underlying compiler result.
When allowUpdatePackageJson (config) or --allow-update (CLI build) is enabled, Susee can update package fields.
| Context | Condition | Updated Fields | Observed Result |
|---|---|---|---|
| Main export build | exportPath: "." with ESM + CommonJS outputs |
main, module |
main: "dist/index.cjs", module: "dist/index.mjs" |
| Main export build | exportPath: "." with declarations |
types |
Set from generated CommonJS declaration path when available |
| Subpath export build | exportPath: "./foo" |
exports (subpath mapping) |
Currently remains {} in tested behavior |
Notes:
- Package update requires a
package.jsonfile in the project root. - With update disabled, package fields are left unchanged.
- Existing tests in this repo currently assert
exportsremains{}for the tested update flows.
From config validation logic:
- At least one
entryPointsitem is required. - Duplicate
exportPathvalues are rejected. - Each
entrypath must exist.
Violations print an error and exit with code 1.
