@templatical/quality
Advanced tools
+18
-0
@@ -264,2 +264,20 @@ import type { Block } from '@templatical/types'; | ||
| /** | ||
| * Run every linter in the package — accessibility, structure, and links — | ||
| * against `content` and return the merged issue list. | ||
| * | ||
| * This is the single entry point callers should prefer. CI guards, the | ||
| * editor's live linter, and headless consumers all funnel through here, so | ||
| * a new linter category is picked up everywhere by registering it in this | ||
| * one fan-out — no consumer has to learn about the new function. | ||
| * | ||
| * Per-category options (`options.accessibility`, `options.structure`, | ||
| * `options.links`) and the global `options.disabled` flag are forwarded | ||
| * unchanged; each sub-linter already short-circuits when its category is | ||
| * `false`, so the per-tool calls below are cheap no-ops when disabled. | ||
| * | ||
| * Issue order is stable: accessibility first, then structure, then links. | ||
| */ | ||
| export declare function lintTemplate(content: TemplateContent, options?: LintOptions): LintIssue[]; | ||
| export declare interface LintThresholds { | ||
@@ -266,0 +284,0 @@ altMaxLength: number; |
+194
-185
@@ -16,3 +16,3 @@ 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"; | ||
| minTouchTargetPx: 44 | ||
| }, p = [ | ||
| }, ne = [ | ||
| "localhost", | ||
@@ -27,12 +27,12 @@ "127.0.0.1", | ||
| //#region src/contrast.ts | ||
| function m(e, t) { | ||
| let n = h(e), r = h(t); | ||
| function p(e, t) { | ||
| let n = m(e), r = m(t); | ||
| if (!n || !r) return NaN; | ||
| let i = _(n), a = _(r), o = Math.max(i, a), s = Math.min(i, a); | ||
| let i = oe(n), a = oe(r), o = Math.max(i, a), s = Math.min(i, a); | ||
| return (o + .05) / (s + .05); | ||
| } | ||
| var ne = /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i, re = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i, ie = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i; | ||
| function h(e) { | ||
| var re = /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i, ie = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i, ae = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i; | ||
| function m(e) { | ||
| if (typeof e != "string") return null; | ||
| let t = e.trim(), n = ie.exec(t); | ||
| let t = e.trim(), n = ae.exec(t); | ||
| if (n) return n[4].toLowerCase() === "ff" ? { | ||
@@ -43,3 +43,3 @@ r: parseInt(n[1], 16), | ||
| } : null; | ||
| let r = re.exec(t); | ||
| let r = ie.exec(t); | ||
| if (r) return { | ||
@@ -50,3 +50,3 @@ r: parseInt(r[1], 16), | ||
| }; | ||
| let i = ne.exec(t); | ||
| let i = re.exec(t); | ||
| return i ? { | ||
@@ -58,10 +58,10 @@ r: parseInt(i[1] + i[1], 16), | ||
| } | ||
| function g(e) { | ||
| return h(e ?? "") !== null; | ||
| function h(e) { | ||
| return m(e ?? "") !== null; | ||
| } | ||
| function _({ r: e, g: t, b: n }) { | ||
| let r = v(e / 255), i = v(t / 255), a = v(n / 255); | ||
| function oe({ r: e, g: t, b: n }) { | ||
| let r = g(e / 255), i = g(t / 255), a = g(n / 255); | ||
| return .2126 * r + .7152 * i + .0722 * a; | ||
| } | ||
| function v(e) { | ||
| function g(e) { | ||
| return e <= .03928 ? e / 12.92 : ((e + .055) / 1.055) ** 2.4; | ||
@@ -71,6 +71,6 @@ } | ||
| //#region src/walk.ts | ||
| var ae = "#ffffff"; | ||
| function y(e, t) { | ||
| let n = g(e.settings.backgroundColor) ? e.settings.backgroundColor.toLowerCase() : ae, r = (e, n) => { | ||
| let i = e.styles?.backgroundColor, a = g(i) ? i.toLowerCase() : n.resolvedBackgroundColor; | ||
| var se = "#ffffff"; | ||
| function _(e, t) { | ||
| let n = h(e.settings.backgroundColor) ? e.settings.backgroundColor.toLowerCase() : se, r = (e, n) => { | ||
| let i = e.styles?.backgroundColor, a = h(i) ? i.toLowerCase() : n.resolvedBackgroundColor; | ||
| t(e, a === n.resolvedBackgroundColor ? n : { | ||
@@ -99,3 +99,3 @@ ...n, | ||
| //#region src/run-rules.ts | ||
| function b(e, t, n, r) { | ||
| function v(e, t, n, r) { | ||
| let i = []; | ||
@@ -111,3 +111,3 @@ function a(e, t, i) { | ||
| } | ||
| y(e, (e, r) => { | ||
| _(e, (e, r) => { | ||
| for (let o of t) { | ||
@@ -128,7 +128,7 @@ let t = n.severity(o.meta.id); | ||
| } | ||
| function x(e) { | ||
| function y(e) { | ||
| let t = e.overrides ?? {}, n = { | ||
| ...f, | ||
| ...e.thresholds ?? {} | ||
| }, r = { nonProductionHosts: e.nonProductionHosts ?? p }, i = e.locale ?? "en", a = e.rules; | ||
| }, r = { nonProductionHosts: e.nonProductionHosts ?? ne }, i = e.locale ?? "en", a = e.rules; | ||
| return { | ||
@@ -145,4 +145,4 @@ locale: i, | ||
| } | ||
| function oe(e, t, n) { | ||
| return x({ | ||
| function ce(e, t, n) { | ||
| return y({ | ||
| locale: e, | ||
@@ -155,4 +155,4 @@ rules: n, | ||
| } | ||
| function se(e, t, n) { | ||
| return x({ | ||
| function le(e, t, n) { | ||
| return y({ | ||
| locale: e, | ||
@@ -165,4 +165,4 @@ rules: n, | ||
| } | ||
| function ce(e, t, n) { | ||
| return x({ | ||
| function ue(e, t, n) { | ||
| return y({ | ||
| locale: e, | ||
@@ -177,3 +177,3 @@ rules: n, | ||
| //#region src/accessibility/messages/de.ts | ||
| var le = /* @__PURE__ */ d({ default: () => ue }), ue = { | ||
| var de = /* @__PURE__ */ d({ default: () => fe }), fe = { | ||
| "a11y.img-missing-alt": "Bild ohne Alt-Text. Füge eine kurze Beschreibung hinzu oder markiere das Bild als dekorativ.", | ||
@@ -199,3 +199,3 @@ "a11y.img-alt-is-filename": "Alt-Text sieht wie ein Dateiname aus (\"{alt}\"). Beschreibe stattdessen kurz, was das Bild zeigt.", | ||
| "a11y.missing-preheader": "Kein Preheader-Text gesetzt. Postfächer zeigen sonst Bruchstücke des ersten Blocks an." | ||
| }, de = /* @__PURE__ */ d({ default: () => S }), S = { | ||
| }, pe = /* @__PURE__ */ d({ default: () => b }), b = { | ||
| "a11y.img-missing-alt": "Image is missing alt text. Add a short description, or mark the image as decorative.", | ||
@@ -221,18 +221,18 @@ "a11y.img-alt-is-filename": "Alt text looks like a filename (\"{alt}\"). Replace with a short description of what the image conveys.", | ||
| "a11y.missing-preheader": "No preheader text set. Inboxes will fall back to fragments of the first block." | ||
| }, C = /* @__PURE__ */ Object.assign({ | ||
| "./de.ts": le, | ||
| "./en.ts": de | ||
| }), w = {}; | ||
| for (let e in C) { | ||
| }, x = /* @__PURE__ */ Object.assign({ | ||
| "./de.ts": de, | ||
| "./en.ts": pe | ||
| }), S = {}; | ||
| for (let e in x) { | ||
| let t = /\.\/([^/]+)\.ts$/.exec(e); | ||
| if (!t) continue; | ||
| let n = t[1]; | ||
| n !== "index" && (w[n] = C[e].default); | ||
| n !== "index" && (S[n] = x[e].default); | ||
| } | ||
| var fe = Object.keys(w); | ||
| function T(e) { | ||
| return w[e.split("-")[0]?.toLowerCase() ?? "en"] ?? w.en ?? S; | ||
| var me = Object.keys(S); | ||
| function C(e) { | ||
| return S[e.split("-")[0]?.toLowerCase() ?? "en"] ?? S.en ?? b; | ||
| } | ||
| function E(e, t, n) { | ||
| let r = T(e)[t] ?? S[t]; | ||
| function w(e, t, n) { | ||
| let r = C(e)[t] ?? b[t]; | ||
| return n ? r.replace(/\{(\w+)\}/g, (e, t) => { | ||
@@ -243,3 +243,3 @@ let r = n[t]; | ||
| } | ||
| var pe = { | ||
| var he = { | ||
| meta: { | ||
@@ -252,6 +252,6 @@ id: "a11y.img-missing-alt", | ||
| } | ||
| }, me = { | ||
| }, ge = { | ||
| id: "a11y.img-alt-is-filename", | ||
| severity: "warning" | ||
| }, he = [ | ||
| }, _e = [ | ||
| /\.(jpe?g|png|gif|webp|svg)$/i, | ||
@@ -262,8 +262,8 @@ /^IMG[_-]?\d+/i, | ||
| /^DSC[_-]?\d+/i | ||
| ], ge = { | ||
| meta: me, | ||
| ], ve = { | ||
| meta: ge, | ||
| block(e) { | ||
| if (!r(e) || e.decorative === !0) return null; | ||
| let t = e.alt?.trim() ?? ""; | ||
| return t === "" || !he.some((e) => e.test(t)) ? null : { | ||
| return t === "" || !_e.some((e) => e.test(t)) ? null : { | ||
| blockId: e.id, | ||
@@ -273,3 +273,3 @@ params: { alt: t } | ||
| } | ||
| }, _e = { | ||
| }, ye = { | ||
| meta: { | ||
@@ -290,3 +290,3 @@ id: "a11y.img-alt-too-long", | ||
| } | ||
| }, ve = { | ||
| }, be = { | ||
| meta: { | ||
@@ -305,3 +305,3 @@ id: "a11y.img-decorative-needs-empty-alt", | ||
| } | ||
| }, ye = /* @__PURE__ */ d({ default: () => be }), be = { | ||
| }, xe = /* @__PURE__ */ d({ default: () => Se }), Se = { | ||
| vagueLinkText: [ | ||
@@ -349,3 +349,3 @@ "hier klicken", | ||
| ] | ||
| }, xe = /* @__PURE__ */ d({ default: () => Se }), Se = { | ||
| }, Ce = /* @__PURE__ */ d({ default: () => we }), we = { | ||
| vagueLinkText: [ | ||
@@ -391,29 +391,29 @@ "click here", | ||
| ] | ||
| }, D = /* @__PURE__ */ Object.assign({ | ||
| "./de.ts": ye, | ||
| "./en.ts": xe | ||
| }), O = {}; | ||
| for (let e in D) { | ||
| }, T = /* @__PURE__ */ Object.assign({ | ||
| "./de.ts": xe, | ||
| "./en.ts": Ce | ||
| }), E = {}; | ||
| for (let e in T) { | ||
| let t = /\.\/([^/]+)\.ts$/.exec(e); | ||
| if (!t) continue; | ||
| let n = t[1]; | ||
| n !== "index" && (O[n] = D[e].default); | ||
| n !== "index" && (E[n] = T[e].default); | ||
| } | ||
| function k(e) { | ||
| return Ce; | ||
| function D(e) { | ||
| return Te; | ||
| } | ||
| function A(e) { | ||
| function O(e) { | ||
| let t = /* @__PURE__ */ new Set(); | ||
| for (let n of Object.values(O)) for (let r of e(n)) t.add(r); | ||
| for (let n of Object.values(E)) for (let r of e(n)) t.add(r); | ||
| return Array.from(t); | ||
| } | ||
| var Ce = { | ||
| vagueLinkText: A((e) => e.vagueLinkText), | ||
| vagueButtonLabels: A((e) => e.vagueButtonLabels), | ||
| linkedImageActionHints: A((e) => e.linkedImageActionHints) | ||
| }, we = Object.keys(O); | ||
| function j(e) { | ||
| var Te = { | ||
| vagueLinkText: O((e) => e.vagueLinkText), | ||
| vagueButtonLabels: O((e) => e.vagueButtonLabels), | ||
| linkedImageActionHints: O((e) => e.linkedImageActionHints) | ||
| }, Ee = Object.keys(E); | ||
| function k(e) { | ||
| return e.toLowerCase().replace(/\s+/g, " ").replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, "").trim(); | ||
| } | ||
| var Te = { | ||
| var De = { | ||
| meta: { | ||
@@ -427,3 +427,3 @@ id: "a11y.img-linked-no-context", | ||
| if (i === "") return null; | ||
| let a = i.toLocaleLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean), o = k(n.locale).linkedImageActionHints; | ||
| let a = i.toLocaleLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean), o = D(n.locale).linkedImageActionHints; | ||
| return a.some((e) => o.includes(e)) ? null : { blockId: e.id }; | ||
@@ -434,3 +434,3 @@ } | ||
| //#region src/html-utils.ts | ||
| function M(e) { | ||
| function A(e) { | ||
| let t = [], n = null, r = "", i = () => { | ||
@@ -461,4 +461,4 @@ n !== null && (n.text = r.trim(), t.push(n), n = null, r = ""); | ||
| } | ||
| function Ee(e) { | ||
| let t = De(e).matchAll(/<\/?a\b[^<>]*>/gi), n = 0; | ||
| function j(e) { | ||
| let t = Oe(e).matchAll(/<\/?a\b[^<>]*>/gi), n = 0; | ||
| for (let e of t) { | ||
@@ -474,3 +474,3 @@ if (e[0].startsWith("</")) { | ||
| } | ||
| function De(e) { | ||
| function Oe(e) { | ||
| let t = "", n = 0; | ||
@@ -490,3 +490,3 @@ for (; n < e.length;) { | ||
| } | ||
| function N(e) { | ||
| function M(e) { | ||
| let t = "", n = new l({ ontext(e) { | ||
@@ -497,3 +497,3 @@ t += e; | ||
| } | ||
| var Oe = { | ||
| var ke = { | ||
| meta: { | ||
@@ -504,9 +504,9 @@ id: "a11y.heading-empty", | ||
| block(e) { | ||
| return !c(e) || N(e.content ?? "") !== "" ? null : { blockId: e.id }; | ||
| return !c(e) || M(e.content ?? "") !== "" ? null : { blockId: e.id }; | ||
| } | ||
| }, ke = { | ||
| }, Ae = { | ||
| id: "a11y.heading-skip-level", | ||
| severity: "error" | ||
| }; | ||
| function P(e, t) { | ||
| function N(e, t) { | ||
| for (let n of e) { | ||
@@ -517,10 +517,10 @@ if (c(n)) { | ||
| } | ||
| if (o(n)) for (let e of n.children) P(e, t); | ||
| if (o(n)) for (let e of n.children) N(e, t); | ||
| } | ||
| } | ||
| var Ae = { | ||
| meta: ke, | ||
| var je = { | ||
| meta: Ae, | ||
| template(e) { | ||
| let t = []; | ||
| P(e.blocks, t); | ||
| N(e.blocks, t); | ||
| let n = [], r = 0; | ||
@@ -536,7 +536,7 @@ for (let e of t) r !== 0 && e.level > r + 1 && n.push({ | ||
| } | ||
| }, je = { | ||
| }, Me = { | ||
| id: "a11y.heading-multiple-h1", | ||
| severity: "warning" | ||
| }; | ||
| function F(e, t) { | ||
| function P(e, t) { | ||
| for (let n of e) { | ||
@@ -547,40 +547,40 @@ if (c(n)) { | ||
| } | ||
| if (o(n)) for (let e of n.children) F(e, t); | ||
| if (o(n)) for (let e of n.children) P(e, t); | ||
| } | ||
| } | ||
| var Me = { | ||
| meta: je, | ||
| var Ne = { | ||
| meta: Me, | ||
| template(e) { | ||
| let t = []; | ||
| F(e.blocks, t); | ||
| P(e.blocks, t); | ||
| let n = t.filter((e) => e.level === 1); | ||
| return n.length <= 1 ? [] : n.slice(1).map((e) => ({ blockId: e.id })); | ||
| } | ||
| }, Ne = { | ||
| }, Pe = { | ||
| id: "a11y.link-empty", | ||
| severity: "error" | ||
| }; | ||
| function Pe(e) { | ||
| function Fe(e) { | ||
| return a(e) || c(e) ? e.content : null; | ||
| } | ||
| var Fe = { | ||
| meta: Ne, | ||
| var Ie = { | ||
| meta: Pe, | ||
| block(e) { | ||
| let t = Pe(e); | ||
| return t === null || !M(t).find((e) => e.text === "" && !e.hasImageWithAlt) ? null : { blockId: e.id }; | ||
| let t = Fe(e); | ||
| return t === null || !A(t).find((e) => e.text === "" && !e.hasImageWithAlt) ? null : { blockId: e.id }; | ||
| } | ||
| }, Ie = { | ||
| }, Le = { | ||
| id: "a11y.link-vague-text", | ||
| severity: "warning" | ||
| }; | ||
| function Le(e) { | ||
| function Re(e) { | ||
| return a(e) || c(e) ? e.content : null; | ||
| } | ||
| var Re = { | ||
| meta: Ie, | ||
| var ze = { | ||
| meta: Le, | ||
| block(e, t, n) { | ||
| let r = Le(e); | ||
| let r = Re(e); | ||
| if (r === null) return null; | ||
| let i = k(n.locale).vagueLinkText, a = M(r).find((e) => { | ||
| let t = j(e.text); | ||
| let i = D(n.locale).vagueLinkText, a = A(r).find((e) => { | ||
| let t = k(e.text); | ||
| return t !== "" && i.includes(t); | ||
@@ -593,14 +593,14 @@ }); | ||
| } | ||
| }, ze = { | ||
| }, Be = { | ||
| id: "a11y.link-href-empty", | ||
| severity: "error" | ||
| }; | ||
| function Be(e) { | ||
| function Ve(e) { | ||
| return a(e) || c(e) ? e.content : null; | ||
| } | ||
| var Ve = { | ||
| meta: ze, | ||
| var He = { | ||
| meta: Be, | ||
| block(e) { | ||
| let t = Be(e); | ||
| return t === null || !M(t).find((e) => { | ||
| let t = Ve(e); | ||
| return t === null || !A(t).find((e) => { | ||
| let t = e.href.trim(); | ||
@@ -610,10 +610,10 @@ return t === "" || t === "#"; | ||
| } | ||
| }, He = { | ||
| }, Ue = { | ||
| id: "a11y.link-target-blank-no-rel", | ||
| severity: "warning" | ||
| }; | ||
| function Ue(e) { | ||
| function We(e) { | ||
| return a(e) || c(e) ? e.content : null; | ||
| } | ||
| function We(e) { | ||
| function Ge(e) { | ||
| if (e === null) return !1; | ||
@@ -623,7 +623,7 @@ let t = e.toLowerCase().split(/\s+/); | ||
| } | ||
| var Ge = { | ||
| meta: He, | ||
| var Ke = { | ||
| meta: Ue, | ||
| block(e) { | ||
| let t = Ue(e); | ||
| return t === null || !M(t).find((e) => e.target === "_blank" && !We(e.rel)) ? null : { | ||
| let t = We(e); | ||
| return t === null || !A(t).find((e) => e.target === "_blank" && !Ge(e.rel)) ? null : { | ||
| blockId: e.id, | ||
@@ -634,3 +634,3 @@ fix: { | ||
| if (!a(e) && !c(e)) return; | ||
| let n = Je(e.content ?? ""); | ||
| let n = Ye(e.content ?? ""); | ||
| t.updateBlock(e.id, { content: n }); | ||
@@ -641,5 +641,5 @@ } | ||
| } | ||
| }, I = /([^\s"'>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g; | ||
| function Ke(e) { | ||
| let t = [], n = new RegExp(I.source, I.flags), r; | ||
| }, F = /([^\s"'>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g; | ||
| function qe(e) { | ||
| let t = [], n = new RegExp(F.source, F.flags), r; | ||
| for (; (r = n.exec(e)) !== null;) { | ||
@@ -656,9 +656,9 @@ let e = r[2] ?? r[3] ?? r[4] ?? null; | ||
| } | ||
| function qe(e) { | ||
| function Je(e) { | ||
| return e.some((e) => e.name.toLowerCase() === "target" && e.value !== null && e.value.toLowerCase() === "_blank"); | ||
| } | ||
| function Je(e) { | ||
| function Ye(e) { | ||
| return e.replace(/<a\b([^>]*)>/gi, (e, t) => { | ||
| let n = Ke(t); | ||
| if (!qe(n)) return e; | ||
| let n = qe(t); | ||
| if (!Je(n)) return e; | ||
| let r = n.find((e) => e.name.toLowerCase() === "rel"); | ||
@@ -676,16 +676,16 @@ if (r) { | ||
| //#region src/accessibility/rules/link-nested-anchor.ts | ||
| var Ye = { | ||
| var Xe = { | ||
| id: "a11y.link-nested-anchor", | ||
| severity: "error" | ||
| }; | ||
| function Xe(e) { | ||
| function Ze(e) { | ||
| return a(e) || c(e) ? e.content : null; | ||
| } | ||
| var Ze = { | ||
| meta: Ye, | ||
| var Qe = { | ||
| meta: Xe, | ||
| block(e) { | ||
| let t = Xe(e); | ||
| return t === null || !Ee(t) ? null : { blockId: e.id }; | ||
| let t = Ze(e); | ||
| return t === null || !j(t) ? null : { blockId: e.id }; | ||
| } | ||
| }, Qe = { | ||
| }, $e = { | ||
| meta: { | ||
@@ -697,6 +697,6 @@ id: "a11y.text-all-caps", | ||
| if (!a(e) && !c(e)) return null; | ||
| let r = N(e.content ?? "").replace(/[^\p{L}]/gu, ""); | ||
| let r = M(e.content ?? "").replace(/[^\p{L}]/gu, ""); | ||
| return r.length < n.thresholds.allCapsMinLength || r !== r.toLocaleUpperCase() ? null : { blockId: e.id }; | ||
| } | ||
| }, $e = { | ||
| }, et = { | ||
| meta: { | ||
@@ -707,4 +707,4 @@ id: "a11y.text-low-contrast", | ||
| block(t, n) { | ||
| if (!c(t) || !g(t.color) || !g(n.resolvedBackgroundColor)) return null; | ||
| let r = e[t.level] >= 24 ? 3 : 4.5, i = m(t.color, n.resolvedBackgroundColor); | ||
| if (!c(t) || !h(t.color) || !h(n.resolvedBackgroundColor)) return null; | ||
| let r = e[t.level] >= 24 ? 3 : 4.5, i = p(t.color, n.resolvedBackgroundColor); | ||
| return Number.isNaN(i) || i >= r ? null : { | ||
@@ -718,7 +718,7 @@ blockId: t.id, | ||
| } | ||
| }, et = { | ||
| }, tt = { | ||
| id: "a11y.text-too-small", | ||
| severity: "warning" | ||
| }; | ||
| function tt(e) { | ||
| function nt(e) { | ||
| return i(e) || ee(e) ? e.fontSize : null; | ||
@@ -728,22 +728,22 @@ } | ||
| //#region src/accessibility/index.ts | ||
| var L = [ | ||
| pe, | ||
| ge, | ||
| _e, | ||
| var I = [ | ||
| he, | ||
| ve, | ||
| Te, | ||
| Oe, | ||
| Ae, | ||
| Me, | ||
| Fe, | ||
| Re, | ||
| Ve, | ||
| Ge, | ||
| Ze, | ||
| ye, | ||
| be, | ||
| De, | ||
| ke, | ||
| je, | ||
| Ne, | ||
| Ie, | ||
| ze, | ||
| He, | ||
| Ke, | ||
| Qe, | ||
| $e, | ||
| et, | ||
| { | ||
| meta: et, | ||
| meta: tt, | ||
| block(e, t, n) { | ||
| let r = tt(e); | ||
| let r = nt(e); | ||
| return r === null || r >= n.thresholds.minFontSize ? null : { | ||
@@ -765,4 +765,4 @@ blockId: e.id, | ||
| if (!t(e)) return null; | ||
| let i = j(e.text ?? ""); | ||
| return i === "" || !k(r.locale).vagueButtonLabels.includes(i) ? null : { | ||
| let i = k(e.text ?? ""); | ||
| return i === "" || !D(r.locale).vagueButtonLabels.includes(i) ? null : { | ||
| blockId: e.id, | ||
@@ -799,3 +799,3 @@ params: { text: e.text } | ||
| if (!t(e)) return null; | ||
| let n = m(e.textColor, e.backgroundColor); | ||
| let n = p(e.textColor, e.backgroundColor); | ||
| if (Number.isNaN(n)) return null; | ||
@@ -822,6 +822,6 @@ let r = e.fontSize >= 24 ? 3 : 4.5; | ||
| ]; | ||
| function nt(e, t = {}) { | ||
| function L(e, t = {}) { | ||
| if (t.disabled === !0 || t.accessibility === !1) return []; | ||
| let n = t.accessibility ?? {}; | ||
| return b(e, L, oe(t.locale, n, L), (e, t, n) => E(e, t, n)); | ||
| return v(e, I, ce(t.locale, n, I), (e, t, n) => w(e, t, n)); | ||
| } | ||
@@ -972,10 +972,10 @@ //#endregion | ||
| ]; | ||
| function _t(e, t = {}) { | ||
| function K(e, t = {}) { | ||
| if (t.disabled === !0 || t.structure === !1) return []; | ||
| let n = t.structure ?? {}; | ||
| return b(e, G, se(t.locale, n, G), (e, t, n) => H(e, t, n)); | ||
| return v(e, G, le(t.locale, n, G), (e, t, n) => H(e, t, n)); | ||
| } | ||
| //#endregion | ||
| //#region src/links/messages/de.ts | ||
| var vt = /* @__PURE__ */ d({ default: () => yt }), yt = { | ||
| var _t = /* @__PURE__ */ d({ default: () => vt }), vt = { | ||
| "link.javascript-protocol": "Die URL verwendet das Protokoll „{protocol}:\", das beliebigen Skriptcode ausführen kann und aus Sicherheitsgründen beim Rendern entfernt wird. Ersetze sie durch eine echte URL oder entferne sie.", | ||
@@ -986,3 +986,3 @@ "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.", | ||
| "link.localhost-or-staging": "Der URL-Host „{host}\" entspricht einem Nicht-Produktionsmuster. Ersetze ihn vor dem Versand durch die Produktions-URL." | ||
| }, bt = /* @__PURE__ */ d({ default: () => K }), K = { | ||
| }, yt = /* @__PURE__ */ d({ default: () => q }), q = { | ||
| "link.javascript-protocol": "URL uses the \"{protocol}:\" protocol, which can execute arbitrary script and is stripped at render time for safety. Replace it with a real link or remove the URL.", | ||
@@ -993,18 +993,18 @@ "link.unsupported-protocol": "URL uses the \"{protocol}\" protocol, which most email clients do not support. Use http, https, mailto, tel, or sms.", | ||
| "link.localhost-or-staging": "URL host \"{host}\" matches a non-production pattern. Replace with the production URL before sending." | ||
| }, q = /* @__PURE__ */ Object.assign({ | ||
| "./de.ts": vt, | ||
| "./en.ts": bt | ||
| }), J = {}; | ||
| for (let e in q) { | ||
| }, J = /* @__PURE__ */ Object.assign({ | ||
| "./de.ts": _t, | ||
| "./en.ts": yt | ||
| }), Y = {}; | ||
| for (let e in J) { | ||
| let t = /\.\/([^/]+)\.ts$/.exec(e); | ||
| if (!t) continue; | ||
| let n = t[1]; | ||
| n !== "index" && (J[n] = q[e].default); | ||
| n !== "index" && (Y[n] = J[e].default); | ||
| } | ||
| var xt = Object.keys(J); | ||
| function Y(e) { | ||
| return J[e.split("-")[0]?.toLowerCase() ?? "en"] ?? J.en ?? K; | ||
| var bt = Object.keys(Y); | ||
| function X(e) { | ||
| return Y[e.split("-")[0]?.toLowerCase() ?? "en"] ?? Y.en ?? q; | ||
| } | ||
| function X(e, t, n) { | ||
| let r = Y(e)[t] ?? K[t]; | ||
| function Z(e, t, n) { | ||
| let r = X(e)[t] ?? q[t]; | ||
| return n ? r.replace(/\{(\w+)\}/g, (e, t) => { | ||
@@ -1017,7 +1017,7 @@ let r = n[t]; | ||
| //#region src/url-walker.ts | ||
| function Z(e) { | ||
| function Q(e) { | ||
| let o = []; | ||
| return y(e, (e) => { | ||
| return _(e, (e) => { | ||
| if (c(e) || a(e) || n(e)) { | ||
| for (let t of M(e.content)) o.push({ | ||
| for (let t of A(e.content)) o.push({ | ||
| url: t.href, | ||
@@ -1079,6 +1079,6 @@ blockId: e.id, | ||
| //#region src/links/rules/javascript-protocol.ts | ||
| var St = { | ||
| var xt = { | ||
| id: "link.javascript-protocol", | ||
| severity: "error" | ||
| }, Q = [ | ||
| }, St = [ | ||
| "javascript", | ||
@@ -1091,10 +1091,10 @@ "data", | ||
| let t = e.replace(/\s+/g, ""); | ||
| for (let e of Q) if (RegExp(`^${e}:`, "i").test(t)) return e; | ||
| for (let e of St) if (RegExp(`^${e}:`, "i").test(t)) return e; | ||
| return null; | ||
| } | ||
| var wt = { | ||
| meta: St, | ||
| meta: xt, | ||
| template(e) { | ||
| let t = []; | ||
| for (let n of Z(e)) { | ||
| for (let n of Q(e)) { | ||
| let e = Ct(n.url); | ||
@@ -1117,3 +1117,3 @@ e !== null && t.push({ | ||
| "sms" | ||
| ]), Dt = new Set(Q); | ||
| ]), Dt = new Set(St); | ||
| function Ot(e) { | ||
@@ -1128,3 +1128,3 @@ if (!e) return null; | ||
| let t = []; | ||
| for (let n of Z(e)) { | ||
| for (let n of Q(e)) { | ||
| let e = Ot(n.url); | ||
@@ -1161,3 +1161,3 @@ e !== null && (Dt.has(e) || Et.has(e) || t.push({ | ||
| let t = []; | ||
| for (let n of Z(e)) jt(n.url) && t.push({ blockId: n.blockId }); | ||
| for (let n of Q(e)) jt(n.url) && t.push({ blockId: n.blockId }); | ||
| return t; | ||
@@ -1181,3 +1181,3 @@ } | ||
| let t = []; | ||
| for (let n of Z(e)) It(n.url) && t.push({ blockId: n.blockId }); | ||
| for (let n of Q(e)) It(n.url) && t.push({ blockId: n.blockId }); | ||
| return t; | ||
@@ -1216,3 +1216,3 @@ } | ||
| let r = n.map(zt), i = []; | ||
| for (let t of Z(e)) { | ||
| for (let t of Q(e)) { | ||
| let e = Bt(t.url); | ||
@@ -1231,3 +1231,3 @@ e !== null && r.some((t) => t.test(e)) && i.push({ | ||
| let n = t.links ?? {}; | ||
| return b(e, $, ce(t.locale, n, $), (e, t, n) => X(e, t, n)); | ||
| return v(e, $, ue(t.locale, n, $), (e, t, n) => Z(e, t, n)); | ||
| } | ||
@@ -1240,4 +1240,13 @@ //#endregion | ||
| //#endregion | ||
| export { L as ACCESSIBILITY_RULES, f as DEFAULT_A11Y_THRESHOLDS, p as DEFAULT_NON_PRODUCTION_HOSTS, $ as LINK_RULES, G as STRUCTURE_RULES, we as SUPPORTED_DICTIONARY_LOCALES, xt as SUPPORTED_LINK_MESSAGE_LOCALES, fe as SUPPORTED_MESSAGE_LOCALES, ot 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, Ee as hasNestedAnchors, Ht as isLintFullyDisabled, g as isOpaqueHex, nt as lintAccessibility, Vt as lintLinks, _t as lintStructure, h as parseHex, y as walkBlocks, Z as walkUrls }; | ||
| //#region src/lint-template.ts | ||
| function Ut(e, t = {}) { | ||
| return Ht(t) ? [] : [ | ||
| ...L(e, t), | ||
| ...K(e, t), | ||
| ...Vt(e, t) | ||
| ]; | ||
| } | ||
| //#endregion | ||
| export { I as ACCESSIBILITY_RULES, f as DEFAULT_A11Y_THRESHOLDS, ne as DEFAULT_NON_PRODUCTION_HOSTS, $ as LINK_RULES, G as STRUCTURE_RULES, Ee as SUPPORTED_DICTIONARY_LOCALES, bt as SUPPORTED_LINK_MESSAGE_LOCALES, me as SUPPORTED_MESSAGE_LOCALES, ot as SUPPORTED_STRUCTURE_MESSAGE_LOCALES, A as extractAnchors, M as extractText, Z as formatLinkMessage, w as formatMessage, H as formatStructureMessage, p as getContrastRatio, D as getDictionary, X as getLinkMessages, C as getMessages, V as getStructureMessages, j as hasNestedAnchors, Ht as isLintFullyDisabled, h as isOpaqueHex, L as lintAccessibility, Vt as lintLinks, K as lintStructure, Ut as lintTemplate, m as parseHex, _ as walkBlocks, Q as walkUrls }; | ||
| //# sourceMappingURL=index.js.map |
+2
-2
| { | ||
| "name": "@templatical/quality", | ||
| "description": "Accessibility linter for Templatical email templates", | ||
| "version": "0.9.1", | ||
| "version": "0.10.0", | ||
| "bugs": "https://github.com/templatical/sdk/issues", | ||
| "dependencies": { | ||
| "htmlparser2": "^12.0.0", | ||
| "@templatical/types": "0.9.1" | ||
| "@templatical/types": "0.10.0" | ||
| }, | ||
@@ -10,0 +10,0 @@ "devDependencies": { |
Sorry, the diff of this file is too big to display
153610
1.8%1550
1.71%+ Added
- Removed
Updated