Package Exports
- @kubb/fabric-core
- @kubb/fabric-core/package.json
- @kubb/fabric-core/parsers
- @kubb/fabric-core/parsers/typescript
- @kubb/fabric-core/plugins
- @kubb/fabric-core/types
Readme
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.
Features
- π¨ 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.
Write a TypeScript file
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 = 1API Reference
Core
createFabric(options?): Fabric
Returns 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.
Events (emitted by the core during processing)
Fabric emits events throughout its lifecycle that plugins and custom code can listen to. These events provide hooks for monitoring progress, transforming files, and performing custom operations.
Lifecycle Events
lifecycle:startβ Emitted when Fabric begins executionlifecycle:endβ Emitted when Fabric completes executionlifecycle:render { fabric }β Emitted when rendering starts (with reactPlugin)
File Management Events
files:added { files }β Emitted when files are added to the FileManager cachefile:resolve:path { file }β Emitted during file path resolution (allows modification)file:resolve:name { file }β Emitted during file name resolution (allows modification)
File Writing Events
files:writing:start { files }β Emitted before writing files to diskfiles:writing:end { files }β Emitted after files are written to disk
File Processing Events
files:processing:start { files }β Emitted before processing beginsfile:processing:start { file, index, total }β Emitted when each file starts processingfile:processing:end { file, index, total }β Emitted when each file finishes processingfile:processing:update { file, source, processed, percentage, total }β Emitted with progress updatesfiles:processing:end { files }β Emitted when all processing completes
Listening to Events
You can listen to events using the Fabric context:
const fabric = createFabric()
fabric.context.on('lifecycle:start', async () => {
console.log('Starting Fabric...')
})
fabric.context.on('file:processing:update', async ({ processed, total, percentage }) => {
console.log(`Progress: ${percentage.toFixed(1)}% (${processed}/${total})`)
})
fabric.context.on('lifecycle:end', async () => {
console.log('Fabric completed!')
})Plugins
fsPlugin
Writes files to disk on file:processing:update, 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 file:processing:update. |
| 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. |
barrelPlugin
Generates index.ts barrel files per folder when files:writing:start is triggered. 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. |
loggerPlugin
Streams Fabric lifecycle activity as colourful consola logs, optional progress bars, and websocket messages that you can consume from custom tooling.
import { loggerPlugin } from '@kubb/fabric-core/plugins'| Option | Type | Default | Description |
|---|---|---|---|
| level | import('consola').LogLevel |
β | Optional explicit log level passed to createConsola. |
| progress | boolean |
true |
Enable/disable the integrated CLI progress bar. |
| websocket | boolean | { host?: string; port?: number } |
true |
Toggle or configure the websocket server that broadcasts Fabric events for future GUIs. |
By default the plugin displays a progress bar, starts a websocket server on an ephemeral port, and announces the URL. Every key lifecycle hook (start, process:*, file:*, write:*, end) is logged with a Fabric tag, animated in the progress bar, and broadcast to connected clientsβperfect for building dashboards on top of Fabric.
graphPlugin
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 |
reactPlugin
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. |
definePlugin
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 { definePlugin } from '@kubb/fabric-core/plugins'
const helloPlugin = definePlugin<{ name?: string }, { sayHello: (msg?: string) => void }>({
name: 'helloPlugin',
install(fabric, options) {
fabric.context.events.on('lifecycle: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!Parsers
typescriptParser
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). |
tsxParser
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. |
defaultParser
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. |
defineParser
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 { defineParser } from '@kubb/fabric-core/parsers'
const vueParser = defineParser<{ 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.