slicejs-cli 2.7.8 → 2.8.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/README.md CHANGED
@@ -17,13 +17,37 @@
17
17
 
18
18
  ## Installation
19
19
 
20
- - Local (recommended):
20
+ ### Local (Recommended)
21
+
22
+ 1. Install as a development dependency:
21
23
 
22
24
  ```bash
23
25
  npm install slicejs-cli --save-dev
24
26
  ```
25
27
 
26
- - Global:
28
+ 2. Add to your `package.json` scripts:
29
+
30
+ ```json
31
+ {
32
+ "scripts": {
33
+ "dev": "slice dev",
34
+ "build": "slice build",
35
+ "slice": "slice"
36
+ }
37
+ }
38
+ ```
39
+
40
+ 3. usage:
41
+
42
+ ```bash
43
+ npm run dev
44
+ # or pass arguments
45
+ npm run slice -- get Button
46
+ ```
47
+
48
+ ### Global (Not Recommended)
49
+
50
+ Global installations can lead to version mismatches and "works on my machine" issues.
27
51
 
28
52
  ```bash
29
53
  npm install -g slicejs-cli
package/client.js CHANGED
@@ -132,6 +132,33 @@ sliceClient
132
132
  await versionChecker.showVersionInfo();
133
133
  });
134
134
 
135
+ // BUNDLE COMMAND
136
+ const bundleCommand = sliceClient.command("bundle")
137
+ .description("Build component bundles for production")
138
+ .action(async (options) => {
139
+ await runWithVersionCheck(async () => {
140
+ await bundle(options);
141
+ });
142
+ });
143
+
144
+ bundleCommand
145
+ .command("clean")
146
+ .description("Remove all generated bundles")
147
+ .action(async () => {
148
+ await cleanBundles();
149
+ });
150
+
151
+ bundleCommand
152
+ .command("info")
153
+ .description("Show information about generated bundles")
154
+ .action(async () => {
155
+ await bundleInfo();
156
+ });
157
+
158
+ bundleCommand
159
+ .option("-a, --analyze", "Analyze project dependencies without bundling")
160
+ .option("-v, --verbose", "Show detailed output");
161
+
135
162
  // DEV COMMAND (DEVELOPMENT) - COMANDO PRINCIPAL
136
163
  sliceClient
137
164
  .command("dev")
@@ -453,25 +480,6 @@ sliceClient
453
480
  subcommandTerm: (cmd) => cmd.name() + ' ' + cmd.usage()
454
481
  });
455
482
 
456
- sliceClient
457
- .command('bundle')
458
- .description('Generate production bundles for optimal loading')
459
- .option('-a, --analyze', 'Only analyze without generating bundles')
460
- .option('-v, --verbose', 'Show detailed information')
461
- .action(bundle);
462
-
463
- // Subcomando: limpiar bundles
464
- sliceClient
465
- .command('bundle:clean')
466
- .description('Remove all generated bundles')
467
- .action(cleanBundles);
468
-
469
- // Subcomando: información
470
- sliceClient
471
- .command('bundle:info')
472
- .description('Show information about generated bundles')
473
- .action(bundleInfo);
474
-
475
483
 
476
484
  // Custom help - SIMPLIFICADO para development only
477
485
  sliceClient.addHelpText('after', `
@@ -1,5 +1,6 @@
1
1
  // commands/startServer/startServer.js - MEJORADO CON VALIDACIÓN Y FEEDBACK
2
2
 
3
+ import bundle from '../bundle/bundle.js';
3
4
  import fs from 'fs-extra';
4
5
  import path from 'path';
5
6
  import { fileURLToPath } from 'url';
@@ -184,19 +185,17 @@ export default async function startServer(options = {}) {
184
185
  throw new Error('Project structure not found. Run "slice init" first.');
185
186
  }
186
187
 
187
- // Verificar disponibilidad del puerto
188
- Print.checkingPort(port);
189
- const portAvailable = await isPortAvailable(port);
190
-
191
- if (!portAvailable) {
192
- throw new Error(
193
- `Port ${port} is already in use. Please:\n` +
194
- ` 1. Stop the process using port ${port}, or\n` +
195
- ` 2. Use a different port: slice ${mode === 'development' ? 'dev' : mode === 'bundled' ? 'start --bundled' : 'start'} -p <port>`
196
- );
188
+ let actualPort = await isPortAvailable(port) ? port : port + 1; // Try one more port
189
+ if(actualPort !== port) {
190
+ // Check if the fallback is available
191
+ const fallbackAvailable = await isPortAvailable(actualPort);
192
+ if(!fallbackAvailable) {
193
+ throw new Error(`Ports ${port} and ${actualPort} are in use.`);
194
+ }
195
+ Print.info(`ℹ️ Port ${port} in use, using ${actualPort} instead.`);
197
196
  }
198
197
 
199
- Print.serverStatus('checking', 'Port available ✓');
198
+ Print.serverStatus('checking', `Port ${actualPort} available ✓`);
200
199
  Print.newLine();
201
200
 
202
201
  if (mode === 'production') {
@@ -214,12 +213,40 @@ export default async function startServer(options = {}) {
214
213
  Print.newLine();
215
214
 
216
215
  // Iniciar el servidor con argumentos
217
- const serverProcess = await startNodeServer(port, mode);
216
+ let serverProcess = await startNodeServer(actualPort, mode);
218
217
 
219
218
  // Configurar watch mode si está habilitado
220
219
  if (watch) {
221
220
  Print.newLine();
222
- const watcher = setupWatcher(serverProcess);
221
+ const watcher = setupWatcher(serverProcess, async (changedPath) => {
222
+ if (serverProcess) {
223
+ serverProcess.kill();
224
+ }
225
+
226
+ // Short delay to ensure port is freed
227
+ await new Promise(r => setTimeout(r, 500));
228
+
229
+ try {
230
+ // If we are in bundled mode, regenerate bundles before restarting
231
+ if (mode === 'bundled') {
232
+ Print.info('🔄 File changed. Regenerating bundles...');
233
+ try {
234
+ await bundle({ verbose: false });
235
+ } catch (err) {
236
+ Print.error('Bundle generation failed during watch restart');
237
+ console.error(err);
238
+ // We continue restarting anyway to show error in browser if possible,
239
+ // or maybe just to keep process alive.
240
+ }
241
+ } else {
242
+ Print.info('🔄 File changed. Restarting server...');
243
+ }
244
+
245
+ serverProcess = await startNodeServer(actualPort, mode);
246
+ } catch (e) {
247
+ Print.error(`Failed to restart server: ${e.message}`);
248
+ }
249
+ });
223
250
 
224
251
  // Cleanup en exit
225
252
  const cleanup = () => {
@@ -7,7 +7,7 @@ import Print from '../Print.js';
7
7
  * @param {ChildProcess} serverProcess - Proceso del servidor
8
8
  * @returns {FSWatcher} - Watcher de chokidar
9
9
  */
10
- export default function setupWatcher(serverProcess) {
10
+ export default function setupWatcher(serverProcess, onRestart) {
11
11
  Print.info('Watch mode enabled - monitoring file changes...');
12
12
  Print.newLine();
13
13
 
@@ -16,6 +16,7 @@ export default function setupWatcher(serverProcess) {
16
16
  /(^|[\/\\])\../, // archivos ocultos
17
17
  '**/node_modules/**',
18
18
  '**/dist/**',
19
+ '**/bundles/**',
19
20
  '**/*.log'
20
21
  ],
21
22
  persistent: true,
@@ -30,19 +31,30 @@ export default function setupWatcher(serverProcess) {
30
31
 
31
32
  watcher
32
33
  .on('change', (path) => {
33
- console.log(chalk.cyan(`📝 File changed: ${path}`));
34
-
35
34
  // Debounce para evitar múltiples reloads
36
35
  clearTimeout(reloadTimeout);
37
36
  reloadTimeout = setTimeout(() => {
38
- console.log(chalk.yellow('🔄 Changes detected, server will reload automatically...'));
39
- }, 200);
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);
40
44
  })
41
45
  .on('add', (path) => {
42
- console.log(chalk.green(`➕ New file added: ${path}`));
46
+ // console.log(chalk.green(`➕ New file added: ${path}`));
47
+ clearTimeout(reloadTimeout);
48
+ reloadTimeout = setTimeout(() => {
49
+ if (onRestart) onRestart(path);
50
+ }, 500);
43
51
  })
44
52
  .on('unlink', (path) => {
45
- console.log(chalk.red(`➖ File removed: ${path}`));
53
+ // console.log(chalk.red(`➖ File removed: ${path}`));
54
+ clearTimeout(reloadTimeout);
55
+ reloadTimeout = setTimeout(() => {
56
+ if (onRestart) onRestart(path);
57
+ }, 500);
46
58
  })
47
59
  .on('error', (error) => {
48
60
  Print.error(`Watcher error: ${error.message}`);
@@ -220,13 +220,66 @@ export default class BundleGenerator {
220
220
  assignHybridBundles(criticalNames) {
221
221
  const routeGroups = new Map();
222
222
 
223
- // Group routes by category
223
+ // First, handle MultiRoute groups
224
+ if (this.analysisData.routeGroups) {
225
+ for (const [groupKey, groupData] of this.analysisData.routeGroups) {
226
+ if (groupData.type === 'multiroute') {
227
+ // Create a bundle for this MultiRoute group
228
+ const allComponents = new Set();
229
+
230
+ // Add the main component (MultiRoute handler)
231
+ const mainComponent = this.analysisData.components.find(c => c.name === groupData.component);
232
+ if (mainComponent) {
233
+ allComponents.add(mainComponent);
234
+
235
+ // Add all components used by this MultiRoute
236
+ const routeComponents = this.getRouteComponents(mainComponent.name);
237
+ for (const comp of routeComponents) {
238
+ allComponents.add(comp);
239
+ // Add transitive dependencies
240
+ const dependencies = this.getComponentDependencies(comp);
241
+ for (const dep of dependencies) {
242
+ allComponents.add(dep);
243
+ }
244
+ }
245
+ }
246
+
247
+ // Filter those already in critical
248
+ const uniqueComponents = Array.from(allComponents).filter(comp =>
249
+ !criticalNames.has(comp.name)
250
+ );
251
+
252
+ if (uniqueComponents.length > 0) {
253
+ const totalSize = uniqueComponents.reduce((sum, c) => sum + c.size, 0);
254
+
255
+ this.bundles.routes[groupKey] = {
256
+ paths: groupData.routes,
257
+ components: uniqueComponents,
258
+ size: totalSize,
259
+ file: `slice-bundle.${groupKey}.js`
260
+ };
261
+
262
+ console.log(`✓ Bundle ${groupKey}: ${uniqueComponents.length} components, ${(totalSize / 1024).toFixed(1)} KB (${groupData.routes.length} routes)`);
263
+ }
264
+ }
265
+ }
266
+ }
267
+
268
+ // Group remaining routes by category (skip those already handled by MultiRoute)
224
269
  for (const route of this.analysisData.routes) {
225
- const category = this.categorizeRoute(route.path);
226
- if (!routeGroups.has(category)) {
227
- routeGroups.set(category, []);
270
+ // Check if this route is already handled by a MultiRoute group
271
+ const isHandledByMultiRoute = this.analysisData.routeGroups &&
272
+ Array.from(this.analysisData.routeGroups.values()).some(group =>
273
+ group.type === 'multiroute' && group.routes.includes(route.path)
274
+ );
275
+
276
+ if (!isHandledByMultiRoute) {
277
+ const category = this.categorizeRoute(route.path);
278
+ if (!routeGroups.has(category)) {
279
+ routeGroups.set(category, []);
280
+ }
281
+ routeGroups.get(category).push(route);
228
282
  }
229
- routeGroups.get(category).push(route);
230
283
  }
231
284
 
232
285
  // Create bundles for each group
@@ -268,9 +321,19 @@ export default class BundleGenerator {
268
321
  }
269
322
 
270
323
  /**
271
- * Categorizes a route path for grouping
324
+ * Categorizes a route path for grouping, considering MultiRoute context
272
325
  */
273
326
  categorizeRoute(routePath) {
327
+ // Check if this route belongs to a MultiRoute handler
328
+ if (this.analysisData.routeGroups) {
329
+ for (const [groupKey, groupData] of this.analysisData.routeGroups) {
330
+ if (groupData.type === 'multiroute' && groupData.routes.includes(routePath)) {
331
+ return groupKey; // Return the MultiRoute group key
332
+ }
333
+ }
334
+ }
335
+
336
+ // Default categorization
274
337
  const path = routePath.toLowerCase();
275
338
 
276
339
  if (path === '/' || path === '/home') return 'home';
@@ -461,7 +524,7 @@ export default class BundleGenerator {
461
524
  name: comp.name,
462
525
  category: comp.category,
463
526
  categoryType: comp.categoryType,
464
- js: this.cleanJavaScript(jsContent),
527
+ js: this.cleanJavaScript(jsContent, comp.name, comp.path),
465
528
  externalDependencies: dependencyContents, // Files imported with import statements
466
529
  componentDependencies: Array.from(comp.dependencies), // Other components this one depends on
467
530
  html: htmlContent,
@@ -484,16 +547,134 @@ export default class BundleGenerator {
484
547
  }
485
548
 
486
549
  /**
487
- * Cleans JavaScript code by removing imports/exports
550
+ * Cleans JavaScript code by processing imports and ensuring class is available globally
488
551
  */
489
- cleanJavaScript(code) {
490
- // Remove export default
491
- code = code.replace(/export\s+default\s+/g, '');
552
+ cleanJavaScript(code, componentName, componentPath) {
553
+ let newCode = code;
554
+
555
+ // 1. In-line local imports to support data files and helpers
556
+ // Matches: import [clause] from '[path]'
557
+ const importRegex = /import\s+([\w\s{},*]+)\s+from\s+['"`]([\.\/][^'"`]+)['"`];?/g;
558
+
559
+ newCode = newCode.replace(importRegex, (match, importClause, importPath) => {
560
+ try {
561
+ const absolutePath = path.resolve(componentPath, importPath);
562
+
563
+ // Try different extensions if file doesn't exist
564
+ let finalPath = absolutePath;
565
+ if (!fs.existsSync(finalPath)) {
566
+ if (fs.existsSync(finalPath + '.js')) finalPath += '.js';
567
+ else if (fs.existsSync(finalPath + '.json')) finalPath += '.json';
568
+ else {
569
+ console.warn(`BundleGenerator: Could not resolve ${importPath} in ${componentName}`);
570
+ return `/* Fail: File not found ${importPath} */`; // return comment so regular import stripper deletes it
571
+ }
572
+ }
573
+
574
+ let fileContent = fs.readFileSync(finalPath, 'utf-8');
575
+
576
+ // Parse Import Clause
577
+ let defaultImport = null;
578
+ let namedImports = [];
579
+
580
+ const cleanClause = importClause.trim();
581
+
582
+ if (cleanClause.includes('{')) {
583
+ // Has named imports
584
+ const parts = cleanClause.split('{');
585
+ const preBrace = parts[0].trim(); // "Default, " or ""
586
+ const braceContent = parts[1].replace('}', '').trim(); // "A, B as C"
587
+
588
+ if (preBrace) {
589
+ defaultImport = preBrace.replace(',', '').trim();
590
+ }
591
+
592
+ if (braceContent) {
593
+ namedImports = braceContent.split(',').map(i => {
594
+ const [name, alias] = i.split(/\s+as\s+/).map(s => s.trim());
595
+ return { name, alias: alias || name };
596
+ });
597
+ }
598
+ } else {
599
+ if (cleanClause.includes('*')) return match; // Skip namespace
600
+ defaultImport = cleanClause;
601
+ }
602
+
603
+ // Transform exported content to local variables
604
+
605
+ // STRATEGY:
606
+ // 1. Convert all 'const/let' to 'var' to allow redeclaration (resolves naming collisions)
607
+ // 2. Wrap exports
608
+
609
+ // Replace const/let with var globally to prevent "Identifier already declared" errors
610
+ // Use a sophisticated regex that handles:
611
+ // - Start of line or indentation
612
+ // - Previous statement termination (; or })
613
+ // - Optional export keyword
614
+ fileContent = fileContent.replace(/(^|[\s;}])(export\s+)?(const|let)(?=\s+)/gm, '$1$2var');
615
+
616
+ const defaultExportName = `__default_${path.basename(finalPath, path.extname(finalPath)).replace(/\W/g, '_')}_${Math.random().toString(36).substr(2, 5)}`;
617
+
618
+ // Handle export default
619
+ if (fileContent.includes('export default')) {
620
+ fileContent = fileContent.replace(/export\s+default\s+/g, `var ${defaultExportName} = `);
621
+ } else {
622
+ // If no export default, but we are importing default, try to find a variable with the same name
623
+ // This happens in legacy data files like: const data = ...; (no export default)
624
+ if (defaultImport) {
625
+ // Check if there is a variable matching the import name in the file content
626
+ const varRegex = new RegExp(`var\\s+${defaultImport}\\s*=`);
627
+ if (varRegex.test(fileContent)) {
628
+ // Found it, map it to our default export name for consistency
629
+ fileContent += `\nvar ${defaultExportName} = ${defaultImport};\n`;
630
+ }
631
+ }
632
+ }
633
+
634
+ // Handle named exports: remove 'export' keyword
635
+ fileContent = fileContent.replace(/^\s*export\s+/gm, '');
636
+ // Also clean up export { ... } statements
637
+ fileContent = fileContent.replace(/^\s*export\s*\{[^}]+\};?/gm, '');
638
+
639
+ let aliasCode = '';
640
+
641
+ // Map Default Import
642
+ if (defaultImport) {
643
+ // Use var for alias to be safe against existing variable
644
+ aliasCode += `if (typeof ${defaultImport} === 'undefined') { var ${defaultImport} = (typeof ${defaultExportName} !== 'undefined') ? ${defaultExportName} : undefined; }\n`;
645
+ }
646
+
647
+ // Map Named Imports with Aliases
648
+ namedImports.forEach(({ name, alias }) => {
649
+ if (name !== alias) {
650
+ aliasCode += `var ${alias} = ${name};\n`;
651
+ }
652
+ });
653
+
654
+ return `/* Inlined: ${importPath} */\n${fileContent}\n${aliasCode}`;
655
+ } catch (e) {
656
+ console.warn(`Failed to inline ${importPath} in ${componentName}: ${e.message}`);
657
+ return `/* Error inlining ${importPath}: ${e.message} */`;
658
+ }
659
+ });
660
+
661
+ // 2. Remove any remaining import statements (non-local or failed ones)
662
+ newCode = newCode.replace(/^.*import\s+.*from\s+['"`].*['"`];?.*$/gm, '');
663
+
664
+ // 3. Remove export default from the component itself
665
+ newCode = newCode.replace(/export\s+default\s+/g, '');
666
+
667
+ // 4. Make sure the class is available globally for bundle evaluation
668
+ if (newCode.includes('customElements.define')) {
669
+ newCode = newCode.replace(/customElements\.define\([^;]+\);?\s*$/, `window.${componentName} = ${componentName};\n$&`);
670
+ } else {
671
+ newCode += `\nwindow.${componentName} = ${componentName};`;
672
+ }
492
673
 
493
- // Remove imports (components will already be available)
494
- code = code.replace(/import\s+.*?from\s+['"].*?['"];?\s*/g, '');
674
+ // 5. Add return statement for bundle evaluation compatibility
675
+ newCode += `\nreturn ${componentName};`;
495
676
 
496
- return code;
677
+ return newCode;
497
678
  }
498
679
 
499
680
  /**
@@ -45,6 +45,7 @@ export default class DependencyAnalyzer {
45
45
  components: Array.from(this.components.values()),
46
46
  routes: Array.from(this.routes.values()),
47
47
  dependencyGraph: this.dependencyGraph,
48
+ routeGroups: this.routeGroups,
48
49
  metrics
49
50
  };
50
51
  }
@@ -153,8 +154,33 @@ export default class DependencyAnalyzer {
153
154
  CallExpression(path) {
154
155
  const { callee, arguments: args } = path.node;
155
156
 
156
- // slice.build('ComponentName', ...)
157
+ // slice.build('MultiRoute', { routes: [...] })
157
158
  if (
159
+ callee.type === 'MemberExpression' &&
160
+ callee.object.name === 'slice' &&
161
+ callee.property.name === 'build' &&
162
+ args[0]?.type === 'StringLiteral' &&
163
+ args[0].value === 'MultiRoute' &&
164
+ args[1]?.type === 'ObjectExpression'
165
+ ) {
166
+ // Add MultiRoute itself
167
+ dependencies.add('MultiRoute');
168
+
169
+ // Extract routes from MultiRoute props
170
+ const routesProp = args[1].properties.find(p => p.key?.name === 'routes');
171
+ if (routesProp?.value?.type === 'ArrayExpression') {
172
+ routesProp.value.elements.forEach(routeElement => {
173
+ if (routeElement.type === 'ObjectExpression') {
174
+ const componentProp = routeElement.properties.find(p => p.key?.name === 'component');
175
+ if (componentProp?.value?.type === 'StringLiteral') {
176
+ dependencies.add(componentProp.value.value);
177
+ }
178
+ }
179
+ });
180
+ }
181
+ }
182
+ // Regular slice.build() calls
183
+ else if (
158
184
  callee.type === 'MemberExpression' &&
159
185
  callee.object.name === 'slice' &&
160
186
  callee.property.name === 'build' &&
@@ -181,7 +207,7 @@ export default class DependencyAnalyzer {
181
207
  }
182
208
 
183
209
  /**
184
- * Analyzes the routes file
210
+ * Analyzes the routes file and detects route groups
185
211
  */
186
212
  async analyzeRoutes() {
187
213
  if (!await fs.pathExists(this.routesPath)) {
@@ -189,7 +215,7 @@ export default class DependencyAnalyzer {
189
215
  }
190
216
 
191
217
  const content = await fs.readFile(this.routesPath, 'utf-8');
192
-
218
+
193
219
  try {
194
220
  const ast = parse(content, {
195
221
  sourceType: 'module',
@@ -225,6 +251,10 @@ export default class DependencyAnalyzer {
225
251
  }
226
252
  }
227
253
  });
254
+
255
+ // Detect and store route groups based on MultiRoute usage
256
+ this.routeGroups = this.detectRouteGroups();
257
+
228
258
  } catch (error) {
229
259
  console.warn(`⚠️ Error parseando rutas: ${error.message}`);
230
260
  }
@@ -249,6 +279,40 @@ export default class DependencyAnalyzer {
249
279
  }
250
280
  }
251
281
 
282
+ /**
283
+ * Detects route groups based on MultiRoute usage
284
+ */
285
+ detectRouteGroups() {
286
+ const routeGroups = new Map();
287
+
288
+ for (const [componentName, component] of this.components) {
289
+ // Check if component uses MultiRoute
290
+ const hasMultiRoute = Array.from(component.dependencies).includes('MultiRoute');
291
+
292
+ if (hasMultiRoute) {
293
+ // Find all routes that point to this component
294
+ const relatedRoutes = Array.from(this.routes.values())
295
+ .filter(route => route.component === componentName);
296
+
297
+ if (relatedRoutes.length > 1) {
298
+ // Group these routes together
299
+ const groupKey = `multiroute-${componentName}`;
300
+ routeGroups.set(groupKey, {
301
+ component: componentName,
302
+ routes: relatedRoutes.map(r => r.path),
303
+ type: 'multiroute'
304
+ });
305
+
306
+ // Mark component as multiroute handler
307
+ component.isMultiRouteHandler = true;
308
+ component.multiRoutePaths = relatedRoutes.map(r => r.path);
309
+ }
310
+ }
311
+ }
312
+
313
+ return routeGroups;
314
+ }
315
+
252
316
  /**
253
317
  * Gets all dependencies of a component (recursive)
254
318
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slicejs-cli",
3
- "version": "2.7.8",
3
+ "version": "2.8.0",
4
4
  "description": "Command client for developing web applications with Slice.js framework",
5
5
  "main": "client.js",
6
6
  "bin": {
@@ -26,6 +26,7 @@
26
26
  ],
27
27
  "author": "vkneider",
28
28
  "type": "module",
29
+ "preferGlobal": false,
29
30
  "license": "ISC",
30
31
  "dependencies": {
31
32
  "chalk": "^5.6.2",
@@ -38,9 +39,7 @@
38
39
  "inquirer": "^12.4.2",
39
40
  "ora": "^8.2.0",
40
41
  "slicejs-web-framework": "latest",
41
- "terser": "^5.43.1"
42
- },
43
- "devDependencies": {
42
+ "terser": "^5.43.1",
44
43
  "@babel/parser": "^7.28.5",
45
44
  "@babel/traverse": "^7.28.5"
46
45
  }
package/post.js CHANGED
@@ -12,14 +12,14 @@ const targetRoot = initCwd || path.resolve(__dirname, '../../');
12
12
  const projectPackageJsonPath = path.join(targetRoot, 'package.json');
13
13
 
14
14
  if (isGlobal) {
15
- console.log('ℹ️ Global installation of slicejs-cli detected.');
16
- console.log(' Skipping scripts setup. Use the binary directly:');
17
- console.log(' slice dev');
18
- console.log(' slice get Button');
15
+ console.log('⚠️ Global installation of slicejs-cli detected.');
16
+ console.log(' We strongly recommend using a local installation to avoid version mismatches.');
17
+ console.log(' Uninstall global: npm uninstall -g slicejs-cli');
19
18
  process.exit(0);
20
19
  }
21
20
 
22
- console.log('ℹ️ Local installation of slicejs-cli detected.');
23
- console.log(' Skipping automatic scripts setup in postinstall.');
24
- console.log(' Use "slice init" to configure project scripts.');
21
+ console.log(' slicejs-cli installed successfully.');
22
+ console.log(' Add the CLI to your package.json scripts:');
23
+ console.log(' "dev": "slice dev"');
24
+ console.log(' Then run: npm run dev');
25
25
  process.exit(0);