react-email 6.6.0 → 6.6.2
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 +14 -0
- package/dist/cli/index.mjs +2 -2
- package/dist/index.cjs +20 -6
- package/dist/index.mjs +20 -6
- package/package.json +3 -3
- package/src/components/markdown/markdown.spec.tsx +1 -1
- package/src/components/markdown/utils/parse-css-in-js-to-inline-css.spec.ts +42 -0
- package/src/components/markdown/utils/parse-css-in-js-to-inline-css.ts +1 -1
- package/src/components/tailwind/utils/css/sanitize-declarations.spec.ts +33 -1
- package/src/components/tailwind/utils/css/sanitize-declarations.ts +19 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# react-email
|
|
2
2
|
|
|
3
|
+
## 6.6.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 437c414: Fix Tailwind opacity modifiers (e.g. `bg-blue-600/50`) rendering an invalid percentage alpha that breaks in some email clients.
|
|
8
|
+
|
|
9
|
+
## 6.6.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 21bac49: Fix `Markdown` corrupting double quotes in `markdownCustomStyles`. Inline style values containing a `"` (e.g. `fontFamily: '"Times New Roman", serif'`) were escaped to the apostrophe entity `'` instead of `"`, silently rewriting the quoted value. Double quotes are now escaped as `"`.
|
|
14
|
+
- Updated dependencies [60a5b09]
|
|
15
|
+
- @react-email/render@2.0.9
|
|
16
|
+
|
|
3
17
|
## 6.6.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/dist/cli/index.mjs
CHANGED
|
@@ -6523,7 +6523,7 @@ const getEmailsDirectoryMetadata = async (absolutePathToEmailsDirectory, keepFil
|
|
|
6523
6523
|
//#region package.json
|
|
6524
6524
|
var package_default = {
|
|
6525
6525
|
name: "react-email",
|
|
6526
|
-
version: "6.6.
|
|
6526
|
+
version: "6.6.2",
|
|
6527
6527
|
description: "A live preview of your emails right in your browser.",
|
|
6528
6528
|
bin: { "email": "./dist/cli/index.mjs" },
|
|
6529
6529
|
type: "module",
|
|
@@ -6562,7 +6562,7 @@ var package_default = {
|
|
|
6562
6562
|
dependencies: {
|
|
6563
6563
|
"@babel/parser": "catalog:",
|
|
6564
6564
|
"@babel/traverse": "catalog:",
|
|
6565
|
-
"@react-email/render": "workspace:>=2.0.
|
|
6565
|
+
"@react-email/render": "workspace:>=2.0.9",
|
|
6566
6566
|
"chokidar": "^4.0.3",
|
|
6567
6567
|
"commander": "catalog:",
|
|
6568
6568
|
"conf": "^15.0.2",
|
package/dist/index.cjs
CHANGED
|
@@ -17730,7 +17730,7 @@ function camelToKebabCase(str) {
|
|
|
17730
17730
|
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
17731
17731
|
}
|
|
17732
17732
|
function escapeQuotes(value) {
|
|
17733
|
-
if (typeof value === "string" && value.includes("\"")) return value.replace(/"/g, "
|
|
17733
|
+
if (typeof value === "string" && value.includes("\"")) return value.replace(/"/g, """);
|
|
17734
17734
|
return value;
|
|
17735
17735
|
}
|
|
17736
17736
|
function parseCssInJsToInlineCss(cssProperties) {
|
|
@@ -38321,11 +38321,25 @@ function sanitizeDeclarations(nodeContainingDeclarations) {
|
|
|
38321
38321
|
const color = children[3];
|
|
38322
38322
|
const opacity = children[4];
|
|
38323
38323
|
if (func.children.last?.type === "Identifier" && func.children.last.name === "transparent" && color?.type === "Function" && color?.name === "rgb" && opacity) {
|
|
38324
|
-
|
|
38325
|
-
|
|
38326
|
-
|
|
38327
|
-
|
|
38328
|
-
|
|
38324
|
+
if (opacity.type === "Percentage") {
|
|
38325
|
+
const alpha = Number.parseFloat(opacity.value) / 100;
|
|
38326
|
+
if (alpha < 1) {
|
|
38327
|
+
color.children.appendData({
|
|
38328
|
+
type: "Operator",
|
|
38329
|
+
value: ","
|
|
38330
|
+
});
|
|
38331
|
+
color.children.appendData({
|
|
38332
|
+
type: "Number",
|
|
38333
|
+
value: alpha.toString()
|
|
38334
|
+
});
|
|
38335
|
+
}
|
|
38336
|
+
} else {
|
|
38337
|
+
color.children.appendData({
|
|
38338
|
+
type: "Operator",
|
|
38339
|
+
value: ","
|
|
38340
|
+
});
|
|
38341
|
+
color.children.appendData(opacity);
|
|
38342
|
+
}
|
|
38329
38343
|
parentListItem.data = color;
|
|
38330
38344
|
}
|
|
38331
38345
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -17709,7 +17709,7 @@ function camelToKebabCase(str) {
|
|
|
17709
17709
|
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
17710
17710
|
}
|
|
17711
17711
|
function escapeQuotes(value) {
|
|
17712
|
-
if (typeof value === "string" && value.includes("\"")) return value.replace(/"/g, "
|
|
17712
|
+
if (typeof value === "string" && value.includes("\"")) return value.replace(/"/g, """);
|
|
17713
17713
|
return value;
|
|
17714
17714
|
}
|
|
17715
17715
|
function parseCssInJsToInlineCss(cssProperties) {
|
|
@@ -38300,11 +38300,25 @@ function sanitizeDeclarations(nodeContainingDeclarations) {
|
|
|
38300
38300
|
const color = children[3];
|
|
38301
38301
|
const opacity = children[4];
|
|
38302
38302
|
if (func.children.last?.type === "Identifier" && func.children.last.name === "transparent" && color?.type === "Function" && color?.name === "rgb" && opacity) {
|
|
38303
|
-
|
|
38304
|
-
|
|
38305
|
-
|
|
38306
|
-
|
|
38307
|
-
|
|
38303
|
+
if (opacity.type === "Percentage") {
|
|
38304
|
+
const alpha = Number.parseFloat(opacity.value) / 100;
|
|
38305
|
+
if (alpha < 1) {
|
|
38306
|
+
color.children.appendData({
|
|
38307
|
+
type: "Operator",
|
|
38308
|
+
value: ","
|
|
38309
|
+
});
|
|
38310
|
+
color.children.appendData({
|
|
38311
|
+
type: "Number",
|
|
38312
|
+
value: alpha.toString()
|
|
38313
|
+
});
|
|
38314
|
+
}
|
|
38315
|
+
} else {
|
|
38316
|
+
color.children.appendData({
|
|
38317
|
+
type: "Operator",
|
|
38318
|
+
value: ","
|
|
38319
|
+
});
|
|
38320
|
+
color.children.appendData(opacity);
|
|
38321
|
+
}
|
|
38308
38322
|
parentListItem.data = color;
|
|
38309
38323
|
}
|
|
38310
38324
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-email",
|
|
3
|
-
"version": "6.6.
|
|
3
|
+
"version": "6.6.2",
|
|
4
4
|
"description": "A live preview of your emails right in your browser.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"email": "./dist/cli/index.mjs"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@babel/parser": "7.27.0",
|
|
39
39
|
"@babel/traverse": "7.27.0",
|
|
40
|
-
"@react-email/render": ">=2.0.
|
|
40
|
+
"@react-email/render": ">=2.0.9",
|
|
41
41
|
"chokidar": "^4.0.3",
|
|
42
42
|
"commander": "^13.0.0",
|
|
43
43
|
"conf": "^15.0.2",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"tsx": "4.21.0",
|
|
75
75
|
"typescript": "5.9.3",
|
|
76
76
|
"yalc": "1.0.0-pre.53",
|
|
77
|
-
"@react-email/render": "2.0.
|
|
77
|
+
"@react-email/render": "2.0.9"
|
|
78
78
|
},
|
|
79
79
|
"scripts": {
|
|
80
80
|
"build": "tsdown",
|
|
@@ -116,7 +116,7 @@ console.log(\`Hello, $\{name}!\`);
|
|
|
116
116
|
</Markdown>,
|
|
117
117
|
);
|
|
118
118
|
expect(actualOutput).toMatchInlineSnapshot(`
|
|
119
|
-
"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><div data-id="react-email-markdown"><p><strong style="font:700 23px / 32px
|
|
119
|
+
"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><div data-id="react-email-markdown"><p><strong style="font:700 23px / 32px "Roobert PRO", system-ui, sans-serif;background:url("path/to/image")">This is sample bold text in markdown</strong> and <em style="font-style:italic">this is italic text</em></p>
|
|
120
120
|
</div><!--/$-->"
|
|
121
121
|
`);
|
|
122
122
|
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { parseCssInJsToInlineCss } from './parse-css-in-js-to-inline-css.js';
|
|
2
|
+
|
|
3
|
+
describe('parseCssInJsToInlineCss', () => {
|
|
4
|
+
it('returns an empty string for undefined styles', () => {
|
|
5
|
+
expect(parseCssInJsToInlineCss(undefined)).toBe('');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('converts camelCase properties to kebab-case', () => {
|
|
9
|
+
expect(parseCssInJsToInlineCss({ fontFamily: 'serif' })).toBe(
|
|
10
|
+
'font-family:serif',
|
|
11
|
+
);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('appends px to numeric values of numerical properties', () => {
|
|
15
|
+
expect(parseCssInJsToInlineCss({ fontSize: 16 })).toBe('font-size:16px');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('joins multiple declarations with a semicolon', () => {
|
|
19
|
+
expect(parseCssInJsToInlineCss({ color: 'red', fontSize: 16 })).toBe(
|
|
20
|
+
'color:red;font-size:16px',
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('escapes double quotes so they do not break the style attribute', () => {
|
|
25
|
+
const result = parseCssInJsToInlineCss({
|
|
26
|
+
fontFamily: '"Times New Roman", serif',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(result).toBe('font-family:"Times New Roman", serif');
|
|
30
|
+
expect(result).not.toContain('"');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('does not corrupt double-quoted values into apostrophes', () => {
|
|
34
|
+
const result = parseCssInJsToInlineCss({
|
|
35
|
+
fontFamily: '"Times New Roman", serif',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// `'` is the apostrophe entity and would silently change the
|
|
39
|
+
// quoted font name; a double quote must be escaped as `"`.
|
|
40
|
+
expect(result).not.toContain(''');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -348,7 +348,39 @@ describe('sanitizeDeclarations', () => {
|
|
|
348
348
|
sanitizeDeclarations(stylesheet);
|
|
349
349
|
const result = generate(stylesheet);
|
|
350
350
|
expect(result).toMatchInlineSnapshot(
|
|
351
|
-
`".bg-blue-600/50{background-color:rgb(21,93,252,
|
|
351
|
+
`".bg-blue-600/50{background-color:rgb(21,93,252,0.6)}"`,
|
|
352
|
+
);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('converts the color-mix opacity percentage into a decimal alpha', () => {
|
|
356
|
+
let stylesheet = parse(`
|
|
357
|
+
.bg-blue-600\\/50 {
|
|
358
|
+
background-color: color-mix(in oklab, oklch(54.6% 0.245 262.881) 50%, transparent);
|
|
359
|
+
}
|
|
360
|
+
`);
|
|
361
|
+
sanitizeDeclarations(stylesheet);
|
|
362
|
+
expect(generate(stylesheet), 'half opacity').toMatchInlineSnapshot(
|
|
363
|
+
`".bg-blue-600\\/50{background-color:rgb(21,93,252,0.5)}"`,
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
stylesheet = parse(`
|
|
367
|
+
.bg-blue-600\\/12\\.5 {
|
|
368
|
+
background-color: color-mix(in oklab, oklch(54.6% 0.245 262.881) 12.5%, transparent);
|
|
369
|
+
}
|
|
370
|
+
`);
|
|
371
|
+
sanitizeDeclarations(stylesheet);
|
|
372
|
+
expect(generate(stylesheet), 'fractional opacity').toMatchInlineSnapshot(
|
|
373
|
+
`".bg-blue-600\\/12\\.5{background-color:rgb(21,93,252,0.125)}"`,
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
stylesheet = parse(`
|
|
377
|
+
.bg-blue-600\\/100 {
|
|
378
|
+
background-color: color-mix(in oklab, oklch(54.6% 0.245 262.881) 100%, transparent);
|
|
379
|
+
}
|
|
380
|
+
`);
|
|
381
|
+
sanitizeDeclarations(stylesheet);
|
|
382
|
+
expect(generate(stylesheet), 'full opacity').toMatchInlineSnapshot(
|
|
383
|
+
`".bg-blue-600\\/100{background-color:rgb(21,93,252)}"`,
|
|
352
384
|
);
|
|
353
385
|
});
|
|
354
386
|
|
|
@@ -384,11 +384,25 @@ export function sanitizeDeclarations(nodeContainingDeclarations: CssNode) {
|
|
|
384
384
|
color?.name === 'rgb' &&
|
|
385
385
|
opacity
|
|
386
386
|
) {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
387
|
+
if (opacity.type === 'Percentage') {
|
|
388
|
+
const alpha = Number.parseFloat(opacity.value) / 100;
|
|
389
|
+
if (alpha < 1) {
|
|
390
|
+
color.children.appendData({
|
|
391
|
+
type: 'Operator',
|
|
392
|
+
value: ',',
|
|
393
|
+
});
|
|
394
|
+
color.children.appendData({
|
|
395
|
+
type: 'Number',
|
|
396
|
+
value: alpha.toString(),
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
color.children.appendData({
|
|
401
|
+
type: 'Operator',
|
|
402
|
+
value: ',',
|
|
403
|
+
});
|
|
404
|
+
color.children.appendData(opacity);
|
|
405
|
+
}
|
|
392
406
|
parentListItem.data = color;
|
|
393
407
|
}
|
|
394
408
|
}
|