@depup/react-email 6.9.3-depup.0 → 6.11.0-depup.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +8 -9
  3. package/changes.json +7 -11
  4. package/dist/cli/index.mjs +74 -33
  5. package/dist/components/container/container.cjs +18 -3
  6. package/dist/components/container/container.d.cts +3 -1
  7. package/dist/components/container/container.d.mts +3 -1
  8. package/dist/components/container/container.mjs +18 -3
  9. package/dist/components/element-marker.cjs +8 -2
  10. package/dist/components/element-marker.d.cts +14 -0
  11. package/dist/components/element-marker.d.mts +14 -0
  12. package/dist/components/element-marker.mjs +8 -3
  13. package/dist/components/index.d.cts +1 -0
  14. package/dist/components/index.d.mts +1 -0
  15. package/dist/components/section/section.cjs +18 -3
  16. package/dist/components/section/section.d.cts +3 -1
  17. package/dist/components/section/section.d.mts +3 -1
  18. package/dist/components/section/section.mjs +18 -3
  19. package/dist/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.cjs +34 -21
  20. package/dist/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.mjs +34 -21
  21. package/dist/index.cjs +2 -0
  22. package/dist/index.d.cts +2 -1
  23. package/dist/index.d.mts +2 -1
  24. package/dist/index.mjs +2 -1
  25. package/package.json +16 -20
  26. package/readme.md +8 -0
  27. package/src/components/container/container.tsx +66 -46
  28. package/src/components/element-marker.ts +25 -2
  29. package/src/components/index.ts +5 -0
  30. package/src/components/section/section.tsx +66 -46
  31. package/src/components/tailwind/tailwind.spec.tsx +13 -0
  32. package/src/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.ts +46 -37
@@ -2,6 +2,8 @@ import * as React$1 from "react";
2
2
 
3
3
  //#region src/components/section/section.d.ts
4
4
  type SectionProps = Readonly<React$1.ComponentPropsWithoutRef<'table'>>;
5
- declare const Section: React$1.ForwardRefExoticComponent<Readonly<Omit<React$1.DetailedHTMLProps<React$1.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "ref">> & React$1.RefAttributes<HTMLTableElement>>;
5
+ declare const Section: React$1.ForwardRefExoticComponent<Readonly<Omit<React$1.DetailedHTMLProps<React$1.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "ref">> & {
6
+ tdClassName?: string;
7
+ } & React$1.RefAttributes<HTMLTableElement>>;
6
8
  //#endregion
7
9
  export { Section, SectionProps };
@@ -2,14 +2,14 @@ import { markAsElement } from "../element-marker.mjs";
2
2
  import * as React$1 from "react";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  //#region src/components/section/section.tsx
5
- const Section = React$1.forwardRef(({ children, style = {}, ...props }, ref) => {
5
+ const Section = React$1.forwardRef(({ children, style = {}, tdClassName, ...props }, ref) => {
6
6
  const tdStyle = {};
7
7
  const tableStyle = {};
8
8
  const styleRecord = style;
9
9
  for (const key in styleRecord) {
10
10
  if (!Object.hasOwn(styleRecord, key)) continue;
11
11
  const value = styleRecord[key];
12
- if (key === "padding" || key === "paddingTop" || key === "paddingRight" || key === "paddingBottom" || key === "paddingLeft") tdStyle[key] = value;
12
+ if (key.startsWith("padding")) tdStyle[key] = value;
13
13
  else tableStyle[key] = value;
14
14
  }
15
15
  return /* @__PURE__ */ jsx("table", {
@@ -23,12 +23,27 @@ const Section = React$1.forwardRef(({ children, style = {}, ...props }, ref) =>
23
23
  ref,
24
24
  style: tableStyle,
25
25
  children: /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", {
26
+ className: tdClassName,
26
27
  style: tdStyle,
27
28
  children
28
29
  }) }) })
29
30
  });
30
31
  });
31
32
  Section.displayName = "Section";
32
- markAsElement(Section);
33
+ markAsElement(Section, { resolveTailwind(props, { style, className, classProperties }) {
34
+ const tableClasses = [];
35
+ const tdClasses = props.tdClassName ? [props.tdClassName] : [];
36
+ for (const name of className?.split(" ") ?? []) {
37
+ const properties = classProperties[name];
38
+ if (properties && properties.length > 0 && properties.every((property) => property.startsWith("padding"))) tdClasses.push(name);
39
+ else tableClasses.push(name);
40
+ }
41
+ return {
42
+ ...props,
43
+ style,
44
+ className: tableClasses.length > 0 ? tableClasses.join(" ") : void 0,
45
+ tdClassName: tdClasses.length > 0 ? tdClasses.join(" ") : void 0
46
+ };
47
+ } });
33
48
  //#endregion
34
49
  export { Section };
@@ -1,35 +1,48 @@
1
1
  const require_runtime = require("../../../../_virtual/_rolldown/runtime.cjs");
2
+ const require_element_marker = require("../../../element-marker.cjs");
2
3
  const require_make_inline_styles_for = require("../css/make-inline-styles-for.cjs");
3
4
  const require_sanitize_class_name = require("../compatibility/sanitize-class-name.cjs");
4
5
  const require_is_component = require("../react/is-component.cjs");
5
6
  let react = require("react");
6
7
  react = require_runtime.__toESM(react, 1);
8
+ let css_tree = require("css-tree");
7
9
  //#region src/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.ts
8
10
  function cloneElementWithInlinedStyles(element, inlinableRules, nonInlinableRules, customProperties) {
9
- const propsToOverwrite = {};
10
- if (element.props.className && !require_is_component.isComponent(element)) {
11
- const classes = element.props.className.trim().split(/\s+/);
12
- const residualClasses = [];
13
- const rules = [];
14
- for (const className of classes) {
15
- const classRules = inlinableRules.get(className);
16
- if (classRules) rules.push(...classRules);
17
- if (nonInlinableRules.has(className)) residualClasses.push(className);
18
- else if (!classRules) residualClasses.push(className);
19
- }
20
- propsToOverwrite.style = {
11
+ if (!element.props.className || require_is_component.isComponent(element)) return react.default.cloneElement(element, element.props, element.props.children);
12
+ const classes = element.props.className.trim().split(/\s+/);
13
+ const residualClasses = [];
14
+ const classProperties = Object.create(null);
15
+ const rules = [];
16
+ for (const className of classes) {
17
+ const classRules = inlinableRules.get(className);
18
+ if (classRules) rules.push(...classRules);
19
+ const nonInlinable = nonInlinableRules.get(className);
20
+ if (nonInlinable) {
21
+ const sanitized = require_sanitize_class_name.sanitizeClassName(className);
22
+ residualClasses.push(sanitized);
23
+ const properties = [];
24
+ for (const rule of nonInlinable) (0, css_tree.walk)(rule, {
25
+ visit: "Declaration",
26
+ enter(declaration) {
27
+ properties.push(declaration.property);
28
+ }
29
+ });
30
+ classProperties[sanitized] = properties;
31
+ } else if (!classRules) residualClasses.push(className);
32
+ }
33
+ const resolved = {
34
+ style: {
21
35
  ...require_make_inline_styles_for.makeInlineStylesFor(rules, customProperties),
22
36
  ...element.props.style
23
- };
24
- if (residualClasses.length > 0) propsToOverwrite.className = residualClasses.map((className) => {
25
- if (nonInlinableRules.has(className)) return require_sanitize_class_name.sanitizeClassName(className);
26
- return className;
27
- }).join(" ");
28
- else propsToOverwrite.className = void 0;
29
- }
30
- const newProps = {
37
+ },
38
+ className: residualClasses.length > 0 ? residualClasses.join(" ") : void 0,
39
+ classProperties
40
+ };
41
+ const resolveTailwind = require_element_marker.getElementOptions(element.type)?.resolveTailwind;
42
+ const newProps = resolveTailwind ? resolveTailwind(element.props, resolved) : {
31
43
  ...element.props,
32
- ...propsToOverwrite
44
+ style: resolved.style,
45
+ className: resolved.className
33
46
  };
34
47
  return react.default.cloneElement(element, newProps, newProps.children);
35
48
  }
@@ -1,33 +1,46 @@
1
+ import { getElementOptions } from "../../../element-marker.mjs";
1
2
  import { makeInlineStylesFor } from "../css/make-inline-styles-for.mjs";
2
3
  import { sanitizeClassName } from "../compatibility/sanitize-class-name.mjs";
3
4
  import { isComponent } from "../react/is-component.mjs";
4
5
  import React from "react";
6
+ import { walk } from "css-tree/dist/csstree.esm";
5
7
  //#region src/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.ts
6
8
  function cloneElementWithInlinedStyles(element, inlinableRules, nonInlinableRules, customProperties) {
7
- const propsToOverwrite = {};
8
- if (element.props.className && !isComponent(element)) {
9
- const classes = element.props.className.trim().split(/\s+/);
10
- const residualClasses = [];
11
- const rules = [];
12
- for (const className of classes) {
13
- const classRules = inlinableRules.get(className);
14
- if (classRules) rules.push(...classRules);
15
- if (nonInlinableRules.has(className)) residualClasses.push(className);
16
- else if (!classRules) residualClasses.push(className);
17
- }
18
- propsToOverwrite.style = {
9
+ if (!element.props.className || isComponent(element)) return React.cloneElement(element, element.props, element.props.children);
10
+ const classes = element.props.className.trim().split(/\s+/);
11
+ const residualClasses = [];
12
+ const classProperties = Object.create(null);
13
+ const rules = [];
14
+ for (const className of classes) {
15
+ const classRules = inlinableRules.get(className);
16
+ if (classRules) rules.push(...classRules);
17
+ const nonInlinable = nonInlinableRules.get(className);
18
+ if (nonInlinable) {
19
+ const sanitized = sanitizeClassName(className);
20
+ residualClasses.push(sanitized);
21
+ const properties = [];
22
+ for (const rule of nonInlinable) walk(rule, {
23
+ visit: "Declaration",
24
+ enter(declaration) {
25
+ properties.push(declaration.property);
26
+ }
27
+ });
28
+ classProperties[sanitized] = properties;
29
+ } else if (!classRules) residualClasses.push(className);
30
+ }
31
+ const resolved = {
32
+ style: {
19
33
  ...makeInlineStylesFor(rules, customProperties),
20
34
  ...element.props.style
21
- };
22
- if (residualClasses.length > 0) propsToOverwrite.className = residualClasses.map((className) => {
23
- if (nonInlinableRules.has(className)) return sanitizeClassName(className);
24
- return className;
25
- }).join(" ");
26
- else propsToOverwrite.className = void 0;
27
- }
28
- const newProps = {
35
+ },
36
+ className: residualClasses.length > 0 ? residualClasses.join(" ") : void 0,
37
+ classProperties
38
+ };
39
+ const resolveTailwind = getElementOptions(element.type)?.resolveTailwind;
40
+ const newProps = resolveTailwind ? resolveTailwind(element.props, resolved) : {
29
41
  ...element.props,
30
- ...propsToOverwrite
42
+ style: resolved.style,
43
+ className: resolved.className
31
44
  };
32
45
  return React.cloneElement(element, newProps, newProps.children);
33
46
  }
package/dist/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_element_marker = require("./components/element-marker.cjs");
2
3
  const require_body = require("./components/body/body.cjs");
3
4
  const require_button = require("./components/button/button.cjs");
4
5
  const require_code_block = require("./components/code-block/code-block.cjs");
@@ -64,6 +65,7 @@ exports.hopscotch = require_themes.hopscotch;
64
65
  exports.inlineStyles = require_inline_styles.inlineStyles;
65
66
  exports.laserwave = require_themes.laserwave;
66
67
  exports.lucario = require_themes.lucario;
68
+ exports.markAsElement = require_element_marker.markAsElement;
67
69
  exports.materialDark = require_themes.materialDark;
68
70
  exports.materialLight = require_themes.materialLight;
69
71
  exports.materialOceanic = require_themes.materialOceanic;
package/dist/index.d.cts CHANGED
@@ -6,6 +6,7 @@ import { CodeBlock, CodeBlockProps } from "./components/code-block/code-block.cj
6
6
  import { CodeInline, CodeInlineProps } from "./components/code-inline/code-inline.cjs";
7
7
  import { Column, ColumnProps } from "./components/column/column.cjs";
8
8
  import { Container, ContainerProps } from "./components/container/container.cjs";
9
+ import { ElementOptions, ResolvedTailwind, markAsElement } from "./components/element-marker.cjs";
9
10
  import { Font, FontProps } from "./components/font/font.cjs";
10
11
  import { Head, HeadProps } from "./components/head/head.cjs";
11
12
  import { Heading, HeadingAs, HeadingProps } from "./components/heading/heading.cjs";
@@ -23,4 +24,4 @@ import { EmailElementProps, Tailwind, TailwindConfig, TailwindProps, pixelBasedP
23
24
  import { TailwindSetup, setupTailwind } from "./components/tailwind/utils/tailwindcss/setup-tailwind.cjs";
24
25
  import { Text, TextProps } from "./components/text/text.cjs";
25
26
  export * from "@react-email/render";
26
- export { Body, BodyProps, Button, ButtonProps, CodeBlock, CodeBlockProps, CodeInline, CodeInlineProps, Column, ColumnProps, Container, ContainerProps, EmailElementProps, Font, FontProps, Head, HeadProps, Heading, HeadingAs, HeadingProps, Hr, HrProps, Html, HtmlProps, Img, ImgProps, Link, LinkProps, Markdown, MarkdownProps, Preview, PreviewProps, PrismLanguage, Row, RowProps, Section, SectionProps, Tailwind, TailwindConfig, TailwindProps, TailwindSetup, Text, TextProps, Theme, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
27
+ export { Body, BodyProps, Button, ButtonProps, CodeBlock, CodeBlockProps, CodeInline, CodeInlineProps, Column, ColumnProps, Container, ContainerProps, ElementOptions, EmailElementProps, Font, FontProps, Head, HeadProps, Heading, HeadingAs, HeadingProps, Hr, HrProps, Html, HtmlProps, Img, ImgProps, Link, LinkProps, Markdown, MarkdownProps, Preview, PreviewProps, PrismLanguage, ResolvedTailwind, Row, RowProps, Section, SectionProps, Tailwind, TailwindConfig, TailwindProps, TailwindSetup, Text, TextProps, Theme, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, markAsElement, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
package/dist/index.d.mts CHANGED
@@ -6,6 +6,7 @@ import { CodeBlock, CodeBlockProps } from "./components/code-block/code-block.mj
6
6
  import { CodeInline, CodeInlineProps } from "./components/code-inline/code-inline.mjs";
7
7
  import { Column, ColumnProps } from "./components/column/column.mjs";
8
8
  import { Container, ContainerProps } from "./components/container/container.mjs";
9
+ import { ElementOptions, ResolvedTailwind, markAsElement } from "./components/element-marker.mjs";
9
10
  import { Font, FontProps } from "./components/font/font.mjs";
10
11
  import { Head, HeadProps } from "./components/head/head.mjs";
11
12
  import { Heading, HeadingAs, HeadingProps } from "./components/heading/heading.mjs";
@@ -23,4 +24,4 @@ import { EmailElementProps, Tailwind, TailwindConfig, TailwindProps, pixelBasedP
23
24
  import { TailwindSetup, setupTailwind } from "./components/tailwind/utils/tailwindcss/setup-tailwind.mjs";
24
25
  import { Text, TextProps } from "./components/text/text.mjs";
25
26
  export * from "@react-email/render";
26
- export { Body, BodyProps, Button, ButtonProps, CodeBlock, CodeBlockProps, CodeInline, CodeInlineProps, Column, ColumnProps, Container, ContainerProps, EmailElementProps, Font, FontProps, Head, HeadProps, Heading, HeadingAs, HeadingProps, Hr, HrProps, Html, HtmlProps, Img, ImgProps, Link, LinkProps, Markdown, MarkdownProps, Preview, PreviewProps, PrismLanguage, Row, RowProps, Section, SectionProps, Tailwind, TailwindConfig, TailwindProps, TailwindSetup, Text, TextProps, Theme, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
27
+ export { Body, BodyProps, Button, ButtonProps, CodeBlock, CodeBlockProps, CodeInline, CodeInlineProps, Column, ColumnProps, Container, ContainerProps, ElementOptions, EmailElementProps, Font, FontProps, Head, HeadProps, Heading, HeadingAs, HeadingProps, Hr, HrProps, Html, HtmlProps, Img, ImgProps, Link, LinkProps, Markdown, MarkdownProps, Preview, PreviewProps, PrismLanguage, ResolvedTailwind, Row, RowProps, Section, SectionProps, Tailwind, TailwindConfig, TailwindProps, TailwindSetup, Text, TextProps, Theme, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, markAsElement, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { markAsElement } from "./components/element-marker.mjs";
1
2
  import { Body } from "./components/body/body.mjs";
2
3
  import { Button } from "./components/button/button.mjs";
3
4
  import { CodeBlock } from "./components/code-block/code-block.mjs";
@@ -22,4 +23,4 @@ import { setupTailwind } from "./components/tailwind/utils/tailwindcss/setup-tai
22
23
  import { Tailwind, pixelBasedPreset } from "./components/tailwind/tailwind.mjs";
23
24
  import { Text } from "./components/text/text.mjs";
24
25
  export * from "@react-email/render";
25
- export { Body, Button, CodeBlock, CodeInline, Column, Container, Font, Head, Heading, Hr, Html, Img, Link, Markdown, Preview, Row, Section, Tailwind, Text, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
26
+ export { Body, Button, CodeBlock, CodeInline, Column, Container, Font, Head, Heading, Hr, Html, Img, Link, Markdown, Preview, Row, Section, Tailwind, Text, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, markAsElement, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@depup/react-email",
3
- "version": "6.9.3-depup.0",
3
+ "version": "6.11.0-depup.0",
4
4
  "description": "A live preview of your emails right in your browser. (with updated dependencies)",
5
5
  "bin": {
6
6
  "email": "./dist/cli/index.mjs"
@@ -35,15 +35,15 @@
35
35
  "email"
36
36
  ],
37
37
  "engines": {
38
- "node": ">=20.0.0"
38
+ "node": ">=20.19.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "react": "^18.0 || ^19.0 || ^19.0.0-rc",
42
42
  "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc"
43
43
  },
44
44
  "dependencies": {
45
- "@babel/parser": "^8.0.4",
46
- "@babel/traverse": "^8.0.4",
45
+ "@babel/parser": "^7.29.9",
46
+ "@babel/traverse": "^8.0.6",
47
47
  "@react-email/render": ">=2.1.0",
48
48
  "chokidar": "^5.0.0",
49
49
  "commander": "^15.0.0",
@@ -54,14 +54,14 @@
54
54
  "glob": "^13.0.6",
55
55
  "jiti": "^2.7.0",
56
56
  "log-symbols": "^7.0.1",
57
- "marked": "^18.0.11",
57
+ "marked": "^18.0.14",
58
58
  "mime-types": "^3.0.2",
59
59
  "normalize-path": "^3.0.0",
60
- "nypm": "^0.6.9",
60
+ "nypm": "^0.6.10",
61
61
  "picospinner": "^3.1.2",
62
62
  "prismjs": "^1.30.0",
63
63
  "prompts": "2.4.2",
64
- "socket.io": "^4.8.3",
64
+ "socket.io": "^4.8.4",
65
65
  "tailwindcss": "^4.3.3",
66
66
  "tsconfig-paths": "4.2.0"
67
67
  },
@@ -73,7 +73,7 @@
73
73
  "@types/mime-types": "2.1.4",
74
74
  "@types/prompts": "2.4.9",
75
75
  "@types/shelljs": "0.10.0",
76
- "next": "16.3.0",
76
+ "next": "16.3.3",
77
77
  "react": "19.2.4",
78
78
  "react-dom": "19.2.4",
79
79
  "shelljs": "0.10.0",
@@ -96,15 +96,11 @@
96
96
  "changes": {
97
97
  "@babel/parser": {
98
98
  "from": "7.29.2",
99
- "to": "^8.0.4"
99
+ "to": "^7.29.9"
100
100
  },
101
101
  "@babel/traverse": {
102
102
  "from": "7.29.0",
103
- "to": "^8.0.4"
104
- },
105
- "chokidar": {
106
- "from": "^4.0.3",
107
- "to": "^5.0.0"
103
+ "to": "^8.0.6"
108
104
  },
109
105
  "commander": {
110
106
  "from": "^13.0.0",
@@ -132,7 +128,7 @@
132
128
  },
133
129
  "marked": {
134
130
  "from": "^15.0.12",
135
- "to": "^18.0.11"
131
+ "to": "^18.0.14"
136
132
  },
137
133
  "mime-types": {
138
134
  "from": "^3.0.0",
@@ -140,7 +136,7 @@
140
136
  },
141
137
  "nypm": {
142
138
  "from": "0.6.6",
143
- "to": "^0.6.9"
139
+ "to": "^0.6.10"
144
140
  },
145
141
  "picospinner": {
146
142
  "from": "^3.0.0",
@@ -148,17 +144,17 @@
148
144
  },
149
145
  "socket.io": {
150
146
  "from": "^4.8.1",
151
- "to": "^4.8.3"
147
+ "to": "^4.8.4"
152
148
  },
153
149
  "tailwindcss": {
154
150
  "from": "^4.1.18",
155
151
  "to": "^4.3.3"
156
152
  }
157
153
  },
158
- "depsUpdated": 15,
154
+ "depsUpdated": 14,
159
155
  "originalPackage": "react-email",
160
- "originalVersion": "6.9.3",
161
- "processedAt": "2026-08-26T00:17:10.469Z",
156
+ "originalVersion": "6.11.0",
157
+ "processedAt": "2026-09-27T01:01:23.007Z",
162
158
  "smokeTest": "failed"
163
159
  }
164
160
  }
package/readme.md CHANGED
@@ -30,6 +30,14 @@ Starts a local development server that will watch your files and automatically r
30
30
  npx react-email dev
31
31
  ```
32
32
 
33
+ Templates are bundled with [esbuild](https://esbuild.github.io/). To run additional esbuild plugins, for example a compile-time transform your templates depend on, pass `--esbuild-plugins` with the path to a module whose default export is an array of plugins (or a function returning one):
34
+
35
+ ```sh
36
+ npx react-email dev --esbuild-plugins ./esbuild-plugins.mjs
37
+ ```
38
+
39
+ The same option is accepted by `email build` and `email export`.
40
+
33
41
  ### `email export`
34
42
 
35
43
  Generates the plain HTML files of your emails into a `out` directory.
@@ -3,57 +3,77 @@ import { markAsElement } from '../element-marker.js';
3
3
 
4
4
  export type ContainerProps = Readonly<React.ComponentPropsWithoutRef<'table'>>;
5
5
 
6
- export const Container = React.forwardRef<HTMLTableElement, ContainerProps>(
7
- ({ children, style = {}, ...props }, ref) => {
8
- // Split padding styles to improve compatibility with Klaviyo and Outlook,
9
- // while preserving user-provided style property order without allocating
10
- // entry arrays on each render.
11
- const tdStyle: React.CSSProperties = {};
12
- const tableStyle: React.CSSProperties = {};
13
-
14
- const styleRecord = style as Record<string, unknown>;
15
-
16
- for (const key in styleRecord) {
17
- if (!Object.hasOwn(styleRecord, key)) {
18
- continue;
19
- }
6
+ export const Container = React.forwardRef<
7
+ HTMLTableElement,
8
+ ContainerProps & { tdClassName?: string }
9
+ >(({ children, style = {}, tdClassName, ...props }, ref) => {
10
+ // Split padding styles to improve compatibility with Klaviyo and Outlook,
11
+ // while preserving user-provided style property order without allocating
12
+ // entry arrays on each render.
13
+ const tdStyle: React.CSSProperties = {};
14
+ const tableStyle: React.CSSProperties = {};
15
+
16
+ const styleRecord = style as Record<string, unknown>;
17
+
18
+ for (const key in styleRecord) {
19
+ if (!Object.hasOwn(styleRecord, key)) {
20
+ continue;
21
+ }
22
+
23
+ const value = styleRecord[key];
20
24
 
21
- const value = styleRecord[key];
25
+ if (key.startsWith('padding')) {
26
+ (tdStyle as Record<string, unknown>)[key] = value;
27
+ } else {
28
+ (tableStyle as Record<string, unknown>)[key] = value;
29
+ }
30
+ }
22
31
 
32
+ return (
33
+ <table
34
+ align="center"
35
+ width="100%"
36
+ {...props}
37
+ border={0}
38
+ cellPadding="0"
39
+ cellSpacing="0"
40
+ ref={ref}
41
+ role="presentation"
42
+ style={{ maxWidth: '37.5em', ...tableStyle }}
43
+ >
44
+ <tbody>
45
+ <tr style={{ width: '100%' }}>
46
+ <td className={tdClassName} style={tdStyle}>
47
+ {children}
48
+ </td>
49
+ </tr>
50
+ </tbody>
51
+ </table>
52
+ );
53
+ });
54
+
55
+ Container.displayName = 'Container';
56
+ markAsElement<ContainerProps & { tdClassName?: string }>(Container, {
57
+ resolveTailwind(props, { style, className, classProperties }) {
58
+ const tableClasses: string[] = [];
59
+ const tdClasses = props.tdClassName ? [props.tdClassName] : [];
60
+ for (const name of className?.split(' ') ?? []) {
61
+ const properties = classProperties[name];
23
62
  if (
24
- key === 'padding' ||
25
- key === 'paddingTop' ||
26
- key === 'paddingRight' ||
27
- key === 'paddingBottom' ||
28
- key === 'paddingLeft'
63
+ properties &&
64
+ properties.length > 0 &&
65
+ properties.every((property) => property.startsWith('padding'))
29
66
  ) {
30
- (tdStyle as Record<string, unknown>)[key] = value;
67
+ tdClasses.push(name);
31
68
  } else {
32
- (tableStyle as Record<string, unknown>)[key] = value;
69
+ tableClasses.push(name);
33
70
  }
34
71
  }
35
-
36
- return (
37
- <table
38
- align="center"
39
- width="100%"
40
- {...props}
41
- border={0}
42
- cellPadding="0"
43
- cellSpacing="0"
44
- ref={ref}
45
- role="presentation"
46
- style={{ maxWidth: '37.5em', ...tableStyle }}
47
- >
48
- <tbody>
49
- <tr style={{ width: '100%' }}>
50
- <td style={tdStyle}>{children}</td>
51
- </tr>
52
- </tbody>
53
- </table>
54
- );
72
+ return {
73
+ ...props,
74
+ style,
75
+ className: tableClasses.length > 0 ? tableClasses.join(' ') : undefined,
76
+ tdClassName: tdClasses.length > 0 ? tdClasses.join(' ') : undefined,
77
+ };
55
78
  },
56
- );
57
-
58
- Container.displayName = 'Container';
59
- markAsElement(Container);
79
+ });
@@ -1,5 +1,28 @@
1
+ import type * as React from 'react';
2
+
1
3
  export const elementMarker = Symbol.for('react-email.element');
2
4
 
3
- export const markAsElement = (component: unknown) => {
4
- (component as Record<symbol, boolean>)[elementMarker] = true;
5
+ export interface ResolvedTailwind {
6
+ style: React.CSSProperties;
7
+ className: string | undefined;
8
+ classProperties: Record<string, string[]>;
9
+ }
10
+
11
+ export interface ElementOptions<Props = object> {
12
+ resolveTailwind?: (props: Props, resolved: ResolvedTailwind) => Props;
13
+ }
14
+
15
+ export const markAsElement = <Props>(
16
+ component: unknown,
17
+ options: ElementOptions<Props> = {},
18
+ ) => {
19
+ (component as Record<symbol, ElementOptions<Props>>)[elementMarker] = options;
20
+ };
21
+
22
+ export const getElementOptions = (
23
+ type: unknown,
24
+ ): ElementOptions | undefined => {
25
+ if (typeof type !== 'function' && typeof type !== 'object') return undefined;
26
+ if (type === null) return undefined;
27
+ return (type as Record<symbol, ElementOptions | undefined>)[elementMarker];
5
28
  };
@@ -4,6 +4,11 @@ export * from './code-block/index.js';
4
4
  export * from './code-inline/index.js';
5
5
  export * from './column/index.js';
6
6
  export * from './container/index.js';
7
+ export {
8
+ type ElementOptions,
9
+ markAsElement,
10
+ type ResolvedTailwind,
11
+ } from './element-marker.js';
7
12
  export * from './font/index.js';
8
13
  export * from './head/index.js';
9
14
  export * from './heading/index.js';