Closed ghost closed 9 years ago
This would not be possible under the current spec due to there not being a concept of module names any more. Everything is keyed by url so virtual modules are an awkward fit now (hopefully this decision will be reconsidered, read #28). Add this to the pile of benefits of module ids over urls.
Also, neither System.define nor System.module have counterparts in the current spec.
Surely it makes the most sense, as @dherman pointed out at https://github.com/jorendorff/js-loaders/issues/83#issuecomment-33991024, to do the following:
name
attribute with System.module
.name
attribute with System.define
, but only if a path is not set for that name
. (This should have the added advantage of showing up within the sources panel as something other than <Anonymous Module>
.)src
, use the address-normalized path as usual. And for any modules with a name
, use the name-normalized path as long as it does not yet exist. It should also be possible to use both the name
and the src
paths if both are provided.I can't imagine any conflicts occurring with this system.
@matthewp that's not accurate, you can have any arbitrary entry in the registry.
Loader.install()
will allow you to define new entries in the registry: https://whatwg.github.io/loader/#reflect-loader-install
The normalization process (to a url), including the sites()
configuration will happen if there is no entry available in the registry for an arbitrary values in the module specifier.
@caridy Interesting, install()
doesn't specify a return value, this means it could be a Promise. This differs from the old System.set / System.get.
@timbur This spec is quite a bit different from the one referenced in that thread, we need a Loader.define
and Loader.module
first. I'm not sure what the authors' opinion is on .define
, I remember there was some problems with the old version. .module
will almost definitely be needed to support <module>
. Maybe a separate issue to track that?
In my opinion, es6-module-loader and systemjs are handling everything perfectly and intuitively, so it would make sense to model anything missing from this spec after their functionality. Plus, those projects are designed to match this spec, so it's a bit of a win-win for everyone.
@timbur systemjs should not dictate what we do here, it is the other way around. We are considering those ideas that we have tried, and play around with in systemjs, but ultimately we have to be pragmatic.
@matthewp I think you're confused. install()
does not fetch deps since you have to provide a [[Module]] instance, which produces a new entry in the registry with [[State]]: "ready"
, there is nothing to return.
I didn't mean to imply that SystemJS should dictate anything here. I was only saying that there is some extra functionality present within SystemJS which makes a lot of sense, and this functionality exists because of practical, real-world necessity/usefulness. I'll provide an example of said necessity shortly (in addition to the example I provided above).
This spec has a loader.provide
which actually gives more flexible behaviour than the System.define
, System.module
. See https://github.com/ModuleLoader/es6-module-loader/blob/1.0/src/loader.js#L429 for how it works.
@timbur note that name
is deprecated in this spec, and modules are only src
based.
@caridy I might be confused, let's break this down:
The normalization process (to a url), including the sites() configuration will happen if there is no entry available in the registry for an arbitrary values in the module specifier.
This normalization process is (potentially) asynchronous, if it goes through the resolve
hook. If that's the case it should return something.
@matthewp the normalization process only happens when importing a module (or a dep), not when installing a new entry in the registry.
Ok, then I did misunderstand you. So this mean keys are really just arbitrary strings, I can do:
System.install("@@foobar", { default: function() { return "bar"; } });
And anyone importing that will get the correct module, cool.
@guybedford Can that same flexible behavior be achieved directly through System
? Or is it currently limited to methods within loader
?
As for the real-world necessity, it makes no sense to render inline modules inaccessible. They're modules, too, right? It should be possible to import them. It also doesn't make sense to give them the src
attribute since the source is contained within itself. The only logical conclusion, in my opinion, is to use name
so that said inline modules can be imported by their names.
The way I see it, from a simple point of view and for predictability's sake, internally, when paths are set and modules are registered, all that matters in the end is the corresponding key. A few simple rules and everything gets insanely simple:
src
, has a file extension, etc.)? If so, use it and adjust the key to its full path name.<module name="...">
or explicitly defined some other way. And if it isn't already registered, hit the server without appending .js
to it.Anything beyond that would be convoluted, adding unnecessary complexity and wasting everyone's time. Keep it as simple and predictable as that and the only conceivable way developers could screw up is if they purposely shoot themselves in the foot.
I like the simplicity of having just src
.
What about this:
<module src="hello.js">
export default 'I am a module'
</module>
@MajorBreakfast that means src
has a different meaning depending on whether there is inner text or not. Would also differ from every other use of src
attributes in html.
@matthewp Hmm, yes. Maybe a <script>
tag + System.install()
(or provide, not sure) comes close enough already. Likely, there even shouldn't be a more convenient way to do this.
@timbur no. we have discarded that a while ago, and we've settled on the idea that inline modules will probably be focus on initialization (just like inline scripts are today), and therefore there is really no need to export things into the runtime from them, instead they just import other modules, and carry on with some initialization routines. I'm curious about what sort of inline script code you have today that modifies the runtime to extend the available functionality.
@guybedford I missed your comment but you're right that .provide
is a better .define
. Don't we still need a key-less form, though? By default a <module>
shouldn't be part of the registry.
@caridy My issue with that line of thinking is that if developers want to do something, they'll figure out a way to do it. It's always better to provide an elegant, predictable solution for people to use, rather than imposing arbitrary restrictions that will lead developers to create ugly hacks/workarounds. And for this particular case I'm 100% certain that will happen.
Consider this scenario: Users load some module specific to their profile. We'll refer to it as a duck
. Every user has their own duck
with custom functionality that could not be easily defined in any form other than JavaScript. It's initialized upon loading the page, and many other modules want to use that particular duck
, expecting a quack
method that is potentially custom (different) for each user.
Suppose we have my duck at timbur/duck.js
:
export function quack () {
// let's pretend this is all kinds of complex logic
console.log('quack!');
}
And we have your duck at caridy/duck.js
:
export function quack () {
// let's pretend this is all kinds of complex logic
alert('quack!');
}
And when I load the page, the HTML generated for me looks something like this:
<module>
import duck from 'timbur/duck.js';
duck.quack();
</module>
And yours of course looks something like this:
<module>
import duck from 'caridy/duck.js';
duck.quack();
</module>
And later on, perhaps some other module wants to make the user's particular duck
quack
, we'll call it goose.js
. There are of course many different ways to make this happen, so let's consider a few.
Without the ability to name (and register) the <module>
above, it's really not even a module. It's just a script. So, as a script, if we want to maintain some reference to the user's duck
, the script itself will need to assign the duck
to some global variable.
<script>
import duck from 'caridy/duck.js';
duck.quack();
window.main = {duck: duck};
</script>
Or for the sake of consistency and "elegance" (if you could call it that), one might pull some other module designed specifically for storing this information:
<script>
import store from 'store.js';
import duck from 'caridy/duck.js';
duck.quack();
store.set('duck', duck);
</script>
And then for our goose.js
module to use the user's particular duck
:
window.main.duck.quack();
Or:
import store from 'store.js';
store.get('duck').quack();
Now let's be honest. The above solutions are ugly and they go against the grain of the module system.
Of course, all of this so far might seem like a contrived example, and most people might find this particular usage an edge case. But again, it's never a good idea to impose restrictions on what should or shouldn't be possible only because it isn't some scenario that is easy to imagine. People are obviously already aware of these possibilities, so why limit them? It's much better to provide an elegant solution at the core.
And finally, let's consider a solution that I would probably use if naming an inline module was not built into the core of <module>
's functionality. I realize this solution relies on System.define
, but let's not be pedantic. The analogy remains.
HTML snippet:
<module name="main">
import duck from 'timbur/duck.js';
duck.quack();
export { duck };
</module>
<script src="register-inline-modules.js"></script>
register-inline-modules.js
:
var modules = document.getElementsByTagName('module');
for (var i = 0; i < modules.length; i++) {
var module = modules[i];
if (module.hasAttribute('name')) {
var name = module.getAttribute('name');
var source = module.innerHTML.substr(1);
System.define(name, source);
}
}
Then goose.js
would very predictably look like this:
import main from 'main';
main.duck.quack();
That is way simpler and follows the module paradigm much more consistently. But we aren't finished yet.
Last but certainly not least, consider that the name
attribute is supported. The following is all we need. No global pollution. No modules designed specifically to remember duck
. No unnecessary extra scripts. It's intuitive and succinct. Just use the registry.
HTML snippet:
<module name="main">
import duck from 'timbur/duck.js';
duck.quack();
export { duck };
</module>
goose.js
:
import main from 'main';
main.duck.quack();
It's entirely possible that I'm being stupid and completely overlooking an easier way to achieve this. And if that is the case, please let me know and I will gladly shut up about this particular example. Either way, I think there are more than enough use cases out there that we haven't thought of which will resemble the above functionality, so why not make it elegant?
@timbur I think the idea is to have a main.js
file (Directly or included in a bundle, e.g. the way SystemJS does it). The module tag code then simply imports that module.
It's always better to provide an elegant, predictable solution for people to use [...]
Not if the solution can be considered a dead end. I think it's good to separate JavaScript from HTML.
But it isn't a dead end. The key point is that it should be possible to elegantly register dynamic modules under some common name without resorting to extra scripts. Yes, in production, all static modules will be minified and bundled. But it would be stupid to create separate bundles for each user due to some dynamic module reference. The dynamic module would obviously automatically be excluded from the bundle, and the most efficient way for modules within the bundle to use it is to have its reference exported upon initialization. I don't know how I can make it any clearer, and I've yet to see a reasonable argument against this functionality, nor a more elegant solution to the problem.
Can the same effect be created with
? Like Tim's example, this would be a per user specific page script. Like Tim's example, the effect is to cause goose.js to see a different main module dynamically, when it runs import. Like Tim's example it makes development more challenging because the module is dynamic.
Unlike Tim's example it does not require a
(To be sure, IMO we don't need a
I was referred here to discuss this. Apologies if I'm going about it wrong. :)
The idea is to allow inline modules to be cached within the loader just like any other module by giving
<module>
aname
attribute.Suppose you have some inline module within HTML like so:
And
some/static/script.js
might look for themain
module:Two other relevant discussions: https://github.com/ModuleLoader/es6-module-loader/pull/343 https://github.com/jorendorff/js-loaders/issues/83#issuecomment-33991024