@kubb/core 3.0.0-alpha.0 → 3.0.0-alpha.10

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 (99) hide show
  1. package/dist/FileManager-BbUCeEyv.d.cts +469 -0
  2. package/dist/FileManager-CpuFz6eH.d.ts +469 -0
  3. package/dist/chunk-2EU7DMPM.js +97 -0
  4. package/dist/chunk-2EU7DMPM.js.map +1 -0
  5. package/dist/chunk-5E2I6KH4.cjs +1071 -0
  6. package/dist/chunk-5E2I6KH4.cjs.map +1 -0
  7. package/dist/{chunk-XRC6KXC3.cjs → chunk-A6PCLWEY.cjs} +5 -5
  8. package/dist/{chunk-XRC6KXC3.cjs.map → chunk-A6PCLWEY.cjs.map} +1 -1
  9. package/dist/chunk-DID47EQD.cjs +102 -0
  10. package/dist/chunk-DID47EQD.cjs.map +1 -0
  11. package/dist/chunk-HBQM723K.js +1063 -0
  12. package/dist/chunk-HBQM723K.js.map +1 -0
  13. package/dist/chunk-L6YLVCKM.js +929 -0
  14. package/dist/chunk-L6YLVCKM.js.map +1 -0
  15. package/dist/{chunk-XRUOSVKX.cjs → chunk-M7NLNCSM.cjs} +71 -38
  16. package/dist/chunk-M7NLNCSM.cjs.map +1 -0
  17. package/dist/{chunk-3OXCZ5DJ.js → chunk-MU3CBCQT.js} +59 -35
  18. package/dist/chunk-MU3CBCQT.js.map +1 -0
  19. package/dist/chunk-NB4JMN75.cjs +1548 -0
  20. package/dist/chunk-NB4JMN75.cjs.map +1 -0
  21. package/dist/chunk-SEH6NUCX.cjs +42 -0
  22. package/dist/chunk-SEH6NUCX.cjs.map +1 -0
  23. package/dist/index.cjs +496 -552
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +9 -477
  26. package/dist/index.d.ts +9 -477
  27. package/dist/index.js +438 -492
  28. package/dist/index.js.map +1 -1
  29. package/dist/{logger-DChjnJMn.d.cts → logger-DvbHXjIO.d.cts} +29 -19
  30. package/dist/{logger-DChjnJMn.d.ts → logger-DvbHXjIO.d.ts} +29 -19
  31. package/dist/logger.cjs +4 -5
  32. package/dist/logger.cjs.map +1 -1
  33. package/dist/logger.d.cts +1 -2
  34. package/dist/logger.d.ts +1 -2
  35. package/dist/logger.js +2 -4
  36. package/dist/mocks.cjs +25 -19
  37. package/dist/mocks.cjs.map +1 -1
  38. package/dist/mocks.d.cts +6 -9
  39. package/dist/mocks.d.ts +6 -9
  40. package/dist/mocks.js +21 -16
  41. package/dist/mocks.js.map +1 -1
  42. package/dist/prompt-DVQN7JTN.cjs +760 -0
  43. package/dist/prompt-DVQN7JTN.cjs.map +1 -0
  44. package/dist/prompt-WQQUN22Z.js +754 -0
  45. package/dist/prompt-WQQUN22Z.js.map +1 -0
  46. package/dist/transformers.cjs +198 -42
  47. package/dist/transformers.cjs.map +1 -1
  48. package/dist/transformers.d.cts +1 -3
  49. package/dist/transformers.d.ts +1 -3
  50. package/dist/transformers.js +155 -14
  51. package/dist/transformers.js.map +1 -1
  52. package/dist/utils.cjs +12 -4
  53. package/dist/utils.cjs.map +1 -1
  54. package/dist/utils.d.cts +31 -3
  55. package/dist/utils.d.ts +31 -3
  56. package/dist/utils.js +11 -4
  57. package/package.json +11 -14
  58. package/src/BarrelManager.ts +93 -107
  59. package/src/{Generator.ts → BaseGenerator.ts} +1 -1
  60. package/src/FileManager.ts +198 -297
  61. package/src/PackageManager.ts +1 -1
  62. package/src/PluginManager.ts +152 -101
  63. package/src/__snapshots__/barrel.json +91 -0
  64. package/src/__snapshots__/grouped.json +114 -0
  65. package/src/__snapshots__/ordered.json +62 -0
  66. package/src/build.ts +86 -171
  67. package/src/errors.ts +0 -11
  68. package/src/index.ts +1 -5
  69. package/src/logger.ts +76 -34
  70. package/src/plugin.ts +3 -3
  71. package/src/transformers/index.ts +2 -3
  72. package/src/transformers/trim.ts +0 -4
  73. package/src/types.ts +35 -40
  74. package/src/utils/TreeNode.ts +132 -50
  75. package/src/utils/executeStrategies.ts +1 -1
  76. package/src/utils/index.ts +2 -1
  77. package/src/utils/parser.ts +157 -0
  78. package/dist/chunk-3OXCZ5DJ.js.map +0 -1
  79. package/dist/chunk-5JZNFPUP.js +0 -309
  80. package/dist/chunk-5JZNFPUP.js.map +0 -1
  81. package/dist/chunk-DKW7IBJV.cjs +0 -299
  82. package/dist/chunk-DKW7IBJV.cjs.map +0 -1
  83. package/dist/chunk-HMLY7DHA.js +0 -16
  84. package/dist/chunk-HMLY7DHA.js.map +0 -1
  85. package/dist/chunk-JKZG2IJR.js +0 -283
  86. package/dist/chunk-JKZG2IJR.js.map +0 -1
  87. package/dist/chunk-LKXUCYWU.cjs +0 -66
  88. package/dist/chunk-LKXUCYWU.cjs.map +0 -1
  89. package/dist/chunk-OPOT6TCT.cjs +0 -320
  90. package/dist/chunk-OPOT6TCT.cjs.map +0 -1
  91. package/dist/chunk-SA2GZKXS.js +0 -596
  92. package/dist/chunk-SA2GZKXS.js.map +0 -1
  93. package/dist/chunk-XRUOSVKX.cjs.map +0 -1
  94. package/dist/chunk-Y3LLJA4H.cjs +0 -1227
  95. package/dist/chunk-Y3LLJA4H.cjs.map +0 -1
  96. package/globals.d.ts +0 -49
  97. package/schema.json +0 -86
  98. package/src/kubb.ts +0 -11
  99. package/src/utils/getParser.ts +0 -17
package/src/logger.ts CHANGED
@@ -3,16 +3,24 @@ import c, { createColors } from 'tinyrainbow'
3
3
 
4
4
  import { EventEmitter } from './utils/EventEmitter.ts'
5
5
 
6
- import type { ConsolaInstance } from 'consola'
7
- import type { Ora } from 'ora'
6
+ import { type ConsolaInstance, type LogLevel, createConsola } from 'consola'
8
7
  import type { Formatter } from 'tinyrainbow'
8
+ import { write } from '@kubb/fs'
9
+ import { resolve } from 'node:path'
9
10
 
10
- //TODO replace with verbose flag and debug flag
11
- export const LogLevel = {
12
- silent: 'silent',
13
- info: 'info',
14
- debug: 'debug',
15
- } as const
11
+ type DebugEvent = { date: Date; logs: string[]; fileName?: string }
12
+
13
+ type Events = {
14
+ start: [message: string]
15
+ success: [message: string]
16
+ error: [message: string, cause: Error]
17
+ warning: [message: string]
18
+ debug: [DebugEvent]
19
+ info: [message: string]
20
+ progress_start: [{ id: string; size: number; message?: string }]
21
+ progressed: [{ id: string; message?: string }]
22
+ progress_stop: [{ id: string }]
23
+ }
16
24
 
17
25
  export const LogMapper = {
18
26
  silent: Number.NEGATIVE_INFINITY,
@@ -20,54 +28,65 @@ export const LogMapper = {
20
28
  debug: 4,
21
29
  } as const
22
30
 
23
- export type LogLevel = keyof typeof LogLevel
24
-
25
- type Events = {
26
- start: [message: string]
27
- end: [message: string]
28
- error: [message: string, cause: Error]
29
- warning: [message: string]
30
- debug: [logs: string[]]
31
- }
32
31
  export type Logger = {
33
32
  /**
34
33
  * Optional config name to show in CLI output
35
34
  */
36
35
  name?: string
37
36
  logLevel: LogLevel
38
- spinner?: Ora
39
37
  consola?: ConsolaInstance
40
38
  on: EventEmitter<Events>['on']
41
39
  emit: EventEmitter<Events>['emit']
40
+ writeLogs: () => Promise<string[]>
42
41
  }
43
42
 
44
43
  type Props = {
45
44
  name?: string
46
- logLevel: LogLevel
47
- spinner?: Ora
45
+ logLevel?: LogLevel
48
46
  consola?: ConsolaInstance
49
47
  }
50
48
 
51
- export function createLogger({ logLevel, name, spinner, consola }: Props): Logger {
49
+ export function createLogger({ logLevel = 3, name, consola: _consola }: Props = {}): Logger {
52
50
  const events = new EventEmitter<Events>()
51
+ const startDate = Date.now()
52
+ const cachedLogs = new Set<DebugEvent>()
53
+
54
+ const consola =
55
+ _consola ||
56
+ createConsola({
57
+ level: logLevel,
58
+ formatOptions: {
59
+ colors: true,
60
+ date: true,
61
+ columns: 120,
62
+ compact: logLevel !== LogMapper.debug,
63
+ },
64
+ }).withTag(name ? randomCliColour(name) : '')
65
+
66
+ consola?.wrapConsole()
53
67
 
54
68
  events.on('start', (message) => {
55
- if (spinner) {
56
- spinner.start(message)
57
- }
69
+ consola.start(message)
58
70
  })
59
71
 
60
- events.on('end', (message) => {
61
- if (spinner) {
62
- spinner.suffixText = ''
63
- spinner.succeed(message)
64
- }
72
+ events.on('success', (message) => {
73
+ consola.success(message)
65
74
  })
66
75
 
67
76
  events.on('warning', (message) => {
68
- if (spinner) {
69
- spinner.warn(c.yellow(message))
77
+ consola.warn(c.yellow(message))
78
+ })
79
+
80
+ events.on('info', (message) => {
81
+ consola.info(c.yellow(message))
82
+ })
83
+
84
+ events.on('debug', (message) => {
85
+ if (message.logs.join('\n\n').length <= 100 && logLevel === LogMapper.debug) {
86
+ console.log(message.logs.join('\n\n'))
70
87
  }
88
+
89
+ cachedLogs.add(message)
71
90
  })
72
91
 
73
92
  events.on('error', (message, cause) => {
@@ -77,17 +96,40 @@ export function createLogger({ logLevel, name, spinner, consola }: Props): Logge
77
96
  throw error
78
97
  })
79
98
 
99
+ if (consola) {
100
+ consola.level = logLevel
101
+ }
102
+
80
103
  const logger: Logger = {
81
104
  name,
82
105
  logLevel,
83
- spinner,
84
106
  consola,
85
- on: (...args) => {
107
+ on(...args) {
86
108
  return events.on(...args)
87
109
  },
88
- emit: (...args) => {
110
+ emit(...args) {
89
111
  return events.emit(...args)
90
112
  },
113
+ async writeLogs() {
114
+ const files: Record<string, string[]> = {}
115
+
116
+ cachedLogs.forEach((log) => {
117
+ const fileName = resolve(process.cwd(), '.kubb', log.fileName || `kubb-${startDate}.log`)
118
+
119
+ if (!files[fileName]) {
120
+ files[fileName] = []
121
+ }
122
+
123
+ files[fileName] = [...files[fileName], `[${log.date.toLocaleString()}]: ${log.logs.join('\n\n')}`]
124
+ })
125
+ await Promise.all(
126
+ Object.entries(files).map(async ([fileName, logs]) => {
127
+ return write(fileName, logs.join('\n'))
128
+ }),
129
+ )
130
+
131
+ return Object.keys(files)
132
+ },
91
133
  }
92
134
 
93
135
  return logger
package/src/plugin.ts CHANGED
@@ -37,7 +37,7 @@ export const pluginCore = createPlugin<PluginCore>((options) => {
37
37
  name: 'core',
38
38
  options,
39
39
  key: ['core'],
40
- api() {
40
+ context() {
41
41
  return {
42
42
  get config() {
43
43
  return options.config
@@ -46,7 +46,7 @@ export const pluginCore = createPlugin<PluginCore>((options) => {
46
46
  return options.getPlugins()
47
47
  },
48
48
  get plugin() {
49
- // see pluginManger.#execute where we override with `.call` the this with the correct plugin
49
+ // see pluginManger.#execute where we override with `.call` the context with the correct plugin
50
50
  return options.plugin as NonNullable<Options['plugin']>
51
51
  },
52
52
  logger,
@@ -67,7 +67,7 @@ export const pluginCore = createPlugin<PluginCore>((options) => {
67
67
  }
68
68
  },
69
69
  resolvePath(baseName) {
70
- const root = path.resolve(this.config.root, this.config.output.path)
70
+ const root = path.resolve(options.config.root, options.config.output.path)
71
71
 
72
72
  return path.resolve(root, baseName)
73
73
  },
@@ -11,7 +11,7 @@ import { searchAndReplace } from './searchAndReplace.ts'
11
11
  import { stringify, stringifyObject } from './stringify.ts'
12
12
  import { toRegExp, toRegExpString } from './toRegExp.ts'
13
13
  import { transformReservedWord } from './transformReservedWord.ts'
14
- import { trim, trimExtName, trimQuotes } from './trim.ts'
14
+ import { trim, trimQuotes } from './trim.ts'
15
15
 
16
16
  export { camelCase, pascalCase, pathCase } from './casing.ts'
17
17
  export { combineCodes } from './combineCodes.ts'
@@ -23,7 +23,7 @@ export { searchAndReplace } from './searchAndReplace.ts'
23
23
  export { stringify, stringifyObject } from './stringify.ts'
24
24
  export { toRegExp, toRegExpString } from './toRegExp.ts'
25
25
  export { transformReservedWord } from './transformReservedWord.ts'
26
- export { trim, trimExtName, trimQuotes } from './trim.ts'
26
+ export { trim, trimQuotes } from './trim.ts'
27
27
  export { merge } from 'remeda'
28
28
  export { orderBy } from 'natural-orderby'
29
29
 
@@ -41,7 +41,6 @@ export default {
41
41
  toRegExpString,
42
42
  trim,
43
43
  trimQuotes,
44
- trimExtName,
45
44
  JSDoc: {
46
45
  createJSDocBlockText,
47
46
  },
@@ -16,7 +16,3 @@ export function trimQuotes(text: string): string {
16
16
 
17
17
  return text
18
18
  }
19
-
20
- export function trimExtName(text: string): string {
21
- return text.replace(/\.[^/.]+$/, '')
22
- }
package/src/types.ts CHANGED
@@ -2,7 +2,6 @@ import type * as KubbFile from '@kubb/fs/types'
2
2
  import type { PossiblePromise } from '@kubb/types'
3
3
  import type { FileManager } from './FileManager.ts'
4
4
  import type { PluginManager } from './PluginManager.ts'
5
- import type { OptionsPlugins, PluginUnion } from './kubb.ts'
6
5
  import type { Logger } from './logger.ts'
7
6
  import type { Cache } from './utils/cache.ts'
8
7
 
@@ -24,11 +23,9 @@ export type UserConfig = Omit<Config, 'root' | 'plugins'> & {
24
23
  */
25
24
  root?: string
26
25
  /**
27
- * Plugin type can be KubbJSONPlugin or Plugin
28
- * Example: ['@kubb/plugin-oas', { output: false }]
29
- * Or: pluginOas({ output: false })
26
+ * Plugin type should be a Kubb plugin
30
27
  */
31
- plugins?: Array<Omit<UnknownUserPlugin, 'api'> | UnionPlugins | [name: string, options: object]>
28
+ plugins?: Array<Omit<UnknownUserPlugin, 'context'>>
32
29
  }
33
30
 
34
31
  export type InputPath = {
@@ -78,6 +75,11 @@ export type Config<TInput = Input> = {
78
75
  * @default true
79
76
  */
80
77
  write?: boolean
78
+ /**
79
+ * Define what needs to exported, here you can also disable the export of barrel files
80
+ * @default `'barrelNamed'`
81
+ */
82
+ exportType?: 'barrel' | 'barrelNamed' | false
81
83
  }
82
84
  /**
83
85
  * Array of Kubb plugins to use.
@@ -99,10 +101,6 @@ export type Config<TInput = Input> = {
99
101
 
100
102
  // plugin
101
103
 
102
- export type UnionPlugins = PluginUnion
103
-
104
- export type ObjectPlugin = keyof OptionsPlugins
105
-
106
104
  export type PluginFactoryOptions<
107
105
  /**
108
106
  * Name to be used for the plugin, this will also be used for they key.
@@ -117,9 +115,9 @@ export type PluginFactoryOptions<
117
115
  */
118
116
  TResolvedOptions extends object = TOptions,
119
117
  /**
120
- * API that you want to expose to other plugins.
118
+ * Context that you want to expose to other plugins.
121
119
  */
122
- TAPI = any,
120
+ TContext = any,
123
121
  /**
124
122
  * When calling `resolvePath` you can specify better types.
125
123
  */
@@ -132,7 +130,7 @@ export type PluginFactoryOptions<
132
130
  key: PluginKey<TName | string>
133
131
  options: TOptions
134
132
  resolvedOptions: TResolvedOptions
135
- api: TAPI
133
+ context: TContext
136
134
  resolvePathOptions: TResolvePathOptions
137
135
  }
138
136
 
@@ -153,19 +151,19 @@ export type UserPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOpti
153
151
  options: TOptions['resolvedOptions']
154
152
  /**
155
153
  * Specifies the preceding plugins for the current plugin. You can pass an array of preceding plugin names, and the current plugin will be executed after these plugins.
156
- * Can be used to validate depended plugins.
154
+ * Can be used to validate dependent plugins.
157
155
  */
158
156
  pre?: Array<string>
159
157
  /**
160
158
  * Specifies the succeeding plugins for the current plugin. You can pass an array of succeeding plugin names, and the current plugin will be executed before these plugins.
161
159
  */
162
160
  post?: Array<string>
163
- } & (TOptions['api'] extends never
161
+ } & (TOptions['context'] extends never
164
162
  ? {
165
- api?: never
163
+ context?: never
166
164
  }
167
165
  : {
168
- api: (this: TOptions['name'] extends 'core' ? null : Omit<PluginContext<TOptions>, 'addFile'>) => TOptions['api']
166
+ context: (this: TOptions['name'] extends 'core' ? null : Omit<PluginContext<TOptions>, 'addFile'>) => TOptions['context']
169
167
  })
170
168
 
171
169
  export type UserPluginWithLifeCycle<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = UserPlugin<TOptions> & PluginLifecycle<TOptions>
@@ -183,9 +181,24 @@ export type Plugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>
183
181
  * @private
184
182
  */
185
183
  key: TOptions['key']
184
+ output?: {
185
+ /**
186
+ * Output to save the clients.
187
+ */
188
+ path: string
189
+ /**
190
+ * Add an extension to the generated imports and exports, default it will not use an extension
191
+ */
192
+ extName?: KubbFile.Extname
193
+ /**
194
+ * Define what needs to exported, here you can also disable the export of barrel files
195
+ * @default `'barrelNamed'`
196
+ */
197
+ exportType?: 'barrel' | 'barrelNamed' | false
198
+ }
186
199
  /**
187
200
  * Specifies the preceding plugins for the current plugin. You can pass an array of preceding plugin names, and the current plugin will be executed after these plugins.
188
- * Can be used to validate depended plugins.
201
+ * Can be used to validate dependent plugins.
189
202
  */
190
203
  pre?: Array<string>
191
204
  /**
@@ -197,14 +210,14 @@ export type Plugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>
197
210
  */
198
211
  options: TOptions['resolvedOptions']
199
212
  /**
200
- * Define an api that can be used by other plugins, see `PluginManager' where we convert from `UserPlugin` to `Plugin`(used when calling `createPlugin`).
213
+ * Define a context that can be used by other plugins, see `PluginManager' where we convert from `UserPlugin` to `Plugin`(used when calling `createPlugin`).
201
214
  */
202
- } & (TOptions['api'] extends never
215
+ } & (TOptions['context'] extends never
203
216
  ? {
204
- api?: never
217
+ context?: never
205
218
  }
206
219
  : {
207
- api: TOptions['api']
220
+ context: TOptions['context']
208
221
  })
209
222
 
210
223
  export type PluginWithLifeCycle<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = Plugin<TOptions> & PluginLifecycle<TOptions>
@@ -229,21 +242,6 @@ export type PluginLifecycle<TOptions extends PluginFactoryOptions = PluginFactor
229
242
  * @example ('pet') => 'Pet'
230
243
  */
231
244
  resolveName?: (this: PluginContext<TOptions>, name: ResolveNameParams['name'], type?: ResolveNameParams['type']) => string
232
- /**
233
- * Makes it possible to run async logic to override the path defined previously by `resolvePath`.
234
- * @type hookFirst
235
- */
236
- load?: (this: Omit<PluginContext<TOptions>, 'addFile'>, path: KubbFile.Path) => PossiblePromise<TransformResult | null>
237
- /**
238
- * Transform the source-code.
239
- * @type hookReduceArg0
240
- */
241
- transform?: (this: Omit<PluginContext<TOptions>, 'addFile'>, source: string, path: KubbFile.Path) => PossiblePromise<TransformResult>
242
- /**
243
- * Write the result to the file-system based on the id(defined by `resolvePath` or changed by `load`).
244
- * @type hookParallel
245
- */
246
- writeFile?: (this: Omit<PluginContext<TOptions>, 'addFile'>, path: KubbFile.Path, source: string | undefined) => PossiblePromise<string | void>
247
245
  /**
248
246
  * End of the plugin lifecycle.
249
247
  * @type hookParallel
@@ -283,7 +281,7 @@ export type PluginContext<TOptions extends PluginFactoryOptions = PluginFactoryO
283
281
  cache: Cache<PluginCache>
284
282
  fileManager: FileManager
285
283
  pluginManager: PluginManager
286
- addFile: (...file: Array<KubbFile.File>) => Promise<Array<KubbFile.File>>
284
+ addFile: (...file: Array<KubbFile.File>) => Promise<Array<KubbFile.ResolvedFile>>
287
285
  resolvePath: (params: ResolvePathParams<TOptions['resolvePathOptions']>) => KubbFile.OptionalPath
288
286
  resolveName: (params: ResolveNameParams) => string
289
287
  logger: Logger
@@ -296,6 +294,3 @@ export type PluginContext<TOptions extends PluginFactoryOptions = PluginFactoryO
296
294
  */
297
295
  plugin: Plugin<TOptions>
298
296
  }
299
-
300
- // null will mean clear the watcher for this key
301
- export type TransformResult = string | null
@@ -1,28 +1,29 @@
1
- import dirTree from 'directory-tree'
2
-
3
- import { FileManager } from '../FileManager.ts'
4
-
5
1
  import type * as KubbFile from '@kubb/fs/types'
6
- import type { DirectoryTree, DirectoryTreeOptions } from 'directory-tree'
7
-
8
- export type TreeNodeOptions = DirectoryTreeOptions
9
-
10
- type BarrelData = { type: KubbFile.Mode; path: KubbFile.Path; name: string }
11
-
12
- export class TreeNode<T = BarrelData> {
13
- public data: T
2
+ import { FileManager } from '../FileManager.ts'
14
3
 
15
- public parent?: TreeNode<T>
4
+ type BarrelData = {
5
+ file?: KubbFile.File
6
+ /**
7
+ * @deprecated use file instead
8
+ */
9
+ type: KubbFile.Mode
10
+ path: string
11
+ name: string
12
+ }
16
13
 
17
- public children: Array<TreeNode<T>> = []
14
+ export class TreeNode {
15
+ data: BarrelData
16
+ parent?: TreeNode
17
+ children: Array<TreeNode> = []
18
+ #cachedLeaves?: Array<TreeNode> = undefined
18
19
 
19
- constructor(data: T, parent?: TreeNode<T>) {
20
+ constructor(data: BarrelData, parent?: TreeNode) {
20
21
  this.data = data
21
22
  this.parent = parent
22
23
  return this
23
24
  }
24
25
 
25
- addChild(data: T): TreeNode<T> {
26
+ addChild(data: BarrelData): TreeNode {
26
27
  const child = new TreeNode(data, this)
27
28
  if (!this.children) {
28
29
  this.children = []
@@ -31,51 +32,37 @@ export class TreeNode<T = BarrelData> {
31
32
  return child
32
33
  }
33
34
 
34
- find(data?: T): TreeNode<T> | null {
35
- if (!data) {
36
- return null
37
- }
38
-
39
- if (data === this.data) {
35
+ get root(): TreeNode {
36
+ if (!this.parent) {
40
37
  return this
41
38
  }
42
-
43
- if (this.children?.length) {
44
- for (let i = 0, { length } = this.children, target: TreeNode<T> | null = null; i < length; i++) {
45
- target = this.children[i]!.find(data)
46
- if (target) {
47
- return target
48
- }
49
- }
50
- }
51
-
52
- return null
39
+ return this.parent.root
53
40
  }
54
41
 
55
- get leaves(): TreeNode<T>[] {
42
+ get leaves(): Array<TreeNode> {
56
43
  if (!this.children || this.children.length === 0) {
57
44
  // this is a leaf
58
45
  return [this]
59
46
  }
60
47
 
48
+ if (this.#cachedLeaves) {
49
+ return this.#cachedLeaves
50
+ }
51
+
61
52
  // if not a leaf, return all children's leaves recursively
62
- const leaves: TreeNode<T>[] = []
53
+ const leaves: TreeNode[] = []
63
54
  if (this.children) {
64
55
  for (let i = 0, { length } = this.children; i < length; i++) {
65
56
  leaves.push.apply(leaves, this.children[i]!.leaves)
66
57
  }
67
58
  }
68
- return leaves
69
- }
70
59
 
71
- get root(): TreeNode<T> {
72
- if (!this.parent) {
73
- return this
74
- }
75
- return this.parent.root
60
+ this.#cachedLeaves = leaves
61
+
62
+ return leaves
76
63
  }
77
64
 
78
- forEach(callback: (treeNode: TreeNode<T>) => void): this {
65
+ forEach(callback: (treeNode: TreeNode) => void): this {
79
66
  if (typeof callback !== 'function') {
80
67
  throw new TypeError('forEach() callback must be a function')
81
68
  }
@@ -93,13 +80,41 @@ export class TreeNode<T = BarrelData> {
93
80
  return this
94
81
  }
95
82
 
96
- public static build(path: string, options: TreeNodeOptions = {}): TreeNode | null {
83
+ findDeep(predicate?: (value: TreeNode, index: number, obj: TreeNode[]) => boolean): TreeNode | undefined {
84
+ if (typeof predicate !== 'function') {
85
+ throw new TypeError('find() predicate must be a function')
86
+ }
87
+
88
+ return this.leaves.find(predicate)
89
+ }
90
+
91
+ forEachDeep(callback: (treeNode: TreeNode) => void): void {
92
+ if (typeof callback !== 'function') {
93
+ throw new TypeError('forEach() callback must be a function')
94
+ }
95
+
96
+ this.leaves.forEach(callback)
97
+ }
98
+
99
+ filterDeep(callback: (treeNode: TreeNode) => boolean): Array<TreeNode> {
100
+ if (typeof callback !== 'function') {
101
+ throw new TypeError('filter() callback must be a function')
102
+ }
103
+
104
+ return this.leaves.filter(callback)
105
+ }
106
+
107
+ mapDeep<T>(callback: (treeNode: TreeNode) => T): Array<T> {
108
+ if (typeof callback !== 'function') {
109
+ throw new TypeError('map() callback must be a function')
110
+ }
111
+
112
+ return this.leaves.map(callback)
113
+ }
114
+
115
+ public static build(files: KubbFile.File[], root?: string): TreeNode | null {
97
116
  try {
98
- const exclude = Array.isArray(options.exclude) ? options.exclude : [options.exclude].filter(Boolean)
99
- const filteredTree = dirTree(path, {
100
- extensions: options.extensions,
101
- exclude: [/node_modules/, ...exclude],
102
- })
117
+ const filteredTree = buildDirectoryTree(files, root)
103
118
 
104
119
  if (!filteredTree) {
105
120
  return null
@@ -108,6 +123,7 @@ export class TreeNode<T = BarrelData> {
108
123
  const treeNode = new TreeNode({
109
124
  name: filteredTree.name,
110
125
  path: filteredTree.path,
126
+ file: filteredTree.file,
111
127
  type: FileManager.getMode(filteredTree.path),
112
128
  })
113
129
 
@@ -115,6 +131,7 @@ export class TreeNode<T = BarrelData> {
115
131
  const subNode = node.addChild({
116
132
  name: item.name,
117
133
  path: item.path,
134
+ file: item.file,
118
135
  type: FileManager.getMode(item.path),
119
136
  })
120
137
 
@@ -129,7 +146,72 @@ export class TreeNode<T = BarrelData> {
129
146
 
130
147
  return treeNode
131
148
  } catch (e) {
132
- throw new Error('Something went wrong with creating index files with the TreehNode class', { cause: e })
149
+ throw new Error('Something went wrong with creating barrel files with the TreeNode class', { cause: e })
133
150
  }
134
151
  }
135
152
  }
153
+
154
+ export type DirectoryTree = {
155
+ name: string
156
+ path: string
157
+ file?: KubbFile.File
158
+ children: Array<DirectoryTree>
159
+ }
160
+
161
+ export function buildDirectoryTree(files: Array<KubbFile.File>, rootFolder = ''): DirectoryTree | null {
162
+ const rootPrefix = rootFolder.endsWith('/') ? rootFolder : `${rootFolder}/`
163
+ const filteredFiles = files.filter((file) => (rootFolder ? file.path.startsWith(rootPrefix) && !file.path.endsWith('.json') : !file.path.endsWith('.json')))
164
+
165
+ if (filteredFiles.length === 0) {
166
+ return null // No files match the root folder
167
+ }
168
+
169
+ const root: DirectoryTree = {
170
+ name: rootFolder || '',
171
+ path: rootFolder || '',
172
+ children: [],
173
+ }
174
+
175
+ filteredFiles.forEach((file) => {
176
+ const path = file.path.slice(rootFolder.length)
177
+ const parts = path.split('/')
178
+ let currentLevel: DirectoryTree[] = root.children
179
+ let currentPath = rootFolder
180
+
181
+ parts.forEach((part, index) => {
182
+ if (index !== 0) {
183
+ currentPath += `/${part}`
184
+ } else {
185
+ currentPath += `${part}`
186
+ }
187
+
188
+ let existingNode = currentLevel.find((node) => node.name === part)
189
+
190
+ if (!existingNode) {
191
+ if (index === parts.length - 1) {
192
+ // If it's the last part, it's a file
193
+ existingNode = {
194
+ name: part,
195
+ file,
196
+ path: currentPath,
197
+ } as DirectoryTree
198
+ } else {
199
+ // Otherwise, it's a folder
200
+ existingNode = {
201
+ name: part,
202
+ path: currentPath,
203
+ children: [],
204
+ } as DirectoryTree
205
+ }
206
+ currentLevel.push(existingNode)
207
+ }
208
+
209
+ // Move to the next level if it's a folder
210
+ if (!existingNode.file) {
211
+ currentLevel = existingNode.children
212
+ }
213
+ })
214
+ })
215
+
216
+ return root
217
+ }
@@ -4,7 +4,7 @@ export type ValueOfPromiseFuncArray<TInput extends Array<unknown>> = TInput exte
4
4
 
5
5
  export function noReturn(): void {}
6
6
 
7
- type SeqOutput<TInput extends Array<PromiseFunc<TValue, null>>, TValue> = Array<Awaited<ValueOfPromiseFuncArray<TInput>>>
7
+ type SeqOutput<TInput extends Array<PromiseFunc<TValue, null>>, TValue> = Promise<Array<Awaited<ValueOfPromiseFuncArray<TInput>>>>
8
8
 
9
9
  /**
10
10
  * Chains promises
@@ -10,4 +10,5 @@ export { timeout } from './timeout.ts'
10
10
  export { getUniqueName, setUniqueName } from './uniqueName.ts'
11
11
  export type { URLObject } from './URLPath.ts'
12
12
  export { URLPath } from './URLPath.ts'
13
- export { getParser } from './getParser.ts'
13
+ export { getFileParser, createFileImport, createFileExport, createFile, createFileParser } from './parser.ts'
14
+ export type { ParserModule } from './parser.ts'