create-fluxstack 1.8.3 → 1.9.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.
@@ -79,34 +79,18 @@ export class Bundler {
79
79
  let liveComponentsGenerator: any = null
80
80
 
81
81
  try {
82
- // šŸš€ PRE-BUILD: Auto-generate Live Components registration
83
- const generatorModule = await import('./live-components-generator')
84
- liveComponentsGenerator = generatorModule.liveComponentsGenerator
85
- const discoveredComponents = await liveComponentsGenerator.preBuild()
86
-
87
- // šŸ”Œ PRE-BUILD: Auto-generate FluxStack Plugins registration
88
- const pluginsGeneratorModule = await import('./flux-plugins-generator')
89
- const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
90
- const discoveredPlugins = await fluxPluginsGenerator.preBuild()
91
-
82
+ // Run pre-build steps
83
+ liveComponentsGenerator = await this.runPreBuildSteps()
84
+
92
85
  // Ensure output directory exists
93
- if (!existsSync(this.config.outDir)) {
94
- mkdirSync(this.config.outDir, { recursive: true })
95
- }
86
+ this.ensureOutputDirectory()
96
87
 
97
- const external = [
98
- "@tailwindcss/vite",
99
- "tailwindcss",
100
- "lightningcss",
101
- "vite",
102
- "@vitejs/plugin-react",
103
- ...(this.config.external || []),
104
- ...(options.external || [])
105
- ]
88
+ // Get external dependencies
89
+ const external = this.getExternalDependencies(options)
106
90
 
107
91
  const buildArgs = [
108
- "bun", "build",
109
- entryPoint,
92
+ "bun", "build",
93
+ entryPoint,
110
94
  "--outdir", this.config.outDir,
111
95
  "--target", this.config.target,
112
96
  ...external.flatMap(ext => ["--external", ext])
@@ -132,20 +116,12 @@ export class Bundler {
132
116
  const exitCode = await buildProcess.exited
133
117
  const duration = Date.now() - startTime
134
118
 
135
- // 🧹 POST-BUILD: Handle auto-generated registration file
136
- // (liveComponentsGenerator already available from above)
137
-
138
119
  if (exitCode === 0) {
139
120
  buildLogger.success(`Server bundle completed in ${buildLogger.formatDuration(duration)}`)
140
-
141
- // Keep generated files for production (they're now baked into bundle)
142
- await liveComponentsGenerator.postBuild(false)
143
-
144
- // Cleanup plugins registry
145
- const pluginsGeneratorModule = await import('./flux-plugins-generator')
146
- const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
147
- await fluxPluginsGenerator.postBuild(false)
148
-
121
+
122
+ // Run post-build cleanup
123
+ await this.runPostBuildCleanup(liveComponentsGenerator)
124
+
149
125
  return {
150
126
  success: true,
151
127
  duration,
@@ -154,15 +130,10 @@ export class Bundler {
154
130
  }
155
131
  } else {
156
132
  buildLogger.error("Server bundle failed")
157
-
158
- // Restore original files since build failed
159
- await liveComponentsGenerator.postBuild(false)
160
-
161
- // Restore plugins registry
162
- const pluginsGeneratorModule = await import('./flux-plugins-generator')
163
- const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
164
- await fluxPluginsGenerator.postBuild(false)
165
-
133
+
134
+ // Run post-build cleanup
135
+ await this.runPostBuildCleanup(liveComponentsGenerator)
136
+
166
137
  const stderr = await new Response(buildProcess.stderr).text()
167
138
  return {
168
139
  success: false,
@@ -172,21 +143,139 @@ export class Bundler {
172
143
  }
173
144
  } catch (error) {
174
145
  const duration = Date.now() - startTime
175
-
146
+
147
+ // 🧹 CLEANUP: Restore original files on error
148
+ try {
149
+ await this.runPostBuildCleanup(liveComponentsGenerator)
150
+ } catch (cleanupError) {
151
+ buildLogger.warn(`Failed to cleanup generated files: ${cleanupError}`)
152
+ }
153
+
154
+ return {
155
+ success: false,
156
+ duration,
157
+ error: error instanceof Error ? error.message : "Unknown error"
158
+ }
159
+ }
160
+ }
161
+
162
+ async compileToExecutable(entryPoint: string, outputName: string = "app", options: BundleOptions = {}): Promise<BundleResult> {
163
+ buildLogger.section('Executable Build', 'šŸ“¦')
164
+
165
+ const startTime = Date.now()
166
+ let liveComponentsGenerator: any = null
167
+
168
+ try {
169
+ // Run pre-build steps
170
+ liveComponentsGenerator = await this.runPreBuildSteps()
171
+
172
+ // Ensure output directory exists
173
+ this.ensureOutputDirectory()
174
+
175
+ const outputPath = join(this.config.outDir, outputName)
176
+
177
+ // Get external dependencies
178
+ const external = this.getExternalDependencies(options)
179
+
180
+ const buildArgs = [
181
+ "bun", "build",
182
+ entryPoint,
183
+ "--compile",
184
+ "--outfile", outputPath,
185
+ "--target", this.config.target,
186
+ ...external.flatMap(ext => ["--external", ext])
187
+ ]
188
+
189
+ if (this.config.sourceMaps) {
190
+ buildArgs.push("--sourcemap")
191
+ }
192
+
193
+ if (this.config.minify) {
194
+ buildArgs.push("--minify")
195
+ }
196
+
197
+ // Add Windows-specific options if provided
198
+ if (options.executable?.windows) {
199
+ const winOpts = options.executable.windows
200
+ if (winOpts.hideConsole) {
201
+ buildArgs.push("--windows-hide-console")
202
+ }
203
+ if (winOpts.icon) {
204
+ buildArgs.push("--windows-icon", winOpts.icon)
205
+ }
206
+ if (winOpts.title) {
207
+ buildArgs.push("--windows-title", winOpts.title)
208
+ }
209
+ if (winOpts.publisher) {
210
+ buildArgs.push("--windows-publisher", winOpts.publisher)
211
+ }
212
+ if (winOpts.version) {
213
+ buildArgs.push("--windows-version", winOpts.version)
214
+ }
215
+ if (winOpts.description) {
216
+ buildArgs.push("--windows-description", winOpts.description)
217
+ }
218
+ if (winOpts.copyright) {
219
+ buildArgs.push("--windows-copyright", winOpts.copyright)
220
+ }
221
+ }
222
+
223
+ // Add custom build arguments if provided
224
+ if (options.executable?.customArgs) {
225
+ buildArgs.push(...options.executable.customArgs)
226
+ }
227
+
228
+ buildLogger.step(`Compiling ${entryPoint} to ${outputPath}...`)
229
+
230
+ const buildProcess = spawn({
231
+ cmd: buildArgs,
232
+ stdout: "pipe",
233
+ stderr: "pipe",
234
+ env: {
235
+ ...process.env,
236
+ NODE_ENV: 'production',
237
+ ...options.env
238
+ }
239
+ })
240
+
241
+ const exitCode = await buildProcess.exited
242
+ const duration = Date.now() - startTime
243
+
244
+ if (exitCode === 0) {
245
+ buildLogger.success(`Executable compiled in ${buildLogger.formatDuration(duration)}`)
246
+
247
+ // Run post-build cleanup
248
+ await this.runPostBuildCleanup(liveComponentsGenerator)
249
+
250
+ return {
251
+ success: true,
252
+ duration,
253
+ outputPath,
254
+ entryPoint: outputPath
255
+ }
256
+ } else {
257
+ buildLogger.error("Executable compilation failed")
258
+
259
+ // Run post-build cleanup
260
+ await this.runPostBuildCleanup(liveComponentsGenerator)
261
+
262
+ const stderr = await new Response(buildProcess.stderr).text()
263
+ return {
264
+ success: false,
265
+ duration,
266
+ error: stderr || "Executable compilation failed"
267
+ }
268
+ }
269
+ } catch (error) {
270
+ const duration = Date.now() - startTime
271
+
176
272
  // 🧹 CLEANUP: Restore original files on error
177
273
  try {
178
- if (liveComponentsGenerator) {
179
- await liveComponentsGenerator.postBuild(false)
180
- }
181
-
182
- // Cleanup plugins registry
183
- const pluginsGeneratorModule = await import('./flux-plugins-generator')
184
- const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
185
- await fluxPluginsGenerator.postBuild(false)
274
+ await this.runPostBuildCleanup(liveComponentsGenerator)
186
275
  } catch (cleanupError) {
187
276
  buildLogger.warn(`Failed to cleanup generated files: ${cleanupError}`)
188
277
  }
189
-
278
+
190
279
  return {
191
280
  success: false,
192
281
  duration,
@@ -195,6 +284,61 @@ export class Bundler {
195
284
  }
196
285
  }
197
286
 
287
+ /**
288
+ * Get list of external dependencies that should not be bundled
289
+ */
290
+ private getExternalDependencies(options: BundleOptions = {}): string[] {
291
+ return [
292
+ "@tailwindcss/vite",
293
+ "tailwindcss",
294
+ "lightningcss",
295
+ "vite",
296
+ "@vitejs/plugin-react",
297
+ "rollup",
298
+ ...(this.config.external || []),
299
+ ...(options.external || [])
300
+ ]
301
+ }
302
+
303
+ /**
304
+ * Ensure output directory exists
305
+ */
306
+ private ensureOutputDirectory(): void {
307
+ if (!existsSync(this.config.outDir)) {
308
+ mkdirSync(this.config.outDir, { recursive: true })
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Run pre-build steps (Live Components and Plugins generation)
314
+ */
315
+ private async runPreBuildSteps(): Promise<any> {
316
+ // šŸš€ PRE-BUILD: Auto-generate Live Components registration
317
+ const generatorModule = await import('./live-components-generator')
318
+ const liveComponentsGenerator = generatorModule.liveComponentsGenerator
319
+ await liveComponentsGenerator.preBuild()
320
+
321
+ // šŸ”Œ PRE-BUILD: Auto-generate FluxStack Plugins registration
322
+ const pluginsGeneratorModule = await import('./flux-plugins-generator')
323
+ const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
324
+ await fluxPluginsGenerator.preBuild()
325
+
326
+ return liveComponentsGenerator
327
+ }
328
+
329
+ /**
330
+ * Run post-build cleanup
331
+ */
332
+ private async runPostBuildCleanup(liveComponentsGenerator: any): Promise<void> {
333
+ if (liveComponentsGenerator) {
334
+ await liveComponentsGenerator.postBuild(false)
335
+ }
336
+
337
+ const pluginsGeneratorModule = await import('./flux-plugins-generator')
338
+ const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
339
+ await fluxPluginsGenerator.postBuild(false)
340
+ }
341
+
198
342
  private async getClientAssets(): Promise<string[]> {
199
343
  // This would analyze the build output to get asset information
200
344
  // For now, return empty array - can be enhanced later
@@ -47,6 +47,10 @@ export class FluxStackBuilder {
47
47
  return await this.bundler.bundleServer("app/server/index.ts")
48
48
  }
49
49
 
50
+ async buildExecutable(outputName: string = "CLauncher", options?: import("../types/build").BundleOptions) {
51
+ return await this.bundler.compileToExecutable("app/server/index.ts", outputName, options)
52
+ }
53
+
50
54
  async createDockerFiles() {
51
55
  buildLogger.section('Docker Configuration', '🐳')
52
56
 
@@ -131,7 +131,7 @@ export default {{camelName}}Config
131
131
  },
132
132
  {
133
133
  path: 'plugins/{{name}}/index.ts',
134
- content: `import type { FluxStackPlugin, PluginContext } from '@/core/types/plugin'
134
+ content: `import type { ErrorContext, FluxStack, PluginContext, RequestContext, ResponseContext } from "@/core/plugins/types"
135
135
  // āœ… Plugin imports its own configuration
136
136
  import { {{camelName}}Config } from './config'
137
137
 
@@ -139,7 +139,7 @@ import { {{camelName}}Config } from './config'
139
139
  * {{pascalName}} Plugin
140
140
  * {{description}}
141
141
  */
142
- export class {{pascalName}}Plugin implements FluxStackPlugin {
142
+ export class {{pascalName}}Plugin implements FluxStack.Plugin {
143
143
  name = '{{name}}'
144
144
  version = '1.0.0'
145
145
 
@@ -173,7 +173,7 @@ export class {{pascalName}}Plugin implements FluxStackPlugin {
173
173
  /**
174
174
  * Request hook - called on each request
175
175
  */
176
- async onRequest?(context: PluginContext, request: Request): Promise<void> {
176
+ async onRequest?(context: RequestContext): Promise<void> {
177
177
  if (!{{camelName}}Config.enabled) return
178
178
 
179
179
  // Add request processing logic
@@ -182,7 +182,7 @@ export class {{pascalName}}Plugin implements FluxStackPlugin {
182
182
  /**
183
183
  * Response hook - called on each response
184
184
  */
185
- async onResponse?(context: PluginContext, response: Response): Promise<void> {
185
+ async onResponse?(context: ResponseContext): Promise<void> {
186
186
  if (!{{camelName}}Config.enabled) return
187
187
 
188
188
  // Add response processing logic
@@ -191,8 +191,8 @@ export class {{pascalName}}Plugin implements FluxStackPlugin {
191
191
  /**
192
192
  * Error hook - called when errors occur
193
193
  */
194
- async onError?(context: PluginContext, error: Error): Promise<void> {
195
- console.error(\`[{{name}}] Error:\`, error)
194
+ async onError?(context: ErrorContext): Promise<void> {
195
+ console.error(\`[{{name}}] Error:\`, context.error)
196
196
 
197
197
  // Add error handling logic
198
198
  }
package/core/cli/index.ts CHANGED
@@ -498,6 +498,106 @@ Examples:
498
498
  await builder.buildServer()
499
499
  }
500
500
  })
501
+
502
+ // Build:exe command (compile to executable)
503
+ cliRegistry.register({
504
+ name: 'build:exe',
505
+ description: 'Compile application to standalone executable',
506
+ category: 'Build',
507
+ usage: 'flux build:exe [options]',
508
+ examples: [
509
+ 'flux build:exe # Compile to CLauncher executable',
510
+ 'flux build:exe --name MyApp # Compile with custom name',
511
+ 'flux build:exe --windows-title "My Launcher" # Set Windows executable title',
512
+ 'flux build:exe --windows-hide-console # Hide console window on Windows'
513
+ ],
514
+ options: [
515
+ {
516
+ name: 'name',
517
+ short: 'n',
518
+ description: 'Name for the executable file',
519
+ type: 'string',
520
+ default: 'CLauncher'
521
+ },
522
+ {
523
+ name: 'windows-hide-console',
524
+ description: 'Hide console window on Windows',
525
+ type: 'boolean',
526
+ default: false
527
+ },
528
+ {
529
+ name: 'windows-icon',
530
+ description: 'Path to .ico file for Windows executable',
531
+ type: 'string'
532
+ },
533
+ {
534
+ name: 'windows-title',
535
+ description: 'Product name for Windows executable',
536
+ type: 'string'
537
+ },
538
+ {
539
+ name: 'windows-publisher',
540
+ description: 'Company name for Windows executable',
541
+ type: 'string'
542
+ },
543
+ {
544
+ name: 'windows-version',
545
+ description: 'Version string for Windows executable (e.g. 1.2.3.4)',
546
+ type: 'string'
547
+ },
548
+ {
549
+ name: 'windows-description',
550
+ description: 'Description for Windows executable',
551
+ type: 'string'
552
+ },
553
+ {
554
+ name: 'windows-copyright',
555
+ description: 'Copyright string for Windows executable',
556
+ type: 'string'
557
+ }
558
+ ],
559
+ handler: async (args, options, context) => {
560
+ const config = getConfigSync()
561
+ const builder = new FluxStackBuilder(config)
562
+
563
+ // Build executable options from CLI args
564
+ const executableOptions: import("../types/build").BundleOptions = {
565
+ executable: {
566
+ windows: {
567
+ hideConsole: options['windows-hide-console'],
568
+ icon: options['windows-icon'],
569
+ title: options['windows-title'],
570
+ publisher: options['windows-publisher'],
571
+ version: options['windows-version'],
572
+ description: options['windows-description'],
573
+ copyright: options['windows-copyright']
574
+ }
575
+ }
576
+ }
577
+
578
+ const result = await builder.buildExecutable(options.name, executableOptions)
579
+
580
+ if (result.success) {
581
+ console.log(`\nāœ… Executable created successfully: ${result.outputPath}`)
582
+
583
+ // Show applied Windows options
584
+ if (process.platform === 'win32' && Object.values(executableOptions.executable?.windows || {}).some(v => v)) {
585
+ console.log('\nšŸ“¦ Windows executable options applied:')
586
+ const winOpts = executableOptions.executable?.windows
587
+ if (winOpts?.hideConsole) console.log(' • Console window hidden')
588
+ if (winOpts?.icon) console.log(` • Icon: ${winOpts.icon}`)
589
+ if (winOpts?.title) console.log(` • Title: ${winOpts.title}`)
590
+ if (winOpts?.publisher) console.log(` • Publisher: ${winOpts.publisher}`)
591
+ if (winOpts?.version) console.log(` • Version: ${winOpts.version}`)
592
+ if (winOpts?.description) console.log(` • Description: ${winOpts.description}`)
593
+ if (winOpts?.copyright) console.log(` • Copyright: ${winOpts.copyright}`)
594
+ }
595
+ } else {
596
+ console.error(`\nāŒ Compilation failed: ${result.error}`)
597
+ process.exit(1)
598
+ }
599
+ }
600
+ })
501
601
  }
502
602
 
503
603
  // Main CLI logic
@@ -1,6 +1,6 @@
1
1
  // šŸ”„ Auto-generated Live Components Registration
2
2
  // This file is automatically generated during build time - DO NOT EDIT MANUALLY
3
- // Generated at: 2025-11-12T22:57:08.521Z
3
+ // Generated at: 2025-11-18T01:48:36.705Z
4
4
 
5
5
  import { LiveClockComponent } from "@/app/server/live/LiveClockComponent"
6
6
  import { componentRegistry } from "@/core/server/live/ComponentRegistry"
@@ -83,6 +83,27 @@ export interface BundleOptions {
83
83
  external?: string[]
84
84
  minify?: boolean
85
85
  sourceMaps?: boolean
86
+ executable?: ExecutableOptions
87
+ }
88
+
89
+ /**
90
+ * Options for compiling standalone executables
91
+ * Note: Cross-platform compilation is limited - executables are built for the current platform.
92
+ * To build for different platforms, run the build on that platform.
93
+ */
94
+ export interface ExecutableOptions {
95
+ // Windows-specific options
96
+ windows?: {
97
+ hideConsole?: boolean
98
+ icon?: string
99
+ title?: string
100
+ publisher?: string
101
+ version?: string
102
+ description?: string
103
+ copyright?: string
104
+ }
105
+ // Additional custom build arguments
106
+ customArgs?: string[]
86
107
  }
87
108
 
88
109
  export interface OptimizationOptions {
@@ -1,6 +1,6 @@
1
- /**
2
- * FluxStack Framework Version
3
- * Single source of truth for version number
4
- * Auto-synced with package.json
5
- */
6
- export const FLUXSTACK_VERSION = '1.8.3'
1
+ /**
2
+ * FluxStack Framework Version
3
+ * Single source of truth for version number
4
+ * Auto-synced with package.json
5
+ */
6
+ export const FLUXSTACK_VERSION = '1.9.1'
@@ -165,20 +165,20 @@ Plugins can intercept and modify requests using hooks:
165
165
 
166
166
  \`\`\`typescript
167
167
  // plugins/my-plugin/index.ts
168
- import type { FluxStackPlugin, PluginContext } from '@/core/types/plugin'
168
+ import type { FluxStack, PluginContext, RequestContext, ResponseContext } from "@/core/plugins/types"
169
169
 
170
- export class MyPlugin implements FluxStackPlugin {
170
+ export class MyPlugin implements FluxStack.Plugin {
171
171
  name = 'my-plugin'
172
172
  version = FLUXSTACK_VERSION
173
173
 
174
174
  // Intercept every request
175
- async onRequest(context: PluginContext, request: Request): Promise<void> {
175
+ async onRequest(context: PluginContext): Promise<void> {
176
176
  // Example: Add custom headers
177
177
  const url = (() => {
178
178
  try {
179
- return new URL(request.url)
179
+ return new URL(PluginContext.request.url)
180
180
  } catch {
181
- const host = request.headers.get('host') || 'localhost'
181
+ const host = PluginContext.request.headers.get('host') || 'localhost'
182
182
  return new URL(request.url, \`http://\${host}\`)
183
183
  }
184
184
  })()
@@ -192,8 +192,8 @@ export class MyPlugin implements FluxStackPlugin {
192
192
  }
193
193
 
194
194
  // Intercept every response
195
- async onResponse(context: PluginContext, response: Response): Promise<void> {
196
- console.log(\`[\${this.name}] Response status: \${response.status}\`)
195
+ async onResponse(context: PluginContext): Promise<void> {
196
+ console.log(\`[\${this.name}] Response status: \${PluginContext.response.status}\`)
197
197
  }
198
198
 
199
199
  // Handle errors
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fluxstack",
3
- "version": "1.8.3",
3
+ "version": "1.9.1",
4
4
  "description": "⚔ Revolutionary full-stack TypeScript framework with Declarative Config System, Elysia + React + Bun",
5
5
  "keywords": [
6
6
  "framework",
@@ -31,6 +31,7 @@
31
31
  "build": "cross-env NODE_ENV=production bun run core/cli/index.ts build",
32
32
  "build:frontend": "bun run core/cli/index.ts build:frontend",
33
33
  "build:backend": "bun run core/cli/index.ts build:backend",
34
+ "build:exe": "cross-env NODE_ENV=production && bun run core/cli/index.ts build && bun run core/cli/index.ts build:exe",
34
35
  "start": "bun run core/cli/index.ts start",
35
36
  "start:frontend": "bun run app/client/frontend-only.ts",
36
37
  "start:backend": "bun run app/server/backend-only.ts",
package/tsconfig.json CHANGED
@@ -46,6 +46,7 @@
46
46
  "examples/**/*",
47
47
  "**/*.test.ts",
48
48
  "**/__tests__/**/*",
49
- "run-env-tests.ts"
49
+ "run-env-tests.ts",
50
+ "plugins/**/scripts/**/*"
50
51
  ]
51
52
  }