@kubb/fabric-core 0.2.15 → 0.2.16
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/{Fabric-0mXLgmur.d.cts → Fabric-AmREkq58.d.ts} +93 -49
- package/dist/{Fabric-C-AqOkTA.d.ts → Fabric-CBrTERuf.d.cts} +93 -49
- package/dist/{defineProperty-B05cRoSl.cjs → defineProperty-Dlhh3lSJ.cjs} +33 -19
- package/dist/defineProperty-Dlhh3lSJ.cjs.map +1 -0
- package/dist/{defineProperty-BZknW4oy.js → defineProperty-_FBdEen_.js} +28 -14
- package/dist/defineProperty-_FBdEen_.js.map +1 -0
- package/dist/index.cjs +57 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -3
- package/dist/index.d.ts +25 -3
- package/dist/index.js +57 -26
- package/dist/index.js.map +1 -1
- package/dist/parsers/typescript.d.cts +2 -2
- package/dist/parsers/typescript.d.ts +2 -2
- package/dist/parsers.d.cts +2 -2
- package/dist/parsers.d.ts +2 -2
- package/dist/plugins.cjs +3 -9
- package/dist/plugins.cjs.map +1 -1
- package/dist/plugins.d.cts +1 -11
- package/dist/plugins.d.ts +1 -11
- package/dist/plugins.js +1 -7
- package/dist/plugins.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +2 -3
- package/dist/types.d.ts +2 -3
- package/dist/types.js.map +1 -1
- package/dist/{typescriptParser-B4-y6QxR.d.cts → typescriptParser-C3B3dzh_.d.cts} +2 -2
- package/dist/{typescriptParser-By3ckLtc.d.ts → typescriptParser-DaOfAlmM.d.ts} +2 -2
- package/package.json +1 -1
- package/src/Fabric.ts +99 -50
- package/src/FileManager.ts +19 -2
- package/src/KubbFile.ts +0 -2
- package/src/createFile.ts +87 -71
- package/src/defineFabric.ts +41 -24
- package/src/plugins/barrelPlugin.ts +0 -8
- package/src/plugins/fsPlugin.ts +0 -8
- package/src/types.ts +0 -1
- package/src/utils/AsyncEventEmitter.ts +29 -8
- package/dist/defineFabric-D_m6CB1s.d.ts +0 -9
- package/dist/defineFabric-Dkt2l0wC.d.cts +0 -9
- package/dist/defineProperty-B05cRoSl.cjs.map +0 -1
- package/dist/defineProperty-BZknW4oy.js.map +0 -1
- package/src/utils/EventEmitter.ts +0 -31
package/src/Fabric.ts
CHANGED
|
@@ -10,51 +10,61 @@ declare global {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Component placeholder type.
|
|
15
|
+
* May later be extended to support specific runtime renderers.
|
|
16
|
+
*/
|
|
13
17
|
export type Component = any
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Defines core runtime options for Fabric.
|
|
21
|
+
*/
|
|
22
|
+
export interface FabricOptions {
|
|
16
23
|
/**
|
|
24
|
+
* Determines how Fabric processes files.
|
|
25
|
+
* - `sequential`: files are processed one by one
|
|
26
|
+
* - `parallel`: files are processed concurrently
|
|
27
|
+
*
|
|
17
28
|
* @default 'sequential'
|
|
18
29
|
*/
|
|
19
30
|
mode?: FabricMode
|
|
20
31
|
}
|
|
21
32
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Available modes for file processing.
|
|
35
|
+
*/
|
|
36
|
+
export type FabricMode = 'sequential' | 'parallel'
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Event definitions emitted during the Fabric lifecycle.
|
|
40
|
+
*/
|
|
41
|
+
export interface FabricEvents {
|
|
42
|
+
/** Called at the beginning of the app lifecycle. */
|
|
26
43
|
start: []
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*/
|
|
44
|
+
|
|
45
|
+
/** Called at the end of the app lifecycle. */
|
|
30
46
|
end: []
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*/
|
|
47
|
+
|
|
48
|
+
/** Called when Fabric is rendering. */
|
|
34
49
|
render: [{ fabric: Fabric }]
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
*/
|
|
50
|
+
|
|
51
|
+
/** Called once before any files are processed. */
|
|
38
52
|
'process:start': [{ files: KubbFile.ResolvedFile[] }]
|
|
39
|
-
/**
|
|
40
|
-
* Called when FileManager is adding files to its cache
|
|
41
|
-
*/
|
|
42
53
|
|
|
54
|
+
/** Called when files are added to the FileManager cache. */
|
|
43
55
|
'file:add': [{ files: KubbFile.ResolvedFile[] }]
|
|
56
|
+
|
|
44
57
|
'write:start': [{ files: KubbFile.ResolvedFile[] }]
|
|
45
58
|
'write:end': [{ files: KubbFile.ResolvedFile[] }]
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*/
|
|
59
|
+
|
|
60
|
+
/** Called for each file when processing begins. */
|
|
49
61
|
'file:start': [{ file: KubbFile.ResolvedFile; index: number; total: number }]
|
|
50
62
|
|
|
51
|
-
/**
|
|
52
|
-
* Called for each file when processing finishes.
|
|
53
|
-
*/
|
|
63
|
+
/** Called for each file when processing completes. */
|
|
54
64
|
'file:end': [{ file: KubbFile.ResolvedFile; index: number; total: number }]
|
|
55
65
|
|
|
56
66
|
/**
|
|
57
|
-
* Called periodically (or
|
|
67
|
+
* Called periodically (or per file) to indicate progress.
|
|
58
68
|
* Useful for progress bars or logging.
|
|
59
69
|
*/
|
|
60
70
|
'process:progress': [
|
|
@@ -67,51 +77,90 @@ export type FabricEvents = {
|
|
|
67
77
|
},
|
|
68
78
|
]
|
|
69
79
|
|
|
70
|
-
/**
|
|
71
|
-
* Called once all files have been processed successfully.
|
|
72
|
-
*/
|
|
80
|
+
/** Called once all files have been processed successfully. */
|
|
73
81
|
'process:end': [{ files: KubbFile.ResolvedFile[] }]
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Shared context passed to all plugins, parsers, and Fabric internals.
|
|
86
|
+
*/
|
|
87
|
+
export interface FabricContext<T extends FabricOptions = FabricOptions> extends AsyncEventEmitter<FabricEvents> {
|
|
88
|
+
/** The active Fabric configuration. */
|
|
89
|
+
config?: FabricConfig<T>
|
|
90
|
+
|
|
91
|
+
/** The internal file manager handling file creation, merging, and writing. */
|
|
78
92
|
fileManager: FileManager
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
|
|
94
|
+
/** List of files currently in memory. */
|
|
95
|
+
files: KubbFile.ResolvedFile[]
|
|
96
|
+
|
|
97
|
+
/** Add new files to the file manager. */
|
|
98
|
+
addFile(...files: KubbFile.File[]): Promise<void>
|
|
99
|
+
|
|
100
|
+
/** Track installed plugins and parsers to prevent duplicates. */
|
|
81
101
|
installedPlugins: Set<Plugin>
|
|
82
102
|
installedParsers: Set<Parser>
|
|
83
|
-
}
|
|
103
|
+
}
|
|
84
104
|
|
|
85
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Base configuration object for Fabric.
|
|
107
|
+
*/
|
|
108
|
+
export interface FabricConfig<T extends FabricOptions = FabricOptions> {
|
|
109
|
+
/** The runtime options used to configure Fabric. */
|
|
110
|
+
options: T
|
|
111
|
+
}
|
|
86
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Utility type that checks whether all properties of `T` are optional.
|
|
115
|
+
*/
|
|
87
116
|
type AllOptional<T> = {} extends T ? true : false
|
|
88
117
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Defines the signature of a plugin or parser's `install` function.
|
|
120
|
+
*/
|
|
93
121
|
export type Install<TOptions = unknown> = TOptions extends any[]
|
|
94
122
|
? (context: FabricContext, ...options: TOptions) => void | Promise<void>
|
|
95
123
|
: AllOptional<TOptions> extends true
|
|
96
|
-
? (context: FabricContext, options
|
|
124
|
+
? (context: FabricContext, options?: TOptions) => void | Promise<void>
|
|
97
125
|
: (context: FabricContext, options: TOptions) => void | Promise<void>
|
|
98
126
|
|
|
99
|
-
|
|
100
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Defines the signature of a plugin or parser's `inject` function.
|
|
129
|
+
* Returns an object that extends the Fabric instance.
|
|
130
|
+
*/
|
|
131
|
+
export type Inject<TOptions = unknown, TExtension extends Record<string, any> = {}> = TOptions extends any[]
|
|
132
|
+
? (context: FabricContext, ...options: TOptions) => Partial<TExtension>
|
|
101
133
|
: AllOptional<TOptions> extends true
|
|
102
|
-
? (context: FabricContext, options
|
|
103
|
-
: (context: FabricContext, options: TOptions) => Partial<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
134
|
+
? (context: FabricContext, options?: TOptions) => Partial<TExtension>
|
|
135
|
+
: (context: FabricContext, options: TOptions) => Partial<TExtension>
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The main Fabric runtime interface.
|
|
139
|
+
* Provides access to the current context, registered plugins, files, and utility methods.
|
|
140
|
+
*/
|
|
141
|
+
export interface Fabric<T extends FabricOptions = FabricOptions> extends Kubb.Fabric {
|
|
142
|
+
/** The shared context for this Fabric instance. */
|
|
143
|
+
context: FabricContext<T>
|
|
144
|
+
|
|
145
|
+
/** The files managed by this Fabric instance. */
|
|
146
|
+
files: KubbFile.ResolvedFile[]
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Install a plugin or parser into Fabric.
|
|
150
|
+
*
|
|
151
|
+
* @param target - The plugin or parser to install.
|
|
152
|
+
* @param options - Optional configuration or arguments for the target.
|
|
153
|
+
* @returns A Fabric instance extended by the plugin (if applicable).
|
|
154
|
+
*/
|
|
155
|
+
use<TPluginOptions = unknown, TMeta extends object = object, TExtension extends Record<string, any> = {}>(
|
|
156
|
+
target: Plugin<TPluginOptions, TExtension> | Parser<TPluginOptions, TMeta>,
|
|
110
157
|
...options: TPluginOptions extends any[]
|
|
111
158
|
? NoInfer<TPluginOptions>
|
|
112
159
|
: AllOptional<TPluginOptions> extends true
|
|
113
160
|
? [NoInfer<TPluginOptions>?] // Optional when all props are optional
|
|
114
161
|
: [NoInfer<TPluginOptions>] // Required otherwise
|
|
115
|
-
): (this &
|
|
116
|
-
|
|
162
|
+
): (this & TExtension) | Promise<this & TExtension>
|
|
163
|
+
|
|
164
|
+
/** Add one or more files to the Fabric file manager. */
|
|
165
|
+
addFile(...files: KubbFile.File[]): Promise<void>
|
|
117
166
|
}
|
package/src/FileManager.ts
CHANGED
|
@@ -22,6 +22,7 @@ type Options = {
|
|
|
22
22
|
|
|
23
23
|
export class FileManager {
|
|
24
24
|
#cache = new Cache<KubbFile.ResolvedFile>()
|
|
25
|
+
#filesCache: Array<KubbFile.ResolvedFile> | null = null
|
|
25
26
|
events: AsyncEventEmitter<FabricEvents>
|
|
26
27
|
processor: FileProcessor
|
|
27
28
|
|
|
@@ -64,6 +65,7 @@ export class FileManager {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
flush() {
|
|
68
|
+
this.#filesCache = null
|
|
67
69
|
this.#cache.flush()
|
|
68
70
|
}
|
|
69
71
|
|
|
@@ -73,21 +75,36 @@ export class FileManager {
|
|
|
73
75
|
|
|
74
76
|
deleteByPath(path: KubbFile.Path): void {
|
|
75
77
|
this.#cache.delete(path)
|
|
78
|
+
this.#filesCache = null
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
clear(): void {
|
|
79
82
|
this.#cache.clear()
|
|
83
|
+
this.#filesCache = null
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
get files(): Array<KubbFile.ResolvedFile> {
|
|
87
|
+
if (this.#filesCache) {
|
|
88
|
+
return [...this.#filesCache]
|
|
89
|
+
}
|
|
90
|
+
|
|
83
91
|
const cachedKeys = this.#cache.keys()
|
|
84
92
|
|
|
85
93
|
// order by path length and if file is a barrel file
|
|
86
94
|
const keys = orderBy(cachedKeys, [(v) => v.length, (v) => trimExtName(v).endsWith('index')])
|
|
87
95
|
|
|
88
|
-
const files
|
|
96
|
+
const files: Array<KubbFile.ResolvedFile> = []
|
|
97
|
+
|
|
98
|
+
for (const key of keys) {
|
|
99
|
+
const file = this.#cache.get(key)
|
|
100
|
+
if (file) {
|
|
101
|
+
files.push(file)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.#filesCache = files
|
|
89
106
|
|
|
90
|
-
return files
|
|
107
|
+
return [...files]
|
|
91
108
|
}
|
|
92
109
|
|
|
93
110
|
//TODO add test and check if write of FileManager contains the newly added file
|
package/src/KubbFile.ts
CHANGED
|
@@ -88,8 +88,6 @@ export type Path = string
|
|
|
88
88
|
|
|
89
89
|
export type AdvancedPath<T extends BaseName = BaseName> = `${BasePath}${T}`
|
|
90
90
|
|
|
91
|
-
export type OptionalPath = Path | undefined | null
|
|
92
|
-
|
|
93
91
|
export type File<TMeta extends object = object> = {
|
|
94
92
|
/**
|
|
95
93
|
* Name to be used to create the path
|
package/src/createFile.ts
CHANGED
|
@@ -5,11 +5,6 @@ import { isDeepEqual, uniqueBy } from 'remeda'
|
|
|
5
5
|
import type * as KubbFile from './KubbFile.ts'
|
|
6
6
|
import { trimExtName } from './utils/trimExtName.ts'
|
|
7
7
|
|
|
8
|
-
function hashObject(obj: Record<string, unknown>): string {
|
|
9
|
-
const str = JSON.stringify(obj, Object.keys(obj).sort())
|
|
10
|
-
return createHash('sha256').update(str).digest('hex')
|
|
11
|
-
}
|
|
12
|
-
|
|
13
8
|
export function combineSources(sources: Array<KubbFile.Source>): Array<KubbFile.Source> {
|
|
14
9
|
return uniqueBy(sources, (obj) => [obj.name, obj.isExportable, obj.isTypeOnly] as const)
|
|
15
10
|
}
|
|
@@ -65,79 +60,100 @@ export function combineExports(exports: Array<KubbFile.Export>): Array<KubbFile.
|
|
|
65
60
|
}
|
|
66
61
|
|
|
67
62
|
export function combineImports(imports: Array<KubbFile.Import>, exports: Array<KubbFile.Export>, source?: string): Array<KubbFile.Import> {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (!source) {
|
|
80
|
-
return true
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const checker = (name?: string) => {
|
|
84
|
-
return name && source.includes(name)
|
|
63
|
+
const exportedNameLookup = new Set<string>()
|
|
64
|
+
for (const item of exports) {
|
|
65
|
+
const { name } = item
|
|
66
|
+
if (!name) {
|
|
67
|
+
continue
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (Array.isArray(name)) {
|
|
71
|
+
for (const value of name) {
|
|
72
|
+
if (value) {
|
|
73
|
+
exportedNameLookup.add(value)
|
|
85
74
|
}
|
|
86
|
-
|
|
87
|
-
return checker(importName) || exports.some(({ name }) => (Array.isArray(name) ? name.some(checker) : checker(name)))
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (curr.path === curr.root) {
|
|
91
|
-
// root and path are the same file, remove the "./" import
|
|
92
|
-
return prev
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// merge all names and check if the importName is being used in the generated source and if not filter those imports out
|
|
96
|
-
if (Array.isArray(name)) {
|
|
97
|
-
name = name.filter((item) => (typeof item === 'string' ? hasImportInSource(item) : hasImportInSource(item.propertyName)))
|
|
98
75
|
}
|
|
76
|
+
continue
|
|
77
|
+
}
|
|
99
78
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const prevByPathNameAndIsTypeOnly = prev.findLast((imp) => imp.path === curr.path && isDeepEqual(imp.name, name) && imp.isTypeOnly)
|
|
103
|
-
|
|
104
|
-
if (prevByPathNameAndIsTypeOnly) {
|
|
105
|
-
// we already have an export that has the same path but uses `isTypeOnly` (import type ...)
|
|
106
|
-
return prev
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// already unique enough or name is empty
|
|
110
|
-
if (uniquePrev || (Array.isArray(name) && !name.length)) {
|
|
111
|
-
return prev
|
|
112
|
-
}
|
|
79
|
+
exportedNameLookup.add(name)
|
|
80
|
+
}
|
|
113
81
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
...curr,
|
|
120
|
-
name,
|
|
121
|
-
},
|
|
122
|
-
]
|
|
123
|
-
}
|
|
82
|
+
const usageCache = new Map<string, boolean>()
|
|
83
|
+
const hasImportInSource = (importName: string): boolean => {
|
|
84
|
+
if (!source) {
|
|
85
|
+
return true
|
|
86
|
+
}
|
|
124
87
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
88
|
+
const cached = usageCache.get(importName)
|
|
89
|
+
if (cached !== undefined) {
|
|
90
|
+
return cached
|
|
91
|
+
}
|
|
128
92
|
|
|
129
|
-
|
|
130
|
-
|
|
93
|
+
const isUsed = source.includes(importName) || exportedNameLookup.has(importName)
|
|
94
|
+
usageCache.set(importName, isUsed)
|
|
131
95
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return prev
|
|
135
|
-
}
|
|
96
|
+
return isUsed
|
|
97
|
+
}
|
|
136
98
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
99
|
+
return orderBy(imports, [
|
|
100
|
+
(v) => !!Array.isArray(v.name),
|
|
101
|
+
(v) => !v.isTypeOnly,
|
|
102
|
+
(v) => v.path,
|
|
103
|
+
(v) => !!v.name,
|
|
104
|
+
(v) => (Array.isArray(v.name) ? orderBy(v.name) : v.name),
|
|
105
|
+
]).reduce<Array<KubbFile.Import>>((prev, curr) => {
|
|
106
|
+
let name = Array.isArray(curr.name) ? [...new Set(curr.name)] : curr.name
|
|
107
|
+
|
|
108
|
+
if (curr.path === curr.root) {
|
|
109
|
+
// root and path are the same file, remove the "./" import
|
|
110
|
+
return prev
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// merge all names and check if the importName is being used in the generated source and if not filter those imports out
|
|
114
|
+
if (Array.isArray(name)) {
|
|
115
|
+
name = name.filter((item) => (typeof item === 'string' ? hasImportInSource(item) : hasImportInSource(item.propertyName)))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const prevByPath = prev.findLast((imp) => imp.path === curr.path && imp.isTypeOnly === curr.isTypeOnly)
|
|
119
|
+
const uniquePrev = prev.findLast((imp) => imp.path === curr.path && isDeepEqual(imp.name, name) && imp.isTypeOnly === curr.isTypeOnly)
|
|
120
|
+
const prevByPathNameAndIsTypeOnly = prev.findLast((imp) => imp.path === curr.path && isDeepEqual(imp.name, name) && imp.isTypeOnly)
|
|
121
|
+
|
|
122
|
+
if (prevByPathNameAndIsTypeOnly) {
|
|
123
|
+
// we already have an export that has the same path but uses `isTypeOnly` (import type ...)
|
|
124
|
+
return prev
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// already unique enough or name is empty
|
|
128
|
+
if (uniquePrev || (Array.isArray(name) && !name.length)) {
|
|
129
|
+
return prev
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// new item, append name
|
|
133
|
+
if (!prevByPath) {
|
|
134
|
+
return [
|
|
135
|
+
...prev,
|
|
136
|
+
{
|
|
137
|
+
...curr,
|
|
138
|
+
name,
|
|
139
|
+
},
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// merge all names when prev and current both have the same isTypeOnly set
|
|
144
|
+
if (prevByPath && Array.isArray(prevByPath.name) && Array.isArray(name) && prevByPath.isTypeOnly === curr.isTypeOnly) {
|
|
145
|
+
prevByPath.name = [...new Set([...prevByPath.name, ...name])]
|
|
146
|
+
|
|
147
|
+
return prev
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// no import was found in the source, ignore import
|
|
151
|
+
if (!Array.isArray(name) && name && !hasImportInSource(name)) {
|
|
152
|
+
return prev
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return [...prev, curr]
|
|
156
|
+
}, [])
|
|
141
157
|
}
|
|
142
158
|
|
|
143
159
|
/**
|
|
@@ -156,7 +172,7 @@ export function createFile<TMeta extends object = object>(file: KubbFile.File<TM
|
|
|
156
172
|
|
|
157
173
|
return {
|
|
158
174
|
...file,
|
|
159
|
-
id:
|
|
175
|
+
id: createHash('sha256').update(file.path).digest('hex'),
|
|
160
176
|
name: trimExtName(file.baseName),
|
|
161
177
|
extname,
|
|
162
178
|
imports: imports,
|
package/src/defineFabric.ts
CHANGED
|
@@ -5,22 +5,40 @@ import type { Parser } from './parsers/types.ts'
|
|
|
5
5
|
import type { Plugin } from './plugins/types.ts'
|
|
6
6
|
import { AsyncEventEmitter } from './utils/AsyncEventEmitter.ts'
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Function that initializes the root Fabric instance.
|
|
10
|
+
*
|
|
11
|
+
* Used for setting up plugins, parsers, or performing side effects
|
|
12
|
+
* once the Fabric context is ready.
|
|
13
|
+
*/
|
|
14
|
+
type FabricInitializer<T extends FabricOptions> = (fabric: Fabric<T>) => void | Promise<void>
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
/**
|
|
17
|
+
* A function returned by {@link defineFabric} that creates a Fabric instance.
|
|
18
|
+
*/
|
|
19
|
+
export type CreateFabric<T extends FabricOptions> = (config?: FabricConfig<T>) => Fabric<T>
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Defines a new Fabric factory function.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* export const createFabric = defineFabric((fabric) => {
|
|
26
|
+
* fabric.use(myPlugin())
|
|
27
|
+
* })
|
|
28
|
+
*/
|
|
29
|
+
export function defineFabric<T extends FabricOptions>(init?: FabricInitializer<T>): CreateFabric<T> {
|
|
30
|
+
function create(config?: FabricConfig<T>): Fabric<T> {
|
|
14
31
|
const events = new AsyncEventEmitter<FabricEvents>()
|
|
15
32
|
const installedPlugins = new Set<Plugin<any>>()
|
|
16
33
|
const installedParsers = new Set<Parser<any>>()
|
|
17
34
|
const fileManager = new FileManager({ events })
|
|
18
|
-
|
|
35
|
+
|
|
36
|
+
const context: FabricContext<T> = {
|
|
19
37
|
get files() {
|
|
20
38
|
return fileManager.files
|
|
21
39
|
},
|
|
22
|
-
async addFile(...
|
|
23
|
-
await fileManager.add(...
|
|
40
|
+
async addFile(...files) {
|
|
41
|
+
await fileManager.add(...files)
|
|
24
42
|
},
|
|
25
43
|
config,
|
|
26
44
|
fileManager,
|
|
@@ -31,57 +49,56 @@ export function defineFabric<TOptions extends FabricOptions>(instance?: RootRend
|
|
|
31
49
|
onOnce: events.onOnce.bind(events),
|
|
32
50
|
removeAll: events.removeAll.bind(events),
|
|
33
51
|
emit: events.emit.bind(events),
|
|
34
|
-
} as FabricContext<
|
|
52
|
+
} as FabricContext<T>
|
|
35
53
|
|
|
36
|
-
const fabric = {
|
|
54
|
+
const fabric: Fabric<T> = {
|
|
37
55
|
context,
|
|
38
56
|
get files() {
|
|
39
57
|
return fileManager.files
|
|
40
58
|
},
|
|
41
|
-
async addFile(...
|
|
42
|
-
await fileManager.add(...
|
|
59
|
+
async addFile(...files) {
|
|
60
|
+
await fileManager.add(...files)
|
|
43
61
|
},
|
|
44
62
|
async use(pluginOrParser, ...options) {
|
|
45
|
-
const args = options
|
|
46
|
-
|
|
47
63
|
if (pluginOrParser.type === 'plugin') {
|
|
48
64
|
if (installedPlugins.has(pluginOrParser)) {
|
|
49
|
-
console.warn(`Plugin ${pluginOrParser.name}
|
|
65
|
+
console.warn(`Plugin "${pluginOrParser.name}" already applied.`)
|
|
50
66
|
} else {
|
|
51
67
|
installedPlugins.add(pluginOrParser)
|
|
52
68
|
}
|
|
53
69
|
|
|
54
|
-
if (
|
|
70
|
+
if (isFunction(pluginOrParser.inject)) {
|
|
55
71
|
const injecter = pluginOrParser.inject
|
|
56
72
|
|
|
57
|
-
const
|
|
58
|
-
Object.assign(fabric,
|
|
73
|
+
const injected = (injecter as any)(context, ...options)
|
|
74
|
+
Object.assign(fabric, injected)
|
|
59
75
|
}
|
|
60
76
|
}
|
|
77
|
+
|
|
61
78
|
if (pluginOrParser.type === 'parser') {
|
|
62
79
|
if (installedParsers.has(pluginOrParser)) {
|
|
63
|
-
console.warn(`Parser ${pluginOrParser.name}
|
|
80
|
+
console.warn(`Parser "${pluginOrParser.name}" already applied.`)
|
|
64
81
|
} else {
|
|
65
82
|
installedParsers.add(pluginOrParser)
|
|
66
83
|
}
|
|
67
84
|
}
|
|
68
85
|
|
|
69
|
-
if (
|
|
86
|
+
if (isFunction(pluginOrParser.install)) {
|
|
70
87
|
const installer = pluginOrParser.install
|
|
71
88
|
|
|
72
|
-
await (installer as any)(context, ...
|
|
89
|
+
await (installer as any)(context, ...options)
|
|
73
90
|
}
|
|
74
91
|
|
|
75
92
|
return fabric
|
|
76
93
|
},
|
|
77
|
-
} as Fabric<
|
|
94
|
+
} as Fabric<T>
|
|
78
95
|
|
|
79
|
-
if (
|
|
80
|
-
|
|
96
|
+
if (init) {
|
|
97
|
+
init(fabric)
|
|
81
98
|
}
|
|
82
99
|
|
|
83
100
|
return fabric
|
|
84
101
|
}
|
|
85
102
|
|
|
86
|
-
return
|
|
103
|
+
return create
|
|
87
104
|
}
|
|
@@ -24,14 +24,6 @@ type ExtendOptions = {
|
|
|
24
24
|
writeEntry(options: WriteEntryOptions): Promise<void>
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// biome-ignore lint/suspicious/noTsIgnore: production ready
|
|
28
|
-
// @ts-ignore
|
|
29
|
-
declare module '@kubb/fabric-core' {
|
|
30
|
-
interface Fabric {
|
|
31
|
-
writeEntry(options: WriteEntryOptions): Promise<void>
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
27
|
declare global {
|
|
36
28
|
namespace Kubb {
|
|
37
29
|
interface Fabric {
|
package/src/plugins/fsPlugin.ts
CHANGED
|
@@ -77,14 +77,6 @@ export async function write(path: string, data: string | undefined, options: { s
|
|
|
77
77
|
return data
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
// biome-ignore lint/suspicious/noTsIgnore: production ready
|
|
81
|
-
// @ts-ignore
|
|
82
|
-
declare module '@kubb/fabric-core' {
|
|
83
|
-
interface Fabric {
|
|
84
|
-
write(options?: WriteOptions): Promise<void>
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
80
|
declare global {
|
|
89
81
|
namespace Kubb {
|
|
90
82
|
interface Fabric {
|
package/src/types.ts
CHANGED
|
@@ -1,27 +1,47 @@
|
|
|
1
1
|
import { EventEmitter as NodeEventEmitter } from 'node:events'
|
|
2
|
+
import type { FabricMode } from '../Fabric.ts'
|
|
3
|
+
|
|
4
|
+
type Options = {
|
|
5
|
+
mode?: FabricMode
|
|
6
|
+
maxListener?: number
|
|
7
|
+
}
|
|
2
8
|
|
|
3
9
|
export class AsyncEventEmitter<TEvents extends Record<string, any>> {
|
|
4
|
-
constructor(maxListener = 100) {
|
|
10
|
+
constructor({ maxListener = 100, mode = 'sequential' }: Options = {}) {
|
|
5
11
|
this.#emitter.setMaxListeners(maxListener)
|
|
12
|
+
this.#mode = mode
|
|
6
13
|
}
|
|
14
|
+
|
|
7
15
|
#emitter = new NodeEventEmitter()
|
|
16
|
+
#mode: FabricMode
|
|
8
17
|
|
|
9
18
|
async emit<TEventName extends keyof TEvents & string>(eventName: TEventName, ...eventArgs: TEvents[TEventName]): Promise<void> {
|
|
10
19
|
const listeners = this.#emitter.listeners(eventName) as Array<(...args: TEvents[TEventName]) => any>
|
|
11
20
|
|
|
12
21
|
if (listeners.length === 0) {
|
|
13
|
-
return
|
|
22
|
+
return
|
|
14
23
|
}
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
listeners
|
|
25
|
+
if (this.#mode === 'sequential') {
|
|
26
|
+
// Run listeners one by one, in order
|
|
27
|
+
for (const listener of listeners) {
|
|
28
|
+
try {
|
|
29
|
+
await listener(...eventArgs)
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.error(`Error in listener for "${eventName}":`, err)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
// Run all listeners concurrently
|
|
36
|
+
const promises = listeners.map(async (listener) => {
|
|
18
37
|
try {
|
|
19
|
-
|
|
38
|
+
await listener(...eventArgs)
|
|
20
39
|
} catch (err) {
|
|
21
|
-
console.error(`Error in
|
|
40
|
+
console.error(`Error in listener for "${eventName}":`, err)
|
|
22
41
|
}
|
|
23
|
-
})
|
|
24
|
-
|
|
42
|
+
})
|
|
43
|
+
await Promise.all(promises)
|
|
44
|
+
}
|
|
25
45
|
}
|
|
26
46
|
|
|
27
47
|
on<TEventName extends keyof TEvents & string>(eventName: TEventName, handler: (...eventArg: TEvents[TEventName]) => void): void {
|
|
@@ -39,6 +59,7 @@ export class AsyncEventEmitter<TEvents extends Record<string, any>> {
|
|
|
39
59
|
off<TEventName extends keyof TEvents & string>(eventName: TEventName, handler: (...eventArg: TEvents[TEventName]) => void): void {
|
|
40
60
|
this.#emitter.off(eventName, handler as any)
|
|
41
61
|
}
|
|
62
|
+
|
|
42
63
|
removeAll(): void {
|
|
43
64
|
this.#emitter.removeAllListeners()
|
|
44
65
|
}
|