@kubb/fabric-core 0.1.5 → 0.1.7
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/dist/{App-9ie0H1SF.d.ts → App-DVWD6TgC.d.cts} +28 -17
- package/dist/{App-KqAHuAyU.d.cts → App-_vPNh477.d.ts} +28 -17
- package/dist/createParser-17uGjfu3.js +11 -0
- package/dist/createParser-17uGjfu3.js.map +1 -0
- package/dist/createParser-C4IkyTs5.cjs +17 -0
- package/dist/createParser-C4IkyTs5.cjs.map +1 -0
- package/dist/{defaultParser-vwyTb1XT.js → defaultParser--HzU9LPa.js} +2 -2
- package/dist/{defaultParser-vwyTb1XT.js.map → defaultParser--HzU9LPa.js.map} +1 -1
- package/dist/{defaultParser-Dl-OrbH1.cjs → defaultParser-n9VW2iVf.cjs} +2 -2
- package/dist/{defaultParser-Dl-OrbH1.cjs.map → defaultParser-n9VW2iVf.cjs.map} +1 -1
- package/dist/defineProperty-BtekiGIK.js +332 -0
- package/dist/defineProperty-BtekiGIK.js.map +1 -0
- package/dist/defineProperty-CspRhtP3.cjs +364 -0
- package/dist/defineProperty-CspRhtP3.cjs.map +1 -0
- package/dist/getRelativePath-C6lvNCs7.cjs +26 -0
- package/dist/getRelativePath-C6lvNCs7.cjs.map +1 -0
- package/dist/getRelativePath-CERJmYkp.js +19 -0
- package/dist/getRelativePath-CERJmYkp.js.map +1 -0
- package/dist/{index-BpPNNyhl.d.ts → index-CfV-59_M.d.ts} +5 -5
- package/dist/{index-DLITiDO5.d.cts → index-DVok6g82.d.cts} +5 -5
- package/dist/index.cjs +81 -225
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +54 -194
- package/dist/index.js.map +1 -1
- package/dist/parsers/typescript.cjs +4 -2
- package/dist/parsers/typescript.d.cts +2 -2
- package/dist/parsers/typescript.d.ts +2 -2
- package/dist/parsers/typescript.js +4 -2
- package/dist/parsers.cjs +5 -3
- package/dist/parsers.cjs.map +1 -1
- package/dist/parsers.d.cts +3 -3
- package/dist/parsers.d.ts +3 -3
- package/dist/parsers.js +5 -3
- package/dist/parsers.js.map +1 -1
- package/dist/plugins.cjs +260 -33
- package/dist/plugins.cjs.map +1 -1
- package/dist/plugins.d.cts +48 -8
- package/dist/plugins.d.ts +48 -8
- package/dist/plugins.js +249 -25
- package/dist/plugins.js.map +1 -1
- package/dist/{chunk-CUT6urMc.cjs → trimExtName-Bb4zGVF1.cjs} +14 -1
- package/dist/trimExtName-Bb4zGVF1.cjs.map +1 -0
- package/dist/trimExtName-CeOVQVbu.js +8 -0
- package/dist/trimExtName-CeOVQVbu.js.map +1 -0
- package/dist/types.d.cts +2 -2
- package/dist/types.d.ts +2 -2
- package/dist/{typescriptParser-Du4RIToQ.d.cts → typescriptParser-BM90H8Tx.d.cts} +2 -2
- package/dist/{typescriptParser-JawJ8wET.cjs → typescriptParser-CNHO6H2_.cjs} +10 -24
- package/dist/typescriptParser-CNHO6H2_.cjs.map +1 -0
- package/dist/{typescriptParser-CrzOv_Aw.js → typescriptParser-CWT7zCJy.js} +5 -18
- package/dist/typescriptParser-CWT7zCJy.js.map +1 -0
- package/dist/{typescriptParser-Dk1rwKyJ.d.ts → typescriptParser-Chjs-RhT.d.ts} +2 -2
- package/package.json +4 -2
- package/src/App.ts +31 -28
- package/src/FileManager.ts +8 -1
- package/src/FileProcessor.ts +48 -18
- package/src/defineApp.ts +5 -10
- package/src/parsers/createParser.ts +1 -1
- package/src/parsers/types.ts +2 -2
- package/src/plugins/barrelPlugin.ts +207 -0
- package/src/plugins/createPlugin.ts +1 -1
- package/src/plugins/fsPlugin.ts +33 -34
- package/src/plugins/index.ts +2 -0
- package/src/plugins/progressPlugin.ts +48 -0
- package/src/plugins/types.ts +3 -3
- package/src/utils/AsyncEventEmitter.ts +8 -0
- package/src/utils/EventEmitter.ts +8 -0
- package/src/utils/TreeNode.ts +118 -0
- package/dist/createParser-B_RpW6sx.js +0 -17
- package/dist/createParser-B_RpW6sx.js.map +0 -1
- package/dist/createParser-DZB5qExa.cjs +0 -29
- package/dist/createParser-DZB5qExa.cjs.map +0 -1
- package/dist/typescriptParser-CrzOv_Aw.js.map +0 -1
- package/dist/typescriptParser-JawJ8wET.cjs.map +0 -1
package/src/App.ts
CHANGED
|
@@ -12,6 +12,13 @@ declare global {
|
|
|
12
12
|
|
|
13
13
|
export type Component = any
|
|
14
14
|
|
|
15
|
+
export type AppOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* @default 'sequential'
|
|
18
|
+
*/
|
|
19
|
+
mode?: AppMode
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
export type AppEvents = {
|
|
16
23
|
/**
|
|
17
24
|
* Called in the beginning of the app lifecycle.
|
|
@@ -49,7 +56,7 @@ export type AppEvents = {
|
|
|
49
56
|
processed: number
|
|
50
57
|
total: number
|
|
51
58
|
percentage: number
|
|
52
|
-
source
|
|
59
|
+
source?: string
|
|
53
60
|
file: KubbFile.ResolvedFile
|
|
54
61
|
},
|
|
55
62
|
]
|
|
@@ -60,7 +67,7 @@ export type AppEvents = {
|
|
|
60
67
|
'process:end': [{ files: KubbFile.ResolvedFile[] }]
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
export type AppContext<TOptions
|
|
70
|
+
export type AppContext<TOptions extends AppOptions> = {
|
|
64
71
|
options?: TOptions
|
|
65
72
|
events: AsyncEventEmitter<AppEvents>
|
|
66
73
|
fileManager: FileManager
|
|
@@ -68,36 +75,32 @@ export type AppContext<TOptions = unknown> = {
|
|
|
68
75
|
installedParsers: Set<Parser>
|
|
69
76
|
}
|
|
70
77
|
|
|
71
|
-
export type
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
export type AppMode = 'sequential' | 'parallel'
|
|
79
|
+
|
|
80
|
+
type AllOptional<T> = {} extends T ? true : false
|
|
81
|
+
|
|
82
|
+
export type Install<TOptions = unknown> = TOptions extends any[]
|
|
83
|
+
? (app: App, ...options: TOptions) => void | Promise<void>
|
|
84
|
+
: AllOptional<TOptions> extends true
|
|
85
|
+
? (app: App, options: TOptions | undefined) => void | Promise<void>
|
|
86
|
+
: (app: App, options: TOptions) => void | Promise<void>
|
|
76
87
|
|
|
77
|
-
export type Inject<TOptions =
|
|
88
|
+
export type Inject<TOptions = unknown, TAppExtension extends Record<string, any> = {}> = TOptions extends any[]
|
|
78
89
|
? (app: App, ...options: TOptions) => Partial<TAppExtension>
|
|
79
|
-
: TOptions extends
|
|
80
|
-
? (app: App, options
|
|
81
|
-
: (app: App) => Partial<TAppExtension>
|
|
90
|
+
: AllOptional<TOptions> extends true
|
|
91
|
+
? (app: App, options: TOptions | undefined) => Partial<TAppExtension>
|
|
92
|
+
: (app: App, options: TOptions) => Partial<TAppExtension>
|
|
82
93
|
|
|
83
|
-
export interface App<TOptions =
|
|
94
|
+
export interface App<TOptions extends AppOptions = AppOptions> extends Kubb.App {
|
|
84
95
|
context: AppContext<TOptions>
|
|
85
96
|
files: Array<KubbFile.ResolvedFile>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
): this & TAppExtension
|
|
94
|
-
// async
|
|
95
|
-
use<TOptions extends any[] | object = any, TMeta extends object = object, TAppExtension extends Record<string, any> = {}>(
|
|
96
|
-
pluginOrParser: Plugin<TOptions, TAppExtension> | Parser<TOptions, TMeta>,
|
|
97
|
-
...options: TOptions extends any[] ? NoInfer<TOptions> : [NoInfer<TOptions>]
|
|
98
|
-
): Promise<this & TAppExtension>
|
|
99
|
-
use<TOptions extends any[] | object = any, TMeta extends object = object, TAppExtension extends Record<string, any> = {}>(
|
|
100
|
-
pluginOrParser: Plugin<TOptions, TAppExtension> | Parser<TOptions, TMeta>,
|
|
101
|
-
): Promise<this & TAppExtension>
|
|
97
|
+
use<TPluginOptions = unknown, TMeta extends object = object, TAppExtension extends Record<string, any> = {}>(
|
|
98
|
+
pluginOrParser: Plugin<TPluginOptions, TAppExtension> | Parser<TPluginOptions, TMeta>,
|
|
99
|
+
...options: TPluginOptions extends any[]
|
|
100
|
+
? NoInfer<TPluginOptions>
|
|
101
|
+
: AllOptional<TPluginOptions> extends true
|
|
102
|
+
? [NoInfer<TPluginOptions>?] // Optional when all props are optional
|
|
103
|
+
: [NoInfer<TPluginOptions>] // Required otherwise
|
|
104
|
+
): (this & TAppExtension) | Promise<this & TAppExtension>
|
|
102
105
|
addFile(...files: Array<KubbFile.File>): Promise<void>
|
|
103
106
|
}
|
package/src/FileManager.ts
CHANGED
|
@@ -22,10 +22,13 @@ type Options = {
|
|
|
22
22
|
|
|
23
23
|
export class FileManager {
|
|
24
24
|
#cache = new Cache<KubbFile.ResolvedFile>()
|
|
25
|
+
events: AsyncEventEmitter<AppEvents>
|
|
25
26
|
processor: FileProcessor
|
|
26
27
|
|
|
27
28
|
constructor({ events = new AsyncEventEmitter<AppEvents>() }: Options = {}) {
|
|
28
29
|
this.processor = new FileProcessor({ events })
|
|
30
|
+
|
|
31
|
+
this.events = events
|
|
29
32
|
return this
|
|
30
33
|
}
|
|
31
34
|
|
|
@@ -86,6 +89,10 @@ export class FileManager {
|
|
|
86
89
|
}
|
|
87
90
|
|
|
88
91
|
async write(options: ProcessFilesProps): Promise<KubbFile.ResolvedFile[]> {
|
|
89
|
-
|
|
92
|
+
const resolvedFiles = await this.processor.run(this.files, options)
|
|
93
|
+
|
|
94
|
+
this.clear()
|
|
95
|
+
|
|
96
|
+
return resolvedFiles
|
|
90
97
|
}
|
|
91
98
|
}
|
package/src/FileProcessor.ts
CHANGED
|
@@ -4,12 +4,16 @@ import pLimit from 'p-limit'
|
|
|
4
4
|
import type { Parser } from './parsers/types.ts'
|
|
5
5
|
import { defaultParser } from './parsers/defaultParser.ts'
|
|
6
6
|
import { AsyncEventEmitter } from './utils/AsyncEventEmitter.ts'
|
|
7
|
-
import type { AppEvents } from './App.ts'
|
|
7
|
+
import type { AppEvents, AppMode } from './App.ts'
|
|
8
8
|
|
|
9
9
|
export type ProcessFilesProps = {
|
|
10
10
|
parsers?: Set<Parser>
|
|
11
11
|
extension?: Record<KubbFile.Extname, KubbFile.Extname | ''>
|
|
12
12
|
dryRun?: boolean
|
|
13
|
+
/**
|
|
14
|
+
* @default 'sequential'
|
|
15
|
+
*/
|
|
16
|
+
mode?: AppMode
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
type GetParseOptions = {
|
|
@@ -31,9 +35,15 @@ export class FileProcessor {
|
|
|
31
35
|
return this
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
async parse(file: KubbFile.ResolvedFile, { parsers
|
|
38
|
+
async parse(file: KubbFile.ResolvedFile, { parsers, extension }: GetParseOptions = {}): Promise<string> {
|
|
35
39
|
const parseExtName = extension?.[file.extname] || undefined
|
|
36
40
|
|
|
41
|
+
if (!parsers) {
|
|
42
|
+
console.warn('No parsers provided, using default parser. If you want to use a specific parser, please provide it in the options.')
|
|
43
|
+
|
|
44
|
+
return defaultParser.parse(file, { extname: parseExtName })
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
if (!file.extname) {
|
|
38
48
|
return defaultParser.parse(file, { extname: parseExtName })
|
|
39
49
|
}
|
|
@@ -53,31 +63,51 @@ export class FileProcessor {
|
|
|
53
63
|
return parser.parse(file, { extname: parseExtName })
|
|
54
64
|
}
|
|
55
65
|
|
|
56
|
-
async run(
|
|
66
|
+
async run(
|
|
67
|
+
files: Array<KubbFile.ResolvedFile>,
|
|
68
|
+
{ parsers, mode = 'sequential', dryRun, extension }: ProcessFilesProps = {},
|
|
69
|
+
): Promise<KubbFile.ResolvedFile[]> {
|
|
57
70
|
await this.events.emit('process:start', { files })
|
|
58
71
|
|
|
59
72
|
let processed = 0
|
|
60
73
|
const total = files.length
|
|
61
74
|
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
await this.events.emit('file:start', { file: resolvedFile, index, total })
|
|
75
|
+
const processOne = async (resolvedFile: KubbFile.ResolvedFile, index: number) => {
|
|
76
|
+
const percentage = (processed / total) * 100
|
|
65
77
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
await this.events.emit('file:start', { file: resolvedFile, index, total })
|
|
79
|
+
|
|
80
|
+
const source = dryRun ? undefined : await this.parse(resolvedFile, { extension, parsers })
|
|
81
|
+
|
|
82
|
+
await this.events.emit('process:progress', {
|
|
83
|
+
file: resolvedFile,
|
|
84
|
+
source,
|
|
85
|
+
processed,
|
|
86
|
+
percentage,
|
|
87
|
+
total,
|
|
88
|
+
})
|
|
73
89
|
|
|
74
|
-
|
|
90
|
+
processed++
|
|
75
91
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
await this.events.emit('file:end', { file: resolvedFile, index, total })
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (mode === 'sequential') {
|
|
96
|
+
async function* asyncFiles() {
|
|
97
|
+
for (let index = 0; index < files.length; index++) {
|
|
98
|
+
yield [files[index], index] as const
|
|
99
|
+
}
|
|
100
|
+
}
|
|
79
101
|
|
|
80
|
-
|
|
102
|
+
for await (const [file, index] of asyncFiles()) {
|
|
103
|
+
if (file) {
|
|
104
|
+
await processOne(file, index)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
const promises = files.map((resolvedFile, index) => this.#limit(() => processOne(resolvedFile, index)))
|
|
109
|
+
await Promise.all(promises)
|
|
110
|
+
}
|
|
81
111
|
|
|
82
112
|
await this.events.emit('process:end', { files })
|
|
83
113
|
|
package/src/defineApp.ts
CHANGED
|
@@ -3,19 +3,19 @@ import { isFunction } from 'remeda'
|
|
|
3
3
|
import type { Plugin } from './plugins/types.ts'
|
|
4
4
|
import type { Parser } from './parsers/types.ts'
|
|
5
5
|
import { AsyncEventEmitter } from './utils/AsyncEventEmitter.ts'
|
|
6
|
-
import type { AppContext,
|
|
6
|
+
import type { AppContext, AppEvents, AppOptions } from './App.ts'
|
|
7
7
|
|
|
8
8
|
import type { App } from './index.ts'
|
|
9
9
|
|
|
10
10
|
type RootRenderFunction<TApp extends App> = (app: TApp) => void | Promise<void>
|
|
11
11
|
|
|
12
|
-
export type DefineApp<TOptions> = (
|
|
12
|
+
export type DefineApp<TOptions> = (options?: TOptions) => App
|
|
13
13
|
|
|
14
|
-
export function defineApp<TOptions
|
|
14
|
+
export function defineApp<TOptions extends AppOptions>(instance?: RootRenderFunction<App<TOptions>>): DefineApp<TOptions> {
|
|
15
15
|
function createApp(options?: TOptions): App {
|
|
16
16
|
const events = new AsyncEventEmitter<AppEvents>()
|
|
17
|
-
const installedPlugins = new Set<Plugin
|
|
18
|
-
const installedParsers = new Set<Parser
|
|
17
|
+
const installedPlugins = new Set<Plugin<any>>()
|
|
18
|
+
const installedParsers = new Set<Parser<any>>()
|
|
19
19
|
const fileManager = new FileManager({ events })
|
|
20
20
|
const context = {
|
|
21
21
|
events,
|
|
@@ -68,15 +68,10 @@ export function defineApp<TOptions = unknown>(instance?: RootRenderFunction<App<
|
|
|
68
68
|
},
|
|
69
69
|
} as App<TOptions>
|
|
70
70
|
|
|
71
|
-
// start
|
|
72
|
-
events.emit('start')
|
|
73
71
|
if (instance) {
|
|
74
72
|
instance(app)
|
|
75
73
|
}
|
|
76
74
|
|
|
77
|
-
// end
|
|
78
|
-
events.emit('end')
|
|
79
|
-
|
|
80
75
|
return app
|
|
81
76
|
}
|
|
82
77
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Parser, UserParser } from './types.ts'
|
|
2
2
|
|
|
3
|
-
export function createParser<TOptions =
|
|
3
|
+
export function createParser<TOptions = unknown, TMeta extends object = any>(parser: UserParser<TOptions, TMeta>): Parser<TOptions, TMeta> {
|
|
4
4
|
return {
|
|
5
5
|
type: 'parser',
|
|
6
6
|
...parser,
|
package/src/parsers/types.ts
CHANGED
|
@@ -5,7 +5,7 @@ type PrintOptions = {
|
|
|
5
5
|
extname?: KubbFile.Extname
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export type Parser<TOptions =
|
|
8
|
+
export type Parser<TOptions = unknown, TMeta extends object = any> = {
|
|
9
9
|
name: string
|
|
10
10
|
type: 'parser'
|
|
11
11
|
/**
|
|
@@ -19,4 +19,4 @@ export type Parser<TOptions = any[], TMeta extends object = any> = {
|
|
|
19
19
|
parse(file: KubbFile.ResolvedFile<TMeta>, options: PrintOptions): Promise<string>
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export type UserParser<TOptions =
|
|
22
|
+
export type UserParser<TOptions = unknown, TMeta extends object = any> = Omit<Parser<TOptions, TMeta>, 'type'>
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/** biome-ignore-all lint/suspicious/useIterableCallbackReturn: not needed */
|
|
2
|
+
|
|
3
|
+
import { createPlugin } from './createPlugin.ts'
|
|
4
|
+
import type * as KubbFile from '../KubbFile.ts'
|
|
5
|
+
import { TreeNode } from '../utils/TreeNode.ts'
|
|
6
|
+
import path, { resolve } from 'node:path'
|
|
7
|
+
import { getRelativePath } from '../utils/getRelativePath.ts'
|
|
8
|
+
import { createFile } from '../createFile.ts'
|
|
9
|
+
|
|
10
|
+
type Mode = 'all' | 'named' | 'propagate' | false
|
|
11
|
+
|
|
12
|
+
type Options = {
|
|
13
|
+
root: string
|
|
14
|
+
mode: Mode
|
|
15
|
+
dryRun?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type WriteEntryOptions = {
|
|
19
|
+
root: string
|
|
20
|
+
mode: Mode
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type ExtendOptions = {
|
|
24
|
+
writeEntry(options: WriteEntryOptions): Promise<void>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// biome-ignore lint/suspicious/noTsIgnore: production ready
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
declare module '@kubb/fabric-core' {
|
|
30
|
+
interface App {
|
|
31
|
+
writeEntry(options: WriteEntryOptions): Promise<void>
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare global {
|
|
36
|
+
namespace Kubb {
|
|
37
|
+
interface App {
|
|
38
|
+
writeEntry(options: WriteEntryOptions): Promise<void>
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type GetBarrelFilesOptions = {
|
|
44
|
+
files: KubbFile.File[]
|
|
45
|
+
root: string
|
|
46
|
+
mode: Mode
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function getBarrelFiles({ files, root, mode }: GetBarrelFilesOptions): Array<KubbFile.File> {
|
|
50
|
+
// Do not generate when propagating or disabled
|
|
51
|
+
if (mode === 'propagate' || mode === false) {
|
|
52
|
+
return []
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const cachedFiles = new Map<KubbFile.Path, KubbFile.File>()
|
|
56
|
+
const dedupe = new Map<KubbFile.Path, Set<string>>()
|
|
57
|
+
|
|
58
|
+
const tree = TreeNode.fromFiles(files, root)
|
|
59
|
+
tree?.forEach((node) => {
|
|
60
|
+
// Only create a barrel for directory-like nodes that have a parent with a path
|
|
61
|
+
if (!node?.children || !node.parent?.data.path) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const parentPath = node.parent.data.path as KubbFile.Path
|
|
66
|
+
const barrelPath = path.join(parentPath, 'index.ts') as KubbFile.Path
|
|
67
|
+
|
|
68
|
+
let barrelFile = cachedFiles.get(barrelPath)
|
|
69
|
+
if (!barrelFile) {
|
|
70
|
+
barrelFile = createFile({
|
|
71
|
+
path: barrelPath,
|
|
72
|
+
baseName: 'index.ts',
|
|
73
|
+
exports: [],
|
|
74
|
+
sources: [],
|
|
75
|
+
})
|
|
76
|
+
cachedFiles.set(barrelPath, barrelFile)
|
|
77
|
+
dedupe.set(barrelPath, new Set<string>())
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const seen = dedupe.get(barrelPath)!
|
|
81
|
+
|
|
82
|
+
// Collect all leaves under the current directory node
|
|
83
|
+
node.leaves.forEach((leaf) => {
|
|
84
|
+
const file = leaf.data.file
|
|
85
|
+
if (!file) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const sources = file.sources || []
|
|
90
|
+
sources.forEach((source) => {
|
|
91
|
+
if (!file.path || !source.isIndexable || !source.name) {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const key = `${source.name}|${source.isTypeOnly ? '1' : '0'}`
|
|
96
|
+
if (seen.has(key)) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
seen.add(key)
|
|
100
|
+
|
|
101
|
+
// Always compute relative path from the parent directory to the file path
|
|
102
|
+
barrelFile!.exports!.push({
|
|
103
|
+
name: [source.name],
|
|
104
|
+
path: getRelativePath(parentPath, file.path),
|
|
105
|
+
isTypeOnly: source.isTypeOnly,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
barrelFile!.sources.push({
|
|
109
|
+
name: source.name,
|
|
110
|
+
isTypeOnly: source.isTypeOnly,
|
|
111
|
+
value: '', // TODO use parser to generate import
|
|
112
|
+
isExportable: mode === 'all' || mode === 'named',
|
|
113
|
+
isIndexable: mode === 'all' || mode === 'named',
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const result = [...cachedFiles.values()]
|
|
120
|
+
|
|
121
|
+
if (mode === 'all') {
|
|
122
|
+
return result.map((file) => ({
|
|
123
|
+
...file,
|
|
124
|
+
exports: file.exports?.map((e) => ({ ...e, name: undefined })),
|
|
125
|
+
}))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return result
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const barrelPlugin = createPlugin<Options, ExtendOptions>({
|
|
132
|
+
name: 'barrel',
|
|
133
|
+
install(app, options) {
|
|
134
|
+
if (!options) {
|
|
135
|
+
throw new Error('Barrel plugin requires options.root and options.mode')
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!options.mode) {
|
|
139
|
+
return undefined
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
app.context.events.onOnce('process:end', async ({ files }) => {
|
|
143
|
+
const root = options.root
|
|
144
|
+
const barrelFiles = getBarrelFiles({ files, root, mode: options.mode })
|
|
145
|
+
|
|
146
|
+
await app.context.fileManager.add(...barrelFiles)
|
|
147
|
+
|
|
148
|
+
await app.context.fileManager.write({
|
|
149
|
+
mode: app.context.options?.mode,
|
|
150
|
+
dryRun: options.dryRun,
|
|
151
|
+
parsers: app.context.installedParsers,
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
},
|
|
155
|
+
inject(app, options) {
|
|
156
|
+
if (!options) {
|
|
157
|
+
throw new Error('Barrel plugin requires options.root and options.mode')
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
async writeEntry({ root, mode }) {
|
|
162
|
+
if (!mode || mode === 'propagate') {
|
|
163
|
+
return undefined
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const rootPath = resolve(root, 'index.ts')
|
|
167
|
+
|
|
168
|
+
const barrelFiles = app.files.filter((file) => {
|
|
169
|
+
return file.sources.some((source) => source.isIndexable)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const entryFile = createFile({
|
|
173
|
+
path: rootPath,
|
|
174
|
+
baseName: 'index.ts',
|
|
175
|
+
exports: barrelFiles
|
|
176
|
+
.flatMap((file) => {
|
|
177
|
+
const containsOnlyTypes = file.sources.every((source) => source.isTypeOnly)
|
|
178
|
+
|
|
179
|
+
return file.sources
|
|
180
|
+
?.map((source) => {
|
|
181
|
+
if (!file.path || !source.isIndexable) {
|
|
182
|
+
return undefined
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
name: mode === 'all' ? undefined : [source.name],
|
|
187
|
+
path: getRelativePath(rootPath, file.path),
|
|
188
|
+
isTypeOnly: mode === 'all' ? containsOnlyTypes : source.isTypeOnly,
|
|
189
|
+
} as KubbFile.Export
|
|
190
|
+
})
|
|
191
|
+
.filter(Boolean)
|
|
192
|
+
})
|
|
193
|
+
.filter(Boolean),
|
|
194
|
+
sources: [],
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
await app.context.fileManager.add(entryFile)
|
|
198
|
+
|
|
199
|
+
await app.context.fileManager.write({
|
|
200
|
+
mode: app.context.options?.mode,
|
|
201
|
+
dryRun: options.dryRun,
|
|
202
|
+
parsers: app.context.installedParsers,
|
|
203
|
+
})
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Plugin, UserPlugin } from './types.ts'
|
|
2
2
|
|
|
3
|
-
export function createPlugin<Options =
|
|
3
|
+
export function createPlugin<Options = unknown, TAppExtension extends Record<string, any> = {}>(
|
|
4
4
|
plugin: UserPlugin<Options, TAppExtension>,
|
|
5
5
|
): Plugin<Options, TAppExtension> {
|
|
6
6
|
return {
|
package/src/plugins/fsPlugin.ts
CHANGED
|
@@ -6,15 +6,15 @@ import type * as KubbFile from '../KubbFile.ts'
|
|
|
6
6
|
|
|
7
7
|
type WriteOptions = {
|
|
8
8
|
extension?: Record<KubbFile.Extname, KubbFile.Extname | ''>
|
|
9
|
-
dryRun?: boolean
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
type Options = {
|
|
12
|
+
dryRun?: boolean
|
|
13
13
|
/**
|
|
14
14
|
* Optional callback that is invoked whenever a file is written by the plugin.
|
|
15
15
|
* Useful for tests to observe write operations without spying on internal functions.
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
onBeforeWrite?: (path: string, data: string | undefined) => void | Promise<void>
|
|
18
18
|
clean?: {
|
|
19
19
|
path: string
|
|
20
20
|
}
|
|
@@ -24,13 +24,14 @@ type ExtendOptions = {
|
|
|
24
24
|
write(options?: WriteOptions): Promise<void>
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export async function write(path: string, data: string, options: { sanity?: boolean } = {}): Promise<string | undefined> {
|
|
28
|
-
if (data.trim() === '') {
|
|
29
|
-
return undefined
|
|
30
|
-
}
|
|
27
|
+
export async function write(path: string, data: string | undefined, options: { sanity?: boolean } = {}): Promise<string | undefined> {
|
|
31
28
|
return switcher(
|
|
32
29
|
{
|
|
33
|
-
node: async (path
|
|
30
|
+
node: async (path, data: string | undefined, { sanity }: { sanity?: boolean }) => {
|
|
31
|
+
if (!data || data?.trim() === '') {
|
|
32
|
+
return undefined
|
|
33
|
+
}
|
|
34
|
+
|
|
34
35
|
try {
|
|
35
36
|
const oldContent = await fs.readFile(resolve(path), {
|
|
36
37
|
encoding: 'utf-8',
|
|
@@ -42,7 +43,7 @@ export async function write(path: string, data: string, options: { sanity?: bool
|
|
|
42
43
|
/* empty */
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
await fs.outputFile(resolve(path), data, { encoding: 'utf-8' })
|
|
46
|
+
await fs.outputFile(resolve(path), data.trim(), { encoding: 'utf-8' })
|
|
46
47
|
|
|
47
48
|
if (sanity) {
|
|
48
49
|
const savedData = await fs.readFile(resolve(path), {
|
|
@@ -58,29 +59,29 @@ export async function write(path: string, data: string, options: { sanity?: bool
|
|
|
58
59
|
|
|
59
60
|
return data
|
|
60
61
|
},
|
|
61
|
-
bun: async (path: string, data: string, { sanity }: { sanity?: boolean }) => {
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
bun: async (path: string, data: string | undefined, { sanity }: { sanity?: boolean }) => {
|
|
63
|
+
if (!data || data?.trim() === '') {
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
const file = Bun.file(resolve(path))
|
|
67
|
-
const savedData = await file.text()
|
|
67
|
+
await Bun.write(resolve(path), data.trim())
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
if (sanity) {
|
|
70
|
+
const file = Bun.file(resolve(path))
|
|
71
|
+
const savedData = await file.text()
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
if (savedData?.toString() !== data?.toString()) {
|
|
74
|
+
throw new Error(`Sanity check failed for ${path}\n\nData[${path.length}]:\n${path}\n\nSaved[${savedData.length}]:\n${savedData}\n`)
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
return
|
|
77
|
-
} catch (e) {
|
|
78
|
-
console.error(e)
|
|
77
|
+
return savedData
|
|
79
78
|
}
|
|
79
|
+
|
|
80
|
+
return data
|
|
80
81
|
},
|
|
81
82
|
},
|
|
82
83
|
'node',
|
|
83
|
-
)(path, data
|
|
84
|
+
)(path, data, options)
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
// biome-ignore lint/suspicious/noTsIgnore: production ready
|
|
@@ -101,31 +102,29 @@ declare global {
|
|
|
101
102
|
|
|
102
103
|
export const fsPlugin = createPlugin<Options, ExtendOptions>({
|
|
103
104
|
name: 'fs',
|
|
104
|
-
|
|
105
|
+
install(app, options = {}) {
|
|
106
|
+
if (options.clean) {
|
|
107
|
+
fs.removeSync(options.clean.path)
|
|
108
|
+
}
|
|
109
|
+
|
|
105
110
|
app.context.events.on('process:progress', async ({ file, source }) => {
|
|
106
|
-
if (options
|
|
107
|
-
await options.
|
|
111
|
+
if (options.onBeforeWrite) {
|
|
112
|
+
await options.onBeforeWrite(file.path, source)
|
|
108
113
|
}
|
|
109
114
|
await write(file.path, source, { sanity: false })
|
|
110
115
|
})
|
|
111
|
-
|
|
112
|
-
app.context.events.on('start', async () => {
|
|
113
|
-
if (options?.clean) {
|
|
114
|
-
await fs.remove(options.clean.path)
|
|
115
|
-
}
|
|
116
|
-
})
|
|
117
116
|
},
|
|
118
|
-
inject(app) {
|
|
117
|
+
inject(app, { dryRun } = {}) {
|
|
119
118
|
return {
|
|
120
119
|
async write(
|
|
121
120
|
options = {
|
|
122
121
|
extension: { '.ts': '.ts' },
|
|
123
|
-
dryRun: false,
|
|
124
122
|
},
|
|
125
123
|
) {
|
|
126
124
|
await app.context.fileManager.write({
|
|
125
|
+
mode: app.context.options?.mode,
|
|
127
126
|
extension: options.extension,
|
|
128
|
-
dryRun
|
|
127
|
+
dryRun,
|
|
129
128
|
parsers: app.context.installedParsers,
|
|
130
129
|
})
|
|
131
130
|
},
|