Basics
Features
Best Practices
Addon Development
Related Docs
Dark Mode

#Packaging and Distribution

Addons are installed from Addon Packages. An Addon Package is an archive that contains your Addon's files, including an addon.json manifest file that tells MercuryCMS how your Addon works.

#Manifest File (addon.json)

{
  "name": "demo",
  "displayName": "Demo",
  "description": "A quick demo addon",
  "author": "Allister Aligerum",
  "version": "1.0.0",
  "dependencies": [
    {"type": "node", "path": "/"}
  ],
  "settingsPath": "/settings",
  "processes": {
    "main": {
      "type": "node",
      "main": "/index.js",
      "ports": 1,
      "interface": true,
      "eventPath": "/api/events"
    }
  },
  "migrations": [
    {"type": "node", "path": "/migrations"}
  ]
}

The name of your Addon must contain only lowercase letters, numbers, and dashes and cannot start or end with a dash. Your addon name must also be unique. Sites cannot have two addons with the same name installed. Your Addon will be installed to a directory of this name, and this name is used in the interface to route requests to your Addon's Screens.

The displayName and description are used in the Addon List.

The version of your Addon uses semver and must be incremented with each update. MercuryCMS will refuse to update your Addon to a lower version than is currently installed.

The dependencies are optional. The only type currently supported is "node" but this will be added to in the future. If you specify a path, MercuryCMS will cd to your Addon's code directory and run npm i to install your dependencies so you don't have to package them yourself. If you want to include your own node_modules in the Addon Package, you can omit the dependencies key.

settingsPath is used to specify the route to navigate to when the user clicks the "Settings" button in the Addons Screen. The default value is /settings and can be omitted if this is where your settings page is. If your Addon does not have any settings, you can omit this value and instead set noSettings to true.

processes is a list of named processes. You can name your processes anything as the names are for your own identification but must not include : or .. Currently, the only supported process type is node which requires a main key. MercuryCMS will cd to your Addon's code directory and run node ${process.main}. This path is relative to your Addon's code directory. ports is a count of how many ports the process would like assigned to it. interface can be set to true if this is the process that serves your Addon's screens.

migrations is an array of Migration directories that will be checked for executable files to be run to migrate old data forward. See Updates and Migrations below. This is optional and only used if your Addon uses Migrations when updating.

eventPath is an optional endpoint provided by your Addon that MercuryCMS will send Events to. This is optional and only used if your Addon uses Events. See Events.

#Packaging

To package your Addon, manually zip your Addon's source code into a file that ends with .addon.zip. For example, my-addon.addon.zip. It's recommended to include your Addon's version in the filename, ex: my-addon-1-0-2.addon.zip. You can automatically create this file by making a simple build script and using cms.packageAddon().

import cms from 'static-cms-addon'

// create ../my-addon-1-0-0.addon.zip
await cms.packageAddon()

// create ../my-addon.addon.zip by passing a custom filename
await cms.packageAddon({outputPath: '../my-addon.addon.zip'})

You can then run this file by running node build.js.

#Installation

You can install Addons from the Addons Screen in MercuryCMS. Click "Install Addon" and select the Addon Package to install it.

You can also upload Addons to the Addon Library in the MercuryCMS Admin. Any Addons installed this way on a CMS instance will automatically update when you update them in the Addon Library.

#Uninstalling

Users can uninstall your Addon from the Addons Screen. This will delete all of your Addon's added Media, Collections, Collection Entries including Entries created by the User in your Addon's Collections, Components, Pages, Layouts, Redirects, Filesystem in your Addon's Storage Path, and Database. Assets and Media Folders will be left behind. You can elect to have some of these items persist after uninstallation by viewing their respective documentation.

#Building

It's very common to create a public directory to store your HTML, CSS, and JavaScript. To make writing in these formats easier, you can use cms.buildDirectory(sourceDir, outputDir, options). This will take a source directory and duplicate it to an output directory. Any .pug, .stylus files it finds will be converted to .html and .css automatically. You can specify {clear: true} in the options argument to delete the output directory before building.

You are also able to write your interface in any languages you want and use reactive libraries like Vue or React.

#Updates and Migrations

To create an update for your Addon, increment the version, package it, and install the new version from the Addons screen. This will replace your Addon's code with the new code while maintaining your local storage, database entries, (Components)[/addon-development/components], Datastores, etc.

Because all Addons are different, it's up to you to determine how to migrate any existing data forward when the new Addon is installed. Mercury provides a simple system for this called Migrations.

For example, consider the situation in which your Addon initializes its settings in the database when it first launches. It checks to see if settings exists and if not, creates it as {importUrl: 'example.com', importApiKey: 'someKey'}. This works well and you have rolled multiple improving versions of your Addon to many different Sites.

Now consider that you add new functionality in version 1.4.0 that adds automatic importing. You now want to set importIntervalMinutes to 60 by default. You have updated the code that checks whether settings exists to automatically create the settings object with that key, but what about all the people who already have the Addon installed?

To handle this (and many other similar problems), you can use Migrations. Create a directory in your Addon's package (typically called /migrations) and put a file in it that will run only once the first time the Site sees it. In this example, the file would look something like this:

// migrations/2022-02-31-add-importIntervalMinutes-to-settings.js
// github issue #123
// add default value for importIntervalMinutes to settings

import cms from 'static-cms-addon'

await cms.database.collection('settings').updateOne({importIntervalMinutes: {$exists: false}}, {$set: {importIntervalMinutes: 60}})

Migrations run in alphabetical order the first time they are seen. This allows a user to install v1.0.0, then skip a few versions before installing v1.4.0 and still run all of the Migrations in between to migrate their data forward to the current format. Note that these Migrations are also run on first install, so it's important not to assume any data is already there. They should also be written in a way to not overwrite any existing data you don't want overwritten.

These Migrations can be used to do things like update DataItems in Datastores add new Assets and Media, add or update files in your local file storage, etc.

#Development Mode

Once you have begun developing an Addon, you will quickly notice it can be tedious to repeatedly install your Addon through the Addons screen. To make this easier, you can put your local version of MercuryCMS in Development Mode.

To enable this mode, press Up, Up, Down, Down, Left, Right, Left, Right, B, A, Enter on any screen. This will make a "Development" tab appear on the Site screen. Here, you will find the Development API Key. When MercuryCMS is in this mode, it will allow requests to be made to MercuryCMS' Development API.

Now we can allow your build script for your Addon to automatically install the Addon to your local MercuryCMS instance every time you build it.

// use axios
let body = {
  file: fs.createReadStream('../my-addon-1-0-0.addon.zip')
}
let options = {
  headers: {
    'Authorization': 'D6TtoyYPII9tWTJ3BMiO5w7ECTeTQ9Po', // use your generated development key
    'Content-Type': 'multipart/form-data'
  }
}
await axios.post('http://localhost:3001/api/development/addon/install', body, options)

// use static-cms-addon
cms.setDevelopmentOptions({developmentCmsUrl: 'http://localhost:3001', developmentApiKey: 'D6TtoyYPII9tWTJ3BMiO5w7ECTeTQ9Po'})
await cms.installAddonPackage({path: '../my-addon-1-0-0.addon.zip'})

You can also use static-cms-addon to install immediately after packaging:

cms.setDevelopmentOptions({developmentCmsUrl: 'http://localhost:3001', developmentApiKey: 'D6TtoyYPII9tWTJ3BMiO5w7ECTeTQ9Po'})
await cms.packageAddon({install: true})

Note that you should not hardcode your development options into your code. These values will be different for each person working on the Addon, and the Development API Key can easily change just by telling MercuryCMS to generate a new one. Because of this, you should store these values in a separate unversioned file. For example:

# .gitignore

/development-options.json
// development-options.json

{
  "developmentCmsUrl": "http://localhost:3001",
  "developmentApiKey": "D6TtoyYPII9tWTJ3BMiO5w7ECTeTQ9Po"
}
// build.js

import cms from 'static-cms-addon'
import fs from 'fs'

let developmentOptions = JSON.parse(fs.readFileSync('development-options.json'))
cms.setDevelopmentOptions(developmentOptions)

cms.buildDirectory('public-src', 'public', {clear: true})
await cms.packageAddon({install: true})

This way, each person that works on the Addon can set this configuration to suit their own development environment without conflicting with anyone else working on the Addon. This also prevents committing sensitive data into your Addon's Git repository.

Consider also providing a development-options.template.json that each person can copy to easily make their own local development-options.json file. This file will be saved into the Git repository, but doesn't contain any sensitive data.

// development-options.template.json

{
  "developmentCmsUrl": "http://localhost:3001",
  "developmentApiKey": "secret"
}

#Development Mode Features

Development Mode also allows you to access Development Mode-exclusive features, like the ability to export Datastores, Components, Pages, and Layouts in a format easily readable. Select "Export For Addon" to create a .zip file that contains the exported item in a format you can read, and that breaks the item up into multiple lines so a versioning system like Git can easily diff changes.