Plugin Types and Lifecycle
Susee supports three plugin types that run at different stages of the build pipeline.
This page explains:
- Which plugin types are available
- The function signature for each type
- Exactly when each type runs in the build lifecycle
For hands-on implementation steps and authoring examples, see How to Write Plugins.
Plugin type summary
Susee plugin definitions come from @suseejs/type and support both sync and async handlers.
1) dependency
Runs during dependency processing, before final code is merged.
- Input:
DepsFiles,compilerOptions - Output: transformed
DepsFiles
Use this when you need to modify dependency file objects (for example, module normalization or file-level transforms before merge).
2) pre-process
Runs after Susee has merged dependency and entry content into one code string, but before TypeScript compilation to .mjs/.cjs.
- Input: merged code string, entry file path
- Output: transformed code string
Use this for code-level edits on the bundled source (banner insertion, rewrites, feature flags, etc.).
3) post-process
Runs after suseeCompiler compiles output code in the Susee compiler stage.
- Input: compiled output code string, entry file path
- Output: transformed code string
Use this for output-level changes that should happen right before files are written.
Plugin object and factory forms
Each item in entryPoints[].plugins can be either:
- A plugin object
- A plugin factory function returning a plugin object
Supported config type:
plugins?: (SuseePlugin | SuseePluginFunction)[];Type signatures
The following shape is supported for each plugin type (sync and async):
type PostProcessPlugin =
| {
type: "post-process";
async: true;
func: (code: string, file?: string) => Promise<string>;
name?: string;
}
| {
type: "post-process";
async: false;
func: (code: string, file?: string) => string;
name?: string;
};
type PreProcessPlugin =
| {
type: "pre-process";
async: true;
func: (code: string, file?: string) => Promise<string>;
name?: string;
}
| {
type: "pre-process";
async: false;
func: (code: string, file?: string) => string;
name?: string;
};
type DependencyPlugin =
| {
type: "dependency";
async: true;
func: (depsFiles: DepsFiles, compilerOptions: ts.CompilerOptions) => Promise<DepsFiles>;
name?: string;
}
| {
type: "dependency";
async: false;
func: (depsFiles: DepsFiles, compilerOptions: ts.CompilerOptions) => DepsFiles;
name?: string;
};Lifecycle order
For each entry and format (esm and/or commonjs), Susee runs this high-level flow:
- Resolve compiler options (
tsconfigFilePath-> roottsconfig.json-> defaults) - Run bundler
- Run
dependencyplugins inside bundler - Run internal bundler normalization steps
- Run
pre-processplugins inside bundler - Compile bundled code with
suseeCompiler - Rename sourcemap reference (
.js.map->.mjs.map/.cjs.map) - Run
post-processplugins in compiler - Write output files
Execution semantics:
- Plugins run in array order.
- Async plugins are awaited.
- Sync and async plugins can be mixed.
Important behavior for plugin factories
When a plugin is provided as a function, Susee invokes the function at each plugin-processing stage.
That means a factory can be called multiple times in a single build:
- In bundler dependency phase
- In bundler pre-process phase
- In compiler post-process phase
And this repeats per output format.
Recommendation:
- Keep plugin factories deterministic and lightweight.
- Avoid relying on one-time mutable state inside factory scope.