diff --git a/.eslintignore b/.eslintignore index 7b8a9039..dff42c6c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,4 +2,6 @@ apps node_modules dist -**/*spec.tsx \ No newline at end of file +**/*spec.tsx + +deprecated \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index 8aeb83dc..6db75136 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,13 @@ module.exports = { extends: ['shellscape/typescript', 'plugin:import/typescript'], overrides: [ + { + files: ['*.d.ts'], + rules: { + 'no-underscore-dangle': 'off', + 'spaced-comment': 'off' + } + }, { files: ['*.ts', '*.tsx'], rules: { @@ -19,7 +26,7 @@ module.exports = { } ], parserOptions: { - project: ['./shared/tsconfig.eslint.json', './packages/*/tsconfig.json'], + project: ['./shared/tsconfig.eslint.json', './packages/**/tsconfig.json'], tsconfigRootDir: __dirname }, rules: { diff --git a/.vscode/settings.json b/.vscode/settings.json index 8b54f915..451c7c1f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,5 @@ { - "prettier.singleQuote": true + "prettier.singleQuote": true, + "typescript.tsdk": "node_modules/typescript/lib", + "css.customData": [".vscode/tailwind.json"] } diff --git a/.vscode/tailwind.json b/.vscode/tailwind.json new file mode 100644 index 00000000..64077edd --- /dev/null +++ b/.vscode/tailwind.json @@ -0,0 +1,9 @@ +{ + "version": 1.1, + "atDirectives": [ + { + "name": "@tailwind", + "description": "Use the @tailwind directive to insert Tailwind's `base`, `components`, `utilities`, and `screens` styles into your CSS." + } + ] +} diff --git a/apps/client/.eslintrc.json b/apps/client/.eslintrc.json deleted file mode 100644 index 943f99f7..00000000 --- a/apps/client/.eslintrc.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": ["next", "prettier"], - "plugins": ["simple-import-sort", "unused-imports"], - "rules": { - "react/no-unescaped-entities": 0, - "react-hooks/rules-of-hooks": 0, - "no-unused-vars": "off", - "simple-import-sort/imports": [ - "error", - { - // The default grouping, but with no blank lines. - "groups": [["^\\u0000", "^@?\\w", "^", "^\\."]] - } - ], - "simple-import-sort/exports": "error" - } -} \ No newline at end of file diff --git a/apps/client/.prettierignore b/apps/client/.prettierignore deleted file mode 100644 index 621285d9..00000000 --- a/apps/client/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ -dist -.next -node_modules \ No newline at end of file diff --git a/apps/client/.prettierrc.js b/apps/client/.prettierrc.js deleted file mode 100644 index 14134b0c..00000000 --- a/apps/client/.prettierrc.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - quoteProps: 'consistent', - singleQuote: true, - trailingComma: 'all', - printWidth: 80, - useTabs: false, - bracketSpacing: true, -}; diff --git a/apps/client/.vscode/settings.json b/apps/client/.vscode/settings.json deleted file mode 100644 index d0679104..00000000 --- a/apps/client/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "typescript.tsdk": "node_modules/typescript/lib", - "typescript.enablePromptUseWorkspaceTsdk": true -} \ No newline at end of file diff --git a/apps/client/emails/airbnb-review.tsx b/apps/client/emails/airbnb-review.tsx deleted file mode 100644 index aabc1be1..00000000 --- a/apps/client/emails/airbnb-review.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import { - Body, - Button, - Container, - Head, - Hr, - Html, - Img, - Link, - Preview, - Row, - Section, - Text, -} from '@react-email/components'; -import * as React from 'react'; - -interface AirbnbReviewEmailProps { - authorName?: string; - authorImage?: string; - reviewText?: string; -} - -const baseUrl = process.env.VERCEL_URL - ? `https://${process.env.VERCEL_URL}` - : ''; - -export const AirbnbReviewEmail = ({ - authorName, - authorImage, - reviewText, -}: AirbnbReviewEmailProps) => { - const previewText = `Read ${authorName}'s review`; - - return ( - - - {previewText} - - -
- -
- Airbnb -
-
- {authorName} -
-
- - Here's what {authorName} wrote - {reviewText} - - Now that the review period is over, we’ve posted {authorName} - ’s review to your Airbnb profile. - - - While it’s too late to write a review of your own, you can - send your feedback to {authorName} using your Airbnb message - thread. - - - - -
- -
- -
- - - Common questions - - - - How do reviews work? - - - - - How do star ratings work? - - - - - Can I leave a review after 14 days? - - -
- - Airbnb, Inc., 888 Brannan St, San Francisco, CA 94103 - - - Report unsafe behavior - -
-
-
-
- - - ); -}; - -AirbnbReviewEmail.PreviewProps = { - authorName: 'Alex', - authorImage: `${baseUrl}/static/airbnb-review-user.jpg`, - reviewText: `“Zeno was a great guest! Easy communication, the apartment was left - in great condition, very polite, and respectful of all house rules. - He’s welcome back anytime and would easily recommend him to any - host!”`, -} as AirbnbReviewEmailProps; - -export default AirbnbReviewEmail; - -const main = { - backgroundColor: '#ffffff', - fontFamily: - '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif', -}; - -const container = { - margin: '0 auto', - padding: '20px 0 48px', - width: '580px', -}; - -const userImage = { - margin: '0 auto', - marginBottom: '16px', - borderRadius: '50%', -}; - -const heading = { - fontSize: '32px', - lineHeight: '1.3', - fontWeight: '700', - color: '#484848', -}; - -const paragraph = { - fontSize: '18px', - lineHeight: '1.4', - color: '#484848', -}; - -const review = { - ...paragraph, - padding: '24px', - backgroundColor: '#f2f3f3', - borderRadius: '4px', -}; - -const button = { - backgroundColor: '#ff5a5f', - borderRadius: '3px', - color: '#fff', - fontSize: '18px', - textDecoration: 'none', - textAlign: 'center' as const, - display: 'block', - width: '100%', -}; - -const link = { - ...paragraph, - color: '#ff5a5f', - display: 'block', -}; - -const reportLink = { - fontSize: '14px', - color: '#9ca299', - textDecoration: 'underline', -}; - -const hr = { - borderColor: '#cccccc', - margin: '20px 0', -}; - -const footer = { - color: '#9ca299', - fontSize: '14px', - marginBottom: '10px', -}; diff --git a/apps/client/next-env.d.ts b/apps/client/next-env.d.ts deleted file mode 100644 index 4f11a03d..00000000 --- a/apps/client/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/client/next.config.js b/apps/client/next.config.js deleted file mode 100644 index 62cf7fe6..00000000 --- a/apps/client/next.config.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { - reactStrictMode: true, - swcMinify: true, - experimental: { - appDir: true, - externalDir: true // compile files that are located next to the .react-email directory - }, -}; - -module.exports = nextConfig; \ No newline at end of file diff --git a/apps/client/package.json b/apps/client/package.json deleted file mode 100644 index efdfe8d5..00000000 --- a/apps/client/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "react-email-client", - "version": "0.0.14", - "description": "The React Email preview application", - "license": "MIT", - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint", - "format:check": "prettier --check \"**/*.{ts,tsx,md}\"", - "format": "prettier --write \"**/*.{ts,tsx,md}\"" - }, - "engines": { - "node": ">=16.0.0" - }, - "dependencies": { - "@radix-ui/colors": "1.0.1", - "@radix-ui/react-collapsible": "1.0.3", - "@radix-ui/react-popover": "1.0.6", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-toggle-group": "1.0.4", - "@radix-ui/react-tooltip": "1.0.6", - "@react-email/render": "0.0.7", - "classnames": "2.3.2", - "framer-motion": "8.5.5", - "next": "13.4.10", - "prism-react-renderer": "1.3.5", - "react": "18.2.0", - "react-dom": "18.2.0" - }, - "devDependencies": { - "@types/classnames": "2.3.1", - "@types/node": "18.11.9", - "@types/react": "18.0.25", - "@types/react-dom": "18.0.9", - "autoprefixer": "10.4.14", - "eslint": "8.45.0", - "eslint-config-next": "13.4.10", - "eslint-config-prettier": "8.8.0", - "eslint-plugin-simple-import-sort": "10.0.0", - "eslint-plugin-unused-imports": "3.0.0", - "postcss": "8.4.26", - "prettier": "3.0.0", - "tailwindcss": "3.3.3", - "typescript": "5.1.6" - } -} diff --git a/apps/client/src/app/home.tsx b/apps/client/src/app/home.tsx deleted file mode 100644 index 7669d215..00000000 --- a/apps/client/src/app/home.tsx +++ /dev/null @@ -1,28 +0,0 @@ -'use client'; - -import Link from 'next/link'; -import { Button, Heading, Text } from '../components'; -import { Shell } from '../components/shell'; - -export default function Home({ navItems }) { - return ( - -
-
- - Welcome to the React Email preview! - - - To start developing your next email template, you can create a{' '} - .jsx or .tsx file under the "emails" - folder. - - - -
-
-
- ); -} diff --git a/apps/client/src/app/layout.tsx b/apps/client/src/app/layout.tsx deleted file mode 100644 index acfda733..00000000 --- a/apps/client/src/app/layout.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import '../styles/globals.css'; -import classnames from 'classnames'; -import { Inter } from 'next/font/google'; - -export const inter = Inter({ - subsets: ['latin'], - variable: '--font-inter', -}); - -export default function RootLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( - - -
- {children} -
- - - ); -} diff --git a/apps/client/src/app/page.tsx b/apps/client/src/app/page.tsx deleted file mode 100644 index 06fe4d5c..00000000 --- a/apps/client/src/app/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { getEmails } from '../utils/get-emails'; -import Home from './home'; - -export default async function Index() { - const { emails } = await getEmails(); - return ; -} - -export const metadata = { - title: 'React Email', -}; diff --git a/apps/client/src/app/preview/[slug]/page.tsx b/apps/client/src/app/preview/[slug]/page.tsx deleted file mode 100644 index 84af9bcb..00000000 --- a/apps/client/src/app/preview/[slug]/page.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { render } from '@react-email/render'; -import { promises as fs } from 'fs'; -import { dirname, join as pathJoin } from 'path'; -import { CONTENT_DIR, getEmails } from '../../../utils/get-emails'; -import Preview from './preview'; - -export const dynamicParams = true; - -export async function generateStaticParams() { - const { emails } = await getEmails(); - - const paths = emails.map((email) => { - return { slug: email }; - }); - - return paths; -} - -export default async function Page({ params }) { - const { emails, filenames } = await getEmails(); - const template = filenames.filter((email) => { - const [fileName] = email.split('.'); - return params.slug === fileName; - }); - - const Email = (await import(`../../../../emails/${params.slug}`)).default; - const previewProps = Email.PreviewProps || {} - const markup = render(, { pretty: true }); - const plainText = render(, { plainText: true }); - const basePath = pathJoin(process.cwd(), CONTENT_DIR); - const path = pathJoin(basePath, template[0]); - - // the file is actually just re-exporting the default export of the original file. We need to resolve this first - const exportTemplateFile: string = await fs.readFile(path, { - encoding: 'utf-8', - }); - const importPath = exportTemplateFile.match(/import Mail from '(.+)';/)![1]; - const originalFilePath = pathJoin(dirname(path), importPath); - - const reactMarkup: string = await fs.readFile(originalFilePath, { - encoding: 'utf-8', - }); - - return ( - - ); -} - -export async function generateMetadata({ params }) { - return { title: `${params.slug} — React Email` }; -} diff --git a/apps/client/src/app/preview/[slug]/preview.tsx b/apps/client/src/app/preview/[slug]/preview.tsx deleted file mode 100644 index be902d55..00000000 --- a/apps/client/src/app/preview/[slug]/preview.tsx +++ /dev/null @@ -1,72 +0,0 @@ -'use client'; - -import { usePathname, useRouter, useSearchParams } from 'next/navigation'; -import React from 'react'; -import { CodeContainer } from '../../../components/code-container'; -import { Shell } from '../../../components/shell'; -import { Tooltip } from '../../../components/tooltip'; - -export default function Preview({ - navItems, - slug, - markup, - reactMarkup, - plainText, -}) { - const router = useRouter(); - const pathname = usePathname(); - const searchParams = useSearchParams(); - const [activeView, setActiveView] = React.useState('desktop'); - const [activeLang, setActiveLang] = React.useState('jsx'); - - React.useEffect(() => { - const view = searchParams.get('view'); - const lang = searchParams.get('lang'); - - if (view === 'source' || view === 'desktop') { - setActiveView(view); - } - - if (lang === 'jsx' || lang === 'markup' || lang === 'markdown') { - setActiveLang(lang); - } - }, [searchParams]); - - const handleViewChange = (view: string) => { - setActiveView(view); - router.push(`${pathname}?view=${view}`); - }; - - const handleLangChange = (lang: string) => { - setActiveLang(lang); - router.push(`${pathname}?view=source&lang=${lang}`); - }; - - return ( - - {activeView === 'desktop' ? ( -