@kubb/core 5.0.0-beta.84 → 5.0.0-beta.86
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/dist/{diagnostics-DuaXn2bT.d.ts → Diagnostics-aWJg-H2d.d.ts} +344 -440
- package/dist/index.cjs +850 -1122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +38 -94
- package/dist/index.js +812 -1084
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +7 -14
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/mocks.js +3 -10
- package/dist/mocks.js.map +1 -1
- package/dist/{memoryStorage-DQ6qXJ8o.cjs → usingCtx-BN2OKJRx.cjs} +182 -520
- package/dist/usingCtx-BN2OKJRx.cjs.map +1 -0
- package/dist/{memoryStorage-BjUIqpYE.js → usingCtx-Cnrm3TcM.js} +179 -481
- package/dist/usingCtx-Cnrm3TcM.js.map +1 -0
- package/package.json +2 -2
- package/dist/memoryStorage-BjUIqpYE.js.map +0 -1
- package/dist/memoryStorage-DQ6qXJ8o.cjs.map +0 -1
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
import "./rolldown-runtime-C0LytTxp.js";
|
|
2
|
-
import { EventEmitter } from "node:events";
|
|
3
2
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
3
|
import { dirname, resolve } from "node:path";
|
|
5
4
|
import { ast, extractStringsFromNodes } from "@kubb/ast";
|
|
5
|
+
import { EventEmitter } from "node:events";
|
|
6
|
+
//#region ../../internals/utils/src/casing.ts
|
|
7
|
+
/**
|
|
8
|
+
* Shared implementation for camelCase and PascalCase conversion.
|
|
9
|
+
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
10
|
+
* and capitalizes each word according to `pascal`.
|
|
11
|
+
*
|
|
12
|
+
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
13
|
+
*/
|
|
14
|
+
function toCamelOrPascal(text, pascal) {
|
|
15
|
+
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
16
|
+
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
17
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
18
|
+
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Converts `text` to camelCase.
|
|
22
|
+
*
|
|
23
|
+
* @example Word boundaries
|
|
24
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
25
|
+
*
|
|
26
|
+
* @example With a prefix
|
|
27
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
28
|
+
*/
|
|
29
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
30
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
6
33
|
//#region ../../internals/utils/src/errors.ts
|
|
7
34
|
/**
|
|
8
35
|
* Thrown when one or more errors occur during a Kubb build.
|
|
@@ -48,152 +75,6 @@ function getErrorMessage(value) {
|
|
|
48
75
|
return value instanceof Error ? value.message : String(value);
|
|
49
76
|
}
|
|
50
77
|
//#endregion
|
|
51
|
-
//#region ../../internals/utils/src/asyncEventEmitter.ts
|
|
52
|
-
/**
|
|
53
|
-
* Typed `EventEmitter` that awaits all async listeners before resolving.
|
|
54
|
-
* Wraps Node's `EventEmitter` with full TypeScript event-map inference.
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```ts
|
|
58
|
-
* const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
|
|
59
|
-
* emitter.on('build', async (name) => { console.log(name) })
|
|
60
|
-
* await emitter.emit('build', 'petstore') // all listeners awaited
|
|
61
|
-
* ```
|
|
62
|
-
*/
|
|
63
|
-
var AsyncEventEmitter = class {
|
|
64
|
-
/**
|
|
65
|
-
* Maximum number of listeners per event before Node emits a memory-leak warning.
|
|
66
|
-
* @default 10
|
|
67
|
-
*/
|
|
68
|
-
constructor(maxListener = 10) {
|
|
69
|
-
this.#emitter.setMaxListeners(maxListener);
|
|
70
|
-
}
|
|
71
|
-
#emitter = new EventEmitter();
|
|
72
|
-
/**
|
|
73
|
-
* Emits `eventName` and awaits all registered listeners sequentially.
|
|
74
|
-
* Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* ```ts
|
|
78
|
-
* await emitter.emit('build', 'petstore')
|
|
79
|
-
* ```
|
|
80
|
-
*/
|
|
81
|
-
emit(eventName, ...eventArgs) {
|
|
82
|
-
const listeners = this.#emitter.listeners(eventName);
|
|
83
|
-
if (listeners.length === 0) return;
|
|
84
|
-
return this.#emitAll(eventName, listeners, eventArgs);
|
|
85
|
-
}
|
|
86
|
-
async #emitAll(eventName, listeners, eventArgs) {
|
|
87
|
-
for (const listener of listeners) try {
|
|
88
|
-
await listener(...eventArgs);
|
|
89
|
-
} catch (err) {
|
|
90
|
-
let serializedArgs;
|
|
91
|
-
try {
|
|
92
|
-
serializedArgs = JSON.stringify(eventArgs);
|
|
93
|
-
} catch {
|
|
94
|
-
serializedArgs = String(eventArgs);
|
|
95
|
-
}
|
|
96
|
-
throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: toError(err) });
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Registers a persistent listener for `eventName`.
|
|
101
|
-
*
|
|
102
|
-
* @example
|
|
103
|
-
* ```ts
|
|
104
|
-
* emitter.on('build', async (name) => { console.log(name) })
|
|
105
|
-
* ```
|
|
106
|
-
*/
|
|
107
|
-
on(eventName, handler) {
|
|
108
|
-
this.#emitter.on(eventName, handler);
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Removes a previously registered listener.
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* ```ts
|
|
115
|
-
* emitter.off('build', handler)
|
|
116
|
-
* ```
|
|
117
|
-
*/
|
|
118
|
-
off(eventName, handler) {
|
|
119
|
-
this.#emitter.off(eventName, handler);
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Returns the number of listeners registered for `eventName`.
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* ```ts
|
|
126
|
-
* emitter.on('build', handler)
|
|
127
|
-
* emitter.listenerCount('build') // 1
|
|
128
|
-
* ```
|
|
129
|
-
*/
|
|
130
|
-
listenerCount(eventName) {
|
|
131
|
-
return this.#emitter.listenerCount(eventName);
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
|
|
135
|
-
* Set this above the expected listener count when many listeners attach by design.
|
|
136
|
-
*
|
|
137
|
-
* @example
|
|
138
|
-
* ```ts
|
|
139
|
-
* emitter.setMaxListeners(40)
|
|
140
|
-
* ```
|
|
141
|
-
*/
|
|
142
|
-
setMaxListeners(max) {
|
|
143
|
-
this.#emitter.setMaxListeners(max);
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Removes all listeners from every event channel.
|
|
147
|
-
*
|
|
148
|
-
* @example
|
|
149
|
-
* ```ts
|
|
150
|
-
* emitter.removeAll()
|
|
151
|
-
* ```
|
|
152
|
-
*/
|
|
153
|
-
removeAll() {
|
|
154
|
-
this.#emitter.removeAllListeners();
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
//#endregion
|
|
158
|
-
//#region ../../internals/utils/src/casing.ts
|
|
159
|
-
/**
|
|
160
|
-
* Shared implementation for camelCase and PascalCase conversion.
|
|
161
|
-
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
162
|
-
* and capitalizes each word according to `pascal`.
|
|
163
|
-
*
|
|
164
|
-
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
165
|
-
*/
|
|
166
|
-
function toCamelOrPascal(text, pascal) {
|
|
167
|
-
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
168
|
-
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
169
|
-
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
170
|
-
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Converts `text` to camelCase.
|
|
174
|
-
*
|
|
175
|
-
* @example Word boundaries
|
|
176
|
-
* `camelCase('hello-world') // 'helloWorld'`
|
|
177
|
-
*
|
|
178
|
-
* @example With a prefix
|
|
179
|
-
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
180
|
-
*/
|
|
181
|
-
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
182
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Converts `text` to PascalCase.
|
|
186
|
-
*
|
|
187
|
-
* @example Word boundaries
|
|
188
|
-
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
189
|
-
*
|
|
190
|
-
* @example With a suffix
|
|
191
|
-
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
192
|
-
*/
|
|
193
|
-
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
194
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
195
|
-
}
|
|
196
|
-
//#endregion
|
|
197
78
|
//#region ../../internals/utils/src/runtime.ts
|
|
198
79
|
/**
|
|
199
80
|
* Detects the JavaScript runtime executing the current process and exposes its name and version.
|
|
@@ -368,151 +249,148 @@ function toFilePath(name, caseLast = camelCase) {
|
|
|
368
249
|
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
369
250
|
}
|
|
370
251
|
//#endregion
|
|
371
|
-
//#region src/
|
|
372
|
-
/**
|
|
373
|
-
* Plugin `include` filter types that select operations directly. When one of these is set
|
|
374
|
-
* without a `schemaName` include, the generate phase pre-scans operations to compute the set
|
|
375
|
-
* of schemas they reach, so unreachable schemas can be pruned for that plugin.
|
|
376
|
-
*/
|
|
377
|
-
const OPERATION_FILTER_TYPES = /* @__PURE__ */ new Set([
|
|
378
|
-
"tag",
|
|
379
|
-
"operationId",
|
|
380
|
-
"path",
|
|
381
|
-
"method",
|
|
382
|
-
"contentType"
|
|
383
|
-
]);
|
|
252
|
+
//#region src/Hookable.ts
|
|
384
253
|
/**
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
254
|
+
* Typed hook emitter that awaits all async listeners before resolving.
|
|
255
|
+
* Wraps Node's `EventEmitter` with full TypeScript hook-map inference.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```ts
|
|
259
|
+
* const hooks = new Hookable<{ build: [name: string] }>()
|
|
260
|
+
* hooks.hook('build', async (name) => { console.log(name) })
|
|
261
|
+
* await hooks.callHook('build', 'petstore') // all listeners awaited
|
|
262
|
+
* ```
|
|
388
263
|
*/
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Fallback for an unstructured error with no specific code.
|
|
392
|
-
*/
|
|
393
|
-
unknown: "KUBB_UNKNOWN",
|
|
394
|
-
/**
|
|
395
|
-
* The `input.path` file or URL could not be read.
|
|
396
|
-
*/
|
|
397
|
-
inputNotFound: "KUBB_INPUT_NOT_FOUND",
|
|
398
|
-
/**
|
|
399
|
-
* An adapter was configured without an `input`.
|
|
400
|
-
*/
|
|
401
|
-
inputRequired: "KUBB_INPUT_REQUIRED",
|
|
402
|
-
/**
|
|
403
|
-
* A `$ref` (or equivalent reference) could not be resolved in the source document.
|
|
404
|
-
*/
|
|
405
|
-
refNotFound: "KUBB_REF_NOT_FOUND",
|
|
406
|
-
/**
|
|
407
|
-
* A server variable value is not allowed by its `enum`.
|
|
408
|
-
*/
|
|
409
|
-
invalidServerVariable: "KUBB_INVALID_SERVER_VARIABLE",
|
|
410
|
-
/**
|
|
411
|
-
* A required plugin is missing from the config.
|
|
412
|
-
*/
|
|
413
|
-
pluginNotFound: "KUBB_PLUGIN_NOT_FOUND",
|
|
414
|
-
/**
|
|
415
|
-
* A plugin threw while generating.
|
|
416
|
-
*/
|
|
417
|
-
pluginFailed: "KUBB_PLUGIN_FAILED",
|
|
418
|
-
/**
|
|
419
|
-
* A plugin reported a non-fatal warning through `ctx.warn`.
|
|
420
|
-
*/
|
|
421
|
-
pluginWarning: "KUBB_PLUGIN_WARNING",
|
|
264
|
+
var Hookable = class {
|
|
422
265
|
/**
|
|
423
|
-
*
|
|
424
|
-
|
|
425
|
-
pluginInfo: "KUBB_PLUGIN_INFO",
|
|
426
|
-
/**
|
|
427
|
-
* A schema uses a `format` Kubb does not map to a specific type. Reserved for
|
|
428
|
-
* adapters to emit as a `warning`.
|
|
429
|
-
*/
|
|
430
|
-
unsupportedFormat: "KUBB_UNSUPPORTED_FORMAT",
|
|
431
|
-
/**
|
|
432
|
-
* A referenced schema or operation is marked `deprecated`. Reserved for adapters
|
|
433
|
-
* to emit as an `info`.
|
|
434
|
-
*/
|
|
435
|
-
deprecated: "KUBB_DEPRECATED",
|
|
436
|
-
/**
|
|
437
|
-
* An adapter is required but the config has none. The build cannot read the input
|
|
438
|
-
* without one.
|
|
266
|
+
* Maximum number of listeners per hook before Node emits a memory-leak warning.
|
|
267
|
+
* @default 10
|
|
439
268
|
*/
|
|
440
|
-
|
|
269
|
+
constructor(maxListener = 10) {
|
|
270
|
+
this.#emitter.setMaxListeners(maxListener);
|
|
271
|
+
}
|
|
272
|
+
#emitter = new EventEmitter();
|
|
441
273
|
/**
|
|
442
|
-
*
|
|
443
|
-
*
|
|
274
|
+
* Calls `hookName` and awaits all registered listeners sequentially.
|
|
275
|
+
* Throws if any listener rejects, wrapping the cause with the hook name and serialized arguments.
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* ```ts
|
|
279
|
+
* await hooks.callHook('build', 'petstore')
|
|
280
|
+
* ```
|
|
444
281
|
*/
|
|
445
|
-
|
|
282
|
+
callHook(hookName, ...hookArgs) {
|
|
283
|
+
const listeners = this.#emitter.listeners(hookName);
|
|
284
|
+
if (listeners.length === 0) return;
|
|
285
|
+
return this.#emitAll(hookName, listeners, hookArgs);
|
|
286
|
+
}
|
|
287
|
+
async #emitAll(hookName, listeners, hookArgs) {
|
|
288
|
+
for (const listener of listeners) try {
|
|
289
|
+
await listener(...hookArgs);
|
|
290
|
+
} catch (err) {
|
|
291
|
+
let serializedArgs;
|
|
292
|
+
try {
|
|
293
|
+
serializedArgs = JSON.stringify(hookArgs);
|
|
294
|
+
} catch {
|
|
295
|
+
serializedArgs = String(hookArgs);
|
|
296
|
+
}
|
|
297
|
+
throw new Error(`Error in async listener for "${hookName}" with hookArgs ${serializedArgs}`, { cause: toError(err) });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
446
300
|
/**
|
|
447
|
-
*
|
|
301
|
+
* Registers a persistent listener for `hookName` and returns a function that removes it.
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* ```ts
|
|
305
|
+
* const unhook = hooks.hook('build', async (name) => { console.log(name) })
|
|
306
|
+
* unhook() // removes it
|
|
307
|
+
* ```
|
|
448
308
|
*/
|
|
449
|
-
|
|
309
|
+
hook(hookName, handler) {
|
|
310
|
+
this.#emitter.on(hookName, handler);
|
|
311
|
+
return () => this.removeHook(hookName, handler);
|
|
312
|
+
}
|
|
450
313
|
/**
|
|
451
|
-
*
|
|
314
|
+
* Registers every handler in `configHooks` at once and returns a function that removes them
|
|
315
|
+
* all. Undefined entries are skipped, so a partial hook object registers only its present keys.
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* ```ts
|
|
319
|
+
* const unhook = hooks.addHooks({ build: onBuild, done: onDone })
|
|
320
|
+
* unhook() // removes both
|
|
321
|
+
* ```
|
|
452
322
|
*/
|
|
453
|
-
|
|
323
|
+
addHooks(configHooks) {
|
|
324
|
+
const unhooks = Object.keys(configHooks).filter((name) => configHooks[name]).map((name) => this.hook(name, configHooks[name]));
|
|
325
|
+
return () => {
|
|
326
|
+
for (const unhook of unhooks) unhook();
|
|
327
|
+
};
|
|
328
|
+
}
|
|
454
329
|
/**
|
|
455
|
-
*
|
|
330
|
+
* Removes a previously registered listener.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```ts
|
|
334
|
+
* hooks.removeHook('build', handler)
|
|
335
|
+
* ```
|
|
456
336
|
*/
|
|
457
|
-
|
|
337
|
+
removeHook(hookName, handler) {
|
|
338
|
+
this.#emitter.off(hookName, handler);
|
|
339
|
+
}
|
|
458
340
|
/**
|
|
459
|
-
*
|
|
341
|
+
* Returns the number of listeners registered for `hookName`.
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```ts
|
|
345
|
+
* hooks.hook('build', handler)
|
|
346
|
+
* hooks.listenerCount('build') // 1
|
|
347
|
+
* ```
|
|
460
348
|
*/
|
|
461
|
-
|
|
349
|
+
listenerCount(hookName) {
|
|
350
|
+
return this.#emitter.listenerCount(hookName);
|
|
351
|
+
}
|
|
462
352
|
/**
|
|
463
|
-
*
|
|
353
|
+
* Raises or lowers the per-hook listener ceiling before Node warns about a memory leak.
|
|
354
|
+
* Set this above the expected listener count when many listeners attach by design.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```ts
|
|
358
|
+
* hooks.setMaxListeners(40)
|
|
359
|
+
* ```
|
|
464
360
|
*/
|
|
465
|
-
|
|
361
|
+
setMaxListeners(max) {
|
|
362
|
+
this.#emitter.setMaxListeners(max);
|
|
363
|
+
}
|
|
466
364
|
/**
|
|
467
|
-
*
|
|
365
|
+
* Removes all listeners from every hook channel.
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* ```ts
|
|
369
|
+
* hooks.removeAllHooks()
|
|
370
|
+
* ```
|
|
468
371
|
*/
|
|
469
|
-
|
|
372
|
+
removeAllHooks() {
|
|
373
|
+
this.#emitter.removeAllListeners();
|
|
374
|
+
}
|
|
470
375
|
};
|
|
471
376
|
//#endregion
|
|
472
|
-
//#region src/createStorage.ts
|
|
473
|
-
/**
|
|
474
|
-
* Defines a custom storage backend. The builder receives user options and
|
|
475
|
-
* returns a `Storage` implementation. Kubb ships with filesystem and in-memory
|
|
476
|
-
* storages. A custom backend writes generated files elsewhere, such as cloud
|
|
477
|
-
* storage or a database.
|
|
478
|
-
*
|
|
479
|
-
* @example In-memory storage (the built-in implementation)
|
|
480
|
-
* ```ts
|
|
481
|
-
* import { createStorage } from '@kubb/core'
|
|
482
|
-
*
|
|
483
|
-
* export const memoryStorage = createStorage(() => {
|
|
484
|
-
* const store = new Map<string, string>()
|
|
485
|
-
*
|
|
486
|
-
* return {
|
|
487
|
-
* name: 'memory',
|
|
488
|
-
* async hasItem(key) {
|
|
489
|
-
* return store.has(key)
|
|
490
|
-
* },
|
|
491
|
-
* async getItem(key) {
|
|
492
|
-
* return store.get(key) ?? null
|
|
493
|
-
* },
|
|
494
|
-
* async setItem(key, value) {
|
|
495
|
-
* store.set(key, value)
|
|
496
|
-
* },
|
|
497
|
-
* async removeItem(key) {
|
|
498
|
-
* store.delete(key)
|
|
499
|
-
* },
|
|
500
|
-
* async getKeys(base) {
|
|
501
|
-
* const keys = [...store.keys()]
|
|
502
|
-
* return base ? keys.filter((k) => k.startsWith(base)) : keys
|
|
503
|
-
* },
|
|
504
|
-
* async clear(base) {
|
|
505
|
-
* if (!base) store.clear()
|
|
506
|
-
* },
|
|
507
|
-
* }
|
|
508
|
-
* })
|
|
509
|
-
* ```
|
|
510
|
-
*/
|
|
511
|
-
function createStorage(build) {
|
|
512
|
-
return (options) => build(options ?? {});
|
|
513
|
-
}
|
|
514
|
-
//#endregion
|
|
515
377
|
//#region src/FileManager.ts
|
|
378
|
+
function joinSources(file) {
|
|
379
|
+
return file.sources.map((source) => extractStringsFromNodes(source.nodes)).filter(Boolean).join("\n\n");
|
|
380
|
+
}
|
|
381
|
+
async function parseCopy(file) {
|
|
382
|
+
let content;
|
|
383
|
+
try {
|
|
384
|
+
content = await read(file.copy);
|
|
385
|
+
} catch (err) {
|
|
386
|
+
throw new Error(`[kubb] Could not copy file into output: ${file.copy}`, { cause: err });
|
|
387
|
+
}
|
|
388
|
+
return [
|
|
389
|
+
file.banner,
|
|
390
|
+
content,
|
|
391
|
+
file.footer
|
|
392
|
+
].filter((segment) => Boolean(segment)).map((segment) => segment.trimEnd()).join("\n");
|
|
393
|
+
}
|
|
516
394
|
function mergeFile(a, b) {
|
|
517
395
|
return {
|
|
518
396
|
...a,
|
|
@@ -537,23 +415,21 @@ function compareFiles(a, b) {
|
|
|
537
415
|
return 0;
|
|
538
416
|
}
|
|
539
417
|
/**
|
|
540
|
-
* In-memory file store for generated files
|
|
541
|
-
*
|
|
542
|
-
* path length (barrel `index.ts` last
|
|
418
|
+
* In-memory file store for generated files, and the writer that turns them into source
|
|
419
|
+
* strings on `storage`. Files sharing a `path` are merged (sources/imports/exports
|
|
420
|
+
* concatenated). The `files` getter is sorted by path length (barrel `index.ts` last
|
|
421
|
+
* within a bucket).
|
|
543
422
|
*
|
|
544
423
|
* @example
|
|
545
424
|
* ```ts
|
|
546
425
|
* const manager = new FileManager()
|
|
547
426
|
* manager.upsert(myFile)
|
|
548
427
|
* manager.files // sorted view
|
|
428
|
+
* await manager.write(manager.files, { storage: fsStorage() })
|
|
549
429
|
* ```
|
|
550
430
|
*/
|
|
551
431
|
var FileManager = class {
|
|
552
|
-
|
|
553
|
-
* Subscribe to file-store changes. Listeners on `upsert` see each resolved file as it lands
|
|
554
|
-
* through `add` or `upsert`.
|
|
555
|
-
*/
|
|
556
|
-
hooks = new AsyncEventEmitter();
|
|
432
|
+
hooks = new Hookable();
|
|
557
433
|
#cache = /* @__PURE__ */ new Map();
|
|
558
434
|
#sorted = null;
|
|
559
435
|
add(...files) {
|
|
@@ -570,7 +446,6 @@ var FileManager = class {
|
|
|
570
446
|
const merged = existing && mergeExisting ? ast.factory.createFile(mergeFile(existing, file)) : ast.factory.createFile(file);
|
|
571
447
|
this.#cache.set(merged.path, merged);
|
|
572
448
|
resolved.push(merged);
|
|
573
|
-
this.hooks.emit("upsert", merged);
|
|
574
449
|
}
|
|
575
450
|
if (resolved.length > 0) this.#sorted = null;
|
|
576
451
|
return resolved;
|
|
@@ -583,13 +458,6 @@ var FileManager = class {
|
|
|
583
458
|
}
|
|
584
459
|
return [...seen.values()];
|
|
585
460
|
}
|
|
586
|
-
getByPath(path) {
|
|
587
|
-
return this.#cache.get(path) ?? null;
|
|
588
|
-
}
|
|
589
|
-
deleteByPath(path) {
|
|
590
|
-
if (!this.#cache.delete(path)) return;
|
|
591
|
-
this.#sorted = null;
|
|
592
|
-
}
|
|
593
461
|
clear() {
|
|
594
462
|
this.#cache.clear();
|
|
595
463
|
this.#sorted = null;
|
|
@@ -600,10 +468,7 @@ var FileManager = class {
|
|
|
600
468
|
*/
|
|
601
469
|
dispose() {
|
|
602
470
|
this.clear();
|
|
603
|
-
this.hooks.
|
|
604
|
-
}
|
|
605
|
-
[Symbol.dispose]() {
|
|
606
|
-
this.dispose();
|
|
471
|
+
this.hooks.removeAllHooks();
|
|
607
472
|
}
|
|
608
473
|
/**
|
|
609
474
|
* All stored files in stable sort order (shortest path first, barrel files
|
|
@@ -612,163 +477,46 @@ var FileManager = class {
|
|
|
612
477
|
get files() {
|
|
613
478
|
return this.#sorted ??= [...this.#cache.values()].sort(compareFiles);
|
|
614
479
|
}
|
|
615
|
-
};
|
|
616
|
-
//#endregion
|
|
617
|
-
//#region src/FileProcessor.ts
|
|
618
|
-
function joinSources(file) {
|
|
619
|
-
const sources = file.sources;
|
|
620
|
-
if (sources.length === 0) return "";
|
|
621
|
-
const parts = [];
|
|
622
|
-
for (const source of sources) {
|
|
623
|
-
const text = extractStringsFromNodes(source.nodes);
|
|
624
|
-
if (text) parts.push(text);
|
|
625
|
-
}
|
|
626
|
-
return parts.join("\n\n");
|
|
627
|
-
}
|
|
628
|
-
async function parseCopy(file) {
|
|
629
|
-
let content;
|
|
630
|
-
try {
|
|
631
|
-
content = await read(file.copy);
|
|
632
|
-
} catch (err) {
|
|
633
|
-
throw new Error(`[kubb] Could not copy file into output: ${file.copy}`, { cause: err });
|
|
634
|
-
}
|
|
635
|
-
return [
|
|
636
|
-
file.banner,
|
|
637
|
-
content,
|
|
638
|
-
file.footer
|
|
639
|
-
].filter((segment) => Boolean(segment)).map((segment) => segment.trimEnd()).join("\n");
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* Turns `FileNode`s into source strings and writes them to storage.
|
|
643
|
-
*
|
|
644
|
-
* Two modes share the same instance. Stateless mode (`parse`, `stream`, `run`) just runs the
|
|
645
|
-
* conversion. Queue mode (`enqueue`, `flush`, `drain`) buffers files deduped by path and
|
|
646
|
-
* writes each batch through storage with up to `STREAM_FLUSH_EVERY` requests in flight.
|
|
647
|
-
*
|
|
648
|
-
* `flush` does not wait for its batch to finish, so dispatch can overlap with IO. The next
|
|
649
|
-
* `flush` or `drain` picks the in-flight batch up. `drain` blocks until everything has been
|
|
650
|
-
* written and is meant for the end of a build.
|
|
651
|
-
*
|
|
652
|
-
* To surface build-level hook signals (`kubb:files:processing:*` and friends) subscribe to
|
|
653
|
-
* `hooks` and re-emit on the kubb bus.
|
|
654
|
-
*/
|
|
655
|
-
var FileProcessor = class {
|
|
656
|
-
hooks = new AsyncEventEmitter();
|
|
657
|
-
#parsers;
|
|
658
|
-
#storage;
|
|
659
|
-
#extension;
|
|
660
|
-
#pending = /* @__PURE__ */ new Map();
|
|
661
|
-
#runningFlush = null;
|
|
662
|
-
constructor(options) {
|
|
663
|
-
this.#parsers = options.parsers ?? null;
|
|
664
|
-
this.#storage = options.storage;
|
|
665
|
-
this.#extension = options.extension ?? null;
|
|
666
|
-
}
|
|
667
480
|
/**
|
|
668
|
-
*
|
|
481
|
+
* Converts a file's AST sources (or its `copy` source) into the final on-disk string.
|
|
669
482
|
*/
|
|
670
|
-
|
|
671
|
-
return this.#pending.size;
|
|
672
|
-
}
|
|
673
|
-
async parse(file) {
|
|
483
|
+
async parse(file, { parsers, extension } = {}) {
|
|
674
484
|
if (file.copy) return parseCopy(file);
|
|
675
|
-
const
|
|
676
|
-
const parseExtName = this.#extension?.[file.extname] || void 0;
|
|
485
|
+
const parseExtName = extension?.[file.extname] || void 0;
|
|
677
486
|
if (!parsers || !file.extname) return joinSources(file);
|
|
678
487
|
const parser = parsers.get(file.extname);
|
|
679
488
|
if (!parser) return joinSources(file);
|
|
680
489
|
return parser.parse(file, { extname: parseExtName });
|
|
681
490
|
}
|
|
682
|
-
|
|
491
|
+
/**
|
|
492
|
+
* Converts and writes every file at once, letting `storage.setItem` decide how much of
|
|
493
|
+
* that runs concurrently.
|
|
494
|
+
*/
|
|
495
|
+
async write(files, { storage, parsers, extension }) {
|
|
496
|
+
if (files.length === 0) return;
|
|
497
|
+
await this.hooks.callHook("start", files);
|
|
683
498
|
const total = files.length;
|
|
684
|
-
if (total === 0) return;
|
|
685
499
|
let processed = 0;
|
|
686
|
-
|
|
687
|
-
const source = await this.parse(file
|
|
500
|
+
await Promise.all(files.map(async (file) => {
|
|
501
|
+
const source = await this.parse(file, {
|
|
502
|
+
parsers,
|
|
503
|
+
extension
|
|
504
|
+
});
|
|
688
505
|
processed++;
|
|
689
|
-
|
|
506
|
+
await this.hooks.callHook("update", {
|
|
690
507
|
file,
|
|
691
508
|
source,
|
|
692
509
|
processed,
|
|
693
510
|
total,
|
|
694
511
|
percentage: processed / total * 100
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
await this.hooks.emit("start", files);
|
|
700
|
-
for await (const { file, source, processed, total, percentage } of this.stream(files)) await this.hooks.emit("update", {
|
|
701
|
-
file,
|
|
702
|
-
source,
|
|
703
|
-
processed,
|
|
704
|
-
percentage,
|
|
705
|
-
total
|
|
706
|
-
});
|
|
707
|
-
await this.hooks.emit("end", files);
|
|
708
|
-
return files;
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Adds a file to the next flush. A later `enqueue` for the same path replaces the previous
|
|
712
|
-
* entry, matching `FileManager.upsert`. Fires the `enqueue` event.
|
|
713
|
-
*/
|
|
714
|
-
enqueue(file) {
|
|
715
|
-
this.#pending.set(file.path, file);
|
|
716
|
-
this.hooks.emit("enqueue", file);
|
|
717
|
-
}
|
|
718
|
-
/**
|
|
719
|
-
* Starts processing the queued files. Waits for any previous flush to finish (so two
|
|
720
|
-
* batches never run together) and then returns without waiting for the new one. The next
|
|
721
|
-
* `flush` or `drain` picks up the in-flight task.
|
|
722
|
-
*/
|
|
723
|
-
async flush() {
|
|
724
|
-
if (this.#runningFlush) await this.#runningFlush;
|
|
725
|
-
if (this.#pending.size === 0) return;
|
|
726
|
-
const batch = [...this.#pending.values()];
|
|
727
|
-
this.#pending.clear();
|
|
728
|
-
this.#runningFlush = this.#processAndWrite(batch).finally(() => {
|
|
729
|
-
this.#runningFlush = null;
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
/**
|
|
733
|
-
* Waits for the in-flight flush and writes any files still queued. Fires the `drain` event
|
|
734
|
-
* when both are done.
|
|
735
|
-
*/
|
|
736
|
-
async drain() {
|
|
737
|
-
if (this.#runningFlush) await this.#runningFlush;
|
|
738
|
-
if (this.#pending.size > 0) {
|
|
739
|
-
const batch = [...this.#pending.values()];
|
|
740
|
-
this.#pending.clear();
|
|
741
|
-
await this.#processAndWrite(batch);
|
|
742
|
-
}
|
|
743
|
-
await this.hooks.emit("drain");
|
|
744
|
-
}
|
|
745
|
-
async #processAndWrite(files) {
|
|
746
|
-
const storage = this.#storage;
|
|
747
|
-
await this.hooks.emit("start", files);
|
|
748
|
-
const queue = [];
|
|
749
|
-
for await (const item of this.stream(files)) {
|
|
750
|
-
await this.hooks.emit("update", item);
|
|
751
|
-
if (item.source) {
|
|
752
|
-
queue.push(storage.setItem(item.file.path, item.source));
|
|
753
|
-
if (queue.length >= 50) await Promise.all(queue.splice(0));
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
await Promise.all(queue);
|
|
757
|
-
await this.hooks.emit("end", files);
|
|
758
|
-
}
|
|
759
|
-
/**
|
|
760
|
-
* Clears every listener and the pending queue.
|
|
761
|
-
*/
|
|
762
|
-
dispose() {
|
|
763
|
-
this.hooks.removeAll();
|
|
764
|
-
this.#pending.clear();
|
|
765
|
-
}
|
|
766
|
-
[Symbol.dispose]() {
|
|
767
|
-
this.dispose();
|
|
512
|
+
});
|
|
513
|
+
if (source) await storage.setItem(file.path, source);
|
|
514
|
+
}));
|
|
515
|
+
await this.hooks.callHook("end", files);
|
|
768
516
|
}
|
|
769
517
|
};
|
|
770
518
|
//#endregion
|
|
771
|
-
//#region \0@oxc-project+runtime@0.
|
|
519
|
+
//#region \0@oxc-project+runtime@0.138.0/helpers/esm/usingCtx.js
|
|
772
520
|
function _usingCtx() {
|
|
773
521
|
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
774
522
|
var n = Error();
|
|
@@ -828,56 +576,6 @@ function _usingCtx() {
|
|
|
828
576
|
};
|
|
829
577
|
}
|
|
830
578
|
//#endregion
|
|
831
|
-
|
|
832
|
-
/**
|
|
833
|
-
* In-memory storage driver. Useful for testing and dry-run scenarios where
|
|
834
|
-
* generated output should be captured without touching the filesystem.
|
|
835
|
-
*
|
|
836
|
-
* All data lives in a `Map` scoped to the storage instance and is discarded
|
|
837
|
-
* when the instance is garbage-collected.
|
|
838
|
-
*
|
|
839
|
-
* @example
|
|
840
|
-
* ```ts
|
|
841
|
-
* import { memoryStorage } from '@kubb/core'
|
|
842
|
-
* import { defineConfig } from 'kubb'
|
|
843
|
-
*
|
|
844
|
-
* export default defineConfig({
|
|
845
|
-
* input: { path: './petStore.yaml' },
|
|
846
|
-
* output: { path: './src/gen' },
|
|
847
|
-
* storage: memoryStorage(),
|
|
848
|
-
* })
|
|
849
|
-
* ```
|
|
850
|
-
*/
|
|
851
|
-
const memoryStorage = createStorage(() => {
|
|
852
|
-
const store = /* @__PURE__ */ new Map();
|
|
853
|
-
return {
|
|
854
|
-
name: "memory",
|
|
855
|
-
async hasItem(key) {
|
|
856
|
-
return store.has(key);
|
|
857
|
-
},
|
|
858
|
-
async getItem(key) {
|
|
859
|
-
return store.get(key) ?? null;
|
|
860
|
-
},
|
|
861
|
-
async setItem(key, value) {
|
|
862
|
-
store.set(key, value);
|
|
863
|
-
},
|
|
864
|
-
async removeItem(key) {
|
|
865
|
-
store.delete(key);
|
|
866
|
-
},
|
|
867
|
-
async getKeys(base) {
|
|
868
|
-
const keys = [...store.keys()];
|
|
869
|
-
return base ? keys.filter((k) => k.startsWith(base)) : keys;
|
|
870
|
-
},
|
|
871
|
-
async clear(base) {
|
|
872
|
-
if (!base) {
|
|
873
|
-
store.clear();
|
|
874
|
-
return;
|
|
875
|
-
}
|
|
876
|
-
for (const key of store.keys()) if (key.startsWith(base)) store.delete(key);
|
|
877
|
-
}
|
|
878
|
-
};
|
|
879
|
-
});
|
|
880
|
-
//#endregion
|
|
881
|
-
export { getErrorMessage as _, createStorage as a, clean as c, write as d, runtime as f, BuildError as g, AsyncEventEmitter as h, FileManager as i, toFilePath as l, pascalCase as m, _usingCtx as n, OPERATION_FILTER_TYPES as o, camelCase as p, FileProcessor as r, diagnosticCode as s, memoryStorage as t, toPosixPath as u };
|
|
579
|
+
export { toFilePath as a, BuildError as c, camelCase as d, clean as i, getErrorMessage as l, FileManager as n, toPosixPath as o, Hookable as r, write as s, _usingCtx as t, toError as u };
|
|
882
580
|
|
|
883
|
-
//# sourceMappingURL=
|
|
581
|
+
//# sourceMappingURL=usingCtx-Cnrm3TcM.js.map
|