@kubb/core 2.0.0-alpha.3 → 2.0.0-alpha.4

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 (52) hide show
  1. package/dist/index.cjs +62 -74
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +18 -38
  4. package/dist/index.d.ts +18 -38
  5. package/dist/index.js +62 -72
  6. package/dist/index.js.map +1 -1
  7. package/dist/utils.cjs +12 -7
  8. package/dist/utils.cjs.map +1 -1
  9. package/dist/utils.d.cts +2 -1
  10. package/dist/utils.d.ts +2 -1
  11. package/dist/utils.js +12 -7
  12. package/dist/utils.js.map +1 -1
  13. package/package.json +8 -7
  14. package/src/BarrelManager.ts +123 -0
  15. package/src/FileManager.ts +482 -0
  16. package/src/Generator.ts +34 -0
  17. package/src/PackageManager.ts +163 -0
  18. package/src/PluginManager.ts +640 -0
  19. package/src/PromiseManager.ts +48 -0
  20. package/src/SchemaGenerator.ts +8 -0
  21. package/src/build.ts +198 -0
  22. package/src/config.ts +21 -0
  23. package/src/errors.ts +12 -0
  24. package/src/index.ts +28 -0
  25. package/src/plugin.ts +80 -0
  26. package/src/types.ts +370 -0
  27. package/src/utils/EventEmitter.ts +24 -0
  28. package/src/utils/FunctionParams.ts +85 -0
  29. package/src/utils/Queue.ts +110 -0
  30. package/src/utils/TreeNode.ts +122 -0
  31. package/src/utils/URLPath.ts +128 -0
  32. package/src/utils/cache.ts +35 -0
  33. package/src/utils/clean.ts +5 -0
  34. package/src/utils/executeStrategies.ts +71 -0
  35. package/src/utils/index.ts +19 -0
  36. package/src/utils/logger.ts +76 -0
  37. package/src/utils/promise.ts +13 -0
  38. package/src/utils/randomColour.ts +39 -0
  39. package/src/utils/read.ts +68 -0
  40. package/src/utils/renderTemplate.ts +31 -0
  41. package/src/utils/throttle.ts +30 -0
  42. package/src/utils/timeout.ts +7 -0
  43. package/src/utils/transformers/combineCodes.ts +3 -0
  44. package/src/utils/transformers/createJSDocBlockText.ts +15 -0
  45. package/src/utils/transformers/escape.ts +31 -0
  46. package/src/utils/transformers/indent.ts +3 -0
  47. package/src/utils/transformers/index.ts +20 -0
  48. package/src/utils/transformers/nameSorter.ts +9 -0
  49. package/src/utils/transformers/searchAndReplace.ts +25 -0
  50. package/src/utils/transformers/transformReservedWord.ts +97 -0
  51. package/src/utils/uniqueName.ts +20 -0
  52. package/src/utils/write.ts +63 -0
package/src/build.ts ADDED
@@ -0,0 +1,198 @@
1
+ import pc from 'picocolors'
2
+
3
+ import { clean } from './utils/clean.ts'
4
+ import { createLogger, LogLevel } from './utils/logger.ts'
5
+ import { randomPicoColour } from './utils/randomColour.ts'
6
+ import { read } from './utils/read.ts'
7
+ import { URLPath } from './utils/URLPath.ts'
8
+ import { isInputPath } from './config.ts'
9
+ import { FileManager } from './FileManager.ts'
10
+ import { PluginManager } from './PluginManager.ts'
11
+ import { isPromise } from './PromiseManager.ts'
12
+
13
+ import type { KubbFile } from './FileManager.ts'
14
+ import type { BuildOutput, KubbPlugin, PluginContext, PluginParameter, TransformResult } from './types.ts'
15
+ import type { Logger } from './utils/logger.ts'
16
+ import type { QueueJob } from './utils/Queue.ts'
17
+
18
+ type BuildOptions = {
19
+ config: PluginContext['config']
20
+ /**
21
+ * @default Logger without the spinner
22
+ */
23
+ logger?: Logger
24
+ }
25
+
26
+ async function transformReducer(
27
+ this: PluginContext,
28
+ _previousCode: string,
29
+ result: TransformResult | Promise<TransformResult>,
30
+ _plugin: KubbPlugin,
31
+ ): Promise<string | null> {
32
+ return result
33
+ }
34
+
35
+ async function setup(options: BuildOptions): Promise<PluginManager> {
36
+ const { config, logger = createLogger({ logLevel: LogLevel.silent }) } = options
37
+
38
+ try {
39
+ if (isInputPath(config) && !new URLPath(config.input.path).isURL) {
40
+ await read(config.input.path)
41
+ }
42
+ } catch (e) {
43
+ if (isInputPath(config)) {
44
+ throw new Error(
45
+ 'Cannot read file/URL defined in `input.path` or set with `kubb generate PATH` in the CLI of your Kubb config ' + pc.dim(config.input.path),
46
+ {
47
+ cause: e,
48
+ },
49
+ )
50
+ }
51
+ }
52
+
53
+ if (config.output.clean) {
54
+ await clean(config.output.path)
55
+ }
56
+
57
+ const queueTask = async (file: KubbFile.File) => {
58
+ const { path } = file
59
+
60
+ let code: string | null = FileManager.getSource(file)
61
+
62
+ const { result: loadedResult } = await pluginManager.hookFirst({
63
+ hookName: 'load',
64
+ parameters: [path],
65
+ })
66
+ if (loadedResult && isPromise(loadedResult)) {
67
+ code = await loadedResult
68
+ }
69
+ if (loadedResult && !isPromise(loadedResult)) {
70
+ code = loadedResult
71
+ }
72
+
73
+ if (code) {
74
+ const transformedCode = await pluginManager.hookReduceArg0({
75
+ hookName: 'transform',
76
+ parameters: [code, path],
77
+ reduce: transformReducer,
78
+ })
79
+
80
+ if (config.output.write || config.output.write === undefined) {
81
+ if (file.meta?.pluginKey) {
82
+ // run only for pluginKey defined in the meta of the file
83
+ return pluginManager.hookForPlugin({
84
+ pluginKey: file.meta?.pluginKey,
85
+ hookName: 'writeFile',
86
+ parameters: [transformedCode, path],
87
+ })
88
+ }
89
+
90
+ return pluginManager.hookFirst({
91
+ hookName: 'writeFile',
92
+ parameters: [transformedCode, path],
93
+ })
94
+ }
95
+ }
96
+ }
97
+
98
+ const pluginManager = new PluginManager(config, { logger, task: queueTask as QueueJob<KubbFile.ResolvedFile>, writeTimeout: 0 })
99
+
100
+ pluginManager.on('execute', (executer) => {
101
+ const { hookName, parameters, plugin } = executer
102
+
103
+ if (hookName === 'writeFile' && logger.spinner) {
104
+ const [code] = parameters as PluginParameter<'writeFile'>
105
+
106
+ if (logger.logLevel === LogLevel.info) {
107
+ logger.spinner.start(`💾 Writing`)
108
+ }
109
+
110
+ if (logger.logLevel === 'debug') {
111
+ logger.info(`PluginKey ${pc.dim(JSON.stringify(plugin.key))} \nwith source\n\n${code}`)
112
+ }
113
+ }
114
+ })
115
+
116
+ pluginManager.on('executed', (executer) => {
117
+ const { hookName, plugin, output, parameters } = executer
118
+ const messsage = `${randomPicoColour(plugin.name)} Executing ${hookName}`
119
+
120
+ if (logger.logLevel === LogLevel.info && logger.spinner) {
121
+ if (hookName === 'writeFile') {
122
+ const [_code, path] = parameters as PluginParameter<'writeFile'>
123
+
124
+ logger.spinner.suffixText = pc.dim(path)
125
+ } else {
126
+ logger.spinner.suffixText = messsage
127
+ }
128
+ }
129
+
130
+ if (logger.logLevel === LogLevel.debug) {
131
+ logger.info(messsage)
132
+ const logs = [
133
+ parameters && `${pc.bgWhite(`Parameters`)} ${randomPicoColour(plugin.name)} ${hookName}`,
134
+ JSON.stringify(parameters, undefined, 2),
135
+ output && `${pc.bgWhite('Output')} ${randomPicoColour(plugin.name)} ${hookName}`,
136
+ output,
137
+ ].filter(Boolean)
138
+
139
+ console.log(logs.join('\n'))
140
+ }
141
+ })
142
+
143
+ return pluginManager
144
+ }
145
+
146
+ export async function build(options: BuildOptions): Promise<BuildOutput> {
147
+ const pluginManager = await setup(options)
148
+
149
+ const { fileManager, logger } = pluginManager
150
+
151
+ await pluginManager.hookParallel<'validate', true>({
152
+ hookName: 'validate',
153
+ parameters: [pluginManager.plugins],
154
+ })
155
+
156
+ await pluginManager.hookParallel({
157
+ hookName: 'buildStart',
158
+ parameters: [options.config],
159
+ })
160
+
161
+ await pluginManager.hookParallel({ hookName: 'buildEnd' })
162
+
163
+ if (!fileManager.isExecuting && logger.spinner) {
164
+ logger.spinner.suffixText = ''
165
+ logger.spinner.succeed(`💾 Writing completed`)
166
+ }
167
+
168
+ return { files: fileManager.files.map((file) => ({ ...file, source: FileManager.getSource(file) })), pluginManager }
169
+ }
170
+
171
+ export async function safeBuild(options: BuildOptions): Promise<BuildOutput> {
172
+ const pluginManager = await setup(options)
173
+
174
+ const { fileManager, logger } = pluginManager
175
+
176
+ try {
177
+ await pluginManager.hookParallel<'validate', true>({
178
+ hookName: 'validate',
179
+ parameters: [pluginManager.plugins],
180
+ })
181
+
182
+ await pluginManager.hookParallel({
183
+ hookName: 'buildStart',
184
+ parameters: [options.config],
185
+ })
186
+
187
+ await pluginManager.hookParallel({ hookName: 'buildEnd' })
188
+
189
+ if (!fileManager.isExecuting && logger.spinner) {
190
+ logger.spinner.suffixText = ''
191
+ logger.spinner.succeed(`💾 Writing completed`)
192
+ }
193
+ } catch (e) {
194
+ return { files: fileManager.files.map((file) => ({ ...file, source: FileManager.getSource(file) })), pluginManager, error: e as Error }
195
+ }
196
+
197
+ return { files: fileManager.files.map((file) => ({ ...file, source: FileManager.getSource(file) })), pluginManager }
198
+ }
package/src/config.ts ADDED
@@ -0,0 +1,21 @@
1
+ import type { CLIOptions, InputPath, KubbConfig, KubbUserConfig, PossiblePromise } from './types.ts'
2
+
3
+ /**
4
+ * Type helper to make it easier to use kubb.config.js
5
+ * accepts a direct {@link KubbConfig} object, or a function that returns it.
6
+ * The function receives a {@link ConfigEnv} object that exposes two properties:
7
+ */
8
+ export function defineConfig(
9
+ options:
10
+ | PossiblePromise<KubbUserConfig | Array<KubbUserConfig>>
11
+ | ((
12
+ /** The options derived from the CLI flags */
13
+ cliOptions: CLIOptions,
14
+ ) => PossiblePromise<KubbUserConfig | Array<KubbUserConfig>>),
15
+ ): typeof options {
16
+ return options
17
+ }
18
+
19
+ export function isInputPath(result: KubbConfig | undefined): result is KubbConfig<InputPath> {
20
+ return !!result && 'path' in (result as any)
21
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Behaves as an Error to log a warning in the console(still stops the execution)
3
+ */
4
+ export class Warning extends Error {
5
+ constructor(message?: string, options?: { cause: Error }) {
6
+ super(message, { cause: options?.cause })
7
+
8
+ this.name = 'Warning'
9
+ }
10
+ }
11
+
12
+ export class ValidationPluginError extends Error {}
package/src/index.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { build } from './build.ts'
2
+
3
+ import type { ObjValueTuple, TupleToUnion } from './types.ts'
4
+
5
+ export { build, safeBuild } from './build.ts'
6
+ export * from './config.ts'
7
+ export * from './errors.ts'
8
+ export * from './FileManager.ts'
9
+ export { Generator } from './Generator.ts'
10
+ export { PackageManager } from './PackageManager.ts'
11
+ // dprint-ignore
12
+ export { createPlugin, pluginName as name, pluginName } from './plugin.ts'
13
+ export { PluginManager } from './PluginManager.ts'
14
+ export { PromiseManager } from './PromiseManager.ts'
15
+ export { SchemaGenerator } from './SchemaGenerator.ts'
16
+ export * from './types.ts'
17
+
18
+ export interface _Register {}
19
+ export type Plugins = _Register
20
+ export type OptionsPlugins = { [K in keyof Plugins]: Plugins[K]['options'] }
21
+
22
+ export type OptionsOfPlugin<K extends keyof Plugins> = Plugins[K]['options']
23
+
24
+ export type PluginUnion = TupleToUnion<ObjValueTuple<OptionsPlugins>>
25
+
26
+ export type Plugin = keyof Plugins
27
+
28
+ export default build
package/src/plugin.ts ADDED
@@ -0,0 +1,80 @@
1
+ import path from 'node:path'
2
+
3
+ import { createPluginCache } from './utils/cache.ts'
4
+
5
+ import type { FileManager } from './FileManager.ts'
6
+ import type { PluginManager } from './PluginManager.ts'
7
+ import type { KubbPlugin, KubbUserPlugin, PluginContext, PluginFactoryOptions } from './types.ts'
8
+
9
+ type KubbPluginFactory<T extends PluginFactoryOptions = PluginFactoryOptions> = (options: T['options']) => KubbUserPlugin<T>
10
+
11
+ export function createPlugin<T extends PluginFactoryOptions = PluginFactoryOptions>(factory: KubbPluginFactory<T>) {
12
+ return (options: T['options']): ReturnType<KubbPluginFactory<T>> => {
13
+ return factory(options)
14
+ }
15
+ }
16
+
17
+ type Options = {
18
+ config: PluginContext['config']
19
+ fileManager: FileManager
20
+ pluginManager: PluginManager
21
+ resolvePath: PluginContext['resolvePath']
22
+ resolveName: PluginContext['resolveName']
23
+ logger: PluginContext['logger']
24
+ getPlugins: () => KubbPlugin[]
25
+ plugin?: PluginContext['plugin']
26
+ }
27
+
28
+ // not publicly exported
29
+ export type CorePluginOptions = PluginFactoryOptions<'core', 'controller', Options, false, PluginContext>
30
+
31
+ export const pluginName = 'core' satisfies CorePluginOptions['name']
32
+ export const pluginKey: CorePluginOptions['key'] = ['controller', pluginName] satisfies CorePluginOptions['key']
33
+
34
+ export const definePlugin = createPlugin<CorePluginOptions>((options) => {
35
+ const { fileManager, pluginManager, resolvePath, resolveName, logger } = options
36
+
37
+ return {
38
+ name: pluginName,
39
+ options,
40
+ key: ['controller', 'core'],
41
+ kind: 'controller',
42
+ api() {
43
+ return {
44
+ get config() {
45
+ return options.config
46
+ },
47
+ get plugins() {
48
+ return options.getPlugins()
49
+ },
50
+ get plugin() {
51
+ // see pluginManger.#execute where we override with `.call` the this with the correct plugin
52
+ return options.plugin as NonNullable<Options['plugin']>
53
+ },
54
+ logger,
55
+ fileManager,
56
+ pluginManager,
57
+ async addFile(...files) {
58
+ const resolvedFiles = await fileManager.add(...files)
59
+
60
+ if (!Array.isArray(resolvedFiles)) {
61
+ return [resolvedFiles]
62
+ }
63
+
64
+ return resolvedFiles
65
+ },
66
+ resolvePath,
67
+ resolveName,
68
+ cache: createPluginCache(),
69
+ }
70
+ },
71
+ resolvePath(baseName) {
72
+ const root = path.resolve(this.config.root, this.config.output.path)
73
+
74
+ return path.resolve(root, baseName)
75
+ },
76
+ resolveName(name) {
77
+ return name
78
+ },
79
+ }
80
+ })