@kubb/cli 5.12.8 → 5.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/{Telemetry-B3ls8iy8.cjs → Telemetry-0Sr_THx9.cjs} +2 -2
- package/dist/{Telemetry-B3ls8iy8.cjs.map → Telemetry-0Sr_THx9.cjs.map} +1 -1
- package/dist/{Telemetry-Cwis9NRC.js → Telemetry-B4hk3xGv.js} +2 -2
- package/dist/{Telemetry-Cwis9NRC.js.map → Telemetry-B4hk3xGv.js.map} +1 -1
- package/dist/{background-BuZJ6isn.cjs → background-6xlebN_Y.cjs} +2 -2
- package/dist/{background-BuZJ6isn.cjs.map → background-6xlebN_Y.cjs.map} +1 -1
- package/dist/{background-nDhk-eNv.js → background-B7oEWLZs.js} +1 -1
- package/dist/{background-BpyuKytZ.js → background-DY4LTodc.js} +2 -2
- package/dist/{background-BpyuKytZ.js.map → background-DY4LTodc.js.map} +1 -1
- package/dist/{config-1tiH3heq.cjs → config-BG_Nr2CZ.cjs} +17 -1
- package/dist/config-BG_Nr2CZ.cjs.map +1 -0
- package/dist/{config-CbjbW2J5.js → config-CNqgDSu3.js} +12 -2
- package/dist/config-CNqgDSu3.js.map +1 -0
- package/dist/config-DWDpFSn1.js +1 -0
- package/dist/credentials-CZK6zPLv.js.map +1 -1
- package/dist/credentials-DDaKqtJn.cjs.map +1 -1
- package/dist/index.cjs +1275 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1276 -98
- package/dist/index.js.map +1 -1
- package/dist/{registry-DxHGGUfy.cjs → registry-9AEIs-Cm.cjs} +3 -3
- package/dist/{registry-DxHGGUfy.cjs.map → registry-9AEIs-Cm.cjs.map} +1 -1
- package/dist/{registry-D8Vxs2q6.js → registry-DLasg7du.js} +3 -3
- package/dist/{registry-D8Vxs2q6.js.map → registry-DLasg7du.js.map} +1 -1
- package/dist/{reporters-DMvezNR1.js → reporters-BfeB9jOt.js} +4 -4
- package/dist/{reporters-DMvezNR1.js.map → reporters-BfeB9jOt.js.map} +1 -1
- package/dist/{reporters-BRt7Cmp0.cjs → reporters-xwOZPZ42.cjs} +4 -4
- package/dist/{reporters-BRt7Cmp0.cjs.map → reporters-xwOZPZ42.cjs.map} +1 -1
- package/dist/{run-8K8KvqtO.js → run--OOU4YSR.js} +5 -5
- package/dist/{run-8K8KvqtO.js.map → run--OOU4YSR.js.map} +1 -1
- package/dist/{run-BPi2eh0e.js → run-BDmTZPT7.js} +7 -7
- package/dist/{run-BPi2eh0e.js.map → run-BDmTZPT7.js.map} +1 -1
- package/dist/{run-BFqPilTx.cjs → run-BTzmKup3.cjs} +2 -2
- package/dist/{run-BFqPilTx.cjs.map → run-BTzmKup3.cjs.map} +1 -1
- package/dist/{run-C61yKdb4.cjs → run-Bgr0F6xv.cjs} +5 -5
- package/dist/{run-C61yKdb4.cjs.map → run-Bgr0F6xv.cjs.map} +1 -1
- package/dist/{run-Be6_6_PJ.cjs → run-C2syKtAg.cjs} +7 -7
- package/dist/{run-Be6_6_PJ.cjs.map → run-C2syKtAg.cjs.map} +1 -1
- package/dist/{run-CfkP7AY1.js → run-C9Q5R-p9.js} +1 -1
- package/dist/{run-BJUijhbO.js → run-DoUk-2Xb.js} +2 -2
- package/dist/{run-BJUijhbO.js.map → run-DoUk-2Xb.js.map} +1 -1
- package/dist/{run-DZgSH5YE.js → run-kRFuEqeA.js} +14 -8
- package/dist/run-kRFuEqeA.js.map +1 -0
- package/dist/{run-Bk-PyLaA.cjs → run-w92b045L.cjs} +14 -8
- package/dist/run-w92b045L.cjs.map +1 -0
- package/dist/{snapshot-BApwIb2T.cjs → snapshot-B4eWToMC.cjs} +6 -6
- package/dist/{snapshot-BApwIb2T.cjs.map → snapshot-B4eWToMC.cjs.map} +1 -1
- package/dist/{snapshot-F8ShCYAC.js → snapshot-O36QFuVw.js} +6 -6
- package/dist/{snapshot-F8ShCYAC.js.map → snapshot-O36QFuVw.js.map} +1 -1
- package/dist/{status-D1O1SZi5.js → status-C5_IHiDc.js} +3 -3
- package/dist/{status-D1O1SZi5.js.map → status-C5_IHiDc.js.map} +1 -1
- package/dist/{status-CNvgGbU7.cjs → status-CteZmcug.cjs} +3 -3
- package/dist/{status-CNvgGbU7.cjs.map → status-CteZmcug.cjs.map} +1 -1
- package/dist/studioWorker.js +2 -2
- package/dist/{summary-D6hq3I9C.js → summary-B1aj8T_V.js} +3 -3
- package/dist/{summary-D6hq3I9C.js.map → summary-B1aj8T_V.js.map} +1 -1
- package/dist/{summary-D2g0ii83.cjs → summary-BuKuPfE_.cjs} +3 -3
- package/dist/{summary-D2g0ii83.cjs.map → summary-BuKuPfE_.cjs.map} +1 -1
- package/dist/{text-BBQbTT22.js → text-CaZG0KF2.js} +2 -2
- package/dist/{text-BBQbTT22.js.map → text-CaZG0KF2.js.map} +1 -1
- package/dist/{text-1NAngHDI.cjs → text-DVPYqR74.cjs} +2 -2
- package/dist/{text-1NAngHDI.cjs.map → text-DVPYqR74.cjs.map} +1 -1
- package/dist/{utils-DNOlQ-6B.js → utils--q06lp55.js} +4 -4
- package/dist/{utils-DNOlQ-6B.js.map → utils--q06lp55.js.map} +1 -1
- package/dist/{utils-N757uO9x.cjs → utils-Ckbz012p.cjs} +9 -3
- package/dist/{utils-N757uO9x.cjs.map → utils-Ckbz012p.cjs.map} +1 -1
- package/package.json +11 -10
- package/dist/config-1tiH3heq.cjs.map +0 -1
- package/dist/config-CbjbW2J5.js.map +0 -1
- package/dist/config-cP7k9YwA.js +0 -1
- package/dist/run-Bk-PyLaA.cjs.map +0 -1
- package/dist/run-DZgSH5YE.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-CPE0t6oh.cjs");
|
|
3
3
|
const require_errors = require("./errors-Cl69p_vI.cjs");
|
|
4
|
-
const require_Telemetry = require("./Telemetry-
|
|
5
|
-
const require_utils = require("./utils-
|
|
6
|
-
const require_text = require("./text-
|
|
7
|
-
const require_registry = require("./registry-
|
|
8
|
-
const require_config = require("./config-
|
|
9
|
-
const require_summary = require("./summary-
|
|
4
|
+
const require_Telemetry = require("./Telemetry-0Sr_THx9.cjs");
|
|
5
|
+
const require_utils = require("./utils-Ckbz012p.cjs");
|
|
6
|
+
const require_text = require("./text-DVPYqR74.cjs");
|
|
7
|
+
const require_registry = require("./registry-9AEIs-Cm.cjs");
|
|
8
|
+
const require_config = require("./config-BG_Nr2CZ.cjs");
|
|
9
|
+
const require_summary = require("./summary-BuKuPfE_.cjs");
|
|
10
10
|
let node_util = require("node:util");
|
|
11
11
|
let _gunshi_plugin_dryrun = require("@gunshi/plugin-dryrun");
|
|
12
12
|
_gunshi_plugin_dryrun = require_rolldown_runtime.__toESM(_gunshi_plugin_dryrun, 1);
|
|
@@ -21,6 +21,45 @@ node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
|
|
|
21
21
|
let _clack_prompts = require("@clack/prompts");
|
|
22
22
|
_clack_prompts = require_rolldown_runtime.__toESM(_clack_prompts, 1);
|
|
23
23
|
let _kubb_core = require("@kubb/core");
|
|
24
|
+
let yuku_parser = require("yuku-parser");
|
|
25
|
+
//#region ../../internals/utils/src/casing.ts
|
|
26
|
+
/**
|
|
27
|
+
* Shared implementation for camelCase and PascalCase conversion.
|
|
28
|
+
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
29
|
+
* and capitalizes each word according to `pascal`.
|
|
30
|
+
*
|
|
31
|
+
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
32
|
+
*/
|
|
33
|
+
function convert(text, pascal) {
|
|
34
|
+
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) => {
|
|
35
|
+
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
36
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
37
|
+
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
38
|
+
}
|
|
39
|
+
const cache = /* @__PURE__ */ new Map();
|
|
40
|
+
const cacheLimit = 1e4;
|
|
41
|
+
function toCamelOrPascal(text, pascal) {
|
|
42
|
+
const key = `${pascal ? "P" : "c"}${text}`;
|
|
43
|
+
const cached = cache.get(key);
|
|
44
|
+
if (cached !== void 0) return cached;
|
|
45
|
+
const result = convert(text, pascal);
|
|
46
|
+
if (cache.size >= cacheLimit) cache.clear();
|
|
47
|
+
cache.set(key, result);
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Converts `text` to camelCase.
|
|
52
|
+
*
|
|
53
|
+
* @example Word boundaries
|
|
54
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
55
|
+
*
|
|
56
|
+
* @example With a prefix
|
|
57
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
58
|
+
*/
|
|
59
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
60
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
24
63
|
//#region src/commands/shared.ts
|
|
25
64
|
const configArg = { config: {
|
|
26
65
|
type: "string",
|
|
@@ -92,6 +131,12 @@ const studioPermissionArgs = {
|
|
|
92
131
|
default: false,
|
|
93
132
|
toKebab: true
|
|
94
133
|
},
|
|
134
|
+
allowRemove: {
|
|
135
|
+
type: "boolean",
|
|
136
|
+
description: "Let Studio remove extensions with kubb remove, which edits kubb.config.ts and uninstalls packages; needs --allow-config-edit too. Asked for once per project when omitted",
|
|
137
|
+
default: false,
|
|
138
|
+
toKebab: true
|
|
139
|
+
},
|
|
95
140
|
allowSaveInput: {
|
|
96
141
|
type: "boolean",
|
|
97
142
|
description: "Let Studio save the spec it sends as the input in kubb.config.ts; needs --allow-config-edit too. Asked for once per project when omitted",
|
|
@@ -134,7 +179,7 @@ function resolveLogLevel({ verbose, silent, logLevel }) {
|
|
|
134
179
|
if (silent) return "silent";
|
|
135
180
|
return logLevel;
|
|
136
181
|
}
|
|
137
|
-
const command$
|
|
182
|
+
const command$14 = (0, gunshi.defineWithTypes)()({
|
|
138
183
|
name: "generate",
|
|
139
184
|
rendering: { header: null },
|
|
140
185
|
description: "Generate types, clients, hooks, and schemas from an OpenAPI specification.",
|
|
@@ -185,7 +230,7 @@ const command$13 = (0, gunshi.defineWithTypes)()({
|
|
|
185
230
|
async run(ctx) {
|
|
186
231
|
const { values } = ctx;
|
|
187
232
|
const logLevel = resolveLogLevel(values);
|
|
188
|
-
const { run } = await Promise.resolve().then(() => require("./run-
|
|
233
|
+
const { run } = await Promise.resolve().then(() => require("./run-C2syKtAg.cjs"));
|
|
189
234
|
await run({
|
|
190
235
|
input: values.input,
|
|
191
236
|
configPath: values.config,
|
|
@@ -199,7 +244,7 @@ const command$13 = (0, gunshi.defineWithTypes)()({
|
|
|
199
244
|
//#endregion
|
|
200
245
|
//#region src/commands/mcp.ts
|
|
201
246
|
/** The runner is imported when the command runs, so listing `kubb --help` never loads `@kubb/mcp`. */
|
|
202
|
-
const command$
|
|
247
|
+
const command$13 = (0, gunshi.define)({
|
|
203
248
|
name: "mcp",
|
|
204
249
|
description: "Start an MCP server for code generation from an AI assistant.",
|
|
205
250
|
examples: [
|
|
@@ -209,7 +254,7 @@ const command$12 = (0, gunshi.define)({
|
|
|
209
254
|
].join("\n"),
|
|
210
255
|
rendering: { header: null },
|
|
211
256
|
async run() {
|
|
212
|
-
const { run } = await Promise.resolve().then(() => require("./run-
|
|
257
|
+
const { run } = await Promise.resolve().then(() => require("./run-BTzmKup3.cjs"));
|
|
213
258
|
await run();
|
|
214
259
|
}
|
|
215
260
|
});
|
|
@@ -224,14 +269,14 @@ function studioCommand({ action, block, ...definition }) {
|
|
|
224
269
|
...definition,
|
|
225
270
|
toKebab: true,
|
|
226
271
|
async run({ values }) {
|
|
227
|
-
const { runStudio } = await Promise.resolve().then(() => require("./run-
|
|
272
|
+
const { runStudio } = await Promise.resolve().then(() => require("./run-w92b045L.cjs"));
|
|
228
273
|
await runStudio(values, action, { block });
|
|
229
274
|
}
|
|
230
275
|
});
|
|
231
276
|
}
|
|
232
277
|
//#endregion
|
|
233
278
|
//#region src/commands/studio/connect.ts
|
|
234
|
-
const command$
|
|
279
|
+
const command$12 = studioCommand({
|
|
235
280
|
name: "connect",
|
|
236
281
|
description: "Connect this project to Kubb Studio and generate from the browser.",
|
|
237
282
|
examples: [
|
|
@@ -254,31 +299,31 @@ const command$11 = studioCommand({
|
|
|
254
299
|
},
|
|
255
300
|
block: true,
|
|
256
301
|
async action(options) {
|
|
257
|
-
const { connect } = await Promise.resolve().then(() => require("./run-
|
|
302
|
+
const { connect } = await Promise.resolve().then(() => require("./run-w92b045L.cjs"));
|
|
258
303
|
await connect(options);
|
|
259
304
|
}
|
|
260
305
|
});
|
|
261
306
|
//#endregion
|
|
262
307
|
//#region src/commands/studio/login.ts
|
|
263
|
-
const command$
|
|
308
|
+
const command$11 = studioCommand({
|
|
264
309
|
name: "login",
|
|
265
310
|
description: "Pair this machine with Kubb Studio without connecting.",
|
|
266
311
|
examples: ["kubb studio login", "kubb studio login --url http://localhost:3000"].join("\n"),
|
|
267
|
-
args: command$
|
|
312
|
+
args: command$12.args,
|
|
268
313
|
async action(options) {
|
|
269
|
-
const { login } = await Promise.resolve().then(() => require("./run-
|
|
314
|
+
const { login } = await Promise.resolve().then(() => require("./run-w92b045L.cjs"));
|
|
270
315
|
await login(options);
|
|
271
316
|
}
|
|
272
317
|
});
|
|
273
318
|
//#endregion
|
|
274
319
|
//#region src/commands/studio/logout.ts
|
|
275
|
-
const command$
|
|
320
|
+
const command$10 = studioCommand({
|
|
276
321
|
name: "logout",
|
|
277
322
|
description: "Forget the stored Kubb Studio token.",
|
|
278
323
|
examples: "kubb studio logout",
|
|
279
324
|
args: {},
|
|
280
325
|
async action() {
|
|
281
|
-
const { stopWorker } = await Promise.resolve().then(() => require("./background-
|
|
326
|
+
const { stopWorker } = await Promise.resolve().then(() => require("./background-6xlebN_Y.cjs"));
|
|
282
327
|
const { clearCredentials } = await Promise.resolve().then(() => require("./credentials-DDaKqtJn.cjs")).then((n) => n.credentials_exports);
|
|
283
328
|
await stopWorker();
|
|
284
329
|
await clearCredentials();
|
|
@@ -287,7 +332,7 @@ const command$9 = studioCommand({
|
|
|
287
332
|
});
|
|
288
333
|
//#endregion
|
|
289
334
|
//#region src/commands/studio/snapshot.ts
|
|
290
|
-
const command$
|
|
335
|
+
const command$9 = (0, gunshi.define)({
|
|
291
336
|
name: "snapshot",
|
|
292
337
|
description: "Generate and publish a Kubb Studio snapshot from CI.",
|
|
293
338
|
examples: ["kubb studio snapshot", "kubb studio snapshot --json"].join("\n"),
|
|
@@ -321,18 +366,18 @@ const command$8 = (0, gunshi.define)({
|
|
|
321
366
|
...logLevelArg
|
|
322
367
|
},
|
|
323
368
|
async run(ctx) {
|
|
324
|
-
const { runner } = await Promise.resolve().then(() => require("./snapshot-
|
|
369
|
+
const { runner } = await Promise.resolve().then(() => require("./snapshot-B4eWToMC.cjs"));
|
|
325
370
|
await runner(ctx);
|
|
326
371
|
}
|
|
327
372
|
});
|
|
328
373
|
//#endregion
|
|
329
374
|
//#region src/commands/studio/background.ts
|
|
330
|
-
const command$
|
|
375
|
+
const command$8 = studioCommand({
|
|
331
376
|
name: "start",
|
|
332
377
|
description: "Keep this project connected to Kubb Studio in the background.",
|
|
333
|
-
args: command$
|
|
378
|
+
args: command$12.args,
|
|
334
379
|
async action(options) {
|
|
335
|
-
const { startWorker } = await Promise.resolve().then(() => require("./background-
|
|
380
|
+
const { startWorker } = await Promise.resolve().then(() => require("./background-6xlebN_Y.cjs"));
|
|
336
381
|
await startWorker(options);
|
|
337
382
|
}
|
|
338
383
|
});
|
|
@@ -340,22 +385,22 @@ const stopCommand = (0, gunshi.define)({
|
|
|
340
385
|
name: "stop",
|
|
341
386
|
description: "Stop this project's background connection and keep its pairing.",
|
|
342
387
|
async run() {
|
|
343
|
-
const { stopWorker } = await Promise.resolve().then(() => require("./background-
|
|
388
|
+
const { stopWorker } = await Promise.resolve().then(() => require("./background-6xlebN_Y.cjs"));
|
|
344
389
|
await stopWorker();
|
|
345
390
|
console.log("Background connection stopped.");
|
|
346
391
|
}
|
|
347
392
|
});
|
|
348
393
|
//#endregion
|
|
349
394
|
//#region src/commands/studio/status.ts
|
|
350
|
-
const command$
|
|
395
|
+
const command$7 = studioCommand({
|
|
351
396
|
name: "status",
|
|
352
397
|
description: "Show the project worker, Kubb Studio pairing, and saved permissions.",
|
|
353
398
|
examples: "kubb studio status",
|
|
354
399
|
args: { url: studioConnectionArgs.url },
|
|
355
400
|
async action(options) {
|
|
356
|
-
const { getWorkerLogPath, getWorkerStatus } = await Promise.resolve().then(() => require("./background-
|
|
401
|
+
const { getWorkerLogPath, getWorkerStatus } = await Promise.resolve().then(() => require("./background-6xlebN_Y.cjs"));
|
|
357
402
|
const { readCredentials } = await Promise.resolve().then(() => require("./credentials-DDaKqtJn.cjs")).then((n) => n.credentials_exports);
|
|
358
|
-
const { formatStatus } = await Promise.resolve().then(() => require("./status-
|
|
403
|
+
const { formatStatus } = await Promise.resolve().then(() => require("./status-CteZmcug.cjs"));
|
|
359
404
|
const [worker, credentials] = await Promise.all([getWorkerStatus(), readCredentials()]);
|
|
360
405
|
console.log([
|
|
361
406
|
"",
|
|
@@ -372,8 +417,8 @@ const command$6 = studioCommand({
|
|
|
372
417
|
});
|
|
373
418
|
//#endregion
|
|
374
419
|
//#region src/commands/studio/index.ts
|
|
375
|
-
const command$
|
|
376
|
-
...command$
|
|
420
|
+
const command$6 = (0, gunshi.define)({
|
|
421
|
+
...command$12,
|
|
377
422
|
name: "studio",
|
|
378
423
|
description: "Connect this project to Kubb Studio, or manage its pairing and snapshots.",
|
|
379
424
|
examples: [
|
|
@@ -385,13 +430,13 @@ const command$5 = (0, gunshi.define)({
|
|
|
385
430
|
"kubb studio snapshot"
|
|
386
431
|
].join("\n"),
|
|
387
432
|
subCommands: {
|
|
388
|
-
start: command$
|
|
433
|
+
start: command$8,
|
|
389
434
|
stop: stopCommand,
|
|
390
|
-
connect: command$
|
|
391
|
-
login: command$
|
|
392
|
-
logout: command$
|
|
393
|
-
status: command$
|
|
394
|
-
snapshot: command$
|
|
435
|
+
connect: command$12,
|
|
436
|
+
login: command$11,
|
|
437
|
+
logout: command$10,
|
|
438
|
+
status: command$7,
|
|
439
|
+
snapshot: command$9
|
|
395
440
|
}
|
|
396
441
|
});
|
|
397
442
|
/**
|
|
@@ -410,7 +455,7 @@ function routeStudioFlags(args) {
|
|
|
410
455
|
//#endregion
|
|
411
456
|
//#region src/commands/validate.ts
|
|
412
457
|
/** The runner is imported when the command runs, so listing `kubb --help` never loads `@kubb/adapter-oas`. */
|
|
413
|
-
const command$
|
|
458
|
+
const command$5 = (0, gunshi.define)({
|
|
414
459
|
name: "validate",
|
|
415
460
|
description: "Check a specification with the configured adapter before generating.",
|
|
416
461
|
examples: ["kubb validate ./openapi.yaml", "kubb validate https://petstore3.swagger.io/api/v3/openapi.json"].join("\n"),
|
|
@@ -423,7 +468,7 @@ const command$4 = (0, gunshi.define)({
|
|
|
423
468
|
}
|
|
424
469
|
},
|
|
425
470
|
async run({ values }) {
|
|
426
|
-
const { run } = await Promise.resolve().then(() => require("./run-
|
|
471
|
+
const { run } = await Promise.resolve().then(() => require("./run-Bgr0F6xv.cjs"));
|
|
427
472
|
await run({
|
|
428
473
|
input: values.input,
|
|
429
474
|
configPath: values.config
|
|
@@ -480,6 +525,959 @@ export default defineConfig({
|
|
|
480
525
|
`;
|
|
481
526
|
}
|
|
482
527
|
//#endregion
|
|
528
|
+
//#region ../../internals/shared/src/configFile/ast.ts
|
|
529
|
+
/** Node types the config editor reads from the yuku AST. */
|
|
530
|
+
const nodeTypes = {
|
|
531
|
+
arrayExpression: "ArrayExpression",
|
|
532
|
+
arrowFunctionExpression: "ArrowFunctionExpression",
|
|
533
|
+
assignmentExpression: "AssignmentExpression",
|
|
534
|
+
blockStatement: "BlockStatement",
|
|
535
|
+
callExpression: "CallExpression",
|
|
536
|
+
exportDefaultDeclaration: "ExportDefaultDeclaration",
|
|
537
|
+
expressionStatement: "ExpressionStatement",
|
|
538
|
+
functionExpression: "FunctionExpression",
|
|
539
|
+
identifier: "Identifier",
|
|
540
|
+
importDeclaration: "ImportDeclaration",
|
|
541
|
+
importSpecifier: "ImportSpecifier",
|
|
542
|
+
literal: "Literal",
|
|
543
|
+
memberExpression: "MemberExpression",
|
|
544
|
+
objectExpression: "ObjectExpression",
|
|
545
|
+
objectPattern: "ObjectPattern",
|
|
546
|
+
property: "Property",
|
|
547
|
+
returnStatement: "ReturnStatement",
|
|
548
|
+
spreadElement: "SpreadElement",
|
|
549
|
+
tSAsExpression: "TSAsExpression",
|
|
550
|
+
tSSatisfiesExpression: "TSSatisfiesExpression",
|
|
551
|
+
templateLiteral: "TemplateLiteral",
|
|
552
|
+
unaryExpression: "UnaryExpression",
|
|
553
|
+
variableDeclaration: "VariableDeclaration"
|
|
554
|
+
};
|
|
555
|
+
const commentTypes = { line: "Line" };
|
|
556
|
+
const propertyKinds = { init: "init" };
|
|
557
|
+
const unaryOperators = {
|
|
558
|
+
minus: "-",
|
|
559
|
+
plus: "+"
|
|
560
|
+
};
|
|
561
|
+
const diagnosticSeverities = { error: "error" };
|
|
562
|
+
/**
|
|
563
|
+
* Parses `source` as a TypeScript module. Never throws: a syntax error, or a native parser that
|
|
564
|
+
* cannot load on this platform, comes back as a `reason`.
|
|
565
|
+
*/
|
|
566
|
+
function parseSource(source) {
|
|
567
|
+
try {
|
|
568
|
+
const { program, comments, diagnostics } = (0, yuku_parser.parse)(source, {
|
|
569
|
+
lang: "ts",
|
|
570
|
+
sourceType: "module",
|
|
571
|
+
preserveParens: false
|
|
572
|
+
});
|
|
573
|
+
if (diagnostics.some((diagnostic) => diagnostic.severity === diagnosticSeverities.error)) return { reason: "the config file could not be parsed" };
|
|
574
|
+
return {
|
|
575
|
+
program,
|
|
576
|
+
comments
|
|
577
|
+
};
|
|
578
|
+
} catch (error) {
|
|
579
|
+
return { reason: `the config parser could not load on this platform: ${require_errors.getErrorMessage(error)}` };
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
/** Parses `source` as one expression, such as a commented-out `pluginTs({ ... })` call. */
|
|
583
|
+
function parseExpression(source) {
|
|
584
|
+
const parsed = parseSource(source);
|
|
585
|
+
if ("reason" in parsed) return void 0;
|
|
586
|
+
const [statement] = parsed.program.body;
|
|
587
|
+
return parsed.program.body.length === 1 && statement?.type === nodeTypes.expressionStatement ? statement.expression : void 0;
|
|
588
|
+
}
|
|
589
|
+
//#endregion
|
|
590
|
+
//#region ../../internals/shared/src/configFile/types.ts
|
|
591
|
+
const extensionKinds = {
|
|
592
|
+
plugin: "plugin",
|
|
593
|
+
adapter: "adapter",
|
|
594
|
+
parser: "parser"
|
|
595
|
+
};
|
|
596
|
+
const ConfigEditOperation = {
|
|
597
|
+
SET_OPTION: "set-option",
|
|
598
|
+
REMOVE_OPTION: "remove-option",
|
|
599
|
+
ADD_PLUGIN: "add-plugin",
|
|
600
|
+
REMOVE_EXTENSION: "remove-extension",
|
|
601
|
+
DISABLE_PLUGIN: "disable-plugin",
|
|
602
|
+
ENABLE_PLUGIN: "enable-plugin",
|
|
603
|
+
SET_INPUT: "set-input",
|
|
604
|
+
SET_OUTPUT: "set-output",
|
|
605
|
+
REMOVE_OUTPUT: "remove-output"
|
|
606
|
+
};
|
|
607
|
+
//#endregion
|
|
608
|
+
//#region ../../internals/shared/src/configFile/rules.ts
|
|
609
|
+
/** `@kubb/plugin-ts` becomes `plugin-ts`, the `name` Kubb plugin factories use. */
|
|
610
|
+
function toPluginName(packageName) {
|
|
611
|
+
return packageName.split("/").pop() ?? packageName;
|
|
612
|
+
}
|
|
613
|
+
/** `@kubb/plugin-react-query` becomes `pluginReactQuery`, the export a plugin package uses by convention. */
|
|
614
|
+
function toExportName(packageName) {
|
|
615
|
+
return camelCase(toPluginName(packageName));
|
|
616
|
+
}
|
|
617
|
+
const KUBB_PLUGIN_SPECIFIER = /^@kubb\/plugin-[\w.-]+$/;
|
|
618
|
+
/** Only a `@kubb/plugin-*` package or an installed extension of `kind` may be imported, never a path or URL. */
|
|
619
|
+
function isAllowedPlugin({ packageName, extensions, kind = extensionKinds.plugin }) {
|
|
620
|
+
if (!(0, _kubb_core.isPackageName)(packageName)) return false;
|
|
621
|
+
return kind === extensionKinds.plugin && KUBB_PLUGIN_SPECIFIER.test(packageName) || extensions.get(packageName)?.manifest.kind === kind;
|
|
622
|
+
}
|
|
623
|
+
const UNSAFE_KEYS = /* @__PURE__ */ new Set([
|
|
624
|
+
"__proto__",
|
|
625
|
+
"constructor",
|
|
626
|
+
"prototype"
|
|
627
|
+
]);
|
|
628
|
+
/** Whether `key` can be an option key without reaching the object's prototype. */
|
|
629
|
+
function isSafeKey(key) {
|
|
630
|
+
return !UNSAFE_KEYS.has(key);
|
|
631
|
+
}
|
|
632
|
+
function isPlainObject(value) {
|
|
633
|
+
if (typeof value !== "object" || value === null) return false;
|
|
634
|
+
const prototype = Object.getPrototypeOf(value);
|
|
635
|
+
return prototype === null || prototype === Object.prototype;
|
|
636
|
+
}
|
|
637
|
+
/** Whether `value` survives a JSON round trip, so an edit never writes a lossy copy into the file. */
|
|
638
|
+
function isOptionValue(value) {
|
|
639
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
640
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
641
|
+
if (Array.isArray(value)) return value.every(isOptionValue);
|
|
642
|
+
if (isPlainObject(value)) return Object.entries(value).every(([key, entry]) => isSafeKey(key) && isOptionValue(entry));
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
/** The root `output` members Studio may set, with the values each takes. */
|
|
646
|
+
const OUTPUT_VALUES = {
|
|
647
|
+
barrel: (value) => value === false || isPlainObject(value) && Object.keys(value).length === 1 && (value.type === "named" || value.type === "all"),
|
|
648
|
+
format: (value) => value === false || [
|
|
649
|
+
"auto",
|
|
650
|
+
"prettier",
|
|
651
|
+
"biome",
|
|
652
|
+
"oxfmt"
|
|
653
|
+
].includes(value),
|
|
654
|
+
lint: (value) => value === false || [
|
|
655
|
+
"auto",
|
|
656
|
+
"eslint",
|
|
657
|
+
"biome",
|
|
658
|
+
"oxlint"
|
|
659
|
+
].includes(value),
|
|
660
|
+
clean: (value) => typeof value === "boolean",
|
|
661
|
+
defaultBanner: (value) => value === false || value === "simple" || value === "full"
|
|
662
|
+
};
|
|
663
|
+
function isOutputKey(key) {
|
|
664
|
+
return Object.hasOwn(OUTPUT_VALUES, key);
|
|
665
|
+
}
|
|
666
|
+
function isOutputValue({ key, value }) {
|
|
667
|
+
return isOutputKey(key) && OUTPUT_VALUES[key](value);
|
|
668
|
+
}
|
|
669
|
+
function pickOutput(output) {
|
|
670
|
+
return Object.fromEntries(Object.entries(output).filter(([key]) => isOutputKey(key)));
|
|
671
|
+
}
|
|
672
|
+
//#endregion
|
|
673
|
+
//#region ../../internals/shared/src/configFile/text.ts
|
|
674
|
+
/**
|
|
675
|
+
* A valid JavaScript identifier, so an import name can only ever print as `import { name } from`,
|
|
676
|
+
* never as source that breaks out of the import statement.
|
|
677
|
+
*/
|
|
678
|
+
const IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/;
|
|
679
|
+
const quotes = {
|
|
680
|
+
single: `'`,
|
|
681
|
+
double: `"`
|
|
682
|
+
};
|
|
683
|
+
/** `source` with the text between `start` and `end` replaced by `text`. */
|
|
684
|
+
function replaceText(source, start, end, text = "") {
|
|
685
|
+
return `${source.slice(0, start)}${text}${source.slice(end)}`;
|
|
686
|
+
}
|
|
687
|
+
/** The 1-based line and 0-based column of `offset` in `source`. */
|
|
688
|
+
function getPosition(source, offset) {
|
|
689
|
+
return {
|
|
690
|
+
line: source.slice(0, offset).split("\n").length,
|
|
691
|
+
column: offset - source.lastIndexOf("\n", offset - 1) - 1
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
/** Writes `value` as a single-line literal with the file's quote style. */
|
|
695
|
+
function printValue(value, quote) {
|
|
696
|
+
if (typeof value === "string") {
|
|
697
|
+
const escaped = JSON.stringify(value).slice(1, -1);
|
|
698
|
+
return quote === quotes.double ? `"${escaped}"` : `'${escaped.replaceAll(`\\"`, `"`).replaceAll(`'`, `\\'`)}'`;
|
|
699
|
+
}
|
|
700
|
+
if (value === null || typeof value !== "object") return String(value);
|
|
701
|
+
if (Array.isArray(value)) return `[${value.map((item) => printValue(item, quote)).join(", ")}]`;
|
|
702
|
+
const entries = Object.entries(value);
|
|
703
|
+
return entries.length ? `{ ${entries.map(([key, item]) => `${printKey(key, quote)}: ${printValue(item, quote)}`).join(", ")} }` : "{}";
|
|
704
|
+
}
|
|
705
|
+
/** An object key as source text: bare when it is an identifier, quoted otherwise. */
|
|
706
|
+
function printKey(key, quote) {
|
|
707
|
+
return IDENTIFIER_PATTERN.test(key) ? key : printValue(key, quote);
|
|
708
|
+
}
|
|
709
|
+
/** `value` wrapped in one object per key of `path`: `wrapPath(['enum', 'type'], 'enum')` is `{ enum: { type: 'enum' } }`. */
|
|
710
|
+
function wrapPath(path, value) {
|
|
711
|
+
return path.reduceRight((inner, key) => ({ [key]: inner }), value);
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Adds `text` as the last item of an object or array literal, following its layout: on a line of
|
|
715
|
+
* its own with the last item's indentation when the literal spans lines, inline otherwise.
|
|
716
|
+
*/
|
|
717
|
+
function appendItem(source, container, text) {
|
|
718
|
+
const { start, end } = container;
|
|
719
|
+
const last = (container.type === nodeTypes.objectExpression ? container.properties : container.elements).filter((item) => item !== null).at(-1);
|
|
720
|
+
if (!last) {
|
|
721
|
+
const inner = source.slice(start + 1, end - 1);
|
|
722
|
+
if (inner.trim()) return replaceText(source, start + 1, start + 1, /^\s/.test(inner) ? `${text},` : `${text}, `);
|
|
723
|
+
return replaceText(source, start, end, container.type === nodeTypes.objectExpression ? `{ ${text} }` : `[${text}]`);
|
|
724
|
+
}
|
|
725
|
+
const between = source.slice(last.end, end - 1);
|
|
726
|
+
if (!between.includes("\n")) return replaceText(source, last.end, last.end, `, ${text}`);
|
|
727
|
+
const lineStart = source.lastIndexOf("\n", last.start - 1) + 1;
|
|
728
|
+
const indent = source.slice(lineStart).match(/^[ \t]*/)?.[0] ?? "";
|
|
729
|
+
const lineEnd = source.indexOf("\n", last.end);
|
|
730
|
+
const comma = /^\s*,/.test(between);
|
|
731
|
+
return `${source.slice(0, last.end)}${comma ? "" : ","}${source.slice(last.end, lineEnd)}\n${indent}${text}${comma ? "," : ""}${source.slice(lineEnd)}`;
|
|
732
|
+
}
|
|
733
|
+
/** Drops the item at `index` from an object or array literal, with its own lines when it sits on them alone. */
|
|
734
|
+
function removeItem(source, node, index) {
|
|
735
|
+
const items = node.type === nodeTypes.objectExpression ? node.properties : node.elements.filter((element) => element !== null);
|
|
736
|
+
if (items.length === 1) return replaceText(source, node.start, node.end, node.type === nodeTypes.objectExpression ? "{}" : "[]");
|
|
737
|
+
const { start, end } = items[index];
|
|
738
|
+
const lineStart = source.lastIndexOf("\n", start - 1) + 1;
|
|
739
|
+
const nextNewline = source.indexOf("\n", end);
|
|
740
|
+
const lineEnd = nextNewline === -1 ? source.length : nextNewline;
|
|
741
|
+
if (!source.slice(lineStart, start).trim() && /^\s*,?\s*(\/\/.*)?$/.test(source.slice(end, lineEnd))) return replaceText(source, lineStart, Math.min(lineEnd + 1, source.length));
|
|
742
|
+
const next = items[index + 1];
|
|
743
|
+
return next ? replaceText(source, start, next.start) : replaceText(source, items[index - 1].end, end);
|
|
744
|
+
}
|
|
745
|
+
//#endregion
|
|
746
|
+
//#region ../../internals/shared/src/configFile/configFile.ts
|
|
747
|
+
/** The marker `disable-plugin` writes, with the block's line count so `enable-plugin` restores exactly those lines. */
|
|
748
|
+
const DISABLED_MARKER_PATTERN = /^kubb:disabled (.+)\s+(\d+)$/;
|
|
749
|
+
const OPERATIONS = new Set(Object.values(ConfigEditOperation));
|
|
750
|
+
const OPTION_OPERATIONS = /* @__PURE__ */ new Set([ConfigEditOperation.SET_OPTION, ConfigEditOperation.REMOVE_OPTION]);
|
|
751
|
+
const OUTPUT_OPERATIONS = /* @__PURE__ */ new Set([ConfigEditOperation.SET_OUTPUT, ConfigEditOperation.REMOVE_OUTPUT]);
|
|
752
|
+
const KINDS = new Set(Object.values(extensionKinds));
|
|
753
|
+
/** Identifiers the editor matches by name. */
|
|
754
|
+
const identifiers = {
|
|
755
|
+
defineConfig: "defineConfig",
|
|
756
|
+
require: "require",
|
|
757
|
+
module: "module",
|
|
758
|
+
exports: "exports"
|
|
759
|
+
};
|
|
760
|
+
const moduleKinds = {
|
|
761
|
+
esm: "esm",
|
|
762
|
+
cjs: "cjs"
|
|
763
|
+
};
|
|
764
|
+
function isNonEmptyString(value) {
|
|
765
|
+
return typeof value === "string" && value !== "";
|
|
766
|
+
}
|
|
767
|
+
/** Edits cross the wire, so their shape is checked before anything reads them. */
|
|
768
|
+
function validateEdit(edit) {
|
|
769
|
+
const { operation, plugin, config, path, kind, input } = typeof edit === "object" && edit !== null ? edit : {};
|
|
770
|
+
if (typeof operation !== "string" || !OPERATIONS.has(operation)) return `unknown operation ${JSON.stringify(operation)}`;
|
|
771
|
+
if (config !== void 0 && typeof config !== "string" && !Number.isSafeInteger(config)) return "config must name an entry or give its index";
|
|
772
|
+
if (operation === ConfigEditOperation.SET_INPUT) return typeof input === "string" && input.trim() !== "" ? void 0 : "the edit names no input";
|
|
773
|
+
const isOption = OPTION_OPERATIONS.has(operation);
|
|
774
|
+
const isOutput = OUTPUT_OPERATIONS.has(operation);
|
|
775
|
+
if (!isOutput && !isNonEmptyString(plugin)) return "the edit names no plugin";
|
|
776
|
+
if (kind !== void 0) {
|
|
777
|
+
if (typeof kind !== "string" || !KINDS.has(kind)) return `unknown kind ${JSON.stringify(kind)}`;
|
|
778
|
+
const takesKind = operation === ConfigEditOperation.ADD_PLUGIN || operation === ConfigEditOperation.REMOVE_EXTENSION;
|
|
779
|
+
if (takesKind && kind === extensionKinds.adapter) return `${operation} takes a plugin or a parser, since a config has one adapter`;
|
|
780
|
+
if (!isOption && !takesKind) return `${operation} edits the plugins array, so it takes no kind`;
|
|
781
|
+
}
|
|
782
|
+
if (!isOption && !isOutput) return void 0;
|
|
783
|
+
if (!Array.isArray(path) || !path.every(isNonEmptyString)) return "path must be a list of option names";
|
|
784
|
+
if (path.length === 0) return "no option path given";
|
|
785
|
+
const unsafe = path.find((key) => !isSafeKey(key));
|
|
786
|
+
if (unsafe !== void 0) return `${unsafe} cannot be used as an option name`;
|
|
787
|
+
if (isOutput && !isOutputKey(path[0])) return `output.${path[0]} can only be changed in the config file`;
|
|
788
|
+
}
|
|
789
|
+
/** Steps through `as`/`satisfies` and `() => ...` wrappers to the config object. */
|
|
790
|
+
function unwrapValue(node) {
|
|
791
|
+
if (!node) return void 0;
|
|
792
|
+
if (node.type === nodeTypes.tSAsExpression || node.type === nodeTypes.tSSatisfiesExpression) return unwrapValue(node.expression);
|
|
793
|
+
if (node.type !== nodeTypes.arrowFunctionExpression && node.type !== nodeTypes.functionExpression) return node;
|
|
794
|
+
if (node.body?.type !== nodeTypes.blockStatement) return unwrapValue(node.body);
|
|
795
|
+
const returned = node.body.body.find((statement) => statement.type === nodeTypes.returnStatement);
|
|
796
|
+
return unwrapValue(returned?.type === nodeTypes.returnStatement ? returned.argument : void 0);
|
|
797
|
+
}
|
|
798
|
+
function isModuleExports(node) {
|
|
799
|
+
return node.type === nodeTypes.memberExpression && !node.computed && node.object.type === nodeTypes.identifier && node.object.name === identifiers.module && node.property.type === nodeTypes.identifier && node.property.name === identifiers.exports;
|
|
800
|
+
}
|
|
801
|
+
/** The exported config value and how the file exports it. */
|
|
802
|
+
function findExport(program) {
|
|
803
|
+
for (const node of program.body) {
|
|
804
|
+
if (node.type === nodeTypes.exportDefaultDeclaration) return {
|
|
805
|
+
exported: node.declaration,
|
|
806
|
+
kind: moduleKinds.esm
|
|
807
|
+
};
|
|
808
|
+
if (node.type === nodeTypes.expressionStatement && node.expression.type === nodeTypes.assignmentExpression && isModuleExports(node.expression.left)) return {
|
|
809
|
+
exported: node.expression.right,
|
|
810
|
+
kind: moduleKinds.cjs
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
return {
|
|
814
|
+
exported: void 0,
|
|
815
|
+
kind: moduleKinds.esm
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
/** Every config object in `export default` or `module.exports = defineConfig(...)`, or why the file is unmanaged. */
|
|
819
|
+
function findConfigs(source) {
|
|
820
|
+
const parsed = parseSource(source);
|
|
821
|
+
if ("reason" in parsed) return parsed;
|
|
822
|
+
const { exported, kind } = findExport(parsed.program);
|
|
823
|
+
const value = unwrapValue(exported);
|
|
824
|
+
if (!value) return { reason: "no `export default` or `module.exports` found" };
|
|
825
|
+
if (value.type !== nodeTypes.callExpression || value.callee.type !== nodeTypes.identifier || value.callee.name !== identifiers.defineConfig) return { reason: "the exported config is not a defineConfig(...) call" };
|
|
826
|
+
const argument = unwrapValue(value.arguments[0]);
|
|
827
|
+
if (!argument) return { reason: "defineConfig(...) was called without a config" };
|
|
828
|
+
const entries = argument.type === nodeTypes.arrayExpression ? argument.elements.map((element) => unwrapValue(element)) : [argument];
|
|
829
|
+
const configs = entries.filter((entry) => entry?.type === nodeTypes.objectExpression);
|
|
830
|
+
if (configs.length !== entries.length) return { reason: "config is not an object literal" };
|
|
831
|
+
return {
|
|
832
|
+
...parsed,
|
|
833
|
+
configs,
|
|
834
|
+
kind
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
function findConfigEntry(configs, ref) {
|
|
838
|
+
return typeof ref === "string" ? configs.find((entry) => getConfigName(entry) === ref) : configs[ref ?? 0];
|
|
839
|
+
}
|
|
840
|
+
function getKey(entry) {
|
|
841
|
+
if (entry.type !== nodeTypes.property || entry.computed) return void 0;
|
|
842
|
+
if (entry.key.type === nodeTypes.identifier) return entry.key.name;
|
|
843
|
+
return entry.key.type === nodeTypes.literal && typeof entry.key.value === "string" ? entry.key.value : void 0;
|
|
844
|
+
}
|
|
845
|
+
/** The last one wins when the key repeats, as at runtime. */
|
|
846
|
+
function findProperty(node, key) {
|
|
847
|
+
const index = node.properties.findLastIndex((entry) => getKey(entry) === key);
|
|
848
|
+
return index === -1 ? void 0 : {
|
|
849
|
+
member: node.properties[index],
|
|
850
|
+
index
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
function getValue(node, key) {
|
|
854
|
+
const member = findProperty(node, key)?.member;
|
|
855
|
+
return member?.type === nodeTypes.property ? member.value : member;
|
|
856
|
+
}
|
|
857
|
+
function getConfigName(config) {
|
|
858
|
+
const name = getValue(config, "name");
|
|
859
|
+
return name?.type === nodeTypes.literal && typeof name.value === "string" ? name.value : void 0;
|
|
860
|
+
}
|
|
861
|
+
function isOverridden(node, index) {
|
|
862
|
+
return node.properties.slice(index + 1).some((entry) => entry.type === nodeTypes.spreadElement);
|
|
863
|
+
}
|
|
864
|
+
/** `key: value`, not a method, getter, setter, or `{ key }` shorthand. */
|
|
865
|
+
function isPlainProperty(member) {
|
|
866
|
+
return !member.method && !member.shorthand && member.kind === propertyKinds.init;
|
|
867
|
+
}
|
|
868
|
+
/** A literal's value, `undefined` for code. */
|
|
869
|
+
function readLiteral(node) {
|
|
870
|
+
switch (node?.type) {
|
|
871
|
+
case nodeTypes.literal: return "regex" in node || "bigint" in node ? void 0 : node.value;
|
|
872
|
+
case nodeTypes.templateLiteral: return node.expressions.length ? void 0 : node.quasis[0]?.value.cooked ?? "";
|
|
873
|
+
case nodeTypes.unaryExpression: {
|
|
874
|
+
const value = readLiteral(node.argument);
|
|
875
|
+
if (typeof value !== "number") return void 0;
|
|
876
|
+
if (node.operator === unaryOperators.minus) return -value;
|
|
877
|
+
if (node.operator === unaryOperators.plus) return value;
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
case nodeTypes.arrayExpression: {
|
|
881
|
+
const values = node.elements.map((element) => readLiteral(element));
|
|
882
|
+
return values.every((value) => value !== void 0) ? values : void 0;
|
|
883
|
+
}
|
|
884
|
+
case nodeTypes.objectExpression: {
|
|
885
|
+
const entries = {};
|
|
886
|
+
for (const entry of node.properties) {
|
|
887
|
+
if (entry.type !== nodeTypes.property) return void 0;
|
|
888
|
+
const key = getKey(entry);
|
|
889
|
+
const value = readLiteral(entry.value);
|
|
890
|
+
if (key === void 0 || !isSafeKey(key) || value === void 0) return void 0;
|
|
891
|
+
entries[key] = value;
|
|
892
|
+
}
|
|
893
|
+
return entries;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
function getRequirePath(node) {
|
|
898
|
+
if (node?.type !== nodeTypes.callExpression || node.callee.type !== nodeTypes.identifier || node.callee.name !== identifiers.require) return void 0;
|
|
899
|
+
const [argument] = node.arguments;
|
|
900
|
+
return argument?.type === nodeTypes.literal && typeof argument.value === "string" ? argument.value : void 0;
|
|
901
|
+
}
|
|
902
|
+
/** `import { exported as local } from '...'`. A default or namespace import has no exported name, so its local name stands in. */
|
|
903
|
+
function importBindings(statement) {
|
|
904
|
+
return statement.specifiers.map((specifier) => {
|
|
905
|
+
const imported = specifier.type === nodeTypes.importSpecifier ? specifier.imported : specifier.local;
|
|
906
|
+
const exported = imported.type === nodeTypes.identifier ? imported.name : imported.value;
|
|
907
|
+
return [specifier.local.name, {
|
|
908
|
+
from: statement.source.value,
|
|
909
|
+
exported
|
|
910
|
+
}];
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
/** `const { exported: local } = require('...')`. */
|
|
914
|
+
function requireBindings(statement) {
|
|
915
|
+
return statement.declarations.flatMap((declarator) => {
|
|
916
|
+
const from = getRequirePath(declarator.init);
|
|
917
|
+
if (from === void 0 || declarator.id.type !== nodeTypes.objectPattern) return [];
|
|
918
|
+
return declarator.id.properties.flatMap((entry) => {
|
|
919
|
+
if (entry.type !== nodeTypes.property || entry.value.type !== nodeTypes.identifier) return [];
|
|
920
|
+
return [[entry.value.name, {
|
|
921
|
+
from,
|
|
922
|
+
exported: getKey(entry) ?? entry.value.name
|
|
923
|
+
}]];
|
|
924
|
+
});
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
function getImports(program) {
|
|
928
|
+
return new Map(program.body.flatMap((statement) => {
|
|
929
|
+
if (statement.type === nodeTypes.importDeclaration) return importBindings(statement);
|
|
930
|
+
if (statement.type === nodeTypes.variableDeclaration) return requireBindings(statement);
|
|
931
|
+
return [];
|
|
932
|
+
}));
|
|
933
|
+
}
|
|
934
|
+
function isImportStatement(node, kind) {
|
|
935
|
+
if (kind === moduleKinds.esm) return node.type === nodeTypes.importDeclaration;
|
|
936
|
+
return node.type === nodeTypes.variableDeclaration && node.declarations.some((declarator) => getRequirePath(declarator.init) !== void 0);
|
|
937
|
+
}
|
|
938
|
+
/** The quote of the first string literal in the file, so new code matches it. */
|
|
939
|
+
function detectQuote(node) {
|
|
940
|
+
if (!node || typeof node !== "object") return void 0;
|
|
941
|
+
const { type, raw } = node;
|
|
942
|
+
if (type === nodeTypes.literal && typeof raw === "string" && (raw[0] === quotes.single || raw[0] === quotes.double)) return raw[0];
|
|
943
|
+
for (const child of Object.values(node)) {
|
|
944
|
+
const quote = detectQuote(child);
|
|
945
|
+
if (quote) return quote;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
/** The `adapter` value, or the elements of the `plugins` or `parsers` array. */
|
|
949
|
+
function factoryNodes(config, kind) {
|
|
950
|
+
if (kind === extensionKinds.adapter) return [getValue(config, "adapter")];
|
|
951
|
+
const array = getValue(config, `${kind}s`);
|
|
952
|
+
return array?.type === nodeTypes.arrayExpression ? array.elements : [];
|
|
953
|
+
}
|
|
954
|
+
/** The factory calls of one kind whose callee the file imports. */
|
|
955
|
+
function findCalls(program, config, kind = extensionKinds.plugin) {
|
|
956
|
+
const imports = getImports(program);
|
|
957
|
+
return factoryNodes(config, kind).flatMap((node) => {
|
|
958
|
+
if (node?.type !== nodeTypes.callExpression || node.callee.type !== nodeTypes.identifier) return [];
|
|
959
|
+
const imported = imports.get(node.callee.name);
|
|
960
|
+
return imported ? [{
|
|
961
|
+
importName: node.callee.name,
|
|
962
|
+
exportName: imported.exported,
|
|
963
|
+
packageName: imported.from,
|
|
964
|
+
call: node
|
|
965
|
+
}] : [];
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
/** Whether nothing but whitespace precedes `offset` on its line. */
|
|
969
|
+
function startsLine(source, offset) {
|
|
970
|
+
return source.slice(source.lastIndexOf("\n", offset - 1) + 1, offset).trim() === "";
|
|
971
|
+
}
|
|
972
|
+
/** The `disable-plugin` blocks in this config, read from marker comments since the call is no longer a node. */
|
|
973
|
+
function findDisabled(source, comments, config) {
|
|
974
|
+
const lines = source.split("\n");
|
|
975
|
+
return comments.flatMap((comment) => {
|
|
976
|
+
const match = comment.type === commentTypes.line ? comment.value.trim().match(DISABLED_MARKER_PATTERN) : null;
|
|
977
|
+
if (!match || comment.start < config.start || comment.end > config.end || !startsLine(source, comment.start)) return [];
|
|
978
|
+
const line = getPosition(source, comment.start).line;
|
|
979
|
+
const end = line + Number(match[2]);
|
|
980
|
+
const code = lines.slice(line, end).map((text) => text.replace(/^(\s*)\/\/ ?/, "$1").replace(/^\s+$/, ""));
|
|
981
|
+
return [{
|
|
982
|
+
packageName: match[1],
|
|
983
|
+
line,
|
|
984
|
+
end,
|
|
985
|
+
code
|
|
986
|
+
}];
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
/** Literal members as `value` and code paths as `locked`, `undefined` for a computed key. */
|
|
990
|
+
function readEditable(node, prefix = []) {
|
|
991
|
+
const value = {};
|
|
992
|
+
const locked = [];
|
|
993
|
+
for (const [index, entry] of node.properties.entries()) {
|
|
994
|
+
if (entry.type === nodeTypes.spreadElement) continue;
|
|
995
|
+
const key = getKey(entry);
|
|
996
|
+
if (key === void 0) return void 0;
|
|
997
|
+
if (findProperty(node, key)?.index !== index) continue;
|
|
998
|
+
const path = [...prefix, key];
|
|
999
|
+
if (!isSafeKey(key)) {
|
|
1000
|
+
locked.push(path);
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
const member = isOverridden(node, index) ? void 0 : entry.value;
|
|
1004
|
+
const literal = readLiteral(member);
|
|
1005
|
+
if (literal !== void 0) {
|
|
1006
|
+
value[key] = literal;
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
if (member?.type !== nodeTypes.objectExpression) {
|
|
1010
|
+
locked.push(path);
|
|
1011
|
+
continue;
|
|
1012
|
+
}
|
|
1013
|
+
const nested = readEditable(member, path);
|
|
1014
|
+
if (!nested) return void 0;
|
|
1015
|
+
value[key] = nested.value;
|
|
1016
|
+
locked.push(...nested.locked);
|
|
1017
|
+
}
|
|
1018
|
+
return {
|
|
1019
|
+
value,
|
|
1020
|
+
locked
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
function readObjectOptions(options) {
|
|
1024
|
+
const entries = {};
|
|
1025
|
+
for (const [index, entry] of options.properties.entries()) {
|
|
1026
|
+
const key = getKey(entry);
|
|
1027
|
+
if (key === void 0 || !isSafeKey(key)) continue;
|
|
1028
|
+
const member = entry.type === nodeTypes.property && !isOverridden(options, index) ? entry.value : void 0;
|
|
1029
|
+
const value = readLiteral(member);
|
|
1030
|
+
if (value !== void 0) {
|
|
1031
|
+
entries[key] = {
|
|
1032
|
+
literal: true,
|
|
1033
|
+
value
|
|
1034
|
+
};
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
entries[key] = {
|
|
1038
|
+
literal: false,
|
|
1039
|
+
...member?.type === nodeTypes.objectExpression ? readEditable(member) : void 0
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
return entries;
|
|
1043
|
+
}
|
|
1044
|
+
function readCallOptions(call) {
|
|
1045
|
+
const options = call.arguments[0];
|
|
1046
|
+
return options?.type === nodeTypes.objectExpression ? readObjectOptions(options) : {};
|
|
1047
|
+
}
|
|
1048
|
+
function readDisabledOptions(code) {
|
|
1049
|
+
const call = parseExpression(code.join("\n").trim().replace(/,$/, ""));
|
|
1050
|
+
return call?.type === nodeTypes.callExpression ? readCallOptions(call) : {};
|
|
1051
|
+
}
|
|
1052
|
+
function toPluginView({ importName, packageName, call }) {
|
|
1053
|
+
return {
|
|
1054
|
+
importName,
|
|
1055
|
+
packageName,
|
|
1056
|
+
options: readCallOptions(call)
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
/** Active and disabled plugins, in source order so a disabled plugin keeps its place. */
|
|
1060
|
+
function readPlugins({ source, parsed, config }) {
|
|
1061
|
+
const importNames = new Map([...getImports(parsed.program)].map(([local, { from }]) => [from, local]));
|
|
1062
|
+
const active = findCalls(parsed.program, config).map((call) => ({
|
|
1063
|
+
line: getPosition(source, call.call.start).line,
|
|
1064
|
+
plugin: toPluginView(call)
|
|
1065
|
+
}));
|
|
1066
|
+
const disabled = findDisabled(source, parsed.comments, config).map(({ packageName, line, code }) => ({
|
|
1067
|
+
line,
|
|
1068
|
+
plugin: {
|
|
1069
|
+
importName: importNames.get(packageName) ?? toExportName(packageName),
|
|
1070
|
+
packageName,
|
|
1071
|
+
options: readDisabledOptions(code),
|
|
1072
|
+
disabled: true
|
|
1073
|
+
}
|
|
1074
|
+
}));
|
|
1075
|
+
return [...active, ...disabled].sort((a, b) => a.line - b.line).map(({ plugin }) => plugin);
|
|
1076
|
+
}
|
|
1077
|
+
function readConfigView({ source, parsed, config }) {
|
|
1078
|
+
const [adapter] = findCalls(parsed.program, config, extensionKinds.adapter).map(toPluginView);
|
|
1079
|
+
const parsers = findCalls(parsed.program, config, extensionKinds.parser).map(toPluginView);
|
|
1080
|
+
const output = getOutput(config);
|
|
1081
|
+
return {
|
|
1082
|
+
name: getConfigName(config),
|
|
1083
|
+
plugins: readPlugins({
|
|
1084
|
+
source,
|
|
1085
|
+
parsed,
|
|
1086
|
+
config
|
|
1087
|
+
}),
|
|
1088
|
+
...adapter && { adapter },
|
|
1089
|
+
...parsers.length ? { parsers } : {},
|
|
1090
|
+
...!("reason" in output) && { output: pickOutput(readObjectOptions(output.object)) }
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
/** Which plugins the file declares and which options Studio may write. */
|
|
1094
|
+
function readConfig(source) {
|
|
1095
|
+
const parsed = findConfigs(source);
|
|
1096
|
+
if ("reason" in parsed) return {
|
|
1097
|
+
managed: false,
|
|
1098
|
+
reason: parsed.reason
|
|
1099
|
+
};
|
|
1100
|
+
return {
|
|
1101
|
+
managed: true,
|
|
1102
|
+
configs: parsed.configs.map((config) => readConfigView({
|
|
1103
|
+
source,
|
|
1104
|
+
parsed,
|
|
1105
|
+
config
|
|
1106
|
+
}))
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
/** The modules the file imports, empty when it cannot be parsed. */
|
|
1110
|
+
function readImports(source) {
|
|
1111
|
+
const parsed = parseSource(source);
|
|
1112
|
+
if ("reason" in parsed) return [];
|
|
1113
|
+
return [...new Set([...getImports(parsed.program).values()].map(({ from }) => from))];
|
|
1114
|
+
}
|
|
1115
|
+
/** Follows `path` to its literal, or to the first missing key. Refuses a value set in code. */
|
|
1116
|
+
function findPath(options, path) {
|
|
1117
|
+
let object = options;
|
|
1118
|
+
for (const [depth, key] of path.entries()) {
|
|
1119
|
+
const found = findProperty(object, key);
|
|
1120
|
+
if (!found) return {
|
|
1121
|
+
object,
|
|
1122
|
+
missing: depth
|
|
1123
|
+
};
|
|
1124
|
+
if (isOverridden(object, found.index)) return { reason: `${path.slice(0, depth + 1).join(".")} can be overridden by a spread after it, so it is customized in code` };
|
|
1125
|
+
const { member, index } = found;
|
|
1126
|
+
if (depth === path.length - 1) return member.type === nodeTypes.property && readLiteral(member.value) !== void 0 ? {
|
|
1127
|
+
object,
|
|
1128
|
+
index,
|
|
1129
|
+
value: member.value
|
|
1130
|
+
} : { reason: `${path.join(".")} is customized in code` };
|
|
1131
|
+
if (member.type !== nodeTypes.property || member.value.type !== nodeTypes.objectExpression) return { reason: `${key} is not an object, so ${path.join(".")} cannot be reached` };
|
|
1132
|
+
object = member.value;
|
|
1133
|
+
}
|
|
1134
|
+
return { reason: "no option path given" };
|
|
1135
|
+
}
|
|
1136
|
+
function setAtPath({ source, options, path, value, quote }) {
|
|
1137
|
+
const target = findPath(options, path);
|
|
1138
|
+
if ("reason" in target) return target;
|
|
1139
|
+
if ("missing" in target) {
|
|
1140
|
+
const [key, ...rest] = path.slice(target.missing);
|
|
1141
|
+
return { source: appendItem(source, target.object, `${printKey(key, quote)}: ${printValue(wrapPath(rest, value), quote)}`) };
|
|
1142
|
+
}
|
|
1143
|
+
return { source: replaceText(source, target.value.start, target.value.end, printValue(value, quote)) };
|
|
1144
|
+
}
|
|
1145
|
+
function removeAtPath({ source, options, path }) {
|
|
1146
|
+
const target = findPath(options, path);
|
|
1147
|
+
if ("reason" in target) return target;
|
|
1148
|
+
if ("missing" in target) return { source };
|
|
1149
|
+
return { source: removeItem(source, target.object, target.index) };
|
|
1150
|
+
}
|
|
1151
|
+
function setOption(source, call, path, value, quote) {
|
|
1152
|
+
if (!isOptionValue(value)) return { reason: "the value is not a literal that can be written to a config file" };
|
|
1153
|
+
const options = call.arguments[0];
|
|
1154
|
+
if (!options) {
|
|
1155
|
+
const closing = call.end - 1;
|
|
1156
|
+
if (source[closing] !== ")") return { reason: "the factory call could not be located in the file" };
|
|
1157
|
+
return { source: replaceText(source, closing, closing, printValue(wrapPath(path, value), quote)) };
|
|
1158
|
+
}
|
|
1159
|
+
if (options.type !== nodeTypes.objectExpression) return { reason: "the factory was not called with an object literal" };
|
|
1160
|
+
return setAtPath({
|
|
1161
|
+
source,
|
|
1162
|
+
options,
|
|
1163
|
+
path,
|
|
1164
|
+
value,
|
|
1165
|
+
quote
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
function removeOption(source, call, path) {
|
|
1169
|
+
const options = call.arguments[0];
|
|
1170
|
+
if (!options) return { source };
|
|
1171
|
+
if (options.type !== nodeTypes.objectExpression) return { reason: "the factory was not called with an object literal" };
|
|
1172
|
+
return removeAtPath({
|
|
1173
|
+
source,
|
|
1174
|
+
options,
|
|
1175
|
+
path
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
function editOption({ source, parsed, config, edit, quote, extensions }) {
|
|
1179
|
+
const kind = edit.kind ?? extensionKinds.plugin;
|
|
1180
|
+
const calls = findCalls(parsed.program, config, kind).filter((entry) => entry.packageName === edit.plugin);
|
|
1181
|
+
const exportName = extensions.get(edit.plugin)?.manifest.export ?? toExportName(edit.plugin);
|
|
1182
|
+
const call = (calls.find((entry) => entry.exportName === exportName) ?? calls[0])?.call;
|
|
1183
|
+
if (!call) {
|
|
1184
|
+
if (kind === extensionKinds.adapter) return { reason: `${edit.plugin} is not the config's adapter` };
|
|
1185
|
+
return { reason: kind === extensionKinds.plugin && findDisabled(source, parsed.comments, config).some((block) => block.packageName === edit.plugin) ? `${edit.plugin} is disabled, enable it first` : `${edit.plugin} is not in the ${kind}s array` };
|
|
1186
|
+
}
|
|
1187
|
+
if (edit.operation === ConfigEditOperation.SET_OPTION) return setOption(source, call, edit.path, edit.value, quote);
|
|
1188
|
+
return removeOption(source, call, edit.path);
|
|
1189
|
+
}
|
|
1190
|
+
function setInput(source, config, input, quote) {
|
|
1191
|
+
const value = printValue(input, quote);
|
|
1192
|
+
const found = findProperty(config, "input");
|
|
1193
|
+
if (!found) return { source: appendItem(source, config, `input: ${value}`) };
|
|
1194
|
+
if (found.member.type !== nodeTypes.property || !isPlainProperty(found.member)) return { reason: "input is not a plain property" };
|
|
1195
|
+
if (isOverridden(config, found.index)) return { reason: "a spread after input could override it" };
|
|
1196
|
+
return { source: replaceText(source, found.member.value.start, found.member.value.end, value) };
|
|
1197
|
+
}
|
|
1198
|
+
function getOutput(config) {
|
|
1199
|
+
const found = findProperty(config, "output");
|
|
1200
|
+
if (found?.member.type !== nodeTypes.property || found.member.value.type !== nodeTypes.objectExpression) return { reason: "output is not an object literal" };
|
|
1201
|
+
if (isOverridden(config, found.index)) return { reason: "a spread after output could override it" };
|
|
1202
|
+
return { object: found.member.value };
|
|
1203
|
+
}
|
|
1204
|
+
function editOutput({ source, config, edit, quote }) {
|
|
1205
|
+
const output = getOutput(config);
|
|
1206
|
+
if ("reason" in output) return output;
|
|
1207
|
+
if (edit.operation === ConfigEditOperation.REMOVE_OUTPUT) return removeAtPath({
|
|
1208
|
+
source,
|
|
1209
|
+
options: output.object,
|
|
1210
|
+
path: edit.path
|
|
1211
|
+
});
|
|
1212
|
+
const [key, ...rest] = edit.path;
|
|
1213
|
+
if (!isOptionValue(edit.value) || !isOutputValue({
|
|
1214
|
+
key,
|
|
1215
|
+
value: wrapPath(rest, edit.value)
|
|
1216
|
+
})) return { reason: `the value is not one output.${key} takes` };
|
|
1217
|
+
return setAtPath({
|
|
1218
|
+
source,
|
|
1219
|
+
options: output.object,
|
|
1220
|
+
path: edit.path,
|
|
1221
|
+
value: edit.value,
|
|
1222
|
+
quote
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
/** Adds the import after the last one, matching its semicolon style. */
|
|
1226
|
+
function addImport({ source, parsed, importNames, moduleSpecifier, quote }) {
|
|
1227
|
+
const lastImport = parsed.program.body.findLast((node) => isImportStatement(node, parsed.kind));
|
|
1228
|
+
const afterLine = lastImport ? getPosition(source, lastImport.end).line : 0;
|
|
1229
|
+
const lines = source.split("\n");
|
|
1230
|
+
const semicolon = afterLine > 0 && lines[afterLine - 1]?.trimEnd().endsWith(";") ? ";" : "";
|
|
1231
|
+
const specifier = `${quote}${moduleSpecifier}${quote}`;
|
|
1232
|
+
const statement = parsed.kind === moduleKinds.esm ? `import { ${importNames.join(", ")} } from ${specifier}` : `const { ${importNames.join(", ")} } = require(${specifier})`;
|
|
1233
|
+
lines.splice(afterLine, 0, `${statement}${semicolon}`);
|
|
1234
|
+
if (afterLine === 0) lines.splice(1, 0, "");
|
|
1235
|
+
return lines.join("\n");
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* The parsers `defineConfig` applies when a config sets no `parsers`, by import name. Adding a parser
|
|
1239
|
+
* to such a config writes these too, or the new array would replace them.
|
|
1240
|
+
*/
|
|
1241
|
+
const DEFAULT_PARSERS = [
|
|
1242
|
+
{
|
|
1243
|
+
importName: "parserTs",
|
|
1244
|
+
packageName: "@kubb/parser-ts"
|
|
1245
|
+
},
|
|
1246
|
+
{
|
|
1247
|
+
importName: "parserTsx",
|
|
1248
|
+
packageName: "@kubb/parser-ts"
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
importName: "parserMd",
|
|
1252
|
+
packageName: "@kubb/parser-md"
|
|
1253
|
+
}
|
|
1254
|
+
];
|
|
1255
|
+
/**
|
|
1256
|
+
* Adds a plugin to `plugins`, or with `kind: 'parser'` a parser to `parsers`. Replaying the same add
|
|
1257
|
+
* is a no-op, while an import name taken by another package is an error.
|
|
1258
|
+
*/
|
|
1259
|
+
function addPlugin({ source, parsed, config, edit, quote, extensions }) {
|
|
1260
|
+
const kind = edit.kind ?? extensionKinds.plugin;
|
|
1261
|
+
const isParser = kind === extensionKinds.parser;
|
|
1262
|
+
if (!isAllowedPlugin({
|
|
1263
|
+
packageName: edit.plugin,
|
|
1264
|
+
extensions,
|
|
1265
|
+
kind
|
|
1266
|
+
})) return { reason: isParser ? `"${edit.plugin}" is not an installed Kubb parser` : `"${edit.plugin}" is not a @kubb/plugin-* package or an installed Kubb plugin` };
|
|
1267
|
+
const importName = extensions.get(edit.plugin)?.manifest.export ?? toExportName(edit.plugin);
|
|
1268
|
+
if (!IDENTIFIER_PATTERN.test(importName)) return { reason: `"${importName}" is not a valid import name` };
|
|
1269
|
+
if (findCalls(parsed.program, config, kind).some((entry) => entry.packageName === edit.plugin && (!isParser || entry.exportName === importName))) return { source };
|
|
1270
|
+
const options = edit.options ?? {};
|
|
1271
|
+
if (!isOptionValue(options)) return { reason: "the options are not literals that can be written to a config file" };
|
|
1272
|
+
const key = `${kind}s`;
|
|
1273
|
+
const array = getValue(config, key);
|
|
1274
|
+
if (array ? array.type !== nodeTypes.arrayExpression : !isParser) return { reason: `${key} is not an array literal` };
|
|
1275
|
+
const defaults = isParser && !array ? DEFAULT_PARSERS.filter((parser) => parser.importName !== importName) : [];
|
|
1276
|
+
const imports = getImports(parsed.program);
|
|
1277
|
+
const missing = /* @__PURE__ */ new Map();
|
|
1278
|
+
for (const item of [...defaults, {
|
|
1279
|
+
importName,
|
|
1280
|
+
packageName: edit.plugin
|
|
1281
|
+
}]) {
|
|
1282
|
+
const from = imports.get(item.importName)?.from;
|
|
1283
|
+
if (from && from !== item.packageName) return { reason: `${item.importName} is already imported from ${from}` };
|
|
1284
|
+
if (!from) missing.set(item.packageName, [...missing.get(item.packageName) ?? [], item.importName]);
|
|
1285
|
+
}
|
|
1286
|
+
const call = `${importName}(${Object.keys(options).length ? printValue(options, quote) : ""})`;
|
|
1287
|
+
const next = array?.type === nodeTypes.arrayExpression ? appendItem(source, array, call) : appendItem(source, config, `parsers: [${[...defaults.map((parser) => `${parser.importName}()`), call].join(", ")}]`);
|
|
1288
|
+
return { source: [...missing].reduceRight((current, [moduleSpecifier, importNames]) => addImport({
|
|
1289
|
+
source: current,
|
|
1290
|
+
parsed,
|
|
1291
|
+
importNames,
|
|
1292
|
+
moduleSpecifier,
|
|
1293
|
+
quote
|
|
1294
|
+
}), next) };
|
|
1295
|
+
}
|
|
1296
|
+
/** One item per line, so a call can be commented out alone. Skipped when comments sit between items. */
|
|
1297
|
+
function splitPlugins(source, config) {
|
|
1298
|
+
const array = getValue(config, "plugins");
|
|
1299
|
+
if (array?.type !== nodeTypes.arrayExpression) return void 0;
|
|
1300
|
+
const items = array.elements.flatMap((element) => element ? [element] : []);
|
|
1301
|
+
const { start, end } = array;
|
|
1302
|
+
const gaps = [source.slice(start + 1, items[0]?.start ?? end - 1), ...items.map((item, index) => source.slice(item.end, items[index + 1]?.start ?? end - 1))];
|
|
1303
|
+
if (!items.length || gaps.some((gap) => !/^[\s,]*$/.test(gap))) return void 0;
|
|
1304
|
+
const lineStart = source.lastIndexOf("\n", start - 1) + 1;
|
|
1305
|
+
const indent = source.slice(lineStart).match(/^[ \t]*/)?.[0] ?? "";
|
|
1306
|
+
return replaceText(source, start, end, `[\n${items.map((item) => `${indent} ${source.slice(item.start, item.end)},`).join("\n")}\n${indent}]`);
|
|
1307
|
+
}
|
|
1308
|
+
function disablePlugin(source, program, config, plugin) {
|
|
1309
|
+
const call = findCalls(program, config).find((entry) => entry.packageName === plugin)?.call;
|
|
1310
|
+
if (!call) return { reason: `${plugin} is not in the plugins array` };
|
|
1311
|
+
const start = getPosition(source, call.start);
|
|
1312
|
+
const end = getPosition(source, call.end);
|
|
1313
|
+
const lines = source.split("\n");
|
|
1314
|
+
const from = start.line - 1;
|
|
1315
|
+
const to = end.line - 1;
|
|
1316
|
+
const firstLine = lines[from] ?? "";
|
|
1317
|
+
if (firstLine.slice(0, start.column).trim() || !/^,?\s*$/.test((lines[to] ?? "").slice(end.column))) return { reason: `${plugin} shares a line with other code, so it cannot be commented out safely` };
|
|
1318
|
+
const indent = firstLine.match(/^\s*/)?.[0] ?? "";
|
|
1319
|
+
const commented = lines.slice(from, to + 1).map((line) => line.trim() ? `${indent}// ${line.slice(indent.length)}` : `${indent}//`);
|
|
1320
|
+
lines.splice(from, commented.length, `${indent}// kubb:disabled ${plugin} ${commented.length}`, ...commented);
|
|
1321
|
+
return { source: lines.join("\n") };
|
|
1322
|
+
}
|
|
1323
|
+
/** Disables the plugin, first putting a one-line `plugins: [a(), b()]` (what `add-plugin` writes) on separate lines. */
|
|
1324
|
+
function disablePluginInConfig({ source, parsed, config, edit }) {
|
|
1325
|
+
const result = disablePlugin(source, parsed.program, config, edit.plugin);
|
|
1326
|
+
if (!("reason" in result) || !result.reason.includes("shares a line")) return result;
|
|
1327
|
+
const split = splitPlugins(source, config);
|
|
1328
|
+
if (split === void 0) return result;
|
|
1329
|
+
const reparsed = findConfigs(split);
|
|
1330
|
+
const reparsedConfig = "reason" in reparsed ? void 0 : findConfigEntry(reparsed.configs, edit.config);
|
|
1331
|
+
if ("reason" in reparsed || !reparsedConfig) return result;
|
|
1332
|
+
return disablePlugin(split, reparsed.program, reparsedConfig, edit.plugin);
|
|
1333
|
+
}
|
|
1334
|
+
function enablePlugin(source, comments, config, plugin) {
|
|
1335
|
+
const block = findDisabled(source, comments, config).find((entry) => entry.packageName === plugin);
|
|
1336
|
+
if (!block) return { reason: `${plugin} is not disabled` };
|
|
1337
|
+
const lines = source.split("\n");
|
|
1338
|
+
lines.splice(block.line - 1, block.end - block.line + 1, ...block.code);
|
|
1339
|
+
return { source: lines.join("\n") };
|
|
1340
|
+
}
|
|
1341
|
+
/** Drops the import statements of `packageName` once nothing else in the file uses their names. */
|
|
1342
|
+
function removeUnusedImports(source, packageName) {
|
|
1343
|
+
const parsed = parseSource(source);
|
|
1344
|
+
if ("reason" in parsed) return source;
|
|
1345
|
+
return parsed.program.body.reduceRight((current, statement) => {
|
|
1346
|
+
const bindings = statement.type === nodeTypes.importDeclaration ? importBindings(statement) : statement.type === nodeTypes.variableDeclaration ? requireBindings(statement) : [];
|
|
1347
|
+
if (!bindings.length || bindings.some(([, { from }]) => from !== packageName)) return current;
|
|
1348
|
+
const rest = replaceText(current, statement.start, statement.end);
|
|
1349
|
+
if (bindings.some(([local]) => new RegExp(`(?<![\\w$])${local.replaceAll("$", "\\$")}(?![\\w$])`).test(rest))) return current;
|
|
1350
|
+
const lineStart = current.lastIndexOf("\n", statement.start - 1) + 1;
|
|
1351
|
+
const lineEnd = current.indexOf("\n", statement.end);
|
|
1352
|
+
const after = lineEnd === -1 ? current.length : lineEnd + 1;
|
|
1353
|
+
return replaceText(current, lineStart, lineStart === 0 && current[after] === "\n" ? after + 1 : after);
|
|
1354
|
+
}, source);
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Removes every call of a plugin or parser package, and the block `disable-plugin` commented out,
|
|
1358
|
+
* then its unused imports. Removing what the config does not hold is a no-op. Refuses to empty
|
|
1359
|
+
* `parsers`, since a config with no parser cannot print files.
|
|
1360
|
+
*/
|
|
1361
|
+
function removeExtension(source, edit) {
|
|
1362
|
+
const kind = edit.kind ?? extensionKinds.plugin;
|
|
1363
|
+
let current = source;
|
|
1364
|
+
for (;;) {
|
|
1365
|
+
const parsed = findConfigs(current);
|
|
1366
|
+
if ("reason" in parsed) return parsed;
|
|
1367
|
+
const config = findConfigEntry(parsed.configs, edit.config);
|
|
1368
|
+
if (!config) return { reason: `no config entry found for ${JSON.stringify(edit.config)}` };
|
|
1369
|
+
const array = getValue(config, `${kind}s`);
|
|
1370
|
+
const calls = findCalls(parsed.program, config, kind).filter((entry) => entry.packageName === edit.plugin);
|
|
1371
|
+
const block = findDisabled(current, parsed.comments, config).find((entry) => entry.packageName === edit.plugin);
|
|
1372
|
+
if (calls[0] && array?.type === nodeTypes.arrayExpression) {
|
|
1373
|
+
const elements = array.elements.filter((element) => element !== null);
|
|
1374
|
+
if (kind === extensionKinds.parser && elements.length === calls.length) return { reason: `${edit.plugin} is the last parser in the parsers array` };
|
|
1375
|
+
current = removeItem(current, array, elements.indexOf(calls[0].call));
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1378
|
+
if (block && kind === extensionKinds.plugin) {
|
|
1379
|
+
const lines = current.split("\n");
|
|
1380
|
+
lines.splice(block.line - 1, block.end - block.line + 1);
|
|
1381
|
+
current = lines.join("\n");
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
return { source: current === source ? source : removeUnusedImports(current, edit.plugin) };
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
/** Every edit re-parses the file, since the previous one rewrote it. */
|
|
1388
|
+
function applyEdit({ source, edit, extensions }) {
|
|
1389
|
+
const parsed = findConfigs(source);
|
|
1390
|
+
if ("reason" in parsed) return parsed;
|
|
1391
|
+
const config = findConfigEntry(parsed.configs, edit.config);
|
|
1392
|
+
if (!config) return { reason: `no config entry found for ${JSON.stringify(edit.config)}` };
|
|
1393
|
+
const quote = detectQuote(parsed.program) ?? quotes.single;
|
|
1394
|
+
switch (edit.operation) {
|
|
1395
|
+
case ConfigEditOperation.SET_INPUT: return setInput(source, config, edit.input, quote);
|
|
1396
|
+
case ConfigEditOperation.SET_OUTPUT:
|
|
1397
|
+
case ConfigEditOperation.REMOVE_OUTPUT: return editOutput({
|
|
1398
|
+
source,
|
|
1399
|
+
config,
|
|
1400
|
+
edit,
|
|
1401
|
+
quote
|
|
1402
|
+
});
|
|
1403
|
+
case ConfigEditOperation.DISABLE_PLUGIN: return disablePluginInConfig({
|
|
1404
|
+
source,
|
|
1405
|
+
parsed,
|
|
1406
|
+
config,
|
|
1407
|
+
edit
|
|
1408
|
+
});
|
|
1409
|
+
case ConfigEditOperation.ENABLE_PLUGIN: return enablePlugin(source, parsed.comments, config, edit.plugin);
|
|
1410
|
+
case ConfigEditOperation.ADD_PLUGIN: return addPlugin({
|
|
1411
|
+
source,
|
|
1412
|
+
parsed,
|
|
1413
|
+
config,
|
|
1414
|
+
edit,
|
|
1415
|
+
quote,
|
|
1416
|
+
extensions
|
|
1417
|
+
});
|
|
1418
|
+
case ConfigEditOperation.REMOVE_EXTENSION: return removeExtension(source, edit);
|
|
1419
|
+
case ConfigEditOperation.SET_OPTION:
|
|
1420
|
+
case ConfigEditOperation.REMOVE_OPTION: return editOption({
|
|
1421
|
+
source,
|
|
1422
|
+
parsed,
|
|
1423
|
+
config,
|
|
1424
|
+
edit,
|
|
1425
|
+
quote,
|
|
1426
|
+
extensions
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
/** Applies edits in place, touching only the text each targets. A refused edit is reported and the rest still run. */
|
|
1431
|
+
function editConfig({ source, edits, extensions = /* @__PURE__ */ new Map() }) {
|
|
1432
|
+
let current = source;
|
|
1433
|
+
const outcomes = edits.map((edit) => {
|
|
1434
|
+
const invalid = validateEdit(edit);
|
|
1435
|
+
if (invalid) return {
|
|
1436
|
+
edit,
|
|
1437
|
+
applied: false,
|
|
1438
|
+
reason: invalid
|
|
1439
|
+
};
|
|
1440
|
+
try {
|
|
1441
|
+
const result = applyEdit({
|
|
1442
|
+
source: current,
|
|
1443
|
+
edit,
|
|
1444
|
+
extensions
|
|
1445
|
+
});
|
|
1446
|
+
if ("reason" in result) return {
|
|
1447
|
+
edit,
|
|
1448
|
+
applied: false,
|
|
1449
|
+
reason: result.reason
|
|
1450
|
+
};
|
|
1451
|
+
current = result.source;
|
|
1452
|
+
return {
|
|
1453
|
+
edit,
|
|
1454
|
+
applied: true
|
|
1455
|
+
};
|
|
1456
|
+
} catch (error) {
|
|
1457
|
+
return {
|
|
1458
|
+
edit,
|
|
1459
|
+
applied: false,
|
|
1460
|
+
reason: require_errors.getErrorMessage(error)
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
});
|
|
1464
|
+
return {
|
|
1465
|
+
source: current,
|
|
1466
|
+
outcomes,
|
|
1467
|
+
changed: current !== source
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
/** Applies `edits` to `filePath`, writing only when something changed. */
|
|
1471
|
+
async function editConfigFile({ filePath, edits, extensions }) {
|
|
1472
|
+
const result = editConfig({
|
|
1473
|
+
source: await require_utils.read(filePath),
|
|
1474
|
+
edits,
|
|
1475
|
+
extensions
|
|
1476
|
+
});
|
|
1477
|
+
if (result.changed) await require_utils.writeRaw(filePath, result.source);
|
|
1478
|
+
return result;
|
|
1479
|
+
}
|
|
1480
|
+
//#endregion
|
|
483
1481
|
//#region src/runners/init/utils.ts
|
|
484
1482
|
/** Metadata for each supported package manager, keyed by its short name. */
|
|
485
1483
|
const packageManagers = {
|
|
@@ -488,6 +1486,7 @@ const packageManagers = {
|
|
|
488
1486
|
lockFiles: ["pnpm-lock.yaml"],
|
|
489
1487
|
initCommand: ["init"],
|
|
490
1488
|
installCommand: ["add", "-D"],
|
|
1489
|
+
removeCommand: ["remove"],
|
|
491
1490
|
runCommand: []
|
|
492
1491
|
},
|
|
493
1492
|
yarn: {
|
|
@@ -495,6 +1494,7 @@ const packageManagers = {
|
|
|
495
1494
|
lockFiles: ["yarn.lock"],
|
|
496
1495
|
initCommand: ["init", "-y"],
|
|
497
1496
|
installCommand: ["add", "-D"],
|
|
1497
|
+
removeCommand: ["remove"],
|
|
498
1498
|
runCommand: []
|
|
499
1499
|
},
|
|
500
1500
|
bun: {
|
|
@@ -502,6 +1502,7 @@ const packageManagers = {
|
|
|
502
1502
|
lockFiles: ["bun.lock", "bun.lockb"],
|
|
503
1503
|
initCommand: ["init", "-y"],
|
|
504
1504
|
installCommand: ["add", "-d"],
|
|
1505
|
+
removeCommand: ["remove"],
|
|
505
1506
|
runCommand: ["run"]
|
|
506
1507
|
},
|
|
507
1508
|
npm: {
|
|
@@ -509,6 +1510,7 @@ const packageManagers = {
|
|
|
509
1510
|
lockFiles: ["package-lock.json"],
|
|
510
1511
|
initCommand: ["init", "-y"],
|
|
511
1512
|
installCommand: ["install", "--save-dev"],
|
|
1513
|
+
removeCommand: ["uninstall"],
|
|
512
1514
|
runCommand: ["run"]
|
|
513
1515
|
}
|
|
514
1516
|
};
|
|
@@ -543,8 +1545,8 @@ const INSTALL_TIMEOUT_MS = 3e5;
|
|
|
543
1545
|
* to `onLine`, so the caller can show it while the install runs; a failed install also keeps the
|
|
544
1546
|
* end of the output in the error.
|
|
545
1547
|
*/
|
|
546
|
-
async function installPackages(packages, packageManager, cwd = node_process.default.cwd(), onLine) {
|
|
547
|
-
const args = [...
|
|
1548
|
+
async function installPackages(packages, packageManager, cwd = node_process.default.cwd(), onLine, command = packageManager.installCommand) {
|
|
1549
|
+
const args = [...command, ...packages];
|
|
548
1550
|
const result = (0, tinyexec.x)(packageManager.name, args, {
|
|
549
1551
|
nodeOptions: {
|
|
550
1552
|
cwd,
|
|
@@ -568,6 +1570,12 @@ async function installPackages(packages, packageManager, cwd = node_process.defa
|
|
|
568
1570
|
throw new Error(`\`${packageManager.name} ${args.join(" ")}\` failed with exit code ${result.exitCode}${output ? `:\n${output}` : ""}`);
|
|
569
1571
|
}
|
|
570
1572
|
/**
|
|
1573
|
+
* Uninstalls the given packages at `cwd` with the package manager's remove command, like {@link installPackages}.
|
|
1574
|
+
*/
|
|
1575
|
+
function uninstallPackages(packages, packageManager, cwd = node_process.default.cwd()) {
|
|
1576
|
+
return installPackages(packages, packageManager, cwd, void 0, packageManager.removeCommand);
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
571
1579
|
* What the selected plugins' `manifests` say is missing from the selection: a `dependencies`
|
|
572
1580
|
* entry, or a `client` plugin next to a `consumer`. Empty when nothing is missing.
|
|
573
1581
|
* `optionalDependencies` are skipped, since `kubb init` writes the default options.
|
|
@@ -741,8 +1749,9 @@ function toSnippet({ artifact, manifest }) {
|
|
|
741
1749
|
/** The `add-plugin` edits for `details`, and the manifests the edits read their export names from. */
|
|
742
1750
|
function toConfigEdits(details) {
|
|
743
1751
|
return {
|
|
744
|
-
edits: details.map(({ artifact }) => ({
|
|
745
|
-
operation:
|
|
1752
|
+
edits: details.map(({ artifact, manifest }) => ({
|
|
1753
|
+
operation: ConfigEditOperation.ADD_PLUGIN,
|
|
1754
|
+
...manifest.kind === extensionKinds.parser ? { kind: extensionKinds.parser } : {},
|
|
746
1755
|
plugin: artifact.package
|
|
747
1756
|
})),
|
|
748
1757
|
extensions: new Map(details.map(({ artifact, manifest }) => [artifact.package, {
|
|
@@ -753,24 +1762,22 @@ function toConfigEdits(details) {
|
|
|
753
1762
|
};
|
|
754
1763
|
}
|
|
755
1764
|
/** The lines adding `details` would put in the config `source`, trimmed, or why it could not add them. */
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
return { lines: result.source.split("\n").filter((line) => {
|
|
1765
|
+
function previewConfig({ details, source }) {
|
|
1766
|
+
const result = editConfig({
|
|
1767
|
+
source,
|
|
1768
|
+
...toConfigEdits(details)
|
|
1769
|
+
});
|
|
1770
|
+
const failed = result.outcomes.find((outcome) => !outcome.applied);
|
|
1771
|
+
if (failed) return { reason: failed.reason ?? "the config file was not changed" };
|
|
1772
|
+
const remaining = source.split("\n");
|
|
1773
|
+
return {
|
|
1774
|
+
lines: result.source.split("\n").filter((line) => {
|
|
767
1775
|
const index = remaining.indexOf(line);
|
|
768
1776
|
if (index !== -1) remaining.splice(index, 1);
|
|
769
1777
|
return index === -1;
|
|
770
|
-
}).map((line) => line.trim())
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
}
|
|
1778
|
+
}).map((line) => line.trim()),
|
|
1779
|
+
source: result.source
|
|
1780
|
+
};
|
|
774
1781
|
}
|
|
775
1782
|
async function selectExtension({ entries, versions, registry }) {
|
|
776
1783
|
if (!entries.length) throw new _kubb_core.Diagnostics.Error({
|
|
@@ -822,12 +1829,13 @@ function summarize(description, max = 64) {
|
|
|
822
1829
|
return sentence.length > max ? `${sentence.slice(0, max - 1).trimEnd()}…` : sentence;
|
|
823
1830
|
}
|
|
824
1831
|
/**
|
|
825
|
-
* Installs an extension from the Kubb registry at its reviewed version and, for a plugin
|
|
826
|
-
* `kubb.config.ts`, offering to add the plugins it needs as well.
|
|
1832
|
+
* Installs an extension from the Kubb registry at its reviewed version and, for a plugin or a parser,
|
|
1833
|
+
* adds it to `kubb.config.ts`, offering to add the plugins it needs as well. An adapter is never
|
|
1834
|
+
* written: the command prints the snippet to swap it in by hand. Without an id, asks which one.
|
|
827
1835
|
* Previews the packages and config lines before asking. Refuses when the running Kubb or Node.js
|
|
828
1836
|
* version is outside its compatibility.
|
|
829
1837
|
*/
|
|
830
|
-
async function run$
|
|
1838
|
+
async function run$5({ id: input, configPath, yes, install }) {
|
|
831
1839
|
const cwd = node_process.default.cwd();
|
|
832
1840
|
const hrStart = node_process.default.hrtime();
|
|
833
1841
|
const summary = (title, status, lines) => require_summary.logCommandSummary({
|
|
@@ -887,8 +1895,8 @@ async function run$4({ id: input, configPath, yes, install }) {
|
|
|
887
1895
|
message: problem,
|
|
888
1896
|
help: "Upgrade Kubb or Node.js to a version the extension supports."
|
|
889
1897
|
});
|
|
890
|
-
const configFile = manifest.kind ===
|
|
891
|
-
if (manifest.kind
|
|
1898
|
+
const configFile = manifest.kind === extensionKinds.adapter ? void 0 : foundConfig;
|
|
1899
|
+
if (manifest.kind !== extensionKinds.adapter && !configFile) throw new _kubb_core.Diagnostics.Error({
|
|
892
1900
|
code: _kubb_core.Diagnostics.code.unknown,
|
|
893
1901
|
severity: "error",
|
|
894
1902
|
message: "No kubb.config.ts found. Run `kubb init` first, or pass --config.",
|
|
@@ -912,15 +1920,19 @@ async function run$4({ id: input, configPath, yes, install }) {
|
|
|
912
1920
|
return;
|
|
913
1921
|
}
|
|
914
1922
|
const targets = [...withDependencies ? missing : [], entry];
|
|
915
|
-
const specs = targets.filter((target) => target === entry || !versions.has(target.artifact.package)).map((target) => require_registry.installSpec(target.artifact));
|
|
916
1923
|
const dependencyStatus = (name) => {
|
|
917
1924
|
if (inConfig(name)) return `${name} ${(0, node_util.styleText)("green", "✓ installed")}`;
|
|
918
1925
|
return targets.some((target) => target.manifest.name === name) ? `${name} ${(0, node_util.styleText)("cyan", "+ adding")}` : `${name} ${(0, node_util.styleText)("yellow", "missing")}`;
|
|
919
1926
|
};
|
|
920
|
-
const preview = configFile && configSource !== void 0 ?
|
|
1927
|
+
const preview = configFile && configSource !== void 0 ? previewConfig({
|
|
921
1928
|
details: targets,
|
|
922
1929
|
source: configSource
|
|
923
1930
|
}) : void 0;
|
|
1931
|
+
const before = readImports(configSource ?? "");
|
|
1932
|
+
const added = preview && "lines" in preview ? readImports(preview.source).filter((name) => !before.includes(name)) : [];
|
|
1933
|
+
const bundled = require_config.BUNDLED_PACKAGES.filter((name) => added.includes(name) && !versions.has(name) && !targets.some((target) => target.artifact.package === name));
|
|
1934
|
+
const kubbVersion = versions.get("kubb");
|
|
1935
|
+
const specs = [...targets.filter((target) => target === entry || !versions.has(target.artifact.package)).map((target) => require_registry.installSpec(target.artifact)), ...bundled.map((name) => kubbVersion ? `${name}@${kubbVersion}` : name)];
|
|
924
1936
|
require_text.logStep(`${kind} ${(0, node_util.styleText)("cyan", manifest.name)} v${artifact.version} · ${summarize(manifest.description)}`);
|
|
925
1937
|
require_text.logBlock([
|
|
926
1938
|
...dependencies.length ? [`Needs ${dependencies.map(dependencyStatus).join(" ")}`] : [],
|
|
@@ -940,7 +1952,7 @@ async function run$4({ id: input, configPath, yes, install }) {
|
|
|
940
1952
|
const packageManager = detectPackageManager(cwd);
|
|
941
1953
|
const packages = () => install ? `${installedPackages.length} installed with ${packageManager.name}` : "installation skipped (--no-install)";
|
|
942
1954
|
if (install) {
|
|
943
|
-
const names = targets.filter((target) => specs.includes(require_registry.installSpec(target.artifact))).map((target) => target.artifact.package);
|
|
1955
|
+
const names = [...targets.filter((target) => specs.includes(require_registry.installSpec(target.artifact))).map((target) => target.artifact.package), ...bundled];
|
|
944
1956
|
await installWithSpinner({
|
|
945
1957
|
packages: specs,
|
|
946
1958
|
names: names.join(", "),
|
|
@@ -951,8 +1963,7 @@ async function run$4({ id: input, configPath, yes, install }) {
|
|
|
951
1963
|
}
|
|
952
1964
|
const unconfigured = configFile ? [] : [entry];
|
|
953
1965
|
if (configFile) for (const target of targets) try {
|
|
954
|
-
const {
|
|
955
|
-
const { outcomes, changed } = await writeConfigEdits({
|
|
1966
|
+
const { outcomes, changed } = await editConfigFile({
|
|
956
1967
|
filePath: configFile,
|
|
957
1968
|
...toConfigEdits([target])
|
|
958
1969
|
});
|
|
@@ -964,14 +1975,14 @@ async function run$4({ id: input, configPath, yes, install }) {
|
|
|
964
1975
|
unconfigured.push(target);
|
|
965
1976
|
}
|
|
966
1977
|
const manual = unconfigured.length > 0;
|
|
967
|
-
if (manual && manifest.kind
|
|
1978
|
+
if (manual && manifest.kind !== extensionKinds.adapter) node_process.default.exitCode = 1;
|
|
968
1979
|
if (manual) require_text.logBlock([
|
|
969
1980
|
"Add the import to your Kubb config and merge the property into its defineConfig call:",
|
|
970
1981
|
"",
|
|
971
1982
|
...unconfigured.map((target) => toSnippet(target))
|
|
972
1983
|
]);
|
|
973
1984
|
const dependenciesMissing = !withDependencies && missing.length > 0;
|
|
974
|
-
summary(manual ? `${kind} needs configuration` : dependenciesMissing ? "Plugin configured; dependencies missing" : configured.length ? install ?
|
|
1985
|
+
summary(manual ? `${kind} needs configuration` : dependenciesMissing ? "Plugin configured; dependencies missing" : configured.length ? install ? `${kind} ready` : `${kind} configured` : `${kind} already configured`, manual || dependenciesMissing ? "warning" : "success", [
|
|
975
1986
|
` Packages ${packages()}`,
|
|
976
1987
|
...configured.map((target) => (0, node_util.styleText)("green", ` + ${target.manifest.export}()`)),
|
|
977
1988
|
"",
|
|
@@ -986,8 +1997,8 @@ async function run$4({ id: input, configPath, yes, install }) {
|
|
|
986
1997
|
node_process.default.exitCode = 1;
|
|
987
1998
|
}
|
|
988
1999
|
}
|
|
989
|
-
require_rolldown_runtime.__name(run$
|
|
990
|
-
const command$
|
|
2000
|
+
require_rolldown_runtime.__name(run$5, "run");
|
|
2001
|
+
const command$4 = (0, gunshi.define)({
|
|
991
2002
|
name: "add",
|
|
992
2003
|
description: "Find and install plugins, adapters, and parsers from the Kubb registry.",
|
|
993
2004
|
examples: [
|
|
@@ -1013,7 +2024,7 @@ const command$3 = (0, gunshi.define)({
|
|
|
1013
2024
|
default: true
|
|
1014
2025
|
}
|
|
1015
2026
|
},
|
|
1016
|
-
run: ({ values }) => run$
|
|
2027
|
+
run: ({ values }) => run$5({
|
|
1017
2028
|
id: values.id,
|
|
1018
2029
|
configPath: values.config,
|
|
1019
2030
|
yes: values.yes,
|
|
@@ -1031,15 +2042,14 @@ function unwrap(value) {
|
|
|
1031
2042
|
return value;
|
|
1032
2043
|
}
|
|
1033
2044
|
/** The full `kubb.config.ts` for `choices`, with each plugin added the way `kubb add` adds it. */
|
|
1034
|
-
|
|
1035
|
-
const {
|
|
1036
|
-
const { source, outcomes } = applyConfigEdits({
|
|
2045
|
+
function buildConfig({ inputPath, outputPath, plugins }) {
|
|
2046
|
+
const { source, outcomes } = editConfig({
|
|
1037
2047
|
source: generateConfigFile({
|
|
1038
2048
|
inputPath,
|
|
1039
2049
|
outputPath
|
|
1040
2050
|
}),
|
|
1041
2051
|
edits: plugins.map((entry) => ({
|
|
1042
|
-
operation:
|
|
2052
|
+
operation: ConfigEditOperation.ADD_PLUGIN,
|
|
1043
2053
|
plugin: entry.artifact.package
|
|
1044
2054
|
})),
|
|
1045
2055
|
extensions: new Map(plugins.map(({ artifact, manifest }) => [artifact.package, {
|
|
@@ -1049,7 +2059,7 @@ async function buildConfig({ inputPath, outputPath, plugins }) {
|
|
|
1049
2059
|
}]))
|
|
1050
2060
|
});
|
|
1051
2061
|
const failed = outcomes.find((outcome) => !outcome.applied);
|
|
1052
|
-
if (failed && failed.edit.operation ===
|
|
2062
|
+
if (failed && failed.edit.operation === ConfigEditOperation.ADD_PLUGIN) throw new _kubb_core.Diagnostics.Error({
|
|
1053
2063
|
code: _kubb_core.Diagnostics.code.unknown,
|
|
1054
2064
|
severity: "error",
|
|
1055
2065
|
message: `Could not add ${failed.edit.plugin} to ${require_config.KUBB_CONFIG_FILENAME}: ${failed.reason}`,
|
|
@@ -1085,7 +2095,7 @@ function generateCommand({ packageManager, script, watch }) {
|
|
|
1085
2095
|
* `generate` script, and runs the first generation.
|
|
1086
2096
|
* Pass `yes: true` to skip all prompts and print one line per step.
|
|
1087
2097
|
*/
|
|
1088
|
-
async function run$
|
|
2098
|
+
async function run$4({ yes, input: inputFlag, output: outputFlag, plugins: pluginsFlag, dryRun }) {
|
|
1089
2099
|
const cwd = node_process.default.cwd();
|
|
1090
2100
|
/** Runs `task` behind a spinner, or prints one line when it is done in `--yes` mode. */
|
|
1091
2101
|
async function step({ start, task, done, failed }) {
|
|
@@ -1108,7 +2118,6 @@ async function run$3({ yes, input: inputFlag, output: outputFlag, plugins: plugi
|
|
|
1108
2118
|
try {
|
|
1109
2119
|
const registry = require_registry.fetchRegistryIndex({ registry: require_registry.registryUrl() }).then((entries) => entries.filter((entry) => entry.kind === "plugin" && !require_registry.incompatibility(entry.manifest)));
|
|
1110
2120
|
registry.catch(() => {});
|
|
1111
|
-
if (!yes) import("@kubb/studio").catch(() => {});
|
|
1112
2121
|
if (!await require_utils.exists(node_path.default.join(cwd, "package.json"))) {
|
|
1113
2122
|
if (!yes) {
|
|
1114
2123
|
if (!unwrap(await _clack_prompts.confirm({
|
|
@@ -1222,7 +2231,7 @@ async function run$3({ yes, input: inputFlag, output: outputFlag, plugins: plugi
|
|
|
1222
2231
|
require_text.logWarn(`${missing.join("\n")}\nAdd the missing plugins.`);
|
|
1223
2232
|
continue;
|
|
1224
2233
|
}
|
|
1225
|
-
const config =
|
|
2234
|
+
const config = buildConfig(choices);
|
|
1226
2235
|
const packages = [require_config.KUBB_PACKAGE_NAME, ...choices.plugins.map((entry) => entry.artifact.package)];
|
|
1227
2236
|
require_text.logStep(`Review ${require_config.KUBB_CONFIG_FILENAME}`);
|
|
1228
2237
|
require_text.logBlock([
|
|
@@ -1274,7 +2283,7 @@ async function run$3({ yes, input: inputFlag, output: outputFlag, plugins: plugi
|
|
|
1274
2283
|
});
|
|
1275
2284
|
await step({
|
|
1276
2285
|
start: `Writing ${require_config.KUBB_CONFIG_FILENAME}`,
|
|
1277
|
-
task: async () => dryRun.run(async () => void await require_utils.write(configPath,
|
|
2286
|
+
task: async () => dryRun.run(async () => void await require_utils.write(configPath, buildConfig(choices)), { message: `write ${require_config.KUBB_CONFIG_FILENAME}` }),
|
|
1278
2287
|
done: () => `Wrote ${require_config.KUBB_CONFIG_FILENAME}`,
|
|
1279
2288
|
failed: `Could not write ${require_config.KUBB_CONFIG_FILENAME}`
|
|
1280
2289
|
});
|
|
@@ -1326,8 +2335,8 @@ async function run$3({ yes, input: inputFlag, output: outputFlag, plugins: plugi
|
|
|
1326
2335
|
node_process.default.exit(1);
|
|
1327
2336
|
}
|
|
1328
2337
|
}
|
|
1329
|
-
require_rolldown_runtime.__name(run$
|
|
1330
|
-
const command$
|
|
2338
|
+
require_rolldown_runtime.__name(run$4, "run");
|
|
2339
|
+
const command$3 = (0, gunshi.defineWithTypes)()({
|
|
1331
2340
|
name: "init",
|
|
1332
2341
|
description: "Set up a config, install plugins, and run the first generation.",
|
|
1333
2342
|
examples: [
|
|
@@ -1364,7 +2373,7 @@ const command$2 = (0, gunshi.defineWithTypes)()({
|
|
|
1364
2373
|
metavar: "plugin-ts,plugin-zod,..."
|
|
1365
2374
|
}
|
|
1366
2375
|
},
|
|
1367
|
-
run: ({ values, extensions }) => run$
|
|
2376
|
+
run: ({ values, extensions }) => run$4({
|
|
1368
2377
|
yes: values.yes,
|
|
1369
2378
|
input: values.input,
|
|
1370
2379
|
output: values.output,
|
|
@@ -1408,7 +2417,7 @@ async function listExtensions({ cwd, registry }) {
|
|
|
1408
2417
|
});
|
|
1409
2418
|
}
|
|
1410
2419
|
/** Prints the installed extensions as a table. */
|
|
1411
|
-
async function run$
|
|
2420
|
+
async function run$3() {
|
|
1412
2421
|
try {
|
|
1413
2422
|
const rows = await listExtensions({
|
|
1414
2423
|
cwd: node_process.default.cwd(),
|
|
@@ -1437,12 +2446,176 @@ async function run$2() {
|
|
|
1437
2446
|
node_process.default.exitCode = 1;
|
|
1438
2447
|
}
|
|
1439
2448
|
}
|
|
1440
|
-
require_rolldown_runtime.__name(run$
|
|
1441
|
-
const command$
|
|
2449
|
+
require_rolldown_runtime.__name(run$3, "run");
|
|
2450
|
+
const command$2 = (0, gunshi.define)({
|
|
1442
2451
|
name: "list",
|
|
1443
2452
|
description: "List installed extensions, their versions, and available updates.",
|
|
1444
2453
|
examples: "kubb list",
|
|
1445
|
-
run: () => run$
|
|
2454
|
+
run: () => run$3()
|
|
2455
|
+
});
|
|
2456
|
+
//#endregion
|
|
2457
|
+
//#region src/runners/remove/run.ts
|
|
2458
|
+
/** The lines of `before` that `after` no longer has, trimmed. */
|
|
2459
|
+
function removedLines({ before, after }) {
|
|
2460
|
+
const remaining = after.split("\n");
|
|
2461
|
+
return before.split("\n").filter((line) => {
|
|
2462
|
+
const index = remaining.indexOf(line);
|
|
2463
|
+
if (index !== -1) remaining.splice(index, 1);
|
|
2464
|
+
return index === -1 && line.trim() !== "";
|
|
2465
|
+
}).map((line) => line.trim());
|
|
2466
|
+
}
|
|
2467
|
+
/**
|
|
2468
|
+
* Removes a plugin or parser from `kubb.config.ts` and uninstalls its package. Refuses the adapter,
|
|
2469
|
+
* which the config needs, and an extension another one in the config depends on. Previews the
|
|
2470
|
+
* change before asking.
|
|
2471
|
+
*/
|
|
2472
|
+
async function run$2({ id: input, configPath, yes, uninstall }) {
|
|
2473
|
+
const cwd = node_process.default.cwd();
|
|
2474
|
+
const hrStart = node_process.default.hrtime();
|
|
2475
|
+
const summary = (title, status, lines) => require_summary.logCommandSummary({
|
|
2476
|
+
title,
|
|
2477
|
+
status,
|
|
2478
|
+
hrStart,
|
|
2479
|
+
block: true,
|
|
2480
|
+
lines
|
|
2481
|
+
});
|
|
2482
|
+
const fail = (title, lines) => {
|
|
2483
|
+
summary(title, "failed", lines);
|
|
2484
|
+
node_process.default.exitCode = 1;
|
|
2485
|
+
};
|
|
2486
|
+
require_text.logIntro({ title: require_text.isInteractiveRun({ yes }) ? void 0 : (0, node_util.styleText)("bgCyan", (0, node_util.styleText)("black", " Kubb Remove ")) });
|
|
2487
|
+
try {
|
|
2488
|
+
const id = require_registry.toRegistryId(input);
|
|
2489
|
+
const configFile = configPath ? node_path.default.resolve(configPath) : require_config.findConfigFile(cwd);
|
|
2490
|
+
if (!configFile) throw new _kubb_core.Diagnostics.Error({
|
|
2491
|
+
code: _kubb_core.Diagnostics.code.unknown,
|
|
2492
|
+
severity: "error",
|
|
2493
|
+
message: "No kubb.config.ts found. Pass its path with --config.",
|
|
2494
|
+
help: "Run `kubb remove <id> --config <path>`."
|
|
2495
|
+
});
|
|
2496
|
+
const configName = node_path.default.relative(cwd, configFile) || configFile;
|
|
2497
|
+
const [entries, installed, source] = await require_utils.withSpinner({
|
|
2498
|
+
message: "Reading the registry",
|
|
2499
|
+
promise: Promise.all([
|
|
2500
|
+
require_registry.fetchRegistryIndex({ registry: require_registry.registryUrl() }),
|
|
2501
|
+
(0, _kubb_core.readInstalledPackages)({ root: cwd }),
|
|
2502
|
+
require_utils.read(configFile)
|
|
2503
|
+
])
|
|
2504
|
+
});
|
|
2505
|
+
const versions = new Map(installed.map((item) => [item.package, item.version]));
|
|
2506
|
+
const entry = entries.find((candidate) => candidate.id === id);
|
|
2507
|
+
if (!entry) {
|
|
2508
|
+
const suggestions = require_registry.closestIds({
|
|
2509
|
+
input: id,
|
|
2510
|
+
ids: entries.map((candidate) => candidate.id)
|
|
2511
|
+
}).map(require_registry.toShortId);
|
|
2512
|
+
return fail(`No extension named ${input}`, suggestions.map((suggestion) => ` kubb remove ${suggestion}`));
|
|
2513
|
+
}
|
|
2514
|
+
const { artifact, manifest } = entry;
|
|
2515
|
+
const kind = manifest.kind.charAt(0).toUpperCase() + manifest.kind.slice(1);
|
|
2516
|
+
if (manifest.kind === extensionKinds.adapter) return fail("The adapter cannot be removed", [` A config needs one adapter. Change it in ${configName} by hand.`]);
|
|
2517
|
+
const view = readConfig(source);
|
|
2518
|
+
const running = new Set(view.managed ? view.configs.flatMap((config) => [...config.plugins, ...config.parsers ?? []].filter((call) => !call.disabled).map((call) => call.packageName)) : []);
|
|
2519
|
+
const dependents = entries.filter((other) => other.id !== entry.id && other.manifest.dependencies?.includes(manifest.name) && running.has(other.artifact.package));
|
|
2520
|
+
if (dependents.length) {
|
|
2521
|
+
const names = dependents.map((other) => other.manifest.name).join(", ");
|
|
2522
|
+
return fail(`${names} ${dependents.length === 1 ? "needs" : "need"} ${manifest.name}`, [` Remove ${names} first.`]);
|
|
2523
|
+
}
|
|
2524
|
+
const edits = [{
|
|
2525
|
+
operation: ConfigEditOperation.REMOVE_EXTENSION,
|
|
2526
|
+
...manifest.kind === extensionKinds.parser ? { kind: extensionKinds.parser } : {},
|
|
2527
|
+
plugin: artifact.package
|
|
2528
|
+
}];
|
|
2529
|
+
const preview = editConfig({
|
|
2530
|
+
source,
|
|
2531
|
+
edits
|
|
2532
|
+
});
|
|
2533
|
+
const outcome = preview.outcomes[0];
|
|
2534
|
+
if (!outcome?.applied) return fail(`Could not edit ${configName}`, [` ${outcome?.reason ?? "the config file was not changed"}`]);
|
|
2535
|
+
const bundled = require_config.BUNDLED_PACKAGES.includes(artifact.package);
|
|
2536
|
+
const stillImported = readImports(preview.source).includes(artifact.package);
|
|
2537
|
+
const uninstalls = uninstall && !bundled && !stillImported && versions.has(artifact.package);
|
|
2538
|
+
if (!preview.changed && !uninstalls) {
|
|
2539
|
+
summary(`${manifest.name} is not in the project`, "success", [" No changes made"]);
|
|
2540
|
+
return;
|
|
2541
|
+
}
|
|
2542
|
+
const packageLine = (() => {
|
|
2543
|
+
if (uninstalls) return `Uninstalls ${artifact.package}`;
|
|
2544
|
+
if (bundled) return `Keeps ${artifact.package}, which ships with kubb`;
|
|
2545
|
+
if (stillImported) return `Keeps ${artifact.package}, which another config entry still imports`;
|
|
2546
|
+
return "Uninstalls nothing";
|
|
2547
|
+
})();
|
|
2548
|
+
require_text.logStep(`${kind} ${(0, node_util.styleText)("cyan", manifest.name)} v${versions.get(artifact.package) ?? artifact.version}`);
|
|
2549
|
+
require_text.logBlock([
|
|
2550
|
+
`Config ${configName}`,
|
|
2551
|
+
...removedLines({
|
|
2552
|
+
before: source,
|
|
2553
|
+
after: preview.source
|
|
2554
|
+
}).map((line) => (0, node_util.styleText)("red", ` - ${line}`)),
|
|
2555
|
+
packageLine
|
|
2556
|
+
]);
|
|
2557
|
+
const confirmed = yes || await _clack_prompts.confirm({
|
|
2558
|
+
message: "Remove it?",
|
|
2559
|
+
initialValue: true
|
|
2560
|
+
});
|
|
2561
|
+
if (_clack_prompts.isCancel(confirmed) || !confirmed) {
|
|
2562
|
+
summary("Remove canceled", "canceled", [` ${kind} ${manifest.name}`, " No changes made"]);
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
if (preview.changed) {
|
|
2566
|
+
const written = await editConfigFile({
|
|
2567
|
+
filePath: configFile,
|
|
2568
|
+
edits
|
|
2569
|
+
});
|
|
2570
|
+
if (!written.outcomes[0]?.applied) return fail(`Could not edit ${configName}`, [` ${written.outcomes[0]?.reason ?? "the config file was not changed"}`]);
|
|
2571
|
+
require_text.logStep(`${manifest.export}() removed from ${configName}`);
|
|
2572
|
+
}
|
|
2573
|
+
const packageManager = detectPackageManager(cwd);
|
|
2574
|
+
if (uninstalls) await require_utils.withSpinner({
|
|
2575
|
+
message: `Uninstalling ${artifact.package} with ${packageManager.name}`,
|
|
2576
|
+
promise: uninstallPackages([artifact.package], packageManager, cwd)
|
|
2577
|
+
});
|
|
2578
|
+
summary(`${kind} removed`, "success", [
|
|
2579
|
+
` Packages ${uninstalls ? `${artifact.package} uninstalled with ${packageManager.name}` : "none uninstalled"}`,
|
|
2580
|
+
"",
|
|
2581
|
+
"Next kubb generate"
|
|
2582
|
+
]);
|
|
2583
|
+
} catch (error) {
|
|
2584
|
+
require_text.logError(require_errors.getErrorMessage(error));
|
|
2585
|
+
fail("Remove failed", [" See the error above"]);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
require_rolldown_runtime.__name(run$2, "run");
|
|
2589
|
+
const command$1 = (0, gunshi.define)({
|
|
2590
|
+
name: "remove",
|
|
2591
|
+
description: "Remove a plugin or parser from kubb.config.ts and uninstall its package.",
|
|
2592
|
+
examples: [
|
|
2593
|
+
"kubb remove plugin-zod",
|
|
2594
|
+
"kubb remove acme/plugin-x --yes",
|
|
2595
|
+
"kubb remove plugin-zod --no-uninstall"
|
|
2596
|
+
].join("\n"),
|
|
2597
|
+
toKebab: true,
|
|
2598
|
+
args: {
|
|
2599
|
+
id: {
|
|
2600
|
+
type: "positional",
|
|
2601
|
+
description: "Registry id: owner/slug, or a slug for an official extension",
|
|
2602
|
+
required: true
|
|
2603
|
+
},
|
|
2604
|
+
...configArg,
|
|
2605
|
+
...yesArg,
|
|
2606
|
+
uninstall: {
|
|
2607
|
+
type: "boolean",
|
|
2608
|
+
description: "Uninstall the package. Pass --no-uninstall to only edit the config",
|
|
2609
|
+
negatable: true,
|
|
2610
|
+
default: true
|
|
2611
|
+
}
|
|
2612
|
+
},
|
|
2613
|
+
run: ({ values }) => run$2({
|
|
2614
|
+
id: values.id,
|
|
2615
|
+
configPath: values.config,
|
|
2616
|
+
yes: values.yes,
|
|
2617
|
+
uninstall: values.uninstall
|
|
2618
|
+
})
|
|
1446
2619
|
});
|
|
1447
2620
|
//#endregion
|
|
1448
2621
|
//#region src/runners/update/run.ts
|
|
@@ -1455,7 +2628,7 @@ async function regenerate({ cwd, configPath }) {
|
|
|
1455
2628
|
const spinner = require_utils.createSpinner();
|
|
1456
2629
|
spinner.start("Generating with the new versions");
|
|
1457
2630
|
try {
|
|
1458
|
-
const [{ getConfigs }, { generate }] = await Promise.all([Promise.resolve().then(() => require("./config-
|
|
2631
|
+
const [{ getConfigs }, { generate }] = await Promise.all([Promise.resolve().then(() => require("./config-BG_Nr2CZ.cjs")).then((n) => n.config_exports), Promise.resolve().then(() => require("./run-C2syKtAg.cjs"))]);
|
|
1459
2632
|
const { configs } = await getConfigs({ configPath });
|
|
1460
2633
|
const dirs = configs.map((config) => node_path.default.resolve(config.root ?? cwd, config.output.path));
|
|
1461
2634
|
const before = new Map((await Promise.all(dirs.map(require_utils.readOutputFiles))).flatMap((files) => [...files]));
|
|
@@ -1504,7 +2677,11 @@ async function run$1({ ids, dryRun, yes }) {
|
|
|
1504
2677
|
});
|
|
1505
2678
|
const versions = new Map(installed.map((item) => [item.package, item.version]));
|
|
1506
2679
|
const tracked = entries.filter((entry) => versions.has(entry.artifact.package) && (!wanted.size || wanted.has(entry.id)));
|
|
1507
|
-
for (const id of wanted)
|
|
2680
|
+
for (const id of wanted) {
|
|
2681
|
+
if (tracked.some((entry) => entry.id === id)) continue;
|
|
2682
|
+
require_text.logWarn(`${id} is not installed, or not in the registry.`);
|
|
2683
|
+
node_process.default.exitCode = 1;
|
|
2684
|
+
}
|
|
1508
2685
|
const current = ({ artifact }) => versions.get(artifact.package);
|
|
1509
2686
|
const isMajor = (entry) => require_registry.bumpOf({
|
|
1510
2687
|
installed: current(entry),
|
|
@@ -1662,19 +2839,20 @@ async function run(argv = process.argv.slice(2)) {
|
|
|
1662
2839
|
const isQuietFlag = args.some((arg) => QUIET_FLAGS.has(arg));
|
|
1663
2840
|
const isJsonOutput = args.includes("--json") || args.some((arg, index) => /^--reporter=.*\bjson\b/.test(arg) || arg === "--reporter" && /\bjson\b/.test(args[index + 1] ?? ""));
|
|
1664
2841
|
if (!require_Telemetry.isDisabled() && !isQuietFlag) console.error(`${(0, node_util.styleText)("yellow", "Notice:")} Kubb collects anonymous telemetry data to help improve the tool. No personal data or file contents are collected. \nTo disable, set ${(0, node_util.styleText)("cyan", "KUBB_DISABLE_TELEMETRY=1")}.\n`);
|
|
1665
|
-
await (0, gunshi.cli)(args, command$
|
|
2842
|
+
await (0, gunshi.cli)(args, command$14, {
|
|
1666
2843
|
name: "kubb",
|
|
1667
2844
|
version: require_Telemetry.version,
|
|
1668
2845
|
description: "Generate code from an OpenAPI specification, manage Kubb extensions, or connect the project to Kubb Studio.",
|
|
1669
2846
|
subCommands: {
|
|
1670
|
-
generate: command$
|
|
1671
|
-
init: command$
|
|
1672
|
-
validate: command$
|
|
1673
|
-
mcp: command$
|
|
1674
|
-
studio: command$
|
|
1675
|
-
add: command$
|
|
2847
|
+
generate: command$14,
|
|
2848
|
+
init: command$3,
|
|
2849
|
+
validate: command$5,
|
|
2850
|
+
mcp: command$13,
|
|
2851
|
+
studio: command$6,
|
|
2852
|
+
add: command$4,
|
|
1676
2853
|
update: command,
|
|
1677
|
-
|
|
2854
|
+
remove: command$1,
|
|
2855
|
+
list: command$2
|
|
1678
2856
|
},
|
|
1679
2857
|
fallbackToEntry: true,
|
|
1680
2858
|
renderHeader: isJsonOutput ? null : async (ctx) => {
|