Skip to main content

Publishing a Plugin

Vendure's plugin-based architecture means you'll be writing a lot of plugins. Some of those plugins may be useful to others, and you may want to share them with the community.

We have created Vendure Hub as a central listing for high-quality Vendure plugins.

This guide will walk you through the process of publishing a plugin to npm and submitting it to Vendure Hub.

Project setup

There are a couple of ways you can structure your plugin project:

Repo structure

We recommend that you use a "monorepo" structure to develop your plugins. This means that you have a single repository which contains all your plugins, each in its own subdirectory. This makes it easy to manage dependencies between plugins, and to share common code such as utility functions & dev tooling.

Even if you only have a single plugin at the moment, it's a good idea to set up your project in this way from the start.

To that end, we provide a monorepo plugin starter template which you can use as a starting point for your plugin development.

This starter template includes support for:

  • Development & build scripts already set up
  • Admin UI extensions already configured
  • End-to-end testing infrastructure fully configured
  • Code generation for your schema extensions

Plugin naming

We recommend that you use scoped packages for your plugins, which means they will be named like @<scope>/<plugin-name>. For example, if your company is called acme, and you are publishing a plugin that implements a loyalty points system, you could name it @acme/vendure-plugin-loyalty-points.

Dependencies

Your plugin should not include Vendure packages as dependencies in the package.json file. You may declare them as a peer dependencies, but this is not a must. The same goes for any of the transitive dependencies of Vendure core such as @nestjs/graphql, @nestjs/common, typeorm etc. You can assume that these dependencies will be available in the Vendure project that uses your plugin.

As for version compatibility, you should use the compatibility property in your plugin definition to ensure that the Vendure project is using a compatible version of Vendure.

License

Your plugin must use a license that is compatible with the GPL v3 license that Vendure uses. This means your package.json file should include "license": "GPL-3.0-or-later", and your repository should include a license file (usually named LICENSE.txt) containing the full text of the GPL v3 license.

Publishing to npm

Once your plugin is ready, you can publish it to npm. This is covered in the npm documentation on publishing packages.

Requirements for Vendure Hub

Vendure Hub is a curated list of high-quality plugins. To be accepted into Vendure Hub, we require some additional requirements be satisfied.

Changelog

Your plugin package must include a CHANGELOG.md file which looks like this:

# Changelog

## 1.6.1 (2024-06-07)

- Fix a bug where the `foo` was not correctly bar (Fixes [#123](https://github.com/myorg/my-repo/issues/31))

## 1.6.0 (2024-03-11)

- Add a new feature to the `bar` service
- Update the `baz` service to use the new `qux` method

... etc

The exact format of the entries is up to you - you can e.g. use Keep a Changelog format, grouping by type of change, using tooling to help generate the entries, etc. The important thing is that the CHANGELOG.md file is present and up-to-date, and published as part of your package by specifying it in the files field of your package.json file.

{
"files": [
"dist",
"README.md",
"CHANGELOG.md"
]
}

Vendure Hub will read the contents of your changelog to display the latest changes in your plugin listing.

Documentation

Good documentation is a key criteria for acceptance into Vendure Hub.

README.md

Your plugin package must include a README.md file which contains full instructions on how to install and use your plugin. Here's a template you can use:

# Acme Loyalty Points Plugin

This plugin adds a loyalty points system to your Vendure store.

## Installation

```bash
npm install @acme/vendure-plugin-loyalty-points
```

Add the plugin to your Vendure config:

```ts
// vendure-config.ts
import { LoyaltyPointsPlugin } from '@acme/vendure-plugin-loyalty-points';

export const config = {
//...
plugins: [
LoyaltyPointsPlugin.init({
enablePartialRedemption: true,
}),
],
};
```

[If your plugin includes UI extensions]
If not already installed, install the `@vendure/ui-devkit` package:

```bash
npm install @vendure/ui-devkit
```

Then set up the compilation of the UI extensions for the Admin UI:

```ts
// vendure-config.ts
import { compileUiExtensions } from '@vendure/ui-devkit/compiler';
import { LoyaltyPointsPlugin } from '@acme/vendure-plugin-loyalty-points';

// ...
plugins: [
AdminUiPlugin.init({
route: 'admin',
port: 3002,
app: compileUiExtensions({
outputPath: path.join(__dirname, '../admin-ui'),
extensions: [LoyaltyPointsPlugin.uiExtensions],
devMode: false,
})
}),
],
```
[/If your plugin includes UI extensions]

## Usage

Describe how to use your plugin here. Make sure to cover the key
functionality and any configuration options. Include examples
where possible.

Make sure to document any extensions made to the GraphQL APIs,
as well as how to integrate the plugin with a storefront app.

JS Docs

All publicly-exposed services, entities, strategies, interfaces etc should be documented using JSDoc comments. Not only does this improve the developer experience for your users, but it also allows Vendure Hub to auto-generate documentation pages for your plugin.

Here are some examples of well-documented plugin code (implementation details omitted for brevity):

  • Tag the plugin with @category Plugin. This will be used when generating the docs pages to group plugins together.
  • Usually the .init() method is the thing that users will call to configure the plugin. Document this method with an example of how to use it.
  • The constructor and any lifecycle methods should be tagged with @internal.
/**
* Advanced search and search analytics for Vendure.
*
* @category Plugin
*/
@VendurePlugin({
imports: [PluginCommonModule],
// ...
})
export class LoyaltyPointsPlugin implements OnApplicationBootstrap {
/** @internal */
static options: LoyaltyPointsPluginInitOptions;

/**
* The static `init()` method is called with the options to
* configure the plugin.
*
* @example
* ```ts
* LoyaltyPointsPlugin.init({
* enablePartialRedemption: true
* }),
* ```
*/
static init(options: LoyaltyPointsPluginInitOptions) {
this.options = options;
return AdvancedSearchPlugin;
}

/**
* The static `uiExtensions` property is used to provide the
* necessary UI extensions to the Admin UI
* in order to display the loyalty points admin features.
* This property is used in the `AdminUiPlugin` initialization.
*
* @example
* ```ts
* import { compileUiExtensions } from '@vendure/ui-devkit/compiler';
* import { AdvancedSearchPlugin } from '@acme/vendure-plugin-loyalty-points';
*
* // ...
* plugins: [
* AdminUiPlugin.init({
* route: 'admin',
* port: 3002,
* app: compileUiExtensions({
* outputPath: path.join(__dirname, '../admin-ui'),
* extensions: [LoyaltyPointsPlugin.uiExtensions],
* devMode: false,
* })
* }),
* ],
* ```
*/
static uiExtensions = advancedSearchPluginUi;

/** @internal */
constructor(/* ... */) {}

/** @internal */
async onApplicationBootstrap() {
// Logic to set up event subscribers etc.
}
}

Tests

Testing is an important part of ensuring the quality of your plugin, as well as preventing regressions when you make changes.

For plugins of any complexity, you should aim to have a suite of end-to-end tests as covered in the testing docs.

In future we may use the test results to help determine the quality of a plugin.

Submitting to Vendure Hub

Once your plugin is published to npm and satisfies the requirements above, you can submit it to Vendure Hub via our contact form, making sure to include a link to the npm package and the GitHub repository.

Publishing a paid plugin

Vendure Hub supports the listing of paid plugins. If you would like to sell plugins through Vendure Hub, please contact us and provide details about the plugins you would like to sell.