@kubb/core 3.0.0-alpha.3 → 3.0.0-alpha.30

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 (112) hide show
  1. package/README.md +13 -4
  2. package/dist/{FileManager-Bw-FNS3q.d.cts → FileManager-CqvmdzNO.d.cts} +128 -90
  3. package/dist/{FileManager-BW--rO8q.d.ts → FileManager-DIArE3b0.d.ts} +128 -90
  4. package/dist/chunk-2EU7DMPM.js +96 -0
  5. package/dist/chunk-2EU7DMPM.js.map +1 -0
  6. package/dist/{chunk-34BPAXR2.cjs → chunk-2UQARE2O.cjs} +54 -37
  7. package/dist/chunk-2UQARE2O.cjs.map +1 -0
  8. package/dist/chunk-4X5FFJPJ.js +8 -13
  9. package/dist/chunk-4X5FFJPJ.js.map +1 -1
  10. package/dist/{chunk-3OXCZ5DJ.js → chunk-E6CN2CZC.js} +63 -54
  11. package/dist/chunk-E6CN2CZC.js.map +1 -0
  12. package/dist/{chunk-25NKJ3DV.js → chunk-HBQM723K.js} +13 -27
  13. package/dist/chunk-HBQM723K.js.map +1 -0
  14. package/dist/{chunk-LM2YQC3T.cjs → chunk-LLKRRIBF.cjs} +81 -51
  15. package/dist/chunk-LLKRRIBF.cjs.map +1 -0
  16. package/dist/chunk-MD2LDZ3Z.js +889 -0
  17. package/dist/chunk-MD2LDZ3Z.js.map +1 -0
  18. package/dist/chunk-OX2X7B4Z.cjs +101 -0
  19. package/dist/chunk-OX2X7B4Z.cjs.map +1 -0
  20. package/dist/{chunk-67C6RBGQ.cjs → chunk-RIW2LFFQ.cjs} +28 -29
  21. package/dist/chunk-RIW2LFFQ.cjs.map +1 -0
  22. package/dist/chunk-SX5FHSVT.cjs +1532 -0
  23. package/dist/chunk-SX5FHSVT.cjs.map +1 -0
  24. package/dist/chunk-VBGWLAET.cjs +42 -0
  25. package/dist/chunk-VBGWLAET.cjs.map +1 -0
  26. package/dist/index.cjs +553 -619
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +7 -22
  29. package/dist/index.d.ts +7 -22
  30. package/dist/index.js +444 -525
  31. package/dist/index.js.map +1 -1
  32. package/dist/{logger-DChjnJMn.d.cts → logger-DvbHXjIO.d.cts} +29 -19
  33. package/dist/{logger-DChjnJMn.d.ts → logger-DvbHXjIO.d.ts} +29 -19
  34. package/dist/logger.cjs +25 -15
  35. package/dist/logger.cjs.map +1 -1
  36. package/dist/logger.d.cts +1 -2
  37. package/dist/logger.d.ts +1 -2
  38. package/dist/logger.js +3 -15
  39. package/dist/logger.js.map +1 -1
  40. package/dist/mocks.cjs +42 -31
  41. package/dist/mocks.cjs.map +1 -1
  42. package/dist/mocks.d.cts +7 -9
  43. package/dist/mocks.d.ts +7 -9
  44. package/dist/mocks.js +35 -33
  45. package/dist/mocks.js.map +1 -1
  46. package/dist/{prompt-6FWP747F.cjs → prompt-2PN2F25D.cjs} +89 -89
  47. package/dist/prompt-2PN2F25D.cjs.map +1 -0
  48. package/dist/{prompt-HK3MWREM.js → prompt-WQQUN22Z.js} +9 -15
  49. package/dist/prompt-WQQUN22Z.js.map +1 -0
  50. package/dist/transformers.cjs +216 -49
  51. package/dist/transformers.cjs.map +1 -1
  52. package/dist/transformers.d.cts +2 -4
  53. package/dist/transformers.d.ts +2 -4
  54. package/dist/transformers.js +149 -35
  55. package/dist/transformers.js.map +1 -1
  56. package/dist/utils.cjs +65 -26
  57. package/dist/utils.cjs.map +1 -1
  58. package/dist/utils.d.cts +29 -3
  59. package/dist/utils.d.ts +29 -3
  60. package/dist/utils.js +3 -26
  61. package/dist/utils.js.map +1 -1
  62. package/package.json +11 -14
  63. package/src/BarrelManager.ts +95 -109
  64. package/src/{Generator.ts → BaseGenerator.ts} +1 -1
  65. package/src/FileManager.ts +199 -304
  66. package/src/PackageManager.ts +1 -1
  67. package/src/PluginManager.ts +152 -93
  68. package/src/PromiseManager.ts +1 -1
  69. package/src/__snapshots__/barrel.json +73 -0
  70. package/src/__snapshots__/grouped.json +120 -0
  71. package/src/__snapshots__/ordered.json +68 -0
  72. package/src/build.ts +86 -131
  73. package/src/config.ts +2 -4
  74. package/src/errors.ts +0 -11
  75. package/src/index.ts +2 -3
  76. package/src/logger.ts +76 -34
  77. package/src/plugin.ts +2 -5
  78. package/src/transformers/escape.ts +0 -10
  79. package/src/transformers/index.ts +2 -3
  80. package/src/transformers/stringify.ts +1 -1
  81. package/src/transformers/trim.ts +0 -4
  82. package/src/types.ts +52 -20
  83. package/src/utils/TreeNode.ts +132 -50
  84. package/src/utils/executeStrategies.ts +3 -3
  85. package/src/utils/index.ts +2 -1
  86. package/src/utils/parser.ts +156 -0
  87. package/dist/chunk-25NKJ3DV.js.map +0 -1
  88. package/dist/chunk-34BPAXR2.cjs.map +0 -1
  89. package/dist/chunk-3OXCZ5DJ.js.map +0 -1
  90. package/dist/chunk-5JZNFPUP.js +0 -309
  91. package/dist/chunk-5JZNFPUP.js.map +0 -1
  92. package/dist/chunk-67C6RBGQ.cjs.map +0 -1
  93. package/dist/chunk-ADC5UNZ5.cjs +0 -1227
  94. package/dist/chunk-ADC5UNZ5.cjs.map +0 -1
  95. package/dist/chunk-HMLY7DHA.js +0 -16
  96. package/dist/chunk-HMLY7DHA.js.map +0 -1
  97. package/dist/chunk-JKZG2IJR.js +0 -283
  98. package/dist/chunk-JKZG2IJR.js.map +0 -1
  99. package/dist/chunk-LM2YQC3T.cjs.map +0 -1
  100. package/dist/chunk-PZT4CTBV.cjs +0 -299
  101. package/dist/chunk-PZT4CTBV.cjs.map +0 -1
  102. package/dist/chunk-SA2GZKXS.js +0 -596
  103. package/dist/chunk-SA2GZKXS.js.map +0 -1
  104. package/dist/chunk-XCPFG6DO.cjs +0 -66
  105. package/dist/chunk-XCPFG6DO.cjs.map +0 -1
  106. package/dist/chunk-YTSNYMHW.cjs +0 -320
  107. package/dist/chunk-YTSNYMHW.cjs.map +0 -1
  108. package/dist/prompt-6FWP747F.cjs.map +0 -1
  109. package/dist/prompt-HK3MWREM.js.map +0 -1
  110. package/schema.json +0 -86
  111. package/src/utils/cache.ts +0 -35
  112. package/src/utils/getParser.ts +0 -17
package/src/plugin.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  import path from 'node:path'
2
2
 
3
- import { createPluginCache } from './utils/cache.ts'
4
-
5
3
  import type { FileManager } from './FileManager.ts'
6
4
  import type { PluginManager } from './PluginManager.ts'
7
5
  import type { Plugin, PluginContext, PluginFactoryOptions, UserPluginWithLifeCycle } from './types.ts'
@@ -46,7 +44,7 @@ export const pluginCore = createPlugin<PluginCore>((options) => {
46
44
  return options.getPlugins()
47
45
  },
48
46
  get plugin() {
49
- // see pluginManger.#execute where we override with `.call` the this with the correct plugin
47
+ // see pluginManger.#execute where we override with `.call` the context with the correct plugin
50
48
  return options.plugin as NonNullable<Options['plugin']>
51
49
  },
52
50
  logger,
@@ -63,11 +61,10 @@ export const pluginCore = createPlugin<PluginCore>((options) => {
63
61
  },
64
62
  resolvePath,
65
63
  resolveName,
66
- cache: createPluginCache(),
67
64
  }
68
65
  },
69
66
  resolvePath(baseName) {
70
- const root = path.resolve(this.config.root, this.config.output.path)
67
+ const root = path.resolve(options.config.root, options.config.output.path)
71
68
 
72
69
  return path.resolve(root, baseName)
73
70
  },
@@ -29,13 +29,3 @@ export function jsStringEscape(input: any): string {
29
29
  }
30
30
  })
31
31
  }
32
-
33
- export function escapeStringRegexp(string: string) {
34
- if (typeof string !== 'string') {
35
- throw new TypeError('Expected a string')
36
- }
37
-
38
- // Escape characters with special meaning either inside or outside character sets.
39
- // Use a simple backslash escape when it’s always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar.
40
- return string.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d')
41
- }
@@ -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
  },
@@ -1,6 +1,6 @@
1
1
  import { trimQuotes } from './trim'
2
2
 
3
- export function stringify(value: string | number | undefined): string {
3
+ export function stringify(value: string | number | boolean | undefined): string {
4
4
  if (value === undefined || value === null) {
5
5
  return '""'
6
6
  }
@@ -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
@@ -3,7 +3,6 @@ import type { PossiblePromise } from '@kubb/types'
3
3
  import type { FileManager } from './FileManager.ts'
4
4
  import type { PluginManager } from './PluginManager.ts'
5
5
  import type { Logger } from './logger.ts'
6
- import type { Cache } from './utils/cache.ts'
7
6
 
8
7
  // config
9
8
 
@@ -30,20 +29,25 @@ export type UserConfig = Omit<Config, 'root' | 'plugins'> & {
30
29
 
31
30
  export type InputPath = {
32
31
  /**
33
- * Path to be used as the input. This can be an absolute path or a path relative to the `root`.
32
+ * Define your Swagger/OpenAPI file. This can be an absolute path or a path relative to the `root`.
34
33
  */
35
34
  path: string
36
35
  }
37
36
 
38
37
  export type InputData = {
39
38
  /**
40
- * `string` or `object` containing the data.
39
+ * `string` or `object` containing your Swagger/OpenAPI data.
41
40
  */
42
41
  data: string | unknown
43
42
  }
44
43
 
45
44
  type Input = InputPath | InputData
46
45
 
46
+ type OutContext = {}
47
+ type OutExtension = (context: OutContext) => Record<KubbFile.Extname, KubbFile.Extname>
48
+
49
+ export type BarrelType = 'all' | 'named'
50
+
47
51
  /**
48
52
  * @private
49
53
  */
@@ -53,33 +57,44 @@ export type Config<TInput = Input> = {
53
57
  */
54
58
  name?: string
55
59
  /**
56
- * Project root directory. Can be an absolute path, or a path relative from
57
- * the location of the config file itself.
60
+ * Project root directory. This can be an absolute path or a path relative to the location of your `kubb.config.ts` file.
58
61
  * @default process.cwd()
59
62
  */
60
63
  root: string
64
+ /**
65
+ * You can use `input.path` or `input.data` depending on the needs you have.
66
+ */
61
67
  input: TInput
62
68
  output: {
63
69
  /**
64
- * Path to be used to export all generated files.
65
- * This can be an absolute path, or a path relative based of the defined `root` option.
70
+ * The path where all generated files will be exported. This can be an absolute path or a path relative to the specified root option.
66
71
  */
67
72
  path: string
68
73
  /**
69
- * Clean output directory before each build.
74
+ * Clean the output directory before each build.
70
75
  */
71
76
  clean?: boolean
72
77
  /**
73
- * Write files to the fileSystem
74
- * This is being used for the playground.
78
+ * Save files to the file system.
75
79
  * @default true
76
80
  */
77
81
  write?: boolean
82
+
83
+ /**
84
+ * Override the extension to the generated imports and exports, default the plugin will add an extension
85
+ * @default `() => ({ '.ts': '.ts'})`
86
+ */
87
+ extension?: OutExtension
88
+ /**
89
+ * Define what needs to exported, here you can also disable the export of barrel files.
90
+ * @default `'named'`
91
+ */
92
+ barrelType?: BarrelType | false
78
93
  }
79
94
  /**
80
- * Array of Kubb plugins to use.
81
- * The plugin/package can forsee some options that you need to pass through.
82
- * Sometimes a plugin is depended on another plugin, if that's the case you will get an error back from the plugin you installed.
95
+ * An array of Kubb plugins that will be used in the generation.
96
+ * Each plugin may include additional configurable options(defined in the plugin itself).
97
+ * If a plugin depends on another plugin, an error will be returned if the required dependency is missing. See pre for more details.
83
98
  */
84
99
  plugins?: Array<Plugin>
85
100
  /**
@@ -146,7 +161,7 @@ export type UserPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOpti
146
161
  options: TOptions['resolvedOptions']
147
162
  /**
148
163
  * 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.
149
- * Can be used to validate depended plugins.
164
+ * Can be used to validate dependent plugins.
150
165
  */
151
166
  pre?: Array<string>
152
167
  /**
@@ -178,7 +193,7 @@ export type Plugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>
178
193
  key: TOptions['key']
179
194
  /**
180
195
  * 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.
181
- * Can be used to validate depended plugins.
196
+ * Can be used to validate dependent plugins.
182
197
  */
183
198
  pre?: Array<string>
184
199
  /**
@@ -233,8 +248,6 @@ export type PluginLifecycleHooks = keyof PluginLifecycle
233
248
 
234
249
  export type PluginParameter<H extends PluginLifecycleHooks> = Parameters<Required<PluginLifecycle>[H]>
235
250
 
236
- export type PluginCache = Record<string, [number, unknown]>
237
-
238
251
  export type ResolvePathParams<TOptions = object> = {
239
252
  pluginKey?: Plugin['key']
240
253
  baseName: string
@@ -253,15 +266,14 @@ export type ResolveNameParams = {
253
266
  * `function` can be used used to customize the exported functions(use of camelCase)
254
267
  * `type` is a special type for TypeScript(use of PascalCase)
255
268
  */
256
- type?: 'file' | 'function' | 'type'
269
+ type?: 'file' | 'function' | 'type' | 'const'
257
270
  }
258
271
 
259
272
  export type PluginContext<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = {
260
273
  config: Config
261
- cache: Cache<PluginCache>
262
274
  fileManager: FileManager
263
275
  pluginManager: PluginManager
264
- addFile: (...file: Array<KubbFile.File>) => Promise<Array<KubbFile.File>>
276
+ addFile: (...file: Array<KubbFile.File>) => Promise<Array<KubbFile.ResolvedFile>>
265
277
  resolvePath: (params: ResolvePathParams<TOptions['resolvePathOptions']>) => KubbFile.OptionalPath
266
278
  resolveName: (params: ResolveNameParams) => string
267
279
  logger: Logger
@@ -274,3 +286,23 @@ export type PluginContext<TOptions extends PluginFactoryOptions = PluginFactoryO
274
286
  */
275
287
  plugin: Plugin<TOptions>
276
288
  }
289
+
290
+ export type Output = {
291
+ /**
292
+ * Output to save the generated files.
293
+ */
294
+ path: string
295
+ /**
296
+ * Define what needs to exported, here you can also disable the export of barrel files.
297
+ * @default `'named'`
298
+ */
299
+ barrelType?: BarrelType | false
300
+ /**
301
+ * Add a banner text in the beginning of every file
302
+ */
303
+ banner?: string
304
+ /**
305
+ * Add a footer text in the beginning of every file
306
+ */
307
+ footer?: string
308
+ }
@@ -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
+ }
@@ -1,10 +1,10 @@
1
1
  type PromiseFunc<T = unknown, T2 = never> = (state?: T) => T2 extends never ? Promise<T> : Promise<T> | T2
2
2
 
3
- export type ValueOfPromiseFuncArray<TInput extends Array<unknown>> = TInput extends Array<PromiseFunc<infer X, infer Y>> ? X | Y : never
3
+ type ValueOfPromiseFuncArray<TInput extends Array<unknown>> = TInput extends Array<PromiseFunc<infer X, infer Y>> ? X | Y : never
4
4
 
5
- export function noReturn(): void {}
5
+ 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'
@@ -0,0 +1,156 @@
1
+ import path from 'node:path'
2
+ import type * as KubbFile from '@kubb/fs/types'
3
+
4
+ import { getRelativePath } from '@kubb/fs'
5
+ import hash from 'object-hash'
6
+ import { combineExports, combineImports, combineSources } from '../FileManager.ts'
7
+ import type { Logger } from '../logger.ts'
8
+
9
+ /**
10
+ * Helper to create a file with name and id set
11
+ */
12
+ export function createFile<TMeta extends object = object>(file: KubbFile.File<TMeta>): KubbFile.ResolvedFile<TMeta> {
13
+ const extname = path.extname(file.baseName) as KubbFile.Extname
14
+
15
+ if (!extname) {
16
+ throw new Error(`No extname found for ${file.baseName}`)
17
+ }
18
+
19
+ const source = file.sources.map((item) => item.value).join('\n\n')
20
+ const exports = file.exports ? combineExports(file.exports) : []
21
+ const imports = file.imports && source ? combineImports(file.imports, exports, source) : []
22
+ const sources = file.sources ? combineSources(file.sources) : []
23
+
24
+ return {
25
+ ...file,
26
+ id: hash({ path: file.path }),
27
+ name: trimExtName(file.baseName),
28
+ extname,
29
+ imports: imports.map((item) => createFileImport(item)),
30
+ exports: exports.map((item) => createFileExport(item)),
31
+ sources: sources.map((item) => createFileSource(item)),
32
+ meta: file.meta || ({} as TMeta),
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Helper to create a fileImport with extname set
38
+ */
39
+ function createFileSource(source: KubbFile.Source): KubbFile.Source {
40
+ return source
41
+ }
42
+
43
+ /**
44
+ * Helper to create a fileImport with extname set
45
+ */
46
+ export function createFileImport(imp: KubbFile.Import): KubbFile.ResolvedImport {
47
+ return {
48
+ ...imp,
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Helper to create a fileExport with extname set
54
+ */
55
+ export function createFileExport(exp: KubbFile.Export): KubbFile.ResolvedExport {
56
+ return {
57
+ ...exp,
58
+ }
59
+ }
60
+
61
+ export type ParserModule<TMeta extends object = object> = {
62
+ format: (source: string) => Promise<string>
63
+ /**
64
+ * Convert a file to string
65
+ */
66
+ print: (file: KubbFile.ResolvedFile<TMeta>, options: PrintOptions) => Promise<string>
67
+ }
68
+
69
+ export function createFileParser<TMeta extends object = object>(parser: ParserModule<TMeta>): ParserModule<TMeta> {
70
+ return parser
71
+ }
72
+
73
+ type PrintOptions = {
74
+ extname?: KubbFile.Extname
75
+ logger?: Logger
76
+ }
77
+
78
+ const typeScriptParser = createFileParser({
79
+ async format(source) {
80
+ const module = await import('@kubb/parser-ts')
81
+
82
+ return module.format(source)
83
+ },
84
+ async print(file, options = { extname: '.ts' }) {
85
+ const module = await import('@kubb/parser-ts')
86
+
87
+ const source = file.sources.map((item) => item.value).join('\n\n')
88
+
89
+ const importNodes = file.imports
90
+ .map((item) => {
91
+ const importPath = item.root ? getRelativePath(item.root, item.path) : item.path
92
+ const hasExtname = !!path.extname(importPath)
93
+
94
+ return module.factory.createImportDeclaration({
95
+ name: item.name,
96
+ path: options.extname && hasExtname ? `${trimExtName(importPath)}${options.extname}` : trimExtName(importPath),
97
+ isTypeOnly: item.isTypeOnly,
98
+ })
99
+ })
100
+ .filter(Boolean)
101
+
102
+ const exportNodes = file.exports
103
+ .map((item) => {
104
+ const exportPath = item.path
105
+
106
+ const hasExtname = !!path.extname(exportPath)
107
+
108
+ return module.factory.createExportDeclaration({
109
+ name: item.name,
110
+ path: options.extname && hasExtname ? `${trimExtName(item.path)}${options.extname}` : trimExtName(item.path),
111
+ isTypeOnly: item.isTypeOnly,
112
+ asAlias: item.asAlias,
113
+ })
114
+ })
115
+ .filter(Boolean)
116
+
117
+ return [file.banner, module.print([...importNodes, ...exportNodes]), source, file.footer].join('\n')
118
+ },
119
+ })
120
+
121
+ const defaultParser = createFileParser({
122
+ async format(source) {
123
+ return source
124
+ },
125
+ async print(file) {
126
+ return file.sources.map((item) => item.value).join('\n\n')
127
+ },
128
+ })
129
+
130
+ const parsers: Record<KubbFile.Extname, ParserModule<any>> = {
131
+ '.ts': typeScriptParser,
132
+ '.js': typeScriptParser,
133
+ '.jsx': typeScriptParser,
134
+ '.tsx': typeScriptParser,
135
+ '.json': defaultParser,
136
+ }
137
+
138
+ export async function getFileParser<TMeta extends object = object>(extname: KubbFile.Extname | undefined): Promise<ParserModule<TMeta>> {
139
+ if (!extname) {
140
+ return defaultParser
141
+ }
142
+
143
+ const parser = parsers[extname]
144
+
145
+ if (!parser) {
146
+ console.warn(`[parser] No parser found for ${extname}, default parser will be used`)
147
+ }
148
+
149
+ return parser || defaultParser
150
+ }
151
+
152
+ function trimExtName(text: string): string {
153
+ const extname = text.split('.').pop()
154
+
155
+ return text.replace(`.${extname}`, '')
156
+ }