diff --git a/.gitignore b/.gitignore index 06dc2511..c8ac1b7f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,4 @@ dist **/.vitepress/cache packages/**/tsconfig.json -packages/jsx-email/README.md -!packages/jsx-email/src/cli/preview-app/tsconfig.json \ No newline at end of file +packages/jsx-email/README.md \ No newline at end of file diff --git a/apps/preview/moon.yml b/apps/preview/moon.yml index dbcfe5cb..9ccbccba 100644 --- a/apps/preview/moon.yml +++ b/apps/preview/moon.yml @@ -11,4 +11,4 @@ tasks: options: cache: false outputStyle: 'stream' - runDepsInParallel: false \ No newline at end of file + runDepsInParallel: false diff --git a/docs/components/qrcode.md b/docs/components/qrcode.md index 6acb80dc..b735c664 100644 --- a/docs/components/qrcode.md +++ b/docs/components/qrcode.md @@ -81,7 +81,5 @@ width?: number; (Optional) The width of the wrapping image in pixels. If not provided, the `size` value will be used. ::: tip -This component also expresses all of the [Common Component Props](https://react.dev/reference/react-dom/components/common) for `ComponentProps<'img'>` +This component also expresses all of the [Common Component Props](https://react.dev/reference/react-dom/components/common) for `ComponentProps<'img'>` ::: - - diff --git a/packages/jsx-email/moon.yml b/packages/jsx-email/moon.yml index 3595eeea..a9f0a00d 100644 --- a/packages/jsx-email/moon.yml +++ b/packages/jsx-email/moon.yml @@ -12,7 +12,6 @@ tasks: - ~:clean.dist - ~:compile - ~:copy.assets - - ~:copy.preview - ~:copy.readme inputs: - src @@ -42,11 +41,6 @@ tasks: options: cache: false - copy.preview: - command: cp -r src/cli/preview-app dist - options: - cache: false - copy.readme: command: cp -r ../../README.md README.md options: diff --git a/packages/jsx-email/package.json b/packages/jsx-email/package.json index 67f1e5f2..c3b3e66a 100644 --- a/packages/jsx-email/package.json +++ b/packages/jsx-email/package.json @@ -9,9 +9,9 @@ "repository": { "type": "git", "url": "https://github.com/shellscape/jsx-email.git", - "directory": "packages/render" + "directory": "packages/jsx-email" }, - "homepage": "https://jsx.email/docs/core/render", + "homepage": "https://jsx.email/", "main": "dist/index.js", "bin": { "email": "./dist/cli/index.js" @@ -45,14 +45,9 @@ }, "dependencies": { "@dot/log": "^0.1.3", - "@jspm/core": "^2.0.1", + "@jsx-email/app-preview": "workspace:*", "@jsx-email/doiuse-email": "^1.0.1", - "@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", "@unocss/core": "^0.57.7", "@unocss/preset-typography": "^0.57.7", "@unocss/preset-uno": "^0.57.7", @@ -65,8 +60,6 @@ "classnames": "2.3.2", "debug": "^4.3.4", "esbuild": "^0.19.3", - "esbuild-plugin-copy": "^2.1.1", - "framer-motion": "8.5.5", "globby": "11.0.4", "hash-it": "^6.0.0", "html-to-text": "9.0.5", @@ -80,19 +73,15 @@ "pretty": "2.0.0", "pretty-bytes": "^5.6.0", "qrcode": "^1.5.3", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-router-dom": "6.16.0", "rehype": "^13.0.1", "rehype-preset-minify": "^7.0.0", "rehype-stringify": "^10.0.0", "rollup-plugin-hypothetical": "^2.1.1", - "shikiji": "^0.6.8", + "shikiji": "^0.8.0", "source-map-js": "^1.0.2", "source-map-support": "^0.5.21", "std-env": "^3.6.0", "superstruct": "^1.0.3", - "tailwindcss": "3.3.3", "titleize": "^4.0.0", "unist-util-visit": "^5.0.0", "vite": "^4.4.9", @@ -104,14 +93,10 @@ "@types/html-minifier-terser": "^7.0.0", "@types/html-to-text": "^9.0.2", "@types/import-local": "^3.1.2", - "@types/object-hash": "^3.0.6", - "@types/postcss-css-variables": "^0.18.3", "@types/pretty": "^2.0.1", "@types/source-map-support": "^0.5.10", "@types/yargs-parser": "^21.0.3", - "hast": "^1.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "hast": "^1.0.0" }, "types": "dist/index.d.mts", "funding": { diff --git a/packages/jsx-email/src/cli/commands/preview.ts b/packages/jsx-email/src/cli/commands/preview.ts index 64f75d6e..2f542cfb 100644 --- a/packages/jsx-email/src/cli/commands/preview.ts +++ b/packages/jsx-email/src/cli/commands/preview.ts @@ -54,8 +54,7 @@ export const start = async (targetPath: string, argv: PreviewOptions) => { } const { host = false, open = true, port = 55420 } = argv; - const { viteConfig } = await import('./vite.config'); - + const { viteConfig } = await import('./vite'); const mergedConfig = { configFile: false, ...viteConfig, @@ -70,7 +69,7 @@ export const start = async (targetPath: string, argv: PreviewOptions) => { const server = await createServer(mergedConfig); - info(chalk`\n 🚀 {yellow JSX email} Preview\n`); + info(chalk`\n 🚀 {yellow jsx-email} Preview\n`); await server.listen(); diff --git a/packages/jsx-email/src/cli/commands/vite.config.ts b/packages/jsx-email/src/cli/commands/vite.ts similarity index 92% rename from packages/jsx-email/src/cli/commands/vite.config.ts rename to packages/jsx-email/src/cli/commands/vite.ts index c87cb326..d0b2a471 100644 --- a/packages/jsx-email/src/cli/commands/vite.config.ts +++ b/packages/jsx-email/src/cli/commands/vite.ts @@ -1,4 +1,4 @@ -import { resolve } from 'path'; +import { dirname, join } from 'path'; import react from '@vitejs/plugin-react'; import { createLogger, defineConfig } from 'vite'; @@ -8,12 +8,11 @@ import { nodePolyfills } from 'vite-plugin-node-polyfills'; // eslint-disable-next-line import hypothetical from 'rollup-plugin-hypothetical'; -const root = resolve(__dirname, '../preview-app'); - -process.chdir(root); - const logger = createLogger(); const { warnOnce: og } = logger; +const root = join(dirname(require.resolve('@jsx-email/app-preview')), 'app'); + +process.chdir(root); logger.warnOnce = (message, options) => { // Note: ignore `Sourcemap for "${file}" points to missing source files` errors diff --git a/packages/jsx-email/src/cli/preview-app/.eslintrc.js b/packages/jsx-email/src/cli/preview-app/.eslintrc.js deleted file mode 100644 index 5b1bfab4..00000000 --- a/packages/jsx-email/src/cli/preview-app/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - rules: { - 'import/extensions': 'off', - 'import/no-default-export': 'off' - } -}; diff --git a/packages/jsx-email/src/cli/preview-app/README.md b/packages/jsx-email/src/cli/preview-app/README.md deleted file mode 100644 index 7bddada0..00000000 --- a/packages/jsx-email/src/cli/preview-app/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# jsx-email CLI Preview App - -These files are not meant to be compiled, but rather shipped as-is with the package, and will be loaded and evaluated by `vite dev` at runtime. Do not compile these files. diff --git a/packages/jsx-email/src/cli/preview-app/index.html b/packages/jsx-email/src/cli/preview-app/index.html deleted file mode 100644 index 5b412429..00000000 --- a/packages/jsx-email/src/cli/preview-app/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - JSX Email - - -
- - - diff --git a/packages/jsx-email/src/cli/preview-app/postcss.config.js b/packages/jsx-email/src/cli/preview-app/postcss.config.js deleted file mode 100644 index cd1a2b94..00000000 --- a/packages/jsx-email/src/cli/preview-app/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - autoprefixer: {}, - tailwindcss: {} - } -}; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/code-container.tsx b/packages/jsx-email/src/cli/preview-app/src/components/code-container.tsx deleted file mode 100644 index 8f2966e7..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/code-container.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import classNames from 'classnames'; -import * as React from 'react'; - -import { type PreviewLanguage, copyTextToClipboard } from '../helpers'; - -import { Code } from './code'; -import { IconButton, IconCheck, IconClipboard, IconDownload } from './icons'; -import { Tooltip } from './tooltip'; - -interface RawProps { - content: string; - language: PreviewLanguage; -} - -interface CodeContainerProps { - activeView: string; - raws: RawProps[]; - setActiveView: (lang: string) => void; -} - -export const CodeContainer: React.FC> = ({ - activeView, - raws - // setActiveLang -}) => { - const [isCopied, setIsCopied] = React.useState(false); - - const renderDownloadIcon = () => { - const value = raws.find((raw) => raw.language === activeView); - const file = new File([value!.content], `email.${value!.language}`); - const url = URL.createObjectURL(file); - - return ( - - - - ); - }; - - const renderClipboardIcon = () => { - const handleClipboard = async () => { - const activeContent = raws.filter(({ language }) => activeView === language); - setIsCopied(true); - await copyTextToClipboard(activeContent[0].content); - setTimeout(() => setIsCopied(false), 3000); - }; - - return ( - - {isCopied ? : } - - ); - }; - - React.useEffect(() => { - setIsCopied(false); - }, [activeView]); - - return ( - <> - - - {renderClipboardIcon()} - - Copy to Clipboard - - - - {renderDownloadIcon()} - - Download - - {raws.map(({ language, content }) => ( -
- {content.trim()} -
- ))} - - ); -}; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/code.tsx b/packages/jsx-email/src/cli/preview-app/src/components/code.tsx deleted file mode 100644 index fa65117b..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/code.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import classnames from 'classnames'; -import { getHighlighter } from 'shikiji'; - -import { type PreviewLanguage } from '../helpers'; - -interface CodeProps { - children: string; - className?: string; - language?: PreviewLanguage; -} - -const theme = 'dark-plus'; -const shiki = await getHighlighter({ - langs: ['html', 'tsx'], - themes: [theme] -}); - -export const Code = ({ children: value, language = 'html' }: CodeProps) => { - // const [isCopied, setIsCopied] = React.useState(false); - const lang = language === 'jsx' ? 'tsx' : language; - const code = language === 'plain' ? value : shiki.codeToHtml(value, { lang, theme }); - const lines = value.split('\n').length; - const css = ` - .${language} .shiki .line:before { - width: calc(${lines.toString().length} * 12px + 12px); - }`; - - return ( - <> - -
- - ); -}; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/heading.tsx b/packages/jsx-email/src/cli/preview-app/src/components/heading.tsx deleted file mode 100644 index 774eadc1..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/heading.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import * as SlotPrimitive from '@radix-ui/react-slot'; -import classnames from 'classnames'; -import * as React from 'react'; - -import { type As, unreachable } from '../helpers'; - -export type HeadingSize = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10'; -export type HeadingColor = 'white' | 'gray'; -export type HeadingWeight = 'medium' | 'bold'; - -interface HeadingOwnProps { - color?: HeadingColor; - size?: HeadingSize; - weight?: HeadingWeight; -} - -type HeadingProps = As<'h1', 'h2', 'h3', 'h4', 'h5', 'h6'> & HeadingOwnProps; - -const getSizesClassNames = (size: HeadingSize | undefined) => { - switch (size) { - case '1': - return 'text-xs'; - case '2': - return 'text-sm'; - case undefined: - case '3': - return 'text-base'; - case '4': - return 'text-lg'; - case '5': - return 'text-xl tracking-[-0.16px]'; - case '6': - return 'text-2xl tracking-[-0.288px]'; - case '7': - return 'text-[28px] leading-[34px] tracking-[-0.416px]'; - case '8': - return 'text-[35px] leading-[42px] tracking-[-0.64px]'; - case '9': - return 'text-6xl leading-[73px] tracking-[-0.896px]'; - case '10': - return [ - 'text-[38px] leading-[46px]', - 'md:text-[70px] md:leading-[85px] tracking-[-1.024px;]' - ]; - default: - return unreachable(size); - } -}; - -const getWeightClassNames = (weight: HeadingWeight | undefined) => { - switch (weight) { - case 'medium': - return 'font-medium'; - case 'bold': - case undefined: - return 'font-bold'; - default: - return unreachable(weight); - } -}; - -export const Heading = React.forwardRef>( - ( - { as: Tag = 'h1', size = '3', className, children, weight = 'bold', ...props }, - forwardedRef - ) => ( - - {children} - - ) -); - -Heading.displayName = 'Heading'; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/icons.tsx b/packages/jsx-email/src/cli/preview-app/src/components/icons.tsx deleted file mode 100644 index 572e5bb5..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/icons.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import classnames from 'classnames'; -import * as React from 'react'; - -export type IconElement = React.ElementRef<'svg'>; -export type RootProps = React.ComponentPropsWithoutRef<'svg'>; - -export interface IconProps extends RootProps { - size?: number; -} - -export const IconBase = React.forwardRef>( - ({ size = 20, ...props }, forwardedRef) => ( - - ) -); - -IconBase.displayName = 'IconBase'; - -export interface IconButtonProps extends React.ComponentPropsWithoutRef<'button'> {} - -export const IconButton = React.forwardRef>( - ({ children, className, ...props }, forwardedRef) => ( - - ) -); - -IconButton.displayName = 'IconButton'; - -export const IconCheck = React.forwardRef>( - ({ ...props }, forwardedRef) => ( - - - - ) -); - -IconCheck.displayName = 'IconCheck'; - -export const IconClipboard = React.forwardRef>( - ({ ...props }, forwardedRef) => ( - - - - - - - ) -); - -IconClipboard.displayName = 'IconClipboard'; - -export const IconDownload = React.forwardRef>( - ({ ...props }, forwardedRef) => ( - - - - ) -); - -IconDownload.displayName = 'IconDownload'; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/index.ts b/packages/jsx-email/src/cli/preview-app/src/components/index.ts deleted file mode 100644 index 61f907a3..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from './code'; -export * from './heading'; -export * from './icons'; -export * from './logo'; -export * from './sidebar'; -export * from './text'; -export * from './topbar'; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/logo.tsx b/packages/jsx-email/src/cli/preview-app/src/components/logo.tsx deleted file mode 100644 index 59cb1eee..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/logo.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import * as React from 'react'; - -type LogoElement = React.ElementRef<'svg'>; -type RootProps = React.ComponentPropsWithoutRef<'svg'>; - -export const Logo = React.forwardRef>((_, __) => ( - - - - - - - - - -)); - -Logo.displayName = 'Logo'; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/shell.tsx b/packages/jsx-email/src/cli/preview-app/src/components/shell.tsx deleted file mode 100644 index 38598fa4..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/shell.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import classNames from 'classnames'; -import * as React from 'react'; - -import { Sidebar } from './sidebar'; -import { Topbar } from './topbar'; - -type ShellElement = React.ElementRef<'div'>; -type RootProps = React.ComponentPropsWithoutRef<'div'>; - -interface ShellProps extends RootProps { - activeView?: string; - html?: string; - setActiveView?: (view: string) => void; - templateNames: string[]; -} - -export const Shell = React.forwardRef>( - ({ title, templateNames, children, html, activeView, setActiveView }, forwardedRef) => { - const [showNav] = React.useState(false); - return ( -
-
- -
- {title && ( - - )} -
-
{children}
-
-
-
-
- ); - } -); - -Shell.displayName = 'Shell'; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/sidebar.tsx b/packages/jsx-email/src/cli/preview-app/src/components/sidebar.tsx deleted file mode 100644 index 6bcb080a..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/sidebar.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import * as Collapsible from '@radix-ui/react-collapsible'; -import classnames from 'classnames'; -import { LayoutGroup, motion } from 'framer-motion'; -import * as React from 'react'; -import { Link } from 'react-router-dom'; - -import { Heading } from './heading'; -import { Logo } from './logo'; - -type SidebarElement = React.ElementRef<'aside'>; -type RootProps = React.ComponentPropsWithoutRef<'aside'>; - -interface SidebarProps extends RootProps { - templateNames: string[]; - title?: string; -} - -export const Sidebar = React.forwardRef>( - ({ className, templateNames, title, ...props }, forwardedRef) => ( - - ) -); - -Sidebar.displayName = 'Sidebar'; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/text.tsx b/packages/jsx-email/src/cli/preview-app/src/components/text.tsx deleted file mode 100644 index b23cb13e..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/text.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import * as SlotPrimitive from '@radix-ui/react-slot'; -import classnames from 'classnames'; -import * as React from 'react'; - -import { type As, unreachable } from '../helpers'; - -export type TextSize = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'; -export type TextColor = 'gray' | 'white'; -export type TextTransform = 'uppercase' | 'lowercase' | 'capitalize'; -export type TextWeight = 'normal' | 'medium'; - -interface TextOwnProps { - color?: TextColor; - size?: TextSize; - transform?: TextTransform; - weight?: TextWeight; -} - -type TextProps = As<'span', 'div', 'p'> & TextOwnProps; - -const getSizesClassNames = (size: TextSize | undefined) => { - switch (size) { - case '1': - return 'text-xs'; - case undefined: - case '2': - return 'text-sm'; - case '3': - return 'text-base'; - case '4': - return 'text-lg'; - case '5': - return ['text-17px', 'md:text-xl tracking-[-0.16px]']; - case '6': - return 'text-2xl tracking-[-0.288px]'; - case '7': - return 'text-[28px] leading-[34px] tracking-[-0.416px]'; - case '8': - return 'text-[35px] leading-[42px] tracking-[-0.64px]'; - case '9': - return 'text-6xl leading-[73px] tracking-[-0.896px]'; - default: - return unreachable(size); - } -}; - -const getWeightClassNames = (weight: TextWeight | undefined) => { - switch (weight) { - case undefined: - case 'normal': - return 'font-normal'; - case 'medium': - return 'font-medium'; - default: - return unreachable(weight); - } -}; - -export const Text = React.forwardRef>( - ( - { as: Tag = 'span', size = '2', transform, weight = 'normal', className, children, ...props }, - forwardedRef - ) => ( - - {children} - - ) -); - -Text.displayName = 'Text'; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/tooltip-content.tsx b/packages/jsx-email/src/cli/preview-app/src/components/tooltip-content.tsx deleted file mode 100644 index 731d98da..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/tooltip-content.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as TooltipPrimitive from '@radix-ui/react-tooltip'; -import classnames from 'classnames'; -import * as React from 'react'; - -type ContentElement = React.ElementRef; -type ContentProps = React.ComponentPropsWithoutRef; - -export interface TooltipProps extends ContentProps {} - -export const TooltipContent = React.forwardRef>( - ({ sideOffset = 6, children, ...props }, forwardedRef) => ( - - - {children} - - - ) -); - -TooltipContent.displayName = 'TooltipContent'; diff --git a/packages/jsx-email/src/cli/preview-app/src/components/tooltip.tsx b/packages/jsx-email/src/cli/preview-app/src/components/tooltip.tsx deleted file mode 100644 index 247d8093..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/tooltip.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import * as TooltipPrimitive from '@radix-ui/react-tooltip'; -import * as React from 'react'; - -import { TooltipContent } from './tooltip-content'; - -type RootProps = React.ComponentPropsWithoutRef; - -export interface TooltipProps extends RootProps {} - -export const TooltipRoot: React.FC> = ({ children, ...props }) => ( - {children} -); - -export const Tooltip = Object.assign(TooltipRoot, { - Arrow: TooltipPrimitive.TooltipArrow, - Content: TooltipContent, - Provider: TooltipPrimitive.TooltipProvider, - Trigger: TooltipPrimitive.TooltipTrigger -}); diff --git a/packages/jsx-email/src/cli/preview-app/src/components/topbar.tsx b/packages/jsx-email/src/cli/preview-app/src/components/topbar.tsx deleted file mode 100644 index 3d335067..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/components/topbar.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import * as ToggleGroup from '@radix-ui/react-toggle-group'; -import classnames from 'classnames'; -import { LayoutGroup, motion } from 'framer-motion'; -import * as React from 'react'; - -import { Heading } from './heading'; - -type TopbarElement = React.ElementRef<'header'>; -type RootProps = React.ComponentPropsWithoutRef<'header'>; - -interface TopbarProps extends RootProps { - activeView?: string; - markup?: string; - setActiveView?: (view: string) => void; - title: string; -} - -export const Topbar = React.forwardRef>( - ({ className, title, markup, activeView, setActiveView, ...props }, forwardedRef) => { - const button = 'text-sm font-medium px-3 py-2 transition ease-in-out duration-200 relative'; - const span = 'absolute left-0 right-0 top-0 bottom-0 bg-cta-bg'; - - return ( -
- - -
- - {setActiveView && ( - { - if (!value) return; - setActiveView(value); - }} - > - - - {activeView === 'desktop' && ( - - )} - - Desktop - - - - - - {activeView === 'jsx' && ( - - )} - - JSX - - - - - - {activeView === 'html' && ( - - )} - - HTML - - - - - - {activeView === 'plain' && ( - - )} - - Plain Text - - - - - )} - -
-
- ); - } -); - -Topbar.displayName = 'Topbar'; diff --git a/packages/jsx-email/src/cli/preview-app/src/css/globals.css b/packages/jsx-email/src/cli/preview-app/src/css/globals.css deleted file mode 100644 index 31946efd..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/css/globals.css +++ /dev/null @@ -1,53 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - html, - body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; - font-weight: 300; - } - - .shiki { - background: inherit !important; - counter-reset: line 0; - display: block; - gap: 0.3em; - grid-auto-rows: 1em; - grid-template-columns: min-content 1fr; - } - - .shiki .line { - display: inline-block; - line-height: 20px; - min-height: auto; - vertical-align: middle; - white-space: pre; - } - - .shiki .line:last-child:before, - .shiki .line:last-child span { - padding-bottom: 30px; - } - - .shiki .line:first-child:before, - .shiki .line:first-child span { - padding-top: 10px; - } - - .shiki .line:before { - background: #3d3a3a; - color: #777; - content: counter(line); - counter-increment: line; - display: inline-block; - margin-right: 10px; - padding-right: 10px; - text-align: right; - } - - .plainText .shiki .line:before { - display: none; - } -} diff --git a/packages/jsx-email/src/cli/preview-app/src/error.tsx b/packages/jsx-email/src/cli/preview-app/src/error.tsx deleted file mode 100644 index a0a5f07b..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/error.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useRouteError } from 'react-router-dom'; - -const { error } = console; - -export const Error = () => { - const err: any = useRouteError(); - error(err); - - return ( -
-

Oops!

-

Sorry, an unexpected error has occurred.

-

- {err.statusText || err.message} -

-
- ); -}; diff --git a/packages/jsx-email/src/cli/preview-app/src/helpers.ts b/packages/jsx-email/src/cli/preview-app/src/helpers.ts deleted file mode 100644 index 12776f1b..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/helpers.ts +++ /dev/null @@ -1,49 +0,0 @@ -export type As< - DefaultTag extends React.ElementType, - T1 extends React.ElementType, - T2 extends React.ElementType = T1, - T3 extends React.ElementType = T1, - T4 extends React.ElementType = T1, - T5 extends React.ElementType = T1 -> = - | (React.ComponentPropsWithRef & { - as?: DefaultTag; - }) - | (React.ComponentPropsWithRef & { - as: T1; - }) - | (React.ComponentPropsWithRef & { - as: T2; - }) - | (React.ComponentPropsWithRef & { - as: T3; - }) - | (React.ComponentPropsWithRef & { - as: T4; - }) - | (React.ComponentPropsWithRef & { - as: T5; - }); - -export type PreviewLanguage = 'html' | 'jsx' | 'plain'; - -export const copyTextToClipboard = async (text: string) => { - try { - await navigator.clipboard.writeText(text); - } catch { - throw new Error('Not able to copy'); - } -}; - -export const languageMap = { - html: '', - jsx: '', - plainText: 'Plain Text' -}; - -export const unreachable = ( - condition: never, - message = `Entered unreachable code. Received '${condition}'.` -): never => { - throw new TypeError(message); -}; diff --git a/packages/jsx-email/src/cli/preview-app/src/home.tsx b/packages/jsx-email/src/cli/preview-app/src/home.tsx deleted file mode 100644 index a6c362c2..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/home.tsx +++ /dev/null @@ -1,39 +0,0 @@ -'use client'; - -import * as SlotPrimitive from '@radix-ui/react-slot'; -import React from 'react'; - -import { Heading, Text } from './components'; -import { Shell } from './components/shell'; - -export const Home = ({ templateNames }: { templateNames: string[] }) => { - React.useEffect(() => { - document.title = 'JSX email'; - }, []); - - return ( - -
- - JSX Email Preview - - - Start creating an email template by running{' '} - email create <template-name> -
-
- Run email help create for a list of options -
-
- Happy coding! -
- - - - Read our Documentation - - -
-
- ); -}; diff --git a/packages/jsx-email/src/cli/preview-app/src/layout.tsx b/packages/jsx-email/src/cli/preview-app/src/layout.tsx deleted file mode 100644 index 4e0fe02d..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/layout.tsx +++ /dev/null @@ -1,8 +0,0 @@ -/* eslint-disable import/first */ -import './css/globals.css'; - -export const Layout = ({ children }: { children: React.ReactNode }) => ( -
-
{children}
-
-); diff --git a/packages/jsx-email/src/cli/preview-app/src/main.tsx b/packages/jsx-email/src/cli/preview-app/src/main.tsx deleted file mode 100644 index 514fef2a..00000000 --- a/packages/jsx-email/src/cli/preview-app/src/main.tsx +++ /dev/null @@ -1,96 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -import { render, renderPlainText } from 'jsx-email'; -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import { createBrowserRouter, type RouteObject, RouterProvider } from 'react-router-dom'; -import { create, type Struct } from 'superstruct'; -import titleize from 'titleize'; - -import { Error } from './error.tsx'; -import { Home } from './home.tsx'; -import { Layout } from './layout.tsx'; -import { Preview } from './preview.tsx'; - -interface TemplateExports { - Name?: string; - PreviewProps?: () => any; - Template: React.ExoticComponent; - TemplateStruct?: Struct; -} - -interface TemplateData extends TemplateExports { - jsx: string; -} - -const { warn } = console; - -const addSpacesForCamelCaseName = (str: string) => str.replace(/([a-z])([A-Z])/g, '$1 $2'); - -const parseName = (path: string) => { - const chunks = path.replace('\\', '/').split('/'); - const segment = chunks.at(-1); - const basename = segment!.split(/\.[^.]+$/)[0]; - - return titleize(addSpacesForCamelCaseName(basename)); -}; - -const modules = import.meta.glob('@/*.{jsx,tsx}', { eager: true }); -const sources = import.meta.glob('@/*.{jsx,tsx}', { as: 'raw', eager: true }); - -const templates = await Promise.all( - Object.entries(modules).map>(async ([path, mod]) => { - const component = mod as TemplateExports; - const result: TemplateData = { - jsx: sources[path], - Name: component.Name || parseName(path), - PreviewProps: component.PreviewProps, - Template: component.Template || (component as any).default, - TemplateStruct: component.TemplateStruct - }; - return result; - }) -); - -const templateNames = templates.map((template) => template.Name!); - -const templateRoutes = templates.map(async (template) => { - const { Name, PreviewProps, Template, TemplateStruct } = template; - let props: any; - - if (TemplateStruct) props = create({}, TemplateStruct); - else if (PreviewProps) props = PreviewProps(); - else if ((Template as any).PreviewProps) { - warn( - `jsx-email: ${Name} → PreviewProps as a property of a component is deprecated. Please used a named export.` - ); - props = (Template as any).PreviewProps; - } - - const html = await render(