slicejs-cli 3.6.1 → 3.6.3
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.
|
@@ -19,12 +19,12 @@ const COMPONENTS_REGISTRY_URL = 'https://raw.githubusercontent.com/VKneider/slic
|
|
|
19
19
|
*/
|
|
20
20
|
const loadConfig = () => sharedLoadConfig(import.meta.url);
|
|
21
21
|
|
|
22
|
-
const fetchWithRetry = async (url, retries = 3, baseDelay = 500) => {
|
|
22
|
+
const fetchWithRetry = async (url, retries = 3, baseDelay = 500, binary = false) => {
|
|
23
23
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
24
24
|
try {
|
|
25
25
|
const response = await fetch(url);
|
|
26
26
|
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
27
|
-
return await response.text();
|
|
27
|
+
return binary ? await response.arrayBuffer() : await response.text();
|
|
28
28
|
} catch (e) {
|
|
29
29
|
if (attempt === retries) throw e;
|
|
30
30
|
const delay = baseDelay * Math.pow(2, attempt);
|
|
@@ -185,6 +185,9 @@ filterOfficialComponents(allComponents) {
|
|
|
185
185
|
// Logical routing components only need JS
|
|
186
186
|
if (['Route', 'MultiRoute', 'Link'].includes(name)) {
|
|
187
187
|
files = [`${name}.js`];
|
|
188
|
+
} else if (name === 'Icon') {
|
|
189
|
+
// Icon component needs font files to render glyphs
|
|
190
|
+
files = [`${name}.js`, `${name}.html`, `${name}.css`, 'slc.eot', 'slc.woff2', 'slc.woff', 'slc.ttf', 'slc.svg'];
|
|
188
191
|
} else {
|
|
189
192
|
// Normal visual components need JS, HTML, CSS
|
|
190
193
|
files = [`${name}.js`, `${name}.html`, `${name}.css`];
|
|
@@ -218,12 +221,18 @@ filterOfficialComponents(allComponents) {
|
|
|
218
221
|
const total = component.files.length;
|
|
219
222
|
let done = 0;
|
|
220
223
|
const spinner = ora(`Downloading ${componentName} 0/${total}`).start();
|
|
224
|
+
const BINARY_EXTENSIONS = ['.eot', '.woff2', '.woff', '.ttf'];
|
|
221
225
|
const worker = async (fileName) => {
|
|
222
226
|
const url = `${DOCS_REPO_BASE_URL}/${category}/${componentName}/${fileName}`;
|
|
223
227
|
const localPath = path.join(targetPath, fileName);
|
|
228
|
+
const isBinary = BINARY_EXTENSIONS.some(ext => fileName.endsWith(ext));
|
|
224
229
|
try {
|
|
225
|
-
const content = await fetchWithRetry(url);
|
|
226
|
-
|
|
230
|
+
const content = await fetchWithRetry(url, 3, 500, isBinary);
|
|
231
|
+
if (isBinary) {
|
|
232
|
+
await fs.writeFile(localPath, Buffer.from(content));
|
|
233
|
+
} else {
|
|
234
|
+
await fs.writeFile(localPath, content, 'utf8');
|
|
235
|
+
}
|
|
227
236
|
downloadedFiles.push(fileName);
|
|
228
237
|
} catch (error) {
|
|
229
238
|
Print.downloadError(fileName);
|
package/commands/init/init.js
CHANGED
|
@@ -32,6 +32,16 @@ async function fetchLatestVersion(packageName) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function getRunningCliVersion() {
|
|
36
|
+
try {
|
|
37
|
+
const cliRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
38
|
+
const cliPkg = fs.readJsonSync(path.join(cliRoot, 'package.json'));
|
|
39
|
+
return typeof cliPkg.version === 'string' ? cliPkg.version : null;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
async function ensurePnpmAllowBuilds(projectRoot) {
|
|
36
46
|
const workspacePath = path.join(projectRoot, 'pnpm-workspace.yaml');
|
|
37
47
|
const allowBuildLine = ' slicejs-cli: true';
|
|
@@ -152,6 +162,9 @@ export default async function initializeProject(options = {}) {
|
|
|
152
162
|
let sliceBaseDir;
|
|
153
163
|
try {
|
|
154
164
|
latestVersion = await fetchLatestVersion('slicejs-web-framework');
|
|
165
|
+
const frameworkPackage = latestVersion
|
|
166
|
+
? `slicejs-web-framework@${latestVersion}`
|
|
167
|
+
: 'slicejs-web-framework';
|
|
155
168
|
const installedPkgPath = getPath(import.meta.url, 'node_modules', 'slicejs-web-framework', 'package.json');
|
|
156
169
|
let installed = null;
|
|
157
170
|
if (await fs.pathExists(installedPkgPath)) {
|
|
@@ -159,11 +172,7 @@ export default async function initializeProject(options = {}) {
|
|
|
159
172
|
installed = pkg.version;
|
|
160
173
|
}
|
|
161
174
|
if (!installed || (latestVersion && installed !== latestVersion)) {
|
|
162
|
-
|
|
163
|
-
// resolves it under its own policies (e.g. pnpm minimumReleaseAge
|
|
164
|
-
// quarantines versions younger than the configured age — pinning
|
|
165
|
-
// the registry's freshest version would make resolution fail).
|
|
166
|
-
execSync(installCommand(packageManager, 'slicejs-web-framework'), { cwd: projectRoot, stdio: 'inherit' });
|
|
175
|
+
execSync(installCommand(packageManager, frameworkPackage), { cwd: projectRoot, stdio: 'inherit' });
|
|
167
176
|
}
|
|
168
177
|
if (await fs.pathExists(installedPkgPath)) {
|
|
169
178
|
const pkg = await fs.readJson(installedPkgPath);
|
|
@@ -194,13 +203,24 @@ export default async function initializeProject(options = {}) {
|
|
|
194
203
|
const cliSpinner = ora('Installing slicejs-cli as devDependency...').start();
|
|
195
204
|
try {
|
|
196
205
|
const cliPkgPath = getPath(import.meta.url, 'node_modules', 'slicejs-cli', 'package.json');
|
|
197
|
-
|
|
198
|
-
|
|
206
|
+
const currentCliVersion = getRunningCliVersion();
|
|
207
|
+
const cliPackage = currentCliVersion
|
|
208
|
+
? `slicejs-cli@${currentCliVersion}`
|
|
209
|
+
: 'slicejs-cli';
|
|
210
|
+
|
|
211
|
+
let installedCliVersion = null;
|
|
212
|
+
if (await fs.pathExists(cliPkgPath)) {
|
|
213
|
+
const pkg = await fs.readJson(cliPkgPath);
|
|
214
|
+
installedCliVersion = pkg.version;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (!installedCliVersion || (currentCliVersion && installedCliVersion !== currentCliVersion)) {
|
|
218
|
+
execSync(installCommand(packageManager, cliPackage, { dev: true }), { cwd: projectRoot, stdio: 'inherit' });
|
|
199
219
|
}
|
|
200
220
|
cliSpinner.succeed('slicejs-cli installed locally');
|
|
201
221
|
} catch (err) {
|
|
202
222
|
cliSpinner.warn('Could not install slicejs-cli locally — scripts will use the global CLI');
|
|
203
|
-
Print.info(`You can add it later with: ${installCommand(packageManager,
|
|
223
|
+
Print.info(`You can add it later with: ${installCommand(packageManager, `slicejs-cli@${getRunningCliVersion() || 'latest'}`, { dev: true })}`);
|
|
204
224
|
}
|
|
205
225
|
|
|
206
226
|
// These derive from sliceBaseDir (which comes from npm install or fallback),
|
|
@@ -280,6 +300,7 @@ export default async function initializeProject(options = {}) {
|
|
|
280
300
|
}
|
|
281
301
|
|
|
282
302
|
// 3. DOWNLOAD ALL VISUAL COMPONENTS FROM OFFICIAL REPOSITORY
|
|
303
|
+
let visualResults = [];
|
|
283
304
|
const componentsSpinner = ora('Loading component registry...').start();
|
|
284
305
|
try {
|
|
285
306
|
const registry = new ComponentRegistry();
|
|
@@ -287,25 +308,23 @@ export default async function initializeProject(options = {}) {
|
|
|
287
308
|
|
|
288
309
|
// Install only the Visual components the starter project uses.
|
|
289
310
|
const allVisualComponents = STARTER_VISUAL_COMPONENTS;
|
|
290
|
-
Print.info(`Installing ${allVisualComponents.length} starter Visual components: ${allVisualComponents.join(', ')}`);
|
|
291
311
|
|
|
292
312
|
if (allVisualComponents.length > 0) {
|
|
293
313
|
componentsSpinner.text = `Installing ${allVisualComponents.length} starter Visual components...`;
|
|
294
314
|
|
|
295
|
-
|
|
315
|
+
visualResults = await registry.installMultipleComponents(
|
|
296
316
|
allVisualComponents,
|
|
297
317
|
'Visual',
|
|
298
318
|
true // force = true for initial installation
|
|
299
319
|
);
|
|
300
320
|
|
|
301
|
-
const successful =
|
|
302
|
-
const failed =
|
|
321
|
+
const successful = visualResults.filter(r => r.success).length;
|
|
322
|
+
const failed = visualResults.filter(r => !r.success).length;
|
|
303
323
|
|
|
304
324
|
if (successful > 0 && failed === 0) {
|
|
305
325
|
componentsSpinner.succeed(`All ${successful} Visual components installed successfully`);
|
|
306
326
|
} else if (successful > 0) {
|
|
307
327
|
componentsSpinner.warn(`${successful} components installed, ${failed} failed`);
|
|
308
|
-
Print.info(`You can install failed components later using "${packageManager} run get -- <component-name>"`);
|
|
309
328
|
} else {
|
|
310
329
|
componentsSpinner.fail('Failed to install Visual components');
|
|
311
330
|
}
|
|
@@ -322,29 +341,28 @@ export default async function initializeProject(options = {}) {
|
|
|
322
341
|
}
|
|
323
342
|
|
|
324
343
|
// 3b. DOWNLOAD STARTER SERVICE COMPONENTS FROM OFFICIAL REPOSITORY
|
|
344
|
+
let serviceResults = [];
|
|
325
345
|
const serviceSpinner = ora('Installing starter Service components...').start();
|
|
326
346
|
try {
|
|
327
347
|
const registry = new ComponentRegistry();
|
|
328
348
|
await registry.loadRegistry();
|
|
329
349
|
|
|
330
350
|
if (STARTER_SERVICE_COMPONENTS.length > 0) {
|
|
331
|
-
Print.info(`Installing ${STARTER_SERVICE_COMPONENTS.length} starter Service components: ${STARTER_SERVICE_COMPONENTS.join(', ')}`);
|
|
332
351
|
serviceSpinner.text = `Installing ${STARTER_SERVICE_COMPONENTS.length} starter Service components...`;
|
|
333
352
|
|
|
334
|
-
|
|
353
|
+
serviceResults = await registry.installMultipleComponents(
|
|
335
354
|
STARTER_SERVICE_COMPONENTS,
|
|
336
355
|
'Service',
|
|
337
356
|
true // force = true for initial installation
|
|
338
357
|
);
|
|
339
358
|
|
|
340
|
-
const successful =
|
|
341
|
-
const failed =
|
|
359
|
+
const successful = serviceResults.filter(r => r.success).length;
|
|
360
|
+
const failed = serviceResults.filter(r => !r.success).length;
|
|
342
361
|
|
|
343
362
|
if (successful > 0 && failed === 0) {
|
|
344
363
|
serviceSpinner.succeed(`All ${successful} Service components installed successfully`);
|
|
345
364
|
} else if (successful > 0) {
|
|
346
365
|
serviceSpinner.warn(`${successful} Service components installed, ${failed} failed`);
|
|
347
|
-
Print.info(`You can install failed components later using "${packageManager} run get -- <component-name>"`);
|
|
348
366
|
} else {
|
|
349
367
|
serviceSpinner.fail('Failed to install Service components');
|
|
350
368
|
}
|
|
@@ -422,6 +440,12 @@ export default async function initializeProject(options = {}) {
|
|
|
422
440
|
}
|
|
423
441
|
|
|
424
442
|
const projectName = path.basename(process.cwd());
|
|
443
|
+
|
|
444
|
+
const failedCount = [...visualResults, ...serviceResults].filter(r => !r.success).length;
|
|
445
|
+
if (failedCount > 0) {
|
|
446
|
+
Print.warning(`${failedCount} component(s) failed to install — run "${packageManager} run get -- <name>" to retry`);
|
|
447
|
+
}
|
|
448
|
+
|
|
425
449
|
Print.success(`Project initialized successfully in "${projectName}/"`);
|
|
426
450
|
Print.newLine();
|
|
427
451
|
Print.title('Next steps:');
|
|
@@ -11,7 +11,7 @@ export default function setupWatcher(serverProcess, onRestart) {
|
|
|
11
11
|
Print.info('Watch mode enabled - monitoring file changes...');
|
|
12
12
|
Print.newLine();
|
|
13
13
|
|
|
14
|
-
const watcher = chokidar.watch(['
|
|
14
|
+
const watcher = chokidar.watch(['api/**/*'], {
|
|
15
15
|
ignored: [
|
|
16
16
|
/(^|[\/\\])\../, // hidden files
|
|
17
17
|
'**/node_modules/**',
|
|
@@ -29,33 +29,20 @@ export default function setupWatcher(serverProcess, onRestart) {
|
|
|
29
29
|
|
|
30
30
|
let reloadTimeout;
|
|
31
31
|
|
|
32
|
+
const handleChange = (changedPath) => {
|
|
33
|
+
clearTimeout(reloadTimeout);
|
|
34
|
+
reloadTimeout = setTimeout(() => {
|
|
35
|
+
if (onRestart) {
|
|
36
|
+
console.log(chalk.yellow('🔄 API change detected, restarting server...'));
|
|
37
|
+
onRestart(changedPath);
|
|
38
|
+
}
|
|
39
|
+
}, 500);
|
|
40
|
+
};
|
|
41
|
+
|
|
32
42
|
watcher
|
|
33
|
-
.on('change',
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
reloadTimeout = setTimeout(() => {
|
|
37
|
-
if(onRestart) {
|
|
38
|
-
console.log(chalk.yellow('🔄 Changes detected, restarting server...'));
|
|
39
|
-
onRestart(path);
|
|
40
|
-
} else {
|
|
41
|
-
console.log(chalk.yellow('🔄 Changes detected, server will reload automatically... (No handler)'));
|
|
42
|
-
}
|
|
43
|
-
}, 500);
|
|
44
|
-
})
|
|
45
|
-
.on('add', (path) => {
|
|
46
|
-
// console.log(chalk.green(`➕ New file added: ${path}`));
|
|
47
|
-
clearTimeout(reloadTimeout);
|
|
48
|
-
reloadTimeout = setTimeout(() => {
|
|
49
|
-
if (onRestart) onRestart(path);
|
|
50
|
-
}, 500);
|
|
51
|
-
})
|
|
52
|
-
.on('unlink', (path) => {
|
|
53
|
-
// console.log(chalk.red(`➖ File removed: ${path}`));
|
|
54
|
-
clearTimeout(reloadTimeout);
|
|
55
|
-
reloadTimeout = setTimeout(() => {
|
|
56
|
-
if (onRestart) onRestart(path);
|
|
57
|
-
}, 500);
|
|
58
|
-
})
|
|
43
|
+
.on('change', handleChange)
|
|
44
|
+
.on('add', handleChange)
|
|
45
|
+
.on('unlink', handleChange)
|
|
59
46
|
.on('error', (error) => {
|
|
60
47
|
Print.error(`Watcher error: ${error.message}`);
|
|
61
48
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slicejs-cli",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.3",
|
|
4
4
|
"description": "Command client for developing web applications with Slice.js framework",
|
|
5
5
|
"main": "client.js",
|
|
6
6
|
"bin": {
|
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
"slice:sync": "slice sync",
|
|
35
35
|
"slice:version": "slice version",
|
|
36
36
|
"slice:update": "slice update",
|
|
37
|
-
"slice:types": "slice types generate"
|
|
37
|
+
"slice:types": "slice types generate",
|
|
38
|
+
"slice:doctor": "node ./node_modules/slicejs-cli/client.js doctor",
|
|
39
|
+
"slice:help": "node ./node_modules/slicejs-cli/client.js --help"
|
|
38
40
|
},
|
|
39
41
|
"keywords": [
|
|
40
42
|
"framework",
|
|
@@ -69,4 +71,4 @@
|
|
|
69
71
|
"devDependencies": {
|
|
70
72
|
"@playwright/test": "^1.60.0"
|
|
71
73
|
}
|
|
72
|
-
}
|
|
74
|
+
}
|