slicejs-cli 3.3.0 → 3.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.
- package/AGENTS.md +247 -0
- package/LICENSE +21 -21
- package/client.js +663 -626
- package/commands/Print.js +163 -167
- package/commands/Validations.js +92 -103
- package/commands/build/build.js +40 -40
- package/commands/buildProduction/buildProduction.js +576 -579
- package/commands/bundle/bundle.js +234 -235
- package/commands/createComponent/VisualComponentTemplate.js +55 -55
- package/commands/createComponent/createComponent.js +124 -126
- package/commands/deleteComponent/deleteComponent.js +77 -77
- package/commands/doctor/doctor.js +366 -369
- package/commands/getComponent/getComponent.js +684 -747
- package/commands/init/init.js +269 -261
- package/commands/listComponents/listComponents.js +172 -175
- package/commands/startServer/startServer.js +261 -264
- package/commands/startServer/watchServer.js +79 -79
- package/commands/types/types.js +69 -27
- package/commands/utils/LocalCliDelegation.js +53 -53
- package/commands/utils/PathHelper.js +75 -68
- package/commands/utils/VersionChecker.js +167 -167
- package/commands/utils/bundling/BundleGenerator.js +2292 -2292
- package/commands/utils/bundling/DependencyAnalyzer.js +925 -933
- package/commands/utils/loadConfig.js +31 -0
- package/commands/utils/updateManager.js +452 -453
- package/docs/superpowers/specs/2026-05-10-pwa-generate-design.md +105 -105
- package/package.json +58 -46
- package/post.js +66 -65
- package/tests/bundle-generator.test.js +691 -708
- package/tests/bundle-v2-register-output.test.js +470 -470
- package/tests/client-launcher-contract.test.js +211 -211
- package/tests/client-update-flow-contract.test.js +272 -272
- package/tests/component-registry-parse.test.js +34 -0
- package/tests/dependency-analyzer.test.js +24 -24
- package/tests/fixtures/components.js +8 -0
- package/tests/fixtures/sliceConfig.json +74 -0
- package/tests/getcomponent.test.js +407 -0
- package/tests/helpers/setup.js +97 -0
- package/tests/init-command-contract.test.js +46 -0
- package/tests/local-cli-delegation.test.js +81 -79
- package/tests/path-helper.test.js +206 -0
- package/tests/types-breakage.test.js +491 -0
- package/tests/types-generator-errors.test.js +361 -0
- package/tests/types-generator.test.js +172 -184
- package/tests/update-manager-notifications.test.js +88 -88
- package/.github/workflows/docs-render-cicd.yml +0 -65
|
@@ -1,579 +1,576 @@
|
|
|
1
|
-
// commands/buildProduction/buildProduction.js -
|
|
2
|
-
|
|
3
|
-
import fs from 'fs-extra';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import {
|
|
6
|
-
import { minify
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Print.
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
await
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
} else
|
|
188
|
-
await fs.copy(srcFilePath, distFilePath);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
'
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
'
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
'
|
|
263
|
-
//
|
|
264
|
-
'
|
|
265
|
-
'
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
'
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
'
|
|
272
|
-
//
|
|
273
|
-
'
|
|
274
|
-
'
|
|
275
|
-
//
|
|
276
|
-
'
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
'
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
async
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
await
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
await
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
Print.newLine();
|
|
485
|
-
Print.
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
Print.
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
return success;
|
|
579
|
-
}
|
|
1
|
+
// commands/buildProduction/buildProduction.js - CLEAN VERSION
|
|
2
|
+
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { minify as terserMinify } from 'terser';
|
|
6
|
+
import { minify } from 'html-minifier-terser';
|
|
7
|
+
import CleanCSS from 'clean-css';
|
|
8
|
+
import Print from '../Print.js';
|
|
9
|
+
import { getSrcPath, getDistPath, getConfigPath } from '../utils/PathHelper.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Loads configuration from sliceConfig.json
|
|
13
|
+
*/
|
|
14
|
+
const loadConfig = () => {
|
|
15
|
+
try {
|
|
16
|
+
const configPath = getConfigPath(import.meta.url);
|
|
17
|
+
const rawData = fs.readFileSync(configPath, 'utf-8');
|
|
18
|
+
return JSON.parse(rawData);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
Print.error(`Loading configuration: ${error.message}`);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks necessary build dependencies
|
|
27
|
+
*/
|
|
28
|
+
async function checkBuildDependencies() {
|
|
29
|
+
const srcDir = getSrcPath(import.meta.url);
|
|
30
|
+
|
|
31
|
+
if (!await fs.pathExists(srcDir)) {
|
|
32
|
+
Print.error('Source directory (/src) not found');
|
|
33
|
+
Print.info('Run "slice init" to initialize your project');
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
await import('terser');
|
|
39
|
+
await import('clean-css');
|
|
40
|
+
await import('html-minifier-terser');
|
|
41
|
+
Print.success('Build dependencies available');
|
|
42
|
+
return true;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
Print.warning('Some build dependencies missing - using fallback copy mode');
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Verifies that critical Slice.js files exist
|
|
51
|
+
*/
|
|
52
|
+
async function verifySliceFiles(srcDir) {
|
|
53
|
+
Print.info('Verifying Slice.js critical files...');
|
|
54
|
+
|
|
55
|
+
const criticalFiles = [
|
|
56
|
+
'sliceConfig.json',
|
|
57
|
+
'Components/components.js',
|
|
58
|
+
'App/index.js'
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
for (const file of criticalFiles) {
|
|
62
|
+
const filePath = path.join(srcDir, file);
|
|
63
|
+
if (!await fs.pathExists(filePath)) {
|
|
64
|
+
throw new Error(`Critical Slice.js file missing: ${file}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Print.success('All critical Slice.js files verified');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Verifies build integrity for Slice.js
|
|
73
|
+
*/
|
|
74
|
+
async function verifyBuildIntegrity(distDir) {
|
|
75
|
+
Print.info('Verifying build integrity for Slice.js...');
|
|
76
|
+
|
|
77
|
+
const criticalBuiltFiles = [
|
|
78
|
+
'sliceConfig.json',
|
|
79
|
+
'Components/components.js',
|
|
80
|
+
'App/index.js'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
for (const file of criticalBuiltFiles) {
|
|
84
|
+
const filePath = path.join(distDir, file);
|
|
85
|
+
if (!await fs.pathExists(filePath)) {
|
|
86
|
+
throw new Error(`Critical built file missing: ${file}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (file === 'Components/components.js') {
|
|
90
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
91
|
+
if (!content.includes('const components') || !content.includes('export default')) {
|
|
92
|
+
throw new Error('components.js structure corrupted during build');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Print.success('Build integrity verified - all Slice.js components preserved');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Copies sliceConfig.json to the dist directory
|
|
102
|
+
*/
|
|
103
|
+
async function copySliceConfig() {
|
|
104
|
+
const srcConfig = getConfigPath(import.meta.url);
|
|
105
|
+
const distConfig = getDistPath(import.meta.url, 'sliceConfig.json');
|
|
106
|
+
|
|
107
|
+
if (await fs.pathExists(srcConfig)) {
|
|
108
|
+
await fs.copy(srcConfig, distConfig);
|
|
109
|
+
Print.info('sliceConfig.json copied to dist');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Processes a complete directory
|
|
115
|
+
*/
|
|
116
|
+
async function processDirectory(srcPath, distPath, baseSrcPath, options) {
|
|
117
|
+
const items = await fs.readdir(srcPath);
|
|
118
|
+
|
|
119
|
+
for (const item of items) {
|
|
120
|
+
const srcItemPath = path.join(srcPath, item);
|
|
121
|
+
const distItemPath = path.join(distPath, item);
|
|
122
|
+
const stat = await fs.stat(srcItemPath);
|
|
123
|
+
|
|
124
|
+
if (stat.isDirectory()) {
|
|
125
|
+
await fs.ensureDir(distItemPath);
|
|
126
|
+
await processDirectory(srcItemPath, distItemPath, baseSrcPath, options);
|
|
127
|
+
} else {
|
|
128
|
+
await processFile(srcItemPath, distItemPath, options);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Processes an individual file
|
|
135
|
+
*/
|
|
136
|
+
async function processFile(srcFilePath, distFilePath, options) {
|
|
137
|
+
const ext = path.extname(srcFilePath).toLowerCase();
|
|
138
|
+
const fileName = path.basename(srcFilePath);
|
|
139
|
+
const isBundleConfig = fileName === 'bundle.config.json' || fileName === 'bundle.config.js';
|
|
140
|
+
const isBundleFolder = srcFilePath.includes(`${path.sep}bundles${path.sep}`);
|
|
141
|
+
|
|
142
|
+
if (isBundleConfig && isBundleFolder) {
|
|
143
|
+
const renamed = fileName.replace('bundle.config', 'bundle.build.config');
|
|
144
|
+
distFilePath = path.join(path.dirname(distFilePath), renamed);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
if (fileName === 'components.js') {
|
|
149
|
+
if (options?.minify === false) {
|
|
150
|
+
await fs.copy(srcFilePath, distFilePath);
|
|
151
|
+
const stat = await fs.stat(srcFilePath);
|
|
152
|
+
const sizeKB = (stat.size / 1024).toFixed(1);
|
|
153
|
+
Print.info(`📄 Copied: ${fileName} (${sizeKB} KB)`);
|
|
154
|
+
} else {
|
|
155
|
+
await processComponentsFile(srcFilePath, distFilePath);
|
|
156
|
+
}
|
|
157
|
+
} else if (ext === '.js') {
|
|
158
|
+
if (options?.minify === false) {
|
|
159
|
+
await fs.copy(srcFilePath, distFilePath);
|
|
160
|
+
const stat = await fs.stat(srcFilePath);
|
|
161
|
+
const sizeKB = (stat.size / 1024).toFixed(1);
|
|
162
|
+
Print.info(`📄 Copied: ${fileName} (${sizeKB} KB)`);
|
|
163
|
+
} else {
|
|
164
|
+
await minifyJavaScript(srcFilePath, distFilePath);
|
|
165
|
+
}
|
|
166
|
+
} else if (ext === '.css') {
|
|
167
|
+
if (options?.minify === false) {
|
|
168
|
+
await fs.copy(srcFilePath, distFilePath);
|
|
169
|
+
const stat = await fs.stat(srcFilePath);
|
|
170
|
+
const sizeKB = (stat.size / 1024).toFixed(1);
|
|
171
|
+
Print.info(`📄 Copied: ${fileName} (${sizeKB} KB)`);
|
|
172
|
+
} else {
|
|
173
|
+
await minifyCSS(srcFilePath, distFilePath);
|
|
174
|
+
}
|
|
175
|
+
} else if (ext === '.html') {
|
|
176
|
+
if (options?.minify === false) {
|
|
177
|
+
await fs.copy(srcFilePath, distFilePath);
|
|
178
|
+
const stat = await fs.stat(srcFilePath);
|
|
179
|
+
const sizeKB = (stat.size / 1024).toFixed(1);
|
|
180
|
+
Print.info(`📄 Copied: ${fileName} (${sizeKB} KB)`);
|
|
181
|
+
} else {
|
|
182
|
+
await minifyHTML(srcFilePath, distFilePath);
|
|
183
|
+
}
|
|
184
|
+
} else if (fileName === 'sliceConfig.json') {
|
|
185
|
+
await fs.copy(srcFilePath, distFilePath);
|
|
186
|
+
Print.info(`📄 Preserved: ${fileName} (configuration file)`);
|
|
187
|
+
} else {
|
|
188
|
+
await fs.copy(srcFilePath, distFilePath);
|
|
189
|
+
const stat = await fs.stat(srcFilePath);
|
|
190
|
+
const sizeKB = (stat.size / 1024).toFixed(1);
|
|
191
|
+
Print.info(`📄 Copied: ${fileName} (${sizeKB} KB)`);
|
|
192
|
+
}
|
|
193
|
+
} catch (error) {
|
|
194
|
+
Print.error(`Processing ${fileName}: ${error.message}`);
|
|
195
|
+
await fs.copy(srcFilePath, distFilePath);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Processes the components.js file in a special way
|
|
201
|
+
*/
|
|
202
|
+
async function processComponentsFile(srcPath, distPath) {
|
|
203
|
+
const content = await fs.readFile(srcPath, 'utf8');
|
|
204
|
+
const originalSize = Buffer.byteLength(content, 'utf8');
|
|
205
|
+
|
|
206
|
+
const result = await terserMinify(content, {
|
|
207
|
+
compress: false,
|
|
208
|
+
mangle: false,
|
|
209
|
+
format: {
|
|
210
|
+
comments: false,
|
|
211
|
+
beautify: false,
|
|
212
|
+
indent_level: 0
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (result.error) {
|
|
217
|
+
throw new Error(`Terser error in components.js: ${result.error}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
await fs.writeFile(distPath, result.code, 'utf8');
|
|
221
|
+
|
|
222
|
+
const minifiedSize = Buffer.byteLength(result.code, 'utf8');
|
|
223
|
+
const savings = Math.round(((originalSize - minifiedSize) / originalSize) * 100);
|
|
224
|
+
|
|
225
|
+
Print.minificationResult(`${path.basename(srcPath)} (preserved structure)`, originalSize, minifiedSize, savings);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Minifies JavaScript files preserving Slice.js architecture
|
|
230
|
+
*/
|
|
231
|
+
async function minifyJavaScript(srcPath, distPath) {
|
|
232
|
+
const content = await fs.readFile(srcPath, 'utf8');
|
|
233
|
+
const originalSize = Buffer.byteLength(content, 'utf8');
|
|
234
|
+
|
|
235
|
+
const result = await terserMinify(content, {
|
|
236
|
+
compress: {
|
|
237
|
+
drop_console: false,
|
|
238
|
+
drop_debugger: true,
|
|
239
|
+
pure_funcs: [],
|
|
240
|
+
passes: 1,
|
|
241
|
+
unused: false,
|
|
242
|
+
side_effects: false,
|
|
243
|
+
reduce_vars: false,
|
|
244
|
+
collapse_vars: false
|
|
245
|
+
},
|
|
246
|
+
mangle: {
|
|
247
|
+
reserved: [
|
|
248
|
+
// Core Slice
|
|
249
|
+
'slice', 'Slice', 'SliceJS', 'window', 'document',
|
|
250
|
+
// Main classes
|
|
251
|
+
'Controller', 'StylesManager', 'Router', 'Logger', 'Debugger',
|
|
252
|
+
// Slice methods
|
|
253
|
+
'getClass', 'isProduction', 'getComponent', 'build', 'setTheme', 'attachTemplate',
|
|
254
|
+
// Controller
|
|
255
|
+
'componentCategories', 'templates', 'classes', 'requestedStyles', 'activeComponents',
|
|
256
|
+
'registerComponent', 'registerComponentsRecursively', 'loadTemplateToComponent',
|
|
257
|
+
'fetchText', 'setComponentProps', 'verifyComponentIds', 'destroyComponent',
|
|
258
|
+
// StylesManager
|
|
259
|
+
'componentStyles', 'themeManager', 'init', 'appendComponentStyles', 'registerComponentStyles',
|
|
260
|
+
// Router
|
|
261
|
+
'routes', 'pathToRouteMap', 'activeRoute', 'navigate', 'matchRoute', 'handleRoute',
|
|
262
|
+
'onRouteChange', 'loadInitialRoute', 'renderRoutesComponentsInPage',
|
|
263
|
+
// Propiedades de componentes
|
|
264
|
+
'sliceId', 'sliceType', 'sliceConfig', 'debuggerProps', 'parentComponent',
|
|
265
|
+
'value', 'customColor', 'icon', 'layout', 'view', 'items', 'columns', 'rows',
|
|
266
|
+
'onClickCallback', 'props',
|
|
267
|
+
// Custom Elements
|
|
268
|
+
'customElements', 'define', 'HTMLElement',
|
|
269
|
+
// Critical DOM APIs
|
|
270
|
+
'addEventListener', 'removeEventListener', 'querySelector', 'querySelectorAll',
|
|
271
|
+
'appendChild', 'removeChild', 'innerHTML', 'textContent', 'style', 'classList',
|
|
272
|
+
// Lifecycle
|
|
273
|
+
'beforeMount', 'afterMount', 'beforeDestroy', 'afterDestroy',
|
|
274
|
+
'mount', 'unmount', 'destroy', 'update', 'start', 'stop',
|
|
275
|
+
// Browser APIs
|
|
276
|
+
'fetch', 'setTimeout', 'clearTimeout', 'localStorage', 'history', 'pushState',
|
|
277
|
+
// Exports/Imports
|
|
278
|
+
'default', 'export', 'import', 'from', 'await', 'async',
|
|
279
|
+
// Nombres de componentes
|
|
280
|
+
'Button', 'Grid', 'Layout', 'HomePage', 'NotFound', 'Loading', 'TreeView', 'Link',
|
|
281
|
+
'FetchManager'
|
|
282
|
+
],
|
|
283
|
+
properties: {
|
|
284
|
+
regex: /^(slice|_|\$|on[A-Z]|get|set|has|is)/
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
format: {
|
|
288
|
+
comments: false,
|
|
289
|
+
beautify: false
|
|
290
|
+
},
|
|
291
|
+
keep_fnames: true,
|
|
292
|
+
keep_classnames: true
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
if (result.error) {
|
|
296
|
+
throw new Error(`Terser error: ${result.error}`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
await fs.writeFile(distPath, result.code, 'utf8');
|
|
300
|
+
|
|
301
|
+
const minifiedSize = Buffer.byteLength(result.code, 'utf8');
|
|
302
|
+
const savings = Math.round(((originalSize - minifiedSize) / originalSize) * 100);
|
|
303
|
+
|
|
304
|
+
Print.minificationResult(path.basename(srcPath), originalSize, minifiedSize, savings);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Minifies CSS files
|
|
309
|
+
*/
|
|
310
|
+
async function minifyCSS(srcPath, distPath) {
|
|
311
|
+
const content = await fs.readFile(srcPath, 'utf8');
|
|
312
|
+
const originalSize = Buffer.byteLength(content, 'utf8');
|
|
313
|
+
|
|
314
|
+
const cleanCSS = new CleanCSS({
|
|
315
|
+
level: 2,
|
|
316
|
+
returnPromise: false
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const result = cleanCSS.minify(content);
|
|
320
|
+
|
|
321
|
+
if (result.errors.length > 0) {
|
|
322
|
+
throw new Error(`CleanCSS errors: ${result.errors.join(', ')}`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
await fs.writeFile(distPath, result.styles, 'utf8');
|
|
326
|
+
|
|
327
|
+
const minifiedSize = Buffer.byteLength(result.styles, 'utf8');
|
|
328
|
+
const savings = Math.round(((originalSize - minifiedSize) / originalSize) * 100);
|
|
329
|
+
|
|
330
|
+
Print.minificationResult(path.basename(srcPath), originalSize, minifiedSize, savings);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Minifies HTML files
|
|
335
|
+
*/
|
|
336
|
+
async function minifyHTML(srcPath, distPath) {
|
|
337
|
+
const content = await fs.readFile(srcPath, 'utf8');
|
|
338
|
+
const originalSize = Buffer.byteLength(content, 'utf8');
|
|
339
|
+
|
|
340
|
+
const minified = await minify(content, {
|
|
341
|
+
collapseWhitespace: true,
|
|
342
|
+
removeComments: true,
|
|
343
|
+
removeRedundantAttributes: true,
|
|
344
|
+
removeScriptTypeAttributes: true,
|
|
345
|
+
removeStyleLinkTypeAttributes: true,
|
|
346
|
+
useShortDoctype: true,
|
|
347
|
+
minifyCSS: true,
|
|
348
|
+
minifyJS: {
|
|
349
|
+
mangle: {
|
|
350
|
+
reserved: ['slice', 'Slice', 'SliceJS', 'sliceId', 'sliceConfig']
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
ignoreCustomFragments: [
|
|
354
|
+
/slice-[\w-]+="[^"]*"/g
|
|
355
|
+
]
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
await fs.writeFile(distPath, minified, 'utf8');
|
|
359
|
+
|
|
360
|
+
const minifiedSize = Buffer.byteLength(minified, 'utf8');
|
|
361
|
+
const savings = Math.round(((originalSize - minifiedSize) / originalSize) * 100);
|
|
362
|
+
|
|
363
|
+
Print.minificationResult(path.basename(srcPath), originalSize, minifiedSize, savings);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Creates an optimized bundle of the main file
|
|
368
|
+
*/
|
|
369
|
+
async function createOptimizedBundle() {
|
|
370
|
+
Print.buildProgress('Creating optimized bundle...');
|
|
371
|
+
|
|
372
|
+
const mainJSPath = getDistPath(import.meta.url, 'App', 'index.js');
|
|
373
|
+
|
|
374
|
+
if (await fs.pathExists(mainJSPath)) {
|
|
375
|
+
Print.success('Main bundle optimized');
|
|
376
|
+
} else {
|
|
377
|
+
Print.warning('No main JavaScript file found for bundling');
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Generates build statistics
|
|
383
|
+
*/
|
|
384
|
+
async function generateBuildStats(srcDir, distDir) {
|
|
385
|
+
Print.buildProgress('Generating build statistics...');
|
|
386
|
+
|
|
387
|
+
const getDirectorySize = async (dirPath) => {
|
|
388
|
+
let totalSize = 0;
|
|
389
|
+
const items = await fs.readdir(dirPath);
|
|
390
|
+
|
|
391
|
+
for (const item of items) {
|
|
392
|
+
const itemPath = path.join(dirPath, item);
|
|
393
|
+
const stat = await fs.stat(itemPath);
|
|
394
|
+
|
|
395
|
+
if (stat.isDirectory()) {
|
|
396
|
+
totalSize += await getDirectorySize(itemPath);
|
|
397
|
+
} else {
|
|
398
|
+
totalSize += stat.size;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return totalSize;
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
const srcSize = await getDirectorySize(srcDir);
|
|
407
|
+
const distSize = await getDirectorySize(distDir);
|
|
408
|
+
const savings = Math.round(((srcSize - distSize) / srcSize) * 100);
|
|
409
|
+
|
|
410
|
+
Print.newLine();
|
|
411
|
+
Print.info(`📊 Build Statistics:`);
|
|
412
|
+
console.log(` Source: ${(srcSize / 1024).toFixed(1)} KB`);
|
|
413
|
+
console.log(` Built: ${(distSize / 1024).toFixed(1)} KB`);
|
|
414
|
+
console.log(` Saved: ${savings}% smaller`);
|
|
415
|
+
|
|
416
|
+
} catch (error) {
|
|
417
|
+
Print.warning('Could not generate build statistics');
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Analyzes the build without building
|
|
423
|
+
*/
|
|
424
|
+
async function analyzeBuild() {
|
|
425
|
+
const distDir = getDistPath(import.meta.url);
|
|
426
|
+
|
|
427
|
+
if (!await fs.pathExists(distDir)) {
|
|
428
|
+
Print.error('No build found to analyze. Run "slice build" first.');
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
Print.info('Analyzing production build...');
|
|
433
|
+
await generateBuildStats(
|
|
434
|
+
getSrcPath(import.meta.url),
|
|
435
|
+
distDir
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* MAIN BUILD FUNCTION
|
|
441
|
+
*/
|
|
442
|
+
export default async function buildProduction(options = {}) {
|
|
443
|
+
const startTime = Date.now();
|
|
444
|
+
|
|
445
|
+
try {
|
|
446
|
+
Print.title('🔨 Building Slice.js project for production...');
|
|
447
|
+
Print.newLine();
|
|
448
|
+
|
|
449
|
+
const srcDir = getSrcPath(import.meta.url);
|
|
450
|
+
const distDir = getDistPath(import.meta.url);
|
|
451
|
+
|
|
452
|
+
if (!await fs.pathExists(srcDir)) {
|
|
453
|
+
throw new Error('Source directory not found. Run "slice init" first.');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
await verifySliceFiles(srcDir);
|
|
457
|
+
|
|
458
|
+
// Clean dist directory
|
|
459
|
+
if (await fs.pathExists(distDir)) {
|
|
460
|
+
if (!options.skipClean) {
|
|
461
|
+
Print.info('Cleaning previous build...');
|
|
462
|
+
await fs.remove(distDir);
|
|
463
|
+
Print.success('Previous build cleaned');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
await fs.ensureDir(distDir);
|
|
468
|
+
await copySliceConfig();
|
|
469
|
+
|
|
470
|
+
// Process files
|
|
471
|
+
Print.info('Processing and optimizing source files for Slice.js...');
|
|
472
|
+
await processDirectory(srcDir, distDir, srcDir, options);
|
|
473
|
+
Print.success('All source files processed and optimized');
|
|
474
|
+
|
|
475
|
+
await verifyBuildIntegrity(distDir);
|
|
476
|
+
await createOptimizedBundle();
|
|
477
|
+
await generateBuildStats(srcDir, distDir);
|
|
478
|
+
|
|
479
|
+
const buildTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
480
|
+
|
|
481
|
+
Print.newLine();
|
|
482
|
+
Print.success(`✨ Slice.js production build completed in ${buildTime}s`);
|
|
483
|
+
Print.info('Your optimized project is ready in the /dist directory');
|
|
484
|
+
Print.newLine();
|
|
485
|
+
Print.info('Next steps:');
|
|
486
|
+
console.log(' • Use "npm run slice:start" to test the production build');
|
|
487
|
+
console.log(' • All Slice.js components and architecture preserved');
|
|
488
|
+
|
|
489
|
+
return true;
|
|
490
|
+
|
|
491
|
+
} catch (error) {
|
|
492
|
+
Print.error(`Build failed: ${error.message}`);
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Preview server for testing the production build
|
|
499
|
+
*/
|
|
500
|
+
export async function serveProductionBuild(port) {
|
|
501
|
+
try {
|
|
502
|
+
const config = loadConfig();
|
|
503
|
+
const defaultPort = config?.server?.port || 3001;
|
|
504
|
+
const finalPort = port || defaultPort;
|
|
505
|
+
|
|
506
|
+
const distDir = getDistPath(import.meta.url);
|
|
507
|
+
|
|
508
|
+
if (!await fs.pathExists(distDir)) {
|
|
509
|
+
throw new Error('No production build found. Run "slice build" first.');
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
Print.info(`Starting production preview server on port ${finalPort}...`);
|
|
513
|
+
|
|
514
|
+
const express = await import('express');
|
|
515
|
+
const app = express.default();
|
|
516
|
+
|
|
517
|
+
app.use(express.default.static(distDir));
|
|
518
|
+
|
|
519
|
+
app.get('*', (req, res) => {
|
|
520
|
+
const indexPath = path.join(distDir, 'App/index.html');
|
|
521
|
+
const fallbackPath = path.join(distDir, 'index.html');
|
|
522
|
+
|
|
523
|
+
if (fs.existsSync(indexPath)) {
|
|
524
|
+
res.sendFile(indexPath);
|
|
525
|
+
} else if (fs.existsSync(fallbackPath)) {
|
|
526
|
+
res.sendFile(fallbackPath);
|
|
527
|
+
} else {
|
|
528
|
+
res.status(404).send('Production build index.html not found');
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
app.listen(finalPort, () => {
|
|
533
|
+
Print.success(`Production preview server running at http://localhost:${finalPort}`);
|
|
534
|
+
Print.info('Press Ctrl+C to stop the server');
|
|
535
|
+
Print.info('This server previews your production build from /dist');
|
|
536
|
+
Print.warning('This is a preview server - use "npm run slice:start" for the full production server');
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
} catch (error) {
|
|
540
|
+
Print.error(`Error starting production preview server: ${error.message}`);
|
|
541
|
+
throw error;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Build command with options
|
|
547
|
+
*/
|
|
548
|
+
export async function buildCommand(options = {}) {
|
|
549
|
+
const config = loadConfig();
|
|
550
|
+
const defaultPort = config?.server?.port || 3001;
|
|
551
|
+
|
|
552
|
+
if (!await checkBuildDependencies()) {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (options.serve) {
|
|
557
|
+
await serveProductionBuild(options.port || defaultPort);
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (options.analyze) {
|
|
562
|
+
await analyzeBuild();
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const success = await buildProduction(options);
|
|
567
|
+
|
|
568
|
+
if (success && options.preview) {
|
|
569
|
+
Print.newLine();
|
|
570
|
+
Print.info('✨ Build completed successfully!');
|
|
571
|
+
Print.info(`Starting preview server on port ${options.port || defaultPort}...`);
|
|
572
|
+
await serveProductionBuild(options.port || defaultPort);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return success;
|
|
576
|
+
}
|