@lupinum/nuxt-email 0.1.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/CHANGELOG.md +49 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/THIRD_PARTY_NOTICES +75 -0
- package/dist/chunks/setup.mjs +149 -0
- package/dist/module.d.mts +15 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +300 -0
- package/dist/runtime/code-block/create-component.d.ts +16 -0
- package/dist/runtime/code-block/create-component.js +106 -0
- package/dist/runtime/components/EBody.d.ts +4 -0
- package/dist/runtime/components/EBody.js +75 -0
- package/dist/runtime/components/EButton.d.ts +11 -0
- package/dist/runtime/components/EButton.js +89 -0
- package/dist/runtime/components/EButton.padding.d.ts +12 -0
- package/dist/runtime/components/EButton.padding.js +118 -0
- package/dist/runtime/components/ECodeInline.d.ts +13 -0
- package/dist/runtime/components/ECodeInline.js +43 -0
- package/dist/runtime/components/EColumn.d.ts +4 -0
- package/dist/runtime/components/EColumn.js +12 -0
- package/dist/runtime/components/EContainer.d.ts +4 -0
- package/dist/runtime/components/EContainer.js +35 -0
- package/dist/runtime/components/EFont.d.ts +24 -0
- package/dist/runtime/components/EFont.js +124 -0
- package/dist/runtime/components/EHead.d.ts +4 -0
- package/dist/runtime/components/EHead.js +17 -0
- package/dist/runtime/components/EHeading.d.ts +7 -0
- package/dist/runtime/components/EHeading.js +23 -0
- package/dist/runtime/components/EHr.d.ts +4 -0
- package/dist/runtime/components/EHr.js +26 -0
- package/dist/runtime/components/EHtml.d.ts +4 -0
- package/dist/runtime/components/EHtml.js +17 -0
- package/dist/runtime/components/EImg.d.ts +7 -0
- package/dist/runtime/components/EImg.js +44 -0
- package/dist/runtime/components/ELink.d.ts +6 -0
- package/dist/runtime/components/ELink.js +35 -0
- package/dist/runtime/components/EMarkdown.d.ts +8 -0
- package/dist/runtime/components/EMarkdown.js +64 -0
- package/dist/runtime/components/EPreview.d.ts +8 -0
- package/dist/runtime/components/EPreview.js +85 -0
- package/dist/runtime/components/ERow.d.ts +4 -0
- package/dist/runtime/components/ERow.js +53 -0
- package/dist/runtime/components/ESection.d.ts +4 -0
- package/dist/runtime/components/ESection.js +35 -0
- package/dist/runtime/components/ETailwind.d.ts +24 -0
- package/dist/runtime/components/ETailwind.js +49 -0
- package/dist/runtime/components/EText.d.ts +4 -0
- package/dist/runtime/components/EText.js +20 -0
- package/dist/runtime/components/EText.margins.d.ts +18 -0
- package/dist/runtime/components/EText.margins.js +81 -0
- package/dist/runtime/components/attributes.d.ts +4 -0
- package/dist/runtime/components/attributes.js +11 -0
- package/dist/runtime/components/email-component-names.d.ts +2 -0
- package/dist/runtime/components/email-component-names.js +20 -0
- package/dist/runtime/components/email-component-registry.d.ts +8 -0
- package/dist/runtime/components/email-component-registry.js +2 -0
- package/dist/runtime/components/index.d.ts +23 -0
- package/dist/runtime/components/index.js +18 -0
- package/dist/runtime/components/markdown/parse-css-in-js-to-inline-css.d.ts +2 -0
- package/dist/runtime/components/markdown/parse-css-in-js-to-inline-css.js +60 -0
- package/dist/runtime/components/markdown/render-markdown.d.ts +2 -0
- package/dist/runtime/components/markdown/render-markdown.js +137 -0
- package/dist/runtime/components/markdown/styles.d.ts +30 -0
- package/dist/runtime/components/markdown/styles.js +91 -0
- package/dist/runtime/components/presentation-table.d.ts +7 -0
- package/dist/runtime/components/presentation-table.js +11 -0
- package/dist/runtime/components/style.d.ts +5 -0
- package/dist/runtime/components/style.js +32 -0
- package/dist/runtime/components/table-padding.d.ts +6 -0
- package/dist/runtime/components/table-padding.js +20 -0
- package/dist/runtime/define-email.d.ts +2 -0
- package/dist/runtime/define-email.js +5 -0
- package/dist/runtime/dev-preview/page-script.d.ts +1 -0
- package/dist/runtime/dev-preview/page-script.js +403 -0
- package/dist/runtime/dev-preview/page.css.d.ts +1 -0
- package/dist/runtime/dev-preview/page.css.js +602 -0
- package/dist/runtime/dev-preview/page.get.d.ts +4 -0
- package/dist/runtime/dev-preview/page.get.js +119 -0
- package/dist/runtime/dev-preview/render.get.d.ts +3 -0
- package/dist/runtime/dev-preview/render.get.js +123 -0
- package/dist/runtime/dev-preview/templates.get.d.ts +7 -0
- package/dist/runtime/dev-preview/templates.get.js +14 -0
- package/dist/runtime/errors.d.ts +4 -0
- package/dist/runtime/errors.js +7 -0
- package/dist/runtime/render/define-email.d.ts +56 -0
- package/dist/runtime/render/define-email.js +40 -0
- package/dist/runtime/render/document.d.ts +4 -0
- package/dist/runtime/render/document.js +75 -0
- package/dist/runtime/render/errors.d.ts +4 -0
- package/dist/runtime/render/errors.js +8 -0
- package/dist/runtime/render/plain-text.d.ts +1 -0
- package/dist/runtime/render/plain-text.js +15 -0
- package/dist/runtime/render/render-component.d.ts +4 -0
- package/dist/runtime/render/render-component.js +46 -0
- package/dist/runtime/render/render-email-component.d.ts +16 -0
- package/dist/runtime/render/render-email-component.js +46 -0
- package/dist/runtime/render/types.d.ts +9 -0
- package/dist/runtime/render/types.js +0 -0
- package/dist/runtime/tailwind/engine/compatibility/escape-class-name.d.ts +8 -0
- package/dist/runtime/tailwind/engine/compatibility/escape-class-name.js +10 -0
- package/dist/runtime/tailwind/engine/compatibility/sanitize-class-name.d.ts +7 -0
- package/dist/runtime/tailwind/engine/compatibility/sanitize-class-name.js +17 -0
- package/dist/runtime/tailwind/engine/compatibility/unescape-class.d.ts +1 -0
- package/dist/runtime/tailwind/engine/compatibility/unescape-class.js +3 -0
- package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.d.ts +63 -0
- package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.js +107 -0
- package/dist/runtime/tailwind/engine/css/extract-rules-per-class.d.ts +6 -0
- package/dist/runtime/tailwind/engine/css/extract-rules-per-class.js +54 -0
- package/dist/runtime/tailwind/engine/css/get-css-property.d.ts +17 -0
- package/dist/runtime/tailwind/engine/css/get-css-property.js +3 -0
- package/dist/runtime/tailwind/engine/css/get-custom-properties.d.ts +8 -0
- package/dist/runtime/tailwind/engine/css/get-custom-properties.js +37 -0
- package/dist/runtime/tailwind/engine/css/is-part-inlinable.d.ts +2 -0
- package/dist/runtime/tailwind/engine/css/is-part-inlinable.js +9 -0
- package/dist/runtime/tailwind/engine/css/is-rule-inlinable.d.ts +2 -0
- package/dist/runtime/tailwind/engine/css/is-rule-inlinable.js +9 -0
- package/dist/runtime/tailwind/engine/css/make-inline-styles-for.d.ts +21 -0
- package/dist/runtime/tailwind/engine/css/make-inline-styles-for.js +62 -0
- package/dist/runtime/tailwind/engine/css/resolve-all-css-variables.d.ts +8 -0
- package/dist/runtime/tailwind/engine/css/resolve-all-css-variables.js +138 -0
- package/dist/runtime/tailwind/engine/css/resolve-calc-expressions.d.ts +5 -0
- package/dist/runtime/tailwind/engine/css/resolve-calc-expressions.js +101 -0
- package/dist/runtime/tailwind/engine/css/sanitize-declarations.d.ts +15 -0
- package/dist/runtime/tailwind/engine/css/sanitize-declarations.js +358 -0
- package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.d.ts +17 -0
- package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.js +30 -0
- package/dist/runtime/tailwind/engine/css/split-mixed-rule.d.ts +12 -0
- package/dist/runtime/tailwind/engine/css/split-mixed-rule.js +37 -0
- package/dist/runtime/tailwind/engine/css/strip-empty-tailwind-vars.d.ts +19 -0
- package/dist/runtime/tailwind/engine/css/strip-empty-tailwind-vars.js +44 -0
- package/dist/runtime/tailwind/engine/css/unwrap-value.d.ts +2 -0
- package/dist/runtime/tailwind/engine/css/unwrap-value.js +6 -0
- package/dist/runtime/tailwind/engine/index.d.ts +63 -0
- package/dist/runtime/tailwind/engine/index.js +53 -0
- package/dist/runtime/tailwind/engine/pixel-based-preset.d.ts +9 -0
- package/dist/runtime/tailwind/engine/pixel-based-preset.js +58 -0
- package/dist/runtime/tailwind/engine/sanitize-stylesheet.d.ts +10 -0
- package/dist/runtime/tailwind/engine/sanitize-stylesheet.js +8 -0
- package/dist/runtime/tailwind/engine/setup-tailwind.d.ts +23 -0
- package/dist/runtime/tailwind/engine/setup-tailwind.js +98 -0
- package/dist/runtime/tailwind/engine/stylesheets.d.ts +4 -0
- package/dist/runtime/tailwind/engine/stylesheets.js +1756 -0
- package/dist/runtime/tailwind/engine/types.d.ts +25 -0
- package/dist/runtime/tailwind/engine/types.js +0 -0
- package/dist/runtime/tailwind/errors.d.ts +7 -0
- package/dist/runtime/tailwind/errors.js +9 -0
- package/dist/runtime/tailwind/inline-utils.d.ts +19 -0
- package/dist/runtime/tailwind/inline-utils.js +24 -0
- package/dist/runtime/tailwind/nested.d.ts +59 -0
- package/dist/runtime/tailwind/nested.js +47 -0
- package/dist/runtime/tailwind/post-render.d.ts +7 -0
- package/dist/runtime/tailwind/post-render.js +156 -0
- package/dist/runtime/template-registry/errors.d.ts +5 -0
- package/dist/runtime/template-registry/errors.js +11 -0
- package/dist/runtime/template-registry/registry.d.ts +13 -0
- package/dist/runtime/testing/index.d.ts +13 -0
- package/dist/runtime/testing/index.js +2 -0
- package/dist/types.d.mts +5 -0
- package/package.json +122 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generate,
|
|
3
|
+
List,
|
|
4
|
+
parse,
|
|
5
|
+
walk
|
|
6
|
+
} from "css-tree";
|
|
7
|
+
function rgbNode(r, g, b, alpha) {
|
|
8
|
+
const children = new List();
|
|
9
|
+
children.appendData({
|
|
10
|
+
type: "Number",
|
|
11
|
+
value: r.toFixed(0)
|
|
12
|
+
});
|
|
13
|
+
children.appendData({
|
|
14
|
+
type: "Operator",
|
|
15
|
+
value: ","
|
|
16
|
+
});
|
|
17
|
+
children.appendData({
|
|
18
|
+
type: "Number",
|
|
19
|
+
value: g.toFixed(0)
|
|
20
|
+
});
|
|
21
|
+
children.appendData({
|
|
22
|
+
type: "Operator",
|
|
23
|
+
value: ","
|
|
24
|
+
});
|
|
25
|
+
children.appendData({
|
|
26
|
+
type: "Number",
|
|
27
|
+
value: b.toFixed(0)
|
|
28
|
+
});
|
|
29
|
+
if (alpha !== 1 && alpha !== void 0) {
|
|
30
|
+
children.appendData({
|
|
31
|
+
type: "Operator",
|
|
32
|
+
value: ","
|
|
33
|
+
});
|
|
34
|
+
children.appendData({
|
|
35
|
+
type: "Number",
|
|
36
|
+
value: alpha.toString()
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
type: "Function",
|
|
41
|
+
name: "rgb",
|
|
42
|
+
children
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const LAB_TO_LMS = {
|
|
46
|
+
l: [0.3963377773761749, 0.2158037573099136],
|
|
47
|
+
m: [-0.1055613458156586, -0.0638541728258133],
|
|
48
|
+
s: [-0.0894841775298119, -1.2914855480194092]
|
|
49
|
+
};
|
|
50
|
+
const LSM_TO_RGB = {
|
|
51
|
+
r: [4.076741636075958, -3.307711539258063, 0.2309699031821043],
|
|
52
|
+
g: [-1.2684379732850315, 2.609757349287688, -0.341319376002657],
|
|
53
|
+
b: [-0.0041960761386756, -0.7034186179359362, 1.7076146940746117]
|
|
54
|
+
};
|
|
55
|
+
function lrgbToRgb(input) {
|
|
56
|
+
const absoluteNumber = Math.abs(input);
|
|
57
|
+
const sign = input < 0 ? -1 : 1;
|
|
58
|
+
if (absoluteNumber > 31308e-7) {
|
|
59
|
+
return sign * (absoluteNumber ** (1 / 2.4) * 1.055 - 0.055);
|
|
60
|
+
}
|
|
61
|
+
return input * 12.92;
|
|
62
|
+
}
|
|
63
|
+
function clamp(value, min, max) {
|
|
64
|
+
return Math.min(Math.max(value, min), max);
|
|
65
|
+
}
|
|
66
|
+
function oklchToOklab(oklch) {
|
|
67
|
+
return {
|
|
68
|
+
l: oklch.l,
|
|
69
|
+
a: oklch.c * Math.cos(oklch.h / 180 * Math.PI),
|
|
70
|
+
b: oklch.c * Math.sin(oklch.h / 180 * Math.PI)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function oklchToRgb(oklch) {
|
|
74
|
+
const oklab = oklchToOklab(oklch);
|
|
75
|
+
const l = (oklab.l + LAB_TO_LMS.l[0] * oklab.a + LAB_TO_LMS.l[1] * oklab.b) ** 3;
|
|
76
|
+
const m = (oklab.l + LAB_TO_LMS.m[0] * oklab.a + LAB_TO_LMS.m[1] * oklab.b) ** 3;
|
|
77
|
+
const s = (oklab.l + LAB_TO_LMS.s[0] * oklab.a + LAB_TO_LMS.s[1] * oklab.b) ** 3;
|
|
78
|
+
const r = 255 * lrgbToRgb(
|
|
79
|
+
LSM_TO_RGB.r[0] * l + LSM_TO_RGB.r[1] * m + LSM_TO_RGB.r[2] * s
|
|
80
|
+
);
|
|
81
|
+
const g = 255 * lrgbToRgb(
|
|
82
|
+
LSM_TO_RGB.g[0] * l + LSM_TO_RGB.g[1] * m + LSM_TO_RGB.g[2] * s
|
|
83
|
+
);
|
|
84
|
+
const b = 255 * lrgbToRgb(
|
|
85
|
+
LSM_TO_RGB.b[0] * l + LSM_TO_RGB.b[1] * m + LSM_TO_RGB.b[2] * s
|
|
86
|
+
);
|
|
87
|
+
return {
|
|
88
|
+
r: clamp(r, 0, 255),
|
|
89
|
+
g: clamp(g, 0, 255),
|
|
90
|
+
b: clamp(b, 0, 255)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function separteShorthandDeclaration(shorthandToReplace, [start, end]) {
|
|
94
|
+
shorthandToReplace.property = start;
|
|
95
|
+
const values = shorthandToReplace.value.type === "Value" ? shorthandToReplace.value.children.toArray().filter(
|
|
96
|
+
(child) => child.type !== "Operator" && child.type !== "WhiteSpace"
|
|
97
|
+
) : [shorthandToReplace.value];
|
|
98
|
+
let endValue = shorthandToReplace.value;
|
|
99
|
+
if (values.length === 2) {
|
|
100
|
+
endValue = {
|
|
101
|
+
type: "Value",
|
|
102
|
+
children: new List().fromArray([values[1]])
|
|
103
|
+
};
|
|
104
|
+
shorthandToReplace.value = {
|
|
105
|
+
type: "Value",
|
|
106
|
+
children: new List().fromArray([values[0]])
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
type: "Declaration",
|
|
111
|
+
property: end,
|
|
112
|
+
value: endValue,
|
|
113
|
+
important: shorthandToReplace.important
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export function sanitizeDeclarations(nodeContainingDeclarations) {
|
|
117
|
+
walk(nodeContainingDeclarations, {
|
|
118
|
+
visit: "Declaration",
|
|
119
|
+
enter(declaration, item, list) {
|
|
120
|
+
if (declaration.value.type === "Raw") {
|
|
121
|
+
declaration.value = parse(declaration.value.value, {
|
|
122
|
+
context: "value"
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (/border-(?:[a-z]+-){0,2}radius\s*:\s*calc\s*\(\s*infinity\s*\*\s*1px\s*\)/i.test(
|
|
126
|
+
generate(declaration)
|
|
127
|
+
)) {
|
|
128
|
+
declaration.value = parse("9999px", { context: "value" });
|
|
129
|
+
}
|
|
130
|
+
walk(declaration, {
|
|
131
|
+
visit: "Function",
|
|
132
|
+
enter(func, funcParentListItem) {
|
|
133
|
+
const children = func.children.toArray();
|
|
134
|
+
if (func.name === "oklch") {
|
|
135
|
+
let l;
|
|
136
|
+
let c;
|
|
137
|
+
let h;
|
|
138
|
+
let a;
|
|
139
|
+
for (const child of children) {
|
|
140
|
+
if (child.type === "Number") {
|
|
141
|
+
if (l === void 0) {
|
|
142
|
+
l = Number.parseFloat(child.value);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (c === void 0) {
|
|
146
|
+
c = Number.parseFloat(child.value);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (h === void 0) {
|
|
150
|
+
h = Number.parseFloat(child.value);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (a === void 0) {
|
|
154
|
+
a = Number.parseFloat(child.value);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (child.type === "Dimension" && child.unit === "deg") {
|
|
159
|
+
if (h === void 0) {
|
|
160
|
+
h = Number.parseFloat(child.value);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (child.type === "Percentage") {
|
|
165
|
+
if (l === void 0) {
|
|
166
|
+
l = Number.parseFloat(child.value) / 100;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (a === void 0) {
|
|
170
|
+
a = Number.parseFloat(child.value) / 100;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (l === void 0 || c === void 0 || h === void 0) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
"Could not determine the parameters of an oklch() function.",
|
|
177
|
+
{
|
|
178
|
+
cause: declaration
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
const rgb = oklchToRgb({
|
|
183
|
+
l,
|
|
184
|
+
c,
|
|
185
|
+
h
|
|
186
|
+
});
|
|
187
|
+
funcParentListItem.data = rgbNode(rgb.r, rgb.g, rgb.b, a);
|
|
188
|
+
}
|
|
189
|
+
if (func.name === "rgb" || func.name === "rgba") {
|
|
190
|
+
let r;
|
|
191
|
+
let g;
|
|
192
|
+
let b;
|
|
193
|
+
let a;
|
|
194
|
+
for (const child of children) {
|
|
195
|
+
if (child.type === "Number") {
|
|
196
|
+
if (r === void 0) {
|
|
197
|
+
r = Number.parseFloat(child.value);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (g === void 0) {
|
|
201
|
+
g = Number.parseFloat(child.value);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (b === void 0) {
|
|
205
|
+
b = Number.parseFloat(child.value);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (a === void 0) {
|
|
209
|
+
a = Number.parseFloat(child.value);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (child.type === "Percentage") {
|
|
214
|
+
if (r === void 0) {
|
|
215
|
+
r = Number.parseFloat(child.value) * 255 / 100;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (g === void 0) {
|
|
219
|
+
g = Number.parseFloat(child.value) * 255 / 100;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (b === void 0) {
|
|
223
|
+
b = Number.parseFloat(child.value) * 255 / 100;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (a === void 0) {
|
|
227
|
+
a = Number.parseFloat(child.value) / 100;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (r === void 0 || g === void 0 || b === void 0) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
"Could not determine the parameters of an rgb() function.",
|
|
234
|
+
{
|
|
235
|
+
cause: declaration
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
if (a === void 0 || a === 1) {
|
|
240
|
+
funcParentListItem.data = rgbNode(r, g, b);
|
|
241
|
+
} else {
|
|
242
|
+
funcParentListItem.data = rgbNode(r, g, b, a);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
walk(declaration, {
|
|
248
|
+
visit: "Hash",
|
|
249
|
+
enter(hash, hashParentListItem) {
|
|
250
|
+
const hex = hash.value.trim();
|
|
251
|
+
if (hex.length === 3) {
|
|
252
|
+
const r2 = Number.parseInt(hex.charAt(0) + hex.charAt(0), 16);
|
|
253
|
+
const g2 = Number.parseInt(hex.charAt(1) + hex.charAt(1), 16);
|
|
254
|
+
const b2 = Number.parseInt(hex.charAt(2) + hex.charAt(2), 16);
|
|
255
|
+
hashParentListItem.data = rgbNode(r2, g2, b2);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (hex.length === 4) {
|
|
259
|
+
const r2 = Number.parseInt(hex.charAt(0) + hex.charAt(0), 16);
|
|
260
|
+
const g2 = Number.parseInt(hex.charAt(1) + hex.charAt(1), 16);
|
|
261
|
+
const b2 = Number.parseInt(hex.charAt(2) + hex.charAt(2), 16);
|
|
262
|
+
const a2 = Number.parseInt(hex.charAt(3) + hex.charAt(3), 16) / 255;
|
|
263
|
+
hashParentListItem.data = rgbNode(r2, g2, b2, a2);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (hex.length === 5) {
|
|
267
|
+
const r2 = Number.parseInt(hex.slice(0, 2), 16);
|
|
268
|
+
const g2 = Number.parseInt(hex.charAt(2) + hex.charAt(2), 16);
|
|
269
|
+
const b2 = Number.parseInt(hex.charAt(3) + hex.charAt(3), 16);
|
|
270
|
+
const a2 = Number.parseInt(hex.charAt(4) + hex.charAt(4), 16) / 255;
|
|
271
|
+
hashParentListItem.data = rgbNode(r2, g2, b2, a2);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (hex.length === 6) {
|
|
275
|
+
const r2 = Number.parseInt(hex.slice(0, 2), 16);
|
|
276
|
+
const g2 = Number.parseInt(hex.slice(2, 4), 16);
|
|
277
|
+
const b2 = Number.parseInt(hex.slice(4, 6), 16);
|
|
278
|
+
hashParentListItem.data = rgbNode(r2, g2, b2);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (hex.length === 7) {
|
|
282
|
+
const r2 = Number.parseInt(hex.slice(0, 2), 16);
|
|
283
|
+
const g2 = Number.parseInt(hex.slice(2, 4), 16);
|
|
284
|
+
const b2 = Number.parseInt(hex.slice(4, 6), 16);
|
|
285
|
+
const a2 = Number.parseInt(hex.charAt(6) + hex.charAt(6), 16) / 255;
|
|
286
|
+
hashParentListItem.data = rgbNode(r2, g2, b2, a2);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const r = Number.parseInt(hex.slice(0, 2), 16);
|
|
290
|
+
const g = Number.parseInt(hex.slice(2, 4), 16);
|
|
291
|
+
const b = Number.parseInt(hex.slice(4, 6), 16);
|
|
292
|
+
const a = Number.parseInt(hex.slice(6, 8), 16) / 255;
|
|
293
|
+
hashParentListItem.data = rgbNode(r, g, b, a);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
walk(declaration, {
|
|
297
|
+
visit: "Function",
|
|
298
|
+
enter(func, parentListItem) {
|
|
299
|
+
if (func.name === "color-mix") {
|
|
300
|
+
const children = func.children.toArray();
|
|
301
|
+
const color = children[3];
|
|
302
|
+
const opacity = children[4];
|
|
303
|
+
if (func.children.last?.type === "Identifier" && func.children.last.name === "transparent" && color?.type === "Function" && color?.name === "rgb" && opacity) {
|
|
304
|
+
if (opacity.type === "Percentage") {
|
|
305
|
+
const alpha = Number.parseFloat(opacity.value) / 100;
|
|
306
|
+
if (alpha < 1) {
|
|
307
|
+
color.children.appendData({
|
|
308
|
+
type: "Operator",
|
|
309
|
+
value: ","
|
|
310
|
+
});
|
|
311
|
+
color.children.appendData({
|
|
312
|
+
type: "Number",
|
|
313
|
+
value: alpha.toString()
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
color.children.appendData({
|
|
318
|
+
type: "Operator",
|
|
319
|
+
value: ","
|
|
320
|
+
});
|
|
321
|
+
color.children.appendData(opacity);
|
|
322
|
+
}
|
|
323
|
+
parentListItem.data = color;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
if (declaration.property === "padding-inline") {
|
|
329
|
+
const paddingRight = separteShorthandDeclaration(declaration, [
|
|
330
|
+
"padding-left",
|
|
331
|
+
"padding-right"
|
|
332
|
+
]);
|
|
333
|
+
list.insertData(paddingRight, item);
|
|
334
|
+
}
|
|
335
|
+
if (declaration.property === "padding-block") {
|
|
336
|
+
const paddingBottom = separteShorthandDeclaration(declaration, [
|
|
337
|
+
"padding-top",
|
|
338
|
+
"padding-bottom"
|
|
339
|
+
]);
|
|
340
|
+
list.insertData(paddingBottom, item);
|
|
341
|
+
}
|
|
342
|
+
if (declaration.property === "margin-inline") {
|
|
343
|
+
const marginRight = separteShorthandDeclaration(declaration, [
|
|
344
|
+
"margin-left",
|
|
345
|
+
"margin-right"
|
|
346
|
+
]);
|
|
347
|
+
list.insertData(marginRight, item);
|
|
348
|
+
}
|
|
349
|
+
if (declaration.property === "margin-block") {
|
|
350
|
+
const paddingBottom = separteShorthandDeclaration(declaration, [
|
|
351
|
+
"margin-top",
|
|
352
|
+
"margin-bottom"
|
|
353
|
+
]);
|
|
354
|
+
list.insertData(paddingBottom, item);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type CssNode } from 'css-tree';
|
|
2
|
+
/**
|
|
3
|
+
* This function goes through a few steps to ensure the best email client support and
|
|
4
|
+
* to ensure that media queries and pseudo classes are applied correctly alongside
|
|
5
|
+
* the inline styles.
|
|
6
|
+
*
|
|
7
|
+
* What it does:
|
|
8
|
+
* 1. Converts all declarations in all rules into important ones
|
|
9
|
+
* 2. Sanitizes class selectors of all non-inlinable rules
|
|
10
|
+
* 3. Removes --tw-* custom property declarations — by this point all CSS
|
|
11
|
+
* variables have been resolved, so these are dead weight in email HTML.
|
|
12
|
+
* 4. Strips empty-fallback var(--tw-*,) refs that Tailwind v4 emits for
|
|
13
|
+
* variant-stacking idioms (filter, font-variant-numeric, etc.) — email
|
|
14
|
+
* clients can't resolve CSS custom properties reliably, so any --tw-*
|
|
15
|
+
* left as a bare empty-fallback ref would reach the client broken.
|
|
16
|
+
*/
|
|
17
|
+
export declare function sanitizeNonInlinableRules(node: CssNode): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { string, walk } from "css-tree";
|
|
2
|
+
import { sanitizeClassName } from "../compatibility/sanitize-class-name.js";
|
|
3
|
+
import { isRuleInlinable } from "./is-rule-inlinable.js";
|
|
4
|
+
import { stripEmptyTailwindVars } from "./strip-empty-tailwind-vars.js";
|
|
5
|
+
export function sanitizeNonInlinableRules(node) {
|
|
6
|
+
walk(node, {
|
|
7
|
+
visit: "Rule",
|
|
8
|
+
enter(rule) {
|
|
9
|
+
if (!isRuleInlinable(rule)) {
|
|
10
|
+
walk(rule.prelude, (node2) => {
|
|
11
|
+
if (node2.type === "ClassSelector") {
|
|
12
|
+
const unescapedClassName = string.decode(node2.name);
|
|
13
|
+
node2.name = sanitizeClassName(unescapedClassName);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
walk(rule, {
|
|
17
|
+
visit: "Declaration",
|
|
18
|
+
enter(declaration, item, list) {
|
|
19
|
+
if (declaration.property.startsWith("--tw-")) {
|
|
20
|
+
list.remove(item);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
declaration.important = true;
|
|
24
|
+
stripEmptyTailwindVars(declaration.value);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Rule } from 'css-tree';
|
|
2
|
+
/**
|
|
3
|
+
* Split a rule into inlinable and non-inlinable parts. Caller must only pass rules
|
|
4
|
+
* for which isRuleInlinable(rule) is false. Returns clones so the original stylesheet is never mutated.
|
|
5
|
+
*
|
|
6
|
+
* @returns inlinablePart: rule with only inlinable block children, or null if none.
|
|
7
|
+
* nonInlinablePart: rule with only non-inlinable block children, or null if none.
|
|
8
|
+
*/
|
|
9
|
+
export declare function splitMixedRule(rule: Rule): {
|
|
10
|
+
inlinablePart: Rule | null;
|
|
11
|
+
nonInlinablePart: Rule | null;
|
|
12
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { clone, find, List } from "css-tree";
|
|
2
|
+
import { isPartInlinable } from "./is-part-inlinable.js";
|
|
3
|
+
export function splitMixedRule(rule) {
|
|
4
|
+
const selectorHasPseudo = rule.prelude !== null && find(
|
|
5
|
+
rule.prelude,
|
|
6
|
+
(node) => node.type === "PseudoClassSelector" || node.type === "PseudoElementSelector"
|
|
7
|
+
) !== null;
|
|
8
|
+
if (selectorHasPseudo) {
|
|
9
|
+
return { inlinablePart: null, nonInlinablePart: clone(rule) };
|
|
10
|
+
}
|
|
11
|
+
const ruleCloneInlinable = clone(rule);
|
|
12
|
+
const ruleCloneNonInlinable = clone(rule);
|
|
13
|
+
const inlinableParts = [];
|
|
14
|
+
const nonInlinableParts = [];
|
|
15
|
+
for (const part of ruleCloneInlinable.block.children.toArray()) {
|
|
16
|
+
if (isPartInlinable(part)) {
|
|
17
|
+
inlinableParts.push(part);
|
|
18
|
+
} else {
|
|
19
|
+
nonInlinableParts.push(part);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const inlinablePart = inlinableParts.length > 0 ? {
|
|
23
|
+
...ruleCloneInlinable,
|
|
24
|
+
block: {
|
|
25
|
+
type: "Block",
|
|
26
|
+
children: new List().fromArray(inlinableParts)
|
|
27
|
+
}
|
|
28
|
+
} : null;
|
|
29
|
+
const nonInlinablePart = nonInlinableParts.length > 0 ? {
|
|
30
|
+
...ruleCloneNonInlinable,
|
|
31
|
+
block: {
|
|
32
|
+
type: "Block",
|
|
33
|
+
children: new List().fromArray(nonInlinableParts)
|
|
34
|
+
}
|
|
35
|
+
} : null;
|
|
36
|
+
return { inlinablePart, nonInlinablePart };
|
|
37
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type CssNode } from 'css-tree';
|
|
2
|
+
/**
|
|
3
|
+
* Tailwind v4 emits variant-stacking idioms like
|
|
4
|
+
* font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) tabular-nums var(--tw-numeric-fraction,)
|
|
5
|
+
* filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-invert,) ...
|
|
6
|
+
* where each var() has an empty fallback so missing variants collapse to nothing.
|
|
7
|
+
* Tailwind deliberately leaves these variant vars undefined until used, so they
|
|
8
|
+
* stay in the output here and produce unresolvable custom properties in email HTML
|
|
9
|
+
* (no email client supports CSS custom properties reliably). Per the CSS spec
|
|
10
|
+
* (https://www.w3.org/TR/css-variables-1/#using-variables) an empty fallback means
|
|
11
|
+
* "use empty string if the variable is undefined", which is exactly what we want.
|
|
12
|
+
*
|
|
13
|
+
* Scoped to the `--tw-` prefix so any user-authored empty-fallback var() refs
|
|
14
|
+
* are left untouched.
|
|
15
|
+
*
|
|
16
|
+
* Uses post-order traversal so an outer var(--tw-X, var(--tw-Y,)) collapses
|
|
17
|
+
* correctly after the inner var() has been removed.
|
|
18
|
+
*/
|
|
19
|
+
export declare function stripEmptyTailwindVars(node: CssNode): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { generate, walk } from "css-tree";
|
|
2
|
+
export function stripEmptyTailwindVars(node) {
|
|
3
|
+
walk(node, {
|
|
4
|
+
visit: "Function",
|
|
5
|
+
leave(func, funcItem, funcList) {
|
|
6
|
+
if (func.name !== "var") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
let variableName;
|
|
10
|
+
walk(func, {
|
|
11
|
+
visit: "Identifier",
|
|
12
|
+
enter(identifier) {
|
|
13
|
+
variableName = identifier.name;
|
|
14
|
+
return this.break;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
if (!variableName?.startsWith("--tw-")) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
let sawComma = false;
|
|
21
|
+
let hasFallbackContent = false;
|
|
22
|
+
func.children.forEach((child) => {
|
|
23
|
+
if (!sawComma) {
|
|
24
|
+
if (child.type === "Operator" && child.value === ",") {
|
|
25
|
+
sawComma = true;
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let childValue = generate(child).trim();
|
|
30
|
+
if (child.type === "Raw") {
|
|
31
|
+
const emptyTailwindVarPattern = /var\(--tw-[^,()]+,\s*\)/g;
|
|
32
|
+
childValue = childValue.replaceAll(emptyTailwindVarPattern, "").trim();
|
|
33
|
+
}
|
|
34
|
+
if (childValue.length > 0) {
|
|
35
|
+
hasFallbackContent = true;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
if (!sawComma || hasFallbackContent) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
funcList.remove(funcItem);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { TailwindEngineOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A single element's inline styles as ordered kebab-case CSS declarations
|
|
4
|
+
* (`cssProperty -> value`). Insertion order matches React Email's object-spread
|
|
5
|
+
* serialization order; `Map.set` semantics (keep first position, overwrite
|
|
6
|
+
* value) mirror plain-object key assignment, so a consumer can replicate
|
|
7
|
+
* `{ ...twStyle, ...authorStyle }` author-wins merging exactly.
|
|
8
|
+
*/
|
|
9
|
+
export type InlineStyleMap = Map<string, string>;
|
|
10
|
+
export interface ComputedStyles {
|
|
11
|
+
/**
|
|
12
|
+
* Per-class inline styles. Only classes that produced at least one inlinable
|
|
13
|
+
* declaration appear here. Keyed by the ORIGINAL (unsanitized) class name.
|
|
14
|
+
*/
|
|
15
|
+
inlinable: Map<string, InlineStyleMap>;
|
|
16
|
+
/**
|
|
17
|
+
* The `<style>` body to inject into the first `<head>`: media-query and
|
|
18
|
+
* pseudo-class rules, unnested and downleveled for email clients, with class
|
|
19
|
+
* selectors sanitized. Empty string when there are no such rules.
|
|
20
|
+
*/
|
|
21
|
+
nonInlinableCss: string;
|
|
22
|
+
/**
|
|
23
|
+
* Classes that must REMAIN on the element, mapped ORIGINAL -> OUTPUT:
|
|
24
|
+
* - a non-inlinable class maps to its sanitized selector name
|
|
25
|
+
* (matching the selectors emitted in {@link nonInlinableCss});
|
|
26
|
+
* - an unknown class (no Tailwind rule at all) maps to itself.
|
|
27
|
+
* A class that only produced inlinable styles is absent — the consumer drops
|
|
28
|
+
* it from the `class` attribute. Mirrors React Email's residual-class logic in
|
|
29
|
+
* `clone-element-with-inlined-styles.ts`.
|
|
30
|
+
*/
|
|
31
|
+
residualClassMap: Map<string, string>;
|
|
32
|
+
/**
|
|
33
|
+
* Original (unsanitized) names of every class that produced a non-inlinable
|
|
34
|
+
* rule, in stylesheet-emission order — the exact list React Email interpolates
|
|
35
|
+
* into its no-`<head>` error (`Array.from(nonInlinableRules.keys())`,
|
|
36
|
+
* tailwind.tsx). This order (breakpoint/variant emission order) differs from the
|
|
37
|
+
* authored class order, so it cannot be reconstructed from {@link residualClassMap}
|
|
38
|
+
* and must be carried canonically.
|
|
39
|
+
*/
|
|
40
|
+
nonInlinableClassNames: string[];
|
|
41
|
+
}
|
|
42
|
+
export interface TailwindEngine {
|
|
43
|
+
/**
|
|
44
|
+
* Compute inline styles, injectable CSS, and residual classes for the given
|
|
45
|
+
* set of class names (collected across the whole `<Tailwind>` subtree).
|
|
46
|
+
*
|
|
47
|
+
* Should be called once per engine with the full class list, matching React
|
|
48
|
+
* Email's one-setup-per-`<Tailwind>` model. The underlying compiler only
|
|
49
|
+
* accumulates candidates, and each call re-parses a fresh stylesheet AST, so
|
|
50
|
+
* repeated calls never cross-contaminate — but a call only "sees" classes in
|
|
51
|
+
* its own argument.
|
|
52
|
+
*/
|
|
53
|
+
computeStyles: (classNames: string[]) => ComputedStyles;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Framework-neutral Tailwind engine. Mirrors the CSS pipeline of React Email's
|
|
57
|
+
* `<Tailwind>` component (`tailwind.tsx`) without any view-layer coupling: a
|
|
58
|
+
* consumer collects the class names used in a rendered email, calls
|
|
59
|
+
* {@link TailwindEngine.computeStyles}, then applies the result to the HTML.
|
|
60
|
+
*/
|
|
61
|
+
export declare function createTailwindEngine(options?: TailwindEngineOptions): Promise<TailwindEngine>;
|
|
62
|
+
export { pixelBasedPreset } from './pixel-based-preset.js';
|
|
63
|
+
export type { TailwindConfig, TailwindEngineOptions } from './types.js';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { generate, List } from "css-tree";
|
|
2
|
+
import { downlevelForEmailClients } from "./css/downlevel-for-email-clients.js";
|
|
3
|
+
import { extractRulesPerClass } from "./css/extract-rules-per-class.js";
|
|
4
|
+
import { getCustomProperties } from "./css/get-custom-properties.js";
|
|
5
|
+
import { makeInlineStylesFor } from "./css/make-inline-styles-for.js";
|
|
6
|
+
import { sanitizeClassName } from "./compatibility/sanitize-class-name.js";
|
|
7
|
+
import { sanitizeNonInlinableRules } from "./css/sanitize-non-inlinable-rules.js";
|
|
8
|
+
import { sanitizeStyleSheet } from "./sanitize-stylesheet.js";
|
|
9
|
+
import { setupTailwind } from "./setup-tailwind.js";
|
|
10
|
+
export async function createTailwindEngine(options = {}) {
|
|
11
|
+
const { config, theme, utility } = options;
|
|
12
|
+
const tailwindSetup = await setupTailwind({
|
|
13
|
+
config,
|
|
14
|
+
cssConfigs: { theme, utility }
|
|
15
|
+
});
|
|
16
|
+
const computeStyles = (classNames) => {
|
|
17
|
+
tailwindSetup.addUtilities(classNames);
|
|
18
|
+
const styleSheet = tailwindSetup.getStyleSheet();
|
|
19
|
+
sanitizeStyleSheet(styleSheet);
|
|
20
|
+
const { inlinable: inlinableRules, nonInlinable: nonInlinableRules } = extractRulesPerClass(styleSheet, classNames);
|
|
21
|
+
const customProperties = getCustomProperties(styleSheet);
|
|
22
|
+
const nonInlineStyles = {
|
|
23
|
+
type: "StyleSheet",
|
|
24
|
+
children: new List().fromArray(
|
|
25
|
+
Array.from(nonInlinableRules.values()).flat()
|
|
26
|
+
)
|
|
27
|
+
};
|
|
28
|
+
sanitizeNonInlinableRules(nonInlineStyles);
|
|
29
|
+
downlevelForEmailClients(nonInlineStyles);
|
|
30
|
+
const nonInlinableCss = nonInlinableRules.size > 0 ? generate(nonInlineStyles) : "";
|
|
31
|
+
const inlinable = /* @__PURE__ */ new Map();
|
|
32
|
+
for (const [className, rules] of inlinableRules) {
|
|
33
|
+
inlinable.set(className, makeInlineStylesFor(rules, customProperties));
|
|
34
|
+
}
|
|
35
|
+
const residualClassMap = /* @__PURE__ */ new Map();
|
|
36
|
+
for (const className of classNames) {
|
|
37
|
+
if (residualClassMap.has(className)) continue;
|
|
38
|
+
if (nonInlinableRules.has(className)) {
|
|
39
|
+
residualClassMap.set(className, sanitizeClassName(className));
|
|
40
|
+
} else if (!inlinableRules.has(className)) {
|
|
41
|
+
residualClassMap.set(className, className);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
inlinable,
|
|
46
|
+
nonInlinableCss,
|
|
47
|
+
residualClassMap,
|
|
48
|
+
nonInlinableClassNames: Array.from(nonInlinableRules.keys())
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
return { computeStyles };
|
|
52
|
+
}
|
|
53
|
+
export { pixelBasedPreset } from "./pixel-based-preset.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TailwindConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A Tailwind config preset that redefines the default `rem`-based `fontSize` and
|
|
4
|
+
* `spacing` scales in pixels, since many email clients handle `px` more
|
|
5
|
+
* predictably than `rem`.
|
|
6
|
+
*
|
|
7
|
+
* Direct port of React Email's `pixelBasedPreset`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const pixelBasedPreset: TailwindConfig;
|