@react-email/tailwind 0.0.13-canary.0 → 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,162 +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;
116
- const propsClassName = modifiedElement.props.className;
117
- if (typeof propsClassName === "string") {
118
- const convertedStyles = [];
119
- const responsiveClassNames = [];
120
- const customClassNames = [];
121
- const allClassNames = propsClassName.split(" ");
122
- allClassNames.forEach((className) => {
123
- const stylesWithoutMediaQueries = twi(className, {
124
- ignoreMediaQueries: true
125
- });
126
- if (stylesWithoutMediaQueries.length > 0) {
127
- convertedStyles.push(stylesWithoutMediaQueries);
128
- } else if (twi(className, { ignoreMediaQueries: false }).length > 0) {
129
- const classPieces = className.split(":");
130
- const mediaQueryParts = classPieces.slice(0, -1);
131
- const twClass = classPieces[classPieces.length - 1];
132
- const importantPrefixedClassName = twClass.startsWith("!") ? className : `${mediaQueryParts.join(":")}:!${twClass}`;
133
- responsiveClassNames.push(importantPrefixedClassName);
150
+ let resultingClassName = modifiedElement.props.className;
151
+ let resultingStyle = modifiedElement.props.style;
152
+ let resultingChildren = [];
153
+ if (modifiedElement.props.className) {
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);
134
162
  } else {
135
- customClassNames.push(className);
163
+ styles.push(
164
+ `${nonMediaQueryTailwindStylesPerClass[escapedClassName]};`
165
+ );
136
166
  }
137
167
  });
138
- const convertedResponsiveStyles = twi(responsiveClassNames, {
139
- ignoreMediaQueries: false,
140
- merge: false
141
- });
142
- headStyles.push(
143
- convertedResponsiveStyles.replace(/^\n+/, "").replace(/\n+$/, "")
144
- );
145
- modifiedElement = React.cloneElement(modifiedElement, __spreadProps(__spreadValues({}, modifiedElement.props), {
146
- className: customClassNames.length > 0 || responsiveClassNames.length > 0 ? customClassNames.concat(responsiveClassNames).join(" ") : void 0,
147
- style: __spreadValues(__spreadValues({}, modifiedElement.props.style), cssToJsxStyle(convertedStyles.join(" ")))
148
- }));
168
+ resultingStyle = __spreadValues(__spreadValues({}, modifiedElement.props.style), cssToJsxStyle(styles.join(" ")));
169
+ resultingClassName = classNamesToKeep.length > 0 ? classNamesToKeep.join(" ") : void 0;
149
170
  }
150
171
  if (modifiedElement.props.children) {
151
- const children = React.Children.toArray(modifiedElement.props.children);
152
- const processedChildren = children.map((child) => {
172
+ resultingChildren = React.Children.toArray(
173
+ modifiedElement.props.children
174
+ ).map((child) => {
153
175
  if (React.isValidElement(child)) {
154
- return processElement(child, headStyles, twi);
176
+ return processElement(child, nonMediaQueryTailwindStylesPerClass);
155
177
  }
156
178
  return child;
157
179
  });
158
- modifiedElement = React.cloneElement(
159
- modifiedElement,
160
- modifiedElement.props,
161
- ...processedChildren
162
- );
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
+ }
163
197
  }
164
198
  return modifiedElement;
165
199
  }
166
- function processHead(child, responsiveStyles) {
167
- let modifiedChild = child;
168
- if (modifiedChild.type === "head" || modifiedChild.type.displayName === "Head") {
169
- const styleElement = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: responsiveStyles });
170
- const headChildren = React.Children.toArray(modifiedChild.props.children);
171
- headChildren.push(styleElement);
172
- modifiedChild = React.cloneElement(
173
- modifiedChild,
174
- modifiedChild.props,
175
- ...headChildren
176
- );
177
- }
178
- if (modifiedChild.props.children) {
179
- const children = React.Children.toArray(modifiedChild.props.children);
180
- const processedChildren = children.map((processedChild) => {
181
- if (React.isValidElement(processedChild)) {
182
- return processHead(processedChild, responsiveStyles);
183
- }
184
- return processedChild;
185
- });
186
- modifiedChild = React.cloneElement(
187
- modifiedChild,
188
- modifiedChild.props,
189
- ...processedChildren
190
- );
191
- }
192
- 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
+ );
193
208
  }
194
209
  var Tailwind = ({ children, config }) => {
195
- const headStyles = [];
196
- const { twi } = (0, import_tw_to_css.tailwindToCSS)({
197
- config
198
- });
199
- const childrenWithInlineStyles = React.Children.map(children, (child) => {
200
- if (React.isValidElement(child)) {
201
- 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 "";
202
227
  }
203
- return child;
204
- });
205
- if (!childrenWithInlineStyles)
206
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
207
- const fullHTML = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: childrenWithInlineStyles }));
208
- const hasResponsiveStyles = new RegExp("@media[^{]+\\{(?<content>[\\s\\S]+?)\\}\\s*\\}", "gm").test(
209
- headStyles.join(" ")
210
228
  );
211
- const hasHTMLAndHead = /<html[^>]*>(?=[\s\S]*<head[^>]*>)/gm.test(fullHTML);
212
- if (hasResponsiveStyles && !hasHTMLAndHead) {
213
- throw new Error(
214
- "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
215
237
  );
216
- }
217
- const childrenWithInlineAndResponsiveStyles = React.Children.map(
218
- childrenWithInlineStyles,
219
- (child) => {
220
- if (React.isValidElement(child)) {
221
- return processHead(child, headStyles);
222
- }
223
- return child;
238
+ if (element.type === "head" || typeof element.type === "function" && "name" in element.type && element.type.name === "Head") {
239
+ headElementIndex = i;
224
240
  }
225
- );
226
- 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 });
227
253
  };
228
254
  // Annotate the CommonJS export names for ESM import in node:
229
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,162 +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;
83
- const propsClassName = modifiedElement.props.className;
84
- if (typeof propsClassName === "string") {
85
- const convertedStyles = [];
86
- const responsiveClassNames = [];
87
- const customClassNames = [];
88
- const allClassNames = propsClassName.split(" ");
89
- allClassNames.forEach((className) => {
90
- const stylesWithoutMediaQueries = twi(className, {
91
- ignoreMediaQueries: true
92
- });
93
- if (stylesWithoutMediaQueries.length > 0) {
94
- convertedStyles.push(stylesWithoutMediaQueries);
95
- } else if (twi(className, { ignoreMediaQueries: false }).length > 0) {
96
- const classPieces = className.split(":");
97
- const mediaQueryParts = classPieces.slice(0, -1);
98
- const twClass = classPieces[classPieces.length - 1];
99
- const importantPrefixedClassName = twClass.startsWith("!") ? className : `${mediaQueryParts.join(":")}:!${twClass}`;
100
- responsiveClassNames.push(importantPrefixedClassName);
117
+ let resultingClassName = modifiedElement.props.className;
118
+ let resultingStyle = modifiedElement.props.style;
119
+ let resultingChildren = [];
120
+ if (modifiedElement.props.className) {
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);
101
129
  } else {
102
- customClassNames.push(className);
130
+ styles.push(
131
+ `${nonMediaQueryTailwindStylesPerClass[escapedClassName]};`
132
+ );
103
133
  }
104
134
  });
105
- const convertedResponsiveStyles = twi(responsiveClassNames, {
106
- ignoreMediaQueries: false,
107
- merge: false
108
- });
109
- headStyles.push(
110
- convertedResponsiveStyles.replace(/^\n+/, "").replace(/\n+$/, "")
111
- );
112
- modifiedElement = React.cloneElement(modifiedElement, __spreadProps(__spreadValues({}, modifiedElement.props), {
113
- className: customClassNames.length > 0 || responsiveClassNames.length > 0 ? customClassNames.concat(responsiveClassNames).join(" ") : void 0,
114
- style: __spreadValues(__spreadValues({}, modifiedElement.props.style), cssToJsxStyle(convertedStyles.join(" ")))
115
- }));
135
+ resultingStyle = __spreadValues(__spreadValues({}, modifiedElement.props.style), cssToJsxStyle(styles.join(" ")));
136
+ resultingClassName = classNamesToKeep.length > 0 ? classNamesToKeep.join(" ") : void 0;
116
137
  }
117
138
  if (modifiedElement.props.children) {
118
- const children = React.Children.toArray(modifiedElement.props.children);
119
- const processedChildren = children.map((child) => {
139
+ resultingChildren = React.Children.toArray(
140
+ modifiedElement.props.children
141
+ ).map((child) => {
120
142
  if (React.isValidElement(child)) {
121
- return processElement(child, headStyles, twi);
143
+ return processElement(child, nonMediaQueryTailwindStylesPerClass);
122
144
  }
123
145
  return child;
124
146
  });
125
- modifiedElement = React.cloneElement(
126
- modifiedElement,
127
- modifiedElement.props,
128
- ...processedChildren
129
- );
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
+ }
130
164
  }
131
165
  return modifiedElement;
132
166
  }
133
- function processHead(child, responsiveStyles) {
134
- let modifiedChild = child;
135
- if (modifiedChild.type === "head" || modifiedChild.type.displayName === "Head") {
136
- const styleElement = /* @__PURE__ */ jsx("style", { children: responsiveStyles });
137
- const headChildren = React.Children.toArray(modifiedChild.props.children);
138
- headChildren.push(styleElement);
139
- modifiedChild = React.cloneElement(
140
- modifiedChild,
141
- modifiedChild.props,
142
- ...headChildren
143
- );
144
- }
145
- if (modifiedChild.props.children) {
146
- const children = React.Children.toArray(modifiedChild.props.children);
147
- const processedChildren = children.map((processedChild) => {
148
- if (React.isValidElement(processedChild)) {
149
- return processHead(processedChild, responsiveStyles);
150
- }
151
- return processedChild;
152
- });
153
- modifiedChild = React.cloneElement(
154
- modifiedChild,
155
- modifiedChild.props,
156
- ...processedChildren
157
- );
158
- }
159
- 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
+ );
160
175
  }
161
176
  var Tailwind = ({ children, config }) => {
162
- const headStyles = [];
163
- const { twi } = tailwindToCSS({
164
- config
165
- });
166
- const childrenWithInlineStyles = React.Children.map(children, (child) => {
167
- if (React.isValidElement(child)) {
168
- 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 "";
169
194
  }
170
- return child;
171
- });
172
- if (!childrenWithInlineStyles)
173
- return /* @__PURE__ */ jsx(Fragment, { children });
174
- const fullHTML = renderToStaticMarkup(/* @__PURE__ */ jsx(Fragment, { children: childrenWithInlineStyles }));
175
- const hasResponsiveStyles = new RegExp("@media[^{]+\\{(?<content>[\\s\\S]+?)\\}\\s*\\}", "gm").test(
176
- headStyles.join(" ")
177
195
  );
178
- const hasHTMLAndHead = /<html[^>]*>(?=[\s\S]*<head[^>]*>)/gm.test(fullHTML);
179
- if (hasResponsiveStyles && !hasHTMLAndHead) {
180
- throw new Error(
181
- "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
182
204
  );
183
- }
184
- const childrenWithInlineAndResponsiveStyles = React.Children.map(
185
- childrenWithInlineStyles,
186
- (child) => {
187
- if (React.isValidElement(child)) {
188
- return processHead(child, headStyles);
189
- }
190
- return child;
205
+ if (element.type === "head" || typeof element.type === "function" && "name" in element.type && element.type.name === "Head") {
206
+ headElementIndex = i;
191
207
  }
192
- );
193
- 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 });
194
220
  };
195
221
  export {
196
222
  Tailwind
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-email/tailwind",
3
- "version": "0.0.13-canary.0",
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
  },