Mosaic + Webpack

Before we start

This tutorial will guide you through the first steps of using Mosaic in project built with Webpack.

If something goes wrong and you are blocked somewhere - feel welcome to check out the result of this tutorial, we uploaded it to GitHub for you 👍🏻

Create and configure a project

First of all, let's create a simple project. We are going to install the following dependencies there.

yarn add -D webpack webpack-cli
yarn add @tilework/mosaic-config-injectors @tilework/mosaic-node-scripts @tilework/mosaic

Then, let's configure Webpack! The configuration below says that the entry point is going to be src/index.js and some other build process details.

const webpack = require('webpack');
const path = require('path');
const ConfigInjectors = require('@tilework/mosaic-config-injectors');

const config = ConfigInjectors.injectWebpackConfig({
    entry: './src/index.js',
    devtool: 'source-map',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    context: __dirname
}, {
    webpack
});

module.exports = config;

Also, let's provide a script for rebuilding and running the project in the package.json. We are going to start the project later on by running these scripts consequently.

{
    "scripts": {
        "build": "webpack",
        "start": "node dist/bundle.js"
    }
}

Implement some logic

We are going to need some source logic to interact with. Let's write it! Note that we need to put a namespace on the getData function in order to make it available to the plugin system.

src/index.js
/** @namespace App/getData */
const getData = () => {
    return ['Souce data'];
};

console.log(getData());

The logic above is going to produce the following output upon being ran.

Create an extension

Local extensions, conventionally, are stored in a packages directory in the project root. So let's create this directory and initialize an npm module within it!

.
├── 📁 packages/
   └── 📁 example-extension/
       └── 📄 package.json
├── 📁 src/
   └── 📄 index.js
└── 📄 package.json

Install and enable the extension

First, we need to install the extension. For the local extensions, we need to add it to the dependencies block manually, as follows. Also, we add the postinstall and postupdate scripts in order for the local package to be symlinked into node_modules directory on any interactions with the dependencies.

package.json
{
    "dependencies": {
        "example-extension": "file:./packages/example-extension"
    },
    "scripts": {
        "postinstall": "node-scripts link",
        "postupdate": "node-scripts link"
    }
}

In order to enable the extension, we should declare this in the package.json file of one of the enabled extensions or themes. For now, the only enabled Mosaic module in the application is our application, hence we are going to declare this in our main package.json file.

package.json
{
    "mosaic": {
        "extensions": {
            "example-extension": true
        }
    }
}

Afterwards, run the dependency installation in your main package in order to trigger the postinstall script and link the extension.

yarn

Declare a plugin

Inside of the extension we created, let's declare a plugin to modify the App component. The name file which the plugin is located in should end with .plugin.js and the file itself should export a configuration object.

For now, let's keep the logic intact, but log a message about the plugin being functional.

packages/sample-extension/src/plugin/App.plugin.js
const plugin = (args, callback) => {
    console.log('The plugin works!');
    
    return callback(...args);
};

export default {
    'App/getData': {
        function: plugin
    }
};

Let's see what our application does when we build and launch it now!

Modify the logic

Now, let's actually do something that could be useful in a real-life situation. Let's provide some additional data with our plugin system! Here, we have a simplistic example, but in some real-life project we can add a route to a router or a context provider to some context providers' list. Anything you want! The algorithm will remain the same.

Let's modify the plugin declaration created above in order to achieve that!

const plugin = (args, callback) => {
    const originalData = callback(...args);
    const additionalData = [
        'Additional data'
    ];

    console.log({ originalData });

    return [
        ...originalData,
        ...additionalData
    ];
};

Enjoy the results

When we launch the project now, we see the following. Great, isn't it?

Last updated