🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
DemoInstallSign in
Socket

@react-email/tailwind

Package Overview
Dependencies
Maintainers
2
Versions
56
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@react-email/tailwind - npm Package Compare versions

Comparing version

to
0.0.11-canary.0

245

dist/index.js

@@ -57,24 +57,151 @@ "use strict";

var import_server = require("react-dom/server");
var import_html_react_parser = __toESM(require("html-react-parser"));
var import_tw_to_css = require("tw-to-css");
// src/utils/css-to-jsx-style.ts
var camelCase = (string) => string.replace(/-(\w|$)/g, (_, p1) => p1.toUpperCase());
var convertPropertyName = (prop) => {
let modifiedProp = prop;
modifiedProp = modifiedProp.toLowerCase();
if (modifiedProp === "float") {
return "cssFloat";
}
if (modifiedProp.startsWith("--")) {
return modifiedProp;
}
if (modifiedProp.startsWith("-ms-")) {
modifiedProp = modifiedProp.substr(1);
}
return camelCase(modifiedProp);
};
var splitDeclarations = (cssText) => {
const declarations = [];
let capturing;
let i = cssText.length;
let last = i;
while (i-- > -1) {
if ((cssText[i] === '"' || cssText[i] === "'") && cssText[i - 1] !== "\\") {
if (!capturing) {
capturing = cssText[i];
} else if (cssText[i] === capturing) {
capturing = false;
}
}
if (!capturing && cssText[i] === ")") {
capturing = cssText[i];
}
if (cssText[i] === "(" && capturing === ")") {
capturing = false;
}
if (i < 0 || !capturing && cssText[i] === ";") {
declarations.unshift(cssText.slice(i + 1, last));
last = i;
}
}
return declarations;
};
var splitDeclaration = (declaration) => {
const i = declaration.indexOf(":");
return [declaration.substr(0, i).trim(), declaration.substr(i + 1).trim()];
};
var cssToJsxStyle = (cssText) => splitDeclarations(cssText).map(splitDeclaration).reduce((styles, [name, value]) => {
if (name && value) {
styles[convertPropertyName(name)] = value;
}
return styles;
}, {});
// src/tailwind.tsx
var import_jsx_runtime = require("react/jsx-runtime");
function processElement(element, headStyles, twi) {
let modifiedElement = element;
if (modifiedElement.props.className) {
const convertedStyles = [];
const responsiveStyles = [];
const classNames = modifiedElement.props.className.split(" ");
const customClassNames = classNames.filter((className) => {
const tailwindClassName = twi(className, { ignoreMediaQueries: true });
if (tailwindClassName) {
convertedStyles.push(tailwindClassName);
return false;
} else if (twi(className, { ignoreMediaQueries: false })) {
responsiveStyles.push(className);
return false;
}
return true;
});
const convertedResponsiveStyles = twi(responsiveStyles, {
ignoreMediaQueries: false,
merge: false
});
headStyles.push(
convertedResponsiveStyles.replace(/^\n+/, "").replace(/\n+$/, "")
);
modifiedElement = React.cloneElement(modifiedElement, __spreadProps(__spreadValues({}, modifiedElement.props), {
className: customClassNames.length ? customClassNames.join(" ") : void 0,
style: __spreadValues(__spreadValues({}, modifiedElement.props.style), cssToJsxStyle(convertedStyles.join(" ")))
}));
}
if (modifiedElement.props.children) {
const children = React.Children.toArray(modifiedElement.props.children);
const processedChildren = children.map((child) => {
if (React.isValidElement(child)) {
return processElement(child, headStyles, twi);
}
return child;
});
modifiedElement = React.cloneElement(
modifiedElement,
modifiedElement.props,
...processedChildren
);
}
return modifiedElement;
}
function processHead(child, responsiveStyles) {
let modifiedChild = child;
if (modifiedChild.type === "head" || modifiedChild.type.displayName === "Head") {
const styleElement = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: responsiveStyles });
const headChildren = React.Children.toArray(modifiedChild.props.children);
headChildren.push(styleElement);
modifiedChild = React.cloneElement(
modifiedChild,
modifiedChild.props,
...headChildren
);
}
if (modifiedChild.props.children) {
const children = React.Children.toArray(modifiedChild.props.children);
const processedChildren = children.map((processedChild) => {
if (React.isValidElement(processedChild)) {
return processHead(processedChild, responsiveStyles);
}
return processedChild;
});
modifiedChild = React.cloneElement(
modifiedChild,
modifiedChild.props,
...processedChildren
);
}
return modifiedChild;
}
var Tailwind = ({ children, config }) => {
const headStyles = [];
const { twi } = (0, import_tw_to_css.tailwindToCSS)({
config
});
const newChildren = React.Children.toArray(children);
const fullHTML = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: newChildren }));
const tailwindCss = twi(fullHTML, {
merge: false,
ignoreMediaQueries: false
const childrenWithInlineStyles = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return processElement(child, headStyles, twi);
}
return child;
});
const css = cleanCss(tailwindCss);
const cssMap = makeCssMap(css);
const headStyle = getMediaQueryCss(css);
if (!childrenWithInlineStyles)
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
const fullHTML = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: childrenWithInlineStyles }));
const hasResponsiveStyles = new RegExp("@media[^{]+\\{(?<content>[\\s\\S]+?)\\}\\s*\\}", "gm").test(
headStyle
headStyles.join(" ")
);
const hasHTML = /<html[^>]*>/gm.test(fullHTML);
const hasHead = /<head[^>]*>/gm.test(fullHTML);
if (hasResponsiveStyles && (!hasHTML || !hasHead)) {
const hasHTMLAndHead = /<html[^>]*>(?=[\s\S]*<head[^>]*>)/gm.test(fullHTML);
if (hasResponsiveStyles && !hasHTMLAndHead) {
throw new Error(

@@ -84,87 +211,13 @@ "Tailwind: To use responsive styles you must have a <html> and <head> element in your template."

}
const reactHTML = React.Children.map(newChildren, (child) => {
if (!React.isValidElement(child))
const childrenWithInlineAndResponsiveStyles = React.Children.map(
childrenWithInlineStyles,
(child) => {
if (React.isValidElement(child)) {
return processHead(child, headStyles);
}
return child;
const html = (0, import_server.renderToStaticMarkup)(child);
const parsedHTML = (0, import_html_react_parser.default)(html, {
replace: (domNode) => {
var _a;
if (domNode instanceof import_html_react_parser.Element) {
if (hasResponsiveStyles && hasHead && domNode.name === "head") {
let newDomNode = null;
if (domNode.children) {
const props = (0, import_html_react_parser.attributesToProps)(domNode.attribs);
newDomNode = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("head", __spreadProps(__spreadValues({}, props), { children: [
(0, import_html_react_parser.domToReact)(domNode.children),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: headStyle })
] }));
}
return newDomNode;
}
if ((_a = domNode.attribs) == null ? void 0 : _a.class) {
const cleanRegex = /[:#\!\-[\]\/\.%]+/g;
const cleanTailwindClasses = domNode.attribs.class.replace(cleanRegex, "_");
const currentStyles = domNode.attribs.style ? `${domNode.attribs.style};` : "";
const tailwindStyles = cleanTailwindClasses.split(" ").map((className) => {
return cssMap[`.${className}`];
}).join(";");
domNode.attribs.style = `${currentStyles} ${tailwindStyles}`;
domNode.attribs.class = domNode.attribs.class.split(" ").filter((className) => {
const cleanedClassName = className.replace(cleanRegex, "_");
return className.search(/^.{2}:/) !== -1 || !cssMap[`.${cleanedClassName}`];
}).join(" ").replace(cleanRegex, "_");
if (domNode.attribs.class === "")
delete domNode.attribs.class;
}
}
}
});
return parsedHTML;
});
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: reactHTML });
}
);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: childrenWithInlineAndResponsiveStyles });
};
Tailwind.displayName = "Tailwind";
function cleanCss(css) {
let newCss = css.replace(/\\/g, "").replace(/[.\!\#\w\d\\:\-\[\]\/\.%\(\))]+(?=\s*?{[^{]*?\})\s*?{/g, (m) => {
return m.replace(new RegExp("(?<=.)[:#\\!\\-[\\\\\\]\\/\\.%]+", "g"), "_");
}).replace(new RegExp("font-family(?<value>[^;\\r\\n]+)", "g"), (m, value) => {
return `font-family${value.replace(/['"]+/g, "")}`;
});
return newCss;
}
function getMediaQueryCss(css) {
var _a, _b;
const mediaQueryRegex = new RegExp("@media[^{]+\\{(?<content>[\\s\\S]+?)\\}\\s*\\}", "gm");
return (_b = (_a = css.replace(mediaQueryRegex, (m) => {
return m.replace(
/([^{]+\{)([\s\S]+?)(\}\s*\})/gm,
(_, start, content, end) => {
const newContent = content.replace(
new RegExp("(?:[\\s\\r\\n]*)?(?<prop>[\\w-]+)\\s*:\\s*(?<value>[^};\\r\\n]+)", "gm"),
(_2, prop, value) => {
return `${prop}: ${value} !important;`;
}
);
return `${start}${newContent}${end}`;
}
);
}).match(/@media\s*([^{]+)\{([^{}]*\{[^{}]*\})*[^{}]*\}/g)) == null ? void 0 : _a.join("")) != null ? _b : "";
}
function makeCssMap(css) {
const cssNoMedia = css.replace(
new RegExp("@media[^{]+\\{(?<content>[\\s\\S]+?)\\}\\s*\\}", "gm"),
""
);
const cssMap = cssNoMedia.split("}").reduce(
(acc, cur) => {
const [key, value] = cur.split("{");
if (key && value) {
acc[key] = value;
}
return acc;
},
{}
);
return cssMap;
}
// Annotate the CommonJS export names for ESM import in node:

@@ -171,0 +224,0 @@ 0 && (module.exports = {

{
"name": "@react-email/tailwind",
"version": "0.0.9",
"version": "0.0.11-canary.0",
"description": "A React component to wrap emails with Tailwind CSS",

@@ -27,9 +27,7 @@ "sideEffects": false,

"build": "tsup src/index.ts --format esm,cjs --dts --external react",
"clean": "rm -rf dist",
"dev": "tsup src/index.ts --format esm,cjs --dts --external react --watch",
"lint": "eslint",
"clean": "rm -rf dist",
"test": "jest",
"test:watch": "jest --watch",
"format:check": "prettier --check \"**/*.{ts,tsx,md}\"",
"format": "prettier --write \"**/*.{ts,tsx,md}\""
"lint": "eslint .",
"test:watch": "vitest",
"test": "vitest run"
},

@@ -47,25 +45,21 @@ "repository": {

"engines": {
"node": ">=16.0.0"
"node": ">=18.0.0"
},
"dependencies": {
"html-react-parser": "4.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"tw-to-css": "0.0.11"
"tw-to-css": "0.0.12"
},
"peerDependencies": {
"react": "18.2.0"
},
"devDependencies": {
"@babel/core": "^7.21.8",
"@babel/core": "7.21.8",
"@babel/preset-react": "7.22.5",
"@react-email/button": "0.0.7",
"@react-email/hr": "workspace:*",
"@react-email/head": "workspace:*",
"@react-email/html": "workspace:*",
"@testing-library/react": "14.0.0",
"@types/jest": "29.5.3",
"@types/react": "18.0.20",
"@types/react-dom": "18.0.6",
"babel-jest": "29.6.1",
"eslint": "8.45.0",
"jest": "29.6.1",
"prettier": "3.0.0",
"react": "18.2.0",
"ts-jest": "29.1.1",
"tsup": "7.1.0",
"eslint-config-custom": "workspace:*",
"tsconfig": "workspace:*",
"typescript": "5.1.6"

@@ -72,0 +66,0 @@ },

Sorry, the diff of this file is not supported yet