@@ -13,6 +13,7 @@ import (
1313 "os"
1414 "path/filepath"
1515 "regexp"
16+ "strconv"
1617 "strings"
1718 "text/template"
1819
@@ -233,11 +234,12 @@ func ServeIndex(indexPath string) func(*App, http.ResponseWriter, *http.Request)
233234 sign := signature ()
234235 base := WithBase ("/" )
235236 templateData := map [string ]any {
236- "base" : base ,
237- "version" : BUILD_REF ,
238- "license" : LICENSE ,
239- "hash" : sign ,
240- "favicon" : favicon (),
237+ "base" : base ,
238+ "version" : BUILD_REF ,
239+ "license" : LICENSE ,
240+ "hash" : sign ,
241+ "favicon" : favicon (),
242+ "bundle_size" : len (preload ),
241243 }
242244 calculatedEtag := QuickHash (base + BUILD_REF + LICENSE + sign , 10 )
243245 head .Set ("ETag" , calculatedEtag )
@@ -258,8 +260,8 @@ func ServeIndex(indexPath string) func(*App, http.ResponseWriter, *http.Request)
258260 }
259261}
260262
261- func ServeBundle () func ( * App , http. ResponseWriter , * http. Request ) {
262- paths := [] string {
263+ var preload = [][] string {
264+ {
263265 "/assets/" + BUILD_REF + "/boot/ctrl_boot_frontoffice.js" ,
264266 "/assets/" + BUILD_REF + "/boot/router_frontoffice.js" ,
265267 "/assets/" + BUILD_REF + "/boot/common.js" ,
@@ -303,6 +305,9 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
303305 "/assets/" + BUILD_REF + "/helpers/sdk.js" ,
304306
305307 "/assets/" + BUILD_REF + "/lib/rx.js" ,
308+ "/assets/" + BUILD_REF + "/lib/ajax.js" ,
309+ },
310+ {
306311 "/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs.min.js" ,
307312 "/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs-ajax.min.js" ,
308313 "/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs-shared.min.js" ,
@@ -311,7 +316,6 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
311316 "/assets/" + BUILD_REF + "/lib/path.js" ,
312317 "/assets/" + BUILD_REF + "/lib/random.js" ,
313318 "/assets/" + BUILD_REF + "/lib/settings.js" ,
314- "/assets/" + BUILD_REF + "/lib/ajax.js" ,
315319 "/assets/" + BUILD_REF + "/lib/animate.js" ,
316320 "/assets/" + BUILD_REF + "/lib/assert.js" ,
317321 "/assets/" + BUILD_REF + "/lib/dom.js" ,
@@ -321,16 +325,16 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
321325 "/assets/" + BUILD_REF + "/lib/error.js" ,
322326
323327 "/assets/" + BUILD_REF + "/locales/index.js" ,
324-
325328 "/assets/" + BUILD_REF + "/model/config.js" ,
326329 "/assets/" + BUILD_REF + "/model/chromecast.js" ,
327330 "/assets/" + BUILD_REF + "/model/session.js" ,
328331 "/assets/" + BUILD_REF + "/model/plugin.js" ,
329332
330333 "/assets/" + BUILD_REF + "/pages/ctrl_logout.js" ,
331334 "/assets/" + BUILD_REF + "/pages/ctrl_error.js" ,
335+ },
336+ {
332337 "/assets/" + BUILD_REF + "/pages/ctrl_homepage.js" ,
333-
334338 "/assets/" + BUILD_REF + "/pages/ctrl_connectpage.js" ,
335339 "/assets/" + BUILD_REF + "/pages/ctrl_connectpage.css" ,
336340 "/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form.css" ,
@@ -341,18 +345,24 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
341345 "/assets/" + BUILD_REF + "/pages/connectpage/model_config.js" ,
342346 "/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form_state.js" ,
343347
348+ "/assets/" + BUILD_REF + "/pages/filespage/thing.js" ,
349+ "/assets/" + BUILD_REF + "/pages/filespage/thing.css" ,
350+ },
351+ {
344352 "/assets/" + BUILD_REF + "/pages/ctrl_filespage.js" ,
345353 "/assets/" + BUILD_REF + "/pages/ctrl_filespage.css" ,
346- "/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.css" ,
347354 "/assets/" + BUILD_REF + "/pages/filespage/model_acl.js" ,
348355 "/assets/" + BUILD_REF + "/pages/filespage/cache.js" ,
349- "/assets/" + BUILD_REF + "/pages/filespage/thing.js" ,
350- "/assets/" + BUILD_REF + "/pages/filespage/thing.css" ,
351356 "/assets/" + BUILD_REF + "/pages/filespage/ctrl_filesystem.js" ,
352357 "/assets/" + BUILD_REF + "/pages/filespage/ctrl_filesystem.css" ,
353358 "/assets/" + BUILD_REF + "/pages/filespage/ctrl_upload.js" ,
354359 "/assets/" + BUILD_REF + "/pages/filespage/ctrl_upload.css" ,
360+ "/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.js" ,
361+ "/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.css" ,
362+ },
363+ {
355364 "/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.js" ,
365+ "/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.css" ,
356366 "/assets/" + BUILD_REF + "/pages/filespage/state_config.js" ,
357367 "/assets/" + BUILD_REF + "/pages/filespage/helper.js" ,
358368 "/assets/" + BUILD_REF + "/pages/filespage/model_files.js" ,
@@ -367,8 +377,6 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
367377 "/assets/" + BUILD_REF + "/pages/filespage/modal_delete.js" ,
368378 "/assets/" + BUILD_REF + "/pages/filespage/state_selection.js" ,
369379 "/assets/" + BUILD_REF + "/pages/filespage/state_newthing.js" ,
370- "/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.js" ,
371- "/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.css" ,
372380
373381 // "/assets/" + BUILD_REF + "/pages/ctrl_viewerpage.js", // TODO: dynamic imports
374382 "/assets/" + BUILD_REF + "/pages/ctrl_viewerpage.css" ,
@@ -379,66 +387,85 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
379387 "/assets/" + BUILD_REF + "/pages/viewerpage/application_downloader.css" ,
380388 "/assets/" + BUILD_REF + "/pages/viewerpage/component_menubar.js" ,
381389 "/assets/" + BUILD_REF + "/pages/viewerpage/component_menubar.css" ,
382- }
383-
384- var isDebug = os .Getenv ("DEBUG" ) == "true"
390+ },
391+ }
385392
386- build := func (quality int ) (bundlePlain []byte , bundleBr []byte , etag string ) {
387- var buf bytes.Buffer
388- for _ , path := range paths {
389- curPath := "/assets/" + strings .TrimPrefix (path , "/assets/" + BUILD_REF + "/" )
390- f := applyPatch (curPath )
391- if f == nil {
392- file , err := WWWPublic .Open (curPath )
393- if err != nil {
394- Log .Warning ("static::bundler failed to find file %s" , err .Error ())
395- continue
393+ func ServeBundle () func (* App , http.ResponseWriter , * http.Request ) {
394+ isDebug := os .Getenv ("DEBUG" ) == "true"
395+ buildChunks := func (quality int ) (chunks [][]byte , chunksBr [][]byte , etags []string ) {
396+ numChunks := len (preload )
397+ chunks = make ([][]byte , numChunks + 1 )
398+ chunksBr = make ([][]byte , numChunks + 1 )
399+ etags = make ([]string , numChunks + 1 )
400+ var fullBuf bytes.Buffer
401+ for i := 0 ; i < numChunks ; i ++ {
402+ var chunkBuf bytes.Buffer
403+ for _ , path := range preload [i ] {
404+ curPath := "/assets/" + strings .TrimPrefix (path , "/assets/" + BUILD_REF + "/" )
405+ f := applyPatch (curPath )
406+ if f == nil {
407+ file , err := WWWPublic .Open (curPath )
408+ if err != nil {
409+ Log .Warning ("static::bundler failed to find file %s" , err .Error ())
410+ continue
411+ }
412+ f = new (bytes.Buffer )
413+ if _ , err := io .Copy (f , file ); err != nil {
414+ Log .Warning ("static::bundler msg=copy_error err=%s" , err .Error ())
415+ continue
416+ }
417+ file .Close ()
396418 }
397- f = new (bytes. Buffer )
398- if _ , err := io . Copy ( f , file ); err != nil {
399- Log .Warning ("static::bundler msg=copy_error err=%s" , err .Error ())
419+ code , err := json . Marshal ( f . String () )
420+ if err != nil {
421+ Log .Warning ("static::bundle msg=marshal_failed path=%s err=%s" , path , err .Error ())
400422 continue
401423 }
402- file .Close ()
424+ line := fmt .Sprintf ("bundler.register(%q, %s);\n " , WithBase (path ), code )
425+ chunkBuf .WriteString (line )
426+ fullBuf .WriteString (line )
403427 }
404- code , err := json .Marshal (f .String ())
405- if err != nil {
406- Log .Warning ("static::bundle msg=marshal_failed path=%s err=%s" , path , err .Error ())
407- continue
408- }
409- fmt .Fprintf (& buf , "bundler.register(%q, %s);\n " , WithBase (path ), code )
428+ chunks [i + 1 ] = chunkBuf .Bytes ()
429+ chunksBr [i + 1 ], _ = cbrotli .Encode (chunks [i + 1 ], cbrotli.WriterOptions {Quality : quality })
430+ etags [i + 1 ] = QuickHash (string (chunks [i + 1 ]), 10 )
410431 }
411- etag = QuickHash (string (bundlePlain ), 10 )
412- bundlePlain = buf .Bytes ()
413- if quality > 0 {
414- bundleBr , _ = cbrotli .Encode (bundlePlain , cbrotli.WriterOptions {Quality : quality })
415- }
416- return bundlePlain , bundleBr , etag
432+ chunks [0 ] = fullBuf .Bytes ()
433+ chunksBr [0 ], _ = cbrotli .Encode (chunks [0 ], cbrotli.WriterOptions {Quality : quality })
434+ etags [0 ] = QuickHash (string (chunks [0 ]), 10 )
435+ return chunks , chunksBr , etags
417436 }
418437
419438 quality := 11
420439 if isDebug {
421440 quality = 8
422441 }
423- bundlePlain , bundleBr , etag := build (quality )
442+ chunks , chunksBr , etags := buildChunks (quality )
424443
425444 return func (ctx * App , res http.ResponseWriter , req * http.Request ) {
426445 if isDebug {
427- bundlePlain , bundleBr , etag = build (quality )
446+ chunks , chunksBr , etags = buildChunks (quality )
447+ }
448+ chunkIndex := 0
449+ if parsed , err := strconv .Atoi (req .URL .Query ().Get ("chunk" )); err == nil {
450+ chunkIndex = parsed
451+ }
452+ if chunkIndex >= len (chunks ) {
453+ http .NotFound (res , req )
454+ return
428455 }
429456 head := res .Header ()
430457 head .Set ("Content-Type" , "application/javascript" )
431458 head .Set ("Cache-Control" , "no-cache" )
432- head .Set ("Etag" , etag )
433- if req .Header .Get ("If-None-Match" ) == etag && etag != "" {
459+ head .Set ("Etag" , etags [ chunkIndex ] )
460+ if req .Header .Get ("If-None-Match" ) == etags [ chunkIndex ] && etags [ chunkIndex ] != "" {
434461 res .WriteHeader (http .StatusNotModified )
435462 return
436- } else if strings .Contains (req .Header .Get ("Accept-Encoding" ), "br" ) && len (bundleBr ) > 0 {
463+ } else if strings .Contains (req .Header .Get ("Accept-Encoding" ), "br" ) && len (chunksBr [ chunkIndex ] ) > 0 {
437464 head .Set ("Content-Encoding" , "br" )
438- res .Write (bundleBr )
465+ res .Write (chunksBr [ chunkIndex ] )
439466 return
440467 }
441- res .Write (bundlePlain )
468+ res .Write (chunks [ chunkIndex ] )
442469 }
443470}
444471
0 commit comments