How to Write Plugins
This guide shows how to create plugins for Susee and wire them into your susee.config.ts.
If you want the conceptual overview of plugin categories and execution order, read Plugin Types and Lifecycle.
1. Pick the right plugin type
Choose your plugin type based on when your logic should run:
dependency: transform dependency file objects before mergepre-process: transform merged source code before compilepost-process: transform compiled output before write
2. Understand the plugin shape
A plugin can be:
- An object
- A factory function that returns an object
Each plugin must include:
typeasync(trueorfalse)func
Optional field:
name
3. Write your first plugin
Example A: simple pre-process plugin (sync)
ts
const addBannerPlugin = {
name: "add-banner",
type: "pre-process" as const,
async: false,
func(code: string, file?: string) {
const target = file ?? "unknown";
return `/* bundled from: ${target} */\n${code}`;
},
};Example B: post-process plugin (async)
ts
const replaceBuildFlagPlugin = {
name: "replace-build-flag",
type: "post-process" as const,
async: true,
async func(code: string) {
return code.replaceAll("__BUILD_FLAG__", "true");
},
};Example C: dependency plugin (sync)
ts
import type ts from "typescript";
import type { DepsFiles } from "@suseejs/type";
const trimDependencyCodePlugin = {
name: "trim-dependency-code",
type: "dependency" as const,
async: false,
func(depsFiles: DepsFiles, _compilerOptions: ts.CompilerOptions): DepsFiles {
return depsFiles.map((dep) => ({
...dep,
content: dep.content.trim(),
}));
},
};4. Register plugins in config
ts
import type { SuSeeConfig } from "susee";
const addBannerPlugin = {
name: "add-banner",
type: "pre-process" as const,
async: false,
func(code: string) {
return `/* package build */\n${code}`;
},
};
const config: SuSeeConfig = {
entryPoints: [
{
entry: "src/index.ts",
exportPath: ".",
format: ["esm", "commonjs"],
plugins: [addBannerPlugin],
},
],
};
export default config;5. Use factory plugins when configurable behavior is needed
ts
function createTagPlugin(tag: string) {
return {
name: "tag-plugin",
type: "post-process" as const,
async: false,
func(code: string) {
return `${code}\n/* ${tag} */`;
},
};
}
export default {
entryPoints: [
{
entry: "src/index.ts",
exportPath: ".",
plugins: [createTagPlugin("release")],
},
],
};Note: factory plugins are invoked at plugin-processing stages, so keep factories deterministic and lightweight.
6. Verify plugin behavior
Run builds and inspect output:
sh
npx suseeWhat to check:
- Your transformed code appears in generated
.mjs/.cjsfiles - Sourcemaps still resolve correctly
- Output remains valid JavaScript
Common mistakes
- Returning the wrong shape (
stringvsDepsFiles) - Mutating shared state in ways that break multiple output formats
- Assuming plugin factories run only once
- Producing syntax-invalid output
Best practices
- Keep one plugin focused on one concern
- Make transforms idempotent where possible
- Add
namefor easier debugging - Prefer pure transforms over hidden side effects