@react-email/tailwind 0.0.4 → 0.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/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,94 @@ 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
+ const currentStyles = domNode.attribs.style ? `${domNode.attribs.style};` : "";
88
+ const tailwindStyles = twi(domNode.attribs.class);
89
+ domNode.attribs.style = `${currentStyles} ${tailwindStyles}`;
90
+ delete domNode.attribs.class;
91
+ }
92
+ }
93
+ }
94
+ }
51
95
  });
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 });
96
+ return parsedHTML;
97
+ });
98
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: reactHTML });
58
99
  };
59
100
  Tailwind.displayName = "Tailwind";
101
+ function getMediaQueryCSS(css) {
102
+ const mediaQueryRegex = /@media[^{]+\{(?<content>[\s\S]+?)\}\s*\}/gm;
103
+ let newCss = css.replace(mediaQueryRegex, (m) => {
104
+ return m.replace(
105
+ /([^{]+\{)([\s\S]+?)(\}\s*\})/gm,
106
+ (_, start, content, end) => {
107
+ const newcontent = content.replace(
108
+ /(?:[\s\r\n]*)?(?<prop>[\w-]+)\s*:\s*(?<value>[^;\r\n]+)/gm,
109
+ (_2, prop, value) => {
110
+ return `${prop}: ${value} !important`;
111
+ }
112
+ );
113
+ return `${start}${newcontent}${end}`;
114
+ }
115
+ );
116
+ }).replace(/[.\!\#\w\d\\:\-\[\]]+\s*?{/g, (m) => {
117
+ return m.replace(/[:#\!\-[\\\]]+/g, "_");
118
+ }).replace(/font-family(?<value>[^;\r\n]+)/g, (m, value) => {
119
+ return `font-family${value.replace(/['"]+/g, "")}`;
120
+ });
121
+ return newCss;
122
+ }
60
123
  // Annotate the CommonJS export names for ESM import in node:
61
124
  0 && (module.exports = {
62
125
  Tailwind
package/dist/index.mjs CHANGED
@@ -1,30 +1,93 @@
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
+ const currentStyles = domNode.attribs.style ? `${domNode.attribs.style};` : "";
56
+ const tailwindStyles = twi(domNode.attribs.class);
57
+ domNode.attribs.style = `${currentStyles} ${tailwindStyles}`;
58
+ delete domNode.attribs.class;
59
+ }
60
+ }
61
+ }
62
+ }
19
63
  });
20
- };
21
- const tailwindStylesToCSS = React.Children.map(
22
- children,
23
- replaceTailwindStyles
24
- );
25
- return /* @__PURE__ */ jsx(Fragment, { children: tailwindStylesToCSS });
64
+ return parsedHTML;
65
+ });
66
+ return /* @__PURE__ */ jsx(Fragment, { children: reactHTML });
26
67
  };
27
68
  Tailwind.displayName = "Tailwind";
69
+ function getMediaQueryCSS(css) {
70
+ const mediaQueryRegex = /@media[^{]+\{(?<content>[\s\S]+?)\}\s*\}/gm;
71
+ let newCss = css.replace(mediaQueryRegex, (m) => {
72
+ return m.replace(
73
+ /([^{]+\{)([\s\S]+?)(\}\s*\})/gm,
74
+ (_, start, content, end) => {
75
+ const newcontent = content.replace(
76
+ /(?:[\s\r\n]*)?(?<prop>[\w-]+)\s*:\s*(?<value>[^;\r\n]+)/gm,
77
+ (_2, prop, value) => {
78
+ return `${prop}: ${value} !important`;
79
+ }
80
+ );
81
+ return `${start}${newcontent}${end}`;
82
+ }
83
+ );
84
+ }).replace(/[.\!\#\w\d\\:\-\[\]]+\s*?{/g, (m) => {
85
+ return m.replace(/[:#\!\-[\\\]]+/g, "_");
86
+ }).replace(/font-family(?<value>[^;\r\n]+)/g, (m, value) => {
87
+ return `font-family${value.replace(/['"]+/g, "")}`;
88
+ });
89
+ return newCss;
90
+ }
28
91
  export {
29
92
  Tailwind
30
93
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-email/tailwind",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
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.9"
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",