-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Plugin architecture #1589
Comments
+1 this sounds awesome! |
+1 |
There are some great ideas here for encouraging the development and use of Knockout plugins. It seems to me, though, that if this system is built into Knockout, that would make it difficult for plugins to support multiple versions of Knockout, something that's usually quite important. |
Thanks @mbest. What are the issues you are thinking might come up between versions of Knockout? There seem to be four possibilities, namely whether the Knockout version and an example plugin are respectively designed with/for the framework or not, just for clarity and illustration:
I think the boilerplate plugin design and the Knockout plugin framework ought to cover all these scenarios. In other words the proposed Knockout plugin framework ought to still allow current plugins to work in the same way, and as the plugin boilerplate ought to be designed in such a way that the Knockout plugin framework is not necessary (but having it reduces the amount of loading/initialization code). In other words, the plugin framework would be optional from the user perspective. That said, do you see something intrinsic to the framework that might prevent it from working this way? |
Thanks for raising this! Your points #2 and #3 are very easy to agree with. I don't see any drawbacks to doing those, and it would certainly aid discoverability a lot of there were some kind of de-facto standard place to acquire plugins and extensions. For point #1 though I admit I'm not quite as sold on what problems it would be solving. There is already a wide range of extensibility points that plugins can attach themselves to, without needing a central "register plugin" API. Having a central API comes with the impression that KO is somehow reflecting over the set of registrations and handling things like avoiding name clashes, supporting unregistrations, and so on, which would not be the case and hasn't so far been a requirement. AMD (or other module systems) already takes care of dynamic loading (and knowing when things are loaded), so for example using Knockout Punches is already as simple as referencing a .js file (or AMD module) and calling Please don't take this as a rejection of #1, since I'm totally interested - I'm just admitting that so far I'm unclear on if or how the overhead of setting up a new centralised API would pay off in terms of what pain points would be alleviated. For #2, are you thinking we'd set up a new website, or would this somehow be bootstrapped on top of an existing package distribution system like NPM? |
Thanks @SteveSanderson -- my pleasure! I think #2 would work on the main site, though a new site works too. It just needs to include the html and the JSON/YAML file listing all the plugins. Github pages might be adequate. How were you thinking a bootstrapping on e.g. NPM might work? The pain that #1 proposes to alleviate is two-fold:
Having written it out, those aren't really all that painful 😀 ... especially with #3 which could illustrate some good & strong conventions. Unless there's some pain that has been forgotten since I wrote the issue out in the first place (quite possible), maybe we can skip this for now and I'll revisit if the pain returns. I have had a little bit of a hand in #3 by creating knockout-plugin-foundation... though I have not yet gotten very far with it. |
@brianmhunt |
@bapti Thank you so much for sharing, that's very important feedback. It is easy to lose perspective, and I hope I don't appear flip-floppy on this point. 😄 I think you have hit on the mark my original thinking on a plugin framework:— lower the barrier to entry with clearly exposed centralized API endpoints, and homogenizing for end-user consistency. Making it easier for others to make it better, so to speak. |
Here's a sample plugin website regarding point 2 above. |
Incidentally, perhaps we could pull brianmhunt/knockout-plugins-foundation into the Knockout organization - with that project serving as the foundation for points 2 and 3 above. |
There are more than 1 issue here I think. Points 2 and 3 are good ideas. Regarding 1, I am not sure what pain point this new API addresses. Here's my experience: I am working on medium to big projects where we create lots of components and custom binding handlers. We use Typescript, AMD modules and requirejs (with optimized production code using r.js). Declaring a new component or handler is very easy and done in a single file. It contains the code plus the registration line (so those things all Using it is just a matter of making sure they have been loaded. Inside the viewmodels that need a custom handler, we put In the end I don't see how additional APIs would make that easier. Maybe for other kind of extensions such as preprocessors (which admittedly we don't use much)? |
@jods4 - thanks for the feedback. You're right that for userland stuff there is not much pain here. I was thinking it might make it easier for plugins like knockout-else, knockout-punches, knockout-secure-binding, and others to register things like preprocessors. Right now there is a lot of duplication of code - sometimes a big percent of a small plugin - just to avoid clobbering other plugins. While the number of plugins / plugin authors is relatively much smaller than users, the idea is to lower the barrier to entry for those who want to push out what might be a useful plugin for others to use. |
A good example of what I was thinking for the plugins architecture is not hugely ambitious, being along the lines of the 135 line microplugin.js library. |
By some strange coincidence I recently had to create some binding preprocessors. Now I see what your first point is about. If you want to write a custom binding handler with its own preprocessor it's easy enough. But if you want to hook your preprocessor into other bindings (e.g. the built-in ones), then the problem is that the preprocessor API only supports one preprocessor per binding as the Isn't the solution to this problem to simply build @mbest's helpers (found in |
I can't help but feel that dependency management is going a little overboard here... If you have preprocessors that depend on one another (is that common? not in my experience), or maybe on a specific binding handler, it's a matter of declaring them in the right order in your page or bundling tool -- or better yet use an AMD system such as requirejs and declare your dependencies properly (that's what I do). I think we're far from the TextEditor with open plugin architecture that is the example given in microplugin readme. |
@jods4 Thanks. The dependency management is rather simple and straightforward, but opens a whole new class of dependency chaining that some argue is otherwise impractical. It's worth at least considering. :) The trick/benefit is for it to remain build-system agnostic, as some folks are using CommonJS, AMD/RequireJS, and soon there'll be ES6. |
Having written a few plugins now for Knockout, I feel like there may be opportunities to make it easier to create, find and use plugins. Here are just a few thoughts on some of the things I feel are good practices (and clearly this is longer-term thinking in the sense that it would break the API for plugins substantially, but I thought I would write it down while it is on my mind).
The below has a lot of "ideal world", but there may be more practical or less-ambitious alternatives that are just as or better suited to the Knockout universe.
1. API ("Using plugins")
A
knockout.plugins
object that exposes:knockout.plugins.register(plugin-spec)
(called by the plugin author when the plugin is loaded)knockout.plugins.use(plugin-name, options)
(called by user, perhaps multiple times to change the options?)knockout.plugins.load(identifier)
(called by .use; may be overloaded by user)The idea here is to streamline a consistent way for plugin authors to add make their contributions available and also for users to get started with those plugins.
In the life-cycle of a plugin, the
plugin-spec
could include the following parameters:name
: The name of the plugin, as argument.use
or future commands such asunregister
handlers
: An object with handlers that this plugin may add, extendingko.bindingHandlers
provider
: An object that provides a binding handler to replace the Knockout binding handlerpreprocess
: A preprocessor function or list of preprocessor functionscustom
: A function that gets called to do anything not handled by the aboveThe idea here is to get rid of as much user-land boilerplate as possible. I.e. every plugin needn't have an
addPreprocessor
function, or to extend thebindingHandlers
object, or have to have a custominit
orsetup
etc function that sets the options for the plugin.So when one wants to use
knockout.punches
, it would be as simple as:In the ES6 modules/RequireJS world, the above would call e.g.
require(['knockout.punches'], ko.plugines.register)
. In the Browserify world the hard-codedrequire('knockout.punches')
needs to come before the.use
call.One would generally not want
ko.applyBindings
to be called while plugins are loading, and there are several ways around this, for example:plugins.on_loaded
callback; orplugins.use(plugins-map)
that returns athenable
e.g.I feel the
ko.applyBindings
call should for backwards compatibility always be synchronous, but perhaps it should throw an error if plugins are not loaded.2. An online Registry
I personally like the style and setup of the Chai.js registry source control, though I would prefer the original to be in the more human-legible yaml or cson. With Chai.js plugin authors submit pull requests to add their respective plugins.
Of course, one then needs a place to find them, like Gulp.js.
In an ideal world the registry could use the Git API to check the last time the plugins were updated, the number of stars/forks, as well as any associated CI test results (which is really just guessing the url of the status image), since this is handy info.
3. Base plugin project
One of the trickiest bits with authoring a knockout plugin is getting started. It would be a great help to authors if there was a boilerplate project (e.g.
knockout-plugin-base
) that could be forked. It could include, for example:dist/
,spec/
,src/
,README
,package.json
,LICENSE
, etc.)config.yaml
)There are undoubtedly some drawbacks to the proposal above (e.g. compelling plugin authors to use one particular test runner), but I would hope that with a plugin architecture like the above the pros would substantially outweigh the cons.
This is just some food for thought, of course. It actually started out as a very short post and modest proposal, honestly. :)
The text was updated successfully, but these errors were encountered: