Last active
September 12, 2024 11:12
-
-
Save balazsorban44/41c2fe0a75b34171967ae7e6e75054ac to your computer and use it in GitHub Desktop.
See https://x.com/balazsorban44/status/1833987377544434024 for context
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from 'node:fs'; | |
import path from 'node:path'; | |
import { renderToStaticMarkup } from 'react-dom/server'; | |
import { convert, type HtmlToTextOptions } from 'html-to-text'; | |
const emailFiles = fs.readdirSync(path.resolve(__dirname, '../src/emails')); | |
interface EmailTemplateConfig { | |
default: React.ReactElement; | |
props: Record<string, string>; | |
subject: string; | |
} | |
const emailTemplates: Record<string, EmailTemplateConfig & { file: string }> = | |
{}; | |
for (const file of emailFiles) { | |
if (file.endsWith('.tsx')) { | |
const emailModule = require(`../src/emails/${file}`) as EmailTemplateConfig; | |
const fileName = file.replace('.tsx', ''); | |
const emailName = fileName | |
// eslint-disable-next-line prefer-named-capture-group | |
.replace(/-([a-z])/g, (_, letter) => (letter as string).toUpperCase()); | |
emailTemplates[emailName] = { | |
file: fileName, | |
default: emailModule.default, | |
props: emailModule.props, | |
subject: emailModule.subject, | |
}; | |
} | |
} | |
const htmlToTextOptions: HtmlToTextOptions = { | |
formatters: { | |
link(elem, _, builder) { | |
const href = (elem.attribs as Record<string, string>).href; | |
const content = elem.children[0].data!; | |
builder.addInline(href === content ? href : `${content} ${href}`); | |
}, | |
}, | |
selectors: [ | |
{ selector: 'a', format: 'link' }, | |
{ selector: 'img', format: 'skip' }, | |
{ selector: 'h1', options: { uppercase: false } }, | |
], | |
}; | |
const imports = [ | |
"import type { EmailContent } from './send-email';", | |
"import escapeHtml from 'escape-html';", | |
]; | |
const content = []; | |
for (const [key, template] of Object.entries(emailTemplates)) { | |
const { file, props, subject } = template; | |
const Element = template.default; | |
const html = renderToStaticMarkup(<Element />); | |
const escapedHtml = html.replace(/\$\{(\w*)\}/g, '${escapeHtml($1)}'); | |
const text = convert(html, htmlToTextOptions); | |
const typeName = `${key.charAt(0).toUpperCase() + key.slice(1)}Props`; | |
imports.push( | |
`import type { TemplateProps as ${typeName} } from '../emails/${file}';`, | |
); | |
content.push(` | |
export function ${key}Content(params: ${typeName}): EmailContent { | |
const { ${Object.keys(props).join(', ')} } = params; | |
return { | |
subject: \`${subject}\`, | |
body: { | |
html: \`${escapedHtml}\`, | |
text: \`${text}\`, | |
}, | |
}; | |
}`); | |
} | |
fs.writeFileSync( | |
path.resolve(__dirname, '../src/utils/emails.ts'), | |
`\ | |
// Generated by \`pnpm build:email\` | |
${imports.join('\n')} | |
${content.join('\n')}`, | |
'utf8', | |
); | |
console.log('Email templates built successfully.'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Body, | |
Container, | |
Head, | |
Html, | |
Section, | |
Text, | |
Tailwind, | |
} from '@react-email/components'; | |
export interface TemplateProps { | |
name: string; | |
} | |
export const props: TemplateProps = { | |
name: '${name}', | |
}; | |
export const subject = `Hi, ${props.name}`; | |
export default function Template(): React.JSX.Element { | |
return ( | |
<Html> | |
<Tailwind> | |
<Head /> | |
<Body> | |
<Container> | |
<Section> | |
<Text>{props.name}</Text> | |
</Section> | |
</Container> | |
</Body> | |
</Tailwind> | |
</Html> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment