@constela/start 1.7.0 → 1.8.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.
@@ -90,8 +90,8 @@ hydrateApp(${hydrateOptions});${widgetMounting}`;
90
90
  function wrapHtml(content, hydrationScript, head, options) {
91
91
  let langAttr = "";
92
92
  if (options?.lang) {
93
- if (!/^[a-zA-Z]{2,3}(-[a-zA-Z]{4})?(-[a-zA-Z]{2}|-[0-9]{3})?$/.test(options.lang)) {
94
- throw new Error(`Invalid lang: ${options.lang}. Expected BCP 47 language tag (e.g., 'en', 'ja', 'en-US', 'zh-Hans-CN').`);
93
+ if (!/^([a-zA-Z]{2,3}|i)(-[a-zA-Z0-9]{1,8})*$/.test(options.lang)) {
94
+ throw new Error(`Invalid lang: ${options.lang}. Expected BCP 47 language tag (e.g., 'en', 'ja', 'en-US', 'zh-Hans-CN', 'zh-cmn-Hans', 'de-DE-u-co-phonebk').`);
95
95
  }
96
96
  langAttr = ` lang="${options.lang}"`;
97
97
  }
@@ -217,6 +217,18 @@ function evaluateJsonLdExpression(expr, ctx) {
217
217
  return "";
218
218
  case "concat":
219
219
  return expr.items.map((item) => String(evaluateJsonLdExpression(item, ctx))).join("");
220
+ case "object": {
221
+ const obj = {};
222
+ if (expr.type) {
223
+ obj["@type"] = expr.type;
224
+ }
225
+ for (const [key, propExpr] of Object.entries(expr.properties)) {
226
+ obj[key] = evaluateJsonLdExpression(propExpr, ctx);
227
+ }
228
+ return obj;
229
+ }
230
+ case "array":
231
+ return expr.items.map((item) => evaluateJsonLdExpression(item, ctx));
220
232
  default:
221
233
  return "";
222
234
  }
@@ -3,7 +3,7 @@ import {
3
3
  generateMetaTags,
4
4
  renderPage,
5
5
  wrapHtml
6
- } from "./chunk-IGSM7NWL.js";
6
+ } from "./chunk-3GFRJEKD.js";
7
7
 
8
8
  // src/router/file-router.ts
9
9
  import fg from "fast-glob";
@@ -2104,9 +2104,19 @@ async function convertToCompiledProgram(pageInfo) {
2104
2104
  if (page.route.meta) {
2105
2105
  program.route.meta = {};
2106
2106
  for (const [key, value] of Object.entries(page.route.meta)) {
2107
- program.route.meta[key] = { expr: "lit", value };
2107
+ const isExpression = value && typeof value === "object" && "expr" in value;
2108
+ program.route.meta[key] = isExpression ? value : { expr: "lit", value };
2108
2109
  }
2109
2110
  }
2111
+ if (page.route.title) {
2112
+ program.route.title = page.route.title;
2113
+ }
2114
+ if (page.route.canonical) {
2115
+ program.route.canonical = page.route.canonical;
2116
+ }
2117
+ if (page.route.jsonLd) {
2118
+ program.route.jsonLd = page.route.jsonLd;
2119
+ }
2110
2120
  }
2111
2121
  const hasImports = Object.keys(resolvedImports).length > 0;
2112
2122
  const hasData = Object.keys(loadedData).length > 0;
@@ -2652,7 +2662,8 @@ async function createDevServer(options = {}) {
2652
2662
  defaultTheme: initialTheme,
2653
2663
  themeStorageKey: "theme"
2654
2664
  } : {},
2655
- importMap
2665
+ importMap,
2666
+ ...options.seo?.lang ? { lang: options.seo.lang } : {}
2656
2667
  });
2657
2668
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
2658
2669
  res.end(html);
@@ -2793,25 +2804,69 @@ h1 { color: #666; }
2793
2804
  return devServer;
2794
2805
  }
2795
2806
 
2807
+ // src/config/config-loader.ts
2808
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
2809
+ import { join as join9 } from "path";
2810
+ var CONFIG_FILENAME = "constela.config.json";
2811
+ async function loadConfig(projectRoot) {
2812
+ const configPath = join9(projectRoot, CONFIG_FILENAME);
2813
+ if (!existsSync7(configPath)) {
2814
+ return {};
2815
+ }
2816
+ let content;
2817
+ try {
2818
+ content = readFileSync5(configPath, "utf-8");
2819
+ } catch (error) {
2820
+ throw new Error(`Failed to read config file: ${configPath}`);
2821
+ }
2822
+ try {
2823
+ return JSON.parse(content);
2824
+ } catch {
2825
+ throw new Error(`Invalid JSON in config file: ${configPath}`);
2826
+ }
2827
+ }
2828
+ async function resolveConfig(fileConfig, cliOptions) {
2829
+ if (!cliOptions) {
2830
+ return { ...fileConfig };
2831
+ }
2832
+ const result = { ...fileConfig };
2833
+ if (cliOptions.css !== void 0) result.css = cliOptions.css;
2834
+ if (cliOptions.cssContent !== void 0) {
2835
+ result.cssContent = cliOptions.cssContent.split(",").map((s) => s.trim()).filter(Boolean);
2836
+ }
2837
+ if (cliOptions.layoutsDir !== void 0) result.layoutsDir = cliOptions.layoutsDir;
2838
+ if (cliOptions.routesDir !== void 0) result.routesDir = cliOptions.routesDir;
2839
+ if (cliOptions.publicDir !== void 0) result.publicDir = cliOptions.publicDir;
2840
+ if (cliOptions.outDir !== void 0) {
2841
+ result.build = { ...result.build, outDir: cliOptions.outDir };
2842
+ }
2843
+ if (cliOptions.port !== void 0 || cliOptions.host !== void 0) {
2844
+ result.dev = { ...result.dev };
2845
+ if (cliOptions.port !== void 0) result.dev.port = cliOptions.port;
2846
+ if (cliOptions.host !== void 0) result.dev.host = cliOptions.host;
2847
+ }
2848
+ return result;
2849
+ }
2850
+
2796
2851
  // src/build/index.ts
2797
- import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
2852
+ import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
2798
2853
  import { mkdir as mkdir2, writeFile, cp, readdir } from "fs/promises";
2799
- import { join as join10, dirname as dirname5, relative as relative5, basename as basename4, isAbsolute as isAbsolute3, resolve as resolve4 } from "path";
2854
+ import { join as join11, dirname as dirname5, relative as relative5, basename as basename4, isAbsolute as isAbsolute3, resolve as resolve4 } from "path";
2800
2855
  import { isCookieInitialExpr as isCookieInitialExpr3 } from "@constela/core";
2801
2856
 
2802
2857
  // src/build/bundler.ts
2803
2858
  import * as esbuild from "esbuild";
2804
- import { existsSync as existsSync7 } from "fs";
2859
+ import { existsSync as existsSync8 } from "fs";
2805
2860
  import { mkdir, readFile } from "fs/promises";
2806
2861
  import { createRequire } from "module";
2807
- import { join as join9, dirname as dirname4, isAbsolute as isAbsolute2, relative as relative4 } from "path";
2862
+ import { join as join10, dirname as dirname4, isAbsolute as isAbsolute2, relative as relative4 } from "path";
2808
2863
  import { fileURLToPath } from "url";
2809
2864
  var __dirname = dirname4(fileURLToPath(import.meta.url));
2810
2865
  async function bundleRuntime(options) {
2811
2866
  const entryContent = `
2812
2867
  export { hydrateApp, createApp } from '@constela/runtime';
2813
2868
  `;
2814
- const outFile = join9(options.outDir, "_constela", "runtime.js");
2869
+ const outFile = join10(options.outDir, "_constela", "runtime.js");
2815
2870
  await mkdir(dirname4(outFile), { recursive: true });
2816
2871
  try {
2817
2872
  await esbuild.build({
@@ -2836,12 +2891,12 @@ async function bundleRuntime(options) {
2836
2891
  }
2837
2892
  async function bundleCSS(options) {
2838
2893
  const cssFiles = Array.isArray(options.css) ? options.css : [options.css];
2839
- const outFile = join9(options.outDir, "_constela", "styles.css");
2894
+ const outFile = join10(options.outDir, "_constela", "styles.css");
2840
2895
  const shouldMinify = options.minify ?? true;
2841
2896
  await mkdir(dirname4(outFile), { recursive: true });
2842
- const resolvedCssFiles = cssFiles.map((f) => isAbsolute2(f) ? f : join9(process.cwd(), f));
2897
+ const resolvedCssFiles = cssFiles.map((f) => isAbsolute2(f) ? f : join10(process.cwd(), f));
2843
2898
  for (const fullPath of resolvedCssFiles) {
2844
- if (!existsSync7(fullPath)) {
2899
+ if (!existsSync8(fullPath)) {
2845
2900
  throw new Error(`CSS file not found: ${fullPath}`);
2846
2901
  }
2847
2902
  }
@@ -2868,7 +2923,7 @@ async function bundleCSS(options) {
2868
2923
  if (options.content.length > 0) {
2869
2924
  const fg4 = (await import("fast-glob")).default;
2870
2925
  const resolvedContentPaths = options.content.map(
2871
- (p) => isAbsolute2(p) ? p : join9(process.cwd(), p)
2926
+ (p) => isAbsolute2(p) ? p : join10(process.cwd(), p)
2872
2927
  );
2873
2928
  const matchedFiles = await fg4(resolvedContentPaths, { onlyFiles: true });
2874
2929
  if (matchedFiles.length === 0) {
@@ -2879,7 +2934,7 @@ async function bundleCSS(options) {
2879
2934
  }
2880
2935
  const sourceDir = dirname4(firstCssFile);
2881
2936
  const sourceDirectives = options.content.map((contentPath) => {
2882
- const absolutePath = isAbsolute2(contentPath) ? contentPath : join9(process.cwd(), contentPath);
2937
+ const absolutePath = isAbsolute2(contentPath) ? contentPath : join10(process.cwd(), contentPath);
2883
2938
  const srcPath = absolutePath.includes("*") ? dirname4(absolutePath.split("*")[0] ?? absolutePath) : absolutePath;
2884
2939
  const relativePath = relative4(sourceDir, srcPath);
2885
2940
  return `@source "${relativePath}";`;
@@ -2961,9 +3016,9 @@ function isDynamicRoute(pattern) {
2961
3016
  function getOutputPath(filePath, outDir) {
2962
3017
  const withoutExt = filePath.replace(/\.(json|ts|tsx|js|jsx)$/, "");
2963
3018
  if (withoutExt === "index" || withoutExt.endsWith("/index")) {
2964
- return join10(outDir, withoutExt + ".html");
3019
+ return join11(outDir, withoutExt + ".html");
2965
3020
  }
2966
- return join10(outDir, withoutExt, "index.html");
3021
+ return join11(outDir, withoutExt, "index.html");
2967
3022
  }
2968
3023
  function paramsToOutputPath(basePattern, params, outDir) {
2969
3024
  let path = basePattern;
@@ -2973,19 +3028,19 @@ function paramsToOutputPath(basePattern, params, outDir) {
2973
3028
  }
2974
3029
  const relativePath = path.startsWith("/") ? path.slice(1) : path;
2975
3030
  if (relativePath === "") {
2976
- return join10(outDir, "index.html");
3031
+ return join11(outDir, "index.html");
2977
3032
  }
2978
- return join10(outDir, relativePath, "index.html");
3033
+ return join11(outDir, relativePath, "index.html");
2979
3034
  }
2980
3035
  async function loadGetStaticPaths(pageFile) {
2981
3036
  const dir = dirname5(pageFile);
2982
3037
  const baseName = basename4(pageFile, ".json");
2983
- const pathsFile = join10(dir, `${baseName}.paths.ts`);
2984
- if (!existsSync8(pathsFile)) {
3038
+ const pathsFile = join11(dir, `${baseName}.paths.ts`);
3039
+ if (!existsSync9(pathsFile)) {
2985
3040
  return null;
2986
3041
  }
2987
3042
  try {
2988
- const content = readFileSync5(pathsFile, "utf-8");
3043
+ const content = readFileSync6(pathsFile, "utf-8");
2989
3044
  const pathsMatch = content.match(/paths:\s*\[([\s\S]*?)\]/);
2990
3045
  if (!pathsMatch) {
2991
3046
  throw new Error(`Invalid getStaticPaths format in ${pathsFile}`);
@@ -3021,12 +3076,12 @@ async function loadGetStaticPaths(pageFile) {
3021
3076
  }
3022
3077
  }
3023
3078
  async function loadLayout2(layoutName, layoutsDir) {
3024
- const layoutPath = join10(layoutsDir, `${layoutName}.json`);
3025
- if (!existsSync8(layoutPath)) {
3079
+ const layoutPath = join11(layoutsDir, `${layoutName}.json`);
3080
+ if (!existsSync9(layoutPath)) {
3026
3081
  throw new Error(`Layout "${layoutName}" not found at ${layoutPath}`);
3027
3082
  }
3028
3083
  try {
3029
- const content = readFileSync5(layoutPath, "utf-8");
3084
+ const content = readFileSync6(layoutPath, "utf-8");
3030
3085
  const parsed = JSON.parse(content);
3031
3086
  if (parsed.imports && Object.keys(parsed.imports).length > 0) {
3032
3087
  const layoutDir = dirname5(layoutPath);
@@ -3361,7 +3416,7 @@ async function processLayouts(pageInfo, layoutsDir, routeParams = {}) {
3361
3416
  };
3362
3417
  return updatedPageInfo;
3363
3418
  }
3364
- async function renderPageToHtml(program, params, runtimePath, cssPath, externalImports, widgets) {
3419
+ async function renderPageToHtml(program, params, runtimePath, cssPath, externalImports, widgets, lang) {
3365
3420
  const normalizedProgram = {
3366
3421
  ...program,
3367
3422
  view: normalizeViewNode(structuredClone(program.view))
@@ -3393,6 +3448,9 @@ async function renderPageToHtml(program, params, runtimePath, cssPath, externalI
3393
3448
  if (externalImports && Object.keys(externalImports).length > 0) {
3394
3449
  wrapOptions.importMap = externalImports;
3395
3450
  }
3451
+ if (lang) {
3452
+ wrapOptions.lang = lang;
3453
+ }
3396
3454
  const themeState = program.state?.["theme"];
3397
3455
  if (themeState) {
3398
3456
  let defaultTheme;
@@ -3414,7 +3472,7 @@ async function renderPageToHtml(program, params, runtimePath, cssPath, externalI
3414
3472
  );
3415
3473
  }
3416
3474
  async function copyPublicDir(publicDir, outDir, generatedFiles) {
3417
- if (!existsSync8(publicDir)) {
3475
+ if (!existsSync9(publicDir)) {
3418
3476
  return;
3419
3477
  }
3420
3478
  await copyDirRecursive(publicDir, outDir, generatedFiles);
@@ -3422,8 +3480,8 @@ async function copyPublicDir(publicDir, outDir, generatedFiles) {
3422
3480
  async function copyDirRecursive(srcDir, destDir, skipFiles) {
3423
3481
  const entries = await readdir(srcDir, { withFileTypes: true });
3424
3482
  for (const entry of entries) {
3425
- const srcPath = join10(srcDir, entry.name);
3426
- const destPath = join10(destDir, entry.name);
3483
+ const srcPath = join11(srcDir, entry.name);
3484
+ const destPath = join11(destDir, entry.name);
3427
3485
  if (entry.isDirectory()) {
3428
3486
  await mkdir2(destPath, { recursive: true });
3429
3487
  await copyDirRecursive(srcPath, destPath, skipFiles);
@@ -3492,10 +3550,12 @@ async function build2(options) {
3492
3550
  }
3493
3551
  const absoluteRoutesDir = isAbsolute3(routesDir) ? routesDir : resolve4(routesDir);
3494
3552
  const projectRoot = dirname5(dirname5(absoluteRoutesDir));
3553
+ const config = await loadConfig(projectRoot);
3554
+ const seoLang = options?.seo?.lang ?? config.seo?.lang;
3495
3555
  for (const route of jsonPages) {
3496
3556
  const relPathFromRoutesDir = relative5(absoluteRoutesDir, route.file);
3497
3557
  const relPathFromProjectRoot = relative5(projectRoot, route.file);
3498
- const content = readFileSync5(route.file, "utf-8");
3558
+ const content = readFileSync6(route.file, "utf-8");
3499
3559
  const page = validateJsonPage(content, route.file);
3500
3560
  if (isDynamicRoute(route.pattern)) {
3501
3561
  const loader = new JsonPageLoader(projectRoot, { routesDir: absoluteRoutesDir });
@@ -3538,13 +3598,13 @@ async function build2(options) {
3538
3598
  processedPageInfo = await processLayouts(boundPageInfo, layoutsDir, params);
3539
3599
  }
3540
3600
  const program = await convertToCompiledProgram(processedPageInfo);
3541
- const html = await renderPageToHtml(program, params, runtimePath, cssPath, processedPageInfo.page.externalImports, processedPageInfo.widgets);
3601
+ const html = await renderPageToHtml(program, params, runtimePath, cssPath, processedPageInfo.page.externalImports, processedPageInfo.widgets, seoLang);
3542
3602
  await mkdir2(dirname5(outputPath), { recursive: true });
3543
3603
  await writeFile(outputPath, html, "utf-8");
3544
3604
  generatedFiles.push(outputPath);
3545
3605
  const slugValue = params["slug"];
3546
3606
  if (slugValue && (slugValue === "index" || slugValue.endsWith("/index"))) {
3547
- const parentOutputPath = join10(dirname5(dirname5(outputPath)), "index.html");
3607
+ const parentOutputPath = join11(dirname5(dirname5(outputPath)), "index.html");
3548
3608
  if (!generatedFiles.includes(parentOutputPath)) {
3549
3609
  await mkdir2(dirname5(parentOutputPath), { recursive: true });
3550
3610
  await writeFile(parentOutputPath, html, "utf-8");
@@ -3566,9 +3626,9 @@ async function build2(options) {
3566
3626
  const routePath = pageInfo.page.route.path;
3567
3627
  const relativePath = routePath.startsWith("/") ? routePath.slice(1) : routePath;
3568
3628
  if (relativePath === "" || relativePath === "/") {
3569
- outputPath = join10(outDir, "index.html");
3629
+ outputPath = join11(outDir, "index.html");
3570
3630
  } else {
3571
- outputPath = join10(outDir, relativePath, "index.html");
3631
+ outputPath = join11(outDir, relativePath, "index.html");
3572
3632
  }
3573
3633
  } else {
3574
3634
  outputPath = getOutputPath(relPathFromRoutesDir, outDir);
@@ -3577,7 +3637,7 @@ async function build2(options) {
3577
3637
  pageInfo = await processLayouts(pageInfo, layoutsDir, {});
3578
3638
  }
3579
3639
  const program = await convertToCompiledProgram(pageInfo);
3580
- const html = await renderPageToHtml(program, {}, runtimePath, cssPath, pageInfo.page.externalImports, pageInfo.widgets);
3640
+ const html = await renderPageToHtml(program, {}, runtimePath, cssPath, pageInfo.page.externalImports, pageInfo.widgets, seoLang);
3581
3641
  await mkdir2(dirname5(outputPath), { recursive: true });
3582
3642
  await writeFile(outputPath, html, "utf-8");
3583
3643
  generatedFiles.push(outputPath);
@@ -3594,50 +3654,6 @@ async function build2(options) {
3594
3654
  };
3595
3655
  }
3596
3656
 
3597
- // src/config/config-loader.ts
3598
- import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
3599
- import { join as join11 } from "path";
3600
- var CONFIG_FILENAME = "constela.config.json";
3601
- async function loadConfig(projectRoot) {
3602
- const configPath = join11(projectRoot, CONFIG_FILENAME);
3603
- if (!existsSync9(configPath)) {
3604
- return {};
3605
- }
3606
- let content;
3607
- try {
3608
- content = readFileSync6(configPath, "utf-8");
3609
- } catch (error) {
3610
- throw new Error(`Failed to read config file: ${configPath}`);
3611
- }
3612
- try {
3613
- return JSON.parse(content);
3614
- } catch {
3615
- throw new Error(`Invalid JSON in config file: ${configPath}`);
3616
- }
3617
- }
3618
- async function resolveConfig(fileConfig, cliOptions) {
3619
- if (!cliOptions) {
3620
- return { ...fileConfig };
3621
- }
3622
- const result = { ...fileConfig };
3623
- if (cliOptions.css !== void 0) result.css = cliOptions.css;
3624
- if (cliOptions.cssContent !== void 0) {
3625
- result.cssContent = cliOptions.cssContent.split(",").map((s) => s.trim()).filter(Boolean);
3626
- }
3627
- if (cliOptions.layoutsDir !== void 0) result.layoutsDir = cliOptions.layoutsDir;
3628
- if (cliOptions.routesDir !== void 0) result.routesDir = cliOptions.routesDir;
3629
- if (cliOptions.publicDir !== void 0) result.publicDir = cliOptions.publicDir;
3630
- if (cliOptions.outDir !== void 0) {
3631
- result.build = { ...result.build, outDir: cliOptions.outDir };
3632
- }
3633
- if (cliOptions.port !== void 0 || cliOptions.host !== void 0) {
3634
- result.dev = { ...result.dev };
3635
- if (cliOptions.port !== void 0) result.dev.port = cliOptions.port;
3636
- if (cliOptions.host !== void 0) result.dev.host = cliOptions.host;
3637
- }
3638
- return result;
3639
- }
3640
-
3641
3657
  // src/utils/terminal.ts
3642
3658
  function hyperlink(url, text) {
3643
3659
  const displayText = text ?? url;
@@ -3673,8 +3689,8 @@ export {
3673
3689
  loadLayout,
3674
3690
  LayoutResolver,
3675
3691
  createDevServer,
3676
- build2 as build,
3677
3692
  loadConfig,
3678
3693
  resolveConfig,
3694
+ build2 as build,
3679
3695
  hyperlink
3680
3696
  };
package/dist/cli/index.js CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  hyperlink,
5
5
  loadConfig,
6
6
  resolveConfig
7
- } from "../chunk-7OAUINHU.js";
8
- import "../chunk-IGSM7NWL.js";
7
+ } from "../chunk-TXTTXC4B.js";
8
+ import "../chunk-3GFRJEKD.js";
9
9
 
10
10
  // src/cli/index.ts
11
11
  import { Command } from "commander";
package/dist/index.d.ts CHANGED
@@ -93,6 +93,10 @@ interface DevServerOptions {
93
93
  layoutsDir?: string;
94
94
  /** CSS entry point(s) for Vite middleware processing */
95
95
  css?: string | string[];
96
+ /** SEO configuration */
97
+ seo?: {
98
+ lang?: string;
99
+ };
96
100
  }
97
101
  /**
98
102
  * Build options
@@ -107,6 +111,10 @@ interface BuildOptions {
107
111
  /** Content paths for Tailwind CSS class scanning (enables PostCSS processing) */
108
112
  cssContent?: string[] | undefined;
109
113
  target?: 'node' | 'edge' | undefined;
114
+ /** SEO configuration */
115
+ seo?: {
116
+ lang?: string;
117
+ };
110
118
  }
111
119
 
112
120
  /**
@@ -232,6 +240,10 @@ interface GenerateStaticPagesOptions {
232
240
  * Primarily used for testing purposes.
233
241
  */
234
242
  staticPathsProvider?: StaticPathsProvider;
243
+ /**
244
+ * Optional language code for HTML lang attribute.
245
+ */
246
+ lang?: string;
235
247
  }
236
248
  /**
237
249
  * Generate static pages for SSG routes
package/dist/index.js CHANGED
@@ -28,14 +28,14 @@ import {
28
28
  transformCsv,
29
29
  transformMdx,
30
30
  transformYaml
31
- } from "./chunk-7OAUINHU.js";
31
+ } from "./chunk-TXTTXC4B.js";
32
32
  import {
33
33
  evaluateMetaExpression,
34
34
  generateHydrationScript,
35
35
  generateMetaTags,
36
36
  renderPage,
37
37
  wrapHtml
38
- } from "./chunk-IGSM7NWL.js";
38
+ } from "./chunk-3GFRJEKD.js";
39
39
 
40
40
  // src/build/ssg.ts
41
41
  import { mkdir, writeFile } from "fs/promises";
@@ -84,7 +84,7 @@ async function getStaticPathsForRoute(route, module, staticPathsProvider) {
84
84
  }
85
85
  return null;
86
86
  }
87
- async function generateSinglePage(pattern, outDir, program, params = {}) {
87
+ async function generateSinglePage(pattern, outDir, program, params = {}, lang) {
88
88
  const outputPath = getOutputPath(pattern, outDir);
89
89
  const outputDir = dirname(outputPath);
90
90
  await mkdir(outputDir, { recursive: true });
@@ -119,6 +119,9 @@ async function generateSinglePage(pattern, outDir, program, params = {}) {
119
119
  wrapOptions.themeStorageKey = "theme";
120
120
  }
121
121
  }
122
+ if (lang) {
123
+ wrapOptions.lang = lang;
124
+ }
122
125
  const html = wrapHtml(
123
126
  content,
124
127
  hydrationScript,
@@ -129,7 +132,7 @@ async function generateSinglePage(pattern, outDir, program, params = {}) {
129
132
  return outputPath;
130
133
  }
131
134
  async function generateStaticPages(routes, outDir, options = {}) {
132
- const { staticPathsProvider } = options;
135
+ const { staticPathsProvider, lang } = options;
133
136
  const generatedPaths = [];
134
137
  const pageRoutes = routes.filter((r) => r.type === "page");
135
138
  for (const route of pageRoutes) {
@@ -154,13 +157,14 @@ async function generateStaticPages(routes, outDir, options = {}) {
154
157
  resolvedPattern,
155
158
  outDir,
156
159
  program,
157
- pathData.params
160
+ pathData.params,
161
+ lang
158
162
  );
159
163
  generatedPaths.push(filePath);
160
164
  }
161
165
  } else {
162
166
  const program = await resolvePageExport(pageExport, {});
163
- const filePath = await generateSinglePage(route.pattern, outDir, program);
167
+ const filePath = await generateSinglePage(route.pattern, outDir, program, {}, lang);
164
168
  generatedPaths.push(filePath);
165
169
  }
166
170
  }
@@ -4,7 +4,7 @@ import {
4
4
  generateMetaTags,
5
5
  renderPage,
6
6
  wrapHtml
7
- } from "../chunk-IGSM7NWL.js";
7
+ } from "../chunk-3GFRJEKD.js";
8
8
  export {
9
9
  evaluateMetaExpression,
10
10
  generateHydrationScript,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/start",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Meta-framework for Constela applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",