Plugins: implementing

As of v3, ScandiPWA supports frontend plugins - reusable extensions that once created can be used in any project utilizing ScandiPWA v3. These can be used to modify the functionality of almost any part of ScandiPWA.

Watch a definitive explanation video

Extension file structure

A ScandiPWA extension is an M2 module (composer package) with an additional directory - scandipwa, which contains ScandiPWA frontend-related functionality. The extension can contain any other M2 directories with backend functionality implementation. For example, the extension below has theetcandModeldirectories.

All directories in scandipwa are optional. However, following the specified structure is mandatory - the app and sw subdirectories of scandipwa follow the same structure as vendor/scandipwa/source/src/(app|sw). These directories have the same meaning: component is for your extension’s components, query is for GraphQL queries, etc.

The plugin directory contains files specifying the configuration and implementation of your plugins. Details are provided below.

A correct file structure is essential for every ScandiPWA extension. Any diversity from the pattern talked about in this article can cause malfunction. ScandiPWA extension’s file structure overview:

Creating a simple extension

  1. In a subdirectory of your package - src/scandipwa/app/plugin, create files for your plugins. By convention, these must end with .plugin.js

    i. Implement your plugin’s logic (see Plugin implementation)

    ii. Configure your plugin’s target (see Plugin configuration)

  2. Enable your extension in scandipwa.json (see Enabling extensions)

  3. Restart the frontend compilation script for the configuration to take effect. This is also necessary whenever the scandipwa.json file is changed.

Plugin implementation

Plugins are used to alter the behavior of functions or classes. This is done by creating wrappers for existing values to control their new behavior, similarly to Magento “around” plugins.

There are 3 main types of plugins: plugins that wrap around functions, those that wrap around other properties, and those that wrap around classes.

  1. Function plugins act as wrappers for the function they plug into, and are called every time the original function’s call is intercepted, instead of that function. The initial function is available in the plugin-wrapper as callback. It is always bound to the context it originated from (instance for members, context for regular functions).

  2. Property plugins modify their instances’ properties during the instantiation time. These plugins are called once for each new instance of a specific class.

  3. Class plugins are meant to modify the classes themselves. It is possible to use them to wrap your component into some HOCs, although such an approach is not recommended. Using these plugins (which wrap around classes) enables extending the original class via inheritance and replacing the original class in the application with the modified one (this replacement happens behind the scenes just as with the other plugin types).

Function plugins

Each plugin which wraps around a function, no matter whether it is a class member or not, is a function of the following form: function plugin(args, callback, instance) { ... } with the following arguments’ meanings.

  • args: an array of arguments that are passed to the function

  • callback: the original function, or the next plugin (which also has a callback which also has either the original function or the next plugin etc.)

  • instance: the instance which is the original call context of the function

Example:

Property plugins

Each plugin that wraps around a property is a function of the following form: function plugin(prop, instance) { ... } with the following arguments’ meanings.

  • prop: the value you are wrapping around. Similar to the callback in the ‘function’ plugins, either has the original property or a value returned from the other plugin(s)

  • instance: an instance this property is a member of

Class plugins

Each plugin that wraps around a class is a function of the following form: function plugin(X) { ... } with the following arguments’ meanings.

  • X: the class you are interacting with

The described below is a not recommended approach. This is risky because it may break some classes’ properties due to the babel-transform-class-properties plugin’s internal mechanics.

The class API is designed to be used as follows. Remember that you SHOULD NEVER copy the original code to your extend’s members, use super , or any other level of abstraction for that.

Plugin configuration

Once you have created your plugin functions, you need to specify which places you want to plug into. In order to do this, each plugin file should have a default export - a plugin configuration object.

In the plugin configuration, the following information can be specified:

  1. Namespace: Every class and function that can be plugged into has a namespace, indicated with the @namespace decorator.

  2. What aspect of the namespace you want to modify…

  3. Name: if you are targetting a class member, you must specify its name.

Modifying classes

  • Use the class plugin if you want to replace the entire class with something else (see an example above). While it is technically possible to replace the class with another class entirely, this is VERY unsafe.

  • Specify member-function plugin if you want to alter the behavior of the class’s method. E.g: plugging into render or componentDidMount.

  • Specify member-property and a property plugin if you want to alter the value of a field of the class. E.g: plugging into state.

  • Specify static-member and a property plugin if you want to modify a class’ static field.

Modifying other functionality

  • Use the function plugin type if you are plugging into a function that has its own namespace and is not a part of a class.

Position (Optional, defaults to 100): Specifies the order in which plugins are applied. Plugins with the lower position will be called before plugins with a higher position.

Plugin configuration object format

Where plugin can be in one of the following four formats:

Example:

Plugging into other plugins’ classes

ScandiPWA allows plugging into plugins’ classes, such as components, queries, etc. The plugin configuration files (.plugin.js) cannot be plugged into. .plugin.js files’ contents can only be modified by overriding them in a theme.

Enabling extensions

  1. Properly place your extension

Any ScandiPWA extension is an M2 module, hence the placement must be the same in order for the M2 backend part to work properly. The only viable place to install the extension without any additional actions is the app/code/<vendor>/<module> directory.

2. Enable the backend part using the standard M2 way: magento se:up, if necessary also magento mo:en <module name> can be used.

3. Reference the extension from your theme’s scandipwa.json. In this file, you can specify the path to the extensions that the theme should use. Without specifying an extension here, all of its plugins will be ignored.

The scandipwa.json has the following format:

Explanation:

  • <name> is an arbitrary name for the plugin

  • <P> is the relative path from Magento root to the extension’s root

Last updated