@@ -20,11 +20,15 @@ let existsSyncSpy: ReturnType<typeof spyOn>
2020let mkdirSyncSpy : ReturnType < typeof spyOn >
2121let readFileSyncSpy : ReturnType < typeof spyOn >
2222let writeFileSyncSpy : ReturnType < typeof spyOn >
23+ let unlinkSyncSpy : ReturnType < typeof spyOn >
2324let getDefaultThemeSpy : ReturnType < typeof spyOn >
2425let getThemeInterfaceSpy : ReturnType < typeof spyOn >
2526let setPrefixSpy : ReturnType < typeof spyOn >
2627let registerThemeSpy : ReturnType < typeof spyOn >
2728let getCssSpy : ReturnType < typeof spyOn >
29+ let importSheetSpy : ReturnType < typeof spyOn >
30+ let importClassMapSpy : ReturnType < typeof spyOn >
31+ let importFileMapSpy : ReturnType < typeof spyOn >
2832let exportSheetSpy : ReturnType < typeof spyOn >
2933let exportClassMapSpy : ReturnType < typeof spyOn >
3034let exportFileMapSpy : ReturnType < typeof spyOn >
@@ -40,11 +44,15 @@ beforeEach(() => {
4044 mkdirSyncSpy = spyOn ( fs , 'mkdirSync' ) . mockReturnValue ( '' as any )
4145 readFileSyncSpy = spyOn ( fs , 'readFileSync' ) . mockReturnValue ( '{}' )
4246 writeFileSyncSpy = spyOn ( fs , 'writeFileSync' ) . mockReturnValue ( undefined )
47+ unlinkSyncSpy = spyOn ( fs , 'unlinkSync' ) . mockReturnValue ( undefined )
4348 getDefaultThemeSpy = spyOn ( wasm , 'getDefaultTheme' ) . mockReturnValue ( undefined )
4449 getThemeInterfaceSpy = spyOn ( wasm , 'getThemeInterface' ) . mockReturnValue ( '' )
4550 setPrefixSpy = spyOn ( wasm , 'setPrefix' ) . mockReturnValue ( undefined )
4651 registerThemeSpy = spyOn ( wasm , 'registerTheme' ) . mockReturnValue ( undefined )
4752 getCssSpy = spyOn ( wasm , 'getCss' ) . mockReturnValue ( '' )
53+ importSheetSpy = spyOn ( wasm , 'importSheet' ) . mockReturnValue ( undefined )
54+ importClassMapSpy = spyOn ( wasm , 'importClassMap' ) . mockReturnValue ( undefined )
55+ importFileMapSpy = spyOn ( wasm , 'importFileMap' ) . mockReturnValue ( undefined )
4856 exportSheetSpy = spyOn ( wasm , 'exportSheet' ) . mockReturnValue (
4957 JSON . stringify ( {
5058 css : { } ,
@@ -84,11 +92,15 @@ afterEach(() => {
8492 mkdirSyncSpy . mockRestore ( )
8593 readFileSyncSpy . mockRestore ( )
8694 writeFileSyncSpy . mockRestore ( )
95+ unlinkSyncSpy . mockRestore ( )
8796 getDefaultThemeSpy . mockRestore ( )
8897 getThemeInterfaceSpy . mockRestore ( )
8998 setPrefixSpy . mockRestore ( )
9099 registerThemeSpy . mockRestore ( )
91100 getCssSpy . mockRestore ( )
101+ importSheetSpy . mockRestore ( )
102+ importClassMapSpy . mockRestore ( )
103+ importFileMapSpy . mockRestore ( )
92104 exportSheetSpy . mockRestore ( )
93105 exportClassMapSpy . mockRestore ( )
94106 exportFileMapSpy . mockRestore ( )
@@ -541,6 +553,66 @@ describe('DevupUINextPlugin', () => {
541553 DevupUI ( { } , { prefix : 'my-prefix' } )
542554 expect ( setPrefixSpy ) . toHaveBeenCalledWith ( 'my-prefix' )
543555 } )
556+ it ( 'should import previous session state on restart' , ( ) => {
557+ process . env . TURBOPACK = '1'
558+ existsSyncSpy
559+ . mockReturnValueOnce ( true ) // distDir
560+ . mockReturnValueOnce ( true ) // cssDir
561+ . mockReturnValueOnce ( true ) // gitignoreFile
562+ . mockReturnValueOnce ( false ) // devupFile in loadDevupConfigSync
563+
564+ // Simulate previous session state files on disk
565+ const prevSheet = {
566+ css : { a : 'color:red' } ,
567+ font_faces : { } ,
568+ global_css_files : [ ] ,
569+ imports : { } ,
570+ keyframes : { } ,
571+ properties : { } ,
572+ }
573+ const prevClassMap = { a : 0 }
574+ const prevFileMap = { 'src/App.tsx' : 0 }
575+
576+ readFileSyncSpy
577+ . mockReturnValueOnce ( JSON . stringify ( prevSheet ) ) // sheetFile
578+ . mockReturnValueOnce ( JSON . stringify ( prevClassMap ) ) // classMapFile
579+ . mockReturnValueOnce ( JSON . stringify ( prevFileMap ) ) // fileMapFile
580+
581+ DevupUI ( { } )
582+
583+ // Verify previous state was imported before registerTheme
584+ expect ( importSheetSpy ) . toHaveBeenCalledWith ( prevSheet )
585+ expect ( importClassMapSpy ) . toHaveBeenCalledWith ( prevClassMap )
586+ expect ( importFileMapSpy ) . toHaveBeenCalledWith ( prevFileMap )
587+ expect ( registerThemeSpy ) . toHaveBeenCalledWith ( { } )
588+
589+ // Verify stale port file was deleted before starting coordinator
590+ expect ( unlinkSyncSpy ) . toHaveBeenCalledWith ( join ( 'df' , 'coordinator.port' ) )
591+ } )
592+ it ( 'should handle missing state files gracefully on first run' , ( ) => {
593+ process . env . TURBOPACK = '1'
594+ existsSyncSpy
595+ . mockReturnValueOnce ( false ) // distDir — doesn't exist
596+ . mockReturnValueOnce ( false ) // cssDir
597+ . mockReturnValueOnce ( false ) // gitignoreFile
598+ . mockReturnValueOnce ( false ) // devupFile
599+
600+ // readFileSync throws for state files (they don't exist)
601+ readFileSyncSpy . mockImplementation ( ( path : string ) => {
602+ throw new Error ( `ENOENT: no such file or directory, open '${ path } '` )
603+ } )
604+
605+ // Should not throw — try-catch handles missing files
606+ DevupUI ( { } )
607+
608+ // importSheet should NOT have been called (readFileSync threw)
609+ expect ( importSheetSpy ) . not . toHaveBeenCalled ( )
610+ expect ( importClassMapSpy ) . not . toHaveBeenCalled ( )
611+ expect ( importFileMapSpy ) . not . toHaveBeenCalled ( )
612+
613+ // registerTheme should still be called with empty theme
614+ expect ( registerThemeSpy ) . toHaveBeenCalledWith ( { } )
615+ } )
544616 it ( 'should start coordinator in development mode' , async ( ) => {
545617 process . env . TURBOPACK = '1'
546618 ; ( process . env as any ) . NODE_ENV = 'development'
0 commit comments