@react-email/tailwind 0.0.3 → 0.0.5

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/index.d.ts CHANGED
@@ -5,6 +5,6 @@ interface TailwindProps {
5
5
  children: React.ReactNode;
6
6
  config?: TailwindConfig;
7
7
  }
8
- declare const Tailwind: React.FC<Readonly<TailwindProps>>;
8
+ declare const Tailwind: React.FC<TailwindProps>;
9
9
 
10
10
  export { Tailwind, TailwindProps };
package/dist/index.js CHANGED
@@ -32,31 +32,92 @@ module.exports = __toCommonJS(src_exports);
32
32
 
33
33
  // src/tailwind.tsx
34
34
  var React = __toESM(require("react"));
35
+ var import_server = require("react-dom/server");
36
+ var import_html_react_parser = __toESM(require("html-react-parser"));
35
37
  var import_tw_to_css = require("tw-to-css");
36
38
  var import_jsx_runtime = require("react/jsx-runtime");
37
- var Tailwind = ({
38
- children,
39
- config
40
- }) => {
41
- const { twj } = (0, import_tw_to_css.tailwindToCSS)({ config });
42
- const replaceTailwindStyles = (child) => {
43
- if (!React.isValidElement(child)) {
39
+ var Tailwind = ({ children, config }) => {
40
+ const { twi } = (0, import_tw_to_css.tailwindToCSS)({
41
+ config
42
+ });
43
+ const newChildren = React.Children.toArray(children);
44
+ const fullHTML = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: newChildren }));
45
+ const headStyle = getMediaQueryCSS(
46
+ twi(fullHTML, {
47
+ merge: false,
48
+ ignoreMediaQueries: false
49
+ })
50
+ );
51
+ const hasResponsiveStyles = /@media[^{]+\{(?<content>[\s\S]+?)\}\s*\}/gm.test(
52
+ headStyle
53
+ );
54
+ const hasHTML = /<html[^>]*>/gm.test(fullHTML);
55
+ const hasHead = /<head[^>]*>/gm.test(fullHTML);
56
+ if (hasResponsiveStyles && !hasHTML && !hasHead) {
57
+ throw new Error(
58
+ "Tailwind: To use responsive styles you must have a <html> and <head> element in your template."
59
+ );
60
+ }
61
+ const reactHTML = React.Children.map(newChildren, (child) => {
62
+ if (!React.isValidElement(child))
44
63
  return child;
45
- }
46
- const { className, children: childChildren, style, ...rest } = child.props;
47
- return React.cloneElement(child, {
48
- ...rest,
49
- children: React.Children.map(childChildren, replaceTailwindStyles),
50
- style: { ...style, ...className ? twj(className) : {} }
64
+ const html = (0, import_server.renderToStaticMarkup)(child);
65
+ const parsedHTML = (0, import_html_react_parser.default)(html, {
66
+ replace: (domNode) => {
67
+ var _a;
68
+ if (domNode instanceof import_html_react_parser.Element) {
69
+ if (hasResponsiveStyles && hasHead && domNode.name === "head") {
70
+ let newDomNode = null;
71
+ if (domNode.children) {
72
+ const style = domNode.children.find(
73
+ (child2) => child2 instanceof import_html_react_parser.Element && child2.name === "style"
74
+ );
75
+ const props = (0, import_html_react_parser.attributesToProps)(domNode.attribs);
76
+ newDomNode = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("head", { ...props, children: style && style instanceof import_html_react_parser.Element ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `${style.children} ${headStyle}` }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: headStyle }) });
77
+ }
78
+ return newDomNode;
79
+ }
80
+ if ((_a = domNode.attribs) == null ? void 0 : _a.class) {
81
+ if (hasResponsiveStyles) {
82
+ domNode.attribs.class = domNode.attribs.class.replace(
83
+ /[:#\!\-[\]]+/g,
84
+ "_"
85
+ );
86
+ } else {
87
+ domNode.attribs.style = `${twi(domNode.attribs.class)} ${domNode.attribs.style || ""}`;
88
+ delete domNode.attribs.class;
89
+ }
90
+ }
91
+ }
92
+ }
51
93
  });
52
- };
53
- const tailwindStylesToCSS = React.Children.map(
54
- children,
55
- replaceTailwindStyles
56
- );
57
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: tailwindStylesToCSS });
94
+ return parsedHTML;
95
+ });
96
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: reactHTML });
58
97
  };
59
98
  Tailwind.displayName = "Tailwind";
99
+ function getMediaQueryCSS(css) {
100
+ const mediaQueryRegex = /@media[^{]+\{(?<content>[\s\S]+?)\}\s*\}/gm;
101
+ let newCss = css.replace(mediaQueryRegex, (m) => {
102
+ return m.replace(
103
+ /([^{]+\{)([\s\S]+?)(\}\s*\})/gm,
104
+ (_, start, content, end) => {
105
+ const newcontent = content.replace(
106
+ /(?:[\s\r\n]*)?(?<prop>[\w-]+)\s*:\s*(?<value>[^;\r\n]+)/gm,
107
+ (_2, prop, value) => {
108
+ return `${prop}: ${value} !important`;
109
+ }
110
+ );
111
+ return `${start}${newcontent}${end}`;
112
+ }
113
+ );
114
+ }).replace(/[.\!\#\w\d\\:\-\[\]]+\s*?{/g, (m) => {
115
+ return m.replace(/[:#\!\-[\\\]]+/g, "_");
116
+ }).replace(/font-family(?<value>[^;\r\n]+)/g, (m, value) => {
117
+ return `font-family${value.replace(/['"]+/g, "")}`;
118
+ });
119
+ return newCss;
120
+ }
60
121
  // Annotate the CommonJS export names for ESM import in node:
61
122
  0 && (module.exports = {
62
123
  Tailwind
package/dist/index.mjs CHANGED
@@ -1,30 +1,91 @@
1
1
  // src/tailwind.tsx
2
2
  import * as React from "react";
3
+ import { renderToStaticMarkup } from "react-dom/server";
4
+ import htmlParser, { attributesToProps, Element } from "html-react-parser";
3
5
  import { tailwindToCSS } from "tw-to-css";
4
6
  import { Fragment, jsx } from "react/jsx-runtime";
5
- var Tailwind = ({
6
- children,
7
- config
8
- }) => {
9
- const { twj } = tailwindToCSS({ config });
10
- const replaceTailwindStyles = (child) => {
11
- if (!React.isValidElement(child)) {
7
+ var Tailwind = ({ children, config }) => {
8
+ const { twi } = tailwindToCSS({
9
+ config
10
+ });
11
+ const newChildren = React.Children.toArray(children);
12
+ const fullHTML = renderToStaticMarkup(/* @__PURE__ */ jsx(Fragment, { children: newChildren }));
13
+ const headStyle = getMediaQueryCSS(
14
+ twi(fullHTML, {
15
+ merge: false,
16
+ ignoreMediaQueries: false
17
+ })
18
+ );
19
+ const hasResponsiveStyles = /@media[^{]+\{(?<content>[\s\S]+?)\}\s*\}/gm.test(
20
+ headStyle
21
+ );
22
+ const hasHTML = /<html[^>]*>/gm.test(fullHTML);
23
+ const hasHead = /<head[^>]*>/gm.test(fullHTML);
24
+ if (hasResponsiveStyles && !hasHTML && !hasHead) {
25
+ throw new Error(
26
+ "Tailwind: To use responsive styles you must have a <html> and <head> element in your template."
27
+ );
28
+ }
29
+ const reactHTML = React.Children.map(newChildren, (child) => {
30
+ if (!React.isValidElement(child))
12
31
  return child;
13
- }
14
- const { className, children: childChildren, style, ...rest } = child.props;
15
- return React.cloneElement(child, {
16
- ...rest,
17
- children: React.Children.map(childChildren, replaceTailwindStyles),
18
- style: { ...style, ...className ? twj(className) : {} }
32
+ const html = renderToStaticMarkup(child);
33
+ const parsedHTML = htmlParser(html, {
34
+ replace: (domNode) => {
35
+ var _a;
36
+ if (domNode instanceof Element) {
37
+ if (hasResponsiveStyles && hasHead && domNode.name === "head") {
38
+ let newDomNode = null;
39
+ if (domNode.children) {
40
+ const style = domNode.children.find(
41
+ (child2) => child2 instanceof Element && child2.name === "style"
42
+ );
43
+ const props = attributesToProps(domNode.attribs);
44
+ newDomNode = /* @__PURE__ */ jsx("head", { ...props, children: style && style instanceof Element ? /* @__PURE__ */ jsx("style", { children: `${style.children} ${headStyle}` }) : /* @__PURE__ */ jsx("style", { children: headStyle }) });
45
+ }
46
+ return newDomNode;
47
+ }
48
+ if ((_a = domNode.attribs) == null ? void 0 : _a.class) {
49
+ if (hasResponsiveStyles) {
50
+ domNode.attribs.class = domNode.attribs.class.replace(
51
+ /[:#\!\-[\]]+/g,
52
+ "_"
53
+ );
54
+ } else {
55
+ domNode.attribs.style = `${twi(domNode.attribs.class)} ${domNode.attribs.style || ""}`;
56
+ delete domNode.attribs.class;
57
+ }
58
+ }
59
+ }
60
+ }
19
61
  });
20
- };
21
- const tailwindStylesToCSS = React.Children.map(
22
- children,
23
- replaceTailwindStyles
24
- );
25
- return /* @__PURE__ */ jsx(Fragment, { children: tailwindStylesToCSS });
62
+ return parsedHTML;
63
+ });
64
+ return /* @__PURE__ */ jsx(Fragment, { children: reactHTML });
26
65
  };
27
66
  Tailwind.displayName = "Tailwind";
67
+ function getMediaQueryCSS(css) {
68
+ const mediaQueryRegex = /@media[^{]+\{(?<content>[\s\S]+?)\}\s*\}/gm;
69
+ let newCss = css.replace(mediaQueryRegex, (m) => {
70
+ return m.replace(
71
+ /([^{]+\{)([\s\S]+?)(\}\s*\})/gm,
72
+ (_, start, content, end) => {
73
+ const newcontent = content.replace(
74
+ /(?:[\s\r\n]*)?(?<prop>[\w-]+)\s*:\s*(?<value>[^;\r\n]+)/gm,
75
+ (_2, prop, value) => {
76
+ return `${prop}: ${value} !important`;
77
+ }
78
+ );
79
+ return `${start}${newcontent}${end}`;
80
+ }
81
+ );
82
+ }).replace(/[.\!\#\w\d\\:\-\[\]]+\s*?{/g, (m) => {
83
+ return m.replace(/[:#\!\-[\\\]]+/g, "_");
84
+ }).replace(/font-family(?<value>[^;\r\n]+)/g, (m, value) => {
85
+ return `font-family${value.replace(/['"]+/g, "")}`;
86
+ });
87
+ return newCss;
88
+ }
28
89
  export {
29
90
  Tailwind
30
91
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-email/tailwind",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "A React component to wrap emails with Tailwind CSS",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -15,6 +15,8 @@
15
15
  "dev": "tsup src/index.ts --format esm,cjs --dts --external react --watch",
16
16
  "lint": "eslint",
17
17
  "clean": "rm -rf dist",
18
+ "test": "jest",
19
+ "test:watch": "jest --watch",
18
20
  "format:check": "prettier --ignore-path ./../../.prettierignore --check \"**/*.{ts,tsx,md}\"",
19
21
  "format": "prettier --ignore-path ./../../.prettierignore --write \"**/*.{ts,tsx,md}\""
20
22
  },
@@ -32,9 +34,13 @@
32
34
  "node": ">=16.0.0"
33
35
  },
34
36
  "dependencies": {
35
- "tw-to-css": "0.0.5"
37
+ "html-react-parser": "3.0.9",
38
+ "react": "18.2.0",
39
+ "react-dom": "18.2.0",
40
+ "tw-to-css": "0.0.10"
36
41
  },
37
42
  "devDependencies": {
43
+ "@testing-library/react": "14.0.0",
38
44
  "@types/react": "18.0.20",
39
45
  "@types/react-dom": "18.0.6",
40
46
  "eslint": "8.23.1",
package/readme.md CHANGED
@@ -29,7 +29,7 @@ npm install @react-email/tailwind -E
29
29
 
30
30
  ## Getting started
31
31
 
32
- Add the component to your email template. Include styles where needed.
32
+ Add the component around your email body content.
33
33
 
34
34
  ```jsx
35
35
  import { Button } from '@react-email/button';