Runtime plugins
Modify source logic without rewriting the source code!
Mosaic introduces the concept of "plugins". This is a feature of extensions.
A plugin is a proxy between the original function (or object) and the function caller (object user). A plugin intercepts invocation of the original function and has control over the arguments and retrieved return value
Plugin declaration files
Also conventionally called ".plugin.js" files, are the most important part of any extension. Because that's where the plugging logic is declared!
An instruction to the plugin system in pseudocode looks as follows.
In order to make this code actually do something in your application, you will need to change KIND, NAMESPACE and MEMBERNAME to values relevant for your application. The process of getting these values is described below in this guide.
Preparations
In order to declare a plugin, some preparations are necessary. First of all, an extension module must be created. You will implement your plugin there.
For the development purposes, it's recommended to have the under-development extension module installed into your project.
Then, you should create a plugin declaration file in the proper place. The guide below describes the process of implementing logic in this plugin declaration file.
Select a target
Target namespace
Each plugin has a target to "plug into". This target is determined by a namespace. Namespace should be present in the your source, which you are willing to change. If your source lacks such a namespace - put it there!
If you are willing to create
Remember to have the source module properly configured in order for namespace comments to be handled correctly by our transformation
Target kind
Plugin system should know the kind of the piece of functionality you are trying to interact with. There are several such kinds.
Currently, the following targets are available for modifications through the plugin system:
function - for functions declared both via regular and arrow syntax, not belonging to any class
member-function - for functions declared either as regular ES6 class members or as class properties (arrow functions)
member-property - for class members declared via class property syntax (but not for functions!)
static-member - for any kind of static members
Member name
For every target kind apart from function, it is necessary to determine the name of the member you are willing to interact with. All of these target kinds are related to classes, and the names of their members will be taken for this purpose.
Due to the function being the only member of its namespace, it has a reduced configuration section and does not require a member name (see in examples)
Implement proxy function
Each target kind expects a function with a different set of arguments. Below is an overview of function implementations for each proxy type. Each of these plugins solely invokes the original functionality, thus leaving the application intact.
Such a proxy function has full control over the call of the target that you are plugging into. Hence, you can modify arguments passed to it, modify the return value, decide on whether to call the original member or not (you usually are required to, see a warning below)
The callback
functions seen below are bound to the initial contexts automatically.
An array of
args
from the callercallback
- the original function (or the next plugin)context
- the original context of a function
Not calling the callback
will prevent the initial logic from being called, thus overwriting it completely. Such an action makes your plugin much less compatible with any other plugins.
This is not recommended, but if critically necessary - do this only acknowledgedly and on your own risk.
Configure the plugin
In order for a plugin declaration file to have impact on the application, it should export a piece of configuration that will be consumed by the plugin system. See the structure of such a configuration in the example below.
Note, that the variables called pluginX
are proxy functions described above.
You can create class members that do not exist in the original classes. It is useful when you need some life-cycle member functions that are not present in the original class.
Last updated