react-email 6.0.4 → 6.0.6
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 +8 -0
- package/dist/cli/index.mjs +1 -1
- package/dist/index.cjs +29 -1
- package/dist/index.mjs +29 -1
- package/package.json +1 -1
- package/src/components/tailwind/utils/css/make-inline-styles-for.spec.ts +75 -2
- package/src/components/tailwind/utils/css/make-inline-styles-for.ts +64 -1
package/CHANGELOG.md
CHANGED
package/dist/cli/index.mjs
CHANGED
|
@@ -6522,7 +6522,7 @@ const getEmailsDirectoryMetadata = async (absolutePathToEmailsDirectory, keepFil
|
|
|
6522
6522
|
//#region package.json
|
|
6523
6523
|
var package_default = {
|
|
6524
6524
|
name: "react-email",
|
|
6525
|
-
version: "6.0.
|
|
6525
|
+
version: "6.0.6",
|
|
6526
6526
|
description: "A live preview of your emails right in your browser.",
|
|
6527
6527
|
bin: { "email": "./dist/cli/index.mjs" },
|
|
6528
6528
|
type: "module",
|
package/dist/index.cjs
CHANGED
|
@@ -37790,7 +37790,35 @@ function makeInlineStylesFor(inlinableRules, customProperties) {
|
|
|
37790
37790
|
visit: "Declaration",
|
|
37791
37791
|
enter(declaration) {
|
|
37792
37792
|
if (declaration.property.startsWith("--")) return;
|
|
37793
|
-
|
|
37793
|
+
walk(declaration.value, {
|
|
37794
|
+
visit: "Function",
|
|
37795
|
+
leave(func, funcItem, funcList) {
|
|
37796
|
+
if (func.name !== "var") return;
|
|
37797
|
+
let variableName;
|
|
37798
|
+
walk(func, {
|
|
37799
|
+
visit: "Identifier",
|
|
37800
|
+
enter(identifier) {
|
|
37801
|
+
variableName = identifier.name;
|
|
37802
|
+
return this.break;
|
|
37803
|
+
}
|
|
37804
|
+
});
|
|
37805
|
+
if (!variableName?.startsWith("--tw-")) return;
|
|
37806
|
+
let sawComma = false;
|
|
37807
|
+
let hasFallbackContent = false;
|
|
37808
|
+
func.children.forEach((child) => {
|
|
37809
|
+
if (!sawComma) {
|
|
37810
|
+
if (child.type === "Operator" && child.value === ",") sawComma = true;
|
|
37811
|
+
return;
|
|
37812
|
+
}
|
|
37813
|
+
let childValue = generate(child).trim();
|
|
37814
|
+
if (child.type === "Raw") childValue = childValue.replaceAll(/var\(--tw-[^,()]+,\s*\)/g, "").trim();
|
|
37815
|
+
if (childValue.length > 0) hasFallbackContent = true;
|
|
37816
|
+
});
|
|
37817
|
+
if (!sawComma || hasFallbackContent) return;
|
|
37818
|
+
funcList.remove(funcItem);
|
|
37819
|
+
}
|
|
37820
|
+
});
|
|
37821
|
+
styles[getReactProperty(declaration.property)] = generate(declaration.value).trim() + (declaration.important ? "!important" : "");
|
|
37794
37822
|
}
|
|
37795
37823
|
});
|
|
37796
37824
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -37769,7 +37769,35 @@ function makeInlineStylesFor(inlinableRules, customProperties) {
|
|
|
37769
37769
|
visit: "Declaration",
|
|
37770
37770
|
enter(declaration) {
|
|
37771
37771
|
if (declaration.property.startsWith("--")) return;
|
|
37772
|
-
|
|
37772
|
+
walk(declaration.value, {
|
|
37773
|
+
visit: "Function",
|
|
37774
|
+
leave(func, funcItem, funcList) {
|
|
37775
|
+
if (func.name !== "var") return;
|
|
37776
|
+
let variableName;
|
|
37777
|
+
walk(func, {
|
|
37778
|
+
visit: "Identifier",
|
|
37779
|
+
enter(identifier) {
|
|
37780
|
+
variableName = identifier.name;
|
|
37781
|
+
return this.break;
|
|
37782
|
+
}
|
|
37783
|
+
});
|
|
37784
|
+
if (!variableName?.startsWith("--tw-")) return;
|
|
37785
|
+
let sawComma = false;
|
|
37786
|
+
let hasFallbackContent = false;
|
|
37787
|
+
func.children.forEach((child) => {
|
|
37788
|
+
if (!sawComma) {
|
|
37789
|
+
if (child.type === "Operator" && child.value === ",") sawComma = true;
|
|
37790
|
+
return;
|
|
37791
|
+
}
|
|
37792
|
+
let childValue = generate(child).trim();
|
|
37793
|
+
if (child.type === "Raw") childValue = childValue.replaceAll(/var\(--tw-[^,()]+,\s*\)/g, "").trim();
|
|
37794
|
+
if (childValue.length > 0) hasFallbackContent = true;
|
|
37795
|
+
});
|
|
37796
|
+
if (!sawComma || hasFallbackContent) return;
|
|
37797
|
+
funcList.remove(funcItem);
|
|
37798
|
+
}
|
|
37799
|
+
});
|
|
37800
|
+
styles[getReactProperty(declaration.property)] = generate(declaration.value).trim() + (declaration.important ? "!important" : "");
|
|
37773
37801
|
}
|
|
37774
37802
|
});
|
|
37775
37803
|
}
|
package/package.json
CHANGED
|
@@ -41,11 +41,84 @@ describe('makeInlineStylesFor()', async () => {
|
|
|
41
41
|
),
|
|
42
42
|
).toMatchInlineSnapshot(`
|
|
43
43
|
{
|
|
44
|
-
"backgroundColor": "
|
|
44
|
+
"backgroundColor": "#3490dc",
|
|
45
45
|
"borderRadius": "0.25rem",
|
|
46
|
-
"color": "
|
|
46
|
+
"color": "#fff",
|
|
47
47
|
"padding": "0.5rem 1rem",
|
|
48
48
|
}
|
|
49
49
|
`);
|
|
50
50
|
});
|
|
51
|
+
|
|
52
|
+
it('strips Tailwind v4 variant-stacking var() refs with empty fallbacks', () => {
|
|
53
|
+
// Tailwind v4 compiles `tabular-nums` to a font-variant-numeric value
|
|
54
|
+
// where every optional variant slot is represented by an unresolved
|
|
55
|
+
// var(--tw-..., ) with an empty fallback. Email clients do not support
|
|
56
|
+
// CSS custom properties reliably, so these must collapse at inline time
|
|
57
|
+
// (per CSS spec, an empty fallback resolves to empty string).
|
|
58
|
+
const tailwindStyles = parse(`
|
|
59
|
+
.tabular-nums {
|
|
60
|
+
font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) tabular-nums var(--tw-numeric-fraction,);
|
|
61
|
+
}
|
|
62
|
+
`) as StyleSheet;
|
|
63
|
+
|
|
64
|
+
expect(
|
|
65
|
+
makeInlineStylesFor(
|
|
66
|
+
tailwindStyles.children.toArray(),
|
|
67
|
+
getCustomProperties(tailwindStyles),
|
|
68
|
+
),
|
|
69
|
+
).toMatchInlineSnapshot(`
|
|
70
|
+
{
|
|
71
|
+
"fontVariantNumeric": "tabular-nums",
|
|
72
|
+
}
|
|
73
|
+
`);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('preserves user-authored empty-fallback var() refs (non --tw- prefix)', () => {
|
|
77
|
+
// The collapse is scoped to Tailwind's --tw-* variant-stacking idiom.
|
|
78
|
+
// A user-authored var(--my-color,) with an empty fallback must pass
|
|
79
|
+
// through unchanged even though it syntactically matches the idiom --
|
|
80
|
+
// the user opted into that semantic and the render target may define
|
|
81
|
+
// --my-color at a higher scope.
|
|
82
|
+
const userStyles = parse(`
|
|
83
|
+
.thing {
|
|
84
|
+
color: var(--my-color,);
|
|
85
|
+
background: var(--brand,) var(--tw-custom,);
|
|
86
|
+
}
|
|
87
|
+
`) as StyleSheet;
|
|
88
|
+
|
|
89
|
+
expect(
|
|
90
|
+
makeInlineStylesFor(
|
|
91
|
+
userStyles.children.toArray(),
|
|
92
|
+
getCustomProperties(userStyles),
|
|
93
|
+
),
|
|
94
|
+
).toMatchInlineSnapshot(`
|
|
95
|
+
{
|
|
96
|
+
"background": "var(--brand,)",
|
|
97
|
+
"color": "var(--my-color,)",
|
|
98
|
+
}
|
|
99
|
+
`);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('collapses outer --tw-* var() that becomes empty after inner --tw-* var() collapses', () => {
|
|
103
|
+
// Regression test for cubic-dev-ai P2 review on PR #3359:
|
|
104
|
+
// pre-order traversal misses outer var(--tw-X, var(--tw-Y,)) because the
|
|
105
|
+
// outer's fallback only becomes empty AFTER the inner collapses. Post-order
|
|
106
|
+
// traversal fixes this.
|
|
107
|
+
const tailwindStyles = parse(`
|
|
108
|
+
.nested {
|
|
109
|
+
font-variant-numeric: var(--tw-outer, var(--tw-inner,)) tabular-nums;
|
|
110
|
+
}
|
|
111
|
+
`) as StyleSheet;
|
|
112
|
+
|
|
113
|
+
expect(
|
|
114
|
+
makeInlineStylesFor(
|
|
115
|
+
tailwindStyles.children.toArray(),
|
|
116
|
+
getCustomProperties(tailwindStyles),
|
|
117
|
+
),
|
|
118
|
+
).toMatchInlineSnapshot(`
|
|
119
|
+
{
|
|
120
|
+
"fontVariantNumeric": "tabular-nums",
|
|
121
|
+
}
|
|
122
|
+
`);
|
|
123
|
+
});
|
|
51
124
|
});
|
|
@@ -59,8 +59,71 @@ export function makeInlineStylesFor(
|
|
|
59
59
|
if (declaration.property.startsWith('--')) {
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
|
+
// Tailwind v4 emits variant-stacking idioms like
|
|
63
|
+
// font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) tabular-nums var(--tw-numeric-fraction,)
|
|
64
|
+
// where each var() has an empty fallback so missing variants collapse to nothing.
|
|
65
|
+
// The walker above replaces var() calls with an initialValue when one is defined,
|
|
66
|
+
// but Tailwind deliberately leaves these variant vars undefined until used, so they
|
|
67
|
+
// stay in the output here and produce unresolvable custom properties in email HTML
|
|
68
|
+
// (no email client supports CSS custom properties reliably). Per the CSS spec
|
|
69
|
+
// (https://www.w3.org/TR/css-variables-1/#using-variables) an empty fallback means
|
|
70
|
+
// "use empty string if the variable is undefined", which is exactly what we want at
|
|
71
|
+
// inline-style time.
|
|
72
|
+
//
|
|
73
|
+
// Scoped to the `--tw-` prefix so any user-authored empty-fallback var() refs
|
|
74
|
+
// (even ones used inside tailwind utilities) are left untouched.
|
|
75
|
+
walk(declaration.value, {
|
|
76
|
+
visit: 'Function',
|
|
77
|
+
leave(func, funcItem, funcList) {
|
|
78
|
+
if (func.name !== 'var') {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let variableName: string | undefined;
|
|
83
|
+
walk(func, {
|
|
84
|
+
visit: 'Identifier',
|
|
85
|
+
enter(identifier) {
|
|
86
|
+
variableName = identifier.name;
|
|
87
|
+
return this.break;
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
if (!variableName?.startsWith('--tw-')) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let sawComma = false;
|
|
95
|
+
let hasFallbackContent = false;
|
|
96
|
+
func.children.forEach((child) => {
|
|
97
|
+
if (!sawComma) {
|
|
98
|
+
if (child.type === 'Operator' && child.value === ',') {
|
|
99
|
+
sawComma = true;
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let childValue = generate(child).trim();
|
|
105
|
+
if (child.type === 'Raw') {
|
|
106
|
+
const emptyTailwindVarPattern = /var\(--tw-[^,()]+,\s*\)/g;
|
|
107
|
+
childValue = childValue
|
|
108
|
+
.replaceAll(emptyTailwindVarPattern, '')
|
|
109
|
+
.trim();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (childValue.length > 0) {
|
|
113
|
+
hasFallbackContent = true;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (!sawComma || hasFallbackContent) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
funcList.remove(funcItem);
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
62
125
|
styles[getReactProperty(declaration.property)] =
|
|
63
|
-
generate(declaration.value) +
|
|
126
|
+
generate(declaration.value).trim() +
|
|
64
127
|
(declaration.important ? '!important' : '');
|
|
65
128
|
},
|
|
66
129
|
});
|