@ijuantm/simpl-addon 2.3.0 → 2.4.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 +57 -18
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -10,7 +10,8 @@ const {exec} = require('child_process');
10
10
  const execAsync = promisify(exec);
11
11
 
12
12
  const COLORS = {
13
- reset: '\x1b[0m', green: '\x1b[32m', yellow: '\x1b[33m', red: '\x1b[31m', cyan: '\x1b[36m', blue: '\x1b[34m', gray: '\x1b[90m', bold: '\x1b[1m', dim: '\x1b[2m'
13
+ reset: '\x1b[0m', green: '\x1b[32m', yellow: '\x1b[33m', red: '\x1b[31m',
14
+ cyan: '\x1b[36m', blue: '\x1b[34m', gray: '\x1b[90m', bold: '\x1b[1m', dim: '\x1b[2m'
14
15
  };
15
16
 
16
17
  const CDN_BASE = 'https://cdn.simpl.iwanvanderwal.nl/framework';
@@ -83,7 +84,7 @@ const getSimplVersion = () => {
83
84
  const showHelp = () => {
84
85
  console.log();
85
86
  log(` ╭${'─'.repeat(62)}╮`);
86
- log(` │ ${COLORS.bold}Simpl Add-on Installer${COLORS.reset}${' '.repeat(38)}│`);
87
+ log(` │ ${COLORS.bold}Simpl Add-on Installer${COLORS.reset}${' '.repeat(36)}│`);
87
88
  log(` ╰${'─'.repeat(62)}╯`);
88
89
  console.log();
89
90
  log(` ${COLORS.bold}Usage:${COLORS.reset}`, 'blue');
@@ -124,11 +125,12 @@ const getAvailableAddons = async (version) => {
124
125
 
125
126
  if (fs.existsSync(localAddonsDir)) return fs.readdirSync(localAddonsDir, {withFileTypes: true})
126
127
  .filter(entry => entry.isFile() && entry.name.endsWith('.zip'))
127
- .map(entry => entry.name.replace('.zip', ''));
128
+ .map(entry => entry.name.replace('.zip', ''))
129
+ .sort();
128
130
 
129
131
  const versionsData = await getVersionsData();
130
132
  const versionMeta = versionsData.versions[version];
131
- return versionMeta?.['add-ons'] || [];
133
+ return (versionMeta?.['add-ons'] || []).sort();
132
134
  };
133
135
 
134
136
  const extractMarkers = (content) => {
@@ -137,9 +139,11 @@ const extractMarkers = (content) => {
137
139
  content.split('\n').forEach((line, i) => {
138
140
  const afterMatch = line.match(/@addon-insert:after\s*\(\s*["'](.+?)["']\s*\)/);
139
141
  const beforeMatch = line.match(/@addon-insert:before\s*\(\s*["'](.+?)["']\s*\)/);
142
+ const replaceMatch = line.match(/@addon-insert:replace\s*\(\s*["'](.+?)["']\s*\)/);
140
143
 
141
144
  if (afterMatch) markers.push({type: 'after', lineIndex: i, searchText: afterMatch[1]});
142
145
  else if (beforeMatch) markers.push({type: 'before', lineIndex: i, searchText: beforeMatch[1]});
146
+ else if (replaceMatch) markers.push({type: 'replace', lineIndex: i, markerName: replaceMatch[1]});
143
147
  else if (line.includes('@addon-insert:prepend')) markers.push({type: 'prepend', lineIndex: i});
144
148
  else if (line.includes('@addon-insert:append')) markers.push({type: 'append', lineIndex: i});
145
149
  });
@@ -149,10 +153,13 @@ const extractMarkers = (content) => {
149
153
 
150
154
  const collectContentBetweenMarkers = (lines, startIndex) => {
151
155
  const content = [];
156
+
152
157
  for (let i = startIndex + 1; i < lines.length; i++) {
153
158
  if (lines[i].trim().includes('@addon-end')) break;
159
+
154
160
  content.push(lines[i]);
155
161
  }
162
+
156
163
  return content;
157
164
  };
158
165
 
@@ -181,6 +188,14 @@ const findInsertIndex = (lines, searchText, type) => {
181
188
  return -1;
182
189
  };
183
190
 
191
+ const findMarkerLine = (lines, markerName) => {
192
+ const markerPattern = new RegExp(`@addon-marker\\s*\\(\\s*["']${markerName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}["']\\s*\\)`);
193
+
194
+ for (let i = 0; i < lines.length; i++) if (markerPattern.test(lines[i])) return i;
195
+
196
+ return -1;
197
+ };
198
+
184
199
  const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
185
200
  const targetContent = fs.readFileSync(targetPath, 'utf8');
186
201
  const addonLines = addonContent.split('\n');
@@ -207,7 +222,7 @@ const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
207
222
  const targetSignature = normalizeContent(newContent.split('\n'));
208
223
 
209
224
  if (signature && targetSignature.includes(signature)) {
210
- operations.push({success: false, type: marker.type, lines: content.length, searchText: marker.searchText});
225
+ operations.push({success: false, type: marker.type, lines: content.length, searchText: marker.searchText || marker.markerName});
211
226
  return;
212
227
  }
213
228
  }
@@ -219,6 +234,18 @@ const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
219
234
  if (!newContent.endsWith('\n')) newContent += '\n';
220
235
  newContent += '\n' + content.join('\n') + '\n';
221
236
  operations.push({success: true, type: 'append', lines: lineCount});
237
+ } else if (marker.type === 'replace' && marker.markerName) {
238
+ const targetLines = newContent.split('\n');
239
+ const markerLine = findMarkerLine(targetLines, marker.markerName);
240
+
241
+ if (markerLine === -1) {
242
+ operations.push({success: false, type: 'notfound', markerName: marker.markerName});
243
+ return;
244
+ }
245
+
246
+ targetLines.splice(markerLine, 1, ...content);
247
+ newContent = targetLines.join('\n');
248
+ operations.push({success: true, type: 'replace', lines: lineCount, markerName: marker.markerName});
222
249
  } else if ((marker.type === 'after' || marker.type === 'before') && marker.searchText) {
223
250
  const targetLines = newContent.split('\n');
224
251
  const insertIndex = findInsertIndex(targetLines, marker.searchText, marker.type);
@@ -249,10 +276,13 @@ const printMergeResults = (relativePath, isEnv, result) => {
249
276
  hasChanges = true;
250
277
  if (op.type === 'prepend') log(`${indent}${COLORS.green}✓${COLORS.reset} Prepended ${COLORS.bold}${op.lines}${COLORS.reset} ${varText}${op.lines !== 1 ? 's' : ''} to file start`);
251
278
  else if (op.type === 'append') log(`${indent}${COLORS.green}✓${COLORS.reset} Appended ${COLORS.bold}${op.lines}${COLORS.reset} ${varText}${op.lines !== 1 ? 's' : ''} to file end`);
279
+ else if (op.type === 'replace') log(`${indent}${COLORS.green}✓${COLORS.reset} Replaced marker ${COLORS.cyan}${op.markerName}${COLORS.reset} with ${COLORS.bold}${op.lines}${COLORS.reset} ${varText}${op.lines !== 1 ? 's' : ''}`);
252
280
  else if (op.type === 'after') log(`${indent}${COLORS.green}✓${COLORS.reset} Inserted ${COLORS.bold}${op.lines}${COLORS.reset} ${varText}${op.lines !== 1 ? 's' : ''} ${COLORS.cyan}after${COLORS.reset} "${COLORS.dim}${op.searchText}${COLORS.reset}"`);
253
281
  else if (op.type === 'before') log(`${indent}${COLORS.green}✓${COLORS.reset} Inserted ${COLORS.bold}${op.lines}${COLORS.reset} ${varText}${op.lines !== 1 ? 's' : ''} ${COLORS.cyan}before${COLORS.reset} "${COLORS.dim}${op.searchText}${COLORS.reset}"`);
254
- } else if (op.type === 'notfound') log(`${indent}${COLORS.yellow}⚠${COLORS.reset} ${COLORS.yellow}Could not find target:${COLORS.reset} "${COLORS.dim}${op.searchText}${COLORS.reset}"`);
255
- else log(`${indent}${COLORS.gray}○${COLORS.reset} ${COLORS.dim}Content already exists (${op.type})${COLORS.reset}`);
282
+ } else if (op.type === 'notfound') {
283
+ const target = op.markerName ? `marker "${COLORS.dim}${op.markerName}${COLORS.reset}"` : `"${COLORS.dim}${op.searchText}${COLORS.reset}"`;
284
+ log(`${indent}${COLORS.yellow}⚠${COLORS.reset} ${COLORS.yellow}Could not find target:${COLORS.reset} ${target}`);
285
+ } else log(`${indent}${COLORS.gray}○${COLORS.reset} ${COLORS.dim}Content already exists (${op.type})${COLORS.reset}`);
256
286
  });
257
287
 
258
288
  return hasChanges;
@@ -380,7 +410,7 @@ const main = async () => {
380
410
 
381
411
  console.log();
382
412
  log(` ╭${'─'.repeat(62)}╮`);
383
- log(` │ ${COLORS.bold}Simpl Add-on Installer${COLORS.reset} ${COLORS.dim}(${version})${COLORS.reset}${' '.repeat(37 - version.length)}│`);
413
+ log(` │ ${COLORS.bold}Simpl Add-on Installer${COLORS.reset} ${COLORS.dim}(v${version})${COLORS.reset}${' '.repeat(36 - version.length)}│`);
384
414
  log(` ╰${'─'.repeat(62)}╯`);
385
415
  console.log();
386
416
 
@@ -449,29 +479,38 @@ const main = async () => {
449
479
  }
450
480
 
451
481
  log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
452
- addons.forEach(name => log(` ${COLORS.cyan}•${COLORS.reset} ${name}`));
482
+ addons.forEach((name, index) => log(` ${COLORS.cyan}${index + 1}.${COLORS.reset} ${name}`));
453
483
  console.log();
454
484
 
455
485
  let addonName;
456
486
 
457
487
  while (true) {
458
- addonName = await promptUser(' Add-on to install');
459
- if (!addonName) {
460
- log(` ${COLORS.red}✗${COLORS.reset} Add-on name cannot be empty`, 'red');
488
+ const input = await promptUser(' Add-on to install (name or number)');
489
+
490
+ if (!input) {
491
+ log(` ${COLORS.red}✗${COLORS.reset} Selection cannot be empty`, 'red');
461
492
  console.log();
462
493
  continue;
463
494
  }
464
- if (!addons.includes(addonName)) {
465
- log(` ${COLORS.red}✗${COLORS.reset} Add-on "${addonName}" not found`, 'red');
466
- console.log();
467
- continue;
495
+
496
+ const numInput = parseInt(input, 10);
497
+ if (!isNaN(numInput) && numInput >= 1 && numInput <= addons.length) {
498
+ addonName = addons[numInput - 1];
499
+ break;
500
+ }
501
+
502
+ if (addons.includes(input)) {
503
+ addonName = input;
504
+ break;
468
505
  }
469
- break;
506
+
507
+ log(` ${COLORS.red}✗${COLORS.reset} Invalid selection "${input}"`, 'red');
508
+ console.log();
470
509
  }
471
510
 
472
511
  console.log();
473
512
  log(` ╭${'─'.repeat(62)}╮`);
474
- log(` │ ${COLORS.bold}Installing: ${COLORS.cyan}${addonName}${COLORS.reset} ${COLORS.dim}(${version})${COLORS.reset}${' '.repeat(46 - addonName.length - version.length)}│`);
513
+ log(` │ ${COLORS.bold}Installing: ${COLORS.cyan}${addonName}${COLORS.reset} ${COLORS.dim}(v${version})${COLORS.reset}${' '.repeat(45 - addonName.length - version.length)}│`);
475
514
  log(` ╰${'─'.repeat(62)}╯`);
476
515
  console.log();
477
516
  log(' 📦 Downloading add-on...', 'bold');
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.3.0",
4
+ "version": "2.4.0",
5
5
  "scripts": {
6
6
  "link": "npm link",
7
7
  "unlink": "npm unlink -g @ijuantm/simpl-addon"