@@ -16,6 +16,7 @@ vi.mock('../../utils/constants.js', async (importOriginal) => {
1616
1717const {
1818 rewritePackageJson,
19+ rewriteStandaloneProject,
1920 parseNvmrcVersion,
2021 detectNodeVersionManagerFile,
2122 migrateNodeVersionManagerFile,
@@ -290,3 +291,145 @@ describe('migrateNodeVersionManagerFile', () => {
290291 expect ( fs . existsSync ( path . join ( tmpDir , '.node-version' ) ) ) . toBe ( false ) ;
291292 } ) ;
292293} ) ;
294+
295+ function makeWorkspaceInfo (
296+ rootDir : string ,
297+ packageManager : PackageManager ,
298+ ) : import ( '../../types/index.js' ) . WorkspaceInfo {
299+ return {
300+ rootDir,
301+ isMonorepo : false ,
302+ monorepoScope : '' ,
303+ workspacePatterns : [ ] ,
304+ parentDirs : [ ] ,
305+ packageManager,
306+ packageManagerVersion : '10.33.0' ,
307+ downloadPackageManager : {
308+ name : 'pnpm' ,
309+ installDir : '/tmp' ,
310+ binPrefix : '/tmp/bin' ,
311+ packageName : 'pnpm' ,
312+ version : '10.33.0' ,
313+ } ,
314+ packages : [ ] ,
315+ } ;
316+ }
317+
318+ function readJson ( filePath : string ) : Record < string , unknown > {
319+ return JSON . parse ( fs . readFileSync ( filePath , 'utf8' ) ) ;
320+ }
321+
322+ function readYaml ( filePath : string ) : string {
323+ return fs . readFileSync ( filePath , 'utf8' ) ;
324+ }
325+
326+ describe ( 'rewriteStandaloneProject pnpm workspace yaml' , ( ) => {
327+ let tmpDir : string ;
328+
329+ beforeEach ( ( ) => {
330+ tmpDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'vp-test-pnpm-' ) ) ;
331+ } ) ;
332+
333+ afterEach ( ( ) => {
334+ fs . rmSync ( tmpDir , { recursive : true , force : true } ) ;
335+ } ) ;
336+
337+ it ( 'creates pnpm-workspace.yaml when no existing pnpm config in package.json' , ( ) => {
338+ fs . writeFileSync (
339+ path . join ( tmpDir , 'package.json' ) ,
340+ JSON . stringify ( { name : 'test' , devDependencies : { vite : '^7.0.0' } } ) ,
341+ ) ;
342+ rewriteStandaloneProject ( tmpDir , makeWorkspaceInfo ( tmpDir , PackageManager . pnpm ) , true , true ) ;
343+
344+ // pnpm-workspace.yaml should be created
345+ expect ( fs . existsSync ( path . join ( tmpDir , 'pnpm-workspace.yaml' ) ) ) . toBe ( true ) ;
346+ const yaml = readYaml ( path . join ( tmpDir , 'pnpm-workspace.yaml' ) ) ;
347+ expect ( yaml ) . toContain ( 'overrides:' ) ;
348+ expect ( yaml ) . toContain ( 'peerDependencyRules:' ) ;
349+ expect ( yaml ) . toContain ( 'catalog:' ) ;
350+
351+ // package.json should not have pnpm section
352+ const pkg = readJson ( path . join ( tmpDir , 'package.json' ) ) ;
353+ expect ( pkg . pnpm ) . toBeUndefined ( ) ;
354+
355+ // devDependencies should use catalog:
356+ const devDeps = pkg . devDependencies as Record < string , string > ;
357+ expect ( devDeps . vite ) . toBe ( 'catalog:' ) ;
358+ expect ( devDeps [ 'vite-plus' ] ) . toBe ( 'catalog:' ) ;
359+ } ) ;
360+
361+ it ( 'keeps pnpm config in package.json when existing pnpm field present' , ( ) => {
362+ fs . writeFileSync (
363+ path . join ( tmpDir , 'package.json' ) ,
364+ JSON . stringify ( {
365+ name : 'test' ,
366+ devDependencies : { vite : '^7.0.0' } ,
367+ pnpm : {
368+ overrides : { 'some-pkg' : '1.0.0' } ,
369+ onlyBuiltDependencies : [ 'esbuild' ] ,
370+ } ,
371+ } ) ,
372+ ) ;
373+ rewriteStandaloneProject ( tmpDir , makeWorkspaceInfo ( tmpDir , PackageManager . pnpm ) , true , true ) ;
374+
375+ // pnpm-workspace.yaml should NOT be created
376+ expect ( fs . existsSync ( path . join ( tmpDir , 'pnpm-workspace.yaml' ) ) ) . toBe ( false ) ;
377+
378+ // package.json should have pnpm.overrides with both existing and vite overrides
379+ const pkg = readJson ( path . join ( tmpDir , 'package.json' ) ) ;
380+ const pnpm = pkg . pnpm as Record < string , unknown > ;
381+ expect ( pnpm ) . toBeDefined ( ) ;
382+ const overrides = pnpm . overrides as Record < string , string > ;
383+ expect ( overrides [ 'some-pkg' ] ) . toBe ( '1.0.0' ) ;
384+ expect ( overrides . vite ) . toBeDefined ( ) ;
385+ expect ( overrides . vitest ) . toBeDefined ( ) ;
386+
387+ // peerDependencyRules should be present
388+ expect ( pnpm . peerDependencyRules ) . toBeDefined ( ) ;
389+ // onlyBuiltDependencies should be preserved
390+ expect ( pnpm . onlyBuiltDependencies ) . toEqual ( [ 'esbuild' ] ) ;
391+ } ) ;
392+
393+ it ( 'preserves custom peerDependencyRules when migrating to pnpm-workspace.yaml' , ( ) => {
394+ // Project has peerDependencyRules but no pnpm.overrides -- pnpm field is present
395+ // so it should keep using package.json
396+ fs . writeFileSync (
397+ path . join ( tmpDir , 'package.json' ) ,
398+ JSON . stringify ( {
399+ name : 'test' ,
400+ devDependencies : { vite : '^7.0.0' } ,
401+ pnpm : {
402+ peerDependencyRules : {
403+ allowAny : [ 'react' , 'vite' ] ,
404+ allowedVersions : { react : '*' , vite : '*' } ,
405+ ignoreMissing : [ '@types/node' ] ,
406+ } ,
407+ } ,
408+ } ) ,
409+ ) ;
410+ rewriteStandaloneProject ( tmpDir , makeWorkspaceInfo ( tmpDir , PackageManager . pnpm ) , true , true ) ;
411+
412+ const pkg = readJson ( path . join ( tmpDir , 'package.json' ) ) ;
413+ const pnpm = pkg . pnpm as Record < string , unknown > ;
414+ const rules = pnpm . peerDependencyRules as Record < string , unknown > ;
415+ // Custom entries preserved, Vite entries merged
416+ expect ( rules . allowAny ) . toEqual ( expect . arrayContaining ( [ 'react' , 'vite' , 'vitest' ] ) ) ;
417+ // ignoreMissing preserved
418+ expect ( rules . ignoreMissing ) . toEqual ( [ '@types/node' ] ) ;
419+ } ) ;
420+
421+ it ( 'moves remaining non-Vite overrides to pnpm-workspace.yaml' , ( ) => {
422+ // Project has NO pnpm field, but we simulate re-migration by creating
423+ // the workspace yaml first, then calling with a package.json that has no pnpm
424+ fs . writeFileSync (
425+ path . join ( tmpDir , 'package.json' ) ,
426+ JSON . stringify ( { name : 'test' , devDependencies : { vite : '^7.0.0' } } ) ,
427+ ) ;
428+ rewriteStandaloneProject ( tmpDir , makeWorkspaceInfo ( tmpDir , PackageManager . pnpm ) , true , true ) ;
429+
430+ // Verify workspace yaml has overrides
431+ const yaml = readYaml ( path . join ( tmpDir , 'pnpm-workspace.yaml' ) ) ;
432+ expect ( yaml ) . toContain ( "vite: 'catalog:'" ) ;
433+ expect ( yaml ) . toContain ( "vitest: 'catalog:'" ) ;
434+ } ) ;
435+ } ) ;
0 commit comments