@stacksjs/stx 0.2.342 → 0.2.344

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 (185) hide show
  1. package/dist/binding-manifest.d.ts +32 -0
  2. package/dist/binding-manifest.js +64 -0
  3. package/dist/caching.js +1 -0
  4. package/dist/chunk-020hpw7m.js +2 -0
  5. package/dist/{chunk-5g9vbhbt.js → chunk-05zfez2g.js} +1 -1
  6. package/dist/{chunk-6nvm939g.js → chunk-0k1mtwby.js} +1 -1
  7. package/dist/chunk-1bct7pfj.js +2 -0
  8. package/dist/{chunk-xec226zb.js → chunk-1c9453w0.js} +1 -1
  9. package/dist/chunk-1exk54qt.js +2 -0
  10. package/dist/{chunk-r46cg4qa.js → chunk-1vt1h19d.js} +1 -1
  11. package/dist/{chunk-cqw3c1xg.js → chunk-1yh6pq6b.js} +1 -1
  12. package/dist/{chunk-0sq5mdrh.js → chunk-34f8c48f.js} +1 -1
  13. package/dist/chunk-39qy76ek.js +2 -0
  14. package/dist/{chunk-hrmxxs0w.js → chunk-3n8avn17.js} +1 -1
  15. package/dist/chunk-3xpk99z4.js +3 -0
  16. package/dist/{chunk-4wpxbkbe.js → chunk-4kbaky4w.js} +1 -1
  17. package/dist/{chunk-c33y19bn.js → chunk-59906baj.js} +1 -1
  18. package/dist/chunk-5apmxb4e.js +2 -0
  19. package/dist/{chunk-j82hfkcf.js → chunk-5atmahe4.js} +319 -321
  20. package/dist/{chunk-mkgbrbr9.js → chunk-5yhv1pe5.js} +1 -1
  21. package/dist/{chunk-p52cm7ch.js → chunk-64n10kpa.js} +1 -1
  22. package/dist/{chunk-anf2y2h5.js → chunk-6h3r8v55.js} +1 -1
  23. package/dist/chunk-6z9pm31g.js +2 -0
  24. package/dist/{chunk-25f0s8t1.js → chunk-72x5v5an.js} +1 -1
  25. package/dist/{chunk-wv8h3d68.js → chunk-72x62kch.js} +2 -2
  26. package/dist/chunk-7d2gfyk8.js +2 -0
  27. package/dist/{chunk-s4mmmwj4.js → chunk-7h9fajgs.js} +1 -1
  28. package/dist/{chunk-gwc54hjc.js → chunk-7kcfbc0j.js} +2 -2
  29. package/dist/{chunk-74r1zw2h.js → chunk-7qp3zphs.js} +1 -1
  30. package/dist/{chunk-1xwfws06.js → chunk-7t6h8crt.js} +2 -2
  31. package/dist/{chunk-sytwy0mg.js → chunk-86c461ca.js} +1 -1
  32. package/dist/chunk-86q2h34b.js +2 -0
  33. package/dist/{chunk-25b8z7xa.js → chunk-89mqnh67.js} +1 -1
  34. package/dist/{chunk-k5b9471y.js → chunk-8cc4vdkh.js} +1 -1
  35. package/dist/chunk-8tjg4wbr.js +2 -0
  36. package/dist/{chunk-1gwbw81h.js → chunk-8v2k21n4.js} +1 -1
  37. package/dist/{chunk-rh7mwwb4.js → chunk-8y1ve8tc.js} +2 -2
  38. package/dist/chunk-94sxzp5x.js +2 -0
  39. package/dist/{chunk-mznr9nyp.js → chunk-99b5tfxc.js} +1 -1
  40. package/dist/{chunk-saqbtgqr.js → chunk-9jbssgem.js} +197 -88
  41. package/dist/{chunk-yr7camcd.js → chunk-9nrgjtah.js} +1 -1
  42. package/dist/chunk-9p5gbhwp.js +2 -0
  43. package/dist/chunk-9yqt0nf2.js +2 -0
  44. package/dist/{chunk-ebd3z4j9.js → chunk-a1ghxrb1.js} +1 -1
  45. package/dist/chunk-a29x23g7.js +2 -0
  46. package/dist/chunk-at622hg0.js +2 -0
  47. package/dist/{chunk-bt8w146r.js → chunk-avwywzsj.js} +2 -2
  48. package/dist/{chunk-m39hb2a2.js → chunk-b5hank5e.js} +1 -1
  49. package/dist/{chunk-ts3wfjh9.js → chunk-bk1zb1xp.js} +1 -1
  50. package/dist/{chunk-ytx17bg2.js → chunk-bpb3xkg4.js} +1 -1
  51. package/dist/{chunk-01ggk8k8.js → chunk-bqdfbwdf.js} +1 -1
  52. package/dist/{chunk-yzv702kk.js → chunk-c30qnb6z.js} +1 -1
  53. package/dist/chunk-cappe4hd.js +2 -0
  54. package/dist/{chunk-drs93sry.js → chunk-cdq28mek.js} +1 -1
  55. package/dist/chunk-cmccdayf.js +2 -0
  56. package/dist/chunk-csv48xrx.js +2 -0
  57. package/dist/{chunk-e0kzyf1g.js → chunk-d86q27j8.js} +1 -1
  58. package/dist/{chunk-yhvrq2qd.js → chunk-dgv43ccd.js} +1 -1
  59. package/dist/{chunk-yq7fc8sa.js → chunk-dsztk2kp.js} +1 -1
  60. package/dist/{chunk-ys56ecvx.js → chunk-dx2s51rr.js} +1 -1
  61. package/dist/{chunk-mbstfjnj.js → chunk-dxkjvajv.js} +1 -1
  62. package/dist/chunk-e4yskgtb.js +2 -0
  63. package/dist/{chunk-8tsr51hj.js → chunk-e631svvs.js} +1 -1
  64. package/dist/{chunk-3b0mgxbk.js → chunk-e7np0h2q.js} +1 -1
  65. package/dist/chunk-efx05ah0.js +2 -0
  66. package/dist/{chunk-p5d8sb50.js → chunk-ejqpq0wc.js} +1 -1
  67. package/dist/chunk-erb294sv.js +2 -0
  68. package/dist/{chunk-k3w3gwm3.js → chunk-evfr2zgg.js} +1 -1
  69. package/dist/chunk-ewpbxy5m.js +2 -0
  70. package/dist/chunk-fs2t4rs3.js +33 -0
  71. package/dist/{chunk-ghd80w8a.js → chunk-g28fs4aa.js} +2 -2
  72. package/dist/chunk-gmm82dst.js +2 -0
  73. package/dist/{chunk-kznb3hdj.js → chunk-gsc2vds7.js} +1 -1
  74. package/dist/chunk-hke0k33t.js +2 -0
  75. package/dist/{chunk-da0tfpa2.js → chunk-hsbbfp0c.js} +1 -1
  76. package/dist/chunk-j5qygdy9.js +2 -0
  77. package/dist/{chunk-c73pyeak.js → chunk-jekrtm2j.js} +1 -1
  78. package/dist/chunk-jzfk5ect.js +2 -0
  79. package/dist/{chunk-rr49zrz6.js → chunk-kfkf9v2d.js} +1 -1
  80. package/dist/chunk-kq30wqp6.js +2 -0
  81. package/dist/{chunk-ntm1761b.js → chunk-krfx5562.js} +10 -10
  82. package/dist/{chunk-48x5bsqv.js → chunk-kw9cgyfc.js} +1 -1
  83. package/dist/{chunk-evtrctj9.js → chunk-m2j5skez.js} +1 -1
  84. package/dist/{chunk-q4cemvkw.js → chunk-mph46gkj.js} +1 -1
  85. package/dist/{chunk-k9ep24bd.js → chunk-mpyjnwje.js} +1 -1
  86. package/dist/{chunk-y956wapy.js → chunk-msywnv5n.js} +1 -1
  87. package/dist/{chunk-d389vyyz.js → chunk-mvat8js5.js} +2 -2
  88. package/dist/{chunk-gkq323fg.js → chunk-mz4breg8.js} +1 -1
  89. package/dist/{chunk-reskzmsb.js → chunk-n5657hm3.js} +1 -1
  90. package/dist/{chunk-4ecv1e3w.js → chunk-nexpxxsa.js} +1 -1
  91. package/dist/{chunk-1t3j8a0q.js → chunk-njx93b4z.js} +1 -1
  92. package/dist/{chunk-fx29n4vc.js → chunk-nm60wh6r.js} +1 -1
  93. package/dist/{chunk-thrv2nbz.js → chunk-ppzz953j.js} +1 -1
  94. package/dist/chunk-q5gzpn1n.js +2 -0
  95. package/dist/{chunk-fm5211ec.js → chunk-rwwfzkz4.js} +1 -1
  96. package/dist/chunk-san1eq0r.js +7 -0
  97. package/dist/chunk-tc4pj651.js +64 -0
  98. package/dist/chunk-tscr6bs8.js +2 -0
  99. package/dist/{chunk-jcbj3xp4.js → chunk-tthrw91n.js} +1 -1
  100. package/dist/{chunk-6nxq8rcw.js → chunk-v6e35j4g.js} +1 -1
  101. package/dist/{chunk-byjy9far.js → chunk-v7aa7v85.js} +1 -1
  102. package/dist/{chunk-r1rfwm01.js → chunk-vhta1be7.js} +1 -1
  103. package/dist/{chunk-hnffkdzc.js → chunk-w5j9tbsk.js} +1 -1
  104. package/dist/{chunk-567g1ttv.js → chunk-wfwj4390.js} +1 -1
  105. package/dist/{chunk-x7dv5rq7.js → chunk-wj21f1e0.js} +1 -1
  106. package/dist/{chunk-tmj45xq5.js → chunk-ws0282jr.js} +1 -1
  107. package/dist/chunk-x054qzma.js +2 -0
  108. package/dist/{chunk-4vcyf1kh.js → chunk-xh9paa2g.js} +2 -2
  109. package/dist/{chunk-f2qbjr3z.js → chunk-xj6bcwqa.js} +1 -1
  110. package/dist/{chunk-hk8j3eps.js → chunk-xvh1k1a4.js} +1 -1
  111. package/dist/{chunk-1spzqjve.js → chunk-y2f41kaq.js} +1 -1
  112. package/dist/{chunk-2swnc8kg.js → chunk-y6xzr30x.js} +1 -1
  113. package/dist/{chunk-beextc84.js → chunk-yg5r7bnm.js} +2 -2
  114. package/dist/{chunk-gbkq5qnw.js → chunk-zkwk82ss.js} +2 -2
  115. package/dist/chunk-zkxnarrh.js +2 -0
  116. package/dist/chunk-zp14ksx1.js +2 -0
  117. package/dist/{chunk-zv4nf0x2.js → chunk-zwhnkxgy.js} +1 -1
  118. package/dist/cli.js +14 -14
  119. package/dist/craft-entry.js +1 -1
  120. package/dist/craft.js +1 -1
  121. package/dist/custom-directives.js +21 -8
  122. package/dist/database.js +1 -1
  123. package/dist/directive-arguments.d.ts +107 -0
  124. package/dist/directive-arguments.js +171 -0
  125. package/dist/expressions.d.ts +1 -1
  126. package/dist/expressions.js +15 -3
  127. package/dist/head.d.ts +7 -30
  128. package/dist/head.js +15 -5
  129. package/dist/includes.js +2 -2
  130. package/dist/meta-arguments.d.ts +52 -0
  131. package/dist/meta-arguments.js +55 -0
  132. package/dist/process.js +1 -1
  133. package/dist/pwa.js +2 -2
  134. package/dist/runtime-globals.d.ts +22 -0
  135. package/dist/runtime-globals.js +46 -0
  136. package/dist/seo.d.ts +39 -17
  137. package/dist/seo.js +221 -255
  138. package/dist/signals.js +193 -84
  139. package/dist/slots.js +3 -3
  140. package/dist/ssg.js +2 -2
  141. package/dist/theme-tokens.d.ts +59 -0
  142. package/dist/theme-tokens.js +77 -17
  143. package/dist/ts-css-config.js +4 -2
  144. package/dist/types/directive-types.d.ts +12 -0
  145. package/dist/utils.d.ts +10 -0
  146. package/dist/utils.js +77 -12
  147. package/dist/variable-extractor.d.ts +1 -1
  148. package/dist/variable-extractor.js +23 -3
  149. package/dist/visual-testing.js +1 -1
  150. package/package.json +5 -5
  151. package/dist/chunk-025z2btp.js +0 -2
  152. package/dist/chunk-19fp0z9s.js +0 -2
  153. package/dist/chunk-23sv24cm.js +0 -2
  154. package/dist/chunk-2p68bkza.js +0 -2
  155. package/dist/chunk-5grrqaqs.js +0 -2
  156. package/dist/chunk-6jx4r94x.js +0 -2
  157. package/dist/chunk-6p6b01kp.js +0 -4
  158. package/dist/chunk-a1rt1fms.js +0 -2
  159. package/dist/chunk-ahpmjg3f.js +0 -64
  160. package/dist/chunk-akp89n3w.js +0 -2
  161. package/dist/chunk-bqhrryqs.js +0 -2
  162. package/dist/chunk-czvzaa42.js +0 -3
  163. package/dist/chunk-de2cfcf5.js +0 -2
  164. package/dist/chunk-exhbc57n.js +0 -2
  165. package/dist/chunk-fhhp6pmx.js +0 -2
  166. package/dist/chunk-fqsrft0a.js +0 -2
  167. package/dist/chunk-gqzccw30.js +0 -2
  168. package/dist/chunk-h47e1c03.js +0 -2
  169. package/dist/chunk-kbemcvp1.js +0 -2
  170. package/dist/chunk-ks4j8fjz.js +0 -2
  171. package/dist/chunk-pbct6nxk.js +0 -2
  172. package/dist/chunk-peerbcwj.js +0 -2
  173. package/dist/chunk-pt1w6vq2.js +0 -2
  174. package/dist/chunk-rgda0gwf.js +0 -2
  175. package/dist/chunk-rwa10jm4.js +0 -2
  176. package/dist/chunk-snsh9ye4.js +0 -2
  177. package/dist/chunk-td39mtts.js +0 -2
  178. package/dist/chunk-trncnwem.js +0 -2
  179. package/dist/chunk-vk2j3vvs.js +0 -2
  180. package/dist/chunk-vph6zpeq.js +0 -2
  181. package/dist/chunk-wa3ks5zv.js +0 -2
  182. package/dist/chunk-xe93ae3r.js +0 -32
  183. package/dist/chunk-xer5kn26.js +0 -2
  184. package/dist/chunk-zv4gb3wb.js +0 -2
  185. package/dist/chunk-zx2krbqf.js +0 -2
package/dist/seo.js CHANGED
@@ -1,267 +1,248 @@
1
+ import {
2
+ describeValue,
3
+ evaluateDirectiveArgument,
4
+ isPlainRecord,
5
+ replaceDirectiveCalls,
6
+ reportDirectiveFailure,
7
+ splitTopLevelArgs,
8
+ warnDirective
9
+ } from "./directive-arguments.js";
1
10
  import { ErrorCodes, inlineError } from "./error-handling.js";
2
11
  import { mergeHeadConfigs } from "./head.js";
3
- import { safeEvaluateObject } from "./safe-evaluator.js";
4
- export function processMetaDirectives(template, context, _filePath, _options) {
5
- let output = template;
6
- output = output.replace(/@meta\(\s*['"]([^'"]+)['"](?:,\s*['"]([^'"]+)['"]\s*)?\)/g, (_, name, content) => {
7
- if (!content && name.includes(":")) {
8
- const parts = name.split(":"), property = name, contextKey = parts.length > 1 ? parts[1] : "";
9
- if (contextKey && context[contextKey])
10
- content = context[contextKey];
11
- else if (property.startsWith("og:") && context.openGraph && context.openGraph[parts[1]])
12
- content = context.openGraph[parts[1]];
13
- else
14
- content = "";
15
- return content ? `<meta property="${escapeHtml(property)}" content="${escapeHtml(content)}">` : "";
16
- }
17
- return content ? `<meta name="${escapeHtml(name)}" content="${escapeHtml(content)}">` : "";
18
- });
19
- const metaTagPat = /@metaTag\s*\(/g;
20
- let metaMatch;
21
- while ((metaMatch = metaTagPat.exec(output)) !== null) {
22
- const mtStart = metaMatch.index, openP = mtStart + metaMatch[0].length - 1;
23
- let d = 1, p = openP + 1;
24
- while (p < output.length && d > 0) {
25
- if (output[p] === "(")
26
- d++;
27
- else if (output[p] === ")") {
28
- d--;
29
- if (d === 0)
30
- break;
31
- }
32
- p++;
33
- }
34
- if (d !== 0)
35
- break;
36
- const attrObject = output.substring(openP + 1, p).trim(), fullEnd = p + 1, mtReplacement = (() => {
37
- try {
38
- const attrs = safeEvaluateObject(attrObject, context);
39
- if (!attrs)
40
- return "";
41
- let tag = "<meta";
42
- if (attrs.name)
43
- tag += ` name="${escapeHtml(String(attrs.name))}"`;
44
- if (attrs.property)
45
- tag += ` property="${escapeHtml(String(attrs.property))}"`;
46
- if (attrs.httpEquiv)
47
- tag += ` http-equiv="${escapeHtml(String(attrs.httpEquiv))}"`;
48
- if (attrs.content)
49
- tag += ` content="${escapeHtml(String(attrs.content))}"`;
50
- tag += ">";
51
- return tag;
52
- } catch (error) {
53
- const errorMessage = error instanceof Error ? error.message : String(error);
54
- return inlineError("MetaTag", errorMessage, ErrorCodes.EVALUATION_ERROR);
55
- }
56
- })();
57
- output = output.substring(0, mtStart) + mtReplacement + output.substring(fullEnd);
58
- metaTagPat.lastIndex = 0;
59
- }
12
+ import { resolveMetaArguments } from "./meta-arguments.js";
13
+ const META_CALL = /(?<![\w$@])@meta\s*\(/g, META_TAG_CALL = /(?<![\w$@])@metaTag\s*\(/g, STRUCTURED_DATA_CALL = /(?<![\w$@])@structuredData\s*\(/g, SEO_CALL = /(?<![\w$@])@seo\s*\(/g;
14
+ function unterminated(label, filePath) {
15
+ return (call) => reportDirectiveFailure(label, call, "the call has no closing parenthesis", filePath, ErrorCodes.INVALID_DIRECTIVE_SYNTAX);
16
+ }
17
+ function renderMetaTag(tag) {
18
+ return "property" in tag ? `<meta property="${escapeHtml(tag.property)}" content="${escapeHtml(tag.content)}">` : `<meta name="${escapeHtml(tag.name)}" content="${escapeHtml(tag.content)}">`;
19
+ }
20
+ export function processMetaDirectives(template, context, filePath, _options) {
21
+ if (!template.includes("@meta"))
22
+ return template;
23
+ let output = replaceDirectiveCalls(template, META_CALL, (args, call) => {
24
+ const resolved = resolveMetaArguments(splitTopLevelArgs(args), context);
25
+ if (resolved.kind === "tag")
26
+ return renderMetaTag(resolved.tag);
27
+ if (resolved.kind === "empty")
28
+ return "";
29
+ return reportDirectiveFailure("Meta", call, resolved.message, filePath, resolved.code);
30
+ }, unterminated("Meta", filePath));
31
+ output = replaceDirectiveCalls(output, META_TAG_CALL, (args, call) => {
32
+ const result = evaluateDirectiveArgument(args, context);
33
+ if (!result.ok)
34
+ return reportDirectiveFailure("MetaTag", call, result.message, filePath, result.code);
35
+ if (result.value === void 0 || result.value === null)
36
+ return "";
37
+ if (!isPlainRecord(result.value))
38
+ return reportDirectiveFailure("MetaTag", call, `expected an object such as { name, content }, got ${describeValue(result.value)}`, filePath, ErrorCodes.TYPE_ERROR);
39
+ const attrs = result.value;
40
+ if (!attrs.name && !attrs.property && !attrs.httpEquiv)
41
+ return reportDirectiveFailure("MetaTag", call, "the tag needs a name, property or httpEquiv", filePath, ErrorCodes.INVALID_DIRECTIVE_SYNTAX);
42
+ if (attrs.content === void 0 || attrs.content === null || attrs.content === "" || attrs.content === !1)
43
+ return "";
44
+ let tag = "<meta";
45
+ if (attrs.name)
46
+ tag += ` name="${escapeHtml(attrs.name)}"`;
47
+ if (attrs.property)
48
+ tag += ` property="${escapeHtml(attrs.property)}"`;
49
+ if (attrs.httpEquiv)
50
+ tag += ` http-equiv="${escapeHtml(attrs.httpEquiv)}"`;
51
+ tag += ` content="${escapeHtml(attrs.content)}">`;
52
+ return tag;
53
+ }, unterminated("MetaTag", filePath));
60
54
  return output;
61
55
  }
62
- export function processStructuredData(template, context, _filePath) {
63
- let output = template;
64
- const structuredDataPattern = /@structuredData\s*\(/g;
65
- let sdMatch;
66
- while ((sdMatch = structuredDataPattern.exec(output)) !== null) {
67
- const sdStart = sdMatch.index, openParen = sdStart + sdMatch[0].length - 1;
68
- let depth = 1, pos = openParen + 1;
69
- while (pos < output.length && depth > 0) {
70
- if (output[pos] === "(")
71
- depth++;
72
- else if (output[pos] === ")") {
73
- depth--;
74
- if (depth === 0)
75
- break;
76
- }
77
- pos++;
56
+ function withSchemaContext(value) {
57
+ const fill = (item) => item["@context"] ? { ...item } : { ...item, "@context": "https://schema.org" };
58
+ if (isPlainRecord(value))
59
+ return fill(value);
60
+ if (Array.isArray(value) && value.length > 0 && value.every(isPlainRecord))
61
+ return value.map(fill);
62
+ return null;
63
+ }
64
+ function jsonLdScript(data) {
65
+ return `<script type="application/ld+json">${JSON.stringify(data).replace(/<\//g, "<\\/")}</script>`;
66
+ }
67
+ export function processStructuredData(template, context, filePath) {
68
+ if (!template.includes("@structuredData"))
69
+ return template;
70
+ return replaceDirectiveCalls(template, STRUCTURED_DATA_CALL, (args, call) => {
71
+ const result = evaluateDirectiveArgument(args, context);
72
+ if (!result.ok)
73
+ return reportDirectiveFailure("StructuredData", call, result.message, filePath, result.code);
74
+ if (result.value === void 0 || result.value === null)
75
+ return "";
76
+ const data = withSchemaContext(result.value);
77
+ if (!data)
78
+ return reportDirectiveFailure("StructuredData", call, `expected an object or an array of objects, got ${describeValue(result.value)}`, filePath, ErrorCodes.TYPE_ERROR);
79
+ try {
80
+ return jsonLdScript(data);
81
+ } catch (error) {
82
+ const message = error instanceof Error ? error.message : String(error);
83
+ return reportDirectiveFailure("StructuredData", call, `the data cannot be written as JSON: ${message}`, filePath, ErrorCodes.EVALUATION_ERROR);
78
84
  }
79
- if (depth !== 0)
80
- break;
81
- const dataObject = output.substring(openParen + 1, pos).trim(), fullEnd = pos + 1, replacement = (() => {
82
- try {
83
- const data = safeEvaluateObject(dataObject, context);
84
- if (!data)
85
- return "";
86
- if (!data["@context"])
87
- data["@context"] = "https://schema.org";
88
- return `<script type="application/ld+json">${JSON.stringify(data).replace(/<\//g, "<\\/")}</script>`;
89
- } catch (error) {
90
- const errorMessage = error instanceof Error ? error.message : String(error);
91
- return inlineError("StructuredData", errorMessage, ErrorCodes.EVALUATION_ERROR);
92
- }
93
- })();
94
- output = output.substring(0, sdStart) + replacement + output.substring(fullEnd);
95
- structuredDataPattern.lastIndex = 0;
96
- }
97
- return output;
85
+ }, unterminated("StructuredData", filePath));
98
86
  }
99
- export function processSeoDirective(template, context, _filePath, _options) {
100
- let output = template;
101
- const seoPat = /@seo\s*\(\s*\{/g;
102
- let seoMatch;
103
- while ((seoMatch = seoPat.exec(output)) !== null) {
104
- const braceStart = output.indexOf("{", seoMatch.index + 4);
105
- let depth = 1, pos = braceStart + 1, inStr = null, esc = !1;
106
- while (pos < output.length && depth > 0) {
107
- const c = output[pos];
108
- if (esc) {
109
- esc = !1;
110
- pos++;
111
- continue;
112
- }
113
- if (c === "\\" && inStr) {
114
- esc = !0;
115
- pos++;
116
- continue;
117
- }
118
- if (inStr) {
119
- if (c === inStr)
120
- inStr = null;
121
- pos++;
122
- continue;
123
- }
124
- if (c === '"' || c === "'" || c === "`") {
125
- inStr = c;
126
- pos++;
127
- continue;
128
- }
129
- if (c === "{")
130
- depth++;
131
- else if (c === "}")
132
- depth--;
133
- pos++;
87
+ const SEO_KEYS = new Set(["title", "description", "keywords", "robots", "canonical", "openGraph", "twitter", "structuredData"]), SEO_KEY_HINTS = {
88
+ image: "openGraph.image",
89
+ url: "canonical or openGraph.url",
90
+ type: "openGraph.type",
91
+ siteName: "openGraph.siteName",
92
+ locale: "openGraph.locale",
93
+ og: "openGraph",
94
+ author: "@meta('author', ...)"
95
+ };
96
+ export function processSeoDirective(template, context, filePath, _options) {
97
+ if (!template.includes("@seo"))
98
+ return template;
99
+ return replaceDirectiveCalls(template, SEO_CALL, (args, call) => {
100
+ if (!args.trim())
101
+ return reportDirectiveFailure("SEO", call, "expected an object: @seo({ title, description }) or @seo(seoVariable)", filePath, ErrorCodes.INVALID_DIRECTIVE_SYNTAX);
102
+ const result = evaluateDirectiveArgument(args, context);
103
+ if (!result.ok)
104
+ return reportDirectiveFailure("SEO", call, result.message, filePath, result.code);
105
+ if (result.value === void 0 || result.value === null)
106
+ return "";
107
+ if (!isPlainRecord(result.value))
108
+ return reportDirectiveFailure("SEO", call, `expected an object such as { title, description }, got ${describeValue(result.value)}`, filePath, ErrorCodes.TYPE_ERROR);
109
+ try {
110
+ return renderSeoTags(result.value, call, filePath);
111
+ } catch (error) {
112
+ const message = error instanceof Error ? error.message : String(error);
113
+ return reportDirectiveFailure("SEO", call, `Error processing @seo directive: ${message}`, filePath, ErrorCodes.EVALUATION_ERROR);
134
114
  }
135
- if (depth !== 0)
136
- break;
137
- const afterBrace = output.substring(pos).match(/^\s*\)/);
138
- if (!afterBrace)
139
- break;
140
- const fullEnd = pos + afterBrace[0].length, seoConfig = output.substring(braceStart, pos), fullMatch = output.substring(seoMatch.index, fullEnd), replacement = ((cfg) => {
141
- try {
142
- const config = safeEvaluateObject(cfg, context);
143
- if (!config)
144
- return "";
145
- let metaTags = "";
146
- if (config.title) {
147
- metaTags += `<title>${escapeHtml(config.title)}</title>
115
+ }, unterminated("SEO", filePath));
116
+ }
117
+ function nestedConfig(config, key, call, filePath) {
118
+ const value = config[key];
119
+ if (value === void 0 || value === null || value === !1)
120
+ return;
121
+ if (isPlainRecord(value))
122
+ return value;
123
+ warnDirective(call, `${key} must be an object, got ${describeValue(value)}; its tags were left out`, filePath);
124
+ return;
125
+ }
126
+ function renderSeoTags(config, call, filePath) {
127
+ const unknown = Object.keys(config).filter((key) => !SEO_KEYS.has(key));
128
+ if (unknown.length > 0) {
129
+ const described = unknown.map((key) => SEO_KEY_HINTS[key] ? `${key} (use ${SEO_KEY_HINTS[key]})` : key).join(", ");
130
+ warnDirective(call, `@seo does not read ${described}; ${unknown.length === 1 ? "it was" : "they were"} ignored`, filePath);
131
+ }
132
+ let metaTags = "";
133
+ if (config.title) {
134
+ metaTags += `<title>${escapeHtml(config.title)}</title>
148
135
  `;
149
- metaTags += `<meta name="title" content="${escapeHtml(config.title)}">
136
+ metaTags += `<meta name="title" content="${escapeHtml(config.title)}">
150
137
  `;
151
- }
152
- if (config.description)
153
- metaTags += `<meta name="description" content="${escapeHtml(config.description)}">
138
+ }
139
+ if (config.description)
140
+ metaTags += `<meta name="description" content="${escapeHtml(config.description)}">
154
141
  `;
155
- if (config.keywords) {
156
- const keywordsStr = Array.isArray(config.keywords) ? config.keywords.join(", ") : config.keywords;
157
- metaTags += `<meta name="keywords" content="${escapeHtml(keywordsStr)}">
142
+ if (config.keywords) {
143
+ const keywordsStr = Array.isArray(config.keywords) ? config.keywords.join(", ") : config.keywords;
144
+ metaTags += `<meta name="keywords" content="${escapeHtml(keywordsStr)}">
158
145
  `;
159
- }
160
- if (config.robots)
161
- metaTags += `<meta name="robots" content="${escapeHtml(config.robots)}">
146
+ }
147
+ if (config.robots)
148
+ metaTags += `<meta name="robots" content="${escapeHtml(config.robots)}">
162
149
  `;
163
- if (config.canonical)
164
- metaTags += `<link rel="canonical" href="${escapeHtml(config.canonical)}">
150
+ if (config.canonical)
151
+ metaTags += `<link rel="canonical" href="${escapeHtml(config.canonical)}">
165
152
  `;
166
- if (config.openGraph) {
167
- const og = config.openGraph;
168
- metaTags += `<meta property="og:type" content="${escapeHtml(og.type || "website")}">
153
+ const og = nestedConfig(config, "openGraph", call, filePath);
154
+ if (og) {
155
+ metaTags += `<meta property="og:type" content="${escapeHtml(og.type || "website")}">
169
156
  `;
170
- if (og.title || config.title) {
171
- const ogTitle = og.title || config.title || "";
172
- metaTags += `<meta property="og:title" content="${escapeHtml(ogTitle)}">
157
+ if (og.title || config.title) {
158
+ const ogTitle = og.title || config.title || "";
159
+ metaTags += `<meta property="og:title" content="${escapeHtml(ogTitle)}">
173
160
  `;
174
- }
175
- if (og.description || config.description) {
176
- const ogDescription = og.description || config.description || "";
177
- metaTags += `<meta property="og:description" content="${escapeHtml(ogDescription)}">
161
+ }
162
+ if (og.description || config.description) {
163
+ const ogDescription = og.description || config.description || "";
164
+ metaTags += `<meta property="og:description" content="${escapeHtml(ogDescription)}">
178
165
  `;
179
- }
180
- if (og.url || config.canonical) {
181
- const ogUrl = og.url || config.canonical || "";
182
- metaTags += `<meta property="og:url" content="${escapeHtml(ogUrl)}">
166
+ }
167
+ if (og.url || config.canonical) {
168
+ const ogUrl = og.url || config.canonical || "";
169
+ metaTags += `<meta property="og:url" content="${escapeHtml(ogUrl)}">
183
170
  `;
184
- }
185
- if (og.image) {
186
- metaTags += `<meta property="og:image" content="${escapeHtml(og.image)}">
171
+ }
172
+ if (og.image) {
173
+ metaTags += `<meta property="og:image" content="${escapeHtml(og.image)}">
187
174
  `;
188
- if (og.imageAlt)
189
- metaTags += `<meta property="og:image:alt" content="${escapeHtml(og.imageAlt)}">
175
+ if (og.imageAlt)
176
+ metaTags += `<meta property="og:image:alt" content="${escapeHtml(og.imageAlt)}">
190
177
  `;
191
- if (og.imageWidth)
192
- metaTags += `<meta property="og:image:width" content="${escapeHtml(String(og.imageWidth))}">
178
+ if (og.imageWidth)
179
+ metaTags += `<meta property="og:image:width" content="${escapeHtml(String(og.imageWidth))}">
193
180
  `;
194
- if (og.imageHeight)
195
- metaTags += `<meta property="og:image:height" content="${escapeHtml(String(og.imageHeight))}">
181
+ if (og.imageHeight)
182
+ metaTags += `<meta property="og:image:height" content="${escapeHtml(String(og.imageHeight))}">
196
183
  `;
197
- if (og.imageType)
198
- metaTags += `<meta property="og:image:type" content="${escapeHtml(og.imageType)}">
184
+ if (og.imageType)
185
+ metaTags += `<meta property="og:image:type" content="${escapeHtml(og.imageType)}">
199
186
  `;
200
- }
201
- if (og.siteName)
202
- metaTags += `<meta property="og:site_name" content="${escapeHtml(og.siteName)}">
187
+ }
188
+ if (og.siteName)
189
+ metaTags += `<meta property="og:site_name" content="${escapeHtml(og.siteName)}">
203
190
  `;
204
- if (og.locale)
205
- metaTags += `<meta property="og:locale" content="${escapeHtml(og.locale)}">
191
+ if (og.locale)
192
+ metaTags += `<meta property="og:locale" content="${escapeHtml(og.locale)}">
206
193
  `;
207
- if (og.profile) {
208
- const profile = [
209
- ["first_name", og.profile.firstName],
210
- ["last_name", og.profile.lastName],
211
- ["username", og.profile.username],
212
- ["gender", og.profile.gender]
213
- ];
214
- for (const [key, value] of profile)
215
- if (value)
216
- metaTags += `<meta property="profile:${key}" content="${escapeHtml(value)}">
194
+ if (og.profile) {
195
+ const profile = [
196
+ ["first_name", og.profile.firstName],
197
+ ["last_name", og.profile.lastName],
198
+ ["username", og.profile.username],
199
+ ["gender", og.profile.gender]
200
+ ];
201
+ for (const [key, value] of profile)
202
+ if (value)
203
+ metaTags += `<meta property="profile:${key}" content="${escapeHtml(value)}">
217
204
  `;
218
- }
219
- }
220
- if (config.twitter) {
221
- const twitter = config.twitter;
222
- metaTags += `<meta name="twitter:card" content="${escapeHtml(twitter.card || "summary_large_image")}">
205
+ }
206
+ }
207
+ const twitter = nestedConfig(config, "twitter", call, filePath);
208
+ if (twitter) {
209
+ metaTags += `<meta name="twitter:card" content="${escapeHtml(twitter.card || "summary_large_image")}">
223
210
  `;
224
- if (twitter.title || config.title) {
225
- const twitterTitle = twitter.title || config.title || "";
226
- metaTags += `<meta name="twitter:title" content="${escapeHtml(twitterTitle)}">
211
+ if (twitter.title || config.title) {
212
+ const twitterTitle = twitter.title || config.title || "";
213
+ metaTags += `<meta name="twitter:title" content="${escapeHtml(twitterTitle)}">
227
214
  `;
228
- }
229
- if (twitter.description || config.description) {
230
- const twitterDesc = twitter.description || config.description || "";
231
- metaTags += `<meta name="twitter:description" content="${escapeHtml(twitterDesc)}">
215
+ }
216
+ if (twitter.description || config.description) {
217
+ const twitterDesc = twitter.description || config.description || "";
218
+ metaTags += `<meta name="twitter:description" content="${escapeHtml(twitterDesc)}">
232
219
  `;
233
- }
234
- if (twitter.image || config.openGraph && config.openGraph.image) {
235
- const twitterImage = twitter.image || (config.openGraph ? config.openGraph.image : "") || "";
236
- metaTags += `<meta name="twitter:image" content="${escapeHtml(twitterImage)}">
220
+ }
221
+ if (twitter.image || og && og.image) {
222
+ const twitterImage = twitter.image || (og ? og.image : "") || "";
223
+ metaTags += `<meta name="twitter:image" content="${escapeHtml(twitterImage)}">
237
224
  `;
238
- const twitterImageAlt = twitter.imageAlt || (!twitter.image && config.openGraph ? config.openGraph.imageAlt : void 0);
239
- if (twitterImageAlt)
240
- metaTags += `<meta name="twitter:image:alt" content="${escapeHtml(twitterImageAlt)}">
225
+ const twitterImageAlt = twitter.imageAlt || (!twitter.image && og ? og.imageAlt : void 0);
226
+ if (twitterImageAlt)
227
+ metaTags += `<meta name="twitter:image:alt" content="${escapeHtml(twitterImageAlt)}">
241
228
  `;
242
- }
243
- if (twitter.site)
244
- metaTags += `<meta name="twitter:site" content="${escapeHtml(twitter.site)}">
229
+ }
230
+ if (twitter.site)
231
+ metaTags += `<meta name="twitter:site" content="${escapeHtml(twitter.site)}">
245
232
  `;
246
- if (twitter.creator)
247
- metaTags += `<meta name="twitter:creator" content="${escapeHtml(twitter.creator)}">
233
+ if (twitter.creator)
234
+ metaTags += `<meta name="twitter:creator" content="${escapeHtml(twitter.creator)}">
248
235
  `;
249
- }
250
- if (config.structuredData) {
251
- const jsonStr = JSON.stringify(config.structuredData).replace(/<\//g, "<\\/");
252
- metaTags += `<script type="application/ld+json">${jsonStr}</script>
236
+ }
237
+ if (config.structuredData !== void 0 && config.structuredData !== null) {
238
+ const data = withSchemaContext(config.structuredData);
239
+ if (data)
240
+ metaTags += `${jsonLdScript(data)}
253
241
  `;
254
- }
255
- return metaTags.trim();
256
- } catch (error) {
257
- const errorMessage = error instanceof Error ? error.message : String(error);
258
- return inlineError("SEO", `Error processing @seo directive: ${errorMessage}`, ErrorCodes.EVALUATION_ERROR);
259
- }
260
- })(seoConfig);
261
- output = output.substring(0, seoMatch.index) + replacement + output.substring(fullEnd);
262
- seoPat.lastIndex = seoMatch.index + replacement.length;
242
+ else
243
+ warnDirective(call, `structuredData must be an object or an array of objects, got ${describeValue(config.structuredData)}; it was left out`, filePath);
263
244
  }
264
- return output;
245
+ return metaTags.trim();
265
246
  }
266
247
  export function injectSeoTags(html, context, options) {
267
248
  if (options.seo?.enabled === !1)
@@ -367,35 +348,20 @@ function readPageDescription(head) {
367
348
  }
368
349
  return "";
369
350
  }
370
- function escapeHtml(str) {
371
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
372
- }
373
- const PROPERTY_NAMESPACES = ["og:", "article:", "book:", "profile:", "fb:", "music:", "video:"];
374
- function unquoteParam(raw) {
375
- const text = raw.trim(), first = text[0];
376
- if ((first === "'" || first === '"' || first === "`") && text.length > 1 && text.at(-1) === first)
377
- return { value: text.slice(1, -1), quoted: !0 };
378
- return { value: text, quoted: !1 };
351
+ function escapeHtml(value) {
352
+ return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
379
353
  }
380
354
  export const metaDirective = {
381
355
  name: "meta",
382
- handler: (_content, params, context, _filePath) => {
383
- if (params.length < 1)
384
- return inlineError("Meta", "meta directive requires at least the meta name", ErrorCodes.INVALID_DIRECTIVE_SYNTAX);
385
- const name = unquoteParam(params[0]).value;
386
- let metaContent;
387
- if (params.length > 1) {
388
- const arg = unquoteParam(params[1]);
389
- metaContent = arg.quoted ? arg.value : String(context?.[arg.value] ?? arg.value);
390
- } else {
391
- const segment = name.includes(":") ? name.slice(name.indexOf(":") + 1) : name, openGraph = context?.openGraph;
392
- metaContent = String(context?.[segment] ?? openGraph?.[segment] ?? "");
393
- }
394
- if (!metaContent)
356
+ rawParams: !0,
357
+ handler: (_content, params, context, filePath) => {
358
+ const resolved = resolveMetaArguments(params, context ?? {});
359
+ if (resolved.kind === "error")
360
+ return reportDirectiveFailure("Meta", `@meta(${params.join(", ")})`, resolved.message, filePath, resolved.code);
361
+ if (resolved.kind === "empty")
395
362
  return "";
396
- const tag = PROPERTY_NAMESPACES.some((prefix) => name.startsWith(prefix)) ? { property: name, content: metaContent } : { name, content: metaContent };
397
363
  if (context)
398
- context.__stx_runtime_head = mergeHeadConfigs(context.__stx_runtime_head ?? {}, { meta: [tag] });
364
+ context.__stx_runtime_head = mergeHeadConfigs(context.__stx_runtime_head ?? {}, { meta: [resolved.tag] });
399
365
  return "";
400
366
  },
401
367
  hasEndTag: !1
@@ -410,7 +376,7 @@ export const metaDirective = {
410
376
  data["@context"] = "https://schema.org";
411
377
  if (!data["@type"])
412
378
  return inlineError("StructuredData", "structuredData requires @type property", ErrorCodes.INVALID_DIRECTIVE_SYNTAX);
413
- return `<script type="application/ld+json">${JSON.stringify(data)}</script>`;
379
+ return jsonLdScript(data);
414
380
  } catch (error) {
415
381
  const errorMessage = error instanceof Error ? error.message : String(error);
416
382
  return inlineError("StructuredData", errorMessage, ErrorCodes.EVALUATION_ERROR);