@ijuantm/simpl-addon 2.6.3 → 2.6.5

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 +134 -187
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -16,8 +16,20 @@ const COLORS = {
16
16
 
17
17
  const CDN_BASE = 'https://cdn.simpl.iwanvanderwal.nl/framework';
18
18
  const LOCAL_RELEASES_DIR = process.env.SIMPL_LOCAL_RELEASES || path.join(process.cwd(), 'local-releases');
19
-
20
- const log = (message, color = 'reset') => console.log(`${COLORS[color]}${message}${COLORS.reset}`);
19
+ const BOX_WIDTH = 62;
20
+
21
+ const log = (message = '', color = 'reset') => console.log(`${COLORS[color]}${message}${COLORS.reset}`);
22
+
23
+ const box = (title) => {
24
+ log();
25
+ log(` ╭${'─'.repeat(BOX_WIDTH)}╮`);
26
+ const contentWidth = BOX_WIDTH - 2; // Remove the two padding characters on each side
27
+ const remainingSpace = contentWidth - (typeof title === 'string' ? title.length : 0);
28
+ const padding = remainingSpace > 0 ? ' '.repeat(remainingSpace) : '';
29
+ log(` │ ${COLORS.bold}${title}${COLORS.reset}${padding}│`);
30
+ log(` ╰${'─'.repeat(BOX_WIDTH)}╯`);
31
+ log();
32
+ };
21
33
 
22
34
  const fetchUrl = (url) => new Promise((resolve, reject) => {
23
35
  https.get(url, res => {
@@ -62,7 +74,6 @@ const downloadFile = (url, dest) => new Promise((resolve, reject) => {
62
74
  const promptUser = (question, defaultValue = '') => new Promise(resolve => {
63
75
  const rl = readline.createInterface({input: process.stdin, output: process.stdout});
64
76
  const prompt = defaultValue ? `${question} ${COLORS.dim}(${defaultValue})${COLORS.reset}: ` : `${question}: `;
65
-
66
77
  rl.question(prompt, answer => {
67
78
  rl.close();
68
79
  resolve(answer.trim() || defaultValue);
@@ -73,48 +84,22 @@ const printAnswer = (question, value) => console.log(`${question}: ${COLORS.cyan
73
84
 
74
85
  const getSimplVersion = () => {
75
86
  const simplFile = path.join(process.cwd(), '.simpl');
76
-
77
87
  if (!fs.existsSync(simplFile)) throw new Error('Not a Simpl project. Missing .simpl file in current directory.');
78
-
79
88
  const config = JSON.parse(fs.readFileSync(simplFile, 'utf8'));
80
-
81
89
  if (!config.version) throw new Error('Invalid .simpl file: missing version field');
82
-
83
90
  return config.version;
84
91
  };
85
92
 
86
- // --- Argument parsing ---
87
-
88
93
  const parseArgs = (args) => {
89
94
  const result = {addon: null, unknownFlags: [], help: false, list: false};
90
-
91
95
  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;
96
+ if (arg === '--help' || arg === '-h') result.help = true;
97
+ else if (arg === '--list' || arg === '-l') result.list = true;
98
+ else if (arg.startsWith('--addon=')) result.addon = arg.slice(8).trim() || null;
99
+ else if (arg.startsWith('-a=')) result.addon = arg.slice(3).trim() || null;
100
+ else if (arg.startsWith('-') && !arg.startsWith('--addon') && !arg.startsWith('-a')) result.unknownFlags.push(arg);
101
+ else if (!arg.startsWith('-') && !result.addon) result.addon = arg;
116
102
  }
117
-
118
103
  return result;
119
104
  };
120
105
 
@@ -123,17 +108,12 @@ const KNOWN_FLAGS = ['--addon', '-a', '--help', '-h', '--list', '-l'];
123
108
  const levenshtein = (a, b) => {
124
109
  const m = a.length, n = b.length;
125
110
  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
-
111
+ for (let i = 1; i <= m; i++) for (let j = 1; j <= n; j++) 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]);
131
112
  return dp[m][n];
132
113
  };
133
114
 
134
115
  const closestMatch = (input, options) => {
135
116
  let best = null, bestDist = Infinity;
136
-
137
117
  for (const opt of options) {
138
118
  const dist = levenshtein(input.toLowerCase(), opt.toLowerCase());
139
119
  if (dist < bestDist) {
@@ -141,50 +121,43 @@ const closestMatch = (input, options) => {
141
121
  best = opt;
142
122
  }
143
123
  }
144
-
145
124
  return bestDist <= Math.max(3, Math.floor(input.length / 2)) ? best : null;
146
125
  };
147
126
 
148
- // --- Fuzzy addon resolution with interactive prompts ---
149
-
150
127
  const promptAddon = async (addons, firstInput = null) => {
151
128
  const askSuggestion = async (input) => {
152
129
  const suggestion = closestMatch(input, addons);
153
- console.log();
154
- log(` ${COLORS.red}✗${COLORS.reset} Add-on ${COLORS.bold}${input}${COLORS.reset} not found`, 'red');
130
+ log();
131
+ log(` ${COLORS.red}✗${COLORS.reset} Add-on ${COLORS.bold}${input}${COLORS.reset} not found`);
155
132
 
156
133
  if (suggestion) {
157
- log(` ${COLORS.yellow}Did you mean:${COLORS.reset} ${COLORS.cyan}${suggestion}${COLORS.reset}?`);
158
- console.log();
134
+ log(` Did you mean: ${COLORS.blue}${suggestion}${COLORS.reset}?`);
135
+ log();
159
136
 
160
137
  while (true) {
161
- const answer = await promptUser(` Use "${suggestion}"? ${COLORS.dim}(yes / no — no lists available add-ons)${COLORS.reset}`);
138
+ const answer = await promptUser(` Use "${suggestion}"? ${COLORS.dim}(yes / no (lists available add-ons))${COLORS.reset}`);
162
139
  const a = answer.toLowerCase();
163
-
164
140
  if (a === 'yes' || a === 'y') return suggestion;
165
141
  if (a === 'no' || a === 'n') break;
166
-
167
- log(` ${COLORS.dim}Please answer yes or no${COLORS.reset}`);
142
+ log(` ${COLORS.yellow}⚠${COLORS.reset} ${COLORS.dim}Please answer yes or no${COLORS.reset}`);
168
143
  }
169
144
  }
170
145
 
171
- console.log();
172
- log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
146
+ log();
147
+ log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
173
148
  listAddons(addons);
174
- console.log();
175
-
149
+ log();
176
150
  return null;
177
151
  };
178
152
 
179
153
  let pending = firstInput;
180
-
181
154
  while (true) {
182
- const input = pending || await promptUser(` Add-on to install ${COLORS.dim}(name or number)${COLORS.reset}`);
155
+ const input = pending || await promptUser(` Add-on to install ${COLORS.dim}(name or number)${COLORS.reset}`);
183
156
  pending = null;
184
157
 
185
158
  if (!input) {
186
- log(` ${COLORS.red}✗${COLORS.reset} Selection cannot be empty`, 'red');
187
- console.log();
159
+ log(` ${COLORS.yellow}⚠${COLORS.reset} ${COLORS.dim}Selection cannot be empty${COLORS.reset}`);
160
+ log();
188
161
  continue;
189
162
  }
190
163
 
@@ -197,42 +170,31 @@ const promptAddon = async (addons, firstInput = null) => {
197
170
  }
198
171
  };
199
172
 
200
- const listAddons = (addons) => addons.forEach((name, i) => log(` ${COLORS.cyan}${i + 1}.${COLORS.reset} ${name}`));
201
-
202
- // --- Help ---
173
+ const listAddons = (addons) => addons.forEach((name, i) => log(` ${COLORS.cyan}${i + 1}.${COLORS.reset} ${name}`));
203
174
 
204
175
  const showHelp = () => {
205
- console.log();
206
- log(` ╭${''.repeat(62)}╮`);
207
- log(`${COLORS.bold}Simpl Add-on Installer${COLORS.reset}${' '.repeat(38)}│`);
208
- log(` ╰${'─'.repeat(62)}╯`);
209
- console.log();
210
- log(` ${COLORS.bold}Usage:${COLORS.reset}`, 'blue');
211
- log(` ${COLORS.dim}npx @ijuantm/simpl-addon${COLORS.reset}`);
212
- log(` ${COLORS.dim}npx @ijuantm/simpl-addon --addon=<name>${COLORS.reset}`);
213
- log(` ${COLORS.dim}npx @ijuantm/simpl-addon --help${COLORS.reset}`);
214
- console.log();
215
- log(` ${COLORS.bold}Options:${COLORS.reset}`, 'blue');
216
- log(` ${COLORS.dim}--addon=<name>, -a=<name>${COLORS.reset} Add-on to install`);
217
- log(` ${COLORS.dim}--list, -l${COLORS.reset} List available add-ons`);
218
- log(` ${COLORS.dim}--help, -h${COLORS.reset} Show this help message`);
219
- console.log();
220
- log(` ${COLORS.bold}Note:${COLORS.reset}`, 'blue');
221
- log(` Run this command from the root of your Simpl project.`);
222
- log(` The add-on version will match your Simpl framework version.`);
223
- console.log();
176
+ box('Simpl Add-on Installer');
177
+ log(` ${COLORS.bold}Usage:${COLORS.reset}`, 'blue');
178
+ log(` ${COLORS.dim}npx @ijuantm/simpl-addon${COLORS.reset}`);
179
+ log(` ${COLORS.dim}npx @ijuantm/simpl-addon --addon=<name>${COLORS.reset}`);
180
+ log(` ${COLORS.dim}npx @ijuantm/simpl-addon --help${COLORS.reset}`);
181
+ log();
182
+ log(` ${COLORS.bold}Options:${COLORS.reset}`, 'blue');
183
+ log(` ${COLORS.dim}--addon=<name>, -a=<name>${COLORS.reset} Add-on to install`);
184
+ log(` ${COLORS.dim}--list, -l${COLORS.reset} List available add-ons`);
185
+ log(` ${COLORS.dim}--help, -h${COLORS.reset} Show this help message`);
186
+ log();
187
+ log(` ${COLORS.bold}Note:${COLORS.reset}`, 'blue');
188
+ log(` Run this command from the root of your Simpl project.`);
189
+ log(` The add-on version will match your Simpl framework version.`);
190
+ log();
224
191
  };
225
192
 
226
193
  const checkServerAvailability = () => new Promise(resolve => {
227
- const req = https.get(`${CDN_BASE}/versions.json`, {timeout: 5000}, res => {
194
+ https.get(`${CDN_BASE}/versions.json`, {timeout: 5000}, res => {
228
195
  res.resume();
229
196
  resolve(res.statusCode === 200);
230
- });
231
- req.on('error', () => resolve(false));
232
- req.on('timeout', () => {
233
- req.destroy();
234
- resolve(false);
235
- });
197
+ }).on('error', () => resolve(false)).on('timeout', () => resolve(false));
236
198
  });
237
199
 
238
200
  const getVersionsData = async () => {
@@ -254,11 +216,10 @@ const getAvailableAddons = async (version) => {
254
216
 
255
217
  const extractMarkers = (content) => {
256
218
  const markers = [];
257
-
258
219
  content.split('\n').forEach((line, i) => {
259
- const afterMatch = line.match(/@addon-insert:after\s*\(\s*(["'])(.+?)\1\s*\)/);
260
- const beforeMatch = line.match(/@addon-insert:before\s*\(\s*(["'])(.+?)\1\s*\)/);
261
- const replaceMatch = line.match(/@addon-insert:replace\s*\(\s*(["'])(.+?)\1\s*\)/);
220
+ const afterMatch = line.match(/@addon-insert:after\s*\(\s*(["'])(.*?)\1\s*\)/);
221
+ const beforeMatch = line.match(/@addon-insert:before\s*\(\s*(["'])(.*?)\1\s*\)/);
222
+ const replaceMatch = line.match(/@addon-insert:replace\s*\(\s*(["'])(.*?)\1\s*\)/);
262
223
 
263
224
  if (afterMatch) markers.push({type: 'after', lineIndex: i, searchText: afterMatch[2]});
264
225
  else if (beforeMatch) markers.push({type: 'before', lineIndex: i, searchText: beforeMatch[2]});
@@ -266,18 +227,15 @@ const extractMarkers = (content) => {
266
227
  else if (line.includes('@addon-insert:prepend')) markers.push({type: 'prepend', lineIndex: i});
267
228
  else if (line.includes('@addon-insert:append')) markers.push({type: 'append', lineIndex: i});
268
229
  });
269
-
270
230
  return markers;
271
231
  };
272
232
 
273
233
  const collectContentBetweenMarkers = (lines, startIndex) => {
274
234
  const content = [];
275
-
276
235
  for (let i = startIndex + 1; i < lines.length; i++) {
277
236
  if (lines[i].trim().includes('@addon-end')) break;
278
237
  content.push(lines[i]);
279
238
  }
280
-
281
239
  return content;
282
240
  };
283
241
 
@@ -285,10 +243,8 @@ const normalizeContent = (lines) => lines.map(l => l.trim()).filter(l => l && !l
285
243
 
286
244
  const processEnvContent = (content, targetContent) => {
287
245
  const envVarsToAdd = [], comments = [];
288
-
289
246
  content.forEach(line => {
290
247
  const trimmed = line.trim();
291
-
292
248
  if (trimmed.startsWith('#') || !trimmed) {
293
249
  comments.push(line);
294
250
  return;
@@ -297,7 +253,6 @@ const processEnvContent = (content, targetContent) => {
297
253
  const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
298
254
  if (match && !new RegExp(`^${match[1]}=`, 'm').test(targetContent)) envVarsToAdd.push(line);
299
255
  });
300
-
301
256
  return {content: [...comments, ...envVarsToAdd], count: envVarsToAdd.length};
302
257
  };
303
258
 
@@ -372,12 +327,11 @@ const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
372
327
  });
373
328
 
374
329
  if (newContent !== targetContent) fs.writeFileSync(targetPath, newContent, 'utf8');
375
-
376
330
  return {modified: newContent !== targetContent, operations};
377
331
  };
378
332
 
379
333
  const printMergeResults = (relativePath, isEnv, result) => {
380
- const indent = ' ';
334
+ const indent = ' ';
381
335
  const varText = isEnv ? 'environment variable' : 'line';
382
336
  let hasChanges = false;
383
337
 
@@ -400,18 +354,15 @@ const printMergeResults = (relativePath, isEnv, result) => {
400
354
 
401
355
  const extractZip = async (zipPath, destDir) => {
402
356
  fs.mkdirSync(destDir, {recursive: true});
403
-
404
- if (process.platform === 'win32') await execAsync(`powershell -command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`);
405
- else await execAsync(`unzip -q "${zipPath}" -d "${destDir}"`);
357
+ const cmd = process.platform === 'win32' ? `powershell -command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"` : `unzip -q "${zipPath}" -d "${destDir}"`;
358
+ await execAsync(cmd);
406
359
 
407
360
  const entries = fs.readdirSync(destDir, {withFileTypes: true});
408
-
409
361
  if (entries.length === 1 && entries[0].isDirectory()) {
410
362
  const nestedDir = path.join(destDir, entries[0].name);
411
363
  fs.readdirSync(nestedDir).forEach(item => fs.renameSync(path.join(nestedDir, item), path.join(destDir, item)));
412
364
  fs.rmdirSync(nestedDir);
413
365
  }
414
-
415
366
  return destDir;
416
367
  };
417
368
 
@@ -443,7 +394,6 @@ const processAddonFiles = (addonDir, targetDir) => {
443
394
  });
444
395
 
445
396
  processDirectory(addonDir);
446
-
447
397
  return {copied, skipped, toMerge};
448
398
  };
449
399
 
@@ -453,8 +403,8 @@ const downloadAddon = async (addonName, version, targetDir) => {
453
403
 
454
404
  try {
455
405
  if (fs.existsSync(localZipPath)) {
456
- console.log();
457
- log(` 💻 Using local add-on files`, 'bold');
406
+ log();
407
+ log(` 💻 Using local add-on files`, 'bold');
458
408
  const sourceDir = await extractZip(localZipPath, tempExtract);
459
409
  const result = processAddonFiles(sourceDir, targetDir);
460
410
  fs.rmSync(tempExtract, {recursive: true, force: true});
@@ -483,14 +433,14 @@ const mergeFiles = (toMerge) => {
483
433
 
484
434
  toMerge.forEach(({content, destPath, relativePath, markers}) => {
485
435
  const isEnv = path.basename(destPath) === '.env';
486
- log(`\n ${COLORS.cyan}•${COLORS.reset} ${COLORS.dim}${relativePath}${COLORS.reset}`);
436
+ log(`\n ${COLORS.cyan}•${COLORS.reset} ${COLORS.dim}${relativePath}${COLORS.reset}`);
487
437
 
488
438
  try {
489
439
  const result = mergeFile(destPath, content, markers, isEnv);
490
440
  if (printMergeResults(relativePath, isEnv, result)) merged.push(relativePath);
491
441
  else unchanged.push(relativePath);
492
442
  } catch (error) {
493
- log(` ${COLORS.red}✗ Error:${COLORS.reset} ${error.message}`, 'red');
443
+ log(` ${COLORS.red}✗ Error:${COLORS.reset} ${error.message}`);
494
444
  failed.push(relativePath);
495
445
  }
496
446
  });
@@ -511,14 +461,14 @@ const main = async () => {
511
461
  for (const flag of parsed.unknownFlags) {
512
462
  const flagName = flag.includes('=') ? flag.slice(0, flag.indexOf('=')) : flag;
513
463
  const suggestion = closestMatch(flagName, KNOWN_FLAGS);
514
- console.log();
515
- log(` ${COLORS.yellow}⚠${COLORS.reset} Unknown option: ${COLORS.bold}${flag}${COLORS.reset}`, 'yellow');
516
- if (suggestion) log(` ${COLORS.dim}Did you mean ${COLORS.reset}${COLORS.cyan}${suggestion}${COLORS.reset}${COLORS.dim}?${COLORS.reset}`);
464
+ log();
465
+ log(` ${COLORS.yellow}⚠${COLORS.reset} Unknown option: ${COLORS.bold}${flag}${COLORS.reset}`, 'yellow');
466
+ if (suggestion) log(` ${COLORS.dim}Did you mean ${COLORS.reset}${COLORS.cyan}${suggestion}${COLORS.reset}${COLORS.dim}?${COLORS.reset}`);
517
467
  }
518
468
 
519
469
  if (parsed.unknownFlags.length > 0) {
520
- log(` ${COLORS.dim}Run with --help to see all available options.${COLORS.reset}`);
521
- console.log();
470
+ log(` ${COLORS.dim}Run with --help to see all available options.${COLORS.reset}`);
471
+ log();
522
472
  process.exit(1);
523
473
  }
524
474
 
@@ -527,59 +477,57 @@ const main = async () => {
527
477
  try {
528
478
  version = getSimplVersion();
529
479
  } catch (error) {
530
- console.log();
531
- log(` ${COLORS.red}✗${COLORS.reset} ${error.message}`, 'red');
532
- console.log();
480
+ log();
481
+ log(` ${COLORS.red}✗${COLORS.reset} ${error.message}`);
482
+ log();
533
483
  process.exit(1);
534
484
  }
535
485
 
536
- console.log();
537
- log(` ╭${'─'.repeat(62)}╮`);
538
- log(` │ ${COLORS.bold}Simpl Add-on Installer${COLORS.reset} ${COLORS.dim}(v${version})${COLORS.reset}${' '.repeat(34 - version.length)}│`);
539
- log(` ╰${'─'.repeat(62)}╯`);
486
+ log();
487
+ box(`Simpl Add-on Installer${COLORS.dim}(v${version})${COLORS.reset}`);
540
488
 
541
489
  let versionsData;
542
490
 
543
491
  try {
544
492
  versionsData = await getVersionsData();
545
493
  } catch (error) {
546
- console.log();
547
- log(` ${COLORS.red}✗${COLORS.reset} Failed to fetch version data`, 'red');
548
- if (error.message === 'CDN server is currently unreachable') log(` ${COLORS.dim}The CDN server is currently unavailable. Please try again later.${COLORS.reset}`);
549
- console.log();
494
+ log();
495
+ log(` ${COLORS.red}✗${COLORS.reset} Failed to fetch version data`);
496
+ if (error.message === 'CDN server is currently unreachable') log(` ${COLORS.dim}The CDN server is currently unavailable. Please try again later.${COLORS.reset}`);
497
+ log();
550
498
  process.exit(1);
551
499
  }
552
500
 
553
501
  const versionMeta = versionsData.versions[version];
554
502
  if (!versionMeta) {
555
- console.log();
556
- log(` ${COLORS.red}✗${COLORS.reset} Version ${COLORS.bold}${version}${COLORS.reset} not found`, 'red');
557
- console.log();
503
+ log();
504
+ log(` ${COLORS.red}✗${COLORS.reset} Version ${COLORS.bold}${version}${COLORS.reset} not found`);
505
+ log();
558
506
  process.exit(1);
559
507
  }
560
508
 
561
509
  if (versionMeta['script-compatible'] === false) {
562
- console.log();
563
- log(` ${COLORS.red}✗${COLORS.reset} Version ${COLORS.bold}${version}${COLORS.reset} is not compatible with this installer`, 'red');
564
- console.log();
565
- log(` ${COLORS.bold}Manual download:${COLORS.reset}`, 'blue');
566
- log(` ${COLORS.cyan}${CDN_BASE}/${version}/add-ons/`, 'cyan');
567
- console.log();
568
- log(` ${COLORS.bold}Available add-ons for this version:${COLORS.reset}`, 'blue');
510
+ log();
511
+ log(` ${COLORS.red}✗${COLORS.reset} Version ${COLORS.bold}${version}${COLORS.reset} is not compatible with this installer`);
512
+ log();
513
+ log(` ${COLORS.bold}Manual download:${COLORS.reset}`, 'blue');
514
+ log(` ${COLORS.cyan}${CDN_BASE}/${version}/add-ons/`, 'cyan');
515
+ log();
516
+ log(` ${COLORS.bold}Available add-ons for this version:${COLORS.reset}`, 'blue');
569
517
 
570
518
  const addons = versionMeta['add-ons'] || [];
571
- if (addons.length === 0) log(` ${COLORS.dim}No add-ons available${COLORS.reset}`);
519
+ if (addons.length === 0) log(` ${COLORS.dim}No add-ons available${COLORS.reset}`);
572
520
  else addons.forEach(name => {
573
- log(` ${COLORS.cyan}•${COLORS.reset} ${name}: ${COLORS.dim}${CDN_BASE}/${version}/add-ons/${name}.zip${COLORS.reset}`);
521
+ log(` ${COLORS.cyan}•${COLORS.reset} ${name}: ${COLORS.dim}${CDN_BASE}/${version}/add-ons/${name}.zip${COLORS.reset}`);
574
522
  });
575
523
 
576
- console.log();
524
+ log();
577
525
  process.exit(1);
578
526
  }
579
527
 
580
528
  if (!parsed.addon) {
581
- console.log();
582
- log(' 🗄️ Fetching available add-ons...', 'bold');
529
+ log();
530
+ log(' 🗄️ Fetching available add-ons...', 'bold');
583
531
  }
584
532
 
585
533
  let addons;
@@ -587,25 +535,25 @@ const main = async () => {
587
535
  try {
588
536
  addons = await getAvailableAddons(version);
589
537
  } catch (error) {
590
- console.log();
591
- log(` ${COLORS.red}✗${COLORS.reset} Failed to fetch add-ons`, 'red');
592
- if (error.message === 'CDN server is currently unreachable') log(` ${COLORS.dim}The CDN server is currently unavailable. Please try again later.${COLORS.reset}`);
593
- console.log();
538
+ log();
539
+ log(` ${COLORS.red}✗${COLORS.reset} Failed to fetch add-ons`);
540
+ if (error.message === 'CDN server is currently unreachable') log(` ${COLORS.dim}The CDN server is currently unavailable. Please try again later.${COLORS.reset}`);
541
+ log();
594
542
  process.exit(1);
595
543
  }
596
544
 
597
545
  if (addons.length === 0) {
598
- console.log();
599
- log(` ${COLORS.yellow}⚠${COLORS.reset} No add-ons available for this version`);
600
- console.log();
546
+ log();
547
+ log(` ${COLORS.yellow}⚠${COLORS.reset} No add-ons available for this version`);
548
+ log();
601
549
  process.exit(0);
602
550
  }
603
551
 
604
552
  if (parsed.list) {
605
- console.log();
606
- log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
553
+ log();
554
+ log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
607
555
  listAddons(addons);
608
- console.log();
556
+ log();
609
557
  process.exit(0);
610
558
  }
611
559
 
@@ -613,73 +561,72 @@ const main = async () => {
613
561
 
614
562
  if (parsed.addon) {
615
563
  addonName = await promptAddon(addons, parsed.addon);
616
- printAnswer(' Add-on to install', addonName);
564
+ printAnswer(' Add-on to install', addonName);
617
565
  } else {
618
- console.log();
619
- log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
566
+ log();
567
+ log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
620
568
  listAddons(addons);
621
- console.log();
569
+ log();
622
570
 
623
571
  addonName = await promptAddon(addons);
624
572
  }
625
573
 
626
- console.log();
627
- log(` ╭${'─'.repeat(62)}╮`);
628
- log(` │ ${COLORS.bold}Installing: ${COLORS.cyan}${addonName}${COLORS.reset} ${COLORS.dim}(v${version})${COLORS.reset}${' '.repeat(44 - addonName.length - version.length)}│`);
629
- log(` ╰${'─'.repeat(62)}╯`);
630
- console.log();
631
- log(` 📦 Downloading ${COLORS.cyan}${addonName}${COLORS.reset} add-on...`, 'bold');
574
+ log();
575
+ box(`Installing: ${COLORS.cyan}${addonName}${COLORS.reset} ${COLORS.dim}(v${version})${COLORS.reset}`);
576
+ log(` 📦 Downloading ${COLORS.cyan}${addonName}${COLORS.reset} add-on...`, 'bold');
632
577
 
633
578
  let copied, skipped, toMerge;
634
579
 
635
580
  try {
636
581
  ({copied, skipped, toMerge} = await downloadAddon(addonName, version, process.cwd()));
637
582
  } catch (error) {
638
- console.log();
639
- log(` ${COLORS.red}✗${COLORS.reset} Installation failed`, 'red');
640
- if (error.message === 'CDN server is currently unreachable') log(` ${COLORS.dim}The CDN server is currently unavailable. Please try again later.${COLORS.reset}`);
641
- else log(` ${COLORS.dim}Please verify the add-on exists and try again${COLORS.reset}`);
642
- console.log();
583
+ log();
584
+ log(` ${COLORS.red}✗${COLORS.reset} Installation failed`);
585
+ if (error.message === 'CDN server is currently unreachable') log(` ${COLORS.dim}The CDN server is currently unavailable. Please try again later.${COLORS.reset}`);
586
+ else log(` ${COLORS.dim}Please verify the add-on exists and try again${COLORS.reset}`);
587
+ log();
643
588
  process.exit(1);
644
589
  }
645
590
 
646
591
  if (copied.length > 0) {
647
- console.log();
648
- log(` ${COLORS.green}✓${COLORS.reset} Copied ${COLORS.bold}${copied.length}${COLORS.reset} new file${copied.length !== 1 ? 's' : ''}`);
592
+ log();
593
+ log(` ${COLORS.green}✓${COLORS.reset} Copied ${COLORS.bold}${copied.length}${COLORS.reset} new file${copied.length !== 1 ? 's' : ''}`);
649
594
  }
650
595
 
651
596
  if (skipped.length > 0) {
652
- console.log();
653
- log(` ${COLORS.gray}○${COLORS.reset} ${COLORS.dim}Skipped ${skipped.length} file${skipped.length !== 1 ? 's' : ''} (no merge markers):${COLORS.reset}`);
654
- skipped.forEach(file => log(` ${COLORS.dim}• ${file}${COLORS.reset}`));
597
+ log();
598
+ log(` ${COLORS.gray}○${COLORS.reset} ${COLORS.dim}Skipped ${skipped.length} file${skipped.length !== 1 ? 's' : ''} (no merge markers):${COLORS.reset}`);
599
+ skipped.forEach(file => log(` ${COLORS.dim}• ${file}${COLORS.reset}`));
655
600
  }
656
601
 
657
602
  if (toMerge.length > 0) {
658
- console.log();
659
- log(' 🔀 Merging existing files...', 'bold');
603
+ log();
604
+ log(' 🔀 Merging existing files...', 'bold');
660
605
  const {merged, failed, unchanged} = mergeFiles(toMerge);
661
606
 
662
- console.log();
663
- log(' ' + '─'.repeat(16), 'gray');
664
- console.log();
607
+ log();
608
+ log(' ' + '─'.repeat(16), 'gray');
609
+ log();
665
610
 
666
- if (merged.length > 0) log(` ${COLORS.green}✓${COLORS.reset} Successfully merged ${COLORS.bold}${merged.length}${COLORS.reset} file${merged.length !== 1 ? 's' : ''}`);
667
- if (unchanged.length > 0) log(` ${COLORS.gray}○${COLORS.reset} ${COLORS.dim}${unchanged.length} file${unchanged.length !== 1 ? 's' : ''} unchanged (content already exists)${COLORS.reset}`);
611
+ if (merged.length > 0) log(` ${COLORS.green}✓${COLORS.reset} Successfully merged ${COLORS.bold}${merged.length}${COLORS.reset} file${merged.length !== 1 ? 's' : ''}`);
612
+ if (unchanged.length > 0) log(` ${COLORS.gray}○${COLORS.reset} ${COLORS.dim}${unchanged.length} file${unchanged.length !== 1 ? 's' : ''} unchanged (content already exists)${COLORS.reset}`);
668
613
 
669
614
  if (failed.length > 0) {
670
- console.log();
671
- log(` ${COLORS.yellow}⚠${COLORS.reset} ${COLORS.yellow}${failed.length} file${failed.length !== 1 ? 's' : ''} failed to merge${COLORS.reset}`);
672
- log(` ${COLORS.yellow}Please review manually:${COLORS.reset}`);
673
- failed.forEach(file => log(` ${COLORS.cyan}• ${file}${COLORS.reset}`));
615
+ log();
616
+ log(` ${COLORS.yellow}⚠${COLORS.reset} ${COLORS.yellow}${failed.length} file${failed.length !== 1 ? 's' : ''} failed to merge${COLORS.reset}`);
617
+ log(` ${COLORS.yellow}Please review manually:${COLORS.reset}`);
618
+ failed.forEach(file => log(` ${COLORS.cyan}• ${file}${COLORS.reset}`));
674
619
  }
675
620
  }
676
621
 
677
- console.log();
678
- log(` ${COLORS.green}✓${COLORS.reset} ${COLORS.bold}${COLORS.green}Installation complete!${COLORS.reset}`, 'green');
679
- console.log();
622
+ log();
623
+ log(` ${COLORS.green}✓${COLORS.reset} ${COLORS.bold}${COLORS.green}Installation complete!${COLORS.reset}`, 'green');
624
+ log();
680
625
  };
681
626
 
682
627
  main().catch(() => {
683
- log(`\n ${COLORS.red}✗${COLORS.reset} Fatal error occurred\n`, 'red');
628
+ log();
629
+ log(` ${COLORS.red}✗${COLORS.reset} Fatal error occurred`);
630
+ log();
684
631
  process.exit(1);
685
632
  });
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.6.3",
4
+ "version": "2.6.5",
5
5
  "scripts": {
6
6
  "link": "npm link",
7
7
  "unlink": "npm unlink -g @ijuantm/simpl-addon"