-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Discussion] API to define loader hooks #62
Comments
I'm a fan of the Simple Getter and Setters approach as discussed in the subclassability issue because it will make it much easier to make custom loaders this way.
I don't think "resolve" is a good name for this hook, the proper name is "locate". It's been discussed ad nauseum about the change from normalize/locate -> resolve, but "locate" is not what was deprecated. Normalize is what was deprecated. Resolve is really just locate. It's locate with the knowledge of the parent url. tldr; I would rename "resolve" to "locate". |
A few thoughts (caveat: I know very little about how the API will work):
If there will only ever be a limited number of hooks then I would not provide a generic solution with strings as keys. For example, a variant of “current proposal” without generic keys is: // Get
var fetchHook = loader.hookFetch();
// Set
loader.hookFetch(function (key) {
// do something here...
}); |
Being able to call them as functions is an important part of the hooks, so if they were events they would be far less useful. One thing you do often when working on loader stuff is generate keys. I want to be able to call resolve (or whatever it is to be called) so that I can get the key for a module. edit: Some hooks like instantiate you would almost never call this way, it's mostly resolve and to a lesser extent translate where this could be useful. But if I'm authoring a custom Loader I need to be able to call them all. |
I'd need some more background on the problems you're trying to solve and how this maps to those problems. Right now, I'm unsure of what I'm reading.
What do these do? And what are hooks? |
@nzakas and co, let me provide more details: The loader's "internal engine" will carry on some procedures to locate, fetch, parse and execute each module, this procedures are executed in a predictable order. But for the sake of aligning with EWM, we want to provide access to the "internal engine" of the loader, specifically, provide a mechanism to control and even alter those procedures in user-land, whether that's by using AOP or any other similar pattern. As today, we have identified four stages during the internal life cycle that we will like to expose somehow, those are the one listed above. The question here is how to provide an API that is simple enough and powerful enough that allow users to tap into the internal procedures of the loader to add some extra functionalities for the loader. Additionally, we want to have a single access point for each of this stages (a single hook), but users might decide to chain them (in user-land using AOP or something else) if they choose to. |
Can you explain what each of those four stages do? It would be helpful for you to walk through an example, like: import { foo } from "bar"; What would the process be to implement this? |
This should probably use symbols, if we ever want to extend the pipeline in the future. Otherwise we may find authors squatting on names that are needed. So |
@domenic I like the idea of using symbols, but using them from @nzakas let me try to use a real example:
As part of the internal process, loader will:
In my mind, people will do a lot of AOP here to support various variations of modules and other assets that modules depends on, ideally that composition process for each hook is nice and easy. |
Why be fancy? Wouldn’t the simplest solution be subclassing plus overriding methods? Your protocol looks like there will only ever be four methods, which is why you don’t need an approach that is very flexible w.r.t. names. To avoid name clashes, you can give hook methods the name prefix However, loader base class methods and loader derived class methods may still clash, which is why non-public methods should use symbols as keys. |
@rauschma right. the problem with that approach is the default loader instance implemented by browsers, which is available thru |
The default instance should have a constructor as well. So you can extend it with: class SuperLoader extends System.loader.constructor {
fetch() {
...
}
}
System.loader = new SuperLoader(); So Reflect.Loader is a bare class with just noops, System.loader.constructor defines the default loader behavior, System.loader is an instance of that. |
That last line is not clear yet @matthewp, essentially because we don't know yet how that affects the behavior of |
Fair enough, so use wrapping if you want to override the default instance. You could still define the hooks in a class, create an instance and then wrap System.loader. I personally would just tell my users to call myCustomLoader.import()... in terms of having portability for libraries that expect to be able to use System.loader, once we have the ability to do One problem with the wrapping approach is that you really need a way to unwrap. I've run into this from time to time, you apply a hook (for testing purposes for example) and later want to remove the hook... it's much easier to simple create a new class and instantiate it for that purpose. |
Ok, decision was to use symbols (thanks @domenic for the suggestion), two main reasons:
We will stick to the same four hooks: The basic usage will be: var fetchHook = System.loader[Reflect.Loader.fetch];
System.loader[Reflect.Loader.fetch] = function (key) {
// do something here...
}; More about this change here: #65 |
Playing devil’s advocate one last time:
It’ll be interesting to see whether people chain hook functions (for the same hook). But doing so manually still seems better than supporting chaining via the API, e.g. via a special registry mechanism. |
Discussion
Purposes:
Questions:
Hooks:
resolve
fetch
translate
instantiate
.The purpose of the hook system is to provides a hookable pipeline, to allow front-end packaging solutions to hook into the loading process. E.g.: if a JavaScript program wants to translate .coffee files to JavaScript on the fly, the Loader defines a "translate" hook that can be used to get the coffee source code and transpile it into javascript before instantiation process.
note: each hook has its own signature, e.g.:
fetch(key)
vstranslate(key, payload)
.Current proposal:
note: it is confusing. the
hook
concept is new, it looks like an observable, but it is not. No precedent on this kind of api AKAIK.Simple Getter and Setters:
note: the problem with this approach is that one of the hooks is called
resolve
, and one method of loader is also calledresolve()
, which is an important one.Getter and Setters:
note: the problem with this approach is that there is no precedent of the concept of hooks, it might be confusing, and on top of that it is ugly IMO.
Others
loader.onFetch()
looks like an observable/event, which is not the case, only 1 hook can be in place.loader.hookFetch
same asloader.fetchHook
.The text was updated successfully, but these errors were encountered: