@noego/forge 0.0.11 → 0.0.13

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 (53) hide show
  1. package/dist/client.cjs +1 -1
  2. package/dist/client.cjs.map +1 -1
  3. package/dist/client.mjs +113 -117
  4. package/dist/client.mjs.map +1 -1
  5. package/dist/options/ServerOptions.cjs +28 -0
  6. package/dist/options/ServerOptions.cjs.map +1 -0
  7. package/dist/options/ServerOptions.d.ts +19 -0
  8. package/dist/options/ServerOptions.mjs +21 -0
  9. package/dist/options/ServerOptions.mjs.map +1 -0
  10. package/dist/options/assets.cjs +42 -0
  11. package/dist/options/assets.cjs.map +1 -0
  12. package/dist/options/assets.d.ts +46 -0
  13. package/dist/options/assets.mjs +38 -0
  14. package/dist/options/assets.mjs.map +1 -0
  15. package/dist/plugins/serverOnlyStub.d.ts +6 -0
  16. package/dist/plugins-cjs/index.plugins.js +9 -0
  17. package/dist/plugins-cjs/index.plugins.js.map +1 -0
  18. package/dist/plugins-cjs/options/ServerOptions.js +28 -0
  19. package/dist/plugins-cjs/options/ServerOptions.js.map +1 -0
  20. package/dist/plugins-cjs/options/assets.js +42 -0
  21. package/dist/plugins-cjs/options/assets.js.map +1 -0
  22. package/dist/plugins-cjs/plugins/serverOnlyStub.js +226 -0
  23. package/dist/plugins-cjs/plugins/serverOnlyStub.js.map +1 -0
  24. package/dist/plugins-cjs/routing/html_render/html_render.js +62 -0
  25. package/dist/plugins-cjs/routing/html_render/html_render.js.map +1 -0
  26. package/dist/plugins-cjs/stubs/server-only.js +10 -0
  27. package/dist/plugins-cjs/stubs/server-only.js.map +1 -0
  28. package/dist/plugins-es/index.plugins.d.ts +2 -0
  29. package/dist/plugins-es/index.plugins.js +2 -0
  30. package/dist/plugins-es/index.plugins.js.map +1 -0
  31. package/dist/plugins-es/options/ServerOptions.d.ts +19 -0
  32. package/dist/plugins-es/options/ServerOptions.js +21 -0
  33. package/dist/plugins-es/options/ServerOptions.js.map +1 -0
  34. package/dist/plugins-es/options/assets.d.ts +46 -0
  35. package/dist/plugins-es/options/assets.js +38 -0
  36. package/dist/plugins-es/options/assets.js.map +1 -0
  37. package/dist/plugins-es/plugins/serverOnlyStub.d.ts +6 -0
  38. package/dist/plugins-es/plugins/serverOnlyStub.js +200 -0
  39. package/dist/plugins-es/plugins/serverOnlyStub.js.map +1 -0
  40. package/dist/plugins-es/routing/html_render/html_render.d.ts +19 -0
  41. package/dist/plugins-es/routing/html_render/html_render.js +53 -0
  42. package/dist/plugins-es/routing/html_render/html_render.js.map +1 -0
  43. package/dist/plugins-es/stubs/server-only.d.ts +3 -0
  44. package/dist/plugins-es/stubs/server-only.js +7 -0
  45. package/dist/plugins-es/stubs/server-only.js.map +1 -0
  46. package/dist/routing/html_render/html_render.d.ts +19 -0
  47. package/dist/routing/html_render/html_render.js +53 -0
  48. package/dist/routing/html_render/html_render.js.map +1 -0
  49. package/dist-ssr/server.cjs +253 -37
  50. package/dist-ssr/server.cjs.map +1 -1
  51. package/dist-ssr/server.js +253 -37
  52. package/dist-ssr/server.js.map +1 -1
  53. package/package.json +11 -4
@@ -3,36 +3,27 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import express from "express";
5
5
  import path from "path";
6
- import deepmerge from "deepmerge";
7
6
  import fs from "fs";
7
+ import deepmerge from "deepmerge";
8
+ import { pathToFileURL } from "url";
8
9
  import Handlebars from "handlebars";
9
10
  import fs$1 from "fs/promises";
10
11
  import yaml from "js-yaml";
11
12
  import "clone-deep";
12
13
  import join from "url-join";
13
14
  import { p as parsePathConfig } from "./path-BqcF5dbs.js";
14
- import { pathToFileURL } from "url";
15
15
  import { render } from "svelte/server";
16
16
  import pathToRegex from "path-to-regex";
17
- import ApplicationRenderer from "../src/components/RecursiveRender.svelte";
18
17
  class ViteComponentLoader {
19
18
  constructor(basePath, vite) {
20
19
  this.basePath = basePath;
21
20
  this.vite = vite;
22
21
  }
23
22
  async load(componentPath, options = { use_base_path: true }) {
24
- let absoluteComponentPath;
25
- const use_base_path = options.use_base_path || false;
26
- if (use_base_path) {
27
- absoluteComponentPath = path.join(this.basePath, componentPath);
28
- } else {
29
- if (path.isAbsolute(componentPath)) {
30
- absoluteComponentPath = componentPath;
31
- } else {
32
- absoluteComponentPath = componentPath;
33
- }
34
- }
23
+ const absoluteComponentPath = this.getComponentFullPath(componentPath, options);
35
24
  console.log(`[ViteComponentLoader] Loading component from path: ${absoluteComponentPath}`);
25
+ const jsPath = absoluteComponentPath.replace(/\.svelte$/, ".js");
26
+ fs.existsSync(jsPath);
36
27
  let vitePath = path.relative(process.cwd(), absoluteComponentPath);
37
28
  vitePath = vitePath.replace(/\\/g, "/");
38
29
  if (!vitePath.startsWith("/")) {
@@ -57,16 +48,74 @@ class ViteComponentLoader {
57
48
  throw error;
58
49
  }
59
50
  }
51
+ getComponentFullPath(componentPath, options = { use_base_path: true }) {
52
+ const use_base_path = options.use_base_path || false;
53
+ if (use_base_path) {
54
+ return path.join(this.basePath, componentPath);
55
+ }
56
+ if (path.isAbsolute(componentPath)) {
57
+ return componentPath;
58
+ }
59
+ return componentPath;
60
+ }
60
61
  }
61
62
  class ProdComponentLoader {
62
63
  constructor(base_path) {
64
+ __publicField(this, "componentMapPromise", null);
63
65
  this.base_path = base_path;
64
66
  }
65
67
  async load(componentPath) {
66
- const component_path = path.join(this.base_path, componentPath);
67
- const module = await import(component_path.replace(/\.svelte$/, ".js"));
68
+ const normalized = this.normalizeKey(componentPath);
69
+ try {
70
+ const map = await this.loadComponentMap();
71
+ const module2 = map[normalized];
72
+ if (module2) {
73
+ return module2;
74
+ }
75
+ } catch (error) {
76
+ console.warn(`[Forge] Failed to load component "${componentPath}" from entry manifest:`, error);
77
+ }
78
+ const component_path = this.getComponentFullPath(componentPath);
79
+ const fallbackPath = component_path.endsWith(".js") ? component_path : component_path.replace(/\.svelte$/, ".js");
80
+ const module = await import(pathToFileURL(fallbackPath).href);
68
81
  return module;
69
82
  }
83
+ getComponentFullPath(componentPath) {
84
+ return path.join(this.base_path, componentPath);
85
+ }
86
+ normalizeKey(componentPath) {
87
+ const trimmed = componentPath.replace(/^\.\//, "");
88
+ if (trimmed.endsWith(".svelte")) {
89
+ return trimmed.replace(/\.svelte$/, ".js");
90
+ }
91
+ return trimmed;
92
+ }
93
+ async loadComponentMap() {
94
+ if (!this.componentMapPromise) {
95
+ const entryPath = path.join(this.base_path, "entry-ssr.js");
96
+ const entryUrl = pathToFileURL(entryPath).href;
97
+ this.componentMapPromise = import(entryUrl).then((mod) => {
98
+ const source = mod.components ?? mod.default ?? {};
99
+ const normalized = {};
100
+ for (const [key, value] of Object.entries(source)) {
101
+ const cleanKey = key.replace(/^\.\//, "");
102
+ normalized[cleanKey] = value;
103
+ if (cleanKey.startsWith("ui/")) {
104
+ normalized[cleanKey.slice(3)] = value;
105
+ }
106
+ if (cleanKey.endsWith(".svelte")) {
107
+ const jsKey = cleanKey.replace(/\.svelte$/, ".js");
108
+ normalized[jsKey] = value;
109
+ }
110
+ }
111
+ return normalized;
112
+ }).catch((error) => {
113
+ this.componentMapPromise = null;
114
+ throw error;
115
+ });
116
+ }
117
+ return this.componentMapPromise;
118
+ }
70
119
  }
71
120
  const default_template = `
72
121
  <!DOCTYPE html>
@@ -257,11 +306,9 @@ async function parse_openapi_config(openapi_config_path) {
257
306
  const defaultViteOptions = {
258
307
  appType: "custom",
259
308
  root: process.cwd(),
260
- // Explicitly set root
261
309
  server: { middlewareMode: true },
262
310
  ssr: {
263
311
  noExternal: ["svelte", /^svelte\/.*/]
264
- // Adjust if needed, maybe add your component specifically if still failing
265
312
  }
266
313
  };
267
314
  const defaultOptions = {
@@ -349,7 +396,7 @@ class ApiAdapter {
349
396
  layout: layout_data,
350
397
  view: view_data
351
398
  };
352
- return server_data;
399
+ return this.mergeLoaderData(route, request_data, server_data);
353
400
  }
354
401
  async getFallbackData(route, request_data) {
355
402
  const view = await this.manager.getViewComponent(route);
@@ -374,7 +421,48 @@ class ApiAdapter {
374
421
  layout: layout_data,
375
422
  view: view_data
376
423
  };
377
- return server_data;
424
+ return this.mergeLoaderData(route, request_data, server_data);
425
+ }
426
+ async mergeLoaderData(route, request_data, server_data) {
427
+ if (!await this.manager.hasLoaders(route)) {
428
+ return server_data;
429
+ }
430
+ const loaders = await this.manager.getLoaders(route);
431
+ const layoutLoaderResults = await Promise.all(
432
+ loaders.layouts.map(async (loader) => {
433
+ if (!loader) {
434
+ return null;
435
+ }
436
+ const data = await loader(request_data);
437
+ return data ?? null;
438
+ })
439
+ );
440
+ const baseLayoutData = server_data.layout || [];
441
+ const maxLayouts = Math.max(baseLayoutData.length, layoutLoaderResults.length);
442
+ const mergedLayouts = Array.from({ length: maxLayouts }, (_, index) => {
443
+ const baseLayoutEntry = baseLayoutData[index];
444
+ const loaderResult = layoutLoaderResults[index];
445
+ if (!loaderResult) {
446
+ return baseLayoutEntry;
447
+ }
448
+ if (baseLayoutEntry && typeof baseLayoutEntry === "object") {
449
+ return { ...baseLayoutEntry, ...loaderResult };
450
+ }
451
+ return loaderResult;
452
+ });
453
+ const viewLoaderResult = loaders.view ? await loaders.view(request_data) : null;
454
+ let mergedView = server_data.view;
455
+ if (viewLoaderResult) {
456
+ if (mergedView && typeof mergedView === "object") {
457
+ mergedView = { ...mergedView, ...viewLoaderResult };
458
+ } else {
459
+ mergedView = viewLoaderResult;
460
+ }
461
+ }
462
+ return {
463
+ layout: mergedLayouts,
464
+ view: mergedView
465
+ };
378
466
  }
379
467
  }
380
468
  class ComponentManager {
@@ -407,6 +495,50 @@ class ComponentManager {
407
495
  async getViewComponent(route) {
408
496
  return await this.componentLoader.load(route.view);
409
497
  }
498
+ async hasLoaders(route) {
499
+ const componentPaths = [...route.layout || [], route.view];
500
+ for (const componentPath of componentPaths) {
501
+ const loader = await this.resolveLoader(componentPath);
502
+ if (loader) {
503
+ return true;
504
+ }
505
+ }
506
+ return false;
507
+ }
508
+ async getLoaders(route) {
509
+ const layoutPaths = route.layout || [];
510
+ const layouts = await Promise.all(layoutPaths.map((layoutPath) => this.resolveLoader(layoutPath)));
511
+ const view = await this.resolveLoader(route.view);
512
+ return {
513
+ layouts,
514
+ view
515
+ };
516
+ }
517
+ getLoaderFilePath(componentPath) {
518
+ if (!componentPath) {
519
+ return null;
520
+ }
521
+ const fullPath = this.componentLoader.getComponentFullPath(componentPath);
522
+ const { dir, name } = path.parse(fullPath);
523
+ return path.join(dir, `${name}.load.ts`);
524
+ }
525
+ async resolveLoader(componentPath) {
526
+ const loaderFilePath = this.getLoaderFilePath(componentPath);
527
+ if (!loaderFilePath) {
528
+ return null;
529
+ }
530
+ try {
531
+ const module = await import(pathToFileURL(loaderFilePath).href);
532
+ const loader = module == null ? void 0 : module.default;
533
+ return typeof loader === "function" ? loader : null;
534
+ } catch (error) {
535
+ if ((error == null ? void 0 : error.code) === "MODULE_NOT_FOUND" || (error == null ? void 0 : error.code) === "ERR_MODULE_NOT_FOUND") {
536
+ return null;
537
+ }
538
+ console.error(`[ComponentManager] Failed to load loader for ${componentPath}:`, error);
539
+ return null;
540
+ }
541
+ }
410
542
  async getView(route) {
411
543
  const view = await this.componentLoader.load(route.view);
412
544
  return view.default;
@@ -611,8 +743,9 @@ function initialize_route_matchers(routes) {
611
743
  class ServerAdapter {
612
744
  }
613
745
  class ExpressServerAdapter extends ServerAdapter {
614
- constructor(server, manager, htmlRender, api_adapter, middleware_adapter, context_builder, isProd = false) {
746
+ constructor(server, manager, htmlRender, api_adapter, middleware_adapter, context_builder, isProd = false, componentDir) {
615
747
  super();
748
+ __publicField(this, "clientRoutes", []);
616
749
  this.server = server;
617
750
  this.manager = manager;
618
751
  this.htmlRender = htmlRender;
@@ -620,6 +753,27 @@ class ExpressServerAdapter extends ServerAdapter {
620
753
  this.middleware_adapter = middleware_adapter;
621
754
  this.context_builder = context_builder;
622
755
  this.isProd = isProd;
756
+ this.componentDir = componentDir;
757
+ }
758
+ async loadApplicationRenderer(componentRoot) {
759
+ if (!this.isProd) {
760
+ const mod = await import("../src/components/RecursiveRender.svelte");
761
+ return mod.default ?? mod;
762
+ }
763
+ const candidates = [
764
+ path.join(componentRoot, "RecursiveRender.js"),
765
+ path.join(componentRoot, "ssr", "RecursiveRender.js")
766
+ ];
767
+ let lastError;
768
+ for (const fp of candidates) {
769
+ try {
770
+ const mod = await import(fp);
771
+ return mod.default ?? mod;
772
+ } catch (e) {
773
+ lastError = e;
774
+ }
775
+ }
776
+ throw lastError ?? new Error("Unable to locate precompiled RecursiveRender.js");
623
777
  }
624
778
  transformRoutesForClient(routes) {
625
779
  var _a, _b;
@@ -637,7 +791,9 @@ class ExpressServerAdapter extends ServerAdapter {
637
791
  return transformed;
638
792
  }
639
793
  async handleRoutes(routes, manifest) {
640
- const matchers = initialize_route_matchers(routes);
794
+ const transformedRoutes = this.transformRoutesForClient(routes);
795
+ this.clientRoutes = transformedRoutes;
796
+ const matchers = initialize_route_matchers(transformedRoutes);
641
797
  this.server.use((req, res, next) => {
642
798
  (async () => {
643
799
  if (res.headersSent) {
@@ -654,13 +810,13 @@ class ExpressServerAdapter extends ServerAdapter {
654
810
  await not_found();
655
811
  return;
656
812
  }
657
- const route_options = routes.filter((r) => r.path === matcher.pattern);
813
+ const route_options = transformedRoutes.filter((r) => r.path === matcher.pattern);
658
814
  const route = route_options.find((r) => r.method.toLowerCase() === method);
659
815
  if (!route) {
660
816
  await not_found();
661
817
  return;
662
818
  }
663
- await this.handleResponse(fullUrl, matcher, route, routes, manifest, req, res).catch((e) => {
819
+ await this.handleResponse(fullUrl, matcher, route, transformedRoutes, manifest, req, res).catch((e) => {
664
820
  res.status(500).send(`
665
821
  <h1>500 Internal Server Error</h1>
666
822
  <pre>${e}</pre>
@@ -671,7 +827,7 @@ class ExpressServerAdapter extends ServerAdapter {
671
827
  });
672
828
  }
673
829
  async handleResponse(url, matcher, route, routes, manifest, req, res) {
674
- var _a, _b;
830
+ var _a, _b, _c;
675
831
  let accepts = req.headers.accept || "";
676
832
  let server_api_call = accepts.toLowerCase().includes("application/json");
677
833
  if (res.headersSent) {
@@ -745,7 +901,12 @@ ${e.stack}</pre>
745
901
  params: request_data.params || {},
746
902
  query: request_data.query
747
903
  };
748
- let { head: head_render, body: body_render, css } = render(ApplicationRenderer, {
904
+ console.log("[SSR DEBUG] layouts:", layouts.map((l) => typeof l), "view:", typeof view);
905
+ const loader = ((_a = this.manager) == null ? void 0 : _a["componentLoader"]) || {};
906
+ const componentRoot = loader["base_path"] || loader["basePath"] || "";
907
+ const applicationRenderer = await this.loadApplicationRenderer(componentRoot);
908
+ console.log("[SSR DEBUG] applicationRenderer:", typeof applicationRenderer);
909
+ const { head: head_render, html: renderedHtml, css } = render(applicationRenderer, {
749
910
  props: {
750
911
  layouts,
751
912
  view,
@@ -756,9 +917,12 @@ ${e.stack}</pre>
756
917
  page
757
918
  }
758
919
  });
920
+ const body_render = renderedHtml;
921
+ console.log("[SSR DEBUG] render result - body length:", (body_render == null ? void 0 : body_render.length) || 0);
922
+ const clientComponentDir = this.isProd ? "assets" : this.componentDir;
759
923
  const head_routing = `
760
924
  <script type='text/javascript'>
761
- window.__ROUTING__ = ${JSON.stringify(this.transformRoutesForClient(routes))}
925
+ window.__ROUTING__ = ${JSON.stringify(this.clientRoutes)}
762
926
  <\/script>
763
927
 
764
928
  <script type='text/javascript'>
@@ -768,16 +932,22 @@ ${e.stack}</pre>
768
932
  <script type='text/javascript'>
769
933
  window.__INITIAL_DATA__ = ${JSON.stringify(server_data || {}).trim()}
770
934
  <\/script>
935
+
936
+ <script type='text/javascript'>
937
+ window.__COMPONENT_DIR__ = ${JSON.stringify(clientComponentDir)}
938
+ <\/script>
771
939
  `;
940
+ console.log("[SSR DEBUG] About to render HTML with APP length:", (body_render == null ? void 0 : body_render.length) || 0);
772
941
  html_render = await this.htmlRender.renderHTML({
773
942
  HEAD: head_routing + (head_render || ""),
774
943
  CSS: css,
775
944
  APP: body_render
776
945
  });
946
+ console.log("[SSR DEBUG] Final HTML length:", (html_render == null ? void 0 : html_render.length) || 0, "contains APP div:", html_render == null ? void 0 : html_render.includes('<div id="app">'));
777
947
  } catch (e) {
778
948
  response_code = 500;
779
949
  const new_body = `<pre>Error rendering HTML:
780
- ${e}` + (((_a = route.layout) == null ? void 0 : _a.length) ? `layouts: ${(_b = route.layout) == null ? void 0 : _b.join(",\n")}` : "") + `
950
+ ${e}` + (((_b = route.layout) == null ? void 0 : _b.length) ? `layouts: ${(_c = route.layout) == null ? void 0 : _c.join(",\n")}` : "") + `
781
951
  view: ${route.view}</pre>`;
782
952
  html_render = await this.htmlRender.renderHTML({
783
953
  APP: new_body
@@ -789,6 +959,7 @@ view: ${route.view}</pre>`;
789
959
  res.end(html_render);
790
960
  }
791
961
  async handleFallback(req, res, server_config) {
962
+ var _a;
792
963
  if (res.headersSent) {
793
964
  console.log("Reply already sent");
794
965
  return;
@@ -829,7 +1000,10 @@ view: ${route.view}</pre>`;
829
1000
  const view = await this.manager.getView(route);
830
1001
  console.log("Fallback view", view);
831
1002
  console.log("Fallback layouts", layouts);
832
- let { head: head_render, body: body_render, css } = render(ApplicationRenderer, {
1003
+ const loader = ((_a = this.manager) == null ? void 0 : _a["componentLoader"]) || {};
1004
+ const componentRoot = loader["base_path"] || loader["basePath"] || "";
1005
+ const applicationRenderer = await this.loadApplicationRenderer(componentRoot);
1006
+ let { head: head_render, html, css } = render(applicationRenderer, {
833
1007
  props: {
834
1008
  layouts,
835
1009
  view,
@@ -837,6 +1011,7 @@ view: ${route.view}</pre>`;
837
1011
  params: request_data.params
838
1012
  }
839
1013
  });
1014
+ const body_render = html;
840
1015
  const html_render = await this.htmlRender.renderHTML({
841
1016
  HEAD: head_render,
842
1017
  CSS: css,
@@ -845,21 +1020,53 @@ view: ${route.view}</pre>`;
845
1020
  res.type("text/html").status(404).send(html_render);
846
1021
  }
847
1022
  }
1023
+ function pathExistsSync(p) {
1024
+ try {
1025
+ return fs.existsSync(p);
1026
+ } catch {
1027
+ return false;
1028
+ }
1029
+ }
1030
+ function resolveRuntimeRoot(candidate) {
1031
+ var _a;
1032
+ const envRoot = process.env.FORGE_ROOT;
1033
+ if (envRoot && path.isAbsolute(envRoot) && pathExistsSync(envRoot)) {
1034
+ return path.normalize(envRoot);
1035
+ }
1036
+ if (candidate && path.isAbsolute(candidate) && pathExistsSync(candidate)) {
1037
+ return path.normalize(candidate);
1038
+ }
1039
+ const entry = (_a = process.argv) == null ? void 0 : _a[1];
1040
+ if (entry) {
1041
+ const entryDir = path.dirname(path.resolve(entry));
1042
+ if (pathExistsSync(entryDir)) return entryDir;
1043
+ }
1044
+ return process.cwd();
1045
+ }
848
1046
  async function createServer(app, options) {
1047
+ var _a;
849
1048
  options = options || defaultOptions;
850
1049
  const full_options = deepmerge(defaultOptions, options);
851
1050
  full_options.open_api_path = ensureFullPath(full_options.viteOptions.root, full_options.open_api_path);
852
- const isProd = full_options.development === false;
853
- console.log(`Running in ${isProd ? "production" : "development"} mode (development flag: ${full_options.development}, NODE_ENV: ${process.env.NODE_ENV})`);
854
- const COMPONENT_DIR = !full_options.component_dir ? full_options.viteOptions.root : path.join(full_options.viteOptions.root, full_options.component_dir);
855
- const root = full_options.viteOptions.root;
1051
+ const root = resolveRuntimeRoot((_a = full_options.viteOptions) == null ? void 0 : _a.root);
1052
+ const COMPONENT_DIR = !full_options.component_dir ? root : path.join(root, full_options.component_dir);
1053
+ const isBuiltEnvironment = process.env.FORGE_BUILT === "true";
1054
+ const isProd = isBuiltEnvironment || full_options.development === false;
1055
+ console.log(`Running in ${isProd ? "production" : "development"} mode (built environment: ${isBuiltEnvironment}, development flag: ${full_options.development}, NODE_ENV: ${process.env.NODE_ENV})`);
856
1056
  let componentLoader;
857
1057
  let vite;
858
1058
  console.log(`Serving components from ${COMPONENT_DIR}`);
1059
+ const rendererFullPath = typeof full_options.renderer === "string" && full_options.renderer !== "default" ? ensureFullPath(root, full_options.renderer) : ensureFullPath(root, "index.html");
1060
+ path.dirname(rendererFullPath);
1061
+ const resolveAssetRoot = (entry) => {
1062
+ if (!entry || typeof entry !== "string") return root;
1063
+ const cleaned = entry.replace(/^\/+/, "");
1064
+ return path.resolve(root, cleaned);
1065
+ };
859
1066
  if (full_options.assets) {
860
1067
  for (const [asset_path, asset_dir] of Object.entries(full_options.assets)) {
861
1068
  for (const asset of asset_dir) {
862
- const asset_root = path.join(full_options.viteOptions.root, asset);
1069
+ const asset_root = resolveAssetRoot(asset);
863
1070
  app.use(asset_path, express.static(asset_root));
864
1071
  console.log(`Serving assets from ${asset_root} at ${asset_path}`);
865
1072
  }
@@ -876,8 +1083,16 @@ ${JSON.stringify(options, null, 2)}`);
876
1083
  componentLoader = new ViteComponentLoader(COMPONENT_DIR, vite);
877
1084
  } else {
878
1085
  console.log("Starting Vite in production mode...");
879
- app.use("/", express.static(path.join(full_options.viteOptions.root, full_options.build_dir)));
880
- console.log(`Serving static files from ${path.join(full_options.viteOptions.root, full_options.build_dir)} at /`);
1086
+ const staticDir = path.join(full_options.viteOptions.root, full_options.build_dir);
1087
+ if (pathExistsSync(staticDir)) {
1088
+ app.use("/", express.static(staticDir));
1089
+ console.log(`Serving static files from ${staticDir} at /`);
1090
+ } else {
1091
+ console.log(`Skipping static file serving (build_dir ${staticDir} not found - using asset configuration)`);
1092
+ }
1093
+ const componentPath = `/${full_options.component_dir}`;
1094
+ app.use(componentPath, express.static(COMPONENT_DIR));
1095
+ console.log(`Serving components at ${componentPath} from ${COMPONENT_DIR}`);
881
1096
  componentLoader = new ProdComponentLoader(COMPONENT_DIR);
882
1097
  }
883
1098
  const manifest = await new ManifestBuilder(
@@ -902,7 +1117,8 @@ ${JSON.stringify(options, null, 2)}`);
902
1117
  api_adapter,
903
1118
  middleware_adapter,
904
1119
  full_options.context_builder,
905
- isProd
1120
+ isProd,
1121
+ full_options.component_dir
906
1122
  );
907
1123
  await adapter.handleRoutes(routeDefs, manifest);
908
1124
  console.log("Routes registered");