@kubb/core 2.0.0-canary.20231030T124958 → 2.0.1

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 (100) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-4A7WG6IA.js +128 -0
  3. package/dist/chunk-4A7WG6IA.js.map +1 -0
  4. package/dist/chunk-54P4AWHI.js +71 -0
  5. package/dist/chunk-54P4AWHI.js.map +1 -0
  6. package/dist/chunk-5TK7TMV6.cjs +131 -0
  7. package/dist/chunk-5TK7TMV6.cjs.map +1 -0
  8. package/dist/chunk-7S67BJXQ.js +85 -0
  9. package/dist/chunk-7S67BJXQ.js.map +1 -0
  10. package/dist/chunk-E3ANGQ5N.cjs +2290 -0
  11. package/dist/chunk-E3ANGQ5N.cjs.map +1 -0
  12. package/dist/chunk-H47IKRXJ.cjs +129 -0
  13. package/dist/chunk-H47IKRXJ.cjs.map +1 -0
  14. package/dist/chunk-HIE46T3F.js +129 -0
  15. package/dist/chunk-HIE46T3F.js.map +1 -0
  16. package/dist/chunk-K2H7BYQB.js +155 -0
  17. package/dist/chunk-K2H7BYQB.js.map +1 -0
  18. package/dist/chunk-NAWI7UXW.js +67 -0
  19. package/dist/chunk-NAWI7UXW.js.map +1 -0
  20. package/dist/chunk-PLVKILIY.cjs +162 -0
  21. package/dist/chunk-PLVKILIY.cjs.map +1 -0
  22. package/dist/chunk-W2FP7ZWW.cjs +71 -0
  23. package/dist/chunk-W2FP7ZWW.cjs.map +1 -0
  24. package/dist/chunk-WZQO3EPM.cjs +91 -0
  25. package/dist/chunk-WZQO3EPM.cjs.map +1 -0
  26. package/dist/chunk-XDHI63G7.cjs +104 -0
  27. package/dist/chunk-XDHI63G7.cjs.map +1 -0
  28. package/dist/chunk-XPOF4D5N.js +18 -0
  29. package/dist/chunk-XPOF4D5N.js.map +1 -0
  30. package/dist/fs.cjs +31 -0
  31. package/dist/fs.cjs.map +1 -0
  32. package/dist/fs.d.cts +5 -0
  33. package/dist/fs.d.ts +5 -0
  34. package/dist/fs.js +11 -0
  35. package/dist/fs.js.map +1 -0
  36. package/dist/index.cjs +1866 -977
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.cts +302 -319
  39. package/dist/index.d.ts +302 -319
  40. package/dist/index.js +1071 -846
  41. package/dist/index.js.map +1 -1
  42. package/dist/logger.cjs +26 -0
  43. package/dist/logger.cjs.map +1 -0
  44. package/dist/logger.d.cts +32 -0
  45. package/dist/logger.d.ts +32 -0
  46. package/dist/logger.js +8 -0
  47. package/dist/logger.js.map +1 -0
  48. package/dist/transformers.cjs +124 -0
  49. package/dist/transformers.cjs.map +1 -0
  50. package/dist/transformers.d.cts +55 -0
  51. package/dist/transformers.d.ts +55 -0
  52. package/dist/transformers.js +95 -0
  53. package/dist/transformers.js.map +1 -0
  54. package/dist/utils.cjs +23 -1163
  55. package/dist/utils.cjs.map +1 -1
  56. package/dist/utils.d.cts +2 -143
  57. package/dist/utils.d.ts +2 -143
  58. package/dist/utils.js +15 -1118
  59. package/dist/utils.js.map +1 -1
  60. package/dist/write-A6VgHkYA.d.cts +10 -0
  61. package/dist/write-A6VgHkYA.d.ts +10 -0
  62. package/package.json +40 -23
  63. package/src/BarrelManager.ts +113 -0
  64. package/src/FileManager.ts +581 -0
  65. package/src/Generator.ts +34 -0
  66. package/src/PackageManager.ts +178 -0
  67. package/src/PluginManager.ts +645 -0
  68. package/src/PromiseManager.ts +51 -0
  69. package/src/build.ts +221 -0
  70. package/src/config.ts +22 -0
  71. package/src/errors.ts +12 -0
  72. package/src/fs/clean.ts +5 -0
  73. package/src/fs/index.ts +3 -0
  74. package/src/fs/read.ts +68 -0
  75. package/src/fs/write.ts +79 -0
  76. package/src/index.ts +27 -0
  77. package/src/logger.ts +121 -0
  78. package/src/plugin.ts +80 -0
  79. package/src/transformers/casing.ts +9 -0
  80. package/src/transformers/combineCodes.ts +3 -0
  81. package/src/transformers/createJSDocBlockText.ts +9 -0
  82. package/src/transformers/escape.ts +31 -0
  83. package/src/transformers/indent.ts +3 -0
  84. package/src/transformers/index.ts +36 -0
  85. package/src/transformers/nameSorter.ts +9 -0
  86. package/src/transformers/searchAndReplace.ts +25 -0
  87. package/src/transformers/transformReservedWord.ts +97 -0
  88. package/src/transformers/trim.ts +7 -0
  89. package/src/types.ts +334 -0
  90. package/src/utils/EventEmitter.ts +24 -0
  91. package/src/utils/FunctionParams.ts +86 -0
  92. package/src/utils/TreeNode.ts +125 -0
  93. package/src/utils/URLPath.ts +133 -0
  94. package/src/utils/cache.ts +35 -0
  95. package/src/utils/executeStrategies.ts +83 -0
  96. package/src/utils/index.ts +8 -0
  97. package/src/utils/promise.ts +13 -0
  98. package/src/utils/renderTemplate.ts +31 -0
  99. package/src/utils/timeout.ts +7 -0
  100. package/src/utils/uniqueName.ts +20 -0
@@ -0,0 +1,581 @@
1
+ /* eslint-disable @typescript-eslint/no-namespace */
2
+ import crypto from 'node:crypto'
3
+ import { extname, resolve } from 'node:path'
4
+
5
+ import { print } from '@kubb/parser'
6
+ import * as factory from '@kubb/parser/factory'
7
+
8
+ import isEqual from 'lodash.isequal'
9
+ import { orderBy } from 'natural-orderby'
10
+ import PQueue from 'p-queue'
11
+
12
+ import { getRelativePath, read } from './fs/read.ts'
13
+ import { write } from './fs/write.ts'
14
+ import { searchAndReplace } from './transformers/searchAndReplace.ts'
15
+ import { trimExtName } from './transformers/trim.ts'
16
+ import { BarrelManager } from './BarrelManager.ts'
17
+
18
+ import type { GreaterThan } from '@kubb/types'
19
+ import type { BarrelManagerOptions } from './BarrelManager.ts'
20
+ import type { KubbPlugin } from './types.ts'
21
+
22
+ type BasePath<T extends string = string> = `${T}/`
23
+
24
+ export namespace KubbFile {
25
+ export type Import = {
26
+ /**
27
+ * Import name to be used
28
+ * @example ["useState"]
29
+ * @example "React"
30
+ */
31
+ name:
32
+ | string
33
+ | Array<
34
+ string | {
35
+ propertyName: string
36
+ name?: string
37
+ }
38
+ >
39
+ /**
40
+ * Path for the import
41
+ * @xample '@kubb/core'
42
+ */
43
+ path: string
44
+ /**
45
+ * Add `type` prefix to the import, this will result in: `import type { Type } from './path'`.
46
+ */
47
+ isTypeOnly?: boolean
48
+ /**
49
+ * Add `* as` prefix to the import, this will result in: `import * as path from './path'`.
50
+ */
51
+ isNameSpace?: boolean
52
+ /**
53
+ * When root is set it will get the path with relative getRelativePath(root, path).
54
+ */
55
+ root?: string
56
+ }
57
+
58
+ export type Export = {
59
+ /**
60
+ * Export name to be used.
61
+ * @example ["useState"]
62
+ * @example "React"
63
+ */
64
+ name?: string | Array<string>
65
+ /**
66
+ * Path for the import.
67
+ * @xample '@kubb/core'
68
+ */
69
+ path: string
70
+ /**
71
+ * Add `type` prefix to the export, this will result in: `export type { Type } from './path'`.
72
+ */
73
+ isTypeOnly?: boolean
74
+ /**
75
+ * Make it possible to override the name, this will result in: `export * as aliasName from './path'`.
76
+ */
77
+ asAlias?: boolean
78
+ }
79
+
80
+ export declare const dataTagSymbol: unique symbol
81
+ export type DataTag<Type, Value> = Type & {
82
+ [dataTagSymbol]: Value
83
+ }
84
+
85
+ export type UUID = string
86
+ export type Source = string
87
+
88
+ export type Extname = '.ts' | '.js' | '.tsx' | '.json' | `.${string}`
89
+
90
+ export type Mode = 'file' | 'directory'
91
+
92
+ /**
93
+ * Name to be used to dynamicly create the baseName(based on input.path)
94
+ * Based on UNIX basename
95
+ * @link https://nodejs.org/api/path.html#pathbasenamepath-suffix
96
+ */
97
+ export type BaseName = `${string}${Extname}`
98
+
99
+ /**
100
+ * Path will be full qualified path to a specified file
101
+ */
102
+ export type Path = string
103
+
104
+ export type AdvancedPath<T extends BaseName = BaseName> = `${BasePath}${T}`
105
+
106
+ export type OptionalPath = Path | undefined | null
107
+
108
+ export type FileMetaBase = {
109
+ pluginKey?: KubbPlugin['key']
110
+ }
111
+
112
+ export type File<
113
+ TMeta extends FileMetaBase = FileMetaBase,
114
+ TBaseName extends BaseName = BaseName,
115
+ > = {
116
+ /**
117
+ * Unique identifier to reuse later
118
+ * @default crypto.randomUUID()
119
+ */
120
+ id?: string
121
+ /**
122
+ * Name to be used to create the path
123
+ * Based on UNIX basename, `${name}.extName`
124
+ * @link https://nodejs.org/api/path.html#pathbasenamepath-suffix
125
+ */
126
+ baseName: TBaseName
127
+ /**
128
+ * Path will be full qualified path to a specified file
129
+ */
130
+ path: AdvancedPath<TBaseName> | Path
131
+ source: Source
132
+ imports?: Import[]
133
+ exports?: Export[]
134
+ /**
135
+ * This will call fileManager.add instead of fileManager.addOrAppend, adding the source when the files already exists
136
+ * This will also ignore the combinefiles utils
137
+ * @default `false`
138
+ */
139
+ override?: boolean
140
+ /**
141
+ * Use extra meta, this is getting used to generate the barrel/index files.
142
+ */
143
+ meta?: TMeta
144
+ /**
145
+ * This will override `process.env[key]` inside the `source`, see `getFileSource`.
146
+ */
147
+ env?: NodeJS.ProcessEnv
148
+ }
149
+
150
+ export type ResolvedFile<
151
+ TMeta extends FileMetaBase = FileMetaBase,
152
+ TBaseName extends BaseName = BaseName,
153
+ > = KubbFile.File<TMeta, TBaseName> & {
154
+ /**
155
+ * @default crypto.randomUUID()
156
+ */
157
+ id: UUID
158
+ /**
159
+ * Contains the first part of the baseName, generated based on baseName
160
+ * @link https://nodejs.org/api/path.html#pathformatpathobject
161
+ */
162
+
163
+ name: string
164
+ }
165
+ }
166
+
167
+ type CacheItem = KubbFile.ResolvedFile & {
168
+ cancel?: () => void
169
+ }
170
+
171
+ type AddResult<T extends Array<KubbFile.File>> = Promise<
172
+ Awaited<GreaterThan<T['length'], 1> extends true ? Promise<KubbFile.ResolvedFile[]> : Promise<KubbFile.ResolvedFile>>
173
+ >
174
+
175
+ type AddIndexesProps = {
176
+ /**
177
+ * Root based on root and output.path specified in the config
178
+ */
179
+ root: string
180
+ /**
181
+ * Output for plugin
182
+ */
183
+ output: {
184
+ path: string
185
+ exportAs?: string
186
+ extName?: KubbFile.Extname
187
+ exportType?: 'barrel' | false
188
+ }
189
+ options?: BarrelManagerOptions
190
+ meta?: KubbFile.File['meta']
191
+ }
192
+
193
+ type Options = {
194
+ queue?: PQueue
195
+ task?: (file: KubbFile.ResolvedFile) => Promise<KubbFile.ResolvedFile>
196
+ }
197
+
198
+ export class FileManager {
199
+ #cache: Map<KubbFile.Path, CacheItem[]> = new Map()
200
+
201
+ #task: Options['task']
202
+ #queue: PQueue
203
+
204
+ constructor({ task = async (file) => file, queue = new PQueue() }: Options = {}) {
205
+ this.#task = task
206
+ this.#queue = queue
207
+
208
+ return this
209
+ }
210
+
211
+ get files(): Array<KubbFile.File> {
212
+ const files: Array<KubbFile.File> = []
213
+ this.#cache.forEach((item) => {
214
+ files.push(...item.flat(1))
215
+ })
216
+
217
+ return files
218
+ }
219
+ get isExecuting(): boolean {
220
+ return this.#queue.size !== 0 && this.#queue.pending !== 0
221
+ }
222
+
223
+ async add<T extends Array<KubbFile.File> = Array<KubbFile.File>>(
224
+ ...files: T
225
+ ): AddResult<T> {
226
+ const promises = combineFiles(files).map((file) => {
227
+ if (file.override) {
228
+ return this.#add(file)
229
+ }
230
+
231
+ return this.#addOrAppend(file)
232
+ })
233
+
234
+ const resolvedFiles = await Promise.all(promises)
235
+
236
+ if (files.length > 1) {
237
+ return resolvedFiles as unknown as AddResult<T>
238
+ }
239
+
240
+ return resolvedFiles[0] as unknown as AddResult<T>
241
+ }
242
+
243
+ async #add(file: KubbFile.File): Promise<KubbFile.ResolvedFile> {
244
+ const controller = new AbortController()
245
+ const resolvedFile: KubbFile.ResolvedFile = { id: crypto.randomUUID(), name: trimExtName(file.baseName), ...file }
246
+
247
+ this.#cache.set(resolvedFile.path, [{ cancel: () => controller.abort(), ...resolvedFile }])
248
+
249
+ return this.#queue.add(
250
+ async () => {
251
+ return this.#task?.(resolvedFile)
252
+ },
253
+ { signal: controller.signal },
254
+ ) as Promise<KubbFile.ResolvedFile>
255
+ }
256
+
257
+ async #addOrAppend(file: KubbFile.File): Promise<KubbFile.ResolvedFile> {
258
+ const previousCaches = this.#cache.get(file.path)
259
+ const previousCache = previousCaches ? previousCaches.at(previousCaches.length - 1) : undefined
260
+
261
+ if (previousCache) {
262
+ this.#cache.delete(previousCache.path)
263
+
264
+ return this.#add({
265
+ ...file,
266
+ source: previousCache.source && file.source ? `${previousCache.source}\n${file.source}` : '',
267
+ imports: [...(previousCache.imports || []), ...(file.imports || [])],
268
+ exports: [...(previousCache.exports || []), ...(file.exports || [])],
269
+ env: { ...(previousCache.env || {}), ...(file.env || {}) },
270
+ })
271
+ }
272
+ return this.#add(file)
273
+ }
274
+
275
+ async addIndexes({ root, output, meta, options = {} }: AddIndexesProps): Promise<Array<KubbFile.File> | undefined> {
276
+ const { exportType = 'barrel' } = output
277
+
278
+ if (!exportType) {
279
+ return undefined
280
+ }
281
+
282
+ const exportPath = output.path.startsWith('./') ? trimExtName(output.path) : `./${trimExtName(output.path)}`
283
+ const mode = FileManager.getMode(output.path)
284
+ const barrelManager = new BarrelManager({ extName: output.extName, ...options })
285
+ const files = barrelManager.getIndexes(resolve(root, output.path))
286
+
287
+ function getPath() {
288
+ if (output.extName) {
289
+ if (mode === 'directory') {
290
+ return `${exportPath}/index${output.extName}`
291
+ }
292
+ return `${exportPath}${output.extName}`
293
+ }
294
+
295
+ return exportPath
296
+ }
297
+
298
+ if (!files) {
299
+ return undefined
300
+ }
301
+
302
+ const rootFile: KubbFile.File = {
303
+ path: resolve(root, 'index.ts'),
304
+ baseName: 'index.ts',
305
+ source: '',
306
+ exports: [
307
+ output.exportAs
308
+ ? {
309
+ name: output.exportAs,
310
+ asAlias: true,
311
+ path: getPath(),
312
+ isTypeOnly: options.isTypeOnly,
313
+ }
314
+ : {
315
+ path: getPath(),
316
+ isTypeOnly: options.isTypeOnly,
317
+ },
318
+ ],
319
+ }
320
+
321
+ await this.#addOrAppend({
322
+ ...rootFile,
323
+ meta: meta ? meta : rootFile.meta,
324
+ })
325
+
326
+ return await Promise.all(
327
+ files.map((file) => {
328
+ return this.#addOrAppend({
329
+ ...file,
330
+ meta: meta ? meta : file.meta,
331
+ })
332
+ }),
333
+ )
334
+ }
335
+
336
+ getCacheByUUID(UUID: KubbFile.UUID): KubbFile.File | undefined {
337
+ let cache: KubbFile.File | undefined
338
+
339
+ this.#cache.forEach((files) => {
340
+ cache = files.find((item) => item.id === UUID)
341
+ })
342
+ return cache
343
+ }
344
+
345
+ get(path: KubbFile.Path): Array<KubbFile.File> | undefined {
346
+ return this.#cache.get(path)
347
+ }
348
+
349
+ remove(path: KubbFile.Path): void {
350
+ const cacheItem = this.get(path)
351
+ if (!cacheItem) {
352
+ return
353
+ }
354
+
355
+ this.#cache.delete(path)
356
+ }
357
+
358
+ async write(...params: Parameters<typeof write>): Promise<string | undefined> {
359
+ return write(...params)
360
+ }
361
+
362
+ async read(...params: Parameters<typeof read>): Promise<string> {
363
+ return read(...params)
364
+ }
365
+
366
+ // statics
367
+
368
+ static getSource<TMeta extends KubbFile.FileMetaBase = KubbFile.FileMetaBase>(file: KubbFile.File<TMeta>): string {
369
+ return getSource<TMeta>(file)
370
+ }
371
+
372
+ static combineFiles<TMeta extends KubbFile.FileMetaBase = KubbFile.FileMetaBase>(files: Array<KubbFile.File<TMeta> | null>): Array<KubbFile.File<TMeta>> {
373
+ return combineFiles<TMeta>(files)
374
+ }
375
+ static getMode(path: string | undefined | null): KubbFile.Mode {
376
+ if (!path) {
377
+ return 'directory'
378
+ }
379
+ return extname(path) ? 'file' : 'directory'
380
+ }
381
+
382
+ static get extensions(): Array<KubbFile.Extname> {
383
+ return ['.js', '.ts', '.tsx']
384
+ }
385
+
386
+ static isExtensionAllowed(baseName: string): boolean {
387
+ return FileManager.extensions.some((extension) => baseName.endsWith(extension))
388
+ }
389
+ }
390
+
391
+ function combineFiles<TMeta extends KubbFile.FileMetaBase = KubbFile.FileMetaBase>(
392
+ files: Array<KubbFile.File<TMeta> | null>,
393
+ ): Array<KubbFile.File<TMeta>> {
394
+ return files.filter(Boolean).reduce((acc, file: KubbFile.File<TMeta>) => {
395
+ const prevIndex = acc.findIndex((item) => item.path === file.path)
396
+
397
+ if (prevIndex === -1) {
398
+ return [...acc, file]
399
+ }
400
+
401
+ const prev = acc[prevIndex]
402
+
403
+ if (prev && file.override) {
404
+ acc[prevIndex] = {
405
+ imports: [],
406
+ exports: [],
407
+ ...file,
408
+ }
409
+ return acc
410
+ }
411
+
412
+ if (prev) {
413
+ acc[prevIndex] = {
414
+ ...file,
415
+ source: prev.source && file.source ? `${prev.source}\n${file.source}` : '',
416
+ imports: [...(prev.imports || []), ...(file.imports || [])],
417
+ exports: [...(prev.exports || []), ...(file.exports || [])],
418
+ env: { ...(prev.env || {}), ...(file.env || {}) },
419
+ }
420
+ }
421
+
422
+ return acc
423
+ }, [] as Array<KubbFile.File<TMeta>>)
424
+ }
425
+
426
+ export function getSource<TMeta extends KubbFile.FileMetaBase = KubbFile.FileMetaBase>(file: KubbFile.File<TMeta>): string {
427
+ if (!FileManager.isExtensionAllowed(file.baseName)) {
428
+ return file.source
429
+ }
430
+
431
+ const exports = file.exports ? combineExports(file.exports) : []
432
+ const imports = file.imports ? combineImports(file.imports, exports, file.source) : []
433
+
434
+ const importNodes = imports.filter(item => {
435
+ // isImportNotNeeded
436
+ // trim extName
437
+ return item.path !== trimExtName(file.path)
438
+ }).map((item) => {
439
+ return factory.createImportDeclaration({
440
+ name: item.name,
441
+ path: item.root ? getRelativePath(item.root, item.path) : item.path,
442
+ isTypeOnly: item.isTypeOnly,
443
+ })
444
+ })
445
+ const exportNodes = exports.map((item) =>
446
+ factory.createExportDeclaration({
447
+ name: item.name,
448
+ path: item.path,
449
+ isTypeOnly: item.isTypeOnly,
450
+ asAlias: item.asAlias,
451
+ })
452
+ )
453
+
454
+ return [print([...importNodes, ...exportNodes]), getEnvSource(file.source, file.env)].join('\n')
455
+ }
456
+
457
+ export function combineExports(exports: Array<KubbFile.Export>): Array<KubbFile.Export> {
458
+ const combinedExports = orderBy(exports, [(v) => !v.isTypeOnly], ['asc']).reduce((prev, curr) => {
459
+ const name = curr.name
460
+ const prevByPath = prev.findLast((imp) => imp.path === curr.path)
461
+ const prevByPathAndIsTypeOnly = prev.findLast((imp) => imp.path === curr.path && isEqual(imp.name, name) && imp.isTypeOnly)
462
+
463
+ if (prevByPathAndIsTypeOnly) {
464
+ // we already have an export that has the same path but uses `isTypeOnly` (export type ...)
465
+ return prev
466
+ }
467
+
468
+ const uniquePrev = prev.findLast(
469
+ (imp) => imp.path === curr.path && isEqual(imp.name, name) && imp.isTypeOnly === curr.isTypeOnly && imp.asAlias === curr.asAlias,
470
+ )
471
+
472
+ if (uniquePrev || (Array.isArray(name) && !name.length) || (prevByPath?.asAlias && !curr.asAlias)) {
473
+ return prev
474
+ }
475
+
476
+ if (!prevByPath) {
477
+ return [
478
+ ...prev,
479
+ {
480
+ ...curr,
481
+ name: Array.isArray(name) ? [...new Set(name)] : name,
482
+ },
483
+ ]
484
+ }
485
+
486
+ if (prevByPath && Array.isArray(prevByPath.name) && Array.isArray(curr.name) && prevByPath.isTypeOnly === curr.isTypeOnly) {
487
+ prevByPath.name = [...new Set([...prevByPath.name, ...curr.name])]
488
+
489
+ return prev
490
+ }
491
+
492
+ return [...prev, curr]
493
+ }, [] as Array<KubbFile.Export>)
494
+
495
+ return orderBy(combinedExports, [(v) => !v.isTypeOnly, (v) => v.asAlias], ['desc', 'desc'])
496
+ }
497
+
498
+ export function combineImports(imports: Array<KubbFile.Import>, exports: Array<KubbFile.Export>, source?: string): Array<KubbFile.Import> {
499
+ const combinedImports = orderBy(imports, [(v) => !v.isTypeOnly], ['asc']).reduce((prev, curr) => {
500
+ let name = Array.isArray(curr.name) ? [...new Set(curr.name)] : curr.name
501
+
502
+ const hasImportInSource = (importName: string) => {
503
+ if (!source) {
504
+ return true
505
+ }
506
+
507
+ const checker = (name?: string) => name && !!source.includes(name)
508
+ return checker(importName) || exports.some(({ name }) => (Array.isArray(name) ? name.some(checker) : checker(name)))
509
+ }
510
+
511
+ if (Array.isArray(name)) {
512
+ name = name.filter((item) => typeof item === 'string' ? hasImportInSource(item) : hasImportInSource(item.propertyName))
513
+ }
514
+
515
+ const prevByPath = prev.findLast((imp) => imp.path === curr.path && imp.isTypeOnly === curr.isTypeOnly)
516
+ const uniquePrev = prev.findLast((imp) => imp.path === curr.path && isEqual(imp.name, name) && imp.isTypeOnly === curr.isTypeOnly)
517
+ const prevByPathNameAndIsTypeOnly = prev.findLast((imp) => imp.path === curr.path && isEqual(imp.name, name) && imp.isTypeOnly)
518
+
519
+ if (prevByPathNameAndIsTypeOnly) {
520
+ // we already have an export that has the same path but uses `isTypeOnly` (import type ...)
521
+ return prev
522
+ }
523
+
524
+ if (uniquePrev || (Array.isArray(name) && !name.length)) {
525
+ return prev
526
+ }
527
+
528
+ if (!prevByPath) {
529
+ return [
530
+ ...prev,
531
+ {
532
+ ...curr,
533
+ name,
534
+ },
535
+ ]
536
+ }
537
+
538
+ if (prevByPath && Array.isArray(prevByPath.name) && Array.isArray(name) && prevByPath.isTypeOnly === curr.isTypeOnly) {
539
+ prevByPath.name = [...new Set([...prevByPath.name, ...name])]
540
+
541
+ return prev
542
+ }
543
+
544
+ if (!Array.isArray(name) && name && !hasImportInSource(name)) {
545
+ return prev
546
+ }
547
+
548
+ return [...prev, curr]
549
+ }, [] as Array<KubbFile.Import>)
550
+
551
+ return orderBy(combinedImports, [(v) => !v.isTypeOnly], ['desc'])
552
+ }
553
+
554
+ function getEnvSource(source: string, env: NodeJS.ProcessEnv | undefined): string {
555
+ if (!env) {
556
+ return source
557
+ }
558
+
559
+ const keys = Object.keys(env)
560
+
561
+ if (!keys.length) {
562
+ return source
563
+ }
564
+
565
+ return keys.reduce((prev, key: string) => {
566
+ const environmentValue = env[key]
567
+ const replaceBy = environmentValue ? `'${environmentValue.replaceAll('"', '')?.replaceAll("'", '')}'` : 'undefined'
568
+
569
+ if (key.toUpperCase() !== key) {
570
+ throw new TypeError(`Environment should be in upperCase for ${key}`)
571
+ }
572
+
573
+ if (typeof replaceBy === 'string') {
574
+ prev = searchAndReplace({ text: prev.replaceAll(`process.env.${key}`, replaceBy), replaceBy, prefix: 'process.env', key })
575
+ // removes `declare const ...`
576
+ prev = searchAndReplace({ text: prev.replaceAll(new RegExp(`(declare const).*\n`, 'ig'), ''), replaceBy, key })
577
+ }
578
+
579
+ return prev
580
+ }, source)
581
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Abstract class that contains the building blocks for plugins to create their own Generator
3
+ * @link idea based on https://github.com/colinhacks/zod/blob/master/src/types.ts#L137
4
+ */
5
+ export abstract class Generator<TOptions = unknown, TContext = unknown> {
6
+ #options: TOptions = {} as TOptions
7
+ #context: TContext = {} as TContext
8
+
9
+ constructor(options?: TOptions, context?: TContext) {
10
+ if (context) {
11
+ this.#context = context
12
+ }
13
+
14
+ if (options) {
15
+ this.#options = options
16
+ }
17
+
18
+ return this
19
+ }
20
+
21
+ get options(): TOptions {
22
+ return this.#options
23
+ }
24
+
25
+ get context(): TContext {
26
+ return this.#context
27
+ }
28
+
29
+ set options(options: TOptions) {
30
+ this.#options = { ...this.#options, ...options }
31
+ }
32
+
33
+ abstract build(...params: unknown[]): unknown
34
+ }