@plop-next/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2871 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // node_modules/picocolors/picocolors.js
28
+ var require_picocolors = __commonJS({
29
+ "node_modules/picocolors/picocolors.js"(exports, module) {
30
+ var p = process || {};
31
+ var argv = p.argv || [];
32
+ var env = p.env || {};
33
+ var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
34
+ var formatter = (open, close, replace = open) => (input2) => {
35
+ let string = "" + input2, index = string.indexOf(close, open.length);
36
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
37
+ };
38
+ var replaceClose = (string, close, replace, index) => {
39
+ let result = "", cursor = 0;
40
+ do {
41
+ result += string.substring(cursor, index) + replace;
42
+ cursor = index + close.length;
43
+ index = string.indexOf(close, cursor);
44
+ } while (~index);
45
+ return result + string.substring(cursor);
46
+ };
47
+ var createColors = (enabled = isColorSupported) => {
48
+ let f = enabled ? formatter : () => String;
49
+ return {
50
+ isColorSupported: enabled,
51
+ reset: f("\x1B[0m", "\x1B[0m"),
52
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
53
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
54
+ italic: f("\x1B[3m", "\x1B[23m"),
55
+ underline: f("\x1B[4m", "\x1B[24m"),
56
+ inverse: f("\x1B[7m", "\x1B[27m"),
57
+ hidden: f("\x1B[8m", "\x1B[28m"),
58
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
59
+ black: f("\x1B[30m", "\x1B[39m"),
60
+ red: f("\x1B[31m", "\x1B[39m"),
61
+ green: f("\x1B[32m", "\x1B[39m"),
62
+ yellow: f("\x1B[33m", "\x1B[39m"),
63
+ blue: f("\x1B[34m", "\x1B[39m"),
64
+ magenta: f("\x1B[35m", "\x1B[39m"),
65
+ cyan: f("\x1B[36m", "\x1B[39m"),
66
+ white: f("\x1B[37m", "\x1B[39m"),
67
+ gray: f("\x1B[90m", "\x1B[39m"),
68
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
69
+ bgRed: f("\x1B[41m", "\x1B[49m"),
70
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
71
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
72
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
73
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
74
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
75
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
76
+ blackBright: f("\x1B[90m", "\x1B[39m"),
77
+ redBright: f("\x1B[91m", "\x1B[39m"),
78
+ greenBright: f("\x1B[92m", "\x1B[39m"),
79
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
80
+ blueBright: f("\x1B[94m", "\x1B[39m"),
81
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
82
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
83
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
84
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
85
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
86
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
87
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
88
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
89
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
90
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
91
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
92
+ };
93
+ };
94
+ module.exports = createColors();
95
+ module.exports.createColors = createColors;
96
+ }
97
+ });
98
+
99
+ // src/ActionRunner.ts
100
+ import { readFile, writeFile, mkdir } from "fs/promises";
101
+ import { existsSync } from "fs";
102
+ import { dirname, resolve, isAbsolute, relative, basename } from "path";
103
+ import { glob } from "tinyglobby";
104
+ var ActionRunner = class {
105
+ constructor(core, opts = {}) {
106
+ this.core = core;
107
+ this.opts = opts;
108
+ }
109
+ async run(type, config, answers) {
110
+ switch (type) {
111
+ case "add":
112
+ return this.runAdd(config, answers);
113
+ case "addMany":
114
+ return this.runAddMany(config, answers);
115
+ case "modify":
116
+ return this.runModify(config, answers);
117
+ case "append":
118
+ return this.runAppend(config, answers);
119
+ default:
120
+ throw new Error(this.core.t("errors.unknownAction", [type], `Unknown action type: "${type}"`));
121
+ }
122
+ }
123
+ async runAdd(config, answers) {
124
+ const path = this.interpolate(config.path ?? "", answers);
125
+ const absolutePath = this.toAbsolute(path);
126
+ const force = this.opts.force || config.force === true;
127
+ if (existsSync(absolutePath)) {
128
+ if (config.skipIfExists) {
129
+ return {
130
+ type: "skip",
131
+ message: `Skipped existing file: ${absolutePath}`,
132
+ path,
133
+ absolutePath
134
+ };
135
+ }
136
+ if (!force) {
137
+ throw new Error(this.core.t("actions.add.alreadyExists", [absolutePath], `File already exists: ${absolutePath}`));
138
+ }
139
+ }
140
+ let content = await this.resolveTemplate(config, answers);
141
+ content = await this.applyTransform(config, content, answers);
142
+ await mkdir(dirname(absolutePath), { recursive: true });
143
+ await writeFile(absolutePath, content, "utf8");
144
+ return {
145
+ type: "add",
146
+ message: this.core.t("actions.add.created", [absolutePath], `Created ${absolutePath}`),
147
+ path,
148
+ absolutePath
149
+ };
150
+ }
151
+ async runAddMany(config, answers) {
152
+ const force = this.opts.force || config.force === true;
153
+ const destination = this.interpolate(config.destination ?? "", answers);
154
+ const destinationAbs = this.toAbsolute(destination);
155
+ const templatePatterns = config.templateFiles;
156
+ if (!templatePatterns) {
157
+ throw new Error("addMany requires templateFiles.");
158
+ }
159
+ const files = await glob(templatePatterns, {
160
+ cwd: this.core.getPlopfilePath() ? dirname(this.core.getPlopfilePath()) : process.cwd(),
161
+ ...config.globOptions ?? {}
162
+ });
163
+ const baseDir = config.base ? this.toAbsolute(this.interpolate(config.base, answers)) : void 0;
164
+ const stripExtensions = config.stripExtensions ?? ["hbs"];
165
+ let written = 0;
166
+ for (const file of files) {
167
+ const templatePath = this.toAbsolute(file);
168
+ const rel = baseDir ? relative(baseDir, templatePath) : basename(templatePath);
169
+ let outRel = rel;
170
+ for (const ext of stripExtensions) {
171
+ const suffix = `.${ext}`;
172
+ if (outRel.endsWith(suffix)) {
173
+ outRel = outRel.slice(0, -suffix.length);
174
+ break;
175
+ }
176
+ }
177
+ outRel = this.interpolate(outRel, answers);
178
+ const outputAbs = resolve(destinationAbs, outRel);
179
+ if (existsSync(outputAbs)) {
180
+ if (config.skipIfExists) {
181
+ continue;
182
+ }
183
+ if (!force) {
184
+ throw new Error(this.core.t("actions.add.alreadyExists", [outputAbs], `File already exists: ${outputAbs}`));
185
+ }
186
+ }
187
+ const raw = await readFile(templatePath, "utf8");
188
+ let content = this.interpolate(raw, { ...answers, ...config.data ?? {} });
189
+ content = await this.applyTransform(config, content, answers);
190
+ await mkdir(dirname(outputAbs), { recursive: true });
191
+ await writeFile(outputAbs, content, "utf8");
192
+ written += 1;
193
+ }
194
+ return {
195
+ type: "addMany",
196
+ message: `Added ${written} file(s) to ${destinationAbs}`,
197
+ path: destination,
198
+ absolutePath: destinationAbs
199
+ };
200
+ }
201
+ async runModify(config, answers) {
202
+ const path = this.interpolate(config.path ?? "", answers);
203
+ const absolutePath = this.toAbsolute(path);
204
+ if (!existsSync(absolutePath)) {
205
+ throw new Error(this.core.t("actions.modify.notFound", [absolutePath], `File not found: ${absolutePath}`));
206
+ }
207
+ let content = await readFile(absolutePath, "utf8");
208
+ const pattern = config.pattern instanceof RegExp ? config.pattern : new RegExp(config.pattern ?? "$", "g");
209
+ const replacement = await this.resolveTemplate(config, answers);
210
+ if (!pattern.test(content)) {
211
+ throw new Error(this.core.t("actions.modify.patternNotFound", [absolutePath], `Pattern not found in: ${absolutePath}`));
212
+ }
213
+ content = content.replace(pattern, replacement);
214
+ content = await this.applyTransform(config, content, answers);
215
+ await writeFile(absolutePath, content, "utf8");
216
+ return {
217
+ type: "modify",
218
+ message: this.core.t("actions.modify.modified", [absolutePath], `Modified ${absolutePath}`),
219
+ path,
220
+ absolutePath
221
+ };
222
+ }
223
+ async runAppend(config, answers) {
224
+ const path = this.interpolate(config.path ?? "", answers);
225
+ const absolutePath = this.toAbsolute(path);
226
+ const addition = await this.resolveTemplate(config, answers);
227
+ let existing = existsSync(absolutePath) ? await readFile(absolutePath, "utf8") : "";
228
+ const unique = config.unique !== false;
229
+ if (!(unique && existing.includes(addition))) {
230
+ if (config.pattern !== void 0) {
231
+ const pattern = config.pattern instanceof RegExp ? config.pattern : new RegExp(String(config.pattern), "g");
232
+ const separator = config.separator ?? "";
233
+ existing = existing.replace(pattern, (match) => `${match}${separator}${addition}`);
234
+ } else {
235
+ existing = `${existing}${addition}`;
236
+ }
237
+ }
238
+ await mkdir(dirname(absolutePath), { recursive: true });
239
+ await writeFile(absolutePath, existing, "utf8");
240
+ return {
241
+ type: "append",
242
+ message: this.core.t("actions.append.appended", [absolutePath], `Appended to ${absolutePath}`),
243
+ path,
244
+ absolutePath
245
+ };
246
+ }
247
+ async resolveTemplate(config, answers) {
248
+ const model = { ...answers, ...config.data ?? {} };
249
+ if (config.templateFile) {
250
+ const templateFile = this.resolveTemplateFilePath(config.templateFile);
251
+ const raw = await readFile(templateFile, "utf8");
252
+ return this.interpolate(raw, model);
253
+ }
254
+ return this.interpolate(config.template ?? "", model);
255
+ }
256
+ toAbsolute(path) {
257
+ if (isAbsolute(path)) return path;
258
+ return resolve(this.opts.dest ?? this.core.getDestBasePath(), path);
259
+ }
260
+ resolveTemplateFilePath(path) {
261
+ if (isAbsolute(path)) {
262
+ return path;
263
+ }
264
+ const plopfilePath = this.core.getPlopfilePath();
265
+ if (plopfilePath) {
266
+ return resolve(dirname(plopfilePath), path);
267
+ }
268
+ return resolve(this.core.getDestBasePath(), path);
269
+ }
270
+ interpolate(template, data) {
271
+ return this.core.renderString(template, data);
272
+ }
273
+ async applyTransform(config, content, answers) {
274
+ if (!config.transform) {
275
+ return content;
276
+ }
277
+ const transformed = await config.transform(content, {
278
+ ...answers,
279
+ ...config.data ?? {}
280
+ });
281
+ return transformed;
282
+ }
283
+ };
284
+
285
+ // src/PlopNextCore.ts
286
+ import Handlebars from "handlebars";
287
+ import { dirname as dirname2, extname, resolve as resolve2 } from "path";
288
+ import { existsSync as existsSync2, readFileSync } from "fs";
289
+ import { createRequire } from "module";
290
+ import {
291
+ camelCase,
292
+ snakeCase,
293
+ dotCase,
294
+ pathCase,
295
+ sentenceCase,
296
+ constantCase,
297
+ kebabCase,
298
+ pascalCase
299
+ } from "change-case";
300
+ import { titleCase } from "title-case";
301
+
302
+ // src/prompts/PromptHandlerRegistry.ts
303
+ var PromptHandlerRegistry = class {
304
+ constructor() {
305
+ this.map = /* @__PURE__ */ new Map();
306
+ }
307
+ /**
308
+ * Register a handler for all its declared types.
309
+ * If a type is already registered, the new handler replaces the previous one,
310
+ * allowing built-in handlers to be overridden by plugins or users.
311
+ */
312
+ register(handler) {
313
+ for (const type of handler.types) {
314
+ this.map.set(type, handler);
315
+ }
316
+ return this;
317
+ }
318
+ /** Return the handler for the given type, or undefined if none is registered. */
319
+ get(type) {
320
+ return this.map.get(type);
321
+ }
322
+ /** True if a handler is registered for the given type. */
323
+ has(type) {
324
+ return this.map.has(type);
325
+ }
326
+ /** List of all registered type strings. */
327
+ getRegisteredTypes() {
328
+ return Array.from(this.map.keys());
329
+ }
330
+ /**
331
+ * Dispatch a prompt to the right handler.
332
+ *
333
+ * @param type The prompt type string.
334
+ * @param config Prompt fields without plop-next-only keys.
335
+ * @returns The user's answer.
336
+ * @throws If no handler is registered for the type and no "input" fallback exists.
337
+ */
338
+ async ask(type, config, options = {}) {
339
+ const handler = this.map.get(type) ?? this.map.get("input");
340
+ if (!handler) {
341
+ throw new Error(
342
+ `No handler registered for prompt type "${type}". Register one with core.registerPrompt(handler).`
343
+ );
344
+ }
345
+ if (!options.allowTheme && Object.prototype.hasOwnProperty.call(config, "theme")) {
346
+ throw new Error(
347
+ `The "theme" prompt field is not supported in plop-next. Use core.setTheme({ ... }) instead.`
348
+ );
349
+ }
350
+ return handler.ask(type, config);
351
+ }
352
+ };
353
+
354
+ // src/prompts/inputPrompt.ts
355
+ import { input } from "@inquirer/prompts";
356
+ var inputPromptHandler = {
357
+ types: ["input"],
358
+ async ask(_type, config) {
359
+ const {
360
+ name: _name,
361
+ message,
362
+ default: defaultValue,
363
+ validate,
364
+ transformer,
365
+ pattern,
366
+ patternError,
367
+ prefill,
368
+ required,
369
+ ...rest
370
+ } = config;
371
+ const runInput = input;
372
+ return runInput({
373
+ message: String(message ?? ""),
374
+ ...defaultValue !== void 0 && { default: String(defaultValue) },
375
+ ...validate !== void 0 && {
376
+ validate
377
+ },
378
+ ...transformer !== void 0 && {
379
+ transformer
380
+ },
381
+ ...pattern !== void 0 && { pattern },
382
+ ...patternError !== void 0 && { patternError: String(patternError) },
383
+ ...prefill !== void 0 && { prefill },
384
+ ...required !== void 0 && { required: Boolean(required) },
385
+ ...rest
386
+ });
387
+ }
388
+ };
389
+
390
+ // src/prompts/numberPrompt.ts
391
+ import { number as numberPrompt } from "@inquirer/prompts";
392
+ var numberPromptHandler = {
393
+ types: ["number"],
394
+ async ask(_type, config) {
395
+ const {
396
+ name: _name,
397
+ message,
398
+ default: defaultValue,
399
+ min,
400
+ max,
401
+ step,
402
+ required,
403
+ validate,
404
+ theme,
405
+ ...rest
406
+ } = config;
407
+ const unsupportedKeys = Object.keys(rest);
408
+ if (unsupportedKeys.length > 0) {
409
+ throw new Error(
410
+ `Prompt type "number" does not support: ${unsupportedKeys.join(", ")}. Supported fields are: message, default, min, max, step, required, validate, theme.`
411
+ );
412
+ }
413
+ if (defaultValue !== void 0 && typeof defaultValue !== "number") {
414
+ throw new Error('Prompt type "number" expects "default" to be a number.');
415
+ }
416
+ if (min !== void 0 && typeof min !== "number") {
417
+ throw new Error('Prompt type "number" expects "min" to be a number.');
418
+ }
419
+ if (max !== void 0 && typeof max !== "number") {
420
+ throw new Error('Prompt type "number" expects "max" to be a number.');
421
+ }
422
+ if (step !== void 0 && step !== "any" && typeof step !== "number") {
423
+ throw new Error('Prompt type "number" expects "step" to be a number or "any".');
424
+ }
425
+ if (required !== void 0 && typeof required !== "boolean") {
426
+ throw new Error('Prompt type "number" expects "required" to be a boolean.');
427
+ }
428
+ if (validate !== void 0 && typeof validate !== "function") {
429
+ throw new Error('Prompt type "number" expects "validate" to be a function.');
430
+ }
431
+ const runNumberPrompt = numberPrompt;
432
+ return runNumberPrompt({
433
+ message: String(message ?? ""),
434
+ ...defaultValue !== void 0 && { default: defaultValue },
435
+ ...validate !== void 0 && {
436
+ validate
437
+ },
438
+ ...min !== void 0 && { min },
439
+ ...max !== void 0 && { max },
440
+ ...step !== void 0 && { step },
441
+ ...required !== void 0 && { required },
442
+ ...theme !== void 0 && { theme }
443
+ });
444
+ }
445
+ };
446
+
447
+ // src/prompts/confirmPrompt.ts
448
+ import { confirm } from "@inquirer/prompts";
449
+ function createConfirmHandler(fn) {
450
+ const confirmFn = fn ?? confirm;
451
+ return {
452
+ types: ["confirm"],
453
+ async ask(_type, config) {
454
+ const {
455
+ name: _name,
456
+ message,
457
+ default: defaultValue,
458
+ transformer,
459
+ theme,
460
+ ...rest
461
+ } = config;
462
+ const unsupportedKeys = Object.keys(rest);
463
+ if (unsupportedKeys.length > 0) {
464
+ throw new Error(
465
+ `Prompt type "confirm" does not support: ${unsupportedKeys.join(", ")}. Supported fields are: message, default, transformer, theme.`
466
+ );
467
+ }
468
+ if (defaultValue !== void 0 && typeof defaultValue !== "boolean") {
469
+ throw new Error('Prompt type "confirm" expects "default" to be a boolean.');
470
+ }
471
+ if (transformer !== void 0 && typeof transformer !== "function") {
472
+ throw new Error('Prompt type "confirm" expects "transformer" to be a function.');
473
+ }
474
+ return confirmFn({
475
+ message: String(message ?? ""),
476
+ ...defaultValue !== void 0 && { default: defaultValue },
477
+ ...transformer !== void 0 && {
478
+ transformer
479
+ },
480
+ ...theme !== void 0 && { theme }
481
+ });
482
+ }
483
+ };
484
+ }
485
+ var confirmPromptHandler = createConfirmHandler();
486
+
487
+ // src/prompts/selectPrompt.ts
488
+ import { select } from "@inquirer/prompts";
489
+ function createSelectHandler(fn) {
490
+ const selectFn = fn ?? select;
491
+ return {
492
+ types: ["list", "select", "generator-select"],
493
+ async ask(type, config) {
494
+ const {
495
+ name: _name,
496
+ message,
497
+ default: defaultValue,
498
+ choices,
499
+ loop,
500
+ pageSize,
501
+ validate,
502
+ theme,
503
+ ...rest
504
+ } = config;
505
+ if (choices === void 0) {
506
+ throw new Error('Prompt type "select" requires a "choices" field.');
507
+ }
508
+ if (validate !== void 0) {
509
+ throw new Error('Prompt type "select" does not support "validate".');
510
+ }
511
+ const unsupportedKeys = Object.keys(rest);
512
+ if (unsupportedKeys.length > 0) {
513
+ throw new Error(
514
+ `Prompt type "select" does not support: ${unsupportedKeys.join(", ")}. Supported fields are: message, choices, default, pageSize, loop, theme.`
515
+ );
516
+ }
517
+ if (pageSize !== void 0 && (typeof pageSize !== "number" || Number.isNaN(pageSize))) {
518
+ throw new Error(
519
+ 'Prompt type "select" expects "pageSize" to be a number.'
520
+ );
521
+ }
522
+ if (loop !== void 0 && typeof loop !== "boolean") {
523
+ throw new Error('Prompt type "select" expects "loop" to be a boolean.');
524
+ }
525
+ return selectFn({
526
+ message: String(message ?? ""),
527
+ choices,
528
+ ...defaultValue !== void 0 && { default: defaultValue },
529
+ ...pageSize !== void 0 && { pageSize },
530
+ ...loop !== void 0 && { loop },
531
+ ...theme !== void 0 && { theme }
532
+ });
533
+ }
534
+ };
535
+ }
536
+ var selectPromptHandler = createSelectHandler();
537
+
538
+ // src/prompts/rawlistPrompt.ts
539
+ import { rawlist } from "@inquirer/prompts";
540
+ var rawlistPromptHandler = {
541
+ types: ["rawlist"],
542
+ async ask(_type, config) {
543
+ const {
544
+ name: _name,
545
+ message,
546
+ choices,
547
+ default: defaultValue,
548
+ loop,
549
+ theme,
550
+ ...rest
551
+ } = config;
552
+ if (choices === void 0) {
553
+ throw new Error('Prompt type "rawlist" requires a "choices" field.');
554
+ }
555
+ const unsupportedKeys = Object.keys(rest);
556
+ if (unsupportedKeys.length > 0) {
557
+ throw new Error(
558
+ `Prompt type "rawlist" does not support: ${unsupportedKeys.join(", ")}. Supported fields are: message, choices, default, loop, theme.`
559
+ );
560
+ }
561
+ if (loop !== void 0 && typeof loop !== "boolean") {
562
+ throw new Error('Prompt type "rawlist" expects "loop" to be a boolean.');
563
+ }
564
+ const runRawlist = rawlist;
565
+ return runRawlist({
566
+ message: String(message ?? ""),
567
+ choices,
568
+ ...defaultValue !== void 0 && { default: defaultValue },
569
+ ...loop !== void 0 && { loop },
570
+ ...theme !== void 0 && { theme }
571
+ });
572
+ }
573
+ };
574
+
575
+ // src/prompts/expandPrompt.ts
576
+ import { expand } from "@inquirer/prompts";
577
+ var expandPromptHandler = {
578
+ types: ["expand"],
579
+ async ask(_type, config) {
580
+ const {
581
+ name: _name,
582
+ message,
583
+ choices,
584
+ default: defaultValue,
585
+ expanded,
586
+ theme,
587
+ ...rest
588
+ } = config;
589
+ if (choices === void 0) {
590
+ throw new Error('Prompt type "expand" requires a "choices" field.');
591
+ }
592
+ const unsupportedKeys = Object.keys(rest);
593
+ if (unsupportedKeys.length > 0) {
594
+ throw new Error(
595
+ `Prompt type "expand" does not support: ${unsupportedKeys.join(", ")}. Supported fields are: message, choices, default, expanded, theme.`
596
+ );
597
+ }
598
+ if (expanded !== void 0 && typeof expanded !== "boolean") {
599
+ throw new Error('Prompt type "expand" expects "expanded" to be a boolean.');
600
+ }
601
+ if (defaultValue !== void 0 && typeof defaultValue !== "string") {
602
+ throw new Error('Prompt type "expand" expects "default" to be a string key.');
603
+ }
604
+ const runExpand = expand;
605
+ return runExpand({
606
+ message: String(message ?? ""),
607
+ choices,
608
+ ...defaultValue !== void 0 && { default: defaultValue },
609
+ ...expanded !== void 0 && { expanded },
610
+ ...theme !== void 0 && { theme }
611
+ });
612
+ }
613
+ };
614
+
615
+ // src/prompts/searchPrompt.ts
616
+ import { search } from "@inquirer/prompts";
617
+ function createSearchHandler(fn) {
618
+ const searchFn = fn ?? search;
619
+ return {
620
+ types: ["search"],
621
+ async ask(_type, config) {
622
+ const {
623
+ name: _name,
624
+ message,
625
+ source,
626
+ pageSize,
627
+ default: defaultValue,
628
+ validate,
629
+ theme,
630
+ ...rest
631
+ } = config;
632
+ if (typeof source !== "function") {
633
+ throw new Error('Prompt type "search" requires a "source" function.');
634
+ }
635
+ const unsupportedKeys = Object.keys(rest);
636
+ if (unsupportedKeys.length > 0) {
637
+ throw new Error(
638
+ `Prompt type "search" does not support: ${unsupportedKeys.join(", ")}. Supported fields are: message, source, pageSize, default, validate, theme.`
639
+ );
640
+ }
641
+ if (pageSize !== void 0 && (typeof pageSize !== "number" || Number.isNaN(pageSize))) {
642
+ throw new Error('Prompt type "search" expects "pageSize" to be a number.');
643
+ }
644
+ if (validate !== void 0 && typeof validate !== "function") {
645
+ throw new Error('Prompt type "search" expects "validate" to be a function.');
646
+ }
647
+ return searchFn({
648
+ message: String(message ?? ""),
649
+ source,
650
+ ...pageSize !== void 0 && { pageSize },
651
+ ...defaultValue !== void 0 && { default: defaultValue },
652
+ ...validate !== void 0 && {
653
+ validate
654
+ },
655
+ ...theme !== void 0 && { theme }
656
+ });
657
+ }
658
+ };
659
+ }
660
+ var searchPromptHandler = createSearchHandler();
661
+
662
+ // src/prompts/checkboxPrompt.ts
663
+ import { checkbox } from "@inquirer/prompts";
664
+ function createCheckboxHandler(fn) {
665
+ const checkboxFn = fn ?? checkbox;
666
+ return {
667
+ types: ["checkbox"],
668
+ async ask(_type, config) {
669
+ const {
670
+ name: _name,
671
+ message,
672
+ default: defaultValue,
673
+ choices,
674
+ pageSize,
675
+ loop,
676
+ required,
677
+ shortcuts,
678
+ validate,
679
+ ...rest
680
+ } = config;
681
+ if (choices === void 0) {
682
+ throw new Error('Prompt type "checkbox" requires a "choices" field.');
683
+ }
684
+ if (defaultValue !== void 0) {
685
+ throw new Error('Prompt type "checkbox" does not support "default". Use choice.checked instead.');
686
+ }
687
+ return checkboxFn({
688
+ message: String(message ?? ""),
689
+ choices,
690
+ ...pageSize !== void 0 && { pageSize: Number(pageSize) },
691
+ ...loop !== void 0 && { loop: Boolean(loop) },
692
+ ...validate !== void 0 && {
693
+ validate
694
+ },
695
+ ...required !== void 0 && { required: Boolean(required) },
696
+ ...shortcuts !== void 0 && { shortcuts },
697
+ ...rest
698
+ });
699
+ }
700
+ };
701
+ }
702
+ var checkboxPromptHandler = createCheckboxHandler();
703
+
704
+ // src/prompts/editorPrompt.ts
705
+ import { editor } from "@inquirer/prompts";
706
+ function createEditorHandler(fn) {
707
+ const editorFn = fn ?? editor;
708
+ return {
709
+ types: ["editor"],
710
+ async ask(_type, config) {
711
+ const {
712
+ name: _name,
713
+ message,
714
+ default: defaultValue,
715
+ validate,
716
+ postfix,
717
+ waitForUserInput,
718
+ file,
719
+ theme,
720
+ ...rest
721
+ } = config;
722
+ const unsupportedKeys = Object.keys(rest);
723
+ if (unsupportedKeys.length > 0) {
724
+ throw new Error(
725
+ `Prompt type "editor" does not support: ${unsupportedKeys.join(", ")}. Supported fields are: message, default, postfix, waitForUserInput, file, validate, theme.`
726
+ );
727
+ }
728
+ if (defaultValue !== void 0 && typeof defaultValue !== "string") {
729
+ throw new Error('Prompt type "editor" expects "default" to be a string.');
730
+ }
731
+ if (postfix !== void 0 && typeof postfix !== "string") {
732
+ throw new Error('Prompt type "editor" expects "postfix" to be a string.');
733
+ }
734
+ if (waitForUserInput !== void 0 && typeof waitForUserInput !== "boolean") {
735
+ throw new Error('Prompt type "editor" expects "waitForUserInput" to be a boolean.');
736
+ }
737
+ if (file !== void 0 && (typeof file !== "object" || file === null)) {
738
+ throw new Error('Prompt type "editor" expects "file" to be an object.');
739
+ }
740
+ if (validate !== void 0 && typeof validate !== "function") {
741
+ throw new Error('Prompt type "editor" expects "validate" to be a function.');
742
+ }
743
+ return editorFn({
744
+ message: String(message ?? ""),
745
+ ...defaultValue !== void 0 && { default: String(defaultValue) },
746
+ ...validate !== void 0 && {
747
+ validate
748
+ },
749
+ ...postfix !== void 0 && { postfix: String(postfix) },
750
+ ...waitForUserInput !== void 0 && { waitForUserInput },
751
+ ...file !== void 0 && { file },
752
+ ...theme !== void 0 && { theme }
753
+ });
754
+ }
755
+ };
756
+ }
757
+ var editorPromptHandler = createEditorHandler();
758
+
759
+ // src/prompts/passwordPrompt.ts
760
+ import { password } from "@inquirer/prompts";
761
+ function createPasswordHandler(fn) {
762
+ const passwordFn = fn ?? password;
763
+ return {
764
+ types: ["password"],
765
+ async ask(_type, config) {
766
+ const {
767
+ name: _name,
768
+ message,
769
+ validate,
770
+ mask,
771
+ theme,
772
+ ...rest
773
+ } = config;
774
+ const unsupportedKeys = Object.keys(rest);
775
+ if (unsupportedKeys.length > 0) {
776
+ throw new Error(
777
+ `Prompt type "password" does not support: ${unsupportedKeys.join(", ")}. Supported fields are: message, mask, validate, theme.`
778
+ );
779
+ }
780
+ if (mask !== void 0 && typeof mask !== "boolean" && typeof mask !== "string") {
781
+ throw new Error('Prompt type "password" expects "mask" to be a boolean or string.');
782
+ }
783
+ if (validate !== void 0 && typeof validate !== "function") {
784
+ throw new Error('Prompt type "password" expects "validate" to be a function.');
785
+ }
786
+ return passwordFn({
787
+ message: String(message ?? ""),
788
+ ...validate !== void 0 && {
789
+ validate
790
+ },
791
+ ...mask !== void 0 && { mask },
792
+ ...theme !== void 0 && { theme }
793
+ });
794
+ }
795
+ };
796
+ }
797
+ var passwordPromptHandler = createPasswordHandler();
798
+
799
+ // src/prompts/registerBuiltins.ts
800
+ var builtInPromptHandlers = [
801
+ inputPromptHandler,
802
+ numberPromptHandler,
803
+ confirmPromptHandler,
804
+ selectPromptHandler,
805
+ rawlistPromptHandler,
806
+ expandPromptHandler,
807
+ searchPromptHandler,
808
+ checkboxPromptHandler,
809
+ editorPromptHandler,
810
+ passwordPromptHandler
811
+ ];
812
+ function registerBuiltInPromptHandlers(registry) {
813
+ for (const handler of builtInPromptHandlers) {
814
+ registry.register(handler);
815
+ }
816
+ }
817
+
818
+ // src/prompts/themeSelector.ts
819
+ var BUILT_IN_PROMPT_THEME_SELECTORS = {
820
+ common: {
821
+ prefix: true,
822
+ spinner: true,
823
+ style: [
824
+ "answer",
825
+ "message",
826
+ "error",
827
+ "defaultAnswer",
828
+ "help",
829
+ "highlight",
830
+ "key"
831
+ ]
832
+ },
833
+ input: {
834
+ prefix: true,
835
+ spinner: true,
836
+ validationFailureMode: true,
837
+ style: ["answer", "message", "error", "defaultAnswer"]
838
+ },
839
+ select: {
840
+ prefix: true,
841
+ spinner: true,
842
+ indexMode: true,
843
+ style: [
844
+ "answer",
845
+ "message",
846
+ "error",
847
+ "help",
848
+ "highlight",
849
+ "description",
850
+ "disabled",
851
+ "keysHelpTip"
852
+ ],
853
+ icon: ["cursor"],
854
+ i18n: true,
855
+ keybindings: true
856
+ },
857
+ list: { baseSelector: "select" },
858
+ "generator-select": { baseSelector: "select" },
859
+ checkbox: {
860
+ prefix: true,
861
+ spinner: true,
862
+ style: [
863
+ "answer",
864
+ "message",
865
+ "error",
866
+ "defaultAnswer",
867
+ "help",
868
+ "highlight",
869
+ "key",
870
+ "disabled",
871
+ "disabledChoice",
872
+ "description",
873
+ "renderSelectedChoices",
874
+ "keysHelpTip"
875
+ ],
876
+ icon: [
877
+ "checked",
878
+ "unchecked",
879
+ "cursor",
880
+ "disabledChecked",
881
+ "disabledUnchecked"
882
+ ],
883
+ i18n: true,
884
+ keybindings: true
885
+ },
886
+ confirm: {
887
+ prefix: true,
888
+ spinner: true,
889
+ style: ["answer", "message", "defaultAnswer"]
890
+ },
891
+ search: {
892
+ prefix: true,
893
+ spinner: true,
894
+ style: [
895
+ "answer",
896
+ "message",
897
+ "error",
898
+ "help",
899
+ "highlight",
900
+ "description",
901
+ "disabled",
902
+ "searchTerm",
903
+ "keysHelpTip"
904
+ ],
905
+ icon: ["cursor"]
906
+ },
907
+ password: {
908
+ prefix: true,
909
+ spinner: true,
910
+ style: ["answer", "message", "error", "help", "maskedText"]
911
+ },
912
+ expand: {
913
+ prefix: true,
914
+ spinner: true,
915
+ style: ["answer", "message", "error", "defaultAnswer", "highlight"]
916
+ },
917
+ editor: {
918
+ prefix: true,
919
+ spinner: true,
920
+ validationFailureMode: true,
921
+ style: ["message", "error", "help", "key", "waitingMessage"]
922
+ },
923
+ number: {
924
+ prefix: true,
925
+ spinner: true,
926
+ style: ["answer", "message", "error", "defaultAnswer"]
927
+ },
928
+ rawlist: {
929
+ prefix: true,
930
+ spinner: true,
931
+ style: ["answer", "message", "error", "highlight", "description"]
932
+ }
933
+ };
934
+ var PromptThemeSelectorRegistry = class {
935
+ constructor() {
936
+ this.selectors = /* @__PURE__ */ new Map();
937
+ this.defaultSelectorName = "common";
938
+ for (const [type, selector] of Object.entries(
939
+ BUILT_IN_PROMPT_THEME_SELECTORS
940
+ )) {
941
+ this.selectors.set(type, cloneSelector(selector));
942
+ }
943
+ }
944
+ register(type, selector) {
945
+ this.selectors.set(type, cloneSelector(selector));
946
+ }
947
+ resolve(type) {
948
+ return this.selectors.get(type);
949
+ }
950
+ registerWithDefault(type, selector) {
951
+ this.register(type, {
952
+ baseSelector: this.defaultSelectorName,
953
+ ...selector ?? {}
954
+ });
955
+ }
956
+ resolveTheme(type, theme) {
957
+ const selector = this.resolve(type);
958
+ if (!selector) {
959
+ return this.resolveTheme(this.defaultSelectorName, theme);
960
+ }
961
+ const resolved = this.expandSelector(selector, /* @__PURE__ */ new Set());
962
+ const source = theme;
963
+ const output = {};
964
+ for (const [field, spec] of Object.entries(resolved)) {
965
+ if (!Object.prototype.hasOwnProperty.call(source, field)) continue;
966
+ if (spec === true) {
967
+ output[field] = source[field];
968
+ } else {
969
+ const sourceObj = source[field];
970
+ if (sourceObj === null || typeof sourceObj !== "object" || Array.isArray(sourceObj)) {
971
+ continue;
972
+ }
973
+ const filtered = {};
974
+ for (const subField of spec) {
975
+ if (Object.prototype.hasOwnProperty.call(sourceObj, subField)) {
976
+ filtered[subField] = sourceObj[subField];
977
+ }
978
+ }
979
+ if (Object.keys(filtered).length > 0) {
980
+ output[field] = filtered;
981
+ }
982
+ }
983
+ }
984
+ return output;
985
+ }
986
+ expandSelector(selector, visited) {
987
+ const result = {};
988
+ if (selector.baseSelector) {
989
+ if (visited.has(selector.baseSelector)) {
990
+ throw new Error(
991
+ `Circular prompt theme selector inheritance detected for "${selector.baseSelector}".`
992
+ );
993
+ }
994
+ const base = this.selectors.get(selector.baseSelector);
995
+ if (!base) {
996
+ throw new Error(
997
+ `Unknown prompt theme selector base "${selector.baseSelector}".`
998
+ );
999
+ }
1000
+ visited.add(selector.baseSelector);
1001
+ const expandedBase = this.expandSelector(base, visited);
1002
+ Object.assign(result, expandedBase);
1003
+ visited.delete(selector.baseSelector);
1004
+ }
1005
+ for (const [field, spec] of Object.entries(selector)) {
1006
+ if (field === "baseSelector") continue;
1007
+ const typedSpec = spec;
1008
+ if (typedSpec === true) {
1009
+ result[field] = true;
1010
+ } else if (Array.isArray(typedSpec)) {
1011
+ const existing = result[field];
1012
+ if (Array.isArray(existing)) {
1013
+ const merged = /* @__PURE__ */ new Set([...existing, ...typedSpec]);
1014
+ result[field] = [...merged];
1015
+ } else {
1016
+ result[field] = [...typedSpec];
1017
+ }
1018
+ }
1019
+ }
1020
+ return result;
1021
+ }
1022
+ };
1023
+ function createPromptThemeSelector(options) {
1024
+ const { selector, ...fields } = options;
1025
+ return {
1026
+ ...selector !== void 0 ? { baseSelector: selector } : {},
1027
+ ...fields
1028
+ };
1029
+ }
1030
+ function cloneSelector(selector) {
1031
+ const cloned = {};
1032
+ if (selector.baseSelector !== void 0) {
1033
+ cloned.baseSelector = selector.baseSelector;
1034
+ }
1035
+ for (const [field, spec] of Object.entries(selector)) {
1036
+ if (field === "baseSelector") continue;
1037
+ cloned[field] = Array.isArray(spec) ? [...spec] : spec;
1038
+ }
1039
+ return cloned;
1040
+ }
1041
+
1042
+ // src/prompts/customPrompt.ts
1043
+ function registerCustomPrompt(promptTypes, name, prompt) {
1044
+ if (!prompt) {
1045
+ throw new Error(`registerPrompt("${name}", prompt) requires a prompt function.`);
1046
+ }
1047
+ promptTypes.set(name, prompt);
1048
+ }
1049
+ function getCustomPrompt(promptTypes, name) {
1050
+ return promptTypes.get(name);
1051
+ }
1052
+ function listCustomPromptTypes(promptTypes) {
1053
+ return Array.from(promptTypes.keys());
1054
+ }
1055
+ function askCustomPrompt(prompt, config) {
1056
+ const { name: _name, ...rest } = config;
1057
+ return Promise.resolve(prompt(rest));
1058
+ }
1059
+
1060
+ // src/theme.ts
1061
+ import { styleText } from "util";
1062
+
1063
+ // ../../node_modules/is-unicode-supported/index.js
1064
+ import process2 from "process";
1065
+ function isUnicodeSupported() {
1066
+ const { env } = process2;
1067
+ const { TERM, TERM_PROGRAM } = env;
1068
+ if (process2.platform !== "win32") {
1069
+ return TERM !== "linux";
1070
+ }
1071
+ return Boolean(env.WT_SESSION) || Boolean(env.TERMINUS_SUBLIME) || env.ConEmuTask === "{cmd::Cmder}" || TERM_PROGRAM === "Terminus-Sublime" || TERM_PROGRAM === "vscode" || TERM === "xterm-256color" || TERM === "alacritty" || TERM === "rxvt-unicode" || TERM === "rxvt-unicode-256color" || env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
1072
+ }
1073
+
1074
+ // ../../node_modules/figures/index.js
1075
+ var common = {
1076
+ circleQuestionMark: "(?)",
1077
+ questionMarkPrefix: "(?)",
1078
+ square: "\u2588",
1079
+ squareDarkShade: "\u2593",
1080
+ squareMediumShade: "\u2592",
1081
+ squareLightShade: "\u2591",
1082
+ squareTop: "\u2580",
1083
+ squareBottom: "\u2584",
1084
+ squareLeft: "\u258C",
1085
+ squareRight: "\u2590",
1086
+ squareCenter: "\u25A0",
1087
+ bullet: "\u25CF",
1088
+ dot: "\u2024",
1089
+ ellipsis: "\u2026",
1090
+ pointerSmall: "\u203A",
1091
+ triangleUp: "\u25B2",
1092
+ triangleUpSmall: "\u25B4",
1093
+ triangleDown: "\u25BC",
1094
+ triangleDownSmall: "\u25BE",
1095
+ triangleLeftSmall: "\u25C2",
1096
+ triangleRightSmall: "\u25B8",
1097
+ home: "\u2302",
1098
+ heart: "\u2665",
1099
+ musicNote: "\u266A",
1100
+ musicNoteBeamed: "\u266B",
1101
+ arrowUp: "\u2191",
1102
+ arrowDown: "\u2193",
1103
+ arrowLeft: "\u2190",
1104
+ arrowRight: "\u2192",
1105
+ arrowLeftRight: "\u2194",
1106
+ arrowUpDown: "\u2195",
1107
+ almostEqual: "\u2248",
1108
+ notEqual: "\u2260",
1109
+ lessOrEqual: "\u2264",
1110
+ greaterOrEqual: "\u2265",
1111
+ identical: "\u2261",
1112
+ infinity: "\u221E",
1113
+ subscriptZero: "\u2080",
1114
+ subscriptOne: "\u2081",
1115
+ subscriptTwo: "\u2082",
1116
+ subscriptThree: "\u2083",
1117
+ subscriptFour: "\u2084",
1118
+ subscriptFive: "\u2085",
1119
+ subscriptSix: "\u2086",
1120
+ subscriptSeven: "\u2087",
1121
+ subscriptEight: "\u2088",
1122
+ subscriptNine: "\u2089",
1123
+ oneHalf: "\xBD",
1124
+ oneThird: "\u2153",
1125
+ oneQuarter: "\xBC",
1126
+ oneFifth: "\u2155",
1127
+ oneSixth: "\u2159",
1128
+ oneEighth: "\u215B",
1129
+ twoThirds: "\u2154",
1130
+ twoFifths: "\u2156",
1131
+ threeQuarters: "\xBE",
1132
+ threeFifths: "\u2157",
1133
+ threeEighths: "\u215C",
1134
+ fourFifths: "\u2158",
1135
+ fiveSixths: "\u215A",
1136
+ fiveEighths: "\u215D",
1137
+ sevenEighths: "\u215E",
1138
+ line: "\u2500",
1139
+ lineBold: "\u2501",
1140
+ lineDouble: "\u2550",
1141
+ lineDashed0: "\u2504",
1142
+ lineDashed1: "\u2505",
1143
+ lineDashed2: "\u2508",
1144
+ lineDashed3: "\u2509",
1145
+ lineDashed4: "\u254C",
1146
+ lineDashed5: "\u254D",
1147
+ lineDashed6: "\u2574",
1148
+ lineDashed7: "\u2576",
1149
+ lineDashed8: "\u2578",
1150
+ lineDashed9: "\u257A",
1151
+ lineDashed10: "\u257C",
1152
+ lineDashed11: "\u257E",
1153
+ lineDashed12: "\u2212",
1154
+ lineDashed13: "\u2013",
1155
+ lineDashed14: "\u2010",
1156
+ lineDashed15: "\u2043",
1157
+ lineVertical: "\u2502",
1158
+ lineVerticalBold: "\u2503",
1159
+ lineVerticalDouble: "\u2551",
1160
+ lineVerticalDashed0: "\u2506",
1161
+ lineVerticalDashed1: "\u2507",
1162
+ lineVerticalDashed2: "\u250A",
1163
+ lineVerticalDashed3: "\u250B",
1164
+ lineVerticalDashed4: "\u254E",
1165
+ lineVerticalDashed5: "\u254F",
1166
+ lineVerticalDashed6: "\u2575",
1167
+ lineVerticalDashed7: "\u2577",
1168
+ lineVerticalDashed8: "\u2579",
1169
+ lineVerticalDashed9: "\u257B",
1170
+ lineVerticalDashed10: "\u257D",
1171
+ lineVerticalDashed11: "\u257F",
1172
+ lineDownLeft: "\u2510",
1173
+ lineDownLeftArc: "\u256E",
1174
+ lineDownBoldLeftBold: "\u2513",
1175
+ lineDownBoldLeft: "\u2512",
1176
+ lineDownLeftBold: "\u2511",
1177
+ lineDownDoubleLeftDouble: "\u2557",
1178
+ lineDownDoubleLeft: "\u2556",
1179
+ lineDownLeftDouble: "\u2555",
1180
+ lineDownRight: "\u250C",
1181
+ lineDownRightArc: "\u256D",
1182
+ lineDownBoldRightBold: "\u250F",
1183
+ lineDownBoldRight: "\u250E",
1184
+ lineDownRightBold: "\u250D",
1185
+ lineDownDoubleRightDouble: "\u2554",
1186
+ lineDownDoubleRight: "\u2553",
1187
+ lineDownRightDouble: "\u2552",
1188
+ lineUpLeft: "\u2518",
1189
+ lineUpLeftArc: "\u256F",
1190
+ lineUpBoldLeftBold: "\u251B",
1191
+ lineUpBoldLeft: "\u251A",
1192
+ lineUpLeftBold: "\u2519",
1193
+ lineUpDoubleLeftDouble: "\u255D",
1194
+ lineUpDoubleLeft: "\u255C",
1195
+ lineUpLeftDouble: "\u255B",
1196
+ lineUpRight: "\u2514",
1197
+ lineUpRightArc: "\u2570",
1198
+ lineUpBoldRightBold: "\u2517",
1199
+ lineUpBoldRight: "\u2516",
1200
+ lineUpRightBold: "\u2515",
1201
+ lineUpDoubleRightDouble: "\u255A",
1202
+ lineUpDoubleRight: "\u2559",
1203
+ lineUpRightDouble: "\u2558",
1204
+ lineUpDownLeft: "\u2524",
1205
+ lineUpBoldDownBoldLeftBold: "\u252B",
1206
+ lineUpBoldDownBoldLeft: "\u2528",
1207
+ lineUpDownLeftBold: "\u2525",
1208
+ lineUpBoldDownLeftBold: "\u2529",
1209
+ lineUpDownBoldLeftBold: "\u252A",
1210
+ lineUpDownBoldLeft: "\u2527",
1211
+ lineUpBoldDownLeft: "\u2526",
1212
+ lineUpDoubleDownDoubleLeftDouble: "\u2563",
1213
+ lineUpDoubleDownDoubleLeft: "\u2562",
1214
+ lineUpDownLeftDouble: "\u2561",
1215
+ lineUpDownRight: "\u251C",
1216
+ lineUpBoldDownBoldRightBold: "\u2523",
1217
+ lineUpBoldDownBoldRight: "\u2520",
1218
+ lineUpDownRightBold: "\u251D",
1219
+ lineUpBoldDownRightBold: "\u2521",
1220
+ lineUpDownBoldRightBold: "\u2522",
1221
+ lineUpDownBoldRight: "\u251F",
1222
+ lineUpBoldDownRight: "\u251E",
1223
+ lineUpDoubleDownDoubleRightDouble: "\u2560",
1224
+ lineUpDoubleDownDoubleRight: "\u255F",
1225
+ lineUpDownRightDouble: "\u255E",
1226
+ lineDownLeftRight: "\u252C",
1227
+ lineDownBoldLeftBoldRightBold: "\u2533",
1228
+ lineDownLeftBoldRightBold: "\u252F",
1229
+ lineDownBoldLeftRight: "\u2530",
1230
+ lineDownBoldLeftBoldRight: "\u2531",
1231
+ lineDownBoldLeftRightBold: "\u2532",
1232
+ lineDownLeftRightBold: "\u252E",
1233
+ lineDownLeftBoldRight: "\u252D",
1234
+ lineDownDoubleLeftDoubleRightDouble: "\u2566",
1235
+ lineDownDoubleLeftRight: "\u2565",
1236
+ lineDownLeftDoubleRightDouble: "\u2564",
1237
+ lineUpLeftRight: "\u2534",
1238
+ lineUpBoldLeftBoldRightBold: "\u253B",
1239
+ lineUpLeftBoldRightBold: "\u2537",
1240
+ lineUpBoldLeftRight: "\u2538",
1241
+ lineUpBoldLeftBoldRight: "\u2539",
1242
+ lineUpBoldLeftRightBold: "\u253A",
1243
+ lineUpLeftRightBold: "\u2536",
1244
+ lineUpLeftBoldRight: "\u2535",
1245
+ lineUpDoubleLeftDoubleRightDouble: "\u2569",
1246
+ lineUpDoubleLeftRight: "\u2568",
1247
+ lineUpLeftDoubleRightDouble: "\u2567",
1248
+ lineUpDownLeftRight: "\u253C",
1249
+ lineUpBoldDownBoldLeftBoldRightBold: "\u254B",
1250
+ lineUpDownBoldLeftBoldRightBold: "\u2548",
1251
+ lineUpBoldDownLeftBoldRightBold: "\u2547",
1252
+ lineUpBoldDownBoldLeftRightBold: "\u254A",
1253
+ lineUpBoldDownBoldLeftBoldRight: "\u2549",
1254
+ lineUpBoldDownLeftRight: "\u2540",
1255
+ lineUpDownBoldLeftRight: "\u2541",
1256
+ lineUpDownLeftBoldRight: "\u253D",
1257
+ lineUpDownLeftRightBold: "\u253E",
1258
+ lineUpBoldDownBoldLeftRight: "\u2542",
1259
+ lineUpDownLeftBoldRightBold: "\u253F",
1260
+ lineUpBoldDownLeftBoldRight: "\u2543",
1261
+ lineUpBoldDownLeftRightBold: "\u2544",
1262
+ lineUpDownBoldLeftBoldRight: "\u2545",
1263
+ lineUpDownBoldLeftRightBold: "\u2546",
1264
+ lineUpDoubleDownDoubleLeftDoubleRightDouble: "\u256C",
1265
+ lineUpDoubleDownDoubleLeftRight: "\u256B",
1266
+ lineUpDownLeftDoubleRightDouble: "\u256A",
1267
+ lineCross: "\u2573",
1268
+ lineBackslash: "\u2572",
1269
+ lineSlash: "\u2571"
1270
+ };
1271
+ var specialMainSymbols = {
1272
+ tick: "\u2714",
1273
+ info: "\u2139",
1274
+ warning: "\u26A0",
1275
+ cross: "\u2718",
1276
+ squareSmall: "\u25FB",
1277
+ squareSmallFilled: "\u25FC",
1278
+ circle: "\u25EF",
1279
+ circleFilled: "\u25C9",
1280
+ circleDotted: "\u25CC",
1281
+ circleDouble: "\u25CE",
1282
+ circleCircle: "\u24DE",
1283
+ circleCross: "\u24E7",
1284
+ circlePipe: "\u24BE",
1285
+ radioOn: "\u25C9",
1286
+ radioOff: "\u25EF",
1287
+ checkboxOn: "\u2612",
1288
+ checkboxOff: "\u2610",
1289
+ checkboxCircleOn: "\u24E7",
1290
+ checkboxCircleOff: "\u24BE",
1291
+ pointer: "\u276F",
1292
+ triangleUpOutline: "\u25B3",
1293
+ triangleLeft: "\u25C0",
1294
+ triangleRight: "\u25B6",
1295
+ lozenge: "\u25C6",
1296
+ lozengeOutline: "\u25C7",
1297
+ hamburger: "\u2630",
1298
+ smiley: "\u32E1",
1299
+ mustache: "\u0DF4",
1300
+ star: "\u2605",
1301
+ play: "\u25B6",
1302
+ nodejs: "\u2B22",
1303
+ oneSeventh: "\u2150",
1304
+ oneNinth: "\u2151",
1305
+ oneTenth: "\u2152"
1306
+ };
1307
+ var specialFallbackSymbols = {
1308
+ tick: "\u221A",
1309
+ info: "i",
1310
+ warning: "\u203C",
1311
+ cross: "\xD7",
1312
+ squareSmall: "\u25A1",
1313
+ squareSmallFilled: "\u25A0",
1314
+ circle: "( )",
1315
+ circleFilled: "(*)",
1316
+ circleDotted: "( )",
1317
+ circleDouble: "( )",
1318
+ circleCircle: "(\u25CB)",
1319
+ circleCross: "(\xD7)",
1320
+ circlePipe: "(\u2502)",
1321
+ radioOn: "(*)",
1322
+ radioOff: "( )",
1323
+ checkboxOn: "[\xD7]",
1324
+ checkboxOff: "[ ]",
1325
+ checkboxCircleOn: "(\xD7)",
1326
+ checkboxCircleOff: "( )",
1327
+ pointer: ">",
1328
+ triangleUpOutline: "\u2206",
1329
+ triangleLeft: "\u25C4",
1330
+ triangleRight: "\u25BA",
1331
+ lozenge: "\u2666",
1332
+ lozengeOutline: "\u25CA",
1333
+ hamburger: "\u2261",
1334
+ smiley: "\u263A",
1335
+ mustache: "\u250C\u2500\u2510",
1336
+ star: "\u2736",
1337
+ play: "\u25BA",
1338
+ nodejs: "\u2666",
1339
+ oneSeventh: "1/7",
1340
+ oneNinth: "1/9",
1341
+ oneTenth: "1/10"
1342
+ };
1343
+ var mainSymbols = { ...common, ...specialMainSymbols };
1344
+ var fallbackSymbols = { ...common, ...specialFallbackSymbols };
1345
+ var shouldUseMain = isUnicodeSupported();
1346
+ var figures = shouldUseMain ? mainSymbols : fallbackSymbols;
1347
+ var figures_default = figures;
1348
+ var replacements = Object.entries(specialMainSymbols);
1349
+
1350
+ // src/theme.ts
1351
+ var BUILT_IN_PROMPT_TYPES = [
1352
+ "input",
1353
+ "select",
1354
+ "generator-select",
1355
+ "list",
1356
+ "checkbox",
1357
+ "confirm",
1358
+ "search",
1359
+ "password",
1360
+ "expand",
1361
+ "editor",
1362
+ "number",
1363
+ "rawlist"
1364
+ ];
1365
+ var defaultTheme = {
1366
+ icon: {
1367
+ idle: styleText("blue", "?"),
1368
+ done: styleText("green", figures_default.tick),
1369
+ cursor: figures_default.pointer,
1370
+ checked: styleText("green", figures_default.circleFilled),
1371
+ unchecked: figures_default.circle,
1372
+ disabledChecked: styleText("green", figures_default.circleDouble),
1373
+ disabledUnchecked: "-"
1374
+ },
1375
+ prefix: {
1376
+ idle: styleText("blue", "?"),
1377
+ done: styleText("green", figures_default.tick)
1378
+ },
1379
+ spinner: {
1380
+ interval: 80,
1381
+ frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"].map(
1382
+ (frame) => styleText("yellow", frame)
1383
+ )
1384
+ },
1385
+ style: {
1386
+ answer: (text) => styleText("cyan", text),
1387
+ message: (text) => styleText("bold", text),
1388
+ error: (text) => styleText("red", `> ${text}`),
1389
+ defaultAnswer: (text) => styleText("dim", `(${text})`),
1390
+ help: (text) => styleText("dim", text),
1391
+ highlight: (text) => styleText("cyan", text),
1392
+ description: (text) => styleText("cyan", text),
1393
+ disabled: (text) => styleText("dim", text),
1394
+ disabledChoice: (text) => styleText("dim", `- ${text}`),
1395
+ searchTerm: (text) => styleText("cyan", text),
1396
+ renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
1397
+ keysHelpTip: (keys) => keys.map(
1398
+ ([key, action]) => `${styleText("bold", key)} ${styleText("dim", action)}`
1399
+ ).join(styleText("dim", " \u2022 ")),
1400
+ key: (text) => styleText("cyan", styleText("bold", `<${text}>`)),
1401
+ maskedText: "[input is masked]",
1402
+ waitingMessage: (enterKey) => `Press ${enterKey} to launch your preferred editor.`
1403
+ },
1404
+ validationFailureMode: "keep",
1405
+ indexMode: "hidden",
1406
+ i18n: {
1407
+ disabledError: "This option is disabled and cannot be selected."
1408
+ },
1409
+ keybindings: [],
1410
+ plopNext: {
1411
+ menuTitle: (text) => styleText(["bold", "underline"], text),
1412
+ welcome: (text) => styleText("dim", text),
1413
+ generatorMenu: {
1414
+ title: (text) => styleText("bold", text),
1415
+ description: (text) => styleText("dim", text)
1416
+ },
1417
+ actionLog: {
1418
+ success: (text) => styleText("green", text),
1419
+ error: (text) => styleText("red", text),
1420
+ warning: (text) => styleText("yellow", text),
1421
+ skipped: (text) => styleText("yellow", text),
1422
+ info: (text) => styleText("dim", text)
1423
+ },
1424
+ errors: {
1425
+ prefix: {
1426
+ error: "\u2716",
1427
+ warning: "\u26A0"
1428
+ },
1429
+ error: (text) => styleText("red", text),
1430
+ warning: (text) => styleText("yellow", text)
1431
+ }
1432
+ }
1433
+ };
1434
+ var PROMPT_TYPE_THEMES = {
1435
+ /**
1436
+ * Checkbox-specific defaults.
1437
+ * - Different disabledError message ("toggled" instead of "selected")
1438
+ */
1439
+ checkbox: {
1440
+ i18n: {
1441
+ disabledError: "This option is disabled and cannot be toggled."
1442
+ }
1443
+ },
1444
+ /**
1445
+ * Search-specific defaults.
1446
+ * - Disabled items are prefixed with "- " for visibility
1447
+ */
1448
+ search: {
1449
+ style: {
1450
+ disabled: (text) => styleText("dim", `- ${text}`)
1451
+ }
1452
+ }
1453
+ };
1454
+
1455
+ // src/PlopNextCore.ts
1456
+ var requireModule = createRequire(import.meta.url);
1457
+ var MODULE_THEME_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".cjs", ".ts", ".cts"]);
1458
+ var PlopNextCore = class {
1459
+ constructor() {
1460
+ this.generators = /* @__PURE__ */ new Map();
1461
+ this.generatorEntries = [];
1462
+ this.actionTypes = /* @__PURE__ */ new Map();
1463
+ /** Legacy custom prompt renderers (registered via addPrompt / setPrompt). */
1464
+ this.promptTypes = /* @__PURE__ */ new Map();
1465
+ /** Typed prompt handlers registered via registerPrompt(handler). */
1466
+ this.promptHandlerRegistry = new PromptHandlerRegistry();
1467
+ this.promptThemeSelectorRegistry = new PromptThemeSelectorRegistry();
1468
+ this.translatableFieldRules = /* @__PURE__ */ new Map();
1469
+ this.showWelcomeMessageFlag = false;
1470
+ this.showTitleFlag = true;
1471
+ this.generatorPageSize = 7;
1472
+ this.destBasePath = process.cwd();
1473
+ this.defaultInclude = {};
1474
+ this.theme = {};
1475
+ this.registerBuiltInHelpers();
1476
+ registerBuiltInPromptHandlers(this.promptHandlerRegistry);
1477
+ }
1478
+ // ── Generators ────────────────────────────────────────────────────
1479
+ /**
1480
+ * Register a named generator.
1481
+ *
1482
+ * @example
1483
+ * plopNext.registerGenerator("component", { prompts: [...], actions: [...] });
1484
+ */
1485
+ registerGenerator(name, config) {
1486
+ if (!this.generators.has(name)) {
1487
+ this.generatorEntries.push({ kind: "generator", name });
1488
+ }
1489
+ this.generators.set(name, config);
1490
+ return this;
1491
+ }
1492
+ addSeparator(text) {
1493
+ this.generatorEntries.push({ kind: "separator", text });
1494
+ return this;
1495
+ }
1496
+ // Backward-compatible alias.
1497
+ setGenerator(name, config) {
1498
+ return this.registerGenerator(name, config);
1499
+ }
1500
+ registerHelper(name, helper) {
1501
+ Handlebars.registerHelper(name, helper);
1502
+ return this;
1503
+ }
1504
+ // Backward-compatible alias.
1505
+ setHelper(name, helper) {
1506
+ return this.registerHelper(name, helper);
1507
+ }
1508
+ registerPartial(name, partial) {
1509
+ Handlebars.registerPartial(name, partial);
1510
+ return this;
1511
+ }
1512
+ // Backward-compatible alias.
1513
+ setPartial(name, partial) {
1514
+ return this.registerPartial(name, partial);
1515
+ }
1516
+ registerActionType(name, actionType) {
1517
+ this.actionTypes.set(name, actionType);
1518
+ return this;
1519
+ }
1520
+ // Backward-compatible alias.
1521
+ setActionType(name, actionType) {
1522
+ return this.registerActionType(name, actionType);
1523
+ }
1524
+ registerPrompt(nameOrHandler, prompt, options) {
1525
+ if (typeof nameOrHandler === "string") {
1526
+ registerCustomPrompt(this.promptTypes, nameOrHandler, prompt);
1527
+ const themeOptions = options?.theme;
1528
+ if (themeOptions && Object.keys(themeOptions).length > 0) {
1529
+ this.promptThemeSelectorRegistry.registerWithDefault(
1530
+ nameOrHandler,
1531
+ createPromptThemeSelector(themeOptions)
1532
+ );
1533
+ } else {
1534
+ this.promptThemeSelectorRegistry.registerWithDefault(nameOrHandler);
1535
+ }
1536
+ if (options?.translatableFields && options.translatableFields.length > 0) {
1537
+ const existing = this.translatableFieldRules.get(nameOrHandler) ?? [];
1538
+ const merged = [...existing, ...options.translatableFields];
1539
+ this.translatableFieldRules.set(nameOrHandler, merged);
1540
+ this.i18nAdapter?.registerTranslatableFields?.(
1541
+ nameOrHandler,
1542
+ options.translatableFields
1543
+ );
1544
+ }
1545
+ return this;
1546
+ }
1547
+ this.promptHandlerRegistry.register(nameOrHandler);
1548
+ return this;
1549
+ }
1550
+ // Backward-compatible alias.
1551
+ setPrompt(name, prompt) {
1552
+ return this.registerPrompt(name, prompt);
1553
+ }
1554
+ getPrompt(name) {
1555
+ return getCustomPrompt(this.promptTypes, name);
1556
+ }
1557
+ getPromptList() {
1558
+ return listCustomPromptTypes(this.promptTypes);
1559
+ }
1560
+ /** List the type strings that have a registered PromptHandler. */
1561
+ getPromptHandlerTypes() {
1562
+ return this.promptHandlerRegistry.getRegisteredTypes();
1563
+ }
1564
+ /**
1565
+ * Dispatch a prompt to the appropriate handler.
1566
+ *
1567
+ * Resolution order:
1568
+ * 1. Legacy PromptRenderer registered via `addPrompt()` / `setPrompt()` — highest priority.
1569
+ * 2. PromptHandler registered via `registerPrompt(handler)` for the exact type.
1570
+ * 3. PromptHandler registered for "input" as fallback.
1571
+ *
1572
+ * @param type Prompt type string (e.g. "input", "list", "datepicker").
1573
+ * @param config All prompt fields minus plop-next-only keys (type/name/filter/when/askAnswered).
1574
+ */
1575
+ async askPrompt(type, config) {
1576
+ if (Object.prototype.hasOwnProperty.call(config, "theme")) {
1577
+ throw new Error(
1578
+ `The "theme" prompt field is not supported in plop-next. Use core.setTheme({ ... }) or core.setTheme("./path/to/theme-file") instead.`
1579
+ );
1580
+ }
1581
+ const promptTheme = this.resolvePromptTheme(type);
1582
+ const configWithTheme = promptTheme === void 0 ? config : {
1583
+ ...config,
1584
+ theme: promptTheme
1585
+ };
1586
+ const customFn = this.getPrompt(type);
1587
+ if (customFn) {
1588
+ return askCustomPrompt(customFn, configWithTheme);
1589
+ }
1590
+ return this.promptHandlerRegistry.ask(type, configWithTheme, {
1591
+ allowTheme: true
1592
+ });
1593
+ }
1594
+ setTheme(theme) {
1595
+ const resolvedTheme = this.resolveThemeInput(theme);
1596
+ this.theme = this.cloneTheme(resolvedTheme);
1597
+ return this;
1598
+ }
1599
+ getTheme() {
1600
+ return this.cloneTheme(this.resolveTheme());
1601
+ }
1602
+ resolveThemeInput(theme) {
1603
+ if (typeof theme !== "string") {
1604
+ return theme;
1605
+ }
1606
+ const absolutePath = resolve2(this.destBasePath, theme);
1607
+ if (!existsSync2(absolutePath)) {
1608
+ throw new Error(`Theme file not found: ${absolutePath}`);
1609
+ }
1610
+ const extension = extname(absolutePath).toLowerCase();
1611
+ const parsed = extension === ".json" ? this.parseThemeJsonFile(absolutePath) : this.parseThemeModuleFile(absolutePath, extension);
1612
+ if (!this.isRecord(parsed)) {
1613
+ throw new Error(
1614
+ `Theme file must contain an object at root: ${absolutePath}`
1615
+ );
1616
+ }
1617
+ if (this.looksLikeI18nSource(parsed) && !this.looksLikeThemeSource(parsed)) {
1618
+ throw new Error(
1619
+ `Invalid theme file at ${absolutePath}: looks like locales/texts content. Use registerLocale(s) or registerTexts for translation files.`
1620
+ );
1621
+ }
1622
+ if (!this.looksLikeThemeSource(parsed)) {
1623
+ throw new Error(
1624
+ `Invalid theme file at ${absolutePath}: no theme fields were detected.`
1625
+ );
1626
+ }
1627
+ return parsed;
1628
+ }
1629
+ parseThemeJsonFile(filePath) {
1630
+ try {
1631
+ return JSON.parse(readFileSync(filePath, "utf8"));
1632
+ } catch (error) {
1633
+ throw new Error(
1634
+ `Invalid theme JSON file at ${filePath}: ${error instanceof Error ? error.message : String(error)}`
1635
+ );
1636
+ }
1637
+ }
1638
+ parseThemeModuleFile(filePath, extension) {
1639
+ if (!MODULE_THEME_FILE_EXTENSIONS.has(extension)) {
1640
+ throw new Error(
1641
+ `Unsupported theme file extension "${extension || "<none>"}" at ${filePath}. Supported extensions: .json, .js, .cjs, .ts, .cts.`
1642
+ );
1643
+ }
1644
+ let loaded;
1645
+ try {
1646
+ loaded = requireModule(filePath);
1647
+ } catch (error) {
1648
+ const message = error instanceof Error ? error.message : String(error);
1649
+ throw new Error(
1650
+ `Unable to load theme module at ${filePath}: ${message}. If this file is ESM-only, import it in your plopfile and pass the object directly to setTheme(...).`
1651
+ );
1652
+ }
1653
+ const unwrapped = this.unwrapModuleDefault(loaded);
1654
+ if (typeof unwrapped === "function") {
1655
+ const resolved = unwrapped();
1656
+ return this.unwrapModuleDefault(resolved);
1657
+ }
1658
+ return unwrapped;
1659
+ }
1660
+ getActionType(name) {
1661
+ return this.actionTypes.get(name);
1662
+ }
1663
+ getActionTypeList() {
1664
+ return Array.from(this.actionTypes.keys());
1665
+ }
1666
+ getHelper(name) {
1667
+ const helpers = Handlebars.helpers;
1668
+ const helper = helpers[name];
1669
+ return typeof helper === "function" ? helper : void 0;
1670
+ }
1671
+ getHelperList() {
1672
+ const helpers = Handlebars.helpers;
1673
+ return Object.keys(helpers).filter(
1674
+ (name) => typeof helpers[name] === "function"
1675
+ );
1676
+ }
1677
+ getPartial(name) {
1678
+ const partials = Handlebars.partials;
1679
+ const partial = partials[name];
1680
+ if (typeof partial === "string") {
1681
+ return partial;
1682
+ }
1683
+ return void 0;
1684
+ }
1685
+ getPartialList() {
1686
+ const partials = Handlebars.partials;
1687
+ return Object.keys(partials);
1688
+ }
1689
+ showWelcomeMessage(show = true) {
1690
+ this.showWelcomeMessageFlag = show;
1691
+ return this;
1692
+ }
1693
+ getWelcomeMessage() {
1694
+ return this.i18nAdapter?.getWelcomeMessage?.() ?? null;
1695
+ }
1696
+ isWelcomeMessageShown() {
1697
+ return this.showWelcomeMessageFlag;
1698
+ }
1699
+ showTitle(show = true) {
1700
+ this.showTitleFlag = show;
1701
+ return this;
1702
+ }
1703
+ isTitleShown() {
1704
+ return this.showTitleFlag;
1705
+ }
1706
+ /**
1707
+ * Set page size used by the generator selection prompt.
1708
+ * Defaults to 7.
1709
+ */
1710
+ setGeneratorPageSize(pageSize) {
1711
+ if (!Number.isInteger(pageSize) || pageSize < 1) {
1712
+ throw new Error(
1713
+ "Generator page size must be an integer greater than or equal to 1."
1714
+ );
1715
+ }
1716
+ this.generatorPageSize = pageSize;
1717
+ return this;
1718
+ }
1719
+ /**
1720
+ * Get page size used by the generator selection prompt.
1721
+ */
1722
+ getGeneratorPageSize() {
1723
+ return this.generatorPageSize;
1724
+ }
1725
+ setI18nAdapter(adapter) {
1726
+ this.i18nAdapter = adapter;
1727
+ return this;
1728
+ }
1729
+ setWarningReporter(reporter) {
1730
+ this.warningReporter = reporter;
1731
+ return this;
1732
+ }
1733
+ reportWarning(warning) {
1734
+ this.warningReporter?.(warning);
1735
+ return this;
1736
+ }
1737
+ clearI18nAdapter() {
1738
+ this.i18nAdapter = void 0;
1739
+ return this;
1740
+ }
1741
+ setPlopfilePath(path) {
1742
+ this.plopfilePath = resolve2(path);
1743
+ this.destBasePath = dirname2(this.plopfilePath);
1744
+ this.pkgCache = void 0;
1745
+ this.pkgCachePath = void 0;
1746
+ return this;
1747
+ }
1748
+ getPlopfilePath() {
1749
+ return this.plopfilePath;
1750
+ }
1751
+ setDestBasePath(path) {
1752
+ this.destBasePath = resolve2(path);
1753
+ return this;
1754
+ }
1755
+ getDestBasePath() {
1756
+ return this.destBasePath;
1757
+ }
1758
+ setDefaultInclude(include) {
1759
+ this.defaultInclude = { ...include };
1760
+ return this;
1761
+ }
1762
+ getDefaultInclude() {
1763
+ return { ...this.defaultInclude };
1764
+ }
1765
+ renderString(template, data) {
1766
+ const compiled = Handlebars.compile(template, { noEscape: true });
1767
+ return compiled(data);
1768
+ }
1769
+ getGenerator(name) {
1770
+ return this.generators.get(name);
1771
+ }
1772
+ getGeneratorList() {
1773
+ const result = [];
1774
+ for (const entry of this.generatorEntries) {
1775
+ if (entry.kind === "separator") {
1776
+ result.push({
1777
+ type: "separator",
1778
+ separator: entry.text ?? ""
1779
+ });
1780
+ } else {
1781
+ const cfg = this.generators.get(entry.name);
1782
+ if (cfg)
1783
+ result.push({ name: entry.name, description: cfg.description });
1784
+ }
1785
+ }
1786
+ return result;
1787
+ }
1788
+ // ── i18n integration ─────────────────────────────────────────────
1789
+ /**
1790
+ * Enable i18n support.
1791
+ *
1792
+ * - `force`: always use this locale.
1793
+ * - `auto` (default): detect locale from process environment,
1794
+ * then fall back to English.
1795
+ */
1796
+ useI18n(options = { auto: true }) {
1797
+ this.i18nAdapter?.use?.(options);
1798
+ return this;
1799
+ }
1800
+ isI18nEnabled() {
1801
+ return this.i18nAdapter?.isEnabled?.() ?? false;
1802
+ }
1803
+ // ── Text resolution ───────────────────────────────────────────────
1804
+ /**
1805
+ * Resolve the display text for a prompt field.
1806
+ *
1807
+ * When i18n is **disabled**: returns `defaultMessage` as-is.
1808
+ * When i18n is **enabled**: looks up `<generatorName>.<promptName>.<field>`
1809
+ * in the active locale, falls back to English, then to `defaultMessage`.
1810
+ *
1811
+ * @param generatorName Generator key, e.g. "component"
1812
+ * @param promptName Prompt name, e.g. "name"
1813
+ * @param field Field name, e.g. "message"
1814
+ * @param defaultMessage Raw string or function defined inside setGenerator
1815
+ */
1816
+ resolveText(generatorName, promptName, field, defaultMessage, args = []) {
1817
+ if (!this.i18nAdapter) {
1818
+ return defaultMessage;
1819
+ }
1820
+ const key = `${generatorName}.${promptName}.${field}`;
1821
+ const fallback = typeof defaultMessage === "function" ? defaultMessage(...args) : defaultMessage;
1822
+ const resolved = this.i18nAdapter.t(key, args, fallback);
1823
+ return resolved !== key ? resolved : defaultMessage ?? key;
1824
+ }
1825
+ /**
1826
+ * Resolve a built-in core UI text (e.g. "cli.welcome").
1827
+ */
1828
+ t(key, args = [], fallback) {
1829
+ if (!this.i18nAdapter) {
1830
+ return fallback ?? key;
1831
+ }
1832
+ return this.i18nAdapter.t(key, args, fallback);
1833
+ }
1834
+ // ── Locale helpers ────────────────────────────────────────────────
1835
+ /**
1836
+ * Add or override messages for an existing locale.
1837
+ * Available whether or not i18n plugin is loaded.
1838
+ */
1839
+ addTexts(locale, texts) {
1840
+ this.i18nAdapter?.registerTexts?.(locale, texts);
1841
+ return this;
1842
+ }
1843
+ registerText(locale, path, text) {
1844
+ this.i18nAdapter?.registerText?.(locale, path, text);
1845
+ return this;
1846
+ }
1847
+ registerLocale(locale, messages, options = {}) {
1848
+ this.i18nAdapter?.registerLocale?.(locale, messages, options);
1849
+ return this;
1850
+ }
1851
+ registerLocales(locales, options = {}) {
1852
+ this.i18nAdapter?.registerLocales?.(locales, options);
1853
+ return this;
1854
+ }
1855
+ registerTexts(localeOrTexts, maybeTexts) {
1856
+ if (typeof localeOrTexts === "string" && typeof maybeTexts !== "undefined") {
1857
+ this.i18nAdapter?.registerTexts?.(localeOrTexts, maybeTexts);
1858
+ return this;
1859
+ }
1860
+ this.i18nAdapter?.registerTexts?.(
1861
+ localeOrTexts
1862
+ );
1863
+ return this;
1864
+ }
1865
+ setLocale(locale) {
1866
+ this.i18nAdapter?.setLocale?.(locale);
1867
+ return this;
1868
+ }
1869
+ getLocale() {
1870
+ return this.i18nAdapter?.getLocale?.() ?? "en";
1871
+ }
1872
+ hasLocale(locale) {
1873
+ if (locale === "en") {
1874
+ return true;
1875
+ }
1876
+ return this.i18nAdapter?.hasLocale?.(locale) ?? false;
1877
+ }
1878
+ async executeActions(actions, answers, options = {}) {
1879
+ const runner = new ActionRunner(this, {
1880
+ dest: options.dest,
1881
+ force: options.force
1882
+ });
1883
+ const steps = [];
1884
+ let failed = false;
1885
+ for (const action of actions) {
1886
+ try {
1887
+ if (typeof action === "string") {
1888
+ steps.push({
1889
+ type: "comment",
1890
+ status: "success",
1891
+ message: action
1892
+ });
1893
+ continue;
1894
+ }
1895
+ if (typeof action === "function") {
1896
+ const message = await action(answers, { type: "custom" }, this);
1897
+ steps.push({
1898
+ type: "function",
1899
+ status: "success",
1900
+ message: String(message)
1901
+ });
1902
+ continue;
1903
+ }
1904
+ if (action.skip) {
1905
+ const skipResult = await action.skip(answers, action);
1906
+ if (skipResult) {
1907
+ steps.push({
1908
+ type: "skip",
1909
+ status: "success",
1910
+ message: typeof skipResult === "string" ? skipResult : "Skipped",
1911
+ path: typeof action.path === "string" ? action.path : void 0
1912
+ });
1913
+ continue;
1914
+ }
1915
+ }
1916
+ if (this.actionTypes.has(action.type)) {
1917
+ const customAction = this.actionTypes.get(
1918
+ action.type
1919
+ );
1920
+ const message = await customAction(
1921
+ answers,
1922
+ action,
1923
+ this
1924
+ );
1925
+ steps.push({
1926
+ type: action.type,
1927
+ status: "success",
1928
+ message: String(message),
1929
+ path: typeof action.path === "string" ? action.path : void 0
1930
+ });
1931
+ continue;
1932
+ }
1933
+ const result = await runner.run(action.type, action, answers);
1934
+ steps.push({
1935
+ type: result.type,
1936
+ status: "success",
1937
+ message: result.message,
1938
+ path: result.path
1939
+ });
1940
+ } catch (error) {
1941
+ const message = error instanceof Error ? error.message : String(error);
1942
+ const type = typeof action === "function" ? "function" : typeof action === "string" ? "comment" : action.type;
1943
+ steps.push({ type, status: "error", message });
1944
+ if (typeof action === "object" && action.abortOnFail !== false) {
1945
+ failed = true;
1946
+ break;
1947
+ }
1948
+ }
1949
+ }
1950
+ return { steps, failed };
1951
+ }
1952
+ async resolveActions(actions, answers) {
1953
+ const resolved = typeof actions === "function" ? await actions(answers) : actions;
1954
+ if (!Array.isArray(resolved)) {
1955
+ throw new Error("Generator actions must resolve to an array.");
1956
+ }
1957
+ return resolved;
1958
+ }
1959
+ getActionTypeDisplay(type, showTypeNames = false) {
1960
+ if (showTypeNames) {
1961
+ return type;
1962
+ }
1963
+ const typeDisplay = {
1964
+ function: "->",
1965
+ add: "++",
1966
+ addMany: "+!",
1967
+ modify: "+-",
1968
+ append: "_+",
1969
+ skip: "--"
1970
+ };
1971
+ return typeDisplay[type] ?? type;
1972
+ }
1973
+ formatActionTargetForDisplay(value) {
1974
+ if (process.platform !== "win32") {
1975
+ return value;
1976
+ }
1977
+ const normalized = value.replaceAll("/", "\\");
1978
+ if (!normalized.startsWith("\\") && !/^[A-Za-z]:\\/.test(normalized)) {
1979
+ return `\\${normalized}`;
1980
+ }
1981
+ return normalized;
1982
+ }
1983
+ // ── Private ───────────────────────────────────────────────────────
1984
+ registerBuiltInHelpers() {
1985
+ const applyCase = (formatter) => {
1986
+ return (value) => formatter(String(value ?? ""));
1987
+ };
1988
+ const helperMap = {
1989
+ camelCase: applyCase(camelCase),
1990
+ snakeCase: applyCase(snakeCase),
1991
+ dashCase: applyCase(kebabCase),
1992
+ kabobCase: applyCase(kebabCase),
1993
+ kebabCase: applyCase(kebabCase),
1994
+ dotCase: applyCase(dotCase),
1995
+ pathCase: applyCase(pathCase),
1996
+ properCase: applyCase(pascalCase),
1997
+ pascalCase: applyCase(pascalCase),
1998
+ lowerCase: (value) => String(value ?? "").toLowerCase(),
1999
+ upperCase: (value) => String(value ?? "").toUpperCase(),
2000
+ sentenceCase: applyCase(sentenceCase),
2001
+ constantCase: applyCase(constantCase),
2002
+ titleCase: applyCase(titleCase),
2003
+ pkg: (propertyPath) => this.readPackageProperty(propertyPath)
2004
+ };
2005
+ for (const [name, helper] of Object.entries(helperMap)) {
2006
+ this.registerHelper(name, helper);
2007
+ }
2008
+ }
2009
+ readPackageProperty(propertyPath) {
2010
+ if (typeof propertyPath !== "string" || propertyPath.trim().length === 0) {
2011
+ return "";
2012
+ }
2013
+ const pkg = this.loadPackageJsonNearPlopfile();
2014
+ if (!pkg) {
2015
+ return "";
2016
+ }
2017
+ const value = this.getPathValue(pkg, propertyPath);
2018
+ if (value === void 0 || value === null) {
2019
+ return "";
2020
+ }
2021
+ return String(value);
2022
+ }
2023
+ loadPackageJsonNearPlopfile() {
2024
+ const baseDirectory = this.plopfilePath ? dirname2(this.plopfilePath) : this.destBasePath;
2025
+ const packagePath = resolve2(baseDirectory, "package.json");
2026
+ if (this.pkgCachePath === packagePath) {
2027
+ return this.pkgCache;
2028
+ }
2029
+ this.pkgCachePath = packagePath;
2030
+ if (!existsSync2(packagePath)) {
2031
+ this.pkgCache = void 0;
2032
+ return void 0;
2033
+ }
2034
+ try {
2035
+ const raw = readFileSync(packagePath, "utf8");
2036
+ const parsed = JSON.parse(raw);
2037
+ this.pkgCache = parsed;
2038
+ return parsed;
2039
+ } catch {
2040
+ this.pkgCache = void 0;
2041
+ return void 0;
2042
+ }
2043
+ }
2044
+ getPathValue(source, propertyPath) {
2045
+ const segments = propertyPath.split(".").filter((segment) => segment.length > 0);
2046
+ let current = source;
2047
+ for (const segment of segments) {
2048
+ if (current === null || typeof current !== "object") {
2049
+ return void 0;
2050
+ }
2051
+ const record = current;
2052
+ if (!(segment in record)) {
2053
+ return void 0;
2054
+ }
2055
+ current = record[segment];
2056
+ }
2057
+ return current;
2058
+ }
2059
+ /**
2060
+ * Returns prepared prompts for a generator: prompt `message` fields are
2061
+ * transformed by plugins (like i18n) before they are rendered by Inquirer.
2062
+ */
2063
+ preparePrompts(generatorName, prompts) {
2064
+ return this.i18nAdapter?.preparePrompts(generatorName, prompts) ?? prompts;
2065
+ }
2066
+ resolvePromptTheme(type) {
2067
+ const theme = this.resolvePromptTypeTheme(type);
2068
+ return this.promptThemeSelectorRegistry.resolveTheme(type, theme);
2069
+ }
2070
+ /**
2071
+ * Resolve the full theme for a specific prompt type.
2072
+ * Merges in order: defaultTheme → type defaults → user global theme → user type theme.
2073
+ */
2074
+ resolvePromptTypeTheme(type) {
2075
+ if (type === "generator-select") {
2076
+ const selectDefaults = PROMPT_TYPE_THEMES["select"];
2077
+ const userSelectTheme = this.resolveUserPromptTypeTheme("select");
2078
+ const userGeneratorSelectTheme = this.resolveUserPromptTypeTheme(type);
2079
+ return this.mergeThemeLayers(
2080
+ selectDefaults,
2081
+ this.resolveNormalizedGlobalTheme(),
2082
+ userSelectTheme,
2083
+ userGeneratorSelectTheme
2084
+ );
2085
+ }
2086
+ const typeDefaults = PROMPT_TYPE_THEMES[type];
2087
+ const userTypeTheme = this.resolveUserPromptTypeTheme(type);
2088
+ return this.mergeThemeLayers(
2089
+ typeDefaults,
2090
+ this.resolveNormalizedGlobalTheme(),
2091
+ userTypeTheme
2092
+ );
2093
+ }
2094
+ resolveTheme() {
2095
+ return this.mergeThemeLayers(this.resolveNormalizedGlobalTheme());
2096
+ }
2097
+ cloneTheme(theme) {
2098
+ return this.cloneUnknown(theme);
2099
+ }
2100
+ resolveNormalizedGlobalTheme() {
2101
+ return this.normalizeThemeScope(this.theme);
2102
+ }
2103
+ resolveUserPromptTypeTheme(type) {
2104
+ const source = this.theme;
2105
+ const rawTypeTheme = source[type];
2106
+ if (!this.isRecord(rawTypeTheme)) {
2107
+ return void 0;
2108
+ }
2109
+ return this.normalizeThemeScope(rawTypeTheme);
2110
+ }
2111
+ normalizeThemeScope(source) {
2112
+ const normalized = {};
2113
+ for (const [key, value] of Object.entries(source)) {
2114
+ if (this.isPromptThemeTypeKey(key)) continue;
2115
+ if (key === "waitingMessage" || key === "maskedText" || key === "disabledError") {
2116
+ continue;
2117
+ }
2118
+ normalized[key] = value;
2119
+ }
2120
+ const style = this.isRecord(normalized.style) ? { ...normalized.style } : {};
2121
+ if (source.waitingMessage !== void 0) {
2122
+ if (typeof source.waitingMessage === "function") {
2123
+ style.waitingMessage = source.waitingMessage;
2124
+ } else if (typeof source.waitingMessage === "string") {
2125
+ const template = source.waitingMessage;
2126
+ style.waitingMessage = (enterKey) => template.replaceAll("{{enterKey}}", enterKey).replaceAll("{enterKey}", enterKey);
2127
+ }
2128
+ }
2129
+ if (typeof source.maskedText === "string") {
2130
+ style.maskedText = source.maskedText;
2131
+ }
2132
+ if (Object.keys(style).length > 0) {
2133
+ normalized.style = style;
2134
+ }
2135
+ const i18n = this.isRecord(normalized.i18n) ? { ...normalized.i18n } : {};
2136
+ if (typeof source.disabledError === "string") {
2137
+ i18n.disabledError = source.disabledError;
2138
+ }
2139
+ if (Object.keys(i18n).length > 0) {
2140
+ normalized.i18n = i18n;
2141
+ }
2142
+ return normalized;
2143
+ }
2144
+ mergeThemeLayers(...layers) {
2145
+ const mergedTheme = {
2146
+ icon: defaultTheme.icon,
2147
+ prefix: defaultTheme.prefix,
2148
+ spinner: {
2149
+ ...defaultTheme.spinner,
2150
+ frames: [...defaultTheme.spinner?.frames ?? []]
2151
+ },
2152
+ style: {
2153
+ ...defaultTheme.style
2154
+ },
2155
+ validationFailureMode: defaultTheme.validationFailureMode,
2156
+ indexMode: defaultTheme.indexMode,
2157
+ i18n: {
2158
+ ...defaultTheme.i18n
2159
+ },
2160
+ keybindings: defaultTheme.keybindings,
2161
+ plopNext: {
2162
+ ...defaultTheme.plopNext,
2163
+ generatorMenu: {
2164
+ ...defaultTheme.plopNext?.generatorMenu
2165
+ },
2166
+ actionLog: {
2167
+ ...defaultTheme.plopNext?.actionLog
2168
+ },
2169
+ errors: {
2170
+ ...defaultTheme.plopNext?.errors,
2171
+ prefix: {
2172
+ ...defaultTheme.plopNext?.errors?.prefix
2173
+ }
2174
+ }
2175
+ }
2176
+ };
2177
+ for (const layer of layers) {
2178
+ if (!layer) continue;
2179
+ mergedTheme.icon = this.mergeIconLayers(mergedTheme.icon, layer.icon);
2180
+ if (layer.prefix !== void 0) {
2181
+ mergedTheme.prefix = layer.prefix;
2182
+ }
2183
+ if (layer.spinner) {
2184
+ mergedTheme.spinner = {
2185
+ ...mergedTheme.spinner,
2186
+ ...layer.spinner,
2187
+ ...Array.isArray(layer.spinner.frames) ? { frames: [...layer.spinner.frames] } : {}
2188
+ };
2189
+ }
2190
+ if (layer.style) {
2191
+ mergedTheme.style = {
2192
+ ...mergedTheme.style,
2193
+ ...layer.style
2194
+ };
2195
+ }
2196
+ if (layer.validationFailureMode !== void 0) {
2197
+ mergedTheme.validationFailureMode = layer.validationFailureMode;
2198
+ }
2199
+ if (layer.indexMode !== void 0) {
2200
+ mergedTheme.indexMode = layer.indexMode;
2201
+ }
2202
+ if (layer.i18n) {
2203
+ mergedTheme.i18n = {
2204
+ ...mergedTheme.i18n,
2205
+ ...layer.i18n
2206
+ };
2207
+ }
2208
+ if (layer.keybindings !== void 0) {
2209
+ mergedTheme.keybindings = [...layer.keybindings];
2210
+ }
2211
+ if (layer.plopNext) {
2212
+ mergedTheme.plopNext = {
2213
+ ...mergedTheme.plopNext,
2214
+ ...layer.plopNext,
2215
+ generatorMenu: {
2216
+ ...mergedTheme.plopNext?.generatorMenu,
2217
+ ...layer.plopNext.generatorMenu
2218
+ },
2219
+ actionLog: {
2220
+ ...mergedTheme.plopNext?.actionLog,
2221
+ ...layer.plopNext.actionLog
2222
+ },
2223
+ errors: {
2224
+ ...mergedTheme.plopNext?.errors,
2225
+ ...layer.plopNext.errors,
2226
+ prefix: {
2227
+ ...mergedTheme.plopNext?.errors?.prefix,
2228
+ ...layer.plopNext.errors?.prefix
2229
+ }
2230
+ }
2231
+ };
2232
+ }
2233
+ }
2234
+ return mergedTheme;
2235
+ }
2236
+ looksLikeThemeSource(value) {
2237
+ const keys = Object.keys(value);
2238
+ if (keys.length === 0) {
2239
+ return false;
2240
+ }
2241
+ const rootThemeKeys = /* @__PURE__ */ new Set([
2242
+ "icon",
2243
+ "prefix",
2244
+ "spinner",
2245
+ "style",
2246
+ "validationFailureMode",
2247
+ "indexMode",
2248
+ "i18n",
2249
+ "keybindings",
2250
+ "plopNext",
2251
+ "waitingMessage",
2252
+ "maskedText",
2253
+ "disabledError"
2254
+ ]);
2255
+ return keys.some(
2256
+ (key) => rootThemeKeys.has(key) || this.isPromptThemeTypeKey(key)
2257
+ );
2258
+ }
2259
+ looksLikeI18nSource(value) {
2260
+ const entries = Object.entries(value);
2261
+ if (entries.length === 0) {
2262
+ return false;
2263
+ }
2264
+ const localeBundle = entries.every(
2265
+ ([key, item]) => this.isLocaleLikeKey(key) && this.isRecord(item)
2266
+ );
2267
+ if (localeBundle) {
2268
+ return true;
2269
+ }
2270
+ const knownI18nRoots = /* @__PURE__ */ new Set([
2271
+ "cli",
2272
+ "inquirer",
2273
+ "actions",
2274
+ "errors",
2275
+ "help"
2276
+ ]);
2277
+ return entries.some(([key]) => knownI18nRoots.has(key));
2278
+ }
2279
+ isLocaleLikeKey(key) {
2280
+ return /^[a-z]{2}(?:[-_][A-Za-z0-9]{2,8})*$/i.test(key);
2281
+ }
2282
+ isRecord(value) {
2283
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2284
+ }
2285
+ isPromptThemeTypeKey(key) {
2286
+ return BUILT_IN_PROMPT_TYPES.includes(key);
2287
+ }
2288
+ unwrapModuleDefault(value) {
2289
+ if (!this.isRecord(value)) {
2290
+ return value;
2291
+ }
2292
+ const defaultValue = value["default"];
2293
+ if (typeof defaultValue !== "undefined") {
2294
+ return defaultValue;
2295
+ }
2296
+ return value;
2297
+ }
2298
+ cloneUnknown(value) {
2299
+ if (Array.isArray(value)) {
2300
+ return value.map((item) => this.cloneUnknown(item));
2301
+ }
2302
+ if (this.isRecord(value)) {
2303
+ const cloned = {};
2304
+ for (const [key, item] of Object.entries(value)) {
2305
+ cloned[key] = this.cloneUnknown(item);
2306
+ }
2307
+ return cloned;
2308
+ }
2309
+ return value;
2310
+ }
2311
+ /**
2312
+ * Merge icon values when applying one layer over another.
2313
+ */
2314
+ mergeIconLayers(base, override) {
2315
+ if (override === void 0) return base;
2316
+ if (typeof override === "string") return override;
2317
+ if (typeof base === "string" || base === void 0) {
2318
+ return { ...override };
2319
+ }
2320
+ return { ...base, ...override };
2321
+ }
2322
+ };
2323
+
2324
+ // src/defaultTexts.ts
2325
+ var CORE_DEFAULT_TEXTS = {
2326
+ cli: {
2327
+ title: "Welcome to plop-next! \u{1F680}",
2328
+ welcomeMessage: null,
2329
+ selectGenerator: "Please choose a generator",
2330
+ noGenerators: "No generators registered. Add some to your plopfile.",
2331
+ generatorNotFound: (name) => `Generator "${name}" not found.`,
2332
+ aborted: "Aborted.",
2333
+ done: "Done!",
2334
+ promptCancelled: "Console exited by user."
2335
+ },
2336
+ inquirer: {
2337
+ confirm: {
2338
+ yesLabel: "Yes",
2339
+ noLabel: "No",
2340
+ hintYes: "Y/n",
2341
+ hintNo: "y/N"
2342
+ },
2343
+ select: {
2344
+ helpNavigate: "navigate",
2345
+ helpSelect: "select"
2346
+ },
2347
+ checkbox: {
2348
+ helpNavigate: "navigate",
2349
+ helpSelect: "select",
2350
+ helpSubmit: "submit",
2351
+ helpAll: "toggle all",
2352
+ helpInvert: "invert selection"
2353
+ },
2354
+ search: {
2355
+ helpNavigate: "navigate",
2356
+ helpSelect: "select"
2357
+ },
2358
+ editor: {
2359
+ waitingMessage: (enterKey) => `Press ${enterKey} to launch your preferred editor.`
2360
+ },
2361
+ password: {
2362
+ maskedText: "[input is hidden]"
2363
+ }
2364
+ },
2365
+ actions: {
2366
+ add: {
2367
+ creating: (path) => `Creating ${path}`,
2368
+ created: (path) => `\u2714 Created ${path}`,
2369
+ alreadyExists: (path) => `File already exists: ${path}`
2370
+ },
2371
+ modify: {
2372
+ modifying: (path) => `Modifying ${path}`,
2373
+ modified: (path) => `\u2714 Modified ${path}`,
2374
+ notFound: (path) => `File not found: ${path}`,
2375
+ patternNotFound: (path) => `Pattern not found in: ${path}`
2376
+ },
2377
+ append: {
2378
+ appending: (path) => `Appending to ${path}`,
2379
+ appended: (path) => `\u2714 Appended to ${path}`
2380
+ }
2381
+ },
2382
+ errors: {
2383
+ unknownAction: (type) => `Unknown action type: "${type}"`,
2384
+ plopfileNotFound: "Could not find a plopfile (plopfile.js or plopfile.ts).",
2385
+ plopfileLoadFailed: (err) => `Failed to load plopfile: ${err}`,
2386
+ generatorNotFound: (name) => `Generator "${name}" not found.`,
2387
+ noGenerators: "No generators registered. Add some to your plopfile.",
2388
+ invalidPrompt: (name, reason) => `Invalid prompt "${name}": ${reason}`,
2389
+ bypassParse: (promptName, promptType, value, detail) => `Cannot assign bypass value "${value}" to ${promptType} prompt "${promptName}"${detail ? `: ${detail}` : ""}`,
2390
+ plopfileLoad: (path) => `Failed to load plopfile: ${path}`,
2391
+ plopfileExport: "Plopfile must export a default function.",
2392
+ userCancelled: "Prompt cancelled by user.",
2393
+ plopfileNotFoundWarning: "No plopfile found. Create a plopfile.js in your project.",
2394
+ forcedLangI18nMissing: (locale) => `Forced locale "${locale}" ignored because @plop-next/i18n is not installed. Falling back to English.`,
2395
+ forcedLangUnavailable: (locale) => `Forced locale "${locale}" is not available. Falling back to English.`
2396
+ },
2397
+ /**
2398
+ * CLI `--help` display texts.
2399
+ * These are read-only and cannot be overridden via `registerLocale` / `registerTexts`.
2400
+ */
2401
+ help: {
2402
+ usage: "Usage:",
2403
+ usage1: "Select from a list of available generators",
2404
+ usage2: "Run a generator registered under that name",
2405
+ usage3: "Run the generator with input data to bypass prompts",
2406
+ options: "Options:",
2407
+ optHelp: "Show this help display",
2408
+ optShowTypeNames: "Show type names instead of abbreviations",
2409
+ optInitTitle: "Plopfile initialization",
2410
+ optGenerateTitle: "Generate Locale, Texts, Theme files",
2411
+ optOthersTitle: "Other options",
2412
+ optInit: "Generate a basic plopfile.ts",
2413
+ optInitJs: "Generate a basic plopfile.js",
2414
+ optInitTs: "Generate a basic plopfile.ts",
2415
+ optDemo: "Generate a demo generator in the plopfile",
2416
+ optI18n: "Initialize plopfile with i18n support",
2417
+ optGenerate: "Generate template: locale | texts | theme",
2418
+ optPath: "Base output directory for generated template files",
2419
+ optExtension: "Generated file extension: ts | js | json",
2420
+ optIncludeCustomTexts: "For locale generation, include translatable keys from plopfile",
2421
+ optVersion: "Print current version",
2422
+ optForce: "Run the generator forcefully",
2423
+ optLang: "Force display locale (e.g. en, fr)",
2424
+ danger: "danger waits for those who venture below the line",
2425
+ lowPlopfile: "Path to the plopfile",
2426
+ lowCwd: "Directory from which relative paths are calculated while locating the plopfile",
2427
+ lowPreload: "String or array of modules to require before running plop-next",
2428
+ lowDest: "Output to this directory instead of the plopfile parent directory",
2429
+ lowNoProgress: "Disable the progress spinner",
2430
+ examples: "Examples:"
2431
+ }
2432
+ };
2433
+ var CORE_DEFAULT_TEXTS_EN = CORE_DEFAULT_TEXTS;
2434
+ var CORE_DEFAULT_HELP_TEXTS = CORE_DEFAULT_TEXTS.help;
2435
+
2436
+ // src/prompts/Separator.ts
2437
+ var Separator = class {
2438
+ constructor(separator) {
2439
+ this.type = "separator";
2440
+ this.separator = separator ?? "---------------";
2441
+ }
2442
+ static isSeparator(choice) {
2443
+ return Boolean(
2444
+ choice && typeof choice === "object" && "type" in choice && choice.type === "separator"
2445
+ );
2446
+ }
2447
+ };
2448
+
2449
+ // src/errors/ErrorHandler.ts
2450
+ var import_picocolors = __toESM(require_picocolors(), 1);
2451
+ import { appendFileSync } from "fs";
2452
+ import { dirname as dirname3 } from "path";
2453
+ var ErrorHandler = class {
2454
+ constructor(options) {
2455
+ this.verbosity = "simple";
2456
+ this.useColors = true;
2457
+ this.errorPrefix = defaultTheme.plopNext?.errors?.prefix?.error ?? "\u2716";
2458
+ this.warningPrefix = defaultTheme.plopNext?.errors?.prefix?.warning ?? "\u26A0";
2459
+ this.errorStyle = defaultTheme.plopNext?.errors?.error ?? ((text) => import_picocolors.default.red(text));
2460
+ this.warningStyle = defaultTheme.plopNext?.errors?.warning ?? ((text) => import_picocolors.default.yellow(text));
2461
+ if (options?.verbosity) this.verbosity = options.verbosity;
2462
+ if (options?.logFile) this.logFile = options.logFile;
2463
+ if (options?.useColors !== void 0) this.useColors = options.useColors;
2464
+ if (options?.theme) {
2465
+ this.applyTheme(options.theme);
2466
+ }
2467
+ }
2468
+ /**
2469
+ * Handle and output an error.
2470
+ * Returns exit behavior and code.
2471
+ */
2472
+ handle(error) {
2473
+ const isPlopError = this.isPlopError(error);
2474
+ if (isPlopError) {
2475
+ const output2 = this.formatError(error);
2476
+ this.logToConsole(output2, true, error.config.isWarning);
2477
+ this.logToFile(output2, error.config.allowLogFile);
2478
+ return {
2479
+ exitCode: error.config.exitCode,
2480
+ shouldExit: error.config.shouldExit
2481
+ };
2482
+ }
2483
+ const output = this.formatError(error);
2484
+ this.logToConsole(output, false, false);
2485
+ this.logToFile(output, true);
2486
+ return {
2487
+ exitCode: 1,
2488
+ shouldExit: true
2489
+ };
2490
+ }
2491
+ isPlopError(error) {
2492
+ return error instanceof Error && "code" in error;
2493
+ }
2494
+ formatError(error) {
2495
+ if (!this.isPlopError(error)) {
2496
+ return this.formatUnhandledError(error);
2497
+ }
2498
+ switch (this.verbosity) {
2499
+ case "simple":
2500
+ return this.formatSimple(error);
2501
+ case "verbose":
2502
+ return this.formatVerbose(error);
2503
+ case "debug":
2504
+ return this.formatDebug(error);
2505
+ }
2506
+ }
2507
+ formatSimple(error) {
2508
+ return this.resolveMessage(error);
2509
+ }
2510
+ formatVerbose(error) {
2511
+ const parts = [];
2512
+ parts.push(`[${error.code}] ${this.resolveMessage(error)}`);
2513
+ if (error.config.showStackTrace && error instanceof Error && error.stack) {
2514
+ const stackLines = error.stack.split("\n").slice(1, 4);
2515
+ if (stackLines.length > 0) {
2516
+ parts.push("\nStack:");
2517
+ parts.push(stackLines.join("\n"));
2518
+ }
2519
+ }
2520
+ return parts.join("\n");
2521
+ }
2522
+ formatDebug(error) {
2523
+ const parts = [];
2524
+ parts.push(`Error Code: ${error.code}`);
2525
+ parts.push(`Message: ${this.resolveMessage(error)}`);
2526
+ parts.push(`Type: ${error.name}`);
2527
+ parts.push(`Operational: ${error.isOperational}`);
2528
+ if (error.config.showStackTrace && error.stack) {
2529
+ parts.push("\nFull Stack Trace:");
2530
+ parts.push(error.stack);
2531
+ }
2532
+ const customKeys = Object.keys(error).filter(
2533
+ (k) => !["message", "name", "code", "isOperational", "stack", "config"].includes(k)
2534
+ );
2535
+ if (customKeys.length > 0) {
2536
+ parts.push("\nAdditional Context:");
2537
+ for (const key of customKeys) {
2538
+ const value = error[key];
2539
+ if (value instanceof Error) {
2540
+ parts.push(` ${key}:`);
2541
+ parts.push(` ${value.message}`);
2542
+ } else {
2543
+ parts.push(` ${key}: ${JSON.stringify(value)}`);
2544
+ }
2545
+ }
2546
+ }
2547
+ return parts.join("\n");
2548
+ }
2549
+ formatUnhandledError(error) {
2550
+ let message = "An unexpected error occurred.";
2551
+ if (error instanceof Error) {
2552
+ message = error.message;
2553
+ if (this.verbosity === "debug" && error.stack) {
2554
+ message += `
2555
+ ${error.stack}`;
2556
+ }
2557
+ } else if (typeof error === "string") {
2558
+ message = error;
2559
+ } else {
2560
+ message = String(error);
2561
+ }
2562
+ return message;
2563
+ }
2564
+ resolveMessage(error) {
2565
+ if (!error.translation) {
2566
+ return error.message;
2567
+ }
2568
+ return this.translator?.(
2569
+ error.translation.key,
2570
+ error.translation.args,
2571
+ error.translation.fallback
2572
+ ) ?? error.translation.fallback;
2573
+ }
2574
+ logToConsole(output, isPlopError, isWarning) {
2575
+ const prefix = isWarning ? this.warningPrefix : this.errorPrefix;
2576
+ const text = `${prefix} ${output}`;
2577
+ if (this.useColors && isPlopError) {
2578
+ if (isWarning) {
2579
+ console.error(this.warningStyle(text));
2580
+ return;
2581
+ }
2582
+ console.error(this.errorStyle(text));
2583
+ } else {
2584
+ console.error(text);
2585
+ }
2586
+ }
2587
+ logToFile(output, allowLogFile = true) {
2588
+ if (!this.logFile || !allowLogFile) return;
2589
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2590
+ const logEntry = `[${timestamp}] ${output}
2591
+ `;
2592
+ try {
2593
+ const dir = dirname3(this.logFile);
2594
+ if (dir !== ".") {
2595
+ }
2596
+ appendFileSync(this.logFile, logEntry);
2597
+ } catch (e) {
2598
+ }
2599
+ }
2600
+ setVerbosity(verbosity) {
2601
+ this.verbosity = verbosity;
2602
+ }
2603
+ setLogFile(logFile) {
2604
+ this.logFile = logFile;
2605
+ }
2606
+ setTheme(theme) {
2607
+ if (!theme?.plopNext?.errors) {
2608
+ return;
2609
+ }
2610
+ this.applyTheme(theme.plopNext.errors);
2611
+ }
2612
+ setTranslator(translator) {
2613
+ this.translator = translator;
2614
+ }
2615
+ applyTheme(theme) {
2616
+ if (theme.prefix?.error) this.errorPrefix = theme.prefix.error;
2617
+ if (theme.prefix?.warning) this.warningPrefix = theme.prefix.warning;
2618
+ if (theme.error) this.errorStyle = theme.error;
2619
+ if (theme.warning) this.warningStyle = theme.warning;
2620
+ }
2621
+ };
2622
+
2623
+ // src/errors/PlopError.ts
2624
+ function resolveDefaultErrorText(key, args = []) {
2625
+ const value = CORE_DEFAULT_TEXTS.errors[key];
2626
+ if (typeof value === "function") {
2627
+ return String(value(...args));
2628
+ }
2629
+ if (typeof value === "string") {
2630
+ return value;
2631
+ }
2632
+ return key;
2633
+ }
2634
+ var PlopError = class _PlopError extends Error {
2635
+ constructor(code, message, config, translation) {
2636
+ super(message);
2637
+ this.isOperational = true;
2638
+ this.code = code;
2639
+ this.config = config;
2640
+ this.translation = translation;
2641
+ this.name = this.constructor.name;
2642
+ Object.setPrototypeOf(this, _PlopError.prototype);
2643
+ }
2644
+ };
2645
+ var GeneratorNotFoundError = class _GeneratorNotFoundError extends PlopError {
2646
+ constructor(generatorName) {
2647
+ const args = [generatorName];
2648
+ const fallback = resolveDefaultErrorText("generatorNotFound", args);
2649
+ super(
2650
+ "GENERATOR_NOT_FOUND",
2651
+ fallback,
2652
+ {
2653
+ isWarning: false,
2654
+ shouldExit: true,
2655
+ exitCode: 1,
2656
+ showStackTrace: false,
2657
+ allowLogFile: true
2658
+ },
2659
+ { key: "errors.generatorNotFound", args, fallback }
2660
+ );
2661
+ Object.setPrototypeOf(this, _GeneratorNotFoundError.prototype);
2662
+ }
2663
+ };
2664
+ var NoGeneratorsError = class _NoGeneratorsError extends PlopError {
2665
+ constructor() {
2666
+ const fallback = resolveDefaultErrorText("noGenerators");
2667
+ super(
2668
+ "NO_GENERATORS",
2669
+ fallback,
2670
+ {
2671
+ isWarning: false,
2672
+ shouldExit: true,
2673
+ exitCode: 1,
2674
+ showStackTrace: false,
2675
+ allowLogFile: true
2676
+ },
2677
+ { key: "errors.noGenerators", fallback }
2678
+ );
2679
+ Object.setPrototypeOf(this, _NoGeneratorsError.prototype);
2680
+ }
2681
+ };
2682
+ var InvalidPromptError = class _InvalidPromptError extends PlopError {
2683
+ constructor(promptName, reason) {
2684
+ const args = [promptName, reason];
2685
+ const fallback = resolveDefaultErrorText("invalidPrompt", args);
2686
+ super(
2687
+ "INVALID_PROMPT",
2688
+ fallback,
2689
+ {
2690
+ isWarning: false,
2691
+ shouldExit: true,
2692
+ exitCode: 1,
2693
+ showStackTrace: false,
2694
+ allowLogFile: true
2695
+ },
2696
+ { key: "errors.invalidPrompt", args, fallback }
2697
+ );
2698
+ Object.setPrototypeOf(this, _InvalidPromptError.prototype);
2699
+ }
2700
+ };
2701
+ var BypassParseError = class _BypassParseError extends PlopError {
2702
+ constructor(promptName, promptType, value, reason) {
2703
+ const args = [promptName, promptType, value, reason];
2704
+ const fallback = resolveDefaultErrorText("bypassParse", args);
2705
+ super(
2706
+ "BYPASS_PARSE_ERROR",
2707
+ fallback,
2708
+ {
2709
+ isWarning: false,
2710
+ shouldExit: true,
2711
+ exitCode: 1,
2712
+ showStackTrace: false,
2713
+ allowLogFile: true
2714
+ },
2715
+ { key: "errors.bypassParse", args, fallback }
2716
+ );
2717
+ Object.setPrototypeOf(this, _BypassParseError.prototype);
2718
+ }
2719
+ };
2720
+ var PlopfileLoadError = class _PlopfileLoadError extends PlopError {
2721
+ constructor(filePath, requireError, importError) {
2722
+ const args = [filePath];
2723
+ const fallback = resolveDefaultErrorText("plopfileLoad", args);
2724
+ super(
2725
+ "PLOPFILE_LOAD_ERROR",
2726
+ fallback,
2727
+ {
2728
+ isWarning: false,
2729
+ shouldExit: true,
2730
+ exitCode: 1,
2731
+ showStackTrace: true,
2732
+ allowLogFile: true
2733
+ },
2734
+ { key: "errors.plopfileLoad", args, fallback }
2735
+ );
2736
+ this.requireError = requireError;
2737
+ this.importError = importError;
2738
+ Object.setPrototypeOf(this, _PlopfileLoadError.prototype);
2739
+ }
2740
+ };
2741
+ var PlopfileExportError = class _PlopfileExportError extends PlopError {
2742
+ constructor(message) {
2743
+ const fallback = message ?? resolveDefaultErrorText("plopfileExport");
2744
+ super(
2745
+ "PLOPFILE_EXPORT_ERROR",
2746
+ fallback,
2747
+ {
2748
+ isWarning: false,
2749
+ shouldExit: true,
2750
+ exitCode: 1,
2751
+ showStackTrace: false,
2752
+ allowLogFile: true
2753
+ },
2754
+ message ? void 0 : { key: "errors.plopfileExport", fallback }
2755
+ );
2756
+ Object.setPrototypeOf(this, _PlopfileExportError.prototype);
2757
+ }
2758
+ };
2759
+ var UserCancelledError = class _UserCancelledError extends PlopError {
2760
+ constructor() {
2761
+ const fallback = resolveDefaultErrorText("userCancelled");
2762
+ super(
2763
+ "USER_CANCELLED",
2764
+ fallback,
2765
+ {
2766
+ isWarning: true,
2767
+ shouldExit: true,
2768
+ exitCode: 0,
2769
+ showStackTrace: false,
2770
+ allowLogFile: false
2771
+ },
2772
+ { key: "errors.userCancelled", fallback }
2773
+ );
2774
+ Object.setPrototypeOf(this, _UserCancelledError.prototype);
2775
+ }
2776
+ };
2777
+ var PlopfileNotFoundWarning = class _PlopfileNotFoundWarning extends PlopError {
2778
+ constructor(message) {
2779
+ const fallback = message ?? resolveDefaultErrorText("plopfileNotFoundWarning");
2780
+ super(
2781
+ "PLOPFILE_NOT_FOUND",
2782
+ fallback,
2783
+ {
2784
+ isWarning: true,
2785
+ shouldExit: true,
2786
+ exitCode: 1,
2787
+ showStackTrace: false,
2788
+ allowLogFile: false
2789
+ },
2790
+ message ? void 0 : { key: "errors.plopfileNotFoundWarning", fallback }
2791
+ );
2792
+ Object.setPrototypeOf(this, _PlopfileNotFoundWarning.prototype);
2793
+ }
2794
+ };
2795
+ var ForcedLangFallbackWarning = class _ForcedLangFallbackWarning extends PlopError {
2796
+ constructor(locale, reason) {
2797
+ const args = [locale];
2798
+ const key = reason === "i18n-missing" ? "forcedLangI18nMissing" : "forcedLangUnavailable";
2799
+ const fallback = resolveDefaultErrorText(key, args);
2800
+ super(
2801
+ "FORCED_LANG_FALLBACK",
2802
+ fallback,
2803
+ {
2804
+ isWarning: true,
2805
+ shouldExit: false,
2806
+ exitCode: 0,
2807
+ showStackTrace: false,
2808
+ allowLogFile: false
2809
+ },
2810
+ {
2811
+ key: `errors.${key}`,
2812
+ args,
2813
+ fallback
2814
+ }
2815
+ );
2816
+ this.locale = locale;
2817
+ this.reason = reason;
2818
+ Object.setPrototypeOf(this, _ForcedLangFallbackWarning.prototype);
2819
+ }
2820
+ };
2821
+ var I18nSourceWarning = class _I18nSourceWarning extends PlopError {
2822
+ constructor(message) {
2823
+ super(
2824
+ "I18N_SOURCE_WARNING",
2825
+ message,
2826
+ {
2827
+ isWarning: true,
2828
+ shouldExit: false,
2829
+ exitCode: 0,
2830
+ showStackTrace: false,
2831
+ allowLogFile: false
2832
+ }
2833
+ );
2834
+ Object.setPrototypeOf(this, _I18nSourceWarning.prototype);
2835
+ }
2836
+ };
2837
+ export {
2838
+ ActionRunner,
2839
+ BypassParseError,
2840
+ CORE_DEFAULT_HELP_TEXTS,
2841
+ CORE_DEFAULT_TEXTS,
2842
+ CORE_DEFAULT_TEXTS_EN,
2843
+ ErrorHandler,
2844
+ ForcedLangFallbackWarning,
2845
+ GeneratorNotFoundError,
2846
+ I18nSourceWarning,
2847
+ InvalidPromptError,
2848
+ NoGeneratorsError,
2849
+ PROMPT_TYPE_THEMES,
2850
+ PlopError,
2851
+ PlopNextCore,
2852
+ PlopfileExportError,
2853
+ PlopfileLoadError,
2854
+ PlopfileNotFoundWarning,
2855
+ PromptHandlerRegistry,
2856
+ Separator,
2857
+ UserCancelledError,
2858
+ askCustomPrompt,
2859
+ createCheckboxHandler,
2860
+ createConfirmHandler,
2861
+ createEditorHandler,
2862
+ createPasswordHandler,
2863
+ createPromptThemeSelector,
2864
+ createSearchHandler,
2865
+ createSelectHandler,
2866
+ defaultTheme,
2867
+ getCustomPrompt,
2868
+ listCustomPromptTypes,
2869
+ registerBuiltInPromptHandlers,
2870
+ registerCustomPrompt
2871
+ };