@ijuantm/simpl-addon 2.5.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/install.js +140 -20
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -69,6 +69,8 @@ const promptUser = (question, defaultValue = '') => new Promise(resolve => {
69
69
  });
70
70
  });
71
71
 
72
+ const printAnswer = (question, value) => console.log(`${question}: ${COLORS.cyan}${value}${COLORS.reset}`);
73
+
72
74
  const getSimplVersion = () => {
73
75
  const simplFile = path.join(process.cwd(), '.simpl');
74
76
 
@@ -81,6 +83,107 @@ const getSimplVersion = () => {
81
83
  return config.version;
82
84
  };
83
85
 
86
+ // --- Argument parsing ---
87
+
88
+ const parseArgs = (args) => {
89
+ const result = {addon: null, unknownFlags: [], help: false, list: false};
90
+
91
+ for (const arg of args) {
92
+ if (arg === '--help' || arg === '-h') {
93
+ result.help = true;
94
+ continue;
95
+ }
96
+ if (arg === '--list' || arg === '-l') {
97
+ result.list = true;
98
+ continue;
99
+ }
100
+
101
+ if (arg.startsWith('--addon=')) {
102
+ result.addon = arg.slice(8).trim() || null;
103
+ continue;
104
+ }
105
+ if (arg.startsWith('-a=')) {
106
+ result.addon = arg.slice(3).trim() || null;
107
+ continue;
108
+ }
109
+
110
+ if (arg.startsWith('-') && !arg.startsWith('--addon') && !arg.startsWith('-a')) {
111
+ result.unknownFlags.push(arg);
112
+ continue;
113
+ }
114
+
115
+ if (!arg.startsWith('-') && !result.addon) result.addon = arg;
116
+ }
117
+
118
+ return result;
119
+ };
120
+
121
+ const KNOWN_FLAGS = ['--addon', '-a', '--help', '-h', '--list', '-l'];
122
+
123
+ const levenshtein = (a, b) => {
124
+ const m = a.length, n = b.length;
125
+ const dp = Array.from({length: m + 1}, (_, i) => Array.from({length: n + 1}, (_, j) => i === 0 ? j : j === 0 ? i : 0));
126
+
127
+ for (let i = 1; i <= m; i++)
128
+ for (let j = 1; j <= n; j++)
129
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
130
+
131
+ return dp[m][n];
132
+ };
133
+
134
+ const closestMatch = (input, options) => {
135
+ let best = null, bestDist = Infinity;
136
+
137
+ for (const opt of options) {
138
+ const dist = levenshtein(input.toLowerCase(), opt.toLowerCase());
139
+ if (dist < bestDist) {
140
+ bestDist = dist;
141
+ best = opt;
142
+ }
143
+ }
144
+
145
+ return bestDist <= Math.max(3, Math.floor(input.length / 2)) ? best : null;
146
+ };
147
+
148
+ // --- Fuzzy addon resolution with interactive prompts ---
149
+
150
+ const resolveAddon = async (input, addons) => {
151
+ if (addons.includes(input)) return input;
152
+
153
+ const suggestion = closestMatch(input, addons);
154
+ console.log();
155
+ log(` ${COLORS.red}✗${COLORS.reset} Add-on ${COLORS.bold}${input}${COLORS.reset} not found`, 'red');
156
+
157
+ if (suggestion) {
158
+ log(` ${COLORS.yellow}Did you mean:${COLORS.reset} ${COLORS.cyan}${suggestion}${COLORS.reset}?`);
159
+ console.log();
160
+
161
+ while (true) {
162
+ const answer = await promptUser(` Use "${suggestion}"? ${COLORS.dim}(yes / no / list)${COLORS.reset}`);
163
+ const a = answer.toLowerCase();
164
+
165
+ if (a === 'yes' || a === 'y') return suggestion;
166
+ if (a === 'list') {
167
+ listAddons(addons);
168
+ break;
169
+ }
170
+ if (a === 'no' || a === 'n') break;
171
+
172
+ log(` ${COLORS.dim}Please answer yes, no, or list${COLORS.reset}`);
173
+ }
174
+ }
175
+
176
+ console.log();
177
+ log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
178
+ listAddons(addons);
179
+ console.log();
180
+ process.exit(1);
181
+ };
182
+
183
+ const listAddons = (addons) => addons.forEach((name, i) => log(` ${COLORS.cyan}${i + 1}.${COLORS.reset} ${name}`));
184
+
185
+ // --- Help ---
186
+
84
187
  const showHelp = () => {
85
188
  console.log();
86
189
  log(` ╭${'─'.repeat(62)}╮`);
@@ -89,11 +192,13 @@ const showHelp = () => {
89
192
  console.log();
90
193
  log(` ${COLORS.bold}Usage:${COLORS.reset}`, 'blue');
91
194
  log(` ${COLORS.dim}npx @ijuantm/simpl-addon${COLORS.reset}`);
92
- log(` ${COLORS.dim}npx @ijuantm/simpl-addon <add-on>${COLORS.reset}`);
195
+ log(` ${COLORS.dim}npx @ijuantm/simpl-addon --addon=<name>${COLORS.reset}`);
93
196
  log(` ${COLORS.dim}npx @ijuantm/simpl-addon --help${COLORS.reset}`);
94
197
  console.log();
95
- log(` ${COLORS.bold}Commands:${COLORS.reset}`, 'blue');
96
- log(` ${COLORS.dim}--help, -h${COLORS.reset} Show this help message`);
198
+ log(` ${COLORS.bold}Options:${COLORS.reset}`, 'blue');
199
+ log(` ${COLORS.dim}--addon=<name>, -a=<name>${COLORS.reset} Add-on to install`);
200
+ log(` ${COLORS.dim}--list, -l${COLORS.reset} List available add-ons`);
201
+ log(` ${COLORS.dim}--help, -h${COLORS.reset} Show this help message`);
97
202
  console.log();
98
203
  log(` ${COLORS.bold}Note:${COLORS.reset}`, 'blue');
99
204
  log(` Run this command from the root of your Simpl project.`);
@@ -378,14 +483,27 @@ const mergeFiles = (toMerge) => {
378
483
 
379
484
  const main = async () => {
380
485
  const args = process.argv.slice(2);
381
- const firstArg = args[0];
486
+ const parsed = parseArgs(args);
382
487
 
383
- if (firstArg === '--help' || firstArg === '-h') {
488
+ if (parsed.help) {
384
489
  showHelp();
385
490
  process.exit(0);
386
491
  }
387
492
 
388
- const directName = firstArg && !firstArg.startsWith('-') ? firstArg : null;
493
+ // Warn about unknown flags and suggest closest known ones
494
+ for (const flag of parsed.unknownFlags) {
495
+ const flagName = flag.includes('=') ? flag.slice(0, flag.indexOf('=')) : flag;
496
+ const suggestion = closestMatch(flagName, KNOWN_FLAGS);
497
+ console.log();
498
+ log(` ${COLORS.yellow}⚠${COLORS.reset} Unknown option: ${COLORS.bold}${flag}${COLORS.reset}`, 'yellow');
499
+ if (suggestion) log(` ${COLORS.dim}Did you mean ${COLORS.reset}${COLORS.cyan}${suggestion}${COLORS.reset}${COLORS.dim}?${COLORS.reset}`);
500
+ }
501
+
502
+ if (parsed.unknownFlags.length > 0) {
503
+ log(` ${COLORS.dim}Run with --help to see all available options.${COLORS.reset}`);
504
+ console.log();
505
+ process.exit(1);
506
+ }
389
507
 
390
508
  let version;
391
509
 
@@ -442,7 +560,7 @@ const main = async () => {
442
560
  process.exit(1);
443
561
  }
444
562
 
445
- if (!directName) {
563
+ if (!parsed.addon) {
446
564
  console.log();
447
565
  log(' 🗄️ Fetching available add-ons...', 'bold');
448
566
  }
@@ -466,23 +584,23 @@ const main = async () => {
466
584
  process.exit(0);
467
585
  }
468
586
 
587
+ if (parsed.list) {
588
+ console.log();
589
+ log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
590
+ listAddons(addons);
591
+ console.log();
592
+ process.exit(0);
593
+ }
594
+
469
595
  let addonName;
470
596
 
471
- if (directName) {
472
- if (!addons.includes(directName)) {
473
- console.log();
474
- log(` ${COLORS.red}✗${COLORS.reset} Add-on ${COLORS.bold}${directName}${COLORS.reset} not found`, 'red');
475
- console.log();
476
- log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
477
- addons.forEach((name, index) => log(` ${COLORS.cyan}${index + 1}.${COLORS.reset} ${name}`));
478
- console.log();
479
- process.exit(1);
480
- }
481
- addonName = directName;
597
+ if (parsed.addon) {
598
+ addonName = await resolveAddon(parsed.addon, addons);
599
+ printAnswer(' Add-on to install', addonName);
482
600
  } else {
483
601
  console.log();
484
602
  log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
485
- addons.forEach((name, index) => log(` ${COLORS.cyan}${index + 1}.${COLORS.reset} ${name}`));
603
+ listAddons(addons);
486
604
  console.log();
487
605
 
488
606
  while (true) {
@@ -505,7 +623,9 @@ const main = async () => {
505
623
  break;
506
624
  }
507
625
 
508
- log(` ${COLORS.red}✗${COLORS.reset} Invalid selection "${input}"`, 'red');
626
+ const suggestion = closestMatch(input, addons);
627
+ log(` ${COLORS.red}✗${COLORS.reset} Invalid selection ${COLORS.bold}${input}${COLORS.reset}`, 'red');
628
+ if (suggestion) log(` ${COLORS.dim}Did you mean ${COLORS.reset}${COLORS.cyan}${suggestion}${COLORS.reset}${COLORS.dim}?${COLORS.reset}`);
509
629
  console.log();
510
630
  }
511
631
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ijuantm/simpl-addon",
3
3
  "description": "CLI tool to install Simpl framework add-ons.",
4
- "version": "2.5.0",
4
+ "version": "2.6.1",
5
5
  "scripts": {
6
6
  "link": "npm link",
7
7
  "unlink": "npm unlink -g @ijuantm/simpl-addon"