11namespace AngleSharp . Js
22{
33 using AngleSharp . Dom ;
4+ using AngleSharp . Io ;
5+ using AngleSharp . Text ;
46 using Jint ;
57 using Jint . Native ;
68 using Jint . Native . Object ;
79 using System ;
810 using System . Collections . Generic ;
11+ using System . IO ;
12+ using System . Linq ;
913 using System . Reflection ;
14+ using System . Text . Json ;
1015
1116 sealed class EngineInstance
1217 {
@@ -17,14 +22,26 @@ sealed class EngineInstance
1722 private readonly ReferenceCache _references ;
1823 private readonly IEnumerable < Assembly > _libs ;
1924 private readonly DomNodeInstance _window ;
25+ private readonly IResourceLoader _resourceLoader ;
26+ private readonly IElement _scriptElement ;
27+ private readonly string _documentUrl ;
2028
2129 #endregion
2230
2331 #region ctor
2432
2533 public EngineInstance ( IWindow window , IDictionary < String , Object > assignments , IEnumerable < Assembly > libs )
2634 {
27- _engine = new Engine ( ) ;
35+ _resourceLoader = window . Document . Context . GetService < IResourceLoader > ( ) ;
36+
37+ _scriptElement = window . Document . CreateElement ( TagNames . Script ) ;
38+
39+ _documentUrl = window . Document . Url ;
40+
41+ _engine = new Engine ( ( options ) =>
42+ {
43+ options . EnableModules ( new JsModuleLoader ( this , _documentUrl , false ) ) ;
44+ } ) ;
2845 _prototypes = new PrototypeCache ( _engine ) ;
2946 _references = new ReferenceCache ( ) ;
3047 _libs = libs ;
@@ -73,12 +90,132 @@ public EngineInstance(IWindow window, IDictionary<String, Object> assignments, I
7390
7491 public ObjectInstance GetDomPrototype ( Type type ) => _prototypes . GetOrCreate ( type , CreatePrototype ) ;
7592
76- public JsValue RunScript ( String source , JsValue context )
93+ public JsValue RunScript ( String source , String type , JsValue context )
7794 {
95+ if ( string . IsNullOrEmpty ( type ) )
96+ {
97+ type = MimeTypeNames . DefaultJavaScript ;
98+ }
99+
78100 lock ( _engine )
79101 {
80- return _engine . Evaluate ( source ) ;
102+ if ( MimeTypeNames . IsJavaScript ( type ) )
103+ {
104+ return _engine . Evaluate ( source ) ;
105+ }
106+ else if ( type . Isi ( "importmap" ) )
107+ {
108+ return LoadImportMap ( source ) ;
109+ }
110+ else if ( type . Isi ( "module" ) )
111+ {
112+ return RunModule ( source ) ;
113+ }
114+ else
115+ {
116+ return JsValue . Undefined ;
117+ }
118+ }
119+ }
120+
121+ private JsValue LoadImportMap ( String source )
122+ {
123+ JsImportMap importMap ;
124+
125+ try
126+ {
127+ importMap = JsonSerializer . Deserialize < JsImportMap > ( source ) ;
128+ }
129+ catch ( JsonException )
130+ {
131+ importMap = null ;
132+ }
133+
134+ // get list of imports based on any scoped imports for the current document path, and any global imports
135+ var imports = new Dictionary < string , Uri > ( ) ;
136+ var documentPathName = Url . Create ( _documentUrl ) . PathName . ToLower ( ) ;
137+
138+ if ( importMap ? . Scopes ? . Count > 0 )
139+ {
140+ var scopePaths = importMap . Scopes . Keys . OrderByDescending ( k => k . Length ) ;
141+
142+ foreach ( var scopePath in scopePaths )
143+ {
144+ if ( ! documentPathName . Contains ( scopePath . ToLower ( ) ) )
145+ {
146+ continue ;
147+ }
148+
149+ var scopeImports = importMap . Scopes [ scopePath ] ;
150+
151+ foreach ( var scopeImport in scopeImports )
152+ {
153+ if ( ! imports . ContainsKey ( scopeImport . Key ) )
154+ {
155+ imports . Add ( scopeImport . Key , scopeImport . Value ) ;
156+ }
157+ }
158+ }
159+ }
160+
161+ if ( importMap ? . Imports ? . Count > 0 )
162+ {
163+ foreach ( var globalImport in importMap . Imports )
164+ {
165+ if ( ! imports . ContainsKey ( globalImport . Key ) )
166+ {
167+ imports . Add ( globalImport . Key , globalImport . Value ) ;
168+ }
169+ }
170+ }
171+
172+ foreach ( var import in imports )
173+ {
174+ var moduleContent = FetchModule ( import . Value ) ;
175+
176+ _engine . Modules . Add ( import . Key , moduleContent ) ;
177+ _engine . Modules . Import ( import . Key ) ;
178+ }
179+
180+ return JsValue . Undefined ;
181+ }
182+
183+ private JsValue RunModule ( String source )
184+ {
185+ var moduleIdentifier = Guid . NewGuid ( ) . ToString ( ) ;
186+
187+ _engine . Modules . Add ( moduleIdentifier , source ) ;
188+ _engine . Modules . Import ( moduleIdentifier ) ;
189+
190+ return JsValue . Undefined ;
191+ }
192+
193+ public string FetchModule ( Uri moduleUrl )
194+ {
195+ if ( _resourceLoader == null )
196+ {
197+ return string . Empty ;
198+ }
199+
200+ if ( ! moduleUrl . IsAbsoluteUri )
201+ {
202+ moduleUrl = new Uri ( new Uri ( _documentUrl ) , moduleUrl ) ;
203+ }
204+
205+ var importUrl = Url . Convert ( moduleUrl ) ;
206+
207+ var request = new ResourceRequest ( _scriptElement , importUrl ) ;
208+
209+ var response = _resourceLoader . FetchAsync ( request ) . Task . Result ;
210+
211+ string content ;
212+
213+ using ( var streamReader = new StreamReader ( response . Content ) )
214+ {
215+ content = streamReader . ReadToEnd ( ) ;
81216 }
217+
218+ return content ;
82219 }
83220
84221 #endregion
0 commit comments