11import fs from 'node:fs' ;
22import path from 'node:path' ;
3+ import { getPackages } from '@manypkg/get-packages' ;
34import logSymbols from 'log-symbols' ;
4- import { installDependencies , type PackageManagerName , runScript } from 'nypm' ;
5+ import { type PackageManagerName , runScript } from 'nypm' ;
56import ora from 'ora' ;
67import {
78 type EmailsDirectory ,
@@ -17,13 +18,15 @@ interface Args {
1718
1819const setNextEnvironmentVariablesForBuild = async (
1920 emailsDirRelativePath : string ,
20- builtPreviewAppPath : string ,
21+ appPath : string ,
22+ rootDirectory : string ,
2123) => {
2224 const nextConfigContents = `
2325import path from 'path';
2426const emailsDirRelativePath = path.normalize('${ emailsDirRelativePath } ');
2527const userProjectLocation = '${ process . cwd ( ) . replace ( / \\ / g, '/' ) } ';
26- const previewServerLocation = '${ builtPreviewAppPath . replace ( / \\ / g, '/' ) } ';
28+ const previewServerLocation = '${ appPath . replace ( / \\ / g, '/' ) } ';
29+ const rootDirectory = '${ rootDirectory . replace ( / \\ / g, '/' ) } ';
2730/** @type {import('next').NextConfig} */
2831const nextConfig = {
2932 env: {
@@ -33,7 +36,10 @@ const nextConfig = {
3336 PREVIEW_SERVER_LOCATION: previewServerLocation,
3437 USER_PROJECT_LOCATION: userProjectLocation
3538 },
36- outputFileTracingRoot: previewServerLocation,
39+ turbopack: {
40+ root: rootDirectory,
41+ },
42+ outputFileTracingRoot: rootDirectory,
3743 serverExternalPackages: ['esbuild'],
3844 typescript: {
3945 ignoreBuildErrors: true
@@ -44,7 +50,7 @@ const nextConfig = {
4450export default nextConfig` ;
4551
4652 await fs . promises . writeFile (
47- path . resolve ( builtPreviewAppPath , './next.config.mjs' ) ,
53+ path . resolve ( appPath , './next.config.mjs' ) ,
4854 nextConfigContents ,
4955 'utf8' ,
5056 ) ;
@@ -84,7 +90,7 @@ const getEmailSlugsFromEmailDirectory = (
8490// after build
8591const forceSSGForEmailPreviews = async (
8692 emailsDirPath : string ,
87- builtPreviewAppPath : string ,
93+ appPath : string ,
8894) => {
8995 const emailDirectoryMetadata = ( await getEmailsDirectoryMetadata (
9096 emailsDirPath ,
@@ -104,15 +110,13 @@ const forceSSGForEmailPreviews = async (
104110 'utf8' ,
105111 ) ;
106112 } ;
113+ await removeForceDynamic ( path . resolve ( appPath , './src/app/layout.tsx' ) ) ;
107114 await removeForceDynamic (
108- path . resolve ( builtPreviewAppPath , './src/app/layout.tsx' ) ,
109- ) ;
110- await removeForceDynamic (
111- path . resolve ( builtPreviewAppPath , './src/app/preview/[...slug]/page.tsx' ) ,
115+ path . resolve ( appPath , './src/app/preview/[...slug]/page.tsx' ) ,
112116 ) ;
113117
114118 await fs . promises . appendFile (
115- path . resolve ( builtPreviewAppPath , './src/app/preview/[...slug]/page.tsx' ) ,
119+ path . resolve ( appPath , './src/app/preview/[...slug]/page.tsx' ) ,
116120 `
117121
118122export function generateStaticParams() {
@@ -124,8 +128,8 @@ export function generateStaticParams() {
124128 ) ;
125129} ;
126130
127- const updatePackageJson = async ( builtPreviewAppPath : string ) => {
128- const packageJsonPath = path . resolve ( builtPreviewAppPath , './package.json' ) ;
131+ const updatePackageJson = async ( appPath : string ) => {
132+ const packageJsonPath = path . resolve ( appPath , './package.json' ) ;
129133 const packageJson = JSON . parse (
130134 await fs . promises . readFile ( packageJsonPath , 'utf8' ) ,
131135 ) as {
@@ -134,22 +138,11 @@ const updatePackageJson = async (builtPreviewAppPath: string) => {
134138 dependencies : Record < string , string > ;
135139 devDependencies : Record < string , string > ;
136140 } ;
137- // Turbopack has some errors with the imports in @react -email/tailwind
138141 packageJson . scripts . build =
139142 'cross-env NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning" next build' ;
140143 packageJson . scripts . start =
141144 'cross-env NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning" next start' ;
142- delete packageJson . scripts . postbuild ;
143-
144- packageJson . name = 'preview-server' ;
145145
146- for ( const [ dependency , version ] of Object . entries (
147- packageJson . devDependencies ,
148- ) ) {
149- packageJson . devDependencies [ dependency ] = version . replace ( 'workspace:' , '' ) ;
150- }
151-
152- delete packageJson . devDependencies [ '@react-email/components' ] ;
153146 delete packageJson . scripts . prepare ;
154147
155148 await fs . promises . writeFile (
@@ -165,6 +158,7 @@ export const build = async ({
165158} : Args ) => {
166159 try {
167160 const previewServerLocation = await getPreviewServerLocation ( ) ;
161+ const { rootDir : rootDirectory } = await getPackages ( process . cwd ( ) ) ;
168162
169163 const spinner = ora ( {
170164 text : 'Starting build process...' ,
@@ -174,6 +168,10 @@ export const build = async ({
174168
175169 spinner . text = `Checking if ${ emailsDirRelativePath } folder exists` ;
176170 if ( ! fs . existsSync ( emailsDirRelativePath ) ) {
171+ spinner . stopAndPersist ( {
172+ symbol : logSymbols . error ,
173+ text : `Directory does not exist: ${ emailsDirRelativePath } ` ,
174+ } ) ;
177175 process . exit ( 1 ) ;
178176 }
179177
@@ -187,20 +185,32 @@ export const build = async ({
187185 await fs . promises . rm ( builtPreviewAppPath , { recursive : true } ) ;
188186 }
189187
190- spinner . text = 'Copying preview app from CLI to `.react-email`' ;
188+ spinner . text = 'Copying UI source to `.react-email`' ;
191189 await fs . promises . cp ( previewServerLocation , builtPreviewAppPath , {
192190 recursive : true ,
193191 filter : ( source : string ) => {
194- // do not copy the CLI files
192+ const relativeSource = path . relative ( previewServerLocation , source ) ;
195193 return (
196- ! / ( \/ | \\ ) c l i ( \/ | \\ ) ? / . test ( source ) &&
197- ! / ( \/ | \\ ) \. n e x t ( \/ | \\ ) ? / . test ( source ) &&
198- ! / ( \/ | \\ ) \. t u r b o ( \/ | \\ ) ? / . test ( source ) &&
199- ! / ( \/ | \\ ) n o d e _ m o d u l e s ( \/ | \\ ) ? $ / . test ( source )
194+ ! / \. n e x t / . test ( relativeSource ) &&
195+ ! / \. t u r b o / . test ( relativeSource ) &&
196+ ! / n o d e _ m o d u l e s \/ .b i n / . test ( relativeSource )
200197 ) ;
201198 } ,
202199 } ) ;
203200
201+ await fs . promises . cp (
202+ path . resolve ( previewServerLocation , 'node_modules/.bin' ) ,
203+ path . resolve ( builtPreviewAppPath , 'node_modules/.bin' ) ,
204+ {
205+ recursive : true ,
206+ // With this enabled, means the symlinks remain relative, which is
207+ // what we want for copying it over. If we don't enable this,
208+ // it resolves the symlink to the original location on disk,
209+ // which ends up causing unexpected errors.
210+ verbatimSymlinks : true ,
211+ } ,
212+ ) ;
213+
204214 if ( fs . existsSync ( staticPath ) ) {
205215 spinner . text =
206216 'Copying `static` folder into `.react-email/public/static`' ;
@@ -218,6 +228,7 @@ export const build = async ({
218228 await setNextEnvironmentVariablesForBuild (
219229 emailsDirRelativePath ,
220230 builtPreviewAppPath ,
231+ rootDirectory ,
221232 ) ;
222233
223234 spinner . text = 'Setting server side generation for the email preview pages' ;
@@ -226,13 +237,6 @@ export const build = async ({
226237 spinner . text = "Updating package.json's build and start scripts" ;
227238 await updatePackageJson ( builtPreviewAppPath ) ;
228239
229- spinner . text = 'Installing dependencies on `.react-email`' ;
230- await installDependencies ( {
231- cwd : builtPreviewAppPath ,
232- silent : true ,
233- packageManager,
234- } ) ;
235-
236240 spinner . stopAndPersist ( {
237241 text : 'Successfully prepared `.react-email` for `next build`' ,
238242 symbol : logSymbols . success ,
0 commit comments