@@ -3,6 +3,13 @@ if (!modulo.registry.vwindow) {
3
3
}
4
4
5
5
modulo . registry . utils . parse = function parse ( parentElem , text ) {
6
+ modulo . assert ( parentElem !== undefined || text !== undefined , 'Must specify text to parse' ) ;
7
+ if ( text === undefined ) { // Specify "null" to create a new document with a detached x tag
8
+ text = parentElem ;
9
+ const vw = new modulo . registry . engines . VirtualWindow ( modulo ) ;
10
+ parentElem = vw . document . createElement ( 'x' ) ; // Create a detached 'x' tag
11
+ }
12
+
6
13
/*
7
14
Simple recursive descent parser for HTML
8
15
*/
@@ -20,15 +27,15 @@ modulo.registry.utils.parse = function parse(parentElem, text) {
20
27
} ;
21
28
// If there's leading text, create a TextNode with that as content
22
29
const pushText = ( _textContent , opts ) =>
23
- topOfStack . childNodes . push ( new modulo . registry . vwindow . HTMLElement ( {
30
+ topOfStack . append ( new modulo . registry . vwindow . HTMLElement ( {
24
31
nodeType : 3 ,
25
32
_textContent,
26
33
...opts ,
27
34
} ) ) ;
28
35
29
36
const { ownerDocument } = parentElem ;
30
37
let elemClassesUC = { } ;
31
- if ( ownerDocument . moduloVirtualWindow . customElements ) {
38
+ if ( ownerDocument && ownerDocument . moduloVirtualWindow . customElements ) {
32
39
elemClassesUC = ownerDocument . moduloVirtualWindow . customElements . elemClassesUC ;
33
40
}
34
41
@@ -119,6 +126,7 @@ modulo.registry.utils.parse = function parse(parentElem, text) {
119
126
throw new Error ( 'Over 9999 parsing steps' ) ;
120
127
}
121
128
}
129
+ return parentElem ;
122
130
}
123
131
124
132
modulo . registry . vwindow . Element = class Element {
@@ -168,6 +176,9 @@ modulo.registry.vwindow.Event = class Event {
168
176
169
177
170
178
179
+ /* Synchronous Promise variant */
180
+ modulo . registry . vwindow . Promise = class PausablePromise {
181
+ }
171
182
172
183
modulo . registry . vwindow . HTMLElement = class HTMLElement extends modulo . registry . vwindow . Element {
173
184
constructor ( opts ) {
@@ -304,6 +315,10 @@ modulo.registry.vwindow.HTMLElement = class HTMLElement extends modulo.registry.
304
315
return this . childNodes . length > 0 ? this . childNodes [ 0 ] : null ;
305
316
}
306
317
318
+ get firstElementChild ( ) {
319
+ return this . children . length > 0 ? this . children [ 0 ] : null ;
320
+ }
321
+
307
322
get nextSibling ( ) {
308
323
if ( ! this . parentNode || ( this . _parentIndex + 1 ) >= this . parentNode . childNodes . length ) {
309
324
return null ;
@@ -367,7 +382,7 @@ modulo.registry.vwindow.HTMLElement = class HTMLElement extends modulo.registry.
367
382
368
383
_fetch ( url , callback ) {
369
384
const vw = this . ownerDocument . moduloVirtualWindow ;
370
- vw . window . fetch ( url )
385
+ vw . fetch ( url )
371
386
. then ( response => response . text ( ) )
372
387
. then ( callback ) ;
373
388
/*
@@ -683,13 +698,73 @@ modulo.register('engine', class VirtualWindow {
683
698
Object . assign ( this , win ) ; // Expose some window properties at top as well
684
699
this . window = Object . assign ( { } , vwindow , win ) ; // Add in all vdom classes
685
700
this . window . exec = this . exec . bind ( this ) ;
686
- //this.window.fetch = window.fetch.bind(window);
687
701
this . window . fetch = this . fetch . bind ( this ) ;
702
+ this . isVirtualFileSystem = false ;
688
703
this . cachedForBlocking = { } ;
704
+
705
+ // If the "virtual-file-system" configuration option is set, we can mount virtual files
706
+ // TODO: Speed up idea: Have the entire source directory get read into
707
+ // memory and mounted on this (even binaries, if specified!), then when
708
+ // doing SSG just work on the memory copy, for lighting fast
709
+ // prebuilding!
710
+ // Eventually, when binary operation become more normal (e.g.
711
+ // <ImageCanvas> type elements), then have VirtualWindow be capable of
712
+ // running those remotely via a RPC bridge, or existing HTTP API or
713
+ // equivalent (e.g. an express server that sits around with a
714
+ // Pupeteer). Or standard ipc stuff?
715
+ if ( this . modulo . config . virtualwindow . virtualFileSystem ) {
716
+ this . setVFS ( this . modulo . config . virtualwindow . virtualFileSystem ) ;
717
+ }
689
718
}
690
719
691
720
fetch ( ...args ) {
692
- return window . fetch ( ...args ) ;
721
+ // The single "fetch" choke point, used by both VirtualWindow and by it's children
722
+ if ( this . isVirtualFileSystem ) {
723
+ return this . fetchVFS ( ...args ) ;
724
+ } else {
725
+ return window . fetch ( ...args ) ;
726
+ }
727
+ }
728
+ fetchVFS ( ...args ) {
729
+ class Response {
730
+ constructor ( url , text ) {
731
+ this . _url = url ;
732
+ this . _textBody = text ;
733
+ }
734
+ json ( ) {
735
+ return new Promise ( ( resolve , reject ) => {
736
+ try {
737
+ resolve ( JSON . parse ( this . _textBody ) ) ;
738
+ } catch ( e ) {
739
+ reject ( e ) ;
740
+ }
741
+ } ) ;
742
+ }
743
+ text ( ) {
744
+ return new Promise ( ( resolve , reject ) => {
745
+ resolve ( this . _textBody ) ;
746
+ } ) ;
747
+ }
748
+ }
749
+ return new Promise ( ( resolve , reject ) => {
750
+ const request = { } ;
751
+ const response = { } ;
752
+ let url = args [ 0 ] || '' ;
753
+ if ( url . toLowerCase ( ) . startsWith ( 'file://' ) ) {
754
+ // Local file protocol, good!
755
+ url = url . substr ( 7 ) ;
756
+ }
757
+ if ( url . toLowerCase ( ) . startsWith ( 'http://' ) ) {
758
+ // Local file protocol, good!
759
+ url = url . substr ( 7 ) ;
760
+ }
761
+
762
+ if ( ! ( url in this . cachedForBlocking ) ) {
763
+ reject ( new Error ( `V404| Virtual FileSystem entry for ${ url } not found` ) ) ;
764
+ } else {
765
+ resolve ( new Response ( url , this . cachedForBlocking [ url ] ) ) ;
766
+ }
767
+ } ) ;
693
768
}
694
769
695
770
makeCustomElements ( ) {
@@ -743,7 +818,7 @@ modulo.register('engine', class VirtualWindow {
743
818
return url ;
744
819
}
745
820
} , new this . window . URL ( url ) ) ;
746
- return window . fetch ( url )
821
+ return this . fetch ( url )
747
822
. then ( response => response . text ( ) )
748
823
. then ( this . execHTML . bind ( this ) )
749
824
}
@@ -754,6 +829,11 @@ modulo.register('engine', class VirtualWindow {
754
829
return func ( this . window , this . document , this . HTMLElement ) ;
755
830
}
756
831
832
+ setVFS ( sources ) {
833
+ this . isVirtualFileSystem = true ;
834
+ Object . assign ( this . cachedForBlocking , sources ) ;
835
+ }
836
+
757
837
execHTML ( pageContent ) {
758
838
// Start by stripping doctype
759
839
const [ doctype , html ] = this . _splitDoctype ( pageContent ) ;
@@ -770,7 +850,7 @@ modulo.register('engine', class VirtualWindow {
770
850
const sources = / < S C R I P T [ ^ > ] * \s S R C \s * = \s * [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / gi;
771
851
const promises = [ ] ;
772
852
for ( const match of pageContent . matchAll ( sources ) ) {
773
- promises . push ( window . fetch ( match [ 1 ] )
853
+ promises . push ( this . fetch ( match [ 1 ] )
774
854
. then ( response => response . text ( ) )
775
855
. then ( text => {
776
856
data [ match [ 1 ] ] = text ;
0 commit comments