@@ -234,7 +234,7 @@ describe("defaultScaffold", () => {
234234 )
235235 } )
236236
237- it ( "should strip packageManager field from package.json for non-pnpm" , async ( ) => {
237+ it ( "should strip packageManager field from package.json for non-pnpm non-monorepo " , async ( ) => {
238238 vi . mocked ( fs . existsSync ) . mockImplementation ( ( p : any ) =>
239239 p . toString ( ) . includes ( "package.json" )
240240 )
@@ -279,6 +279,14 @@ describe("defaultScaffold", () => {
279279 )
280280 } ) as any )
281281
282+ // Mock execa to return a version for bun --version.
283+ vi . mocked ( execa ) . mockImplementation ( ( ( cmd : string , args : string [ ] ) => {
284+ if ( cmd === "bun" && args [ 0 ] === "--version" ) {
285+ return Promise . resolve ( { stdout : "1.2.0" } as any )
286+ }
287+ return Promise . resolve ( { stdout : "" , exitCode : 0 } as any )
288+ } ) as any )
289+
282290 const template = createTestTemplate ( )
283291
284292 await template . scaffold ( {
@@ -300,7 +308,7 @@ describe("defaultScaffold", () => {
300308 expect ( adaptCall ) . toBeDefined ( )
301309 const written = JSON . parse ( adaptCall ! [ 1 ] as string )
302310 expect ( written . workspaces ) . toEqual ( [ "apps/*" , "packages/*" ] )
303- expect ( written . packageManager ) . toBeUndefined ( )
311+ expect ( written . packageManager ) . toBe ( "bun@1.2.0" )
304312 } )
305313
306314 it ( "should rewrite workspace: protocol refs to * for npm monorepo" , async ( ) => {
@@ -391,6 +399,165 @@ describe("defaultScaffold", () => {
391399 expect ( vi . mocked ( fs . readdir ) ) . not . toHaveBeenCalled ( )
392400 } )
393401
402+ it ( "should set packageManager to detected version for monorepo with bun" , async ( ) => {
403+ vi . mocked ( fs . existsSync ) . mockImplementation ( ( p : any ) => {
404+ const s = p . toString ( )
405+ return s . includes ( "pnpm-workspace.yaml" ) || s . includes ( "package.json" )
406+ } )
407+
408+ vi . mocked ( fs . readFile ) . mockImplementation ( ( ( filePath : string ) => {
409+ if ( filePath . includes ( "pnpm-workspace.yaml" ) ) {
410+ return Promise . resolve ( "packages:\n - 'apps/*'\n" )
411+ }
412+ return Promise . resolve (
413+ JSON . stringify ( { name : "my-mono" , packageManager : "pnpm@9.0.0" } )
414+ )
415+ } ) as any )
416+
417+ vi . mocked ( execa ) . mockImplementation ( ( ( cmd : string , args : string [ ] ) => {
418+ if ( cmd === "bun" && args [ 0 ] === "--version" ) {
419+ return Promise . resolve ( { stdout : "1.2.5" } as any )
420+ }
421+ return Promise . resolve ( { stdout : "" , exitCode : 0 } as any )
422+ } ) as any )
423+
424+ const template = createTestTemplate ( )
425+
426+ await template . scaffold ( {
427+ projectPath : "/test/my-app" ,
428+ packageManager : "bun" ,
429+ cwd : "/test" ,
430+ } )
431+
432+ const writeCalls = vi . mocked ( fs . writeFile ) . mock . calls
433+ const adaptCall = writeCalls . find (
434+ ( call ) => call [ 0 ] === path . join ( "/test/my-app" , "package.json" )
435+ )
436+ expect ( adaptCall ) . toBeDefined ( )
437+ const written = JSON . parse ( adaptCall ! [ 1 ] as string )
438+ expect ( written . packageManager ) . toBe ( "bun@1.2.5" )
439+ } )
440+
441+ it ( "should set packageManager to detected version for monorepo with npm" , async ( ) => {
442+ vi . mocked ( fs . existsSync ) . mockImplementation ( ( p : any ) => {
443+ const s = p . toString ( )
444+ return s . includes ( "pnpm-workspace.yaml" ) || s . includes ( "package.json" )
445+ } )
446+
447+ vi . mocked ( fs . readFile ) . mockImplementation ( ( ( filePath : string ) => {
448+ if ( filePath . includes ( "pnpm-workspace.yaml" ) ) {
449+ return Promise . resolve ( "packages:\n - 'apps/*'\n" )
450+ }
451+ return Promise . resolve (
452+ JSON . stringify ( { name : "my-mono" , packageManager : "pnpm@9.0.0" } )
453+ )
454+ } ) as any )
455+
456+ // Mock readdir for rewriteWorkspaceProtocol.
457+ vi . mocked ( fs . readdir ) . mockResolvedValue ( [ ] as any )
458+
459+ vi . mocked ( execa ) . mockImplementation ( ( ( cmd : string , args : string [ ] ) => {
460+ if ( cmd === "npm" && args [ 0 ] === "--version" ) {
461+ return Promise . resolve ( { stdout : "10.8.1" } as any )
462+ }
463+ return Promise . resolve ( { stdout : "" , exitCode : 0 } as any )
464+ } ) as any )
465+
466+ const template = createTestTemplate ( )
467+
468+ await template . scaffold ( {
469+ projectPath : "/test/my-app" ,
470+ packageManager : "npm" ,
471+ cwd : "/test" ,
472+ } )
473+
474+ const writeCalls = vi . mocked ( fs . writeFile ) . mock . calls
475+ const adaptCall = writeCalls . find (
476+ ( call ) => call [ 0 ] === path . join ( "/test/my-app" , "package.json" )
477+ )
478+ expect ( adaptCall ) . toBeDefined ( )
479+ const written = JSON . parse ( adaptCall ! [ 1 ] as string )
480+ expect ( written . packageManager ) . toBe ( "npm@10.8.1" )
481+ } )
482+
483+ it ( "should set packageManager to yarn version for monorepo with yarn" , async ( ) => {
484+ vi . mocked ( fs . existsSync ) . mockImplementation ( ( p : any ) => {
485+ const s = p . toString ( )
486+ return s . includes ( "pnpm-workspace.yaml" ) || s . includes ( "package.json" )
487+ } )
488+
489+ vi . mocked ( fs . readFile ) . mockImplementation ( ( ( filePath : string ) => {
490+ if ( filePath . includes ( "pnpm-workspace.yaml" ) ) {
491+ return Promise . resolve ( "packages:\n - 'apps/*'\n" )
492+ }
493+ return Promise . resolve (
494+ JSON . stringify ( { name : "my-mono" , packageManager : "pnpm@9.0.0" } )
495+ )
496+ } ) as any )
497+
498+ vi . mocked ( execa ) . mockImplementation ( ( ( cmd : string , args : string [ ] ) => {
499+ if ( cmd === "yarn" && args [ 0 ] === "--version" ) {
500+ return Promise . resolve ( { stdout : "4.6.0" } as any )
501+ }
502+ return Promise . resolve ( { stdout : "" , exitCode : 0 } as any )
503+ } ) as any )
504+
505+ const template = createTestTemplate ( )
506+
507+ await template . scaffold ( {
508+ projectPath : "/test/my-app" ,
509+ packageManager : "yarn" ,
510+ cwd : "/test" ,
511+ } )
512+
513+ const writeCalls = vi . mocked ( fs . writeFile ) . mock . calls
514+ const adaptCall = writeCalls . find (
515+ ( call ) => call [ 0 ] === path . join ( "/test/my-app" , "package.json" )
516+ )
517+ expect ( adaptCall ) . toBeDefined ( )
518+ const written = JSON . parse ( adaptCall ! [ 1 ] as string )
519+ expect ( written . packageManager ) . toBe ( "yarn@4.6.0" )
520+ } )
521+
522+ it ( "should fallback to wildcard version if version detection fails" , async ( ) => {
523+ vi . mocked ( fs . existsSync ) . mockImplementation ( ( p : any ) => {
524+ const s = p . toString ( )
525+ return s . includes ( "pnpm-workspace.yaml" ) || s . includes ( "package.json" )
526+ } )
527+
528+ vi . mocked ( fs . readFile ) . mockImplementation ( ( ( filePath : string ) => {
529+ if ( filePath . includes ( "pnpm-workspace.yaml" ) ) {
530+ return Promise . resolve ( "packages:\n - 'apps/*'\n" )
531+ }
532+ return Promise . resolve (
533+ JSON . stringify ( { name : "my-mono" , packageManager : "pnpm@9.0.0" } )
534+ )
535+ } ) as any )
536+
537+ vi . mocked ( execa ) . mockImplementation ( ( ( cmd : string , args : string [ ] ) => {
538+ if ( args [ 0 ] === "--version" ) {
539+ return Promise . reject ( new Error ( "command not found" ) )
540+ }
541+ return Promise . resolve ( { stdout : "" , exitCode : 0 } as any )
542+ } ) as any )
543+
544+ const template = createTestTemplate ( )
545+
546+ await template . scaffold ( {
547+ projectPath : "/test/my-app" ,
548+ packageManager : "bun" ,
549+ cwd : "/test" ,
550+ } )
551+
552+ const writeCalls = vi . mocked ( fs . writeFile ) . mock . calls
553+ const adaptCall = writeCalls . find (
554+ ( call ) => call [ 0 ] === path . join ( "/test/my-app" , "package.json" )
555+ )
556+ expect ( adaptCall ) . toBeDefined ( )
557+ const written = JSON . parse ( adaptCall ! [ 1 ] as string )
558+ expect ( written . packageManager ) . toBe ( "bun@*" )
559+ } )
560+
394561 it ( "should write project name to package.json" , async ( ) => {
395562 vi . mocked ( fs . existsSync ) . mockImplementation ( ( p : any ) =>
396563 p . toString ( ) . includes ( "package.json" )
0 commit comments