bertui 1.2.9 → 2.0.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/README.md +44 -242
- package/TYPES_PATCH.md +17 -0
- package/bin/bertui.js +2 -7
- package/package.json +32 -98
- package/src/config.ts +4 -0
- package/src/index.ts +32 -0
- package/src/optional.ts +49 -0
- package/src/router.ts +3 -0
- package/tsconfig.json +29 -0
- package/LICENSE +0 -21
- package/index.js +0 -103
- package/src/analyzer/index.js +0 -370
- package/src/build/compiler/file-transpiler.js +0 -216
- package/src/build/compiler/index.js +0 -31
- package/src/build/compiler/route-discoverer.js +0 -49
- package/src/build/compiler/router-generator.js +0 -105
- package/src/build/css-builder.js +0 -81
- package/src/build/generators/html-generator.js +0 -263
- package/src/build/generators/robots-generator.js +0 -58
- package/src/build/generators/sitemap-generator.js +0 -63
- package/src/build/image-optimizer.js +0 -137
- package/src/build/processors/asset-processor.js +0 -19
- package/src/build/processors/css-builder.js +0 -142
- package/src/build/server-island-validator.js +0 -67
- package/src/build/ssr-renderer.js +0 -64
- package/src/build.js +0 -273
- package/src/cli.js +0 -131
- package/src/client/compiler.js +0 -522
- package/src/client/fast-refresh.js +0 -72
- package/src/client/hmr-runtime.js +0 -59
- package/src/compiler/index.js +0 -25
- package/src/compiler/router-generator-pure.js +0 -104
- package/src/compiler/transform.js +0 -149
- package/src/config/defaultConfig.js +0 -37
- package/src/config/index.js +0 -2
- package/src/config/loadConfig.js +0 -64
- package/src/config/og-image.png +0 -0
- package/src/css/index.js +0 -46
- package/src/css/processor.js +0 -172
- package/src/dev.js +0 -68
- package/src/hydration/index.js +0 -151
- package/src/image-optimizer/index.js +0 -103
- package/src/images/index.js +0 -102
- package/src/images/processor.js +0 -169
- package/src/layouts/index.js +0 -165
- package/src/loading/index.js +0 -210
- package/src/logger/logger.js +0 -320
- package/src/logger/notes.md +0 -20
- package/src/middleware/index.js +0 -182
- package/src/router/Router.js +0 -150
- package/src/router/SSRRouter.js +0 -156
- package/src/router/index.js +0 -3
- package/src/scaffolder/index.js +0 -310
- package/src/serve.js +0 -193
- package/src/server/dev-handler.js +0 -195
- package/src/server/dev-server-utils.js +0 -406
- package/src/server/dev-server.js +0 -15
- package/src/server/hmr-handler.js +0 -148
- package/src/server/index.js +0 -3
- package/src/server/notes.md +0 -1
- package/src/server/request-handler.js +0 -36
- package/src/server-islands/extractor.js +0 -198
- package/src/server-islands/index.js +0 -59
- package/src/styles/bertui.css +0 -210
- package/src/utils/cache.js +0 -297
- package/src/utils/env.js +0 -87
- package/src/utils/importhow.js +0 -52
- package/src/utils/index.js +0 -11
- package/src/utils/meta-extractor.js +0 -127
- package/types/bin/bertui.d.ts +0 -3
- package/types/bin/bertui.d.ts.map +0 -1
- package/types/error-overlay.d.ts +0 -2
- package/types/error-overlay.d.ts.map +0 -1
- package/types/index.d.ts +0 -26
- package/types/index.d.ts.map +0 -1
- package/types/scripts/fix-wasm-exports.d.ts +0 -2
- package/types/scripts/fix-wasm-exports.d.ts.map +0 -1
- package/types/src/analyzer/index.d.ts +0 -8
- package/types/src/analyzer/index.d.ts.map +0 -1
- package/types/src/build/compiler/file-transpiler.d.ts +0 -5
- package/types/src/build/compiler/file-transpiler.d.ts.map +0 -1
- package/types/src/build/compiler/index.d.ts +0 -12
- package/types/src/build/compiler/index.d.ts.map +0 -1
- package/types/src/build/compiler/route-discoverer.d.ts +0 -2
- package/types/src/build/compiler/route-discoverer.d.ts.map +0 -1
- package/types/src/build/compiler/router-generator.d.ts +0 -2
- package/types/src/build/compiler/router-generator.d.ts.map +0 -1
- package/types/src/build/css-builder.d.ts +0 -18
- package/types/src/build/css-builder.d.ts.map +0 -1
- package/types/src/build/generators/html-generator.d.ts +0 -2
- package/types/src/build/generators/html-generator.d.ts.map +0 -1
- package/types/src/build/generators/robots-generator.d.ts +0 -11
- package/types/src/build/generators/robots-generator.d.ts.map +0 -1
- package/types/src/build/generators/sitemap-generator.d.ts +0 -5
- package/types/src/build/generators/sitemap-generator.d.ts.map +0 -1
- package/types/src/build/image-optimizer.d.ts +0 -11
- package/types/src/build/image-optimizer.d.ts.map +0 -1
- package/types/src/build/processors/asset-processor.d.ts +0 -2
- package/types/src/build/processors/asset-processor.d.ts.map +0 -1
- package/types/src/build/processors/css-builder.d.ts +0 -2
- package/types/src/build/processors/css-builder.d.ts.map +0 -1
- package/types/src/build/server-island-validator.d.ts +0 -27
- package/types/src/build/server-island-validator.d.ts.map +0 -1
- package/types/src/build.d.ts +0 -5
- package/types/src/build.d.ts.map +0 -1
- package/types/src/cli.d.ts +0 -2
- package/types/src/cli.d.ts.map +0 -1
- package/types/src/client/compiler.d.ts +0 -16
- package/types/src/client/compiler.d.ts.map +0 -1
- package/types/src/client/fast-refresh.d.ts +0 -3
- package/types/src/client/fast-refresh.d.ts.map +0 -1
- package/types/src/client/hmr-runtime.d.ts +0 -4
- package/types/src/client/hmr-runtime.d.ts.map +0 -1
- package/types/src/compiler/index.d.ts +0 -8
- package/types/src/compiler/index.d.ts.map +0 -1
- package/types/src/compiler/router-generator-pure.d.ts +0 -2
- package/types/src/compiler/router-generator-pure.d.ts.map +0 -1
- package/types/src/compiler/transform.d.ts +0 -36
- package/types/src/compiler/transform.d.ts.map +0 -1
- package/types/src/config/defaultConfig.d.ts +0 -26
- package/types/src/config/defaultConfig.d.ts.map +0 -1
- package/types/src/config/index.d.ts +0 -3
- package/types/src/config/index.d.ts.map +0 -1
- package/types/src/config/loadConfig.d.ts +0 -2
- package/types/src/config/loadConfig.d.ts.map +0 -1
- package/types/src/css/index.d.ts +0 -6
- package/types/src/css/index.d.ts.map +0 -1
- package/types/src/css/processor.d.ts +0 -23
- package/types/src/css/processor.d.ts.map +0 -1
- package/types/src/dev.d.ts +0 -2
- package/types/src/dev.d.ts.map +0 -1
- package/types/src/hydration/index.d.ts +0 -33
- package/types/src/hydration/index.d.ts.map +0 -1
- package/types/src/image-optimizer/index.d.ts +0 -24
- package/types/src/image-optimizer/index.d.ts.map +0 -1
- package/types/src/images/index.d.ts +0 -12
- package/types/src/images/index.d.ts.map +0 -1
- package/types/src/images/processor.d.ts +0 -30
- package/types/src/images/processor.d.ts.map +0 -1
- package/types/src/layouts/index.d.ts +0 -28
- package/types/src/layouts/index.d.ts.map +0 -1
- package/types/src/loading/index.d.ts +0 -28
- package/types/src/loading/index.d.ts.map +0 -1
- package/types/src/logger/logger.d.ts +0 -30
- package/types/src/logger/logger.d.ts.map +0 -1
- package/types/src/middleware/index.d.ts +0 -61
- package/types/src/middleware/index.d.ts.map +0 -1
- package/types/src/router/Router.d.ts +0 -16
- package/types/src/router/Router.d.ts.map +0 -1
- package/types/src/router/SSRRouter.d.ts +0 -20
- package/types/src/router/SSRRouter.d.ts.map +0 -1
- package/types/src/router/index.d.ts +0 -3
- package/types/src/router/index.d.ts.map +0 -1
- package/types/src/scaffolder/index.d.ts +0 -14
- package/types/src/scaffolder/index.d.ts.map +0 -1
- package/types/src/serve.d.ts +0 -3
- package/types/src/serve.d.ts.map +0 -1
- package/types/src/server/dev-handler.d.ts +0 -13
- package/types/src/server/dev-handler.d.ts.map +0 -1
- package/types/src/server/dev-server-utils.d.ts +0 -6
- package/types/src/server/dev-server-utils.d.ts.map +0 -1
- package/types/src/server/dev-server.d.ts +0 -18
- package/types/src/server/dev-server.d.ts.map +0 -1
- package/types/src/server/hmr-handler.d.ts +0 -19
- package/types/src/server/hmr-handler.d.ts.map +0 -1
- package/types/src/server/index.d.ts +0 -4
- package/types/src/server/index.d.ts.map +0 -1
- package/types/src/server/request-handler.d.ts +0 -19
- package/types/src/server/request-handler.d.ts.map +0 -1
- package/types/src/server-islands/extractor.d.ts +0 -16
- package/types/src/server-islands/extractor.d.ts.map +0 -1
- package/types/src/server-islands/index.d.ts +0 -3
- package/types/src/server-islands/index.d.ts.map +0 -1
- package/types/src/utils/cache.d.ts +0 -52
- package/types/src/utils/cache.d.ts.map +0 -1
- package/types/src/utils/env.d.ts +0 -20
- package/types/src/utils/env.d.ts.map +0 -1
- package/types/src/utils/importhow.d.ts +0 -15
- package/types/src/utils/importhow.d.ts.map +0 -1
- package/types/src/utils/index.d.ts +0 -3
- package/types/src/utils/index.d.ts.map +0 -1
- package/types/src/utils/meta-extractor.d.ts +0 -13
- package/types/src/utils/meta-extractor.d.ts.map +0 -1
package/src/middleware/index.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
// bertui/src/middleware/index.js
|
|
2
|
-
// Middleware system - src/middleware.ts runs before every request
|
|
3
|
-
|
|
4
|
-
import { join, extname } from 'path';
|
|
5
|
-
import { existsSync } from 'fs';
|
|
6
|
-
import logger from '../logger/logger.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Middleware context passed to every middleware function
|
|
10
|
-
*/
|
|
11
|
-
export class MiddlewareContext {
|
|
12
|
-
constructor(request, options = {}) {
|
|
13
|
-
this.request = request;
|
|
14
|
-
this.url = new URL(request.url);
|
|
15
|
-
this.pathname = this.url.pathname;
|
|
16
|
-
this.method = request.method;
|
|
17
|
-
this.headers = Object.fromEntries(request.headers.entries());
|
|
18
|
-
this.params = options.params || {};
|
|
19
|
-
this.route = options.route || null;
|
|
20
|
-
this._response = null;
|
|
21
|
-
this._redirectTo = null;
|
|
22
|
-
this._stopped = false;
|
|
23
|
-
this.locals = {}; // Share data between middlewares and pages
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** Respond early - stops further processing */
|
|
27
|
-
respond(body, init = {}) {
|
|
28
|
-
this._response = new Response(body, {
|
|
29
|
-
status: init.status || 200,
|
|
30
|
-
headers: {
|
|
31
|
-
'Content-Type': 'text/html',
|
|
32
|
-
...init.headers
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
this._stopped = true;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Redirect to another URL */
|
|
39
|
-
redirect(url, status = 302) {
|
|
40
|
-
this._redirectTo = url;
|
|
41
|
-
this._response = Response.redirect(url, status);
|
|
42
|
-
this._stopped = true;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Set a response header (added to final response) */
|
|
46
|
-
setHeader(key, value) {
|
|
47
|
-
if (!this._extraHeaders) this._extraHeaders = {};
|
|
48
|
-
this._extraHeaders[key] = value;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Check if middleware stopped the chain */
|
|
52
|
-
get stopped() {
|
|
53
|
-
return this._stopped;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Load and run user middleware from src/middleware.ts or src/middleware.js
|
|
59
|
-
*/
|
|
60
|
-
export async function loadMiddleware(root) {
|
|
61
|
-
const candidates = [
|
|
62
|
-
join(root, 'src', 'middleware.ts'),
|
|
63
|
-
join(root, 'src', 'middleware.tsx'),
|
|
64
|
-
join(root, 'src', 'middleware.js'),
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
for (const path of candidates) {
|
|
68
|
-
if (existsSync(path)) {
|
|
69
|
-
try {
|
|
70
|
-
// Transpile if TypeScript
|
|
71
|
-
const ext = extname(path);
|
|
72
|
-
let code = await Bun.file(path).text();
|
|
73
|
-
|
|
74
|
-
if (ext === '.ts' || ext === '.tsx') {
|
|
75
|
-
const transpiler = new Bun.Transpiler({
|
|
76
|
-
loader: ext === '.tsx' ? 'tsx' : 'ts',
|
|
77
|
-
target: 'bun',
|
|
78
|
-
});
|
|
79
|
-
code = await transpiler.transform(code);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Write to temp file and import
|
|
83
|
-
const tmpPath = join(root, '.bertui', 'middleware.js');
|
|
84
|
-
await Bun.write(tmpPath, code);
|
|
85
|
-
|
|
86
|
-
const mod = await import(`${tmpPath}?t=${Date.now()}`);
|
|
87
|
-
logger.success('✅ Middleware loaded: ' + path.replace(root, ''));
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
default: mod.default || null,
|
|
91
|
-
onRequest: mod.onRequest || mod.default || null,
|
|
92
|
-
onResponse: mod.onResponse || null,
|
|
93
|
-
onError: mod.onError || null,
|
|
94
|
-
};
|
|
95
|
-
} catch (err) {
|
|
96
|
-
logger.error(`Failed to load middleware: ${err.message}`);
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Run middleware chain for a request
|
|
107
|
-
* Returns a Response if middleware intercepted, null to continue
|
|
108
|
-
*/
|
|
109
|
-
export async function runMiddleware(middlewareMod, request, routeInfo = {}) {
|
|
110
|
-
if (!middlewareMod) return null;
|
|
111
|
-
|
|
112
|
-
const ctx = new MiddlewareContext(request, routeInfo);
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
// Run onRequest middleware
|
|
116
|
-
if (middlewareMod.onRequest) {
|
|
117
|
-
await middlewareMod.onRequest(ctx);
|
|
118
|
-
if (ctx.stopped) {
|
|
119
|
-
logger.debug(`🛡️ Middleware intercepted: ${ctx.pathname}`);
|
|
120
|
-
return ctx._response;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return null; // Continue to route handler
|
|
125
|
-
} catch (err) {
|
|
126
|
-
logger.error(`Middleware error: ${err.message}`);
|
|
127
|
-
|
|
128
|
-
// Run error handler if defined
|
|
129
|
-
if (middlewareMod.onError) {
|
|
130
|
-
try {
|
|
131
|
-
await middlewareMod.onError(ctx, err);
|
|
132
|
-
if (ctx._response) return ctx._response;
|
|
133
|
-
} catch (e) {
|
|
134
|
-
logger.error(`Middleware error handler failed: ${e.message}`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* MiddlewareManager - watches and reloads middleware on change
|
|
144
|
-
*/
|
|
145
|
-
export class MiddlewareManager {
|
|
146
|
-
constructor(root) {
|
|
147
|
-
this.root = root;
|
|
148
|
-
this.middleware = null;
|
|
149
|
-
this.watcher = null;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async load() {
|
|
153
|
-
this.middleware = await loadMiddleware(this.root);
|
|
154
|
-
return this;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async run(request, routeInfo = {}) {
|
|
158
|
-
return runMiddleware(this.middleware, request, routeInfo);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
watch() {
|
|
162
|
-
const candidates = [
|
|
163
|
-
join(this.root, 'src', 'middleware.ts'),
|
|
164
|
-
join(this.root, 'src', 'middleware.tsx'),
|
|
165
|
-
join(this.root, 'src', 'middleware.js'),
|
|
166
|
-
];
|
|
167
|
-
|
|
168
|
-
const existing = candidates.find(existsSync);
|
|
169
|
-
if (!existing) return;
|
|
170
|
-
|
|
171
|
-
const { watch } = require('fs');
|
|
172
|
-
this.watcher = watch(existing, async () => {
|
|
173
|
-
logger.info('🔄 Reloading middleware...');
|
|
174
|
-
this.middleware = await loadMiddleware(this.root);
|
|
175
|
-
logger.success('✅ Middleware reloaded');
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
dispose() {
|
|
180
|
-
if (this.watcher) this.watcher.close();
|
|
181
|
-
}
|
|
182
|
-
}
|
package/src/router/Router.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
// src/router/Router.js - SSR COMPATIBLE VERSION
|
|
2
|
-
import { useState, useEffect, createContext, useContext } from 'react';
|
|
3
|
-
|
|
4
|
-
const RouterContext = createContext(null);
|
|
5
|
-
|
|
6
|
-
// ✅ FIX: SSR-safe useRouter
|
|
7
|
-
export function useRouter() {
|
|
8
|
-
const context = useContext(RouterContext);
|
|
9
|
-
|
|
10
|
-
// During SSR (when window doesn't exist), return a mock router
|
|
11
|
-
if (typeof window === 'undefined') {
|
|
12
|
-
return {
|
|
13
|
-
pathname: '/',
|
|
14
|
-
params: {},
|
|
15
|
-
navigate: () => {},
|
|
16
|
-
currentRoute: null,
|
|
17
|
-
isSSR: true
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (!context) {
|
|
22
|
-
throw new Error('useRouter must be used within a Router component');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return context;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function Router({ routes }) {
|
|
29
|
-
const [currentRoute, setCurrentRoute] = useState(null);
|
|
30
|
-
const [params, setParams] = useState({});
|
|
31
|
-
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
matchAndSetRoute(window.location.pathname);
|
|
34
|
-
|
|
35
|
-
const handlePopState = () => {
|
|
36
|
-
matchAndSetRoute(window.location.pathname);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
window.addEventListener('popstate', handlePopState);
|
|
40
|
-
return () => window.removeEventListener('popstate', handlePopState);
|
|
41
|
-
}, [routes]);
|
|
42
|
-
|
|
43
|
-
function matchAndSetRoute(pathname) {
|
|
44
|
-
// Try static routes first
|
|
45
|
-
for (const route of routes) {
|
|
46
|
-
if (route.type === 'static' && route.path === pathname) {
|
|
47
|
-
setCurrentRoute(route);
|
|
48
|
-
setParams({});
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Try dynamic routes
|
|
54
|
-
for (const route of routes) {
|
|
55
|
-
if (route.type === 'dynamic') {
|
|
56
|
-
const pattern = route.path.replace(/\[([^\]]+)\]/g, '([^/]+)');
|
|
57
|
-
const regex = new RegExp('^' + pattern + '$');
|
|
58
|
-
const match = pathname.match(regex);
|
|
59
|
-
|
|
60
|
-
if (match) {
|
|
61
|
-
const paramNames = [...route.path.matchAll(/\[([^\]]+)\]/g)].map(m => m[1]);
|
|
62
|
-
const extractedParams = {};
|
|
63
|
-
paramNames.forEach((name, i) => {
|
|
64
|
-
extractedParams[name] = match[i + 1];
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
setCurrentRoute(route);
|
|
68
|
-
setParams(extractedParams);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// No match found
|
|
75
|
-
setCurrentRoute(null);
|
|
76
|
-
setParams({});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function navigate(path) {
|
|
80
|
-
if (typeof window !== 'undefined') {
|
|
81
|
-
window.history.pushState({}, '', path);
|
|
82
|
-
matchAndSetRoute(path);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const routerValue = {
|
|
87
|
-
currentRoute,
|
|
88
|
-
params,
|
|
89
|
-
navigate,
|
|
90
|
-
pathname: typeof window !== 'undefined' ? window.location.pathname : '/',
|
|
91
|
-
isSSR: typeof window === 'undefined'
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const Component = currentRoute?.component;
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<RouterContext.Provider value={routerValue}>
|
|
98
|
-
{Component ? <Component params={params} /> : <NotFound />}
|
|
99
|
-
</RouterContext.Provider>
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ✅ FIX: SSR-safe Link component
|
|
104
|
-
export function Link({ to, children, ...props }) {
|
|
105
|
-
// Try to get router, but don't fail if it doesn't exist
|
|
106
|
-
let router;
|
|
107
|
-
try {
|
|
108
|
-
router = useRouter();
|
|
109
|
-
} catch (e) {
|
|
110
|
-
// During SSR, router might not be available
|
|
111
|
-
router = null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function handleClick(e) {
|
|
115
|
-
// During SSR, just use normal link behavior
|
|
116
|
-
if (typeof window === 'undefined') return;
|
|
117
|
-
|
|
118
|
-
// If no router or navigate function, use normal link
|
|
119
|
-
if (!router || !router.navigate) return;
|
|
120
|
-
|
|
121
|
-
// Only prevent default if we have client-side routing
|
|
122
|
-
e.preventDefault();
|
|
123
|
-
router.navigate(to);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<a href={to} onClick={handleClick} {...props}>
|
|
128
|
-
{children}
|
|
129
|
-
</a>
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function NotFound() {
|
|
134
|
-
return (
|
|
135
|
-
<div style={{
|
|
136
|
-
display: 'flex',
|
|
137
|
-
flexDirection: 'column',
|
|
138
|
-
alignItems: 'center',
|
|
139
|
-
justifyContent: 'center',
|
|
140
|
-
minHeight: '100vh',
|
|
141
|
-
fontFamily: 'system-ui'
|
|
142
|
-
}}>
|
|
143
|
-
<h1 style={{ fontSize: '6rem', margin: 0 }}>404</h1>
|
|
144
|
-
<p style={{ fontSize: '1.5rem', color: '#666' }}>Page not found</p>
|
|
145
|
-
<a href="/" style={{ color: '#10b981', textDecoration: 'none', fontSize: '1.2rem' }}>
|
|
146
|
-
Go home
|
|
147
|
-
</a>
|
|
148
|
-
</div>
|
|
149
|
-
);
|
|
150
|
-
}
|
package/src/router/SSRRouter.js
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
// src/router/SSRRouter.jsx
|
|
2
|
-
// SSR-Compatible Router for BertUI - Works during build AND runtime
|
|
3
|
-
import React, { useState, useEffect, createContext, useContext } from 'react';
|
|
4
|
-
|
|
5
|
-
const RouterContext = createContext(null);
|
|
6
|
-
|
|
7
|
-
// ✅ SSR-safe useRouter hook
|
|
8
|
-
export function useRouter() {
|
|
9
|
-
const context = useContext(RouterContext);
|
|
10
|
-
|
|
11
|
-
// During SSR, provide a mock router
|
|
12
|
-
if (!context) {
|
|
13
|
-
return {
|
|
14
|
-
pathname: '/',
|
|
15
|
-
params: {},
|
|
16
|
-
navigate: () => {},
|
|
17
|
-
isSSR: true
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return context;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// ✅ SSR-safe Router component
|
|
25
|
-
export function SSRRouter({ routes, initialPath = '/' }) {
|
|
26
|
-
const [currentRoute, setCurrentRoute] = useState(null);
|
|
27
|
-
const [params, setParams] = useState({});
|
|
28
|
-
const [isClient, setIsClient] = useState(false);
|
|
29
|
-
|
|
30
|
-
// Detect if we're in the browser
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
setIsClient(true);
|
|
33
|
-
matchAndSetRoute(window.location.pathname);
|
|
34
|
-
|
|
35
|
-
const handlePopState = () => {
|
|
36
|
-
matchAndSetRoute(window.location.pathname);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
window.addEventListener('popstate', handlePopState);
|
|
40
|
-
return () => window.removeEventListener('popstate', handlePopState);
|
|
41
|
-
}, [routes]);
|
|
42
|
-
|
|
43
|
-
// Match route on server-side (SSR)
|
|
44
|
-
if (!isClient && !currentRoute) {
|
|
45
|
-
const matched = matchRoute(initialPath, routes);
|
|
46
|
-
if (matched) {
|
|
47
|
-
return React.createElement(
|
|
48
|
-
RouterContext.Provider,
|
|
49
|
-
{ value: { pathname: initialPath, params: matched.params, navigate: () => {}, isSSR: true } },
|
|
50
|
-
React.createElement(matched.route.component, { params: matched.params })
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function matchRoute(pathname, routesList) {
|
|
56
|
-
// Try static routes first
|
|
57
|
-
for (const route of routesList) {
|
|
58
|
-
if (route.type === 'static' && route.path === pathname) {
|
|
59
|
-
return { route, params: {} };
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Try dynamic routes
|
|
64
|
-
for (const route of routesList) {
|
|
65
|
-
if (route.type === 'dynamic') {
|
|
66
|
-
const pattern = route.path.replace(/\[([^\]]+)\]/g, '([^/]+)');
|
|
67
|
-
const regex = new RegExp('^' + pattern + '$');
|
|
68
|
-
const match = pathname.match(regex);
|
|
69
|
-
|
|
70
|
-
if (match) {
|
|
71
|
-
const paramNames = [...route.path.matchAll(/\[([^\]]+)\]/g)].map(m => m[1]);
|
|
72
|
-
const extractedParams = {};
|
|
73
|
-
paramNames.forEach((name, i) => {
|
|
74
|
-
extractedParams[name] = match[i + 1];
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
return { route, params: extractedParams };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function matchAndSetRoute(pathname) {
|
|
86
|
-
const matched = matchRoute(pathname, routes);
|
|
87
|
-
|
|
88
|
-
if (matched) {
|
|
89
|
-
setCurrentRoute(matched.route);
|
|
90
|
-
setParams(matched.params);
|
|
91
|
-
} else {
|
|
92
|
-
setCurrentRoute(null);
|
|
93
|
-
setParams({});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function navigate(path) {
|
|
98
|
-
if (typeof window !== 'undefined') {
|
|
99
|
-
window.history.pushState({}, '', path);
|
|
100
|
-
matchAndSetRoute(path);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const routerValue = {
|
|
105
|
-
currentRoute,
|
|
106
|
-
params,
|
|
107
|
-
navigate,
|
|
108
|
-
pathname: typeof window !== 'undefined' ? window.location.pathname : initialPath,
|
|
109
|
-
isSSR: !isClient
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const Component = currentRoute?.component;
|
|
113
|
-
|
|
114
|
-
return React.createElement(
|
|
115
|
-
RouterContext.Provider,
|
|
116
|
-
{ value: routerValue },
|
|
117
|
-
Component ? React.createElement(Component, { params }) : React.createElement(NotFound, null)
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// ✅ SSR-safe Link component
|
|
122
|
-
export function Link({ to, children, ...props }) {
|
|
123
|
-
const { navigate, isSSR } = useRouter();
|
|
124
|
-
|
|
125
|
-
function handleClick(e) {
|
|
126
|
-
// Don't prevent default during SSR
|
|
127
|
-
if (isSSR) return;
|
|
128
|
-
|
|
129
|
-
e.preventDefault();
|
|
130
|
-
navigate(to);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return React.createElement('a', { href: to, onClick: handleClick, ...props }, children);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function NotFound() {
|
|
137
|
-
return React.createElement(
|
|
138
|
-
'div',
|
|
139
|
-
{
|
|
140
|
-
style: {
|
|
141
|
-
display: 'flex',
|
|
142
|
-
flexDirection: 'column',
|
|
143
|
-
alignItems: 'center',
|
|
144
|
-
justifyContent: 'center',
|
|
145
|
-
minHeight: '100vh',
|
|
146
|
-
fontFamily: 'system-ui'
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
React.createElement('h1', { style: { fontSize: '6rem', margin: 0 } }, '404'),
|
|
150
|
-
React.createElement('p', { style: { fontSize: '1.5rem', color: '#666' } }, 'Page not found'),
|
|
151
|
-
React.createElement('a', {
|
|
152
|
-
href: '/',
|
|
153
|
-
style: { color: '#10b981', textDecoration: 'none', fontSize: '1.2rem' }
|
|
154
|
-
}, 'Go home')
|
|
155
|
-
);
|
|
156
|
-
}
|
package/src/router/index.js
DELETED