Skip to content

Commit b1d7c1d

Browse files
committed
Proper examples, and complication for cloning ModuleSource
1 parent 02244d2 commit b1d7c1d

1 file changed

Lines changed: 115 additions & 22 deletions

File tree

README.md

Lines changed: 115 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ We extend the `ModuleSource` constructor to accept an optional handler.
3434

3535
```ts
3636
interface 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
4444
object to any invocation of the `importHook` and `importMetaHook`, so the hooks
4545
may 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
5258
type ModuleHandler = {
5359
importHook?: ImportHook,
@@ -163,36 +169,123 @@ a canonicalized copy of the given attributes bag for the `importHook`.
163169

164170
```js
165171
await 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

Comments
 (0)