create-fluxstack 1.10.1 → 1.12.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/.dockerignore +1 -2
- package/Dockerfile +8 -8
- package/LLMD/INDEX.md +64 -0
- package/LLMD/MAINTENANCE.md +197 -0
- package/LLMD/MIGRATION.md +156 -0
- package/LLMD/config/.gitkeep +1 -0
- package/LLMD/config/declarative-system.md +268 -0
- package/LLMD/config/environment-vars.md +327 -0
- package/LLMD/config/runtime-reload.md +401 -0
- package/LLMD/core/.gitkeep +1 -0
- package/LLMD/core/build-system.md +599 -0
- package/LLMD/core/framework-lifecycle.md +229 -0
- package/LLMD/core/plugin-system.md +451 -0
- package/LLMD/patterns/.gitkeep +1 -0
- package/LLMD/patterns/anti-patterns.md +297 -0
- package/LLMD/patterns/project-structure.md +264 -0
- package/LLMD/patterns/type-safety.md +440 -0
- package/LLMD/reference/.gitkeep +1 -0
- package/LLMD/reference/cli-commands.md +250 -0
- package/LLMD/reference/plugin-hooks.md +357 -0
- package/LLMD/reference/routing.md +39 -0
- package/LLMD/reference/troubleshooting.md +364 -0
- package/LLMD/resources/.gitkeep +1 -0
- package/LLMD/resources/controllers.md +465 -0
- package/LLMD/resources/live-components.md +703 -0
- package/LLMD/resources/live-rooms.md +482 -0
- package/LLMD/resources/live-upload.md +130 -0
- package/LLMD/resources/plugins-external.md +617 -0
- package/LLMD/resources/routes-eden.md +254 -0
- package/README.md +37 -17
- package/app/client/index.html +0 -1
- package/app/client/src/App.tsx +107 -150
- package/app/client/src/components/AppLayout.tsx +68 -0
- package/app/client/src/components/BackButton.tsx +13 -0
- package/app/client/src/components/DemoPage.tsx +20 -0
- package/app/client/src/components/LiveUploadWidget.tsx +204 -0
- package/app/client/src/lib/eden-api.ts +85 -60
- package/app/client/src/live/ChatDemo.tsx +107 -0
- package/app/client/src/live/CounterDemo.tsx +206 -0
- package/app/client/src/live/FormDemo.tsx +119 -0
- package/app/client/src/live/RoomChatDemo.tsx +242 -0
- package/app/client/src/live/UploadDemo.tsx +21 -0
- package/app/client/src/main.tsx +4 -1
- package/app/client/src/pages/ApiTestPage.tsx +108 -0
- package/app/client/src/pages/HomePage.tsx +76 -0
- package/app/server/app.ts +1 -4
- package/app/server/controllers/users.controller.ts +36 -44
- package/app/server/index.ts +25 -35
- package/app/server/live/LiveChat.ts +77 -0
- package/app/server/live/LiveCounter.ts +67 -0
- package/app/server/live/LiveForm.ts +63 -0
- package/app/server/live/LiveLocalCounter.ts +32 -0
- package/app/server/live/LiveRoomChat.ts +285 -0
- package/app/server/live/LiveUpload.ts +81 -0
- package/app/server/routes/index.ts +3 -1
- package/app/server/routes/room.routes.ts +117 -0
- package/app/server/routes/users.routes.ts +35 -27
- package/app/shared/types/index.ts +14 -2
- package/config/app.config.ts +2 -62
- package/config/client.config.ts +2 -95
- package/config/database.config.ts +2 -99
- package/config/fluxstack.config.ts +25 -45
- package/config/index.ts +57 -38
- package/config/monitoring.config.ts +2 -114
- package/config/plugins.config.ts +2 -80
- package/config/server.config.ts +2 -68
- package/config/services.config.ts +2 -130
- package/config/system/app.config.ts +29 -0
- package/config/system/build.config.ts +49 -0
- package/config/system/client.config.ts +68 -0
- package/config/system/database.config.ts +17 -0
- package/config/system/fluxstack.config.ts +114 -0
- package/config/{logger.config.ts → system/logger.config.ts} +3 -1
- package/config/system/monitoring.config.ts +114 -0
- package/config/system/plugins.config.ts +84 -0
- package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
- package/config/system/server.config.ts +68 -0
- package/config/system/services.config.ts +46 -0
- package/config/{system.config.ts → system/system.config.ts} +1 -1
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +39 -27
- package/core/build/live-components-generator.ts +3 -3
- package/core/build/optimizer.ts +235 -235
- package/core/cli/command-registry.ts +6 -4
- package/core/cli/commands/build.ts +79 -0
- package/core/cli/commands/create.ts +54 -0
- package/core/cli/commands/dev.ts +101 -0
- package/core/cli/commands/help.ts +34 -0
- package/core/cli/commands/index.ts +34 -0
- package/core/cli/commands/make-plugin.ts +90 -0
- package/core/cli/commands/plugin-add.ts +197 -0
- package/core/cli/commands/plugin-deps.ts +2 -2
- package/core/cli/commands/plugin-list.ts +208 -0
- package/core/cli/commands/plugin-remove.ts +170 -0
- package/core/cli/generators/component.ts +769 -769
- package/core/cli/generators/controller.ts +1 -1
- package/core/cli/generators/index.ts +146 -146
- package/core/cli/generators/interactive.ts +227 -227
- package/core/cli/generators/plugin.ts +2 -2
- package/core/cli/generators/prompts.ts +82 -82
- package/core/cli/generators/route.ts +6 -6
- package/core/cli/generators/service.ts +2 -2
- package/core/cli/generators/template-engine.ts +4 -3
- package/core/cli/generators/types.ts +2 -2
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +115 -686
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/LiveComponentsProvider.tsx +60 -8
- package/core/client/api/eden.ts +183 -0
- package/core/client/api/index.ts +11 -0
- package/core/client/components/Live.tsx +104 -0
- package/core/client/fluxstack.ts +1 -9
- package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/hooks/useChunkedUpload.ts +85 -35
- package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
- package/core/client/hooks/useLiveComponent.ts +800 -0
- package/core/client/hooks/useLiveUpload.ts +71 -0
- package/core/client/hooks/useRoom.ts +409 -0
- package/core/client/hooks/useRoomProxy.ts +382 -0
- package/core/client/index.ts +17 -68
- package/core/client/standalone-entry.ts +8 -0
- package/core/client/standalone.ts +74 -53
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +70 -291
- package/core/config/schema.ts +42 -723
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +47 -40
- package/core/framework/types.ts +2 -2
- package/core/index.ts +23 -4
- package/core/live/ComponentRegistry.ts +3 -3
- package/core/live/types.ts +77 -0
- package/core/plugins/built-in/index.ts +134 -134
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +111 -47
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +68 -265
- package/core/plugins/built-in/vite/index.ts +85 -185
- package/core/plugins/built-in/vite/vite-dev.ts +10 -16
- package/core/plugins/config.ts +9 -7
- package/core/plugins/dependency-manager.ts +31 -1
- package/core/plugins/discovery.ts +19 -7
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +203 -203
- package/core/plugins/manager.ts +27 -39
- package/core/plugins/module-resolver.ts +19 -8
- package/core/plugins/registry.ts +255 -19
- package/core/plugins/types.ts +20 -53
- package/core/server/framework.ts +66 -43
- package/core/server/index.ts +15 -15
- package/core/server/live/ComponentRegistry.ts +78 -71
- package/core/server/live/FileUploadManager.ts +23 -10
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/LiveRoomManager.ts +261 -0
- package/core/server/live/RoomEventBus.ts +234 -0
- package/core/server/live/RoomStateManager.ts +172 -0
- package/core/server/live/StateSignature.ts +643 -643
- package/core/server/live/WebSocketConnectionManager.ts +30 -19
- package/core/server/live/auto-generated-components.ts +21 -9
- package/core/server/live/index.ts +14 -0
- package/core/server/live/websocket-plugin.ts +214 -67
- package/core/server/middleware/elysia-helpers.ts +7 -2
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +180 -180
- package/core/server/plugins/static-files-plugin.ts +69 -69
- package/core/server/plugins/swagger.ts +1 -1
- package/core/server/rooms/RoomBroadcaster.ts +357 -0
- package/core/server/rooms/RoomSystem.ts +463 -0
- package/core/server/rooms/index.ts +13 -0
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +12 -12
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/build.ts +219 -219
- package/core/types/config.ts +56 -26
- package/core/types/index.ts +4 -4
- package/core/types/plugin.ts +107 -107
- package/core/types/types.ts +353 -14
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +2 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +36 -1
- package/core/utils/errors/index.ts +49 -5
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +6 -16
- package/core/utils/index.ts +17 -17
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +13 -9
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +6 -1
- package/core/utils/logger/stack-trace.ts +3 -1
- package/core/utils/logger/startup-banner.ts +82 -82
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +66 -66
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +8 -7
- package/package.json +12 -13
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
- package/plugins/crypto-auth/client/components/index.ts +11 -11
- package/plugins/crypto-auth/client/index.ts +11 -11
- package/plugins/crypto-auth/config/index.ts +1 -1
- package/plugins/crypto-auth/index.ts +4 -4
- package/plugins/crypto-auth/package.json +65 -65
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/index.ts +21 -21
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/tsconfig.api-strict.json +16 -0
- package/tsconfig.json +48 -52
- package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
- package/types/global.d.ts +29 -29
- package/types/vitest.d.ts +8 -8
- package/vite.config.ts +38 -62
- package/vitest.config.live.ts +10 -9
- package/vitest.config.ts +29 -17
- package/app/client/README.md +0 -69
- package/app/client/SIMPLIFICATION.md +0 -140
- package/app/client/frontend-only.ts +0 -12
- package/app/client/src/live/FileUploadExample.tsx +0 -359
- package/app/client/src/live/MinimalLiveClock.tsx +0 -47
- package/app/client/src/live/QuickUploadTest.tsx +0 -193
- package/app/client/tsconfig.app.json +0 -45
- package/app/client/tsconfig.json +0 -7
- package/app/client/zustand-setup.md +0 -65
- package/app/server/backend-only.ts +0 -18
- package/app/server/live/LiveClockComponent.ts +0 -215
- package/app/server/live/LiveFileUploadComponent.ts +0 -77
- package/app/server/routes/env-test.ts +0 -110
- package/core/client/hooks/index.ts +0 -7
- package/core/client/hooks/useHybridLiveComponent.ts +0 -685
- package/core/client/hooks/useTypedLiveComponent.ts +0 -133
- package/core/client/hooks/useWebSocket.ts +0 -361
- package/core/config/env.ts +0 -546
- package/core/config/loader.ts +0 -522
- package/core/config/runtime-config.ts +0 -327
- package/core/config/validator.ts +0 -540
- package/core/server/backend-entry.ts +0 -51
- package/core/server/standalone.ts +0 -106
- package/core/utils/regenerate-files.ts +0 -69
- package/fluxstack.config.ts +0 -354
package/core/build/index.ts
CHANGED
|
@@ -19,30 +19,39 @@ export class FluxStackBuilder {
|
|
|
19
19
|
this.config = config
|
|
20
20
|
this.pluginRegistry = pluginRegistry
|
|
21
21
|
|
|
22
|
+
const optimization = this.config.optimization || {
|
|
23
|
+
minify: true,
|
|
24
|
+
treeshake: true,
|
|
25
|
+
compress: true,
|
|
26
|
+
removeUnusedCSS: false,
|
|
27
|
+
optimizeImages: false,
|
|
28
|
+
bundleAnalyzer: false
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
// Initialize bundler with configuration
|
|
23
32
|
this.bundler = new Bundler({
|
|
24
|
-
target: config.build.target,
|
|
25
|
-
outDir: config.build.outDir,
|
|
26
|
-
sourceMaps: config.build.sourceMaps,
|
|
27
|
-
minify:
|
|
28
|
-
external: config.build.external
|
|
33
|
+
target: config.build.target ?? 'bun',
|
|
34
|
+
outDir: config.build.outDir ?? 'dist',
|
|
35
|
+
sourceMaps: config.build.sourceMaps ?? false,
|
|
36
|
+
minify: optimization.minify,
|
|
37
|
+
external: config.build.external || []
|
|
29
38
|
})
|
|
30
39
|
|
|
31
40
|
// Initialize optimizer with configuration
|
|
32
41
|
this.optimizer = new Optimizer({
|
|
33
|
-
treeshake:
|
|
34
|
-
compress:
|
|
35
|
-
removeUnusedCSS:
|
|
36
|
-
optimizeImages:
|
|
37
|
-
bundleAnalysis:
|
|
42
|
+
treeshake: optimization.treeshake ?? true,
|
|
43
|
+
compress: optimization.compress || false,
|
|
44
|
+
removeUnusedCSS: optimization.removeUnusedCSS || false,
|
|
45
|
+
optimizeImages: optimization.optimizeImages || false,
|
|
46
|
+
bundleAnalysis: optimization.bundleAnalyzer || false
|
|
38
47
|
})
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
async buildClient() {
|
|
42
51
|
return await this.bundler.bundleClient({
|
|
43
52
|
env: {
|
|
44
|
-
VITE_BUILD_OUTDIR: this.config.
|
|
45
|
-
VITE_BUILD_SOURCEMAPS: this.config.
|
|
53
|
+
VITE_BUILD_OUTDIR: this.config.clientBuild.outDir ?? 'dist/client',
|
|
54
|
+
VITE_BUILD_SOURCEMAPS: (this.config.clientBuild.sourceMaps ?? false).toString()
|
|
46
55
|
}
|
|
47
56
|
})
|
|
48
57
|
}
|
|
@@ -60,7 +69,7 @@ export class FluxStackBuilder {
|
|
|
60
69
|
async createDockerFiles() {
|
|
61
70
|
buildLogger.section('Docker Configuration', '🐳')
|
|
62
71
|
|
|
63
|
-
const distDir = this.config.build.outDir
|
|
72
|
+
const distDir = this.config.build.outDir ?? 'dist'
|
|
64
73
|
buildLogger.step(`Output directory: ${distDir}`)
|
|
65
74
|
|
|
66
75
|
// Ensure dist directory exists
|
|
@@ -72,9 +81,12 @@ export class FluxStackBuilder {
|
|
|
72
81
|
buildLogger.success('Directory already exists')
|
|
73
82
|
}
|
|
74
83
|
|
|
84
|
+
// Get current Bun version for Docker image
|
|
85
|
+
const bunVersion = typeof Bun !== 'undefined' ? Bun.version : '1.3'
|
|
86
|
+
|
|
75
87
|
// Dockerfile optimizado para produção
|
|
76
88
|
const dockerfile = `# FluxStack Production Docker Image
|
|
77
|
-
FROM oven/bun
|
|
89
|
+
FROM oven/bun:${bunVersion}-alpine AS production
|
|
78
90
|
|
|
79
91
|
WORKDIR /app
|
|
80
92
|
|
|
@@ -245,9 +257,9 @@ MONITORING_ENABLED=true
|
|
|
245
257
|
const startTime = Date.now()
|
|
246
258
|
|
|
247
259
|
const buildContext: BuildContext = {
|
|
248
|
-
target: this.config.build.target,
|
|
249
|
-
outDir: this.config.build.outDir,
|
|
250
|
-
mode: (this.config.build.mode
|
|
260
|
+
target: this.config.build.target ?? 'bun',
|
|
261
|
+
outDir: this.config.build.outDir ?? 'dist',
|
|
262
|
+
mode: (this.config.build.mode ?? 'production') as 'development' | 'production',
|
|
251
263
|
config: this.config
|
|
252
264
|
}
|
|
253
265
|
|
|
@@ -307,12 +319,12 @@ MONITORING_ENABLED=true
|
|
|
307
319
|
}
|
|
308
320
|
|
|
309
321
|
// Process assets and execute onBuildAsset hooks
|
|
310
|
-
await this.processAssets(this.config.build.outDir)
|
|
322
|
+
await this.processAssets(this.config.build.outDir ?? 'dist')
|
|
311
323
|
|
|
312
324
|
// Optimize build if enabled
|
|
313
325
|
let optimizationResult
|
|
314
326
|
if (this.config.build.optimize) {
|
|
315
|
-
optimizationResult = await this.optimizer.optimize(this.config.build.outDir)
|
|
327
|
+
optimizationResult = await this.optimizer.optimize(this.config.build.outDir ?? 'dist')
|
|
316
328
|
}
|
|
317
329
|
|
|
318
330
|
// Create Docker files
|
|
@@ -329,7 +341,7 @@ MONITORING_ENABLED=true
|
|
|
329
341
|
// Print build summary
|
|
330
342
|
buildLogger.summary('Build Completed Successfully', [
|
|
331
343
|
{ label: 'Build Time', value: buildLogger.formatDuration(duration), highlight: true },
|
|
332
|
-
{ label: 'Output Directory', value: this.config.build.outDir },
|
|
344
|
+
{ label: 'Output Directory', value: this.config.build.outDir ?? 'dist' },
|
|
333
345
|
{ label: 'Client Assets', value: clientResult.assets?.length || 0 },
|
|
334
346
|
{ label: 'Total Size', value: buildLogger.formatSize(optimizationResult?.optimizedSize || 0) },
|
|
335
347
|
{ label: 'Compression', value: optimizationResult?.compressionRatio ? `${optimizationResult.compressionRatio.toFixed(2)}%` : 'N/A' },
|
|
@@ -422,10 +434,10 @@ MONITORING_ENABLED=true
|
|
|
422
434
|
optimizationResult?: any
|
|
423
435
|
): Promise<BuildManifest> {
|
|
424
436
|
return {
|
|
425
|
-
version: this.config.app.version,
|
|
437
|
+
version: this.config.app.version ?? '0.0.0',
|
|
426
438
|
timestamp: new Date().toISOString(),
|
|
427
|
-
target: this.config.build.target,
|
|
428
|
-
mode: this.config.build.mode
|
|
439
|
+
target: this.config.build.target ?? 'bun',
|
|
440
|
+
mode: this.config.build.mode ?? 'production',
|
|
429
441
|
client: {
|
|
430
442
|
entryPoints: [],
|
|
431
443
|
chunks: [],
|
|
@@ -439,9 +451,9 @@ MONITORING_ENABLED=true
|
|
|
439
451
|
},
|
|
440
452
|
assets: [],
|
|
441
453
|
optimization: {
|
|
442
|
-
minified: this.config.
|
|
443
|
-
treeshaken: this.config.
|
|
444
|
-
compressed: this.config.
|
|
454
|
+
minified: this.config.optimization?.minify ?? false,
|
|
455
|
+
treeshaken: this.config.optimization?.treeshake ?? false,
|
|
456
|
+
compressed: this.config.optimization?.compress ?? false,
|
|
445
457
|
originalSize: optimizationResult?.originalSize || 0,
|
|
446
458
|
optimizedSize: optimizationResult?.optimizedSize || 0,
|
|
447
459
|
compressionRatio: optimizationResult?.compressionRatio || 0
|
|
@@ -533,4 +545,4 @@ MONITORING_ENABLED=true
|
|
|
533
545
|
|
|
534
546
|
return 'other'
|
|
535
547
|
}
|
|
536
|
-
}
|
|
548
|
+
}
|
|
@@ -18,7 +18,7 @@ export class LiveComponentsGenerator {
|
|
|
18
18
|
private backupFilePath: string
|
|
19
19
|
private entryPointPath: string
|
|
20
20
|
private entryPointBackupPath: string
|
|
21
|
-
private readonly importLine = 'import "
|
|
21
|
+
private readonly importLine = 'import "@core/server/live/auto-generated-components"'
|
|
22
22
|
|
|
23
23
|
constructor() {
|
|
24
24
|
// Scan components from app/ directory (user code)
|
|
@@ -75,7 +75,7 @@ export class LiveComponentsGenerator {
|
|
|
75
75
|
className,
|
|
76
76
|
componentName,
|
|
77
77
|
// Path relative to core/server/live/ where the generated file will be
|
|
78
|
-
filePath:
|
|
78
|
+
filePath: `@app/server/live/${fileName}`
|
|
79
79
|
})
|
|
80
80
|
|
|
81
81
|
buildLogger.step(`Discovered component: ${className} → ${componentName}`)
|
|
@@ -116,7 +116,7 @@ export class LiveComponentsGenerator {
|
|
|
116
116
|
// Generated at: ${new Date().toISOString()}
|
|
117
117
|
|
|
118
118
|
${imports}
|
|
119
|
-
import { componentRegistry } from "
|
|
119
|
+
import { componentRegistry } from "@core/server/live/ComponentRegistry"
|
|
120
120
|
|
|
121
121
|
// Register all components statically for production bundle
|
|
122
122
|
function registerAllComponents() {
|
package/core/build/optimizer.ts
CHANGED
|
@@ -1,236 +1,236 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, statSync, readdirSync } from "fs"
|
|
2
|
-
import { join, extname } from "path"
|
|
3
|
-
import { gzipSync } from "zlib"
|
|
4
|
-
import type { OptimizationConfig, OptimizationResult } from "../types/build"
|
|
5
|
-
import { buildLogger } from "../utils/build-logger"
|
|
6
|
-
|
|
7
|
-
export interface OptimizerConfig {
|
|
8
|
-
treeshake: boolean
|
|
9
|
-
compress: boolean
|
|
10
|
-
removeUnusedCSS: boolean
|
|
11
|
-
optimizeImages: boolean
|
|
12
|
-
bundleAnalysis: boolean
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class Optimizer {
|
|
16
|
-
private config: OptimizerConfig
|
|
17
|
-
|
|
18
|
-
constructor(config: OptimizerConfig) {
|
|
19
|
-
this.config = config
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async optimize(buildPath: string): Promise<OptimizationResult> {
|
|
23
|
-
buildLogger.section('Build Optimization', '🔧')
|
|
24
|
-
|
|
25
|
-
const startTime = Date.now()
|
|
26
|
-
const results: OptimizationResult = {
|
|
27
|
-
success: true,
|
|
28
|
-
duration: 0,
|
|
29
|
-
originalSize: 0,
|
|
30
|
-
optimizedSize: 0,
|
|
31
|
-
compressionRatio: 0,
|
|
32
|
-
optimizations: []
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
// Get original size
|
|
37
|
-
results.originalSize = await this.calculateDirectorySize(buildPath)
|
|
38
|
-
buildLogger.step(`Original size: ${buildLogger.formatSize(results.originalSize)}`)
|
|
39
|
-
|
|
40
|
-
// Apply optimizations (minification removed for compatibility)
|
|
41
|
-
|
|
42
|
-
if (this.config.compress) {
|
|
43
|
-
await this.compressAssets(buildPath, results)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (this.config.removeUnusedCSS) {
|
|
47
|
-
await this.removeUnusedCSS(buildPath, results)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (this.config.optimizeImages) {
|
|
51
|
-
await this.optimizeImages(buildPath, results)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (this.config.bundleAnalysis) {
|
|
55
|
-
await this.analyzeBundles(buildPath, results)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Calculate final size and compression ratio
|
|
59
|
-
results.optimizedSize = await this.calculateDirectorySize(buildPath)
|
|
60
|
-
results.compressionRatio = results.originalSize > 0
|
|
61
|
-
? ((results.originalSize - results.optimizedSize) / results.originalSize) * 100
|
|
62
|
-
: 0
|
|
63
|
-
|
|
64
|
-
results.duration = Date.now() - startTime
|
|
65
|
-
|
|
66
|
-
buildLogger.success(`Optimization completed in ${buildLogger.formatDuration(results.duration)}`)
|
|
67
|
-
|
|
68
|
-
// Create optimization summary table
|
|
69
|
-
const optimizationData = results.optimizations.map(opt => ({
|
|
70
|
-
type: opt.type,
|
|
71
|
-
description: opt.description,
|
|
72
|
-
saved: buildLogger.formatSize(opt.sizeSaved)
|
|
73
|
-
}))
|
|
74
|
-
|
|
75
|
-
if (optimizationData.length > 0) {
|
|
76
|
-
buildLogger.table(
|
|
77
|
-
[
|
|
78
|
-
{ header: 'Optimization', key: 'type', width: 20, align: 'left', color: 'cyan' },
|
|
79
|
-
{ header: 'Description', key: 'description', width: 35, align: 'left' },
|
|
80
|
-
{ header: 'Size Saved', key: 'saved', width: 12, align: 'right', color: 'green' }
|
|
81
|
-
],
|
|
82
|
-
optimizationData
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return results
|
|
87
|
-
|
|
88
|
-
} catch (error) {
|
|
89
|
-
results.success = false
|
|
90
|
-
results.duration = Date.now() - startTime
|
|
91
|
-
results.error = error instanceof Error ? error.message : "Unknown optimization error"
|
|
92
|
-
|
|
93
|
-
buildLogger.error(`Optimization failed: ${results.error}`)
|
|
94
|
-
return results
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Minification methods removed for compatibility with Bun bundler
|
|
99
|
-
|
|
100
|
-
private async compressAssets(buildPath: string, results: OptimizationResult): Promise<void> {
|
|
101
|
-
buildLogger.step("Compressing assets...")
|
|
102
|
-
|
|
103
|
-
const files = this.getFilesRecursively(buildPath)
|
|
104
|
-
let compressedCount = 0
|
|
105
|
-
|
|
106
|
-
for (const file of files) {
|
|
107
|
-
const ext = extname(file).toLowerCase()
|
|
108
|
-
|
|
109
|
-
if (['.js', '.css', '.html', '.json', '.svg'].includes(ext)) {
|
|
110
|
-
try {
|
|
111
|
-
const content = readFileSync(file)
|
|
112
|
-
const compressed = gzipSync(content)
|
|
113
|
-
|
|
114
|
-
// Only create .gz file if it's significantly smaller
|
|
115
|
-
if (compressed.length < content.length * 0.9) {
|
|
116
|
-
writeFileSync(file + '.gz', compressed)
|
|
117
|
-
compressedCount++
|
|
118
|
-
}
|
|
119
|
-
} catch (error) {
|
|
120
|
-
// Silently skip files that can't be compressed
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
buildLogger.success(`Compressed ${compressedCount} files`)
|
|
126
|
-
results.optimizations.push({
|
|
127
|
-
type: 'compression',
|
|
128
|
-
description: `Created gzip versions for ${compressedCount} files`,
|
|
129
|
-
sizeSaved: 0
|
|
130
|
-
})
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
private async removeUnusedCSS(buildPath: string, results: OptimizationResult): Promise<void> {
|
|
134
|
-
buildLogger.step("Analyzing CSS...")
|
|
135
|
-
|
|
136
|
-
// This is a placeholder - real implementation would use PurgeCSS or similar
|
|
137
|
-
results.optimizations.push({
|
|
138
|
-
type: 'css-purging',
|
|
139
|
-
description: 'CSS purging not implemented yet',
|
|
140
|
-
sizeSaved: 0
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
private async optimizeImages(buildPath: string, results: OptimizationResult): Promise<void> {
|
|
145
|
-
buildLogger.step("Optimizing images...")
|
|
146
|
-
|
|
147
|
-
// This is a placeholder - real implementation would use imagemin or similar
|
|
148
|
-
results.optimizations.push({
|
|
149
|
-
type: 'image-optimization',
|
|
150
|
-
description: 'Image optimization not implemented yet',
|
|
151
|
-
sizeSaved: 0
|
|
152
|
-
})
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
private async analyzeBundles(buildPath: string, results: OptimizationResult): Promise<void> {
|
|
156
|
-
buildLogger.step("Analyzing bundles...")
|
|
157
|
-
|
|
158
|
-
const files = this.getFilesRecursively(buildPath)
|
|
159
|
-
const jsFiles = files.filter(f => extname(f) === '.js')
|
|
160
|
-
|
|
161
|
-
let totalJSSize = 0
|
|
162
|
-
for (const file of jsFiles) {
|
|
163
|
-
totalJSSize += statSync(file).size
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
results.optimizations.push({
|
|
167
|
-
type: 'bundle-analysis',
|
|
168
|
-
description: `Analyzed ${jsFiles.length} JS bundles (${(totalJSSize / 1024).toFixed(2)} KB total)`,
|
|
169
|
-
sizeSaved: 0
|
|
170
|
-
})
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private async calculateDirectorySize(dirPath: string): Promise<number> {
|
|
174
|
-
const files = this.getFilesRecursively(dirPath)
|
|
175
|
-
let totalSize = 0
|
|
176
|
-
|
|
177
|
-
for (const file of files) {
|
|
178
|
-
try {
|
|
179
|
-
totalSize += statSync(file).size
|
|
180
|
-
} catch (error) {
|
|
181
|
-
// Ignore files that can't be read
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return totalSize
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
private getFilesRecursively(dir: string): string[] {
|
|
189
|
-
const files: string[] = []
|
|
190
|
-
|
|
191
|
-
try {
|
|
192
|
-
const items = readdirSync(dir, { withFileTypes: true })
|
|
193
|
-
|
|
194
|
-
for (const item of items) {
|
|
195
|
-
const fullPath = join(dir, item.name)
|
|
196
|
-
|
|
197
|
-
if (item.isDirectory()) {
|
|
198
|
-
files.push(...this.getFilesRecursively(fullPath))
|
|
199
|
-
} else {
|
|
200
|
-
files.push(fullPath)
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
} catch (error) {
|
|
204
|
-
// Ignore directories that can't be read
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return files
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
async createOptimizationReport(result: OptimizationResult): Promise<string> {
|
|
211
|
-
const report = `
|
|
212
|
-
# Build Optimization Report
|
|
213
|
-
|
|
214
|
-
## Summary
|
|
215
|
-
- **Status**: ${result.success ? '✅ Success' : '❌ Failed'}
|
|
216
|
-
- **Duration**: ${result.duration}ms
|
|
217
|
-
- **Original Size**: ${(result.originalSize / 1024).toFixed(2)} KB
|
|
218
|
-
- **Optimized Size**: ${(result.optimizedSize / 1024).toFixed(2)} KB
|
|
219
|
-
- **Size Reduction**: ${result.compressionRatio.toFixed(2)}%
|
|
220
|
-
|
|
221
|
-
## Optimizations Applied
|
|
222
|
-
|
|
223
|
-
${result.optimizations.map(opt =>
|
|
224
|
-
`### ${opt.type.charAt(0).toUpperCase() + opt.type.slice(1)}
|
|
225
|
-
- ${opt.description}
|
|
226
|
-
- Size Saved: ${(opt.sizeSaved / 1024).toFixed(2)} KB`
|
|
227
|
-
).join('\n\n')}
|
|
228
|
-
|
|
229
|
-
${result.error ? `## Error\n${result.error}` : ''}
|
|
230
|
-
|
|
231
|
-
Generated at: ${new Date().toISOString()}
|
|
232
|
-
`
|
|
233
|
-
|
|
234
|
-
return report.trim()
|
|
235
|
-
}
|
|
1
|
+
import { readFileSync, writeFileSync, statSync, readdirSync } from "fs"
|
|
2
|
+
import { join, extname } from "path"
|
|
3
|
+
import { gzipSync } from "zlib"
|
|
4
|
+
import type { OptimizationConfig, OptimizationResult } from "../types/build"
|
|
5
|
+
import { buildLogger } from "../utils/build-logger"
|
|
6
|
+
|
|
7
|
+
export interface OptimizerConfig {
|
|
8
|
+
treeshake: boolean
|
|
9
|
+
compress: boolean
|
|
10
|
+
removeUnusedCSS: boolean
|
|
11
|
+
optimizeImages: boolean
|
|
12
|
+
bundleAnalysis: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class Optimizer {
|
|
16
|
+
private config: OptimizerConfig
|
|
17
|
+
|
|
18
|
+
constructor(config: OptimizerConfig) {
|
|
19
|
+
this.config = config
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async optimize(buildPath: string): Promise<OptimizationResult> {
|
|
23
|
+
buildLogger.section('Build Optimization', '🔧')
|
|
24
|
+
|
|
25
|
+
const startTime = Date.now()
|
|
26
|
+
const results: OptimizationResult = {
|
|
27
|
+
success: true,
|
|
28
|
+
duration: 0,
|
|
29
|
+
originalSize: 0,
|
|
30
|
+
optimizedSize: 0,
|
|
31
|
+
compressionRatio: 0,
|
|
32
|
+
optimizations: []
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
// Get original size
|
|
37
|
+
results.originalSize = await this.calculateDirectorySize(buildPath)
|
|
38
|
+
buildLogger.step(`Original size: ${buildLogger.formatSize(results.originalSize)}`)
|
|
39
|
+
|
|
40
|
+
// Apply optimizations (minification removed for compatibility)
|
|
41
|
+
|
|
42
|
+
if (this.config.compress) {
|
|
43
|
+
await this.compressAssets(buildPath, results)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (this.config.removeUnusedCSS) {
|
|
47
|
+
await this.removeUnusedCSS(buildPath, results)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (this.config.optimizeImages) {
|
|
51
|
+
await this.optimizeImages(buildPath, results)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (this.config.bundleAnalysis) {
|
|
55
|
+
await this.analyzeBundles(buildPath, results)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Calculate final size and compression ratio
|
|
59
|
+
results.optimizedSize = await this.calculateDirectorySize(buildPath)
|
|
60
|
+
results.compressionRatio = results.originalSize > 0
|
|
61
|
+
? ((results.originalSize - results.optimizedSize) / results.originalSize) * 100
|
|
62
|
+
: 0
|
|
63
|
+
|
|
64
|
+
results.duration = Date.now() - startTime
|
|
65
|
+
|
|
66
|
+
buildLogger.success(`Optimization completed in ${buildLogger.formatDuration(results.duration)}`)
|
|
67
|
+
|
|
68
|
+
// Create optimization summary table
|
|
69
|
+
const optimizationData = results.optimizations.map(opt => ({
|
|
70
|
+
type: opt.type,
|
|
71
|
+
description: opt.description,
|
|
72
|
+
saved: buildLogger.formatSize(opt.sizeSaved)
|
|
73
|
+
}))
|
|
74
|
+
|
|
75
|
+
if (optimizationData.length > 0) {
|
|
76
|
+
buildLogger.table(
|
|
77
|
+
[
|
|
78
|
+
{ header: 'Optimization', key: 'type', width: 20, align: 'left', color: 'cyan' },
|
|
79
|
+
{ header: 'Description', key: 'description', width: 35, align: 'left' },
|
|
80
|
+
{ header: 'Size Saved', key: 'saved', width: 12, align: 'right', color: 'green' }
|
|
81
|
+
],
|
|
82
|
+
optimizationData
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return results
|
|
87
|
+
|
|
88
|
+
} catch (error) {
|
|
89
|
+
results.success = false
|
|
90
|
+
results.duration = Date.now() - startTime
|
|
91
|
+
results.error = error instanceof Error ? error.message : "Unknown optimization error"
|
|
92
|
+
|
|
93
|
+
buildLogger.error(`Optimization failed: ${results.error}`)
|
|
94
|
+
return results
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Minification methods removed for compatibility with Bun bundler
|
|
99
|
+
|
|
100
|
+
private async compressAssets(buildPath: string, results: OptimizationResult): Promise<void> {
|
|
101
|
+
buildLogger.step("Compressing assets...")
|
|
102
|
+
|
|
103
|
+
const files = this.getFilesRecursively(buildPath)
|
|
104
|
+
let compressedCount = 0
|
|
105
|
+
|
|
106
|
+
for (const file of files) {
|
|
107
|
+
const ext = extname(file).toLowerCase()
|
|
108
|
+
|
|
109
|
+
if (['.js', '.css', '.html', '.json', '.svg'].includes(ext)) {
|
|
110
|
+
try {
|
|
111
|
+
const content = readFileSync(file)
|
|
112
|
+
const compressed = gzipSync(content)
|
|
113
|
+
|
|
114
|
+
// Only create .gz file if it's significantly smaller
|
|
115
|
+
if (compressed.length < content.length * 0.9) {
|
|
116
|
+
writeFileSync(file + '.gz', compressed)
|
|
117
|
+
compressedCount++
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
// Silently skip files that can't be compressed
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
buildLogger.success(`Compressed ${compressedCount} files`)
|
|
126
|
+
results.optimizations.push({
|
|
127
|
+
type: 'compression',
|
|
128
|
+
description: `Created gzip versions for ${compressedCount} files`,
|
|
129
|
+
sizeSaved: 0
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private async removeUnusedCSS(buildPath: string, results: OptimizationResult): Promise<void> {
|
|
134
|
+
buildLogger.step("Analyzing CSS...")
|
|
135
|
+
|
|
136
|
+
// This is a placeholder - real implementation would use PurgeCSS or similar
|
|
137
|
+
results.optimizations.push({
|
|
138
|
+
type: 'css-purging',
|
|
139
|
+
description: 'CSS purging not implemented yet',
|
|
140
|
+
sizeSaved: 0
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private async optimizeImages(buildPath: string, results: OptimizationResult): Promise<void> {
|
|
145
|
+
buildLogger.step("Optimizing images...")
|
|
146
|
+
|
|
147
|
+
// This is a placeholder - real implementation would use imagemin or similar
|
|
148
|
+
results.optimizations.push({
|
|
149
|
+
type: 'image-optimization',
|
|
150
|
+
description: 'Image optimization not implemented yet',
|
|
151
|
+
sizeSaved: 0
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private async analyzeBundles(buildPath: string, results: OptimizationResult): Promise<void> {
|
|
156
|
+
buildLogger.step("Analyzing bundles...")
|
|
157
|
+
|
|
158
|
+
const files = this.getFilesRecursively(buildPath)
|
|
159
|
+
const jsFiles = files.filter(f => extname(f) === '.js')
|
|
160
|
+
|
|
161
|
+
let totalJSSize = 0
|
|
162
|
+
for (const file of jsFiles) {
|
|
163
|
+
totalJSSize += statSync(file).size
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
results.optimizations.push({
|
|
167
|
+
type: 'bundle-analysis',
|
|
168
|
+
description: `Analyzed ${jsFiles.length} JS bundles (${(totalJSSize / 1024).toFixed(2)} KB total)`,
|
|
169
|
+
sizeSaved: 0
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private async calculateDirectorySize(dirPath: string): Promise<number> {
|
|
174
|
+
const files = this.getFilesRecursively(dirPath)
|
|
175
|
+
let totalSize = 0
|
|
176
|
+
|
|
177
|
+
for (const file of files) {
|
|
178
|
+
try {
|
|
179
|
+
totalSize += statSync(file).size
|
|
180
|
+
} catch (error) {
|
|
181
|
+
// Ignore files that can't be read
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return totalSize
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private getFilesRecursively(dir: string): string[] {
|
|
189
|
+
const files: string[] = []
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const items = readdirSync(dir, { withFileTypes: true })
|
|
193
|
+
|
|
194
|
+
for (const item of items) {
|
|
195
|
+
const fullPath = join(dir, item.name)
|
|
196
|
+
|
|
197
|
+
if (item.isDirectory()) {
|
|
198
|
+
files.push(...this.getFilesRecursively(fullPath))
|
|
199
|
+
} else {
|
|
200
|
+
files.push(fullPath)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
// Ignore directories that can't be read
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return files
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async createOptimizationReport(result: OptimizationResult): Promise<string> {
|
|
211
|
+
const report = `
|
|
212
|
+
# Build Optimization Report
|
|
213
|
+
|
|
214
|
+
## Summary
|
|
215
|
+
- **Status**: ${result.success ? '✅ Success' : '❌ Failed'}
|
|
216
|
+
- **Duration**: ${result.duration}ms
|
|
217
|
+
- **Original Size**: ${(result.originalSize / 1024).toFixed(2)} KB
|
|
218
|
+
- **Optimized Size**: ${(result.optimizedSize / 1024).toFixed(2)} KB
|
|
219
|
+
- **Size Reduction**: ${result.compressionRatio.toFixed(2)}%
|
|
220
|
+
|
|
221
|
+
## Optimizations Applied
|
|
222
|
+
|
|
223
|
+
${result.optimizations.map(opt =>
|
|
224
|
+
`### ${opt.type.charAt(0).toUpperCase() + opt.type.slice(1)}
|
|
225
|
+
- ${opt.description}
|
|
226
|
+
- Size Saved: ${(opt.sizeSaved / 1024).toFixed(2)} KB`
|
|
227
|
+
).join('\n\n')}
|
|
228
|
+
|
|
229
|
+
${result.error ? `## Error\n${result.error}` : ''}
|
|
230
|
+
|
|
231
|
+
Generated at: ${new Date().toISOString()}
|
|
232
|
+
`
|
|
233
|
+
|
|
234
|
+
return report.trim()
|
|
235
|
+
}
|
|
236
236
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CliCommand, CliContext, CliArgument, CliOption } from "../plugins/types"
|
|
2
|
-
import {
|
|
3
|
-
import { logger } from "
|
|
2
|
+
import { fluxStackConfig } from "@config"
|
|
3
|
+
import { logger } from "@core/utils/logger"
|
|
4
4
|
import { createTimer, formatBytes, isProduction, isDevelopment } from "../utils/helpers"
|
|
5
5
|
|
|
6
6
|
export class CliCommandRegistry {
|
|
@@ -9,7 +9,7 @@ export class CliCommandRegistry {
|
|
|
9
9
|
private context: CliContext
|
|
10
10
|
|
|
11
11
|
constructor() {
|
|
12
|
-
const config =
|
|
12
|
+
const config = fluxStackConfig
|
|
13
13
|
|
|
14
14
|
this.context = {
|
|
15
15
|
config,
|
|
@@ -321,4 +321,6 @@ Use "flux help <command>" for more information about a specific command.`)
|
|
|
321
321
|
}
|
|
322
322
|
|
|
323
323
|
// Global registry instance
|
|
324
|
-
export const cliRegistry = new CliCommandRegistry()
|
|
324
|
+
export const cliRegistry = new CliCommandRegistry()
|
|
325
|
+
|
|
326
|
+
export type CLICommand = CliCommand
|