@ijuantm/simpl-addon 1.0.0 → 1.1.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.
- package/.editorconfig +9 -0
- package/install.js +160 -81
- package/package.json +1 -1
package/.editorconfig
ADDED
package/install.js
CHANGED
|
@@ -3,28 +3,22 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const https = require('https');
|
|
6
|
+
const {promisify} = require('util');
|
|
7
|
+
const {exec} = require('child_process');
|
|
8
|
+
|
|
9
|
+
const execAsync = promisify(exec);
|
|
6
10
|
|
|
7
11
|
const COLORS = {
|
|
8
12
|
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'
|
|
9
13
|
};
|
|
10
14
|
|
|
11
|
-
const
|
|
12
|
-
const REPO_BASE = 'https://api.github.com/repos/IJuanTM/simpl/contents/add-ons';
|
|
13
|
-
const RAW_BASE = `https://raw.githubusercontent.com/IJuanTM/simpl/${BRANCH}/add-ons`;
|
|
15
|
+
const CDN_BASE = 'https://cdn.simpl.iwanvanderwal.nl/framework';
|
|
14
16
|
|
|
15
17
|
const log = (message, color = 'reset') => console.log(`${COLORS[color]}${message}${COLORS.reset}`);
|
|
16
18
|
|
|
17
19
|
const fetchUrl = (url) => new Promise((resolve, reject) => {
|
|
18
|
-
|
|
19
|
-
if (process.env.GITHUB_TOKEN) headers['Authorization'] = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
20
|
-
|
|
21
|
-
https.get(url, {headers}, res => {
|
|
20
|
+
https.get(url, res => {
|
|
22
21
|
if (res.statusCode === 302 || res.statusCode === 301) return fetchUrl(res.headers.location).then(resolve).catch(reject);
|
|
23
|
-
if (res.statusCode === 403) {
|
|
24
|
-
const resetTime = res.headers['x-ratelimit-reset'];
|
|
25
|
-
const resetDate = resetTime ? new Date(resetTime * 1000).toLocaleTimeString() : 'unknown';
|
|
26
|
-
return reject(new Error(`GitHub API rate limit exceeded. Resets at ${resetDate}. ${process.env.GITHUB_TOKEN ? 'Token is set but may be invalid.' : 'Set GITHUB_TOKEN environment variable to increase limit.'}`));
|
|
27
|
-
}
|
|
28
22
|
if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage || 'Request failed'}`));
|
|
29
23
|
|
|
30
24
|
let data = '';
|
|
@@ -33,51 +27,82 @@ const fetchUrl = (url) => new Promise((resolve, reject) => {
|
|
|
33
27
|
}).on('error', reject);
|
|
34
28
|
});
|
|
35
29
|
|
|
30
|
+
const downloadFile = (url, dest) => new Promise((resolve, reject) => {
|
|
31
|
+
const file = fs.createWriteStream(dest);
|
|
32
|
+
|
|
33
|
+
https.get(url, res => {
|
|
34
|
+
if (res.statusCode === 302 || res.statusCode === 301) {
|
|
35
|
+
fs.unlinkSync(dest);
|
|
36
|
+
return downloadFile(res.headers.location, dest).then(resolve).catch(reject);
|
|
37
|
+
}
|
|
38
|
+
if (res.statusCode !== 200) {
|
|
39
|
+
fs.unlinkSync(dest);
|
|
40
|
+
return reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage || 'Request failed'}`));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
res.pipe(file);
|
|
44
|
+
file.on('finish', () => {
|
|
45
|
+
file.close();
|
|
46
|
+
resolve();
|
|
47
|
+
});
|
|
48
|
+
}).on('error', err => {
|
|
49
|
+
fs.unlinkSync(dest);
|
|
50
|
+
reject(err);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
file.on('error', err => {
|
|
54
|
+
fs.unlinkSync(dest);
|
|
55
|
+
reject(err);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
36
59
|
const showHelp = () => {
|
|
37
60
|
console.log();
|
|
38
61
|
log(` ╭${'─'.repeat(62)}╮`);
|
|
39
62
|
log(` │ ${COLORS.bold}Simpl Add-on Installer${COLORS.reset}${' '.repeat(38)}│`);
|
|
40
63
|
log(` ╰${'─'.repeat(62)}╯`);
|
|
41
64
|
console.log();
|
|
42
|
-
log(
|
|
43
|
-
log(
|
|
44
|
-
log(
|
|
45
|
-
log(
|
|
65
|
+
log(` ${COLORS.bold}Usage:${COLORS.reset}`, 'blue');
|
|
66
|
+
log(` ${COLORS.dim}npx @ijuantm/simpl-addon <addon-name> [version]${COLORS.reset}`);
|
|
67
|
+
log(` ${COLORS.dim}npx @ijuantm/simpl-addon --list [version]${COLORS.reset}`);
|
|
68
|
+
log(` ${COLORS.dim}npx @ijuantm/simpl-addon --help${COLORS.reset}`);
|
|
46
69
|
console.log();
|
|
47
|
-
log(
|
|
48
|
-
log(
|
|
49
|
-
log(
|
|
50
|
-
log(' --help, -h Show this help message');
|
|
70
|
+
log(` ${COLORS.bold}Arguments:${COLORS.reset}`, 'blue');
|
|
71
|
+
log(` ${COLORS.dim}addon-name${COLORS.reset} Name of the add-on to install`);
|
|
72
|
+
log(` ${COLORS.dim}version${COLORS.reset} Framework version (default: latest)`);
|
|
51
73
|
console.log();
|
|
52
|
-
log(
|
|
53
|
-
log(
|
|
54
|
-
log(
|
|
74
|
+
log(` ${COLORS.bold}Commands:${COLORS.reset}`, 'blue');
|
|
75
|
+
log(` ${COLORS.dim}--list, -l${COLORS.reset} List all available add-ons`);
|
|
76
|
+
log(` ${COLORS.dim}--help, -h${COLORS.reset} Show this help message`);
|
|
77
|
+
console.log();
|
|
78
|
+
log(` ${COLORS.bold}Examples:${COLORS.reset}`, 'blue');
|
|
79
|
+
log(` ${COLORS.dim}npx @ijuantm/simpl-addon auth${COLORS.reset}`);
|
|
80
|
+
log(` ${COLORS.dim}npx @ijuantm/simpl-addon auth 1.5.0${COLORS.reset}`);
|
|
81
|
+
log(` ${COLORS.dim}npx @ijuantm/simpl-addon --list${COLORS.reset}`);
|
|
55
82
|
console.log();
|
|
56
83
|
};
|
|
57
84
|
|
|
58
|
-
const listAddons = async () => {
|
|
85
|
+
const listAddons = async (version) => {
|
|
59
86
|
console.log();
|
|
60
87
|
log(` ╭${'─'.repeat(62)}╮`);
|
|
61
|
-
log(` │ ${COLORS.bold}Available Add-ons${COLORS.reset}${' '.repeat(
|
|
88
|
+
log(` │ ${COLORS.bold}Available Add-ons${COLORS.reset} ${COLORS.dim}(${version})${COLORS.reset}${' '.repeat(39 - version.length)}│`);
|
|
62
89
|
log(` ╰${'─'.repeat(62)}╯`);
|
|
63
90
|
console.log();
|
|
64
|
-
log(' 📦 Fetching add-ons
|
|
91
|
+
log(' 📦 Fetching available add-ons...', 'bold');
|
|
65
92
|
|
|
66
93
|
try {
|
|
67
|
-
const response = await fetchUrl(`${
|
|
68
|
-
const addons = JSON.parse(response)
|
|
94
|
+
const response = await fetchUrl(`${CDN_BASE}/${version}/add-ons/list.json`);
|
|
95
|
+
const addons = JSON.parse(response)['add-ons'];
|
|
69
96
|
|
|
70
97
|
console.log();
|
|
71
98
|
|
|
72
|
-
if (addons.length === 0) {
|
|
73
|
-
|
|
74
|
-
} else {
|
|
75
|
-
addons.forEach(name => log(` ${COLORS.cyan}•${COLORS.reset} ${name}`));
|
|
76
|
-
}
|
|
99
|
+
if (addons.length === 0) log(` ${COLORS.yellow}⚠${COLORS.reset} No add-ons available`);
|
|
100
|
+
else addons.forEach(name => log(` ${COLORS.cyan}•${COLORS.reset} ${name}`));
|
|
77
101
|
} catch (error) {
|
|
78
102
|
console.log();
|
|
79
103
|
log(` ${COLORS.red}✗${COLORS.reset} Failed to fetch add-ons: ${error.message}`, 'red');
|
|
80
104
|
console.log();
|
|
105
|
+
|
|
81
106
|
process.exit(1);
|
|
82
107
|
}
|
|
83
108
|
|
|
@@ -91,7 +116,10 @@ const extractMarkers = (content) => {
|
|
|
91
116
|
const afterMatch = line.match(/@addon-insert:after\s*\(\s*["'](.+?)["']\s*\)/);
|
|
92
117
|
const beforeMatch = line.match(/@addon-insert:before\s*\(\s*["'](.+?)["']\s*\)/);
|
|
93
118
|
|
|
94
|
-
if (afterMatch) markers.push({type: 'after', lineIndex: i, searchText: afterMatch[1]});
|
|
119
|
+
if (afterMatch) markers.push({type: 'after', lineIndex: i, searchText: afterMatch[1]});
|
|
120
|
+
else if (beforeMatch) markers.push({type: 'before', lineIndex: i, searchText: beforeMatch[1]});
|
|
121
|
+
else if (line.includes('@addon-insert:prepend')) markers.push({type: 'prepend', lineIndex: i});
|
|
122
|
+
else if (line.includes('@addon-insert:append')) markers.push({type: 'append', lineIndex: i});
|
|
95
123
|
});
|
|
96
124
|
|
|
97
125
|
return markers;
|
|
@@ -102,15 +130,14 @@ const collectContentBetweenMarkers = (lines, startIndex) => {
|
|
|
102
130
|
|
|
103
131
|
for (let i = startIndex + 1; i < lines.length; i++) {
|
|
104
132
|
if (lines[i].trim().includes('@addon-end')) break;
|
|
133
|
+
|
|
105
134
|
content.push(lines[i]);
|
|
106
135
|
}
|
|
107
136
|
|
|
108
137
|
return content;
|
|
109
138
|
};
|
|
110
139
|
|
|
111
|
-
const normalizeContent = (lines) => lines.map(l => l.trim())
|
|
112
|
-
.filter(l => l && !l.startsWith('//') && !l.startsWith('#') && !l.startsWith('/*') && !l.startsWith('*'))
|
|
113
|
-
.join('|');
|
|
140
|
+
const normalizeContent = (lines) => lines.map(l => l.trim()).filter(l => l && !l.startsWith('//') && !l.startsWith('#') && !l.startsWith('/*') && !l.startsWith('*')).join('|');
|
|
114
141
|
|
|
115
142
|
const processEnvContent = (content, targetContent) => {
|
|
116
143
|
const envVarsToAdd = [], comments = [];
|
|
@@ -124,6 +151,7 @@ const processEnvContent = (content, targetContent) => {
|
|
|
124
151
|
}
|
|
125
152
|
|
|
126
153
|
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
154
|
+
|
|
127
155
|
if (match && !new RegExp(`^${match[1]}=`, 'm').test(targetContent)) envVarsToAdd.push(line);
|
|
128
156
|
});
|
|
129
157
|
|
|
@@ -131,10 +159,7 @@ const processEnvContent = (content, targetContent) => {
|
|
|
131
159
|
};
|
|
132
160
|
|
|
133
161
|
const findInsertIndex = (lines, searchText, type) => {
|
|
134
|
-
for (let i = 0; i < lines.length; i++)
|
|
135
|
-
if (lines[i].includes(searchText)) return type === 'before' ? i : i + 1;
|
|
136
|
-
}
|
|
137
|
-
|
|
162
|
+
for (let i = 0; i < lines.length; i++) if (lines[i].includes(searchText)) return type === 'before' ? i : i + 1;
|
|
138
163
|
return -1;
|
|
139
164
|
};
|
|
140
165
|
|
|
@@ -142,16 +167,19 @@ const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
|
|
|
142
167
|
const targetContent = fs.readFileSync(targetPath, 'utf8');
|
|
143
168
|
const addonLines = addonContent.split('\n');
|
|
144
169
|
const operations = [];
|
|
170
|
+
|
|
145
171
|
let newContent = targetContent;
|
|
146
172
|
|
|
147
173
|
markers.forEach(marker => {
|
|
148
174
|
let content = collectContentBetweenMarkers(addonLines, marker.lineIndex);
|
|
175
|
+
|
|
149
176
|
if (content.length === 0) return;
|
|
150
177
|
|
|
151
178
|
let lineCount = content.length;
|
|
152
179
|
|
|
153
180
|
if (isEnv) {
|
|
154
181
|
const processed = processEnvContent(content, newContent);
|
|
182
|
+
|
|
155
183
|
content = processed.content;
|
|
156
184
|
lineCount = processed.count;
|
|
157
185
|
|
|
@@ -171,10 +199,13 @@ const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
|
|
|
171
199
|
|
|
172
200
|
if (marker.type === 'prepend') {
|
|
173
201
|
newContent = content.join('\n') + '\n' + newContent;
|
|
202
|
+
|
|
174
203
|
operations.push({success: true, type: 'prepend', lines: lineCount});
|
|
175
204
|
} else if (marker.type === 'append') {
|
|
176
205
|
if (!newContent.endsWith('\n')) newContent += '\n';
|
|
206
|
+
|
|
177
207
|
newContent += '\n' + content.join('\n') + '\n';
|
|
208
|
+
|
|
178
209
|
operations.push({success: true, type: 'append', lines: lineCount});
|
|
179
210
|
} else if ((marker.type === 'after' || marker.type === 'before') && marker.searchText) {
|
|
180
211
|
const targetLines = newContent.split('\n');
|
|
@@ -186,74 +217,107 @@ const mergeFile = (targetPath, addonContent, markers, isEnv = false) => {
|
|
|
186
217
|
}
|
|
187
218
|
|
|
188
219
|
targetLines.splice(insertIndex, 0, ...content);
|
|
220
|
+
|
|
189
221
|
newContent = targetLines.join('\n');
|
|
222
|
+
|
|
190
223
|
operations.push({success: true, type: marker.type, lines: lineCount, searchText: marker.searchText});
|
|
191
224
|
}
|
|
192
225
|
});
|
|
193
226
|
|
|
194
227
|
if (newContent !== targetContent) fs.writeFileSync(targetPath, newContent, 'utf8');
|
|
228
|
+
|
|
195
229
|
return {modified: newContent !== targetContent, operations};
|
|
196
230
|
};
|
|
197
231
|
|
|
198
232
|
const printMergeResults = (relativePath, isEnv, result) => {
|
|
199
233
|
const indent = ' ';
|
|
200
234
|
const varText = isEnv ? 'environment variable' : 'line';
|
|
235
|
+
|
|
201
236
|
let hasChanges = false;
|
|
202
237
|
|
|
203
238
|
result.operations.forEach(op => {
|
|
204
239
|
if (op.success) {
|
|
205
240
|
hasChanges = true;
|
|
206
241
|
|
|
207
|
-
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`);
|
|
208
|
-
|
|
242
|
+
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`);
|
|
243
|
+
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`);
|
|
244
|
+
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}"`);
|
|
245
|
+
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}"`);
|
|
246
|
+
} 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}"`);
|
|
247
|
+
else log(`${indent}${COLORS.gray}○${COLORS.reset} ${COLORS.dim}Content already exists (${op.type})${COLORS.reset}`);
|
|
209
248
|
});
|
|
210
249
|
|
|
211
250
|
return hasChanges;
|
|
212
251
|
};
|
|
213
252
|
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
let files;
|
|
253
|
+
const extractZip = async zipPath => {
|
|
254
|
+
const tempExtract = path.join(process.cwd(), '__temp_extract_addon__');
|
|
217
255
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
256
|
+
if (process.platform === 'win32') {
|
|
257
|
+
await execAsync(`powershell -command "Expand-Archive -Path '${zipPath}' -DestinationPath '${tempExtract}' -Force"`);
|
|
258
|
+
} else {
|
|
259
|
+
await execAsync(`unzip -q "${zipPath}" -d "${tempExtract}"`);
|
|
222
260
|
}
|
|
223
261
|
|
|
262
|
+
const entries = fs.readdirSync(tempExtract, {withFileTypes: true});
|
|
263
|
+
const sourceDir = entries.length === 1 && entries[0].isDirectory() ? path.join(tempExtract, entries[0].name) : tempExtract;
|
|
264
|
+
|
|
265
|
+
return {sourceDir, tempExtract};
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const processAddonFiles = (addonDir, targetDir) => {
|
|
224
269
|
const copied = [], skipped = [], toMerge = [];
|
|
225
270
|
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
if (file.name === 'README.md') continue;
|
|
271
|
+
const processDirectory = (dir, basePath = '') => fs.readdirSync(dir, {withFileTypes: true}).forEach(entry => {
|
|
272
|
+
if (entry.name === 'README.md') return;
|
|
229
273
|
|
|
230
|
-
|
|
231
|
-
|
|
274
|
+
const srcPath = path.join(dir, entry.name);
|
|
275
|
+
const relativePath = path.join(basePath, entry.name).replace(/\\/g, '/');
|
|
276
|
+
const destPath = path.join(targetDir, relativePath);
|
|
232
277
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
await processFiles(subFiles, relativePath);
|
|
237
|
-
} else {
|
|
238
|
-
const content = await fetchUrl(`${RAW_BASE}/${addonName}/${relativePath}`);
|
|
278
|
+
if (entry.isDirectory()) processDirectory(srcPath, relativePath);
|
|
279
|
+
else {
|
|
280
|
+
const content = fs.readFileSync(srcPath, 'utf8');
|
|
239
281
|
|
|
240
|
-
|
|
241
|
-
|
|
282
|
+
if (fs.existsSync(destPath)) {
|
|
283
|
+
const markers = extractMarkers(content);
|
|
284
|
+
|
|
285
|
+
if (markers.length > 0 || entry.name === '.env') toMerge.push({content, destPath, relativePath, markers});
|
|
286
|
+
else skipped.push(relativePath);
|
|
287
|
+
} else {
|
|
288
|
+
fs.mkdirSync(path.dirname(destPath), {recursive: true});
|
|
289
|
+
fs.copyFileSync(srcPath, destPath);
|
|
242
290
|
|
|
243
|
-
|
|
244
|
-
} else {
|
|
245
|
-
fs.mkdirSync(path.dirname(destPath), {recursive: true});
|
|
246
|
-
fs.writeFileSync(destPath, content, 'utf8');
|
|
247
|
-
copied.push(relativePath);
|
|
248
|
-
}
|
|
291
|
+
copied.push(relativePath);
|
|
249
292
|
}
|
|
250
293
|
}
|
|
251
|
-
};
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
processDirectory(addonDir);
|
|
252
297
|
|
|
253
|
-
await processFiles(files);
|
|
254
298
|
return {copied, skipped, toMerge};
|
|
255
299
|
};
|
|
256
300
|
|
|
301
|
+
const downloadAddon = async (addonName, version, targetDir) => {
|
|
302
|
+
const zipUrl = `${CDN_BASE}/${version}/add-ons/${addonName}.zip`;
|
|
303
|
+
const tempZip = path.join(process.cwd(), `temp-addon-${addonName}.zip`);
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
await downloadFile(zipUrl, tempZip);
|
|
307
|
+
|
|
308
|
+
const {sourceDir, tempExtract} = await extractZip(tempZip);
|
|
309
|
+
const result = processAddonFiles(sourceDir, targetDir);
|
|
310
|
+
|
|
311
|
+
fs.unlinkSync(tempZip);
|
|
312
|
+
fs.rmSync(tempExtract, {recursive: true, force: true});
|
|
313
|
+
|
|
314
|
+
return result;
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (fs.existsSync(tempZip)) fs.unlinkSync(tempZip);
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
257
321
|
const mergeFiles = (toMerge) => {
|
|
258
322
|
if (toMerge.length === 0) return {merged: [], failed: [], unchanged: []};
|
|
259
323
|
|
|
@@ -261,13 +325,17 @@ const mergeFiles = (toMerge) => {
|
|
|
261
325
|
|
|
262
326
|
toMerge.forEach(({content, destPath, relativePath, markers}) => {
|
|
263
327
|
const isEnv = path.basename(destPath) === '.env';
|
|
264
|
-
|
|
328
|
+
|
|
329
|
+
log(`\n ${COLORS.cyan}•${COLORS.reset} ${COLORS.dim}${relativePath}${COLORS.reset}`);
|
|
265
330
|
|
|
266
331
|
try {
|
|
267
332
|
const result = mergeFile(destPath, content, markers, isEnv);
|
|
268
|
-
|
|
333
|
+
|
|
334
|
+
if (printMergeResults(relativePath, isEnv, result)) merged.push(relativePath);
|
|
335
|
+
else unchanged.push(relativePath);
|
|
269
336
|
} catch (error) {
|
|
270
337
|
log(` ${COLORS.red}✗ Error:${COLORS.reset} ${error.message}`, 'red');
|
|
338
|
+
|
|
271
339
|
failed.push(relativePath);
|
|
272
340
|
}
|
|
273
341
|
});
|
|
@@ -276,34 +344,42 @@ const mergeFiles = (toMerge) => {
|
|
|
276
344
|
};
|
|
277
345
|
|
|
278
346
|
const main = async () => {
|
|
279
|
-
const
|
|
347
|
+
const args = process.argv.slice(2);
|
|
348
|
+
const firstArg = args[0];
|
|
280
349
|
|
|
281
|
-
if (!
|
|
350
|
+
if (!firstArg || firstArg === '--help' || firstArg === '-h') {
|
|
282
351
|
showHelp();
|
|
352
|
+
|
|
283
353
|
process.exit(0);
|
|
284
354
|
}
|
|
285
355
|
|
|
286
|
-
if (
|
|
287
|
-
|
|
356
|
+
if (firstArg === '--list' || firstArg === '-l') {
|
|
357
|
+
const version = args[1] || 'latest';
|
|
358
|
+
await listAddons(version);
|
|
359
|
+
|
|
288
360
|
process.exit(0);
|
|
289
361
|
}
|
|
290
362
|
|
|
363
|
+
const addonName = firstArg;
|
|
364
|
+
const version = args[1] || 'latest';
|
|
365
|
+
|
|
291
366
|
console.log();
|
|
292
367
|
log(` ╭${'─'.repeat(62)}╮`);
|
|
293
|
-
log(` │ ${COLORS.bold}Installing
|
|
368
|
+
log(` │ ${COLORS.bold}Installing Add-on: ${COLORS.cyan}${addonName}${COLORS.reset} ${COLORS.dim}(${version})${COLORS.reset}${' '.repeat(37 - addonName.length - version.length)}│`);
|
|
294
369
|
log(` ╰${'─'.repeat(62)}╯`);
|
|
295
370
|
console.log();
|
|
296
|
-
log(' 📦 Downloading add-on
|
|
371
|
+
log(' 📦 Downloading add-on...', 'bold');
|
|
297
372
|
|
|
298
373
|
let copied, skipped, toMerge;
|
|
299
374
|
|
|
300
375
|
try {
|
|
301
|
-
({copied, skipped, toMerge} = await
|
|
376
|
+
({copied, skipped, toMerge} = await downloadAddon(addonName, version, process.cwd()));
|
|
302
377
|
} catch (error) {
|
|
303
378
|
console.log();
|
|
304
379
|
log(` ${COLORS.red}✗${COLORS.reset} ${error.message}`, 'red');
|
|
305
|
-
log(` ${COLORS.dim}Run ${COLORS.
|
|
380
|
+
log(` ${COLORS.dim}Run ${COLORS.dim}npx @ijuantm/simpl-addon --list${COLORS.reset} to see available add-ons`);
|
|
306
381
|
console.log();
|
|
382
|
+
|
|
307
383
|
process.exit(1);
|
|
308
384
|
}
|
|
309
385
|
|
|
@@ -315,6 +391,7 @@ const main = async () => {
|
|
|
315
391
|
if (skipped.length > 0) {
|
|
316
392
|
console.log();
|
|
317
393
|
log(` ${COLORS.gray}○${COLORS.reset} ${COLORS.dim}Skipped ${skipped.length} file${skipped.length !== 1 ? 's' : ''} (no merge markers):${COLORS.reset}`);
|
|
394
|
+
|
|
318
395
|
skipped.forEach(file => log(` ${COLORS.dim}• ${file}${COLORS.reset}`));
|
|
319
396
|
}
|
|
320
397
|
|
|
@@ -334,6 +411,7 @@ const main = async () => {
|
|
|
334
411
|
console.log();
|
|
335
412
|
log(` ${COLORS.yellow}⚠${COLORS.reset} ${COLORS.yellow}${failed.length} file${failed.length !== 1 ? 's' : ''} failed to merge${COLORS.reset}`);
|
|
336
413
|
log(` ${COLORS.yellow}Please review manually:${COLORS.reset}`);
|
|
414
|
+
|
|
337
415
|
failed.forEach(file => log(` ${COLORS.cyan}• ${file}${COLORS.reset}`));
|
|
338
416
|
}
|
|
339
417
|
}
|
|
@@ -345,5 +423,6 @@ const main = async () => {
|
|
|
345
423
|
|
|
346
424
|
main().catch(err => {
|
|
347
425
|
log(`\n ${COLORS.red}✗${COLORS.reset} Fatal error: ${err.message}\n`, 'red');
|
|
426
|
+
|
|
348
427
|
process.exit(1);
|
|
349
428
|
});
|