-
Notifications
You must be signed in to change notification settings - Fork 255
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
WASM Plugin / extension system #104
Comments
Here are some of the possible ways we can approach this: Option 1: FFIThe plugins can be exported as libraries by the plugin author and they would be imported as ".so" files through dynamic linking by trunk itself. Pros
ConsI see a few:
Option 2: Microservice approachWe can follow a microservice-like architecture where the plugins can be a microservice and can communicate with the main trunk CLI using a socket (either TCP socket or a unix socket, but windows is finicky about unix sockets) and transfer data using that. I've made a similar approach when I was working on project Juno. In fact, we can use project Juno itself and build using it, since we'll end up saving time by not writing most of the base for the communication service, and project Juno gives us libraries in Rust to help us with the communication. Pros
Cons
Option 3: Embedded scriptingMy personal favorite: some kind of embedded scripting to control what the plugin does. I'd personally recommend Rhai but I'm open for suggestions. The plugin content can be downloaded as a script file and can be loaded into Rhai and executed from there. Pros
Cons
My personal suggestion is to go with option 3. But I'd love to hear more opinions and suggestions so that we can iterate on this and come up with a good approach to this 😬 Food for thought: |
If we can do safe FFI (like with rodrimati1992/abi_stable_crates/) then that sounds like the best option to me. Its an option worth exploring. If that doesn't work out for whatever reason, scripting language support definitely sounds like a good option (maybe we can even use Python, if that's possible). |
How about using |
@lukechu10 ahhh yea. I love that idea! I'm really glad that you mentioned this @lukechu10! I feel like we would be fools not to pursue this as a foremost option. We are literally a bundler for Rust WASM hahaha!
This is good. Love it. Part of what we will need to do is provide specific levels of access to filesystem or other such capabilities, but that can be an integral part of the plugin ABI. |
I am going to update the header of this issue to reflect this option as the primary option moving forward. My big questions right now if we go with this option are:
|
I love the idea of having a plugin system. That would be quite useful, and I'd like to contribute to that. |
@DzenanJupic agreed. Currently the pipeline system has not concept of
AlternativeThat said, instead of using stages we could use a graph structure. Here's the idea:
This has the advantage of being able to explicitly declare dependencies and order of execution. With stages, dependency order is not really explicit, and if one pipeline depends upon the output of another pipeline from the same stage, then dependency resolution is a bit of a mess. Turns out, there are lots of nice tools for working with graphs in Rust. I'm actually leaning towards the graph approach. @DzenanJupic thoughts? |
@thedodd That sounds like a great idea and has definitely advantages over just using individual build stages. Especially because this also allows for parallel execution of independent paths in the graph. If, for example, there's a JS plugin, that minifies javascript files and removes dead code, another plugin/pipeline for compiling SASS could just run side by side with the JS plugin. So implementing a graph like a build system would come with great performance advantages. |
Regarding the question what the API of the I think that there are three main ways to do this:
I personally think that a mix of two and three could be a good fit. Option two would give plugin authors the ability to communicate with trunk, which can be quite useful. And option three gives the user a lot of control over the plugin. What do you think? Is there something I forgot or got wrong? |
@DzenanJupic solid overview. I would actually propose a modified version of option
This will provide all of the We can pass along whatever data we see fit to the plugins, but at a minimum it should include the
OutputsThat said, if we go with the modified Downloading PluginsFortunately for us, @dnaka91 has done some really great work over in #146 related to downloading & caching of various artifacts (wasm-bindgen, wasm-opt &c). We SHOULD be able to use these same abstractions to find and download WASM modules to be loaded into our WASM runtime for execution. There are still outstanding questions on exactly what our protocol should be for defining the location of modules. An easy approach may be to just require plugin authors to use https://wapm.io/ (WASM package manager), but maybe we need more flexibility / less locking. Thoughts? |
Just adding my two cents. I would like to propose a 4th option Protobuf/gRPC. Was looking a bit at the wasmtime and wasmer crates and the interaction with a wasm module seems to require quite a lot of unsafe code and in general lots of effort to pass data around. Therefore, it could be combined with solution 3 and just use Protobuf for all data. This would allow to have a properly defined interface and allow future upgrades easily. JSON might work as well but doesn't have a schema that can be easily shared. Another approach would be to require the plugin to be a binary that's then simply called with the Protobuf message passed in over stdin (that's how the protoc compiler does it). And if the binary needs to continue to run (for example invoking it again and again could be slow due to upstart every time), then eventually gRPC could be used, run over stdin/stdout or local TCP. These approaches have similar issues as to option 2, the Microservice approach, but would help simplify the interface between trunk and plugins. This is just an idea from my side, option 3 sounds great as well. |
@dnaka91 thanks for the input. Very interesting set of options. I do think the binary approach (a la protoc) is worth consideration. One thing I would like to share: https://github.com/wasmerio/wasmer/blob/master/examples/table.rs
I will say that I have a lot of hesitation about getting into the land of starting networked components for the plugin system. As soon as we get into that realm, the number of potential problems which users may come across will rise sharply. I would like to keep that option as a last resort. |
Another thing which we will probably want to do, if we go down the WASM path (which almost without a doubt we will/should), is provide a Trunk CLI subcommand to aid in compiling Rust WASM plugins. We could leverage the work @dnaka91 has already started on downloading and using wasm-opt to optimize the final WASM for the user. I'll add a few on this topic to this issue's description. |
How about a similar approach to For plugins that are only used project-wide, there could be a similar directory in the project folder. |
Maybe nushell is worth a look, too. It basically builds on the idea of piping structured data from one program to another. Maybe that would even be enough. So building with trunk would actually just be a set of programs and their accumulated pipes. Pros (I think):
One could easily download these programs according to a config file (maybe even describe a graphed pipeline in there). Under any circumstances I think the probably greatest advantage with other systems solving similar Problems in other ecosystems is the complete customizability of a build (see webpack or even gradle for java). Which I can only see be accomplished through embracing the WASM approach being embraced maybe even further than previously discussed? Would really like to hear your thoughts on that. |
The simplicity of being able to pass a protobuf payload (or the like) between CLI binaries does seem quite nice. Can't beat the simplicity of such a model. Now that @dnaka91 's auto-download system has landed, we could leverage that to be able to download and install basically any binary (given enough info), and make them available for potentially multiple projects. This would also work for downloading wasm modules. The plugins could live on a Github release page or any other location really. All of these patterns have similar pros:
Non-wasm cons:
WASM:
|
Moving the discussion over to #223. We can re-open this if needed. |
Abstract
Given that there will always be new features that folks want to add to Trunk, but adding too much to Trunk core would cause a fair amount of bloat, we will eventually want to expose a plugin system. The following is a non-exhaustive list of what we would like to see from the plugin system (please comment below if you would like to add or remove items):
<link data-trunk data-plugin rel="my-plugin-type" any-other-attrs="will be passed to plugin"/>
(this needs further discussion).rel="my-plugin-type"
.The Trunk team should build and maintain a
trunk-plugin
library which exposes common types used by Trunk itself, and which plugins should use to facilitate communication between Trunk and the plugin. Big shoutout to @lukechu10 for pointing out that WASM is perfect for this.We need to gather some feedback.
We will also want to discuss what Trunk itself should do in order to aid plugin authors in compiling, optimizing and publishing their WASM-plugins.
There is a lot to discuss here.
The text was updated successfully, but these errors were encountered: