@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.
- package/dist/binding-manifest.d.ts +32 -0
- package/dist/binding-manifest.js +64 -0
- package/dist/caching.js +1 -0
- package/dist/chunk-020hpw7m.js +2 -0
- package/dist/{chunk-5g9vbhbt.js → chunk-05zfez2g.js} +1 -1
- package/dist/{chunk-6nvm939g.js → chunk-0k1mtwby.js} +1 -1
- package/dist/chunk-1bct7pfj.js +2 -0
- package/dist/{chunk-xec226zb.js → chunk-1c9453w0.js} +1 -1
- package/dist/chunk-1exk54qt.js +2 -0
- package/dist/{chunk-r46cg4qa.js → chunk-1vt1h19d.js} +1 -1
- package/dist/{chunk-cqw3c1xg.js → chunk-1yh6pq6b.js} +1 -1
- package/dist/{chunk-0sq5mdrh.js → chunk-34f8c48f.js} +1 -1
- package/dist/chunk-39qy76ek.js +2 -0
- package/dist/{chunk-hrmxxs0w.js → chunk-3n8avn17.js} +1 -1
- package/dist/chunk-3xpk99z4.js +3 -0
- package/dist/{chunk-4wpxbkbe.js → chunk-4kbaky4w.js} +1 -1
- package/dist/{chunk-c33y19bn.js → chunk-59906baj.js} +1 -1
- package/dist/chunk-5apmxb4e.js +2 -0
- package/dist/{chunk-j82hfkcf.js → chunk-5atmahe4.js} +319 -321
- package/dist/{chunk-mkgbrbr9.js → chunk-5yhv1pe5.js} +1 -1
- package/dist/{chunk-p52cm7ch.js → chunk-64n10kpa.js} +1 -1
- package/dist/{chunk-anf2y2h5.js → chunk-6h3r8v55.js} +1 -1
- package/dist/chunk-6z9pm31g.js +2 -0
- package/dist/{chunk-25f0s8t1.js → chunk-72x5v5an.js} +1 -1
- package/dist/{chunk-wv8h3d68.js → chunk-72x62kch.js} +2 -2
- package/dist/chunk-7d2gfyk8.js +2 -0
- package/dist/{chunk-s4mmmwj4.js → chunk-7h9fajgs.js} +1 -1
- package/dist/{chunk-gwc54hjc.js → chunk-7kcfbc0j.js} +2 -2
- package/dist/{chunk-74r1zw2h.js → chunk-7qp3zphs.js} +1 -1
- package/dist/{chunk-1xwfws06.js → chunk-7t6h8crt.js} +2 -2
- package/dist/{chunk-sytwy0mg.js → chunk-86c461ca.js} +1 -1
- package/dist/chunk-86q2h34b.js +2 -0
- package/dist/{chunk-25b8z7xa.js → chunk-89mqnh67.js} +1 -1
- package/dist/{chunk-k5b9471y.js → chunk-8cc4vdkh.js} +1 -1
- package/dist/chunk-8tjg4wbr.js +2 -0
- package/dist/{chunk-1gwbw81h.js → chunk-8v2k21n4.js} +1 -1
- package/dist/{chunk-rh7mwwb4.js → chunk-8y1ve8tc.js} +2 -2
- package/dist/chunk-94sxzp5x.js +2 -0
- package/dist/{chunk-mznr9nyp.js → chunk-99b5tfxc.js} +1 -1
- package/dist/{chunk-saqbtgqr.js → chunk-9jbssgem.js} +197 -88
- package/dist/{chunk-yr7camcd.js → chunk-9nrgjtah.js} +1 -1
- package/dist/chunk-9p5gbhwp.js +2 -0
- package/dist/chunk-9yqt0nf2.js +2 -0
- package/dist/{chunk-ebd3z4j9.js → chunk-a1ghxrb1.js} +1 -1
- package/dist/chunk-a29x23g7.js +2 -0
- package/dist/chunk-at622hg0.js +2 -0
- package/dist/{chunk-bt8w146r.js → chunk-avwywzsj.js} +2 -2
- package/dist/{chunk-m39hb2a2.js → chunk-b5hank5e.js} +1 -1
- package/dist/{chunk-ts3wfjh9.js → chunk-bk1zb1xp.js} +1 -1
- package/dist/{chunk-ytx17bg2.js → chunk-bpb3xkg4.js} +1 -1
- package/dist/{chunk-01ggk8k8.js → chunk-bqdfbwdf.js} +1 -1
- package/dist/{chunk-yzv702kk.js → chunk-c30qnb6z.js} +1 -1
- package/dist/chunk-cappe4hd.js +2 -0
- package/dist/{chunk-drs93sry.js → chunk-cdq28mek.js} +1 -1
- package/dist/chunk-cmccdayf.js +2 -0
- package/dist/chunk-csv48xrx.js +2 -0
- package/dist/{chunk-e0kzyf1g.js → chunk-d86q27j8.js} +1 -1
- package/dist/{chunk-yhvrq2qd.js → chunk-dgv43ccd.js} +1 -1
- package/dist/{chunk-yq7fc8sa.js → chunk-dsztk2kp.js} +1 -1
- package/dist/{chunk-ys56ecvx.js → chunk-dx2s51rr.js} +1 -1
- package/dist/{chunk-mbstfjnj.js → chunk-dxkjvajv.js} +1 -1
- package/dist/chunk-e4yskgtb.js +2 -0
- package/dist/{chunk-8tsr51hj.js → chunk-e631svvs.js} +1 -1
- package/dist/{chunk-3b0mgxbk.js → chunk-e7np0h2q.js} +1 -1
- package/dist/chunk-efx05ah0.js +2 -0
- package/dist/{chunk-p5d8sb50.js → chunk-ejqpq0wc.js} +1 -1
- package/dist/chunk-erb294sv.js +2 -0
- package/dist/{chunk-k3w3gwm3.js → chunk-evfr2zgg.js} +1 -1
- package/dist/chunk-ewpbxy5m.js +2 -0
- package/dist/chunk-fs2t4rs3.js +33 -0
- package/dist/{chunk-ghd80w8a.js → chunk-g28fs4aa.js} +2 -2
- package/dist/chunk-gmm82dst.js +2 -0
- package/dist/{chunk-kznb3hdj.js → chunk-gsc2vds7.js} +1 -1
- package/dist/chunk-hke0k33t.js +2 -0
- package/dist/{chunk-da0tfpa2.js → chunk-hsbbfp0c.js} +1 -1
- package/dist/chunk-j5qygdy9.js +2 -0
- package/dist/{chunk-c73pyeak.js → chunk-jekrtm2j.js} +1 -1
- package/dist/chunk-jzfk5ect.js +2 -0
- package/dist/{chunk-rr49zrz6.js → chunk-kfkf9v2d.js} +1 -1
- package/dist/chunk-kq30wqp6.js +2 -0
- package/dist/{chunk-ntm1761b.js → chunk-krfx5562.js} +10 -10
- package/dist/{chunk-48x5bsqv.js → chunk-kw9cgyfc.js} +1 -1
- package/dist/{chunk-evtrctj9.js → chunk-m2j5skez.js} +1 -1
- package/dist/{chunk-q4cemvkw.js → chunk-mph46gkj.js} +1 -1
- package/dist/{chunk-k9ep24bd.js → chunk-mpyjnwje.js} +1 -1
- package/dist/{chunk-y956wapy.js → chunk-msywnv5n.js} +1 -1
- package/dist/{chunk-d389vyyz.js → chunk-mvat8js5.js} +2 -2
- package/dist/{chunk-gkq323fg.js → chunk-mz4breg8.js} +1 -1
- package/dist/{chunk-reskzmsb.js → chunk-n5657hm3.js} +1 -1
- package/dist/{chunk-4ecv1e3w.js → chunk-nexpxxsa.js} +1 -1
- package/dist/{chunk-1t3j8a0q.js → chunk-njx93b4z.js} +1 -1
- package/dist/{chunk-fx29n4vc.js → chunk-nm60wh6r.js} +1 -1
- package/dist/{chunk-thrv2nbz.js → chunk-ppzz953j.js} +1 -1
- package/dist/chunk-q5gzpn1n.js +2 -0
- package/dist/{chunk-fm5211ec.js → chunk-rwwfzkz4.js} +1 -1
- package/dist/chunk-san1eq0r.js +7 -0
- package/dist/chunk-tc4pj651.js +64 -0
- package/dist/chunk-tscr6bs8.js +2 -0
- package/dist/{chunk-jcbj3xp4.js → chunk-tthrw91n.js} +1 -1
- package/dist/{chunk-6nxq8rcw.js → chunk-v6e35j4g.js} +1 -1
- package/dist/{chunk-byjy9far.js → chunk-v7aa7v85.js} +1 -1
- package/dist/{chunk-r1rfwm01.js → chunk-vhta1be7.js} +1 -1
- package/dist/{chunk-hnffkdzc.js → chunk-w5j9tbsk.js} +1 -1
- package/dist/{chunk-567g1ttv.js → chunk-wfwj4390.js} +1 -1
- package/dist/{chunk-x7dv5rq7.js → chunk-wj21f1e0.js} +1 -1
- package/dist/{chunk-tmj45xq5.js → chunk-ws0282jr.js} +1 -1
- package/dist/chunk-x054qzma.js +2 -0
- package/dist/{chunk-4vcyf1kh.js → chunk-xh9paa2g.js} +2 -2
- package/dist/{chunk-f2qbjr3z.js → chunk-xj6bcwqa.js} +1 -1
- package/dist/{chunk-hk8j3eps.js → chunk-xvh1k1a4.js} +1 -1
- package/dist/{chunk-1spzqjve.js → chunk-y2f41kaq.js} +1 -1
- package/dist/{chunk-2swnc8kg.js → chunk-y6xzr30x.js} +1 -1
- package/dist/{chunk-beextc84.js → chunk-yg5r7bnm.js} +2 -2
- package/dist/{chunk-gbkq5qnw.js → chunk-zkwk82ss.js} +2 -2
- package/dist/chunk-zkxnarrh.js +2 -0
- package/dist/chunk-zp14ksx1.js +2 -0
- package/dist/{chunk-zv4nf0x2.js → chunk-zwhnkxgy.js} +1 -1
- package/dist/cli.js +14 -14
- package/dist/craft-entry.js +1 -1
- package/dist/craft.js +1 -1
- package/dist/custom-directives.js +21 -8
- package/dist/database.js +1 -1
- package/dist/directive-arguments.d.ts +107 -0
- package/dist/directive-arguments.js +171 -0
- package/dist/expressions.d.ts +1 -1
- package/dist/expressions.js +15 -3
- package/dist/head.d.ts +7 -30
- package/dist/head.js +15 -5
- package/dist/includes.js +2 -2
- package/dist/meta-arguments.d.ts +52 -0
- package/dist/meta-arguments.js +55 -0
- package/dist/process.js +1 -1
- package/dist/pwa.js +2 -2
- package/dist/runtime-globals.d.ts +22 -0
- package/dist/runtime-globals.js +46 -0
- package/dist/seo.d.ts +39 -17
- package/dist/seo.js +221 -255
- package/dist/signals.js +193 -84
- package/dist/slots.js +3 -3
- package/dist/ssg.js +2 -2
- package/dist/theme-tokens.d.ts +59 -0
- package/dist/theme-tokens.js +77 -17
- package/dist/ts-css-config.js +4 -2
- package/dist/types/directive-types.d.ts +12 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +77 -12
- package/dist/variable-extractor.d.ts +1 -1
- package/dist/variable-extractor.js +23 -3
- package/dist/visual-testing.js +1 -1
- package/package.json +5 -5
- package/dist/chunk-025z2btp.js +0 -2
- package/dist/chunk-19fp0z9s.js +0 -2
- package/dist/chunk-23sv24cm.js +0 -2
- package/dist/chunk-2p68bkza.js +0 -2
- package/dist/chunk-5grrqaqs.js +0 -2
- package/dist/chunk-6jx4r94x.js +0 -2
- package/dist/chunk-6p6b01kp.js +0 -4
- package/dist/chunk-a1rt1fms.js +0 -2
- package/dist/chunk-ahpmjg3f.js +0 -64
- package/dist/chunk-akp89n3w.js +0 -2
- package/dist/chunk-bqhrryqs.js +0 -2
- package/dist/chunk-czvzaa42.js +0 -3
- package/dist/chunk-de2cfcf5.js +0 -2
- package/dist/chunk-exhbc57n.js +0 -2
- package/dist/chunk-fhhp6pmx.js +0 -2
- package/dist/chunk-fqsrft0a.js +0 -2
- package/dist/chunk-gqzccw30.js +0 -2
- package/dist/chunk-h47e1c03.js +0 -2
- package/dist/chunk-kbemcvp1.js +0 -2
- package/dist/chunk-ks4j8fjz.js +0 -2
- package/dist/chunk-pbct6nxk.js +0 -2
- package/dist/chunk-peerbcwj.js +0 -2
- package/dist/chunk-pt1w6vq2.js +0 -2
- package/dist/chunk-rgda0gwf.js +0 -2
- package/dist/chunk-rwa10jm4.js +0 -2
- package/dist/chunk-snsh9ye4.js +0 -2
- package/dist/chunk-td39mtts.js +0 -2
- package/dist/chunk-trncnwem.js +0 -2
- package/dist/chunk-vk2j3vvs.js +0 -2
- package/dist/chunk-vph6zpeq.js +0 -2
- package/dist/chunk-wa3ks5zv.js +0 -2
- package/dist/chunk-xe93ae3r.js +0 -32
- package/dist/chunk-xer5kn26.js +0 -2
- package/dist/chunk-zv4gb3wb.js +0 -2
- 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 {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
136
|
+
metaTags += `<meta name="title" content="${escapeHtml(config.title)}">
|
|
150
137
|
`;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
138
|
+
}
|
|
139
|
+
if (config.description)
|
|
140
|
+
metaTags += `<meta name="description" content="${escapeHtml(config.description)}">
|
|
154
141
|
`;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
161
|
-
|
|
146
|
+
}
|
|
147
|
+
if (config.robots)
|
|
148
|
+
metaTags += `<meta name="robots" content="${escapeHtml(config.robots)}">
|
|
162
149
|
`;
|
|
163
|
-
|
|
164
|
-
|
|
150
|
+
if (config.canonical)
|
|
151
|
+
metaTags += `<link rel="canonical" href="${escapeHtml(config.canonical)}">
|
|
165
152
|
`;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
186
|
-
|
|
171
|
+
}
|
|
172
|
+
if (og.image) {
|
|
173
|
+
metaTags += `<meta property="og:image" content="${escapeHtml(og.image)}">
|
|
187
174
|
`;
|
|
188
|
-
|
|
189
|
-
|
|
175
|
+
if (og.imageAlt)
|
|
176
|
+
metaTags += `<meta property="og:image:alt" content="${escapeHtml(og.imageAlt)}">
|
|
190
177
|
`;
|
|
191
|
-
|
|
192
|
-
|
|
178
|
+
if (og.imageWidth)
|
|
179
|
+
metaTags += `<meta property="og:image:width" content="${escapeHtml(String(og.imageWidth))}">
|
|
193
180
|
`;
|
|
194
|
-
|
|
195
|
-
|
|
181
|
+
if (og.imageHeight)
|
|
182
|
+
metaTags += `<meta property="og:image:height" content="${escapeHtml(String(og.imageHeight))}">
|
|
196
183
|
`;
|
|
197
|
-
|
|
198
|
-
|
|
184
|
+
if (og.imageType)
|
|
185
|
+
metaTags += `<meta property="og:image:type" content="${escapeHtml(og.imageType)}">
|
|
199
186
|
`;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
187
|
+
}
|
|
188
|
+
if (og.siteName)
|
|
189
|
+
metaTags += `<meta property="og:site_name" content="${escapeHtml(og.siteName)}">
|
|
203
190
|
`;
|
|
204
|
-
|
|
205
|
-
|
|
191
|
+
if (og.locale)
|
|
192
|
+
metaTags += `<meta property="og:locale" content="${escapeHtml(og.locale)}">
|
|
206
193
|
`;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
244
|
-
|
|
229
|
+
}
|
|
230
|
+
if (twitter.site)
|
|
231
|
+
metaTags += `<meta name="twitter:site" content="${escapeHtml(twitter.site)}">
|
|
245
232
|
`;
|
|
246
|
-
|
|
247
|
-
|
|
233
|
+
if (twitter.creator)
|
|
234
|
+
metaTags += `<meta name="twitter:creator" content="${escapeHtml(twitter.creator)}">
|
|
248
235
|
`;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
371
|
-
return
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
379
353
|
}
|
|
380
354
|
export const metaDirective = {
|
|
381
355
|
name: "meta",
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if (
|
|
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
|
|
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);
|