@@ -34,7 +34,7 @@ We extend the `ModuleSource` constructor to accept an optional handler.
3434
3535``` ts
3636interface ModuleSource {
37- constructor (source : string , handler ? : ModuleHandler );
37+ constructor (source : string | ModuleSource , handler ? : ModuleHandler );
3838}
3939```
4040
@@ -44,10 +44,16 @@ The implementation will thereafter pass the handler as the receiver
4444object to any invocation of the ` importHook ` and ` importMetaHook ` , so the hooks
4545may consult other properties of the handler.
4646
47- > The handler is necessary for capturing a base specifier for resolving a
48- > relative import specifier, and allows 262 to avoid unnecssary specificity
47+ > Aside, the handler is necessary for capturing a base specifier for resolving
48+ > a relative import specifier, and allows 262 to avoid unnecssary specificity
4949> about resolution algorithms.
5050
51+ Because the identity of a ` ModuleSource ` is a key in a realm's module map for
52+ purposes of denoting a corresponding module instance, we introduce the ability
53+ to construct a ` ModuleSource ` from the precompiled text and host data of
54+ another module source, but producing a distinct identity for purposes of
55+ multiple instantiation.
56+
5157``` ts
5258type ModuleHandler = {
5359 importHook? : ImportHook ,
@@ -163,36 +169,123 @@ a canonicalized copy of the given attributes bag for the `importHook`.
163169
164170``` js
165171await import (' z' , { type: ' json' , extraneous: ' very' });
166- // ->
167- wait importHook (' z' , { extraneous: ' very' , type: ' json' });
168172```
169173
170- # Examples
174+ So the implementation then calls the ` importHook ` with the canonicalized import
175+ attributes.
176+
177+ ``` js
178+ await importHook (' z' , { extraneous: ' very' , type: ' json' });
179+ ```
180+
181+ ## Examples
171182
172- ### Import Kicker
183+ ## Mocking
173184
174- Any dynamic import function is suitable for initializing, linking, and
175- evaluating a module instance from a module source.
176- This necessarily implies advancing all of its transitive dependencies to their
177- terminal state or any one into a failed state.
185+ With an ` importHook ` , a test can isolate a specific module and mock one or more
186+ of its dependencies.
187+ In this example, we import a test fixture module and execute it in an
188+ environment where its ` fs ` dependency is met by ` mock-fs ` and all other
189+ imports fall through to the host.
178190
179191``` js
180- const source = new ModuleSource (` ` );
181- const namespace = await import (source);
192+ import source fixture from ' fixture.js' ;
193+
194+ await import (new ModuleSource (fixture, {
195+ importHook (specifier , attributes ) {
196+ if (specifier === ' fs' ) {
197+ return import .source(' mock-fs' );
198+ } else {
199+ return import .source(specifier, attributes);
200+ }
201+ }
202+ }));
182203```
183204
184- ### ModuleSource Idempotence
205+ ## Mocking Membrane
206+
207+ With a slight complication, the fixture can be imported in an environment where
208+ its transitive dependencies experience the same distortion of ` fs ` to
209+ ` mock-fs ` .
210+
211+ ``` js
212+ import source fixture from ' fixture.js' ;
213+
214+ const handler = {
215+ importHook (specifier , attributes ) {
216+ if (specifier === ' fs' ) {
217+ return import .source(' mock-fs' );
218+ } else {
219+ const source = await import .source(specifier, attributes);
220+ return new ModuleSource (source, handler);
221+ }
222+ },
223+ }
224+
225+ await import (new ModuleSource (fixture, handler));
226+ ```
227+
228+ This example is admittedly reductive for illustration purposes.
229+ The ` importHook ` is also responsible for resolving module-relative and
230+ package-relative URLs.
231+
232+ ## Module system extension
185233
186- Since an execution environment associates a ` ModuleSource ` object with an
187- internal module record, importing the same module source in the same execution
188- context will produce a fresh promise for the same module exports namespace
189- exotic object as all previous imports .
234+ In this example, we create a module source that transitively traps all imports
235+ and loads source for them with ` fetch ` and treats all import specifiers as
236+ URLs, resolving relative URLs to the location of the containing module source,
237+ and provides ` import.meta.url ` .
190238
191239``` js
192- const source = new ModuleSource (` ` );
193- const namespace1 = await import (source);
194- const namespace2 = await import (source);
195- namespace1 === namespace2; // true
240+ const handlerPrototype = {
241+ async importHook (importSpecifier ) {
242+ const url = new URL (importSpecifier, this .url ).href ;
243+ const response = await fetch (url);
244+ const text = await response .text ();
245+ return new ModuleSource (text, {
246+ __proto__: handlerPrototype,
247+ url,
248+ });
249+ },
250+ importMetaHook (meta ) {
251+ meta .url = this .url ;
252+ },
253+ };
254+ const source = new ModuleSource (`
255+ import 'example.js';
256+ ` , { __proto__, url: import .meta.url });
257+ await import (source);
258+ ` ` `
259+
260+ With a slight complication, the hook can accommodate the ` type` ` json` import
261+ attribute, or arbitrary types and arbitrary other import attributes.
262+
263+ ` ` ` js
264+ const handlerPrototype = {
265+ async importHook (importSpecifier , { type }) {
266+ const url = new URL (importSpecifier, this .url ).href ;
267+ const response = await fetch (url);
268+ if (type === ' json' ) {
269+ const json = await response .text ();
270+ // Virtual module sources (to be proposed) would obviate the need to
271+ // embed the JSON in a JS module source.
272+ return new ModuleSource (` export default ${ json} ` );
273+ } else if (type === undefined ) {
274+ const text = await response .text ();
275+ return new ModuleSource (text, {
276+ __proto__: handlerPrototype,
277+ url,
278+ });
279+ }
280+ },
281+ importMetaHook (meta ) {
282+ meta .url = this .url ;
283+ },
284+ };
285+ const source = new ModuleSource (`
286+ import 'example.js';
287+ ` , { __proto__, url: import .meta.url });
288+ await import (source);
196289` ` `
197290
198291# Intersection Semantics
0 commit comments