@maizzle/framework 6.0.0-rc.21 → 6.0.0-rc.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/components/Body.vue +1 -1
  2. package/dist/components/CodeBlock.vue +1 -1
  3. package/dist/components/CodeInline.vue +72 -2
  4. package/dist/components/Column.vue +2 -1
  5. package/dist/components/Container.vue +1 -11
  6. package/dist/components/Img.vue +199 -4
  7. package/dist/components/Preheader.vue +33 -5
  8. package/dist/components/Section.vue +9 -14
  9. package/dist/components/Text.vue +1 -1
  10. package/dist/composables/defineConfig.d.ts +3 -4
  11. package/dist/composables/defineConfig.d.ts.map +1 -1
  12. package/dist/composables/defineConfig.js +3 -4
  13. package/dist/composables/defineConfig.js.map +1 -1
  14. package/dist/composables/renderContext.d.ts +0 -1
  15. package/dist/composables/renderContext.d.ts.map +1 -1
  16. package/dist/composables/renderContext.js.map +1 -1
  17. package/dist/composables/usePreheader.d.ts +6 -5
  18. package/dist/composables/usePreheader.d.ts.map +1 -1
  19. package/dist/composables/usePreheader.js +3 -3
  20. package/dist/composables/usePreheader.js.map +1 -1
  21. package/dist/composables/useTransformers.d.ts +1 -1
  22. package/dist/composables/useTransformers.js +1 -1
  23. package/dist/composables/useTransformers.js.map +1 -1
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +2 -2
  26. package/dist/render/createRenderer.js +2 -2
  27. package/dist/render/createRenderer.js.map +1 -1
  28. package/dist/transformers/addAttributes.d.ts +18 -8
  29. package/dist/transformers/addAttributes.d.ts.map +1 -1
  30. package/dist/transformers/addAttributes.js +22 -8
  31. package/dist/transformers/addAttributes.js.map +1 -1
  32. package/dist/transformers/columnWidth.d.ts.map +1 -1
  33. package/dist/transformers/columnWidth.js +136 -150
  34. package/dist/transformers/columnWidth.js.map +1 -1
  35. package/dist/transformers/entities.d.ts.map +1 -1
  36. package/dist/transformers/entities.js +1 -0
  37. package/dist/transformers/entities.js.map +1 -1
  38. package/dist/transformers/index.d.ts.map +1 -1
  39. package/dist/transformers/index.js +7 -5
  40. package/dist/transformers/index.js.map +1 -1
  41. package/dist/transformers/inlineCss.js +2 -7
  42. package/dist/transformers/inlineCss.js.map +1 -1
  43. package/dist/transformers/minifyCodeInline.d.ts +29 -0
  44. package/dist/transformers/minifyCodeInline.d.ts.map +1 -0
  45. package/dist/transformers/minifyCodeInline.js +36 -0
  46. package/dist/transformers/minifyCodeInline.js.map +1 -0
  47. package/dist/transformers/msoPlaceholders.d.ts +10 -5
  48. package/dist/transformers/msoPlaceholders.d.ts.map +1 -1
  49. package/dist/transformers/msoPlaceholders.js +38 -7
  50. package/dist/transformers/msoPlaceholders.js.map +1 -1
  51. package/dist/transformers/safeSelectors.d.ts +37 -0
  52. package/dist/transformers/safeSelectors.d.ts.map +1 -0
  53. package/dist/transformers/{safeClassNames.js → safeSelectors.js} +24 -5
  54. package/dist/transformers/safeSelectors.js.map +1 -0
  55. package/dist/transformers/shorthandCss.js +38 -7
  56. package/dist/transformers/shorthandCss.js.map +1 -1
  57. package/dist/types/config.d.ts +2 -2
  58. package/dist/types/config.d.ts.map +1 -1
  59. package/dist/utils/ast/serializer.d.ts.map +1 -1
  60. package/dist/utils/ast/serializer.js +27 -17
  61. package/dist/utils/ast/serializer.js.map +1 -1
  62. package/dist/utils/cssBox.d.ts +42 -0
  63. package/dist/utils/cssBox.d.ts.map +1 -0
  64. package/dist/utils/cssBox.js +156 -0
  65. package/dist/utils/cssBox.js.map +1 -0
  66. package/package.json +1 -1
  67. package/dist/transformers/safeClassNames.d.ts +0 -22
  68. package/dist/transformers/safeClassNames.d.ts.map +0 -1
  69. package/dist/transformers/safeClassNames.js.map +0 -1
@@ -1,30 +1,40 @@
1
- import { walk } from "./walker.js";
2
1
  import render from "dom-serializer";
3
2
  //#region src/utils/ast/serializer.ts
3
+ const RAW_TEXT_ELEMENTS = new Set(["script", "style"]);
4
4
  /**
5
- * Re-encode < and > as entities in text nodes inside <code> elements.
5
+ * Re-encode `<` and `>` in text nodes before serialization.
6
6
  *
7
- * The DOM parser decodes entities like &#x3C; into raw < in text nodes.
8
- * With encodeEntities: false the serializer outputs them as-is, which
9
- * creates broken HTML (e.g. </a> inside a code block closes the real tag).
7
+ * The parser decodes entities like `&lt;` into raw `<` in text nodes. With
8
+ * `encodeEntities: false` (needed so other transformers can emit entity
9
+ * strings such as `&nbsp;` verbatim), the serializer would write those raw
10
+ * `<` characters as-is, turning escaped text (e.g. a Vue `{{ html }}`
11
+ * interpolation containing `<p>...</p>`) into real DOM downstream.
10
12
  *
11
- * We selectively fix this for <code> contents only, so the rest of the
12
- * document (where encodeEntities: false is needed) is unaffected.
13
+ * `&` is intentionally not re-encoded: the `entities` transformer writes
14
+ * literal entity strings (`&nbsp;`, `&mdash;`) directly into text-node data
15
+ * and relies on the serializer leaving them alone.
16
+ *
17
+ * Skip `<script>` / `<style>` — their children are raw-text containers, and
18
+ * CSS/JS legitimately contains `<` and `>`.
13
19
  */
14
- function encodeCodeTextNodes(dom) {
15
- walk(dom, (node) => {
16
- const el = node;
17
- if (el.name !== "code") return;
18
- walk(el.children ?? [], (child) => {
19
- if (child.type === "text") {
20
- const text = child;
20
+ function encodeTextNodes(dom, inRawText = false) {
21
+ for (const node of dom) {
22
+ if (node.type === "text") {
23
+ if (!inRawText) {
24
+ const text = node;
21
25
  text.data = text.data.replace(/</g, "&lt;").replace(/>/g, "&gt;");
22
26
  }
23
- });
24
- });
27
+ continue;
28
+ }
29
+ if ("children" in node && node.children?.length) {
30
+ const el = node;
31
+ const nextRaw = inRawText || RAW_TEXT_ELEMENTS.has(el.name);
32
+ encodeTextNodes(el.children, nextRaw);
33
+ }
34
+ }
25
35
  }
26
36
  function serialize(dom, options) {
27
- encodeCodeTextNodes(dom);
37
+ encodeTextNodes(dom);
28
38
  return render(dom, {
29
39
  encodeEntities: false,
30
40
  ...options
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.js","names":[],"sources":["../../../src/utils/ast/serializer.ts"],"sourcesContent":["import render from 'dom-serializer'\nimport type { ChildNode } from 'domhandler'\nimport type { DomSerializerOptions } from 'dom-serializer'\nimport { walk } from './walker.ts'\n\n/**\n * Re-encode < and > as entities in text nodes inside <code> elements.\n *\n * The DOM parser decodes entities like &#x3C; into raw < in text nodes.\n * With encodeEntities: false the serializer outputs them as-is, which\n * creates broken HTML (e.g. </a> inside a code block closes the real tag).\n *\n * We selectively fix this for <code> contents only, so the rest of the\n * document (where encodeEntities: false is needed) is unaffected.\n */\nfunction encodeCodeTextNodes(dom: ChildNode[]): void {\n walk(dom, (node) => {\n const el = node as import('domhandler').Element\n if (el.name !== 'code') return\n\n walk(el.children ?? [], (child) => {\n if (child.type === 'text') {\n const text = child as import('domhandler').Text\n text.data = text.data.replace(/</g, '&lt;').replace(/>/g, '&gt;')\n }\n })\n })\n}\n\nexport function serialize(dom: ChildNode[], options?: DomSerializerOptions): string {\n encodeCodeTextNodes(dom)\n return render(dom, { encodeEntities: false, ...options })\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,SAAS,oBAAoB,KAAwB;CACnD,KAAK,MAAM,SAAS;EAClB,MAAM,KAAK;EACX,IAAI,GAAG,SAAS,QAAQ;EAExB,KAAK,GAAG,YAAY,EAAE,GAAG,UAAU;GACjC,IAAI,MAAM,SAAS,QAAQ;IACzB,MAAM,OAAO;IACb,KAAK,OAAO,KAAK,KAAK,QAAQ,MAAM,OAAO,CAAC,QAAQ,MAAM,OAAO;;IAEnE;GACF;;AAGJ,SAAgB,UAAU,KAAkB,SAAwC;CAClF,oBAAoB,IAAI;CACxB,OAAO,OAAO,KAAK;EAAE,gBAAgB;EAAO,GAAG;EAAS,CAAC"}
1
+ {"version":3,"file":"serializer.js","names":[],"sources":["../../../src/utils/ast/serializer.ts"],"sourcesContent":["import render from 'dom-serializer'\nimport type { ChildNode, Element, Text } from 'domhandler'\nimport type { DomSerializerOptions } from 'dom-serializer'\n\nconst RAW_TEXT_ELEMENTS = new Set(['script', 'style'])\n\n/**\n * Re-encode `<` and `>` in text nodes before serialization.\n *\n * The parser decodes entities like `&lt;` into raw `<` in text nodes. With\n * `encodeEntities: false` (needed so other transformers can emit entity\n * strings such as `&nbsp;` verbatim), the serializer would write those raw\n * `<` characters as-is, turning escaped text (e.g. a Vue `{{ html }}`\n * interpolation containing `<p>...</p>`) into real DOM downstream.\n *\n * `&` is intentionally not re-encoded: the `entities` transformer writes\n * literal entity strings (`&nbsp;`, `&mdash;`) directly into text-node data\n * and relies on the serializer leaving them alone.\n *\n * Skip `<script>` / `<style>` — their children are raw-text containers, and\n * CSS/JS legitimately contains `<` and `>`.\n */\nfunction encodeTextNodes(dom: ChildNode[], inRawText = false): void {\n for (const node of dom) {\n if (node.type === 'text') {\n if (!inRawText) {\n const text = node as Text\n text.data = text.data\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n }\n continue\n }\n\n if ('children' in node && (node as Element).children?.length) {\n const el = node as Element\n const nextRaw = inRawText || RAW_TEXT_ELEMENTS.has(el.name)\n encodeTextNodes(el.children as ChildNode[], nextRaw)\n }\n }\n}\n\nexport function serialize(dom: ChildNode[], options?: DomSerializerOptions): string {\n encodeTextNodes(dom)\n return render(dom, { encodeEntities: false, ...options })\n}\n"],"mappings":";;AAIA,MAAM,oBAAoB,IAAI,IAAI,CAAC,UAAU,QAAQ,CAAC;;;;;;;;;;;;;;;;;AAkBtD,SAAS,gBAAgB,KAAkB,YAAY,OAAa;CAClE,KAAK,MAAM,QAAQ,KAAK;EACtB,IAAI,KAAK,SAAS,QAAQ;GACxB,IAAI,CAAC,WAAW;IACd,MAAM,OAAO;IACb,KAAK,OAAO,KAAK,KACd,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,OAAO;;GAE1B;;EAGF,IAAI,cAAc,QAAS,KAAiB,UAAU,QAAQ;GAC5D,MAAM,KAAK;GACX,MAAM,UAAU,aAAa,kBAAkB,IAAI,GAAG,KAAK;GAC3D,gBAAgB,GAAG,UAAyB,QAAQ;;;;AAK1D,SAAgB,UAAU,KAAkB,SAAwC;CAClF,gBAAgB,IAAI;CACpB,OAAO,OAAO,KAAK;EAAE,gBAAgB;EAAO,GAAG;EAAS,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { Root } from "postcss";
2
+
3
+ //#region src/utils/cssBox.d.ts
4
+ declare const NO_BORDER_STYLES: Set<string>;
5
+ /**
6
+ * Parse a length token into px. Handles `Npx`, `Nrem`, `Nem`, `Npt`, and
7
+ * unitless N (treated as px). Returns null for percentages, calc(),
8
+ * keywords, or anything that doesn't reduce to a concrete length.
9
+ */
10
+ declare function lengthToPx(value: string): number | null;
11
+ /**
12
+ * Expand a 1-4 token CSS shorthand (T R B L) into a left/right pair:
13
+ * 1: all sides
14
+ * 2: TB RL
15
+ * 3: T RL B
16
+ * 4: T R B L
17
+ */
18
+ declare function shorthandSides(value: string): {
19
+ left?: string;
20
+ right?: string;
21
+ };
22
+ /**
23
+ * Read horizontal padding (left + right) px from a parsed style root.
24
+ * Percentages are skipped since they'd need a known container width.
25
+ */
26
+ declare function horizontalPaddingPx(root: Root): number;
27
+ /**
28
+ * Extract a px length from a CSS border shorthand (e.g. `1px solid red` → 1).
29
+ * Returns null when the value indicates no border (`none` or `hidden`).
30
+ * Defaults to 3px (CSS `medium`) when a visible style is set but no
31
+ * explicit width token is present in the shorthand value.
32
+ */
33
+ declare function shorthandBorderWidthPx(value: string): number | null;
34
+ /**
35
+ * Read horizontal border widths (left + right) px from a parsed style root.
36
+ * Per-side `border-style: none|hidden` overrides count as zero
37
+ * contribution. Returns total px or 0 when nothing resolves.
38
+ */
39
+ declare function horizontalBorderPx(root: Root): number;
40
+ //#endregion
41
+ export { NO_BORDER_STYLES, horizontalBorderPx, horizontalPaddingPx, lengthToPx, shorthandBorderWidthPx, shorthandSides };
42
+ //# sourceMappingURL=cssBox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cssBox.d.ts","names":[],"sources":["../../src/utils/cssBox.ts"],"mappings":";;;cAEa,gBAAA,EAAgB,GAAA;;AAA7B;;;;iBAOgB,UAAA,CAAW,KAAA;AAA3B;;;;;AAoBA;;AApBA,iBAoBgB,cAAA,CAAe,KAAA;EAAkB,IAAA;EAAe,KAAA;AAAA;;;;AAehE;iBAAgB,mBAAA,CAAoB,IAAA,EAAM,IAAA;;;;AA8B1C;;;iBAAgB,sBAAA,CAAuB,KAAA;;AAevC;;;;iBAAgB,kBAAA,CAAmB,IAAA,EAAM,IAAA"}
@@ -0,0 +1,156 @@
1
+ //#region src/utils/cssBox.ts
2
+ const NO_BORDER_STYLES = new Set(["none", "hidden"]);
3
+ /**
4
+ * Parse a length token into px. Handles `Npx`, `Nrem`, `Nem`, `Npt`, and
5
+ * unitless N (treated as px). Returns null for percentages, calc(),
6
+ * keywords, or anything that doesn't reduce to a concrete length.
7
+ */
8
+ function lengthToPx(value) {
9
+ const m = value.trim().match(/^([\d.]+)(px|rem|em|pt)?$/i);
10
+ if (!m) return null;
11
+ const n = parseFloat(m[1]);
12
+ switch ((m[2] || "px").toLowerCase()) {
13
+ case "px": return n;
14
+ case "rem":
15
+ case "em": return n * 16;
16
+ case "pt": return n * 1.333;
17
+ default: return null;
18
+ }
19
+ }
20
+ /**
21
+ * Expand a 1-4 token CSS shorthand (T R B L) into a left/right pair:
22
+ * 1: all sides
23
+ * 2: TB RL
24
+ * 3: T RL B
25
+ * 4: T R B L
26
+ */
27
+ function shorthandSides(value) {
28
+ const parts = value.trim().split(/\s+/);
29
+ switch (parts.length) {
30
+ case 1: return {
31
+ left: parts[0],
32
+ right: parts[0]
33
+ };
34
+ case 2:
35
+ case 3: return {
36
+ left: parts[1],
37
+ right: parts[1]
38
+ };
39
+ case 4: return {
40
+ left: parts[3],
41
+ right: parts[1]
42
+ };
43
+ default: return {};
44
+ }
45
+ }
46
+ /**
47
+ * Read horizontal padding (left + right) px from a parsed style root.
48
+ * Percentages are skipped since they'd need a known container width.
49
+ */
50
+ function horizontalPaddingPx(root) {
51
+ let left = null;
52
+ let right = null;
53
+ root.walkDecls((d) => {
54
+ switch (d.prop) {
55
+ case "padding": {
56
+ const { left: l, right: r } = shorthandSides(d.value);
57
+ if (l) left = lengthToPx(l);
58
+ if (r) right = lengthToPx(r);
59
+ break;
60
+ }
61
+ case "padding-left":
62
+ left = lengthToPx(d.value);
63
+ break;
64
+ case "padding-right":
65
+ right = lengthToPx(d.value);
66
+ break;
67
+ }
68
+ });
69
+ return (left ?? 0) + (right ?? 0);
70
+ }
71
+ /**
72
+ * Extract a px length from a CSS border shorthand (e.g. `1px solid red` → 1).
73
+ * Returns null when the value indicates no border (`none` or `hidden`).
74
+ * Defaults to 3px (CSS `medium`) when a visible style is set but no
75
+ * explicit width token is present in the shorthand value.
76
+ */
77
+ function shorthandBorderWidthPx(value) {
78
+ const tokens = value.trim().split(/\s+/);
79
+ if (tokens.some((t) => NO_BORDER_STYLES.has(t.toLowerCase()))) return null;
80
+ for (const t of tokens) {
81
+ const px = lengthToPx(t);
82
+ if (px != null) return px;
83
+ }
84
+ return 3;
85
+ }
86
+ /**
87
+ * Read horizontal border widths (left + right) px from a parsed style root.
88
+ * Per-side `border-style: none|hidden` overrides count as zero
89
+ * contribution. Returns total px or 0 when nothing resolves.
90
+ */
91
+ function horizontalBorderPx(root) {
92
+ let left = null;
93
+ let right = null;
94
+ let leftNone = false;
95
+ let rightNone = false;
96
+ root.walkDecls((d) => {
97
+ switch (d.prop) {
98
+ case "border": {
99
+ const w = shorthandBorderWidthPx(d.value);
100
+ if (w == null) leftNone = rightNone = true;
101
+ else {
102
+ left = right = w;
103
+ leftNone = rightNone = false;
104
+ }
105
+ break;
106
+ }
107
+ case "border-width": {
108
+ const { left: l, right: r } = shorthandSides(d.value);
109
+ if (l) left = lengthToPx(l) ?? left;
110
+ if (r) right = lengthToPx(r) ?? right;
111
+ break;
112
+ }
113
+ case "border-style": {
114
+ const { left: l, right: r } = shorthandSides(d.value);
115
+ if (l && NO_BORDER_STYLES.has(l.toLowerCase())) leftNone = true;
116
+ if (r && NO_BORDER_STYLES.has(r.toLowerCase())) rightNone = true;
117
+ break;
118
+ }
119
+ case "border-left": {
120
+ const w = shorthandBorderWidthPx(d.value);
121
+ if (w == null) leftNone = true;
122
+ else {
123
+ left = w;
124
+ leftNone = false;
125
+ }
126
+ break;
127
+ }
128
+ case "border-right": {
129
+ const w = shorthandBorderWidthPx(d.value);
130
+ if (w == null) rightNone = true;
131
+ else {
132
+ right = w;
133
+ rightNone = false;
134
+ }
135
+ break;
136
+ }
137
+ case "border-left-width":
138
+ left = lengthToPx(d.value) ?? left;
139
+ break;
140
+ case "border-right-width":
141
+ right = lengthToPx(d.value) ?? right;
142
+ break;
143
+ case "border-left-style":
144
+ if (NO_BORDER_STYLES.has(d.value.trim().toLowerCase())) leftNone = true;
145
+ break;
146
+ case "border-right-style":
147
+ if (NO_BORDER_STYLES.has(d.value.trim().toLowerCase())) rightNone = true;
148
+ break;
149
+ }
150
+ });
151
+ return (leftNone ? 0 : left ?? 0) + (rightNone ? 0 : right ?? 0);
152
+ }
153
+ //#endregion
154
+ export { NO_BORDER_STYLES, horizontalBorderPx, horizontalPaddingPx, lengthToPx, shorthandBorderWidthPx, shorthandSides };
155
+
156
+ //# sourceMappingURL=cssBox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cssBox.js","names":[],"sources":["../../src/utils/cssBox.ts"],"sourcesContent":["import type { Root } from 'postcss'\n\nexport const NO_BORDER_STYLES = new Set(['none', 'hidden'])\n\n/**\n * Parse a length token into px. Handles `Npx`, `Nrem`, `Nem`, `Npt`, and\n * unitless N (treated as px). Returns null for percentages, calc(),\n * keywords, or anything that doesn't reduce to a concrete length.\n */\nexport function lengthToPx(value: string): number | null {\n const m = value.trim().match(/^([\\d.]+)(px|rem|em|pt)?$/i)\n if (!m) return null\n const n = parseFloat(m[1])\n switch ((m[2] || 'px').toLowerCase()) {\n case 'px': return n\n case 'rem':\n case 'em': return n * 16\n case 'pt': return n * 1.333\n default: return null\n }\n}\n\n/**\n * Expand a 1-4 token CSS shorthand (T R B L) into a left/right pair:\n * 1: all sides\n * 2: TB RL\n * 3: T RL B\n * 4: T R B L\n */\nexport function shorthandSides(value: string): { left?: string; right?: string } {\n const parts = value.trim().split(/\\s+/)\n switch (parts.length) {\n case 1: return { left: parts[0], right: parts[0] }\n case 2:\n case 3: return { left: parts[1], right: parts[1] }\n case 4: return { left: parts[3], right: parts[1] }\n default: return {}\n }\n}\n\n/**\n * Read horizontal padding (left + right) px from a parsed style root.\n * Percentages are skipped since they'd need a known container width.\n */\nexport function horizontalPaddingPx(root: Root): number {\n let left: number | null = null\n let right: number | null = null\n\n root.walkDecls((d) => {\n switch (d.prop) {\n case 'padding': {\n const { left: l, right: r } = shorthandSides(d.value)\n if (l) left = lengthToPx(l)\n if (r) right = lengthToPx(r)\n break\n }\n case 'padding-left':\n left = lengthToPx(d.value)\n break\n case 'padding-right':\n right = lengthToPx(d.value)\n break\n }\n })\n\n return (left ?? 0) + (right ?? 0)\n}\n\n/**\n * Extract a px length from a CSS border shorthand (e.g. `1px solid red` → 1).\n * Returns null when the value indicates no border (`none` or `hidden`).\n * Defaults to 3px (CSS `medium`) when a visible style is set but no\n * explicit width token is present in the shorthand value.\n */\nexport function shorthandBorderWidthPx(value: string): number | null {\n const tokens = value.trim().split(/\\s+/)\n if (tokens.some((t) => NO_BORDER_STYLES.has(t.toLowerCase()))) return null\n for (const t of tokens) {\n const px = lengthToPx(t)\n if (px != null) return px\n }\n return 3\n}\n\n/**\n * Read horizontal border widths (left + right) px from a parsed style root.\n * Per-side `border-style: none|hidden` overrides count as zero\n * contribution. Returns total px or 0 when nothing resolves.\n */\nexport function horizontalBorderPx(root: Root): number {\n let left: number | null = null\n let right: number | null = null\n let leftNone = false\n let rightNone = false\n\n root.walkDecls((d) => {\n switch (d.prop) {\n case 'border': {\n const w = shorthandBorderWidthPx(d.value)\n if (w == null) {\n leftNone = rightNone = true\n }\n else {\n left = right = w\n leftNone = rightNone = false\n }\n break\n }\n case 'border-width': {\n const { left: l, right: r } = shorthandSides(d.value)\n if (l) left = lengthToPx(l) ?? left\n if (r) right = lengthToPx(r) ?? right\n break\n }\n case 'border-style': {\n const { left: l, right: r } = shorthandSides(d.value)\n if (l && NO_BORDER_STYLES.has(l.toLowerCase())) leftNone = true\n if (r && NO_BORDER_STYLES.has(r.toLowerCase())) rightNone = true\n break\n }\n case 'border-left': {\n const w = shorthandBorderWidthPx(d.value)\n if (w == null) leftNone = true\n else { left = w; leftNone = false }\n break\n }\n case 'border-right': {\n const w = shorthandBorderWidthPx(d.value)\n if (w == null) rightNone = true\n else { right = w; rightNone = false }\n break\n }\n case 'border-left-width':\n left = lengthToPx(d.value) ?? left\n break\n case 'border-right-width':\n right = lengthToPx(d.value) ?? right\n break\n case 'border-left-style':\n if (NO_BORDER_STYLES.has(d.value.trim().toLowerCase())) leftNone = true\n break\n case 'border-right-style':\n if (NO_BORDER_STYLES.has(d.value.trim().toLowerCase())) rightNone = true\n break\n }\n })\n\n return (leftNone ? 0 : (left ?? 0)) + (rightNone ? 0 : (right ?? 0))\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maizzle/framework",
3
- "version": "6.0.0-rc.21",
3
+ "version": "6.0.0-rc.22",
4
4
  "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,22 +0,0 @@
1
- import { CssConfig } from "../types/config.js";
2
- import { ChildNode } from "domhandler";
3
-
4
- //#region src/transformers/safeClassNames.d.ts
5
- /**
6
- * Safe class names transformer.
7
- *
8
- * Replaces unsafe characters (`:`, `/`, `[`, `]`, etc.) in:
9
- * - CSS selectors inside `<style>` tags
10
- * - HTML `class` attributes
11
- *
12
- * This makes Tailwind utility classes like `sm:text-base` safe for
13
- * email clients that cannot handle escaped characters in class names.
14
- *
15
- * Enabled by default. Disable by setting `css.safe` to `false`.
16
- * Customize replacements by passing a `Record<string, string>` — user
17
- * values are merged on top of the defaults.
18
- */
19
- declare function safeClassNames(dom: ChildNode[], config?: CssConfig): ChildNode[];
20
- //#endregion
21
- export { safeClassNames };
22
- //# sourceMappingURL=safeClassNames.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"safeClassNames.d.ts","names":[],"sources":["../../src/transformers/safeClassNames.ts"],"mappings":";;;;;;AAsGA;;;;;;;;;;;;iBAAgB,cAAA,CAAe,GAAA,EAAK,SAAA,IAAa,MAAA,GAAQ,SAAA,GAAiB,SAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"safeClassNames.js","names":[],"sources":["../../src/transformers/safeClassNames.ts"],"sourcesContent":["import postcss from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport type { ChildNode, Element } from 'domhandler'\nimport { walk } from '../utils/ast/index.ts'\nimport type { CssConfig } from '../types/config.ts'\n\nconst DEFAULT_REPLACEMENTS: Record<string, string> = {\n ':': '-',\n '/': '-',\n '%': 'pc',\n '.': '_',\n ',': '_',\n '#': '_',\n '[': '',\n ']': '',\n '(': '',\n ')': '',\n '{': '',\n '}': '',\n '!': '-i',\n '&': 'and-',\n '<': 'lt-',\n '=': 'eq-',\n '>': 'gt-',\n '|': 'or-',\n '@': 'at-',\n '?': 'q-',\n '\\\\': '-',\n '\"': '-',\n \"'\": '-',\n '*': '-',\n '+': '-',\n ';': '-',\n '^': '-',\n '`': '-',\n '~': '-',\n '$': '-',\n}\n\nfunction escapeForRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/**\n * Replace escaped special characters in CSS selectors.\n *\n * Tailwind generates selectors like `.sm\\:text-base`. This function\n * replaces the `\\:` with `-` (or whatever the configured replacement is)\n * so the selector becomes `.sm-text-base`, which is safe for email clients.\n */\nfunction processCssSelectors(css: string, replacements: Record<string, string>): string {\n // Matches \\<char> in CSS selectors — e.g. \\: \\/ \\. \\[ etc.\n const selectorRegex = new RegExp(\n `\\\\\\\\(${Object.keys(replacements).map(escapeForRegex).join('|')})`,\n 'g',\n )\n\n return postcss([\n (root: postcss.Root) => {\n root.walkRules((rule: postcss.Rule) => {\n rule.selector = rule.selector\n .replace(selectorRegex, (_matched, char) => replacements[char] ?? _matched)\n // Handle CSS unicode escape for comma (\\2c → _)\n .replaceAll('\\\\2c ', '_')\n })\n },\n ]).process(css, { parser: safeParser }).css\n}\n\n/**\n * Replace unsafe special characters in a class attribute value.\n *\n * Splits on whitespace and replaces each char from the replacements map\n * in each class token individually.\n */\nfunction processClassAttr(classStr: string, replacements: Record<string, string>): string {\n return classStr\n .split(/\\s+/)\n .filter(Boolean)\n .map((cls) => {\n for (const [from, to] of Object.entries(replacements)) {\n cls = cls.split(from).join(to)\n }\n return cls\n })\n .join(' ')\n}\n\n/**\n * Safe class names transformer.\n *\n * Replaces unsafe characters (`:`, `/`, `[`, `]`, etc.) in:\n * - CSS selectors inside `<style>` tags\n * - HTML `class` attributes\n *\n * This makes Tailwind utility classes like `sm:text-base` safe for\n * email clients that cannot handle escaped characters in class names.\n *\n * Enabled by default. Disable by setting `css.safe` to `false`.\n * Customize replacements by passing a `Record<string, string>` — user\n * values are merged on top of the defaults.\n */\nexport function safeClassNames(dom: ChildNode[], config: CssConfig = {}): ChildNode[] {\n const option = config.safe ?? true\n\n if (!option) return dom\n\n const replacements: Record<string, string> =\n option && typeof option === 'object'\n ? { ...DEFAULT_REPLACEMENTS, ...option }\n : DEFAULT_REPLACEMENTS\n\n walk(dom, (node) => {\n const el = node as Element\n\n // Process CSS selectors inside <style> tags\n if (el.name === 'style' && el.children?.length) {\n const text = el.children.find((c) => c.type === 'text') as any\n if (text?.data?.trim()) {\n text.data = processCssSelectors(text.data, replacements)\n }\n }\n\n // Replace special chars in class attributes\n if ('attribs' in el && el.attribs?.class) {\n el.attribs.class = processClassAttr(el.attribs.class, replacements)\n }\n })\n\n return dom\n}\n"],"mappings":";;;;;AAMA,MAAM,uBAA+C;CACnD,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;AAED,SAAS,eAAe,GAAmB;CACzC,OAAO,EAAE,QAAQ,uBAAuB,OAAO;;;;;;;;;AAUjD,SAAS,oBAAoB,KAAa,cAA8C;CAEtF,MAAM,gBAAgB,IAAI,OACxB,QAAQ,OAAO,KAAK,aAAa,CAAC,IAAI,eAAe,CAAC,KAAK,IAAI,CAAC,IAChE,IACD;CAED,OAAO,QAAQ,EACZ,SAAuB;EACtB,KAAK,WAAW,SAAuB;GACrC,KAAK,WAAW,KAAK,SAClB,QAAQ,gBAAgB,UAAU,SAAS,aAAa,SAAS,SAAS,CAE1E,WAAW,SAAS,IAAI;IAC3B;GAEL,CAAC,CAAC,QAAQ,KAAK,EAAE,QAAQ,YAAY,CAAC,CAAC;;;;;;;;AAS1C,SAAS,iBAAiB,UAAkB,cAA8C;CACxF,OAAO,SACJ,MAAM,MAAM,CACZ,OAAO,QAAQ,CACf,KAAK,QAAQ;EACZ,KAAK,MAAM,CAAC,MAAM,OAAO,OAAO,QAAQ,aAAa,EACnD,MAAM,IAAI,MAAM,KAAK,CAAC,KAAK,GAAG;EAEhC,OAAO;GACP,CACD,KAAK,IAAI;;;;;;;;;;;;;;;;AAiBd,SAAgB,eAAe,KAAkB,SAAoB,EAAE,EAAe;CACpF,MAAM,SAAS,OAAO,QAAQ;CAE9B,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,eACJ,UAAU,OAAO,WAAW,WACxB;EAAE,GAAG;EAAsB,GAAG;EAAQ,GACtC;CAEN,KAAK,MAAM,SAAS;EAClB,MAAM,KAAK;EAGX,IAAI,GAAG,SAAS,WAAW,GAAG,UAAU,QAAQ;GAC9C,MAAM,OAAO,GAAG,SAAS,MAAM,MAAM,EAAE,SAAS,OAAO;GACvD,IAAI,MAAM,MAAM,MAAM,EACpB,KAAK,OAAO,oBAAoB,KAAK,MAAM,aAAa;;EAK5D,IAAI,aAAa,MAAM,GAAG,SAAS,OACjC,GAAG,QAAQ,QAAQ,iBAAiB,GAAG,QAAQ,OAAO,aAAa;GAErE;CAEF,OAAO"}