@flexireact/core 4.0.0 → 4.1.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/README.md +147 -770
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +441 -701
- package/dist/cli/index.js.map +1 -1
- package/dist/core/build/index.d.ts +99 -0
- package/dist/core/build/index.js +2 -2
- package/dist/core/build/index.js.map +1 -1
- package/dist/core/client/index.d.ts +164 -0
- package/dist/core/client/index.js.map +1 -1
- package/dist/core/config.d.ts +165 -0
- package/dist/core/config.js +62 -32
- package/dist/core/config.js.map +1 -1
- package/dist/core/index.d.ts +1978 -0
- package/dist/core/index.js +309 -202
- package/dist/core/index.js.map +1 -1
- package/dist/core/server/index.d.ts +16 -0
- package/dist/core/server/index.js +305 -198
- package/dist/core/server/index.js.map +1 -1
- package/dist/core/start-dev.d.ts +2 -0
- package/dist/core/start-dev.js +305 -198
- package/dist/core/start-dev.js.map +1 -1
- package/dist/core/start-prod.d.ts +2 -0
- package/dist/core/start-prod.js +305 -198
- package/dist/core/start-prod.js.map +1 -1
- package/package.json +23 -11
package/dist/core/index.js
CHANGED
|
@@ -2,46 +2,59 @@
|
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { pathToFileURL } from "url";
|
|
5
|
-
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
var BuildConfigSchema = z.object({
|
|
8
|
+
target: z.string().default("es2022"),
|
|
9
|
+
minify: z.boolean().default(true),
|
|
10
|
+
sourcemap: z.boolean().default(true),
|
|
11
|
+
splitting: z.boolean().default(true)
|
|
12
|
+
}).default({});
|
|
13
|
+
var ServerConfigSchema = z.object({
|
|
14
|
+
port: z.number().min(1).max(65535).default(3e3),
|
|
15
|
+
host: z.string().default("localhost")
|
|
16
|
+
}).default({});
|
|
17
|
+
var SSGConfigSchema = z.object({
|
|
18
|
+
enabled: z.boolean().default(false),
|
|
19
|
+
paths: z.array(z.string()).default([])
|
|
20
|
+
}).default({});
|
|
21
|
+
var IslandsConfigSchema = z.object({
|
|
22
|
+
enabled: z.boolean().default(true),
|
|
23
|
+
directive: z.string().default("use island")
|
|
24
|
+
}).default({});
|
|
25
|
+
var RSCConfigSchema = z.object({
|
|
26
|
+
enabled: z.boolean().default(true)
|
|
27
|
+
}).default({});
|
|
28
|
+
var PluginSchema = z.object({
|
|
29
|
+
name: z.string(),
|
|
30
|
+
setup: z.function().optional()
|
|
31
|
+
}).passthrough();
|
|
32
|
+
var FlexiReactConfigSchema = z.object({
|
|
6
33
|
// Directories
|
|
7
|
-
pagesDir: "pages",
|
|
8
|
-
layoutsDir: "layouts",
|
|
9
|
-
publicDir: "public",
|
|
10
|
-
outDir: ".flexi",
|
|
34
|
+
pagesDir: z.string().default("pages"),
|
|
35
|
+
layoutsDir: z.string().default("layouts"),
|
|
36
|
+
publicDir: z.string().default("public"),
|
|
37
|
+
outDir: z.string().default(".flexi"),
|
|
11
38
|
// Build options
|
|
12
|
-
build:
|
|
13
|
-
target: "es2022",
|
|
14
|
-
minify: true,
|
|
15
|
-
sourcemap: true,
|
|
16
|
-
splitting: true
|
|
17
|
-
},
|
|
39
|
+
build: BuildConfigSchema,
|
|
18
40
|
// Server options
|
|
19
|
-
server:
|
|
20
|
-
port: 3e3,
|
|
21
|
-
host: "localhost"
|
|
22
|
-
},
|
|
41
|
+
server: ServerConfigSchema,
|
|
23
42
|
// SSG options
|
|
24
|
-
ssg:
|
|
25
|
-
enabled: false,
|
|
26
|
-
paths: []
|
|
27
|
-
},
|
|
43
|
+
ssg: SSGConfigSchema,
|
|
28
44
|
// Islands (partial hydration)
|
|
29
|
-
islands:
|
|
30
|
-
enabled: true
|
|
31
|
-
},
|
|
45
|
+
islands: IslandsConfigSchema,
|
|
32
46
|
// RSC options
|
|
33
|
-
rsc:
|
|
34
|
-
enabled: true
|
|
35
|
-
},
|
|
47
|
+
rsc: RSCConfigSchema,
|
|
36
48
|
// Plugins
|
|
37
|
-
plugins: [],
|
|
49
|
+
plugins: z.array(PluginSchema).default([]),
|
|
38
50
|
// Styles (CSS files to include)
|
|
39
|
-
styles: [],
|
|
51
|
+
styles: z.array(z.string()).default([]),
|
|
40
52
|
// Scripts (JS files to include)
|
|
41
|
-
scripts: [],
|
|
53
|
+
scripts: z.array(z.string()).default([]),
|
|
42
54
|
// Favicon path
|
|
43
|
-
favicon: null
|
|
44
|
-
};
|
|
55
|
+
favicon: z.string().nullable().default(null)
|
|
56
|
+
});
|
|
57
|
+
var defaultConfig = FlexiReactConfigSchema.parse({});
|
|
45
58
|
async function loadConfig(projectRoot) {
|
|
46
59
|
const configPathTs = path.join(projectRoot, "flexireact.config.ts");
|
|
47
60
|
const configPathJs = path.join(projectRoot, "flexireact.config.js");
|
|
@@ -53,10 +66,22 @@ async function loadConfig(projectRoot) {
|
|
|
53
66
|
const module = await import(`${configUrl}?t=${Date.now()}`);
|
|
54
67
|
userConfig = module.default || module;
|
|
55
68
|
} catch (error) {
|
|
56
|
-
console.warn(
|
|
69
|
+
console.warn(pc.yellow(`\u26A0 Failed to load config: ${error.message}`));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const merged = deepMerge(defaultConfig, userConfig);
|
|
73
|
+
try {
|
|
74
|
+
return FlexiReactConfigSchema.parse(merged);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (err instanceof z.ZodError) {
|
|
77
|
+
console.error(pc.red("\u2716 Configuration validation failed:"));
|
|
78
|
+
for (const issue of err.issues) {
|
|
79
|
+
console.error(pc.dim(` - ${issue.path.join(".")}: ${issue.message}`));
|
|
80
|
+
}
|
|
81
|
+
process.exit(1);
|
|
57
82
|
}
|
|
83
|
+
throw err;
|
|
58
84
|
}
|
|
59
|
-
return deepMerge(defaultConfig, userConfig);
|
|
60
85
|
}
|
|
61
86
|
function deepMerge(target, source) {
|
|
62
87
|
const result = { ...target };
|
|
@@ -259,7 +284,7 @@ function buildRouteTree(pagesDir, layoutsDir, appDir = null, routesDir = null) {
|
|
|
259
284
|
appRoutes: [],
|
|
260
285
|
// Next.js style app router routes
|
|
261
286
|
flexiRoutes: []
|
|
262
|
-
// FlexiReact
|
|
287
|
+
// FlexiReact v4 routes/ directory
|
|
263
288
|
};
|
|
264
289
|
const routesDirPath = routesDir || path3.join(projectRoot, "routes");
|
|
265
290
|
if (fs3.existsSync(routesDirPath)) {
|
|
@@ -610,32 +635,72 @@ async function renderPage(options) {
|
|
|
610
635
|
favicon = null,
|
|
611
636
|
isSSG = false,
|
|
612
637
|
route = "/",
|
|
613
|
-
needsHydration = false
|
|
638
|
+
needsHydration = false,
|
|
639
|
+
componentPath = ""
|
|
614
640
|
} = options;
|
|
615
641
|
const renderStart = Date.now();
|
|
616
642
|
try {
|
|
617
|
-
let element
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
643
|
+
let element;
|
|
644
|
+
let content;
|
|
645
|
+
let isClientOnly = false;
|
|
646
|
+
try {
|
|
647
|
+
element = React2.createElement(Component, props);
|
|
648
|
+
if (error) {
|
|
649
|
+
element = React2.createElement(ErrorBoundaryWrapper, {
|
|
650
|
+
fallback: error,
|
|
651
|
+
children: element
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
if (loading) {
|
|
655
|
+
element = React2.createElement(React2.Suspense, {
|
|
656
|
+
fallback: React2.createElement(loading),
|
|
657
|
+
children: element
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
for (const layout of [...layouts].reverse()) {
|
|
661
|
+
if (layout.Component) {
|
|
662
|
+
const LayoutComponent = layout.Component;
|
|
663
|
+
element = React2.createElement(LayoutComponent, {
|
|
664
|
+
...layout.props
|
|
665
|
+
}, element);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
content = renderToString(element);
|
|
669
|
+
} catch (renderErr) {
|
|
670
|
+
const isHookError = renderErr.message?.includes("useState") || renderErr.message?.includes("useEffect") || renderErr.message?.includes("useContext") || renderErr.message?.includes("useReducer") || renderErr.message?.includes("useRef") || renderErr.message?.includes("Invalid hook call");
|
|
671
|
+
if (isHookError || needsHydration) {
|
|
672
|
+
isClientOnly = true;
|
|
673
|
+
const placeholderHtml = `
|
|
674
|
+
<div id="flexi-root" data-client-component="true" data-component-path="${escapeHtml(componentPath)}">
|
|
675
|
+
<div class="flexi-loading" style="display:flex;align-items:center;justify-content:center;min-height:200px;color:#666;">
|
|
676
|
+
<div style="text-align:center;">
|
|
677
|
+
<div style="width:40px;height:40px;border:3px solid #e0e0e0;border-top-color:#3b82f6;border-radius:50%;animation:spin 1s linear infinite;margin:0 auto 12px;"></div>
|
|
678
|
+
<div>Loading...</div>
|
|
679
|
+
</div>
|
|
680
|
+
</div>
|
|
681
|
+
<style>@keyframes spin{to{transform:rotate(360deg)}}</style>
|
|
682
|
+
</div>
|
|
683
|
+
`;
|
|
684
|
+
let wrappedContent = placeholderHtml;
|
|
685
|
+
for (const layout of [...layouts].reverse()) {
|
|
686
|
+
if (layout.Component) {
|
|
687
|
+
try {
|
|
688
|
+
const layoutElement = React2.createElement(layout.Component, {
|
|
689
|
+
...layout.props,
|
|
690
|
+
children: React2.createElement("div", {
|
|
691
|
+
dangerouslySetInnerHTML: { __html: wrappedContent }
|
|
692
|
+
})
|
|
693
|
+
});
|
|
694
|
+
wrappedContent = renderToString(layoutElement);
|
|
695
|
+
} catch {
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
content = wrappedContent;
|
|
700
|
+
} else {
|
|
701
|
+
throw renderErr;
|
|
636
702
|
}
|
|
637
703
|
}
|
|
638
|
-
const content = renderToString(element);
|
|
639
704
|
const renderTime = Date.now() - renderStart;
|
|
640
705
|
const islandScripts = generateIslandScripts(islands);
|
|
641
706
|
return buildHtmlDocument({
|
|
@@ -649,7 +714,8 @@ async function renderPage(options) {
|
|
|
649
714
|
isSSG,
|
|
650
715
|
renderTime,
|
|
651
716
|
route,
|
|
652
|
-
isClientComponent: needsHydration
|
|
717
|
+
isClientComponent: needsHydration || isClientOnly,
|
|
718
|
+
componentPath
|
|
653
719
|
});
|
|
654
720
|
} catch (err) {
|
|
655
721
|
console.error("Render Error:", err);
|
|
@@ -807,7 +873,7 @@ function generateDevToolbar(options = {}) {
|
|
|
807
873
|
const timeColor = renderTime < 50 ? "#00FF9C" : renderTime < 200 ? "#fbbf24" : "#ef4444";
|
|
808
874
|
const timeLabel = renderTime < 50 ? "Fast" : renderTime < 200 ? "OK" : "Slow";
|
|
809
875
|
return `
|
|
810
|
-
<!-- FlexiReact
|
|
876
|
+
<!-- FlexiReact v4.1.0 Dev Toolbar -->
|
|
811
877
|
<div id="flexi-dev-toolbar" class="flexi-dev-collapsed">
|
|
812
878
|
<style>
|
|
813
879
|
#flexi-dev-toolbar {
|
|
@@ -1115,7 +1181,7 @@ function generateDevToolbar(options = {}) {
|
|
|
1115
1181
|
<div class="flexi-dev-header-logo">F</div>
|
|
1116
1182
|
<div class="flexi-dev-header-info">
|
|
1117
1183
|
<div class="flexi-dev-header-title">FlexiReact</div>
|
|
1118
|
-
<div class="flexi-dev-header-subtitle">
|
|
1184
|
+
<div class="flexi-dev-header-subtitle">v4.1.0 \u2022 Development</div>
|
|
1119
1185
|
</div>
|
|
1120
1186
|
<button class="flexi-dev-close" onclick="this.closest('#flexi-dev-toolbar').classList.remove('flexi-dev-open')">\u2715</button>
|
|
1121
1187
|
</div>
|
|
@@ -1168,9 +1234,9 @@ function generateDevToolbar(options = {}) {
|
|
|
1168
1234
|
</div>
|
|
1169
1235
|
|
|
1170
1236
|
<script>
|
|
1171
|
-
// FlexiReact
|
|
1237
|
+
// FlexiReact v4 DevTools
|
|
1172
1238
|
window.__FLEXI_DEV__ = {
|
|
1173
|
-
version: '
|
|
1239
|
+
version: '4.1.0',
|
|
1174
1240
|
renderTime: ${renderTime},
|
|
1175
1241
|
pageType: '${pageType}',
|
|
1176
1242
|
route: '${route}',
|
|
@@ -1205,7 +1271,7 @@ function generateDevToolbar(options = {}) {
|
|
|
1205
1271
|
|
|
1206
1272
|
// Console branding
|
|
1207
1273
|
console.log(
|
|
1208
|
-
'%c \u26A1 FlexiReact
|
|
1274
|
+
'%c \u26A1 FlexiReact v4.1.0 %c ${pageType} %c ${renderTime}ms ',
|
|
1209
1275
|
'background: #00FF9C; color: #000; font-weight: bold; padding: 2px 6px; border-radius: 4px 0 0 4px;',
|
|
1210
1276
|
'background: #1e1e1e; color: #fafafa; padding: 2px 6px;',
|
|
1211
1277
|
'background: ${timeColor}20; color: ${timeColor}; padding: 2px 6px; border-radius: 0 4px 4px 0;'
|
|
@@ -1306,7 +1372,7 @@ function renderError(statusCode, message, stack = null) {
|
|
|
1306
1372
|
401: { title: "Unauthorized", icon: "key", color: "#8b5cf6", desc: "Please log in to access this page." }
|
|
1307
1373
|
};
|
|
1308
1374
|
const errorInfo = errorMessages[statusCode] || { title: "Error", icon: "alert", color: "#ef4444", desc: message };
|
|
1309
|
-
const errorFramesHtml = showStack && errorDetails?.frames?.length > 0 ? errorDetails.frames.slice(0, 5).map((frame, i) => `
|
|
1375
|
+
const errorFramesHtml = showStack && errorDetails?.frames?.length && errorDetails.frames.length > 0 ? errorDetails.frames.slice(0, 5).map((frame, i) => `
|
|
1310
1376
|
<div class="error-frame ${i === 0 ? "error-frame-first" : ""}">
|
|
1311
1377
|
<div class="error-frame-fn">${escapeHtml(frame.fn)}</div>
|
|
1312
1378
|
<div class="error-frame-loc">${escapeHtml(frame.file)}:${frame.line}:${frame.col}</div>
|
|
@@ -1606,7 +1672,7 @@ function renderError(statusCode, message, stack = null) {
|
|
|
1606
1672
|
${isDev ? `
|
|
1607
1673
|
<div class="dev-badge">
|
|
1608
1674
|
<div class="dev-badge-dot"></div>
|
|
1609
|
-
FlexiReact
|
|
1675
|
+
FlexiReact v4.1.0
|
|
1610
1676
|
</div>
|
|
1611
1677
|
` : ""}
|
|
1612
1678
|
</body>
|
|
@@ -2229,12 +2295,12 @@ function Island({ component: Component, props = {}, name, clientPath }) {
|
|
|
2229
2295
|
clientPath,
|
|
2230
2296
|
props
|
|
2231
2297
|
});
|
|
2232
|
-
const
|
|
2298
|
+
const placeholder = `<div class="island-loading" data-island-placeholder="${name}">Loading...</div>`;
|
|
2233
2299
|
return React3.createElement("div", {
|
|
2234
2300
|
"data-island": islandId,
|
|
2235
2301
|
"data-island-name": name,
|
|
2236
2302
|
"data-island-props": JSON.stringify(props),
|
|
2237
|
-
dangerouslySetInnerHTML: { __html:
|
|
2303
|
+
dangerouslySetInnerHTML: { __html: placeholder }
|
|
2238
2304
|
});
|
|
2239
2305
|
}
|
|
2240
2306
|
function getRegisteredIslands() {
|
|
@@ -2425,10 +2491,7 @@ var colors = {
|
|
|
2425
2491
|
reset: "\x1B[0m",
|
|
2426
2492
|
bold: "\x1B[1m",
|
|
2427
2493
|
dim: "\x1B[2m",
|
|
2428
|
-
italic: "\x1B[3m",
|
|
2429
|
-
underline: "\x1B[4m",
|
|
2430
2494
|
// Text colors
|
|
2431
|
-
black: "\x1B[30m",
|
|
2432
2495
|
red: "\x1B[31m",
|
|
2433
2496
|
green: "\x1B[32m",
|
|
2434
2497
|
yellow: "\x1B[33m",
|
|
@@ -2436,22 +2499,7 @@ var colors = {
|
|
|
2436
2499
|
magenta: "\x1B[35m",
|
|
2437
2500
|
cyan: "\x1B[36m",
|
|
2438
2501
|
white: "\x1B[37m",
|
|
2439
|
-
gray: "\x1B[90m"
|
|
2440
|
-
// Bright colors
|
|
2441
|
-
brightRed: "\x1B[91m",
|
|
2442
|
-
brightGreen: "\x1B[92m",
|
|
2443
|
-
brightYellow: "\x1B[93m",
|
|
2444
|
-
brightBlue: "\x1B[94m",
|
|
2445
|
-
brightMagenta: "\x1B[95m",
|
|
2446
|
-
brightCyan: "\x1B[96m",
|
|
2447
|
-
brightWhite: "\x1B[97m",
|
|
2448
|
-
// Background colors
|
|
2449
|
-
bgRed: "\x1B[41m",
|
|
2450
|
-
bgGreen: "\x1B[42m",
|
|
2451
|
-
bgYellow: "\x1B[43m",
|
|
2452
|
-
bgBlue: "\x1B[44m",
|
|
2453
|
-
bgMagenta: "\x1B[45m",
|
|
2454
|
-
bgCyan: "\x1B[46m"
|
|
2502
|
+
gray: "\x1B[90m"
|
|
2455
2503
|
};
|
|
2456
2504
|
var c = colors;
|
|
2457
2505
|
function getStatusColor(status) {
|
|
@@ -2462,16 +2510,7 @@ function getStatusColor(status) {
|
|
|
2462
2510
|
return c.white;
|
|
2463
2511
|
}
|
|
2464
2512
|
function getMethodColor(method) {
|
|
2465
|
-
|
|
2466
|
-
GET: c.brightGreen,
|
|
2467
|
-
POST: c.brightBlue,
|
|
2468
|
-
PUT: c.brightYellow,
|
|
2469
|
-
PATCH: c.brightMagenta,
|
|
2470
|
-
DELETE: c.brightRed,
|
|
2471
|
-
OPTIONS: c.gray,
|
|
2472
|
-
HEAD: c.gray
|
|
2473
|
-
};
|
|
2474
|
-
return methodColors[method] || c.white;
|
|
2513
|
+
return c.white;
|
|
2475
2514
|
}
|
|
2476
2515
|
function formatTime2(ms) {
|
|
2477
2516
|
if (ms < 1) return `${c.gray}<1ms${c.reset}`;
|
|
@@ -2480,137 +2519,107 @@ function formatTime2(ms) {
|
|
|
2480
2519
|
return `${c.red}${ms}ms${c.reset}`;
|
|
2481
2520
|
}
|
|
2482
2521
|
var LOGO = `
|
|
2483
|
-
${c.
|
|
2484
|
-
${c.green} \u2502${c.reset} ${c.green}\u2502${c.reset}
|
|
2485
|
-
${c.green} \u2502${c.reset} ${c.brightGreen}\u26A1${c.reset} ${c.bold}${c.white}F L E X I R E A C T${c.reset} ${c.dim}v1.0.0${c.reset} ${c.green}\u2502${c.reset}
|
|
2486
|
-
${c.green} \u2502${c.reset} ${c.green}\u2502${c.reset}
|
|
2487
|
-
${c.green} \u2502${c.reset} ${c.dim}The Modern React Framework${c.reset} ${c.green}\u2502${c.reset}
|
|
2488
|
-
${c.green} \u2502${c.reset} ${c.green}\u2502${c.reset}
|
|
2489
|
-
${c.green} \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F${c.reset}
|
|
2522
|
+
${c.white}\u25B2${c.reset} ${c.bold}FlexiReact${c.reset} ${c.dim}4.1.0${c.reset}
|
|
2490
2523
|
`;
|
|
2491
|
-
var MINI_LOGO = `${c.brightGreen}\u26A1${c.reset} ${c.bold}FlexiReact${c.reset}`;
|
|
2492
|
-
var READY_MSG = ` ${c.green}\u25B2${c.reset} ${c.bold}Ready${c.reset} in`;
|
|
2493
2524
|
var logger = {
|
|
2494
2525
|
// Show startup logo
|
|
2495
2526
|
logo() {
|
|
2496
2527
|
console.log(LOGO);
|
|
2528
|
+
console.log(`${c.dim} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`);
|
|
2529
|
+
console.log("");
|
|
2497
2530
|
},
|
|
2498
|
-
// Server started -
|
|
2531
|
+
// Server started - Minimalist style
|
|
2499
2532
|
serverStart(config, startTime = Date.now()) {
|
|
2500
|
-
const { port, host, mode, pagesDir
|
|
2533
|
+
const { port, host, mode, pagesDir } = config;
|
|
2501
2534
|
const elapsed = Date.now() - startTime;
|
|
2535
|
+
console.log(` ${c.green}\u2714${c.reset} ${c.bold}Ready${c.reset} in ${elapsed}ms`);
|
|
2502
2536
|
console.log("");
|
|
2503
|
-
console.log(`
|
|
2504
|
-
console.log("");
|
|
2505
|
-
console.log(`
|
|
2506
|
-
console.log(` ${c.dim}\u251C${c.reset} ${c.bold}Environment:${c.reset} ${mode === "development" ? `${c.yellow}development${c.reset}` : `${c.green}production${c.reset}`}`);
|
|
2507
|
-
if (islands) {
|
|
2508
|
-
console.log(` ${c.dim}\u251C${c.reset} ${c.bold}Islands:${c.reset} ${c.green}enabled${c.reset}`);
|
|
2509
|
-
}
|
|
2510
|
-
if (rsc) {
|
|
2511
|
-
console.log(` ${c.dim}\u251C${c.reset} ${c.bold}RSC:${c.reset} ${c.green}enabled${c.reset}`);
|
|
2512
|
-
}
|
|
2513
|
-
console.log(` ${c.dim}\u2514${c.reset} ${c.bold}Pages:${c.reset} ${c.dim}${pagesDir}${c.reset}`);
|
|
2537
|
+
console.log(` ${c.bold}Local:${c.reset} ${c.cyan}http://${host}:${port}${c.reset}`);
|
|
2538
|
+
console.log(` ${c.bold}Mode:${c.reset} ${mode === "development" ? c.yellow : c.green}${mode}${c.reset}`);
|
|
2539
|
+
console.log(` ${c.bold}Pages:${c.reset} ${c.dim}${pagesDir}${c.reset}`);
|
|
2514
2540
|
console.log("");
|
|
2515
2541
|
},
|
|
2516
|
-
// HTTP request log - Compact single line
|
|
2542
|
+
// HTTP request log - Compact single line
|
|
2517
2543
|
request(method, path11, status, time, extra = {}) {
|
|
2518
2544
|
const methodColor = getMethodColor(method);
|
|
2519
2545
|
const statusColor = getStatusColor(status);
|
|
2520
2546
|
const timeStr = formatTime2(time);
|
|
2521
|
-
let badge =
|
|
2522
|
-
if (extra.type === "
|
|
2523
|
-
badge = `${c.
|
|
2524
|
-
} else if (extra.type === "dynamic" || extra.type === "ssr") {
|
|
2525
|
-
badge = `${c.magenta}\u0192${c.reset}`;
|
|
2547
|
+
let badge = `${c.dim}\u25CB${c.reset}`;
|
|
2548
|
+
if (extra.type === "dynamic" || extra.type === "ssr") {
|
|
2549
|
+
badge = `${c.white}\u0192${c.reset}`;
|
|
2526
2550
|
} else if (extra.type === "api") {
|
|
2527
|
-
badge = `${c.
|
|
2528
|
-
} else if (extra.type === "asset") {
|
|
2529
|
-
badge = `${c.dim}\u25E6${c.reset}`;
|
|
2530
|
-
} else {
|
|
2531
|
-
badge = `${c.magenta}\u0192${c.reset}`;
|
|
2551
|
+
badge = `${c.cyan}\u03BB${c.reset}`;
|
|
2532
2552
|
}
|
|
2533
2553
|
const statusStr = `${statusColor}${status}${c.reset}`;
|
|
2534
2554
|
const methodStr = `${methodColor}${method}${c.reset}`;
|
|
2535
|
-
console.log(`
|
|
2555
|
+
console.log(` ${badge} ${methodStr} ${path11} ${statusStr} ${c.dim}${timeStr}${c.reset}`);
|
|
2536
2556
|
},
|
|
2537
2557
|
// Info message
|
|
2538
2558
|
info(msg) {
|
|
2539
|
-
console.log(`
|
|
2559
|
+
console.log(` ${c.cyan}\u2139${c.reset} ${msg}`);
|
|
2540
2560
|
},
|
|
2541
2561
|
// Success message
|
|
2542
2562
|
success(msg) {
|
|
2543
|
-
console.log(`
|
|
2563
|
+
console.log(` ${c.green}\u2714${c.reset} ${msg}`);
|
|
2544
2564
|
},
|
|
2545
2565
|
// Warning message
|
|
2546
2566
|
warn(msg) {
|
|
2547
|
-
console.log(`
|
|
2567
|
+
console.log(` ${c.yellow}\u26A0${c.reset} ${c.yellow}${msg}${c.reset}`);
|
|
2548
2568
|
},
|
|
2549
2569
|
// Error message
|
|
2550
2570
|
error(msg, err = null) {
|
|
2551
|
-
console.log(`
|
|
2571
|
+
console.log(` ${c.red}\u2716${c.reset} ${c.red}${msg}${c.reset}`);
|
|
2552
2572
|
if (err && err.stack) {
|
|
2573
|
+
console.log("");
|
|
2553
2574
|
const stack = err.stack.split("\n").slice(1, 4).join("\n");
|
|
2554
2575
|
console.log(`${c.dim}${stack}${c.reset}`);
|
|
2576
|
+
console.log("");
|
|
2555
2577
|
}
|
|
2556
2578
|
},
|
|
2557
2579
|
// Compilation message
|
|
2558
2580
|
compile(file, time) {
|
|
2559
|
-
console.log(`
|
|
2581
|
+
console.log(` ${c.white}\u25CF${c.reset} Compiling ${c.dim}${file}${c.reset} ${c.dim}(${time}ms)${c.reset}`);
|
|
2560
2582
|
},
|
|
2561
2583
|
// Hot reload
|
|
2562
2584
|
hmr(file) {
|
|
2563
|
-
console.log(`
|
|
2585
|
+
console.log(` ${c.green}\u21BB${c.reset} Fast Refresh ${c.dim}${file}${c.reset}`);
|
|
2564
2586
|
},
|
|
2565
2587
|
// Plugin loaded
|
|
2566
2588
|
plugin(name) {
|
|
2567
|
-
console.log(`
|
|
2589
|
+
console.log(` ${c.cyan}\u25C6${c.reset} Plugin ${c.dim}${name}${c.reset}`);
|
|
2568
2590
|
},
|
|
2569
2591
|
// Route info
|
|
2570
2592
|
route(path11, type) {
|
|
2571
|
-
const
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
api: c.blue
|
|
2575
|
-
};
|
|
2576
|
-
const color = typeColors[type] || c.white;
|
|
2577
|
-
console.log(` ${c.dim}\u251C\u2500${c.reset} ${path11} ${color}[${type}]${c.reset}`);
|
|
2593
|
+
const typeLabel = type === "api" ? "\u03BB" : type === "dynamic" ? "\u0192" : "\u25CB";
|
|
2594
|
+
const color = type === "api" ? c.cyan : type === "dynamic" ? c.white : c.dim;
|
|
2595
|
+
console.log(` ${color}${typeLabel}${c.reset} ${path11}`);
|
|
2578
2596
|
},
|
|
2579
2597
|
// Divider
|
|
2580
2598
|
divider() {
|
|
2581
|
-
console.log(`${c.dim}
|
|
2599
|
+
console.log(`${c.dim} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`);
|
|
2582
2600
|
},
|
|
2583
2601
|
// Blank line
|
|
2584
2602
|
blank() {
|
|
2585
2603
|
console.log("");
|
|
2586
2604
|
},
|
|
2587
|
-
// Port in use error
|
|
2605
|
+
// Port in use error
|
|
2588
2606
|
portInUse(port) {
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
${c.cyan}npx kill-port ${port}${c.reset}
|
|
2596
|
-
|
|
2597
|
-
${c.yellow}2.${c.reset} Use a different port in ${c.cyan}flexireact.config.js${c.reset}:
|
|
2598
|
-
${c.dim}server: { port: 3001 }${c.reset}
|
|
2599
|
-
|
|
2600
|
-
${c.yellow}3.${c.reset} Set PORT environment variable:
|
|
2601
|
-
${c.cyan}PORT=3001 npm run dev${c.reset}
|
|
2602
|
-
`);
|
|
2607
|
+
this.error(`Port ${port} is already in use.`);
|
|
2608
|
+
this.blank();
|
|
2609
|
+
console.log(` ${c.dim}Try:${c.reset}`);
|
|
2610
|
+
console.log(` 1. Kill the process on port ${port}`);
|
|
2611
|
+
console.log(` 2. Use a different port via PORT env var`);
|
|
2612
|
+
this.blank();
|
|
2603
2613
|
},
|
|
2604
2614
|
// Build info
|
|
2605
2615
|
build(stats) {
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
`);
|
|
2616
|
+
this.blank();
|
|
2617
|
+
console.log(` ${c.green}\u2714${c.reset} Build completed`);
|
|
2618
|
+
this.blank();
|
|
2619
|
+
console.log(` ${c.dim}Route${c.reset} ${c.dim}Size${c.reset}`);
|
|
2620
|
+
console.log(` ${c.dim}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`);
|
|
2621
|
+
console.log(` ${c.dim}Total time:${c.reset} ${c.white}${stats.time}ms${c.reset}`);
|
|
2622
|
+
this.blank();
|
|
2614
2623
|
}
|
|
2615
2624
|
};
|
|
2616
2625
|
|
|
@@ -3038,22 +3047,69 @@ function serializeArgs(args) {
|
|
|
3038
3047
|
return arg;
|
|
3039
3048
|
});
|
|
3040
3049
|
}
|
|
3050
|
+
var ALLOWED_SERIALIZED_TYPES = /* @__PURE__ */ new Set(["FormData", "Date", "File"]);
|
|
3051
|
+
var MAX_PAYLOAD_DEPTH = 10;
|
|
3052
|
+
var MAX_STRING_LENGTH = 1e6;
|
|
3053
|
+
function validateInput(obj, depth = 0) {
|
|
3054
|
+
if (depth > MAX_PAYLOAD_DEPTH) {
|
|
3055
|
+
throw new Error("Payload too deeply nested");
|
|
3056
|
+
}
|
|
3057
|
+
if (obj === null || obj === void 0) return true;
|
|
3058
|
+
if (typeof obj === "string") {
|
|
3059
|
+
if (obj.length > MAX_STRING_LENGTH) {
|
|
3060
|
+
throw new Error("String value too long");
|
|
3061
|
+
}
|
|
3062
|
+
return true;
|
|
3063
|
+
}
|
|
3064
|
+
if (typeof obj !== "object") return true;
|
|
3065
|
+
if ("__proto__" in obj || "constructor" in obj || "prototype" in obj) {
|
|
3066
|
+
throw new Error("Invalid payload: prototype pollution attempt detected");
|
|
3067
|
+
}
|
|
3068
|
+
if ("$$type" in obj) {
|
|
3069
|
+
if (!ALLOWED_SERIALIZED_TYPES.has(obj.$$type)) {
|
|
3070
|
+
throw new Error(`Invalid serialized type: ${obj.$$type}`);
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
if (Array.isArray(obj)) {
|
|
3074
|
+
for (const item of obj) {
|
|
3075
|
+
validateInput(item, depth + 1);
|
|
3076
|
+
}
|
|
3077
|
+
} else {
|
|
3078
|
+
for (const value of Object.values(obj)) {
|
|
3079
|
+
validateInput(value, depth + 1);
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
return true;
|
|
3083
|
+
}
|
|
3041
3084
|
function deserializeArgs(args) {
|
|
3085
|
+
validateInput(args);
|
|
3042
3086
|
return args.map((arg) => {
|
|
3043
3087
|
if (arg && typeof arg === "object") {
|
|
3044
3088
|
if (arg.$$type === "FormData") {
|
|
3045
3089
|
const formData = new FormData();
|
|
3046
|
-
for (const [key, value] of Object.entries(arg.data)) {
|
|
3090
|
+
for (const [key, value] of Object.entries(arg.data || {})) {
|
|
3091
|
+
if (typeof key !== "string" || key.startsWith("__")) continue;
|
|
3047
3092
|
if (Array.isArray(value)) {
|
|
3048
|
-
value.forEach((v) =>
|
|
3049
|
-
|
|
3050
|
-
|
|
3093
|
+
value.forEach((v) => {
|
|
3094
|
+
if (typeof v === "string" || typeof v === "number") {
|
|
3095
|
+
formData.append(key, String(v));
|
|
3096
|
+
}
|
|
3097
|
+
});
|
|
3098
|
+
} else if (typeof value === "string" || typeof value === "number") {
|
|
3099
|
+
formData.append(key, String(value));
|
|
3051
3100
|
}
|
|
3052
3101
|
}
|
|
3053
3102
|
return formData;
|
|
3054
3103
|
}
|
|
3055
3104
|
if (arg.$$type === "Date") {
|
|
3056
|
-
|
|
3105
|
+
const date = new Date(arg.value);
|
|
3106
|
+
if (isNaN(date.getTime())) {
|
|
3107
|
+
throw new Error("Invalid date value");
|
|
3108
|
+
}
|
|
3109
|
+
return date;
|
|
3110
|
+
}
|
|
3111
|
+
if (arg.$$type === "File") {
|
|
3112
|
+
return { name: String(arg.name || ""), type: String(arg.type || ""), size: Number(arg.size || 0) };
|
|
3057
3113
|
}
|
|
3058
3114
|
}
|
|
3059
3115
|
return arg;
|
|
@@ -3586,7 +3642,6 @@ async function createServer(options = {}) {
|
|
|
3586
3642
|
const serverStartTime = Date.now();
|
|
3587
3643
|
const projectRoot = options.projectRoot || process.cwd();
|
|
3588
3644
|
const isDev = options.mode === "development";
|
|
3589
|
-
logger.logo();
|
|
3590
3645
|
const rawConfig = await loadConfig(projectRoot);
|
|
3591
3646
|
const config = resolvePaths(rawConfig, projectRoot);
|
|
3592
3647
|
await loadPlugins(projectRoot, config);
|
|
@@ -3597,7 +3652,7 @@ async function createServer(options = {}) {
|
|
|
3597
3652
|
const loadModule = createModuleLoader(isDev);
|
|
3598
3653
|
const server = http.createServer(async (req, res) => {
|
|
3599
3654
|
const startTime = Date.now();
|
|
3600
|
-
const url = new URL(req.url, `http://${req.headers.host || "localhost"}`);
|
|
3655
|
+
const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
|
|
3601
3656
|
const pathname = url.pathname;
|
|
3602
3657
|
try {
|
|
3603
3658
|
await pluginManager.runHook(PluginHooks.REQUEST, req, res);
|
|
@@ -3605,7 +3660,7 @@ async function createServer(options = {}) {
|
|
|
3605
3660
|
if (!middlewareResult.continue) {
|
|
3606
3661
|
return;
|
|
3607
3662
|
}
|
|
3608
|
-
const effectivePath = middlewareResult.rewritten ? new URL(req.url, `http://${req.headers.host}`).pathname : pathname;
|
|
3663
|
+
const effectivePath = middlewareResult.rewritten ? new URL(req.url || "/", `http://${req.headers.host}`).pathname : pathname;
|
|
3609
3664
|
if (await serveStaticFile(res, config.publicDir, effectivePath)) {
|
|
3610
3665
|
return;
|
|
3611
3666
|
}
|
|
@@ -3619,6 +3674,26 @@ async function createServer(options = {}) {
|
|
|
3619
3674
|
const componentName = effectivePath.slice(18).replace(".js", "");
|
|
3620
3675
|
return await serveClientComponent(res, config.pagesDir, componentName);
|
|
3621
3676
|
}
|
|
3677
|
+
if (effectivePath.startsWith("/_flexi/client/")) {
|
|
3678
|
+
const componentName = effectivePath.slice(15).replace(".js", "");
|
|
3679
|
+
const searchDirs = [
|
|
3680
|
+
config.pagesDir,
|
|
3681
|
+
path8.join(process.cwd(), "app", "components"),
|
|
3682
|
+
path8.join(process.cwd(), "routes"),
|
|
3683
|
+
path8.join(process.cwd(), "routes", "(public)")
|
|
3684
|
+
];
|
|
3685
|
+
for (const dir of searchDirs) {
|
|
3686
|
+
try {
|
|
3687
|
+
await serveClientComponent(res, dir, componentName);
|
|
3688
|
+
return;
|
|
3689
|
+
} catch {
|
|
3690
|
+
continue;
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
3694
|
+
res.end("Component not found: " + componentName);
|
|
3695
|
+
return;
|
|
3696
|
+
}
|
|
3622
3697
|
if (effectivePath === "/_flexi/action" && req.method === "POST") {
|
|
3623
3698
|
return await handleServerAction(req, res);
|
|
3624
3699
|
}
|
|
@@ -3669,7 +3744,7 @@ async function createServer(options = {}) {
|
|
|
3669
3744
|
const duration = Date.now() - startTime;
|
|
3670
3745
|
if (isDev) {
|
|
3671
3746
|
const routeType = pathname.startsWith("/api/") ? "api" : pathname.startsWith("/_flexi/") ? "asset" : pathname.match(/\.(js|css|png|jpg|svg|ico)$/) ? "asset" : "dynamic";
|
|
3672
|
-
logger.request(req.method, pathname, res.statusCode, duration, { type: routeType });
|
|
3747
|
+
logger.request(req.method || "GET", pathname, res.statusCode, duration, { type: routeType });
|
|
3673
3748
|
}
|
|
3674
3749
|
await pluginManager.runHook(PluginHooks.RESPONSE, req, res, duration);
|
|
3675
3750
|
}
|
|
@@ -3730,9 +3805,9 @@ async function serveStaticFile(res, baseDir, pathname) {
|
|
|
3730
3805
|
async function handleApiRoute(req, res, route, loadModule) {
|
|
3731
3806
|
try {
|
|
3732
3807
|
const module = await loadModule(route.filePath);
|
|
3733
|
-
const method = req.method.toLowerCase();
|
|
3808
|
+
const method = (req.method || "GET").toLowerCase();
|
|
3734
3809
|
const body = await parseBody(req);
|
|
3735
|
-
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
3810
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
3736
3811
|
const query = Object.fromEntries(url.searchParams);
|
|
3737
3812
|
const enhancedReq = {
|
|
3738
3813
|
...req,
|
|
@@ -3758,11 +3833,28 @@ async function handleApiRoute(req, res, route, loadModule) {
|
|
|
3758
3833
|
}
|
|
3759
3834
|
async function handleServerAction(req, res) {
|
|
3760
3835
|
try {
|
|
3836
|
+
const contentType = req.headers["content-type"];
|
|
3837
|
+
if (!contentType?.includes("application/json")) {
|
|
3838
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
3839
|
+
res.end(JSON.stringify({ success: false, error: "Invalid content type" }));
|
|
3840
|
+
return;
|
|
3841
|
+
}
|
|
3842
|
+
const contentLength = parseInt(req.headers["content-length"] || "0", 10);
|
|
3843
|
+
if (contentLength > 10 * 1024 * 1024) {
|
|
3844
|
+
res.writeHead(413, { "Content-Type": "application/json" });
|
|
3845
|
+
res.end(JSON.stringify({ success: false, error: "Payload too large" }));
|
|
3846
|
+
return;
|
|
3847
|
+
}
|
|
3761
3848
|
const body = await parseBody(req);
|
|
3762
3849
|
const { actionId, args } = body;
|
|
3763
|
-
if (!actionId) {
|
|
3850
|
+
if (!actionId || typeof actionId !== "string" || !/^[a-zA-Z0-9_]+$/.test(actionId)) {
|
|
3764
3851
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
3765
|
-
res.end(JSON.stringify({ success: false, error: "
|
|
3852
|
+
res.end(JSON.stringify({ success: false, error: "Invalid actionId format" }));
|
|
3853
|
+
return;
|
|
3854
|
+
}
|
|
3855
|
+
if (args !== void 0 && !Array.isArray(args)) {
|
|
3856
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
3857
|
+
res.end(JSON.stringify({ success: false, error: "Invalid args format" }));
|
|
3766
3858
|
return;
|
|
3767
3859
|
}
|
|
3768
3860
|
const deserializedArgs = deserializeArgs(args || []);
|
|
@@ -3779,10 +3871,11 @@ async function handleServerAction(req, res) {
|
|
|
3779
3871
|
res.end(JSON.stringify(result));
|
|
3780
3872
|
} catch (error) {
|
|
3781
3873
|
console.error("Server Action Error:", error);
|
|
3874
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
3782
3875
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
3783
3876
|
res.end(JSON.stringify({
|
|
3784
3877
|
success: false,
|
|
3785
|
-
error: error.message
|
|
3878
|
+
error: isDev ? error.message : "Action execution failed"
|
|
3786
3879
|
}));
|
|
3787
3880
|
}
|
|
3788
3881
|
}
|
|
@@ -4002,6 +4095,8 @@ async function serveClientComponent(res, pagesDir, componentName) {
|
|
|
4002
4095
|
const useCallback = window.useCallback;
|
|
4003
4096
|
const useMemo = window.useMemo;
|
|
4004
4097
|
const useRef = window.useRef;
|
|
4098
|
+
const useContext = window.useContext;
|
|
4099
|
+
const useReducer = window.useReducer;
|
|
4005
4100
|
`
|
|
4006
4101
|
});
|
|
4007
4102
|
let code = result.code;
|
|
@@ -4022,41 +4117,53 @@ async function serveClientComponent(res, pagesDir, componentName) {
|
|
|
4022
4117
|
function generateClientHydrationScript(componentPath, props) {
|
|
4023
4118
|
const ext = path8.extname(componentPath);
|
|
4024
4119
|
const componentName = path8.basename(componentPath, ext);
|
|
4120
|
+
const propsJson = JSON.stringify(props);
|
|
4025
4121
|
return `
|
|
4026
4122
|
<script type="module">
|
|
4027
|
-
// FlexiReact Client Hydration
|
|
4123
|
+
// FlexiReact Client Hydration v4.1
|
|
4124
|
+
import React, { useState, useEffect, useCallback, useMemo, useRef, useContext, useReducer } from 'https://esm.sh/react@19.0.0';
|
|
4125
|
+
import { createRoot, hydrateRoot } from 'https://esm.sh/react-dom@19.0.0/client';
|
|
4126
|
+
|
|
4127
|
+
// Make React hooks available globally
|
|
4128
|
+
window.React = React;
|
|
4129
|
+
window.useState = useState;
|
|
4130
|
+
window.useEffect = useEffect;
|
|
4131
|
+
window.useCallback = useCallback;
|
|
4132
|
+
window.useMemo = useMemo;
|
|
4133
|
+
window.useRef = useRef;
|
|
4134
|
+
window.useContext = useContext;
|
|
4135
|
+
window.useReducer = useReducer;
|
|
4136
|
+
|
|
4028
4137
|
(async function() {
|
|
4029
4138
|
try {
|
|
4030
|
-
|
|
4031
|
-
const
|
|
4139
|
+
// Fetch and execute the component
|
|
4140
|
+
const response = await fetch('/_flexi/client/${componentName}.js');
|
|
4141
|
+
if (!response.ok) throw new Error('Failed to load component: ' + response.status);
|
|
4032
4142
|
|
|
4033
|
-
// Make React available globally for the component
|
|
4034
|
-
window.React = React.default || React;
|
|
4035
|
-
window.useState = React.useState;
|
|
4036
|
-
window.useEffect = React.useEffect;
|
|
4037
|
-
window.useCallback = React.useCallback;
|
|
4038
|
-
window.useMemo = React.useMemo;
|
|
4039
|
-
window.useRef = React.useRef;
|
|
4040
|
-
|
|
4041
|
-
// Fetch the component code
|
|
4042
|
-
const response = await fetch('/_flexi/component/${componentName}.js');
|
|
4043
4143
|
const code = await response.text();
|
|
4044
|
-
|
|
4045
|
-
// Create and import the module
|
|
4046
4144
|
const blob = new Blob([code], { type: 'application/javascript' });
|
|
4047
4145
|
const moduleUrl = URL.createObjectURL(blob);
|
|
4048
4146
|
const module = await import(moduleUrl);
|
|
4049
4147
|
|
|
4050
4148
|
const Component = module.default;
|
|
4051
|
-
|
|
4149
|
+
if (!Component) throw new Error('No default export found');
|
|
4052
4150
|
|
|
4053
|
-
|
|
4054
|
-
const
|
|
4055
|
-
ReactDOM.hydrateRoot(root, window.React.createElement(Component, props));
|
|
4151
|
+
const props = ${propsJson};
|
|
4152
|
+
const container = document.getElementById('flexi-root') || document.getElementById('root');
|
|
4056
4153
|
|
|
4057
|
-
|
|
4154
|
+
if (container) {
|
|
4155
|
+
// Use createRoot for client components (not hydrate since we rendered placeholder)
|
|
4156
|
+
const root = createRoot(container);
|
|
4157
|
+
root.render(React.createElement(Component, props));
|
|
4158
|
+
console.log('%c\u26A1 FlexiReact', 'color: #00FF9C; font-weight: bold', 'Component mounted');
|
|
4159
|
+
}
|
|
4058
4160
|
} catch (error) {
|
|
4059
|
-
console.error('\u26A1 FlexiReact: Hydration failed', error);
|
|
4161
|
+
console.error('%c\u26A1 FlexiReact', 'color: #ef4444; font-weight: bold', 'Hydration failed:', error);
|
|
4162
|
+
// Show error in UI
|
|
4163
|
+
const container = document.getElementById('flexi-root');
|
|
4164
|
+
if (container) {
|
|
4165
|
+
container.innerHTML = '<div style="color:#ef4444;padding:20px;text-align:center;">Failed to load component: ' + error.message + '</div>';
|
|
4166
|
+
}
|
|
4060
4167
|
}
|
|
4061
4168
|
})();
|
|
4062
4169
|
</script>`;
|
|
@@ -4341,7 +4448,7 @@ async function copyPublicAssets(publicDir, outDir) {
|
|
|
4341
4448
|
function generateManifest(options) {
|
|
4342
4449
|
const { routes, clientResult, serverResult, config } = options;
|
|
4343
4450
|
return {
|
|
4344
|
-
version: "
|
|
4451
|
+
version: "4.1.0",
|
|
4345
4452
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4346
4453
|
routes: {
|
|
4347
4454
|
pages: routes.pages.map((r) => ({
|
|
@@ -6302,7 +6409,7 @@ function formatBytes2(bytes) {
|
|
|
6302
6409
|
}
|
|
6303
6410
|
|
|
6304
6411
|
// core/index.ts
|
|
6305
|
-
var VERSION = "
|
|
6412
|
+
var VERSION = "4.0.0";
|
|
6306
6413
|
var core_default = {
|
|
6307
6414
|
VERSION,
|
|
6308
6415
|
createServer
|