@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.
- package/dist/components/Body.vue +1 -1
- package/dist/components/CodeBlock.vue +1 -1
- package/dist/components/CodeInline.vue +72 -2
- package/dist/components/Column.vue +2 -1
- package/dist/components/Container.vue +1 -11
- package/dist/components/Img.vue +199 -4
- package/dist/components/Preheader.vue +33 -5
- package/dist/components/Section.vue +9 -14
- package/dist/components/Text.vue +1 -1
- package/dist/composables/defineConfig.d.ts +3 -4
- package/dist/composables/defineConfig.d.ts.map +1 -1
- package/dist/composables/defineConfig.js +3 -4
- package/dist/composables/defineConfig.js.map +1 -1
- package/dist/composables/renderContext.d.ts +0 -1
- package/dist/composables/renderContext.d.ts.map +1 -1
- package/dist/composables/renderContext.js.map +1 -1
- package/dist/composables/usePreheader.d.ts +6 -5
- package/dist/composables/usePreheader.d.ts.map +1 -1
- package/dist/composables/usePreheader.js +3 -3
- package/dist/composables/usePreheader.js.map +1 -1
- package/dist/composables/useTransformers.d.ts +1 -1
- package/dist/composables/useTransformers.js +1 -1
- package/dist/composables/useTransformers.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/render/createRenderer.js +2 -2
- package/dist/render/createRenderer.js.map +1 -1
- package/dist/transformers/addAttributes.d.ts +18 -8
- package/dist/transformers/addAttributes.d.ts.map +1 -1
- package/dist/transformers/addAttributes.js +22 -8
- package/dist/transformers/addAttributes.js.map +1 -1
- package/dist/transformers/columnWidth.d.ts.map +1 -1
- package/dist/transformers/columnWidth.js +136 -150
- package/dist/transformers/columnWidth.js.map +1 -1
- package/dist/transformers/entities.d.ts.map +1 -1
- package/dist/transformers/entities.js +1 -0
- package/dist/transformers/entities.js.map +1 -1
- package/dist/transformers/index.d.ts.map +1 -1
- package/dist/transformers/index.js +7 -5
- package/dist/transformers/index.js.map +1 -1
- package/dist/transformers/inlineCss.js +2 -7
- package/dist/transformers/inlineCss.js.map +1 -1
- package/dist/transformers/minifyCodeInline.d.ts +29 -0
- package/dist/transformers/minifyCodeInline.d.ts.map +1 -0
- package/dist/transformers/minifyCodeInline.js +36 -0
- package/dist/transformers/minifyCodeInline.js.map +1 -0
- package/dist/transformers/msoPlaceholders.d.ts +10 -5
- package/dist/transformers/msoPlaceholders.d.ts.map +1 -1
- package/dist/transformers/msoPlaceholders.js +38 -7
- package/dist/transformers/msoPlaceholders.js.map +1 -1
- package/dist/transformers/safeSelectors.d.ts +37 -0
- package/dist/transformers/safeSelectors.d.ts.map +1 -0
- package/dist/transformers/{safeClassNames.js → safeSelectors.js} +24 -5
- package/dist/transformers/safeSelectors.js.map +1 -0
- package/dist/transformers/shorthandCss.js +38 -7
- package/dist/transformers/shorthandCss.js.map +1 -1
- package/dist/types/config.d.ts +2 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/ast/serializer.d.ts.map +1 -1
- package/dist/utils/ast/serializer.js +27 -17
- package/dist/utils/ast/serializer.js.map +1 -1
- package/dist/utils/cssBox.d.ts +42 -0
- package/dist/utils/cssBox.d.ts.map +1 -0
- package/dist/utils/cssBox.js +156 -0
- package/dist/utils/cssBox.js.map +1 -0
- package/package.json +1 -1
- package/dist/transformers/safeClassNames.d.ts +0 -22
- package/dist/transformers/safeClassNames.d.ts.map +0 -1
- 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
|
|
5
|
+
* Re-encode `<` and `>` in text nodes before serialization.
|
|
6
6
|
*
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* The parser decodes entities like `<` into raw `<` in text nodes. With
|
|
8
|
+
* `encodeEntities: false` (needed so other transformers can emit entity
|
|
9
|
+
* strings such as ` ` 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
|
-
*
|
|
12
|
-
*
|
|
13
|
+
* `&` is intentionally not re-encoded: the `entities` transformer writes
|
|
14
|
+
* literal entity strings (` `, `—`) 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
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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, "<").replace(/>/g, ">");
|
|
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
|
-
|
|
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'\
|
|
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 `<` into raw `<` in text nodes. With\n * `encodeEntities: false` (needed so other transformers can emit entity\n * strings such as ` ` 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 (` `, `—`) 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, '<')\n .replace(/>/g, '>')\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,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"}
|