@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 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?: () => Promise<void>;
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
- return context.env || process.env || {};
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