@kubb/fabric-core 0.2.14 → 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.
Files changed (57) hide show
  1. package/README.md +5 -1
  2. package/dist/{Fabric-BfnUdEpq.d.cts → Fabric-AmREkq58.d.ts} +108 -63
  3. package/dist/{Fabric-CxMkO4Rt.d.ts → Fabric-CBrTERuf.d.cts} +108 -63
  4. package/dist/{defineProperty-CspRhtP3.cjs → defineProperty-Dlhh3lSJ.cjs} +58 -44
  5. package/dist/defineProperty-Dlhh3lSJ.cjs.map +1 -0
  6. package/dist/{defineProperty-BtekiGIK.js → defineProperty-_FBdEen_.js} +53 -39
  7. package/dist/defineProperty-_FBdEen_.js.map +1 -0
  8. package/dist/index.cjs +102 -60
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +25 -3
  11. package/dist/index.d.ts +25 -3
  12. package/dist/index.js +102 -60
  13. package/dist/index.js.map +1 -1
  14. package/dist/parsers/typescript.d.cts +2 -2
  15. package/dist/parsers/typescript.d.ts +2 -2
  16. package/dist/parsers.cjs.map +1 -1
  17. package/dist/parsers.d.cts +2 -2
  18. package/dist/parsers.d.ts +2 -2
  19. package/dist/parsers.js.map +1 -1
  20. package/dist/plugins.cjs +98 -104
  21. package/dist/plugins.cjs.map +1 -1
  22. package/dist/plugins.d.cts +30 -40
  23. package/dist/plugins.d.ts +30 -40
  24. package/dist/plugins.js +94 -100
  25. package/dist/plugins.js.map +1 -1
  26. package/dist/types.cjs.map +1 -1
  27. package/dist/types.d.cts +2 -3
  28. package/dist/types.d.ts +2 -3
  29. package/dist/types.js.map +1 -1
  30. package/dist/{typescriptParser-Dz9T1BQ1.d.cts → typescriptParser-C3B3dzh_.d.cts} +2 -2
  31. package/dist/typescriptParser-CNHO6H2_.cjs.map +1 -1
  32. package/dist/typescriptParser-CWT7zCJy.js.map +1 -1
  33. package/dist/{typescriptParser-DypTa1AN.d.ts → typescriptParser-DaOfAlmM.d.ts} +2 -2
  34. package/package.json +1 -1
  35. package/src/Fabric.ts +102 -52
  36. package/src/FileManager.ts +23 -6
  37. package/src/FileProcessor.ts +3 -4
  38. package/src/KubbFile.ts +0 -2
  39. package/src/createFile.ts +90 -74
  40. package/src/defineFabric.ts +53 -28
  41. package/src/index.ts +3 -3
  42. package/src/parsers/tsxParser.ts +1 -1
  43. package/src/parsers/types.ts +1 -1
  44. package/src/parsers/typescriptParser.ts +1 -1
  45. package/src/plugins/barrelPlugin.ts +13 -21
  46. package/src/plugins/fsPlugin.ts +8 -16
  47. package/src/plugins/graphPlugin.ts +8 -9
  48. package/src/plugins/index.ts +2 -3
  49. package/src/plugins/progressPlugin.ts +6 -6
  50. package/src/plugins/types.ts +1 -1
  51. package/src/types.ts +1 -2
  52. package/src/utils/AsyncEventEmitter.ts +29 -8
  53. package/dist/defineFabric-CR1OjcoI.d.ts +0 -9
  54. package/dist/defineFabric-TvKfRefj.d.cts +0 -9
  55. package/dist/defineProperty-BtekiGIK.js.map +0 -1
  56. package/dist/defineProperty-CspRhtP3.cjs.map +0 -1
  57. package/src/utils/EventEmitter.ts +0 -31
package/src/Fabric.ts CHANGED
@@ -1,8 +1,8 @@
1
+ import type { FileManager } from './FileManager.ts'
1
2
  import type * as KubbFile from './KubbFile.ts'
2
- import type { Plugin } from './plugins/types.ts'
3
3
  import type { Parser } from './parsers/types.ts'
4
+ import type { Plugin } from './plugins/types.ts'
4
5
  import type { AsyncEventEmitter } from './utils/AsyncEventEmitter.ts'
5
- import type { FileManager } from './FileManager.ts'
6
6
 
7
7
  declare global {
8
8
  namespace Kubb {
@@ -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
- export type FabricOptions = {
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
- export type FabricEvents = {
23
- /**
24
- * Called in the beginning of the app lifecycle.
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
- * Called in the end of the app lifecycle.
29
- */
44
+
45
+ /** Called at the end of the app lifecycle. */
30
46
  end: []
31
- /**
32
- * Called when being rendered
33
- */
47
+
48
+ /** Called when Fabric is rendering. */
34
49
  render: [{ fabric: Fabric }]
35
- /**
36
- * Called once before processing any files.
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
- * Called for each file when processing begins.
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 after each file) to indicate progress.
67
+ * Called periodically (or per file) to indicate progress.
58
68
  * Useful for progress bars or logging.
59
69
  */
60
70
  'process:progress': [
@@ -67,50 +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
- export type FabricContext<TOptions extends FabricOptions> = {
77
- config?: FabricConfig<TOptions>
78
- events: AsyncEventEmitter<FabricEvents>
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. */
79
92
  fileManager: FileManager
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. */
80
101
  installedPlugins: Set<Plugin>
81
102
  installedParsers: Set<Parser>
82
103
  }
83
104
 
84
- export type FabricMode = 'sequential' | 'parallel'
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
+ }
85
112
 
113
+ /**
114
+ * Utility type that checks whether all properties of `T` are optional.
115
+ */
86
116
  type AllOptional<T> = {} extends T ? true : false
87
117
 
88
- export type FabricConfig<TOptions extends FabricOptions> = {
89
- options: TOptions
90
- }
91
-
118
+ /**
119
+ * Defines the signature of a plugin or parser's `install` function.
120
+ */
92
121
  export type Install<TOptions = unknown> = TOptions extends any[]
93
- ? (app: Fabric, ...options: TOptions) => void | Promise<void>
122
+ ? (context: FabricContext, ...options: TOptions) => void | Promise<void>
94
123
  : AllOptional<TOptions> extends true
95
- ? (app: Fabric, options: TOptions | undefined) => void | Promise<void>
96
- : (app: Fabric, options: TOptions) => void | Promise<void>
124
+ ? (context: FabricContext, options?: TOptions) => void | Promise<void>
125
+ : (context: FabricContext, options: TOptions) => void | Promise<void>
97
126
 
98
- export type Inject<TOptions = unknown, TAppExtension extends Record<string, any> = {}> = TOptions extends any[]
99
- ? (app: Fabric, ...options: TOptions) => Partial<TAppExtension>
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>
100
133
  : AllOptional<TOptions> extends true
101
- ? (app: Fabric, options: TOptions | undefined) => Partial<TAppExtension>
102
- : (app: Fabric, options: TOptions) => Partial<TAppExtension>
103
-
104
- export interface Fabric<TOptions extends FabricOptions = FabricOptions> extends Kubb.Fabric {
105
- context: FabricContext<TOptions>
106
- files: Array<KubbFile.ResolvedFile>
107
- use<TPluginOptions = unknown, TMeta extends object = object, TAppExtension extends Record<string, any> = {}>(
108
- pluginOrParser: Plugin<TPluginOptions, TAppExtension> | Parser<TPluginOptions, TMeta>,
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>,
109
157
  ...options: TPluginOptions extends any[]
110
158
  ? NoInfer<TPluginOptions>
111
159
  : AllOptional<TPluginOptions> extends true
112
160
  ? [NoInfer<TPluginOptions>?] // Optional when all props are optional
113
161
  : [NoInfer<TPluginOptions>] // Required otherwise
114
- ): (this & TAppExtension) | Promise<this & TAppExtension>
115
- addFile(...files: Array<KubbFile.File>): Promise<void>
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>
116
166
  }
@@ -1,11 +1,11 @@
1
- import type * as KubbFile from './KubbFile.ts'
2
- import { Cache } from './utils/Cache.ts'
3
- import { trimExtName } from './utils/trimExtName.ts'
4
1
  import { orderBy } from 'natural-orderby'
5
2
  import { createFile } from './createFile.ts'
3
+ import type { FabricEvents } from './Fabric.ts'
6
4
  import { FileProcessor, type ProcessFilesProps } from './FileProcessor.ts'
5
+ import type * as KubbFile from './KubbFile.ts'
7
6
  import { AsyncEventEmitter } from './utils/AsyncEventEmitter.ts'
8
- import type { FabricEvents } from './Fabric.ts'
7
+ import { Cache } from './utils/Cache.ts'
8
+ import { trimExtName } from './utils/trimExtName.ts'
9
9
 
10
10
  function mergeFile<TMeta extends object = object>(a: KubbFile.File<TMeta>, b: KubbFile.File<TMeta>): KubbFile.File<TMeta> {
11
11
  return {
@@ -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 = keys.map((key) => this.#cache.get(key))
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.filter(Boolean)
107
+ return [...files]
91
108
  }
92
109
 
93
110
  //TODO add test and check if write of FileManager contains the newly added file
@@ -1,10 +1,9 @@
1
- import type * as KubbFile from './KubbFile.ts'
2
1
  import pLimit from 'p-limit'
3
-
4
- import type { Parser } from './parsers/types.ts'
2
+ import type { FabricEvents, FabricMode } from './Fabric.ts'
3
+ import type * as KubbFile from './KubbFile.ts'
5
4
  import { defaultParser } from './parsers/defaultParser.ts'
5
+ import type { Parser } from './parsers/types.ts'
6
6
  import { AsyncEventEmitter } from './utils/AsyncEventEmitter.ts'
7
- import type { FabricEvents, FabricMode } from './Fabric.ts'
8
7
 
9
8
  export type ProcessFilesProps = {
10
9
  parsers?: Set<Parser>
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
@@ -1,14 +1,9 @@
1
- import type * as KubbFile from './KubbFile.ts'
2
- import { trimExtName } from './utils/trimExtName.ts'
3
1
  import { createHash } from 'node:crypto'
4
2
  import path from 'node:path'
5
- import { isDeepEqual, uniqueBy } from 'remeda'
6
3
  import { orderBy } from 'natural-orderby'
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
- }
4
+ import { isDeepEqual, uniqueBy } from 'remeda'
5
+ import type * as KubbFile from './KubbFile.ts'
6
+ import { trimExtName } from './utils/trimExtName.ts'
12
7
 
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)
@@ -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
- return orderBy(imports, [
69
- (v) => !!Array.isArray(v.name),
70
- (v) => !v.isTypeOnly,
71
- (v) => v.path,
72
- (v) => !!v.name,
73
- (v) => (Array.isArray(v.name) ? orderBy(v.name) : v.name),
74
- ]).reduce(
75
- (prev, curr) => {
76
- let name = Array.isArray(curr.name) ? [...new Set(curr.name)] : curr.name
77
-
78
- const hasImportInSource = (importName: string) => {
79
- if (!source) {
80
- return true
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)
81
74
  }
82
-
83
- const checker = (name?: string) => {
84
- return name && source.includes(name)
85
- }
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
- const prevByPath = prev.findLast((imp) => imp.path === curr.path && imp.isTypeOnly === curr.isTypeOnly)
101
- const uniquePrev = prev.findLast((imp) => imp.path === curr.path && isDeepEqual(imp.name, name) && imp.isTypeOnly === curr.isTypeOnly)
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
- }
79
+ exportedNameLookup.add(name)
80
+ }
108
81
 
109
- // already unique enough or name is empty
110
- if (uniquePrev || (Array.isArray(name) && !name.length)) {
111
- return prev
112
- }
82
+ const usageCache = new Map<string, boolean>()
83
+ const hasImportInSource = (importName: string): boolean => {
84
+ if (!source) {
85
+ return true
86
+ }
113
87
 
114
- // new item, append name
115
- if (!prevByPath) {
116
- return [
117
- ...prev,
118
- {
119
- ...curr,
120
- name,
121
- },
122
- ]
123
- }
88
+ const cached = usageCache.get(importName)
89
+ if (cached !== undefined) {
90
+ return cached
91
+ }
124
92
 
125
- // merge all names when prev and current both have the same isTypeOnly set
126
- if (prevByPath && Array.isArray(prevByPath.name) && Array.isArray(name) && prevByPath.isTypeOnly === curr.isTypeOnly) {
127
- prevByPath.name = [...new Set([...prevByPath.name, ...name])]
93
+ const isUsed = source.includes(importName) || exportedNameLookup.has(importName)
94
+ usageCache.set(importName, isUsed)
128
95
 
129
- return prev
130
- }
131
-
132
- // no import was found in the source, ignore import
133
- if (!Array.isArray(name) && name && !hasImportInSource(name)) {
134
- return prev
135
- }
96
+ return isUsed
97
+ }
136
98
 
137
- return [...prev, curr]
138
- },
139
- [] as Array<KubbFile.Import>,
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: hashObject({ path: file.path }),
175
+ id: createHash('sha256').update(file.path).digest('hex'),
160
176
  name: trimExtName(file.baseName),
161
177
  extname,
162
178
  imports: imports,
@@ -1,79 +1,104 @@
1
- import { FileManager } from './FileManager.ts'
2
1
  import { isFunction } from 'remeda'
3
- import type { Plugin } from './plugins/types.ts'
2
+ import type { Fabric, FabricConfig, FabricContext, FabricEvents, FabricOptions } from './Fabric.ts'
3
+ import { FileManager } from './FileManager.ts'
4
4
  import type { Parser } from './parsers/types.ts'
5
+ import type { Plugin } from './plugins/types.ts'
5
6
  import { AsyncEventEmitter } from './utils/AsyncEventEmitter.ts'
6
- import type { FabricConfig, FabricContext, FabricEvents, FabricOptions } from './Fabric.ts'
7
-
8
- import type { Fabric } from './Fabric.ts'
9
7
 
10
- type RootRenderFunction<TOptions extends FabricOptions> = (fabric: Fabric<TOptions>) => void | Promise<void>
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>
11
15
 
12
- export type DefineFabric<TOptions extends FabricOptions> = (config?: FabricConfig<TOptions>) => Fabric<TOptions>
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>
13
20
 
14
- export function defineFabric<TOptions extends FabricOptions>(instance?: RootRenderFunction<TOptions>): DefineFabric<TOptions> {
15
- function creator(config?: FabricConfig<TOptions>): Fabric<TOptions> {
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> {
16
31
  const events = new AsyncEventEmitter<FabricEvents>()
17
32
  const installedPlugins = new Set<Plugin<any>>()
18
33
  const installedParsers = new Set<Parser<any>>()
19
34
  const fileManager = new FileManager({ events })
20
- const context = {
21
- events,
35
+
36
+ const context: FabricContext<T> = {
37
+ get files() {
38
+ return fileManager.files
39
+ },
40
+ async addFile(...files) {
41
+ await fileManager.add(...files)
42
+ },
22
43
  config,
23
44
  fileManager,
24
45
  installedPlugins,
25
46
  installedParsers,
26
- } as FabricContext<TOptions>
47
+ on: events.on.bind(events),
48
+ off: events.off.bind(events),
49
+ onOnce: events.onOnce.bind(events),
50
+ removeAll: events.removeAll.bind(events),
51
+ emit: events.emit.bind(events),
52
+ } as FabricContext<T>
27
53
 
28
- const fabric = {
54
+ const fabric: Fabric<T> = {
29
55
  context,
30
56
  get files() {
31
57
  return fileManager.files
32
58
  },
33
- async addFile(...newFiles) {
34
- await fileManager.add(...newFiles)
59
+ async addFile(...files) {
60
+ await fileManager.add(...files)
35
61
  },
36
62
  async use(pluginOrParser, ...options) {
37
- const args = options
38
-
39
63
  if (pluginOrParser.type === 'plugin') {
40
64
  if (installedPlugins.has(pluginOrParser)) {
41
- console.warn(`Plugin ${pluginOrParser.name} has already been applied to target fabric.`)
65
+ console.warn(`Plugin "${pluginOrParser.name}" already applied.`)
42
66
  } else {
43
67
  installedPlugins.add(pluginOrParser)
44
68
  }
45
69
 
46
- if (pluginOrParser.inject && isFunction(pluginOrParser.inject)) {
70
+ if (isFunction(pluginOrParser.inject)) {
47
71
  const injecter = pluginOrParser.inject
48
72
 
49
- const extraApp = (injecter as any)(fabric, ...args)
50
- Object.assign(fabric, extraApp)
73
+ const injected = (injecter as any)(context, ...options)
74
+ Object.assign(fabric, injected)
51
75
  }
52
76
  }
77
+
53
78
  if (pluginOrParser.type === 'parser') {
54
79
  if (installedParsers.has(pluginOrParser)) {
55
- console.warn(`Parser ${pluginOrParser.name} has already been applied to target fabric.`)
80
+ console.warn(`Parser "${pluginOrParser.name}" already applied.`)
56
81
  } else {
57
82
  installedParsers.add(pluginOrParser)
58
83
  }
59
84
  }
60
85
 
61
- if (pluginOrParser && isFunction(pluginOrParser.install)) {
86
+ if (isFunction(pluginOrParser.install)) {
62
87
  const installer = pluginOrParser.install
63
88
 
64
- await (installer as any)(fabric, ...args)
89
+ await (installer as any)(context, ...options)
65
90
  }
66
91
 
67
92
  return fabric
68
93
  },
69
- } as Fabric<TOptions>
94
+ } as Fabric<T>
70
95
 
71
- if (instance) {
72
- instance(fabric)
96
+ if (init) {
97
+ init(fabric)
73
98
  }
74
99
 
75
100
  return fabric
76
101
  }
77
102
 
78
- return creator
103
+ return create
79
104
  }
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { createFabric } from './createFabric.ts'
2
- export { defineFabric } from './defineFabric.ts'
3
- export { FileManager } from './FileManager.ts'
4
2
  export { createFile } from './createFile.ts'
5
- export { FileProcessor } from './FileProcessor.ts'
3
+ export { defineFabric } from './defineFabric.ts'
6
4
  // we need this to override the globals of `fabric.use`
7
5
  export type { Fabric } from './Fabric.ts'
6
+ export { FileManager } from './FileManager.ts'
7
+ export { FileProcessor } from './FileProcessor.ts'
@@ -1,5 +1,5 @@
1
- import { typescriptParser } from './typescriptParser.ts'
2
1
  import { createParser } from './createParser.ts'
2
+ import { typescriptParser } from './typescriptParser.ts'
3
3
 
4
4
  export const tsxParser = createParser({
5
5
  name: 'tsx',
@@ -1,5 +1,5 @@
1
- import type * as KubbFile from '../KubbFile.ts'
2
1
  import type { Install } from '../Fabric.ts'
2
+ import type * as KubbFile from '../KubbFile.ts'
3
3
 
4
4
  type PrintOptions = {
5
5
  extname?: KubbFile.Extname
@@ -1,7 +1,7 @@
1
+ import path from 'node:path'
1
2
  import ts from 'typescript'
2
3
  import { getRelativePath } from '../utils/getRelativePath.ts'
3
4
  import { trimExtName } from '../utils/trimExtName.ts'
4
- import path from 'node:path'
5
5
  import { createParser } from './createParser.ts'
6
6
 
7
7
  const { factory } = ts