@stacksjs/stx 0.2.342 → 0.2.343

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 (167) hide show
  1. package/dist/caching.js +1 -0
  2. package/dist/{chunk-48x5bsqv.js → chunk-0dpgh7a6.js} +1 -1
  3. package/dist/{chunk-evtrctj9.js → chunk-0fp16c1x.js} +1 -1
  4. package/dist/{chunk-p5d8sb50.js → chunk-0s3ry262.js} +1 -1
  5. package/dist/{chunk-tmj45xq5.js → chunk-12qteqk8.js} +1 -1
  6. package/dist/chunk-160g7ynw.js +2 -0
  7. package/dist/chunk-16hr5y08.js +2 -0
  8. package/dist/{chunk-anf2y2h5.js → chunk-1bfvdjpy.js} +1 -1
  9. package/dist/{chunk-y956wapy.js → chunk-1h1c2048.js} +1 -1
  10. package/dist/{chunk-bt8w146r.js → chunk-1qtd4ex2.js} +2 -2
  11. package/dist/{chunk-2swnc8kg.js → chunk-1zav2sf1.js} +1 -1
  12. package/dist/{chunk-gbkq5qnw.js → chunk-2853ye06.js} +2 -2
  13. package/dist/{chunk-4vcyf1kh.js → chunk-36j9qset.js} +2 -2
  14. package/dist/{chunk-74r1zw2h.js → chunk-3d3x7dwy.js} +1 -1
  15. package/dist/chunk-3h97y3n3.js +2 -0
  16. package/dist/chunk-3j915h2d.js +2 -0
  17. package/dist/{chunk-1gwbw81h.js → chunk-45ydb29q.js} +1 -1
  18. package/dist/{chunk-ts3wfjh9.js → chunk-4fafxr8d.js} +1 -1
  19. package/dist/chunk-4hkg6kx4.js +2 -0
  20. package/dist/{chunk-8tsr51hj.js → chunk-4pg6wbt6.js} +1 -1
  21. package/dist/{chunk-saqbtgqr.js → chunk-4vd0rqfs.js} +1 -1
  22. package/dist/{chunk-wv8h3d68.js → chunk-53sjbp3z.js} +1 -1
  23. package/dist/chunk-57494khk.js +2 -0
  24. package/dist/{chunk-4wpxbkbe.js → chunk-57dxdvjh.js} +1 -1
  25. package/dist/{chunk-k3w3gwm3.js → chunk-5azeswj2.js} +1 -1
  26. package/dist/{chunk-k5b9471y.js → chunk-5hq25yky.js} +1 -1
  27. package/dist/chunk-5xavmr4t.js +2 -0
  28. package/dist/{chunk-kznb3hdj.js → chunk-5zemhhwe.js} +1 -1
  29. package/dist/{chunk-ghd80w8a.js → chunk-62vng8g8.js} +2 -2
  30. package/dist/{chunk-reskzmsb.js → chunk-649dfqqz.js} +1 -1
  31. package/dist/{chunk-6nxq8rcw.js → chunk-6g8rvnhc.js} +1 -1
  32. package/dist/chunk-6rsfkms1.js +2 -0
  33. package/dist/{chunk-xe93ae3r.js → chunk-6xs80tka.js} +3 -3
  34. package/dist/{chunk-s4mmmwj4.js → chunk-7mc1k1tq.js} +1 -1
  35. package/dist/{chunk-25b8z7xa.js → chunk-7r79j1hs.js} +1 -1
  36. package/dist/{chunk-drs93sry.js → chunk-7yfk9393.js} +1 -1
  37. package/dist/{chunk-r46cg4qa.js → chunk-7zxv2631.js} +1 -1
  38. package/dist/{chunk-d389vyyz.js → chunk-8ah5mhst.js} +2 -2
  39. package/dist/{chunk-zv4gb3wb.js → chunk-905j49fe.js} +1 -1
  40. package/dist/chunk-9czj483k.js +2 -0
  41. package/dist/chunk-9kv0qavn.js +2 -0
  42. package/dist/{chunk-m39hb2a2.js → chunk-a0ajg9z5.js} +1 -1
  43. package/dist/{chunk-mznr9nyp.js → chunk-ag8gdk51.js} +1 -1
  44. package/dist/{chunk-gwc54hjc.js → chunk-amfq5rnz.js} +2 -2
  45. package/dist/{chunk-rh7mwwb4.js → chunk-aqxwwq6w.js} +2 -2
  46. package/dist/{chunk-hk8j3eps.js → chunk-ay336sqb.js} +1 -1
  47. package/dist/chunk-bcz8yp2g.js +2 -0
  48. package/dist/{chunk-rr49zrz6.js → chunk-brjfzbq9.js} +1 -1
  49. package/dist/chunk-bxefsc7k.js +2 -0
  50. package/dist/chunk-c4kv3r8t.js +2 -0
  51. package/dist/{chunk-yzv702kk.js → chunk-c7qdnc5d.js} +1 -1
  52. package/dist/{chunk-ys56ecvx.js → chunk-daxr6p6q.js} +1 -1
  53. package/dist/{chunk-yhvrq2qd.js → chunk-dj5mqnp1.js} +1 -1
  54. package/dist/{chunk-da0tfpa2.js → chunk-e53pwshq.js} +1 -1
  55. package/dist/{chunk-k9ep24bd.js → chunk-e6rxvz3p.js} +1 -1
  56. package/dist/{chunk-x7dv5rq7.js → chunk-ec18ftcg.js} +1 -1
  57. package/dist/chunk-eh3meyd3.js +64 -0
  58. package/dist/{chunk-yq7fc8sa.js → chunk-em8cr7wy.js} +1 -1
  59. package/dist/{chunk-thrv2nbz.js → chunk-ey1tsk22.js} +1 -1
  60. package/dist/{chunk-e0kzyf1g.js → chunk-fbmjw0bh.js} +1 -1
  61. package/dist/chunk-fytemc96.js +2 -0
  62. package/dist/{chunk-zv4nf0x2.js → chunk-g0dbtyk7.js} +1 -1
  63. package/dist/chunk-g7gc0ten.js +2 -0
  64. package/dist/chunk-gqsxxd81.js +2 -0
  65. package/dist/{chunk-sytwy0mg.js → chunk-grnmr3y1.js} +1 -1
  66. package/dist/{chunk-4ecv1e3w.js → chunk-gvfgrve8.js} +1 -1
  67. package/dist/chunk-gx9z0wmm.js +2 -0
  68. package/dist/{chunk-3b0mgxbk.js → chunk-h6y99nak.js} +1 -1
  69. package/dist/chunk-hq3phk1a.js +3 -0
  70. package/dist/{chunk-01ggk8k8.js → chunk-hrdd26r4.js} +1 -1
  71. package/dist/{chunk-567g1ttv.js → chunk-hzbty7hz.js} +1 -1
  72. package/dist/{chunk-6nvm939g.js → chunk-jagf7h9y.js} +1 -1
  73. package/dist/{chunk-xec226zb.js → chunk-jh6f1a9y.js} +1 -1
  74. package/dist/chunk-jnxzzzm4.js +2 -0
  75. package/dist/chunk-jy6jwxhs.js +2 -0
  76. package/dist/{chunk-r1rfwm01.js → chunk-k3dtvay1.js} +1 -1
  77. package/dist/chunk-k8skvm0d.js +2 -0
  78. package/dist/{chunk-1xwfws06.js → chunk-kbah2k8e.js} +2 -2
  79. package/dist/{chunk-beextc84.js → chunk-kgbz86sa.js} +2 -2
  80. package/dist/chunk-khqn40gv.js +2 -0
  81. package/dist/{chunk-fx29n4vc.js → chunk-m0xaw0qw.js} +1 -1
  82. package/dist/chunk-mhbzrh5y.js +2 -0
  83. package/dist/{chunk-mbstfjnj.js → chunk-mwvxhv0r.js} +1 -1
  84. package/dist/{chunk-cqw3c1xg.js → chunk-p3awq80d.js} +1 -1
  85. package/dist/chunk-p8anntjb.js +2 -0
  86. package/dist/chunk-pwe51myj.js +2 -0
  87. package/dist/{chunk-gkq323fg.js → chunk-pwhf5kte.js} +1 -1
  88. package/dist/{chunk-25f0s8t1.js → chunk-q6825wwz.js} +1 -1
  89. package/dist/{chunk-f2qbjr3z.js → chunk-q9ckfwhh.js} +1 -1
  90. package/dist/{chunk-ytx17bg2.js → chunk-rns7wjy9.js} +1 -1
  91. package/dist/chunk-rqqdvnp5.js +2 -0
  92. package/dist/chunk-rzg9pars.js +7 -0
  93. package/dist/{chunk-yr7camcd.js → chunk-s60eccqk.js} +1 -1
  94. package/dist/{chunk-ntm1761b.js → chunk-s7qbba8r.js} +10 -10
  95. package/dist/{chunk-hrmxxs0w.js → chunk-safmgrq5.js} +1 -1
  96. package/dist/{chunk-ebd3z4j9.js → chunk-stqm4brp.js} +1 -1
  97. package/dist/{chunk-1t3j8a0q.js → chunk-t00ga194.js} +1 -1
  98. package/dist/{chunk-fm5211ec.js → chunk-tqw1yean.js} +1 -1
  99. package/dist/{chunk-hnffkdzc.js → chunk-tz68qv47.js} +1 -1
  100. package/dist/{chunk-1spzqjve.js → chunk-vaggjvsm.js} +1 -1
  101. package/dist/{chunk-q4cemvkw.js → chunk-vj3nx26v.js} +1 -1
  102. package/dist/chunk-w1mbrq1g.js +2 -0
  103. package/dist/chunk-w84qe2m6.js +2 -0
  104. package/dist/{chunk-c33y19bn.js → chunk-wdaf98ya.js} +1 -1
  105. package/dist/chunk-wjm7hv5e.js +2 -0
  106. package/dist/{chunk-byjy9far.js → chunk-wnmegbw0.js} +1 -1
  107. package/dist/{chunk-0sq5mdrh.js → chunk-wyhb3nb0.js} +1 -1
  108. package/dist/{chunk-j82hfkcf.js → chunk-xgw269cf.js} +234 -236
  109. package/dist/{chunk-jcbj3xp4.js → chunk-y5sxa2sq.js} +1 -1
  110. package/dist/{chunk-mkgbrbr9.js → chunk-yefnrr5z.js} +1 -1
  111. package/dist/chunk-yr7s02f7.js +2 -0
  112. package/dist/{chunk-p52cm7ch.js → chunk-z9hb80mk.js} +1 -1
  113. package/dist/{chunk-c73pyeak.js → chunk-zcytk5w9.js} +1 -1
  114. package/dist/chunk-zq3tm2c2.js +2 -0
  115. package/dist/{chunk-5g9vbhbt.js → chunk-zr1x54bt.js} +1 -1
  116. package/dist/cli.js +14 -14
  117. package/dist/craft-entry.js +1 -1
  118. package/dist/craft.js +1 -1
  119. package/dist/custom-directives.js +21 -8
  120. package/dist/database.js +1 -1
  121. package/dist/directive-arguments.d.ts +107 -0
  122. package/dist/directive-arguments.js +171 -0
  123. package/dist/head.d.ts +7 -30
  124. package/dist/head.js +15 -5
  125. package/dist/meta-arguments.d.ts +52 -0
  126. package/dist/meta-arguments.js +55 -0
  127. package/dist/process.js +1 -1
  128. package/dist/pwa.js +2 -2
  129. package/dist/seo.d.ts +39 -17
  130. package/dist/seo.js +221 -255
  131. package/dist/ssg.js +2 -2
  132. package/dist/types/directive-types.d.ts +12 -0
  133. package/dist/visual-testing.js +1 -1
  134. package/package.json +5 -5
  135. package/dist/chunk-025z2btp.js +0 -2
  136. package/dist/chunk-19fp0z9s.js +0 -2
  137. package/dist/chunk-23sv24cm.js +0 -2
  138. package/dist/chunk-2p68bkza.js +0 -2
  139. package/dist/chunk-5grrqaqs.js +0 -2
  140. package/dist/chunk-6jx4r94x.js +0 -2
  141. package/dist/chunk-6p6b01kp.js +0 -4
  142. package/dist/chunk-a1rt1fms.js +0 -2
  143. package/dist/chunk-ahpmjg3f.js +0 -64
  144. package/dist/chunk-akp89n3w.js +0 -2
  145. package/dist/chunk-bqhrryqs.js +0 -2
  146. package/dist/chunk-czvzaa42.js +0 -3
  147. package/dist/chunk-de2cfcf5.js +0 -2
  148. package/dist/chunk-exhbc57n.js +0 -2
  149. package/dist/chunk-fhhp6pmx.js +0 -2
  150. package/dist/chunk-fqsrft0a.js +0 -2
  151. package/dist/chunk-gqzccw30.js +0 -2
  152. package/dist/chunk-h47e1c03.js +0 -2
  153. package/dist/chunk-kbemcvp1.js +0 -2
  154. package/dist/chunk-ks4j8fjz.js +0 -2
  155. package/dist/chunk-pbct6nxk.js +0 -2
  156. package/dist/chunk-peerbcwj.js +0 -2
  157. package/dist/chunk-pt1w6vq2.js +0 -2
  158. package/dist/chunk-rgda0gwf.js +0 -2
  159. package/dist/chunk-rwa10jm4.js +0 -2
  160. package/dist/chunk-snsh9ye4.js +0 -2
  161. package/dist/chunk-td39mtts.js +0 -2
  162. package/dist/chunk-trncnwem.js +0 -2
  163. package/dist/chunk-vk2j3vvs.js +0 -2
  164. package/dist/chunk-vph6zpeq.js +0 -2
  165. package/dist/chunk-wa3ks5zv.js +0 -2
  166. package/dist/chunk-xer5kn26.js +0 -2
  167. 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);
package/dist/ssg.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{Bk as Fq,wk as Uq,xk as Iq}from"./chunk-byjy9far.js";import{Ck as Eq,Dk as Tq,Ek as Dq}from"./chunk-c0n802j1.js";import{Zk as _q,al as zq,bl as Aq}from"./chunk-4wpxbkbe.js";import"./chunk-gbkq5qnw.js";import"./chunk-fvsefztj.js";import{Np as P,Sm as Oq,mo as l,po as Hq,vr as n}from"./chunk-j82hfkcf.js";import"./chunk-rh7mwwb4.js";import"./chunk-1t3j8a0q.js";import"./chunk-01ggk8k8.js";import"./chunk-25b8z7xa.js";import"./chunk-hk8j3eps.js";import{lt as Xq}from"./chunk-xe93ae3r.js";import{qt as d}from"./chunk-1spzqjve.js";import"./chunk-f2qbjr3z.js";import"./chunk-1xwfws06.js";import"./chunk-r1rfwm01.js";import{Xt as Bq}from"./chunk-ahpmjg3f.js";import"./chunk-bt8w146r.js";import"./chunk-6p6b01kp.js";import"./chunk-x7dv5rq7.js";import"./chunk-e0kzyf1g.js";import"./chunk-wv8h3d68.js";import{bw as Yq,cw as Vq,dw as jq,ew as Gq,fw as Lq,gw as Nq}from"./chunk-beextc84.js";import"./chunk-yr7camcd.js";import{xw as y}from"./chunk-p5d8sb50.js";import O from"fs";import j from"path";import{createHash as xq}from"crypto";function yq(q){try{return new Bun.Transpiler({loader:"js",minifyWhitespace:!0}).transformSync(q).replace(/\}(var |let |const |function )/g,"};$1").trim()}catch{return q}}var Pq=/<script\s+type="stx\/island"\s+data-stx-island="([^"]+)"([^>]*)>([\s\S]*?)<\/script>/g;function Cq(q){return Bun.hash(q).toString(16)}function Qq(q,Z={}){if(!q.includes('type="stx/island"'))return{html:q,chunks:[]};let $=new Map,Q=q.replace(Pq,(J,W,z,A)=>{let G=A.trim();if(!G)return J;if(/\bdata-stx-src=/.test(z))return J;if(Z.minify)G=yq(G);let X=Cq(G);if(!$.has(X))$.set(X,G);let B=Z.integrity?` data-stx-integrity="sha384-${xq("sha384").update(G).digest("base64")}"`:"";return`<script type="stx/island" data-stx-island="${W}" data-stx-src="/_stx/islands/${X}.js"${B}${z}></script>`}),K=Array.from($,([J,W])=>({hash:J,code:W}));return{html:Q,chunks:K}}var gq=new Set(["visible","idle"]);function Zq(q){if(!q.includes("data-stx-src="))return q;let Z=new Map,$=/data-stx-scope="([^"]+)"[^>]*?stx-hydrate="([^"]+)"/g,Q;while((Q=$.exec(q))!==null)Z.set(Q[1],Q[2]);let K=new Set,J=/data-stx-island="([^"]+)"[^>]*?data-stx-src="([^"]+)"/g,W;while((W=J.exec(q))!==null)if(gq.has(Z.get(W[1])||""))K.add(W[2]);let z=q.lastIndexOf("</head>");if(K.size===0||z===-1)return q;let A=Array.from(K).filter((G)=>!q.includes(`rel="prefetch" href="${G}"`)).map((G)=>`<link rel="prefetch" href="${G}" as="script">`).join("");if(!A)return q;return q.slice(0,z)+A+q.slice(z)}var fq=["STX_SITE_URL","SITE_URL","URL","VERCEL_URL","CF_PAGES_URL"];function h(q){let Z=q.trim();if(!Z)return null;let $=/^https?:\/\//i.test(Z)?Z:`https://${Z}`;try{return new URL($).origin+new URL($).pathname.replace(/\/$/,"")}catch{return null}}function mq(q){for(let Z of fq){let $=q[Z];if(!$)continue;let Q=h($);if(Q)return{url:Q,source:"env",via:Z}}return null}function $q(q){if(!q||typeof q!=="object")return null;let Z=q,$=Z.site?.url??Z.url??Z.app?.url;return typeof $==="string"?h($):null}async function dq(q){let{existsSync:Z}=await import("fs"),$=await import("path");for(let Q of["site.config.ts","site.config.js",".config/site.config.ts"]){let K=$.resolve(q,Q);if(!Z(K))continue;try{let J=await import(K),W=$q(J.default??J);if(W)return W}catch{}}return null}async function Kq(q={}){let Z=q.cwd??process.cwd(),$=q.env??process.env;if(q.explicit){let W=h(q.explicit);if(W)return{url:W,source:"option"}}let Q=$q(q.stxConfig);if(Q)return{url:Q,source:"stx.config",via:"site.url"};let K=await dq(Z);if(K)return{url:K,source:"site.config",via:"url"};let J=mq($);if(J)return J;return{url:"http://localhost",source:"fallback"}}function Wq(){return["No site URL configured \u2014 sitemap.xml and robots.txt will point at http://localhost."," Set one in stx.config.ts (site: { url: 'https://example.com' }), in site.config.ts,"," via --domain, or with SITE_URL in the build environment."].join(`
2
+ import{Bk as Fq,wk as Uq,xk as Iq}from"./chunk-wnmegbw0.js";import{Ck as Eq,Dk as Tq,Ek as Dq}from"./chunk-c0n802j1.js";import{Zk as _q,al as zq,bl as Aq}from"./chunk-57dxdvjh.js";import"./chunk-2853ye06.js";import"./chunk-fvsefztj.js";import{Jp as P,Sm as Oq,io as l,lo as Hq,rr as n}from"./chunk-xgw269cf.js";import"./chunk-aqxwwq6w.js";import"./chunk-t00ga194.js";import"./chunk-hrdd26r4.js";import"./chunk-7r79j1hs.js";import"./chunk-ay336sqb.js";import{ht as Xq}from"./chunk-6xs80tka.js";import{mt as d}from"./chunk-vaggjvsm.js";import"./chunk-q9ckfwhh.js";import"./chunk-kbah2k8e.js";import"./chunk-k3dtvay1.js";import{Tt as Bq}from"./chunk-eh3meyd3.js";import"./chunk-rzg9pars.js";import"./chunk-1qtd4ex2.js";import"./chunk-ec18ftcg.js";import"./chunk-fbmjw0bh.js";import"./chunk-53sjbp3z.js";import{lw as Yq,mw as Vq,nw as jq,ow as Gq,pw as Lq,qw as Nq}from"./chunk-kgbz86sa.js";import"./chunk-s60eccqk.js";import{Hw as y}from"./chunk-0s3ry262.js";import O from"fs";import j from"path";import{createHash as xq}from"crypto";function yq(q){try{return new Bun.Transpiler({loader:"js",minifyWhitespace:!0}).transformSync(q).replace(/\}(var |let |const |function )/g,"};$1").trim()}catch{return q}}var Pq=/<script\s+type="stx\/island"\s+data-stx-island="([^"]+)"([^>]*)>([\s\S]*?)<\/script>/g;function Cq(q){return Bun.hash(q).toString(16)}function Qq(q,Z={}){if(!q.includes('type="stx/island"'))return{html:q,chunks:[]};let $=new Map,Q=q.replace(Pq,(J,W,z,A)=>{let G=A.trim();if(!G)return J;if(/\bdata-stx-src=/.test(z))return J;if(Z.minify)G=yq(G);let X=Cq(G);if(!$.has(X))$.set(X,G);let B=Z.integrity?` data-stx-integrity="sha384-${xq("sha384").update(G).digest("base64")}"`:"";return`<script type="stx/island" data-stx-island="${W}" data-stx-src="/_stx/islands/${X}.js"${B}${z}></script>`}),K=Array.from($,([J,W])=>({hash:J,code:W}));return{html:Q,chunks:K}}var gq=new Set(["visible","idle"]);function Zq(q){if(!q.includes("data-stx-src="))return q;let Z=new Map,$=/data-stx-scope="([^"]+)"[^>]*?stx-hydrate="([^"]+)"/g,Q;while((Q=$.exec(q))!==null)Z.set(Q[1],Q[2]);let K=new Set,J=/data-stx-island="([^"]+)"[^>]*?data-stx-src="([^"]+)"/g,W;while((W=J.exec(q))!==null)if(gq.has(Z.get(W[1])||""))K.add(W[2]);let z=q.lastIndexOf("</head>");if(K.size===0||z===-1)return q;let A=Array.from(K).filter((G)=>!q.includes(`rel="prefetch" href="${G}"`)).map((G)=>`<link rel="prefetch" href="${G}" as="script">`).join("");if(!A)return q;return q.slice(0,z)+A+q.slice(z)}var fq=["STX_SITE_URL","SITE_URL","URL","VERCEL_URL","CF_PAGES_URL"];function h(q){let Z=q.trim();if(!Z)return null;let $=/^https?:\/\//i.test(Z)?Z:`https://${Z}`;try{return new URL($).origin+new URL($).pathname.replace(/\/$/,"")}catch{return null}}function mq(q){for(let Z of fq){let $=q[Z];if(!$)continue;let Q=h($);if(Q)return{url:Q,source:"env",via:Z}}return null}function $q(q){if(!q||typeof q!=="object")return null;let Z=q,$=Z.site?.url??Z.url??Z.app?.url;return typeof $==="string"?h($):null}async function dq(q){let{existsSync:Z}=await import("fs"),$=await import("path");for(let Q of["site.config.ts","site.config.js",".config/site.config.ts"]){let K=$.resolve(q,Q);if(!Z(K))continue;try{let J=await import(K),W=$q(J.default??J);if(W)return W}catch{}}return null}async function Kq(q={}){let Z=q.cwd??process.cwd(),$=q.env??process.env;if(q.explicit){let W=h(q.explicit);if(W)return{url:W,source:"option"}}let Q=$q(q.stxConfig);if(Q)return{url:Q,source:"stx.config",via:"site.url"};let K=await dq(Z);if(K)return{url:K,source:"site.config",via:"url"};let J=mq($);if(J)return J;return{url:"http://localhost",source:"fallback"}}function Wq(){return["No site URL configured \u2014 sitemap.xml and robots.txt will point at http://localhost."," Set one in stx.config.ts (site: { url: 'https://example.com' }), in site.config.ts,"," via --domain, or with SITE_URL in the build environment."].join(`
3
3
  `)}class wq{cacheDir;cache=new Map;loaded=!1;salt;constructor(q,Z=""){this.cacheDir=q,this.salt=Z}async load(){if(this.loaded)return;let q=j.join(this.cacheDir,"build-cache.json");if(await Bun.file(q).exists())try{let Z=JSON.parse(await Bun.file(q).text());for(let[$,Q]of Object.entries(Z))this.cache.set($,Q)}catch{}this.loaded=!0}async save(){await O.promises.mkdir(this.cacheDir,{recursive:!0});let q=j.join(this.cacheDir,"build-cache.json"),Z={};for(let[$,Q]of this.cache)Z[$]=Q;await Bun.write(q,JSON.stringify(Z))}getHash(q,Z){let $=O.statSync(q),Q=`${this.salt}|${q}:${$.mtimeMs}`;for(let J of Z)if(O.existsSync(J)){let W=O.statSync(J);Q+=`:${J}:${W.mtimeMs}`}let K=0;for(let J=0;J<Q.length;J++){let W=Q.charCodeAt(J);K=(K<<5)-K+W,K=K&K}return Math.abs(K).toString(36)}get(q,Z){let $=this.cache.get(q);if(!$)return null;let Q=this.getHash(Z,$.dependencies??[]);if($.hash!==Q)return this.cache.delete(q),null;return $.html}set(q,Z,$,Q){let K=this.getHash(Z,$);this.cache.set(q,{hash:K,html:Q,generatedAt:Date.now(),dependencies:$})}}class Rq{cacheDir;constructor(q){this.cacheDir=q}getFilePath(q){let Z=q.replace(/[^a-zA-Z0-9]/g,"_")+".json";return j.join(this.cacheDir,"isr",Z)}async get(q){let Z=this.getFilePath(q);if(!await Bun.file(Z).exists())return null;try{return JSON.parse(await Bun.file(Z).text())}catch{return null}}async set(q,Z){let $=this.getFilePath(q);await O.promises.mkdir(j.dirname($),{recursive:!0}),await Bun.write($,JSON.stringify(Z))}async invalidate(q){let Z=this.getFilePath(q);if(await Bun.file(Z).exists())await O.promises.unlink(Z)}async invalidatePattern(q){let Z=j.join(this.cacheDir,"isr");if(!O.existsSync(Z))return;let $=await O.promises.readdir(Z);for(let Q of $){let K=Q.replace(/_/g,"/").replace(".json","");if(q.test(K))await O.promises.unlink(j.join(Z,Q))}}async needsRevalidation(q){let Z=await this.get(q);if(!Z)return!0;return Date.now()>Z.revalidateAfter}}async function pq(q){try{let $=(await Bun.file(q).text()).match(/<script\s+server\b[^>]*>([\s\S]*?)<\/script>/i);if(!$)return null;let Q=$[1];if(!Q.includes("getStaticPaths"))return null;let K=Q.match(/(?:export\s+)?(?:async\s+)?function\s+getStaticPaths\s*\([^)]*\)\s*\{([\s\S]*?)\n[\t ]*\}/);if(!K)return null;return await Function(`
4
4
  return (async function getStaticPaths() {
5
5
  ${K[1]}
@@ -41,7 +41,7 @@ ${q.map((Q)=>{let K=new URL(Q.loc,Z).href,J=` <url>
41
41
  ${$.join(`
42
42
  `)}
43
43
  </channel>
44
- </rss>`}async function aq(q={}){let Z=Date.now();Aq();let $=await n(process.cwd()),Q=$.build||{},K=await Kq({explicit:q.domain||Q.domain,stxConfig:$});if(K.source==="fallback"&&(q.sitemap??Q.sitemap??!0))console.warn(Wq());let J={router:q.router??$?.router??{},pagesDir:q.pagesDir||Q.pagesDir||"pages",outputDir:q.outputDir||Q.outputDir||"dist",baseUrl:q.baseUrl||Q.baseUrl||"/",domain:K.url,revalidate:q.revalidate??Q.revalidate??!1,sitemap:q.sitemap??Q.sitemap??!0,sitemapExclude:q.sitemapExclude||Q.sitemapExclude||[],robots:q.robots??Q.robots??!0,rss:q.rss??Q.rss??!1,concurrency:q.concurrency||Q.concurrency||10,cache:q.cache??Q.cache??!0,cacheDir:q.cacheDir||Q.cacheDir||d(process.cwd(),"ssg-cache"),contentLoaders:q.contentLoaders||[],hooks:q.hooks||{},minify:q.minify??Q.minify??!0,generate404:q.generate404??Q.generate404??!0,publicDir:q.publicDir||Q.publicDir||"public",trailingSlash:q.trailingSlash??Q.trailingSlash??!1,cleanOutput:q.cleanOutput??Q.cleanOutput??!0,cacheSalt:q.cacheSalt??"",partialsDir:q.partialsDir||$?.partialsDir||"",componentsDir:q.componentsDir||$?.componentsDir||"",failOnIncludeError:q.failOnIncludeError??!0,chunkIslands:q.chunkIslands===!0,prefetchIslands:q.prefetchIslands===!0,integrityIslands:q.integrityIslands===!0},W={totalPages:0,successCount:0,failedCount:0,cachedCount:0,buildTime:0,pages:[],errors:[]},z=new Set,A=0,G={},X={};await J.hooks.onBuildStart?.();try{if(J.cleanOutput&&O.existsSync(J.outputDir))await O.promises.rm(J.outputDir,{recursive:!0});await O.promises.mkdir(J.outputDir,{recursive:!0});let L=J.cacheSalt;if(!L){let{resolveUserCssConfig:Y,fingerprintConfig:N}=await import("./chunk-gkq323fg.js");try{L=N(await Y(process.cwd()))}catch{}}let{prepareImageDelivery:_}=await import("./chunk-8tsr51hj.js"),F=await _(J.publicDir,J.outputDir);if(F.count>0)console.log(`Optimized ${F.count} image(s) for responsive delivery`),L=[L,F.fingerprint].filter(Boolean).join(":");let C=new wq(J.cacheDir,L);if(J.cache)await C.load();try{let{generateCss:Y}=await import("./chunk-gkq323fg.js");await Y('<div class="bg-black"></div>')}catch{}Yq();let kq=$?.routeMiddleware?.dir||"middleware";await jq(process.cwd(),kq);let p=Vq();if(p.length>0)console.log(`Loaded ${p.length} middleware: ${p.join(", ")}`);let bq=$?.routeMiddleware?.global||[];Fq($?.layoutsDir,d(process.cwd()));let i=l(".",{pagesDir:J.pagesDir,pagesDirs:$?._layerPageDirs});console.log(`Found ${i.length} routes`);let g=[];for(let Y of i){let N=await uq(Y);for(let H of N)g.push({route:Y,url:H.url,params:H.params,props:H.props,revalidate:H.revalidate??J.revalidate})}W.totalPages=g.length,console.log(`Generating ${W.totalPages} pages...`);let b=[],s=[];for(let Y=0;Y<g.length;Y+=J.concurrency)s.push(g.slice(Y,Y+J.concurrency));for(let Y of s)await Promise.all(Y.map(async(N)=>{let{route:H,url:V,params:S,props:Sq,revalidate:a}=N;try{await J.hooks.onPageStart?.(V);let w=await Bun.file(H.filePath).text(),R=JJ(w),vq=R?.middleware?Array.isArray(R.middleware)?R.middleware:[R.middleware]:[],t=[...bq,...vq];if(t.length>0){let D=Gq(V,S,R||{}),M=Lq(D,null,void 0,{isServer:!0}),E=await Nq(t,M);if(E.redirect){let U=typeof E.redirect==="string"?E.redirect:E.redirect.path||E.redirect,x=QJ(String(U),V),k=ZJ(V,J);await O.promises.mkdir(j.dirname(k),{recursive:!0}),await Bun.write(k,x),W.successCount++,W.pages.push({route:V,outputPath:k,size:Buffer.byteLength(x),cached:!1}),b.push({loc:V,lastmod:new Date().toISOString()}),console.log(` \u21B3 ${V} \u2192 redirect to ${U}`);return}if(E.abort){let U=typeof E.abort==="string"?E.abort:E.abort?.error?.message||"aborted";console.warn(` \u21B3 ${V} \u2014 middleware aborted: ${U}`);return}}let e=w.match(/definePageMeta\s*\(\s*\{[\s\S]*?validate\s*:\s*((?:\([^)]*\)|[^,}])*(?:\{[\s\S]*?\})?)/m);if(e)try{if(!await Function("route",`
44
+ </rss>`}async function aq(q={}){let Z=Date.now();Aq();let $=await n(process.cwd()),Q=$.build||{},K=await Kq({explicit:q.domain||Q.domain,stxConfig:$});if(K.source==="fallback"&&(q.sitemap??Q.sitemap??!0))console.warn(Wq());let J={router:q.router??$?.router??{},pagesDir:q.pagesDir||Q.pagesDir||"pages",outputDir:q.outputDir||Q.outputDir||"dist",baseUrl:q.baseUrl||Q.baseUrl||"/",domain:K.url,revalidate:q.revalidate??Q.revalidate??!1,sitemap:q.sitemap??Q.sitemap??!0,sitemapExclude:q.sitemapExclude||Q.sitemapExclude||[],robots:q.robots??Q.robots??!0,rss:q.rss??Q.rss??!1,concurrency:q.concurrency||Q.concurrency||10,cache:q.cache??Q.cache??!0,cacheDir:q.cacheDir||Q.cacheDir||d(process.cwd(),"ssg-cache"),contentLoaders:q.contentLoaders||[],hooks:q.hooks||{},minify:q.minify??Q.minify??!0,generate404:q.generate404??Q.generate404??!0,publicDir:q.publicDir||Q.publicDir||"public",trailingSlash:q.trailingSlash??Q.trailingSlash??!1,cleanOutput:q.cleanOutput??Q.cleanOutput??!0,cacheSalt:q.cacheSalt??"",partialsDir:q.partialsDir||$?.partialsDir||"",componentsDir:q.componentsDir||$?.componentsDir||"",failOnIncludeError:q.failOnIncludeError??!0,chunkIslands:q.chunkIslands===!0,prefetchIslands:q.prefetchIslands===!0,integrityIslands:q.integrityIslands===!0},W={totalPages:0,successCount:0,failedCount:0,cachedCount:0,buildTime:0,pages:[],errors:[]},z=new Set,A=0,G={},X={};await J.hooks.onBuildStart?.();try{if(J.cleanOutput&&O.existsSync(J.outputDir))await O.promises.rm(J.outputDir,{recursive:!0});await O.promises.mkdir(J.outputDir,{recursive:!0});let L=J.cacheSalt;if(!L){let{resolveUserCssConfig:Y,fingerprintConfig:N}=await import("./chunk-pwhf5kte.js");try{L=N(await Y(process.cwd()))}catch{}}let{prepareImageDelivery:_}=await import("./chunk-4pg6wbt6.js"),F=await _(J.publicDir,J.outputDir);if(F.count>0)console.log(`Optimized ${F.count} image(s) for responsive delivery`),L=[L,F.fingerprint].filter(Boolean).join(":");let C=new wq(J.cacheDir,L);if(J.cache)await C.load();try{let{generateCss:Y}=await import("./chunk-pwhf5kte.js");await Y('<div class="bg-black"></div>')}catch{}Yq();let kq=$?.routeMiddleware?.dir||"middleware";await jq(process.cwd(),kq);let p=Vq();if(p.length>0)console.log(`Loaded ${p.length} middleware: ${p.join(", ")}`);let bq=$?.routeMiddleware?.global||[];Fq($?.layoutsDir,d(process.cwd()));let i=l(".",{pagesDir:J.pagesDir,pagesDirs:$?._layerPageDirs});console.log(`Found ${i.length} routes`);let g=[];for(let Y of i){let N=await uq(Y);for(let H of N)g.push({route:Y,url:H.url,params:H.params,props:H.props,revalidate:H.revalidate??J.revalidate})}W.totalPages=g.length,console.log(`Generating ${W.totalPages} pages...`);let b=[],s=[];for(let Y=0;Y<g.length;Y+=J.concurrency)s.push(g.slice(Y,Y+J.concurrency));for(let Y of s)await Promise.all(Y.map(async(N)=>{let{route:H,url:V,params:S,props:Sq,revalidate:a}=N;try{await J.hooks.onPageStart?.(V);let w=await Bun.file(H.filePath).text(),R=JJ(w),vq=R?.middleware?Array.isArray(R.middleware)?R.middleware:[R.middleware]:[],t=[...bq,...vq];if(t.length>0){let D=Gq(V,S,R||{}),M=Lq(D,null,void 0,{isServer:!0}),E=await Nq(t,M);if(E.redirect){let U=typeof E.redirect==="string"?E.redirect:E.redirect.path||E.redirect,x=QJ(String(U),V),k=ZJ(V,J);await O.promises.mkdir(j.dirname(k),{recursive:!0}),await Bun.write(k,x),W.successCount++,W.pages.push({route:V,outputPath:k,size:Buffer.byteLength(x),cached:!1}),b.push({loc:V,lastmod:new Date().toISOString()}),console.log(` \u21B3 ${V} \u2192 redirect to ${U}`);return}if(E.abort){let U=typeof E.abort==="string"?E.abort:E.abort?.error?.message||"aborted";console.warn(` \u21B3 ${V} \u2014 middleware aborted: ${U}`);return}}let e=w.match(/definePageMeta\s*\(\s*\{[\s\S]*?validate\s*:\s*((?:\([^)]*\)|[^,}])*(?:\{[\s\S]*?\})?)/m);if(e)try{if(!await Function("route",`
45
45
  const validate = ${e[1]};
46
46
  return validate(route);
47
47
  `)({params:S})){console.warn(`Validation failed for ${V}, skipping...`);return}}catch{}let I=null,qq=!1;if(J.cache){if(I=C.get(V,H.filePath),I)qq=!0,W.cachedCount++}if(!I){let D=await Mq(H,S,Sq,J,$);if(I=D.html,J.failOnIncludeError&&D.includeFailures.length>0)throw Error(`${D.includeFailures.length} include(s) failed to resolve, so this page would have shipped with an error banner where the markup should be:
@@ -292,6 +292,18 @@ export interface CustomDirective {
292
292
  handler: CustomDirectiveHandler;
293
293
  /** Whether the directive has a closing tag (e.g., @directive...@enddirective) */
294
294
  hasEndTag?: boolean;
295
+ /**
296
+ * Pass each argument exactly as written, quotes included, split only at
297
+ * top-level commas, instead of with its quotes stripped.
298
+ *
299
+ * Stripping quotes erases the difference between `'title'` (the word) and
300
+ * `title` (a variable), and splitting at every comma breaks `fn(a, b)` apart,
301
+ * so a directive that evaluates its arguments needs this. The call is also
302
+ * matched by balance rather than a pattern, so `'Smile :)'` and nested calls
303
+ * stay inside it. Parse a literal with `parseStringLiteral` and evaluate the
304
+ * rest with `evaluateDirectiveArgument` (directive-arguments.ts).
305
+ */
306
+ rawParams?: boolean;
295
307
  /** Optional description for documentation */
296
308
  description?: string;
297
309
  }