react-email 3.0.5 → 3.0.7-canary.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.
Files changed (192) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cli/index.js +764 -769
  3. package/dist/cli/index.mjs +488 -499
  4. package/dist/preview/.next/BUILD_ID +1 -1
  5. package/dist/preview/.next/app-build-manifest.json +10 -10
  6. package/dist/preview/.next/build-manifest.json +3 -3
  7. package/dist/preview/.next/cache/.rscinfo +1 -1
  8. package/dist/preview/.next/cache/webpack/client-production/0.pack +0 -0
  9. package/dist/preview/.next/cache/webpack/client-production/index.pack +0 -0
  10. package/dist/preview/.next/cache/webpack/edge-server-production/index.pack +0 -0
  11. package/dist/preview/.next/cache/webpack/server-production/0.pack +0 -0
  12. package/dist/preview/.next/cache/webpack/server-production/index.pack +0 -0
  13. package/dist/preview/.next/next-minimal-server.js.nft.json +1 -1
  14. package/dist/preview/.next/next-server.js.nft.json +1 -1
  15. package/dist/preview/.next/prerender-manifest.json +1 -1
  16. package/dist/preview/.next/required-server-files.json +1 -1
  17. package/dist/preview/.next/server/app/_not-found/page.js +1 -1
  18. package/dist/preview/.next/server/app/_not-found/page.js.nft.json +1 -1
  19. package/dist/preview/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/dist/preview/.next/server/app/favicon.ico/route.js +1 -1
  21. package/dist/preview/.next/server/app/page.js +1 -1
  22. package/dist/preview/.next/server/app/page.js.nft.json +1 -1
  23. package/dist/preview/.next/server/app/page_client-reference-manifest.js +1 -1
  24. package/dist/preview/.next/server/app/preview/[...slug]/page.js +7 -6
  25. package/dist/preview/.next/server/app/preview/[...slug]/page.js.nft.json +1 -1
  26. package/dist/preview/.next/server/app/preview/[...slug]/page_client-reference-manifest.js +1 -1
  27. package/dist/preview/.next/server/chunks/196.js +5 -0
  28. package/dist/preview/.next/server/chunks/650.js +1 -0
  29. package/dist/preview/.next/server/chunks/720.js +3 -3
  30. package/dist/preview/.next/server/middleware-build-manifest.js +1 -1
  31. package/dist/preview/.next/server/next-font-manifest.js +1 -1
  32. package/dist/preview/.next/server/next-font-manifest.json +1 -1
  33. package/dist/preview/.next/server/pages/500.html +1 -1
  34. package/dist/preview/.next/server/server-reference-manifest.js +1 -1
  35. package/dist/preview/.next/server/server-reference-manifest.json +1 -1
  36. package/dist/preview/.next/static/chunks/154-ca55c1fde27d0922.js +1 -0
  37. package/dist/preview/.next/static/chunks/app/layout-7b9224888c7ffd05.js +1 -0
  38. package/dist/preview/.next/static/chunks/app/page-36853df2e13e583f.js +1 -0
  39. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-48ce00470ce06446.js +1 -0
  40. package/dist/preview/.next/static/chunks/main-app-cd104297c6bcc87e.js +1 -0
  41. package/dist/preview/.next/static/css/{eb0a93282704d7ab.css → a34876a6c565fff8.css} +1 -1
  42. package/dist/preview/.next/trace +21 -20
  43. package/dist/preview/.next/types/app/layout.ts +1 -1
  44. package/dist/preview/.next/types/app/preview/[...slug]/page.ts +1 -1
  45. package/package.json +4 -8
  46. package/postcss.config.js +1 -1
  47. package/src/actions/get-email-path-from-slug.ts +7 -4
  48. package/src/actions/get-emails-directory-metadata-action.ts +19 -0
  49. package/src/actions/render-email-by-path.tsx +3 -3
  50. package/src/app/layout.tsx +2 -2
  51. package/src/app/page.tsx +1 -1
  52. package/src/app/preview/[...slug]/page.tsx +2 -2
  53. package/src/app/preview/[...slug]/preview.tsx +2 -2
  54. package/src/components/button.tsx +1 -1
  55. package/src/components/code-container.tsx +1 -1
  56. package/src/components/heading.tsx +1 -1
  57. package/src/components/sidebar/sidebar-directory-children.tsx +5 -8
  58. package/src/components/sidebar/sidebar-directory.tsx +2 -2
  59. package/src/components/sidebar/sidebar.tsx +2 -2
  60. package/src/components/text.tsx +1 -1
  61. package/src/components/tooltip-content.tsx +1 -1
  62. package/src/components/tooltip.tsx +1 -1
  63. package/src/components/topbar.tsx +1 -1
  64. package/src/contexts/emails.tsx +3 -5
  65. package/src/hooks/use-email-rendering-result.ts +2 -2
  66. package/src/utils/cn.ts +1 -1
  67. package/src/utils/esbuild/renderring-utilities-exporter.ts +1 -1
  68. package/src/utils/get-email-component.ts +6 -6
  69. package/src/{actions → utils}/get-emails-directory-metadata.spec.ts +1 -2
  70. package/src/utils/get-emails-directory-metadata.ts +119 -0
  71. package/src/utils/improve-error-with-sourcemap.ts +1 -1
  72. package/src/utils/static-node-modules-for-vm.ts +6 -6
  73. package/tsconfig.json +1 -6
  74. package/.eslintrc.js +0 -52
  75. package/.prettierignore +0 -3
  76. package/.prettierrc.js +0 -8
  77. package/dist/index.d.mts +0 -20
  78. package/dist/index.d.ts +0 -20
  79. package/dist/index.js +0 -96
  80. package/dist/index.mjs +0 -21
  81. package/dist/package/index.d.mts +0 -33
  82. package/dist/package/index.d.ts +0 -33
  83. package/dist/package/index.js +0 -62
  84. package/dist/package/index.mjs +0 -7
  85. package/dist/preview/.next/cache/eslint/.cache_1vyas3k +0 -1
  86. package/dist/preview/.next/server/chunks/420.js +0 -1
  87. package/dist/preview/.next/server/chunks/625.js +0 -5
  88. package/dist/preview/.next/static/chunks/154-69b91a0c2fd801b8.js +0 -1
  89. package/dist/preview/.next/static/chunks/app/layout-e1b6f1534cbbe5bd.js +0 -1
  90. package/dist/preview/.next/static/chunks/app/page-2c3e297e38c526ef.js +0 -1
  91. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-2b4988ba6daf34e1.js +0 -1
  92. package/dist/preview/.next/static/chunks/main-app-2c7f96205a73f128.js +0 -1
  93. package/src/actions/get-emails-directory-metadata.ts +0 -123
  94. package/src/package/body/dist/index.d.mts +0 -6
  95. package/src/package/body/dist/index.d.ts +0 -6
  96. package/src/package/body/dist/index.js +0 -79
  97. package/src/package/body/dist/index.mjs +0 -45
  98. package/src/package/button/dist/index.d.mts +0 -6
  99. package/src/package/button/dist/index.d.ts +0 -6
  100. package/src/package/button/dist/index.js +0 -252
  101. package/src/package/button/dist/index.mjs +0 -218
  102. package/src/package/code-block/dist/index.d.mts +0 -4906
  103. package/src/package/code-block/dist/index.d.ts +0 -4906
  104. package/src/package/code-block/dist/index.js +0 -18205
  105. package/src/package/code-block/dist/index.mjs +0 -18133
  106. package/src/package/code-inline/dist/index.d.mts +0 -11
  107. package/src/package/code-inline/dist/index.d.ts +0 -11
  108. package/src/package/code-inline/dist/index.js +0 -106
  109. package/src/package/code-inline/dist/index.mjs +0 -72
  110. package/src/package/column/dist/index.d.mts +0 -6
  111. package/src/package/column/dist/index.d.ts +0 -6
  112. package/src/package/column/dist/index.js +0 -79
  113. package/src/package/column/dist/index.mjs +0 -45
  114. package/src/package/components/dist/index.d.mts +0 -20
  115. package/src/package/components/dist/index.d.ts +0 -20
  116. package/src/package/components/dist/index.js +0 -62
  117. package/src/package/components/dist/index.mjs +0 -21
  118. package/src/package/container/dist/index.d.mts +0 -6
  119. package/src/package/container/dist/index.d.ts +0 -6
  120. package/src/package/container/dist/index.js +0 -93
  121. package/src/package/container/dist/index.mjs +0 -59
  122. package/src/package/font/dist/index.d.mts +0 -25
  123. package/src/package/font/dist/index.d.ts +0 -25
  124. package/src/package/font/dist/index.js +0 -55
  125. package/src/package/font/dist/index.mjs +0 -28
  126. package/src/package/head/dist/index.d.mts +0 -6
  127. package/src/package/head/dist/index.d.ts +0 -6
  128. package/src/package/head/dist/index.js +0 -83
  129. package/src/package/head/dist/index.mjs +0 -49
  130. package/src/package/heading/dist/index.d.mts +0 -43
  131. package/src/package/heading/dist/index.d.ts +0 -43
  132. package/src/package/heading/dist/index.js +0 -113
  133. package/src/package/heading/dist/index.mjs +0 -79
  134. package/src/package/hr/dist/index.d.mts +0 -6
  135. package/src/package/hr/dist/index.d.ts +0 -6
  136. package/src/package/hr/dist/index.js +0 -89
  137. package/src/package/hr/dist/index.mjs +0 -55
  138. package/src/package/html/dist/index.d.mts +0 -6
  139. package/src/package/html/dist/index.d.ts +0 -6
  140. package/src/package/html/dist/index.js +0 -79
  141. package/src/package/html/dist/index.mjs +0 -45
  142. package/src/package/img/dist/index.d.mts +0 -6
  143. package/src/package/img/dist/index.d.ts +0 -6
  144. package/src/package/img/dist/index.js +0 -94
  145. package/src/package/img/dist/index.mjs +0 -60
  146. package/src/package/link/dist/index.d.mts +0 -6
  147. package/src/package/link/dist/index.d.ts +0 -6
  148. package/src/package/link/dist/index.js +0 -90
  149. package/src/package/link/dist/index.mjs +0 -56
  150. package/src/package/markdown/dist/index.d.mts +0 -15
  151. package/src/package/markdown/dist/index.d.ts +0 -15
  152. package/src/package/markdown/dist/index.js +0 -92
  153. package/src/package/markdown/dist/index.mjs +0 -58
  154. package/src/package/preview/dist/index.d.mts +0 -12
  155. package/src/package/preview/dist/index.d.ts +0 -12
  156. package/src/package/preview/dist/index.js +0 -108
  157. package/src/package/preview/dist/index.mjs +0 -73
  158. package/src/package/render/dist/browser/index.d.mts +0 -24
  159. package/src/package/render/dist/browser/index.d.ts +0 -24
  160. package/src/package/render/dist/browser/index.js +0 -250
  161. package/src/package/render/dist/browser/index.mjs +0 -214
  162. package/src/package/render/dist/index.d.mts +0 -23
  163. package/src/package/render/dist/index.d.ts +0 -23
  164. package/src/package/render/dist/index.js +0 -768
  165. package/src/package/render/dist/index.mjs +0 -733
  166. package/src/package/render/dist/node/index.d.mts +0 -27
  167. package/src/package/render/dist/node/index.d.ts +0 -27
  168. package/src/package/render/dist/node/index.js +0 -212
  169. package/src/package/render/dist/node/index.mjs +0 -176
  170. package/src/package/row/dist/index.d.mts +0 -10
  171. package/src/package/row/dist/index.d.ts +0 -10
  172. package/src/package/row/dist/index.js +0 -93
  173. package/src/package/row/dist/index.mjs +0 -59
  174. package/src/package/section/dist/index.d.mts +0 -6
  175. package/src/package/section/dist/index.d.ts +0 -6
  176. package/src/package/section/dist/index.js +0 -93
  177. package/src/package/section/dist/index.mjs +0 -59
  178. package/src/package/tailwind/dist/index.d.ts +0 -19
  179. package/src/package/tailwind/dist/index.js +0 -48
  180. package/src/package/tailwind/dist/index.mjs +0 -17167
  181. package/src/package/tailwind/dist/tailwindcss/config.d.ts +0 -376
  182. package/src/package/tailwind/dist/tailwindcss/generated/.gitkeep +0 -0
  183. package/src/package/tailwind/dist/tailwindcss/generated/colors.d.ts +0 -298
  184. package/src/package/tailwind/dist/tailwindcss/generated/corePluginList.d.ts +0 -1
  185. package/src/package/tailwind/dist/tailwindcss/generated/default-theme.d.ts +0 -397
  186. package/src/package/tailwind/dist/tailwindcss/index.d.ts +0 -11
  187. package/src/package/text/dist/index.d.mts +0 -6
  188. package/src/package/text/dist/index.d.ts +0 -6
  189. package/src/package/text/dist/index.js +0 -89
  190. package/src/package/text/dist/index.mjs +0 -55
  191. /package/dist/preview/.next/static/{6K68y2QEZ1dLbv-Xhi30p → 3Ni4t_kYMdpL72HxSPHgK}/_buildManifest.js +0 -0
  192. /package/dist/preview/.next/static/{6K68y2QEZ1dLbv-Xhi30p → 3Ni4t_kYMdpL72HxSPHgK}/_ssgManifest.js +0 -0
@@ -1,4 +1,4 @@
1
- // File: /home/gabriel/Projects/resend/react-email/packages/react-email/src/app/layout.tsx
1
+ // File: /home/gabriel/Projects/Resend/react-email.git/tertiary/packages/react-email/src/app/layout.tsx
2
2
  import * as entry from '../../../src/app/layout.js'
3
3
  import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
4
4
 
@@ -1,4 +1,4 @@
1
- // File: /home/gabriel/Projects/resend/react-email/packages/react-email/src/app/preview/[...slug]/page.tsx
1
+ // File: /home/gabriel/Projects/Resend/react-email.git/tertiary/packages/react-email/src/app/preview/[...slug]/page.tsx
2
2
  import * as entry from '../../../../../src/app/preview/[...slug]/page.js'
3
3
  import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-email",
3
- "version": "3.0.5",
3
+ "version": "3.0.7-canary.0",
4
4
  "description": "A live preview of your emails right in your browser.",
5
5
  "bin": {
6
6
  "email": "./dist/cli/index.js"
@@ -25,7 +25,7 @@
25
25
  "chokidar": "4.0.3",
26
26
  "commander": "11.1.0",
27
27
  "debounce": "2.0.0",
28
- "esbuild": "0.19.11",
28
+ "esbuild": "0.23.0",
29
29
  "glob": "10.3.4",
30
30
  "log-symbols": "4.1.0",
31
31
  "mime-types": "2.1.35",
@@ -53,9 +53,6 @@
53
53
  "@vercel/style-guide": "5.1.0",
54
54
  "autoprefixer": "10.4.20",
55
55
  "clsx": "2.1.0",
56
- "eslint": "8.50.0",
57
- "eslint-config-prettier": "9.0.0",
58
- "eslint-config-turbo": "2.1.0",
59
56
  "framer-motion": "12.0.0-alpha.2",
60
57
  "postcss": "8.4.40",
61
58
  "prism-react-renderer": "2.1.0",
@@ -72,14 +69,13 @@
72
69
  "tsx": "4.9.0",
73
70
  "typescript": "5.1.6",
74
71
  "vitest": "1.1.3",
75
- "@react-email/render": "1.0.4"
72
+ "@react-email/render": "1.0.5-canary.0"
76
73
  },
77
74
  "scripts": {
78
75
  "build": "tsup-node && node build-preview-server.mjs",
79
76
  "dev": "tsup-node --watch",
80
77
  "test": "vitest run",
81
78
  "test:watch": "vitest",
82
- "clean": "rm -rf dist",
83
- "lint": "eslint . && tsc"
79
+ "clean": "rm -rf dist"
84
80
  }
85
81
  }
package/postcss.config.js CHANGED
@@ -5,4 +5,4 @@ module.exports = {
5
5
  tailwindcss: { config: path.resolve(__dirname, 'tailwind.config.ts') },
6
6
  autoprefixer: {},
7
7
  },
8
- }
8
+ };
@@ -1,6 +1,6 @@
1
1
  'use server';
2
- import path from 'node:path';
3
2
  import fs from 'node:fs';
3
+ import path from 'node:path';
4
4
  import { cache } from 'react';
5
5
  import { emailsDirectoryAbsolutePath } from '../utils/emails-directory-absolute-path';
6
6
 
@@ -13,11 +13,14 @@ export const getEmailPathFromSlug = cache(async (slug: string) => {
13
13
 
14
14
  if (fs.existsSync(`${pathWithoutExtension}.tsx`)) {
15
15
  return `${pathWithoutExtension}.tsx`;
16
- } else if (fs.existsSync(`${pathWithoutExtension}.jsx`)) {
16
+ }
17
+ if (fs.existsSync(`${pathWithoutExtension}.jsx`)) {
17
18
  return `${pathWithoutExtension}.jsx`;
18
- } else if (fs.existsSync(`${pathWithoutExtension}.ts`)) {
19
+ }
20
+ if (fs.existsSync(`${pathWithoutExtension}.ts`)) {
19
21
  return `${pathWithoutExtension}.ts`;
20
- } else if (fs.existsSync(`${pathWithoutExtension}.js`)) {
22
+ }
23
+ if (fs.existsSync(`${pathWithoutExtension}.js`)) {
21
24
  return `${pathWithoutExtension}.js`;
22
25
  }
23
26
 
@@ -0,0 +1,19 @@
1
+ 'use server';
2
+
3
+ import type { EmailsDirectory } from '../utils/get-emails-directory-metadata';
4
+ import { getEmailsDirectoryMetadata } from '../utils/get-emails-directory-metadata';
5
+
6
+ export const getEmailsDirectoryMetadataAction = async (
7
+ absolutePathToEmailsDirectory: string,
8
+ keepFileExtensions = false,
9
+ isSubDirectory = false,
10
+
11
+ baseDirectoryPath = absolutePathToEmailsDirectory,
12
+ ): Promise<EmailsDirectory | undefined> => {
13
+ return getEmailsDirectoryMetadata(
14
+ absolutePathToEmailsDirectory,
15
+ keepFileExtensions,
16
+ isSubDirectory,
17
+ baseDirectoryPath,
18
+ );
19
+ };
@@ -1,13 +1,13 @@
1
1
  'use server';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import ora from 'ora';
5
- import logSymbols from 'log-symbols';
6
4
  import chalk from 'chalk';
5
+ import logSymbols from 'log-symbols';
6
+ import ora from 'ora';
7
7
  import { getEmailComponent } from '../utils/get-email-component';
8
- import type { ErrorObject } from '../utils/types/error-object';
9
8
  import { improveErrorWithSourceMap } from '../utils/improve-error-with-sourcemap';
10
9
  import { registerSpinnerAutostopping } from '../utils/register-spinner-autostopping';
10
+ import type { ErrorObject } from '../utils/types/error-object';
11
11
 
12
12
  export interface RenderedEmailMetadata {
13
13
  markup: string;
@@ -1,8 +1,8 @@
1
1
  import type { Metadata } from 'next';
2
2
  import './globals.css';
3
- import { getEmailsDirectoryMetadata } from '../actions/get-emails-directory-metadata';
4
- import { emailsDirectoryAbsolutePath } from '../utils/emails-directory-absolute-path';
5
3
  import { EmailsProvider } from '../contexts/emails';
4
+ import { emailsDirectoryAbsolutePath } from '../utils/emails-directory-absolute-path';
5
+ import { getEmailsDirectoryMetadata } from '../utils/get-emails-directory-metadata';
6
6
  import { inter } from './inter';
7
7
 
8
8
  export const metadata: Metadata = {
package/src/app/page.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
- import Link from 'next/link';
3
2
  import Image from 'next/image';
3
+ import Link from 'next/link';
4
4
  import { Button, Heading, Text } from '../components';
5
5
  import { Shell } from '../components/shell';
6
6
  import { emailsDirectoryAbsolutePath } from '../utils/emails-directory-absolute-path';
@@ -1,10 +1,10 @@
1
1
  import path from 'node:path';
2
- import { Suspense } from 'react';
3
2
  import { redirect } from 'next/navigation';
3
+ import { Suspense } from 'react';
4
4
  import { getEmailPathFromSlug } from '../../../actions/get-email-path-from-slug';
5
- import { getEmailsDirectoryMetadata } from '../../../actions/get-emails-directory-metadata';
6
5
  import { renderEmailByPath } from '../../../actions/render-email-by-path';
7
6
  import { emailsDirectoryAbsolutePath } from '../../../utils/emails-directory-absolute-path';
7
+ import { getEmailsDirectoryMetadata } from '../../../utils/get-emails-directory-metadata';
8
8
  import Home from '../../page';
9
9
  import Preview from './preview';
10
10
 
@@ -3,13 +3,13 @@
3
3
  import { usePathname, useRouter, useSearchParams } from 'next/navigation';
4
4
  import React from 'react';
5
5
  import { Toaster } from 'sonner';
6
- import { useHotreload } from '../../../hooks/use-hot-reload';
7
6
  import type { EmailRenderingResult } from '../../../actions/render-email-by-path';
8
7
  import { CodeContainer } from '../../../components/code-container';
9
8
  import { Shell } from '../../../components/shell';
10
9
  import { Tooltip } from '../../../components/tooltip';
11
- import { useRenderingMetadata } from '../../../hooks/use-rendering-metadata';
12
10
  import { useEmailRenderingResult } from '../../../hooks/use-email-rendering-result';
11
+ import { useHotreload } from '../../../hooks/use-hot-reload';
12
+ import { useRenderingMetadata } from '../../../hooks/use-rendering-metadata';
13
13
  import { RenderingError } from './rendering-error';
14
14
 
15
15
  interface PreviewProps {
@@ -1,7 +1,7 @@
1
1
  import * as SlotPrimitive from '@radix-ui/react-slot';
2
2
  import * as React from 'react';
3
- import { unreachable } from '../utils/unreachable';
4
3
  import { cn } from '../utils/cn';
4
+ import { unreachable } from '../utils/unreachable';
5
5
 
6
6
  type ButtonElement = React.ElementRef<'button'>;
7
7
  type RootProps = React.ComponentPropsWithoutRef<'button'>;
@@ -2,8 +2,8 @@ import { LayoutGroup, motion } from 'framer-motion';
2
2
  import type { Language } from 'prism-react-renderer';
3
3
  import * as React from 'react';
4
4
  import { copyTextToClipboard } from '../utils';
5
- import languageMap from '../utils/language-map';
6
5
  import { tabTransition } from '../utils/constants';
6
+ import languageMap from '../utils/language-map';
7
7
  import { Code } from './code';
8
8
  import { IconButton } from './icons/icon-button';
9
9
  import { IconCheck } from './icons/icon-check';
@@ -1,6 +1,6 @@
1
1
  import * as SlotPrimitive from '@radix-ui/react-slot';
2
2
  import * as React from 'react';
3
- import { type As, unreachable, cn } from '../utils';
3
+ import { type As, cn, unreachable } from '../utils';
4
4
 
5
5
  export type HeadingSize =
6
6
  | '1'
@@ -1,10 +1,9 @@
1
- import { AnimatePresence, LayoutGroup, motion } from 'framer-motion';
2
1
  import * as Collapsible from '@radix-ui/react-collapsible';
2
+ import { AnimatePresence, LayoutGroup, motion } from 'framer-motion';
3
3
  import Link from 'next/link';
4
4
  import { useSearchParams } from 'next/navigation';
5
- import type { EmailsDirectory } from '../../actions/get-emails-directory-metadata';
6
- import { emailsDirectoryAbsolutePath } from '../../utils/emails-directory-absolute-path';
7
5
  import { cn } from '../../utils';
6
+ import type { EmailsDirectory } from '../../utils/get-emails-directory-metadata';
8
7
  import { IconFile } from '../icons/icon-file';
9
8
  import { SidebarDirectory } from './sidebar-directory';
10
9
 
@@ -15,15 +14,13 @@ export const SidebarDirectoryChildren = (props: {
15
14
  isRoot?: boolean;
16
15
  }) => {
17
16
  const searchParams = useSearchParams();
18
- const isBaseEmailsDirectory =
19
- props.emailsDirectoryMetadata.absolutePath === emailsDirectoryAbsolutePath;
20
17
 
21
18
  return (
22
19
  <AnimatePresence initial={false}>
23
20
  {props.open ? (
24
21
  <Collapsible.Content
25
22
  asChild
26
- className="relative data-[root=true]:mt-2 overflow-y-hidden pl-1"
23
+ className="relative overflow-y-hidden pl-1"
27
24
  forceMount
28
25
  >
29
26
  <motion.div
@@ -35,7 +32,7 @@ export const SidebarDirectoryChildren = (props: {
35
32
  <div className="line absolute left-2.5 w-px h-full bg-slate-6" />
36
33
  )}
37
34
 
38
- <div className="data-[root=true]:py-2 flex flex-col truncate">
35
+ <div className="flex flex-col truncate">
39
36
  <LayoutGroup id="sidebar">
40
37
  {props.emailsDirectoryMetadata.subDirectories.map(
41
38
  (subDirectory) => (
@@ -50,7 +47,7 @@ export const SidebarDirectoryChildren = (props: {
50
47
 
51
48
  {props.emailsDirectoryMetadata.emailFilenames.map(
52
49
  (emailFilename, index) => {
53
- const emailSlug = isBaseEmailsDirectory
50
+ const emailSlug = props.isRoot
54
51
  ? emailFilename
55
52
  : `${props.emailsDirectoryMetadata.relativePath}/${emailFilename}`;
56
53
 
@@ -2,11 +2,11 @@
2
2
  import * as Collapsible from '@radix-ui/react-collapsible';
3
3
  import * as React from 'react';
4
4
  import { cn } from '../../utils';
5
- import { type EmailsDirectory } from '../../actions/get-emails-directory-metadata';
5
+ import type { EmailsDirectory } from '../../utils/get-emails-directory-metadata';
6
6
  import { Heading } from '../heading';
7
+ import { IconArrowDown } from '../icons/icon-arrow-down';
7
8
  import { IconFolder } from '../icons/icon-folder';
8
9
  import { IconFolderOpen } from '../icons/icon-folder-open';
9
- import { IconArrowDown } from '../icons/icon-arrow-down';
10
10
  import { SidebarDirectoryChildren } from './sidebar-directory-children';
11
11
 
12
12
  interface SidebarDirectoryProps {
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
- import * as React from 'react';
4
3
  import * as Collapsible from '@radix-ui/react-collapsible';
4
+ import * as React from 'react';
5
5
  import { useEmails } from '../../contexts/emails';
6
6
  import { cn } from '../../utils';
7
7
  import { Logo } from '../logo';
@@ -29,7 +29,7 @@ export const Sidebar = ({
29
29
  <Logo />
30
30
  </div>
31
31
  <nav className="p-4 flex-grow lg:pt-0 pl-0 w-screen h-[calc(100vh_-_70px)] lg:w-full lg:min-w-[275px] lg:max-w-[275px] flex flex-col overflow-y-auto">
32
- <Collapsible.Root>
32
+ <Collapsible.Root open>
33
33
  <React.Suspense>
34
34
  <SidebarDirectoryChildren
35
35
  currentEmailOpenSlug={currentEmailOpenSlug}
@@ -1,6 +1,6 @@
1
1
  import * as SlotPrimitive from '@radix-ui/react-slot';
2
2
  import * as React from 'react';
3
- import { type As, unreachable, cn } from '../utils';
3
+ import { type As, cn, unreachable } from '../utils';
4
4
 
5
5
  export type TextSize = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
6
6
  export type TextColor = 'gray' | 'white';
@@ -1,7 +1,7 @@
1
1
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
2
2
  import * as React from 'react';
3
- import { cn } from '../utils';
4
3
  import { inter } from '../app/inter';
4
+ import { cn } from '../utils';
5
5
 
6
6
  type ContentElement = React.ElementRef<typeof TooltipPrimitive.Content>;
7
7
  type ContentProps = React.ComponentPropsWithoutRef<
@@ -1,5 +1,5 @@
1
1
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
2
- import * as React from 'react';
2
+ import type * as React from 'react';
3
3
  import { TooltipContent } from './tooltip-content';
4
4
 
5
5
  type RootProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>;
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import * as ToggleGroup from '@radix-ui/react-toggle-group';
3
3
  import { motion } from 'framer-motion';
4
- import * as React from 'react';
4
+ import type * as React from 'react';
5
5
  import { cn } from '../utils';
6
6
  import { tabTransition } from '../utils/constants';
7
7
  import { Heading } from './heading';
@@ -1,10 +1,8 @@
1
1
  'use client';
2
2
  import { createContext, useContext, useState } from 'react';
3
- import {
4
- getEmailsDirectoryMetadata,
5
- type EmailsDirectory,
6
- } from '../actions/get-emails-directory-metadata';
3
+ import { getEmailsDirectoryMetadataAction } from '../actions/get-emails-directory-metadata-action';
7
4
  import { useHotreload } from '../hooks/use-hot-reload';
5
+ import type { EmailsDirectory } from '../utils/get-emails-directory-metadata';
8
6
 
9
7
  const EmailsContext = createContext<
10
8
  | {
@@ -37,7 +35,7 @@ export const EmailsProvider = (props: {
37
35
  // the rules of hooks
38
36
  // eslint-disable-next-line react-hooks/rules-of-hooks
39
37
  useHotreload(async () => {
40
- const metadata = await getEmailsDirectoryMetadata(
38
+ const metadata = await getEmailsDirectoryMetadataAction(
41
39
  props.initialEmailsDirectoryMetadata.absolutePath,
42
40
  );
43
41
  if (metadata) {
@@ -1,9 +1,9 @@
1
1
  import { useState } from 'react';
2
+ import { getEmailPathFromSlug } from '../actions/get-email-path-from-slug';
2
3
  import {
3
- renderEmailByPath,
4
4
  type EmailRenderingResult,
5
+ renderEmailByPath,
5
6
  } from '../actions/render-email-by-path';
6
- import { getEmailPathFromSlug } from '../actions/get-email-path-from-slug';
7
7
  import { useHotreload } from './use-hot-reload';
8
8
 
9
9
  export const useEmailRenderingResult = (
package/src/utils/cn.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { clsx, type ClassValue } from 'clsx';
1
+ import { type ClassValue, clsx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
3
 
4
4
  export const cn = (...inputs: ClassValue[]) => {
@@ -1,5 +1,5 @@
1
- import path from 'node:path';
2
1
  import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
3
  import type { Loader, PluginBuild, ResolveOptions } from 'esbuild';
4
4
  import { escapeStringForRegex } from './escape-string-for-regex';
5
5
 
@@ -1,15 +1,15 @@
1
1
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
2
  import path from 'node:path';
3
3
  import vm from 'node:vm';
4
- import type React from 'react';
5
- import { type RawSourceMap } from 'source-map-js';
6
- import { type OutputFile, build, type BuildFailure } from 'esbuild';
7
4
  import type { render } from '@react-email/render';
8
- import type { EmailTemplate as EmailComponent } from './types/email-template';
9
- import type { ErrorObject } from './types/error-object';
5
+ import { type BuildFailure, type OutputFile, build } from 'esbuild';
6
+ import type React from 'react';
7
+ import type { RawSourceMap } from 'source-map-js';
8
+ import { renderingUtilitiesExporter } from './esbuild/renderring-utilities-exporter';
10
9
  import { improveErrorWithSourceMap } from './improve-error-with-sourcemap';
11
10
  import { staticNodeModulesForVM } from './static-node-modules-for-vm';
12
- import { renderingUtilitiesExporter } from './esbuild/renderring-utilities-exporter';
11
+ import type { EmailTemplate as EmailComponent } from './types/email-template';
12
+ import type { ErrorObject } from './types/error-object';
13
13
 
14
14
  export const getEmailComponent = async (
15
15
  emailPath: string,
@@ -4,7 +4,7 @@ import { getEmailsDirectoryMetadata } from './get-emails-directory-metadata';
4
4
  test('getEmailsDirectoryMetadata on demo emails', async () => {
5
5
  const emailsDirectoryPath = path.resolve(
6
6
  __dirname,
7
- '../../../../apps/demo/emails/',
7
+ '../../../../apps/demo/emails',
8
8
  );
9
9
  expect(await getEmailsDirectoryMetadata(emailsDirectoryPath)).toEqual({
10
10
  absolutePath: emailsDirectoryPath,
@@ -18,7 +18,6 @@ test('getEmailsDirectoryMetadata on demo emails', async () => {
18
18
  relativePath: 'magic-links',
19
19
  emailFilenames: [
20
20
  'aws-verify-email',
21
- 'jobaccepted-magic-link',
22
21
  'linear-login-code',
23
22
  'notion-magic-link',
24
23
  'plaid-verify-identity',
@@ -0,0 +1,119 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ const isFileAnEmail = (fullPath: string): boolean => {
6
+ const stat = fs.statSync(fullPath);
7
+
8
+ if (stat.isDirectory()) return false;
9
+
10
+ const { ext } = path.parse(fullPath);
11
+
12
+ if (!['.js', '.tsx', '.jsx'].includes(ext)) return false;
13
+
14
+ // This is to avoid a possible race condition where the file doesn't exist anymore
15
+ // once we are checking if it is an actual email, this couuld cause issues that
16
+ // would be very hard to debug and find out the why of it happening.
17
+ if (!fs.existsSync(fullPath)) {
18
+ return false;
19
+ }
20
+
21
+ // check with a heuristic to see if the file has at least
22
+ // a default export
23
+ const fileContents = fs.readFileSync(fullPath, 'utf8');
24
+
25
+ return /\bexport\s+default\b/gm.test(fileContents);
26
+ };
27
+
28
+ export interface EmailsDirectory {
29
+ absolutePath: string;
30
+ relativePath: string;
31
+ directoryName: string;
32
+ emailFilenames: string[];
33
+ subDirectories: EmailsDirectory[];
34
+ }
35
+
36
+ const mergeDirectoriesWithSubDirectories = (
37
+ emailsDirectoryMetadata: EmailsDirectory,
38
+ ): EmailsDirectory => {
39
+ let currentResultingMergedDirectory: EmailsDirectory =
40
+ emailsDirectoryMetadata;
41
+
42
+ while (
43
+ currentResultingMergedDirectory.emailFilenames.length === 0 &&
44
+ currentResultingMergedDirectory.subDirectories.length === 1
45
+ ) {
46
+ const onlySubDirectory = currentResultingMergedDirectory.subDirectories[0]!;
47
+ currentResultingMergedDirectory = {
48
+ ...onlySubDirectory,
49
+ directoryName: path.join(
50
+ currentResultingMergedDirectory.directoryName,
51
+ onlySubDirectory.directoryName,
52
+ ),
53
+ };
54
+ }
55
+
56
+ return currentResultingMergedDirectory;
57
+ };
58
+
59
+ export const getEmailsDirectoryMetadata = async (
60
+ absolutePathToEmailsDirectory: string,
61
+ keepFileExtensions = false,
62
+ isSubDirectory = false,
63
+
64
+ baseDirectoryPath = absolutePathToEmailsDirectory,
65
+ ): Promise<EmailsDirectory | undefined> => {
66
+ if (!fs.existsSync(absolutePathToEmailsDirectory)) return;
67
+
68
+ const dirents = await fs.promises.readdir(absolutePathToEmailsDirectory, {
69
+ withFileTypes: true,
70
+ });
71
+
72
+ const emailFilenames = dirents
73
+ .filter((dirent) =>
74
+ isFileAnEmail(path.join(absolutePathToEmailsDirectory, dirent.name)),
75
+ )
76
+ .map((dirent) =>
77
+ keepFileExtensions
78
+ ? dirent.name
79
+ : dirent.name.replace(path.extname(dirent.name), ''),
80
+ );
81
+
82
+ const subDirectories = await Promise.all(
83
+ dirents
84
+ .filter(
85
+ (dirent) =>
86
+ dirent.isDirectory() &&
87
+ !dirent.name.startsWith('_') &&
88
+ dirent.name !== 'static',
89
+ )
90
+ .map((dirent) => {
91
+ const direntAbsolutePath = path.join(
92
+ absolutePathToEmailsDirectory,
93
+ dirent.name,
94
+ );
95
+
96
+ return getEmailsDirectoryMetadata(
97
+ direntAbsolutePath,
98
+ keepFileExtensions,
99
+ true,
100
+ baseDirectoryPath,
101
+ ) as Promise<EmailsDirectory>;
102
+ }),
103
+ );
104
+
105
+ const emailsMetadata = {
106
+ absolutePath: absolutePathToEmailsDirectory,
107
+ relativePath: path.relative(
108
+ baseDirectoryPath,
109
+ absolutePathToEmailsDirectory,
110
+ ),
111
+ directoryName: absolutePathToEmailsDirectory.split(path.sep).pop()!,
112
+ emailFilenames,
113
+ subDirectories,
114
+ } satisfies EmailsDirectory;
115
+
116
+ return isSubDirectory
117
+ ? mergeDirectoriesWithSubDirectories(emailsMetadata)
118
+ : emailsMetadata;
119
+ };
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
+ import { type RawSourceMap, SourceMapConsumer } from 'source-map-js';
2
3
  import * as stackTraceParser from 'stacktrace-parser';
3
- import { SourceMapConsumer, type RawSourceMap } from 'source-map-js';
4
4
  import type { ErrorObject } from './types/error-object';
5
5
 
6
6
  export const improveErrorWithSourceMap = (
@@ -48,15 +48,15 @@ import zlib from 'node:zlib';
48
48
  */
49
49
  export const staticNodeModulesForVM = {
50
50
  assert,
51
- 'async_hooks': asyncHooks,
51
+ async_hooks: asyncHooks,
52
52
  buffer,
53
- 'child_process': childProcess,
53
+ child_process: childProcess,
54
54
  cluster,
55
55
  console,
56
56
  constants,
57
57
  crypto,
58
58
  dgram,
59
- 'diagnostics_channel': diagnosticsChannel,
59
+ diagnostics_channel: diagnosticsChannel,
60
60
  dns,
61
61
  domain,
62
62
  events,
@@ -70,14 +70,14 @@ export const staticNodeModulesForVM = {
70
70
  net,
71
71
  os,
72
72
  path,
73
- 'perf_hooks': perfHooks,
73
+ perf_hooks: perfHooks,
74
74
  process,
75
75
  punycode,
76
76
  querystring,
77
77
  readline,
78
78
  repl,
79
79
  stream,
80
- 'string_decoder': stringDecoder,
80
+ string_decoder: stringDecoder,
81
81
  timers,
82
82
  'timers/promises': timersPromises,
83
83
  tls,
@@ -87,6 +87,6 @@ export const staticNodeModulesForVM = {
87
87
  'util/types': utilTypes,
88
88
  v8,
89
89
  vm,
90
- 'worker_threads': workerThreads,
90
+ worker_threads: workerThreads,
91
91
  zlib,
92
92
  };
package/tsconfig.json CHANGED
@@ -30,10 +30,5 @@
30
30
  "outDir": "dist"
31
31
  },
32
32
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
33
- "exclude": [
34
- "tsconfig.export.json",
35
- ".next",
36
- "dist",
37
- "node_modules"
38
- ]
33
+ "exclude": ["tsconfig.export.json", ".next", "dist", "node_modules"]
39
34
  }