@lolyjs/core 0.1.0-alpha.8 → 0.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1325,24 +1325,69 @@ function createClientConfig(projectRoot, mode) {
1325
1325
  init_globals();
1326
1326
  import path12 from "path";
1327
1327
  import fs11 from "fs";
1328
- function startClientBundler(projectRoot) {
1329
- const { config, outDir } = createClientConfig(projectRoot, "production");
1328
+ function startClientBundler(projectRoot, mode = "development") {
1329
+ const { config, outDir } = createClientConfig(projectRoot, mode);
1330
1330
  copyStaticAssets(projectRoot, outDir);
1331
1331
  const compiler = rspack2(config);
1332
+ let isBuilding = false;
1333
+ let buildResolve = null;
1334
+ let buildPromise = null;
1335
+ let lastBuildTime = Date.now();
1336
+ compiler.hooks.compile.tap("HotReload", () => {
1337
+ isBuilding = true;
1338
+ buildPromise = new Promise((resolve3) => {
1339
+ buildResolve = resolve3;
1340
+ });
1341
+ });
1332
1342
  compiler.watch({}, (err, stats) => {
1333
1343
  if (err) {
1334
1344
  console.error("[framework][client] Rspack error:", err);
1345
+ isBuilding = false;
1346
+ lastBuildTime = Date.now();
1347
+ if (buildResolve) {
1348
+ buildResolve();
1349
+ buildResolve = null;
1350
+ buildPromise = null;
1351
+ }
1352
+ return;
1353
+ }
1354
+ if (!stats) {
1355
+ isBuilding = false;
1356
+ lastBuildTime = Date.now();
1335
1357
  return;
1336
1358
  }
1337
- if (!stats) return;
1338
1359
  if (stats.hasErrors()) {
1339
1360
  console.error(
1340
1361
  "[framework][client] Build with errors:\n",
1341
1362
  stats.toString("errors-only")
1342
1363
  );
1364
+ } else {
1365
+ console.log("[framework][client] \u2713 Client bundle rebuilt successfully");
1366
+ }
1367
+ isBuilding = false;
1368
+ lastBuildTime = Date.now();
1369
+ if (buildResolve) {
1370
+ buildResolve();
1371
+ buildResolve = null;
1372
+ buildPromise = null;
1343
1373
  }
1344
1374
  });
1345
- return { outDir };
1375
+ return {
1376
+ outDir,
1377
+ waitForBuild: async () => {
1378
+ if (isBuilding && buildPromise) {
1379
+ await buildPromise;
1380
+ await new Promise((resolve3) => setTimeout(resolve3, 100));
1381
+ return;
1382
+ }
1383
+ const timeSinceLastBuild = Date.now() - lastBuildTime;
1384
+ if (timeSinceLastBuild < 500) {
1385
+ await new Promise((resolve3) => setTimeout(resolve3, 200));
1386
+ return;
1387
+ }
1388
+ return Promise.resolve();
1389
+ }
1390
+ };
1346
1391
  }
1347
1392
  function buildClientBundle(projectRoot) {
1348
1393
  const { config, outDir } = createClientConfig(projectRoot, "production");
@@ -3697,7 +3742,9 @@ import path19 from "path";
3697
3742
  function setupHotReload({
3698
3743
  app,
3699
3744
  appDir,
3700
- route = "/__fw/hot"
3745
+ route = "/__fw/hot",
3746
+ waitForBuild,
3747
+ onFileChange
3701
3748
  }) {
3702
3749
  const clients = /* @__PURE__ */ new Set();
3703
3750
  app.get(route, (req, res) => {
@@ -3718,9 +3765,25 @@ data: connected
3718
3765
  ignoreInitial: true,
3719
3766
  ignored: ["**/node_modules/**", `**/${BUILD_FOLDER_NAME}/**`, "**/.git/**"]
3720
3767
  });
3721
- function broadcastReload(reason, filePath) {
3768
+ async function broadcastReload(reason, filePath) {
3722
3769
  const rel = path19.relative(appDir, filePath);
3723
3770
  console.log(`[hot-reload] ${reason}: ${rel}`);
3771
+ if (onFileChange) {
3772
+ try {
3773
+ await onFileChange(filePath);
3774
+ } catch (error) {
3775
+ console.warn("[hot-reload] Error in onFileChange callback:", error);
3776
+ }
3777
+ }
3778
+ if (waitForBuild) {
3779
+ try {
3780
+ console.log("[hot-reload] Waiting for client bundle to finish...");
3781
+ await waitForBuild();
3782
+ console.log("[hot-reload] Client bundle ready, sending reload event");
3783
+ } catch (error) {
3784
+ console.warn("[hot-reload] Error waiting for build:", error);
3785
+ }
3786
+ }
3724
3787
  for (const res of clients) {
3725
3788
  res.write(`event: message
3726
3789
  data: reload:${rel}
@@ -3757,14 +3820,29 @@ function setupServer(app, options) {
3757
3820
  };
3758
3821
  };
3759
3822
  var getRoutes = getRoutes2;
3760
- setupHotReload({ app, appDir });
3823
+ const { outDir, waitForBuild } = startClientBundler(projectRoot, "development");
3824
+ const onFileChange = async (filePath) => {
3825
+ const rel = path21.relative(appDir, filePath);
3826
+ const isPageFile = filePath.includes("page.tsx") || filePath.includes("page.ts") || filePath.includes("layout.tsx") || filePath.includes("layout.ts") || filePath.includes("_not-found") || filePath.includes("_error");
3827
+ const isTsFile = filePath.endsWith(".ts") || filePath.endsWith(".tsx");
3828
+ if (isTsFile) {
3829
+ clearAppRequireCache(appDir);
3830
+ console.log(`[hot-reload] Cleared require cache for: ${rel}`);
3831
+ }
3832
+ if (isPageFile) {
3833
+ const loader = new FilesystemRouteLoader(appDir);
3834
+ const newRoutes = loader.loadRoutes();
3835
+ writeClientRoutesManifest(newRoutes, projectRoot);
3836
+ console.log("[hot-reload] Client routes manifest reloaded");
3837
+ }
3838
+ };
3839
+ setupHotReload({ app, appDir, waitForBuild, onFileChange });
3840
+ app.use("/static", express.static(outDir));
3761
3841
  const routes = routeLoader.loadRoutes();
3762
3842
  const wssRoutes = routeLoader.loadWssRoutes();
3763
3843
  const notFoundPage = routeLoader.loadNotFoundRoute();
3764
3844
  const errorPage = routeLoader.loadErrorRoute();
3765
3845
  writeClientRoutesManifest(routes, projectRoot);
3766
- const { outDir } = startClientBundler(projectRoot);
3767
- app.use("/static", express.static(outDir));
3768
3846
  return {
3769
3847
  routes,
3770
3848
  wssRoutes,
@@ -4327,7 +4405,7 @@ async function handlePageRequest(options) {
4327
4405
  const { errorPage, req, res, routeChunks, theme, projectRoot } = options;
4328
4406
  const reqLogger = getRequestLogger(req);
4329
4407
  if (errorPage) {
4330
- await renderErrorPageWithStream(errorPage, req, res, error, routeChunks || {}, theme, projectRoot);
4408
+ await renderErrorPageWithStream(errorPage, req, res, error, routeChunks || {}, theme, projectRoot, options.env);
4331
4409
  } else {
4332
4410
  reqLogger.error("Unhandled error in page request", error, {
4333
4411
  urlPath: options.urlPath,
@@ -4355,9 +4433,9 @@ async function handlePageRequestInternal(options) {
4355
4433
  theme,
4356
4434
  projectRoot
4357
4435
  } = options;
4358
- const clientJsPath = projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
4359
- const clientCssPath = projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
4360
- const assetManifest = projectRoot ? loadAssetManifest(projectRoot) : null;
4436
+ const clientJsPath = env === "dev" ? "/static/client.js" : projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
4437
+ const clientCssPath = env === "dev" ? "/static/client.css" : projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
4438
+ const assetManifest = env === "prod" && projectRoot ? loadAssetManifest(projectRoot) : null;
4361
4439
  const isDataReq = isDataRequest(req);
4362
4440
  if (env === "prod" && ssgOutDir) {
4363
4441
  if (isDataReq) {
@@ -4413,7 +4491,7 @@ async function handlePageRequestInternal(options) {
4413
4491
  const reqLogger = getRequestLogger(req);
4414
4492
  reqLogger.error("SSR shell error", err, { route: "not-found" });
4415
4493
  if (!res.headersSent && errorPage) {
4416
- renderErrorPageWithStream(errorPage, req, res, err, routeChunks);
4494
+ renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env);
4417
4495
  } else if (!res.headersSent) {
4418
4496
  res.statusCode = 500;
4419
4497
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -4460,7 +4538,7 @@ async function handlePageRequestInternal(options) {
4460
4538
  return;
4461
4539
  } else {
4462
4540
  if (errorPage) {
4463
- await renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot);
4541
+ await renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env);
4464
4542
  return;
4465
4543
  } else {
4466
4544
  throw error;
@@ -4522,7 +4600,7 @@ async function handlePageRequestInternal(options) {
4522
4600
  const reqLogger = getRequestLogger(req);
4523
4601
  reqLogger.error("SSR shell error", err, { route: matched?.route?.pattern || "unknown" });
4524
4602
  if (!res.headersSent && errorPage) {
4525
- renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot);
4603
+ renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env);
4526
4604
  } else if (!res.headersSent) {
4527
4605
  res.statusCode = 500;
4528
4606
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -4540,7 +4618,7 @@ async function handlePageRequestInternal(options) {
4540
4618
  abort();
4541
4619
  });
4542
4620
  }
4543
- async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot) {
4621
+ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env = "dev") {
4544
4622
  try {
4545
4623
  const isDataReq = isDataRequest(req);
4546
4624
  const ctx = {
@@ -4569,9 +4647,9 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
4569
4647
  return;
4570
4648
  }
4571
4649
  const appTree = buildAppTree(errorPage, { error: String(error) }, initialData.props);
4572
- const clientJsPath = projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
4573
- const clientCssPath = projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
4574
- const assetManifest = projectRoot ? loadAssetManifest(projectRoot) : null;
4650
+ const clientJsPath = env === "dev" ? "/static/client.js" : projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
4651
+ const clientCssPath = env === "dev" ? "/static/client.css" : projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
4652
+ const assetManifest = env === "prod" && projectRoot ? loadAssetManifest(projectRoot) : null;
4575
4653
  const chunkName = routeChunks[ERROR_CHUNK_KEY];
4576
4654
  let chunkHref = null;
4577
4655
  if (chunkName != null) {