@cloudwerk/cli 0.1.1 → 0.2.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.
Files changed (2) hide show
  1. package/dist/index.js +112 -1
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -2406,10 +2406,33 @@ async function loadPageModule(absolutePath, verbose = false) {
2406
2406
  }
2407
2407
  validatedLoader = rawModule.loader;
2408
2408
  }
2409
+ let validatedAction = void 0;
2410
+ if ("action" in rawModule && rawModule.action !== void 0) {
2411
+ if (typeof rawModule.action !== "function") {
2412
+ throw new Error(
2413
+ `Page action export must be a function, got ${typeof rawModule.action}`
2414
+ );
2415
+ }
2416
+ validatedAction = rawModule.action;
2417
+ }
2418
+ const actionMethods = ["POST", "PUT", "PATCH", "DELETE"];
2419
+ const validatedMethods = {};
2420
+ for (const method of actionMethods) {
2421
+ if (method in rawModule && rawModule[method] !== void 0) {
2422
+ if (typeof rawModule[method] !== "function") {
2423
+ throw new Error(
2424
+ `Page ${method} export must be a function, got ${typeof rawModule[method]}`
2425
+ );
2426
+ }
2427
+ validatedMethods[method] = rawModule[method];
2428
+ }
2429
+ }
2409
2430
  const module = {
2410
2431
  default: rawModule.default,
2411
2432
  config: validatedConfig,
2412
- loader: validatedLoader
2433
+ loader: validatedLoader,
2434
+ action: validatedAction,
2435
+ ...validatedMethods
2413
2436
  };
2414
2437
  pageModuleCache.set(absolutePath, { module, mtime });
2415
2438
  return module;
@@ -2583,6 +2606,23 @@ async function executeLoader(loader, args, c) {
2583
2606
  throw error;
2584
2607
  }
2585
2608
  }
2609
+ async function executeAction(action, args, c) {
2610
+ try {
2611
+ const result = await Promise.resolve(action(args));
2612
+ if (result instanceof Response) {
2613
+ return { response: result };
2614
+ }
2615
+ return { data: result ?? {} };
2616
+ } catch (error) {
2617
+ if (error instanceof NotFoundError) {
2618
+ return { response: await Promise.resolve(c.notFound()) };
2619
+ }
2620
+ if (error instanceof RedirectError) {
2621
+ return { response: c.redirect(error.url, error.status) };
2622
+ }
2623
+ throw error;
2624
+ }
2625
+ }
2586
2626
  async function registerRoutes(app, manifest, logger, verbose = false) {
2587
2627
  const registeredRoutes = [];
2588
2628
  for (const route of manifest.routes) {
@@ -2666,6 +2706,77 @@ async function registerRoutes(app, manifest, logger, verbose = false) {
2666
2706
  filePath: route.filePath
2667
2707
  });
2668
2708
  logger.debug(`Registered page ${route.urlPattern}`);
2709
+ const actionMethods = ["POST", "PUT", "PATCH", "DELETE"];
2710
+ for (const method of actionMethods) {
2711
+ const action = pageModule[method] ?? pageModule.action;
2712
+ if (action && typeof action === "function") {
2713
+ const actionFn = action;
2714
+ registerMethod(app, method, route.urlPattern, async (c) => {
2715
+ try {
2716
+ const params = c.req.param();
2717
+ const searchParams = parseSearchParams(c);
2718
+ const request = c.req.raw;
2719
+ const actionArgs = { params, request, context: c };
2720
+ const actionResult = await executeAction(actionFn, actionArgs, c);
2721
+ if (actionResult.response) {
2722
+ return actionResult.response;
2723
+ }
2724
+ const loaderArgs = { params, request, context: c };
2725
+ const layoutLoaderData = [];
2726
+ for (let index = 0; index < layoutModules.length; index++) {
2727
+ const layoutModule = layoutModules[index];
2728
+ if (layoutModule.loader) {
2729
+ const result = await executeLoader(layoutModule.loader, loaderArgs, c);
2730
+ if (result.response) {
2731
+ return result.response;
2732
+ }
2733
+ layoutLoaderData[index] = result.data;
2734
+ } else {
2735
+ layoutLoaderData[index] = {};
2736
+ }
2737
+ }
2738
+ let pageLoaderData = {};
2739
+ if (pageModule.loader) {
2740
+ const result = await executeLoader(pageModule.loader, loaderArgs, c);
2741
+ if (result.response) {
2742
+ return result.response;
2743
+ }
2744
+ pageLoaderData = result.data;
2745
+ }
2746
+ const pageProps = {
2747
+ params,
2748
+ searchParams,
2749
+ actionData: actionResult.data,
2750
+ ...pageLoaderData
2751
+ };
2752
+ let element = await Promise.resolve(PageComponent(pageProps));
2753
+ for (let i = layouts.length - 1; i >= 0; i--) {
2754
+ const Layout = layouts[i];
2755
+ const layoutProps = {
2756
+ children: element,
2757
+ params,
2758
+ ...layoutLoaderData[i]
2759
+ };
2760
+ element = await Promise.resolve(Layout(layoutProps));
2761
+ }
2762
+ return render(element);
2763
+ } catch (error) {
2764
+ const message = error instanceof Error ? error.message : String(error);
2765
+ logger.error(`Error executing action ${route.filePath}: ${message}`);
2766
+ return c.html(
2767
+ `<!DOCTYPE html><html><body><h1>Internal Server Error</h1></body></html>`,
2768
+ 500
2769
+ );
2770
+ }
2771
+ });
2772
+ registeredRoutes.push({
2773
+ method,
2774
+ pattern: route.urlPattern,
2775
+ filePath: route.filePath
2776
+ });
2777
+ logger.debug(`Registered ${method} action ${route.urlPattern}`);
2778
+ }
2779
+ }
2669
2780
  } catch (error) {
2670
2781
  const message = error instanceof Error ? error.message : String(error);
2671
2782
  logger.error(`Failed to load page ${route.filePath}: ${message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudwerk/cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Dev server, build, deploy commands for Cloudwerk",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,7 @@
25
25
  "commander": "^12.1.0",
26
26
  "esbuild": "^0.25.0",
27
27
  "picocolors": "^1.1.0",
28
- "@cloudwerk/core": "^0.1.1",
28
+ "@cloudwerk/core": "^0.2.0",
29
29
  "@cloudwerk/ui": "^0.1.1"
30
30
  },
31
31
  "devDependencies": {