[go: up one dir, main page]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tailwind): Smaller bundle size #1383

Merged
merged 128 commits into from
Oct 7, 2024
Merged
Changes from 1 commit
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
2092215
feat(head): iOS formatting meta tag as default (#1369)
endymion1818 Mar 28, 2024
be72c02
fix(create-email): Tailwind imported from @react-email/tailwind which…
hank619 Mar 29, 2024
af59dc4
feat(tailwind): Context-aware treatment for components inside of Tail…
gabrielmfern Mar 14, 2024
522fccd
chore(tailwind): Add a unit test to check if context is working insid…
gabrielmfern Mar 14, 2024
8405061
chore: Add postcss-selector-parser
gabrielmfern Mar 19, 2024
2a82fa9
feat(tailwind/hooks): useTailwind() hook that allows to straight up g…
gabrielmfern Mar 19, 2024
931c0d4
feat(tailwind): Function to convert css property to React property
gabrielmfern Mar 19, 2024
09fca98
feat(tailwind): Function to sanitize media queries using postcss util…
gabrielmfern Mar 19, 2024
bdba3ec
feat(tailwind): Function to generate JSX inline styles from a postcss…
gabrielmfern Mar 19, 2024
7cc4084
feat(tailwind): Function to sanitize a postcss declaration
gabrielmfern Mar 19, 2024
96fde73
feat(tailwind): Rewrite with the new utilities
gabrielmfern Mar 19, 2024
de39375
chore: Update the unit tests
gabrielmfern Mar 19, 2024
8d207df
fix(tailwind): No Head error being thrown with a Head inside of a com…
gabrielmfern Mar 19, 2024
e9d6e0a
chore: Remove unused utilities
gabrielmfern Mar 19, 2024
780f575
feat(tailwind): Sanitize all digits at the start of classes into the …
gabrielmfern Mar 19, 2024
e61542c
fix(tailwind): Unit test with wrong expected class name
gabrielmfern Mar 19, 2024
bfd3b30
fix(tailwind): Missing keys after head processing
gabrielmfern Mar 19, 2024
51cb876
fix(tailwind): Media rules not being merged when sanitizing
gabrielmfern Mar 19, 2024
37fa1a5
feat(tailwind): A utiltiy for walking and processing all of the eleme…
gabrielmfern Mar 20, 2024
60aff21
feat(tailwind): Component rewrite using the new walkElements utility
gabrielmfern Mar 20, 2024
dd5a6a1
chore(tailwind): Update the unit tests
gabrielmfern Mar 20, 2024
4dcc010
chore(tailwind): Rename the inlineStyles utility to makeInlineStylesFor
gabrielmfern Mar 20, 2024
6cc0760
feat(tailwind): Separate hook for cloning element with inlined styles
gabrielmfern Mar 20, 2024
07227e7
feat(tailwind): INline styles with the useCloneElementWIthInlinedStyl…
gabrielmfern Mar 20, 2024
00c3155
feat(tailwind): Unit test to check if the media queries are found out…
gabrielmfern Mar 20, 2024
28a8db2
fix(tailwind): Wrongly improted Node from postcss
gabrielmfern Mar 20, 2024
d1017e5
chore(tailwind): Remove things that would allow for React hooks to ru…
gabrielmfern Mar 26, 2024
5bd576d
chore(tailwind): Remove polyfills
gabrielmfern Mar 26, 2024
eb64545
feat(tailwind): Integration test for building a Next app using the Ta…
gabrielmfern Feb 23, 2024
f8e3cd1
fix(tailwind): next app build test running infinitely on watch
gabrielmfern Feb 23, 2024
fafeb98
chore(tailwind): Ignore automated-test-next-app
gabrielmfern Feb 23, 2024
a9ff8a4
fix(tailwind): Automated testing Next app not using pnpm to install d…
gabrielmfern Feb 29, 2024
2be9cd4
chore(tailwind): Use pnpm to run build script on testing Next app
gabrielmfern Feb 29, 2024
83a6907
chore(tailwind): Add manual production environment for vite to build …
gabrielmfern Mar 26, 2024
60575f8
chore(tailwind): Update comment for mapReactTree utility
gabrielmfern Mar 27, 2024
b9577da
chore: update lock
gabrielmfern Mar 27, 2024
8750a97
chore(tailwind): Update tests to remove escaping characters on tests
gabrielmfern Mar 27, 2024
5830dd6
chore(tailwind): Move automated-test-next-app into using npm
gabrielmfern Mar 27, 2024
4fc00a0
chore(tailwind): remove unused get-css-for-markup utility
gabrielmfern Mar 27, 2024
89c7345
chore(tailwind): Move automated-test-next-app into integrations/nextjs
gabrielmfern Mar 27, 2024
9ead21c
chore(tailwind): Format
gabrielmfern Mar 27, 2024
5831990
chore(tailwind): Remove unused rulesFor utility
gabrielmfern Mar 27, 2024
1d9e044
chore(tailwind): Update the `ignorePatterns` of the eslint config
gabrielmfern Mar 27, 2024
d7ff7cf
chore(tailwind): Update the styles ordering in the <Button> test
gabrielmfern Mar 29, 2024
615759d
chore(deps): update dependency postcss to v8.4.38 (#1392)
renovate[bot] Apr 1, 2024
db68c92
chore(tailwind): Update the integration README
gabrielmfern Apr 2, 2024
ffbdb61
feat(tailwind): Vite with React integration test
gabrielmfern Apr 2, 2024
2a0513a
chore(tailwind): Attempt removing the yalc files to check if tests st…
gabrielmfern Apr 2, 2024
93ffa8b
fix(tailwind): React.forwardRef components not working (#1335)
nzben Apr 3, 2024
5aae339
Merge branch 'canary' into fix/tailwind-context-issues
gabrielmfern Apr 2, 2024
d81734f
chore(tailwind): Format
gabrielmfern Apr 2, 2024
0fa5ad6
fix(tailwind): Grammar for the missing `<head>` element error
gabrielmfern Apr 2, 2024
fbb3283
fix(tailwind): Escaped characters inside of recently merged test
gabrielmfern Apr 3, 2024
41ae846
chore(tailwind): Format
gabrielmfern Apr 3, 2024
6297e7b
feat(head): iOS formatting meta tag as default (#1369)
endymion1818 Mar 28, 2024
45c69f6
fix(create-email): Tailwind imported from @react-email/tailwind which…
hank619 Mar 29, 2024
a6f4809
chore(deps): update dependency postcss to v8.4.38 (#1392)
renovate[bot] Apr 1, 2024
85a0771
fix(tailwind): React.forwardRef components not working (#1335)
nzben Apr 3, 2024
44427a3
chore(components, create-email, head, tailwind): Bump for canary rele…
gabrielmfern Apr 3, 2024
4d8b119
fix(tailwind): Children always being transformed into an array (#1397)
gabrielmfern Apr 3, 2024
de8597f
chore(components, create-email, react-email, tailwind): Bump for cana…
gabrielmfern Apr 3, 2024
b7ad24b
fix(render): Tests for the web env not using the server.browser versi…
gabrielmfern Apr 4, 2024
74052e2
fix(react-email): Issue with `email dev` when there is a `browserslis…
gabrielmfern Apr 4, 2024
3312f09
feat(react-email): Improved error stack traces for preview server (#1…
gabrielmfern Apr 19, 2024
3e84153
fix(react-email, render): Dependabot security vulnerabilities from de…
gabrielmfern Apr 19, 2024
2c2b070
chore(deps): update dependency vite [security] (#1399)
renovate[bot] Apr 19, 2024
6ff630a
fix(react-email): Missing URLSearchParams on global context for email…
vlaforet Apr 19, 2024
ba50d8d
Merge branch 'canary' into fix/tailwind-context-issues
gabrielmfern Apr 16, 2024
342eff3
fix: artifcats from merging with canary
gabrielmfern Apr 16, 2024
dbd181c
chore: format
gabrielmfern Apr 16, 2024
a173dbd
chore: lint
gabrielmfern Apr 16, 2024
c6658fa
chore: rename wrong describe content of the map-react-tree utilility'…
gabrielmfern Apr 23, 2024
13bf3a4
chore: remove duplicate test for React.forwardRef with Tailwind
gabrielmfern Apr 23, 2024
0e796d8
chore: update locks for integrations
gabrielmfern Apr 23, 2024
4adadfe
Merge branch 'canary' into fix/tailwind-context-issues
gabrielmfern May 2, 2024
72a130e
remove test that should have been deleted during rebase
gabrielmfern Jun 18, 2024
3ad43ba
remove extra forward ref unit test
gabrielmfern Jun 18, 2024
63752ba
update test for mapReactTree to account for the new way of inlining s…
gabrielmfern Sep 18, 2024
86f83da
update so that our implementation of CSS variable resolution is not a…
gabrielmfern Sep 18, 2024
5dbc1bc
update tests
gabrielmfern Sep 18, 2024
f3d2258
update integration tests
gabrielmfern Sep 18, 2024
328f399
update react-email tests using vercel-invite-user template
gabrielmfern Sep 18, 2024
eb7683a
add changeset
gabrielmfern Sep 18, 2024
6250043
remove some unused dependencies
gabrielmfern Sep 18, 2024
7d40a27
update snapshots
gabrielmfern Sep 18, 2024
080c1a9
make class manipulation test more thorough
gabrielmfern Sep 18, 2024
2336757
fix style manipulation
gabrielmfern Sep 18, 2024
4b9aebd
fix Button test and one other snapshot
gabrielmfern Sep 18, 2024
dbbbb02
update CLI snapshots
gabrielmfern Sep 18, 2024
62b0069
format
gabrielmfern Sep 18, 2024
ff69f3c
fix rebase artifacts
gabrielmfern Sep 18, 2024
d3e644d
format
gabrielmfern Sep 18, 2024
3ecc61b
sort web's package json
gabrielmfern Oct 1, 2024
f32468d
Use proper casing for emailHtml variable
gabrielmfern Oct 2, 2024
ba2ff94
Remove unecessary comment
gabrielmfern Oct 2, 2024
560c789
use node instead of element
gabrielmfern Oct 2, 2024
bd8f344
remove unecessary comment for makeInlineStylesFor
gabrielmfern Oct 2, 2024
b628b75
add as const for better type-safety
gabrielmfern Oct 2, 2024
1e54278
Update packages/tailwind/src/utils/compatibility/convert-css-property…
gabrielmfern Oct 2, 2024
d70bca3
Update packages/tailwind/src/utils/compatibility/convert-css-property…
gabrielmfern Oct 2, 2024
4bb0907
remove cssFloat property usage to match previous changes
gabrielmfern Oct 2, 2024
db5ec7b
use a type-definition instead of casting
gabrielmfern Oct 2, 2024
1a4aa50
rename useTailwind to setupTailwind
gabrielmfern Oct 2, 2024
101f23a
remove unused useSuspendedPromise
gabrielmfern Oct 2, 2024
47621af
format
gabrielmfern Oct 2, 2024
8e42a37
Revert "remove unused useSuspendedPromise"
gabrielmfern Oct 2, 2024
2c3db71
update tailwind
gabrielmfern Oct 2, 2024
a4aaf89
call extra functions that newer Tailwind versions call
gabrielmfern Oct 2, 2024
a9cf355
properly await and suspend for the one async step that Tailwind takes…
gabrielmfern Oct 3, 2024
4fe351e
Revert "update tailwind"
gabrielmfern Oct 3, 2024
5cb18c5
update snapshots due to suspense
gabrielmfern Oct 3, 2024
34d3185
update locks
gabrielmfern Oct 3, 2024
b029406
format
gabrielmfern Oct 3, 2024
99d26b7
make the whole code sync again and remove suspension
gabrielmfern Oct 4, 2024
bdcf3ef
remove unecesssary uuid
gabrielmfern Oct 4, 2024
856575f
fix build issues
gabrielmfern Oct 4, 2024
650dae4
update snapshots
gabrielmfern Oct 4, 2024
939576b
format
gabrielmfern Oct 4, 2024
7709146
fix mapReactTree test
gabrielmfern Oct 4, 2024
500a9af
improve name for rgb parser regex
gabrielmfern Oct 4, 2024
47b3d56
remove unecessary comment
gabrielmfern Oct 4, 2024
9444227
remove unecessary linting rule disable
gabrielmfern Oct 4, 2024
4402797
sort dependencies
gabrielmfern Oct 4, 2024
2c9203c
move rgb parser regex to be recreated each time
gabrielmfern Oct 4, 2024
6b1ee48
fix linting
gabrielmfern Oct 4, 2024
f33e946
remove previous version's tailwind patch
gabrielmfern Oct 4, 2024
1a0d92b
update lock
gabrielmfern Oct 4, 2024
6219b1b
format
gabrielmfern Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(tailwind): Rewrite with the new utilities
  • Loading branch information
gabrielmfern committed Mar 29, 2024
commit 96fde73087dbfd078864725dc49cce1e03a9622d
156 changes: 112 additions & 44 deletions packages/tailwind/src/tailwind.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import * as React from "react";
import type { Config as TailwindOriginalConfig } from "tailwindcss";
import { useTailwindStyles } from "./hooks/use-tailwind-styles";
import { useStyleInlining } from "./hooks/use-style-inlining";
import { Root } from "postcss";
import { inlineStyles } from "./utils/css/inline-styles";
import { sanitizeClassName } from "./utils/compatibility/sanitize-class-name";
import { minifyCss } from "./utils/css/minify-css";
import { useTailwind } from "./hooks/use-tailwind";
import { sanitizeMediaQueries } from "./utils/css/media-queries/sanitize-media-queries";
import { sanitizeDeclarations } from "./utils/css/sanitize-declarations";

export type TailwindConfig = Omit<TailwindOriginalConfig, "content">;

Expand All @@ -19,41 +22,17 @@ interface EmailElementProps {
}

export const Tailwind: React.FC<TailwindProps> = ({ children, config }) => {
const { stylePerClassMap, nonInlinableClasses, sanitizedMediaQueries } =
useTailwindStyles(children, config ?? {});
const tailwind = useTailwind(config ?? {});

const inline = useStyleInlining(stylePerClassMap);

const nonInlineStylesToApply = sanitizedMediaQueries.filter(
(style) => style.trim().length > 0,
);

const hasNonInlineStylesToApply = nonInlineStylesToApply.length > 0;
let hasAppliedNonInlineStyles = false as boolean;
const nonInlineStylesRootToApply = new Root();
const mediaQueryClassesForAllElement: string[] = [];
let hasNonInlineStylesToApply = false as boolean;
gabrielmfern marked this conversation as resolved.
Show resolved Hide resolved

function processElement(
element: React.ReactElement<EmailElementProps>,
): React.ReactElement<EmailElementProps> {
const propsToOverwrite = {} as Partial<EmailElementProps>;

if (!hasAppliedNonInlineStyles && hasNonInlineStylesToApply) {
if (element.type === "head") {
hasAppliedNonInlineStyles = true;

/* only minify here since it is the only place that is going to be in the DOM */
const styleElement = (
<style>{minifyCss(nonInlineStylesToApply.join(""))}</style>
);

return React.cloneElement(
element,
element.props,
element.props.children,
styleElement,
);
}
}

if (element.props.children) {
propsToOverwrite.children = React.Children.map(
element.props.children,
Expand All @@ -68,20 +47,39 @@ export const Tailwind: React.FC<TailwindProps> = ({ children, config }) => {
}

if (element.props.className) {
const { styles, residualClassName } = inline(element.props.className);
const rootForClasses = tailwind.generateRootForClasses(
element.props.className.split(" "),
);
sanitizeDeclarations(rootForClasses);

const { sanitizedAtRules, mediaQueryClasses } =
sanitizeMediaQueries(rootForClasses);
mediaQueryClassesForAllElement.push(...mediaQueryClasses);
nonInlineStylesRootToApply.append(...sanitizedAtRules);

if (mediaQueryClasses.length > 0 && !hasNonInlineStylesToApply) {
hasNonInlineStylesToApply = true;
}

const { styles, residualClassName } = inlineStyles(
element.props.className,
rootForClasses,
);
propsToOverwrite.style = {
...element.props.style,
...styles,
};

if (residualClassName.trim().length > 0) {
propsToOverwrite.className = residualClassName;

/*
We sanitize only the class names of Tailwind classes that we are not going to inline
to avoid unpredictable behavior on the user's code. If we did sanitize all class names
to avoid unpredictable behavior on the user's code. If we did sanitize all classes
a user-defined class could end up also being sanitized which would lead to unexpected
behavior and bugs that are hard to track.
*/
for (const singleClass of nonInlinableClasses) {
for (const singleClass of mediaQueryClasses) {
propsToOverwrite.className = propsToOverwrite.className.replace(
singleClass,
sanitizeClassName(singleClass),
Expand Down Expand Up @@ -119,7 +117,7 @@ export const Tailwind: React.FC<TailwindProps> = ({ children, config }) => {
return React.cloneElement(element, newProps, newChildren);
}

const childrenArray =
let childrenArray =
React.Children.map(children, (child) => {
if (React.isValidElement<EmailElementProps>(child)) {
const element = child;
Expand All @@ -130,18 +128,88 @@ export const Tailwind: React.FC<TailwindProps> = ({ children, config }) => {
return child;
}) ?? [];

if (hasNonInlineStylesToApply && !hasAppliedNonInlineStyles) {
throw new Error(
`You are trying to use the following Tailwind classes that have media queries: ${nonInlinableClasses.join(
" ",
)}.
For the media queries to work properly on rendering, they need to be added into a <style> tag inside of a <head> tag,
the Tailwind component tried finding a <head> element but just wasn't able to find it.
let hasAppliedNonInlineStyles = false as boolean;

if (hasNonInlineStylesToApply) {
const processElementToApplyNonInlineStyles = (
element: React.ReactElement<EmailElementProps>,
): React.ReactElement<EmailElementProps> => {
if (!hasAppliedNonInlineStyles && hasNonInlineStylesToApply) {
let newChildren = element.props.children;
// if it has children and is not the head element,
// then it goes through all the children trying to process them
// to find the <head> element
if (element.props.children && element.type !== 'head') {
newChildren = React.Children.map(element.props.children, (child) => {
if (React.isValidElement<EmailElementProps>(child)) {
return processElementToApplyNonInlineStyles(child);
}

return child;
});
}

if (typeof element.type === "function") {
const OriginalComponent = element.type as React.FC;

Make sure that you have either a <head> element at some point inside of the <Tailwind> component at any depth.
return React.createElement(
(props: EmailElementProps) => {
const renderedComponent = OriginalComponent(props);
if (React.isValidElement<EmailElementProps>(renderedComponent)) {
return processElementToApplyNonInlineStyles(renderedComponent);
}

If you do already have a <head> element at some depth, please file a bug https://github.com/resend/react-email/issues/new?assignees=&labels=Type%3A+Bug&projects=&template=1.bug_report.yml.`,
);
return renderedComponent;
},
element.props,
newChildren,
);
}

if (element.type === "head") {
hasAppliedNonInlineStyles = true;

/* only minify here since it is the only place that is going to be in the DOM */
const styleElement = (
<style>
{minifyCss(nonInlineStylesRootToApply.toString().trim())}
</style>
);

return React.cloneElement(
element,
element.props,
newChildren,
styleElement,
);
}

return React.cloneElement(element, element.props, newChildren);
}

return element;
};
childrenArray = childrenArray.map((child) => {
if (React.isValidElement<EmailElementProps>(child)) {
return processElementToApplyNonInlineStyles(child);
}

return child;
});

if (!hasAppliedNonInlineStyles) {
throw new Error(
`You are trying to use the following Tailwind classes that have media queries: ${mediaQueryClassesForAllElement.join(
" ",
)}.
For the media queries to work properly on rendering, they need to be added into a <style> tag inside of a <head> tag,
the Tailwind component tried finding a <head> element but just wasn't able to find it.

Make sure that you have either a <head> element at some point inside of the <Tailwind> component at any depth.

If you do already have a <head> element at some depth, please file a bug https://github.com/resend/react-email/issues/new?assignees=&labels=Type%3A+Bug&projects=&template=1.bug_report.yml.`,
);
}
}

return <>{childrenArray}</>;
Expand Down