jorendorff / js-loaders

Pseudoimplementation of the proposed ES6 module loaders.
54 stars 7 forks source link

Add System.getName(module) #53

Open EisenbergEffect opened 10 years ago

EisenbergEffect commented 10 years ago

Looking at the System API, we have get and set, based on the module name and it seems that the module registry itself is completely internal. In various scenarios, it's useful to know what the name is for a particular module instance. I'd love to see the System API rounded out to include this other side of the equation.

briandipalma commented 10 years ago

The normalized name I guess, not the first id used to load the module? On Nov 21, 2013 7:31 PM, "Rob Eisenberg" notifications@github.com wrote:

Looking at the System API, we have get and set, based on the module name and it seams that the module registry itself is completely internal. In various scenarios, it's useful to know what the name or id is for a particular module instance. I'd love to see the System API rounded out to include this other side of the equation.

— Reply to this email directly or view it on GitHubhttps://github.com/jorendorff/js-loaders/issues/53 .

EisenbergEffect commented 10 years ago

Yes. The normalized name.

jrburke commented 10 years ago

In AMD/CommonJS systems, the local module object inside the module (in node, the object used for module.exports = code) has module.id for this, and then module.url for the location that would be used to fetch the module. Both have been useful in an AMD context. module.id in particular, for things like route/controller registration based on IDs.

EisenbergEffect commented 10 years ago

That's similar to what I want to use it for. In Durandal, we use some simple conventions to pair a module with a view. So, say you want to "render" and object to the screen. We first lookup the module id of the object, then we perform some transformation on it to arrive at an .html file. Then we import it, bind the two and add it to the dom. It's extremely flexible and allows us to do some powerful, polymorphic composition of the dom. But, it's not really possible if we can't get an id/name from a module instance.

jorendorff commented 10 years ago

I'd like to understand this better.

In @jrburke's case, where the module wants to know its own name, why is the module author in the dark about what module he or she is writing? Can you link to some real code that uses this feature?

In @EisenbergEffect's case, where we want to render an object to the screen, why do you need to know a module id? Can you link to some real code that does this?

The reason I ask about the second case is that I'd expect something like this:

function render(obj) {
    loader.import(obj.viewModuleId).then(viewModule => {
        var view = viewModule.newViewBoundTo(obj);
        view.showIn(document);
    });
}

I don't think there's any particular reason modules shouldn't have names; I'm pressing on this because understanding use cases has to precede design...

EisenbergEffect commented 10 years ago

Our code looks something like this right now (using RequireJS...and this is very simplified):

function render(obj){
    var moduleId = getModuleId(obj);
    var viewId = transformModuleIdToViewIdBasedOnConventions(moduleId);

    require([viewId], function(html){
        //parse html
        //bind dom element to obj
        //place bound element in document
    });
}

We don't want to force developers to have to manually add an id property to all of their objects. The information is currently available, so we use it to conventionally render views, simply based on module name. In actuality, we have a pluggable view location service with a default location convention that you can customize on a per app basis. There are other interesting things too, but the most important ingredient is being able to get an id/name given an instance of a module's object. From looking at the Loaders spec, it seems that this info is there, just no way to access it at present. Also, since there's no Loader hook that runs after the link stage, providing access to the fully realized module object and its metadata, there doesn't seem to be any way to build this functionality at present. I'd rather have the additional Loader hook (which I believe there's another ticket for), but I would settle for a getName API for my own personal uses at the moment.

jorendorff commented 10 years ago

From looking at the Loaders spec, it seems that this info is there, just no way to access it at present.

Oddly enough, it's not there; modules really don't have names in the current proposal. But it wouldn't be expensive to add.

Thanks for the hint; I really would like a pointer to the real code too, though! None of this is terribly easy to grep for in the Durandal codebase. :)

EisenbergEffect commented 10 years ago

Ah. In the spec I thought it mentioned a module registry. Regardless, it would be nice to have. Now, to the actual code, here's where some of it happens: https://github.com/BlueSpire/Durandal/blob/master/src/durandal/js/composition.js#L505

It's inside an extension to the binding system which allows a bound object to be rendered by locating it's view, binding it and injecting it into the dom. The viewLocator is what does that actual acquisition of the view. Here's where the interesting part is: https://github.com/BlueSpire/Durandal/blob/master/src/durandal/js/viewLocator.js#L75

After we convert the module id into a view id, we then use require.js to get the view. I'm fairly sure the same lifecycle could be replicated, given there's a way to get the module name or plug in after the linking stage and build up my own reigstry.

If it's still not clear, I may be able to whip up a little sample directly on top of require.js and send it your way.

samth commented 10 years ago

There is a registry, but it's not a one-to-one mapping between names and modules, which is I think what @jorendorff meant.

For people asking for this feature, what should it do when a module is available under multiple names?

jorendorff commented 10 years ago

@samth I can imagine an answer to that. Module names would be baked in at creation time.

So if the Module is the result of a Load, it takes that Load Record's [[Name]] field. If it's the result of loader.define() or a <module name=> element, that specifies its name. The Module constructor would let you specify the name as an optional argument. Host-provided modules could have host-specified names. Etc.

And from then on, the module's name just remains the same, even if you use loader.set() to make it available under another name, just as when you do var foo = function bar(){}; the .name of the function is still bar.

jorendorff commented 10 years ago

Thanks for the pointer, @EisenbergEffect.

What Durandal's doing here still seems unusual to me because I think of modules as code, not arbitrary objects. Maybe that's just me?

In any case, all @jrburke and @EisenbergEffect are asking for is not to have to repeat themselves. I can dig it.

In Python, __name__ is the current module's name. It's mainly used for if __name__ == '__main__': which is a need we have not addressed in any way. :-\

samth commented 10 years ago

On the __main__ issue:

jrburke commented 10 years ago

On the code example front, from an in-progress, experimental web app:

There is a Deck module that manages a deck of view cards. Each card module can send updates to all the cards of its type that are in a Deck. The Deck knows what cards to target by the module ID passed to it. So in a particular card, listing calls Deck.updateCards and then Deck.updateCards finds all dom nodes associated with that module ID and does the update.

Navigation to the next card is by URLs with fragment IDs where the main part of the fragment ID is the module of the card to use next. In that linked example, it is 'app/detail'. Deck loads that module to create the card.

Deck is a reusable library, and does not know what kinds of cards will be registered with it. Similarly, the cards themselves use module.id to refer to their ID instead of a hard-coded name to allow easier renames of files without then also modifying the file contents. All they do is reference the other card IDs in their template for the next card to display.

Deck also stores some data based on its module ID. Since Deck could also be installed into a project in a name other than "Deck", this is useful, even allows two Deck modules in a project (as long as they were loaded under different module IDs).

On the node main question:

http://nodejs.org/docs/latest/api/all.html#all_accessing_the_main_module