React integration for Kubb's fabric - JSX runtime and component-based code generation with React reconciler for building type-safe generators
npm install @kubb/react-fabric[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![Coverage][coverage-src]][coverage-href]
[![License][license-src]][license-href]
[![Sponsors][sponsors-src]][sponsors-href]
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.
``ts
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 },
],
imports: [],
exports: [],
})
await fabric.write()
`
Creates a file generated/index.ts with the following content:`ts`
export const x = 1
β 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.$3
- lifecycle:start
- lifecycle:end
- lifecycle:render { fabric }
- files:added { files }
- files:writing:start { files }
- files:writing:end { files }
- file:processing:start { file, index, total }
- file:processing:end { file, index, total }
- files:processing:start { files }
- files:processing:update { file, source, processed, percentage, total }
- files:processing:end { files }
Plugins
#### fsPlugin
Writes files to disk on files: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 | β | Called right before each file write on files: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 | β | 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 | Render a React component tree to the terminal and emit the core start event. |
| renderToString | (App: React.ElementType) => Promise | Render a React component tree and return the final output as a string (without writing to stdout). |
| waitUntilExit | () => Promise | Wait until the rendered app exits, resolves when unmounted and emits the core end event. |####
definePluginFactory 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:
`ts
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
#### typescriptParserPrints 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). |####
tsxParserDelegates 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. |####
defaultParserFallback 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:
`ts
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.use accepts both plugins and parsers. The fsPlugin handles I/O and adds fabric.write. Parsers decide how files are converted to strings for specific extensions.
> - When extension mapping is provided to fabric.write, Fabric picks a parser whose extNames` include the fileβs extension. Otherwise, the default parser is used.[npm-version-src]: https://img.shields.io/npm/v/@kubb/fabric-core?flat&colorA=18181B&colorB=f58517
[npm-version-href]: https://npmjs.com/package/@kubb/fabric-core
[npm-downloads-src]: https://img.shields.io/npm/dm/@kubb/fabric-core?flat&colorA=18181B&colorB=f58517
[npm-downloads-href]: https://npmjs.com/package/@kubb/fabric-core
[license-src]: https://img.shields.io/github/license/kubb-labs/fabric.svg?flat&colorA=18181B&colorB=f58517
[license-href]: https://github.com/kubb-labs/fabric/blob/main/LICENSE
[build-src]: https://img.shields.io/github/actions/workflow/status/kubb-labs/fabric/ci.yaml?style=flat&colorA=18181B&colorB=f58517
[build-href]: https://www.npmjs.com/package/@kubb/fabric-core
[minified-src]: https://img.shields.io/bundlephobia/min/@kubb/fabric-core?style=flat&colorA=18181B&colorB=f58517
[minified-href]: https://www.npmjs.com/package/@kubb/fabric-core
[coverage-src]: https://img.shields.io/codecov/c/github/kubb-labs/fabric?style=flat&colorA=18181B&colorB=f58517
[coverage-href]: https://www.npmjs.com/package/@kubb/fabric-core
[contributors-src]: https://img.shields.io/github/contributors/kubb-labs/fabric?style=flat&colorA=18181B&colorB=f58517&label=%20
[contributors-href]: #contributors-
[sponsors-src]: https://img.shields.io/github/sponsors/stijnvanhulle?style=flat&colorA=18181B&colorB=f58517
[sponsors-href]: https://github.com/sponsors/stijnvanhulle/