@kubb/fabric-core 0.2.15 → 0.2.17

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 (43) hide show
  1. package/dist/{Fabric-C-AqOkTA.d.ts → Fabric-BezqNTQ9.d.cts} +95 -46
  2. package/dist/{Fabric-0mXLgmur.d.cts → Fabric-CVe8cc8b.d.ts} +95 -46
  3. package/dist/{defineProperty-B05cRoSl.cjs → defineProperty-DZi5DvrW.cjs} +39 -13
  4. package/dist/defineProperty-DZi5DvrW.cjs.map +1 -0
  5. package/dist/{defineProperty-BZknW4oy.js → defineProperty-DcP1vZ2K.js} +28 -14
  6. package/dist/defineProperty-DcP1vZ2K.js.map +1 -0
  7. package/dist/index.cjs +81 -33
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +25 -3
  10. package/dist/index.d.ts +25 -3
  11. package/dist/index.js +81 -33
  12. package/dist/index.js.map +1 -1
  13. package/dist/parsers/typescript.d.cts +2 -2
  14. package/dist/parsers/typescript.d.ts +2 -2
  15. package/dist/parsers.d.cts +2 -2
  16. package/dist/parsers.d.ts +2 -2
  17. package/dist/plugins.cjs +5 -13
  18. package/dist/plugins.cjs.map +1 -1
  19. package/dist/plugins.d.cts +7 -11
  20. package/dist/plugins.d.ts +7 -11
  21. package/dist/plugins.js +3 -11
  22. package/dist/plugins.js.map +1 -1
  23. package/dist/types.cjs.map +1 -1
  24. package/dist/types.d.cts +2 -3
  25. package/dist/types.d.ts +2 -3
  26. package/dist/types.js.map +1 -1
  27. package/dist/{typescriptParser-By3ckLtc.d.ts → typescriptParser-B5SxjtvV.d.ts} +2 -2
  28. package/dist/{typescriptParser-B4-y6QxR.d.cts → typescriptParser-PfAO0SSm.d.cts} +2 -2
  29. package/package.json +1 -1
  30. package/src/Fabric.ts +96 -48
  31. package/src/FileManager.ts +35 -14
  32. package/src/KubbFile.ts +0 -2
  33. package/src/createFile.ts +87 -71
  34. package/src/defineFabric.ts +41 -24
  35. package/src/plugins/barrelPlugin.ts +7 -9
  36. package/src/plugins/fsPlugin.ts +1 -9
  37. package/src/types.ts +0 -1
  38. package/src/utils/AsyncEventEmitter.ts +41 -8
  39. package/dist/defineFabric-D_m6CB1s.d.ts +0 -9
  40. package/dist/defineFabric-Dkt2l0wC.d.cts +0 -9
  41. package/dist/defineProperty-B05cRoSl.cjs.map +0 -1
  42. package/dist/defineProperty-BZknW4oy.js.map +0 -1
  43. package/src/utils/EventEmitter.ts +0 -31
package/dist/types.d.cts CHANGED
@@ -1,3 +1,2 @@
1
- import { a as FabricOptions, i as FabricMode, m as KubbFile_d_exports, n as FabricConfig, r as FabricContext } from "./Fabric-0mXLgmur.cjs";
2
- import { t as DefineFabric } from "./defineFabric-Dkt2l0wC.cjs";
3
- export { type DefineFabric, type FabricConfig, type FabricContext, type FabricMode, type FabricOptions, KubbFile_d_exports as KubbFile };
1
+ import { a as FabricOptions, i as FabricMode, m as KubbFile_d_exports, n as FabricConfig, r as FabricContext } from "./Fabric-BezqNTQ9.cjs";
2
+ export { type FabricConfig, type FabricContext, type FabricMode, type FabricOptions, KubbFile_d_exports as KubbFile };
package/dist/types.d.ts CHANGED
@@ -1,3 +1,2 @@
1
- import { a as FabricOptions, i as FabricMode, m as KubbFile_d_exports, n as FabricConfig, r as FabricContext } from "./Fabric-C-AqOkTA.js";
2
- import { t as DefineFabric } from "./defineFabric-D_m6CB1s.js";
3
- export { type DefineFabric, type FabricConfig, type FabricContext, type FabricMode, type FabricOptions, KubbFile_d_exports as KubbFile };
1
+ import { a as FabricOptions, i as FabricMode, m as KubbFile_d_exports, n as FabricConfig, r as FabricContext } from "./Fabric-CVe8cc8b.js";
2
+ export { type FabricConfig, type FabricContext, type FabricMode, type FabricOptions, KubbFile_d_exports as KubbFile };
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../src/KubbFile.ts"],"sourcesContent":["type BasePath<T extends string = string> = `${T}/`\n\nexport type Import = {\n /**\n * Import name to be used\n * @example [\"useState\"]\n * @example \"React\"\n */\n name:\n | string\n | Array<\n | string\n | {\n propertyName: string\n name?: string\n }\n >\n /**\n * Path for the import\n * @example '@kubb/core'\n */\n path: string\n /**\n * Add `type` prefix to the import, this will result in: `import type { Type } from './path'`.\n */\n isTypeOnly?: boolean\n\n isNameSpace?: boolean\n /**\n * When root is set it will get the path with relative getRelativePath(root, path).\n */\n root?: string\n}\n\nexport type Source = {\n name?: string\n value?: string\n isTypeOnly?: boolean\n /**\n * Has const or type 'export'\n * @default false\n */\n isExportable?: boolean\n /**\n * When set, barrel generation will add this\n * @default false\n */\n isIndexable?: boolean\n}\n\nexport type Export = {\n /**\n * Export name to be used.\n * @example [\"useState\"]\n * @example \"React\"\n */\n name?: string | Array<string>\n /**\n * Path for the import.\n * @example '@kubb/core'\n */\n path: string\n /**\n * Add `type` prefix to the export, this will result in: `export type { Type } from './path'`.\n */\n isTypeOnly?: boolean\n /**\n * Make it possible to override the name, this will result in: `export * as aliasName from './path'`.\n */\n asAlias?: boolean\n}\n\nexport type Extname = '.ts' | '.js' | '.tsx' | '.json' | `.${string}`\n\nexport type Mode = 'single' | 'split'\n\n/**\n * Name to be used to dynamicly create the baseName(based on input.path)\n * Based on UNIX basename\n * @link https://nodejs.org/api/path.html#pathbasenamepath-suffix\n */\nexport type BaseName = `${string}.${string}`\n\n/**\n * Path will be full qualified path to a specified file\n */\nexport type Path = string\n\nexport type AdvancedPath<T extends BaseName = BaseName> = `${BasePath}${T}`\n\nexport type OptionalPath = Path | undefined | null\n\nexport type File<TMeta extends object = object> = {\n /**\n * Name to be used to create the path\n * Based on UNIX basename, `${name}.extname`\n * @link https://nodejs.org/api/path.html#pathbasenamepath-suffix\n */\n baseName: BaseName\n /**\n * Path will be full qualified path to a specified file\n */\n path: AdvancedPath<BaseName> | Path\n sources: Array<Source>\n imports?: Array<Import>\n exports?: Array<Export>\n /**\n * Use extra meta, this is getting used to generate the barrel/index files.\n */\n meta?: TMeta\n banner?: string\n footer?: string\n}\n\nexport type ResolvedImport = Import\n\nexport type ResolvedExport = Export\n\nexport type ResolvedFile<TMeta extends object = object> = File<TMeta> & {\n /**\n * @default hash\n */\n id: string\n /**\n * Contains the first part of the baseName, generated based on baseName\n * @link https://nodejs.org/api/path.html#pathformatpathobject\n */\n name: string\n extname: Extname\n imports: Array<ResolvedImport>\n exports: Array<ResolvedExport>\n}\n"],"mappings":""}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../src/KubbFile.ts"],"sourcesContent":["type BasePath<T extends string = string> = `${T}/`\n\nexport type Import = {\n /**\n * Import name to be used\n * @example [\"useState\"]\n * @example \"React\"\n */\n name:\n | string\n | Array<\n | string\n | {\n propertyName: string\n name?: string\n }\n >\n /**\n * Path for the import\n * @example '@kubb/core'\n */\n path: string\n /**\n * Add `type` prefix to the import, this will result in: `import type { Type } from './path'`.\n */\n isTypeOnly?: boolean\n\n isNameSpace?: boolean\n /**\n * When root is set it will get the path with relative getRelativePath(root, path).\n */\n root?: string\n}\n\nexport type Source = {\n name?: string\n value?: string\n isTypeOnly?: boolean\n /**\n * Has const or type 'export'\n * @default false\n */\n isExportable?: boolean\n /**\n * When set, barrel generation will add this\n * @default false\n */\n isIndexable?: boolean\n}\n\nexport type Export = {\n /**\n * Export name to be used.\n * @example [\"useState\"]\n * @example \"React\"\n */\n name?: string | Array<string>\n /**\n * Path for the import.\n * @example '@kubb/core'\n */\n path: string\n /**\n * Add `type` prefix to the export, this will result in: `export type { Type } from './path'`.\n */\n isTypeOnly?: boolean\n /**\n * Make it possible to override the name, this will result in: `export * as aliasName from './path'`.\n */\n asAlias?: boolean\n}\n\nexport type Extname = '.ts' | '.js' | '.tsx' | '.json' | `.${string}`\n\nexport type Mode = 'single' | 'split'\n\n/**\n * Name to be used to dynamicly create the baseName(based on input.path)\n * Based on UNIX basename\n * @link https://nodejs.org/api/path.html#pathbasenamepath-suffix\n */\nexport type BaseName = `${string}.${string}`\n\n/**\n * Path will be full qualified path to a specified file\n */\nexport type Path = string\n\nexport type AdvancedPath<T extends BaseName = BaseName> = `${BasePath}${T}`\n\nexport type File<TMeta extends object = object> = {\n /**\n * Name to be used to create the path\n * Based on UNIX basename, `${name}.extname`\n * @link https://nodejs.org/api/path.html#pathbasenamepath-suffix\n */\n baseName: BaseName\n /**\n * Path will be full qualified path to a specified file\n */\n path: AdvancedPath<BaseName> | Path\n sources: Array<Source>\n imports?: Array<Import>\n exports?: Array<Export>\n /**\n * Use extra meta, this is getting used to generate the barrel/index files.\n */\n meta?: TMeta\n banner?: string\n footer?: string\n}\n\nexport type ResolvedImport = Import\n\nexport type ResolvedExport = Export\n\nexport type ResolvedFile<TMeta extends object = object> = File<TMeta> & {\n /**\n * @default hash\n */\n id: string\n /**\n * Contains the first part of the baseName, generated based on baseName\n * @link https://nodejs.org/api/path.html#pathformatpathobject\n */\n name: string\n extname: Extname\n imports: Array<ResolvedImport>\n exports: Array<ResolvedExport>\n}\n"],"mappings":""}
@@ -1,4 +1,4 @@
1
- import { u as Parser } from "./Fabric-C-AqOkTA.js";
1
+ import { u as Parser } from "./Fabric-CVe8cc8b.js";
2
2
  import ts from "typescript";
3
3
 
4
4
  //#region src/parsers/typescriptParser.d.ts
@@ -47,4 +47,4 @@ declare function createExport({
47
47
  declare const typescriptParser: Parser<[], any>;
48
48
  //#endregion
49
49
  export { typescriptParser as i, createImport as n, print as r, createExport as t };
50
- //# sourceMappingURL=typescriptParser-By3ckLtc.d.ts.map
50
+ //# sourceMappingURL=typescriptParser-B5SxjtvV.d.ts.map
@@ -1,4 +1,4 @@
1
- import { u as Parser } from "./Fabric-0mXLgmur.cjs";
1
+ import { u as Parser } from "./Fabric-BezqNTQ9.cjs";
2
2
  import ts from "typescript";
3
3
 
4
4
  //#region src/parsers/typescriptParser.d.ts
@@ -47,4 +47,4 @@ declare function createExport({
47
47
  declare const typescriptParser: Parser<[], any>;
48
48
  //#endregion
49
49
  export { typescriptParser as i, createImport as n, print as r, createExport as t };
50
- //# sourceMappingURL=typescriptParser-B4-y6QxR.d.cts.map
50
+ //# sourceMappingURL=typescriptParser-PfAO0SSm.d.cts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/fabric-core",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "description": "Core functionality for Kubb's plugin-based code generation system, providing the foundation for transforming OpenAPI specifications.",
5
5
  "keywords": [
6
6
  "typescript",
package/src/Fabric.ts CHANGED
@@ -10,51 +10,63 @@ 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
53
  /**
40
54
  * Called when FileManager is adding files to its cache
41
55
  */
42
-
43
56
  'file:add': [{ files: KubbFile.ResolvedFile[] }]
57
+ 'file:resolve:path': [{ file: KubbFile.File }]
58
+ 'file:resolve:name': [{ file: KubbFile.File }]
44
59
  'write:start': [{ files: KubbFile.ResolvedFile[] }]
45
60
  'write:end': [{ files: KubbFile.ResolvedFile[] }]
46
- /**
47
- * Called for each file when processing begins.
48
- */
61
+
62
+ /** Called for each file when processing begins. */
49
63
  'file:start': [{ file: KubbFile.ResolvedFile; index: number; total: number }]
50
64
 
51
- /**
52
- * Called for each file when processing finishes.
53
- */
65
+ /** Called for each file when processing completes. */
54
66
  'file:end': [{ file: KubbFile.ResolvedFile; index: number; total: number }]
55
67
 
56
68
  /**
57
- * Called periodically (or after each file) to indicate progress.
69
+ * Called periodically (or per file) to indicate progress.
58
70
  * Useful for progress bars or logging.
59
71
  */
60
72
  'process:progress': [
@@ -67,51 +79,87 @@ export type FabricEvents = {
67
79
  },
68
80
  ]
69
81
 
70
- /**
71
- * Called once all files have been processed successfully.
72
- */
82
+ /** Called once all files have been processed successfully. */
73
83
  'process:end': [{ files: KubbFile.ResolvedFile[] }]
74
84
  }
75
85
 
76
- export type FabricContext<TOptions extends FabricOptions = FabricOptions> = {
77
- config?: FabricConfig<TOptions>
86
+ /**
87
+ * Shared context passed to all plugins, parsers, and Fabric internals.
88
+ */
89
+ export interface FabricContext<T extends FabricOptions = FabricOptions> extends AsyncEventEmitter<FabricEvents> {
90
+ /** The active Fabric configuration. */
91
+ config: FabricConfig<T>
92
+
93
+ /** The internal file manager handling file creation, merging, and writing. */
78
94
  fileManager: FileManager
79
- files: Array<KubbFile.ResolvedFile>
80
- addFile(...files: Array<KubbFile.File>): Promise<void>
95
+
96
+ /** List of files currently in memory. */
97
+ files: KubbFile.ResolvedFile[]
98
+
99
+ /** Add new files to the file manager. */
100
+ addFile(...files: KubbFile.File[]): Promise<void>
101
+
102
+ /** Track installed plugins and parsers to prevent duplicates. */
81
103
  installedPlugins: Set<Plugin>
82
104
  installedParsers: Set<Parser>
83
- } & AsyncEventEmitter<FabricEvents>
105
+ }
84
106
 
85
- export type FabricMode = 'sequential' | 'parallel'
107
+ /**
108
+ * Base configuration object for Fabric.
109
+ */
110
+ export type FabricConfig<T extends FabricOptions = FabricOptions> = T
86
111
 
112
+ /**
113
+ * Utility type that checks whether all properties of `T` are optional.
114
+ */
87
115
  type AllOptional<T> = {} extends T ? true : false
88
116
 
89
- export type FabricConfig<TOptions extends FabricOptions> = {
90
- options: TOptions
91
- }
92
-
117
+ /**
118
+ * Defines the signature of a plugin or parser's `install` function.
119
+ */
93
120
  export type Install<TOptions = unknown> = TOptions extends any[]
94
121
  ? (context: FabricContext, ...options: TOptions) => void | Promise<void>
95
122
  : AllOptional<TOptions> extends true
96
- ? (context: FabricContext, options: TOptions | undefined) => void | Promise<void>
123
+ ? (context: FabricContext, options?: TOptions) => void | Promise<void>
97
124
  : (context: FabricContext, options: TOptions) => void | Promise<void>
98
125
 
99
- export type Inject<TOptions = unknown, TAppExtension extends Record<string, any> = {}> = TOptions extends any[]
100
- ? (context: FabricContext, ...options: TOptions) => Partial<TAppExtension>
126
+ /**
127
+ * Defines the signature of a plugin or parser's `inject` function.
128
+ * Returns an object that extends the Fabric instance.
129
+ */
130
+ export type Inject<TOptions = unknown, TExtension extends Record<string, any> = {}> = TOptions extends any[]
131
+ ? (context: FabricContext, ...options: TOptions) => Partial<TExtension>
101
132
  : AllOptional<TOptions> extends true
102
- ? (context: FabricContext, options: TOptions | undefined) => Partial<TAppExtension>
103
- : (context: FabricContext, options: TOptions) => Partial<TAppExtension>
104
-
105
- export interface Fabric<TOptions extends FabricOptions = FabricOptions> extends Kubb.Fabric {
106
- context: FabricContext<TOptions>
107
- files: Array<KubbFile.ResolvedFile>
108
- use<TPluginOptions = unknown, TMeta extends object = object, TAppExtension extends Record<string, any> = {}>(
109
- pluginOrParser: Plugin<TPluginOptions, TAppExtension> | Parser<TPluginOptions, TMeta>,
133
+ ? (context: FabricContext, options?: TOptions) => Partial<TExtension>
134
+ : (context: FabricContext, options: TOptions) => Partial<TExtension>
135
+
136
+ /**
137
+ * The main Fabric runtime interface.
138
+ * Provides access to the current context, registered plugins, files, and utility methods.
139
+ */
140
+ export interface Fabric<T extends FabricOptions = FabricOptions> extends Kubb.Fabric {
141
+ /** The shared context for this Fabric instance. */
142
+ context: FabricContext<T>
143
+
144
+ /** The files managed by this Fabric instance. */
145
+ files: KubbFile.ResolvedFile[]
146
+
147
+ /**
148
+ * Install a plugin or parser into Fabric.
149
+ *
150
+ * @param target - The plugin or parser to install.
151
+ * @param options - Optional configuration or arguments for the target.
152
+ * @returns A Fabric instance extended by the plugin (if applicable).
153
+ */
154
+ use<TPluginOptions = unknown, TMeta extends object = object, TExtension extends Record<string, any> = {}>(
155
+ target: Plugin<TPluginOptions, TExtension> | Parser<TPluginOptions, TMeta>,
110
156
  ...options: TPluginOptions extends any[]
111
157
  ? NoInfer<TPluginOptions>
112
158
  : AllOptional<TPluginOptions> extends true
113
159
  ? [NoInfer<TPluginOptions>?] // Optional when all props are optional
114
160
  : [NoInfer<TPluginOptions>] // Required otherwise
115
- ): (this & TAppExtension) | Promise<this & TAppExtension>
116
- addFile(...files: Array<KubbFile.File>): Promise<void>
161
+ ): (this & TExtension) | Promise<this & TExtension>
162
+
163
+ /** Add one or more files to the Fabric file manager. */
164
+ addFile(...files: KubbFile.File[]): Promise<void>
117
165
  }
@@ -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
 
@@ -32,23 +33,27 @@ export class FileManager {
32
33
  return this
33
34
  }
34
35
 
35
- async add(...files: Array<KubbFile.File>) {
36
- const resolvedFiles: Array<KubbFile.ResolvedFile> = []
36
+ async #resolvePath(file: KubbFile.File): Promise<KubbFile.File> {
37
+ await this.events.emit('file:resolve:path', { file })
37
38
 
38
- const mergedFiles = new Map<string, KubbFile.File>()
39
+ return file
40
+ }
39
41
 
40
- files.forEach((file) => {
41
- const existing = mergedFiles.get(file.path)
42
- if (existing) {
43
- mergedFiles.set(file.path, mergeFile(existing, file))
44
- } else {
45
- mergedFiles.set(file.path, file)
46
- }
47
- })
42
+ async #resolveName(file: KubbFile.File): Promise<KubbFile.File> {
43
+ await this.events.emit('file:resolve:name', { file })
44
+
45
+ return file
46
+ }
47
+
48
+ async add(...files: Array<KubbFile.File>) {
49
+ const resolvedFiles: Array<KubbFile.ResolvedFile> = []
48
50
 
49
- for (const file of mergedFiles.values()) {
51
+ for (let file of files) {
50
52
  const existing = this.#cache.get(file.path)
51
53
 
54
+ file = await this.#resolveName(file)
55
+ file = await this.#resolvePath(file)
56
+
52
57
  const merged = existing ? mergeFile(existing, file) : file
53
58
  const resolvedFile = createFile(merged)
54
59
 
@@ -64,6 +69,7 @@ export class FileManager {
64
69
  }
65
70
 
66
71
  flush() {
72
+ this.#filesCache = null
67
73
  this.#cache.flush()
68
74
  }
69
75
 
@@ -73,21 +79,36 @@ export class FileManager {
73
79
 
74
80
  deleteByPath(path: KubbFile.Path): void {
75
81
  this.#cache.delete(path)
82
+ this.#filesCache = null
76
83
  }
77
84
 
78
85
  clear(): void {
79
86
  this.#cache.clear()
87
+ this.#filesCache = null
80
88
  }
81
89
 
82
90
  get files(): Array<KubbFile.ResolvedFile> {
91
+ if (this.#filesCache) {
92
+ return [...this.#filesCache]
93
+ }
94
+
83
95
  const cachedKeys = this.#cache.keys()
84
96
 
85
97
  // order by path length and if file is a barrel file
86
98
  const keys = orderBy(cachedKeys, [(v) => v.length, (v) => trimExtName(v).endsWith('index')])
87
99
 
88
- const files = keys.map((key) => this.#cache.get(key))
100
+ const files: Array<KubbFile.ResolvedFile> = []
101
+
102
+ for (const key of keys) {
103
+ const file = this.#cache.get(key)
104
+ if (file) {
105
+ files.push(file)
106
+ }
107
+ }
108
+
109
+ this.#filesCache = files
89
110
 
90
- return files.filter(Boolean)
111
+ return [...files]
91
112
  }
92
113
 
93
114
  //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
- 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
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
- 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
- }
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
- // new item, append name
115
- if (!prevByPath) {
116
- return [
117
- ...prev,
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
- // 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])]
88
+ const cached = usageCache.get(importName)
89
+ if (cached !== undefined) {
90
+ return cached
91
+ }
128
92
 
129
- return prev
130
- }
93
+ const isUsed = source.includes(importName) || exportedNameLookup.has(importName)
94
+ usageCache.set(importName, isUsed)
131
95
 
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,