@constela/start 1.2.18 → 1.2.20

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.
@@ -2,7 +2,7 @@ import {
2
2
  generateHydrationScript,
3
3
  renderPage,
4
4
  wrapHtml
5
- } from "./chunk-PUTC5BCP.js";
5
+ } from "./chunk-LIHJUGSX.js";
6
6
 
7
7
  // src/router/file-router.ts
8
8
  import fg from "fast-glob";
@@ -2782,7 +2782,7 @@ async function processLayouts(pageInfo, layoutsDir, routeParams = {}) {
2782
2782
  };
2783
2783
  return updatedPageInfo;
2784
2784
  }
2785
- async function renderPageToHtml(program, params, runtimePath, cssPath) {
2785
+ async function renderPageToHtml(program, params, runtimePath, cssPath, externalImports) {
2786
2786
  const normalizedProgram = {
2787
2787
  ...program,
2788
2788
  view: normalizeViewNode(structuredClone(program.view))
@@ -2800,7 +2800,24 @@ async function renderPageToHtml(program, params, runtimePath, cssPath) {
2800
2800
  };
2801
2801
  const cssLinkTag = cssPath ? `<link rel="stylesheet" href="${cssPath}">` : void 0;
2802
2802
  const hydrationScript = generateHydrationScript(normalizedProgram, void 0, routeContext);
2803
- return wrapHtml(content, hydrationScript, cssLinkTag, runtimePath ? { runtimePath } : void 0);
2803
+ const wrapOptions = {};
2804
+ if (runtimePath) {
2805
+ wrapOptions.runtimePath = runtimePath;
2806
+ }
2807
+ if (externalImports && Object.keys(externalImports).length > 0) {
2808
+ wrapOptions.importMap = externalImports;
2809
+ }
2810
+ const themeState = program.state?.["theme"];
2811
+ if (themeState?.initial) {
2812
+ wrapOptions.defaultTheme = themeState.initial;
2813
+ wrapOptions.themeStorageKey = "theme";
2814
+ }
2815
+ return wrapHtml(
2816
+ content,
2817
+ hydrationScript,
2818
+ cssLinkTag,
2819
+ Object.keys(wrapOptions).length > 0 ? wrapOptions : void 0
2820
+ );
2804
2821
  }
2805
2822
  async function copyPublicDir(publicDir, outDir, generatedFiles) {
2806
2823
  if (!existsSync8(publicDir)) {
@@ -2911,13 +2928,12 @@ async function build2(options) {
2911
2928
  let boundPageInfo = pageInfo;
2912
2929
  if (pathEntry.data && pageInfo.page.getStaticPaths?.source) {
2913
2930
  const source = pageInfo.page.getStaticPaths.source;
2914
- const sourceName = typeof source === "string" ? source : source.name;
2915
- if (sourceName) {
2931
+ if (typeof source === "string") {
2916
2932
  boundPageInfo = {
2917
2933
  ...pageInfo,
2918
2934
  loadedData: {
2919
2935
  ...pageInfo.loadedData,
2920
- [sourceName]: pathEntry.data
2936
+ [source]: pathEntry.data
2921
2937
  // Replace array with current item
2922
2938
  }
2923
2939
  };
@@ -2928,10 +2944,19 @@ async function build2(options) {
2928
2944
  processedPageInfo = await processLayouts(boundPageInfo, layoutsDir, params);
2929
2945
  }
2930
2946
  const program = await convertToCompiledProgram(processedPageInfo);
2931
- const html = await renderPageToHtml(program, params, runtimePath, cssPath);
2947
+ const html = await renderPageToHtml(program, params, runtimePath, cssPath, processedPageInfo.page.externalImports);
2932
2948
  await mkdir2(dirname5(outputPath), { recursive: true });
2933
2949
  await writeFile(outputPath, html, "utf-8");
2934
2950
  generatedFiles.push(outputPath);
2951
+ const slugValue = params["slug"];
2952
+ if (slugValue && (slugValue === "index" || slugValue.endsWith("/index"))) {
2953
+ const parentOutputPath = join9(dirname5(dirname5(outputPath)), "index.html");
2954
+ if (!generatedFiles.includes(parentOutputPath)) {
2955
+ await mkdir2(dirname5(parentOutputPath), { recursive: true });
2956
+ await writeFile(parentOutputPath, html, "utf-8");
2957
+ generatedFiles.push(parentOutputPath);
2958
+ }
2959
+ }
2935
2960
  let routePath = route.pattern;
2936
2961
  for (const [key, value] of Object.entries(params)) {
2937
2962
  routePath = routePath.replace(`:${key}`, value);
@@ -2958,7 +2983,7 @@ async function build2(options) {
2958
2983
  pageInfo = await processLayouts(pageInfo, layoutsDir, {});
2959
2984
  }
2960
2985
  const program = await convertToCompiledProgram(pageInfo);
2961
- const html = await renderPageToHtml(program, {}, runtimePath, cssPath);
2986
+ const html = await renderPageToHtml(program, {}, runtimePath, cssPath, pageInfo.page.externalImports);
2962
2987
  await mkdir2(dirname5(outputPath), { recursive: true });
2963
2988
  await writeFile(outputPath, html, "utf-8");
2964
2989
  generatedFiles.push(outputPath);
@@ -71,7 +71,7 @@ ${routeDeclaration ? "\n" + routeDeclaration : ""}${widgetDeclarations ? "\n" +
71
71
  hydrateApp(${hydrateOptions});${widgetMounting}`;
72
72
  }
73
73
  function wrapHtml(content, hydrationScript, head, options) {
74
- const htmlClass = options?.theme === "dark" ? ' class="dark"' : "";
74
+ const htmlClass = options?.defaultTheme === "dark" || options?.theme === "dark" ? ' class="dark"' : "";
75
75
  let processedScript = hydrationScript;
76
76
  let importMapScript = "";
77
77
  if (options?.runtimePath) {
@@ -82,11 +82,37 @@ function wrapHtml(content, hydrationScript, head, options) {
82
82
  /from\s+['"]@constela\/runtime['"]/g,
83
83
  `from '${options.runtimePath}'`
84
84
  );
85
+ if (options?.importMap && Object.keys(options.importMap).length > 0) {
86
+ const importMapJson = JSON.stringify({ imports: options.importMap }, null, 2);
87
+ importMapScript = `<script type="importmap">
88
+ ${importMapJson}
89
+ </script>
90
+ `;
91
+ }
85
92
  } else if (options?.importMap && Object.keys(options.importMap).length > 0) {
86
93
  const importMapJson = JSON.stringify({ imports: options.importMap }, null, 2);
87
94
  importMapScript = `<script type="importmap">
88
95
  ${importMapJson}
89
96
  </script>
97
+ `;
98
+ }
99
+ let themeScript = "";
100
+ if (options?.themeStorageKey) {
101
+ if (!/^[a-zA-Z0-9_-]+$/.test(options.themeStorageKey)) {
102
+ throw new Error(`Invalid themeStorageKey: ${options.themeStorageKey}. Only alphanumeric characters, underscores, and hyphens are allowed.`);
103
+ }
104
+ themeScript = `<script>
105
+ (function() {
106
+ try {
107
+ var theme = localStorage.getItem('${options.themeStorageKey}');
108
+ if (theme === 'dark') {
109
+ document.documentElement.classList.add('dark');
110
+ } else if (theme === 'light') {
111
+ document.documentElement.classList.remove('dark');
112
+ }
113
+ } catch (e) {}
114
+ })();
115
+ </script>
90
116
  `;
91
117
  }
92
118
  return `<!DOCTYPE html>
@@ -94,7 +120,7 @@ ${importMapJson}
94
120
  <head>
95
121
  <meta charset="utf-8">
96
122
  <meta name="viewport" content="width=device-width, initial-scale=1">
97
- ${importMapScript}${head ?? ""}
123
+ ${themeScript}${importMapScript}${head ?? ""}
98
124
  </head>
99
125
  <body>
100
126
  <div id="app">${content}</div>
package/dist/cli/index.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  createDevServer,
4
4
  loadConfig,
5
5
  resolveConfig
6
- } from "../chunk-XOQABZYW.js";
7
- import "../chunk-PUTC5BCP.js";
6
+ } from "../chunk-AXCIL6BP.js";
7
+ import "../chunk-LIHJUGSX.js";
8
8
 
9
9
  // src/cli/index.ts
10
10
  import { Command } from "commander";
package/dist/index.js CHANGED
@@ -23,12 +23,12 @@ import {
23
23
  transformCsv,
24
24
  transformMdx,
25
25
  transformYaml
26
- } from "./chunk-XOQABZYW.js";
26
+ } from "./chunk-AXCIL6BP.js";
27
27
  import {
28
28
  generateHydrationScript,
29
29
  renderPage,
30
30
  wrapHtml
31
- } from "./chunk-PUTC5BCP.js";
31
+ } from "./chunk-LIHJUGSX.js";
32
32
 
33
33
  // src/build/ssg.ts
34
34
  import { mkdir, writeFile } from "fs/promises";
@@ -112,7 +112,18 @@ async function generateSinglePage(pattern, outDir, program, params = {}) {
112
112
  path: pattern
113
113
  };
114
114
  const hydrationScript = generateHydrationScript(program, void 0, routeContext);
115
- const html = wrapHtml(content, hydrationScript);
115
+ const wrapOptions = {};
116
+ const themeState = program.state?.["theme"];
117
+ if (themeState?.initial) {
118
+ wrapOptions.defaultTheme = themeState.initial;
119
+ wrapOptions.themeStorageKey = "theme";
120
+ }
121
+ const html = wrapHtml(
122
+ content,
123
+ hydrationScript,
124
+ void 0,
125
+ Object.keys(wrapOptions).length > 0 ? wrapOptions : void 0
126
+ );
116
127
  await writeFile(outputPath, html, "utf-8");
117
128
  return outputPath;
118
129
  }
@@ -16,6 +16,10 @@ interface WrapHtmlOptions {
16
16
  importMap?: Record<string, string>;
17
17
  /** Path to bundled runtime for production builds. When provided, replaces @constela/runtime imports and excludes importmap. */
18
18
  runtimePath?: string;
19
+ /** localStorage key for theme persistence. When provided, generates anti-flash script. */
20
+ themeStorageKey?: string;
21
+ /** Default theme to use when no stored preference exists */
22
+ defaultTheme?: 'dark' | 'light';
19
23
  }
20
24
  interface WidgetConfig {
21
25
  /** The DOM element ID where the widget should be mounted */
@@ -2,7 +2,7 @@ import {
2
2
  generateHydrationScript,
3
3
  renderPage,
4
4
  wrapHtml
5
- } from "../chunk-PUTC5BCP.js";
5
+ } from "../chunk-LIHJUGSX.js";
6
6
  export {
7
7
  generateHydrationScript,
8
8
  renderPage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/start",
3
- "version": "1.2.18",
3
+ "version": "1.2.20",
4
4
  "description": "Meta-framework for Constela applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -43,11 +43,11 @@
43
43
  "postcss": "^8.5.0",
44
44
  "@tailwindcss/postcss": "^4.0.0",
45
45
  "tailwindcss": "^4.0.0",
46
- "@constela/router": "8.0.0",
46
+ "@constela/compiler": "0.7.1",
47
47
  "@constela/core": "0.7.0",
48
48
  "@constela/runtime": "0.10.2",
49
- "@constela/compiler": "0.7.1",
50
- "@constela/server": "3.0.1"
49
+ "@constela/server": "3.0.1",
50
+ "@constela/router": "8.0.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/mdast": "^4.0.4",