weifuwu 0.33.0 → 0.33.2
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 +303 -145
- package/dist/client/app.d.ts +21 -0
- package/dist/client/index.d.ts +21 -0
- package/dist/client/index.js +320 -0
- package/dist/client/jsx-runtime.d.ts +60 -0
- package/dist/client/jsx-runtime.js +320 -0
- package/dist/client/router.d.ts +51 -0
- package/dist/client/signal.d.ts +19 -0
- package/dist/client/types.d.ts +45 -0
- package/dist/index.d.ts +0 -7
- package/dist/index.js +13 -925
- package/dist/types.d.ts +0 -1
- package/package.json +9 -42
- package/dist/middleware/esbuild-dev.d.ts +0 -69
- package/dist/middleware/esbuild-dev.js +0 -335
- package/dist/middleware/tailwind-dev.d.ts +0 -43
- package/dist/middleware/tailwind-dev.js +0 -199
- package/dist/react/client.d.ts +0 -76
- package/dist/react/client.js +0 -182
- package/dist/react/compile.d.ts +0 -17
- package/dist/react/context.d.ts +0 -2
- package/dist/react/error-boundary.d.ts +0 -30
- package/dist/react/hooks.d.ts +0 -6
- package/dist/react/index.d.ts +0 -92
- package/dist/react/index.js +0 -910
- package/dist/react/types.d.ts +0 -146
package/dist/index.js
CHANGED
|
@@ -225,14 +225,14 @@ function serve(router, options) {
|
|
|
225
225
|
if (!server.listening) return;
|
|
226
226
|
server.close();
|
|
227
227
|
server.closeIdleConnections();
|
|
228
|
-
return new Promise((
|
|
228
|
+
return new Promise((resolve3) => {
|
|
229
229
|
const timer = setTimeout(() => {
|
|
230
230
|
server.closeAllConnections();
|
|
231
|
-
|
|
231
|
+
resolve3();
|
|
232
232
|
}, timeoutMs);
|
|
233
233
|
server.on("close", () => {
|
|
234
234
|
clearTimeout(timer);
|
|
235
|
-
|
|
235
|
+
resolve3();
|
|
236
236
|
});
|
|
237
237
|
});
|
|
238
238
|
}
|
|
@@ -277,7 +277,7 @@ function createHub(opts) {
|
|
|
277
277
|
}
|
|
278
278
|
});
|
|
279
279
|
}
|
|
280
|
-
function
|
|
280
|
+
function join3(key, ws) {
|
|
281
281
|
if (!channels.has(key)) {
|
|
282
282
|
channels.set(key, /* @__PURE__ */ new Set());
|
|
283
283
|
redisSub?.subscribe(`${prefix}${key}`);
|
|
@@ -339,7 +339,7 @@ function createHub(opts) {
|
|
|
339
339
|
redisPub = void 0;
|
|
340
340
|
redisSub = null;
|
|
341
341
|
}
|
|
342
|
-
return { join:
|
|
342
|
+
return { join: join3, leave, broadcast, close };
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
// src/core/ws.ts
|
|
@@ -589,7 +589,6 @@ var Router = class _Router {
|
|
|
589
589
|
_route(method, path, ...args) {
|
|
590
590
|
return this._routeImpl(method, path, args);
|
|
591
591
|
}
|
|
592
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
593
592
|
_routeImpl(method, path, args) {
|
|
594
593
|
const last = args[args.length - 1];
|
|
595
594
|
if (last instanceof _Router) {
|
|
@@ -926,13 +925,13 @@ function serveStatic(root, options) {
|
|
|
926
925
|
let fileHandle;
|
|
927
926
|
try {
|
|
928
927
|
fileHandle = await open(filePath, "r");
|
|
929
|
-
let
|
|
928
|
+
let stat = await fileHandle.stat();
|
|
930
929
|
const realPath = await realpath(filePath);
|
|
931
930
|
if (!realPath.startsWith(rootDir + sep) && realPath !== rootDir) {
|
|
932
931
|
await fileHandle.close();
|
|
933
932
|
return new Response("Forbidden", { status: 403 });
|
|
934
933
|
}
|
|
935
|
-
if (
|
|
934
|
+
if (stat.isDirectory()) {
|
|
936
935
|
await fileHandle.close();
|
|
937
936
|
const indexFile = opts.index ?? "index.html";
|
|
938
937
|
filePath = resolve(filePath, indexFile);
|
|
@@ -940,29 +939,29 @@ function serveStatic(root, options) {
|
|
|
940
939
|
return new Response("Forbidden", { status: 403 });
|
|
941
940
|
}
|
|
942
941
|
fileHandle = await open(filePath, "r");
|
|
943
|
-
|
|
944
|
-
if (!
|
|
942
|
+
stat = await fileHandle.stat();
|
|
943
|
+
if (!stat.isFile()) {
|
|
945
944
|
await fileHandle.close();
|
|
946
945
|
return new Response("Not Found", { status: 404 });
|
|
947
946
|
}
|
|
948
947
|
}
|
|
949
948
|
const mimeType = MIME_TYPES[extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
950
|
-
const etag = `"${
|
|
949
|
+
const etag = `"${stat.ino}-${stat.size}-${stat.mtimeMs}"`;
|
|
951
950
|
const ifNoneMatch = req.headers.get("if-none-match");
|
|
952
951
|
if (ifNoneMatch === etag) {
|
|
953
952
|
await fileHandle.close();
|
|
954
953
|
return new Response(null, { status: 304 });
|
|
955
954
|
}
|
|
956
955
|
const ifModifiedSince = req.headers.get("if-modified-since");
|
|
957
|
-
if (ifModifiedSince &&
|
|
956
|
+
if (ifModifiedSince && stat.mtimeMs <= new Date(ifModifiedSince).getTime()) {
|
|
958
957
|
await fileHandle.close();
|
|
959
958
|
return new Response(null, { status: 304 });
|
|
960
959
|
}
|
|
961
960
|
const headers = {
|
|
962
961
|
"Content-Type": mimeType,
|
|
963
|
-
"Content-Length": String(
|
|
962
|
+
"Content-Length": String(stat.size),
|
|
964
963
|
ETag: etag,
|
|
965
|
-
"Last-Modified":
|
|
964
|
+
"Last-Modified": stat.mtime.toUTCString(),
|
|
966
965
|
"Cache-Control": opts.immutable ? `public, max-age=${opts.maxAge ?? 31536e3}, immutable` : `public, max-age=${opts.maxAge ?? 0}`
|
|
967
966
|
};
|
|
968
967
|
const readStream = fileHandle.createReadStream();
|
|
@@ -1181,536 +1180,6 @@ function sandbox(opts = {}) {
|
|
|
1181
1180
|
};
|
|
1182
1181
|
}
|
|
1183
1182
|
|
|
1184
|
-
// src/middleware/esbuild-dev.ts
|
|
1185
|
-
import { resolve as resolve3, relative, dirname } from "node:path";
|
|
1186
|
-
import { stat, readFile as readFile2, realpath as realpath2 } from "node:fs/promises";
|
|
1187
|
-
function escapeHtml(s) {
|
|
1188
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1189
|
-
}
|
|
1190
|
-
function defaultErrorTemplate(errors) {
|
|
1191
|
-
return `<!DOCTYPE html>
|
|
1192
|
-
<html>
|
|
1193
|
-
<head>
|
|
1194
|
-
<meta charset="utf-8">
|
|
1195
|
-
<title>Build Error</title>
|
|
1196
|
-
<style>
|
|
1197
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
1198
|
-
body { font-family: system-ui; background: #1a1a2e; color: #e0e0e0; padding: 2rem; }
|
|
1199
|
-
.overlay { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; }
|
|
1200
|
-
.card { background: #16213e; border: 1px solid #e74c3c; border-radius: 12px; padding: 2rem; max-width: 800px; width: 100%; box-shadow: 0 8px 32px rgba(0,0,0,0.4); }
|
|
1201
|
-
h1 { color: #e74c3c; font-size: 1.5rem; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
|
|
1202
|
-
pre { background: #0f0f23; padding: 1.5rem; border-radius: 8px; overflow: auto; font-size: 0.85rem; line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
|
|
1203
|
-
</style>
|
|
1204
|
-
</head>
|
|
1205
|
-
<body>
|
|
1206
|
-
<div class="overlay">
|
|
1207
|
-
<div class="card">
|
|
1208
|
-
<h1>\u26A0 esbuild Build Error</h1>
|
|
1209
|
-
<pre>${escapeHtml(errors)}</pre>
|
|
1210
|
-
</div>
|
|
1211
|
-
</div>
|
|
1212
|
-
</body>
|
|
1213
|
-
</html>`;
|
|
1214
|
-
}
|
|
1215
|
-
async function collectDeps(entryPath) {
|
|
1216
|
-
const files = /* @__PURE__ */ new Map();
|
|
1217
|
-
const visited = /* @__PURE__ */ new Set();
|
|
1218
|
-
const queue2 = [entryPath];
|
|
1219
|
-
while (queue2.length > 0) {
|
|
1220
|
-
const p = queue2.shift();
|
|
1221
|
-
try {
|
|
1222
|
-
const real = await realpath2(p);
|
|
1223
|
-
if (visited.has(real)) continue;
|
|
1224
|
-
visited.add(real);
|
|
1225
|
-
const s = await stat(real);
|
|
1226
|
-
files.set(real, s.mtimeMs);
|
|
1227
|
-
} catch {
|
|
1228
|
-
continue;
|
|
1229
|
-
}
|
|
1230
|
-
try {
|
|
1231
|
-
const content = await readFile2(p, "utf-8");
|
|
1232
|
-
const importRe = /from\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]/g;
|
|
1233
|
-
let m;
|
|
1234
|
-
while ((m = importRe.exec(content)) !== null) {
|
|
1235
|
-
const spec = m[1] ?? m[2];
|
|
1236
|
-
if (spec.startsWith(".") || spec.startsWith("/")) {
|
|
1237
|
-
const dir = dirname(p);
|
|
1238
|
-
const resolved = resolve3(dir, spec);
|
|
1239
|
-
for (const ext of ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", "/index.ts", "/index.tsx", "/index.js"]) {
|
|
1240
|
-
const candidate = resolved + ext;
|
|
1241
|
-
if (!visited.has(candidate)) {
|
|
1242
|
-
queue2.push(candidate);
|
|
1243
|
-
break;
|
|
1244
|
-
}
|
|
1245
|
-
break;
|
|
1246
|
-
}
|
|
1247
|
-
queue2.push(resolved);
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
} catch {
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
return files;
|
|
1254
|
-
}
|
|
1255
|
-
function esbuildDev(opts) {
|
|
1256
|
-
const cache = opts.cache ?? "memory";
|
|
1257
|
-
const importmap = opts.importmap ?? false;
|
|
1258
|
-
const importmapOverrides = opts.importmapOverrides ?? {};
|
|
1259
|
-
const errorTemplate = opts.errorTemplate ?? defaultErrorTemplate;
|
|
1260
|
-
const entries = Object.entries(opts.entries).map(([p, e]) => {
|
|
1261
|
-
const path = p.startsWith("/") ? p : "/" + p;
|
|
1262
|
-
const config = typeof e === "string" ? { entry: e } : e;
|
|
1263
|
-
return { path, config };
|
|
1264
|
-
});
|
|
1265
|
-
const cacheStore = /* @__PURE__ */ new Map();
|
|
1266
|
-
const chunkMap = /* @__PURE__ */ new Map();
|
|
1267
|
-
let esbuild_ = null;
|
|
1268
|
-
let esbuildLoadError = null;
|
|
1269
|
-
async function getEsbuild() {
|
|
1270
|
-
if (esbuild_) return esbuild_;
|
|
1271
|
-
try {
|
|
1272
|
-
esbuild_ = await import("esbuild");
|
|
1273
|
-
return esbuild_;
|
|
1274
|
-
} catch (err) {
|
|
1275
|
-
esbuildLoadError = `esbuild is not installed. Run: npm install -D esbuild
|
|
1276
|
-
|
|
1277
|
-
${String(err)}`;
|
|
1278
|
-
throw new Error(esbuildLoadError, { cause: err });
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
let importmapScript = null;
|
|
1282
|
-
function buildImportmap() {
|
|
1283
|
-
const mappings = {};
|
|
1284
|
-
Object.assign(mappings, importmapOverrides);
|
|
1285
|
-
for (const { path, config } of entries) {
|
|
1286
|
-
const entryName = path.split("/").pop() ?? "";
|
|
1287
|
-
if (entryName.includes("vendor") || config.external && config.external.length === 0 || !config.external && config.bundle !== false) {
|
|
1288
|
-
if (!mappings["react"]) mappings["react"] = path;
|
|
1289
|
-
if (!mappings["react/jsx-runtime"]) mappings["react/jsx-runtime"] = path;
|
|
1290
|
-
if (!mappings["react-dom/client"]) mappings["react-dom/client"] = path;
|
|
1291
|
-
if (!mappings["react-dom"]) mappings["react-dom"] = path;
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
const json = JSON.stringify({ imports: mappings });
|
|
1295
|
-
return `<script type="importmap">${json}</script>`;
|
|
1296
|
-
}
|
|
1297
|
-
async function compile(entry) {
|
|
1298
|
-
const esbuild = await getEsbuild();
|
|
1299
|
-
const isClientRouter = !!entry.clientRouter;
|
|
1300
|
-
let entryAbs;
|
|
1301
|
-
let buildOpts;
|
|
1302
|
-
if (isClientRouter) {
|
|
1303
|
-
const cr = entry.clientRouter;
|
|
1304
|
-
const virtualModule = "weifuwu:client-entry";
|
|
1305
|
-
entryAbs = virtualModule;
|
|
1306
|
-
const layoutImport = `import * as _layout from '${cr.layout}'
|
|
1307
|
-
const _lx = Object.entries(_layout).filter(([,v]) => typeof v === 'function')
|
|
1308
|
-
const Layout = _layout.default || (_lx[0]?.[1])`;
|
|
1309
|
-
let routesCode;
|
|
1310
|
-
if (cr.pages) {
|
|
1311
|
-
const entries2 = Object.entries(cr.pages).map(
|
|
1312
|
-
([path, component]) => ` '${path}': () => import('${component}'),`
|
|
1313
|
-
);
|
|
1314
|
-
routesCode = `const routes = {
|
|
1315
|
-
${entries2.join("\n")}
|
|
1316
|
-
}`;
|
|
1317
|
-
} else if (cr.routes) {
|
|
1318
|
-
routesCode = `import { routes } from '${cr.routes}'`;
|
|
1319
|
-
} else {
|
|
1320
|
-
routesCode = "const routes = {}";
|
|
1321
|
-
}
|
|
1322
|
-
const fallbackLine = cr.fallback ? `const fallback = () => import('${cr.fallback}')` : "";
|
|
1323
|
-
const fallbackOpt = cr.fallback ? " fallback," : "";
|
|
1324
|
-
const generatedCode = [
|
|
1325
|
-
`import { createBrowserRouter } from 'weifuwu/react/client'`,
|
|
1326
|
-
layoutImport,
|
|
1327
|
-
routesCode,
|
|
1328
|
-
fallbackLine,
|
|
1329
|
-
"",
|
|
1330
|
-
"createBrowserRouter({",
|
|
1331
|
-
" layout: Layout,",
|
|
1332
|
-
" routes,",
|
|
1333
|
-
fallbackOpt,
|
|
1334
|
-
"})"
|
|
1335
|
-
].filter(Boolean).join("\n");
|
|
1336
|
-
buildOpts = {
|
|
1337
|
-
entryPoints: [virtualModule],
|
|
1338
|
-
bundle: entry.bundle ?? true,
|
|
1339
|
-
external: entry.external ?? [],
|
|
1340
|
-
minify: entry.minify ?? true,
|
|
1341
|
-
platform: entry.platform ?? "browser",
|
|
1342
|
-
format: entry.format ?? "esm",
|
|
1343
|
-
sourcemap: entry.sourcemap ?? false,
|
|
1344
|
-
splitting: entry.splitting ?? false,
|
|
1345
|
-
...entry.splitting ? { outdir: resolve3(".weifuwu-esbuild-out") } : {},
|
|
1346
|
-
define: entry.define,
|
|
1347
|
-
loader: entry.loader,
|
|
1348
|
-
write: false,
|
|
1349
|
-
logLevel: "silent",
|
|
1350
|
-
plugins: [{
|
|
1351
|
-
name: "weifuwu-client-router",
|
|
1352
|
-
setup(build) {
|
|
1353
|
-
build.onResolve({ filter: new RegExp(`^${virtualModule.replace(/:/g, "\\:")}$`) }, () => ({
|
|
1354
|
-
path: virtualModule,
|
|
1355
|
-
namespace: "weifuwu-client"
|
|
1356
|
-
}));
|
|
1357
|
-
build.onLoad({ filter: /.*/, namespace: "weifuwu-client" }, () => ({
|
|
1358
|
-
contents: generatedCode,
|
|
1359
|
-
loader: "ts",
|
|
1360
|
-
resolveDir: process.cwd()
|
|
1361
|
-
}));
|
|
1362
|
-
}
|
|
1363
|
-
}]
|
|
1364
|
-
};
|
|
1365
|
-
} else {
|
|
1366
|
-
entryAbs = resolve3(entry.entry);
|
|
1367
|
-
buildOpts = {
|
|
1368
|
-
entryPoints: [entryAbs],
|
|
1369
|
-
bundle: entry.bundle ?? true,
|
|
1370
|
-
external: entry.external ?? [],
|
|
1371
|
-
minify: entry.minify ?? true,
|
|
1372
|
-
platform: entry.platform ?? "browser",
|
|
1373
|
-
format: entry.format ?? "esm",
|
|
1374
|
-
sourcemap: entry.sourcemap ?? false,
|
|
1375
|
-
splitting: entry.splitting ?? false,
|
|
1376
|
-
...entry.splitting ? { outdir: dirname(entryAbs) } : {},
|
|
1377
|
-
define: entry.define,
|
|
1378
|
-
loader: entry.loader,
|
|
1379
|
-
write: false,
|
|
1380
|
-
logLevel: "silent"
|
|
1381
|
-
};
|
|
1382
|
-
}
|
|
1383
|
-
const result = await esbuild.build(buildOpts);
|
|
1384
|
-
const msgs = [];
|
|
1385
|
-
for (const w of result.warnings) {
|
|
1386
|
-
const loc = w.location ? ` at ${relative(".", w.location.file ?? "")}:${w.location.line}:${w.location.column}` : "";
|
|
1387
|
-
msgs.push(`[warn] ${w.text}${loc}`);
|
|
1388
|
-
}
|
|
1389
|
-
for (const e of result.errors) {
|
|
1390
|
-
const loc = e.location ? ` at ${relative(".", e.location.file ?? "")}:${e.location.line}:${e.location.column}` : "";
|
|
1391
|
-
msgs.push(`[error] ${e.text}${loc}`);
|
|
1392
|
-
}
|
|
1393
|
-
if (result.errors.length > 0) {
|
|
1394
|
-
throw new Error(msgs.join("\n"));
|
|
1395
|
-
}
|
|
1396
|
-
const outputFiles = result.outputFiles;
|
|
1397
|
-
const entryFile = outputFiles.find((f) => f.path === entryAbs) ?? outputFiles[0];
|
|
1398
|
-
const code = entryFile?.text ?? "";
|
|
1399
|
-
let chunks;
|
|
1400
|
-
if (outputFiles.length > 1) {
|
|
1401
|
-
chunks = /* @__PURE__ */ new Map();
|
|
1402
|
-
for (const f of outputFiles) {
|
|
1403
|
-
if (f === entryFile) continue;
|
|
1404
|
-
const chunkName = f.path.split("/").pop() ?? f.path;
|
|
1405
|
-
chunks.set(chunkName, { code: f.text, etag: `"esbuild-${hashCode(f.text)}"` });
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
const etag = `"esbuild-${hashCode(code)}"`;
|
|
1409
|
-
if (msgs.length > 0) {
|
|
1410
|
-
console.error("[esbuildDev]", msgs.join("\n"));
|
|
1411
|
-
}
|
|
1412
|
-
return { code, etag, chunks };
|
|
1413
|
-
}
|
|
1414
|
-
function isCacheValid2(cached) {
|
|
1415
|
-
return Promise.all(
|
|
1416
|
-
[...cached.files].map(async ([file, mtime]) => {
|
|
1417
|
-
try {
|
|
1418
|
-
const s = await stat(file);
|
|
1419
|
-
return s.mtimeMs === mtime;
|
|
1420
|
-
} catch {
|
|
1421
|
-
return false;
|
|
1422
|
-
}
|
|
1423
|
-
})
|
|
1424
|
-
).then((results) => results.every(Boolean));
|
|
1425
|
-
}
|
|
1426
|
-
return async (req, ctx, next) => {
|
|
1427
|
-
const url = new URL(req.url);
|
|
1428
|
-
const pathname = url.pathname;
|
|
1429
|
-
if (importmap && pathname === "/assets/importmap") {
|
|
1430
|
-
if (!importmapScript) importmapScript = buildImportmap();
|
|
1431
|
-
return new Response(importmapScript, {
|
|
1432
|
-
headers: {
|
|
1433
|
-
"Content-Type": "application/javascript; charset=utf-8",
|
|
1434
|
-
"Cache-Control": "no-cache"
|
|
1435
|
-
}
|
|
1436
|
-
});
|
|
1437
|
-
}
|
|
1438
|
-
const matched = entries.find((e) => e.path === pathname);
|
|
1439
|
-
const chunkName = pathname.split("/").pop() ?? "";
|
|
1440
|
-
const chunkOwner = chunkMap.get(chunkName);
|
|
1441
|
-
if (!matched && !chunkOwner) return next(req, ctx);
|
|
1442
|
-
if (!matched && chunkOwner) {
|
|
1443
|
-
const ownerCache = cacheStore.get(chunkOwner);
|
|
1444
|
-
const chunk = ownerCache?.chunks?.get(chunkName);
|
|
1445
|
-
if (chunk) {
|
|
1446
|
-
if (req.headers.get("if-none-match") === chunk.etag) {
|
|
1447
|
-
return new Response(null, { status: 304 });
|
|
1448
|
-
}
|
|
1449
|
-
return new Response(chunk.code, {
|
|
1450
|
-
headers: {
|
|
1451
|
-
"Content-Type": "text/javascript; charset=utf-8",
|
|
1452
|
-
ETag: chunk.etag,
|
|
1453
|
-
"Cache-Control": "no-cache"
|
|
1454
|
-
}
|
|
1455
|
-
});
|
|
1456
|
-
}
|
|
1457
|
-
return next(req, ctx);
|
|
1458
|
-
}
|
|
1459
|
-
const { config } = matched;
|
|
1460
|
-
try {
|
|
1461
|
-
if (cache === "memory") {
|
|
1462
|
-
const cached = cacheStore.get(pathname);
|
|
1463
|
-
if (cached) {
|
|
1464
|
-
const valid = await isCacheValid2(cached);
|
|
1465
|
-
if (valid) {
|
|
1466
|
-
if (req.headers.get("if-none-match") === cached.etag) {
|
|
1467
|
-
return new Response(null, { status: 304 });
|
|
1468
|
-
}
|
|
1469
|
-
return new Response(cached.code, {
|
|
1470
|
-
headers: {
|
|
1471
|
-
"Content-Type": "text/javascript; charset=utf-8",
|
|
1472
|
-
ETag: cached.etag,
|
|
1473
|
-
"Cache-Control": "no-cache"
|
|
1474
|
-
}
|
|
1475
|
-
});
|
|
1476
|
-
}
|
|
1477
|
-
for (const [cn] of cached.chunks ?? []) chunkMap.delete(cn);
|
|
1478
|
-
cacheStore.delete(pathname);
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
const { code, etag, chunks } = await compile(config);
|
|
1482
|
-
const depEntry = config.clientRouter?.routes ?? (config.clientRouter?.pages ? Object.values(config.clientRouter.pages)[0] : void 0) ?? config.entry;
|
|
1483
|
-
const deps = await collectDeps(resolve3(depEntry));
|
|
1484
|
-
if (cache === "memory") {
|
|
1485
|
-
cacheStore.set(pathname, { code, etag, files: deps, chunks });
|
|
1486
|
-
if (chunks) {
|
|
1487
|
-
for (const [cn] of chunks) chunkMap.set(cn, pathname);
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
return new Response(code, {
|
|
1491
|
-
headers: {
|
|
1492
|
-
"Content-Type": "text/javascript; charset=utf-8",
|
|
1493
|
-
ETag: etag,
|
|
1494
|
-
"Cache-Control": "no-cache"
|
|
1495
|
-
}
|
|
1496
|
-
});
|
|
1497
|
-
} catch (err) {
|
|
1498
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1499
|
-
const html = errorTemplate(message);
|
|
1500
|
-
return new Response(html, {
|
|
1501
|
-
status: 500,
|
|
1502
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1503
|
-
});
|
|
1504
|
-
}
|
|
1505
|
-
};
|
|
1506
|
-
}
|
|
1507
|
-
function hashCode(s) {
|
|
1508
|
-
let hash = 0;
|
|
1509
|
-
for (let i = 0; i < s.length; i++) {
|
|
1510
|
-
const ch = s.charCodeAt(i);
|
|
1511
|
-
hash = (hash << 5) - hash + ch;
|
|
1512
|
-
hash |= 0;
|
|
1513
|
-
}
|
|
1514
|
-
return Math.abs(hash).toString(36);
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
// src/middleware/tailwind-dev.ts
|
|
1518
|
-
import { resolve as resolve4 } from "node:path";
|
|
1519
|
-
import { stat as stat2, readFile as readFile3, glob } from "node:fs/promises";
|
|
1520
|
-
function escapeHtml2(s) {
|
|
1521
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1522
|
-
}
|
|
1523
|
-
function defaultErrorTemplate2(errors) {
|
|
1524
|
-
return `<!DOCTYPE html>
|
|
1525
|
-
<html>
|
|
1526
|
-
<head>
|
|
1527
|
-
<meta charset="utf-8">
|
|
1528
|
-
<title>Tailwind Build Error</title>
|
|
1529
|
-
<style>
|
|
1530
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
1531
|
-
body { font-family: system-ui; background: #1a1a2e; color: #e0e0e0; padding: 2rem; }
|
|
1532
|
-
.overlay { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; }
|
|
1533
|
-
.card { background: #16213e; border: 1px solid #06b6d4; border-radius: 12px; padding: 2rem; max-width: 800px; width: 100%; box-shadow: 0 8px 32px rgba(0,0,0,0.4); }
|
|
1534
|
-
h1 { color: #06b6d4; font-size: 1.5rem; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
|
|
1535
|
-
pre { background: #0f0f23; padding: 1.5rem; border-radius: 8px; overflow: auto; font-size: 0.85rem; line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
|
|
1536
|
-
</style>
|
|
1537
|
-
</head>
|
|
1538
|
-
<body>
|
|
1539
|
-
<div class="overlay">
|
|
1540
|
-
<div class="card">
|
|
1541
|
-
<h1>\u{1F3A8} Tailwind CSS Build Error</h1>
|
|
1542
|
-
<pre>${escapeHtml2(errors)}</pre>
|
|
1543
|
-
</div>
|
|
1544
|
-
</div>
|
|
1545
|
-
</body>
|
|
1546
|
-
</html>`;
|
|
1547
|
-
}
|
|
1548
|
-
var TW_CANDIDATE_RE = /[a-z][\w-]*(?::[a-z][\w-]*)*(?:-\[[^\]]*\])*(?:\/[0-9]+)?/gi;
|
|
1549
|
-
var CLASS_CONTEXT_RE = /(?:class(?:Name)?\s*[=:]\s*["'`]([^"'`]+)["'`]|["'`]([^"'`\n]{2,})["'`])/g;
|
|
1550
|
-
async function extractCandidates(patterns, root) {
|
|
1551
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1552
|
-
for (const pattern of patterns) {
|
|
1553
|
-
const resolved = resolve4(root, pattern);
|
|
1554
|
-
try {
|
|
1555
|
-
for await (const file of glob(resolved)) {
|
|
1556
|
-
try {
|
|
1557
|
-
const content = await readFile3(file, "utf-8");
|
|
1558
|
-
for (const m of content.matchAll(CLASS_CONTEXT_RE)) {
|
|
1559
|
-
const str = m[1] ?? m[2];
|
|
1560
|
-
if (!str) continue;
|
|
1561
|
-
for (const c of str.matchAll(TW_CANDIDATE_RE)) {
|
|
1562
|
-
seen.add(c[0]);
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
} catch {
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
} catch {
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
return [...seen];
|
|
1572
|
-
}
|
|
1573
|
-
async function collectFileMtimes(files) {
|
|
1574
|
-
const map = /* @__PURE__ */ new Map();
|
|
1575
|
-
await Promise.all(
|
|
1576
|
-
files.map(async (f) => {
|
|
1577
|
-
try {
|
|
1578
|
-
const s = await stat2(f);
|
|
1579
|
-
map.set(f, s.mtimeMs);
|
|
1580
|
-
} catch {
|
|
1581
|
-
}
|
|
1582
|
-
})
|
|
1583
|
-
);
|
|
1584
|
-
return map;
|
|
1585
|
-
}
|
|
1586
|
-
function isCacheValid(cached) {
|
|
1587
|
-
return Promise.all(
|
|
1588
|
-
[...cached.files].map(async ([file, mtime]) => {
|
|
1589
|
-
try {
|
|
1590
|
-
const s = await stat2(file);
|
|
1591
|
-
return s.mtimeMs === mtime;
|
|
1592
|
-
} catch {
|
|
1593
|
-
return false;
|
|
1594
|
-
}
|
|
1595
|
-
})
|
|
1596
|
-
).then((results) => results.every(Boolean));
|
|
1597
|
-
}
|
|
1598
|
-
function tailwindDev(opts) {
|
|
1599
|
-
const cacheMode = opts.cache ?? "memory";
|
|
1600
|
-
const errorTemplate = opts.errorTemplate ?? defaultErrorTemplate2;
|
|
1601
|
-
const entries = Object.entries(opts.entries).map(([p, e]) => {
|
|
1602
|
-
const path = p.startsWith("/") ? p : "/" + p;
|
|
1603
|
-
const config = typeof e === "string" ? { entry: e } : e;
|
|
1604
|
-
return { path, config };
|
|
1605
|
-
});
|
|
1606
|
-
const cacheStore = /* @__PURE__ */ new Map();
|
|
1607
|
-
let compileFn = null;
|
|
1608
|
-
let loadError = null;
|
|
1609
|
-
async function getCompile() {
|
|
1610
|
-
if (compileFn) return compileFn;
|
|
1611
|
-
try {
|
|
1612
|
-
const mod = await import("@tailwindcss/node");
|
|
1613
|
-
compileFn = mod.compile;
|
|
1614
|
-
return compileFn;
|
|
1615
|
-
} catch (err) {
|
|
1616
|
-
loadError = `@tailwindcss/node is not installed. Run: npm install -D tailwindcss @tailwindcss/node
|
|
1617
|
-
|
|
1618
|
-
${String(err)}`;
|
|
1619
|
-
throw new Error(loadError, { cause: err });
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
async function compileCss(entry) {
|
|
1623
|
-
const compile = await getCompile();
|
|
1624
|
-
const entryAbs = resolve4(entry.entry);
|
|
1625
|
-
const cssSource = await readFile3(entryAbs, "utf-8");
|
|
1626
|
-
const baseDir = resolve4(entry.entry, "..");
|
|
1627
|
-
const deps = [entryAbs];
|
|
1628
|
-
const result = await compile(cssSource, {
|
|
1629
|
-
base: baseDir,
|
|
1630
|
-
onDependency: (p) => {
|
|
1631
|
-
if (!deps.includes(p)) deps.push(p);
|
|
1632
|
-
}
|
|
1633
|
-
});
|
|
1634
|
-
const contentPatterns = entry.content ?? [];
|
|
1635
|
-
for (const src of result.sources) {
|
|
1636
|
-
if (!src.negated) {
|
|
1637
|
-
contentPatterns.push(resolve4(src.base, src.pattern));
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
let candidates = [];
|
|
1641
|
-
if (contentPatterns.length > 0) {
|
|
1642
|
-
candidates = await extractCandidates(contentPatterns, baseDir);
|
|
1643
|
-
}
|
|
1644
|
-
const css = result.build(candidates);
|
|
1645
|
-
for (const pattern of contentPatterns) {
|
|
1646
|
-
try {
|
|
1647
|
-
for await (const file of glob(resolve4(baseDir, pattern))) {
|
|
1648
|
-
if (!deps.includes(file)) deps.push(file);
|
|
1649
|
-
}
|
|
1650
|
-
} catch {
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
const etag = `"tw-${hashCode2(css)}"`;
|
|
1654
|
-
return { css, etag, files: deps };
|
|
1655
|
-
}
|
|
1656
|
-
return async (req, ctx, next) => {
|
|
1657
|
-
const url = new URL(req.url);
|
|
1658
|
-
const pathname = url.pathname;
|
|
1659
|
-
const matched = entries.find((e) => e.path === pathname);
|
|
1660
|
-
if (!matched) return next(req, ctx);
|
|
1661
|
-
const { config } = matched;
|
|
1662
|
-
try {
|
|
1663
|
-
if (cacheMode === "memory") {
|
|
1664
|
-
const cached = cacheStore.get(pathname);
|
|
1665
|
-
if (cached) {
|
|
1666
|
-
const valid = await isCacheValid(cached);
|
|
1667
|
-
if (valid) {
|
|
1668
|
-
if (req.headers.get("if-none-match") === cached.etag) {
|
|
1669
|
-
return new Response(null, { status: 304 });
|
|
1670
|
-
}
|
|
1671
|
-
return new Response(cached.css, {
|
|
1672
|
-
headers: {
|
|
1673
|
-
"Content-Type": "text/css; charset=utf-8",
|
|
1674
|
-
ETag: cached.etag,
|
|
1675
|
-
"Cache-Control": "no-cache"
|
|
1676
|
-
}
|
|
1677
|
-
});
|
|
1678
|
-
}
|
|
1679
|
-
cacheStore.delete(pathname);
|
|
1680
|
-
}
|
|
1681
|
-
}
|
|
1682
|
-
const { css, etag, files } = await compileCss(config);
|
|
1683
|
-
if (cacheMode === "memory") {
|
|
1684
|
-
const mtimes = await collectFileMtimes(files);
|
|
1685
|
-
cacheStore.set(pathname, { css, etag, files: mtimes });
|
|
1686
|
-
}
|
|
1687
|
-
return new Response(css, {
|
|
1688
|
-
headers: {
|
|
1689
|
-
"Content-Type": "text/css; charset=utf-8",
|
|
1690
|
-
ETag: etag,
|
|
1691
|
-
"Cache-Control": "no-cache"
|
|
1692
|
-
}
|
|
1693
|
-
});
|
|
1694
|
-
} catch (err) {
|
|
1695
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1696
|
-
const html = errorTemplate(message);
|
|
1697
|
-
return new Response(html, {
|
|
1698
|
-
status: 500,
|
|
1699
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1700
|
-
});
|
|
1701
|
-
}
|
|
1702
|
-
};
|
|
1703
|
-
}
|
|
1704
|
-
function hashCode2(s) {
|
|
1705
|
-
let hash = 0;
|
|
1706
|
-
for (let i = 0; i < s.length; i++) {
|
|
1707
|
-
const ch = s.charCodeAt(i);
|
|
1708
|
-
hash = (hash << 5) - hash + ch;
|
|
1709
|
-
hash |= 0;
|
|
1710
|
-
}
|
|
1711
|
-
return Math.abs(hash).toString(36);
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
1183
|
// src/middleware/rate-limit.ts
|
|
1715
1184
|
function defaultKey(_req, _ctx) {
|
|
1716
1185
|
const forwarded = _req.headers.get("x-forwarded-for");
|
|
@@ -2490,379 +1959,6 @@ function queue(opts) {
|
|
|
2490
1959
|
return q2;
|
|
2491
1960
|
}
|
|
2492
1961
|
|
|
2493
|
-
// src/react/index.ts
|
|
2494
|
-
import { createElement as createElement2 } from "react";
|
|
2495
|
-
import { renderToReadableStream } from "react-dom/server";
|
|
2496
|
-
|
|
2497
|
-
// src/react/compile.ts
|
|
2498
|
-
import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile4 } from "node:fs/promises";
|
|
2499
|
-
import { resolve as resolve5, join as join3, dirname as dirname2 } from "node:path";
|
|
2500
|
-
import { createHash } from "node:crypto";
|
|
2501
|
-
import { existsSync } from "node:fs";
|
|
2502
|
-
var EXTERNAL_PKGS = [
|
|
2503
|
-
"react",
|
|
2504
|
-
"react/jsx-runtime",
|
|
2505
|
-
"react-dom",
|
|
2506
|
-
"react-dom/server",
|
|
2507
|
-
"react-dom/client",
|
|
2508
|
-
"weifuwu",
|
|
2509
|
-
"weifuwu/react"
|
|
2510
|
-
];
|
|
2511
|
-
var FRAMEWORK_IMPORTS = [
|
|
2512
|
-
"react",
|
|
2513
|
-
"react/jsx-runtime",
|
|
2514
|
-
"react-dom",
|
|
2515
|
-
"react-dom/server",
|
|
2516
|
-
"react-dom/client",
|
|
2517
|
-
"weifuwu",
|
|
2518
|
-
"weifuwu/react"
|
|
2519
|
-
];
|
|
2520
|
-
var memCache = /* @__PURE__ */ new Map();
|
|
2521
|
-
function buildResolveMap() {
|
|
2522
|
-
const map = {};
|
|
2523
|
-
for (const spec of FRAMEWORK_IMPORTS) {
|
|
2524
|
-
try {
|
|
2525
|
-
map[spec] = import.meta.resolve(spec);
|
|
2526
|
-
} catch {
|
|
2527
|
-
}
|
|
2528
|
-
}
|
|
2529
|
-
return map;
|
|
2530
|
-
}
|
|
2531
|
-
var _resolveMap = null;
|
|
2532
|
-
function resolveMap() {
|
|
2533
|
-
if (!_resolveMap) _resolveMap = buildResolveMap();
|
|
2534
|
-
return _resolveMap;
|
|
2535
|
-
}
|
|
2536
|
-
function rewriteImports(code) {
|
|
2537
|
-
const map = resolveMap();
|
|
2538
|
-
let result = code;
|
|
2539
|
-
for (const [spec, url] of Object.entries(map)) {
|
|
2540
|
-
const quoted = JSON.stringify(spec);
|
|
2541
|
-
const escaped = quoted.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2542
|
-
result = result.replace(new RegExp(`(from\\s+)${escaped}`, "g"), `$1${JSON.stringify(url)}`).replace(new RegExp(`(import\\s+)${escaped}(\\s*;)`, "g"), `$1${JSON.stringify(url)}$2`).replace(new RegExp(`(export\\s+.*?from\\s+)${escaped}`, "g"), `$1${JSON.stringify(url)}`);
|
|
2543
|
-
}
|
|
2544
|
-
return result;
|
|
2545
|
-
}
|
|
2546
|
-
var _cacheDir = null;
|
|
2547
|
-
function setReactCacheDir(dir) {
|
|
2548
|
-
_cacheDir = dir;
|
|
2549
|
-
}
|
|
2550
|
-
function getCacheDir() {
|
|
2551
|
-
if (_cacheDir) return _cacheDir;
|
|
2552
|
-
_cacheDir = join3(process.cwd(), "node_modules", ".weifuwu", "react");
|
|
2553
|
-
return _cacheDir;
|
|
2554
|
-
}
|
|
2555
|
-
async function loadTsxModule(entryPath) {
|
|
2556
|
-
const abs = resolve5(entryPath);
|
|
2557
|
-
let source;
|
|
2558
|
-
try {
|
|
2559
|
-
source = await readFile4(abs, "utf-8");
|
|
2560
|
-
} catch {
|
|
2561
|
-
throw new Error(`Cannot read file: ${entryPath}`);
|
|
2562
|
-
}
|
|
2563
|
-
const sourceHash = createHash("sha256").update(source).digest("hex").slice(0, 16);
|
|
2564
|
-
const memCached = memCache.get(abs);
|
|
2565
|
-
if (memCached && memCached.sourceHash === sourceHash) {
|
|
2566
|
-
return memCached.mod;
|
|
2567
|
-
}
|
|
2568
|
-
const tmpDir = getCacheDir();
|
|
2569
|
-
const tmpFile = join3(tmpDir, `${sourceHash}.mjs`);
|
|
2570
|
-
if (existsSync(tmpFile)) {
|
|
2571
|
-
try {
|
|
2572
|
-
const mod2 = await import(tmpFile + "?" + sourceHash);
|
|
2573
|
-
memCache.set(abs, { sourceHash, mod: mod2 });
|
|
2574
|
-
return mod2;
|
|
2575
|
-
} catch {
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
const esbuild = await import("esbuild");
|
|
2579
|
-
const result = await esbuild.build({
|
|
2580
|
-
entryPoints: [abs],
|
|
2581
|
-
bundle: true,
|
|
2582
|
-
write: false,
|
|
2583
|
-
format: "esm",
|
|
2584
|
-
platform: "node",
|
|
2585
|
-
jsx: "automatic",
|
|
2586
|
-
external: EXTERNAL_PKGS,
|
|
2587
|
-
logLevel: "silent"
|
|
2588
|
-
});
|
|
2589
|
-
let code = result.outputFiles[0]?.text ?? "";
|
|
2590
|
-
if (!code) throw new Error(`esbuild produced empty output for ${entryPath}`);
|
|
2591
|
-
code = rewriteImports(code);
|
|
2592
|
-
await mkdir3(dirname2(tmpFile), { recursive: true });
|
|
2593
|
-
await writeFile3(tmpFile, code);
|
|
2594
|
-
const mod = await import(tmpFile + "?" + sourceHash);
|
|
2595
|
-
memCache.set(abs, { sourceHash, mod });
|
|
2596
|
-
return mod;
|
|
2597
|
-
}
|
|
2598
|
-
async function loadTsxComponent(entryPath) {
|
|
2599
|
-
const mod = await loadTsxModule(entryPath);
|
|
2600
|
-
if (mod.default && typeof mod.default === "function") return mod.default;
|
|
2601
|
-
for (const [, val] of Object.entries(mod)) {
|
|
2602
|
-
if (typeof val === "function") return val;
|
|
2603
|
-
}
|
|
2604
|
-
throw new Error(`No component export found in ${entryPath}. Export a default or named component function.`);
|
|
2605
|
-
}
|
|
2606
|
-
|
|
2607
|
-
// src/react/context.ts
|
|
2608
|
-
import { createContext } from "react";
|
|
2609
|
-
var CTX_KEY = /* @__PURE__ */ Symbol.for("weifuwu.react.ServerDataContext");
|
|
2610
|
-
function getServerDataContext() {
|
|
2611
|
-
const globalStore = globalThis;
|
|
2612
|
-
if (globalStore[CTX_KEY]) return globalStore[CTX_KEY];
|
|
2613
|
-
const ctx = createContext({});
|
|
2614
|
-
ctx.displayName = "ServerData";
|
|
2615
|
-
globalStore[CTX_KEY] = ctx;
|
|
2616
|
-
return ctx;
|
|
2617
|
-
}
|
|
2618
|
-
var ServerDataContext = getServerDataContext();
|
|
2619
|
-
|
|
2620
|
-
// src/react/error-boundary.ts
|
|
2621
|
-
import { createElement, Component } from "react";
|
|
2622
|
-
var ErrorBoundary = class extends Component {
|
|
2623
|
-
state = { hasError: false, error: null };
|
|
2624
|
-
static getDerivedStateFromError(error) {
|
|
2625
|
-
return { hasError: true, error };
|
|
2626
|
-
}
|
|
2627
|
-
componentDidCatch(error) {
|
|
2628
|
-
this.props.onError?.(error);
|
|
2629
|
-
}
|
|
2630
|
-
render() {
|
|
2631
|
-
if (this.state.hasError) {
|
|
2632
|
-
if (this.props.fallback) return this.props.fallback;
|
|
2633
|
-
return createElement(
|
|
2634
|
-
"div",
|
|
2635
|
-
{ style: { padding: "2rem", textAlign: "center" } },
|
|
2636
|
-
createElement("h1", null, "Something went wrong"),
|
|
2637
|
-
createElement("p", null, this.state.error?.message ?? "Unknown error")
|
|
2638
|
-
);
|
|
2639
|
-
}
|
|
2640
|
-
return this.props.children;
|
|
2641
|
-
}
|
|
2642
|
-
};
|
|
2643
|
-
|
|
2644
|
-
// src/react/hooks.ts
|
|
2645
|
-
import { useContext } from "react";
|
|
2646
|
-
function useServerData() {
|
|
2647
|
-
return useContext(ServerDataContext);
|
|
2648
|
-
}
|
|
2649
|
-
|
|
2650
|
-
// src/react/index.ts
|
|
2651
|
-
function HtmlShell({ children, importMap, stylesheets, data }) {
|
|
2652
|
-
const headChildren = [
|
|
2653
|
-
createElement2("meta", { charSet: "utf-8", key: "charset" }),
|
|
2654
|
-
createElement2("meta", { name: "viewport", content: "width=device-width, initial-scale=1", key: "viewport" })
|
|
2655
|
-
];
|
|
2656
|
-
if (stylesheets) {
|
|
2657
|
-
for (const href of stylesheets) {
|
|
2658
|
-
headChildren.push(
|
|
2659
|
-
createElement2("link", { rel: "stylesheet", href, key: `css-${href}` })
|
|
2660
|
-
);
|
|
2661
|
-
}
|
|
2662
|
-
}
|
|
2663
|
-
if (importMap) {
|
|
2664
|
-
headChildren.push(
|
|
2665
|
-
createElement2("script", {
|
|
2666
|
-
type: "importmap",
|
|
2667
|
-
key: "importmap",
|
|
2668
|
-
dangerouslySetInnerHTML: { __html: JSON.stringify(importMap) }
|
|
2669
|
-
})
|
|
2670
|
-
);
|
|
2671
|
-
}
|
|
2672
|
-
const bodyChildren = [
|
|
2673
|
-
createElement2("div", { id: "root", key: "root" }, children)
|
|
2674
|
-
];
|
|
2675
|
-
if (data && Object.keys(data).length > 0) {
|
|
2676
|
-
bodyChildren.push(
|
|
2677
|
-
createElement2("script", {
|
|
2678
|
-
id: "__WEIFUWU_DATA__",
|
|
2679
|
-
type: "application/json",
|
|
2680
|
-
key: "weifuwu-data",
|
|
2681
|
-
dangerouslySetInnerHTML: { __html: JSON.stringify(data).replace(/</g, "\\u003c") }
|
|
2682
|
-
})
|
|
2683
|
-
);
|
|
2684
|
-
}
|
|
2685
|
-
return createElement2(
|
|
2686
|
-
"html",
|
|
2687
|
-
{ lang: "en" },
|
|
2688
|
-
createElement2("head", null, ...headChildren),
|
|
2689
|
-
createElement2("body", null, ...bodyChildren)
|
|
2690
|
-
);
|
|
2691
|
-
}
|
|
2692
|
-
async function renderComponent(Component2, data, layout, renderOpts) {
|
|
2693
|
-
let element = createElement2(Component2, renderOpts.props ?? {});
|
|
2694
|
-
if (layout) {
|
|
2695
|
-
element = createElement2(layout, { children: element });
|
|
2696
|
-
}
|
|
2697
|
-
element = createElement2(ServerDataContext.Provider, { value: data }, element);
|
|
2698
|
-
const page = createElement2(HtmlShell, {
|
|
2699
|
-
children: element,
|
|
2700
|
-
importMap: renderOpts.importMap,
|
|
2701
|
-
stylesheets: renderOpts.stylesheets,
|
|
2702
|
-
data: Object.keys(data).length > 0 ? data : void 0
|
|
2703
|
-
});
|
|
2704
|
-
const rstream = await renderToReadableStream(page, {
|
|
2705
|
-
bootstrapScripts: renderOpts.bootstrapScripts,
|
|
2706
|
-
bootstrapModules: renderOpts.bootstrapModules
|
|
2707
|
-
});
|
|
2708
|
-
if (renderOpts.stream === false) {
|
|
2709
|
-
await rstream.allReady;
|
|
2710
|
-
}
|
|
2711
|
-
return new Response(rstream, {
|
|
2712
|
-
status: renderOpts.status ?? 200,
|
|
2713
|
-
headers: { "content-type": "text/html; charset=utf-8", ...renderOpts.headers }
|
|
2714
|
-
});
|
|
2715
|
-
}
|
|
2716
|
-
function react(opts) {
|
|
2717
|
-
if (opts && "pages" in opts) {
|
|
2718
|
-
return (app) => createFullReactApp(app, opts);
|
|
2719
|
-
}
|
|
2720
|
-
if (opts?.cacheDir) setReactCacheDir(opts.cacheDir);
|
|
2721
|
-
let LayoutComponent = null;
|
|
2722
|
-
let layoutLoaded = false;
|
|
2723
|
-
let layoutLoadError = null;
|
|
2724
|
-
async function getLayout() {
|
|
2725
|
-
if (!opts?.layout) return null;
|
|
2726
|
-
if (layoutLoaded) {
|
|
2727
|
-
if (layoutLoadError) throw layoutLoadError;
|
|
2728
|
-
return LayoutComponent;
|
|
2729
|
-
}
|
|
2730
|
-
try {
|
|
2731
|
-
LayoutComponent = await loadTsxComponent(opts.layout);
|
|
2732
|
-
layoutLoaded = true;
|
|
2733
|
-
return LayoutComponent;
|
|
2734
|
-
} catch (err) {
|
|
2735
|
-
layoutLoadError = err instanceof Error ? err : new Error(String(err));
|
|
2736
|
-
layoutLoaded = true;
|
|
2737
|
-
throw layoutLoadError;
|
|
2738
|
-
}
|
|
2739
|
-
}
|
|
2740
|
-
return async (_req, ctx, next) => {
|
|
2741
|
-
ctx.render = async (path, renderOpts) => {
|
|
2742
|
-
let data = renderOpts?.data ?? {};
|
|
2743
|
-
if (renderOpts?.loader) {
|
|
2744
|
-
const loaderData = await renderOpts.loader(ctx);
|
|
2745
|
-
data = { ...data, ...loaderData };
|
|
2746
|
-
}
|
|
2747
|
-
const Component2 = await loadTsxComponent(path);
|
|
2748
|
-
const layout = await getLayout();
|
|
2749
|
-
return renderComponent(Component2, data, layout, renderOpts ?? {});
|
|
2750
|
-
};
|
|
2751
|
-
return next(_req, ctx);
|
|
2752
|
-
};
|
|
2753
|
-
}
|
|
2754
|
-
async function resolveLoader(modulePath) {
|
|
2755
|
-
const mod = await loadTsxModule(modulePath);
|
|
2756
|
-
return typeof mod.loader === "function" ? mod.loader : null;
|
|
2757
|
-
}
|
|
2758
|
-
async function callLoader(loader, ctx, data, rethrow = true) {
|
|
2759
|
-
if (!loader) return data;
|
|
2760
|
-
try {
|
|
2761
|
-
return { ...data, ...await loader(ctx) };
|
|
2762
|
-
} catch (err) {
|
|
2763
|
-
if (rethrow) throw err;
|
|
2764
|
-
return data;
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
2767
|
-
function createFullReactApp(app, opts) {
|
|
2768
|
-
const stylesheets = [...opts.stylesheets ?? []];
|
|
2769
|
-
if (opts.tailwind) {
|
|
2770
|
-
const twPath = opts.tailwind.path ?? "/assets/tailwind.css";
|
|
2771
|
-
const twEntry = opts.tailwind.entry ?? "./styles/input.css";
|
|
2772
|
-
app.use(tailwindDev({ entries: { [twPath]: { entry: twEntry } } }));
|
|
2773
|
-
if (!stylesheets.includes(twPath)) stylesheets.push(twPath);
|
|
2774
|
-
}
|
|
2775
|
-
app.use(react({ layout: opts.layout, cacheDir: opts.cacheDir }));
|
|
2776
|
-
const layoutLoaderP = resolveLoader(opts.layout);
|
|
2777
|
-
const notFoundLoaderP = opts.notFound ? resolveLoader(opts.notFound) : Promise.resolve(null);
|
|
2778
|
-
const clientCfg = typeof opts.client === "object" ? opts.client : {};
|
|
2779
|
-
const clientPath = clientCfg.path ?? "/assets/client.js";
|
|
2780
|
-
const bootstrapModules = [...opts.bootstrapModules ?? []];
|
|
2781
|
-
if (opts.client !== false && !bootstrapModules.includes(clientPath)) {
|
|
2782
|
-
bootstrapModules.push(clientPath);
|
|
2783
|
-
}
|
|
2784
|
-
const renderOpts = {
|
|
2785
|
-
stylesheets: stylesheets.length > 0 ? stylesheets : void 0,
|
|
2786
|
-
bootstrapModules: bootstrapModules.length > 0 ? bootstrapModules : void 0,
|
|
2787
|
-
stream: opts.stream
|
|
2788
|
-
};
|
|
2789
|
-
for (const [path, component] of Object.entries(opts.pages)) {
|
|
2790
|
-
app.get(path, async (_req, ctx) => {
|
|
2791
|
-
let data = {};
|
|
2792
|
-
data = await callLoader(await layoutLoaderP, ctx, data);
|
|
2793
|
-
const pageLoader = opts.loaders?.[path] ?? await resolveLoader(component);
|
|
2794
|
-
data = await callLoader(pageLoader, ctx, data);
|
|
2795
|
-
return ctx.render(component, { ...renderOpts, data });
|
|
2796
|
-
});
|
|
2797
|
-
}
|
|
2798
|
-
if (opts.notFound) {
|
|
2799
|
-
const notFoundPath = opts.notFound;
|
|
2800
|
-
app.onError(async (err, _req, ctx) => {
|
|
2801
|
-
const status = typeof err === "object" && err !== null && "status" in err ? err.status : 500;
|
|
2802
|
-
if (ctx.render) {
|
|
2803
|
-
let data = { error: String(err) };
|
|
2804
|
-
data = await callLoader(await layoutLoaderP, ctx, data, false);
|
|
2805
|
-
data = await callLoader(await notFoundLoaderP, ctx, data, false);
|
|
2806
|
-
return ctx.render(notFoundPath, { ...renderOpts, status, data });
|
|
2807
|
-
}
|
|
2808
|
-
return new Response("Internal Server Error", { status });
|
|
2809
|
-
});
|
|
2810
|
-
}
|
|
2811
|
-
if (opts.client !== false) {
|
|
2812
|
-
app.use(esbuildDev({
|
|
2813
|
-
entries: {
|
|
2814
|
-
[clientPath]: {
|
|
2815
|
-
clientRouter: {
|
|
2816
|
-
pages: opts.pages,
|
|
2817
|
-
layout: opts.layout,
|
|
2818
|
-
fallback: opts.notFound
|
|
2819
|
-
},
|
|
2820
|
-
bundle: true,
|
|
2821
|
-
splitting: clientCfg.splitting ?? true,
|
|
2822
|
-
minify: clientCfg.minify ?? false
|
|
2823
|
-
}
|
|
2824
|
-
}
|
|
2825
|
-
}));
|
|
2826
|
-
}
|
|
2827
|
-
}
|
|
2828
|
-
function extractImportPath(fn) {
|
|
2829
|
-
const src = fn.toString();
|
|
2830
|
-
const m = src.match(/import\s*\(\s*['"]([^'"]+)['"]\s*\)/);
|
|
2831
|
-
if (!m) throw new Error(`Cannot extract import path from: ${src}`);
|
|
2832
|
-
return m[1];
|
|
2833
|
-
}
|
|
2834
|
-
function reactRouter(app, routes, opts = {}) {
|
|
2835
|
-
let LayoutComponent = null;
|
|
2836
|
-
let layoutPromise = null;
|
|
2837
|
-
async function getLayout() {
|
|
2838
|
-
if (!opts.layout) return null;
|
|
2839
|
-
if (LayoutComponent) return LayoutComponent;
|
|
2840
|
-
if (!layoutPromise) {
|
|
2841
|
-
layoutPromise = loadTsxComponent(opts.layout).then((c) => {
|
|
2842
|
-
LayoutComponent = c;
|
|
2843
|
-
return c;
|
|
2844
|
-
});
|
|
2845
|
-
}
|
|
2846
|
-
return layoutPromise;
|
|
2847
|
-
}
|
|
2848
|
-
for (const [path, importer] of Object.entries(routes)) {
|
|
2849
|
-
app.get(path, async (_req, ctx) => {
|
|
2850
|
-
const cmpPath = extractImportPath(importer);
|
|
2851
|
-
const Component2 = await loadTsxComponent(cmpPath);
|
|
2852
|
-
const layout = await getLayout();
|
|
2853
|
-
let data = {};
|
|
2854
|
-
const loader = opts.loaders?.[path];
|
|
2855
|
-
if (loader) {
|
|
2856
|
-
data = await loader(ctx);
|
|
2857
|
-
}
|
|
2858
|
-
return renderComponent(Component2, data, layout, opts);
|
|
2859
|
-
});
|
|
2860
|
-
}
|
|
2861
|
-
}
|
|
2862
|
-
function Link({ href, children, ...props }) {
|
|
2863
|
-
return createElement2("a", { href, ...props }, children);
|
|
2864
|
-
}
|
|
2865
|
-
|
|
2866
1962
|
// src/ai/index.ts
|
|
2867
1963
|
function ai(opts) {
|
|
2868
1964
|
let aiModule = null;
|
|
@@ -5224,14 +4320,11 @@ export {
|
|
|
5224
4320
|
Base,
|
|
5225
4321
|
CMS,
|
|
5226
4322
|
DEFAULT_MAX_BODY,
|
|
5227
|
-
ErrorBoundary,
|
|
5228
4323
|
HttpError,
|
|
5229
4324
|
KB,
|
|
5230
|
-
Link,
|
|
5231
4325
|
MIGRATIONS_TABLE,
|
|
5232
4326
|
Messager,
|
|
5233
4327
|
Router,
|
|
5234
|
-
ServerDataContext,
|
|
5235
4328
|
UserModule,
|
|
5236
4329
|
agent,
|
|
5237
4330
|
ai,
|
|
@@ -5242,7 +4335,6 @@ export {
|
|
|
5242
4335
|
createHub,
|
|
5243
4336
|
currentTrace,
|
|
5244
4337
|
currentTraceId,
|
|
5245
|
-
esbuildDev,
|
|
5246
4338
|
graphql,
|
|
5247
4339
|
helmet,
|
|
5248
4340
|
kb,
|
|
@@ -5251,18 +4343,14 @@ export {
|
|
|
5251
4343
|
postgres,
|
|
5252
4344
|
queue,
|
|
5253
4345
|
rateLimit,
|
|
5254
|
-
react,
|
|
5255
|
-
reactRouter,
|
|
5256
4346
|
redis,
|
|
5257
4347
|
requireRole,
|
|
5258
4348
|
runWithTrace,
|
|
5259
4349
|
sandbox,
|
|
5260
4350
|
serve,
|
|
5261
4351
|
serveStatic,
|
|
5262
|
-
tailwindDev,
|
|
5263
4352
|
trace,
|
|
5264
4353
|
traceElapsed,
|
|
5265
4354
|
upload,
|
|
5266
|
-
useServerData,
|
|
5267
4355
|
user
|
|
5268
4356
|
};
|