Kubb Fabric is a language-agnostic toolkit for generating code and files using JSX and TypeScript. It offers a lightweight layer for file generation while orchestrating the overall process of creating and managing files.
Warning
Fabric is under active development. Until a stable 1.0 release, minor versions may occasionally include breaking changes. Please check release notes and PR titles for breaking changes.
- 🎨 Declarative file generation — Create files effortlessly using JSX or JavaScript syntax.
- 📦 Cross-runtime support — Works seamlessly with Node.js and Bun.
- 🧩 Built-in debugging utilities — Simplify development and inspect generation flows with ease.
- ⚡ Fast and lightweight — Minimal overhead, maximum performance.
Below is a minimal example showing how createFabric works together with plugins and parsers via fabric.use.
import { createFabric } from '@kubb/fabric-core'
import { fsPlugin } from '@kubb/fabric-core/plugins'
import { typescriptParser } from '@kubb/fabric-core/parsers'
const fabric = createFabric()
fabric.use(fsPlugin, {
dryRun: false,
onBeforeWrite: (path, data) => {
console.log('About to write:', path)
},
clean: { path: './generated' },
})
fabric.use(typescriptParser)
await fabric.addFile({
baseName: 'index.ts',
path: './generated/index.ts',
sources: [
{ value: 'export const x = 1', isExportable: true },
],
})
await fabric.write()Creates a file generated/index.ts with the following content:
export const x = 1Returns a Fabric instance with:
fabric.use(pluginOrParser, ...options) => Fabric— register plugins and parsers.fabric.addFile(...files)— queue in-memory files to generate.fabric.files— getter with all queued files.fabric.context— internal context holding events, options, FileManager, installed plugins/parsers.
Factory to create your own createFabric with an optional bootstrap instance(fabric) called on creation.
startendrender { fabric }file:add { files }write:start { files }write:end { files }file:start { file, index, total }file:end { file, index, total }process:start { files }process:progress { file, source, processed, percentage, total }process:end { files }
Writes files to disk on process:progress, supports dry runs and cleaning an output folder before writing.
import { fsPlugin } from '@kubb/fabric-core/plugins'
| Option | Type | Default | Description |
|---|---|---|---|
| dryRun | boolean |
false |
If true, do not write files to disk. |
| onBeforeWrite | (path: string, data: string | undefined) => void | Promise<void> |
— | Called right before each file write on process:progress. |
| clean | { path: string } |
— | If provided, removes the directory at path before writing any files. |
Injected fabric.write options (via fsPlugin):
| Option | Type | Default | Description |
|---|---|---|---|
| extension | Record<Extname, Extname | ''> |
— | Maps input file extensions to output extensions. When set, the matching parser (by extNames) is used. |
Generates index.ts barrel files per folder at process:end. writeEntry creates a single entry barrel at root.
import { barrelPlugin } from '@kubb/fabric-core/plugins'
| Option | Type | Default | Description |
|---|---|---|---|
| root | string |
— | Root directory to generate barrel files for. |
| mode | 'all' | 'named' | 'propagate' | false |
— | Controls how exports are generated: all exports, only named exports, propagate (skip barrels), or disabled. |
| dryRun | boolean |
false |
If true, computes barrels but skips writing. |
Injected fabric.writeEntry parameters (via barrelPlugin):
| Param | Type | Description |
|---|---|---|
| root | string |
Root directory where the entry index.ts should be created. |
| mode | 'all' | 'named' | 'propagate' | false |
Controls which export style to use for the entry barrel. |
Shows a CLI progress bar by listening to core events.
import { progressPlugin } from '@kubb/fabric-core/plugins'
| Option | Type | Default | Description |
|---|---|---|---|
| — | — | — | This plugin has no options, it displays a CLI progress bar by listening to core events. |
Shows a graph of all files
import { graphPlugin } from '@kubb/fabric-core/plugins'
| Option | Type | Default | Description |
|---|---|---|---|
| root | string |
Root directory where to start searching from. | |
| open | boolean |
false | Open a webpage with the generated graph |
Enables rendering React components to the terminal or to a string. Useful for CLI UIs and templating.
import { reactPlugin } from '@kubb/react-fabric/plugins'
| Option | Type | Default | Description |
|---|---|---|---|
| stdout | NodeJS.WriteStream |
— | Optional output stream used to print the rendered content while the app is running. If set, the output is written progressively. |
| stdin | NodeJS.ReadStream |
— | Optional input stream for interactive components. |
| stderr | NodeJS.WriteStream |
— | Optional error output stream. |
| debug | boolean |
— | When true, logs render/unmount information to the console to aid debugging. |
Injected methods (via reactPlugin):
| Method | Signature | Description |
|---|---|---|
render |
(App: React.ElementType) => Promise<void> | void |
Render a React component tree to the terminal and emit the core start event. |
renderToString |
(App: React.ElementType) => Promise<string> | string |
Render a React component tree and return the final output as a string (without writing to stdout). |
waitUntilExit |
() => Promise<void> |
Wait until the rendered app exits, resolves when unmounted and emits the core end event. |
Factory to declare a plugin that can be registered via fabric.use.
| Field | Required | Description |
|---|---|---|
name |
Yes | String identifier of your plugin. |
install(fabric, options) |
Yes | Called when the plugin is registered. You can subscribe to core events and perform side effects here. |
inject?(fabric, options) |
No | Return synchronously the runtime methods/properties to merge into fabric (e.g. write, render). This must not be async. |
Example:
import { createFabric } from '@kubb/fabric-core'
import { createPlugin } from '@kubb/fabric-core/plugins'
const helloPlugin = createPlugin<{ name?: string }, { sayHello: (msg?: string) => void }>({
name: 'helloPlugin',
install(fabric, options) {
fabric.context.events.on('start', () => {
console.log('Fabric started')
})
},
inject(fabric, options) {
return {
sayHello(msg = options?.name ?? 'world') {
console.log(`Hello ${msg}!`)
},
}
},
})
const fabric = createFabric()
await fabric.use(helloPlugin, { name: 'Fabric' })
fabric.sayHello() // -> Hello Fabric!Prints TS/JS imports/exports and sources, supports extname mapping for generated import/export paths.
import { typescriptParser } from '@kubb/fabric-core/parsers'
| Option | Type | Default | Description |
|---|---|---|---|
| file | KubbFile.File |
- | File that will be used to be parsed. |
| extname | string |
'.ts' |
Extension to use when emitting import/export paths (e.g., rewrite ./file to ./file.ts). |
Delegates to typescriptParser with TSX printing settings.
import { tsxParser } from '@kubb/fabric-core/parsers'
| Option | Type | Default | Description |
|---|---|---|---|
| file | KubbFile.File |
- | File that will be used to be parsed. |
| extname | string |
'.tsx' |
Extension to use when emitting import/export paths for TSX/JSX files. |
Fallback parser used when no extension mapping is provided to fabric.write.
import { defaultParser } @kubb/fabric-core/parsers`
| Option | Type | Default | Description |
|---|---|---|---|
| file | KubbFile.File |
- | File that will be used to be parsed. |
Factory to declare a parser that can be registered via fabric.use and selected by extNames during fabirc.write.
| Field | Required | Description |
|---|---|---|
name |
Yes | String identifier of your parser. |
extNames |
Yes | List of file extensions this parser can handle (e.g. ['.ts']). Use undefined for the default parser fallback. |
install(fabric, options) |
No | Optional setup when the parser is registered (subscribe to events, set state, etc.). |
parse(file, { extname }) |
Yes | Must return the final string that will be written for the given file. |
Example:
import { createFabric } from '@kubb/fabric-core'
import { createParser } from '@kubb/fabric-core/parsers'
const vueParser = createParser<{ banner?: string }>({
name: 'vueParser',
extNames: ['.vue'],
async install(fabric, options) {
// Optional setup
},
async parse(file, { extname }) {
const banner = file.options?.banner ?? ''
const sources = file.sources.map(s => s.value).join('\n')
return `${banner}\n${sources}`
},
})
const fabric = createFabric()
fabric.use(vueParser)
fabric.use(fsPlugin); // make it possible to write to the filesystem
fabric.write({ extension: { '.vue': '.ts' } })Note
fabric.useaccepts both plugins and parsers. ThefsPluginhandles I/O and addsfabric.write. Parsers decide how files are converted to strings for specific extensions.- When extension mapping is provided to
fabric.write, Fabric picks a parser whoseextNamesinclude the file’s extension. Otherwise, the default parser is used.
Kubb uses an MIT-licensed open source project with its ongoing development made possible entirely by the support of Sponsors. If you would like to become a sponsor, please consider: