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