@kubb/core 1.0.0-beta.9 → 1.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 (36) hide show
  1. package/dist/index.cjs +526 -244
  2. package/dist/index.d.ts +296 -231
  3. package/dist/index.js +521 -245
  4. package/package.json +8 -10
  5. package/src/build.ts +30 -47
  6. package/src/config.ts +2 -2
  7. package/src/generators/SchemaGenerator.ts +1 -1
  8. package/src/generators/index.ts +2 -2
  9. package/src/index.ts +9 -8
  10. package/src/managers/fileManager/FileManager.ts +19 -6
  11. package/src/managers/fileManager/index.ts +3 -4
  12. package/src/managers/fileManager/types.ts +17 -1
  13. package/src/managers/fileManager/utils.ts +111 -11
  14. package/src/managers/index.ts +2 -2
  15. package/src/managers/pluginManager/ParallelPluginError.ts +15 -0
  16. package/src/managers/pluginManager/PluginError.ts +11 -0
  17. package/src/managers/pluginManager/PluginManager.ts +133 -68
  18. package/src/managers/pluginManager/index.ts +5 -3
  19. package/src/managers/pluginManager/types.ts +10 -1
  20. package/src/managers/pluginManager/validate.ts +1 -1
  21. package/src/plugin.ts +44 -12
  22. package/src/types.ts +35 -8
  23. package/src/utils/clean.ts +5 -0
  24. package/src/utils/getEncodedText.ts +3 -0
  25. package/src/utils/getStackTrace.ts +19 -0
  26. package/src/utils/index.ts +17 -11
  27. package/src/utils/isPromise.ts +1 -1
  28. package/src/utils/objectToParameters.ts +6 -4
  29. package/src/utils/read.ts +3 -3
  30. package/src/utils/renderTemplate.ts +11 -0
  31. package/src/utils/transformReservedWord.ts +97 -0
  32. package/src/utils/write.ts +2 -16
  33. package/dist/index.cjs.map +0 -1
  34. package/dist/index.js.map +0 -1
  35. /package/src/utils/{queue.ts → Queue.ts} +0 -0
  36. /package/src/{managers/fileManager → utils}/TreeNode.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/core",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.1",
4
4
  "description": "Generator core",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,23 +42,21 @@
42
42
  "dependencies": {
43
43
  "change-case": "^4.1.2",
44
44
  "directory-tree": "^3.5.1",
45
- "lodash.uniq": "^4.5.0",
46
- "rimraf": "^3.0.2",
47
- "uuid": "^9.0.0"
45
+ "rimraf": "^5.0.1",
46
+ "@kubb/ts-codegen": "1.0.1"
48
47
  },
49
48
  "devDependencies": {
50
- "@types/lodash.uniq": "^4.5.7",
51
- "@types/rimraf": "^3.0.2",
52
- "@types/uuid": "^9.0.1",
53
49
  "tsup": "^6.7.0",
54
- "typescript": "^5.0.3"
50
+ "ora": "^6.3.1",
51
+ "typescript": "^5.1.3"
55
52
  },
56
53
  "publishConfig": {
57
54
  "access": "public",
58
55
  "registry": "https://registry.npmjs.org/"
59
56
  },
60
57
  "engines": {
61
- "node": "^12.17.0 || ^14.13 || >=16.0.0"
58
+ "node": ">=16",
59
+ "pnpm": ">=8"
62
60
  },
63
61
  "scripts": {
64
62
  "build": "tsup",
@@ -69,6 +67,6 @@
69
67
  "test": "vitest --passWithNoTests",
70
68
  "upgrade": "ncu -u",
71
69
  "upgrade:local": "ncu --interactive --doctor",
72
- "typecheck": "tsc -p ./tsconfig.json --noEmit"
70
+ "typecheck": "tsc -p ./tsconfig.json --noEmit --emitDeclarationOnly false"
73
71
  }
74
72
  }
package/src/build.ts CHANGED
@@ -1,33 +1,17 @@
1
1
  /* eslint-disable no-async-promise-executor */
2
- import pathParser from 'path'
2
+ import { PluginManager } from './managers/pluginManager/index.ts'
3
+ import { clean, read } from './utils/index.ts'
4
+ import { getFileSource } from './managers/fileManager/index.ts'
3
5
 
4
- import { isURL } from './utils/isURL'
5
- import { PluginManager } from './managers/pluginManager'
6
- import { clean, read } from './utils'
7
- import { getFileSource } from './managers/fileManager'
8
-
9
- import type { FileManager, File } from './managers/fileManager'
10
- import type { QueueTask } from './utils'
11
- import type { PluginContext, TransformResult, LogLevel, KubbPlugin } from './types'
12
-
13
- type BuildOutput = {
14
- files: FileManager['files']
15
- }
16
-
17
- // Same type as ora
18
- type Spinner = {
19
- start: (text?: string) => Spinner
20
- succeed: (text: string) => Spinner
21
- fail: (text?: string) => Spinner
22
- stopAndPersist: (options: { text: string }) => Spinner
23
- render: () => Spinner
24
- text: string
25
- info: (text: string) => Spinner
26
- }
6
+ import type { OnExecute } from './managers/pluginManager/index.ts'
7
+ import type { File } from './managers/fileManager/index.ts'
8
+ import type { QueueTask } from './utils/index.ts'
9
+ import type { PluginContext, TransformResult, LogLevel, KubbPlugin, BuildOutput } from './types.ts'
10
+ import type { Ora } from 'ora'
27
11
 
28
12
  export type Logger = {
29
13
  log: (message: string, logLevel: LogLevel) => void
30
- spinner?: Spinner
14
+ spinner?: Ora
31
15
  }
32
16
  type BuildOptions = {
33
17
  config: PluginContext['config']
@@ -46,9 +30,15 @@ async function transformReducer(
46
30
  return result
47
31
  }
48
32
 
49
- async function buildImplementation(options: BuildOptions, done: (output: BuildOutput) => void) {
33
+ export async function build(options: BuildOptions): Promise<BuildOutput> {
50
34
  const { config, logger } = options
51
35
 
36
+ try {
37
+ await read(config.input.path)
38
+ } catch (e: any) {
39
+ throw new Error('Cannot read file defined in `input.path` or set with --input in the CLI of your Kubb config', { cause: e })
40
+ }
41
+
52
42
  if (config.output.clean) {
53
43
  await clean(config.output.path)
54
44
  }
@@ -82,7 +72,19 @@ async function buildImplementation(options: BuildOptions, done: (output: BuildOu
82
72
  }
83
73
  }
84
74
 
85
- const pluginManager = new PluginManager(config, { logger, task: queueTask as QueueTask })
75
+ const onExecute: OnExecute = (executer) => {
76
+ if (!executer) {
77
+ return
78
+ }
79
+
80
+ const { strategy, hookName, plugin } = executer
81
+
82
+ if (config.logLevel === 'info' && logger?.spinner) {
83
+ logger.spinner.text = `[${strategy}] ${hookName}: Excecuting task for plugin ${plugin.name} \n`
84
+ }
85
+ }
86
+
87
+ const pluginManager = new PluginManager(config, { task: queueTask as QueueTask, onExecute })
86
88
  const { plugins, fileManager } = pluginManager
87
89
 
88
90
  await pluginManager.hookParallel<'validate', true>({
@@ -96,25 +98,6 @@ async function buildImplementation(options: BuildOptions, done: (output: BuildOu
96
98
  })
97
99
 
98
100
  await pluginManager.hookParallel({ hookName: 'buildEnd' })
99
- setTimeout(() => {
100
- done({ files: fileManager.files.map((file) => ({ ...file, source: getFileSource(file) })) })
101
- }, 500)
102
-
103
- pluginManager.fileManager.add({
104
- path: isURL(config.input.path) ? config.input.path : pathParser.resolve(config.root, config.input.path),
105
- fileName: isURL(config.input.path) ? 'input' : config.input.path,
106
- source: isURL(config.input.path) ? config.input.path : await read(pathParser.resolve(config.root, config.input.path)),
107
- })
108
- }
109
-
110
- export type KubbBuild = (options: BuildOptions) => Promise<BuildOutput>
111
101
 
112
- export function build(options: BuildOptions): Promise<BuildOutput> {
113
- return new Promise(async (resolve, reject) => {
114
- try {
115
- await buildImplementation(options, resolve)
116
- } catch (e) {
117
- reject(e)
118
- }
119
- })
102
+ return { files: fileManager.files.map((file) => ({ ...file, source: getFileSource(file) })), pluginManager }
120
103
  }
package/src/config.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { MaybePromise, KubbUserConfig, CLIOptions } from './types'
1
+ import type { MaybePromise, KubbUserConfig, CLIOptions } from './types.ts'
2
2
 
3
3
  /**
4
- * Type helper to make it easier to use kubb.config.ts
4
+ * Type helper to make it easier to use kubb.config.js
5
5
  * accepts a direct {@link KubbConfig} object, or a function that returns it.
6
6
  * The function receives a {@link ConfigEnv} object that exposes two properties:
7
7
  */
@@ -1,4 +1,4 @@
1
- import { Generator } from './Generator'
1
+ import { Generator } from './Generator.ts'
2
2
  /**
3
3
  * Abstract class that contains the building blocks for plugins to create their own SchemaGenerator
4
4
  */
@@ -1,2 +1,2 @@
1
- export * from './SchemaGenerator'
2
- export * from './Generator'
1
+ export * from './SchemaGenerator.ts'
2
+ export * from './Generator.ts'
package/src/index.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  /* eslint-disable @typescript-eslint/no-empty-interface */
2
- import { build } from './build'
2
+ import { build } from './build.ts'
3
3
 
4
- export * from './config'
5
- export * from './build'
6
- export { CorePluginOptions, createPlugin, name } from './plugin'
7
- export * from './utils'
8
- export * from './types'
9
- export * from './managers'
10
- export * from './generators'
4
+ export * from './config.ts'
5
+ export * from './build.ts'
6
+ export * from './types.ts'
7
+ export { CorePluginOptions, createPlugin, name } from './plugin.ts'
8
+
9
+ export * from './utils/index.ts'
10
+ export * from './managers/index.ts'
11
+ export * from './generators/index.ts'
11
12
 
12
13
  export default build
@@ -1,9 +1,9 @@
1
- import { v4 as uuidv4 } from 'uuid'
1
+ import crypto from 'node:crypto'
2
2
 
3
- import { write, read } from '../../utils'
3
+ import { write, read } from '../../utils/index.ts'
4
4
 
5
- import type { QueueTask, Queue } from '../../utils'
6
- import type { CacheStore, UUID, Status, File } from './types'
5
+ import type { QueueTask, Queue } from '../../utils/index.ts'
6
+ import type { CacheStore, UUID, Status, File } from './types.ts'
7
7
 
8
8
  export class FileManager {
9
9
  private cache: Map<CacheStore['id'], CacheStore> = new Map()
@@ -23,7 +23,7 @@ export class FileManager {
23
23
  return this.cache.get(id)
24
24
  }
25
25
 
26
- private getCacheByPath(path: string | undefined): CacheStore | undefined {
26
+ getCacheByPath(path: string | undefined): CacheStore | undefined {
27
27
  let cache
28
28
 
29
29
  this.cache.forEach((item) => {
@@ -44,7 +44,7 @@ export class FileManager {
44
44
  }
45
45
 
46
46
  async add(file: File) {
47
- const cacheItem = { id: uuidv4(), file, status: 'new' as Status }
47
+ const cacheItem = { id: crypto.randomUUID(), file, status: 'new' as Status }
48
48
 
49
49
  this.cache.set(cacheItem.id, cacheItem)
50
50
 
@@ -58,14 +58,27 @@ export class FileManager {
58
58
  }
59
59
 
60
60
  addOrAppend(file: File) {
61
+ if (!file.path.endsWith(file.fileName)) {
62
+ // console.warn(`Path ${file.path}(file.path) should end with the fileName ${file.fileName}(file.filename)`)
63
+ }
64
+
61
65
  const previousCache = this.getCacheByPath(file.path)
62
66
 
63
67
  if (previousCache) {
68
+ // empty source will also return true when using includes
69
+ const sourceAlreadyExists = file.source && previousCache.file.source.includes(file.source)
70
+
71
+ if (sourceAlreadyExists) {
72
+ return Promise.resolve(file)
73
+ }
74
+
64
75
  this.cache.delete(previousCache.id)
76
+
65
77
  return this.add({
66
78
  ...file,
67
79
  source: `${previousCache.file.source}\n${file.source}`,
68
80
  imports: [...(previousCache.file.imports || []), ...(file.imports || [])],
81
+ exports: [...(previousCache.file.exports || []), ...(file.exports || [])],
69
82
  })
70
83
  }
71
84
  return this.add(file)
@@ -1,4 +1,3 @@
1
- export * from './FileManager'
2
- export * from './types'
3
- export * from './TreeNode'
4
- export * from './utils'
1
+ export * from './FileManager.ts'
2
+ export * from './types.ts'
3
+ export * from './utils.ts'
@@ -1,9 +1,16 @@
1
1
  type Import = {
2
- name: string | string[] | readonly string[]
2
+ name: string | string[]
3
3
  path: string
4
4
  isTypeOnly?: boolean
5
5
  }
6
6
 
7
+ type Export = {
8
+ name?: string | string[]
9
+ path: string
10
+ isTypeOnly?: boolean
11
+ asAlias?: boolean
12
+ }
13
+
7
14
  export type File = {
8
15
  /**
9
16
  * Name to be used to dynamicly create the fileName(based on input.path)
@@ -15,6 +22,15 @@ export type File = {
15
22
  path: string
16
23
  source: string
17
24
  imports?: Import[]
25
+ exports?: Export[]
26
+ /**
27
+ * This will call fileManager.add instead of fileManager.addOrAppend, adding the source when the files already exists
28
+ * @default `false`
29
+ */
30
+ override?: boolean
31
+ meta?: {
32
+ pluginName?: string
33
+ }
18
34
  }
19
35
 
20
36
  export type UUID = string
@@ -1,6 +1,76 @@
1
- import uniq from 'lodash.uniq'
1
+ import pathParser from 'node:path'
2
2
 
3
- import type { File } from './types'
3
+ import { createImportDeclaration, createExportDeclaration, print } from '@kubb/ts-codegen'
4
+
5
+ import { TreeNode } from '../../utils/index.ts'
6
+
7
+ import type { PathMode, TreeNodeOptions } from '../../utils/index.ts'
8
+ import type { Path } from '../../types.ts'
9
+ import type ts from 'typescript'
10
+ import type { File } from './types.ts'
11
+
12
+ export function writeIndexes(root: string, options: TreeNodeOptions) {
13
+ const tree = TreeNode.build<{ type: PathMode; path: Path; name: string }>(root, { extensions: /\.ts/, ...options })
14
+
15
+ if (!tree) {
16
+ return undefined
17
+ }
18
+
19
+ const fileReducer = (files: File[], item: typeof tree) => {
20
+ if (!item.children) {
21
+ return []
22
+ }
23
+
24
+ if (item.children?.length > 1) {
25
+ const path = pathParser.resolve(item.data.path, 'index.ts')
26
+ const exports = item.children
27
+ .map((file) => {
28
+ if (!file) {
29
+ return undefined
30
+ }
31
+
32
+ const importPath: string = file.data.type === 'directory' ? `./${file.data.name}` : `./${file.data.name.replace(/\.[^.]*$/, '')}`
33
+
34
+ // TODO weird hacky fix
35
+ if (importPath.includes('index') && path.includes('index')) {
36
+ return undefined
37
+ }
38
+
39
+ return { path: importPath }
40
+ })
41
+ .filter(Boolean) as File['exports']
42
+
43
+ files.push({
44
+ path,
45
+ fileName: 'index.ts',
46
+ source: '',
47
+ exports,
48
+ })
49
+ } else {
50
+ item.children?.forEach((child) => {
51
+ const path = pathParser.resolve(item.data.path, 'index.ts')
52
+ const importPath = child.data.type === 'directory' ? `./${child.data.name}` : `./${child.data.name.replace(/\.[^.]*$/, '')}`
53
+
54
+ files.push({
55
+ path,
56
+ fileName: 'index.ts',
57
+ source: '',
58
+ exports: [{ path: importPath }],
59
+ })
60
+ })
61
+ }
62
+
63
+ item.children.forEach((childItem) => {
64
+ fileReducer(files, childItem)
65
+ })
66
+
67
+ return files
68
+ }
69
+
70
+ const files = fileReducer([], tree)
71
+
72
+ return files
73
+ }
4
74
 
5
75
  export function combineFiles(files: Array<File | null>) {
6
76
  return files.filter(Boolean).reduce((acc, curr: File | null) => {
@@ -15,6 +85,7 @@ export function combineFiles(files: Array<File | null>) {
15
85
  ...curr,
16
86
  source: `${prev.source}\n${curr.source}`,
17
87
  imports: [...(prev.imports || []), ...(curr.imports || [])],
88
+ exports: [...(prev.exports || []), ...(curr.exports || [])],
18
89
  }
19
90
  } else {
20
91
  acc.push(curr)
@@ -25,18 +96,21 @@ export function combineFiles(files: Array<File | null>) {
25
96
  }
26
97
 
27
98
  export function getFileSource(file: File) {
99
+ let { source } = file
100
+
28
101
  // TODO make generic check
29
102
  if (!file.fileName.endsWith('.ts')) {
30
103
  return file.source
31
104
  }
32
105
  const imports: File['imports'] = []
106
+ const exports: File['exports'] = []
33
107
 
34
108
  file.imports?.forEach((curr) => {
35
109
  const exists = imports.find((imp) => imp.path === curr.path)
36
110
  if (!exists) {
37
111
  imports.push({
38
112
  ...curr,
39
- name: Array.isArray(curr.name) ? uniq(curr.name) : curr.name,
113
+ name: Array.isArray(curr.name) ? [...new Set(curr.name)] : curr.name,
40
114
  })
41
115
  }
42
116
 
@@ -46,22 +120,48 @@ export function getFileSource(file: File) {
46
120
 
47
121
  if (exists && Array.isArray(exists.name)) {
48
122
  if (Array.isArray(curr.name)) {
49
- exists.name = uniq([...exists.name, ...curr.name])
123
+ exists.name = [...new Set([...exists.name, ...curr.name])]
50
124
  }
51
125
  }
52
126
  })
53
127
 
54
- const importSource = imports.reduce((prev, curr) => {
55
- if (Array.isArray(curr.name)) {
56
- return `${prev}\nimport ${curr.isTypeOnly ? 'type ' : ''}{ ${curr.name.join(', ')} } from "${curr.path}";`
128
+ file.exports?.forEach((curr) => {
129
+ const exists = exports.find((imp) => imp.path === curr.path)
130
+ if (!exists) {
131
+ exports.push({
132
+ ...curr,
133
+ name: Array.isArray(curr.name) ? [...new Set(curr.name)] : curr.name,
134
+ })
57
135
  }
58
136
 
59
- return `${prev}\nimport ${curr.isTypeOnly ? 'type ' : ''}${curr.name} from "${curr.path}";`
60
- }, '')
137
+ if (exists && !Array.isArray(exists.name) && exists.name !== curr.name && exists.asAlias === curr.asAlias) {
138
+ exports.push(curr)
139
+ }
140
+
141
+ if (exists && Array.isArray(exists.name)) {
142
+ if (Array.isArray(curr.name)) {
143
+ exists.name = [...new Set([...exists.name, ...curr.name])]
144
+ }
145
+ }
146
+ })
147
+
148
+ const importNodes = imports.reduce((prev, curr) => {
149
+ return [...prev, createImportDeclaration({ name: curr.name, path: curr.path, isTypeOnly: curr.isTypeOnly })]
150
+ }, [] as ts.ImportDeclaration[])
151
+ const importSource = print(importNodes)
152
+
153
+ const exportNodes = exports.reduce((prev, curr) => {
154
+ return [...prev, createExportDeclaration({ name: curr.name, path: curr.path, asAlias: curr.asAlias })]
155
+ }, [] as ts.ExportDeclaration[])
156
+ const exportSource = print(exportNodes)
61
157
 
62
158
  if (importSource) {
63
- return `${importSource}\n${file.source}`
159
+ source = `${importSource}\n${source}`
160
+ }
161
+
162
+ if (exportSource) {
163
+ source = `${exportSource}\n${source}`
64
164
  }
65
165
 
66
- return file.source
166
+ return source
67
167
  }
@@ -1,2 +1,2 @@
1
- export * from './fileManager'
2
- export * from './pluginManager'
1
+ export * from './fileManager/index.ts'
2
+ export * from './pluginManager/index.ts'
@@ -0,0 +1,15 @@
1
+ import type { PluginError } from './PluginError.ts'
2
+ import type { PluginManager } from './PluginManager'
3
+
4
+ export class ParallelPluginError extends Error {
5
+ public errors: PluginError[]
6
+
7
+ public pluginManager: PluginManager
8
+
9
+ constructor(message: string, options: { cause?: Error; errors: PluginError[]; pluginManager: PluginManager }) {
10
+ super(message, { cause: options.cause })
11
+
12
+ this.errors = options.errors
13
+ this.pluginManager = options.pluginManager
14
+ }
15
+ }
@@ -0,0 +1,11 @@
1
+ import type { PluginManager } from './PluginManager'
2
+
3
+ export class PluginError extends Error {
4
+ public pluginManager: PluginManager
5
+
6
+ constructor(message: string, options: { cause?: Error; pluginManager: PluginManager }) {
7
+ super(message, { cause: options.cause })
8
+
9
+ this.pluginManager = options.pluginManager
10
+ }
11
+ }