@kubb/fabric-core 0.12.10 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +55 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +2 -2
- package/dist/{Root-D0c_2tPd.js → onProcessExit-7fgW-UTF.js} +48 -34
- package/dist/onProcessExit-7fgW-UTF.js.map +1 -0
- package/dist/{Root-Cjrx-0QZ.cjs → onProcessExit-C7dVg0S0.cjs} +53 -33
- package/dist/onProcessExit-C7dVg0S0.cjs.map +1 -0
- package/dist/plugins.cjs +43 -277
- package/dist/plugins.cjs.map +1 -1
- package/dist/plugins.d.cts +7 -37
- package/dist/plugins.d.ts +7 -37
- package/dist/plugins.js +30 -259
- package/dist/plugins.js.map +1 -1
- package/package.json +6 -16
- package/src/FileManager.ts +6 -2
- package/src/createFile.ts +12 -45
- package/src/index.ts +3 -2
- package/src/plugins/fsPlugin.ts +8 -6
- package/src/plugins/fsxPlugin/Runtime.ts +4 -7
- package/src/plugins/index.ts +0 -1
- package/src/plugins/loggerPlugin.ts +15 -192
- package/src/utils/onProcessExit.ts +35 -0
- package/dist/Root-Cjrx-0QZ.cjs.map +0 -1
- package/dist/Root-D0c_2tPd.js.map +0 -1
- package/src/plugins/graphPlugin.ts +0 -136
package/dist/plugins.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins.js","names":[],"sources":["../src/plugins/definePlugin.ts","../src/plugins/barrelPlugin.ts","../src/plugins/fsPlugin.ts","../src/plugins/fsxPlugin/Runtime.ts","../src/plugins/fsxPlugin/fsxPlugin.ts","../src/utils/open.ts","../src/plugins/graphPlugin.ts","../src/plugins/loggerPlugin.ts"],"sourcesContent":["import type { Plugin, UserPlugin } from './types.ts'\n\n/**\n * Defines a Fabric plugin with type safety.\n *\n * Use this function to create plugins that hook into Fabric's lifecycle\n * events and extend its functionality.\n *\n * @param plugin - The plugin configuration object\n * @returns A typed plugin ready to use with Fabric\n *\n * @example\n * ```ts\n * import { definePlugin } from '@kubb/fabric-core'\n *\n * export const myPlugin = definePlugin({\n * name: 'my-plugin',\n * async setup(fabric) {\n * fabric.context.on('write:start', (files) => {\n * console.log(`Writing ${files.length} files`)\n * })\n * }\n * })\n * ```\n */\nexport function definePlugin<Options = unknown, TAppExtension extends Record<string, any> = {}>(\n plugin: UserPlugin<Options, TAppExtension>,\n): Plugin<Options, TAppExtension> {\n return {\n type: 'plugin',\n ...plugin,\n }\n}\n","/** biome-ignore-all lint/suspicious/useIterableCallbackReturn: not needed */\n\nimport path from 'node:path'\nimport { createFile } from '../createFile.ts'\nimport type * as KubbFile from '../KubbFile.ts'\nimport { getRelativePath } from '../utils/getRelativePath.ts'\nimport { TreeNode } from '../utils/TreeNode.ts'\nimport { definePlugin } from './definePlugin.ts'\n\ntype Mode = 'all' | 'named' | 'propagate' | false\n\ntype Options = {\n root: string\n mode: Mode\n dryRun?: boolean\n}\n\ntype WriteEntryOptions = {\n root: string\n mode: Mode\n}\n\ntype ExtendOptions = {\n /**\n * `fabric.writeEntry` should be called before `fabric.write`\n */\n writeEntry(options: WriteEntryOptions): Promise<void>\n}\n\ndeclare global {\n namespace Kubb {\n interface Fabric {\n /**\n * `fabric.writeEntry` should be called before `fabric.write`\n */\n writeEntry(options: WriteEntryOptions): Promise<void>\n }\n }\n}\n\ntype GetBarrelFilesOptions = {\n files: KubbFile.File[]\n root: string\n mode: Mode\n}\n\nexport function getBarrelFiles({ files, root, mode }: GetBarrelFilesOptions): Array<KubbFile.File> {\n // Do not generate when propagating or disabled\n if (mode === 'propagate' || mode === false) {\n return []\n }\n\n const indexableSourcesMap = new Map<KubbFile.File, Array<KubbFile.Source>>()\n\n for (const file of files) {\n const indexableSources: Array<KubbFile.Source> = []\n for (const source of file.sources || []) {\n if (source.isIndexable && source.name) {\n indexableSources.push(source)\n }\n }\n if (indexableSources.length > 0) {\n indexableSourcesMap.set(file, indexableSources)\n }\n }\n\n const cachedFiles = new Map<KubbFile.Path, KubbFile.File>()\n const dedupe = new Map<KubbFile.Path, Set<string>>()\n\n const treeNode = TreeNode.fromFiles(files, root)\n\n if (!treeNode) {\n return []\n }\n\n treeNode.forEach((node) => {\n // Only create a barrel for directory-like nodes that have a parent with a path\n if (!node?.children || !node.parent?.data.path) {\n return\n }\n\n const parentPath = node.parent.data.path as KubbFile.Path\n const barrelPath = path.join(parentPath, 'index.ts') as KubbFile.Path\n\n let barrelFile = cachedFiles.get(barrelPath)\n if (!barrelFile) {\n barrelFile = createFile({\n path: barrelPath,\n baseName: 'index.ts',\n imports: [],\n exports: [],\n sources: [],\n })\n cachedFiles.set(barrelPath, barrelFile)\n dedupe.set(barrelPath, new Set<string>())\n }\n\n const seen = dedupe.get(barrelPath)!\n\n for (const leaf of node.leaves) {\n const file = leaf.data.file\n if (!file || !file.path) {\n continue\n }\n\n const indexableSources = indexableSourcesMap.get(file)\n if (!indexableSources) {\n continue\n }\n\n for (const source of indexableSources) {\n const key = `${source.name}|${source.isTypeOnly ? '1' : '0'}`\n if (seen.has(key)) {\n continue\n }\n seen.add(key)\n\n // Always compute relative path from the parent directory to the file path\n barrelFile.exports!.push({\n name: [source.name!],\n path: getRelativePath(parentPath, file.path),\n isTypeOnly: source.isTypeOnly,\n })\n\n barrelFile!.sources.push({\n name: source.name!,\n isTypeOnly: source.isTypeOnly,\n value: '', // TODO use parser to generate import\n isExportable: mode === 'all' || mode === 'named',\n isIndexable: mode === 'all' || mode === 'named',\n })\n }\n }\n })\n\n const result = [...cachedFiles.values()]\n\n if (mode === 'all') {\n return result.map((file) => ({\n ...file,\n exports: file.exports?.map((e) => ({ ...e, name: undefined })),\n }))\n }\n\n return result\n}\n\nexport const barrelPlugin = definePlugin<Options, ExtendOptions>({\n name: 'barrel',\n install(ctx, options) {\n if (!options) {\n throw new Error('Barrel plugin requires options.root and options.mode')\n }\n\n if (!options.mode) {\n return undefined\n }\n\n ctx.on('files:writing:start', async (files) => {\n const root = options.root\n const barrelFiles = getBarrelFiles({ files, root, mode: options.mode })\n\n await ctx.fileManager.add(...barrelFiles)\n })\n },\n inject(ctx, options) {\n if (!options) {\n throw new Error('Barrel plugin requires options.root and options.mode')\n }\n\n return {\n async writeEntry({ root, mode }) {\n if (!mode || mode === 'propagate') {\n return undefined\n }\n\n const rootPath = path.resolve(root, 'index.ts')\n\n const barrelFiles: Array<KubbFile.ResolvedFile> = []\n for (const file of ctx.files) {\n for (const source of file.sources) {\n if (source.isIndexable) {\n barrelFiles.push(file)\n\n break\n }\n }\n }\n\n const fileTypeCache = new Map<KubbFile.ResolvedFile, boolean>()\n for (const file of barrelFiles) {\n fileTypeCache.set(\n file,\n file.sources.every((source) => source.isTypeOnly),\n )\n }\n\n const exports: Array<KubbFile.Export> = []\n for (const file of barrelFiles) {\n const containsOnlyTypes = fileTypeCache.get(file) ?? false\n\n for (const source of file.sources) {\n if (!file.path || !source.isIndexable) {\n continue\n }\n\n exports.push({\n name: mode === 'all' ? undefined : [source.name],\n path: getRelativePath(rootPath, file.path),\n isTypeOnly: mode === 'all' ? containsOnlyTypes : source.isTypeOnly,\n } as KubbFile.Export)\n }\n }\n\n const entryFile = createFile({\n path: rootPath,\n baseName: 'index.ts',\n imports: [],\n exports,\n sources: [],\n })\n\n await ctx.addFile(entryFile)\n\n await ctx.fileManager.write({\n mode: ctx.config.mode,\n dryRun: options.dryRun,\n parsers: ctx.installedParsers,\n })\n },\n }\n },\n})\n","import { resolve } from 'node:path'\nimport fs from 'fs-extra'\nimport type * as KubbFile from '../KubbFile.ts'\nimport { definePlugin } from './definePlugin.ts'\n\ntype WriteOptions = {\n extension?: Record<KubbFile.Extname, KubbFile.Extname | ''>\n}\n\ntype Options = {\n dryRun?: boolean\n /**\n * Optional callback that is invoked whenever a file is written by the plugin.\n * Useful for tests to observe write operations without spying on internal functions.\n */\n onBeforeWrite?: (path: string, data: string | undefined) => void | Promise<void>\n clean?: {\n path: string\n }\n}\n\ntype ExtendOptions = {\n write(options?: WriteOptions): Promise<void>\n}\n\nexport async function write(path: string, data: string | undefined, options: { sanity?: boolean } = {}): Promise<string | undefined> {\n if (typeof Bun !== 'undefined') {\n if (!data || data?.trim() === '') {\n return undefined\n }\n\n await Bun.write(resolve(path), data.trim())\n\n if (options?.sanity) {\n const file = Bun.file(resolve(path))\n const savedData = await file.text()\n\n if (savedData?.toString() !== data?.toString()) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n\n return savedData\n }\n\n return data\n }\n\n if (!data || data?.trim() === '') {\n return undefined\n }\n\n try {\n const oldContent = await fs.readFile(resolve(path), {\n encoding: 'utf-8',\n })\n if (oldContent?.toString() === data?.toString()) {\n return\n }\n } catch (_err) {\n /* empty */\n }\n\n await fs.outputFile(resolve(path), data.trim(), { encoding: 'utf-8' })\n\n if (options?.sanity) {\n const savedData = await fs.readFile(resolve(path), {\n encoding: 'utf-8',\n })\n\n if (savedData?.toString() !== data?.toString()) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n\n return savedData\n }\n\n return data\n}\n\ndeclare global {\n namespace Kubb {\n interface Fabric {\n write(options?: WriteOptions): Promise<void>\n }\n }\n}\n\nexport const fsPlugin = definePlugin<Options, ExtendOptions>({\n name: 'fs',\n install(ctx, options = {}) {\n if (options.clean) {\n fs.removeSync(options.clean.path)\n }\n\n ctx.on('file:processing:update', async ({ file, source }) => {\n if (options.onBeforeWrite) {\n await options.onBeforeWrite(file.path, source)\n }\n await write(file.path, source, { sanity: false })\n })\n },\n inject(ctx, { dryRun } = {}) {\n return {\n async write(\n options = {\n extension: { '.ts': '.ts' },\n },\n ) {\n await ctx.fileManager.write({\n mode: ctx.config.mode,\n extension: options.extension,\n dryRun,\n parsers: ctx.installedParsers,\n })\n\n await ctx.emit('lifecycle:end')\n },\n }\n },\n})\n","import { onExit } from 'signal-exit'\nimport { Root } from '../../components/Root.ts'\nimport type { ComponentNode } from '../../composables/useNodeTree.ts'\nimport type { FabricElement } from '../../Fabric.ts'\nimport type { FileManager } from '../../FileManager.ts'\nimport { TreeNode } from '../../utils/TreeNode.ts'\n\ntype Options = {\n fileManager: FileManager\n treeNode?: TreeNode<ComponentNode>\n debug?: boolean\n}\n\nexport class Runtime {\n readonly #options: Options\n exitPromise?: Promise<void>\n\n constructor(options: Options) {\n this.#options = options\n\n // Unmount when process exits\n this.unsubscribeExit = onExit(\n (code) => {\n this.unmount(code)\n },\n { alwaysLast: false },\n ).bind(this)\n }\n\n get fileManager() {\n return this.#options.fileManager\n }\n\n #renderPromise: Promise<void> = Promise.resolve()\n resolveExitPromise: () => void = () => {}\n rejectExitPromise: (reason?: Error) => void = () => {}\n unsubscribeExit: () => void = () => {}\n\n onError(error: Error): void {\n throw error\n }\n\n onExit(error?: Error): void {\n setTimeout(() => {\n this.unmount(error)\n }, 0)\n }\n\n async render(node: FabricElement): Promise<string> {\n const treeNode = this.#options.treeNode || new TreeNode<ComponentNode>({ type: 'Root', props: {} })\n\n const props = {\n fileManager: this.fileManager,\n treeNode,\n onExit: this.onExit.bind(this),\n onError: this.onError.bind(this),\n }\n\n try {\n treeNode.data.props = props\n\n const element = Root({ ...props, children: node })\n\n await this.#renderPromise\n\n return element()?.toString() || ''\n } catch (e) {\n props.onError(e as Error)\n return ''\n }\n }\n\n unmount(error?: Error | number | null): void {\n if (this.#options?.debug) {\n console.log('Unmount', error)\n }\n\n this.unsubscribeExit()\n\n if (error instanceof Error) {\n this.rejectExitPromise(error)\n return\n }\n\n this.resolveExitPromise()\n }\n\n async waitUntilExit(): Promise<void> {\n if (!this.exitPromise) {\n this.exitPromise = new Promise((resolve, reject) => {\n this.resolveExitPromise = resolve\n this.rejectExitPromise = reject\n })\n }\n\n return this.exitPromise\n }\n}\n","import type { ComponentNode } from '../../composables/useNodeTree.ts'\nimport type { FabricElement } from '../../Fabric.ts'\nimport { definePlugin } from '../../plugins/definePlugin.ts'\nimport type { TreeNode } from '../../utils/TreeNode.ts'\nimport { Runtime } from './Runtime.ts'\n\nexport type Options = {\n treeNode?: TreeNode<ComponentNode>\n /**\n * Set this to true to always see the result of the render in the console(line per render)\n */\n debug?: boolean\n}\n\ntype ExtendOptions = {\n render(App: FabricElement<any>): Promise<string>\n waitUntilExit(): Promise<void>\n}\n\ndeclare global {\n namespace Kubb {\n interface Fabric {\n render(App: FabricElement<any>): Promise<string>\n waitUntilExit(): Promise<void>\n }\n }\n}\n\nexport const fsxPlugin = definePlugin<Options, ExtendOptions>({\n name: 'fsx',\n install() {},\n inject(ctx, options = {}) {\n const runtime = new Runtime({ fileManager: ctx.fileManager, ...options })\n\n return {\n async render(App) {\n await ctx.emit('lifecycle:start')\n return runtime.render(App)\n },\n async waitUntilExit() {\n await runtime.waitUntilExit()\n },\n }\n },\n})\n","import { spawn } from 'node:child_process'\n\nconst spawnBin = (bin: string, args: string[]): Promise<boolean> => {\n return new Promise((resolve) => {\n const process = spawn(bin, args, {\n detached: true,\n shell: false,\n windowsHide: true,\n })\n\n process.on('close', (code) => {\n resolve(!code)\n })\n })\n}\n\ntype Options = {\n app?: string\n}\n\nexport async function open(path: string, options?: Options): Promise<boolean> {\n const app = options?.app\n\n if (process.platform === 'win32') {\n return spawnBin('cmd.exe', ['/c', 'start', app || '', path.replace(/[&^]/g, '^$&')])\n }\n\n if (process.platform === 'linux') {\n return spawnBin(app || 'xdg-open', [path])\n }\n if (process.platform === 'darwin') {\n return spawnBin('open', app ? ['-a', app, path] : [path])\n }\n\n throw new Error(`Unsupported platform, could not open \"${path}\"`)\n}\n","import http from 'node:http'\nimport type { AddressInfo } from 'node:net'\nimport path from 'node:path'\nimport handler from 'serve-handler'\nimport { createFile } from '../createFile.ts'\nimport type * as KubbFile from '../KubbFile.ts'\nimport { open } from '../utils/open.ts'\nimport { type Graph, TreeNode } from '../utils/TreeNode.ts'\nimport { definePlugin } from './definePlugin.ts'\n\ntype Options = {\n root: string\n /**\n * @default false\n */\n open?: boolean\n}\n\ntype GetGraphOptions = {\n files: KubbFile.File[]\n root: string\n}\n\nexport function getGraph({ files, root }: GetGraphOptions): Graph | undefined {\n const treeNode = TreeNode.fromFiles(files, root)\n\n if (!treeNode) {\n return undefined\n }\n\n return TreeNode.toGraph(treeNode)\n}\nconst html = `\n <!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <title>File Graph</title>\n <script type=\"module\">\n import { Network } from 'https://cdn.jsdelivr.net/npm/vis-network/standalone/esm/vis-network.min.js'\n\n async function main() {\n const res = await fetch('./graph.json')\n const { nodes, edges } = await res.json()\n const container = document.getElementById('graph')\n\n const network = new Network(\n container,\n { nodes, edges },\n {\n layout: { hierarchical: { direction: 'UD', sortMethod: 'directed' } },\n nodes: { shape: 'box', font: { face: 'monospace' } },\n edges: { arrows: 'to' },\n physics: false,\n },\n )\n }\n\n main()\n </script>\n <style>\n html, body, #graph { height: 100%; margin: 0; }\n </style>\n </head>\n <body>\n <div id=\"graph\"></div>\n </body>\n</html>\n`\n\nasync function serve(root: string) {\n const server = http.createServer((req, res) => {\n return handler(req, res, {\n public: root,\n cleanUrls: true,\n })\n })\n\n server.listen(0, async () => {\n const { port } = server.address() as AddressInfo\n console.log(`Running on http://localhost:${port}/graph.html`)\n\n await open(`http://localhost:${port}/graph.html`)\n })\n}\n\nexport const graphPlugin = definePlugin<Options>({\n name: 'graph',\n install(ctx, options) {\n if (!options) {\n throw new Error('Graph plugin requires options.root and options.mode')\n }\n\n ctx.on('files:writing:start', async (files) => {\n const root = options.root\n\n const graph = getGraph({ files, root })\n\n if (!graph) {\n return undefined\n }\n\n const graphFile = createFile({\n baseName: 'graph.json',\n path: path.join(root, 'graph.json'),\n sources: [\n {\n name: 'graph',\n value: JSON.stringify(graph, null, 2),\n },\n ],\n imports: [],\n exports: [],\n })\n\n const graphHtmlFile = createFile({\n baseName: 'graph.html',\n path: path.join(root, 'graph.html'),\n sources: [\n {\n name: 'graph',\n value: html,\n },\n ],\n imports: [],\n exports: [],\n })\n\n await ctx.addFile(graphFile, graphHtmlFile)\n\n if (options.open) {\n await serve(root)\n }\n })\n },\n})\n","import http from 'node:http'\nimport type { AddressInfo } from 'node:net'\nimport { relative } from 'node:path'\nimport * as clack from '@clack/prompts'\nimport pc from 'picocolors'\nimport { WebSocket, WebSocketServer } from 'ws'\nimport type { FabricEvents } from '../Fabric.ts'\nimport type * as KubbFile from '../KubbFile.ts'\nimport { definePlugin } from './definePlugin.ts'\n\ntype Broadcast = <T = unknown>(event: keyof FabricEvents | string, payload: T) => void\ntype WebSocketOptions = {\n /**\n * Hostname to bind the websocket server to.\n * @default '127.0.0.1'\n */\n host?: string\n /**\n * Port to bind the websocket server to.\n * @default 0 (random available port)\n */\n port?: number\n}\n\ntype Options = {\n /**\n * Toggle progress bar output.\n * @default true\n */\n progress?: boolean\n /**\n * Toggle or configure the websocket broadcast server.\n * When `true`, a websocket server is started on an ephemeral port.\n * When `false`, websocket support is disabled.\n * When providing an object, the server uses the supplied host and port.\n * @default true\n */\n websocket?: boolean | WebSocketOptions\n}\n\nfunction normalizeAddress(address: AddressInfo): {\n host: string\n port: number\n} {\n const host = address.address === '::' ? '127.0.0.1' : address.address\n\n return { host, port: address.port }\n}\n\nfunction serializeFile(file: KubbFile.File | KubbFile.ResolvedFile) {\n return {\n path: file.path,\n baseName: file.baseName,\n name: 'name' in file ? file.name : undefined,\n extname: 'extname' in file ? file.extname : undefined,\n }\n}\n\nfunction pluralize(word: string, count: number) {\n return `${count} ${word}${count === 1 ? '' : 's'}`\n}\n\nconst DEFAULT_PROGRESS_BAR_SIZE = 30\n\nexport const loggerPlugin = definePlugin<Options>({\n name: 'logger',\n install(ctx, options = {}) {\n const { websocket = true, progress = true } = options\n\n const state = {\n spinner: clack.spinner(),\n isSpinning: false,\n progressBar: undefined as ReturnType<typeof clack.progress> | undefined,\n }\n\n function formatPath(path: string) {\n return relative(process.cwd(), path)\n }\n\n let server: http.Server | undefined\n let wss: WebSocketServer | undefined\n\n const broadcast: Broadcast = (event, payload) => {\n if (!wss) {\n return\n }\n\n const message = JSON.stringify({ event, payload })\n\n for (const client of wss.clients) {\n if (client.readyState === WebSocket.OPEN) {\n client.send(message)\n }\n }\n }\n\n if (websocket) {\n const { host = '127.0.0.1', port = 0 } = typeof websocket === 'boolean' ? {} : websocket\n\n server = http.createServer()\n wss = new WebSocketServer({ server })\n\n server.listen(port, host, () => {\n const addressInfo = server?.address()\n\n if (addressInfo && typeof addressInfo === 'object') {\n const { host: resolvedHost, port: resolvedPort } = normalizeAddress(addressInfo)\n const url = `ws://${resolvedHost}:${resolvedPort}`\n\n clack.log.info(`${pc.blue('ℹ')} Logger websocket listening on ${url}`)\n broadcast('websocket:ready', { url })\n }\n })\n\n wss.on('connection', (socket) => {\n clack.log.info(`${pc.blue('ℹ')} Logger websocket client connected`)\n socket.send(\n JSON.stringify({\n event: 'welcome',\n payload: {\n message: 'Connected to Fabric log stream',\n timestamp: Date.now(),\n },\n }),\n )\n })\n\n wss.on('error', (error) => {\n clack.log.error(`${pc.red('✗')} Logger websocket error: ${error.message}`)\n })\n }\n\n ctx.on('lifecycle:start', async () => {\n clack.intro(`${pc.blue('Fabric')} ${pc.dim('Starting run')}`)\n broadcast('lifecycle:start', { timestamp: Date.now() })\n })\n\n ctx.on('lifecycle:render', async () => {\n clack.log.info(`${pc.blue('ℹ')} Rendering application graph`)\n broadcast('lifecycle:render', { timestamp: Date.now() })\n })\n\n ctx.on('files:added', async (files) => {\n if (!files.length) {\n return\n }\n\n clack.log.info(`${pc.blue('ℹ')} Queued ${pluralize('file', files.length)}`)\n broadcast('files:added', {\n files: files.map(serializeFile),\n })\n })\n\n ctx.on('file:resolve:path', async (file) => {\n clack.log.step(`Resolving path for ${pc.dim(formatPath(file.path))}`)\n broadcast('file:resolve:path', { file: serializeFile(file) })\n })\n\n ctx.on('file:resolve:name', async (file) => {\n clack.log.step(`Resolving name for ${pc.dim(formatPath(file.path))}`)\n broadcast('file:resolve:name', { file: serializeFile(file) })\n })\n\n ctx.on('files:processing:start', async (files) => {\n clack.log.step(`Processing ${pc.green(pluralize('file', files.length))}`)\n broadcast('files:processing:start', {\n total: files.length,\n timestamp: Date.now(),\n })\n\n if (progress) {\n state.progressBar = clack.progress({\n style: 'block',\n max: files.length,\n size: DEFAULT_PROGRESS_BAR_SIZE,\n })\n state.progressBar.start(`Processing ${files.length} files`)\n }\n })\n\n ctx.on('file:processing:start', async (file, index, total) => {\n if (!state.progressBar) {\n clack.log.step(`Processing ${pc.dim(`[${index + 1}/${total}]`)} ${formatPath(file.path)}`)\n }\n\n broadcast('file:processing:start', {\n index,\n total,\n file: serializeFile(file),\n })\n })\n\n ctx.on('file:processing:update', async ({ processed, total, percentage, file }) => {\n broadcast('file:processing:update', {\n processed,\n total,\n percentage,\n file: serializeFile(file),\n })\n\n if (state.progressBar) {\n // undefined = auto-increment by 1\n state.progressBar.advance(undefined, `Writing ${formatPath(file.path)}`)\n } else {\n const formattedPercentage = Number.isFinite(percentage) ? percentage.toFixed(1) : '0.0'\n clack.log.step(`Progress ${pc.green(`${formattedPercentage}%`)} ${pc.dim(`(${processed}/${total})`)} → ${formatPath(file.path)}`)\n }\n })\n\n ctx.on('file:processing:end', async (file, index, total) => {\n if (state.progressBar) {\n state.progressBar.message(`${pc.green('✓')} Finished ${pc.dim(`[${index + 1}/${total}]`)} ${formatPath(file.path)}`)\n } else {\n clack.log.success(`${pc.green('✓')} Finished ${pc.dim(`[${index + 1}/${total}]`)} ${formatPath(file.path)}`)\n }\n\n broadcast('file:processing:end', {\n index,\n total,\n file: serializeFile(file),\n })\n })\n\n ctx.on('files:writing:start', async (files) => {\n broadcast('files:writing:start', {\n files: files.map(serializeFile),\n })\n })\n\n ctx.on('files:writing:end', async (files) => {\n broadcast('files:writing:end', {\n files: files.map(serializeFile),\n })\n })\n\n ctx.on('files:processing:end', async (files) => {\n if (state.progressBar) {\n state.progressBar.stop(`${pc.green('✓')} Processed ${pluralize('file', files.length)}`)\n state.progressBar = undefined\n } else {\n clack.log.success(`${pc.green('✓')} Processed ${pluralize('file', files.length)}`)\n }\n\n broadcast('files:processing:end', {\n total: files.length,\n timestamp: Date.now(),\n })\n })\n\n ctx.on('lifecycle:end', async () => {\n if (state.progressBar) {\n state.progressBar.stop()\n state.progressBar = undefined\n }\n\n clack.outro(`${pc.blue('Fabric')} ${pc.dim('completed')}`)\n\n broadcast('lifecycle:end', { timestamp: Date.now() })\n\n const closures: Array<Promise<void>> = []\n\n if (wss) {\n const wsServer = wss\n\n closures.push(\n new Promise((resolve) => {\n for (const client of wsServer.clients) {\n client.close()\n }\n wsServer.close(() => resolve())\n }),\n )\n }\n\n if (server) {\n const httpServer = server\n\n closures.push(\n new Promise((resolve) => {\n httpServer.close(() => resolve())\n }),\n )\n }\n\n if (closures.length) {\n await Promise.allSettled(closures)\n clack.log.info(`${pc.blue('ℹ')} Logger websocket closed`)\n }\n })\n },\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugins.js","names":[],"sources":["../src/plugins/definePlugin.ts","../src/plugins/barrelPlugin.ts","../src/plugins/fsPlugin.ts","../src/plugins/fsxPlugin/Runtime.ts","../src/plugins/fsxPlugin/fsxPlugin.ts","../src/plugins/loggerPlugin.ts"],"sourcesContent":["import type { Plugin, UserPlugin } from './types.ts'\n\n/**\n * Defines a Fabric plugin with type safety.\n *\n * Use this function to create plugins that hook into Fabric's lifecycle\n * events and extend its functionality.\n *\n * @param plugin - The plugin configuration object\n * @returns A typed plugin ready to use with Fabric\n *\n * @example\n * ```ts\n * import { definePlugin } from '@kubb/fabric-core'\n *\n * export const myPlugin = definePlugin({\n * name: 'my-plugin',\n * async setup(fabric) {\n * fabric.context.on('write:start', (files) => {\n * console.log(`Writing ${files.length} files`)\n * })\n * }\n * })\n * ```\n */\nexport function definePlugin<Options = unknown, TAppExtension extends Record<string, any> = {}>(\n plugin: UserPlugin<Options, TAppExtension>,\n): Plugin<Options, TAppExtension> {\n return {\n type: 'plugin',\n ...plugin,\n }\n}\n","/** biome-ignore-all lint/suspicious/useIterableCallbackReturn: not needed */\n\nimport path from 'node:path'\nimport { createFile } from '../createFile.ts'\nimport type * as KubbFile from '../KubbFile.ts'\nimport { getRelativePath } from '../utils/getRelativePath.ts'\nimport { TreeNode } from '../utils/TreeNode.ts'\nimport { definePlugin } from './definePlugin.ts'\n\ntype Mode = 'all' | 'named' | 'propagate' | false\n\ntype Options = {\n root: string\n mode: Mode\n dryRun?: boolean\n}\n\ntype WriteEntryOptions = {\n root: string\n mode: Mode\n}\n\ntype ExtendOptions = {\n /**\n * `fabric.writeEntry` should be called before `fabric.write`\n */\n writeEntry(options: WriteEntryOptions): Promise<void>\n}\n\ndeclare global {\n namespace Kubb {\n interface Fabric {\n /**\n * `fabric.writeEntry` should be called before `fabric.write`\n */\n writeEntry(options: WriteEntryOptions): Promise<void>\n }\n }\n}\n\ntype GetBarrelFilesOptions = {\n files: KubbFile.File[]\n root: string\n mode: Mode\n}\n\nexport function getBarrelFiles({ files, root, mode }: GetBarrelFilesOptions): Array<KubbFile.File> {\n // Do not generate when propagating or disabled\n if (mode === 'propagate' || mode === false) {\n return []\n }\n\n const indexableSourcesMap = new Map<KubbFile.File, Array<KubbFile.Source>>()\n\n for (const file of files) {\n const indexableSources: Array<KubbFile.Source> = []\n for (const source of file.sources || []) {\n if (source.isIndexable && source.name) {\n indexableSources.push(source)\n }\n }\n if (indexableSources.length > 0) {\n indexableSourcesMap.set(file, indexableSources)\n }\n }\n\n const cachedFiles = new Map<KubbFile.Path, KubbFile.File>()\n const dedupe = new Map<KubbFile.Path, Set<string>>()\n\n const treeNode = TreeNode.fromFiles(files, root)\n\n if (!treeNode) {\n return []\n }\n\n treeNode.forEach((node) => {\n // Only create a barrel for directory-like nodes that have a parent with a path\n if (!node?.children || !node.parent?.data.path) {\n return\n }\n\n const parentPath = node.parent.data.path as KubbFile.Path\n const barrelPath = path.join(parentPath, 'index.ts') as KubbFile.Path\n\n let barrelFile = cachedFiles.get(barrelPath)\n if (!barrelFile) {\n barrelFile = createFile({\n path: barrelPath,\n baseName: 'index.ts',\n imports: [],\n exports: [],\n sources: [],\n })\n cachedFiles.set(barrelPath, barrelFile)\n dedupe.set(barrelPath, new Set<string>())\n }\n\n const seen = dedupe.get(barrelPath)!\n\n for (const leaf of node.leaves) {\n const file = leaf.data.file\n if (!file || !file.path) {\n continue\n }\n\n const indexableSources = indexableSourcesMap.get(file)\n if (!indexableSources) {\n continue\n }\n\n for (const source of indexableSources) {\n const key = `${source.name}|${source.isTypeOnly ? '1' : '0'}`\n if (seen.has(key)) {\n continue\n }\n seen.add(key)\n\n // Always compute relative path from the parent directory to the file path\n barrelFile.exports!.push({\n name: [source.name!],\n path: getRelativePath(parentPath, file.path),\n isTypeOnly: source.isTypeOnly,\n })\n\n barrelFile!.sources.push({\n name: source.name!,\n isTypeOnly: source.isTypeOnly,\n value: '', // TODO use parser to generate import\n isExportable: mode === 'all' || mode === 'named',\n isIndexable: mode === 'all' || mode === 'named',\n })\n }\n }\n })\n\n const result = [...cachedFiles.values()]\n\n if (mode === 'all') {\n return result.map((file) => ({\n ...file,\n exports: file.exports?.map((e) => ({ ...e, name: undefined })),\n }))\n }\n\n return result\n}\n\nexport const barrelPlugin = definePlugin<Options, ExtendOptions>({\n name: 'barrel',\n install(ctx, options) {\n if (!options) {\n throw new Error('Barrel plugin requires options.root and options.mode')\n }\n\n if (!options.mode) {\n return undefined\n }\n\n ctx.on('files:writing:start', async (files) => {\n const root = options.root\n const barrelFiles = getBarrelFiles({ files, root, mode: options.mode })\n\n await ctx.fileManager.add(...barrelFiles)\n })\n },\n inject(ctx, options) {\n if (!options) {\n throw new Error('Barrel plugin requires options.root and options.mode')\n }\n\n return {\n async writeEntry({ root, mode }) {\n if (!mode || mode === 'propagate') {\n return undefined\n }\n\n const rootPath = path.resolve(root, 'index.ts')\n\n const barrelFiles: Array<KubbFile.ResolvedFile> = []\n for (const file of ctx.files) {\n for (const source of file.sources) {\n if (source.isIndexable) {\n barrelFiles.push(file)\n\n break\n }\n }\n }\n\n const fileTypeCache = new Map<KubbFile.ResolvedFile, boolean>()\n for (const file of barrelFiles) {\n fileTypeCache.set(\n file,\n file.sources.every((source) => source.isTypeOnly),\n )\n }\n\n const exports: Array<KubbFile.Export> = []\n for (const file of barrelFiles) {\n const containsOnlyTypes = fileTypeCache.get(file) ?? false\n\n for (const source of file.sources) {\n if (!file.path || !source.isIndexable) {\n continue\n }\n\n exports.push({\n name: mode === 'all' ? undefined : [source.name],\n path: getRelativePath(rootPath, file.path),\n isTypeOnly: mode === 'all' ? containsOnlyTypes : source.isTypeOnly,\n } as KubbFile.Export)\n }\n }\n\n const entryFile = createFile({\n path: rootPath,\n baseName: 'index.ts',\n imports: [],\n exports,\n sources: [],\n })\n\n await ctx.addFile(entryFile)\n\n await ctx.fileManager.write({\n mode: ctx.config.mode,\n dryRun: options.dryRun,\n parsers: ctx.installedParsers,\n })\n },\n }\n },\n})\n","import { rmSync } from 'node:fs'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, resolve } from 'node:path'\nimport type * as KubbFile from '../KubbFile.ts'\nimport { definePlugin } from './definePlugin.ts'\n\ntype WriteOptions = {\n extension?: Record<KubbFile.Extname, KubbFile.Extname | ''>\n}\n\ntype Options = {\n dryRun?: boolean\n /**\n * Optional callback that is invoked whenever a file is written by the plugin.\n * Useful for tests to observe write operations without spying on internal functions.\n */\n onBeforeWrite?: (path: string, data: string | undefined) => void | Promise<void>\n clean?: {\n path: string\n }\n}\n\ntype ExtendOptions = {\n write(options?: WriteOptions): Promise<void>\n}\n\nexport async function write(path: string, data: string | undefined, options: { sanity?: boolean } = {}): Promise<string | undefined> {\n if (typeof Bun !== 'undefined') {\n if (!data || data?.trim() === '') {\n return undefined\n }\n\n await Bun.write(resolve(path), data.trim())\n\n if (options?.sanity) {\n const file = Bun.file(resolve(path))\n const savedData = await file.text()\n\n if (savedData?.toString() !== data?.toString()) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n\n return savedData\n }\n\n return data\n }\n\n if (!data || data?.trim() === '') {\n return undefined\n }\n\n try {\n const oldContent = await readFile(resolve(path), {\n encoding: 'utf-8',\n })\n if (oldContent?.toString() === data?.toString()) {\n return\n }\n } catch (_err) {\n /* empty */\n }\n\n await mkdir(dirname(resolve(path)), { recursive: true })\n await writeFile(resolve(path), data.trim(), { encoding: 'utf-8' })\n\n if (options?.sanity) {\n const savedData = await readFile(resolve(path), {\n encoding: 'utf-8',\n })\n\n if (savedData?.toString() !== data?.toString()) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n\n return savedData\n }\n\n return data\n}\n\ndeclare global {\n namespace Kubb {\n interface Fabric {\n write(options?: WriteOptions): Promise<void>\n }\n }\n}\n\nexport const fsPlugin = definePlugin<Options, ExtendOptions>({\n name: 'fs',\n install(ctx, options = {}) {\n if (options.clean) {\n rmSync(options.clean.path, { recursive: true, force: true })\n }\n\n ctx.on('file:processing:update', async ({ file, source }) => {\n if (options.onBeforeWrite) {\n await options.onBeforeWrite(file.path, source)\n }\n await write(file.path, source, { sanity: false })\n })\n },\n inject(ctx, { dryRun } = {}) {\n return {\n async write(\n options = {\n extension: { '.ts': '.ts' },\n },\n ) {\n await ctx.fileManager.write({\n mode: ctx.config.mode,\n extension: options.extension,\n dryRun,\n parsers: ctx.installedParsers,\n })\n\n await ctx.emit('lifecycle:end')\n },\n }\n },\n})\n","import { Root } from '../../components/Root.ts'\nimport type { ComponentNode } from '../../composables/useNodeTree.ts'\nimport type { FabricElement } from '../../Fabric.ts'\nimport type { FileManager } from '../../FileManager.ts'\nimport { onProcessExit } from '../../utils/onProcessExit.ts'\nimport { TreeNode } from '../../utils/TreeNode.ts'\n\ntype Options = {\n fileManager: FileManager\n treeNode?: TreeNode<ComponentNode>\n debug?: boolean\n}\n\nexport class Runtime {\n readonly #options: Options\n exitPromise?: Promise<void>\n\n constructor(options: Options) {\n this.#options = options\n\n // Unmount when process exits\n this.unsubscribeExit = onProcessExit((code) => {\n this.unmount(code)\n })\n }\n\n get fileManager() {\n return this.#options.fileManager\n }\n\n #renderPromise: Promise<void> = Promise.resolve()\n resolveExitPromise: () => void = () => {}\n rejectExitPromise: (reason?: Error) => void = () => {}\n unsubscribeExit: () => void = () => {}\n\n onError(error: Error): void {\n throw error\n }\n\n onExit(error?: Error): void {\n setTimeout(() => {\n this.unmount(error)\n }, 0)\n }\n\n async render(node: FabricElement): Promise<string> {\n const treeNode = this.#options.treeNode || new TreeNode<ComponentNode>({ type: 'Root', props: {} })\n\n const props = {\n fileManager: this.fileManager,\n treeNode,\n onExit: this.onExit.bind(this),\n onError: this.onError.bind(this),\n }\n\n try {\n treeNode.data.props = props\n\n const element = Root({ ...props, children: node })\n\n await this.#renderPromise\n\n return element()?.toString() || ''\n } catch (e) {\n props.onError(e as Error)\n return ''\n }\n }\n\n unmount(error?: Error | number | null): void {\n if (this.#options?.debug) {\n console.log('Unmount', error)\n }\n\n this.unsubscribeExit()\n\n if (error instanceof Error) {\n this.rejectExitPromise(error)\n return\n }\n\n this.resolveExitPromise()\n }\n\n async waitUntilExit(): Promise<void> {\n if (!this.exitPromise) {\n this.exitPromise = new Promise((resolve, reject) => {\n this.resolveExitPromise = resolve\n this.rejectExitPromise = reject\n })\n }\n\n return this.exitPromise\n }\n}\n","import type { ComponentNode } from '../../composables/useNodeTree.ts'\nimport type { FabricElement } from '../../Fabric.ts'\nimport { definePlugin } from '../../plugins/definePlugin.ts'\nimport type { TreeNode } from '../../utils/TreeNode.ts'\nimport { Runtime } from './Runtime.ts'\n\nexport type Options = {\n treeNode?: TreeNode<ComponentNode>\n /**\n * Set this to true to always see the result of the render in the console(line per render)\n */\n debug?: boolean\n}\n\ntype ExtendOptions = {\n render(App: FabricElement<any>): Promise<string>\n waitUntilExit(): Promise<void>\n}\n\ndeclare global {\n namespace Kubb {\n interface Fabric {\n render(App: FabricElement<any>): Promise<string>\n waitUntilExit(): Promise<void>\n }\n }\n}\n\nexport const fsxPlugin = definePlugin<Options, ExtendOptions>({\n name: 'fsx',\n install() {},\n inject(ctx, options = {}) {\n const runtime = new Runtime({ fileManager: ctx.fileManager, ...options })\n\n return {\n async render(App) {\n await ctx.emit('lifecycle:start')\n return runtime.render(App)\n },\n async waitUntilExit() {\n await runtime.waitUntilExit()\n },\n }\n },\n})\n","import { relative } from 'node:path'\nimport { styleText } from 'node:util'\nimport * as clack from '@clack/prompts'\nimport { definePlugin } from './definePlugin.ts'\n\ntype Options = {\n /**\n * Toggle progress bar output.\n * @default true\n */\n progress?: boolean\n}\n\nfunction pluralize(word: string, count: number) {\n return `${count} ${word}${count === 1 ? '' : 's'}`\n}\n\nconst DEFAULT_PROGRESS_BAR_SIZE = 30\n\nexport const loggerPlugin = definePlugin<Options>({\n name: 'logger',\n install(ctx, options = {}) {\n const { progress = true } = options\n\n const state = {\n spinner: clack.spinner(),\n isSpinning: false,\n progressBar: undefined as ReturnType<typeof clack.progress> | undefined,\n }\n\n function formatPath(path: string) {\n return relative(process.cwd(), path)\n }\n\n ctx.on('lifecycle:start', async () => {\n clack.intro(`${styleText('blue', 'Fabric')} ${styleText('dim', 'Starting run')}`)\n })\n\n ctx.on('lifecycle:render', async () => {\n clack.log.info(`${styleText('blue', 'ℹ')} Rendering application graph`)\n })\n\n ctx.on('files:added', async (files) => {\n if (!files.length) {\n return\n }\n\n clack.log.info(`${styleText('blue', 'ℹ')} Queued ${pluralize('file', files.length)}`)\n })\n\n ctx.on('file:resolve:path', async (file) => {\n clack.log.step(`Resolving path for ${styleText('dim', formatPath(file.path))}`)\n })\n\n ctx.on('file:resolve:name', async (file) => {\n clack.log.step(`Resolving name for ${styleText('dim', formatPath(file.path))}`)\n })\n\n ctx.on('files:processing:start', async (files) => {\n clack.log.step(`Processing ${styleText('green', pluralize('file', files.length))}`)\n\n if (progress) {\n state.progressBar = clack.progress({\n style: 'block',\n max: files.length,\n size: DEFAULT_PROGRESS_BAR_SIZE,\n })\n state.progressBar.start(`Processing ${files.length} files`)\n }\n })\n\n ctx.on('file:processing:start', async (file, index, total) => {\n if (!state.progressBar) {\n clack.log.step(`Processing ${styleText('dim', `[${index + 1}/${total}]`)} ${formatPath(file.path)}`)\n }\n })\n\n ctx.on('file:processing:update', async ({ processed, total, percentage, file }) => {\n if (state.progressBar) {\n // undefined = auto-increment by 1\n state.progressBar.advance(undefined, `Writing ${formatPath(file.path)}`)\n } else {\n const formattedPercentage = Number.isFinite(percentage) ? percentage.toFixed(1) : '0.0'\n clack.log.step(`Progress ${styleText('green', `${formattedPercentage}%`)} ${styleText('dim', `(${processed}/${total})`)} → ${formatPath(file.path)}`)\n }\n })\n\n ctx.on('file:processing:end', async (file, index, total) => {\n if (state.progressBar) {\n state.progressBar.message(`${styleText('green', '✓')} Finished ${styleText('dim', `[${index + 1}/${total}]`)} ${formatPath(file.path)}`)\n } else {\n clack.log.success(`${styleText('green', '✓')} Finished ${styleText('dim', `[${index + 1}/${total}]`)} ${formatPath(file.path)}`)\n }\n })\n\n ctx.on('files:processing:end', async (files) => {\n if (state.progressBar) {\n state.progressBar.stop(`${styleText('green', '✓')} Processed ${pluralize('file', files.length)}`)\n state.progressBar = undefined\n } else {\n clack.log.success(`${styleText('green', '✓')} Processed ${pluralize('file', files.length)}`)\n }\n })\n\n ctx.on('lifecycle:end', async () => {\n if (state.progressBar) {\n state.progressBar.stop()\n state.progressBar = undefined\n }\n\n clack.outro(`${styleText('blue', 'Fabric')} ${styleText('dim', 'completed')}`)\n })\n },\n})\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/fabric-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Core functionality for Kubb's fabric - A language-agnostic toolkit providing the foundation for plugin-based code generation with TypeScript support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codegen",
|
|
@@ -83,22 +83,12 @@
|
|
|
83
83
|
}
|
|
84
84
|
],
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@clack/prompts": "^1.0.
|
|
87
|
-
"fs-extra": "^11.3.3",
|
|
88
|
-
"natural-orderby": "^5.0.0",
|
|
86
|
+
"@clack/prompts": "^1.0.1",
|
|
89
87
|
"p-limit": "^7.3.0",
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"serve-handler": "^6.1.6",
|
|
93
|
-
"signal-exit": "^4.1.0",
|
|
94
|
-
"typescript": "5.9.3",
|
|
95
|
-
"ws": "^8.19.0"
|
|
96
|
-
},
|
|
97
|
-
"devDependencies": {
|
|
98
|
-
"@types/fs-extra": "^11.0.4",
|
|
99
|
-
"@types/serve-handler": "^6.1.4",
|
|
100
|
-
"@types/ws": "^8.18.1"
|
|
88
|
+
"remeda": "^2.33.6",
|
|
89
|
+
"typescript": "5.9.3"
|
|
101
90
|
},
|
|
91
|
+
"devDependencies": {},
|
|
102
92
|
"engines": {
|
|
103
93
|
"node": ">=20"
|
|
104
94
|
},
|
|
@@ -108,7 +98,7 @@
|
|
|
108
98
|
},
|
|
109
99
|
"scripts": {
|
|
110
100
|
"build": "tsdown && size-limit",
|
|
111
|
-
"clean": "
|
|
101
|
+
"clean": "node -e \"require('fs').rmSync('./dist', {recursive:true,force:true})\"",
|
|
112
102
|
"lint": "pnpm biome lint .",
|
|
113
103
|
"lint:fix": "pnpm biome lint --fix --unsafe .",
|
|
114
104
|
"release": "pnpm publish --no-git-check",
|
package/src/FileManager.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sortBy } from 'remeda'
|
|
2
2
|
import { createFile } from './createFile.ts'
|
|
3
3
|
import type { FabricEvents } from './Fabric.ts'
|
|
4
4
|
import { FileProcessor, type ProcessFilesProps } from './FileProcessor.ts'
|
|
@@ -137,7 +137,11 @@ export class FileManager {
|
|
|
137
137
|
const cachedKeys = this.#cache.keys()
|
|
138
138
|
|
|
139
139
|
// order by path length and if file is a barrel file
|
|
140
|
-
const keys =
|
|
140
|
+
const keys = sortBy(
|
|
141
|
+
cachedKeys,
|
|
142
|
+
(v) => v.length,
|
|
143
|
+
(v) => trimExtName(v).endsWith('index'),
|
|
144
|
+
)
|
|
141
145
|
|
|
142
146
|
const files: Array<KubbFile.ResolvedFile> = []
|
|
143
147
|
|
package/src/createFile.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
import {
|
|
4
|
-
import { uniqueBy } from 'remeda'
|
|
3
|
+
import { sortBy, uniqueBy } from 'remeda'
|
|
5
4
|
import type * as KubbFile from './KubbFile.ts'
|
|
6
5
|
import { trimExtName } from './utils/trimExtName.ts'
|
|
7
6
|
|
|
@@ -18,21 +17,21 @@ export function combineSources(sources: Array<KubbFile.Source>): Array<KubbFile.
|
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
export function combineExports(exports: Array<KubbFile.Export>): Array<KubbFile.Export> {
|
|
21
|
-
const sorted =
|
|
20
|
+
const sorted = sortBy(
|
|
21
|
+
exports,
|
|
22
22
|
(v) => !!Array.isArray(v.name),
|
|
23
23
|
(v) => !v.isTypeOnly,
|
|
24
24
|
(v) => v.path,
|
|
25
25
|
(v) => !!v.name,
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// join with null byte — can't appear in identifiers, so avoids collisions between e.g. ['a','b'] and ['a,b']
|
|
27
|
+
(v) => (Array.isArray(v.name) ? [...v.name].sort().join('\0') : (v.name ?? '')),
|
|
28
|
+
)
|
|
28
29
|
|
|
29
30
|
const prev: Array<KubbFile.Export> = []
|
|
30
31
|
// Map to track items by path for O(1) lookup
|
|
31
32
|
const pathMap = new Map<string, KubbFile.Export>()
|
|
32
33
|
// Map to track unique items by path+name+isTypeOnly+asAlias
|
|
33
34
|
const uniqueMap = new Map<string, KubbFile.Export>()
|
|
34
|
-
// Map to track items by path+name where isTypeOnly=true (for type-only check)
|
|
35
|
-
const pathNameTypeTrueMap = new Map<string, KubbFile.Export>()
|
|
36
35
|
|
|
37
36
|
for (const curr of sorted) {
|
|
38
37
|
const name = curr.name
|
|
@@ -42,14 +41,6 @@ export function combineExports(exports: Array<KubbFile.Export>): Array<KubbFile.
|
|
|
42
41
|
// Create unique key for path+name+isTypeOnly
|
|
43
42
|
const nameKey = Array.isArray(name) ? JSON.stringify(name) : name || ''
|
|
44
43
|
const pathNameTypeKey = `${pathKey}:${nameKey}:${curr.isTypeOnly}`
|
|
45
|
-
// Check if there's already an item with the same path+name but with isTypeOnly=true
|
|
46
|
-
const pathNameKey = `${pathKey}:${nameKey}`
|
|
47
|
-
const prevByPathAndIsTypeOnly = pathNameTypeTrueMap.get(pathNameKey)
|
|
48
|
-
|
|
49
|
-
if (prevByPathAndIsTypeOnly) {
|
|
50
|
-
// we already have an export that has the same path and name but uses `isTypeOnly` (export type ...)
|
|
51
|
-
continue
|
|
52
|
-
}
|
|
53
44
|
|
|
54
45
|
// Create unique key for path+name+isTypeOnly+asAlias
|
|
55
46
|
const uniqueKey = `${pathNameTypeKey}:${curr.asAlias || ''}`
|
|
@@ -68,10 +59,6 @@ export function combineExports(exports: Array<KubbFile.Export>): Array<KubbFile.
|
|
|
68
59
|
prev.push(newItem)
|
|
69
60
|
pathMap.set(pathKey, newItem)
|
|
70
61
|
uniqueMap.set(uniqueKey, newItem)
|
|
71
|
-
// Track items with isTypeOnly=true for the type-only check
|
|
72
|
-
if (newItem.isTypeOnly) {
|
|
73
|
-
pathNameTypeTrueMap.set(pathNameKey, newItem)
|
|
74
|
-
}
|
|
75
62
|
continue
|
|
76
63
|
}
|
|
77
64
|
|
|
@@ -83,10 +70,6 @@ export function combineExports(exports: Array<KubbFile.Export>): Array<KubbFile.
|
|
|
83
70
|
|
|
84
71
|
prev.push(curr)
|
|
85
72
|
uniqueMap.set(uniqueKey, curr)
|
|
86
|
-
// Track items with isTypeOnly=true for the type-only check
|
|
87
|
-
if (curr.isTypeOnly) {
|
|
88
|
-
pathNameTypeTrueMap.set(pathNameKey, curr)
|
|
89
|
-
}
|
|
90
73
|
}
|
|
91
74
|
|
|
92
75
|
return prev
|
|
@@ -129,21 +112,21 @@ export function combineImports(imports: Array<KubbFile.Import>, exports: Array<K
|
|
|
129
112
|
return isUsed
|
|
130
113
|
}
|
|
131
114
|
|
|
132
|
-
const sorted =
|
|
133
|
-
|
|
115
|
+
const sorted = sortBy(
|
|
116
|
+
imports,
|
|
117
|
+
(v) => Array.isArray(v.name),
|
|
134
118
|
(v) => !v.isTypeOnly,
|
|
135
119
|
(v) => v.path,
|
|
136
120
|
(v) => !!v.name,
|
|
137
|
-
|
|
138
|
-
|
|
121
|
+
// join with null byte — can't appear in identifiers, so avoids collisions between e.g. ['a','b'] and ['a,b']
|
|
122
|
+
(v) => (Array.isArray(v.name) ? [...v.name].sort().join('\0') : (v.name ?? '')),
|
|
123
|
+
)
|
|
139
124
|
|
|
140
125
|
const prev: Array<KubbFile.Import> = []
|
|
141
126
|
// Map to track items by path+isTypeOnly for O(1) lookup
|
|
142
127
|
const pathTypeMap = new Map<string, KubbFile.Import>()
|
|
143
128
|
// Map to track unique items by path+name+isTypeOnly
|
|
144
129
|
const uniqueMap = new Map<string, KubbFile.Import>()
|
|
145
|
-
// Map to track items by path+name where isTypeOnly=true (for type-only check)
|
|
146
|
-
const pathNameTypeTrueMap = new Map<string, KubbFile.Import>()
|
|
147
130
|
|
|
148
131
|
for (const curr of sorted) {
|
|
149
132
|
let name = Array.isArray(curr.name) ? [...new Set(curr.name)] : curr.name
|
|
@@ -165,14 +148,6 @@ export function combineImports(imports: Array<KubbFile.Import>, exports: Array<K
|
|
|
165
148
|
const nameKey = Array.isArray(name) ? JSON.stringify(name) : name || ''
|
|
166
149
|
const pathNameTypeKey = `${curr.path}:${nameKey}:${curr.isTypeOnly}`
|
|
167
150
|
const uniquePrev = uniqueMap.get(pathNameTypeKey)
|
|
168
|
-
// Check if there's already an item with the same path+name but with isTypeOnly=true
|
|
169
|
-
const pathNameKey = `${curr.path}:${nameKey}`
|
|
170
|
-
const prevByPathNameAndIsTypeOnly = pathNameTypeTrueMap.get(pathNameKey)
|
|
171
|
-
|
|
172
|
-
if (prevByPathNameAndIsTypeOnly) {
|
|
173
|
-
// we already have an import that has the same path and name but uses `isTypeOnly` (import type ...)
|
|
174
|
-
continue
|
|
175
|
-
}
|
|
176
151
|
|
|
177
152
|
// already unique enough or name is empty
|
|
178
153
|
if (uniquePrev || (Array.isArray(name) && !name.length)) {
|
|
@@ -188,10 +163,6 @@ export function combineImports(imports: Array<KubbFile.Import>, exports: Array<K
|
|
|
188
163
|
prev.push(newItem)
|
|
189
164
|
pathTypeMap.set(pathTypeKey, newItem)
|
|
190
165
|
uniqueMap.set(pathNameTypeKey, newItem)
|
|
191
|
-
// Track items with isTypeOnly=true for the type-only check
|
|
192
|
-
if (newItem.isTypeOnly) {
|
|
193
|
-
pathNameTypeTrueMap.set(pathNameKey, newItem)
|
|
194
|
-
}
|
|
195
166
|
continue
|
|
196
167
|
}
|
|
197
168
|
|
|
@@ -208,10 +179,6 @@ export function combineImports(imports: Array<KubbFile.Import>, exports: Array<K
|
|
|
208
179
|
|
|
209
180
|
prev.push(curr)
|
|
210
181
|
uniqueMap.set(pathNameTypeKey, curr)
|
|
211
|
-
// Track items with isTypeOnly=true for the type-only check
|
|
212
|
-
if (curr.isTypeOnly) {
|
|
213
|
-
pathNameTypeTrueMap.set(pathNameKey, curr)
|
|
214
|
-
}
|
|
215
182
|
}
|
|
216
183
|
|
|
217
184
|
return prev
|
package/src/index.ts
CHANGED
|
@@ -18,7 +18,6 @@ export { useLifecycle } from './composables/useLifecycle.ts'
|
|
|
18
18
|
export { useNodeTree } from './composables/useNodeTree.ts'
|
|
19
19
|
|
|
20
20
|
// context api
|
|
21
|
-
|
|
22
21
|
export { createContext, inject, provide, unprovide } from './context.ts'
|
|
23
22
|
export { AppContext } from './contexts/AppContext.ts'
|
|
24
23
|
export { FileContext } from './contexts/FileContext.ts'
|
|
@@ -26,15 +25,17 @@ export { NodeTreeContext } from './contexts/NodeTreeContext.ts'
|
|
|
26
25
|
export { RenderContext } from './contexts/RenderContext.ts'
|
|
27
26
|
export { RootContext } from './contexts/RootContext.ts'
|
|
28
27
|
export { createComponent } from './createComponent.ts'
|
|
28
|
+
|
|
29
29
|
// helpers
|
|
30
30
|
export { createFabric } from './createFabric.ts'
|
|
31
31
|
export { createFile } from './createFile.ts'
|
|
32
|
+
|
|
32
33
|
// utils
|
|
33
34
|
export type { Fabric } from './Fabric.ts'
|
|
34
|
-
|
|
35
35
|
export { FileManager } from './FileManager.ts'
|
|
36
36
|
export { FileProcessor } from './FileProcessor.ts'
|
|
37
37
|
export { renderIndent, renderIntrinsic } from './intrinsic.ts'
|
|
38
38
|
export { createJSDoc } from './utils/createJSDoc.ts'
|
|
39
39
|
export { getRelativePath } from './utils/getRelativePath.ts'
|
|
40
|
+
export { onProcessExit } from './utils/onProcessExit.ts'
|
|
40
41
|
export { TreeNode } from './utils/TreeNode.ts'
|
package/src/plugins/fsPlugin.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { rmSync } from 'node:fs'
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { dirname, resolve } from 'node:path'
|
|
3
4
|
import type * as KubbFile from '../KubbFile.ts'
|
|
4
5
|
import { definePlugin } from './definePlugin.ts'
|
|
5
6
|
|
|
@@ -50,7 +51,7 @@ export async function write(path: string, data: string | undefined, options: { s
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
try {
|
|
53
|
-
const oldContent = await
|
|
54
|
+
const oldContent = await readFile(resolve(path), {
|
|
54
55
|
encoding: 'utf-8',
|
|
55
56
|
})
|
|
56
57
|
if (oldContent?.toString() === data?.toString()) {
|
|
@@ -60,10 +61,11 @@ export async function write(path: string, data: string | undefined, options: { s
|
|
|
60
61
|
/* empty */
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
await
|
|
64
|
+
await mkdir(dirname(resolve(path)), { recursive: true })
|
|
65
|
+
await writeFile(resolve(path), data.trim(), { encoding: 'utf-8' })
|
|
64
66
|
|
|
65
67
|
if (options?.sanity) {
|
|
66
|
-
const savedData = await
|
|
68
|
+
const savedData = await readFile(resolve(path), {
|
|
67
69
|
encoding: 'utf-8',
|
|
68
70
|
})
|
|
69
71
|
|
|
@@ -89,7 +91,7 @@ export const fsPlugin = definePlugin<Options, ExtendOptions>({
|
|
|
89
91
|
name: 'fs',
|
|
90
92
|
install(ctx, options = {}) {
|
|
91
93
|
if (options.clean) {
|
|
92
|
-
|
|
94
|
+
rmSync(options.clean.path, { recursive: true, force: true })
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
ctx.on('file:processing:update', async ({ file, source }) => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { onExit } from 'signal-exit'
|
|
2
1
|
import { Root } from '../../components/Root.ts'
|
|
3
2
|
import type { ComponentNode } from '../../composables/useNodeTree.ts'
|
|
4
3
|
import type { FabricElement } from '../../Fabric.ts'
|
|
5
4
|
import type { FileManager } from '../../FileManager.ts'
|
|
5
|
+
import { onProcessExit } from '../../utils/onProcessExit.ts'
|
|
6
6
|
import { TreeNode } from '../../utils/TreeNode.ts'
|
|
7
7
|
|
|
8
8
|
type Options = {
|
|
@@ -19,12 +19,9 @@ export class Runtime {
|
|
|
19
19
|
this.#options = options
|
|
20
20
|
|
|
21
21
|
// Unmount when process exits
|
|
22
|
-
this.unsubscribeExit =
|
|
23
|
-
(code)
|
|
24
|
-
|
|
25
|
-
},
|
|
26
|
-
{ alwaysLast: false },
|
|
27
|
-
).bind(this)
|
|
22
|
+
this.unsubscribeExit = onProcessExit((code) => {
|
|
23
|
+
this.unmount(code)
|
|
24
|
+
})
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
get fileManager() {
|
package/src/plugins/index.ts
CHANGED
|
@@ -2,5 +2,4 @@ export { barrelPlugin } from './barrelPlugin.ts'
|
|
|
2
2
|
export { definePlugin } from './definePlugin.ts'
|
|
3
3
|
export { fsPlugin } from './fsPlugin.ts'
|
|
4
4
|
export { fsxPlugin } from './fsxPlugin/fsxPlugin.ts'
|
|
5
|
-
export { graphPlugin } from './graphPlugin.ts'
|
|
6
5
|
export { loggerPlugin } from './loggerPlugin.ts'
|