@ijuantm/simpl-addon 2.2.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.
- package/install.js +110 -21
- 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',
|
|
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(
|
|
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');
|
|
@@ -114,14 +115,22 @@ const checkServerAvailability = () => new Promise(resolve => {
|
|
|
114
115
|
});
|
|
115
116
|
});
|
|
116
117
|
|
|
118
|
+
const getVersionsData = async () => {
|
|
119
|
+
if (!await checkServerAvailability()) throw new Error('CDN server is currently unreachable');
|
|
120
|
+
return JSON.parse(await fetchUrl(`${CDN_BASE}/versions.json`));
|
|
121
|
+
};
|
|
122
|
+
|
|
117
123
|
const getAvailableAddons = async (version) => {
|
|
118
|
-
const
|
|
124
|
+
const localAddonsDir = path.join(LOCAL_RELEASES_DIR, version, 'add-ons');
|
|
119
125
|
|
|
120
|
-
if (fs.existsSync(
|
|
126
|
+
if (fs.existsSync(localAddonsDir)) return fs.readdirSync(localAddonsDir, {withFileTypes: true})
|
|
127
|
+
.filter(entry => entry.isFile() && entry.name.endsWith('.zip'))
|
|
128
|
+
.map(entry => entry.name.replace('.zip', ''))
|
|
129
|
+
.sort();
|
|
121
130
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return
|
|
131
|
+
const versionsData = await getVersionsData();
|
|
132
|
+
const versionMeta = versionsData.versions[version];
|
|
133
|
+
return (versionMeta?.['add-ons'] || []).sort();
|
|
125
134
|
};
|
|
126
135
|
|
|
127
136
|
const extractMarkers = (content) => {
|
|
@@ -130,9 +139,11 @@ const extractMarkers = (content) => {
|
|
|
130
139
|
content.split('\n').forEach((line, i) => {
|
|
131
140
|
const afterMatch = line.match(/@addon-insert:after\s*\(\s*["'](.+?)["']\s*\)/);
|
|
132
141
|
const beforeMatch = line.match(/@addon-insert:before\s*\(\s*["'](.+?)["']\s*\)/);
|
|
142
|
+
const replaceMatch = line.match(/@addon-insert:replace\s*\(\s*["'](.+?)["']\s*\)/);
|
|
133
143
|
|
|
134
144
|
if (afterMatch) markers.push({type: 'after', lineIndex: i, searchText: afterMatch[1]});
|
|
135
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]});
|
|
136
147
|
else if (line.includes('@addon-insert:prepend')) markers.push({type: 'prepend', lineIndex: i});
|
|
137
148
|
else if (line.includes('@addon-insert:append')) markers.push({type: 'append', lineIndex: i});
|
|
138
149
|
});
|
|
@@ -142,10 +153,13 @@ const extractMarkers = (content) => {
|
|
|
142
153
|
|
|
143
154
|
const collectContentBetweenMarkers = (lines, startIndex) => {
|
|
144
155
|
const content = [];
|
|
156
|
+
|
|
145
157
|
for (let i = startIndex + 1; i < lines.length; i++) {
|
|
146
158
|
if (lines[i].trim().includes('@addon-end')) break;
|
|
159
|
+
|
|
147
160
|
content.push(lines[i]);
|
|
148
161
|
}
|
|
162
|
+
|
|
149
163
|
return content;
|
|
150
164
|
};
|
|
151
165
|
|
|
@@ -174,6 +188,14 @@ const findInsertIndex = (lines, searchText, type) => {
|
|
|
174
188
|
return -1;
|
|
175
189
|
};
|
|
176
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
|
+
|
|
177
199
|
const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
|
|
178
200
|
const targetContent = fs.readFileSync(targetPath, 'utf8');
|
|
179
201
|
const addonLines = addonContent.split('\n');
|
|
@@ -200,7 +222,7 @@ const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
|
|
|
200
222
|
const targetSignature = normalizeContent(newContent.split('\n'));
|
|
201
223
|
|
|
202
224
|
if (signature && targetSignature.includes(signature)) {
|
|
203
|
-
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});
|
|
204
226
|
return;
|
|
205
227
|
}
|
|
206
228
|
}
|
|
@@ -212,6 +234,18 @@ const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
|
|
|
212
234
|
if (!newContent.endsWith('\n')) newContent += '\n';
|
|
213
235
|
newContent += '\n' + content.join('\n') + '\n';
|
|
214
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});
|
|
215
249
|
} else if ((marker.type === 'after' || marker.type === 'before') && marker.searchText) {
|
|
216
250
|
const targetLines = newContent.split('\n');
|
|
217
251
|
const insertIndex = findInsertIndex(targetLines, marker.searchText, marker.type);
|
|
@@ -242,10 +276,13 @@ const printMergeResults = (relativePath, isEnv, result) => {
|
|
|
242
276
|
hasChanges = true;
|
|
243
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`);
|
|
244
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' : ''}`);
|
|
245
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}"`);
|
|
246
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}"`);
|
|
247
|
-
} else if (op.type === 'notfound')
|
|
248
|
-
|
|
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}`);
|
|
249
286
|
});
|
|
250
287
|
|
|
251
288
|
return hasChanges;
|
|
@@ -373,9 +410,52 @@ const main = async () => {
|
|
|
373
410
|
|
|
374
411
|
console.log();
|
|
375
412
|
log(` ╭${'─'.repeat(62)}╮`);
|
|
376
|
-
log(` │ ${COLORS.bold}Simpl Add-on Installer${COLORS.reset} ${COLORS.dim}(${version})${COLORS.reset}${' '.repeat(
|
|
413
|
+
log(` │ ${COLORS.bold}Simpl Add-on Installer${COLORS.reset} ${COLORS.dim}(v${version})${COLORS.reset}${' '.repeat(36 - version.length)}│`);
|
|
377
414
|
log(` ╰${'─'.repeat(62)}╯`);
|
|
378
415
|
console.log();
|
|
416
|
+
|
|
417
|
+
let versionsData;
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
versionsData = await getVersionsData();
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.log();
|
|
423
|
+
log(` ${COLORS.red}✗${COLORS.reset} Failed to fetch version data`, 'red');
|
|
424
|
+
if (error.message === 'CDN server is currently unreachable') log(` ${COLORS.dim}The CDN server is currently unavailable. Please try again later.${COLORS.reset}`);
|
|
425
|
+
console.log();
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const versionMeta = versionsData.versions[version];
|
|
430
|
+
if (!versionMeta) {
|
|
431
|
+
console.log();
|
|
432
|
+
log(` ${COLORS.red}✗${COLORS.reset} Version ${COLORS.bold}${version}${COLORS.reset} not found`, 'red');
|
|
433
|
+
console.log();
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (versionMeta['script-compatible'] === false) {
|
|
438
|
+
console.log();
|
|
439
|
+
log(` ${COLORS.red}✗${COLORS.reset} Version ${COLORS.bold}${version}${COLORS.reset} is not compatible with this installer`, 'red');
|
|
440
|
+
console.log();
|
|
441
|
+
log(` ${COLORS.bold}Manual download:${COLORS.reset}`, 'blue');
|
|
442
|
+
log(` ${COLORS.cyan}${CDN_BASE}/${version}/add-ons/`, 'cyan');
|
|
443
|
+
console.log();
|
|
444
|
+
log(` ${COLORS.bold}Available add-ons for this version:${COLORS.reset}`, 'blue');
|
|
445
|
+
|
|
446
|
+
const addons = versionMeta['add-ons'] || [];
|
|
447
|
+
if (addons.length === 0) {
|
|
448
|
+
log(` ${COLORS.dim}No add-ons available${COLORS.reset}`);
|
|
449
|
+
} else {
|
|
450
|
+
addons.forEach(name => {
|
|
451
|
+
log(` ${COLORS.cyan}•${COLORS.reset} ${name}: ${COLORS.dim}${CDN_BASE}/${version}/add-ons/${name}.zip${COLORS.reset}`);
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
console.log();
|
|
456
|
+
process.exit(1);
|
|
457
|
+
}
|
|
458
|
+
|
|
379
459
|
log(' 📦 Fetching available add-ons...', 'bold');
|
|
380
460
|
|
|
381
461
|
let addons;
|
|
@@ -399,29 +479,38 @@ const main = async () => {
|
|
|
399
479
|
}
|
|
400
480
|
|
|
401
481
|
log(` ${COLORS.bold}Available add-ons:${COLORS.reset}`, 'blue');
|
|
402
|
-
addons.forEach(name => log(` ${COLORS.cyan}
|
|
482
|
+
addons.forEach((name, index) => log(` ${COLORS.cyan}${index + 1}.${COLORS.reset} ${name}`));
|
|
403
483
|
console.log();
|
|
404
484
|
|
|
405
485
|
let addonName;
|
|
406
486
|
|
|
407
487
|
while (true) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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');
|
|
411
492
|
console.log();
|
|
412
493
|
continue;
|
|
413
494
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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;
|
|
418
505
|
}
|
|
419
|
-
|
|
506
|
+
|
|
507
|
+
log(` ${COLORS.red}✗${COLORS.reset} Invalid selection "${input}"`, 'red');
|
|
508
|
+
console.log();
|
|
420
509
|
}
|
|
421
510
|
|
|
422
511
|
console.log();
|
|
423
512
|
log(` ╭${'─'.repeat(62)}╮`);
|
|
424
|
-
log(` │ ${COLORS.bold}Installing: ${COLORS.cyan}${addonName}${COLORS.reset} ${COLORS.dim}(${version})${COLORS.reset}${' '.repeat(
|
|
513
|
+
log(` │ ${COLORS.bold}Installing: ${COLORS.cyan}${addonName}${COLORS.reset} ${COLORS.dim}(v${version})${COLORS.reset}${' '.repeat(45 - addonName.length - version.length)}│`);
|
|
425
514
|
log(` ╰${'─'.repeat(62)}╯`);
|
|
426
515
|
console.log();
|
|
427
516
|
log(' 📦 Downloading add-on...', 'bold');
|