weifuwu 0.33.1 → 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 +160 -3
- 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 -4
- package/dist/index.js +11 -475
- package/package.json +11 -24
- package/dist/middleware/esbuild-dev.d.ts +0 -55
- package/dist/middleware/esbuild-dev.js +0 -266
- package/dist/middleware/tailwind-dev.d.ts +0 -43
- package/dist/middleware/tailwind-dev.js +0 -199
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
|
}
|
|
@@ -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,467 +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 entryAbs = resolve3(entry.entry);
|
|
1300
|
-
const buildOpts = {
|
|
1301
|
-
entryPoints: [entryAbs],
|
|
1302
|
-
bundle: entry.bundle ?? true,
|
|
1303
|
-
external: entry.external ?? [],
|
|
1304
|
-
minify: entry.minify ?? true,
|
|
1305
|
-
platform: entry.platform ?? "browser",
|
|
1306
|
-
format: entry.format ?? "esm",
|
|
1307
|
-
sourcemap: entry.sourcemap ?? false,
|
|
1308
|
-
splitting: entry.splitting ?? false,
|
|
1309
|
-
...entry.splitting ? { outdir: dirname(entryAbs) } : {},
|
|
1310
|
-
define: entry.define,
|
|
1311
|
-
loader: entry.loader,
|
|
1312
|
-
write: false,
|
|
1313
|
-
logLevel: "silent"
|
|
1314
|
-
};
|
|
1315
|
-
const result = await esbuild.build(buildOpts);
|
|
1316
|
-
const msgs = [];
|
|
1317
|
-
for (const w of result.warnings) {
|
|
1318
|
-
const loc = w.location ? ` at ${relative(".", w.location.file ?? "")}:${w.location.line}:${w.location.column}` : "";
|
|
1319
|
-
msgs.push(`[warn] ${w.text}${loc}`);
|
|
1320
|
-
}
|
|
1321
|
-
for (const e of result.errors) {
|
|
1322
|
-
const loc = e.location ? ` at ${relative(".", e.location.file ?? "")}:${e.location.line}:${e.location.column}` : "";
|
|
1323
|
-
msgs.push(`[error] ${e.text}${loc}`);
|
|
1324
|
-
}
|
|
1325
|
-
if (result.errors.length > 0) {
|
|
1326
|
-
throw new Error(msgs.join("\n"));
|
|
1327
|
-
}
|
|
1328
|
-
const outputFiles = result.outputFiles;
|
|
1329
|
-
const entryFile = outputFiles.find((f) => f.path === entryAbs) ?? outputFiles[0];
|
|
1330
|
-
const code = entryFile?.text ?? "";
|
|
1331
|
-
let chunks;
|
|
1332
|
-
if (outputFiles.length > 1) {
|
|
1333
|
-
chunks = /* @__PURE__ */ new Map();
|
|
1334
|
-
for (const f of outputFiles) {
|
|
1335
|
-
if (f === entryFile) continue;
|
|
1336
|
-
const chunkName = f.path.split("/").pop() ?? f.path;
|
|
1337
|
-
chunks.set(chunkName, { code: f.text, etag: `"esbuild-${hashCode(f.text)}"` });
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
const etag = `"esbuild-${hashCode(code)}"`;
|
|
1341
|
-
if (msgs.length > 0) {
|
|
1342
|
-
console.error("[esbuildDev]", msgs.join("\n"));
|
|
1343
|
-
}
|
|
1344
|
-
return { code, etag, chunks };
|
|
1345
|
-
}
|
|
1346
|
-
function isCacheValid2(cached) {
|
|
1347
|
-
return Promise.all(
|
|
1348
|
-
[...cached.files].map(async ([file, mtime]) => {
|
|
1349
|
-
try {
|
|
1350
|
-
const s = await stat(file);
|
|
1351
|
-
return s.mtimeMs === mtime;
|
|
1352
|
-
} catch {
|
|
1353
|
-
return false;
|
|
1354
|
-
}
|
|
1355
|
-
})
|
|
1356
|
-
).then((results) => results.every(Boolean));
|
|
1357
|
-
}
|
|
1358
|
-
return async (req, ctx, next) => {
|
|
1359
|
-
const url = new URL(req.url);
|
|
1360
|
-
const pathname = url.pathname;
|
|
1361
|
-
if (importmap && pathname === "/assets/importmap") {
|
|
1362
|
-
if (!importmapScript) importmapScript = buildImportmap();
|
|
1363
|
-
return new Response(importmapScript, {
|
|
1364
|
-
headers: {
|
|
1365
|
-
"Content-Type": "application/javascript; charset=utf-8",
|
|
1366
|
-
"Cache-Control": "no-cache"
|
|
1367
|
-
}
|
|
1368
|
-
});
|
|
1369
|
-
}
|
|
1370
|
-
const matched = entries.find((e) => e.path === pathname);
|
|
1371
|
-
const chunkName = pathname.split("/").pop() ?? "";
|
|
1372
|
-
const chunkOwner = chunkMap.get(chunkName);
|
|
1373
|
-
if (!matched && !chunkOwner) return next(req, ctx);
|
|
1374
|
-
if (!matched && chunkOwner) {
|
|
1375
|
-
const ownerCache = cacheStore.get(chunkOwner);
|
|
1376
|
-
const chunk = ownerCache?.chunks?.get(chunkName);
|
|
1377
|
-
if (chunk) {
|
|
1378
|
-
if (req.headers.get("if-none-match") === chunk.etag) {
|
|
1379
|
-
return new Response(null, { status: 304 });
|
|
1380
|
-
}
|
|
1381
|
-
return new Response(chunk.code, {
|
|
1382
|
-
headers: {
|
|
1383
|
-
"Content-Type": "text/javascript; charset=utf-8",
|
|
1384
|
-
ETag: chunk.etag,
|
|
1385
|
-
"Cache-Control": "no-cache"
|
|
1386
|
-
}
|
|
1387
|
-
});
|
|
1388
|
-
}
|
|
1389
|
-
return next(req, ctx);
|
|
1390
|
-
}
|
|
1391
|
-
const { config } = matched;
|
|
1392
|
-
try {
|
|
1393
|
-
if (cache === "memory") {
|
|
1394
|
-
const cached = cacheStore.get(pathname);
|
|
1395
|
-
if (cached) {
|
|
1396
|
-
const valid = await isCacheValid2(cached);
|
|
1397
|
-
if (valid) {
|
|
1398
|
-
if (req.headers.get("if-none-match") === cached.etag) {
|
|
1399
|
-
return new Response(null, { status: 304 });
|
|
1400
|
-
}
|
|
1401
|
-
return new Response(cached.code, {
|
|
1402
|
-
headers: {
|
|
1403
|
-
"Content-Type": "text/javascript; charset=utf-8",
|
|
1404
|
-
ETag: cached.etag,
|
|
1405
|
-
"Cache-Control": "no-cache"
|
|
1406
|
-
}
|
|
1407
|
-
});
|
|
1408
|
-
}
|
|
1409
|
-
for (const [cn] of cached.chunks ?? []) chunkMap.delete(cn);
|
|
1410
|
-
cacheStore.delete(pathname);
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
const { code, etag, chunks } = await compile(config);
|
|
1414
|
-
const deps = await collectDeps(resolve3(config.entry));
|
|
1415
|
-
if (cache === "memory") {
|
|
1416
|
-
cacheStore.set(pathname, { code, etag, files: deps, chunks });
|
|
1417
|
-
if (chunks) {
|
|
1418
|
-
for (const [cn] of chunks) chunkMap.set(cn, pathname);
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
return new Response(code, {
|
|
1422
|
-
headers: {
|
|
1423
|
-
"Content-Type": "text/javascript; charset=utf-8",
|
|
1424
|
-
ETag: etag,
|
|
1425
|
-
"Cache-Control": "no-cache"
|
|
1426
|
-
}
|
|
1427
|
-
});
|
|
1428
|
-
} catch (err) {
|
|
1429
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1430
|
-
const html = errorTemplate(message);
|
|
1431
|
-
return new Response(html, {
|
|
1432
|
-
status: 500,
|
|
1433
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1434
|
-
});
|
|
1435
|
-
}
|
|
1436
|
-
};
|
|
1437
|
-
}
|
|
1438
|
-
function hashCode(s) {
|
|
1439
|
-
let hash = 0;
|
|
1440
|
-
for (let i = 0; i < s.length; i++) {
|
|
1441
|
-
const ch = s.charCodeAt(i);
|
|
1442
|
-
hash = (hash << 5) - hash + ch;
|
|
1443
|
-
hash |= 0;
|
|
1444
|
-
}
|
|
1445
|
-
return Math.abs(hash).toString(36);
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
// src/middleware/tailwind-dev.ts
|
|
1449
|
-
import { resolve as resolve4 } from "node:path";
|
|
1450
|
-
import { stat as stat2, readFile as readFile3, glob } from "node:fs/promises";
|
|
1451
|
-
function escapeHtml2(s) {
|
|
1452
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1453
|
-
}
|
|
1454
|
-
function defaultErrorTemplate2(errors) {
|
|
1455
|
-
return `<!DOCTYPE html>
|
|
1456
|
-
<html>
|
|
1457
|
-
<head>
|
|
1458
|
-
<meta charset="utf-8">
|
|
1459
|
-
<title>Tailwind Build Error</title>
|
|
1460
|
-
<style>
|
|
1461
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
1462
|
-
body { font-family: system-ui; background: #1a1a2e; color: #e0e0e0; padding: 2rem; }
|
|
1463
|
-
.overlay { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; }
|
|
1464
|
-
.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); }
|
|
1465
|
-
h1 { color: #06b6d4; font-size: 1.5rem; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
|
|
1466
|
-
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; }
|
|
1467
|
-
</style>
|
|
1468
|
-
</head>
|
|
1469
|
-
<body>
|
|
1470
|
-
<div class="overlay">
|
|
1471
|
-
<div class="card">
|
|
1472
|
-
<h1>\u{1F3A8} Tailwind CSS Build Error</h1>
|
|
1473
|
-
<pre>${escapeHtml2(errors)}</pre>
|
|
1474
|
-
</div>
|
|
1475
|
-
</div>
|
|
1476
|
-
</body>
|
|
1477
|
-
</html>`;
|
|
1478
|
-
}
|
|
1479
|
-
var TW_CANDIDATE_RE = /[a-z][\w-]*(?::[a-z][\w-]*)*(?:-\[[^\]]*\])*(?:\/[0-9]+)?/gi;
|
|
1480
|
-
var CLASS_CONTEXT_RE = /(?:class(?:Name)?\s*[=:]\s*["'`]([^"'`]+)["'`]|["'`]([^"'`\n]{2,})["'`])/g;
|
|
1481
|
-
async function extractCandidates(patterns, root) {
|
|
1482
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1483
|
-
for (const pattern of patterns) {
|
|
1484
|
-
const resolved = resolve4(root, pattern);
|
|
1485
|
-
try {
|
|
1486
|
-
for await (const file of glob(resolved)) {
|
|
1487
|
-
try {
|
|
1488
|
-
const content = await readFile3(file, "utf-8");
|
|
1489
|
-
for (const m of content.matchAll(CLASS_CONTEXT_RE)) {
|
|
1490
|
-
const str = m[1] ?? m[2];
|
|
1491
|
-
if (!str) continue;
|
|
1492
|
-
for (const c of str.matchAll(TW_CANDIDATE_RE)) {
|
|
1493
|
-
seen.add(c[0]);
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
} catch {
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
} catch {
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
return [...seen];
|
|
1503
|
-
}
|
|
1504
|
-
async function collectFileMtimes(files) {
|
|
1505
|
-
const map = /* @__PURE__ */ new Map();
|
|
1506
|
-
await Promise.all(
|
|
1507
|
-
files.map(async (f) => {
|
|
1508
|
-
try {
|
|
1509
|
-
const s = await stat2(f);
|
|
1510
|
-
map.set(f, s.mtimeMs);
|
|
1511
|
-
} catch {
|
|
1512
|
-
}
|
|
1513
|
-
})
|
|
1514
|
-
);
|
|
1515
|
-
return map;
|
|
1516
|
-
}
|
|
1517
|
-
function isCacheValid(cached) {
|
|
1518
|
-
return Promise.all(
|
|
1519
|
-
[...cached.files].map(async ([file, mtime]) => {
|
|
1520
|
-
try {
|
|
1521
|
-
const s = await stat2(file);
|
|
1522
|
-
return s.mtimeMs === mtime;
|
|
1523
|
-
} catch {
|
|
1524
|
-
return false;
|
|
1525
|
-
}
|
|
1526
|
-
})
|
|
1527
|
-
).then((results) => results.every(Boolean));
|
|
1528
|
-
}
|
|
1529
|
-
function tailwindDev(opts) {
|
|
1530
|
-
const cacheMode = opts.cache ?? "memory";
|
|
1531
|
-
const errorTemplate = opts.errorTemplate ?? defaultErrorTemplate2;
|
|
1532
|
-
const entries = Object.entries(opts.entries).map(([p, e]) => {
|
|
1533
|
-
const path = p.startsWith("/") ? p : "/" + p;
|
|
1534
|
-
const config = typeof e === "string" ? { entry: e } : e;
|
|
1535
|
-
return { path, config };
|
|
1536
|
-
});
|
|
1537
|
-
const cacheStore = /* @__PURE__ */ new Map();
|
|
1538
|
-
let compileFn = null;
|
|
1539
|
-
let loadError = null;
|
|
1540
|
-
async function getCompile() {
|
|
1541
|
-
if (compileFn) return compileFn;
|
|
1542
|
-
try {
|
|
1543
|
-
const mod = await import("@tailwindcss/node");
|
|
1544
|
-
compileFn = mod.compile;
|
|
1545
|
-
return compileFn;
|
|
1546
|
-
} catch (err) {
|
|
1547
|
-
loadError = `@tailwindcss/node is not installed. Run: npm install -D tailwindcss @tailwindcss/node
|
|
1548
|
-
|
|
1549
|
-
${String(err)}`;
|
|
1550
|
-
throw new Error(loadError, { cause: err });
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
async function compileCss(entry) {
|
|
1554
|
-
const compile = await getCompile();
|
|
1555
|
-
const entryAbs = resolve4(entry.entry);
|
|
1556
|
-
const cssSource = await readFile3(entryAbs, "utf-8");
|
|
1557
|
-
const baseDir = resolve4(entry.entry, "..");
|
|
1558
|
-
const deps = [entryAbs];
|
|
1559
|
-
const result = await compile(cssSource, {
|
|
1560
|
-
base: baseDir,
|
|
1561
|
-
onDependency: (p) => {
|
|
1562
|
-
if (!deps.includes(p)) deps.push(p);
|
|
1563
|
-
}
|
|
1564
|
-
});
|
|
1565
|
-
const contentPatterns = entry.content ?? [];
|
|
1566
|
-
for (const src of result.sources) {
|
|
1567
|
-
if (!src.negated) {
|
|
1568
|
-
contentPatterns.push(resolve4(src.base, src.pattern));
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
let candidates = [];
|
|
1572
|
-
if (contentPatterns.length > 0) {
|
|
1573
|
-
candidates = await extractCandidates(contentPatterns, baseDir);
|
|
1574
|
-
}
|
|
1575
|
-
const css = result.build(candidates);
|
|
1576
|
-
for (const pattern of contentPatterns) {
|
|
1577
|
-
try {
|
|
1578
|
-
for await (const file of glob(resolve4(baseDir, pattern))) {
|
|
1579
|
-
if (!deps.includes(file)) deps.push(file);
|
|
1580
|
-
}
|
|
1581
|
-
} catch {
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
const etag = `"tw-${hashCode2(css)}"`;
|
|
1585
|
-
return { css, etag, files: deps };
|
|
1586
|
-
}
|
|
1587
|
-
return async (req, ctx, next) => {
|
|
1588
|
-
const url = new URL(req.url);
|
|
1589
|
-
const pathname = url.pathname;
|
|
1590
|
-
const matched = entries.find((e) => e.path === pathname);
|
|
1591
|
-
if (!matched) return next(req, ctx);
|
|
1592
|
-
const { config } = matched;
|
|
1593
|
-
try {
|
|
1594
|
-
if (cacheMode === "memory") {
|
|
1595
|
-
const cached = cacheStore.get(pathname);
|
|
1596
|
-
if (cached) {
|
|
1597
|
-
const valid = await isCacheValid(cached);
|
|
1598
|
-
if (valid) {
|
|
1599
|
-
if (req.headers.get("if-none-match") === cached.etag) {
|
|
1600
|
-
return new Response(null, { status: 304 });
|
|
1601
|
-
}
|
|
1602
|
-
return new Response(cached.css, {
|
|
1603
|
-
headers: {
|
|
1604
|
-
"Content-Type": "text/css; charset=utf-8",
|
|
1605
|
-
ETag: cached.etag,
|
|
1606
|
-
"Cache-Control": "no-cache"
|
|
1607
|
-
}
|
|
1608
|
-
});
|
|
1609
|
-
}
|
|
1610
|
-
cacheStore.delete(pathname);
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
const { css, etag, files } = await compileCss(config);
|
|
1614
|
-
if (cacheMode === "memory") {
|
|
1615
|
-
const mtimes = await collectFileMtimes(files);
|
|
1616
|
-
cacheStore.set(pathname, { css, etag, files: mtimes });
|
|
1617
|
-
}
|
|
1618
|
-
return new Response(css, {
|
|
1619
|
-
headers: {
|
|
1620
|
-
"Content-Type": "text/css; charset=utf-8",
|
|
1621
|
-
ETag: etag,
|
|
1622
|
-
"Cache-Control": "no-cache"
|
|
1623
|
-
}
|
|
1624
|
-
});
|
|
1625
|
-
} catch (err) {
|
|
1626
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1627
|
-
const html = errorTemplate(message);
|
|
1628
|
-
return new Response(html, {
|
|
1629
|
-
status: 500,
|
|
1630
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1631
|
-
});
|
|
1632
|
-
}
|
|
1633
|
-
};
|
|
1634
|
-
}
|
|
1635
|
-
function hashCode2(s) {
|
|
1636
|
-
let hash = 0;
|
|
1637
|
-
for (let i = 0; i < s.length; i++) {
|
|
1638
|
-
const ch = s.charCodeAt(i);
|
|
1639
|
-
hash = (hash << 5) - hash + ch;
|
|
1640
|
-
hash |= 0;
|
|
1641
|
-
}
|
|
1642
|
-
return Math.abs(hash).toString(36);
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
1183
|
// src/middleware/rate-limit.ts
|
|
1646
1184
|
function defaultKey(_req, _ctx) {
|
|
1647
1185
|
const forwarded = _req.headers.get("x-forwarded-for");
|
|
@@ -4797,7 +4335,6 @@ export {
|
|
|
4797
4335
|
createHub,
|
|
4798
4336
|
currentTrace,
|
|
4799
4337
|
currentTraceId,
|
|
4800
|
-
esbuildDev,
|
|
4801
4338
|
graphql,
|
|
4802
4339
|
helmet,
|
|
4803
4340
|
kb,
|
|
@@ -4812,7 +4349,6 @@ export {
|
|
|
4812
4349
|
sandbox,
|
|
4813
4350
|
serve,
|
|
4814
4351
|
serveStatic,
|
|
4815
|
-
tailwindDev,
|
|
4816
4352
|
trace,
|
|
4817
4353
|
traceElapsed,
|
|
4818
4354
|
upload,
|
package/package.json
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weifuwu",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.33.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "0.33.2",
|
|
5
|
+
"description": "AI SaaS framework — (req, ctx) => Response",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"default": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./client": {
|
|
12
|
+
"types": "./dist/client/index.d.ts",
|
|
13
|
+
"default": "./dist/client/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./client/jsx-runtime": {
|
|
16
|
+
"types": "./dist/client/index.d.ts",
|
|
17
|
+
"default": "./dist/client/index.js"
|
|
10
18
|
}
|
|
11
19
|
},
|
|
12
20
|
"files": [
|
|
@@ -30,31 +38,10 @@
|
|
|
30
38
|
"postgres": "^3.4.9",
|
|
31
39
|
"ws": "^8"
|
|
32
40
|
},
|
|
33
|
-
"peerDependencies": {
|
|
34
|
-
"@tailwindcss/node": "^4",
|
|
35
|
-
"esbuild": "^0.28",
|
|
36
|
-
"tailwindcss": "^4"
|
|
37
|
-
},
|
|
38
|
-
"peerDependenciesMeta": {
|
|
39
|
-
"@tailwindcss/node": {
|
|
40
|
-
"optional": true
|
|
41
|
-
},
|
|
42
|
-
"esbuild": {
|
|
43
|
-
"optional": true
|
|
44
|
-
},
|
|
45
|
-
"tailwindcss": {
|
|
46
|
-
"optional": true
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
41
|
"devDependencies": {
|
|
50
|
-
"@eslint/js": "^10.0.1",
|
|
51
|
-
"@tailwindcss/node": "^4.3.2",
|
|
52
42
|
"@types/node": "^25",
|
|
53
43
|
"@types/ws": "^8",
|
|
54
|
-
"esbuild": "^0.28.1"
|
|
55
|
-
"globals": "^17.7.0",
|
|
56
|
-
"tailwindcss": "^4.3.2",
|
|
57
|
-
"typescript-eslint": "^8.62.1"
|
|
44
|
+
"esbuild": "^0.28.1"
|
|
58
45
|
},
|
|
59
46
|
"sideEffects": false
|
|
60
47
|
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* esbuildDev — on-the-fly esbuild compilation middleware for development.
|
|
3
|
-
*
|
|
4
|
-
* Compiles TypeScript/JSX/TSX client bundles in memory on first request,
|
|
5
|
-
* caches results based on source file mtime, and serves with ETag support.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* import { esbuildDev } from 'weifuwu'
|
|
10
|
-
*
|
|
11
|
-
* app.use(esbuildDev({
|
|
12
|
-
* entries: {
|
|
13
|
-
* '/assets/vendor.js': { entry: './client/vendor.ts', bundle: true },
|
|
14
|
-
* '/assets/client.js': { entry: './client/client.ts', external: ['react', 'react-dom/client'] },
|
|
15
|
-
* },
|
|
16
|
-
* importmap: true, // auto-generate importmap script
|
|
17
|
-
* }))
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
import type { Middleware, Context } from '../types.ts';
|
|
21
|
-
export interface EsbuildDevEntry {
|
|
22
|
-
/** Source entry point relative to cwd or absolute. */
|
|
23
|
-
entry?: string;
|
|
24
|
-
/** Bundle all dependencies (default: true). */
|
|
25
|
-
bundle?: boolean;
|
|
26
|
-
/** Packages to leave external (not bundled). */
|
|
27
|
-
external?: string[];
|
|
28
|
-
/** Minify output (default: true). */
|
|
29
|
-
minify?: boolean;
|
|
30
|
-
/** Target platform (default: 'browser'). */
|
|
31
|
-
platform?: 'browser' | 'neutral' | 'node';
|
|
32
|
-
/** Output format (default: 'esm'). */
|
|
33
|
-
format?: 'esm' | 'iife';
|
|
34
|
-
/** Generate sourcemaps (default: false). */
|
|
35
|
-
sourcemap?: boolean | 'inline' | 'external' | 'linked';
|
|
36
|
-
/** Enable code splitting for dynamic import() (default: false). */
|
|
37
|
-
splitting?: boolean;
|
|
38
|
-
/** Compile-time constant substitution. */
|
|
39
|
-
define?: Record<string, string>;
|
|
40
|
-
/** Custom loaders per file extension. */
|
|
41
|
-
loader?: Record<string, string>;
|
|
42
|
-
}
|
|
43
|
-
export interface EsbuildDevOptions {
|
|
44
|
-
/** Map of URL path → entry config (string shorthand for { entry }). */
|
|
45
|
-
entries: Record<string, EsbuildDevEntry | string>;
|
|
46
|
-
/** Auto-generate a /assets/importmap script that maps react → vendor */
|
|
47
|
-
importmap?: boolean;
|
|
48
|
-
/** Importmap overrides (e.g. { 'react': '/assets/custom-vendor.js' }). */
|
|
49
|
-
importmapOverrides?: Record<string, string>;
|
|
50
|
-
/** Cache strategy: 'memory' keeps compiled results, 'none' recompiles every request. */
|
|
51
|
-
cache?: 'memory' | 'none';
|
|
52
|
-
/** Custom HTML template for build errors (receives escaped error text). */
|
|
53
|
-
errorTemplate?: (errors: string) => string;
|
|
54
|
-
}
|
|
55
|
-
export declare function esbuildDev(opts: EsbuildDevOptions): Middleware<Context, Context>;
|