@constela/start 1.6.4 → 1.7.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.
@@ -3,7 +3,7 @@ import {
3
3
  generateMetaTags,
4
4
  renderPage,
5
5
  wrapHtml
6
- } from "./chunk-PG3Y4EJN.js";
6
+ } from "./chunk-IGSM7NWL.js";
7
7
 
8
8
  // src/router/file-router.ts
9
9
  import fg from "fast-glob";
@@ -88,6 +88,13 @@ ${routeDeclaration ? "\n" + routeDeclaration : ""}${widgetDeclarations ? "\n" +
88
88
  hydrateApp(${hydrateOptions});${widgetMounting}`;
89
89
  }
90
90
  function wrapHtml(content, hydrationScript, head, options) {
91
+ let langAttr = "";
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').`);
95
+ }
96
+ langAttr = ` lang="${options.lang}"`;
97
+ }
91
98
  const htmlClass = options?.defaultTheme === "dark" || options?.theme === "dark" ? ' class="dark"' : "";
92
99
  let processedScript = hydrationScript;
93
100
  let importMapScript = "";
@@ -149,7 +156,7 @@ ${importMapJson}
149
156
  `;
150
157
  }
151
158
  return `<!DOCTYPE html>
152
- <html${htmlClass}>
159
+ <html${langAttr}${htmlClass}>
153
160
  <head>
154
161
  <meta charset="utf-8">
155
162
  <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -190,6 +197,30 @@ function evaluateMetaExpression(expr, ctx) {
190
197
  return "";
191
198
  }
192
199
  }
200
+ function evaluateJsonLdExpression(expr, ctx) {
201
+ switch (expr.expr) {
202
+ case "lit":
203
+ return expr.value;
204
+ case "route":
205
+ if (expr.source === "param") {
206
+ return ctx.params[expr.name] ?? "";
207
+ } else if (expr.source === "query") {
208
+ return ctx.query[expr.name] ?? "";
209
+ } else if (expr.source === "path") {
210
+ return ctx.path;
211
+ }
212
+ return "";
213
+ case "bin":
214
+ if (expr.op === "+") {
215
+ return String(evaluateJsonLdExpression(expr.left, ctx)) + String(evaluateJsonLdExpression(expr.right, ctx));
216
+ }
217
+ return "";
218
+ case "concat":
219
+ return expr.items.map((item) => String(evaluateJsonLdExpression(item, ctx))).join("");
220
+ default:
221
+ return "";
222
+ }
223
+ }
193
224
  function generateMetaTags(route, ctx) {
194
225
  if (!route) {
195
226
  return "";
@@ -215,6 +246,24 @@ function generateMetaTags(route, ctx) {
215
246
  }
216
247
  }
217
248
  }
249
+ if (route.canonical) {
250
+ const canonicalValue = evaluateMetaExpression(route.canonical, ctx);
251
+ if (canonicalValue) {
252
+ tags.push(`<link rel="canonical" href="${escapeHtmlForMeta(canonicalValue)}">`);
253
+ }
254
+ }
255
+ if (route.jsonLd) {
256
+ const jsonLdObject = {
257
+ "@context": "https://schema.org",
258
+ "@type": route.jsonLd.type
259
+ };
260
+ for (const [key, expr] of Object.entries(route.jsonLd.properties)) {
261
+ const value = evaluateJsonLdExpression(expr, ctx);
262
+ jsonLdObject[key] = value;
263
+ }
264
+ const jsonString = escapeJsonForScript(JSON.stringify(jsonLdObject));
265
+ tags.push(`<script type="application/ld+json">${jsonString}</script>`);
266
+ }
218
267
  return tags.join("\n");
219
268
  }
220
269
 
package/dist/cli/index.js CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  hyperlink,
5
5
  loadConfig,
6
6
  resolveConfig
7
- } from "../chunk-LKFIMB3Q.js";
8
- import "../chunk-PG3Y4EJN.js";
7
+ } from "../chunk-7OAUINHU.js";
8
+ import "../chunk-IGSM7NWL.js";
9
9
 
10
10
  // src/cli/index.ts
11
11
  import { Command } from "commander";
package/dist/index.d.ts CHANGED
@@ -540,6 +540,11 @@ interface ConstelaConfigFile {
540
540
  port?: number;
541
541
  host?: string;
542
542
  };
543
+ /** SEO settings */
544
+ seo?: {
545
+ /** HTML lang attribute (e.g., 'ja', 'en', 'zh-CN') */
546
+ lang?: string;
547
+ };
543
548
  }
544
549
  interface CLIOptions {
545
550
  css?: string | undefined;
package/dist/index.js CHANGED
@@ -28,14 +28,14 @@ import {
28
28
  transformCsv,
29
29
  transformMdx,
30
30
  transformYaml
31
- } from "./chunk-LKFIMB3Q.js";
31
+ } from "./chunk-7OAUINHU.js";
32
32
  import {
33
33
  evaluateMetaExpression,
34
34
  generateHydrationScript,
35
35
  generateMetaTags,
36
36
  renderPage,
37
37
  wrapHtml
38
- } from "./chunk-PG3Y4EJN.js";
38
+ } from "./chunk-IGSM7NWL.js";
39
39
 
40
40
  // src/build/ssg.ts
41
41
  import { mkdir, writeFile } from "fs/promises";
@@ -13,6 +13,8 @@ interface SSRContext {
13
13
  }
14
14
  interface WrapHtmlOptions {
15
15
  theme?: 'dark' | 'light';
16
+ /** HTML lang attribute for SEO */
17
+ lang?: string;
16
18
  /** Import map entries for resolving bare module specifiers */
17
19
  importMap?: Record<string, string>;
18
20
  /** Path to bundled runtime for production builds. When provided, replaces @constela/runtime imports and excludes importmap. */
@@ -4,7 +4,7 @@ import {
4
4
  generateMetaTags,
5
5
  renderPage,
6
6
  wrapHtml
7
- } from "../chunk-PG3Y4EJN.js";
7
+ } from "../chunk-IGSM7NWL.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.6.4",
3
+ "version": "1.7.0",
4
4
  "description": "Meta-framework for Constela applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,11 +44,11 @@
44
44
  "@tailwindcss/postcss": "^4.0.0",
45
45
  "tailwindcss": "^4.0.0",
46
46
  "ws": "^8.18.0",
47
- "@constela/compiler": "0.11.4",
48
- "@constela/router": "14.0.0",
49
- "@constela/runtime": "0.16.4",
50
- "@constela/core": "0.12.3",
51
- "@constela/server": "8.0.1"
47
+ "@constela/compiler": "0.12.0",
48
+ "@constela/core": "0.13.0",
49
+ "@constela/router": "15.0.0",
50
+ "@constela/runtime": "0.16.5",
51
+ "@constela/server": "9.0.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/ws": "^8.5.0",