@react-email/tailwind 0.0.12 → 0.0.13-canary.1

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.mts CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as React from 'react';
2
- import { TailwindConfig } from 'tw-to-css';
2
+ import { Config } from 'tailwindcss';
3
3
 
4
+ type TailwindConfig = Omit<Config, "content">;
4
5
  interface TailwindProps {
5
6
  children: React.ReactNode;
6
7
  config?: TailwindConfig;
7
8
  }
8
9
  declare const Tailwind: React.FC<TailwindProps>;
9
10
 
10
- export { Tailwind, TailwindProps };
11
+ export { Tailwind, TailwindConfig, TailwindProps };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as React from 'react';
2
- import { TailwindConfig } from 'tw-to-css';
2
+ import { Config } from 'tailwindcss';
3
3
 
4
+ type TailwindConfig = Omit<Config, "content">;
4
5
  interface TailwindProps {
5
6
  children: React.ReactNode;
6
7
  config?: TailwindConfig;
7
8
  }
8
9
  declare const Tailwind: React.FC<TailwindProps>;
9
10
 
10
- export { Tailwind, TailwindProps };
11
+ export { Tailwind, TailwindConfig, TailwindProps };
package/dist/index.js CHANGED
@@ -54,7 +54,6 @@ module.exports = __toCommonJS(src_exports);
54
54
  // src/tailwind.tsx
55
55
  var React = __toESM(require("react"));
56
56
  var import_server = require("react-dom/server");
57
- var import_tw_to_css = require("tw-to-css");
58
57
 
59
58
  // src/utils/css-to-jsx-style.ts
60
59
  var camelCase = (string) => string.replace(/-(\w|$)/g, (_, p1) => p1.toUpperCase());
@@ -68,155 +67,189 @@ var convertPropertyName = (prop) => {
68
67
  return modifiedProp;
69
68
  }
70
69
  if (modifiedProp.startsWith("-ms-")) {
71
- modifiedProp = modifiedProp.substr(1);
70
+ modifiedProp = modifiedProp.slice(1);
72
71
  }
73
72
  return camelCase(modifiedProp);
74
73
  };
75
- var splitDeclarations = (cssText) => {
76
- const declarations = [];
77
- let capturing;
78
- let i = cssText.length;
79
- let last = i;
80
- while (i-- > -1) {
81
- if ((cssText[i] === '"' || cssText[i] === "'") && cssText[i - 1] !== "\\") {
82
- if (!capturing) {
83
- capturing = cssText[i];
84
- } else if (cssText[i] === capturing) {
85
- capturing = false;
86
- }
87
- }
88
- if (!capturing && cssText[i] === ")") {
89
- capturing = cssText[i];
90
- }
91
- if (cssText[i] === "(" && capturing === ")") {
92
- capturing = false;
93
- }
94
- if (i < 0 || !capturing && cssText[i] === ";") {
95
- declarations.unshift(cssText.slice(i + 1, last));
96
- last = i;
74
+ var cssToJsxStyle = (cssText) => {
75
+ const style = {};
76
+ const declarations = cssText.matchAll(
77
+ /([a-zA-Z0-9\-_]+)\s*:\s*('[^']*'[^;]*|"[^"]*"[^;]*|.*?\([^)]*\)[^;]*|[^;]*);?/gm
78
+ );
79
+ for (const [_declaration, property, value] of declarations) {
80
+ if (property.length > 0 && value.trim().length > 0) {
81
+ style[convertPropertyName(property)] = value.trim();
97
82
  }
98
83
  }
99
- return declarations;
84
+ return style;
100
85
  };
101
- var splitDeclaration = (declaration) => {
102
- const i = declaration.indexOf(":");
103
- return [declaration.substr(0, i).trim(), declaration.substr(i + 1).trim()];
86
+
87
+ // src/utils/get-css-for-markup.ts
88
+ var import_postcss = __toESM(require("postcss"));
89
+ var import_tailwindcss = __toESM(require("tailwindcss"));
90
+ var import_postcss_css_variables = __toESM(require("postcss-css-variables"));
91
+ global.__OXIDE__ = void 0;
92
+ var getCssForMarkup = (markup, config) => {
93
+ const corePlugins = config == null ? void 0 : config.corePlugins;
94
+ const tailwindConfig = __spreadProps(__spreadValues({}, config), {
95
+ corePlugins: __spreadValues({
96
+ preflight: false
97
+ }, corePlugins)
98
+ });
99
+ const processor = (0, import_postcss.default)([
100
+ (0, import_tailwindcss.default)(__spreadProps(__spreadValues({}, tailwindConfig), {
101
+ content: [{ raw: markup, extension: "html" }]
102
+ })),
103
+ (0, import_postcss_css_variables.default)()
104
+ ]);
105
+ const result = processor.process(
106
+ String.raw`
107
+ @tailwind base;
108
+ @tailwind components;
109
+ @tailwind utilities;
110
+ `,
111
+ { from: void 0 }
112
+ // no need to use from since the `content` context is sent into tailwind
113
+ );
114
+ return result.css;
115
+ };
116
+
117
+ // src/utils/minify-css.ts
118
+ var minifyCss = (css) => {
119
+ return css.replace(/\/\*[\s\S]*?\*\//gm, "").replace(/;\s+/gm, ";").replace(/:\s+/gm, ":").replace(/\)\s*{/gm, "){").replace(/\s+\(/gm, "(").replace(/{\s+/gm, "{").replace(/}\s+/gm, "}").replace(/\s*{/gm, "{").replace(/;?\s*}/gm, "}");
104
120
  };
105
- var cssToJsxStyle = (cssText) => splitDeclarations(cssText).map(splitDeclaration).reduce((styles, [name, value]) => {
106
- if (name && value) {
107
- styles[convertPropertyName(name)] = value;
121
+
122
+ // src/utils/get-css-class-properties-map.ts
123
+ var getStylesPerClassMap = (css) => {
124
+ const map = {};
125
+ for (const [_match, className, contents] of css.matchAll(
126
+ /\s*\.([\S]+)\s*{([^}]*)}/gm
127
+ )) {
128
+ map[className.trim()] = contents.replace(/^\n+/, "").replace(/\n+$/, "").trim();
108
129
  }
109
- return styles;
110
- }, {});
130
+ return map;
131
+ };
132
+
133
+ // src/utils/escape-class-name.ts
134
+ var escapeClassName = (className) => {
135
+ return className.replace(
136
+ /* we need this look ahead capturing group to avoid using negative look behinds */
137
+ /([^\\]|^)(?=([^a-zA-Z0-9\-_]))/g,
138
+ (match, prefixCharacter, characterToEscape) => {
139
+ if (prefixCharacter === "" && characterToEscape === "\\")
140
+ return match;
141
+ return `${prefixCharacter}\\`;
142
+ }
143
+ );
144
+ };
111
145
 
112
146
  // src/tailwind.tsx
113
147
  var import_jsx_runtime = require("react/jsx-runtime");
114
- function processElement(element, headStyles, twi) {
148
+ function processElement(element, nonMediaQueryTailwindStylesPerClass) {
115
149
  let modifiedElement = element;
150
+ let resultingClassName = modifiedElement.props.className;
151
+ let resultingStyle = modifiedElement.props.style;
152
+ let resultingChildren = [];
116
153
  if (modifiedElement.props.className) {
117
- const convertedStyles = [];
118
- const responsiveStyles = [];
119
- const classNames = modifiedElement.props.className.split(" ");
120
- const customClassNames = classNames.filter((className) => {
121
- const tailwindClassName = twi(className, { ignoreMediaQueries: true });
122
- if (tailwindClassName) {
123
- convertedStyles.push(tailwindClassName);
124
- return false;
125
- } else if (twi(className, { ignoreMediaQueries: false })) {
126
- responsiveStyles.push(className);
127
- return false;
154
+ const fullClassName = modifiedElement.props.className;
155
+ const classNames = fullClassName.split(" ");
156
+ const classNamesToKeep = [];
157
+ const styles = [];
158
+ classNames.forEach((className) => {
159
+ const escapedClassName = escapeClassName(className);
160
+ if (typeof nonMediaQueryTailwindStylesPerClass[escapedClassName] === "undefined") {
161
+ classNamesToKeep.push(className);
162
+ } else {
163
+ styles.push(
164
+ `${nonMediaQueryTailwindStylesPerClass[escapedClassName]};`
165
+ );
128
166
  }
129
- return true;
130
- });
131
- const convertedResponsiveStyles = twi(responsiveStyles, {
132
- ignoreMediaQueries: false,
133
- merge: false
134
167
  });
135
- headStyles.push(
136
- convertedResponsiveStyles.replace(/^\n+/, "").replace(/\n+$/, "")
137
- );
138
- modifiedElement = React.cloneElement(modifiedElement, __spreadProps(__spreadValues({}, modifiedElement.props), {
139
- className: customClassNames.length ? customClassNames.join(" ") : void 0,
140
- style: __spreadValues(__spreadValues({}, modifiedElement.props.style), cssToJsxStyle(convertedStyles.join(" ")))
141
- }));
168
+ resultingStyle = __spreadValues(__spreadValues({}, modifiedElement.props.style), cssToJsxStyle(styles.join(" ")));
169
+ resultingClassName = classNamesToKeep.length > 0 ? classNamesToKeep.join(" ") : void 0;
142
170
  }
143
171
  if (modifiedElement.props.children) {
144
- const children = React.Children.toArray(modifiedElement.props.children);
145
- const processedChildren = children.map((child) => {
172
+ resultingChildren = React.Children.toArray(
173
+ modifiedElement.props.children
174
+ ).map((child) => {
146
175
  if (React.isValidElement(child)) {
147
- return processElement(child, headStyles, twi);
176
+ return processElement(child, nonMediaQueryTailwindStylesPerClass);
148
177
  }
149
178
  return child;
150
179
  });
151
- modifiedElement = React.cloneElement(
152
- modifiedElement,
153
- modifiedElement.props,
154
- ...processedChildren
155
- );
180
+ }
181
+ modifiedElement = React.cloneElement(
182
+ modifiedElement,
183
+ __spreadValues(__spreadProps(__spreadValues({}, modifiedElement.props), {
184
+ className: resultingClassName
185
+ }), typeof resultingStyle === "undefined" ? {} : { style: resultingStyle }),
186
+ ...resultingChildren
187
+ );
188
+ if (typeof modifiedElement.type === "function") {
189
+ const component = modifiedElement.type;
190
+ const renderedComponent = component(modifiedElement.props);
191
+ if (React.isValidElement(renderedComponent)) {
192
+ modifiedElement = processElement(
193
+ renderedComponent,
194
+ nonMediaQueryTailwindStylesPerClass
195
+ );
196
+ }
156
197
  }
157
198
  return modifiedElement;
158
199
  }
159
- function processHead(child, responsiveStyles) {
160
- let modifiedChild = child;
161
- if (modifiedChild.type === "head" || modifiedChild.type.displayName === "Head") {
162
- const styleElement = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: responsiveStyles });
163
- const headChildren = React.Children.toArray(modifiedChild.props.children);
164
- headChildren.push(styleElement);
165
- modifiedChild = React.cloneElement(
166
- modifiedChild,
167
- modifiedChild.props,
168
- ...headChildren
169
- );
170
- }
171
- if (modifiedChild.props.children) {
172
- const children = React.Children.toArray(modifiedChild.props.children);
173
- const processedChildren = children.map((processedChild) => {
174
- if (React.isValidElement(processedChild)) {
175
- return processHead(processedChild, responsiveStyles);
176
- }
177
- return processedChild;
178
- });
179
- modifiedChild = React.cloneElement(
180
- modifiedChild,
181
- modifiedChild.props,
182
- ...processedChildren
183
- );
184
- }
185
- return modifiedChild;
200
+ function processHead(headElement, responsiveStyles) {
201
+ const styleElement = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: minifyCss(responsiveStyles.join("")) });
202
+ return React.cloneElement(
203
+ headElement,
204
+ headElement.props,
205
+ ...React.Children.toArray(headElement.props.children),
206
+ styleElement
207
+ );
186
208
  }
187
209
  var Tailwind = ({ children, config }) => {
188
- const headStyles = [];
189
- const { twi } = (0, import_tw_to_css.tailwindToCSS)({
190
- config
191
- });
192
- const childrenWithInlineStyles = React.Children.map(children, (child) => {
193
- if (React.isValidElement(child)) {
194
- return processElement(child, headStyles, twi);
210
+ let headStyles = [];
211
+ const markupWithTailwindClasses = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children }));
212
+ const markupCSS = getCssForMarkup(markupWithTailwindClasses, config);
213
+ const nonMediaQueryCSS = markupCSS.replaceAll(
214
+ /@media\s*\(.*\)\s*{\s*\.(.*)\s*{[\s\S]*}\s*}/gm,
215
+ (mediaQuery, _className) => {
216
+ headStyles.push(
217
+ mediaQuery.replace(/[\r\n|\r|\n]+/g, "").replace(/\s+/g, " ").replaceAll(/\s*\.[\S]+\s*{([^}]*)}/gm, (match, content) => {
218
+ return match.replace(
219
+ content,
220
+ content.split(";").map(
221
+ (propertyDeclaration) => propertyDeclaration.endsWith("!important") ? propertyDeclaration.trim() : `${propertyDeclaration.trim()}!important`
222
+ ).join(";")
223
+ );
224
+ })
225
+ );
226
+ return "";
195
227
  }
196
- return child;
197
- });
198
- if (!childrenWithInlineStyles)
199
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
200
- const fullHTML = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: childrenWithInlineStyles }));
201
- const hasResponsiveStyles = new RegExp("@media[^{]+\\{(?<content>[\\s\\S]+?)\\}\\s*\\}", "gm").test(
202
- headStyles.join(" ")
203
228
  );
204
- const hasHTMLAndHead = /<html[^>]*>(?=[\s\S]*<head[^>]*>)/gm.test(fullHTML);
205
- if (hasResponsiveStyles && !hasHTMLAndHead) {
206
- throw new Error(
207
- "Tailwind: To use responsive styles you must have a <html> and <head> element in your template."
229
+ const nonMediaQueryTailwindStylesPerClass = getStylesPerClassMap(nonMediaQueryCSS);
230
+ const childrenArray = React.Children.toArray(children);
231
+ const validElementsWithIndexes = childrenArray.map((child, i) => [child, i]).filter(([child]) => React.isValidElement(child));
232
+ let headElementIndex = -1;
233
+ validElementsWithIndexes.forEach(([element, i]) => {
234
+ childrenArray[i] = processElement(
235
+ element,
236
+ nonMediaQueryTailwindStylesPerClass
208
237
  );
209
- }
210
- const childrenWithInlineAndResponsiveStyles = React.Children.map(
211
- childrenWithInlineStyles,
212
- (child) => {
213
- if (React.isValidElement(child)) {
214
- return processHead(child, headStyles);
215
- }
216
- return child;
238
+ if (element.type === "head" || typeof element.type === "function" && "name" in element.type && element.type.name === "Head") {
239
+ headElementIndex = i;
217
240
  }
218
- );
219
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: childrenWithInlineAndResponsiveStyles });
241
+ });
242
+ headStyles = headStyles.filter((style) => style.trim().length > 0);
243
+ if (headStyles.length > 0) {
244
+ if (headElementIndex === -1) {
245
+ throw new Error(
246
+ "Tailwind: To use responsive styles you must have a <head> element as a direct child of the Tailwind component."
247
+ );
248
+ }
249
+ const [headElement, headAllElementsIndex] = validElementsWithIndexes[headElementIndex];
250
+ childrenArray[headAllElementsIndex] = processHead(headElement, headStyles);
251
+ }
252
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: childrenArray });
220
253
  };
221
254
  // Annotate the CommonJS export names for ESM import in node:
222
255
  0 && (module.exports = {
package/dist/index.mjs CHANGED
@@ -21,7 +21,6 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
21
  // src/tailwind.tsx
22
22
  import * as React from "react";
23
23
  import { renderToStaticMarkup } from "react-dom/server";
24
- import { tailwindToCSS } from "tw-to-css";
25
24
 
26
25
  // src/utils/css-to-jsx-style.ts
27
26
  var camelCase = (string) => string.replace(/-(\w|$)/g, (_, p1) => p1.toUpperCase());
@@ -35,155 +34,189 @@ var convertPropertyName = (prop) => {
35
34
  return modifiedProp;
36
35
  }
37
36
  if (modifiedProp.startsWith("-ms-")) {
38
- modifiedProp = modifiedProp.substr(1);
37
+ modifiedProp = modifiedProp.slice(1);
39
38
  }
40
39
  return camelCase(modifiedProp);
41
40
  };
42
- var splitDeclarations = (cssText) => {
43
- const declarations = [];
44
- let capturing;
45
- let i = cssText.length;
46
- let last = i;
47
- while (i-- > -1) {
48
- if ((cssText[i] === '"' || cssText[i] === "'") && cssText[i - 1] !== "\\") {
49
- if (!capturing) {
50
- capturing = cssText[i];
51
- } else if (cssText[i] === capturing) {
52
- capturing = false;
53
- }
54
- }
55
- if (!capturing && cssText[i] === ")") {
56
- capturing = cssText[i];
57
- }
58
- if (cssText[i] === "(" && capturing === ")") {
59
- capturing = false;
60
- }
61
- if (i < 0 || !capturing && cssText[i] === ";") {
62
- declarations.unshift(cssText.slice(i + 1, last));
63
- last = i;
41
+ var cssToJsxStyle = (cssText) => {
42
+ const style = {};
43
+ const declarations = cssText.matchAll(
44
+ /([a-zA-Z0-9\-_]+)\s*:\s*('[^']*'[^;]*|"[^"]*"[^;]*|.*?\([^)]*\)[^;]*|[^;]*);?/gm
45
+ );
46
+ for (const [_declaration, property, value] of declarations) {
47
+ if (property.length > 0 && value.trim().length > 0) {
48
+ style[convertPropertyName(property)] = value.trim();
64
49
  }
65
50
  }
66
- return declarations;
51
+ return style;
67
52
  };
68
- var splitDeclaration = (declaration) => {
69
- const i = declaration.indexOf(":");
70
- return [declaration.substr(0, i).trim(), declaration.substr(i + 1).trim()];
53
+
54
+ // src/utils/get-css-for-markup.ts
55
+ import postcss from "postcss";
56
+ import tailwindcss from "tailwindcss";
57
+ import postcssCssVariables from "postcss-css-variables";
58
+ global.__OXIDE__ = void 0;
59
+ var getCssForMarkup = (markup, config) => {
60
+ const corePlugins = config == null ? void 0 : config.corePlugins;
61
+ const tailwindConfig = __spreadProps(__spreadValues({}, config), {
62
+ corePlugins: __spreadValues({
63
+ preflight: false
64
+ }, corePlugins)
65
+ });
66
+ const processor = postcss([
67
+ tailwindcss(__spreadProps(__spreadValues({}, tailwindConfig), {
68
+ content: [{ raw: markup, extension: "html" }]
69
+ })),
70
+ postcssCssVariables()
71
+ ]);
72
+ const result = processor.process(
73
+ String.raw`
74
+ @tailwind base;
75
+ @tailwind components;
76
+ @tailwind utilities;
77
+ `,
78
+ { from: void 0 }
79
+ // no need to use from since the `content` context is sent into tailwind
80
+ );
81
+ return result.css;
82
+ };
83
+
84
+ // src/utils/minify-css.ts
85
+ var minifyCss = (css) => {
86
+ return css.replace(/\/\*[\s\S]*?\*\//gm, "").replace(/;\s+/gm, ";").replace(/:\s+/gm, ":").replace(/\)\s*{/gm, "){").replace(/\s+\(/gm, "(").replace(/{\s+/gm, "{").replace(/}\s+/gm, "}").replace(/\s*{/gm, "{").replace(/;?\s*}/gm, "}");
71
87
  };
72
- var cssToJsxStyle = (cssText) => splitDeclarations(cssText).map(splitDeclaration).reduce((styles, [name, value]) => {
73
- if (name && value) {
74
- styles[convertPropertyName(name)] = value;
88
+
89
+ // src/utils/get-css-class-properties-map.ts
90
+ var getStylesPerClassMap = (css) => {
91
+ const map = {};
92
+ for (const [_match, className, contents] of css.matchAll(
93
+ /\s*\.([\S]+)\s*{([^}]*)}/gm
94
+ )) {
95
+ map[className.trim()] = contents.replace(/^\n+/, "").replace(/\n+$/, "").trim();
75
96
  }
76
- return styles;
77
- }, {});
97
+ return map;
98
+ };
99
+
100
+ // src/utils/escape-class-name.ts
101
+ var escapeClassName = (className) => {
102
+ return className.replace(
103
+ /* we need this look ahead capturing group to avoid using negative look behinds */
104
+ /([^\\]|^)(?=([^a-zA-Z0-9\-_]))/g,
105
+ (match, prefixCharacter, characterToEscape) => {
106
+ if (prefixCharacter === "" && characterToEscape === "\\")
107
+ return match;
108
+ return `${prefixCharacter}\\`;
109
+ }
110
+ );
111
+ };
78
112
 
79
113
  // src/tailwind.tsx
80
114
  import { Fragment, jsx } from "react/jsx-runtime";
81
- function processElement(element, headStyles, twi) {
115
+ function processElement(element, nonMediaQueryTailwindStylesPerClass) {
82
116
  let modifiedElement = element;
117
+ let resultingClassName = modifiedElement.props.className;
118
+ let resultingStyle = modifiedElement.props.style;
119
+ let resultingChildren = [];
83
120
  if (modifiedElement.props.className) {
84
- const convertedStyles = [];
85
- const responsiveStyles = [];
86
- const classNames = modifiedElement.props.className.split(" ");
87
- const customClassNames = classNames.filter((className) => {
88
- const tailwindClassName = twi(className, { ignoreMediaQueries: true });
89
- if (tailwindClassName) {
90
- convertedStyles.push(tailwindClassName);
91
- return false;
92
- } else if (twi(className, { ignoreMediaQueries: false })) {
93
- responsiveStyles.push(className);
94
- return false;
121
+ const fullClassName = modifiedElement.props.className;
122
+ const classNames = fullClassName.split(" ");
123
+ const classNamesToKeep = [];
124
+ const styles = [];
125
+ classNames.forEach((className) => {
126
+ const escapedClassName = escapeClassName(className);
127
+ if (typeof nonMediaQueryTailwindStylesPerClass[escapedClassName] === "undefined") {
128
+ classNamesToKeep.push(className);
129
+ } else {
130
+ styles.push(
131
+ `${nonMediaQueryTailwindStylesPerClass[escapedClassName]};`
132
+ );
95
133
  }
96
- return true;
97
- });
98
- const convertedResponsiveStyles = twi(responsiveStyles, {
99
- ignoreMediaQueries: false,
100
- merge: false
101
134
  });
102
- headStyles.push(
103
- convertedResponsiveStyles.replace(/^\n+/, "").replace(/\n+$/, "")
104
- );
105
- modifiedElement = React.cloneElement(modifiedElement, __spreadProps(__spreadValues({}, modifiedElement.props), {
106
- className: customClassNames.length ? customClassNames.join(" ") : void 0,
107
- style: __spreadValues(__spreadValues({}, modifiedElement.props.style), cssToJsxStyle(convertedStyles.join(" ")))
108
- }));
135
+ resultingStyle = __spreadValues(__spreadValues({}, modifiedElement.props.style), cssToJsxStyle(styles.join(" ")));
136
+ resultingClassName = classNamesToKeep.length > 0 ? classNamesToKeep.join(" ") : void 0;
109
137
  }
110
138
  if (modifiedElement.props.children) {
111
- const children = React.Children.toArray(modifiedElement.props.children);
112
- const processedChildren = children.map((child) => {
139
+ resultingChildren = React.Children.toArray(
140
+ modifiedElement.props.children
141
+ ).map((child) => {
113
142
  if (React.isValidElement(child)) {
114
- return processElement(child, headStyles, twi);
143
+ return processElement(child, nonMediaQueryTailwindStylesPerClass);
115
144
  }
116
145
  return child;
117
146
  });
118
- modifiedElement = React.cloneElement(
119
- modifiedElement,
120
- modifiedElement.props,
121
- ...processedChildren
122
- );
147
+ }
148
+ modifiedElement = React.cloneElement(
149
+ modifiedElement,
150
+ __spreadValues(__spreadProps(__spreadValues({}, modifiedElement.props), {
151
+ className: resultingClassName
152
+ }), typeof resultingStyle === "undefined" ? {} : { style: resultingStyle }),
153
+ ...resultingChildren
154
+ );
155
+ if (typeof modifiedElement.type === "function") {
156
+ const component = modifiedElement.type;
157
+ const renderedComponent = component(modifiedElement.props);
158
+ if (React.isValidElement(renderedComponent)) {
159
+ modifiedElement = processElement(
160
+ renderedComponent,
161
+ nonMediaQueryTailwindStylesPerClass
162
+ );
163
+ }
123
164
  }
124
165
  return modifiedElement;
125
166
  }
126
- function processHead(child, responsiveStyles) {
127
- let modifiedChild = child;
128
- if (modifiedChild.type === "head" || modifiedChild.type.displayName === "Head") {
129
- const styleElement = /* @__PURE__ */ jsx("style", { children: responsiveStyles });
130
- const headChildren = React.Children.toArray(modifiedChild.props.children);
131
- headChildren.push(styleElement);
132
- modifiedChild = React.cloneElement(
133
- modifiedChild,
134
- modifiedChild.props,
135
- ...headChildren
136
- );
137
- }
138
- if (modifiedChild.props.children) {
139
- const children = React.Children.toArray(modifiedChild.props.children);
140
- const processedChildren = children.map((processedChild) => {
141
- if (React.isValidElement(processedChild)) {
142
- return processHead(processedChild, responsiveStyles);
143
- }
144
- return processedChild;
145
- });
146
- modifiedChild = React.cloneElement(
147
- modifiedChild,
148
- modifiedChild.props,
149
- ...processedChildren
150
- );
151
- }
152
- return modifiedChild;
167
+ function processHead(headElement, responsiveStyles) {
168
+ const styleElement = /* @__PURE__ */ jsx("style", { children: minifyCss(responsiveStyles.join("")) });
169
+ return React.cloneElement(
170
+ headElement,
171
+ headElement.props,
172
+ ...React.Children.toArray(headElement.props.children),
173
+ styleElement
174
+ );
153
175
  }
154
176
  var Tailwind = ({ children, config }) => {
155
- const headStyles = [];
156
- const { twi } = tailwindToCSS({
157
- config
158
- });
159
- const childrenWithInlineStyles = React.Children.map(children, (child) => {
160
- if (React.isValidElement(child)) {
161
- return processElement(child, headStyles, twi);
177
+ let headStyles = [];
178
+ const markupWithTailwindClasses = renderToStaticMarkup(/* @__PURE__ */ jsx(Fragment, { children }));
179
+ const markupCSS = getCssForMarkup(markupWithTailwindClasses, config);
180
+ const nonMediaQueryCSS = markupCSS.replaceAll(
181
+ /@media\s*\(.*\)\s*{\s*\.(.*)\s*{[\s\S]*}\s*}/gm,
182
+ (mediaQuery, _className) => {
183
+ headStyles.push(
184
+ mediaQuery.replace(/[\r\n|\r|\n]+/g, "").replace(/\s+/g, " ").replaceAll(/\s*\.[\S]+\s*{([^}]*)}/gm, (match, content) => {
185
+ return match.replace(
186
+ content,
187
+ content.split(";").map(
188
+ (propertyDeclaration) => propertyDeclaration.endsWith("!important") ? propertyDeclaration.trim() : `${propertyDeclaration.trim()}!important`
189
+ ).join(";")
190
+ );
191
+ })
192
+ );
193
+ return "";
162
194
  }
163
- return child;
164
- });
165
- if (!childrenWithInlineStyles)
166
- return /* @__PURE__ */ jsx(Fragment, { children });
167
- const fullHTML = renderToStaticMarkup(/* @__PURE__ */ jsx(Fragment, { children: childrenWithInlineStyles }));
168
- const hasResponsiveStyles = new RegExp("@media[^{]+\\{(?<content>[\\s\\S]+?)\\}\\s*\\}", "gm").test(
169
- headStyles.join(" ")
170
195
  );
171
- const hasHTMLAndHead = /<html[^>]*>(?=[\s\S]*<head[^>]*>)/gm.test(fullHTML);
172
- if (hasResponsiveStyles && !hasHTMLAndHead) {
173
- throw new Error(
174
- "Tailwind: To use responsive styles you must have a <html> and <head> element in your template."
196
+ const nonMediaQueryTailwindStylesPerClass = getStylesPerClassMap(nonMediaQueryCSS);
197
+ const childrenArray = React.Children.toArray(children);
198
+ const validElementsWithIndexes = childrenArray.map((child, i) => [child, i]).filter(([child]) => React.isValidElement(child));
199
+ let headElementIndex = -1;
200
+ validElementsWithIndexes.forEach(([element, i]) => {
201
+ childrenArray[i] = processElement(
202
+ element,
203
+ nonMediaQueryTailwindStylesPerClass
175
204
  );
176
- }
177
- const childrenWithInlineAndResponsiveStyles = React.Children.map(
178
- childrenWithInlineStyles,
179
- (child) => {
180
- if (React.isValidElement(child)) {
181
- return processHead(child, headStyles);
182
- }
183
- return child;
205
+ if (element.type === "head" || typeof element.type === "function" && "name" in element.type && element.type.name === "Head") {
206
+ headElementIndex = i;
184
207
  }
185
- );
186
- return /* @__PURE__ */ jsx(Fragment, { children: childrenWithInlineAndResponsiveStyles });
208
+ });
209
+ headStyles = headStyles.filter((style) => style.trim().length > 0);
210
+ if (headStyles.length > 0) {
211
+ if (headElementIndex === -1) {
212
+ throw new Error(
213
+ "Tailwind: To use responsive styles you must have a <head> element as a direct child of the Tailwind component."
214
+ );
215
+ }
216
+ const [headElement, headAllElementsIndex] = validElementsWithIndexes[headElementIndex];
217
+ childrenArray[headAllElementsIndex] = processHead(headElement, headStyles);
218
+ }
219
+ return /* @__PURE__ */ jsx(Fragment, { children: childrenArray });
187
220
  };
188
221
  export {
189
222
  Tailwind
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-email/tailwind",
3
- "version": "0.0.12",
3
+ "version": "0.0.13-canary.1",
4
4
  "description": "A React component to wrap emails with Tailwind CSS",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -44,9 +44,11 @@
44
44
  "node": ">=18.0.0"
45
45
  },
46
46
  "dependencies": {
47
+ "postcss": "8.4.31",
48
+ "postcss-css-variables": "0.19.0",
47
49
  "react": "18.2.0",
48
50
  "react-dom": "18.2.0",
49
- "tw-to-css": "0.0.12"
51
+ "tailwindcss": "3.3.2"
50
52
  },
51
53
  "peerDependencies": {
52
54
  "react": "18.2.0"
@@ -54,11 +56,13 @@
54
56
  "devDependencies": {
55
57
  "@babel/core": "7.21.8",
56
58
  "@babel/preset-react": "7.22.5",
57
- "@react-email/hr": "workspace:*",
58
59
  "@react-email/head": "workspace:*",
60
+ "@react-email/hr": "workspace:*",
59
61
  "@react-email/html": "workspace:*",
60
62
  "@testing-library/react": "14.0.0",
63
+ "@types/postcss-css-variables": "0.18.2",
61
64
  "eslint-config-custom": "workspace:*",
65
+ "eslint-plugin-regex": "1.10.0",
62
66
  "tsconfig": "workspace:*",
63
67
  "typescript": "5.1.6"
64
68
  },