@@ -212,6 +212,8 @@ class CodeInjector implements ICodeInjector {
212
212
}
213
213
214
214
async prepareSources ( ) {
215
+ // collects all files and folders into SPA_TMP_DIR
216
+
215
217
// check spa tmp folder exists and create if not
216
218
try {
217
219
await fs . promises . access ( this . spaTmpPath ( ) , fs . constants . F_OK ) ;
@@ -734,6 +736,39 @@ class CodeInjector implements ICodeInjector {
734
736
this . allWatchers . push ( watcher ) ;
735
737
}
736
738
739
+ async tryReadFile ( filePath : string ) {
740
+ try {
741
+ const content = await fs . promises . readFile ( filePath , 'utf-8' ) ;
742
+ return content ;
743
+ } catch ( e ) {
744
+ // file does not exist
745
+ process . env . HEAVY_DEBUG && console . log ( `🪲File ${ filePath } does not exist, returning null` ) ;
746
+ return null ;
747
+ }
748
+ }
749
+
750
+ async computeSourcesHash ( folderPath : string = this . spaTmpPath ( ) ) {
751
+ const files = await fs . promises . readdir ( folderPath , { withFileTypes : true } ) ;
752
+ const hashes = await Promise . all (
753
+ files . map ( async ( file ) => {
754
+ const filePath = path . join ( folderPath , file . name ) ;
755
+
756
+ // 🚫 Skip node_modules
757
+ if ( file . name === 'node_modules' || file . name === 'dist' ) {
758
+ return '' ;
759
+ }
760
+
761
+ if ( file . isDirectory ( ) ) {
762
+ return this . computeSourcesHash ( filePath ) ;
763
+ } else {
764
+ const content = await fs . promises . readFile ( filePath , 'utf-8' ) ;
765
+ return md5hash ( content ) ;
766
+ }
767
+ } )
768
+ ) ;
769
+ return md5hash ( hashes . join ( '' ) ) ;
770
+ }
771
+
737
772
async bundleNow ( { hotReload = false } : { hotReload : boolean } ) {
738
773
console . log ( `${ this . adminforth . formatAdminForth ( ) } Bundling ${ hotReload ? 'and listening for changes (🔥 Hotreload)' : ' (no hot reload)' } ` ) ;
739
774
this . adminforth . runningHotReload = hotReload ;
@@ -754,28 +789,55 @@ class CodeInjector implements ICodeInjector {
754
789
}
755
790
756
791
const cwd = this . spaTmpPath ( ) ;
792
+ const serveDir = this . getServeDir ( ) ;
757
793
758
794
759
- await this . runNpmShell ( { command : 'run i18n:extract' , cwd} ) ;
795
+ const sourcesHash = await this . computeSourcesHash ( this . spaTmpPath ( ) ) ;
796
+
797
+ const buildHash = await this . tryReadFile ( path . join ( serveDir , '.adminforth_build_hash' ) ) ;
798
+ const messagesHash = await this . tryReadFile ( path . join ( serveDir , '.adminforth_messages_hash' ) ) ;
760
799
761
- // probably add option to build with tsh check (plain 'build')
762
- const serveDir = this . getServeDir ( ) ;
763
- // remove serveDir if exists
764
- try {
765
- await fs . promises . rm ( serveDir , { recursive : true } ) ;
766
- } catch ( e ) {
767
- // ignore
800
+ const skipBuild = buildHash === sourcesHash ;
801
+ const skipExtract = messagesHash === sourcesHash ;
802
+
803
+
804
+
805
+ if ( ! skipExtract ) {
806
+ await this . runNpmShell ( { command : 'run i18n:extract' , cwd} ) ;
807
+
808
+ // create serveDir if not exists
809
+ await fs . promises . mkdir ( serveDir , { recursive : true } ) ;
810
+
811
+ // copy i18n messages to serve dir
812
+ await fsExtra . copy ( path . join ( cwd , 'i18n-messages.json' ) , path . join ( serveDir , 'i18n-messages.json' ) ) ;
813
+
814
+ // save hash
815
+ await fs . promises . writeFile ( path . join ( serveDir , '.adminforth_messages_hash' ) , sourcesHash ) ;
816
+ } else {
817
+ console . log ( `Skipping AdminForth i18n messages extraction - it is already done for these sources set` ) ;
768
818
}
769
- await fs . promises . mkdir ( serveDir , { recursive : true } ) ;
770
-
771
- // copy i18n messages to serve dir
772
- await fsExtra . copy ( path . join ( cwd , 'i18n-messages.json' ) , path . join ( serveDir , 'i18n-messages.json' ) ) ;
773
819
774
820
if ( ! hotReload ) {
775
- await this . runNpmShell ( { command : 'run build-only' , cwd} ) ;
821
+ if ( ! skipBuild ) {
822
+ // remove serveDir if exists
823
+ try {
824
+ await fs . promises . rm ( serveDir , { recursive : true } ) ;
825
+ } catch ( e ) {
826
+ // ignore
827
+ }
828
+ await fs . promises . mkdir ( serveDir , { recursive : true } ) ;
829
+
830
+ // TODO probably add option to build with tsh check (plain 'build')
831
+ await this . runNpmShell ( { command : 'run build-only' , cwd} ) ;
832
+
833
+ // coy dist to serveDir
834
+ await fsExtra . copy ( path . join ( cwd , 'dist' ) , serveDir , { recursive : true } ) ;
776
835
777
- // coy dist to serveDir
778
- await fsExtra . copy ( path . join ( cwd , 'dist' ) , serveDir , { recursive : true } ) ;
836
+ // save hash
837
+ await fs . promises . writeFile ( path . join ( serveDir , '.adminforth_build_hash' ) , sourcesHash ) ;
838
+ } else {
839
+ console . log ( `Skipping AdminForth SPA bundle - it is already done for these sources set` ) ;
840
+ }
779
841
} else {
780
842
781
843
const command = 'run dev' ;
0 commit comments