# Runtime plugins

**Mosaic** introduces the concept of "plugins". This is a feature of [**extensions**](https://github.com/tilework/docs/tree/8e55fec3b15347bf6ef4b877289277dce07cccb5/extensions/extensions.md).

**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.

{% tabs %}
{% tab title="Pseudocode" %}

```javascript
Dear Plugin System,
    intercept member of kind KIND
    with name MEMBERNAME
    of namespace NAMESPACE
    instead of it, provide P(..., Y.X, ...) to the application
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const P = (...) => { ... }

export default {
    'NAMESPACE': {
        'KIND': {
            'MEMBERNAME': P
        }
    }
}
```

{% endtab %}
{% endtabs %}

In order to make this code actually do something in your application, you will need to change [KIND](#determine-target-kind), [NAMESPACE](#determine-target-namespace) and [MEMBERNAME](#member-name) 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](https://docs.mosaic.js.org/develop-an-extension/broken-reference) must be [created](https://docs.mosaic.js.org/develop-an-extension/anatomy-of-an-extension). You will implement your plugin there.

For the development purposes, it's recommended to have the under-development extension module [installed](https://docs.mosaic.js.org/install-an-extension/use-a-local-extension) into your project.

Then, you should create a plugin declaration file in the [proper place](https://docs.mosaic.js.org/anatomy-of-an-extension#file-structure). The guide below describes the process of implementing logic in this plugin declaration file. &#x20;

## Select a target

### Target namespace

**Each** plugin has a **target** to "plug into". This target is determined by a [namespace](https://docs.mosaic.js.org/namespaces#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!&#x20;

If you are willing to create&#x20;

{% hint style="info" %}
Remember to have the source module [properly configured](https://docs.mosaic.js.org/namespaces#how-namespace-comments-work) in order for namespace comments to be handled correctly by our [transformation](https://docs.mosaic.js.org/namespaces#how-namespace-comments-work)
{% endhint %}

### Target kind

Plugin system should know the kind of the piece of functionality you are trying to interact with. There are several such kinds.&#x20;

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.

{% hint style="info" %}
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)
{% endhint %}

## Implement proxy function

Each [target kind](#determine-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)

{% hint style="info" %}
The `callback` functions seen below are bound to the initial contexts automatically.
{% endhint %}

{% tabs %}
{% tab title="function" %}

* An array of **`args`** from the caller
* **`callback`** - the original function (or the next plugin)
* **`context`** - the original context of a function

```javascript
const plugin0 = (args, callback, context) => {
    return callback(...args);
}
```

{% endtab %}

{% tab title="member-function" %}

* An array of **`args`** from the caller
* **`callback`** - the original method (or the next plugin)
* **`instance`** - the original class instance, `this` of the invokable member

```javascript
const plugin1 = (args, callback, instance) => {
    return callback(...args);
}
```

{% endtab %}

{% tab title="member-property" %}

* **`member`** - the original value of the property
* **`instance`** - the original class instance, `this` of the invokable member

```javascript
const plugin2 = (member, instance) => {
    return member;
}
```

{% endtab %}

{% tab title="static-member" %}

* An array of **`arguments`** from the caller
* **`callback`** - the original method (or the next plugin)
* **`Class`** - the original class, `this` of the invokable member

```javascript
const plugin3 = (args, callback, Class) => {
    return callback(...args);
}
```

{% endtab %}
{% endtabs %}

{% hint style="danger" %}
**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.
{% endhint %}

## 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](#implement-proxy-function).

{% tabs %}
{% tab title="Initial functionality" %}

```javascript
/** @namespace Namespace/Of/Some/Function */
const fn = () => { ... }

/** @namespace Namespace/Of/Some/Class */
class SomeClass {
    propertyName = { ... }
    functionName() { ... }
    static staticName() { ... }
}
```

{% endtab %}

{% tab title="Plugins: concise" %}

```javascript
export default {
    'Namespace/Of/Some/Function': {
        'function': plugin0
    },
    'Namespace/Of/Some/Class': {
        'member-function': {
            functionName: plugin1
        },
        'member-property': {
            propertyName: plugin2
        },
        'static-member': {
            staticName: plugin3
        }
    }
};
```

{% endtab %}

{% tab title="Plugins: granular" %}

```javascript
export default {
    'Namespace/Of/Something': {
        'function': {
            position: 100,
            implementation: plugin0
        }
    }
};
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
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.
{% endhint %}
