@@ -21,6 +21,7 @@ import { performSafetyChecks, isSafeToWrite } from '../utils/safety.js';
2121import { detectMonorepo , isInMonorepo } from '../utils/monorepo-detector.js' ;
2222import { getRollbackManager } from '../utils/rollback.js' ;
2323import { getMergedConfig } from '../utils/config-manager.js' ;
24+ import { createDryRunManager } from '../utils/dry-run.js' ;
2425
2526/**
2627 * Creates the `add` command for adding OpenNext to existing projects
@@ -63,6 +64,10 @@ export function addCommand(): Command {
6364 '--skip-backup' ,
6465 'Skip creating backups of existing configuration files'
6566 )
67+ . option (
68+ '--dry-run' ,
69+ 'Preview changes without applying them'
70+ )
6671 . addHelpText (
6772 'after' ,
6873 `
@@ -103,17 +108,25 @@ Troubleshooting:
103108 • Verify Node.js version is 18+ (check with: node --version)
104109`
105110 )
106- . action ( async ( _options : {
111+ . action ( async ( options : {
107112 yes ?: boolean ;
108113 workerName ?: string ;
109114 cachingStrategy ?: string ;
110115 skipCloudflareCheck ?: boolean ;
111116 skipBackup ?: boolean ;
117+ dryRun ?: boolean ;
112118 } ) => {
113119 const rollbackManager = getRollbackManager ( ) ;
114120 const projectRoot = process . cwd ( ) ;
121+ const dryRun = options . dryRun || false ;
122+ const dryRunManager = createDryRunManager ( dryRun ) ;
115123
116124 try {
125+ if ( dryRun ) {
126+ p . intro ( '🔍 OpenNext.js CLI (Dry Run)' ) ;
127+ p . note ( 'Running in dry-run mode. No files will be modified.' , 'Dry Run Mode' ) ;
128+ }
129+
117130 // Safety checks
118131 logger . section ( 'Safety Checks' ) ;
119132 const safetyCheck = performSafetyChecks ( projectRoot , 'add' ) ;
@@ -132,7 +145,7 @@ Troubleshooting:
132145 logger . warning ( ` • ${ warning } ` ) ;
133146 }
134147
135- if ( ! _options . yes ) {
148+ if ( ! options . yes ) {
136149 const continueAnyway = await promptConfirmation (
137150 'Continue despite warnings?' ,
138151 false
@@ -151,7 +164,7 @@ Troubleshooting:
151164 p . log . info ( `Type: ${ monorepo . type } ` ) ;
152165 p . log . info ( `Root: ${ monorepo . rootPath } ` ) ;
153166
154- if ( ! _options . yes ) {
167+ if ( ! options . yes ) {
155168 const confirmed = await promptConfirmation (
156169 `You're in a ${ monorepo . type } monorepo. Continue with setup in this package?` ,
157170 true
@@ -165,8 +178,8 @@ Troubleshooting:
165178
166179 // Load CLI configuration
167180 const cliConfig = getMergedConfig ( projectRoot ) ;
168- if ( cliConfig . autoBackup === false && ! _options . skipBackup ) {
169- _options . skipBackup = true ;
181+ if ( cliConfig . autoBackup === false && ! options . skipBackup ) {
182+ options . skipBackup = true ;
170183 }
171184
172185 logger . section ( 'Project Detection' ) ;
@@ -226,7 +239,7 @@ Troubleshooting:
226239 }
227240
228241 // Backup existing files
229- if ( ! _options . skipBackup ) {
242+ if ( ! options . skipBackup ) {
230243 const filesToBackup = [ 'wrangler.toml' , 'open-next.config.ts' , 'next.config.mjs' ] ;
231244 const backups = backupFiles ( filesToBackup , join ( projectRoot , '.backup' ) ) ;
232245 if ( backups . some ( ( b ) => b !== undefined ) ) {
@@ -248,31 +261,65 @@ Troubleshooting:
248261
249262 logger . section ( 'Setup' ) ;
250263
251- // Generate configuration files
252- const configSpinner = p . spinner ( ) ;
253- configSpinner . start ( 'Generating configuration files...' ) ;
254- await generateCloudflareConfig ( config , process . cwd ( ) ) ;
255- configSpinner . stop ( 'Configuration files generated' ) ;
264+ // Track files that would be created
265+ if ( dryRun ) {
266+ dryRunManager . track ( { type : 'create' , path : 'open-next.config.ts' } ) ;
267+ dryRunManager . track ( { type : 'create' , path : 'wrangler.toml' } ) ;
268+ dryRunManager . track ( { type : 'update' , path : 'next.config.mjs' } ) ;
269+ dryRunManager . track ( { type : 'update' , path : 'package.json' } ) ;
270+ dryRunManager . track ( { type : 'create' , path : 'scripts/patch-nextjs-source.js' } ) ;
271+ dryRunManager . track ( { type : 'create' , path : 'scripts/patch-init.js' } ) ;
272+
273+ dryRunManager . displayPreview ( ) ;
274+
275+ p . note (
276+ 'Dependencies that would be installed:\n' +
277+ ' • @opennextjs/cloudflare\n' +
278+ ' • wrangler (dev)' ,
279+ 'Dry Run Preview'
280+ ) ;
281+
282+ p . outro ( 'Dry run complete. Run without --dry-run to apply changes.' ) ;
283+ return ;
284+ }
285+
286+ // Use tasks() for sequential operations
287+ const tasks = [
288+ {
289+ title : 'Generating configuration files' ,
290+ task : async ( ) => {
291+ await generateCloudflareConfig ( config , process . cwd ( ) ) ;
292+ } ,
293+ } ,
294+ ] ;
256295
257296 // Install OpenNext.js Cloudflare if not already installed
258297 if ( ! detection . hasOpenNext ) {
259- const installSpinner = p . spinner ( ) ;
260- installSpinner . start ( 'Installing @opennextjs/cloudflare...' ) ;
261- addDependency ( '@opennextjs/cloudflare' , false ) ;
262- installSpinner . stop ( '@opennextjs/cloudflare installed' ) ;
298+ tasks . push ( {
299+ title : 'Installing @opennextjs/cloudflare' ,
300+ task : async ( ) => {
301+ addDependency ( '@opennextjs/cloudflare' , false ) ;
302+ } ,
303+ } ) ;
263304 }
264305
265- // Install wrangler as dev dependency
266- const wranglerSpinner = p . spinner ( ) ;
267- wranglerSpinner . start ( 'Installing wrangler...' ) ;
268- addDependency ( 'wrangler' , true ) ;
269- wranglerSpinner . stop ( 'wrangler installed' ) ;
306+ // Always install wrangler
307+ tasks . push (
308+ {
309+ title : 'Installing wrangler' ,
310+ task : async ( ) => {
311+ addDependency ( 'wrangler' , true ) ;
312+ } ,
313+ } ,
314+ {
315+ title : 'Installing dependencies' ,
316+ task : async ( ) => {
317+ installDependencies ( ) ;
318+ } ,
319+ }
320+ ) ;
270321
271- // Install all dependencies
272- const depsSpinner = p . spinner ( ) ;
273- depsSpinner . start ( 'Installing dependencies...' ) ;
274- installDependencies ( ) ;
275- depsSpinner . stop ( 'Dependencies installed' ) ;
322+ await p . tasks ( tasks ) ;
276323
277324 logger . success ( 'OpenNext.js Cloudflare has been added to your project!' ) ;
278325 p . note (
0 commit comments