Skip to content
susee

Susee

A high-performance TypeScript library bundler

NPM

oxc npm version license OpenSSF Baseline OpenSSF Best Practices

important

Use susee v2.3.0 or above. In v2.3.0, the core bundler was ported from TypeScript to Rust (powered by oxc) with assistance from the glm-5.2:cloud model served via the Ollama platform, fixing multiple bugs present in earlier versions. Older versions are no longer recommended.

See the bug information since v2.3.0.

Overview

susee is a TypeScript-first bundler powered by oxc, specialized for library packages. Unlike general-purpose bundlers, susee focuses on consolidating a package's local TypeScript dependency tree into consolidated source units and compiling them into dual-format artifacts (ESM and CommonJS).

Key Features

  • TypeScript-first build flow — built around library development, not application bundling. Preserves a package-oriented workflow with declaration output and clean library artifacts.
  • Dual output support — produces both ESM and CommonJS from the same entry definition, so packages work with modern import and legacy require ecosystems.
  • Duplicate declaration validation — when source consolidation produces conflicting top-level declarations, the build fails with file and location output instead of silently renaming.
  • Fast, low-overhead builds — a lean pipeline that fits package development and release workflows without app-level complexity.
  • Package metadata update — can update package.json exports, main, module, and types fields after build output is generated.
  • Built-in minification — runs the oxc-minify minifier over emitted JavaScript when enabled.
  • CLI and programmatic API — use the CLI for local development/CI, or call the build API for custom scripting.
  • JSX support — detects JSX in bundled output and validates the JSX runtime (React or a configured jsxImportSource) before compiling.

Install

npm i -D susee

Verify the installation:

npx susee --version

Quick Start

1. Create a config file

Generate a starter susee.config.{ts,js,mjs} in your project root:

npx susee init

The interactive prompt asks whether your project is TypeScript. For TypeScript projects, it writes susee.config.ts; for JavaScript projects, it writes susee.config.js (ESM) or susee.config.mjs (CommonJS) based on your package.json#type.

2. Define your entries

// susee.config.ts
import type { SuSeeConfig } from "susee";

const config: SuSeeConfig = {
  entryPoints: [
    {
      entry: "src/index.ts",   // required — entry file path
      exportPath: ".",         // required — "." for main export, or "./foo"
      format: ["esm"],         // optional, default ["esm"]
      tsconfigFilePath: undefined, // optional, custom tsconfig
      checks: {                // optional, all default false
        checkAnonymous: false,
        checkDefaultExports: false,
        checkNpmInstalled: false,
      },
      minify: false,           // optional: true | { options: MinifyOptions }
    },
  ],
  outDir: "dist",              // optional, default "dist"
  allowUpdatePackageJson: false, // optional, default false
};

export default config;

3. Build

npx susee build

Susee reads your config, bundles each entry point, compiles to ESM and/or CommonJS, and writes the output to dist by default.

CLI

susee build                           Build using susee.config.{ts,js,mjs}
susee init                            Generate susee.config.{ts,js,mjs}
susee check                           Run lint checks on a dependency tree without bundling
susee bundle <entry> [options]        Bundle a single entry file to disk without compiling
susee --version / -v                  Print version
susee --help / -h                     Show help
susee build <entry> [options]         Build from a single entry file

Build Flags

Flag Type Default Description
--entry <path> string Entry file (optional if given positionally)
--outdir <path> string dist Output directory
--format cjs|commonjs|esm|both esm Output module format (both = CJS + ESM)
--tsconfig <path> string undefined Custom tsconfig path
--allow-update[=true|false] boolean false Allow package.json updates
--minify[=true|false] boolean false Minify output JS
--check[=true|false] boolean false Run bundler lint checks

Bundle Flags

The susee bundle command writes the bundled source (before TypeScript compilation) to disk. It supports --entry, --outdir, and --check[=true|false].

Flags accept both --flag=value and --flag value syntax.

Examples

npx susee build src/index.ts --outdir dist
npx susee build src/index.ts --format commonjs
npx susee build src/index.ts --format both        # emit CJS + ESM
npx susee build --entry src/index.ts --format esm --tsconfig tsconfig.build.json
npx susee build src/index.ts --minify
npx susee build src/index.ts --check              # run lint checks during build
npx susee bundle src/index.ts --outdir bundled   # write bundled source only
npx susee check                                   # lint the dependency tree from config

Programmatic API

build()

import { build, type SuSeeConfig } from "susee";

const config: SuSeeConfig = {
  entryPoints: [
    { entry: "src/index.ts", exportPath: "." },
  ],
  outDir: "dist",
  allowUpdatePackageJson: true,
};

await build(config);

build() resolves options from the argument first, then from a root config file. If neither is available it logs an error and exits with code 1.

suseeBundle()

Bundle a single entry point into a consolidated source string without compiling or writing to disk:

import { suseeBundle, type CheckOptions } from "susee";

const code: string = suseeBundle("src/index.ts");

// with lint checks enabled
const checks: CheckOptions = {
  checkAnonymous: true,
  checkDefaultExports: true,
  checkNpmInstalled: true,
};
const checked = suseeBundle("src/index.ts", checks);

suseeBundle() runs the oxc-powered bundler over the entry's local dependency tree and returns the bundled source. When CheckOptions are provided the bundler runs diagnostics (anonymous declarations, default exports, npm-installed deps) before returning.

How It Works

flowchart TD
    A[CLI / Programmatic API] --> B["build()"]
    B --> C{options provided?}
    C -->|yes| D[generateBuildOptions]
    C -->|no| E[finalSuseeConfig]
    E -->|no config| F[Error + exit 1]
    E -->|found| D
    D --> G[Compiler]
    G --> H[For each entry point]
    H --> I["bundler() — suseeBundler (oxc)"]
    I --> J[Bundled source string]
    J --> K{format}
    K -->|commonjs| L["_commonjs()"]
    K -->|esm| M["_esm()"]
    L --> N["suseeCompiler — ts6 in-memory host"]
    M --> N
    N --> O["getCompilerOptions — tsconfig → per-format"]
    O --> P["ts6.createProgram + emit"]
    P --> Q{minify?}
    Q -->|yes| R[oxcMinify]
    Q -->|no| S["Write .cjs/.mjs + .d.* + .map"]
    R --> S
    S --> T{update package?}
    T -->|yes| U["files.writePackageJson"]
Loading

The pipeline bundles each entry point's local dependency tree into a single source string, compiles it in-memory with the TypeScript compiler (@suseejs/ts6), optionally minifies with oxc-minify, and writes dual-format artifacts with declaration and source-map files.

Source Architecture

src/
├── index.ts            # Public API — re-exports build, suseeBundle, SuSeeConfig, CheckOptions
├── build.ts            # Build orchestrator — resolves config, runs Compiler
├── bundler.ts          # Wrapper around @suseejs/susee_bundler (oxc) + CLI bundle writer
├── cli/
│   ├── index.ts        # CLI entrypoint & command dispatch (build/init/check/bundle)
│   ├── parse_args.ts   # Parses CLI flags into SuSeeConfig / bundle opts
│   ├── init.ts         # `susee init` — scaffolds config file
│   ├── lint.ts         # `susee check` — runs suseeLint over the dependency tree
│   └── print_help.ts   # `susee --help` output
├── compiler/
│   ├── index.ts        # Compiler class — bundles + emits CJS/ESM + types
│   ├── suseeCompiler.ts# In-memory TypeScript compilation host
│   └── tsoptions.ts    # Resolves tsconfig.json into per-format options
├── config/
│   └── index.ts        # Config types, validation, and normalization
└── helpers/
    ├── files.ts        # File system namespace + package.json writer
    └── minify.ts       # oxc-minify wrapper

See src/README.md for detailed module documentation.

Configuration Reference

SuSeeConfig

Field Type Required Default Description
entryPoints EntryPoint[] yes Array of entry point definitions
outDir string no dist Output directory
allowUpdatePackageJson boolean no false Allow susee to update package.json

EntryPoint

Field Type Required Default Description
entry string yes Entry file path
exportPath "." | "./${string}" yes Export path for this entry
format ("commonjs" | "esm")[] no ["esm"] Output module formats
tsconfigFilePath string no undefined Custom tsconfig path
checks CheckOptions no all false Bundler lint checks
minify boolean | { options: MinifyOptions } no false Minify output

CheckOptions

Field Type Default Description
checkAnonymous boolean false Check for anonymous declarations
checkDefaultExports boolean false Check default exports
checkNpmInstalled boolean false Check that npm deps are installed

TSConfig Resolution Priority

For each entry point, compiler options resolve in this order:

  1. Custom tsconfigFilePath on the entry point
  2. tsconfig.json at the project root
  3. Susee defaults (module: ES2020 for ESM, module: CommonJS for CJS, target: Latest)

Development

npm run build    # compile src/ via oxnode build.ts
npm run lint     # oxlint
npm run fmt      # oxfmt

Key Dependencies

License

Apache-2.0 © Pho Thin Maung

About

TypeScript library bundler.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages