Discussion: Module Reflection - import.reflect
In import * as m from myModule
, m.default
is already the default
export; Object.keys(m)
is already the list of export names, and
Object.entries(m).filter(([k, v]) => typeof v === 'function'))
is the
list of functions.
Maybe my example was misleading by using an import
. What I'm talking about is inspecting the module from within itself. Similar to this
or self
, AFAIK, I can't get a reference to the module from within the module - e.g., module
. Say, I'm in a module that has a default export of class "Foo". In a function in that module (or class), I want to create an instance of that default export. I can do it by name new Foo()
. But, in more generic code, I need to do it by "role" - default
. So my generic version wants something that represents new module.default()
. This is because I may not have access to the name - say the function is in a base class. Take for example a "type" module, that is a wrapper for a single class, and the filename = class name. One way I can implement instanceof
is a string approach:
// Foo.mjs
export default class Foo {
static type = import.meta.url;
meta = { type: import.meta.url };
// instanceof
static [Symbol.hasInstance](instance) {
return instance.meta.type === Foo.type;
}
}
Some weaknesses are it's strings, and I need to know the name of the class (for Foo.type
). So I can't move this to a base class.
With module reflection, I could do this with objects:
// Foo.mjs
export default class Foo {
meta = { type: import.reflect.default };
// instanceof
static [Symbol.hasInstance](instance) {
return instance.meta.type === import.reflect.default;
}
}
A similar idea would be to define a Module Scope - module
. Within a module, using module
would be like a "static self" - it works on the structure (e.g., definition) of the module.
Then import.reflect.default
could be module.default
.
import * as Self from '.'
should work in every implementation of ESM
that's shipped so far that I'm aware of.
Interesting approach. Covers "public" (exported) members, which is about 90% of my use cases. Works great! Thanks.
It seems the recursion has problems with Symbol
, at least in Chrome.
import * as module from "/foo.mjs";
export default class Foo{ }
const bar = Symbol("Bar");
Load page, enter debug mode, and reload. The debugger disconnects.
Would it be worthwhile to add reflection to modules as a distinct feature?
import.meta.url
is a basic form of reflection, but it seems (?)meta
is trying to be a fairly loose specification. Being able query a module can be helpful. For example, if I can see the static imports, I can conditionally do dynamic imports. Or (thinking out loud here), can "generic" modules be built wheredefault
is the generic?Some of the possibilities are
import.reflect.default
- returnsdefault
in its native form (function, class, value)import.reflect.imports
- provides a list of static imports. Not sure what this should be (value or key-value (e.g., [importName, module]), but should be enumerable.import.reflect.functions
- provides a list of exported functions. Can be use, for instance, to validate the shape of a module.This can be exposed to an importing module.