@kubb/core 5.0.0-beta.3 → 5.0.0-beta.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.
- package/README.md +8 -38
- package/dist/KubbDriver-CFx2DdhF.js +2131 -0
- package/dist/KubbDriver-CFx2DdhF.js.map +1 -0
- package/dist/KubbDriver-vyD7F0Ip.cjs +2252 -0
- package/dist/KubbDriver-vyD7F0Ip.cjs.map +1 -0
- package/dist/{types-CC09VtBt.d.ts → createKubb-6zii1jo-.d.ts} +1610 -1257
- package/dist/index.cjs +351 -1125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -186
- package/dist/index.js +341 -1119
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +30 -21
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +5 -5
- package/dist/mocks.js +29 -20
- package/dist/mocks.js.map +1 -1
- package/package.json +6 -18
- package/src/FileManager.ts +78 -61
- package/src/FileProcessor.ts +48 -38
- package/src/KubbDriver.ts +930 -0
- package/src/constants.ts +11 -6
- package/src/createAdapter.ts +113 -17
- package/src/createKubb.ts +1039 -478
- package/src/createRenderer.ts +58 -27
- package/src/createStorage.ts +36 -23
- package/src/defineGenerator.ts +127 -15
- package/src/defineLogger.ts +66 -7
- package/src/defineMiddleware.ts +19 -17
- package/src/defineParser.ts +30 -13
- package/src/definePlugin.ts +329 -14
- package/src/defineResolver.ts +365 -167
- package/src/devtools.ts +8 -1
- package/src/index.ts +2 -2
- package/src/mocks.ts +11 -14
- package/src/storages/fsStorage.ts +13 -37
- package/src/types.ts +48 -1292
- package/dist/PluginDriver-BXibeQk-.cjs +0 -1036
- package/dist/PluginDriver-BXibeQk-.cjs.map +0 -1
- package/dist/PluginDriver-DV3p2Hky.js +0 -945
- package/dist/PluginDriver-DV3p2Hky.js.map +0 -1
- package/src/Kubb.ts +0 -300
- package/src/PluginDriver.ts +0 -424
- package/src/renderNode.ts +0 -35
- package/src/utils/diagnostics.ts +0 -18
- package/src/utils/isInputPath.ts +0 -10
- package/src/utils/packageJSON.ts +0 -99
package/dist/index.js
CHANGED
|
@@ -1,184 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { a as
|
|
3
|
-
import { EventEmitter } from "node:events";
|
|
1
|
+
import "./chunk--u3MIqq1.js";
|
|
2
|
+
import { a as FileManager, c as DEFAULT_BANNER, d as logLevel, f as URLPath, i as FileProcessor, l as DEFAULT_EXTENSION, m as BuildError, o as defineResolver, p as AsyncEventEmitter, r as _usingCtx, s as definePlugin, t as KubbDriver, u as DEFAULT_STUDIO_URL } from "./KubbDriver-CFx2DdhF.js";
|
|
4
3
|
import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
5
4
|
import { dirname, join, resolve } from "node:path";
|
|
6
5
|
import * as ast from "@kubb/ast";
|
|
7
|
-
import { collectUsedSchemaNames, extractStringsFromNodes, transform, walk } from "@kubb/ast";
|
|
8
6
|
import { version } from "node:process";
|
|
9
|
-
//#region ../../internals/utils/src/errors.ts
|
|
10
|
-
/**
|
|
11
|
-
* Thrown when one or more errors occur during a Kubb build.
|
|
12
|
-
* Carries the full list of underlying errors on `errors`.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```ts
|
|
16
|
-
* throw new BuildError('Build failed', { errors: [err1, err2] })
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
var BuildError = class extends Error {
|
|
20
|
-
errors;
|
|
21
|
-
constructor(message, options) {
|
|
22
|
-
super(message, { cause: options.cause });
|
|
23
|
-
this.name = "BuildError";
|
|
24
|
-
this.errors = options.errors;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* Coerces an unknown thrown value to an `Error` instance.
|
|
29
|
-
* Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```ts
|
|
33
|
-
* try { ... } catch(err) {
|
|
34
|
-
* throw new BuildError('Build failed', { cause: toError(err), errors: [] })
|
|
35
|
-
* }
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
function toError(value) {
|
|
39
|
-
return value instanceof Error ? value : new Error(String(value));
|
|
40
|
-
}
|
|
41
|
-
//#endregion
|
|
42
|
-
//#region ../../internals/utils/src/asyncEventEmitter.ts
|
|
43
|
-
/**
|
|
44
|
-
* Typed `EventEmitter` that awaits all async listeners before resolving.
|
|
45
|
-
* Wraps Node's `EventEmitter` with full TypeScript event-map inference.
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```ts
|
|
49
|
-
* const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
|
|
50
|
-
* emitter.on('build', async (name) => { console.log(name) })
|
|
51
|
-
* await emitter.emit('build', 'petstore') // all listeners awaited
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
var AsyncEventEmitter = class {
|
|
55
|
-
/**
|
|
56
|
-
* Maximum number of listeners per event before Node emits a memory-leak warning.
|
|
57
|
-
* @default 10
|
|
58
|
-
*/
|
|
59
|
-
constructor(maxListener = 10) {
|
|
60
|
-
this.#emitter.setMaxListeners(maxListener);
|
|
61
|
-
}
|
|
62
|
-
#emitter = new EventEmitter();
|
|
63
|
-
/**
|
|
64
|
-
* Emits `eventName` and awaits all registered listeners sequentially.
|
|
65
|
-
* Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```ts
|
|
69
|
-
* await emitter.emit('build', 'petstore')
|
|
70
|
-
* ```
|
|
71
|
-
*/
|
|
72
|
-
async emit(eventName, ...eventArgs) {
|
|
73
|
-
const listeners = this.#emitter.listeners(eventName);
|
|
74
|
-
if (listeners.length === 0) return;
|
|
75
|
-
for (const listener of listeners) try {
|
|
76
|
-
await listener(...eventArgs);
|
|
77
|
-
} catch (err) {
|
|
78
|
-
let serializedArgs;
|
|
79
|
-
try {
|
|
80
|
-
serializedArgs = JSON.stringify(eventArgs);
|
|
81
|
-
} catch {
|
|
82
|
-
serializedArgs = String(eventArgs);
|
|
83
|
-
}
|
|
84
|
-
throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: toError(err) });
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Registers a persistent listener for `eventName`.
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```ts
|
|
92
|
-
* emitter.on('build', async (name) => { console.log(name) })
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
on(eventName, handler) {
|
|
96
|
-
this.#emitter.on(eventName, handler);
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Registers a one-shot listener that removes itself after the first invocation.
|
|
100
|
-
*
|
|
101
|
-
* @example
|
|
102
|
-
* ```ts
|
|
103
|
-
* emitter.onOnce('build', async (name) => { console.log(name) })
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
onOnce(eventName, handler) {
|
|
107
|
-
const wrapper = (...args) => {
|
|
108
|
-
this.off(eventName, wrapper);
|
|
109
|
-
return handler(...args);
|
|
110
|
-
};
|
|
111
|
-
this.on(eventName, wrapper);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Removes a previously registered listener.
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* ```ts
|
|
118
|
-
* emitter.off('build', handler)
|
|
119
|
-
* ```
|
|
120
|
-
*/
|
|
121
|
-
off(eventName, handler) {
|
|
122
|
-
this.#emitter.off(eventName, handler);
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Returns the number of listeners registered for `eventName`.
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* ```ts
|
|
129
|
-
* emitter.on('build', handler)
|
|
130
|
-
* emitter.listenerCount('build') // 1
|
|
131
|
-
* ```
|
|
132
|
-
*/
|
|
133
|
-
listenerCount(eventName) {
|
|
134
|
-
return this.#emitter.listenerCount(eventName);
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Removes all listeners from every event channel.
|
|
138
|
-
*
|
|
139
|
-
* @example
|
|
140
|
-
* ```ts
|
|
141
|
-
* emitter.removeAll()
|
|
142
|
-
* ```
|
|
143
|
-
*/
|
|
144
|
-
removeAll() {
|
|
145
|
-
this.#emitter.removeAllListeners();
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
//#endregion
|
|
149
|
-
//#region ../../internals/utils/src/time.ts
|
|
150
|
-
/**
|
|
151
|
-
* Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
|
|
152
|
-
* Rounds to 2 decimal places for sub-millisecond precision without noise.
|
|
153
|
-
*
|
|
154
|
-
* @example
|
|
155
|
-
* ```ts
|
|
156
|
-
* const start = process.hrtime()
|
|
157
|
-
* doWork()
|
|
158
|
-
* getElapsedMs(start) // 42.35
|
|
159
|
-
* ```
|
|
160
|
-
*/
|
|
161
|
-
function getElapsedMs(hrStart) {
|
|
162
|
-
const [seconds, nanoseconds] = process.hrtime(hrStart);
|
|
163
|
-
const ms = seconds * 1e3 + nanoseconds / 1e6;
|
|
164
|
-
return Math.round(ms * 100) / 100;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
|
|
168
|
-
*
|
|
169
|
-
* @example
|
|
170
|
-
* ```ts
|
|
171
|
-
* formatMs(250) // '250ms'
|
|
172
|
-
* formatMs(1500) // '1.50s'
|
|
173
|
-
* formatMs(90000) // '1m 30.0s'
|
|
174
|
-
* ```
|
|
175
|
-
*/
|
|
176
|
-
function formatMs(ms) {
|
|
177
|
-
if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
|
|
178
|
-
if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
179
|
-
return `${Math.round(ms)}ms`;
|
|
180
|
-
}
|
|
181
|
-
//#endregion
|
|
182
7
|
//#region ../../internals/utils/src/fs.ts
|
|
183
8
|
/**
|
|
184
9
|
* Resolves to `true` when the file or directory at `path` exists.
|
|
@@ -245,491 +70,81 @@ async function clean(path) {
|
|
|
245
70
|
});
|
|
246
71
|
}
|
|
247
72
|
//#endregion
|
|
248
|
-
//#region ../../internals/utils/src/reserved.ts
|
|
249
|
-
/**
|
|
250
|
-
* JavaScript and Java reserved words.
|
|
251
|
-
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
252
|
-
*/
|
|
253
|
-
const reservedWords = new Set([
|
|
254
|
-
"abstract",
|
|
255
|
-
"arguments",
|
|
256
|
-
"boolean",
|
|
257
|
-
"break",
|
|
258
|
-
"byte",
|
|
259
|
-
"case",
|
|
260
|
-
"catch",
|
|
261
|
-
"char",
|
|
262
|
-
"class",
|
|
263
|
-
"const",
|
|
264
|
-
"continue",
|
|
265
|
-
"debugger",
|
|
266
|
-
"default",
|
|
267
|
-
"delete",
|
|
268
|
-
"do",
|
|
269
|
-
"double",
|
|
270
|
-
"else",
|
|
271
|
-
"enum",
|
|
272
|
-
"eval",
|
|
273
|
-
"export",
|
|
274
|
-
"extends",
|
|
275
|
-
"false",
|
|
276
|
-
"final",
|
|
277
|
-
"finally",
|
|
278
|
-
"float",
|
|
279
|
-
"for",
|
|
280
|
-
"function",
|
|
281
|
-
"goto",
|
|
282
|
-
"if",
|
|
283
|
-
"implements",
|
|
284
|
-
"import",
|
|
285
|
-
"in",
|
|
286
|
-
"instanceof",
|
|
287
|
-
"int",
|
|
288
|
-
"interface",
|
|
289
|
-
"let",
|
|
290
|
-
"long",
|
|
291
|
-
"native",
|
|
292
|
-
"new",
|
|
293
|
-
"null",
|
|
294
|
-
"package",
|
|
295
|
-
"private",
|
|
296
|
-
"protected",
|
|
297
|
-
"public",
|
|
298
|
-
"return",
|
|
299
|
-
"short",
|
|
300
|
-
"static",
|
|
301
|
-
"super",
|
|
302
|
-
"switch",
|
|
303
|
-
"synchronized",
|
|
304
|
-
"this",
|
|
305
|
-
"throw",
|
|
306
|
-
"throws",
|
|
307
|
-
"transient",
|
|
308
|
-
"true",
|
|
309
|
-
"try",
|
|
310
|
-
"typeof",
|
|
311
|
-
"var",
|
|
312
|
-
"void",
|
|
313
|
-
"volatile",
|
|
314
|
-
"while",
|
|
315
|
-
"with",
|
|
316
|
-
"yield",
|
|
317
|
-
"Array",
|
|
318
|
-
"Date",
|
|
319
|
-
"hasOwnProperty",
|
|
320
|
-
"Infinity",
|
|
321
|
-
"isFinite",
|
|
322
|
-
"isNaN",
|
|
323
|
-
"isPrototypeOf",
|
|
324
|
-
"length",
|
|
325
|
-
"Math",
|
|
326
|
-
"name",
|
|
327
|
-
"NaN",
|
|
328
|
-
"Number",
|
|
329
|
-
"Object",
|
|
330
|
-
"prototype",
|
|
331
|
-
"String",
|
|
332
|
-
"toString",
|
|
333
|
-
"undefined",
|
|
334
|
-
"valueOf"
|
|
335
|
-
]);
|
|
336
|
-
/**
|
|
337
|
-
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
338
|
-
*
|
|
339
|
-
* @example
|
|
340
|
-
* ```ts
|
|
341
|
-
* isValidVarName('status') // true
|
|
342
|
-
* isValidVarName('class') // false (reserved word)
|
|
343
|
-
* isValidVarName('42foo') // false (starts with digit)
|
|
344
|
-
* ```
|
|
345
|
-
*/
|
|
346
|
-
function isValidVarName(name) {
|
|
347
|
-
if (!name || reservedWords.has(name)) return false;
|
|
348
|
-
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
349
|
-
}
|
|
350
|
-
//#endregion
|
|
351
|
-
//#region ../../internals/utils/src/urlPath.ts
|
|
352
|
-
/**
|
|
353
|
-
* Parses and transforms an OpenAPI/Swagger path string into various URL formats.
|
|
354
|
-
*
|
|
355
|
-
* @example
|
|
356
|
-
* const p = new URLPath('/pet/{petId}')
|
|
357
|
-
* p.URL // '/pet/:petId'
|
|
358
|
-
* p.template // '`/pet/${petId}`'
|
|
359
|
-
*/
|
|
360
|
-
var URLPath = class {
|
|
361
|
-
/**
|
|
362
|
-
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
363
|
-
*/
|
|
364
|
-
path;
|
|
365
|
-
#options;
|
|
366
|
-
constructor(path, options = {}) {
|
|
367
|
-
this.path = path;
|
|
368
|
-
this.#options = options;
|
|
369
|
-
}
|
|
370
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
371
|
-
*
|
|
372
|
-
* @example
|
|
373
|
-
* ```ts
|
|
374
|
-
* new URLPath('/pet/{petId}').URL // '/pet/:petId'
|
|
375
|
-
* ```
|
|
376
|
-
*/
|
|
377
|
-
get URL() {
|
|
378
|
-
return this.toURLPath();
|
|
379
|
-
}
|
|
380
|
-
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
381
|
-
*
|
|
382
|
-
* @example
|
|
383
|
-
* ```ts
|
|
384
|
-
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
385
|
-
* new URLPath('/pet/{petId}').isURL // false
|
|
386
|
-
* ```
|
|
387
|
-
*/
|
|
388
|
-
get isURL() {
|
|
389
|
-
try {
|
|
390
|
-
return !!new URL(this.path).href;
|
|
391
|
-
} catch {
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
397
|
-
*
|
|
398
|
-
* @example
|
|
399
|
-
* new URLPath('/pet/{petId}').template // '`/pet/${petId}`'
|
|
400
|
-
* new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
|
|
401
|
-
*/
|
|
402
|
-
get template() {
|
|
403
|
-
return this.toTemplateString();
|
|
404
|
-
}
|
|
405
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
406
|
-
*
|
|
407
|
-
* @example
|
|
408
|
-
* ```ts
|
|
409
|
-
* new URLPath('/pet/{petId}').object
|
|
410
|
-
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
411
|
-
* ```
|
|
412
|
-
*/
|
|
413
|
-
get object() {
|
|
414
|
-
return this.toObject();
|
|
415
|
-
}
|
|
416
|
-
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
417
|
-
*
|
|
418
|
-
* @example
|
|
419
|
-
* ```ts
|
|
420
|
-
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
421
|
-
* new URLPath('/pet').params // undefined
|
|
422
|
-
* ```
|
|
423
|
-
*/
|
|
424
|
-
get params() {
|
|
425
|
-
return this.getParams();
|
|
426
|
-
}
|
|
427
|
-
#transformParam(raw) {
|
|
428
|
-
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
429
|
-
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
433
|
-
*/
|
|
434
|
-
#eachParam(fn) {
|
|
435
|
-
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
436
|
-
const raw = match[1];
|
|
437
|
-
fn(raw, this.#transformParam(raw));
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
toObject({ type = "path", replacer, stringify } = {}) {
|
|
441
|
-
const object = {
|
|
442
|
-
url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
|
|
443
|
-
params: this.getParams()
|
|
444
|
-
};
|
|
445
|
-
if (stringify) {
|
|
446
|
-
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
447
|
-
if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
|
|
448
|
-
return `{ url: '${object.url}' }`;
|
|
449
|
-
}
|
|
450
|
-
return object;
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
454
|
-
* An optional `replacer` can transform each extracted parameter name before interpolation.
|
|
455
|
-
*
|
|
456
|
-
* @example
|
|
457
|
-
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
458
|
-
*/
|
|
459
|
-
toTemplateString({ prefix = "", replacer } = {}) {
|
|
460
|
-
return `\`${prefix}${this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
461
|
-
if (i % 2 === 0) return part;
|
|
462
|
-
const param = this.#transformParam(part);
|
|
463
|
-
return `\${${replacer ? replacer(param) : param}}`;
|
|
464
|
-
}).join("")}\``;
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
468
|
-
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
469
|
-
* Returns `undefined` when no path parameters are found.
|
|
470
|
-
*
|
|
471
|
-
* @example
|
|
472
|
-
* ```ts
|
|
473
|
-
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
474
|
-
* // { petId: 'petId', tagId: 'tagId' }
|
|
475
|
-
* ```
|
|
476
|
-
*/
|
|
477
|
-
getParams(replacer) {
|
|
478
|
-
const params = {};
|
|
479
|
-
this.#eachParam((_raw, param) => {
|
|
480
|
-
const key = replacer ? replacer(param) : param;
|
|
481
|
-
params[key] = key;
|
|
482
|
-
});
|
|
483
|
-
return Object.keys(params).length > 0 ? params : void 0;
|
|
484
|
-
}
|
|
485
|
-
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
486
|
-
*
|
|
487
|
-
* @example
|
|
488
|
-
* ```ts
|
|
489
|
-
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
490
|
-
* ```
|
|
491
|
-
*/
|
|
492
|
-
toURLPath() {
|
|
493
|
-
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
494
|
-
}
|
|
495
|
-
};
|
|
496
|
-
//#endregion
|
|
497
73
|
//#region src/createAdapter.ts
|
|
498
74
|
/**
|
|
499
|
-
*
|
|
75
|
+
* Defines a custom adapter that translates a spec format into Kubb's universal
|
|
76
|
+
* AST. Use this when you need to consume GraphQL, gRPC, AsyncAPI, or another
|
|
77
|
+
* domain-specific schema. Built-in adapters: `@kubb/adapter-oas` for
|
|
78
|
+
* OpenAPI/Swagger documents.
|
|
500
79
|
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
* @note Adapters must parse their input format to Kubb's `InputNode` structure.
|
|
80
|
+
* Adapters must return an `InputNode` from `parse`. That node is what every
|
|
81
|
+
* plugin in the build consumes.
|
|
505
82
|
*
|
|
506
83
|
* @example
|
|
507
84
|
* ```ts
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
* options,
|
|
512
|
-
* async parse(source) {
|
|
513
|
-
* // Transform source format to InputNode
|
|
514
|
-
* return { ... }
|
|
515
|
-
* },
|
|
516
|
-
* }
|
|
517
|
-
* })
|
|
85
|
+
* import { createAdapter, ast, type AdapterFactoryOptions } from '@kubb/core'
|
|
86
|
+
*
|
|
87
|
+
* type MyAdapter = AdapterFactoryOptions<'my-adapter', { validate?: boolean }>
|
|
518
88
|
*
|
|
519
|
-
*
|
|
520
|
-
*
|
|
89
|
+
* export const myAdapter = createAdapter<MyAdapter>((options) => ({
|
|
90
|
+
* name: 'my-adapter',
|
|
91
|
+
* options,
|
|
92
|
+
* document: null,
|
|
93
|
+
* async parse(_source) {
|
|
94
|
+
* // Convert `source` (path or inline data) into an InputNode.
|
|
95
|
+
* return ast.createInput()
|
|
96
|
+
* },
|
|
97
|
+
* getImports: () => [],
|
|
98
|
+
* async validate() {
|
|
99
|
+
* // Throw or call ctx.error here when the spec is invalid.
|
|
100
|
+
* },
|
|
101
|
+
* }))
|
|
521
102
|
* ```
|
|
522
103
|
*/
|
|
523
104
|
function createAdapter(build) {
|
|
524
105
|
return (options) => build(options ?? {});
|
|
525
106
|
}
|
|
526
107
|
//#endregion
|
|
527
|
-
//#region
|
|
528
|
-
var
|
|
529
|
-
static {
|
|
530
|
-
__name(this, "Node");
|
|
531
|
-
}
|
|
532
|
-
value;
|
|
533
|
-
next;
|
|
534
|
-
constructor(value) {
|
|
535
|
-
this.value = value;
|
|
536
|
-
}
|
|
537
|
-
};
|
|
538
|
-
var Queue = class {
|
|
539
|
-
#head;
|
|
540
|
-
#tail;
|
|
541
|
-
#size;
|
|
542
|
-
constructor() {
|
|
543
|
-
this.clear();
|
|
544
|
-
}
|
|
545
|
-
enqueue(value) {
|
|
546
|
-
const node = new Node$1(value);
|
|
547
|
-
if (this.#head) {
|
|
548
|
-
this.#tail.next = node;
|
|
549
|
-
this.#tail = node;
|
|
550
|
-
} else {
|
|
551
|
-
this.#head = node;
|
|
552
|
-
this.#tail = node;
|
|
553
|
-
}
|
|
554
|
-
this.#size++;
|
|
555
|
-
}
|
|
556
|
-
dequeue() {
|
|
557
|
-
const current = this.#head;
|
|
558
|
-
if (!current) return;
|
|
559
|
-
this.#head = this.#head.next;
|
|
560
|
-
this.#size--;
|
|
561
|
-
if (!this.#head) this.#tail = void 0;
|
|
562
|
-
return current.value;
|
|
563
|
-
}
|
|
564
|
-
peek() {
|
|
565
|
-
if (!this.#head) return;
|
|
566
|
-
return this.#head.value;
|
|
567
|
-
}
|
|
568
|
-
clear() {
|
|
569
|
-
this.#head = void 0;
|
|
570
|
-
this.#tail = void 0;
|
|
571
|
-
this.#size = 0;
|
|
572
|
-
}
|
|
573
|
-
get size() {
|
|
574
|
-
return this.#size;
|
|
575
|
-
}
|
|
576
|
-
*[Symbol.iterator]() {
|
|
577
|
-
let current = this.#head;
|
|
578
|
-
while (current) {
|
|
579
|
-
yield current.value;
|
|
580
|
-
current = current.next;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
*drain() {
|
|
584
|
-
while (this.#head) yield this.dequeue();
|
|
585
|
-
}
|
|
586
|
-
};
|
|
587
|
-
//#endregion
|
|
588
|
-
//#region ../../node_modules/.pnpm/p-limit@7.3.0/node_modules/p-limit/index.js
|
|
589
|
-
function pLimit(concurrency) {
|
|
590
|
-
let rejectOnClear = false;
|
|
591
|
-
if (typeof concurrency === "object") ({concurrency, rejectOnClear = false} = concurrency);
|
|
592
|
-
validateConcurrency(concurrency);
|
|
593
|
-
if (typeof rejectOnClear !== "boolean") throw new TypeError("Expected `rejectOnClear` to be a boolean");
|
|
594
|
-
const queue = new Queue();
|
|
595
|
-
let activeCount = 0;
|
|
596
|
-
const resumeNext = () => {
|
|
597
|
-
if (activeCount < concurrency && queue.size > 0) {
|
|
598
|
-
activeCount++;
|
|
599
|
-
queue.dequeue().run();
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
const next = () => {
|
|
603
|
-
activeCount--;
|
|
604
|
-
resumeNext();
|
|
605
|
-
};
|
|
606
|
-
const run = async (function_, resolve, arguments_) => {
|
|
607
|
-
const result = (async () => function_(...arguments_))();
|
|
608
|
-
resolve(result);
|
|
609
|
-
try {
|
|
610
|
-
await result;
|
|
611
|
-
} catch {}
|
|
612
|
-
next();
|
|
613
|
-
};
|
|
614
|
-
const enqueue = (function_, resolve, reject, arguments_) => {
|
|
615
|
-
const queueItem = { reject };
|
|
616
|
-
new Promise((internalResolve) => {
|
|
617
|
-
queueItem.run = internalResolve;
|
|
618
|
-
queue.enqueue(queueItem);
|
|
619
|
-
}).then(run.bind(void 0, function_, resolve, arguments_));
|
|
620
|
-
if (activeCount < concurrency) resumeNext();
|
|
621
|
-
};
|
|
622
|
-
const generator = (function_, ...arguments_) => new Promise((resolve, reject) => {
|
|
623
|
-
enqueue(function_, resolve, reject, arguments_);
|
|
624
|
-
});
|
|
625
|
-
Object.defineProperties(generator, {
|
|
626
|
-
activeCount: { get: () => activeCount },
|
|
627
|
-
pendingCount: { get: () => queue.size },
|
|
628
|
-
clearQueue: { value() {
|
|
629
|
-
if (!rejectOnClear) {
|
|
630
|
-
queue.clear();
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
const abortError = AbortSignal.abort().reason;
|
|
634
|
-
while (queue.size > 0) queue.dequeue().reject(abortError);
|
|
635
|
-
} },
|
|
636
|
-
concurrency: {
|
|
637
|
-
get: () => concurrency,
|
|
638
|
-
set(newConcurrency) {
|
|
639
|
-
validateConcurrency(newConcurrency);
|
|
640
|
-
concurrency = newConcurrency;
|
|
641
|
-
queueMicrotask(() => {
|
|
642
|
-
while (activeCount < concurrency && queue.size > 0) resumeNext();
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
},
|
|
646
|
-
map: { async value(iterable, function_) {
|
|
647
|
-
const promises = Array.from(iterable, (value, index) => this(function_, value, index));
|
|
648
|
-
return Promise.all(promises);
|
|
649
|
-
} }
|
|
650
|
-
});
|
|
651
|
-
return generator;
|
|
652
|
-
}
|
|
653
|
-
function validateConcurrency(concurrency) {
|
|
654
|
-
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
655
|
-
}
|
|
656
|
-
//#endregion
|
|
657
|
-
//#region src/FileProcessor.ts
|
|
658
|
-
function joinSources(file) {
|
|
659
|
-
return file.sources.map((item) => extractStringsFromNodes(item.nodes)).filter(Boolean).join("\n\n");
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* Converts a single file to a string using the registered parsers.
|
|
663
|
-
* Falls back to joining source values when no matching parser is found.
|
|
664
|
-
*
|
|
665
|
-
* @internal
|
|
666
|
-
*/
|
|
667
|
-
var FileProcessor = class {
|
|
668
|
-
#limit = pLimit(100);
|
|
669
|
-
async parse(file, { parsers, extension } = {}) {
|
|
670
|
-
const parseExtName = extension?.[file.extname] || void 0;
|
|
671
|
-
if (!parsers || !file.extname) return joinSources(file);
|
|
672
|
-
const parser = parsers.get(file.extname);
|
|
673
|
-
if (!parser) return joinSources(file);
|
|
674
|
-
return parser.parse(file, { extname: parseExtName });
|
|
675
|
-
}
|
|
676
|
-
async run(files, { parsers, mode = "sequential", extension, onStart, onEnd, onUpdate } = {}) {
|
|
677
|
-
await onStart?.(files);
|
|
678
|
-
const total = files.length;
|
|
679
|
-
let processed = 0;
|
|
680
|
-
const processOne = async (file) => {
|
|
681
|
-
const source = await this.parse(file, {
|
|
682
|
-
extension,
|
|
683
|
-
parsers
|
|
684
|
-
});
|
|
685
|
-
const currentProcessed = ++processed;
|
|
686
|
-
const percentage = currentProcessed / total * 100;
|
|
687
|
-
await onUpdate?.({
|
|
688
|
-
file,
|
|
689
|
-
source,
|
|
690
|
-
processed: currentProcessed,
|
|
691
|
-
percentage,
|
|
692
|
-
total
|
|
693
|
-
});
|
|
694
|
-
};
|
|
695
|
-
if (mode === "sequential") for (const file of files) await processOne(file);
|
|
696
|
-
else await Promise.all(files.map((file) => this.#limit(() => processOne(file))));
|
|
697
|
-
await onEnd?.(files);
|
|
698
|
-
return files;
|
|
699
|
-
}
|
|
700
|
-
};
|
|
108
|
+
//#region package.json
|
|
109
|
+
var version$1 = "5.0.0-beta.30";
|
|
701
110
|
//#endregion
|
|
702
111
|
//#region src/createStorage.ts
|
|
703
112
|
/**
|
|
704
|
-
*
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
*
|
|
113
|
+
* Defines a custom storage backend. The builder receives user options and
|
|
114
|
+
* returns a `Storage` implementation. Kubb ships with filesystem and
|
|
115
|
+
* in-memory storages — reach for this when you need to write generated files
|
|
116
|
+
* elsewhere (cloud storage, a database, a remote API).
|
|
708
117
|
*
|
|
709
|
-
* @
|
|
710
|
-
*
|
|
711
|
-
* @example
|
|
118
|
+
* @example In-memory storage (the built-in implementation)
|
|
712
119
|
* ```ts
|
|
713
120
|
* import { createStorage } from '@kubb/core'
|
|
714
121
|
*
|
|
715
122
|
* export const memoryStorage = createStorage(() => {
|
|
716
123
|
* const store = new Map<string, string>()
|
|
124
|
+
*
|
|
717
125
|
* return {
|
|
718
126
|
* name: 'memory',
|
|
719
|
-
* async hasItem(key) {
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
-
* async
|
|
127
|
+
* async hasItem(key) {
|
|
128
|
+
* return store.has(key)
|
|
129
|
+
* },
|
|
130
|
+
* async getItem(key) {
|
|
131
|
+
* return store.get(key) ?? null
|
|
132
|
+
* },
|
|
133
|
+
* async setItem(key, value) {
|
|
134
|
+
* store.set(key, value)
|
|
135
|
+
* },
|
|
136
|
+
* async removeItem(key) {
|
|
137
|
+
* store.delete(key)
|
|
138
|
+
* },
|
|
723
139
|
* async getKeys(base) {
|
|
724
140
|
* const keys = [...store.keys()]
|
|
725
141
|
* return base ? keys.filter((k) => k.startsWith(base)) : keys
|
|
726
142
|
* },
|
|
727
|
-
* async clear(base) {
|
|
143
|
+
* async clear(base) {
|
|
144
|
+
* if (!base) store.clear()
|
|
145
|
+
* },
|
|
728
146
|
* }
|
|
729
147
|
* })
|
|
730
|
-
*
|
|
731
|
-
* // Instantiate:
|
|
732
|
-
* const storage = memoryStorage()
|
|
733
148
|
* ```
|
|
734
149
|
*/
|
|
735
150
|
function createStorage(build) {
|
|
@@ -738,12 +153,6 @@ function createStorage(build) {
|
|
|
738
153
|
//#endregion
|
|
739
154
|
//#region src/storages/fsStorage.ts
|
|
740
155
|
/**
|
|
741
|
-
* Detects the filesystem error used to indicate that a path does not exist.
|
|
742
|
-
*/
|
|
743
|
-
function isMissingPathError(error) {
|
|
744
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
156
|
* Built-in filesystem storage driver.
|
|
748
157
|
*
|
|
749
158
|
* This is the default storage when no `storage` option is configured in the root config.
|
|
@@ -774,17 +183,15 @@ const fsStorage = createStorage(() => ({
|
|
|
774
183
|
try {
|
|
775
184
|
await access(resolve(key));
|
|
776
185
|
return true;
|
|
777
|
-
} catch (
|
|
778
|
-
|
|
779
|
-
throw new Error(`Failed to access storage item "${key}"`, { cause: error });
|
|
186
|
+
} catch (_error) {
|
|
187
|
+
return false;
|
|
780
188
|
}
|
|
781
189
|
},
|
|
782
190
|
async getItem(key) {
|
|
783
191
|
try {
|
|
784
192
|
return await readFile(resolve(key), "utf8");
|
|
785
|
-
} catch (
|
|
786
|
-
|
|
787
|
-
throw new Error(`Failed to read storage item "${key}"`, { cause: error });
|
|
193
|
+
} catch (_error) {
|
|
194
|
+
return null;
|
|
788
195
|
}
|
|
789
196
|
},
|
|
790
197
|
async setItem(key, value) {
|
|
@@ -794,23 +201,22 @@ const fsStorage = createStorage(() => ({
|
|
|
794
201
|
await rm(resolve(key), { force: true });
|
|
795
202
|
},
|
|
796
203
|
async getKeys(base) {
|
|
797
|
-
const keys = [];
|
|
798
204
|
const resolvedBase = resolve(base ?? process.cwd());
|
|
799
|
-
async function walk(dir, prefix) {
|
|
205
|
+
async function* walk(dir, prefix) {
|
|
800
206
|
let entries;
|
|
801
207
|
try {
|
|
802
208
|
entries = await readdir(dir, { withFileTypes: true });
|
|
803
|
-
} catch (
|
|
804
|
-
|
|
805
|
-
throw new Error(`Failed to list storage keys under "${resolvedBase}"`, { cause: error });
|
|
209
|
+
} catch (_error) {
|
|
210
|
+
return;
|
|
806
211
|
}
|
|
807
212
|
for (const entry of entries) {
|
|
808
213
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
809
|
-
if (entry.isDirectory())
|
|
810
|
-
else
|
|
214
|
+
if (entry.isDirectory()) yield* walk(join(dir, entry.name), rel);
|
|
215
|
+
else yield rel;
|
|
811
216
|
}
|
|
812
217
|
}
|
|
813
|
-
|
|
218
|
+
const keys = [];
|
|
219
|
+
for await (const key of walk(resolvedBase, "")) keys.push(key);
|
|
814
220
|
return keys;
|
|
815
221
|
},
|
|
816
222
|
async clear(base) {
|
|
@@ -819,475 +225,272 @@ const fsStorage = createStorage(() => ({
|
|
|
819
225
|
}
|
|
820
226
|
}));
|
|
821
227
|
//#endregion
|
|
822
|
-
//#region
|
|
823
|
-
var version$1 = "5.0.0-beta.3";
|
|
824
|
-
//#endregion
|
|
825
|
-
//#region src/utils/diagnostics.ts
|
|
228
|
+
//#region src/createKubb.ts
|
|
826
229
|
/**
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
230
|
+
* Builds a `Storage` view scoped to the file paths produced by the current build.
|
|
231
|
+
* Reads delegate to the underlying `storage` so source bytes stay where they were
|
|
232
|
+
* written; writes register the key so subsequent reads and `getKeys` are scoped
|
|
233
|
+
* to this build's output.
|
|
831
234
|
*/
|
|
832
|
-
function
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
` • Output: ${userConfig.output?.path || "not specified"}`,
|
|
860
|
-
` • Plugins: ${userConfig.plugins?.length || 0}`,
|
|
861
|
-
"Output Settings:",
|
|
862
|
-
` • Storage: ${userConfig.storage ? `custom(${userConfig.storage.name})` : userConfig.output?.write === false ? "disabled" : "filesystem (default)"}`,
|
|
863
|
-
` • Formatter: ${userConfig.output?.format || "none"}`,
|
|
864
|
-
` • Linter: ${userConfig.output?.lint || "none"}`,
|
|
865
|
-
"Environment:",
|
|
866
|
-
Object.entries(diagnosticInfo).map(([key, value]) => ` • ${key}: ${value}`).join("\n")
|
|
867
|
-
]
|
|
868
|
-
});
|
|
869
|
-
try {
|
|
870
|
-
if (isInputPath(userConfig) && !new URLPath(userConfig.input.path).isURL) {
|
|
871
|
-
await exists(userConfig.input.path);
|
|
872
|
-
await hooks.emit("kubb:debug", {
|
|
873
|
-
date: /* @__PURE__ */ new Date(),
|
|
874
|
-
logs: [`✓ Input file validated: ${userConfig.input.path}`]
|
|
875
|
-
});
|
|
876
|
-
}
|
|
877
|
-
} catch (caughtError) {
|
|
878
|
-
if (isInputPath(userConfig)) {
|
|
879
|
-
const error = caughtError;
|
|
880
|
-
throw new Error(`Cannot read file/URL defined in \`input.path\` or set with \`kubb generate PATH\` in the CLI of your Kubb config ${userConfig.input.path}`, { cause: error });
|
|
235
|
+
function createSourcesView(storage) {
|
|
236
|
+
const paths = /* @__PURE__ */ new Set();
|
|
237
|
+
return createStorage(() => ({
|
|
238
|
+
name: `${storage.name}:sources`,
|
|
239
|
+
async hasItem(key) {
|
|
240
|
+
return paths.has(key) && await storage.hasItem(key);
|
|
241
|
+
},
|
|
242
|
+
async getItem(key) {
|
|
243
|
+
return paths.has(key) ? storage.getItem(key) : null;
|
|
244
|
+
},
|
|
245
|
+
async setItem(key, value) {
|
|
246
|
+
paths.add(key);
|
|
247
|
+
await storage.setItem(key, value);
|
|
248
|
+
},
|
|
249
|
+
async removeItem(key) {
|
|
250
|
+
paths.delete(key);
|
|
251
|
+
await storage.removeItem(key);
|
|
252
|
+
},
|
|
253
|
+
async getKeys(base) {
|
|
254
|
+
if (!base) return [...paths];
|
|
255
|
+
const result = [];
|
|
256
|
+
for (const key of paths) if (key.startsWith(base)) result.push(key);
|
|
257
|
+
return result;
|
|
258
|
+
},
|
|
259
|
+
async clear() {
|
|
260
|
+
paths.clear();
|
|
261
|
+
await storage.clear();
|
|
881
262
|
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
|
|
263
|
+
}))();
|
|
264
|
+
}
|
|
265
|
+
function resolveConfig(userConfig) {
|
|
266
|
+
return {
|
|
885
267
|
...userConfig,
|
|
886
268
|
root: userConfig.root || process.cwd(),
|
|
887
269
|
parsers: userConfig.parsers ?? [],
|
|
888
|
-
adapter: userConfig.adapter,
|
|
889
270
|
output: {
|
|
890
271
|
format: false,
|
|
891
272
|
lint: false,
|
|
892
|
-
write: true,
|
|
893
273
|
extension: DEFAULT_EXTENSION,
|
|
894
274
|
defaultBanner: DEFAULT_BANNER,
|
|
895
275
|
...userConfig.output
|
|
896
276
|
},
|
|
277
|
+
storage: userConfig.storage ?? fsStorage(),
|
|
897
278
|
devtools: userConfig.devtools ? {
|
|
898
279
|
studioUrl: DEFAULT_STUDIO_URL,
|
|
899
280
|
...typeof userConfig.devtools === "boolean" ? {} : userConfig.devtools
|
|
900
281
|
} : void 0,
|
|
901
|
-
plugins: userConfig.plugins
|
|
282
|
+
plugins: userConfig.plugins ?? []
|
|
902
283
|
};
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
const driver = new PluginDriver(config, { hooks });
|
|
912
|
-
function registerMiddlewareHook(event, middlewareHooks) {
|
|
913
|
-
const handler = middlewareHooks[event];
|
|
914
|
-
if (handler) hooks.on(event, handler);
|
|
915
|
-
}
|
|
916
|
-
for (const middleware of config.middleware ?? []) for (const event of Object.keys(middleware.hooks)) registerMiddlewareHook(event, middleware.hooks);
|
|
917
|
-
const adapter = config.adapter;
|
|
918
|
-
if (!adapter) throw new Error("No adapter configured. Please provide an adapter in your kubb.config.ts.");
|
|
919
|
-
const source = inputToAdapterSource(config);
|
|
920
|
-
await hooks.emit("kubb:debug", {
|
|
921
|
-
date: /* @__PURE__ */ new Date(),
|
|
922
|
-
logs: [`Running adapter: ${adapter.name}`]
|
|
923
|
-
});
|
|
924
|
-
driver.adapter = adapter;
|
|
925
|
-
driver.inputNode = await adapter.parse(source);
|
|
926
|
-
await hooks.emit("kubb:debug", {
|
|
927
|
-
date: /* @__PURE__ */ new Date(),
|
|
928
|
-
logs: [
|
|
929
|
-
`✓ Adapter '${adapter.name}' resolved InputNode`,
|
|
930
|
-
` • Schemas: ${driver.inputNode.schemas.length}`,
|
|
931
|
-
` • Operations: ${driver.inputNode.operations.length}`
|
|
932
|
-
]
|
|
933
|
-
});
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Returns a snapshot of the current runtime environment.
|
|
287
|
+
*
|
|
288
|
+
* Useful for attaching context to debug logs and error reports so that
|
|
289
|
+
* issues can be reproduced without manual information gathering.
|
|
290
|
+
*/
|
|
291
|
+
function getDiagnosticInfo() {
|
|
934
292
|
return {
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
293
|
+
nodeVersion: version,
|
|
294
|
+
KubbVersion: version$1,
|
|
295
|
+
platform: process.platform,
|
|
296
|
+
arch: process.arch,
|
|
297
|
+
cwd: process.cwd()
|
|
940
298
|
};
|
|
941
299
|
}
|
|
300
|
+
function isInputPath(config) {
|
|
301
|
+
return typeof config?.input === "object" && config.input !== null && "path" in config.input;
|
|
302
|
+
}
|
|
942
303
|
/**
|
|
943
|
-
*
|
|
944
|
-
* (`
|
|
304
|
+
* Kubb code-generation instance bound to a single config entry. Resolves the user
|
|
305
|
+
* config during `setup()` and shares `hooks`, `storage`, `driver`, and `config` across
|
|
306
|
+
* the `setup → build` lifecycle.
|
|
307
|
+
*
|
|
308
|
+
* Attach event listeners to `.hooks` before calling `setup()` or `build()`.
|
|
945
309
|
*
|
|
946
|
-
*
|
|
947
|
-
*
|
|
948
|
-
*
|
|
949
|
-
*
|
|
950
|
-
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```ts
|
|
312
|
+
* const kubb = createKubb(userConfig)
|
|
313
|
+
* kubb.hooks.on('kubb:plugin:end', ({ plugin, duration }) => console.log(plugin.name, duration))
|
|
314
|
+
* const { files, failedPlugins } = await kubb.safeBuild()
|
|
315
|
+
* ```
|
|
951
316
|
*/
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
317
|
+
var Kubb = class {
|
|
318
|
+
hooks;
|
|
319
|
+
#userConfig;
|
|
320
|
+
#config = null;
|
|
321
|
+
#driver = null;
|
|
322
|
+
#storage = null;
|
|
323
|
+
constructor(userConfig, options = {}) {
|
|
324
|
+
this.#userConfig = userConfig;
|
|
325
|
+
this.hooks = options.hooks ?? new AsyncEventEmitter();
|
|
326
|
+
}
|
|
327
|
+
get storage() {
|
|
328
|
+
if (!this.#storage) throw new Error("[kubb] setup() must be called before accessing storage");
|
|
329
|
+
return this.#storage;
|
|
330
|
+
}
|
|
331
|
+
get driver() {
|
|
332
|
+
if (!this.#driver) throw new Error("[kubb] setup() must be called before accessing driver");
|
|
333
|
+
return this.#driver;
|
|
334
|
+
}
|
|
335
|
+
get config() {
|
|
336
|
+
if (!this.#config) throw new Error("[kubb] setup() must be called before accessing config");
|
|
337
|
+
return this.#config;
|
|
958
338
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
"
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
if (hasOperationBasedIncludes && !hasSchemaNameIncludes) allowedSchemaNames = collectUsedSchemaNames(inputNode.operations.filter((op) => resolver.resolveOptions(op, {
|
|
976
|
-
options: plugin.options,
|
|
977
|
-
exclude,
|
|
978
|
-
include,
|
|
979
|
-
override
|
|
980
|
-
}) !== null), inputNode.schemas);
|
|
981
|
-
await walk(inputNode, {
|
|
982
|
-
depth: "shallow",
|
|
983
|
-
async schema(node) {
|
|
984
|
-
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node;
|
|
985
|
-
if (allowedSchemaNames !== void 0 && transformedNode.name && !allowedSchemaNames.has(transformedNode.name)) return;
|
|
986
|
-
const options = resolver.resolveOptions(transformedNode, {
|
|
987
|
-
options: plugin.options,
|
|
988
|
-
exclude,
|
|
989
|
-
include,
|
|
990
|
-
override
|
|
339
|
+
/**
|
|
340
|
+
* Resolves config and initializes the driver. `build()` calls this automatically.
|
|
341
|
+
*/
|
|
342
|
+
async setup() {
|
|
343
|
+
const config = resolveConfig(this.#userConfig);
|
|
344
|
+
const driver = new KubbDriver(config, { hooks: this.hooks });
|
|
345
|
+
const storage = createSourcesView(config.storage);
|
|
346
|
+
await this.hooks.emit("kubb:debug", {
|
|
347
|
+
date: /* @__PURE__ */ new Date(),
|
|
348
|
+
logs: this.#configLogs(config)
|
|
349
|
+
});
|
|
350
|
+
if (isInputPath(this.#userConfig) && !new URLPath(this.#userConfig.input.path).isURL) try {
|
|
351
|
+
await exists(this.#userConfig.input.path);
|
|
352
|
+
await this.hooks.emit("kubb:debug", {
|
|
353
|
+
date: /* @__PURE__ */ new Date(),
|
|
354
|
+
logs: [`✓ Input file validated: ${this.#userConfig.input.path}`]
|
|
991
355
|
});
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
await applyHookResult(await gen.schema(transformedNode, ctx), driver, resolveRenderer(gen));
|
|
1000
|
-
}
|
|
1001
|
-
await driver.hooks.emit("kubb:generate:schema", transformedNode, ctx);
|
|
1002
|
-
},
|
|
1003
|
-
async operation(node) {
|
|
1004
|
-
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node;
|
|
1005
|
-
const options = resolver.resolveOptions(transformedNode, {
|
|
1006
|
-
options: plugin.options,
|
|
1007
|
-
exclude,
|
|
1008
|
-
include,
|
|
1009
|
-
override
|
|
356
|
+
} catch (caughtError) {
|
|
357
|
+
throw new Error(`Cannot read file/URL defined in \`input.path\` or set with \`kubb generate PATH\` in the CLI of your Kubb config ${this.#userConfig.input.path}`, { cause: caughtError });
|
|
358
|
+
}
|
|
359
|
+
if (config.output.clean) {
|
|
360
|
+
await this.hooks.emit("kubb:debug", {
|
|
361
|
+
date: /* @__PURE__ */ new Date(),
|
|
362
|
+
logs: ["Cleaning output directories", ` • Output: ${config.output.path}`]
|
|
1010
363
|
});
|
|
1011
|
-
|
|
1012
|
-
collectedOperations.push(transformedNode);
|
|
1013
|
-
const ctx = {
|
|
1014
|
-
...generatorContext,
|
|
1015
|
-
options
|
|
1016
|
-
};
|
|
1017
|
-
for (const gen of generators) {
|
|
1018
|
-
if (!gen.operation) continue;
|
|
1019
|
-
await applyHookResult(await gen.operation(transformedNode, ctx), driver, resolveRenderer(gen));
|
|
1020
|
-
}
|
|
1021
|
-
await driver.hooks.emit("kubb:generate:operation", transformedNode, ctx);
|
|
1022
|
-
}
|
|
364
|
+
await config.storage.clear(resolve(config.root, config.output.path));
|
|
1023
365
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
366
|
+
await driver.setup();
|
|
367
|
+
this.#config = config;
|
|
368
|
+
this.#driver = driver;
|
|
369
|
+
this.#storage = storage;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Runs the full pipeline and throws on any plugin error.
|
|
373
|
+
* Automatically calls `setup()` if needed.
|
|
374
|
+
*/
|
|
375
|
+
async build() {
|
|
376
|
+
const out = await this.safeBuild();
|
|
377
|
+
if (out.error) throw out.error;
|
|
378
|
+
if (out.failedPlugins.size > 0) {
|
|
379
|
+
const errors = [...out.failedPlugins].map(({ error }) => error);
|
|
380
|
+
throw new BuildError(`Build Error with ${out.failedPlugins.size} failed plugins`, { errors });
|
|
1033
381
|
}
|
|
1034
|
-
|
|
382
|
+
return out;
|
|
1035
383
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
await hooks.emit("kubb:debug", {
|
|
1061
|
-
date: timestamp,
|
|
1062
|
-
logs: ["Starting plugin...", ` • Plugin Name: ${plugin.name}`]
|
|
1063
|
-
});
|
|
1064
|
-
if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) await runPluginAstHooks(plugin, context);
|
|
1065
|
-
const duration = getElapsedMs(hrStart);
|
|
1066
|
-
pluginTimings.set(plugin.name, duration);
|
|
1067
|
-
await hooks.emit("kubb:plugin:end", {
|
|
1068
|
-
plugin,
|
|
1069
|
-
duration,
|
|
1070
|
-
success: true,
|
|
1071
|
-
config,
|
|
1072
|
-
get files() {
|
|
1073
|
-
return driver.fileManager.files;
|
|
1074
|
-
},
|
|
1075
|
-
upsertFile: (...files) => driver.fileManager.upsert(...files)
|
|
1076
|
-
});
|
|
1077
|
-
await hooks.emit("kubb:debug", {
|
|
1078
|
-
date: /* @__PURE__ */ new Date(),
|
|
1079
|
-
logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
|
|
1080
|
-
});
|
|
1081
|
-
} catch (caughtError) {
|
|
1082
|
-
const error = caughtError;
|
|
1083
|
-
const errorTimestamp = /* @__PURE__ */ new Date();
|
|
1084
|
-
const duration = getElapsedMs(hrStart);
|
|
1085
|
-
await hooks.emit("kubb:plugin:end", {
|
|
1086
|
-
plugin,
|
|
1087
|
-
duration,
|
|
1088
|
-
success: false,
|
|
1089
|
-
error,
|
|
1090
|
-
config,
|
|
1091
|
-
get files() {
|
|
1092
|
-
return driver.fileManager.files;
|
|
1093
|
-
},
|
|
1094
|
-
upsertFile: (...files) => driver.fileManager.upsert(...files)
|
|
1095
|
-
});
|
|
1096
|
-
await hooks.emit("kubb:debug", {
|
|
1097
|
-
date: errorTimestamp,
|
|
1098
|
-
logs: [
|
|
1099
|
-
"✗ Plugin start failed",
|
|
1100
|
-
` • Plugin Name: ${plugin.name}`,
|
|
1101
|
-
` • Error: ${error.constructor.name} - ${error.message}`,
|
|
1102
|
-
" • Stack Trace:",
|
|
1103
|
-
error.stack || "No stack trace available"
|
|
1104
|
-
]
|
|
1105
|
-
});
|
|
1106
|
-
failedPlugins.add({
|
|
1107
|
-
plugin,
|
|
1108
|
-
error
|
|
1109
|
-
});
|
|
1110
|
-
}
|
|
384
|
+
/**
|
|
385
|
+
* Runs the full pipeline and captures errors in `BuildOutput` instead of throwing.
|
|
386
|
+
* Automatically calls `setup()` if needed.
|
|
387
|
+
*/
|
|
388
|
+
async safeBuild() {
|
|
389
|
+
try {
|
|
390
|
+
var _usingCtx$1 = _usingCtx();
|
|
391
|
+
if (!this.#driver) await this.setup();
|
|
392
|
+
const cleanup = _usingCtx$1.u(this);
|
|
393
|
+
const driver = cleanup.driver;
|
|
394
|
+
const storage = cleanup.storage;
|
|
395
|
+
const { failedPlugins, pluginTimings, error } = await driver.run({ storage });
|
|
396
|
+
return {
|
|
397
|
+
failedPlugins,
|
|
398
|
+
files: driver.fileManager.files,
|
|
399
|
+
driver,
|
|
400
|
+
pluginTimings,
|
|
401
|
+
storage,
|
|
402
|
+
...error ? { error } : {}
|
|
403
|
+
};
|
|
404
|
+
} catch (_) {
|
|
405
|
+
_usingCtx$1.e = _;
|
|
406
|
+
} finally {
|
|
407
|
+
_usingCtx$1.d();
|
|
1111
408
|
}
|
|
1112
|
-
await hooks.emit("kubb:plugins:end", {
|
|
1113
|
-
config,
|
|
1114
|
-
get files() {
|
|
1115
|
-
return driver.fileManager.files;
|
|
1116
|
-
},
|
|
1117
|
-
upsertFile: (...files) => driver.fileManager.upsert(...files)
|
|
1118
|
-
});
|
|
1119
|
-
const files = driver.fileManager.files;
|
|
1120
|
-
const parsersMap = /* @__PURE__ */ new Map();
|
|
1121
|
-
for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
|
|
1122
|
-
const fileProcessor = new FileProcessor();
|
|
1123
|
-
await hooks.emit("kubb:debug", {
|
|
1124
|
-
date: /* @__PURE__ */ new Date(),
|
|
1125
|
-
logs: [`Writing ${files.length} files...`]
|
|
1126
|
-
});
|
|
1127
|
-
await fileProcessor.run(files, {
|
|
1128
|
-
parsers: parsersMap,
|
|
1129
|
-
extension: config.output.extension,
|
|
1130
|
-
onStart: async (processingFiles) => {
|
|
1131
|
-
await hooks.emit("kubb:files:processing:start", { files: processingFiles });
|
|
1132
|
-
},
|
|
1133
|
-
onUpdate: async ({ file, source, processed, total, percentage }) => {
|
|
1134
|
-
await hooks.emit("kubb:file:processing:update", {
|
|
1135
|
-
file,
|
|
1136
|
-
source,
|
|
1137
|
-
processed,
|
|
1138
|
-
total,
|
|
1139
|
-
percentage,
|
|
1140
|
-
config
|
|
1141
|
-
});
|
|
1142
|
-
if (source) {
|
|
1143
|
-
await storage?.setItem(file.path, source);
|
|
1144
|
-
sources.set(file.path, source);
|
|
1145
|
-
}
|
|
1146
|
-
},
|
|
1147
|
-
onEnd: async (processedFiles) => {
|
|
1148
|
-
await hooks.emit("kubb:files:processing:end", { files: processedFiles });
|
|
1149
|
-
await hooks.emit("kubb:debug", {
|
|
1150
|
-
date: /* @__PURE__ */ new Date(),
|
|
1151
|
-
logs: [`✓ File write process completed for ${processedFiles.length} files`]
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
});
|
|
1155
|
-
await hooks.emit("kubb:build:end", {
|
|
1156
|
-
files,
|
|
1157
|
-
config,
|
|
1158
|
-
outputDir: resolve(config.root, config.output.path)
|
|
1159
|
-
});
|
|
1160
|
-
return {
|
|
1161
|
-
failedPlugins,
|
|
1162
|
-
files,
|
|
1163
|
-
driver,
|
|
1164
|
-
pluginTimings,
|
|
1165
|
-
sources
|
|
1166
|
-
};
|
|
1167
|
-
} catch (error) {
|
|
1168
|
-
return {
|
|
1169
|
-
failedPlugins,
|
|
1170
|
-
files: [],
|
|
1171
|
-
driver,
|
|
1172
|
-
pluginTimings,
|
|
1173
|
-
error,
|
|
1174
|
-
sources
|
|
1175
|
-
};
|
|
1176
|
-
} finally {
|
|
1177
|
-
driver.dispose();
|
|
1178
409
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
const { files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(setupResult);
|
|
1182
|
-
if (error) throw error;
|
|
1183
|
-
if (failedPlugins.size > 0) {
|
|
1184
|
-
const errors = [...failedPlugins].map(({ error }) => error);
|
|
1185
|
-
throw new BuildError(`Build Error with ${failedPlugins.size} failed plugins`, { errors });
|
|
410
|
+
dispose() {
|
|
411
|
+
this.#driver?.dispose();
|
|
1186
412
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
return {
|
|
1210
|
-
type: "path",
|
|
1211
|
-
path: resolve(config.root, config.input.path)
|
|
1212
|
-
};
|
|
1213
|
-
}
|
|
413
|
+
[Symbol.dispose]() {
|
|
414
|
+
this.dispose();
|
|
415
|
+
}
|
|
416
|
+
#configLogs(config) {
|
|
417
|
+
const u = this.#userConfig;
|
|
418
|
+
const diag = getDiagnosticInfo();
|
|
419
|
+
return [
|
|
420
|
+
"Configuration:",
|
|
421
|
+
` • Name: ${u.name || "unnamed"}`,
|
|
422
|
+
` • Root: ${u.root || process.cwd()}`,
|
|
423
|
+
` • Output: ${u.output?.path || "not specified"}`,
|
|
424
|
+
` • Plugins: ${u.plugins?.length || 0}`,
|
|
425
|
+
"Output Settings:",
|
|
426
|
+
` • Storage: ${config.storage.name}`,
|
|
427
|
+
` • Formatter: ${u.output?.format || "none"}`,
|
|
428
|
+
` • Linter: ${u.output?.lint || "none"}`,
|
|
429
|
+
`Running adapter: ${config.adapter?.name || "none"}`,
|
|
430
|
+
"Environment:",
|
|
431
|
+
Object.entries(diag).map(([key, value]) => ` • ${key}: ${value}`).join("\n")
|
|
432
|
+
];
|
|
433
|
+
}
|
|
434
|
+
};
|
|
1214
435
|
/**
|
|
1215
|
-
*
|
|
1216
|
-
*
|
|
1217
|
-
* Accepts a user-facing config shape and resolves it to a full {@link Config} during
|
|
1218
|
-
* `setup()`. The instance then holds shared state (`hooks`, `sources`, `driver`, `config`)
|
|
1219
|
-
* across the `setup → build` lifecycle. Attach event listeners to `kubb.hooks` before
|
|
1220
|
-
* calling `setup()` or `build()`.
|
|
436
|
+
* Constructs a {@link Kubb} build orchestrator from a user config. Equivalent
|
|
437
|
+
* to `new Kubb(userConfig, options)` and the canonical public entry point.
|
|
1221
438
|
*
|
|
1222
439
|
* @example
|
|
1223
440
|
* ```ts
|
|
1224
|
-
*
|
|
441
|
+
* import { createKubb } from '@kubb/core'
|
|
442
|
+
* import { adapterOas } from '@kubb/adapter-oas'
|
|
443
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1225
444
|
*
|
|
1226
|
-
* kubb
|
|
1227
|
-
*
|
|
445
|
+
* const kubb = createKubb({
|
|
446
|
+
* input: { path: './petStore.yaml' },
|
|
447
|
+
* output: { path: './src/gen' },
|
|
448
|
+
* adapter: adapterOas(),
|
|
449
|
+
* plugins: [pluginTs()],
|
|
1228
450
|
* })
|
|
1229
451
|
*
|
|
1230
|
-
*
|
|
452
|
+
* await kubb.build()
|
|
1231
453
|
* ```
|
|
1232
454
|
*/
|
|
1233
455
|
function createKubb(userConfig, options = {}) {
|
|
1234
|
-
|
|
1235
|
-
let setupResult;
|
|
1236
|
-
const instance = {
|
|
1237
|
-
get hooks() {
|
|
1238
|
-
return hooks;
|
|
1239
|
-
},
|
|
1240
|
-
get sources() {
|
|
1241
|
-
return setupResult?.sources ?? /* @__PURE__ */ new Map();
|
|
1242
|
-
},
|
|
1243
|
-
get driver() {
|
|
1244
|
-
return setupResult?.driver;
|
|
1245
|
-
},
|
|
1246
|
-
get config() {
|
|
1247
|
-
return setupResult?.config;
|
|
1248
|
-
},
|
|
1249
|
-
async setup() {
|
|
1250
|
-
setupResult = await setup(userConfig, { hooks });
|
|
1251
|
-
},
|
|
1252
|
-
async build() {
|
|
1253
|
-
if (!setupResult) await instance.setup();
|
|
1254
|
-
return build(setupResult);
|
|
1255
|
-
},
|
|
1256
|
-
async safeBuild() {
|
|
1257
|
-
if (!setupResult) await instance.setup();
|
|
1258
|
-
return safeBuild(setupResult);
|
|
1259
|
-
}
|
|
1260
|
-
};
|
|
1261
|
-
return instance;
|
|
456
|
+
return new Kubb(userConfig, options);
|
|
1262
457
|
}
|
|
1263
458
|
//#endregion
|
|
1264
459
|
//#region src/createRenderer.ts
|
|
1265
460
|
/**
|
|
1266
|
-
*
|
|
461
|
+
* Defines a renderer factory. Renderers turn the generator's return value
|
|
462
|
+
* (JSX, a template string, a tree of any shape) into `FileNode`s that get
|
|
463
|
+
* written to disk.
|
|
1267
464
|
*
|
|
1268
|
-
*
|
|
1269
|
-
* renderer
|
|
1270
|
-
* to
|
|
465
|
+
* Use this to support output formats beyond JSX — for instance, a Handlebars
|
|
466
|
+
* renderer, a string-template renderer, or a renderer that writes binary
|
|
467
|
+
* files. Plugins and generators pick the renderer to use via the `renderer`
|
|
468
|
+
* field on `defineGenerator`.
|
|
1271
469
|
*
|
|
1272
|
-
* @example
|
|
470
|
+
* @example A minimal renderer that wraps a custom runtime
|
|
1273
471
|
* ```ts
|
|
1274
|
-
*
|
|
1275
|
-
*
|
|
1276
|
-
*
|
|
472
|
+
* import { createRenderer } from '@kubb/core'
|
|
473
|
+
*
|
|
474
|
+
* export const myRenderer = createRenderer(() => {
|
|
475
|
+
* const runtime = new MyRuntime()
|
|
1277
476
|
* return {
|
|
1278
|
-
* async render(element) {
|
|
1279
|
-
*
|
|
1280
|
-
*
|
|
477
|
+
* async render(element) {
|
|
478
|
+
* await runtime.render(element)
|
|
479
|
+
* },
|
|
480
|
+
* get files() {
|
|
481
|
+
* return runtime.files
|
|
482
|
+
* },
|
|
483
|
+
* dispose() {
|
|
484
|
+
* runtime.dispose()
|
|
485
|
+
* },
|
|
486
|
+
* unmount(error) {
|
|
487
|
+
* runtime.dispose(error)
|
|
488
|
+
* },
|
|
489
|
+
* [Symbol.dispose]() {
|
|
490
|
+
* this.dispose()
|
|
491
|
+
* },
|
|
1281
492
|
* }
|
|
1282
493
|
* })
|
|
1283
|
-
*
|
|
1284
|
-
* // packages/plugin-zod/src/generators/zodGenerator.tsx
|
|
1285
|
-
* import { jsxRenderer } from '@kubb/renderer-jsx'
|
|
1286
|
-
* export const zodGenerator = defineGenerator<PluginZod>({
|
|
1287
|
-
* name: 'zod',
|
|
1288
|
-
* renderer: jsxRenderer,
|
|
1289
|
-
* schema(node, options) { return <File ...>...</File> },
|
|
1290
|
-
* })
|
|
1291
494
|
* ```
|
|
1292
495
|
*/
|
|
1293
496
|
function createRenderer(factory) {
|
|
@@ -1296,9 +499,32 @@ function createRenderer(factory) {
|
|
|
1296
499
|
//#endregion
|
|
1297
500
|
//#region src/defineGenerator.ts
|
|
1298
501
|
/**
|
|
1299
|
-
* Defines a generator
|
|
1300
|
-
*
|
|
1301
|
-
*
|
|
502
|
+
* Defines a generator: a unit of work that runs during the plugin's AST walk
|
|
503
|
+
* and produces files. Plugins register generators via `ctx.addGenerator()`
|
|
504
|
+
* inside `kubb:plugin:setup`.
|
|
505
|
+
*
|
|
506
|
+
* The returned object is the input as-is, but with `this` types preserved so
|
|
507
|
+
* `schema`/`operation`/`operations` methods are correctly typed against the
|
|
508
|
+
* plugin's `PluginFactoryOptions`. Renderer elements and `FileNode[]` returns
|
|
509
|
+
* are both handled by the runtime — pick whichever style fits.
|
|
510
|
+
*
|
|
511
|
+
* @example JSX-based schema generator
|
|
512
|
+
* ```tsx
|
|
513
|
+
* import { defineGenerator } from '@kubb/core'
|
|
514
|
+
* import { jsxRenderer } from '@kubb/renderer-jsx'
|
|
515
|
+
*
|
|
516
|
+
* export const typeGenerator = defineGenerator({
|
|
517
|
+
* name: 'typescript',
|
|
518
|
+
* renderer: jsxRenderer,
|
|
519
|
+
* schema(node, ctx) {
|
|
520
|
+
* return (
|
|
521
|
+
* <File path={`${ctx.root}/${node.name}.ts`}>
|
|
522
|
+
* <Type node={node} resolver={ctx.resolver} />
|
|
523
|
+
* </File>
|
|
524
|
+
* )
|
|
525
|
+
* },
|
|
526
|
+
* })
|
|
527
|
+
* ```
|
|
1302
528
|
*/
|
|
1303
529
|
function defineGenerator(generator) {
|
|
1304
530
|
return generator;
|
|
@@ -1306,15 +532,33 @@ function defineGenerator(generator) {
|
|
|
1306
532
|
//#endregion
|
|
1307
533
|
//#region src/defineLogger.ts
|
|
1308
534
|
/**
|
|
1309
|
-
*
|
|
535
|
+
* Defines a typed logger. Use the second type parameter to declare a return
|
|
536
|
+
* value from `install`, which is handy when the logger exposes a sink factory
|
|
537
|
+
* or cleanup callback to the caller.
|
|
1310
538
|
*
|
|
1311
|
-
* @example
|
|
539
|
+
* @example Basic logger
|
|
1312
540
|
* ```ts
|
|
541
|
+
* import { defineLogger } from '@kubb/core'
|
|
542
|
+
*
|
|
1313
543
|
* export const myLogger = defineLogger({
|
|
1314
544
|
* name: 'my-logger',
|
|
1315
|
-
* install(context
|
|
1316
|
-
* context.on('kubb:info', (message) => console.log('ℹ', message))
|
|
1317
|
-
* context.on('kubb:error', (error) => console.error('✗', error.message))
|
|
545
|
+
* install(context) {
|
|
546
|
+
* context.on('kubb:info', ({ message }) => console.log('ℹ', message))
|
|
547
|
+
* context.on('kubb:error', ({ error }) => console.error('✗', error.message))
|
|
548
|
+
* },
|
|
549
|
+
* })
|
|
550
|
+
* ```
|
|
551
|
+
*
|
|
552
|
+
* @example Logger that returns a hook sink factory
|
|
553
|
+
* ```ts
|
|
554
|
+
* import { defineLogger, type LoggerOptions } from '@kubb/core'
|
|
555
|
+
* import type { HookSinkFactory } from './sinks'
|
|
556
|
+
*
|
|
557
|
+
* export const myLogger = defineLogger<LoggerOptions, HookSinkFactory>({
|
|
558
|
+
* name: 'my-logger',
|
|
559
|
+
* install(context) {
|
|
560
|
+
* // … register event handlers …
|
|
561
|
+
* return () => ({ onStdout: console.log })
|
|
1318
562
|
* },
|
|
1319
563
|
* })
|
|
1320
564
|
* ```
|
|
@@ -1325,18 +569,17 @@ function defineLogger(logger) {
|
|
|
1325
569
|
//#endregion
|
|
1326
570
|
//#region src/defineMiddleware.ts
|
|
1327
571
|
/**
|
|
1328
|
-
* Creates a middleware factory
|
|
572
|
+
* Creates a middleware factory. Middleware fires after every plugin handler
|
|
573
|
+
* for the same event, which makes it the natural place for post-processing
|
|
574
|
+
* (barrel files, lint runs, audit logs).
|
|
1329
575
|
*
|
|
1330
|
-
*
|
|
1331
|
-
*
|
|
576
|
+
* Per-build state belongs inside the factory closure so each `createKubb`
|
|
577
|
+
* invocation gets its own isolated instance.
|
|
1332
578
|
*
|
|
1333
|
-
* @
|
|
1334
|
-
*
|
|
1335
|
-
* @example
|
|
579
|
+
* @example Stateless middleware
|
|
1336
580
|
* ```ts
|
|
1337
581
|
* import { defineMiddleware } from '@kubb/core'
|
|
1338
582
|
*
|
|
1339
|
-
* // Stateless middleware
|
|
1340
583
|
* export const logMiddleware = defineMiddleware(() => ({
|
|
1341
584
|
* name: 'log-middleware',
|
|
1342
585
|
* hooks: {
|
|
@@ -1345,8 +588,12 @@ function defineLogger(logger) {
|
|
|
1345
588
|
* },
|
|
1346
589
|
* },
|
|
1347
590
|
* }))
|
|
591
|
+
* ```
|
|
592
|
+
*
|
|
593
|
+
* @example Middleware with options and per-build state
|
|
594
|
+
* ```ts
|
|
595
|
+
* import { defineMiddleware } from '@kubb/core'
|
|
1348
596
|
*
|
|
1349
|
-
* // Middleware with options and per-build state
|
|
1350
597
|
* export const prefixMiddleware = defineMiddleware((options: { prefix: string } = { prefix: '' }) => {
|
|
1351
598
|
* const seen = new Set<string>()
|
|
1352
599
|
* return {
|
|
@@ -1366,20 +613,23 @@ function defineMiddleware(factory) {
|
|
|
1366
613
|
//#endregion
|
|
1367
614
|
//#region src/defineParser.ts
|
|
1368
615
|
/**
|
|
1369
|
-
* Defines a parser with type
|
|
1370
|
-
*
|
|
1371
|
-
* @note Call the returned factory with optional options to instantiate the parser.
|
|
616
|
+
* Defines a parser with type-safe `this`. Used to register handlers for new
|
|
617
|
+
* file extensions or to plug a non-TypeScript output into the build.
|
|
1372
618
|
*
|
|
1373
619
|
* @example
|
|
1374
620
|
* ```ts
|
|
1375
|
-
* import { defineParser } from '@kubb/core'
|
|
621
|
+
* import { defineParser, ast } from '@kubb/core'
|
|
1376
622
|
*
|
|
1377
623
|
* export const jsonParser = defineParser({
|
|
1378
624
|
* name: 'json',
|
|
1379
625
|
* extNames: ['.json'],
|
|
1380
626
|
* parse(file) {
|
|
1381
|
-
*
|
|
1382
|
-
*
|
|
627
|
+
* return file.sources
|
|
628
|
+
* .map((source) => ast.extractStringsFromNodes(source.nodes ?? []))
|
|
629
|
+
* .join('\n')
|
|
630
|
+
* },
|
|
631
|
+
* print(...nodes) {
|
|
632
|
+
* return nodes.map(String).join('\n')
|
|
1383
633
|
* },
|
|
1384
634
|
* })
|
|
1385
635
|
* ```
|
|
@@ -1388,34 +638,6 @@ function defineParser(parser) {
|
|
|
1388
638
|
return parser;
|
|
1389
639
|
}
|
|
1390
640
|
//#endregion
|
|
1391
|
-
//#region src/definePlugin.ts
|
|
1392
|
-
/**
|
|
1393
|
-
* Wraps a factory function and returns a typed `Plugin` with lifecycle handlers grouped under `hooks`.
|
|
1394
|
-
*
|
|
1395
|
-
* Handlers live in a single `hooks` object (inspired by Astro integrations).
|
|
1396
|
-
* All lifecycle events from `KubbHooks` are available for subscription.
|
|
1397
|
-
*
|
|
1398
|
-
* @note For real plugins, use a `PluginFactoryOptions` type parameter to get type-safe context in `kubb:plugin:setup`.
|
|
1399
|
-
* Plugin names should follow the convention `plugin-<feature>` (e.g., `plugin-react-query`, `plugin-zod`).
|
|
1400
|
-
*
|
|
1401
|
-
* @example
|
|
1402
|
-
* ```ts
|
|
1403
|
-
* import { definePlugin } from '@kubb/core'
|
|
1404
|
-
*
|
|
1405
|
-
* export const pluginTs = definePlugin((options: { prefix?: string } = {}) => ({
|
|
1406
|
-
* name: 'plugin-ts',
|
|
1407
|
-
* hooks: {
|
|
1408
|
-
* 'kubb:plugin:setup'(ctx) {
|
|
1409
|
-
* ctx.setResolver(resolverTs)
|
|
1410
|
-
* },
|
|
1411
|
-
* },
|
|
1412
|
-
* }))
|
|
1413
|
-
* ```
|
|
1414
|
-
*/
|
|
1415
|
-
function definePlugin(factory) {
|
|
1416
|
-
return (options) => factory(options ?? {});
|
|
1417
|
-
}
|
|
1418
|
-
//#endregion
|
|
1419
641
|
//#region src/storages/memoryStorage.ts
|
|
1420
642
|
/**
|
|
1421
643
|
* In-memory storage driver. Useful for testing and dry-run scenarios where
|
|
@@ -1466,6 +688,6 @@ const memoryStorage = createStorage(() => {
|
|
|
1466
688
|
};
|
|
1467
689
|
});
|
|
1468
690
|
//#endregion
|
|
1469
|
-
export { AsyncEventEmitter, FileManager, FileProcessor,
|
|
691
|
+
export { AsyncEventEmitter, FileManager, FileProcessor, KubbDriver, URLPath, ast, createAdapter, createKubb, createRenderer, createStorage, defineGenerator, defineLogger, defineMiddleware, defineParser, definePlugin, defineResolver, fsStorage, isInputPath, logLevel, memoryStorage };
|
|
1470
692
|
|
|
1471
693
|
//# sourceMappingURL=index.js.map
|