@kosdev-code/kos-ui-cli 2.0.30 → 2.0.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kosdev-code/kos-ui-cli",
3
- "version": "2.0.30",
3
+ "version": "2.0.32",
4
4
  "bin": {
5
5
  "kosui": "./src/lib/cli.mjs"
6
6
  },
@@ -20,7 +20,7 @@
20
20
  "main": "./src/index.js",
21
21
  "kos": {
22
22
  "build": {
23
- "gitHash": "0f92875bc028bb7f4c46e8a12c3be28c1ca3c2ca"
23
+ "gitHash": "ea8751ddd6a1c43f56f22bab3bf99a0e3bd3f824"
24
24
  }
25
25
  },
26
26
  "publishConfig": {
package/src/lib/cli.mjs CHANGED
@@ -28,34 +28,560 @@ const showBanner =
28
28
  command !== "help" && process.env.DISABLE_CLI_BANNER !== "true";
29
29
 
30
30
  async function launch() {
31
- const plop = await nodePlop(configPath);
32
-
33
- plop.setActionType("clearCache", (answers, config, plop) => {
34
- clearCache();
35
- return "[ok] CLI cache cleared successfully.";
36
- });
37
-
31
+ // Check for help modes before initializing plop to avoid expensive model parsing
32
+ const shouldShowHelp = parsedArgs.help;
33
+
38
34
  if (!command || command === "help") {
35
+ // For general help, we still need plop to list generators
36
+ const plop = await nodePlop(configPath);
37
+
39
38
  console.warn("--- KOS CLI Help ---");
39
+ console.log("\nAvailable Generators:");
40
40
  plop.getGeneratorList().forEach((g) => {
41
41
  console.log(`- ${g.name}: ${g.description}`);
42
42
  });
43
+
44
+ console.log("\nUsage:");
45
+ console.log(" kosui <generator> [options]");
46
+ console.log(" kosui <generator> --help # Show generator-specific help");
47
+ console.log("\nGlobal Options:");
48
+ console.log(" --no-cache Disable cache");
49
+ console.log(" --refresh Clear cache and refresh");
50
+ console.log(
51
+ " --interactive, -i Force interactive mode (ignore provided arguments)"
52
+ );
53
+ console.log(" --help Show this help");
54
+
43
55
  return;
44
56
  }
45
57
 
58
+ // For generator-specific help, check if we can show help without full plop initialization
59
+ if (shouldShowHelp) {
60
+ const earlyMeta = await getGeneratorMetadata(command);
61
+
62
+ if (earlyMeta && Object.keys(earlyMeta).length > 0) {
63
+ // Show help immediately for --help flag
64
+ console.log(`--- ${earlyMeta?.name || command} Help ---`);
65
+ console.log(earlyMeta.description || "No description available");
66
+
67
+ if (earlyMeta?.namedArguments) {
68
+ console.log("\nNamed Arguments:");
69
+ Object.entries(earlyMeta.namedArguments).forEach(([cliArg, promptName]) => {
70
+ console.log(` --${cliArg} Maps to prompt: ${promptName}`);
71
+ });
72
+ }
73
+
74
+ console.log("\nExamples:");
75
+
76
+ // Generate examples dynamically from the generator's named arguments
77
+ if (earlyMeta?.namedArguments) {
78
+ const args = Object.keys(earlyMeta.namedArguments);
79
+
80
+ // Generate example values based on argument names
81
+ const generateExampleValue = (argName) => {
82
+ switch (argName) {
83
+ case 'name':
84
+ case 'componentName':
85
+ return command.includes('plugin') ? 'MyPlugin' : 'MyComponent';
86
+ case 'modelName':
87
+ return 'my-model';
88
+ case 'workspaceName':
89
+ return 'my-workspace';
90
+ case 'project':
91
+ case 'componentProject':
92
+ case 'modelProject':
93
+ return 'my-ui-lib';
94
+ case 'registrationProject':
95
+ return 'my-lib';
96
+ case 'companionParent':
97
+ return 'parent-model';
98
+ case 'extensionPoint':
99
+ return 'utility';
100
+ case 'group':
101
+ return 'appearance';
102
+ case 'locale':
103
+ return 'en';
104
+ case 'container':
105
+ case 'singleton':
106
+ case 'parentAware':
107
+ case 'dataServices':
108
+ return true;
109
+ case 'dryRun':
110
+ return true;
111
+ default:
112
+ return `my-${argName}`;
113
+ }
114
+ };
115
+
116
+ // Create basic example with most important arguments
117
+ const basicArgs = [];
118
+ const booleanArgs = ['container', 'singleton', 'parentAware', 'dataServices', 'dryRun'];
119
+
120
+ // Pick the most relevant arguments for each generator type
121
+ const getRelevantArgs = (args, command) => {
122
+ const relevantArgs = [];
123
+
124
+ // Always prefer name-type arguments first
125
+ const nameArgs = ['name', 'componentName', 'modelName', 'workspaceName'].filter(arg => args.includes(arg));
126
+ if (nameArgs.length > 0) {
127
+ relevantArgs.push(nameArgs[0]); // Take the first name argument
128
+ }
129
+
130
+ // Then add project-type arguments
131
+ const projectArgs = ['project', 'componentProject', 'modelProject', 'registrationProject'].filter(arg => args.includes(arg));
132
+ if (projectArgs.length > 0) {
133
+ relevantArgs.push(projectArgs[0]); // Take the first project argument
134
+ }
135
+
136
+ // Add other specific arguments
137
+ const otherArgs = args.filter(arg =>
138
+ !nameArgs.includes(arg) &&
139
+ !projectArgs.includes(arg) &&
140
+ !booleanArgs.includes(arg) &&
141
+ arg !== 'interactive'
142
+ );
143
+
144
+ // Add up to 2 more relevant arguments
145
+ relevantArgs.push(...otherArgs.slice(0, 2));
146
+
147
+ return relevantArgs;
148
+ };
149
+
150
+ const relevantArgs = getRelevantArgs(args, command);
151
+
152
+ relevantArgs.forEach(argName => {
153
+ const value = generateExampleValue(argName);
154
+ basicArgs.push(`--${argName} ${value}`);
155
+ });
156
+
157
+ // Create advanced example with boolean flags
158
+ const advancedBooleanArgs = [];
159
+ booleanArgs.forEach(argName => {
160
+ if (args.includes(argName)) {
161
+ advancedBooleanArgs.push(`--${argName}`);
162
+ }
163
+ });
164
+
165
+ // Show basic example
166
+ if (basicArgs.length > 0) {
167
+ console.log(` kosui ${command} ${basicArgs.join(' ')}`);
168
+ }
169
+
170
+ // Show advanced example with boolean flags if any exist
171
+ if (basicArgs.length > 0 && advancedBooleanArgs.length > 0) {
172
+ console.log(` kosui ${command} ${basicArgs.slice(0, 2).join(' ')} ${advancedBooleanArgs.join(' ')}`);
173
+ }
174
+
175
+ // If no basic args, show a minimal example
176
+ if (basicArgs.length === 0) {
177
+ console.log(` kosui ${command} [options]`);
178
+ }
179
+ }
180
+
181
+ console.log(` kosui ${command} --interactive # Force interactive mode`);
182
+ console.log(` kosui ${command} -i # Force interactive mode (short form)`);
183
+ return;
184
+ }
185
+ }
186
+
187
+ // Also check for no-arguments case (when only global flags like --no-cache are present)
188
+ const earlyMeta = await getGeneratorMetadata(command);
189
+ if (earlyMeta?.namedArguments) {
190
+ const namedArgumentsMap = earlyMeta.namedArguments;
191
+ const providedAnswers = {};
192
+ Object.entries(namedArgumentsMap).forEach(([cliArg, promptName]) => {
193
+ if (cliArg === "interactive") return;
194
+ if (parsedArgs[cliArg] !== undefined) {
195
+ let value = parsedArgs[cliArg];
196
+ if (value === "true" || value === "y" || value === "yes") {
197
+ value = true;
198
+ } else if (value === "false" || value === "n" || value === "no") {
199
+ value = false;
200
+ }
201
+ providedAnswers[promptName] = value;
202
+ }
203
+ });
204
+ const hasAnyAnswers = Object.keys(providedAnswers).length > 0;
205
+ const forceInteractive = parsedArgs.interactive || parsedArgs.i;
206
+
207
+ // Show help for no-arguments case
208
+ if (!hasAnyAnswers && !forceInteractive) {
209
+ console.log(`--- ${earlyMeta?.name || command} Help ---`);
210
+ console.log(earlyMeta.description || "No description available");
211
+
212
+ if (earlyMeta?.namedArguments) {
213
+ console.log("\nNamed Arguments:");
214
+ Object.entries(earlyMeta.namedArguments).forEach(([cliArg, promptName]) => {
215
+ console.log(` --${cliArg} Maps to prompt: ${promptName}`);
216
+ });
217
+ }
218
+
219
+ console.log("\nExamples:");
220
+
221
+ // Generate examples dynamically from the generator's named arguments
222
+ if (earlyMeta?.namedArguments) {
223
+ const args = Object.keys(earlyMeta.namedArguments);
224
+
225
+ // Generate example values based on argument names
226
+ const generateExampleValue = (argName) => {
227
+ switch (argName) {
228
+ case 'name':
229
+ case 'componentName':
230
+ return command.includes('plugin') ? 'MyPlugin' : 'MyComponent';
231
+ case 'modelName':
232
+ return 'my-model';
233
+ case 'workspaceName':
234
+ return 'my-workspace';
235
+ case 'project':
236
+ case 'componentProject':
237
+ case 'modelProject':
238
+ return 'my-ui-lib';
239
+ case 'registrationProject':
240
+ return 'my-lib';
241
+ case 'companionParent':
242
+ return 'parent-model';
243
+ case 'extensionPoint':
244
+ return 'utility';
245
+ case 'group':
246
+ return 'appearance';
247
+ case 'locale':
248
+ return 'en';
249
+ case 'container':
250
+ case 'singleton':
251
+ case 'parentAware':
252
+ case 'dataServices':
253
+ return true;
254
+ case 'dryRun':
255
+ return true;
256
+ default:
257
+ return `my-${argName}`;
258
+ }
259
+ };
260
+
261
+ // Create basic example with most important arguments
262
+ const basicArgs = [];
263
+ const booleanArgs = ['container', 'singleton', 'parentAware', 'dataServices', 'dryRun'];
264
+
265
+ // Pick the most relevant arguments for each generator type
266
+ const getRelevantArgs = (args, command) => {
267
+ const relevantArgs = [];
268
+
269
+ // Always prefer name-type arguments first
270
+ const nameArgs = ['name', 'componentName', 'modelName', 'workspaceName'].filter(arg => args.includes(arg));
271
+ if (nameArgs.length > 0) {
272
+ relevantArgs.push(nameArgs[0]); // Take the first name argument
273
+ }
274
+
275
+ // Then add project-type arguments
276
+ const projectArgs = ['project', 'componentProject', 'modelProject', 'registrationProject'].filter(arg => args.includes(arg));
277
+ if (projectArgs.length > 0) {
278
+ relevantArgs.push(projectArgs[0]); // Take the first project argument
279
+ }
280
+
281
+ // Add other specific arguments
282
+ const otherArgs = args.filter(arg =>
283
+ !nameArgs.includes(arg) &&
284
+ !projectArgs.includes(arg) &&
285
+ !booleanArgs.includes(arg) &&
286
+ arg !== 'interactive'
287
+ );
288
+
289
+ // Add up to 2 more relevant arguments
290
+ relevantArgs.push(...otherArgs.slice(0, 2));
291
+
292
+ return relevantArgs;
293
+ };
294
+
295
+ const relevantArgs = getRelevantArgs(args, command);
296
+
297
+ relevantArgs.forEach(argName => {
298
+ const value = generateExampleValue(argName);
299
+ basicArgs.push(`--${argName} ${value}`);
300
+ });
301
+
302
+ // Create advanced example with boolean flags
303
+ const advancedBooleanArgs = [];
304
+ booleanArgs.forEach(argName => {
305
+ if (args.includes(argName)) {
306
+ advancedBooleanArgs.push(`--${argName}`);
307
+ }
308
+ });
309
+
310
+ // Show basic example
311
+ if (basicArgs.length > 0) {
312
+ console.log(` kosui ${command} ${basicArgs.join(' ')}`);
313
+ }
314
+
315
+ // Show advanced example with boolean flags if any exist
316
+ if (basicArgs.length > 0 && advancedBooleanArgs.length > 0) {
317
+ console.log(` kosui ${command} ${basicArgs.slice(0, 2).join(' ')} ${advancedBooleanArgs.join(' ')}`);
318
+ }
319
+
320
+ // If no basic args, show a minimal example
321
+ if (basicArgs.length === 0) {
322
+ console.log(` kosui ${command} [options]`);
323
+ }
324
+ }
325
+
326
+ console.log(` kosui ${command} --interactive # Force interactive mode`);
327
+ console.log(` kosui ${command} -i # Force interactive mode (short form)`);
328
+ return;
329
+ }
330
+ }
331
+
332
+ const plop = await nodePlop(configPath);
333
+
334
+ plop.setActionType("clearCache", (answers, config, plop) => {
335
+ clearCache();
336
+ return "[ok] CLI cache cleared successfully.";
337
+ });
338
+
46
339
  const generator = plop.getGenerator(command);
47
340
 
48
341
  if (!generator) {
49
342
  console.error(`[kos-cli] Generator "${command}" not found.`);
50
343
  process.exit(1);
51
344
  }
345
+
52
346
  const meta = await getGeneratorMetadata(command);
347
+
348
+
53
349
  if (showBanner) {
54
350
  console.log(`--- Running KOS Generator: ${meta?.name || command} ---`);
55
351
  }
56
352
 
57
353
  try {
58
- const answers = await generator.runPrompts();
354
+ // Extract positional arguments for prompt bypass
355
+ const positionalArgs = parsedArgs._.slice(1); // Remove command from args
356
+
357
+ // Get named arguments mapping from generator metadata
358
+ const namedArgumentsMap = meta?.namedArguments || {};
359
+
360
+ // Convert CLI args to answer format using generator's mapping
361
+ const providedAnswers = {};
362
+ Object.entries(namedArgumentsMap).forEach(([cliArg, promptName]) => {
363
+ // Skip 'interactive' - it's handled as a special CLI flag, not a prompt argument
364
+ if (cliArg === "interactive") {
365
+ return;
366
+ }
367
+
368
+ if (parsedArgs[cliArg] !== undefined) {
369
+ let value = parsedArgs[cliArg];
370
+
371
+ // Convert string boolean values to actual booleans
372
+ if (value === "true" || value === "y" || value === "yes") {
373
+ value = true;
374
+ } else if (value === "false" || value === "n" || value === "no") {
375
+ value = false;
376
+ }
377
+
378
+ providedAnswers[promptName] = value;
379
+ }
380
+ });
381
+
382
+ // Also handle positional arguments
383
+ const prompts = generator.prompts || [];
384
+ positionalArgs.forEach((arg, index) => {
385
+ if (prompts[index] && !prompts[index].when) {
386
+ // Only non-conditional prompts
387
+ const promptName = prompts[index].name;
388
+ if (providedAnswers[promptName] === undefined) {
389
+ let value = arg;
390
+
391
+ // Convert string boolean values to actual booleans
392
+ if (value === "true" || value === "y" || value === "yes") {
393
+ value = true;
394
+ } else if (value === "false" || value === "n" || value === "no") {
395
+ value = false;
396
+ }
397
+
398
+ providedAnswers[promptName] = value;
399
+ }
400
+ }
401
+ });
402
+
403
+ // Check if we have ANY provided answers
404
+ const hasAnyAnswers = Object.keys(providedAnswers).length > 0;
405
+
406
+ let answers;
407
+
408
+ // Check if user wants to force interactive mode
409
+ const forceInteractive = parsedArgs.interactive || parsedArgs.i;
410
+
411
+ // If no arguments provided and no interactive flag, show help
412
+ if (!hasAnyAnswers && !forceInteractive) {
413
+ console.log(`--- ${meta?.name || command} Help ---`);
414
+ console.log(generator.description || "No description available");
415
+
416
+ if (meta?.namedArguments) {
417
+ console.log("\nNamed Arguments:");
418
+ Object.entries(meta.namedArguments).forEach(([cliArg, promptName]) => {
419
+ console.log(` --${cliArg} Maps to prompt: ${promptName}`);
420
+ });
421
+ }
422
+
423
+ console.log("\nExamples:");
424
+
425
+ // Generate examples dynamically from the generator's named arguments
426
+ if (meta?.namedArguments) {
427
+ const args = Object.keys(meta.namedArguments);
428
+
429
+ // Generate example values based on argument names
430
+ const generateExampleValue = (argName) => {
431
+ switch (argName) {
432
+ case 'name':
433
+ case 'componentName':
434
+ return command.includes('plugin') ? 'MyPlugin' : 'MyComponent';
435
+ case 'modelName':
436
+ return 'my-model';
437
+ case 'workspaceName':
438
+ return 'my-workspace';
439
+ case 'project':
440
+ case 'componentProject':
441
+ case 'modelProject':
442
+ return 'my-ui-lib';
443
+ case 'registrationProject':
444
+ return 'my-lib';
445
+ case 'companionParent':
446
+ return 'parent-model';
447
+ case 'extensionPoint':
448
+ return 'utility';
449
+ case 'group':
450
+ return 'appearance';
451
+ case 'locale':
452
+ return 'en';
453
+ case 'container':
454
+ case 'singleton':
455
+ case 'parentAware':
456
+ case 'dataServices':
457
+ return true;
458
+ case 'dryRun':
459
+ return true;
460
+ default:
461
+ return `my-${argName}`;
462
+ }
463
+ };
464
+
465
+ // Create basic example with most important arguments
466
+ const basicArgs = [];
467
+ const booleanArgs = ['container', 'singleton', 'parentAware', 'dataServices', 'dryRun'];
468
+
469
+ // Pick the most relevant arguments for each generator type
470
+ const getRelevantArgs = (args, command) => {
471
+ const relevantArgs = [];
472
+
473
+ // Always prefer name-type arguments first
474
+ const nameArgs = ['name', 'componentName', 'modelName', 'workspaceName'].filter(arg => args.includes(arg));
475
+ if (nameArgs.length > 0) {
476
+ relevantArgs.push(nameArgs[0]); // Take the first name argument
477
+ }
478
+
479
+ // Then add project-type arguments
480
+ const projectArgs = ['project', 'componentProject', 'modelProject', 'registrationProject'].filter(arg => args.includes(arg));
481
+ if (projectArgs.length > 0) {
482
+ relevantArgs.push(projectArgs[0]); // Take the first project argument
483
+ }
484
+
485
+ // Add other specific arguments
486
+ const otherArgs = args.filter(arg =>
487
+ !nameArgs.includes(arg) &&
488
+ !projectArgs.includes(arg) &&
489
+ !booleanArgs.includes(arg) &&
490
+ arg !== 'interactive'
491
+ );
492
+
493
+ // Add up to 2 more relevant arguments
494
+ relevantArgs.push(...otherArgs.slice(0, 2));
495
+
496
+ return relevantArgs;
497
+ };
498
+
499
+ const relevantArgs = getRelevantArgs(args, command);
500
+
501
+ relevantArgs.forEach(argName => {
502
+ const value = generateExampleValue(argName);
503
+ basicArgs.push(`--${argName} ${value}`);
504
+ });
505
+
506
+ // Create advanced example with boolean flags
507
+ const advancedBooleanArgs = [];
508
+ booleanArgs.forEach(argName => {
509
+ if (args.includes(argName)) {
510
+ advancedBooleanArgs.push(`--${argName}`);
511
+ }
512
+ });
513
+
514
+ // Show basic example
515
+ if (basicArgs.length > 0) {
516
+ console.log(` kosui ${command} ${basicArgs.join(' ')}`);
517
+ }
518
+
519
+ // Show advanced example with boolean flags if any exist
520
+ if (basicArgs.length > 0 && advancedBooleanArgs.length > 0) {
521
+ console.log(` kosui ${command} ${basicArgs.slice(0, 2).join(' ')} ${advancedBooleanArgs.join(' ')}`);
522
+ }
523
+
524
+ // If no basic args, show a minimal example
525
+ if (basicArgs.length === 0) {
526
+ console.log(` kosui ${command} [options]`);
527
+ }
528
+ }
529
+
530
+ console.log(` kosui ${command} --interactive # Force interactive mode`);
531
+ console.log(` kosui ${command} -i # Force interactive mode (short form)`);
532
+ return;
533
+ }
534
+
535
+ if (hasAnyAnswers && !forceInteractive) {
536
+ // Create bypass array for all prompts
537
+ const bypassArgs = [];
538
+
539
+ // Fill bypass array with provided values or defaults
540
+ for (const prompt of prompts) {
541
+ if (prompt.when) {
542
+ // Skip conditional prompts - they can't be bypassed
543
+ continue;
544
+ }
545
+
546
+ const providedValue = providedAnswers[prompt.name];
547
+ if (providedValue !== undefined) {
548
+ bypassArgs.push(providedValue);
549
+ } else {
550
+ // Use default value or skip with "_"
551
+ bypassArgs.push(prompt.default !== undefined ? prompt.default : "_");
552
+ }
553
+ }
554
+
555
+ // Only bypass if we have all non-conditional prompts
556
+ const nonConditionalPrompts = prompts.filter((p) => !p.when);
557
+ if (bypassArgs.length === nonConditionalPrompts.length) {
558
+ // We can bypass all non-conditional prompts
559
+ answers = await generator.runPrompts(bypassArgs);
560
+
561
+ // Add any conditional prompt values that were provided
562
+ Object.assign(answers, providedAnswers);
563
+ } else {
564
+ // Fall back to interactive mode
565
+ console.log(
566
+ "Note: Running interactive mode. Your provided arguments will be applied automatically."
567
+ );
568
+ answers = await generator.runPrompts();
569
+ Object.assign(answers, providedAnswers);
570
+ }
571
+ } else {
572
+ // Run prompts interactively
573
+ if (forceInteractive) {
574
+ console.log(
575
+ "Note: Running in interactive mode. All prompts will be shown."
576
+ );
577
+
578
+ // In interactive mode, ignore provided arguments and run all prompts
579
+ answers = await generator.runPrompts();
580
+ } else {
581
+ answers = await generator.runPrompts();
582
+ }
583
+ }
584
+
59
585
  const results = await generator.runActions(answers);
60
586
 
61
587
  console.log("[kos-cli] Generator completed successfully!");
@@ -7,6 +7,12 @@ import { required } from "../../utils/validators.mjs";
7
7
  export const metadata = {
8
8
  key: "component",
9
9
  name: "KOS React Component",
10
+ namedArguments: {
11
+ name: "componentName",
12
+ componentName: "componentName",
13
+ project: "componentProject",
14
+ componentProject: "componentProject"
15
+ }
10
16
  };
11
17
 
12
18
  export default async function (plop) {
@@ -8,6 +8,11 @@ import { required } from "../../utils/validators.mjs";
8
8
  export const metadata = {
9
9
  key: "i18n:namespace",
10
10
  name: "KOS i18n Project Namespace",
11
+ namedArguments: {
12
+ name: "name",
13
+ locale: "locale",
14
+ project: "project",
15
+ },
11
16
  };
12
17
 
13
18
  export default async function (plop) {
@@ -0,0 +1,80 @@
1
+ // generators/model/add-future.mjs
2
+ import { actionFactory } from "../../utils/action-factory.mjs";
3
+ import { execute } from "../../utils/exec.mjs";
4
+ import { getAllModels } from "../../utils/nx-context.mjs";
5
+ import { DEFAULT_PROMPTS } from "../../utils/prompts.mjs";
6
+
7
+ export const metadata = {
8
+ key: "add-future",
9
+ name: "Add Future Support to Model",
10
+ invalidateCache: true,
11
+ namedArguments: {
12
+ modelName: "modelName",
13
+ project: "modelProject",
14
+ modelProject: "modelProject",
15
+ futureType: "futureType",
16
+ updateServices: "updateServices",
17
+ dryRun: "dryRun",
18
+ interactive: "interactive",
19
+ },
20
+ };
21
+
22
+ export default async function (plop) {
23
+ const allModels = await getAllModels();
24
+ const modelChoices = allModels.map((m) => ({
25
+ name: `${m.model} (${m.project})`,
26
+ value: m.model,
27
+ }));
28
+
29
+ plop.setActionType("addFutureToModel", async function (answers) {
30
+ const modelProject = allModels.find(
31
+ (m) => m.model === answers.modelName
32
+ )?.project;
33
+
34
+ const command = `npx nx generate @kosdev-code/kos-nx-plugin:kos-add-future-to-model \\
35
+ --modelName=${answers.modelName} \\
36
+ --modelProject=${modelProject} \\
37
+ --futureType=${answers.futureType} \\
38
+ --updateServices=${!!answers.updateServices} \\
39
+ --no-interactive ${answers.dryRun ? "--dryRun" : ""} --verbose`;
40
+
41
+ try {
42
+ await execute(command);
43
+ } catch (error) {
44
+ throw new Error(error);
45
+ }
46
+
47
+ return `Future support (${answers.futureType}) added to model ${answers.modelName} in ${modelProject}`;
48
+ });
49
+
50
+ plop.setGenerator("add-future", {
51
+ description: "Add Future support to an existing KOS Model",
52
+ prompts: [
53
+ ...DEFAULT_PROMPTS,
54
+ {
55
+ type: "list",
56
+ name: "modelName",
57
+ message: "Which model do you want to add Future support to?",
58
+ choices: modelChoices,
59
+ },
60
+ {
61
+ type: "list",
62
+ name: "futureType",
63
+ message: "What type of Future support?",
64
+ choices: [
65
+ { name: "Minimal (external access only)", value: "minimal" },
66
+ { name: "Complete (internal + external access)", value: "complete" },
67
+ ],
68
+ default: "minimal",
69
+ },
70
+ {
71
+ type: "confirm",
72
+ name: "updateServices",
73
+ message: "Update/create services file with Future operations?",
74
+ default: true,
75
+ },
76
+ ],
77
+
78
+ actions: actionFactory("addFutureToModel", metadata),
79
+ });
80
+ }
@@ -6,12 +6,30 @@ import {
6
6
  getLibraryProjects,
7
7
  getProjectDetails,
8
8
  } from "../../utils/nx-context.mjs";
9
- import { DEFAULT_PROMPTS, MODEL_PROMPTS } from "../../utils/prompts.mjs";
9
+ import {
10
+ COMPANION_PROMPTS,
11
+ DEFAULT_PROMPTS,
12
+ MODEL_PROMPTS,
13
+ } from "../../utils/prompts.mjs";
10
14
  import { required } from "../../utils/validators.mjs";
11
15
 
12
16
  export const metadata = {
13
17
  key: "model:companion",
14
18
  name: "KOS Companion Model",
19
+ namedArguments: {
20
+ name: "modelName",
21
+ modelName: "modelName",
22
+ project: "modelProject",
23
+ modelProject: "modelProject",
24
+ companionParent: "companionParent",
25
+ companionPattern: "companionPattern",
26
+ container: "container",
27
+ parentAware: "parentAware",
28
+ singleton: "singleton",
29
+ dataServices: "dataServices",
30
+ dryRun: "dryRun",
31
+ interactive: "interactive",
32
+ },
15
33
  };
16
34
 
17
35
  export default async function (plop) {
@@ -39,6 +57,7 @@ export default async function (plop) {
39
57
  --companion=true \
40
58
  --companionModel=${answers.companionParent} \
41
59
  --companionModelProject=${companionProject} \
60
+ --companionPattern=${answers.companionPattern} \
42
61
  --no-interactive ${answers.dryRun ? "--dryRun" : ""} --verbose`;
43
62
 
44
63
  try {
@@ -71,6 +90,7 @@ export default async function (plop) {
71
90
  message: "Select the companion parent model",
72
91
  choices: modelChoices,
73
92
  },
93
+ ...COMPANION_PROMPTS,
74
94
  ...MODEL_PROMPTS,
75
95
  ],
76
96
  actions: actionFactory("createCompanionModel", metadata),
@@ -11,6 +11,16 @@ import { DEFAULT_PROMPTS, MODEL_PROMPTS } from "../../utils/prompts.mjs";
11
11
  export const metadata = {
12
12
  key: "container",
13
13
  name: "KOS Container Model",
14
+ namedArguments: {
15
+ modelName: "modelName",
16
+ registrationProject: "registrationProject",
17
+ container: "container",
18
+ parentAware: "parentAware",
19
+ singleton: "singleton",
20
+ dataServices: "dataServices",
21
+ dryRun: "dryRun",
22
+ interactive: "interactive",
23
+ },
14
24
  };
15
25
 
16
26
  export default async function (plop) {
@@ -5,6 +5,11 @@ import { getAllModels, getAllProjects } from "../../utils/nx-context.mjs";
5
5
  export const metadata = {
6
6
  key: "context",
7
7
  name: "KOS Model React Context",
8
+ namedArguments: {
9
+ modelName: "modelName",
10
+ componentProject: "componentProject",
11
+ project: "componentProject",
12
+ },
8
13
  };
9
14
 
10
15
  export default async function (plop) {
@@ -5,6 +5,11 @@ import { getAllModels, getAllProjects } from "../../utils/nx-context.mjs";
5
5
  export const metadata = {
6
6
  key: "hook",
7
7
  name: "KOS Model React Hook",
8
+ namedArguments: {
9
+ modelName: "modelName",
10
+ componentProject: "componentProject",
11
+ project: "componentProject"
12
+ }
8
13
  };
9
14
 
10
15
  export default async function (plop) {
@@ -2,7 +2,6 @@
2
2
  import { actionFactory } from "../../utils/action-factory.mjs";
3
3
  import { execute } from "../../utils/exec.mjs";
4
4
  import {
5
- getAllProjects,
6
5
  getLibraryProjects,
7
6
  getProjectDetails,
8
7
  } from "../../utils/nx-context.mjs";
@@ -13,10 +12,22 @@ export const metadata = {
13
12
  key: "model",
14
13
  name: "KOS Model",
15
14
  invalidateCache: true,
15
+ namedArguments: {
16
+ name: "modelName",
17
+ modelName: "modelName",
18
+ project: "modelProject",
19
+ modelProject: "modelProject",
20
+ container: "container",
21
+ parentAware: "parentAware",
22
+ singleton: "singleton",
23
+ dataServices: "dataServices",
24
+ futureAware: "futureAware",
25
+ dryRun: "dryRun",
26
+ interactive: "interactive"
27
+ }
16
28
  };
17
29
 
18
30
  export default async function (plop) {
19
- const allProjects = await getAllProjects();
20
31
  const libraryProjects = await getLibraryProjects();
21
32
 
22
33
  plop.setActionType("createModel", async function (answers) {
@@ -29,6 +40,7 @@ export default async function (plop) {
29
40
  --dataServices=${!!answers.dataServices} \
30
41
  --singleton=${!!answers.singleton} \
31
42
  --parentAware=${!!answers.parentAware} \
43
+ --futureAware=${answers.futureAware || 'none'} \
32
44
  --no-interactive ${answers.dryRun ? "--dryRun" : ""} --verbose`;
33
45
 
34
46
  try {
@@ -4,13 +4,78 @@ import { execute } from "../../utils/exec.mjs";
4
4
  import { getAllProjects } from "../../utils/nx-context.mjs";
5
5
  import { required } from "../../utils/validators.mjs";
6
6
  export const metadata = [
7
- { key: "pluginComponent", name: "KOS UI Plugin Component" },
8
- { key: "plugin:cui", name: "KOS UI Plugin CUI Configuration" },
9
- { key: "plugin:setup", name: "KOS UI Plugin Setup Step" },
10
- { key: "plugin:utility", name: "KOS UI Plugin Utility" },
11
- { key: "plugin:setting", name: "KOS UI Plugin Setting" },
12
- { key: "plugin:nav", name: "KOS UI Plugin Navigation View" },
13
- { key: "plugin:cp", name: "KOS UI Plugin Control Pour" },
7
+ {
8
+ key: "pluginComponent",
9
+ name: "KOS UI Plugin Component",
10
+ namedArguments: {
11
+ name: "componentName",
12
+ componentName: "componentName",
13
+ project: "componentProject",
14
+ componentProject: "componentProject",
15
+ extensionPoint: "extensionPoint"
16
+ }
17
+ },
18
+ {
19
+ key: "plugin:cui",
20
+ name: "KOS UI Plugin CUI Configuration",
21
+ namedArguments: {
22
+ name: "componentName",
23
+ componentName: "componentName",
24
+ project: "componentProject",
25
+ componentProject: "componentProject"
26
+ }
27
+ },
28
+ {
29
+ key: "plugin:setup",
30
+ name: "KOS UI Plugin Setup Step",
31
+ namedArguments: {
32
+ name: "componentName",
33
+ componentName: "componentName",
34
+ project: "componentProject",
35
+ componentProject: "componentProject"
36
+ }
37
+ },
38
+ {
39
+ key: "plugin:utility",
40
+ name: "KOS UI Plugin Utility",
41
+ namedArguments: {
42
+ name: "componentName",
43
+ componentName: "componentName",
44
+ project: "componentProject",
45
+ componentProject: "componentProject"
46
+ }
47
+ },
48
+ {
49
+ key: "plugin:setting",
50
+ name: "KOS UI Plugin Setting",
51
+ namedArguments: {
52
+ name: "componentName",
53
+ componentName: "componentName",
54
+ project: "componentProject",
55
+ componentProject: "componentProject",
56
+ group: "group"
57
+ }
58
+ },
59
+ {
60
+ key: "plugin:nav",
61
+ name: "KOS UI Plugin Navigation View",
62
+ namedArguments: {
63
+ name: "componentName",
64
+ componentName: "componentName",
65
+ project: "componentProject",
66
+ componentProject: "componentProject"
67
+ }
68
+ },
69
+ {
70
+ key: "plugin:cp",
71
+ name: "KOS UI Plugin Control Pour",
72
+ namedArguments: {
73
+ name: "componentName",
74
+ componentName: "componentName",
75
+ project: "componentProject",
76
+ componentProject: "componentProject"
77
+ }
78
+ },
14
79
  ];
15
80
 
16
81
  export default async function (plop) {
@@ -7,6 +7,7 @@ export const metadata = {
7
7
  key: "project",
8
8
  name: "KOS UI App Project",
9
9
  invalidateCache: true,
10
+ namedArguments: { name: "name" },
10
11
  };
11
12
  export default async function (plop) {
12
13
  plop.setActionType("createUiProject", async function (answers) {
@@ -6,6 +6,7 @@ export const metadata = {
6
6
  key: "i18n",
7
7
  name: "KOS Localization Project",
8
8
  invalidateCache: true,
9
+ namedArguments: { name: "name" },
9
10
  };
10
11
  export default async function (plop) {
11
12
  plop.setActionType("createI18nProject", async function (answers) {
@@ -6,6 +6,7 @@ export const metadata = {
6
6
  key: "plugin",
7
7
  name: "KOS Plugin Project",
8
8
  invalidateCache: true,
9
+ namedArguments: { name: "name" },
9
10
  };
10
11
  export default async function (plop) {
11
12
  plop.setActionType("createPluginProject", async function (answers) {
@@ -7,6 +7,7 @@ export const metadata = {
7
7
  key: "project:splash",
8
8
  name: "KOS Splash Screen Project",
9
9
  invalidateCache: true,
10
+ namedArguments: { name: "name" },
10
11
  };
11
12
  export default async function (plop) {
12
13
  plop.setActionType("createSplashProject", async function (answers) {
@@ -6,6 +6,7 @@ export const metadata = {
6
6
  key: "theme",
7
7
  name: "KOS Theme Project",
8
8
  invalidateCache: true,
9
+ namedArguments: { name: "name" },
9
10
  };
10
11
  export default async function (plop) {
11
12
  plop.setActionType("createThemeProject", async function (answers) {
@@ -5,6 +5,10 @@ import { required } from "../../utils/validators.mjs";
5
5
  export const metadata = {
6
6
  key: "workspace",
7
7
  name: "Create a new KOS UI Workspace",
8
+ namedArguments: {
9
+ name: "workspaceName",
10
+ workspaceName: "workspaceName"
11
+ }
8
12
  };
9
13
  export default async function (plop) {
10
14
  plop.setActionType("createWorkspace", async function (answers) {
@@ -2,6 +2,7 @@
2
2
  import { detectWorkspace } from "./utils/nx-context.mjs";
3
3
 
4
4
  // Generator registration
5
+ import registerAddFuture from "./generators/model/add-future.mjs";
5
6
  import registerCompanion from "./generators/model/companion.mjs";
6
7
  import registerContainer from "./generators/model/container.mjs";
7
8
  import registerContext from "./generators/model/context.mjs";
@@ -40,6 +41,7 @@ export default async function (plop) {
40
41
  }
41
42
 
42
43
  await registerKosModel(plop);
44
+ await registerAddFuture(plop);
43
45
  await registerHook(plop);
44
46
  await registerCompanion(plop);
45
47
  await registerContainer(plop);
@@ -29,7 +29,6 @@ export const MODEL_PROMPTS = [
29
29
  name: "parentAware",
30
30
  message: "Aware of parent container?",
31
31
  default: false,
32
- when: (answers) => !!answers.container,
33
32
  },
34
33
  {
35
34
  type: "confirm",
@@ -43,4 +42,34 @@ export const MODEL_PROMPTS = [
43
42
  message: "Create data services?",
44
43
  default: true,
45
44
  },
45
+ {
46
+ type: "list",
47
+ name: "futureAware",
48
+ message: "Include Future-aware capabilities?",
49
+ choices: [
50
+ { name: "No Future support", value: "none" },
51
+ { name: "Minimal (external access only)", value: "minimal" },
52
+ { name: "Complete (internal + external access)", value: "complete" },
53
+ ],
54
+ default: "none",
55
+ },
56
+ ];
57
+
58
+ export const COMPANION_PROMPTS = [
59
+ {
60
+ type: "list",
61
+ name: "companionPattern",
62
+ message: "Which companion pattern to use?",
63
+ choices: [
64
+ {
65
+ name: "Composition (recommended) - explicit parent access via this.parent",
66
+ value: "composition",
67
+ },
68
+ {
69
+ name: "Decorator - transparent parent access, drop-in replacement",
70
+ value: "decorator",
71
+ },
72
+ ],
73
+ default: "composition",
74
+ },
46
75
  ];