react-email 1.1.1 → 1.2.0

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.
@@ -20,7 +20,7 @@ exports.components = [
20
20
  },
21
21
  {
22
22
  title: 'layout.tsx',
23
- content: "import * as React from 'react';\nimport { Topbar } from './topbar';\nimport { Sidebar } from './sidebar';\n\ntype LayoutElement = React.ElementRef<'div'>;\ntype RootProps = React.ComponentPropsWithoutRef<'div'>;\n\ninterface LayoutProps extends RootProps {\n navItems: string[];\n viewMode?: string;\n setViewMode?: (viewMode: string) => void;\n}\n\nexport const Layout = React.forwardRef<LayoutElement, Readonly<LayoutProps>>(\n (\n { className, title, navItems, children, viewMode, setViewMode, ...props },\n forwardedRef,\n ) => {\n return (\n <>\n <div className=\"flex justify-between h-screen\">\n <Sidebar navItems={navItems} />\n <main className=\"w-full bg-slate-2\">\n {title && (\n <Topbar\n title={title}\n viewMode={viewMode}\n setViewMode={setViewMode}\n />\n )}\n <div className=\"relative h-[calc(100vh_-_70px)] overflow-auto\">\n <div className=\"mx-auto\">{children}</div>\n </div>\n </main>\n </div>\n </>\n );\n },\n);\n\nLayout.displayName = 'Layout';\n",
23
+ content: "import * as React from 'react';\nimport { Topbar } from './topbar';\nimport { Sidebar } from './sidebar';\n\ntype LayoutElement = React.ElementRef<'div'>;\ntype RootProps = React.ComponentPropsWithoutRef<'div'>;\n\ninterface LayoutProps extends RootProps {\n navItems: string[];\n viewMode?: string;\n setViewMode?: (viewMode: string) => void;\n}\n\nexport const Layout = React.forwardRef<LayoutElement, Readonly<LayoutProps>>(\n (\n { className, title, navItems, children, viewMode, setViewMode, ...props },\n forwardedRef,\n ) => {\n return (\n <>\n <div className=\"flex justify-between h-screen\">\n <Sidebar navItems={navItems} />\n <main className=\"w-[calc(100%_-_275px)] bg-slate-2\">\n {title && (\n <Topbar\n title={title}\n viewMode={viewMode}\n setViewMode={setViewMode}\n />\n )}\n <div className=\"relative h-[calc(100vh_-_70px)] overflow-auto\">\n <div className=\"mx-auto\">{children}</div>\n </div>\n </main>\n </div>\n </>\n );\n },\n);\n\nLayout.displayName = 'Layout';\n",
24
24
  },
25
25
  {
26
26
  title: 'logo.tsx',
@@ -12,11 +12,11 @@ exports.pages = [
12
12
  },
13
13
  {
14
14
  title: 'index.tsx',
15
- content: "import { promises as fs } from 'fs';\nimport path from 'path';\nimport { Button, Heading, Text } from '../components';\nimport { Layout } from '../components/layout';\nimport Link from 'next/link';\n\ninterface HomeProps {}\n\nexport const CONTENT_DIR = 'emails';\n\nconst getEmails = async () => {\n const emailsDirectory = path.join(process.cwd(), CONTENT_DIR);\n const filenames = await fs.readdir(emailsDirectory);\n const emails = filenames.map((file) => file.replace('.tsx', ''));\n\n return emails;\n};\n\nexport async function getStaticProps({ params }) {\n try {\n const emails = await getEmails();\n return emails\n ? { props: { navItems: emails } }\n : { props: { navItems: [] } };\n } catch (error) {\n console.error(error);\n return { props: { navItems: [] } };\n }\n}\n\nconst Home: React.FC<Readonly<HomeProps>> = ({ navItems }: any) => {\n return (\n <Layout navItems={navItems}>\n <div className=\"max-w-md border border-slate-6 mx-auto mt-56 rounded-md p-8\">\n <Heading as=\"h2\" weight=\"medium\">\n Welcome to the React Email preview!\n </Heading>\n <Text as=\"p\" className=\"mt-2 mb-4\">\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia dolorum\n vitae rem laudantium eos similique nobis facilis, quaerat excepturi\n ratione asperiores iste suscipit perspiciatis quam minima delectus,\n eveniet quas quo!\n </Text>\n\n <Button asChild>\n <Link href=\"https://react.email/docs\">Check the docs</Link>\n </Button>\n </div>\n </Layout>\n );\n};\n\nexport default Home;\n",
15
+ content: "import { promises as fs } from 'fs';\nimport path from 'path';\nimport { Button, Heading, Text } from '../components';\nimport { Layout } from '../components/layout';\nimport Link from 'next/link';\n\ninterface HomeProps {}\n\nexport const CONTENT_DIR = 'emails';\n\nconst getEmails = async () => {\n const emailsDirectory = path.join(process.cwd(), CONTENT_DIR);\n const filenames = await fs.readdir(emailsDirectory);\n const emails = filenames.map((file) => file.replace('.tsx', ''));\n\n return emails;\n};\n\nexport async function getStaticProps({ params }) {\n try {\n const emails = await getEmails();\n return emails\n ? { props: { navItems: emails } }\n : { props: { navItems: [] } };\n } catch (error) {\n console.error(error);\n return { props: { navItems: [] } };\n }\n}\n\nconst Home: React.FC<Readonly<HomeProps>> = ({ navItems }: any) => {\n return (\n <Layout navItems={navItems}>\n <div className=\"max-w-md border border-slate-6 mx-auto mt-56 rounded-md p-8\">\n <Heading as=\"h2\" weight=\"medium\">\n Welcome to the React Email preview!\n </Heading>\n <Text as=\"p\" className=\"mt-2 mb-4\">\n To start developing your next email template, you can create a{' '}\n <code>.jsx</code> or <code>.tsx</code> file under the \"emails\" folder.\n </Text>\n\n <Button asChild>\n <Link href=\"https://react.email/docs\">Check the docs</Link>\n </Button>\n </div>\n </Layout>\n );\n};\n\nexport default Home;\n",
16
16
  },
17
17
  {
18
18
  dir: 'preview',
19
19
  title: '[slug].tsx',
20
- content: "import { promises as fs } from 'fs';\nimport path from 'path';\nimport { render } from '@react-email/render';\nimport { GetStaticPaths } from 'next';\nimport { Layout } from '../../components/layout';\nimport * as React from 'react';\nimport { Code } from '../../components';\nimport Head from 'next/head';\n\ninterface PreviewProps {}\n\nexport const CONTENT_DIR = 'emails';\n\nconst getEmails = async () => {\n const emailsDirectory = path.join(process.cwd(), CONTENT_DIR);\n const filenames = await fs.readdir(emailsDirectory);\n const emails = filenames.map((file) => file.replace('.tsx', ''));\n\n return emails;\n};\n\nexport const getStaticPaths: GetStaticPaths = async () => {\n const emails = await getEmails();\n\n const paths = emails.map((email) => {\n return { params: { slug: email } };\n });\n return { paths, fallback: true };\n};\n\nexport async function getStaticProps({ params }) {\n try {\n const emails = await getEmails();\n const Email = (await import(`../../../emails/${params.slug}`)).default;\n const markup = render(<Email />, { pretty: true });\n\n return emails\n ? { props: { navItems: emails, slug: params.slug, markup } }\n : { notFound: true };\n } catch (error) {\n console.error(error);\n return { notFound: true };\n }\n}\n\nconst Preview: React.FC<Readonly<PreviewProps>> = ({\n navItems,\n markup,\n slug,\n}: any) => {\n const [viewMode, setViewMode] = React.useState('desktop');\n const title = `${slug} — React Email`;\n\n return (\n <Layout\n navItems={navItems}\n title={slug}\n viewMode={viewMode}\n setViewMode={setViewMode}\n >\n <Head>\n <title>{title}</title>\n </Head>\n {viewMode === 'desktop' ? (\n <iframe\n srcDoc={markup}\n frameBorder=\"0\"\n className=\"w-full h-[calc(100vh_-_70px)]\"\n />\n ) : (\n <div className=\"max-w-[864px] mx-auto py-10\">\n <Code>{markup}</Code>\n </div>\n )}\n </Layout>\n );\n};\n\nexport default Preview;\n",
20
+ content: "import { promises as fs } from 'fs';\nimport path from 'path';\nimport { render } from '@react-email/render';\nimport { GetStaticPaths } from 'next';\nimport { Layout } from '../../components/layout';\nimport * as React from 'react';\nimport { Code } from '../../components';\nimport Head from 'next/head';\n\ninterface PreviewProps {}\n\nexport const CONTENT_DIR = 'emails';\n\nconst getEmails = async () => {\n const emailsDirectory = path.join(process.cwd(), CONTENT_DIR);\n const filenames = await fs.readdir(emailsDirectory);\n const emails = filenames.map((file) => file.replace('.tsx', ''));\n\n return emails;\n};\n\nexport const getStaticPaths: GetStaticPaths = async () => {\n const emails = await getEmails();\n\n const paths = emails.map((email) => {\n return { params: { slug: email } };\n });\n return { paths, fallback: true };\n};\n\nexport async function getStaticProps({ params }) {\n try {\n const emails = await getEmails();\n const Email = (await import(`../../../emails/${params.slug}`)).default;\n const markup = render(<Email />, { pretty: true });\n\n const path = `${process.cwd()}/${CONTENT_DIR}/${params.slug}`;\n const reactMarkup = await fs.readFile(`${path}.tsx`, {\n encoding: 'utf-8',\n });\n\n return emails\n ? { props: { navItems: emails, slug: params.slug, markup, reactMarkup } }\n : { notFound: true };\n } catch (error) {\n console.error(error);\n return { notFound: true };\n }\n}\n\nconst Preview: React.FC<Readonly<PreviewProps>> = ({\n navItems,\n markup,\n reactMarkup,\n slug,\n}: any) => {\n const [viewMode, setViewMode] = React.useState('desktop');\n const title = `${slug} — React Email`;\n\n return (\n <Layout\n navItems={navItems}\n title={slug}\n viewMode={viewMode}\n setViewMode={setViewMode}\n >\n <Head>\n <title>{title}</title>\n </Head>\n {viewMode === 'desktop' ? (\n <iframe\n srcDoc={markup}\n frameBorder=\"0\"\n className=\"w-full h-[calc(100vh_-_70px)]\"\n />\n ) : (\n <div className=\"flex gap-6 mx-auto p-6\">\n <Code>{reactMarkup}</Code>\n <Code>{markup}</Code>\n </div>\n )}\n </Layout>\n );\n};\n\nexport default Preview;\n",
21
21
  },
22
22
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-email",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "A live preview of your emails right in your browser.",
5
5
  "bin": {
6
6
  "email": "./dist/index.js"
@@ -12,6 +12,7 @@
12
12
  "scripts": {
13
13
  "build": "tsc",
14
14
  "prepare": "npm run build",
15
+ "preprepare": "ts-node ./scripts/prepare-preview",
15
16
  "format:check": "prettier --check \"**/*.{js,ts,tsx,json}\"",
16
17
  "format": "prettier --write \"**/*.{js,ts,tsx,json}\"",
17
18
  "lint": "eslint",
@@ -20,7 +20,7 @@ export const Layout = React.forwardRef<LayoutElement, Readonly<LayoutProps>>(
20
20
  <>
21
21
  <div className="flex justify-between h-screen">
22
22
  <Sidebar navItems={navItems} />
23
- <main className="w-full bg-slate-2">
23
+ <main className="w-[calc(100%_-_275px)] bg-slate-2">
24
24
  {title && (
25
25
  <Topbar
26
26
  title={title}
@@ -34,8 +34,13 @@ export async function getStaticProps({ params }) {
34
34
  const Email = (await import(`../../../emails/${params.slug}`)).default;
35
35
  const markup = render(<Email />, { pretty: true });
36
36
 
37
+ const path = `${process.cwd()}/${CONTENT_DIR}/${params.slug}`;
38
+ const reactMarkup = await fs.readFile(`${path}.tsx`, {
39
+ encoding: 'utf-8',
40
+ });
41
+
37
42
  return emails
38
- ? { props: { navItems: emails, slug: params.slug, markup } }
43
+ ? { props: { navItems: emails, slug: params.slug, markup, reactMarkup } }
39
44
  : { notFound: true };
40
45
  } catch (error) {
41
46
  console.error(error);
@@ -46,6 +51,7 @@ export async function getStaticProps({ params }) {
46
51
  const Preview: React.FC<Readonly<PreviewProps>> = ({
47
52
  navItems,
48
53
  markup,
54
+ reactMarkup,
49
55
  slug,
50
56
  }: any) => {
51
57
  const [viewMode, setViewMode] = React.useState('desktop');
@@ -68,7 +74,8 @@ const Preview: React.FC<Readonly<PreviewProps>> = ({
68
74
  className="w-full h-[calc(100vh_-_70px)]"
69
75
  />
70
76
  ) : (
71
- <div className="max-w-[864px] mx-auto py-10">
77
+ <div className="flex gap-6 mx-auto p-6">
78
+ <Code>{reactMarkup}</Code>
72
79
  <Code>{markup}</Code>
73
80
  </div>
74
81
  )}
@@ -22,7 +22,7 @@ export const components = [
22
22
  {
23
23
  title: 'layout.tsx',
24
24
  content:
25
- "import * as React from 'react';\nimport { Topbar } from './topbar';\nimport { Sidebar } from './sidebar';\n\ntype LayoutElement = React.ElementRef<'div'>;\ntype RootProps = React.ComponentPropsWithoutRef<'div'>;\n\ninterface LayoutProps extends RootProps {\n navItems: string[];\n viewMode?: string;\n setViewMode?: (viewMode: string) => void;\n}\n\nexport const Layout = React.forwardRef<LayoutElement, Readonly<LayoutProps>>(\n (\n { className, title, navItems, children, viewMode, setViewMode, ...props },\n forwardedRef,\n ) => {\n return (\n <>\n <div className=\"flex justify-between h-screen\">\n <Sidebar navItems={navItems} />\n <main className=\"w-full bg-slate-2\">\n {title && (\n <Topbar\n title={title}\n viewMode={viewMode}\n setViewMode={setViewMode}\n />\n )}\n <div className=\"relative h-[calc(100vh_-_70px)] overflow-auto\">\n <div className=\"mx-auto\">{children}</div>\n </div>\n </main>\n </div>\n </>\n );\n },\n);\n\nLayout.displayName = 'Layout';\n",
25
+ "import * as React from 'react';\nimport { Topbar } from './topbar';\nimport { Sidebar } from './sidebar';\n\ntype LayoutElement = React.ElementRef<'div'>;\ntype RootProps = React.ComponentPropsWithoutRef<'div'>;\n\ninterface LayoutProps extends RootProps {\n navItems: string[];\n viewMode?: string;\n setViewMode?: (viewMode: string) => void;\n}\n\nexport const Layout = React.forwardRef<LayoutElement, Readonly<LayoutProps>>(\n (\n { className, title, navItems, children, viewMode, setViewMode, ...props },\n forwardedRef,\n ) => {\n return (\n <>\n <div className=\"flex justify-between h-screen\">\n <Sidebar navItems={navItems} />\n <main className=\"w-[calc(100%_-_275px)] bg-slate-2\">\n {title && (\n <Topbar\n title={title}\n viewMode={viewMode}\n setViewMode={setViewMode}\n />\n )}\n <div className=\"relative h-[calc(100vh_-_70px)] overflow-auto\">\n <div className=\"mx-auto\">{children}</div>\n </div>\n </main>\n </div>\n </>\n );\n },\n);\n\nLayout.displayName = 'Layout';\n",
26
26
  },
27
27
  {
28
28
  title: 'logo.tsx',
@@ -12,12 +12,12 @@ export const pages = [
12
12
  {
13
13
  title: 'index.tsx',
14
14
  content:
15
- "import { promises as fs } from 'fs';\nimport path from 'path';\nimport { Button, Heading, Text } from '../components';\nimport { Layout } from '../components/layout';\nimport Link from 'next/link';\n\ninterface HomeProps {}\n\nexport const CONTENT_DIR = 'emails';\n\nconst getEmails = async () => {\n const emailsDirectory = path.join(process.cwd(), CONTENT_DIR);\n const filenames = await fs.readdir(emailsDirectory);\n const emails = filenames.map((file) => file.replace('.tsx', ''));\n\n return emails;\n};\n\nexport async function getStaticProps({ params }) {\n try {\n const emails = await getEmails();\n return emails\n ? { props: { navItems: emails } }\n : { props: { navItems: [] } };\n } catch (error) {\n console.error(error);\n return { props: { navItems: [] } };\n }\n}\n\nconst Home: React.FC<Readonly<HomeProps>> = ({ navItems }: any) => {\n return (\n <Layout navItems={navItems}>\n <div className=\"max-w-md border border-slate-6 mx-auto mt-56 rounded-md p-8\">\n <Heading as=\"h2\" weight=\"medium\">\n Welcome to the React Email preview!\n </Heading>\n <Text as=\"p\" className=\"mt-2 mb-4\">\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia dolorum\n vitae rem laudantium eos similique nobis facilis, quaerat excepturi\n ratione asperiores iste suscipit perspiciatis quam minima delectus,\n eveniet quas quo!\n </Text>\n\n <Button asChild>\n <Link href=\"https://react.email/docs\">Check the docs</Link>\n </Button>\n </div>\n </Layout>\n );\n};\n\nexport default Home;\n",
15
+ "import { promises as fs } from 'fs';\nimport path from 'path';\nimport { Button, Heading, Text } from '../components';\nimport { Layout } from '../components/layout';\nimport Link from 'next/link';\n\ninterface HomeProps {}\n\nexport const CONTENT_DIR = 'emails';\n\nconst getEmails = async () => {\n const emailsDirectory = path.join(process.cwd(), CONTENT_DIR);\n const filenames = await fs.readdir(emailsDirectory);\n const emails = filenames.map((file) => file.replace('.tsx', ''));\n\n return emails;\n};\n\nexport async function getStaticProps({ params }) {\n try {\n const emails = await getEmails();\n return emails\n ? { props: { navItems: emails } }\n : { props: { navItems: [] } };\n } catch (error) {\n console.error(error);\n return { props: { navItems: [] } };\n }\n}\n\nconst Home: React.FC<Readonly<HomeProps>> = ({ navItems }: any) => {\n return (\n <Layout navItems={navItems}>\n <div className=\"max-w-md border border-slate-6 mx-auto mt-56 rounded-md p-8\">\n <Heading as=\"h2\" weight=\"medium\">\n Welcome to the React Email preview!\n </Heading>\n <Text as=\"p\" className=\"mt-2 mb-4\">\n To start developing your next email template, you can create a{' '}\n <code>.jsx</code> or <code>.tsx</code> file under the \"emails\" folder.\n </Text>\n\n <Button asChild>\n <Link href=\"https://react.email/docs\">Check the docs</Link>\n </Button>\n </div>\n </Layout>\n );\n};\n\nexport default Home;\n",
16
16
  },
17
17
  {
18
18
  dir: 'preview',
19
19
  title: '[slug].tsx',
20
20
  content:
21
- "import { promises as fs } from 'fs';\nimport path from 'path';\nimport { render } from '@react-email/render';\nimport { GetStaticPaths } from 'next';\nimport { Layout } from '../../components/layout';\nimport * as React from 'react';\nimport { Code } from '../../components';\nimport Head from 'next/head';\n\ninterface PreviewProps {}\n\nexport const CONTENT_DIR = 'emails';\n\nconst getEmails = async () => {\n const emailsDirectory = path.join(process.cwd(), CONTENT_DIR);\n const filenames = await fs.readdir(emailsDirectory);\n const emails = filenames.map((file) => file.replace('.tsx', ''));\n\n return emails;\n};\n\nexport const getStaticPaths: GetStaticPaths = async () => {\n const emails = await getEmails();\n\n const paths = emails.map((email) => {\n return { params: { slug: email } };\n });\n return { paths, fallback: true };\n};\n\nexport async function getStaticProps({ params }) {\n try {\n const emails = await getEmails();\n const Email = (await import(`../../../emails/${params.slug}`)).default;\n const markup = render(<Email />, { pretty: true });\n\n return emails\n ? { props: { navItems: emails, slug: params.slug, markup } }\n : { notFound: true };\n } catch (error) {\n console.error(error);\n return { notFound: true };\n }\n}\n\nconst Preview: React.FC<Readonly<PreviewProps>> = ({\n navItems,\n markup,\n slug,\n}: any) => {\n const [viewMode, setViewMode] = React.useState('desktop');\n const title = `${slug} — React Email`;\n\n return (\n <Layout\n navItems={navItems}\n title={slug}\n viewMode={viewMode}\n setViewMode={setViewMode}\n >\n <Head>\n <title>{title}</title>\n </Head>\n {viewMode === 'desktop' ? (\n <iframe\n srcDoc={markup}\n frameBorder=\"0\"\n className=\"w-full h-[calc(100vh_-_70px)]\"\n />\n ) : (\n <div className=\"max-w-[864px] mx-auto py-10\">\n <Code>{markup}</Code>\n </div>\n )}\n </Layout>\n );\n};\n\nexport default Preview;\n",
21
+ "import { promises as fs } from 'fs';\nimport path from 'path';\nimport { render } from '@react-email/render';\nimport { GetStaticPaths } from 'next';\nimport { Layout } from '../../components/layout';\nimport * as React from 'react';\nimport { Code } from '../../components';\nimport Head from 'next/head';\n\ninterface PreviewProps {}\n\nexport const CONTENT_DIR = 'emails';\n\nconst getEmails = async () => {\n const emailsDirectory = path.join(process.cwd(), CONTENT_DIR);\n const filenames = await fs.readdir(emailsDirectory);\n const emails = filenames.map((file) => file.replace('.tsx', ''));\n\n return emails;\n};\n\nexport const getStaticPaths: GetStaticPaths = async () => {\n const emails = await getEmails();\n\n const paths = emails.map((email) => {\n return { params: { slug: email } };\n });\n return { paths, fallback: true };\n};\n\nexport async function getStaticProps({ params }) {\n try {\n const emails = await getEmails();\n const Email = (await import(`../../../emails/${params.slug}`)).default;\n const markup = render(<Email />, { pretty: true });\n\n const path = `${process.cwd()}/${CONTENT_DIR}/${params.slug}`;\n const reactMarkup = await fs.readFile(`${path}.tsx`, {\n encoding: 'utf-8',\n });\n\n return emails\n ? { props: { navItems: emails, slug: params.slug, markup, reactMarkup } }\n : { notFound: true };\n } catch (error) {\n console.error(error);\n return { notFound: true };\n }\n}\n\nconst Preview: React.FC<Readonly<PreviewProps>> = ({\n navItems,\n markup,\n reactMarkup,\n slug,\n}: any) => {\n const [viewMode, setViewMode] = React.useState('desktop');\n const title = `${slug} — React Email`;\n\n return (\n <Layout\n navItems={navItems}\n title={slug}\n viewMode={viewMode}\n setViewMode={setViewMode}\n >\n <Head>\n <title>{title}</title>\n </Head>\n {viewMode === 'desktop' ? (\n <iframe\n srcDoc={markup}\n frameBorder=\"0\"\n className=\"w-full h-[calc(100vh_-_70px)]\"\n />\n ) : (\n <div className=\"flex gap-6 mx-auto p-6\">\n <Code>{reactMarkup}</Code>\n <Code>{markup}</Code>\n </div>\n )}\n </Layout>\n );\n};\n\nexport default Preview;\n",
22
22
  },
23
23
  ];