@templatical/quality 0.7.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +168 -63
- package/dist/index.js +643 -253
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,67 +1,74 @@
|
|
|
1
|
-
import { HEADING_LEVEL_FONT_SIZE as e, isButton as t,
|
|
2
|
-
import { Parser as
|
|
1
|
+
import { HEADING_LEVEL_FONT_SIZE as e, isButton as t, isHtml as n, isImage as r, isMenu as i, isParagraph as a, isSection as o, isSocialIcons as s, isTable as ee, isTitle as c, isVideo as te } from "@templatical/types";
|
|
2
|
+
import { Parser as l } from "htmlparser2";
|
|
3
3
|
//#region \0rolldown/runtime.js
|
|
4
|
-
var
|
|
4
|
+
var u = Object.defineProperty, d = (e, t) => {
|
|
5
5
|
let n = {};
|
|
6
|
-
for (var r in e)
|
|
6
|
+
for (var r in e) u(n, r, {
|
|
7
7
|
get: e[r],
|
|
8
8
|
enumerable: !0
|
|
9
9
|
});
|
|
10
|
-
return t ||
|
|
11
|
-
},
|
|
10
|
+
return t || u(n, Symbol.toStringTag, { value: "Module" }), n;
|
|
11
|
+
}, f = {
|
|
12
12
|
altMaxLength: 125,
|
|
13
13
|
minFontSize: 14,
|
|
14
14
|
allCapsMinLength: 20,
|
|
15
15
|
minTouchTargetPx: 44
|
|
16
|
-
}
|
|
16
|
+
}, p = [
|
|
17
|
+
"localhost",
|
|
18
|
+
"127.0.0.1",
|
|
19
|
+
"0.0.0.0",
|
|
20
|
+
"*.local",
|
|
21
|
+
"*.staging.*",
|
|
22
|
+
"*.dev.*"
|
|
23
|
+
];
|
|
17
24
|
//#endregion
|
|
18
25
|
//#region src/contrast.ts
|
|
19
|
-
function
|
|
20
|
-
let n =
|
|
26
|
+
function m(e, t) {
|
|
27
|
+
let n = g(e), r = g(t);
|
|
21
28
|
if (!n || !r) return NaN;
|
|
22
|
-
let i =
|
|
29
|
+
let i = v(n), a = v(r), o = Math.max(i, a), s = Math.min(i, a);
|
|
23
30
|
return (o + .05) / (s + .05);
|
|
24
31
|
}
|
|
25
|
-
var
|
|
26
|
-
function
|
|
32
|
+
var ne = /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i, h = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
|
|
33
|
+
function g(e) {
|
|
27
34
|
if (typeof e != "string") return null;
|
|
28
|
-
let t = e.trim(), n =
|
|
35
|
+
let t = e.trim(), n = h.exec(t);
|
|
29
36
|
if (n) return {
|
|
30
37
|
r: parseInt(n[1], 16),
|
|
31
38
|
g: parseInt(n[2], 16),
|
|
32
39
|
b: parseInt(n[3], 16)
|
|
33
40
|
};
|
|
34
|
-
let r =
|
|
41
|
+
let r = ne.exec(t);
|
|
35
42
|
return r ? {
|
|
36
43
|
r: parseInt(r[1] + r[1], 16),
|
|
37
44
|
g: parseInt(r[2] + r[2], 16),
|
|
38
45
|
b: parseInt(r[3] + r[3], 16)
|
|
39
46
|
} : null;
|
|
40
47
|
}
|
|
41
|
-
function
|
|
42
|
-
return
|
|
48
|
+
function _(e) {
|
|
49
|
+
return g(e ?? "") !== null;
|
|
43
50
|
}
|
|
44
|
-
function
|
|
45
|
-
let r =
|
|
51
|
+
function v({ r: e, g: t, b: n }) {
|
|
52
|
+
let r = y(e / 255), i = y(t / 255), a = y(n / 255);
|
|
46
53
|
return .2126 * r + .7152 * i + .0722 * a;
|
|
47
54
|
}
|
|
48
|
-
function
|
|
55
|
+
function y(e) {
|
|
49
56
|
return e <= .03928 ? e / 12.92 : ((e + .055) / 1.055) ** 2.4;
|
|
50
57
|
}
|
|
51
58
|
//#endregion
|
|
52
59
|
//#region src/walk.ts
|
|
53
|
-
var
|
|
60
|
+
var re = "#ffffff";
|
|
54
61
|
function b(e, t) {
|
|
55
|
-
let n =
|
|
56
|
-
if (t(e, n), !
|
|
57
|
-
let i = e.styles?.backgroundColor,
|
|
62
|
+
let n = _(e.settings.backgroundColor) ? e.settings.backgroundColor.toLowerCase() : re, r = (e, n) => {
|
|
63
|
+
if (t(e, n), !o(e)) return;
|
|
64
|
+
let i = e.styles?.backgroundColor, a = _(i) ? i.toLowerCase() : n.resolvedBackgroundColor;
|
|
58
65
|
e.children.forEach((t, i) => {
|
|
59
66
|
t.forEach((t) => r(t, {
|
|
60
67
|
parent: e,
|
|
61
68
|
section: e,
|
|
62
69
|
columnIndex: i,
|
|
63
70
|
depth: n.depth + 1,
|
|
64
|
-
resolvedBackgroundColor:
|
|
71
|
+
resolvedBackgroundColor: a
|
|
65
72
|
}));
|
|
66
73
|
});
|
|
67
74
|
};
|
|
@@ -74,91 +81,137 @@ function b(e, t) {
|
|
|
74
81
|
});
|
|
75
82
|
}
|
|
76
83
|
//#endregion
|
|
84
|
+
//#region src/run-rules.ts
|
|
85
|
+
function x(e, t, n, r) {
|
|
86
|
+
if (n.disabled === !0) return [];
|
|
87
|
+
let i = ie(n, t), a = [];
|
|
88
|
+
function o(e, t, n) {
|
|
89
|
+
return {
|
|
90
|
+
blockId: n.blockId,
|
|
91
|
+
ruleId: e,
|
|
92
|
+
severity: t,
|
|
93
|
+
message: r(i.locale, e, n.params),
|
|
94
|
+
fix: n.fix
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
b(e, (e, n) => {
|
|
98
|
+
for (let r of t) {
|
|
99
|
+
let t = i.severity(r.meta.id);
|
|
100
|
+
if (t === "off" || !r.block) continue;
|
|
101
|
+
let s = r.block(e, n, i);
|
|
102
|
+
s !== null && a.push(o(r.meta.id, t, s));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
for (let n of t) {
|
|
106
|
+
let t = i.severity(n.meta.id);
|
|
107
|
+
if (t === "off" || !n.template) continue;
|
|
108
|
+
let r = n.template(e, i);
|
|
109
|
+
for (let e of r) a.push(o(n.meta.id, t, e));
|
|
110
|
+
}
|
|
111
|
+
return a;
|
|
112
|
+
}
|
|
113
|
+
function ie(e, t) {
|
|
114
|
+
let n = e.rules ?? {}, r = {
|
|
115
|
+
...f,
|
|
116
|
+
...e.thresholds ?? {}
|
|
117
|
+
}, i = { nonProductionHosts: e.links?.nonProductionHosts ?? p };
|
|
118
|
+
return {
|
|
119
|
+
locale: e.locale ?? "en",
|
|
120
|
+
rules: n,
|
|
121
|
+
thresholds: r,
|
|
122
|
+
links: i,
|
|
123
|
+
severity: (e) => {
|
|
124
|
+
let r = n[e];
|
|
125
|
+
return r === void 0 ? t.find((t) => t.meta.id === e)?.meta.severity ?? "warning" : r;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
77
130
|
//#region src/accessibility/messages/de.ts
|
|
78
|
-
var
|
|
79
|
-
"img-missing-alt": "Bild ohne Alt-Text. Füge eine kurze Beschreibung hinzu oder markiere das Bild als dekorativ.",
|
|
80
|
-
"img-alt-is-filename": "Alt-Text sieht wie ein Dateiname aus (\"{alt}\"). Beschreibe stattdessen kurz, was das Bild zeigt.",
|
|
81
|
-
"img-alt-too-long": "Alt-Text ist {length} Zeichen lang; bleibe unter {max}.",
|
|
82
|
-
"img-decorative-needs-empty-alt": "Dekoratives Bild hat Alt-Text. Entferne den Alt-Text oder hebe die Markierung als dekorativ auf.",
|
|
83
|
-
"img-linked-no-context": "Verlinktes Bild beschreibt nur das Motiv, nicht das Linkziel. Nenne das Ziel (z. B. „Frühlingssale ansehen“).",
|
|
84
|
-
"heading-empty": "Überschrift ist leer. Füge Text hinzu oder entferne den Block.",
|
|
85
|
-
"heading-skip-level": "Überschrift springt von H{from} auf H{to}. Eine Ebene pro Schritt.",
|
|
86
|
-
"heading-multiple-h1": "E-Mail enthält mehr als eine H1. Verwende H1 nur einmal für die Hauptüberschrift.",
|
|
87
|
-
"link-empty": "Ein Link in diesem Block hat keinen Text und kein beschriebenes Bild.",
|
|
88
|
-
"link-vague-text": "Link-Text „{text}“ ist unspezifisch. Beschreibe stattdessen das Ziel.",
|
|
89
|
-
"link-href-empty": "Ein Link in diesem Block hat ein leeres oder „#“-href.",
|
|
90
|
-
"link-target-blank-no-rel": "Link öffnet in neuem Tab, aber rel=\"noopener\" fehlt – ergänze es, damit das Ziel nicht auf window.opener zugreifen kann.",
|
|
91
|
-
"text-all-caps": "Längere Texte in Großbuchstaben sind schwerer lesbar. Verwende Groß- und Kleinschreibung.",
|
|
92
|
-
"text-low-contrast": "Überschriftskontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
|
|
93
|
-
"text-too-small": "Text ist {size}px; mindestens {min}px verwenden.",
|
|
94
|
-
"button-vague-label": "Button-Beschriftung „{text}“ ist unspezifisch. Beschreibe die Aktion.",
|
|
95
|
-
"button-touch-target": "Button ist etwa {height}px hoch; mindestens {min}px verwenden, um Fehltipper auf Mobilgeräten zu vermeiden.",
|
|
96
|
-
"button-low-contrast": "Buttontextkontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
|
|
97
|
-
"missing-preheader": "Kein Preheader-Text gesetzt. Postfächer zeigen sonst Bruchstücke des ersten Blocks an."
|
|
98
|
-
},
|
|
99
|
-
"img-missing-alt": "Image is missing alt text. Add a short description, or mark the image as decorative.",
|
|
100
|
-
"img-alt-is-filename": "Alt text looks like a filename (\"{alt}\"). Replace with a short description of what the image conveys.",
|
|
101
|
-
"img-alt-too-long": "Alt text is {length} characters; aim for under {max}.",
|
|
102
|
-
"img-decorative-needs-empty-alt": "Decorative image has alt text. Either clear the alt text or unmark the image as decorative.",
|
|
103
|
-
"img-linked-no-context": "Linked image alt describes the image but not the link destination. Include where the link goes (e.g. 'Buy spring sale').",
|
|
104
|
-
"heading-empty": "Heading is empty. Add text or remove the block.",
|
|
105
|
-
"heading-skip-level": "Heading jumps from H{from} to H{to}. Step one level at a time.",
|
|
106
|
-
"heading-multiple-h1": "Email has more than one H1. Use H1 once for the main heading.",
|
|
107
|
-
"link-empty": "A link in this block has no text and no described image.",
|
|
108
|
-
"link-vague-text": "Link text \"{text}\" is vague. Describe the destination instead.",
|
|
109
|
-
"link-href-empty": "A link in this block has an empty or '#' href.",
|
|
110
|
-
"link-target-blank-no-rel": "Link opens in a new tab but is missing rel=\"noopener\" — add it to prevent the destination from accessing window.opener.",
|
|
111
|
-
"text-all-caps": "Long all-caps text is harder to read for everyone. Use sentence case.",
|
|
112
|
-
"text-low-contrast": "Heading contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
|
|
113
|
-
"text-too-small": "Text is {size}px; aim for at least {min}px.",
|
|
114
|
-
"button-vague-label": "Button label \"{text}\" is vague. Describe the action.",
|
|
115
|
-
"button-touch-target": "Button is roughly {height}px tall; aim for at least {min}px to avoid mis-taps on mobile.",
|
|
116
|
-
"button-low-contrast": "Button text contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
|
|
117
|
-
"missing-preheader": "No preheader text set. Inboxes will fall back to fragments of the first block."
|
|
118
|
-
},
|
|
119
|
-
"./de.ts":
|
|
120
|
-
"./en.ts":
|
|
121
|
-
}),
|
|
122
|
-
for (let e in
|
|
131
|
+
var ae = /* @__PURE__ */ d({ default: () => oe }), oe = {
|
|
132
|
+
"a11y.img-missing-alt": "Bild ohne Alt-Text. Füge eine kurze Beschreibung hinzu oder markiere das Bild als dekorativ.",
|
|
133
|
+
"a11y.img-alt-is-filename": "Alt-Text sieht wie ein Dateiname aus (\"{alt}\"). Beschreibe stattdessen kurz, was das Bild zeigt.",
|
|
134
|
+
"a11y.img-alt-too-long": "Alt-Text ist {length} Zeichen lang; bleibe unter {max}.",
|
|
135
|
+
"a11y.img-decorative-needs-empty-alt": "Dekoratives Bild hat Alt-Text. Entferne den Alt-Text oder hebe die Markierung als dekorativ auf.",
|
|
136
|
+
"a11y.img-linked-no-context": "Verlinktes Bild beschreibt nur das Motiv, nicht das Linkziel. Nenne das Ziel (z. B. „Frühlingssale ansehen“).",
|
|
137
|
+
"a11y.heading-empty": "Überschrift ist leer. Füge Text hinzu oder entferne den Block.",
|
|
138
|
+
"a11y.heading-skip-level": "Überschrift springt von H{from} auf H{to}. Eine Ebene pro Schritt.",
|
|
139
|
+
"a11y.heading-multiple-h1": "E-Mail enthält mehr als eine H1. Verwende H1 nur einmal für die Hauptüberschrift.",
|
|
140
|
+
"a11y.link-empty": "Ein Link in diesem Block hat keinen Text und kein beschriebenes Bild.",
|
|
141
|
+
"a11y.link-vague-text": "Link-Text „{text}“ ist unspezifisch. Beschreibe stattdessen das Ziel.",
|
|
142
|
+
"a11y.link-href-empty": "Ein Link in diesem Block hat ein leeres oder „#“-href.",
|
|
143
|
+
"a11y.link-target-blank-no-rel": "Link öffnet in neuem Tab, aber rel=\"noopener\" fehlt – ergänze es, damit das Ziel nicht auf window.opener zugreifen kann.",
|
|
144
|
+
"a11y.text-all-caps": "Längere Texte in Großbuchstaben sind schwerer lesbar. Verwende Groß- und Kleinschreibung.",
|
|
145
|
+
"a11y.text-low-contrast": "Überschriftskontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
|
|
146
|
+
"a11y.text-too-small": "Text ist {size}px; mindestens {min}px verwenden.",
|
|
147
|
+
"a11y.button-vague-label": "Button-Beschriftung „{text}“ ist unspezifisch. Beschreibe die Aktion.",
|
|
148
|
+
"a11y.button-touch-target": "Button ist etwa {height}px hoch; mindestens {min}px verwenden, um Fehltipper auf Mobilgeräten zu vermeiden.",
|
|
149
|
+
"a11y.button-low-contrast": "Buttontextkontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
|
|
150
|
+
"a11y.missing-preheader": "Kein Preheader-Text gesetzt. Postfächer zeigen sonst Bruchstücke des ersten Blocks an."
|
|
151
|
+
}, se = /* @__PURE__ */ d({ default: () => S }), S = {
|
|
152
|
+
"a11y.img-missing-alt": "Image is missing alt text. Add a short description, or mark the image as decorative.",
|
|
153
|
+
"a11y.img-alt-is-filename": "Alt text looks like a filename (\"{alt}\"). Replace with a short description of what the image conveys.",
|
|
154
|
+
"a11y.img-alt-too-long": "Alt text is {length} characters; aim for under {max}.",
|
|
155
|
+
"a11y.img-decorative-needs-empty-alt": "Decorative image has alt text. Either clear the alt text or unmark the image as decorative.",
|
|
156
|
+
"a11y.img-linked-no-context": "Linked image alt describes the image but not the link destination. Include where the link goes (e.g. 'Buy spring sale').",
|
|
157
|
+
"a11y.heading-empty": "Heading is empty. Add text or remove the block.",
|
|
158
|
+
"a11y.heading-skip-level": "Heading jumps from H{from} to H{to}. Step one level at a time.",
|
|
159
|
+
"a11y.heading-multiple-h1": "Email has more than one H1. Use H1 once for the main heading.",
|
|
160
|
+
"a11y.link-empty": "A link in this block has no text and no described image.",
|
|
161
|
+
"a11y.link-vague-text": "Link text \"{text}\" is vague. Describe the destination instead.",
|
|
162
|
+
"a11y.link-href-empty": "A link in this block has an empty or '#' href.",
|
|
163
|
+
"a11y.link-target-blank-no-rel": "Link opens in a new tab but is missing rel=\"noopener\" — add it to prevent the destination from accessing window.opener.",
|
|
164
|
+
"a11y.text-all-caps": "Long all-caps text is harder to read for everyone. Use sentence case.",
|
|
165
|
+
"a11y.text-low-contrast": "Heading contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
|
|
166
|
+
"a11y.text-too-small": "Text is {size}px; aim for at least {min}px.",
|
|
167
|
+
"a11y.button-vague-label": "Button label \"{text}\" is vague. Describe the action.",
|
|
168
|
+
"a11y.button-touch-target": "Button is roughly {height}px tall; aim for at least {min}px to avoid mis-taps on mobile.",
|
|
169
|
+
"a11y.button-low-contrast": "Button text contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
|
|
170
|
+
"a11y.missing-preheader": "No preheader text set. Inboxes will fall back to fragments of the first block."
|
|
171
|
+
}, C = /* @__PURE__ */ Object.assign({
|
|
172
|
+
"./de.ts": ae,
|
|
173
|
+
"./en.ts": se
|
|
174
|
+
}), w = {};
|
|
175
|
+
for (let e in C) {
|
|
123
176
|
let t = /\.\/([^/]+)\.ts$/.exec(e);
|
|
124
177
|
if (!t) continue;
|
|
125
178
|
let n = t[1];
|
|
126
|
-
n !== "index" && (
|
|
179
|
+
n !== "index" && (w[n] = C[e].default);
|
|
127
180
|
}
|
|
128
|
-
var
|
|
129
|
-
function
|
|
130
|
-
return
|
|
181
|
+
var ce = Object.keys(w);
|
|
182
|
+
function T(e) {
|
|
183
|
+
return w[e.split("-")[0]?.toLowerCase() ?? "en"] ?? w.en ?? S;
|
|
131
184
|
}
|
|
132
|
-
function
|
|
133
|
-
let r =
|
|
185
|
+
function E(e, t, n) {
|
|
186
|
+
let r = T(e)[t] ?? S[t];
|
|
134
187
|
return n ? r.replace(/\{(\w+)\}/g, (e, t) => {
|
|
135
188
|
let r = n[t];
|
|
136
189
|
return r === void 0 ? `{${t}}` : String(r);
|
|
137
190
|
}) : r;
|
|
138
191
|
}
|
|
139
|
-
var
|
|
192
|
+
var le = {
|
|
140
193
|
meta: {
|
|
141
|
-
id: "img-missing-alt",
|
|
194
|
+
id: "a11y.img-missing-alt",
|
|
142
195
|
severity: "error"
|
|
143
196
|
},
|
|
144
197
|
block(e) {
|
|
145
|
-
return !
|
|
198
|
+
return !r(e) || e.decorative === !0 || (e.alt?.trim() ?? "") !== "" || (e.src ?? "").trim() === "" ? null : { blockId: e.id };
|
|
146
199
|
}
|
|
147
|
-
},
|
|
148
|
-
id: "img-alt-is-filename",
|
|
200
|
+
}, ue = {
|
|
201
|
+
id: "a11y.img-alt-is-filename",
|
|
149
202
|
severity: "warning"
|
|
150
|
-
},
|
|
203
|
+
}, de = [
|
|
151
204
|
/\.(jpe?g|png|gif|webp|svg)$/i,
|
|
152
205
|
/^IMG[_-]?\d+/i,
|
|
153
206
|
/^Untitled/i,
|
|
154
207
|
/^Screen[\s_-]?Shot/i,
|
|
155
208
|
/^DSC[_-]?\d+/i
|
|
156
|
-
],
|
|
157
|
-
meta:
|
|
209
|
+
], fe = {
|
|
210
|
+
meta: ue,
|
|
158
211
|
block(e) {
|
|
159
|
-
if (!
|
|
212
|
+
if (!r(e) || e.decorative === !0) return null;
|
|
160
213
|
let t = e.alt?.trim() ?? "";
|
|
161
|
-
return t === "" || !
|
|
214
|
+
return t === "" || !de.some((e) => e.test(t)) ? null : {
|
|
162
215
|
blockId: e.id,
|
|
163
216
|
params: { alt: t },
|
|
164
217
|
fix: {
|
|
@@ -167,29 +220,29 @@ var A = {
|
|
|
167
220
|
}
|
|
168
221
|
};
|
|
169
222
|
}
|
|
170
|
-
},
|
|
223
|
+
}, pe = {
|
|
171
224
|
meta: {
|
|
172
|
-
id: "img-alt-too-long",
|
|
225
|
+
id: "a11y.img-alt-too-long",
|
|
173
226
|
severity: "warning"
|
|
174
227
|
},
|
|
175
|
-
block(e, t,
|
|
176
|
-
if (!
|
|
228
|
+
block(e, t, n) {
|
|
229
|
+
if (!r(e) || e.decorative === !0) return null;
|
|
177
230
|
let i = e.alt ?? "";
|
|
178
|
-
return i.length <=
|
|
231
|
+
return i.length <= n.thresholds.altMaxLength ? null : {
|
|
179
232
|
blockId: e.id,
|
|
180
233
|
params: {
|
|
181
234
|
length: i.length,
|
|
182
|
-
max:
|
|
235
|
+
max: n.thresholds.altMaxLength
|
|
183
236
|
}
|
|
184
237
|
};
|
|
185
238
|
}
|
|
186
|
-
},
|
|
239
|
+
}, me = {
|
|
187
240
|
meta: {
|
|
188
|
-
id: "img-decorative-needs-empty-alt",
|
|
241
|
+
id: "a11y.img-decorative-needs-empty-alt",
|
|
189
242
|
severity: "info"
|
|
190
243
|
},
|
|
191
244
|
block(e) {
|
|
192
|
-
return !
|
|
245
|
+
return !r(e) || e.decorative !== !0 || (e.alt ?? "") === "" ? null : {
|
|
193
246
|
blockId: e.id,
|
|
194
247
|
fix: {
|
|
195
248
|
description: "Clear alt text",
|
|
@@ -197,7 +250,7 @@ var A = {
|
|
|
197
250
|
}
|
|
198
251
|
};
|
|
199
252
|
}
|
|
200
|
-
},
|
|
253
|
+
}, he = /* @__PURE__ */ d({ default: () => ge }), ge = {
|
|
201
254
|
vagueLinkText: [
|
|
202
255
|
"hier klicken",
|
|
203
256
|
"hier",
|
|
@@ -240,7 +293,7 @@ var A = {
|
|
|
240
293
|
"anschauen",
|
|
241
294
|
"jetzt"
|
|
242
295
|
]
|
|
243
|
-
},
|
|
296
|
+
}, _e = /* @__PURE__ */ d({ default: () => ve }), ve = {
|
|
244
297
|
vagueLinkText: [
|
|
245
298
|
"click here",
|
|
246
299
|
"here",
|
|
@@ -281,49 +334,49 @@ var A = {
|
|
|
281
334
|
"redeem",
|
|
282
335
|
"watch"
|
|
283
336
|
]
|
|
284
|
-
},
|
|
285
|
-
"./de.ts":
|
|
286
|
-
"./en.ts":
|
|
287
|
-
}),
|
|
288
|
-
for (let e in
|
|
337
|
+
}, D = /* @__PURE__ */ Object.assign({
|
|
338
|
+
"./de.ts": he,
|
|
339
|
+
"./en.ts": _e
|
|
340
|
+
}), O = {};
|
|
341
|
+
for (let e in D) {
|
|
289
342
|
let t = /\.\/([^/]+)\.ts$/.exec(e);
|
|
290
343
|
if (!t) continue;
|
|
291
344
|
let n = t[1];
|
|
292
|
-
n !== "index" && (
|
|
345
|
+
n !== "index" && (O[n] = D[e].default);
|
|
293
346
|
}
|
|
294
|
-
function
|
|
295
|
-
return
|
|
347
|
+
function k(e) {
|
|
348
|
+
return ye;
|
|
296
349
|
}
|
|
297
|
-
function
|
|
350
|
+
function A(e) {
|
|
298
351
|
let t = /* @__PURE__ */ new Set();
|
|
299
|
-
for (let n of Object.values(
|
|
352
|
+
for (let n of Object.values(O)) for (let r of e(n)) t.add(r);
|
|
300
353
|
return Array.from(t);
|
|
301
354
|
}
|
|
302
|
-
var
|
|
303
|
-
vagueLinkText:
|
|
304
|
-
vagueButtonLabels:
|
|
305
|
-
linkedImageActionHints:
|
|
306
|
-
},
|
|
307
|
-
function
|
|
355
|
+
var ye = {
|
|
356
|
+
vagueLinkText: A((e) => e.vagueLinkText),
|
|
357
|
+
vagueButtonLabels: A((e) => e.vagueButtonLabels),
|
|
358
|
+
linkedImageActionHints: A((e) => e.linkedImageActionHints)
|
|
359
|
+
}, be = Object.keys(O);
|
|
360
|
+
function j(e) {
|
|
308
361
|
return e.toLowerCase().replace(/\s+/g, " ").replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, "").trim();
|
|
309
362
|
}
|
|
310
|
-
var
|
|
363
|
+
var xe = {
|
|
311
364
|
meta: {
|
|
312
|
-
id: "img-linked-no-context",
|
|
365
|
+
id: "a11y.img-linked-no-context",
|
|
313
366
|
severity: "warning"
|
|
314
367
|
},
|
|
315
|
-
block(e, t,
|
|
316
|
-
if (!
|
|
368
|
+
block(e, t, n) {
|
|
369
|
+
if (!r(e) || e.decorative === !0 || !e.linkUrl || e.linkUrl.trim() === "") return null;
|
|
317
370
|
let i = (e.alt ?? "").trim();
|
|
318
371
|
if (i === "") return null;
|
|
319
|
-
let a = i.toLocaleLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean), o =
|
|
372
|
+
let a = i.toLocaleLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean), o = k(n.locale).linkedImageActionHints;
|
|
320
373
|
return a.some((e) => o.includes(e)) ? null : { blockId: e.id };
|
|
321
374
|
}
|
|
322
375
|
};
|
|
323
376
|
//#endregion
|
|
324
377
|
//#region src/html-utils.ts
|
|
325
|
-
function
|
|
326
|
-
let t = [], n = [], r = new
|
|
378
|
+
function M(e) {
|
|
379
|
+
let t = [], n = [], r = new l({
|
|
327
380
|
onopentag(e, t) {
|
|
328
381
|
if (e === "a") {
|
|
329
382
|
let e = {
|
|
@@ -353,38 +406,38 @@ function U(e) {
|
|
|
353
406
|
});
|
|
354
407
|
return r.write(e), r.end(), t;
|
|
355
408
|
}
|
|
356
|
-
function
|
|
357
|
-
let t = "", n = new
|
|
409
|
+
function N(e) {
|
|
410
|
+
let t = "", n = new l({ ontext(e) {
|
|
358
411
|
t += e;
|
|
359
412
|
} });
|
|
360
413
|
return n.write(e), n.end(), t.trim();
|
|
361
414
|
}
|
|
362
|
-
var
|
|
415
|
+
var Se = {
|
|
363
416
|
meta: {
|
|
364
|
-
id: "heading-empty",
|
|
417
|
+
id: "a11y.heading-empty",
|
|
365
418
|
severity: "error"
|
|
366
419
|
},
|
|
367
420
|
block(e) {
|
|
368
|
-
return !
|
|
421
|
+
return !c(e) || N(e.content ?? "") !== "" ? null : { blockId: e.id };
|
|
369
422
|
}
|
|
370
|
-
},
|
|
371
|
-
id: "heading-skip-level",
|
|
423
|
+
}, Ce = {
|
|
424
|
+
id: "a11y.heading-skip-level",
|
|
372
425
|
severity: "error"
|
|
373
426
|
};
|
|
374
|
-
function
|
|
427
|
+
function P(e, t) {
|
|
375
428
|
for (let n of e) {
|
|
376
|
-
if (
|
|
429
|
+
if (c(n)) {
|
|
377
430
|
t.push(n);
|
|
378
431
|
continue;
|
|
379
432
|
}
|
|
380
|
-
if (
|
|
433
|
+
if (o(n)) for (let e of n.children) P(e, t);
|
|
381
434
|
}
|
|
382
435
|
}
|
|
383
|
-
var
|
|
384
|
-
meta:
|
|
436
|
+
var we = {
|
|
437
|
+
meta: Ce,
|
|
385
438
|
template(e) {
|
|
386
439
|
let t = [];
|
|
387
|
-
|
|
440
|
+
P(e.blocks, t);
|
|
388
441
|
let n = [], r = 0;
|
|
389
442
|
for (let e of t) r !== 0 && e.level > r + 1 && n.push({
|
|
390
443
|
blockId: e.id,
|
|
@@ -395,54 +448,54 @@ var J = {
|
|
|
395
448
|
}), r = e.level;
|
|
396
449
|
return n;
|
|
397
450
|
}
|
|
398
|
-
},
|
|
399
|
-
id: "heading-multiple-h1",
|
|
451
|
+
}, Te = {
|
|
452
|
+
id: "a11y.heading-multiple-h1",
|
|
400
453
|
severity: "warning"
|
|
401
454
|
};
|
|
402
|
-
function
|
|
455
|
+
function F(e, t) {
|
|
403
456
|
for (let n of e) {
|
|
404
|
-
if (
|
|
457
|
+
if (c(n)) {
|
|
405
458
|
t.push(n);
|
|
406
459
|
continue;
|
|
407
460
|
}
|
|
408
|
-
if (
|
|
461
|
+
if (o(n)) for (let e of n.children) F(e, t);
|
|
409
462
|
}
|
|
410
463
|
}
|
|
411
|
-
var
|
|
412
|
-
meta:
|
|
464
|
+
var Ee = {
|
|
465
|
+
meta: Te,
|
|
413
466
|
template(e) {
|
|
414
467
|
let t = [];
|
|
415
|
-
|
|
468
|
+
F(e.blocks, t);
|
|
416
469
|
let n = t.filter((e) => e.level === 1);
|
|
417
470
|
return n.length <= 1 ? [] : n.slice(1).map((e) => ({ blockId: e.id }));
|
|
418
471
|
}
|
|
419
|
-
},
|
|
420
|
-
id: "link-empty",
|
|
472
|
+
}, De = {
|
|
473
|
+
id: "a11y.link-empty",
|
|
421
474
|
severity: "error"
|
|
422
475
|
};
|
|
423
|
-
function
|
|
424
|
-
return
|
|
476
|
+
function Oe(e) {
|
|
477
|
+
return a(e) || c(e) ? e.content : null;
|
|
425
478
|
}
|
|
426
|
-
var
|
|
427
|
-
meta:
|
|
479
|
+
var ke = {
|
|
480
|
+
meta: De,
|
|
428
481
|
block(e) {
|
|
429
|
-
let t =
|
|
430
|
-
return t === null || !
|
|
482
|
+
let t = Oe(e);
|
|
483
|
+
return t === null || !M(t).find((e) => e.text === "" && !e.hasImageWithAlt) ? null : { blockId: e.id };
|
|
431
484
|
}
|
|
432
|
-
},
|
|
433
|
-
id: "link-vague-text",
|
|
485
|
+
}, Ae = {
|
|
486
|
+
id: "a11y.link-vague-text",
|
|
434
487
|
severity: "warning"
|
|
435
488
|
};
|
|
436
|
-
function
|
|
437
|
-
return
|
|
489
|
+
function je(e) {
|
|
490
|
+
return a(e) || c(e) ? e.content : null;
|
|
438
491
|
}
|
|
439
|
-
var
|
|
440
|
-
meta:
|
|
492
|
+
var Me = {
|
|
493
|
+
meta: Ae,
|
|
441
494
|
block(e, t, n) {
|
|
442
|
-
let r =
|
|
495
|
+
let r = je(e);
|
|
443
496
|
if (r === null) return null;
|
|
444
|
-
let i =
|
|
445
|
-
let t =
|
|
497
|
+
let i = k(n.locale).vagueLinkText, a = M(r).find((e) => {
|
|
498
|
+
let t = j(e.text);
|
|
446
499
|
return t !== "" && i.includes(t);
|
|
447
500
|
});
|
|
448
501
|
return a ? {
|
|
@@ -450,53 +503,53 @@ var de = {
|
|
|
450
503
|
params: { text: a.text }
|
|
451
504
|
} : null;
|
|
452
505
|
}
|
|
453
|
-
},
|
|
454
|
-
id: "link-href-empty",
|
|
506
|
+
}, Ne = {
|
|
507
|
+
id: "a11y.link-href-empty",
|
|
455
508
|
severity: "error"
|
|
456
509
|
};
|
|
457
|
-
function
|
|
458
|
-
return
|
|
510
|
+
function Pe(e) {
|
|
511
|
+
return a(e) || c(e) ? e.content : null;
|
|
459
512
|
}
|
|
460
|
-
var
|
|
461
|
-
meta:
|
|
513
|
+
var Fe = {
|
|
514
|
+
meta: Ne,
|
|
462
515
|
block(e) {
|
|
463
|
-
let t =
|
|
464
|
-
return t === null || !
|
|
516
|
+
let t = Pe(e);
|
|
517
|
+
return t === null || !M(t).find((e) => {
|
|
465
518
|
let t = e.href.trim();
|
|
466
519
|
return t === "" || t === "#";
|
|
467
520
|
}) ? null : { blockId: e.id };
|
|
468
521
|
}
|
|
469
|
-
},
|
|
470
|
-
id: "link-target-blank-no-rel",
|
|
522
|
+
}, Ie = {
|
|
523
|
+
id: "a11y.link-target-blank-no-rel",
|
|
471
524
|
severity: "warning"
|
|
472
525
|
};
|
|
473
|
-
function
|
|
474
|
-
return
|
|
526
|
+
function Le(e) {
|
|
527
|
+
return a(e) || c(e) ? e.content : null;
|
|
475
528
|
}
|
|
476
|
-
function
|
|
529
|
+
function Re(e) {
|
|
477
530
|
if (e === null) return !1;
|
|
478
531
|
let t = e.toLowerCase().split(/\s+/);
|
|
479
532
|
return t.includes("noopener") || t.includes("noreferrer");
|
|
480
533
|
}
|
|
481
|
-
var
|
|
482
|
-
meta:
|
|
534
|
+
var ze = {
|
|
535
|
+
meta: Ie,
|
|
483
536
|
block(e) {
|
|
484
|
-
let t =
|
|
485
|
-
return t === null || !
|
|
537
|
+
let t = Le(e);
|
|
538
|
+
return t === null || !M(t).find((e) => e.target === "_blank" && !Re(e.rel)) ? null : {
|
|
486
539
|
blockId: e.id,
|
|
487
540
|
fix: {
|
|
488
541
|
description: "Add rel=\"noopener\"",
|
|
489
542
|
apply: (t) => {
|
|
490
|
-
if (!
|
|
491
|
-
let n =
|
|
543
|
+
if (!a(e) && !c(e)) return;
|
|
544
|
+
let n = He(e.content ?? "");
|
|
492
545
|
t.updateBlock(e.id, { content: n });
|
|
493
546
|
}
|
|
494
547
|
}
|
|
495
548
|
};
|
|
496
549
|
}
|
|
497
|
-
},
|
|
498
|
-
function
|
|
499
|
-
let t = [], n = new RegExp(
|
|
550
|
+
}, I = /([^\s"'>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
551
|
+
function Be(e) {
|
|
552
|
+
let t = [], n = new RegExp(I.source, I.flags), r;
|
|
500
553
|
for (; (r = n.exec(e)) !== null;) {
|
|
501
554
|
let e = r[2] ?? r[3] ?? r[4] ?? null;
|
|
502
555
|
t.push({
|
|
@@ -508,13 +561,13 @@ function ye(e) {
|
|
|
508
561
|
}
|
|
509
562
|
return t;
|
|
510
563
|
}
|
|
511
|
-
function
|
|
564
|
+
function Ve(e) {
|
|
512
565
|
return e.some((e) => e.name.toLowerCase() === "target" && e.value !== null && e.value.toLowerCase() === "_blank");
|
|
513
566
|
}
|
|
514
|
-
function
|
|
567
|
+
function He(e) {
|
|
515
568
|
return e.replace(/<a\b([^>]*)>/gi, (e, t) => {
|
|
516
|
-
let n =
|
|
517
|
-
if (!
|
|
569
|
+
let n = Be(t);
|
|
570
|
+
if (!Ve(n)) return e;
|
|
518
571
|
let r = n.find((e) => e.name.toLowerCase() === "rel");
|
|
519
572
|
if (r) {
|
|
520
573
|
let n = (r.value ?? "").toLowerCase().split(/\s+/);
|
|
@@ -525,24 +578,24 @@ function xe(e) {
|
|
|
525
578
|
return `<a${t} rel="noopener">`;
|
|
526
579
|
});
|
|
527
580
|
}
|
|
528
|
-
var
|
|
581
|
+
var Ue = {
|
|
529
582
|
meta: {
|
|
530
|
-
id: "text-all-caps",
|
|
583
|
+
id: "a11y.text-all-caps",
|
|
531
584
|
severity: "warning"
|
|
532
585
|
},
|
|
533
586
|
block(e, t, n) {
|
|
534
|
-
if (!
|
|
535
|
-
let r =
|
|
587
|
+
if (!a(e) && !c(e)) return null;
|
|
588
|
+
let r = N(e.content ?? "").replace(/[^\p{L}]/gu, "");
|
|
536
589
|
return r.length < n.thresholds.allCapsMinLength || r !== r.toLocaleUpperCase() ? null : { blockId: e.id };
|
|
537
590
|
}
|
|
538
|
-
},
|
|
591
|
+
}, We = {
|
|
539
592
|
meta: {
|
|
540
|
-
id: "text-low-contrast",
|
|
593
|
+
id: "a11y.text-low-contrast",
|
|
541
594
|
severity: "error"
|
|
542
595
|
},
|
|
543
596
|
block(t, n) {
|
|
544
|
-
if (!
|
|
545
|
-
let r = e[t.level] >= 24 ? 3 : 4.5, i =
|
|
597
|
+
if (!c(t) || !_(t.color) || !_(n.resolvedBackgroundColor)) return null;
|
|
598
|
+
let r = e[t.level] >= 24 ? 3 : 4.5, i = m(t.color, n.resolvedBackgroundColor);
|
|
546
599
|
return Number.isNaN(i) || i >= r ? null : {
|
|
547
600
|
blockId: t.id,
|
|
548
601
|
params: {
|
|
@@ -551,34 +604,34 @@ var Se = {
|
|
|
551
604
|
}
|
|
552
605
|
};
|
|
553
606
|
}
|
|
554
|
-
},
|
|
555
|
-
id: "text-too-small",
|
|
607
|
+
}, Ge = {
|
|
608
|
+
id: "a11y.text-too-small",
|
|
556
609
|
severity: "warning"
|
|
557
610
|
};
|
|
558
|
-
function
|
|
559
|
-
return
|
|
611
|
+
function Ke(e) {
|
|
612
|
+
return i(e) || ee(e) ? e.fontSize : null;
|
|
560
613
|
}
|
|
561
614
|
//#endregion
|
|
562
615
|
//#region src/accessibility/index.ts
|
|
563
|
-
var
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
ie,
|
|
568
|
-
H,
|
|
569
|
-
G,
|
|
570
|
-
J,
|
|
571
|
-
ae,
|
|
572
|
-
ce,
|
|
573
|
-
de,
|
|
616
|
+
var L = [
|
|
617
|
+
le,
|
|
618
|
+
fe,
|
|
619
|
+
pe,
|
|
574
620
|
me,
|
|
575
|
-
|
|
621
|
+
xe,
|
|
576
622
|
Se,
|
|
577
|
-
|
|
623
|
+
we,
|
|
624
|
+
Ee,
|
|
625
|
+
ke,
|
|
626
|
+
Me,
|
|
627
|
+
Fe,
|
|
628
|
+
ze,
|
|
629
|
+
Ue,
|
|
630
|
+
We,
|
|
578
631
|
{
|
|
579
|
-
meta:
|
|
632
|
+
meta: Ge,
|
|
580
633
|
block(e, t, n) {
|
|
581
|
-
let r =
|
|
634
|
+
let r = Ke(e);
|
|
582
635
|
return r === null || r >= n.thresholds.minFontSize ? null : {
|
|
583
636
|
blockId: e.id,
|
|
584
637
|
params: {
|
|
@@ -590,13 +643,13 @@ var $ = [
|
|
|
590
643
|
},
|
|
591
644
|
{
|
|
592
645
|
meta: {
|
|
593
|
-
id: "button-vague-label",
|
|
646
|
+
id: "a11y.button-vague-label",
|
|
594
647
|
severity: "warning"
|
|
595
648
|
},
|
|
596
649
|
block(e, n, r) {
|
|
597
650
|
if (!t(e)) return null;
|
|
598
|
-
let i =
|
|
599
|
-
return i === "" || !
|
|
651
|
+
let i = j(e.text ?? "");
|
|
652
|
+
return i === "" || !k(r.locale).vagueButtonLabels.includes(i) ? null : {
|
|
600
653
|
blockId: e.id,
|
|
601
654
|
params: { text: e.text }
|
|
602
655
|
};
|
|
@@ -604,7 +657,7 @@ var $ = [
|
|
|
604
657
|
},
|
|
605
658
|
{
|
|
606
659
|
meta: {
|
|
607
|
-
id: "button-touch-target",
|
|
660
|
+
id: "a11y.button-touch-target",
|
|
608
661
|
severity: "warning"
|
|
609
662
|
},
|
|
610
663
|
block(e, n, r) {
|
|
@@ -623,12 +676,12 @@ var $ = [
|
|
|
623
676
|
},
|
|
624
677
|
{
|
|
625
678
|
meta: {
|
|
626
|
-
id: "button-low-contrast",
|
|
679
|
+
id: "a11y.button-low-contrast",
|
|
627
680
|
severity: "error"
|
|
628
681
|
},
|
|
629
682
|
block(e) {
|
|
630
683
|
if (!t(e)) return null;
|
|
631
|
-
let n =
|
|
684
|
+
let n = m(e.textColor, e.backgroundColor);
|
|
632
685
|
if (Number.isNaN(n)) return null;
|
|
633
686
|
let r = e.fontSize >= 24 ? 3 : 4.5;
|
|
634
687
|
return n >= r ? null : {
|
|
@@ -642,7 +695,7 @@ var $ = [
|
|
|
642
695
|
},
|
|
643
696
|
{
|
|
644
697
|
meta: {
|
|
645
|
-
id: "missing-preheader",
|
|
698
|
+
id: "a11y.missing-preheader",
|
|
646
699
|
severity: "info"
|
|
647
700
|
},
|
|
648
701
|
template(e) {
|
|
@@ -650,50 +703,387 @@ var $ = [
|
|
|
650
703
|
}
|
|
651
704
|
}
|
|
652
705
|
];
|
|
653
|
-
function
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
706
|
+
function qe(e, t = {}) {
|
|
707
|
+
return x(e, L, t, (e, t, n) => E(e, t, n));
|
|
708
|
+
}
|
|
709
|
+
//#endregion
|
|
710
|
+
//#region src/structure/messages/de.ts
|
|
711
|
+
var Je = /* @__PURE__ */ d({ default: () => Ye }), Ye = {
|
|
712
|
+
"structure.duplicate-block-id": "Block-ID erscheint {count}-mal im Baum. Jeder Block muss eine eindeutige ID haben.",
|
|
713
|
+
"structure.section-column-mismatch": "Sektion verwendet Layout „{layout}\" (erwartet {expected} Spalten), hat aber {actual}. Deutet auf beschädigten Zustand hin.",
|
|
714
|
+
"structure.nested-section": "Sektion ist in einer anderen Sektion verschachtelt. Sektionen können nicht verschachtelt werden – der Renderer wird sich falsch verhalten.",
|
|
715
|
+
"structure.empty-section": "Sektion enthält keine Blöcke. Entferne sie oder füge Inhalt hinzu.",
|
|
716
|
+
"structure.empty-column": "Spalte {columnIndex} dieser Sektion ist leer. Füge Inhalt hinzu oder reduziere die Spaltenanzahl."
|
|
717
|
+
}, Xe = /* @__PURE__ */ d({ default: () => R }), R = {
|
|
718
|
+
"structure.duplicate-block-id": "Block id appears {count} times in the tree. Each block must have a unique id.",
|
|
719
|
+
"structure.section-column-mismatch": "Section uses layout \"{layout}\" (expects {expected} columns) but has {actual}. Indicates corrupted state.",
|
|
720
|
+
"structure.nested-section": "Section is nested inside another section. Sections cannot nest — the renderer will misbehave.",
|
|
721
|
+
"structure.empty-section": "Section has no blocks. Remove it or add content.",
|
|
722
|
+
"structure.empty-column": "Column {columnIndex} of this section is empty. Add content or reduce the column count."
|
|
723
|
+
}, z = /* @__PURE__ */ Object.assign({
|
|
724
|
+
"./de.ts": Je,
|
|
725
|
+
"./en.ts": Xe
|
|
726
|
+
}), B = {};
|
|
727
|
+
for (let e in z) {
|
|
728
|
+
let t = /\.\/([^/]+)\.ts$/.exec(e);
|
|
729
|
+
if (!t) continue;
|
|
730
|
+
let n = t[1];
|
|
731
|
+
n !== "index" && (B[n] = z[e].default);
|
|
732
|
+
}
|
|
733
|
+
var Ze = Object.keys(B);
|
|
734
|
+
function V(e) {
|
|
735
|
+
return B[e.split("-")[0]?.toLowerCase() ?? "en"] ?? B.en ?? R;
|
|
736
|
+
}
|
|
737
|
+
function H(e, t, n) {
|
|
738
|
+
let r = V(e)[t] ?? R[t];
|
|
739
|
+
return n ? r.replace(/\{(\w+)\}/g, (e, t) => {
|
|
740
|
+
let r = n[t];
|
|
741
|
+
return r === void 0 ? `{${t}}` : String(r);
|
|
742
|
+
}) : r;
|
|
743
|
+
}
|
|
744
|
+
//#endregion
|
|
745
|
+
//#region src/structure/rules/duplicate-block-id.ts
|
|
746
|
+
var Qe = {
|
|
747
|
+
id: "structure.duplicate-block-id",
|
|
748
|
+
severity: "error"
|
|
749
|
+
};
|
|
750
|
+
function U(e, t) {
|
|
751
|
+
for (let n of e) if (t.set(n.id, (t.get(n.id) ?? 0) + 1), o(n)) for (let e of n.children) U(e, t);
|
|
752
|
+
}
|
|
753
|
+
var $e = {
|
|
754
|
+
meta: Qe,
|
|
755
|
+
template(e) {
|
|
756
|
+
let t = /* @__PURE__ */ new Map();
|
|
757
|
+
U(e.blocks, t);
|
|
758
|
+
let n = [];
|
|
759
|
+
for (let [e, r] of t) r > 1 && n.push({
|
|
760
|
+
blockId: e,
|
|
761
|
+
params: { count: r }
|
|
762
|
+
});
|
|
763
|
+
return n;
|
|
764
|
+
}
|
|
765
|
+
}, et = {
|
|
766
|
+
id: "structure.empty-column",
|
|
767
|
+
severity: "warning"
|
|
768
|
+
};
|
|
769
|
+
function W(e, t) {
|
|
770
|
+
for (let n of e) {
|
|
771
|
+
if (!o(n)) continue;
|
|
772
|
+
let e = n;
|
|
773
|
+
e.children.length > 1 && e.children.forEach((n, r) => {
|
|
774
|
+
n.length === 0 && t.push({
|
|
775
|
+
blockId: e.id,
|
|
776
|
+
params: { columnIndex: r + 1 }
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
for (let n of e.children) W(n, t);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
var tt = {
|
|
783
|
+
meta: et,
|
|
784
|
+
template(e) {
|
|
785
|
+
let t = [];
|
|
786
|
+
return W(e.blocks, t), t;
|
|
787
|
+
}
|
|
788
|
+
}, nt = {
|
|
789
|
+
id: "structure.empty-section",
|
|
790
|
+
severity: "warning"
|
|
791
|
+
};
|
|
792
|
+
function rt(e) {
|
|
793
|
+
return e.children.length === 0 ? !0 : e.children.every((e) => e.length === 0);
|
|
794
|
+
}
|
|
795
|
+
var it = {
|
|
796
|
+
meta: nt,
|
|
797
|
+
block(e) {
|
|
798
|
+
if (!o(e)) return null;
|
|
799
|
+
let t = e;
|
|
800
|
+
return rt(t) ? {
|
|
801
|
+
blockId: t.id,
|
|
802
|
+
fix: {
|
|
803
|
+
description: "Remove the empty section",
|
|
804
|
+
apply: (e) => {
|
|
805
|
+
e.removeBlock(t.id);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
} : null;
|
|
809
|
+
}
|
|
810
|
+
}, at = {
|
|
811
|
+
meta: {
|
|
812
|
+
id: "structure.nested-section",
|
|
813
|
+
severity: "error"
|
|
814
|
+
},
|
|
815
|
+
block(e, t) {
|
|
816
|
+
if (!o(e) || t.section === null) return null;
|
|
817
|
+
let n = t.section;
|
|
657
818
|
return {
|
|
658
|
-
blockId:
|
|
659
|
-
|
|
660
|
-
severity: t,
|
|
661
|
-
message: k(n.locale, e, r.params),
|
|
662
|
-
fix: r.fix
|
|
819
|
+
blockId: e.id,
|
|
820
|
+
params: { parentId: n.id }
|
|
663
821
|
};
|
|
664
822
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
823
|
+
}, ot = {
|
|
824
|
+
id: "structure.section-column-mismatch",
|
|
825
|
+
severity: "error"
|
|
826
|
+
};
|
|
827
|
+
function st(e) {
|
|
828
|
+
return e === "1" ? 1 : e === "3" ? 3 : 2;
|
|
829
|
+
}
|
|
830
|
+
//#endregion
|
|
831
|
+
//#region src/structure/index.ts
|
|
832
|
+
var G = [
|
|
833
|
+
$e,
|
|
834
|
+
it,
|
|
835
|
+
tt,
|
|
836
|
+
at,
|
|
837
|
+
{
|
|
838
|
+
meta: ot,
|
|
839
|
+
block(e) {
|
|
840
|
+
if (!o(e)) return null;
|
|
841
|
+
let t = e, n = st(t.columns), r = t.children.length;
|
|
842
|
+
return r === n ? null : {
|
|
843
|
+
blockId: t.id,
|
|
844
|
+
params: {
|
|
845
|
+
layout: t.columns,
|
|
846
|
+
expected: n,
|
|
847
|
+
actual: r
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
];
|
|
853
|
+
function ct(e, t = {}) {
|
|
854
|
+
return x(e, G, t, (e, t, n) => H(e, t, n));
|
|
855
|
+
}
|
|
856
|
+
//#endregion
|
|
857
|
+
//#region src/links/messages/de.ts
|
|
858
|
+
var lt = /* @__PURE__ */ d({ default: () => ut }), ut = {
|
|
859
|
+
"link.javascript-protocol": "Die URL verwendet das „javascript:\"-Protokoll, das aus Sicherheitsgründen beim Rendern entfernt wird. Ersetze sie durch eine echte URL oder entferne sie.",
|
|
860
|
+
"link.unsupported-protocol": "Die URL verwendet das Protokoll „{protocol}\", das von den meisten E-Mail-Clients nicht unterstützt wird. Verwende http, https, mailto, tel oder sms.",
|
|
861
|
+
"link.malformed-mailto": "Der mailto:-Link ist fehlerhaft. Erwartet wird eine einzelne Empfängeradresse vor einer eventuellen Querystring (z. B. mailto:hallo@example.com).",
|
|
862
|
+
"link.malformed-tel": "Der tel:-Link enthält Zeichen, die keine Ziffern, +, Leerzeichen, Bindestriche, Klammern oder Punkte sind.",
|
|
863
|
+
"link.localhost-or-staging": "Der URL-Host „{host}\" entspricht einem Nicht-Produktionsmuster. Ersetze ihn vor dem Versand durch die Produktions-URL."
|
|
864
|
+
}, dt = /* @__PURE__ */ d({ default: () => K }), K = {
|
|
865
|
+
"link.javascript-protocol": "URL uses the \"javascript:\" protocol, which is stripped at render time for safety. Replace it with a real link or remove the URL.",
|
|
866
|
+
"link.unsupported-protocol": "URL uses the \"{protocol}\" protocol, which most email clients do not support. Use http, https, mailto, tel, or sms.",
|
|
867
|
+
"link.malformed-mailto": "mailto: link is malformed. Expected a single recipient address before any query string (e.g. mailto:hello@example.com).",
|
|
868
|
+
"link.malformed-tel": "tel: link contains characters that are not digits, +, spaces, dashes, parentheses, or dots.",
|
|
869
|
+
"link.localhost-or-staging": "URL host \"{host}\" matches a non-production pattern. Replace with the production URL before sending."
|
|
870
|
+
}, q = /* @__PURE__ */ Object.assign({
|
|
871
|
+
"./de.ts": lt,
|
|
872
|
+
"./en.ts": dt
|
|
873
|
+
}), J = {};
|
|
874
|
+
for (let e in q) {
|
|
875
|
+
let t = /\.\/([^/]+)\.ts$/.exec(e);
|
|
876
|
+
if (!t) continue;
|
|
877
|
+
let n = t[1];
|
|
878
|
+
n !== "index" && (J[n] = q[e].default);
|
|
879
|
+
}
|
|
880
|
+
var ft = Object.keys(J);
|
|
881
|
+
function Y(e) {
|
|
882
|
+
return J[e.split("-")[0]?.toLowerCase() ?? "en"] ?? J.en ?? K;
|
|
883
|
+
}
|
|
884
|
+
function X(e, t, n) {
|
|
885
|
+
let r = Y(e)[t] ?? K[t];
|
|
886
|
+
return n ? r.replace(/\{(\w+)\}/g, (e, t) => {
|
|
887
|
+
let r = n[t];
|
|
888
|
+
return r === void 0 ? `{${t}}` : String(r);
|
|
889
|
+
}) : r;
|
|
890
|
+
}
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/url-walker.ts
|
|
893
|
+
function Z(e) {
|
|
894
|
+
let o = [];
|
|
895
|
+
return b(e, (e) => {
|
|
896
|
+
if (c(e) || a(e) || n(e)) {
|
|
897
|
+
for (let t of M(e.content)) o.push({
|
|
898
|
+
url: t.href,
|
|
899
|
+
blockId: e.id,
|
|
900
|
+
source: "anchor",
|
|
901
|
+
label: t.text
|
|
902
|
+
});
|
|
903
|
+
return;
|
|
671
904
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
905
|
+
if (t(e)) {
|
|
906
|
+
o.push({
|
|
907
|
+
url: e.url,
|
|
908
|
+
blockId: e.id,
|
|
909
|
+
source: "button",
|
|
910
|
+
label: e.text
|
|
911
|
+
});
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (r(e)) {
|
|
915
|
+
e.linkUrl && e.linkUrl !== "" && o.push({
|
|
916
|
+
url: e.linkUrl,
|
|
917
|
+
blockId: e.id,
|
|
918
|
+
source: "image-link",
|
|
919
|
+
label: e.alt || void 0
|
|
920
|
+
});
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
if (te(e)) {
|
|
924
|
+
o.push({
|
|
925
|
+
url: e.url,
|
|
926
|
+
blockId: e.id,
|
|
927
|
+
source: "video",
|
|
928
|
+
label: e.alt || void 0
|
|
929
|
+
});
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
if (i(e)) {
|
|
933
|
+
for (let t of e.items) o.push({
|
|
934
|
+
url: t.url,
|
|
935
|
+
blockId: e.id,
|
|
936
|
+
source: "menu-item",
|
|
937
|
+
label: t.text
|
|
938
|
+
});
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
if (s(e)) {
|
|
942
|
+
for (let t of e.icons) o.push({
|
|
943
|
+
url: t.url,
|
|
944
|
+
blockId: e.id,
|
|
945
|
+
source: "social-icon",
|
|
946
|
+
label: t.platform
|
|
947
|
+
});
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
}), o;
|
|
951
|
+
}
|
|
952
|
+
//#endregion
|
|
953
|
+
//#region src/links/rules/javascript-protocol.ts
|
|
954
|
+
var pt = {
|
|
955
|
+
id: "link.javascript-protocol",
|
|
956
|
+
severity: "error"
|
|
957
|
+
};
|
|
958
|
+
function mt(e) {
|
|
959
|
+
if (!e) return !1;
|
|
960
|
+
let t = e.replace(/\s+/g, "");
|
|
961
|
+
return /^javascript:/i.test(t);
|
|
962
|
+
}
|
|
963
|
+
var ht = {
|
|
964
|
+
meta: pt,
|
|
965
|
+
template(e) {
|
|
966
|
+
let t = [];
|
|
967
|
+
for (let n of Z(e)) mt(n.url) && t.push({ blockId: n.blockId });
|
|
968
|
+
return t;
|
|
678
969
|
}
|
|
679
|
-
|
|
970
|
+
}, gt = {
|
|
971
|
+
id: "link.unsupported-protocol",
|
|
972
|
+
severity: "warning"
|
|
973
|
+
}, _t = new Set([
|
|
974
|
+
"http",
|
|
975
|
+
"https",
|
|
976
|
+
"mailto",
|
|
977
|
+
"tel",
|
|
978
|
+
"sms"
|
|
979
|
+
]);
|
|
980
|
+
function vt(e) {
|
|
981
|
+
if (!e) return null;
|
|
982
|
+
let t = e.trim(), n = /^([a-z][a-z0-9+\-.]*):/i.exec(t);
|
|
983
|
+
return n ? n[1].toLowerCase() : null;
|
|
680
984
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
let n = t[e];
|
|
692
|
-
return n === void 0 ? $.find((t) => t.meta.id === e)?.meta.severity ?? "warning" : n;
|
|
985
|
+
var yt = {
|
|
986
|
+
meta: gt,
|
|
987
|
+
template(e) {
|
|
988
|
+
let t = [];
|
|
989
|
+
for (let n of Z(e)) {
|
|
990
|
+
let e = vt(n.url);
|
|
991
|
+
e !== null && e !== "javascript" && (_t.has(e) || t.push({
|
|
992
|
+
blockId: n.blockId,
|
|
993
|
+
params: { protocol: e }
|
|
994
|
+
}));
|
|
693
995
|
}
|
|
694
|
-
|
|
996
|
+
return t;
|
|
997
|
+
}
|
|
998
|
+
}, bt = {
|
|
999
|
+
id: "link.malformed-mailto",
|
|
1000
|
+
severity: "warning"
|
|
1001
|
+
};
|
|
1002
|
+
function Q(e) {
|
|
1003
|
+
let t = e.trim();
|
|
1004
|
+
if (!/^mailto:/i.test(t)) return !1;
|
|
1005
|
+
let [n] = t.slice(7).split("?", 2);
|
|
1006
|
+
if (n.trim() === "") return !0;
|
|
1007
|
+
let r = n.split(",").map((e) => e.trim());
|
|
1008
|
+
for (let e of r) {
|
|
1009
|
+
if (e === "") return !0;
|
|
1010
|
+
let t = e.split("@");
|
|
1011
|
+
if (t.length !== 2) return !0;
|
|
1012
|
+
let [n, r] = t;
|
|
1013
|
+
if (n === "" || r === "" || !r.includes(".")) return !0;
|
|
1014
|
+
}
|
|
1015
|
+
return !1;
|
|
1016
|
+
}
|
|
1017
|
+
var xt = {
|
|
1018
|
+
meta: bt,
|
|
1019
|
+
template(e) {
|
|
1020
|
+
let t = [];
|
|
1021
|
+
for (let n of Z(e)) Q(n.url) && t.push({ blockId: n.blockId });
|
|
1022
|
+
return t;
|
|
1023
|
+
}
|
|
1024
|
+
}, St = {
|
|
1025
|
+
id: "link.malformed-tel",
|
|
1026
|
+
severity: "warning"
|
|
1027
|
+
}, Ct = /^[+0-9\s().\-]+$/;
|
|
1028
|
+
function wt(e) {
|
|
1029
|
+
let t = e.trim();
|
|
1030
|
+
if (!/^tel:/i.test(t)) return !1;
|
|
1031
|
+
let n = t.slice(4).trim();
|
|
1032
|
+
return n === "" ? !0 : !Ct.test(n);
|
|
1033
|
+
}
|
|
1034
|
+
var Tt = {
|
|
1035
|
+
meta: St,
|
|
1036
|
+
template(e) {
|
|
1037
|
+
let t = [];
|
|
1038
|
+
for (let n of Z(e)) wt(n.url) && t.push({ blockId: n.blockId });
|
|
1039
|
+
return t;
|
|
1040
|
+
}
|
|
1041
|
+
}, Et = {
|
|
1042
|
+
id: "link.localhost-or-staging",
|
|
1043
|
+
severity: "warning"
|
|
1044
|
+
};
|
|
1045
|
+
function Dt(e) {
|
|
1046
|
+
let t = e.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
1047
|
+
return RegExp(`^${t}$`, "i");
|
|
1048
|
+
}
|
|
1049
|
+
function Ot(e) {
|
|
1050
|
+
if (!e) return null;
|
|
1051
|
+
let t = e.trim();
|
|
1052
|
+
if (!/^(https?|ftps?):\/\//i.test(t)) return null;
|
|
1053
|
+
try {
|
|
1054
|
+
return new URL(t).hostname.toLowerCase();
|
|
1055
|
+
} catch {
|
|
1056
|
+
return null;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
//#endregion
|
|
1060
|
+
//#region src/links/index.ts
|
|
1061
|
+
var $ = [
|
|
1062
|
+
ht,
|
|
1063
|
+
yt,
|
|
1064
|
+
xt,
|
|
1065
|
+
Tt,
|
|
1066
|
+
{
|
|
1067
|
+
meta: Et,
|
|
1068
|
+
template(e, t) {
|
|
1069
|
+
let n = t.links.nonProductionHosts;
|
|
1070
|
+
if (n.length === 0) return [];
|
|
1071
|
+
let r = n.map(Dt), i = [];
|
|
1072
|
+
for (let t of Z(e)) {
|
|
1073
|
+
let e = Ot(t.url);
|
|
1074
|
+
e !== null && r.some((t) => t.test(e)) && i.push({
|
|
1075
|
+
blockId: t.blockId,
|
|
1076
|
+
params: { host: e }
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
return i;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
];
|
|
1083
|
+
function kt(e, t = {}) {
|
|
1084
|
+
return x(e, $, t, (e, t, n) => X(e, t, n));
|
|
695
1085
|
}
|
|
696
1086
|
//#endregion
|
|
697
|
-
export {
|
|
1087
|
+
export { L as ACCESSIBILITY_RULES, f as DEFAULT_A11Y_THRESHOLDS, p as DEFAULT_NON_PRODUCTION_HOSTS, $ as LINK_RULES, G as STRUCTURE_RULES, be as SUPPORTED_DICTIONARY_LOCALES, ft as SUPPORTED_LINK_MESSAGE_LOCALES, ce as SUPPORTED_MESSAGE_LOCALES, Ze as SUPPORTED_STRUCTURE_MESSAGE_LOCALES, M as extractAnchors, N as extractText, X as formatLinkMessage, E as formatMessage, H as formatStructureMessage, m as getContrastRatio, k as getDictionary, Y as getLinkMessages, T as getMessages, V as getStructureMessages, _ as isOpaqueHex, qe as lintAccessibility, kt as lintLinks, ct as lintStructure, g as parseHex, b as walkBlocks, Z as walkUrls };
|
|
698
1088
|
|
|
699
1089
|
//# sourceMappingURL=index.js.map
|