create-fluxstack 1.16.0 → 1.17.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/CHANGELOG.md +80 -0
- package/app/client/src/App.tsx +8 -0
- package/app/client/src/live/AuthDemo.tsx +4 -4
- package/core/build/bundler.ts +40 -26
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +92 -21
- package/core/cli/command-registry.ts +44 -46
- package/core/cli/commands/build.ts +11 -6
- package/core/cli/commands/create.ts +7 -5
- package/core/cli/commands/dev.ts +6 -5
- package/core/cli/commands/help.ts +3 -2
- package/core/cli/commands/make-plugin.ts +8 -7
- package/core/cli/commands/plugin-add.ts +60 -43
- package/core/cli/commands/plugin-deps.ts +73 -57
- package/core/cli/commands/plugin-list.ts +44 -41
- package/core/cli/commands/plugin-remove.ts +33 -22
- package/core/cli/generators/component.ts +770 -769
- package/core/cli/generators/controller.ts +9 -8
- package/core/cli/generators/index.ts +148 -146
- package/core/cli/generators/interactive.ts +228 -227
- package/core/cli/generators/plugin.ts +11 -10
- package/core/cli/generators/prompts.ts +83 -82
- package/core/cli/generators/route.ts +7 -6
- package/core/cli/generators/service.ts +10 -9
- package/core/cli/generators/template-engine.ts +2 -1
- package/core/cli/generators/types.ts +7 -7
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +9 -8
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/standalone.ts +18 -17
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +1 -0
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +73 -113
- package/core/framework/types.ts +2 -2
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +6 -3
- package/core/plugins/built-in/monitoring/index.ts +110 -68
- package/core/plugins/built-in/static/index.ts +2 -2
- package/core/plugins/built-in/swagger/index.ts +9 -9
- package/core/plugins/built-in/vite/index.ts +3 -3
- package/core/plugins/built-in/vite/vite-dev.ts +3 -3
- package/core/plugins/config.ts +50 -47
- package/core/plugins/discovery.ts +10 -4
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +206 -203
- package/core/plugins/manager.ts +21 -20
- package/core/plugins/registry.ts +76 -12
- package/core/plugins/types.ts +14 -14
- package/core/server/framework.ts +3 -189
- package/core/server/live/auto-generated-components.ts +11 -29
- package/core/server/live/index.ts +41 -31
- package/core/server/live/websocket-plugin.ts +11 -1
- package/core/server/middleware/elysia-helpers.ts +16 -15
- package/core/server/middleware/errorHandling.ts +14 -14
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +181 -180
- package/core/server/plugins/static-files-plugin.ts +4 -3
- package/core/server/plugins/swagger.ts +11 -8
- package/core/server/rooms/RoomBroadcaster.ts +11 -10
- package/core/server/rooms/RoomSystem.ts +14 -11
- package/core/server/services/BaseService.ts +7 -7
- package/core/server/services/ServiceContainer.ts +5 -5
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +28 -27
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/config.ts +5 -5
- package/core/types/index.ts +1 -1
- package/core/types/plugin.ts +2 -2
- package/core/types/types.ts +3 -3
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +10 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +30 -20
- package/core/utils/errors/index.ts +54 -46
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +19 -16
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +2 -2
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +13 -3
- package/core/utils/logger/startup-banner.ts +2 -2
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +67 -66
- package/core/utils/version.ts +1 -1
- package/package.json +104 -100
- package/playwright-report/index.html +85 -0
- package/playwright.config.ts +31 -0
- 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/package.json +65 -65
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +6 -5
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +6 -5
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/plugins/crypto-auth/server/middlewares.ts +19 -19
- package/vite.config.ts +13 -0
- package/app/client/.live-stubs/LiveAdminPanel.js +0 -5
- package/app/client/.live-stubs/LiveCounter.js +0 -9
- package/app/client/.live-stubs/LiveForm.js +0 -11
- package/app/client/.live-stubs/LiveLocalCounter.js +0 -8
- package/app/client/.live-stubs/LivePingPong.js +0 -10
- package/app/client/.live-stubs/LiveRoomChat.js +0 -11
- package/app/client/.live-stubs/LiveSharedCounter.js +0 -10
- package/app/client/.live-stubs/LiveUpload.js +0 -15
- package/app/server/live/register-components.ts +0 -19
- package/core/build/live-components-generator.ts +0 -321
- package/core/live/ComponentRegistry.ts +0 -403
- package/core/live/types.ts +0 -241
- package/workspace.json +0 -6
|
@@ -1,326 +1,326 @@
|
|
|
1
|
-
// 🚀 FluxStack Plugins - Auto Registration Generator
|
|
2
|
-
// Automatically generates plugin registration during build time
|
|
3
|
-
|
|
4
|
-
import { existsSync, readdirSync, writeFileSync, unlinkSync, readFileSync, statSync } from 'fs'
|
|
5
|
-
import { join, extname, basename } from 'path'
|
|
6
|
-
import { buildLogger } from '../utils/build-logger'
|
|
7
|
-
|
|
8
|
-
export interface PluginInfo {
|
|
9
|
-
pluginDir: string
|
|
10
|
-
pluginName: string
|
|
11
|
-
entryFile: string
|
|
12
|
-
relativePath: string
|
|
13
|
-
type: 'external' | 'built-in'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class FluxPluginsGenerator {
|
|
17
|
-
private pluginsPath: string
|
|
18
|
-
private builtInPluginsPath: string
|
|
19
|
-
private registrationFilePath: string
|
|
20
|
-
private backupFilePath: string
|
|
21
|
-
|
|
22
|
-
constructor() {
|
|
23
|
-
this.pluginsPath = join(process.cwd(), 'plugins')
|
|
24
|
-
this.builtInPluginsPath = join(process.cwd(), 'core', 'plugins', 'built-in')
|
|
25
|
-
this.registrationFilePath = join(process.cwd(), 'core', 'plugins', 'auto-registry.ts')
|
|
26
|
-
this.backupFilePath = join(process.cwd(), 'core', 'plugins', 'auto-registry.backup.ts')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Scan plugin directories and discover external plugins only
|
|
31
|
-
* Built-in plugins are handled by the core system
|
|
32
|
-
*/
|
|
33
|
-
discoverPlugins(): PluginInfo[] {
|
|
34
|
-
const plugins: PluginInfo[] = []
|
|
35
|
-
|
|
36
|
-
// Discover external plugins only
|
|
37
|
-
if (existsSync(this.pluginsPath)) {
|
|
38
|
-
const externalPlugins = this.discoverPluginsInDirectory(this.pluginsPath, 'external')
|
|
39
|
-
plugins.push(...externalPlugins)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Note: Built-in plugins are automatically loaded by core system
|
|
43
|
-
// No need to include them in auto-generation
|
|
44
|
-
|
|
45
|
-
return plugins
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Discover plugins in a specific directory
|
|
50
|
-
*/
|
|
51
|
-
private discoverPluginsInDirectory(directory: string, type: 'external' | 'built-in'): PluginInfo[] {
|
|
52
|
-
const plugins: PluginInfo[] = []
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const entries = readdirSync(directory)
|
|
56
|
-
|
|
57
|
-
for (const entry of entries) {
|
|
58
|
-
const pluginDir = join(directory, entry)
|
|
59
|
-
|
|
60
|
-
// Skip files, only process directories
|
|
61
|
-
if (!statSync(pluginDir).isDirectory()) {
|
|
62
|
-
continue
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Look for plugin entry point
|
|
66
|
-
const entryFile = this.findPluginEntryFile(pluginDir)
|
|
67
|
-
if (entryFile) {
|
|
68
|
-
const relativePath = type === 'external'
|
|
69
|
-
? `../../plugins/${entry}`
|
|
70
|
-
: `./built-in/${entry}`
|
|
71
|
-
|
|
72
|
-
plugins.push({
|
|
73
|
-
pluginDir,
|
|
74
|
-
pluginName: entry,
|
|
75
|
-
entryFile,
|
|
76
|
-
relativePath,
|
|
77
|
-
type
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
buildLogger.step(`Discovered ${type} plugin: ${entry} (${entryFile})`)
|
|
81
|
-
} else {
|
|
82
|
-
buildLogger.warn(`Plugin '${entry}' has no valid entry point`)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
} catch (error) {
|
|
86
|
-
// Silently skip directories that can't be scanned
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return plugins
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Find the main plugin entry file
|
|
94
|
-
*/
|
|
95
|
-
private findPluginEntryFile(pluginDir: string): string | null {
|
|
96
|
-
const possibleFiles = [
|
|
97
|
-
'index.ts',
|
|
98
|
-
'index',
|
|
99
|
-
'plugin.ts',
|
|
100
|
-
'plugin',
|
|
101
|
-
'src/index.ts',
|
|
102
|
-
'src/index',
|
|
103
|
-
'dist/index'
|
|
104
|
-
]
|
|
105
|
-
|
|
106
|
-
for (const file of possibleFiles) {
|
|
107
|
-
const filePath = join(pluginDir, file)
|
|
108
|
-
if (existsSync(filePath)) {
|
|
109
|
-
return basename(file)
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return null
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Generate the registration file with all discovered plugins
|
|
118
|
-
*/
|
|
119
|
-
generateRegistrationFile(plugins: PluginInfo[]): void {
|
|
120
|
-
// Backup existing file if it exists
|
|
121
|
-
if (existsSync(this.registrationFilePath)) {
|
|
122
|
-
const existingContent = readFileSync(this.registrationFilePath, 'utf-8')
|
|
123
|
-
writeFileSync(this.backupFilePath, existingContent)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// All discovered plugins are external (built-in are handled by core)
|
|
127
|
-
const externalPlugins = plugins
|
|
128
|
-
|
|
129
|
-
// Generate imports
|
|
130
|
-
const imports = plugins
|
|
131
|
-
.map(plugin => {
|
|
132
|
-
const importName = this.getImportName(plugin.pluginName)
|
|
133
|
-
const importPath = plugin.entryFile === 'index.ts' || plugin.entryFile === 'index'
|
|
134
|
-
? plugin.relativePath
|
|
135
|
-
: `${plugin.relativePath}/${plugin.entryFile.replace(/\.(ts|js)$/, '')}`
|
|
136
|
-
return `import ${importName} from "${importPath}"`
|
|
137
|
-
})
|
|
138
|
-
.join('\n')
|
|
139
|
-
|
|
140
|
-
// Generate plugin array
|
|
141
|
-
const pluginsList = plugins
|
|
142
|
-
.map(plugin => {
|
|
143
|
-
const importName = this.getImportName(plugin.pluginName)
|
|
144
|
-
return ` ${importName}`
|
|
145
|
-
})
|
|
146
|
-
.join(',\n')
|
|
147
|
-
|
|
148
|
-
// Generate file content
|
|
149
|
-
const fileContent = `// 🔥 Auto-generated FluxStack External Plugins Registry
|
|
150
|
-
// This file is automatically generated during build time - DO NOT EDIT MANUALLY
|
|
151
|
-
// Built-in plugins are handled by the core system automatically
|
|
152
|
-
// Generated at: ${new Date().toISOString()}
|
|
153
|
-
|
|
154
|
-
import type { Plugin } from './types'
|
|
155
|
-
|
|
156
|
-
${imports}
|
|
157
|
-
|
|
158
|
-
// Auto-discovered external plugins array
|
|
159
|
-
export const discoveredPlugins: Plugin[] = [
|
|
160
|
-
${pluginsList}
|
|
161
|
-
]
|
|
162
|
-
|
|
163
|
-
// All discovered plugins are external (built-in handled by core)
|
|
164
|
-
export const externalPlugins = discoveredPlugins
|
|
165
|
-
|
|
166
|
-
// Plugin registration function
|
|
167
|
-
export async function registerDiscoveredPlugins(registry:
|
|
168
|
-
if (discoveredPlugins.length === 0) {
|
|
169
|
-
console.log('📦 No external plugins to register')
|
|
170
|
-
return
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
console.log(\`📦 Registering \${discoveredPlugins.length} auto-discovered external plugins...\`)
|
|
174
|
-
|
|
175
|
-
let registered = 0
|
|
176
|
-
let failed = 0
|
|
177
|
-
|
|
178
|
-
for (const plugin of discoveredPlugins) {
|
|
179
|
-
try {
|
|
180
|
-
await registry.register(plugin)
|
|
181
|
-
registered++
|
|
182
|
-
console.log(\`✅ Registered external plugin: \${plugin.name}\`)
|
|
183
|
-
} catch (error) {
|
|
184
|
-
failed++
|
|
185
|
-
console.error(\`❌ Failed to register external plugin \${plugin.name}:\`, error)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
console.log(\`📊 External plugin registration complete: \${registered} registered, \${failed} failed\`)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Export plugin names for easy access
|
|
193
|
-
export const pluginNames = discoveredPlugins.map(p => p.name)
|
|
194
|
-
|
|
195
|
-
console.log('🔍 Auto-discovered ${plugins.length} external plugins' + (pluginNames.length > 0 ? ': ' + pluginNames.join(', ') : ''))
|
|
196
|
-
`
|
|
197
|
-
|
|
198
|
-
writeFileSync(this.registrationFilePath, fileContent)
|
|
199
|
-
buildLogger.success(`Generated registry for ${plugins.length} plugins`)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Generate a valid import name from plugin name
|
|
204
|
-
*/
|
|
205
|
-
private getImportName(pluginName: string): string {
|
|
206
|
-
// Convert kebab-case to PascalCase for import names
|
|
207
|
-
return pluginName
|
|
208
|
-
.split('-')
|
|
209
|
-
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
210
|
-
.join('') + 'Plugin'
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Restore the original registration file from backup
|
|
215
|
-
*/
|
|
216
|
-
restoreOriginalFile(): void {
|
|
217
|
-
if (existsSync(this.backupFilePath)) {
|
|
218
|
-
const backupContent = readFileSync(this.backupFilePath, 'utf-8')
|
|
219
|
-
writeFileSync(this.registrationFilePath, backupContent)
|
|
220
|
-
unlinkSync(this.backupFilePath)
|
|
221
|
-
} else {
|
|
222
|
-
// If no backup exists, remove the generated file
|
|
223
|
-
if (existsSync(this.registrationFilePath)) {
|
|
224
|
-
unlinkSync(this.registrationFilePath)
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Check if the current registration file is auto-generated
|
|
231
|
-
*/
|
|
232
|
-
isAutoGenerated(): boolean {
|
|
233
|
-
if (!existsSync(this.registrationFilePath)) {
|
|
234
|
-
return false
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const content = readFileSync(this.registrationFilePath, 'utf-8')
|
|
238
|
-
return content.includes('// 🔥 Auto-generated FluxStack Plugins Registry')
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Pre-build hook: Generate registration file
|
|
243
|
-
*/
|
|
244
|
-
async preBuild(): Promise<PluginInfo[]> {
|
|
245
|
-
buildLogger.section('FluxStack Plugins Discovery', '🔌')
|
|
246
|
-
|
|
247
|
-
const plugins = this.discoverPlugins()
|
|
248
|
-
|
|
249
|
-
if (plugins.length === 0) {
|
|
250
|
-
buildLogger.warn('No FluxStack Plugins found')
|
|
251
|
-
return []
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Create table of discovered plugins
|
|
255
|
-
const pluginData = plugins.map(p => ({
|
|
256
|
-
plugin: p.pluginName,
|
|
257
|
-
type: p.type,
|
|
258
|
-
entry: p.entryFile
|
|
259
|
-
}))
|
|
260
|
-
|
|
261
|
-
buildLogger.table(
|
|
262
|
-
[
|
|
263
|
-
{ header: 'Plugin', key: 'plugin', width: 25, align: 'left', color: 'cyan' },
|
|
264
|
-
{ header: 'Type', key: 'type', width: 12, align: 'left', color: 'yellow' },
|
|
265
|
-
{ header: 'Entry Point', key: 'entry', width: 20, align: 'left', color: 'gray' }
|
|
266
|
-
],
|
|
267
|
-
pluginData
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
this.generateRegistrationFile(plugins)
|
|
271
|
-
|
|
272
|
-
return plugins
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Post-build hook: Clean up generated file (optional)
|
|
277
|
-
*/
|
|
278
|
-
async postBuild(keepGenerated: boolean = false): Promise<void> {
|
|
279
|
-
buildLogger.step('Cleaning up FluxStack Plugins registry...')
|
|
280
|
-
|
|
281
|
-
if (keepGenerated) {
|
|
282
|
-
// Remove backup since we're keeping the generated version
|
|
283
|
-
if (existsSync(this.backupFilePath)) {
|
|
284
|
-
unlinkSync(this.backupFilePath)
|
|
285
|
-
}
|
|
286
|
-
} else {
|
|
287
|
-
this.restoreOriginalFile()
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Development mode: Check if registration needs update
|
|
293
|
-
*/
|
|
294
|
-
needsUpdate(): boolean {
|
|
295
|
-
if (!this.isAutoGenerated()) {
|
|
296
|
-
return false // No auto-generated file, don't touch manual setup
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const plugins = this.discoverPlugins()
|
|
300
|
-
const currentContent = readFileSync(this.registrationFilePath, 'utf-8')
|
|
301
|
-
|
|
302
|
-
// Check if all discovered plugins are in the current file
|
|
303
|
-
for (const plugin of plugins) {
|
|
304
|
-
const importName = this.getImportName(plugin.pluginName)
|
|
305
|
-
if (!currentContent.includes(importName)) {
|
|
306
|
-
return true
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return false
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Development mode: Update registration if needed
|
|
315
|
-
*/
|
|
316
|
-
updateIfNeeded(): void {
|
|
317
|
-
if (this.needsUpdate()) {
|
|
318
|
-
buildLogger.info('FluxStack Plugins changed, updating registry...')
|
|
319
|
-
const plugins = this.discoverPlugins()
|
|
320
|
-
this.generateRegistrationFile(plugins)
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Export singleton instance
|
|
1
|
+
// 🚀 FluxStack Plugins - Auto Registration Generator
|
|
2
|
+
// Automatically generates plugin registration during build time
|
|
3
|
+
|
|
4
|
+
import { existsSync, readdirSync, writeFileSync, unlinkSync, readFileSync, statSync } from 'fs'
|
|
5
|
+
import { join, extname, basename } from 'path'
|
|
6
|
+
import { buildLogger } from '../utils/build-logger'
|
|
7
|
+
|
|
8
|
+
export interface PluginInfo {
|
|
9
|
+
pluginDir: string
|
|
10
|
+
pluginName: string
|
|
11
|
+
entryFile: string
|
|
12
|
+
relativePath: string
|
|
13
|
+
type: 'external' | 'built-in'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class FluxPluginsGenerator {
|
|
17
|
+
private pluginsPath: string
|
|
18
|
+
private builtInPluginsPath: string
|
|
19
|
+
private registrationFilePath: string
|
|
20
|
+
private backupFilePath: string
|
|
21
|
+
|
|
22
|
+
constructor() {
|
|
23
|
+
this.pluginsPath = join(process.cwd(), 'plugins')
|
|
24
|
+
this.builtInPluginsPath = join(process.cwd(), 'core', 'plugins', 'built-in')
|
|
25
|
+
this.registrationFilePath = join(process.cwd(), 'core', 'plugins', 'auto-registry.ts')
|
|
26
|
+
this.backupFilePath = join(process.cwd(), 'core', 'plugins', 'auto-registry.backup.ts')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Scan plugin directories and discover external plugins only
|
|
31
|
+
* Built-in plugins are handled by the core system
|
|
32
|
+
*/
|
|
33
|
+
discoverPlugins(): PluginInfo[] {
|
|
34
|
+
const plugins: PluginInfo[] = []
|
|
35
|
+
|
|
36
|
+
// Discover external plugins only
|
|
37
|
+
if (existsSync(this.pluginsPath)) {
|
|
38
|
+
const externalPlugins = this.discoverPluginsInDirectory(this.pluginsPath, 'external')
|
|
39
|
+
plugins.push(...externalPlugins)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Note: Built-in plugins are automatically loaded by core system
|
|
43
|
+
// No need to include them in auto-generation
|
|
44
|
+
|
|
45
|
+
return plugins
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Discover plugins in a specific directory
|
|
50
|
+
*/
|
|
51
|
+
private discoverPluginsInDirectory(directory: string, type: 'external' | 'built-in'): PluginInfo[] {
|
|
52
|
+
const plugins: PluginInfo[] = []
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const entries = readdirSync(directory)
|
|
56
|
+
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
const pluginDir = join(directory, entry)
|
|
59
|
+
|
|
60
|
+
// Skip files, only process directories
|
|
61
|
+
if (!statSync(pluginDir).isDirectory()) {
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Look for plugin entry point
|
|
66
|
+
const entryFile = this.findPluginEntryFile(pluginDir)
|
|
67
|
+
if (entryFile) {
|
|
68
|
+
const relativePath = type === 'external'
|
|
69
|
+
? `../../plugins/${entry}`
|
|
70
|
+
: `./built-in/${entry}`
|
|
71
|
+
|
|
72
|
+
plugins.push({
|
|
73
|
+
pluginDir,
|
|
74
|
+
pluginName: entry,
|
|
75
|
+
entryFile,
|
|
76
|
+
relativePath,
|
|
77
|
+
type
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
buildLogger.step(`Discovered ${type} plugin: ${entry} (${entryFile})`)
|
|
81
|
+
} else {
|
|
82
|
+
buildLogger.warn(`Plugin '${entry}' has no valid entry point`)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
// Silently skip directories that can't be scanned
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return plugins
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Find the main plugin entry file
|
|
94
|
+
*/
|
|
95
|
+
private findPluginEntryFile(pluginDir: string): string | null {
|
|
96
|
+
const possibleFiles = [
|
|
97
|
+
'index.ts',
|
|
98
|
+
'index',
|
|
99
|
+
'plugin.ts',
|
|
100
|
+
'plugin',
|
|
101
|
+
'src/index.ts',
|
|
102
|
+
'src/index',
|
|
103
|
+
'dist/index'
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
for (const file of possibleFiles) {
|
|
107
|
+
const filePath = join(pluginDir, file)
|
|
108
|
+
if (existsSync(filePath)) {
|
|
109
|
+
return basename(file)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Generate the registration file with all discovered plugins
|
|
118
|
+
*/
|
|
119
|
+
generateRegistrationFile(plugins: PluginInfo[]): void {
|
|
120
|
+
// Backup existing file if it exists
|
|
121
|
+
if (existsSync(this.registrationFilePath)) {
|
|
122
|
+
const existingContent = readFileSync(this.registrationFilePath, 'utf-8')
|
|
123
|
+
writeFileSync(this.backupFilePath, existingContent)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// All discovered plugins are external (built-in are handled by core)
|
|
127
|
+
const externalPlugins = plugins
|
|
128
|
+
|
|
129
|
+
// Generate imports
|
|
130
|
+
const imports = plugins
|
|
131
|
+
.map(plugin => {
|
|
132
|
+
const importName = this.getImportName(plugin.pluginName)
|
|
133
|
+
const importPath = plugin.entryFile === 'index.ts' || plugin.entryFile === 'index'
|
|
134
|
+
? plugin.relativePath
|
|
135
|
+
: `${plugin.relativePath}/${plugin.entryFile.replace(/\.(ts|js)$/, '')}`
|
|
136
|
+
return `import ${importName} from "${importPath}"`
|
|
137
|
+
})
|
|
138
|
+
.join('\n')
|
|
139
|
+
|
|
140
|
+
// Generate plugin array
|
|
141
|
+
const pluginsList = plugins
|
|
142
|
+
.map(plugin => {
|
|
143
|
+
const importName = this.getImportName(plugin.pluginName)
|
|
144
|
+
return ` ${importName}`
|
|
145
|
+
})
|
|
146
|
+
.join(',\n')
|
|
147
|
+
|
|
148
|
+
// Generate file content
|
|
149
|
+
const fileContent = `// 🔥 Auto-generated FluxStack External Plugins Registry
|
|
150
|
+
// This file is automatically generated during build time - DO NOT EDIT MANUALLY
|
|
151
|
+
// Built-in plugins are handled by the core system automatically
|
|
152
|
+
// Generated at: ${new Date().toISOString()}
|
|
153
|
+
|
|
154
|
+
import type { Plugin } from './types'
|
|
155
|
+
|
|
156
|
+
${imports}
|
|
157
|
+
|
|
158
|
+
// Auto-discovered external plugins array
|
|
159
|
+
export const discoveredPlugins: Plugin[] = [
|
|
160
|
+
${pluginsList}
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
// All discovered plugins are external (built-in handled by core)
|
|
164
|
+
export const externalPlugins = discoveredPlugins
|
|
165
|
+
|
|
166
|
+
// Plugin registration function
|
|
167
|
+
export async function registerDiscoveredPlugins(registry: { register: (plugin: Plugin) => Promise<void> }): Promise<void> {
|
|
168
|
+
if (discoveredPlugins.length === 0) {
|
|
169
|
+
console.log('📦 No external plugins to register')
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.log(\`📦 Registering \${discoveredPlugins.length} auto-discovered external plugins...\`)
|
|
174
|
+
|
|
175
|
+
let registered = 0
|
|
176
|
+
let failed = 0
|
|
177
|
+
|
|
178
|
+
for (const plugin of discoveredPlugins) {
|
|
179
|
+
try {
|
|
180
|
+
await registry.register(plugin)
|
|
181
|
+
registered++
|
|
182
|
+
console.log(\`✅ Registered external plugin: \${plugin.name}\`)
|
|
183
|
+
} catch (error) {
|
|
184
|
+
failed++
|
|
185
|
+
console.error(\`❌ Failed to register external plugin \${plugin.name}:\`, error)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log(\`📊 External plugin registration complete: \${registered} registered, \${failed} failed\`)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Export plugin names for easy access
|
|
193
|
+
export const pluginNames = discoveredPlugins.map(p => p.name)
|
|
194
|
+
|
|
195
|
+
console.log('🔍 Auto-discovered ${plugins.length} external plugins' + (pluginNames.length > 0 ? ': ' + pluginNames.join(', ') : ''))
|
|
196
|
+
`
|
|
197
|
+
|
|
198
|
+
writeFileSync(this.registrationFilePath, fileContent)
|
|
199
|
+
buildLogger.success(`Generated registry for ${plugins.length} plugins`)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Generate a valid import name from plugin name
|
|
204
|
+
*/
|
|
205
|
+
private getImportName(pluginName: string): string {
|
|
206
|
+
// Convert kebab-case to PascalCase for import names
|
|
207
|
+
return pluginName
|
|
208
|
+
.split('-')
|
|
209
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
210
|
+
.join('') + 'Plugin'
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Restore the original registration file from backup
|
|
215
|
+
*/
|
|
216
|
+
restoreOriginalFile(): void {
|
|
217
|
+
if (existsSync(this.backupFilePath)) {
|
|
218
|
+
const backupContent = readFileSync(this.backupFilePath, 'utf-8')
|
|
219
|
+
writeFileSync(this.registrationFilePath, backupContent)
|
|
220
|
+
unlinkSync(this.backupFilePath)
|
|
221
|
+
} else {
|
|
222
|
+
// If no backup exists, remove the generated file
|
|
223
|
+
if (existsSync(this.registrationFilePath)) {
|
|
224
|
+
unlinkSync(this.registrationFilePath)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Check if the current registration file is auto-generated
|
|
231
|
+
*/
|
|
232
|
+
isAutoGenerated(): boolean {
|
|
233
|
+
if (!existsSync(this.registrationFilePath)) {
|
|
234
|
+
return false
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const content = readFileSync(this.registrationFilePath, 'utf-8')
|
|
238
|
+
return content.includes('// 🔥 Auto-generated FluxStack Plugins Registry')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Pre-build hook: Generate registration file
|
|
243
|
+
*/
|
|
244
|
+
async preBuild(): Promise<PluginInfo[]> {
|
|
245
|
+
buildLogger.section('FluxStack Plugins Discovery', '🔌')
|
|
246
|
+
|
|
247
|
+
const plugins = this.discoverPlugins()
|
|
248
|
+
|
|
249
|
+
if (plugins.length === 0) {
|
|
250
|
+
buildLogger.warn('No FluxStack Plugins found')
|
|
251
|
+
return []
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Create table of discovered plugins
|
|
255
|
+
const pluginData = plugins.map(p => ({
|
|
256
|
+
plugin: p.pluginName,
|
|
257
|
+
type: p.type,
|
|
258
|
+
entry: p.entryFile
|
|
259
|
+
}))
|
|
260
|
+
|
|
261
|
+
buildLogger.table(
|
|
262
|
+
[
|
|
263
|
+
{ header: 'Plugin', key: 'plugin', width: 25, align: 'left', color: 'cyan' },
|
|
264
|
+
{ header: 'Type', key: 'type', width: 12, align: 'left', color: 'yellow' },
|
|
265
|
+
{ header: 'Entry Point', key: 'entry', width: 20, align: 'left', color: 'gray' }
|
|
266
|
+
],
|
|
267
|
+
pluginData
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
this.generateRegistrationFile(plugins)
|
|
271
|
+
|
|
272
|
+
return plugins
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Post-build hook: Clean up generated file (optional)
|
|
277
|
+
*/
|
|
278
|
+
async postBuild(keepGenerated: boolean = false): Promise<void> {
|
|
279
|
+
buildLogger.step('Cleaning up FluxStack Plugins registry...')
|
|
280
|
+
|
|
281
|
+
if (keepGenerated) {
|
|
282
|
+
// Remove backup since we're keeping the generated version
|
|
283
|
+
if (existsSync(this.backupFilePath)) {
|
|
284
|
+
unlinkSync(this.backupFilePath)
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
this.restoreOriginalFile()
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Development mode: Check if registration needs update
|
|
293
|
+
*/
|
|
294
|
+
needsUpdate(): boolean {
|
|
295
|
+
if (!this.isAutoGenerated()) {
|
|
296
|
+
return false // No auto-generated file, don't touch manual setup
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const plugins = this.discoverPlugins()
|
|
300
|
+
const currentContent = readFileSync(this.registrationFilePath, 'utf-8')
|
|
301
|
+
|
|
302
|
+
// Check if all discovered plugins are in the current file
|
|
303
|
+
for (const plugin of plugins) {
|
|
304
|
+
const importName = this.getImportName(plugin.pluginName)
|
|
305
|
+
if (!currentContent.includes(importName)) {
|
|
306
|
+
return true
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return false
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Development mode: Update registration if needed
|
|
315
|
+
*/
|
|
316
|
+
updateIfNeeded(): void {
|
|
317
|
+
if (this.needsUpdate()) {
|
|
318
|
+
buildLogger.info('FluxStack Plugins changed, updating registry...')
|
|
319
|
+
const plugins = this.discoverPlugins()
|
|
320
|
+
this.generateRegistrationFile(plugins)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Export singleton instance
|
|
326
326
|
export const fluxPluginsGenerator = new FluxPluginsGenerator()
|