@getcronit/pylon 3.0.0-canary-20250206125311.f228add1e2e04c8af5d91db6063583bd317de463 → 3.0.0-canary-20250211154244.6f2521705b2882fe77d53141a59749d7cbd96d5c
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/dist/index.d.ts +5 -1
- package/dist/index.js +648 -3
- package/dist/index.js.map +4 -4
- package/dist/plugins/use-pages/build/app-utils.d.ts +7 -0
- package/dist/plugins/use-pages/build/index.d.ts +2 -0
- package/dist/plugins/use-pages/build/plugins/image-plugin.d.ts +2 -0
- package/dist/plugins/use-pages/build/plugins/inject-app-hydration.d.ts +2 -0
- package/dist/plugins/use-pages/build/plugins/postcss-plugin.d.ts +2 -0
- package/dist/plugins/use-pages/index.d.ts +4 -0
- package/dist/plugins/use-pages/setup/app-loader.d.ts +14 -0
- package/dist/plugins/use-pages/setup/index.d.ts +10 -0
- package/package.json +31 -3
package/dist/index.d.ts
CHANGED
|
@@ -8,12 +8,16 @@ export { handler } from './app/pylon-handler.js';
|
|
|
8
8
|
export { getEnv } from './get-env.js';
|
|
9
9
|
export { createDecorator } from './create-decorator.js';
|
|
10
10
|
export { createPubSub as experimentalCreatePubSub } from 'graphql-yoga';
|
|
11
|
+
export { usePages, type PageProps, PageData } from './plugins/use-pages/index';
|
|
11
12
|
import type { Plugin as YogaPlugin } from 'graphql-yoga';
|
|
12
13
|
import { MiddlewareHandler } from 'hono';
|
|
14
|
+
import { BuildContext, BuildOptions } from 'esbuild';
|
|
13
15
|
export type Plugin<PluginContext extends Record<string, any> = {}, TServerContext extends Record<string, any> = {}, TUserContext = {}> = YogaPlugin<PluginContext, TServerContext, TUserContext> & {
|
|
14
16
|
middleware?: MiddlewareHandler<Env>;
|
|
15
17
|
setup?: (app: typeof pylonApp) => void;
|
|
16
|
-
build?: (
|
|
18
|
+
build?: <T extends BuildOptions>(args: {
|
|
19
|
+
onBuild: () => void;
|
|
20
|
+
}) => Promise<Omit<BuildContext<T>, 'serve'>>;
|
|
17
21
|
};
|
|
18
22
|
export type PylonConfig = {
|
|
19
23
|
landingPage?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -636,6 +636,7 @@ function requireAuth(checks) {
|
|
|
636
636
|
import { Hono } from "hono";
|
|
637
637
|
import { logger } from "hono/logger";
|
|
638
638
|
import { sentry } from "@hono/sentry";
|
|
639
|
+
import { except } from "hono/combine";
|
|
639
640
|
var app = new Hono();
|
|
640
641
|
app.use("*", sentry());
|
|
641
642
|
app.use("*", async (c, next) => {
|
|
@@ -649,7 +650,7 @@ app.use("*", async (c, next) => {
|
|
|
649
650
|
});
|
|
650
651
|
});
|
|
651
652
|
});
|
|
652
|
-
app.use("*", logger());
|
|
653
|
+
app.use("*", except(["/__pylon/static/*"], logger()));
|
|
653
654
|
app.use((c, next) => {
|
|
654
655
|
c.req.id = crypto.randomUUID();
|
|
655
656
|
return next();
|
|
@@ -1191,7 +1192,9 @@ function getEnv() {
|
|
|
1191
1192
|
const skipTracing = arguments[0] === true;
|
|
1192
1193
|
try {
|
|
1193
1194
|
const context = asyncContext.getStore();
|
|
1194
|
-
|
|
1195
|
+
const ctx = context.env || process.env || {};
|
|
1196
|
+
ctx.NODE_ENV = ctx.NODE_ENV || process.env.NODE_ENV || "development";
|
|
1197
|
+
return ctx;
|
|
1195
1198
|
} catch {
|
|
1196
1199
|
return process.env;
|
|
1197
1200
|
} finally {
|
|
@@ -1207,6 +1210,647 @@ function getEnv() {
|
|
|
1207
1210
|
|
|
1208
1211
|
// src/index.ts
|
|
1209
1212
|
import { createPubSub } from "graphql-yoga";
|
|
1213
|
+
|
|
1214
|
+
// src/plugins/use-pages/setup/index.tsx
|
|
1215
|
+
import fs2 from "fs";
|
|
1216
|
+
import path3 from "path";
|
|
1217
|
+
import reactServer from "react-dom/server";
|
|
1218
|
+
import { Readable } from "stream";
|
|
1219
|
+
|
|
1220
|
+
// src/plugins/use-pages/setup/app-loader.tsx
|
|
1221
|
+
import { useMemo } from "react";
|
|
1222
|
+
import { jsx } from "react/jsx-runtime";
|
|
1223
|
+
var AppLoader = (props) => {
|
|
1224
|
+
props.client.useHydrateCache({ cacheSnapshot: props.pylonData.cacheSnapshot });
|
|
1225
|
+
const data = props.client.useQuery();
|
|
1226
|
+
const page = useMemo(() => {
|
|
1227
|
+
const page2 = /* @__PURE__ */ jsx(
|
|
1228
|
+
props.App,
|
|
1229
|
+
{
|
|
1230
|
+
pageProps: {
|
|
1231
|
+
...props.pylonData.pageProps,
|
|
1232
|
+
data
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
);
|
|
1236
|
+
return page2;
|
|
1237
|
+
}, [props]);
|
|
1238
|
+
return /* @__PURE__ */ jsx(props.Router, { ...props.routerProps, children: page });
|
|
1239
|
+
};
|
|
1240
|
+
|
|
1241
|
+
// src/plugins/use-pages/setup/index.tsx
|
|
1242
|
+
import { trimTrailingSlash } from "hono/trailing-slash";
|
|
1243
|
+
import { StaticRouter } from "react-router";
|
|
1244
|
+
import sharp from "sharp";
|
|
1245
|
+
import { createHash } from "crypto";
|
|
1246
|
+
import { tmpdir } from "os";
|
|
1247
|
+
import { pipeline } from "stream/promises";
|
|
1248
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1249
|
+
var disableCacheMiddleware = async (c, next) => {
|
|
1250
|
+
if (c.env.NODE_ENV === "development") {
|
|
1251
|
+
c.header(
|
|
1252
|
+
"Cache-Control",
|
|
1253
|
+
"no-store, no-cache, must-revalidate, proxy-revalidate"
|
|
1254
|
+
);
|
|
1255
|
+
c.header("Pragma", "no-cache");
|
|
1256
|
+
c.header("Expires", "0");
|
|
1257
|
+
c.header("Surrogate-Control", "no-store");
|
|
1258
|
+
}
|
|
1259
|
+
return next();
|
|
1260
|
+
};
|
|
1261
|
+
var setup = (app2) => {
|
|
1262
|
+
const pagesFilePath = path3.resolve(process.cwd(), ".pylon", "pages.json");
|
|
1263
|
+
let pageRoutes = [];
|
|
1264
|
+
try {
|
|
1265
|
+
pageRoutes = JSON.parse(fs2.readFileSync(pagesFilePath, "utf-8"));
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
console.error("Error reading pages.json", error);
|
|
1268
|
+
}
|
|
1269
|
+
app2.use(trimTrailingSlash());
|
|
1270
|
+
let App = void 0;
|
|
1271
|
+
let client = void 0;
|
|
1272
|
+
app2.on(
|
|
1273
|
+
"GET",
|
|
1274
|
+
pageRoutes.map((pageRoute) => pageRoute.slug),
|
|
1275
|
+
disableCacheMiddleware,
|
|
1276
|
+
async (c) => {
|
|
1277
|
+
if (!App) {
|
|
1278
|
+
const module = await import(`${process.cwd()}/.pylon/__pylon/pages/app.js`);
|
|
1279
|
+
App = module.default;
|
|
1280
|
+
}
|
|
1281
|
+
if (!client) {
|
|
1282
|
+
client = await import(`${process.cwd()}/.pylon/client`);
|
|
1283
|
+
}
|
|
1284
|
+
const pageProps = {
|
|
1285
|
+
params: c.req.param(),
|
|
1286
|
+
searchParams: c.req.query(),
|
|
1287
|
+
path: c.req.path
|
|
1288
|
+
};
|
|
1289
|
+
let cacheSnapshot = void 0;
|
|
1290
|
+
const prepared = await client.prepareReactRender(
|
|
1291
|
+
/* @__PURE__ */ jsx2(
|
|
1292
|
+
AppLoader,
|
|
1293
|
+
{
|
|
1294
|
+
Router: StaticRouter,
|
|
1295
|
+
routerProps: {
|
|
1296
|
+
location: c.req.path
|
|
1297
|
+
},
|
|
1298
|
+
App,
|
|
1299
|
+
client,
|
|
1300
|
+
pylonData: {
|
|
1301
|
+
pageProps,
|
|
1302
|
+
cacheSnapshot: void 0
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
)
|
|
1306
|
+
);
|
|
1307
|
+
cacheSnapshot = prepared.cacheSnapshot;
|
|
1308
|
+
const stream = await reactServer.renderToReadableStream(
|
|
1309
|
+
/* @__PURE__ */ jsx2(
|
|
1310
|
+
AppLoader,
|
|
1311
|
+
{
|
|
1312
|
+
Router: StaticRouter,
|
|
1313
|
+
routerProps: {
|
|
1314
|
+
location: c.req.path
|
|
1315
|
+
},
|
|
1316
|
+
App,
|
|
1317
|
+
client,
|
|
1318
|
+
pylonData: {
|
|
1319
|
+
pageProps,
|
|
1320
|
+
cacheSnapshot: prepared.cacheSnapshot
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
),
|
|
1324
|
+
{
|
|
1325
|
+
bootstrapModules: ["/__pylon/static/app.js"],
|
|
1326
|
+
bootstrapScriptContent: `window.__PYLON_DATA__ = ${JSON.stringify({
|
|
1327
|
+
pageProps,
|
|
1328
|
+
cacheSnapshot
|
|
1329
|
+
})}`
|
|
1330
|
+
}
|
|
1331
|
+
);
|
|
1332
|
+
return c.body(stream);
|
|
1333
|
+
}
|
|
1334
|
+
);
|
|
1335
|
+
app2.get("/__pylon/static/*", disableCacheMiddleware, async (c) => {
|
|
1336
|
+
const filePath = path3.resolve(
|
|
1337
|
+
process.cwd(),
|
|
1338
|
+
".pylon",
|
|
1339
|
+
"__pylon",
|
|
1340
|
+
"static",
|
|
1341
|
+
c.req.path.replace("/__pylon/static/", "")
|
|
1342
|
+
);
|
|
1343
|
+
if (!fs2.existsSync(filePath)) {
|
|
1344
|
+
throw new Error("File not found");
|
|
1345
|
+
}
|
|
1346
|
+
if (filePath.endsWith(".js")) {
|
|
1347
|
+
c.res.headers.set("Content-Type", "text/javascript");
|
|
1348
|
+
} else if (filePath.endsWith(".css")) {
|
|
1349
|
+
c.res.headers.set("Content-Type", "text/css");
|
|
1350
|
+
} else if (filePath.endsWith(".html")) {
|
|
1351
|
+
c.res.headers.set("Content-Type", "text/html");
|
|
1352
|
+
} else if (filePath.endsWith(".json")) {
|
|
1353
|
+
c.res.headers.set("Content-Type", "application/json");
|
|
1354
|
+
} else if (filePath.endsWith(".png")) {
|
|
1355
|
+
c.res.headers.set("Content-Type", "image/png");
|
|
1356
|
+
} else if (filePath.endsWith(".jpg") || filePath.endsWith(".jpeg")) {
|
|
1357
|
+
c.res.headers.set("Content-Type", "image/jpeg");
|
|
1358
|
+
} else if (filePath.endsWith(".gif")) {
|
|
1359
|
+
c.res.headers.set("Content-Type", "image/gif");
|
|
1360
|
+
} else if (filePath.endsWith(".svg")) {
|
|
1361
|
+
c.res.headers.set("Content-Type", "image/svg+xml");
|
|
1362
|
+
} else if (filePath.endsWith(".ico")) {
|
|
1363
|
+
c.res.headers.set("Content-Type", "image/x-icon");
|
|
1364
|
+
}
|
|
1365
|
+
const stream = fs2.createReadStream(filePath);
|
|
1366
|
+
const a = Readable.toWeb(stream);
|
|
1367
|
+
return c.body(a);
|
|
1368
|
+
});
|
|
1369
|
+
app2.get("/__pylon/image", async (c) => {
|
|
1370
|
+
console.log("image optimization route");
|
|
1371
|
+
try {
|
|
1372
|
+
const { src, w, h, q = "75", format = "webp" } = c.req.query();
|
|
1373
|
+
const queryStringHash = createHash("sha256").update(JSON.stringify(c.req.query())).digest("hex");
|
|
1374
|
+
if (!src) {
|
|
1375
|
+
return c.json({ error: "Missing parameters." }, 400);
|
|
1376
|
+
}
|
|
1377
|
+
let imagePath = path3.join(process.cwd(), ".pylon", src);
|
|
1378
|
+
if (src.startsWith("http://") || src.startsWith("https://")) {
|
|
1379
|
+
imagePath = await downloadImage(src);
|
|
1380
|
+
}
|
|
1381
|
+
try {
|
|
1382
|
+
console.log("imagePath", imagePath);
|
|
1383
|
+
await fs2.promises.access(imagePath);
|
|
1384
|
+
} catch {
|
|
1385
|
+
return c.json({ error: "Image not found" }, 404);
|
|
1386
|
+
}
|
|
1387
|
+
const metadata = await sharp(imagePath).metadata();
|
|
1388
|
+
if (!metadata.width || !metadata.height) {
|
|
1389
|
+
return c.json(
|
|
1390
|
+
{
|
|
1391
|
+
error: "Invalid image metadata. Width and height are required for resizing."
|
|
1392
|
+
},
|
|
1393
|
+
400
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
const { width: finalWidth, height: finalHeight } = calculateDimensions(
|
|
1397
|
+
metadata.width,
|
|
1398
|
+
metadata.height,
|
|
1399
|
+
w ? parseInt(w) : void 0,
|
|
1400
|
+
h ? parseInt(h) : void 0
|
|
1401
|
+
);
|
|
1402
|
+
const cachePath = path3.join(IMAGE_CACHE_DIR, queryStringHash);
|
|
1403
|
+
let imageFormat = format.toLowerCase();
|
|
1404
|
+
if (!isSupportedFormat(imageFormat)) {
|
|
1405
|
+
throw new Error("Unsupported image format");
|
|
1406
|
+
}
|
|
1407
|
+
const quality = parseInt(q);
|
|
1408
|
+
console.log("quality", quality);
|
|
1409
|
+
const image = await sharp(imagePath).resize(finalWidth, finalHeight).toFormat(imageFormat, {
|
|
1410
|
+
quality
|
|
1411
|
+
}).toFile(cachePath);
|
|
1412
|
+
console.log("image", image);
|
|
1413
|
+
c.res.headers.set("Content-Type", getContentType(image.format));
|
|
1414
|
+
return c.body(
|
|
1415
|
+
Readable.toWeb(fs2.createReadStream(cachePath))
|
|
1416
|
+
);
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
console.error("Error processing the image:", error);
|
|
1419
|
+
return c.json({ error: "Error processing the image" }, 500);
|
|
1420
|
+
}
|
|
1421
|
+
});
|
|
1422
|
+
};
|
|
1423
|
+
var IMAGE_CACHE_DIR = path3.join(process.cwd(), ".cache/__pylon/images");
|
|
1424
|
+
fs2.promises.mkdir(IMAGE_CACHE_DIR, { recursive: true });
|
|
1425
|
+
var calculateDimensions = (originalWidth, originalHeight, width, height) => {
|
|
1426
|
+
if (!width && !height) {
|
|
1427
|
+
return { width: originalWidth, height: originalHeight };
|
|
1428
|
+
}
|
|
1429
|
+
if (width && !height) {
|
|
1430
|
+
height = Math.round(width * originalHeight / originalWidth);
|
|
1431
|
+
} else if (height && !width) {
|
|
1432
|
+
width = Math.round(height * originalWidth / originalHeight);
|
|
1433
|
+
}
|
|
1434
|
+
return { width, height };
|
|
1435
|
+
};
|
|
1436
|
+
function isSupportedFormat(format) {
|
|
1437
|
+
const supportedFormats = sharp.format;
|
|
1438
|
+
return Object.keys(supportedFormats).includes(format);
|
|
1439
|
+
}
|
|
1440
|
+
var getContentType = (format) => {
|
|
1441
|
+
switch (format.toLowerCase()) {
|
|
1442
|
+
case "webp":
|
|
1443
|
+
return "image/webp";
|
|
1444
|
+
case "jpeg":
|
|
1445
|
+
case "jpg":
|
|
1446
|
+
return "image/jpeg";
|
|
1447
|
+
case "png":
|
|
1448
|
+
return "image/png";
|
|
1449
|
+
case "gif":
|
|
1450
|
+
return "image/gif";
|
|
1451
|
+
case "svg":
|
|
1452
|
+
return "image/svg+xml";
|
|
1453
|
+
default:
|
|
1454
|
+
return "application/octet-stream";
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
var downloadImage = async (url) => {
|
|
1458
|
+
const response = await fetch(url);
|
|
1459
|
+
if (!response.ok)
|
|
1460
|
+
throw new Error(`Failed to download image: ${response.statusText}`);
|
|
1461
|
+
const ext = path3.extname(new URL(url).pathname) || ".jpg";
|
|
1462
|
+
const tempFilePath = path3.join(tmpdir(), `image-${Date.now()}${ext}`);
|
|
1463
|
+
const fileStream = fs2.createWriteStream(tempFilePath);
|
|
1464
|
+
await pipeline(response.body, fileStream);
|
|
1465
|
+
return tempFilePath;
|
|
1466
|
+
};
|
|
1467
|
+
|
|
1468
|
+
// src/plugins/use-pages/build/index.ts
|
|
1469
|
+
import path7 from "path";
|
|
1470
|
+
|
|
1471
|
+
// src/plugins/use-pages/build/app-utils.ts
|
|
1472
|
+
import path4 from "path";
|
|
1473
|
+
import glob from "tiny-glob";
|
|
1474
|
+
function fnv1aHash(str) {
|
|
1475
|
+
let hash = 2166136261;
|
|
1476
|
+
for (let i = 0; i < str.length; i++) {
|
|
1477
|
+
hash ^= str.charCodeAt(i);
|
|
1478
|
+
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
1479
|
+
}
|
|
1480
|
+
return (hash >>> 0).toString(16);
|
|
1481
|
+
}
|
|
1482
|
+
var APP_DIR = path4.join(process.cwd(), "pages");
|
|
1483
|
+
async function getPageRoutes(dir = APP_DIR) {
|
|
1484
|
+
const routes = [];
|
|
1485
|
+
const pagePattern = path4.join(dir, "**/page.{ts,tsx,js}");
|
|
1486
|
+
const layoutPattern = path4.join(dir, "**/layout.tsx");
|
|
1487
|
+
const pageFiles = await glob(pagePattern);
|
|
1488
|
+
const layoutFiles = await glob(layoutPattern);
|
|
1489
|
+
for (const pagePath of pageFiles) {
|
|
1490
|
+
const relativePagePath = path4.relative(APP_DIR, pagePath);
|
|
1491
|
+
let slug = "/" + relativePagePath.replace(/page\.(ts|tsx|js)$/, "").replace(/\[([\w-]+)\]/g, ":$1");
|
|
1492
|
+
slug = slug.replace(/\/$/, "");
|
|
1493
|
+
const layouts = layoutFiles.filter((layout) => {
|
|
1494
|
+
return pagePath.startsWith(layout.replace("layout.tsx", ""));
|
|
1495
|
+
});
|
|
1496
|
+
const layoutsWithoutRootLayout = layouts.slice(1);
|
|
1497
|
+
routes.push({
|
|
1498
|
+
pagePath,
|
|
1499
|
+
slug: slug || "/",
|
|
1500
|
+
layouts: layoutsWithoutRootLayout
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
return routes;
|
|
1504
|
+
}
|
|
1505
|
+
var generateAppFile = (pageRoutes) => {
|
|
1506
|
+
const makePageMap = (routes) => {
|
|
1507
|
+
const pageMap2 = {};
|
|
1508
|
+
for (const route of routes) {
|
|
1509
|
+
pageMap2[route.pagePath] = `Page${fnv1aHash(route.pagePath)}`;
|
|
1510
|
+
}
|
|
1511
|
+
return pageMap2;
|
|
1512
|
+
};
|
|
1513
|
+
const makeLayoutMap = (routes) => {
|
|
1514
|
+
const layoutMap2 = {};
|
|
1515
|
+
for (const route of routes) {
|
|
1516
|
+
for (const layout of route.layouts) {
|
|
1517
|
+
layoutMap2[layout] = `Layout${fnv1aHash(layout)}`;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
return layoutMap2;
|
|
1521
|
+
};
|
|
1522
|
+
const pageMap = makePageMap(pageRoutes);
|
|
1523
|
+
const layoutMap = makeLayoutMap(pageRoutes);
|
|
1524
|
+
const importPages = Object.keys(pageMap).map((pagePath, index) => {
|
|
1525
|
+
const importLocation = `../${pagePath}`.replace(".tsx", ".js");
|
|
1526
|
+
const componentName = pageMap[pagePath];
|
|
1527
|
+
return `const ${componentName} = lazy(() => import('${importLocation}'))
|
|
1528
|
+
`;
|
|
1529
|
+
}).join("\n");
|
|
1530
|
+
const importLayouts = Object.keys(layoutMap).map((layoutPath, index) => {
|
|
1531
|
+
const importLocation = `../${layoutPath}`.replace(".tsx", ".js");
|
|
1532
|
+
const componentName = layoutMap[layoutPath];
|
|
1533
|
+
return `const ${componentName} = lazy(() => import('${importLocation}'))
|
|
1534
|
+
`;
|
|
1535
|
+
}).join("\n");
|
|
1536
|
+
const appComponent = `"use client";
|
|
1537
|
+
import {lazy, Suspense} from 'react'
|
|
1538
|
+
import { Routes, Route } from 'react-router';
|
|
1539
|
+
${importPages}
|
|
1540
|
+
const RootLayout = lazy(() => import('../pages/layout.js'))
|
|
1541
|
+
${importLayouts}
|
|
1542
|
+
|
|
1543
|
+
const App: React.FC<{pageProps: any}> = ({pageProps}) => (
|
|
1544
|
+
<RootLayout>
|
|
1545
|
+
<meta charSet="utf-8" />
|
|
1546
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1547
|
+
<link rel="stylesheet" href="/__pylon/static/app.css" />
|
|
1548
|
+
<Routes>
|
|
1549
|
+
${pageRoutes.map((route, index) => {
|
|
1550
|
+
return `<Route key={${index}} index={${index === 0 ? "true" : "false"}} path="${route.slug}" element={
|
|
1551
|
+
<Suspense fallback={<div>...</div>}>
|
|
1552
|
+
${route.layouts.reduceRight((child, layoutPath, layoutIndex) => {
|
|
1553
|
+
const layoutName = layoutMap[layoutPath];
|
|
1554
|
+
return `<${layoutName}>${child}</${layoutName}>`;
|
|
1555
|
+
}, `<${pageMap[route.pagePath]} {...pageProps} />`)}
|
|
1556
|
+
|
|
1557
|
+
</Suspense>} />`;
|
|
1558
|
+
}).join("\n")}
|
|
1559
|
+
</Routes>
|
|
1560
|
+
</RootLayout>
|
|
1561
|
+
);
|
|
1562
|
+
|
|
1563
|
+
export default App;
|
|
1564
|
+
`;
|
|
1565
|
+
return appComponent;
|
|
1566
|
+
};
|
|
1567
|
+
|
|
1568
|
+
// src/plugins/use-pages/build/index.ts
|
|
1569
|
+
import chokidar from "chokidar";
|
|
1570
|
+
import fs6 from "fs/promises";
|
|
1571
|
+
import esbuild from "esbuild";
|
|
1572
|
+
|
|
1573
|
+
// src/plugins/use-pages/build/plugins/inject-app-hydration.ts
|
|
1574
|
+
import path5 from "path";
|
|
1575
|
+
import fs3 from "fs/promises";
|
|
1576
|
+
var injectAppHydrationPlugin = {
|
|
1577
|
+
name: "inject-hydration",
|
|
1578
|
+
setup(build2) {
|
|
1579
|
+
build2.onLoad({ filter: /.*/, namespace: "file" }, async (args) => {
|
|
1580
|
+
if (args.path === path5.resolve(process.cwd(), ".pylon", "app.tsx")) {
|
|
1581
|
+
let contents = await fs3.readFile(args.path, "utf-8");
|
|
1582
|
+
const clientPath = path5.resolve(process.cwd(), ".pylon/client");
|
|
1583
|
+
const pathToClient = path5.relative(path5.dirname(args.path), clientPath);
|
|
1584
|
+
contents += `
|
|
1585
|
+
import {hydrateRoot} from 'react-dom/client'
|
|
1586
|
+
import * as client from './${pathToClient}'
|
|
1587
|
+
import {BrowserRouter} from 'react-router'
|
|
1588
|
+
import React, {useMemo} from 'react'
|
|
1589
|
+
|
|
1590
|
+
const pylonData = window.__PYLON_DATA__
|
|
1591
|
+
|
|
1592
|
+
console.log('pylonData', pylonData)
|
|
1593
|
+
|
|
1594
|
+
const AppLoader = (props: {
|
|
1595
|
+
client: any
|
|
1596
|
+
pylonData: {
|
|
1597
|
+
pageProps: Omit<PageProps, 'data'>
|
|
1598
|
+
cacheSnapshot?: any
|
|
1599
|
+
}
|
|
1600
|
+
App: React.FC<{
|
|
1601
|
+
pageProps: PageProps
|
|
1602
|
+
}>
|
|
1603
|
+
Router: React.FC<any>
|
|
1604
|
+
routerProps: any
|
|
1605
|
+
}) => {
|
|
1606
|
+
props.client.useHydrateCache({cacheSnapshot: props.pylonData.cacheSnapshot})
|
|
1607
|
+
|
|
1608
|
+
const data = props.client.useQuery()
|
|
1609
|
+
const page = useMemo(() => {
|
|
1610
|
+
const page = (
|
|
1611
|
+
<props.App
|
|
1612
|
+
pageProps={{
|
|
1613
|
+
...props.pylonData.pageProps,
|
|
1614
|
+
data
|
|
1615
|
+
}}
|
|
1616
|
+
/>
|
|
1617
|
+
)
|
|
1618
|
+
|
|
1619
|
+
return page
|
|
1620
|
+
}, [props])
|
|
1621
|
+
|
|
1622
|
+
return <props.Router {...props.routerProps}>{page}</props.Router>
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
|
|
1626
|
+
hydrateRoot(
|
|
1627
|
+
document,
|
|
1628
|
+
<AppLoader Router={BrowserRouter} client={client} pylonData={pylonData} App={App} />
|
|
1629
|
+
)
|
|
1630
|
+
`;
|
|
1631
|
+
return {
|
|
1632
|
+
loader: "tsx",
|
|
1633
|
+
contents
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
};
|
|
1639
|
+
|
|
1640
|
+
// src/plugins/use-pages/build/plugins/image-plugin.ts
|
|
1641
|
+
import { createHash as createHash2 } from "crypto";
|
|
1642
|
+
import path6 from "path";
|
|
1643
|
+
import sharp2 from "sharp";
|
|
1644
|
+
import fs4 from "fs/promises";
|
|
1645
|
+
var imagePlugin = {
|
|
1646
|
+
name: "image-plugin",
|
|
1647
|
+
setup(build2) {
|
|
1648
|
+
const outdir = build2.initialOptions.outdir;
|
|
1649
|
+
const publicPath = build2.initialOptions.publicPath;
|
|
1650
|
+
if (!outdir || !publicPath) {
|
|
1651
|
+
throw new Error("outdir and publicPath must be set in esbuild options");
|
|
1652
|
+
}
|
|
1653
|
+
build2.onResolve({ filter: /\.(png|jpe?g)$/ }, async (args) => {
|
|
1654
|
+
const filePath = path6.resolve(args.resolveDir, args.path);
|
|
1655
|
+
const fileName = path6.basename(filePath);
|
|
1656
|
+
const extname = path6.extname(filePath);
|
|
1657
|
+
const hash = createHash2("md5").update(filePath + await fs4.readFile(filePath)).digest("hex").slice(0, 8);
|
|
1658
|
+
const newFilename = `${fileName}-${hash}${extname}`;
|
|
1659
|
+
const newFilePath = path6.join(outdir, "media", newFilename);
|
|
1660
|
+
await fs4.mkdir(path6.dirname(newFilePath), { recursive: true });
|
|
1661
|
+
await fs4.copyFile(filePath, newFilePath);
|
|
1662
|
+
return {
|
|
1663
|
+
path: newFilePath,
|
|
1664
|
+
namespace: "image"
|
|
1665
|
+
};
|
|
1666
|
+
});
|
|
1667
|
+
build2.onLoad({ filter: /\.png$|\.jpg$/ }, async (args) => {
|
|
1668
|
+
const image = sharp2(args.path);
|
|
1669
|
+
const metadata = await image.metadata();
|
|
1670
|
+
const url = `${publicPath}/media/${path6.basename(args.path)}`;
|
|
1671
|
+
const searchParams = new URLSearchParams({});
|
|
1672
|
+
if (metadata.width) {
|
|
1673
|
+
searchParams.set("w", metadata.width.toString());
|
|
1674
|
+
}
|
|
1675
|
+
if (metadata.height) {
|
|
1676
|
+
searchParams.set("h", metadata.height.toString());
|
|
1677
|
+
}
|
|
1678
|
+
const output = image.resize({
|
|
1679
|
+
width: Math.min(metadata.width ?? 16, 16),
|
|
1680
|
+
height: Math.min(metadata.height ?? 16, 16),
|
|
1681
|
+
fit: "inside"
|
|
1682
|
+
}).toFormat("webp", {
|
|
1683
|
+
quality: 20,
|
|
1684
|
+
alphaQuality: 20,
|
|
1685
|
+
smartSubsample: true
|
|
1686
|
+
});
|
|
1687
|
+
const { data, info } = await output.toBuffer({ resolveWithObject: true });
|
|
1688
|
+
const dataURIBase64 = `data:image/${info.format};base64,${data.toString(
|
|
1689
|
+
"base64"
|
|
1690
|
+
)}`;
|
|
1691
|
+
if (dataURIBase64) {
|
|
1692
|
+
searchParams.set("blurDataURL", dataURIBase64);
|
|
1693
|
+
}
|
|
1694
|
+
return {
|
|
1695
|
+
contents: `${url}?${searchParams.toString()}`,
|
|
1696
|
+
loader: "text"
|
|
1697
|
+
};
|
|
1698
|
+
});
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
|
|
1702
|
+
// src/plugins/use-pages/build/plugins/postcss-plugin.ts
|
|
1703
|
+
import fs5 from "fs/promises";
|
|
1704
|
+
import loadConfig from "postcss-load-config";
|
|
1705
|
+
import postcss from "postcss";
|
|
1706
|
+
var postcssPlugin = {
|
|
1707
|
+
name: "postcss-plugin",
|
|
1708
|
+
setup(build2) {
|
|
1709
|
+
build2.onLoad({ filter: /.css$/, namespace: "file" }, async (args) => {
|
|
1710
|
+
const { plugins, options } = await loadConfig();
|
|
1711
|
+
const css = await fs5.readFile(args.path, "utf-8");
|
|
1712
|
+
const result = await postcss(plugins).process(css, {
|
|
1713
|
+
...options,
|
|
1714
|
+
from: args.path
|
|
1715
|
+
}).then((result2) => result2);
|
|
1716
|
+
return {
|
|
1717
|
+
contents: result.css,
|
|
1718
|
+
loader: "css"
|
|
1719
|
+
};
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
};
|
|
1723
|
+
|
|
1724
|
+
// src/plugins/use-pages/build/index.ts
|
|
1725
|
+
var DIST_STATIC_DIR = path7.join(process.cwd(), ".pylon/__pylon/static");
|
|
1726
|
+
var DIST_PAGES_DIR = path7.join(process.cwd(), ".pylon/__pylon/pages");
|
|
1727
|
+
async function updateFileIfChanged(path8, newContent) {
|
|
1728
|
+
try {
|
|
1729
|
+
const currentContent = await fs6.readFile(path8, "utf8");
|
|
1730
|
+
if (currentContent === newContent) {
|
|
1731
|
+
return false;
|
|
1732
|
+
}
|
|
1733
|
+
} catch (err) {
|
|
1734
|
+
if (err.code !== "ENOENT") throw err;
|
|
1735
|
+
}
|
|
1736
|
+
await fs6.writeFile(path8, newContent, "utf8");
|
|
1737
|
+
return true;
|
|
1738
|
+
}
|
|
1739
|
+
var build = async () => {
|
|
1740
|
+
const buildAppFile = async () => {
|
|
1741
|
+
const pagesRoutes = await getPageRoutes();
|
|
1742
|
+
const appContent = generateAppFile(pagesRoutes);
|
|
1743
|
+
const appFilePath = path7.resolve(process.cwd(), ".pylon", "app.tsx");
|
|
1744
|
+
const state = await updateFileIfChanged(appFilePath, appContent);
|
|
1745
|
+
if (state) {
|
|
1746
|
+
console.log("Updated app file");
|
|
1747
|
+
}
|
|
1748
|
+
};
|
|
1749
|
+
const writeOnEndPlugin = {
|
|
1750
|
+
name: "write-on-end",
|
|
1751
|
+
setup(build2) {
|
|
1752
|
+
build2.onEnd(async (result) => {
|
|
1753
|
+
result.outputFiles?.forEach(async (file) => {
|
|
1754
|
+
await fs6.mkdir(path7.dirname(file.path), { recursive: true });
|
|
1755
|
+
await updateFileIfChanged(file.path, file.text);
|
|
1756
|
+
});
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
let pagesWatcher = null;
|
|
1761
|
+
const clientCtx = await esbuild.context({
|
|
1762
|
+
write: false,
|
|
1763
|
+
metafile: true,
|
|
1764
|
+
absWorkingDir: process.cwd(),
|
|
1765
|
+
plugins: [
|
|
1766
|
+
injectAppHydrationPlugin,
|
|
1767
|
+
imagePlugin,
|
|
1768
|
+
postcssPlugin,
|
|
1769
|
+
writeOnEndPlugin
|
|
1770
|
+
],
|
|
1771
|
+
publicPath: "/__pylon/static",
|
|
1772
|
+
assetNames: "assets/[name]-[hash]",
|
|
1773
|
+
chunkNames: "chunks/[name]-[hash]",
|
|
1774
|
+
format: "esm",
|
|
1775
|
+
platform: "browser",
|
|
1776
|
+
entryPoints: [".pylon/app.tsx"],
|
|
1777
|
+
outdir: DIST_STATIC_DIR,
|
|
1778
|
+
bundle: true,
|
|
1779
|
+
splitting: true,
|
|
1780
|
+
minify: false,
|
|
1781
|
+
loader: {
|
|
1782
|
+
// Map file extensions to the file loader
|
|
1783
|
+
".svg": "file",
|
|
1784
|
+
".woff": "file",
|
|
1785
|
+
".woff2": "file"
|
|
1786
|
+
},
|
|
1787
|
+
define: {
|
|
1788
|
+
"process.env.NODE_ENV": '"production"'
|
|
1789
|
+
},
|
|
1790
|
+
mainFields: ["browser", "module", "main"]
|
|
1791
|
+
});
|
|
1792
|
+
const serverCtx = await esbuild.context({
|
|
1793
|
+
write: false,
|
|
1794
|
+
absWorkingDir: process.cwd(),
|
|
1795
|
+
plugins: [imagePlugin, postcssPlugin, writeOnEndPlugin],
|
|
1796
|
+
publicPath: "/__pylon/static",
|
|
1797
|
+
assetNames: "assets/[name]-[hash]",
|
|
1798
|
+
chunkNames: "chunks/[name]-[hash]",
|
|
1799
|
+
format: "esm",
|
|
1800
|
+
platform: "node",
|
|
1801
|
+
entryPoints: [".pylon/app.tsx"],
|
|
1802
|
+
outdir: DIST_PAGES_DIR,
|
|
1803
|
+
bundle: true,
|
|
1804
|
+
packages: "external",
|
|
1805
|
+
splitting: false,
|
|
1806
|
+
minify: false,
|
|
1807
|
+
loader: {
|
|
1808
|
+
// Map file extensions to the file loader
|
|
1809
|
+
".svg": "file",
|
|
1810
|
+
".woff": "file",
|
|
1811
|
+
".woff2": "file"
|
|
1812
|
+
}
|
|
1813
|
+
});
|
|
1814
|
+
return {
|
|
1815
|
+
watch: async () => {
|
|
1816
|
+
console.log("Watching pages directory...");
|
|
1817
|
+
pagesWatcher = chokidar.watch("pages", { ignoreInitial: false });
|
|
1818
|
+
pagesWatcher.on("all", (event, path8) => {
|
|
1819
|
+
if (["add", "change", "unlink"].includes(event)) {
|
|
1820
|
+
buildAppFile();
|
|
1821
|
+
}
|
|
1822
|
+
});
|
|
1823
|
+
await Promise.all([clientCtx.watch(), serverCtx.watch()]);
|
|
1824
|
+
},
|
|
1825
|
+
dispose: async () => {
|
|
1826
|
+
console.log("Disposing pages");
|
|
1827
|
+
if (pagesWatcher) {
|
|
1828
|
+
pagesWatcher.close();
|
|
1829
|
+
}
|
|
1830
|
+
Promise.all([clientCtx.dispose(), serverCtx.dispose()]);
|
|
1831
|
+
},
|
|
1832
|
+
rebuild: async () => {
|
|
1833
|
+
console.log("Rebuilding pages");
|
|
1834
|
+
await buildAppFile();
|
|
1835
|
+
await Promise.all([clientCtx.rebuild(), serverCtx.rebuild()]);
|
|
1836
|
+
return {};
|
|
1837
|
+
},
|
|
1838
|
+
cancel: async () => {
|
|
1839
|
+
if (pagesWatcher) {
|
|
1840
|
+
await pagesWatcher.close();
|
|
1841
|
+
}
|
|
1842
|
+
await Promise.all([clientCtx.cancel(), serverCtx.cancel()]);
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
};
|
|
1846
|
+
|
|
1847
|
+
// src/plugins/use-pages/index.ts
|
|
1848
|
+
function usePages() {
|
|
1849
|
+
return {
|
|
1850
|
+
setup,
|
|
1851
|
+
build
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1210
1854
|
export {
|
|
1211
1855
|
ServiceError,
|
|
1212
1856
|
app,
|
|
@@ -1219,6 +1863,7 @@ export {
|
|
|
1219
1863
|
handler,
|
|
1220
1864
|
requireAuth,
|
|
1221
1865
|
setContext,
|
|
1222
|
-
useAuth
|
|
1866
|
+
useAuth,
|
|
1867
|
+
usePages
|
|
1223
1868
|
};
|
|
1224
1869
|
//# sourceMappingURL=index.js.map
|