@ijuantm/simpl-addon 2.3.0 → 2.4.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 +56 -21
  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';
@@ -124,11 +125,11 @@ 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
- const versionsData = await getVersionsData();
130
- const versionMeta = versionsData.versions[version];
131
- return versionMeta?.['add-ons'] || [];
131
+ const versionMeta = (await getVersionsData()).versions[version];
132
+ return (versionMeta?.['add-ons'] || []).sort();
132
133
  };
133
134
 
134
135
  const extractMarkers = (content) => {
@@ -137,9 +138,11 @@ const extractMarkers = (content) => {
137
138
  content.split('\n').forEach((line, i) => {
138
139
  const afterMatch = line.match(/@addon-insert:after\s*\(\s*["'](.+?)["']\s*\)/);
139
140
  const beforeMatch = line.match(/@addon-insert:before\s*\(\s*["'](.+?)["']\s*\)/);
141
+ const replaceMatch = line.match(/@addon-insert:replace\s*\(\s*["'](.+?)["']\s*\)/);
140
142
 
141
143
  if (afterMatch) markers.push({type: 'after', lineIndex: i, searchText: afterMatch[1]});
142
144
  else if (beforeMatch) markers.push({type: 'before', lineIndex: i, searchText: beforeMatch[1]});
145
+ else if (replaceMatch) markers.push({type: 'replace', lineIndex: i, markerName: replaceMatch[1]});
143
146
  else if (line.includes('@addon-insert:prepend')) markers.push({type: 'prepend', lineIndex: i});
144
147
  else if (line.includes('@addon-insert:append')) markers.push({type: 'append', lineIndex: i});
145
148
  });
@@ -149,10 +152,12 @@ const extractMarkers = (content) => {
149
152
 
150
153
  const collectContentBetweenMarkers = (lines, startIndex) => {
151
154
  const content = [];
155
+
152
156
  for (let i = startIndex + 1; i < lines.length; i++) {
153
157
  if (lines[i].trim().includes('@addon-end')) break;
154
158
  content.push(lines[i]);
155
159
  }
160
+
156
161
  return content;
157
162
  };
158
163
 
@@ -181,6 +186,12 @@ const findInsertIndex = (lines, searchText, type) => {
181
186
  return -1;
182
187
  };
183
188
 
189
+ const findMarkerLine = (lines, markerName) => {
190
+ const markerPattern = new RegExp(`@addon-marker\\s*\\(\\s*["']${markerName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}["']\\s*\\)`);
191
+ for (let i = 0; i < lines.length; i++) if (markerPattern.test(lines[i])) return i;
192
+ return -1;
193
+ };
194
+
184
195
  const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
185
196
  const targetContent = fs.readFileSync(targetPath, 'utf8');
186
197
  const addonLines = addonContent.split('\n');
@@ -207,7 +218,7 @@ const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
207
218
  const targetSignature = normalizeContent(newContent.split('\n'));
208
219
 
209
220
  if (signature && targetSignature.includes(signature)) {
210
- operations.push({success: false, type: marker.type, lines: content.length, searchText: marker.searchText});
221
+ operations.push({success: false, type: marker.type, lines: content.length, searchText: marker.searchText || marker.markerName});
211
222
  return;
212
223
  }
213
224
  }
@@ -219,6 +230,18 @@ const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
219
230
  if (!newContent.endsWith('\n')) newContent += '\n';
220
231
  newContent += '\n' + content.join('\n') + '\n';
221
232
  operations.push({success: true, type: 'append', lines: lineCount});
233
+ } else if (marker.type === 'replace' && marker.markerName) {
234
+ const targetLines = newContent.split('\n');
235
+ const markerLine = findMarkerLine(targetLines, marker.markerName);
236
+
237
+ if (markerLine === -1) {
238
+ operations.push({success: false, type: 'notfound', markerName: marker.markerName});
239
+ return;
240
+ }
241
+
242
+ targetLines.splice(markerLine, 1, ...content);
243
+ newContent = targetLines.join('\n');
244
+ operations.push({success: true, type: 'replace', lines: lineCount, markerName: marker.markerName});
222
245
  } else if ((marker.type === 'after' || marker.type === 'before') && marker.searchText) {
223
246
  const targetLines = newContent.split('\n');
224
247
  const insertIndex = findInsertIndex(targetLines, marker.searchText, marker.type);
@@ -249,10 +272,13 @@ const printMergeResults = (relativePath, isEnv, result) => {
249
272
  hasChanges = true;
250
273
  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
274
  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`);
252
- 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
- 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}`);
275
+ 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' : ''}`);
276
+ 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}`);
277
+ 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}`);
278
+ } else if (op.type === 'notfound') {
279
+ const target = op.markerName ? `marker ${COLORS.dim}${op.markerName}${COLORS.reset}` : `${COLORS.dim}${op.searchText}${COLORS.reset}`;
280
+ log(`${indent}${COLORS.yellow}⚠${COLORS.reset} ${COLORS.yellow}Could not find target:${COLORS.reset} ${target}`);
281
+ } else log(`${indent}${COLORS.gray}○${COLORS.reset} ${COLORS.dim}Content already exists (${op.type})${COLORS.reset}`);
256
282
  });
257
283
 
258
284
  return hasChanges;
@@ -380,7 +406,7 @@ const main = async () => {
380
406
 
381
407
  console.log();
382
408
  log(` ╭${'─'.repeat(62)}╮`);
383
- log(` │ ${COLORS.bold}Simpl Add-on Installer${COLORS.reset} ${COLORS.dim}(${version})${COLORS.reset}${' '.repeat(37 - version.length)}│`);
409
+ log(` │ ${COLORS.bold}Simpl Add-on Installer${COLORS.reset} ${COLORS.dim}(v${version})${COLORS.reset}${' '.repeat(36 - version.length)}│`);
384
410
  log(` ╰${'─'.repeat(62)}╯`);
385
411
  console.log();
386
412
 
@@ -449,29 +475,38 @@ const main = async () => {
449
475
  }
450
476
 
451
477
  log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
452
- addons.forEach(name => log(` ${COLORS.cyan}•${COLORS.reset} ${name}`));
478
+ addons.forEach((name, index) => log(` ${COLORS.cyan}${index + 1}.${COLORS.reset} ${name}`));
453
479
  console.log();
454
480
 
455
481
  let addonName;
456
482
 
457
483
  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');
484
+ const input = await promptUser(` Add-on to install ${COLORS.dim}(name or number)${COLORS.reset}`);
485
+
486
+ if (!input) {
487
+ log(` ${COLORS.red}✗${COLORS.reset} Selection cannot be empty`, 'red');
461
488
  console.log();
462
489
  continue;
463
490
  }
464
- if (!addons.includes(addonName)) {
465
- log(` ${COLORS.red}✗${COLORS.reset} Add-on "${addonName}" not found`, 'red');
466
- console.log();
467
- continue;
491
+
492
+ const numInput = parseInt(input, 10);
493
+ if (!isNaN(numInput) && numInput >= 1 && numInput <= addons.length) {
494
+ addonName = addons[numInput - 1];
495
+ break;
496
+ }
497
+
498
+ if (addons.includes(input)) {
499
+ addonName = input;
500
+ break;
468
501
  }
469
- break;
502
+
503
+ log(` ${COLORS.red}✗${COLORS.reset} Invalid selection "${input}"`, 'red');
504
+ console.log();
470
505
  }
471
506
 
472
507
  console.log();
473
508
  log(` ╭${'─'.repeat(62)}╮`);
474
- log(` │ ${COLORS.bold}Installing: ${COLORS.cyan}${addonName}${COLORS.reset} ${COLORS.dim}(${version})${COLORS.reset}${' '.repeat(46 - addonName.length - version.length)}│`);
509
+ log(` │ ${COLORS.bold}Installing: ${COLORS.cyan}${addonName}${COLORS.reset} ${COLORS.dim}(v${version})${COLORS.reset}${' '.repeat(45 - addonName.length - version.length)}│`);
475
510
  log(` ╰${'─'.repeat(62)}╯`);
476
511
  console.log();
477
512
  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.1",
5
5
  "scripts": {
6
6
  "link": "npm link",
7
7
  "unlink": "npm unlink -g @ijuantm/simpl-addon"