How To Write A Plugin

Use svrx-create-plugin to help you create plugins more easily

First Plugin

Let's create our first plugin —— svrx-plugin-hello-world

File Structure

└── svrx-plugin-hello-world
  ├── client.js
  ├── index.js
  └── package.json

Only package.json and index.js are required

  • package.json

{
  "name" : "svrx-plugin-hello-world",
  "engines": {
    "svrx" : "0.0.x"
  }
}

Only two fields are required by package.json

  • name: package name must be a string beginning with svrx-plugin, to help svrx find it in npm.

  • engines.svrx: Define the runnable svrx version of this plugin,svrx will automatically load the latest matching plugin engines.svrx can be a valid semver,like 0.1.x or ~0.1

- index.js

Where

  1. configSchema Plugin param definition based on JSON Schema ,checkout JSON Schema for more detail. Here we just set a user field, it is a string

  2. assets: client resource setting,they will be automatically injected into the page

    • style: css resource injection

    • script: script resource injection

      All resources will be merged into a single file.

  3. hook.onCreate: a function invoke when plugin been creating, we can control it by injected service in this example, we only use two services

    • logger: logger service

    • config: config service

Check out setion service to find out other services that available in hook.onCreate

- client.js

There is a global variable named svrx will be injected into all pages, it has some built-in services

svrx is only accessible inside the plugin script, don't worry about global pollution

You will see Hello svrx from browser in console panel

Unlike in server side, config in client is passed through websocket, so api is async, and return a promise

svrx also provide other client services, please check out client api for help

publish && running

There'll be a failure when trying to publish it by npm publish. Because svrx-plugin-hello-world has been published by official team

So we skip this step and try the plugin directly

Check the terminal and browser console log, we will find Hello svrx from browser, which means plugin has worked successfully.

You can also run it in programmatical way

Further reading: How to test plugin?

Server Side Service

You can use service in two places

  • hooks.onCreate

  • plugin event

We will explain these 7 services in turn

middleware

Middleware is used for adding koa-style middleware to implement backend logic injection

- middleware.add(name, definition)

Adding koa-style middleware

Usage

Param

  • name [String]: A unique middleware name

    in debug mode, it can be used to track the middleware call process

  • definition.priority [Number]: default is 10. Svrx will assemble the middleware according to the priority from high to low, that is, the request will be passed to the high priority plugin first.

  • definition.onRoute [Function]: A koa-style middleware .If definition is a function, it will automatically become definition.onRoute

- middleware.del(name)

Delete a middleware with the specified name

Usage

Param

  • name: middleware name

injector

injector is used for rewriting the response and inject client resources.

- injector.add(type, resource)

Add a resource for injection , only js and css has been supported.

The rule as follow.

  • The style will be merged into /svrx/svrx-client.css and injected before the closing head tag

  • The script will be merged into /svrx/svrx-client.js and injected before the closing body tag

Usage

The content will be merged into bundle script

Param

  • type: Only support script and style

  • resource.content [String]: resource content

  • resource.filename [String]: the path of resource file, must be a absolute path

Content has a higher priority than filename, so you can take one of them.

- injector.replace(pattern, replacement)

injector.replace will transform response body with some or all matches of a pattern replaced by a replacement.

Usage

The above example replace all svrx with server-x

Param

  • pattern [String|RegExp]

  • replacement [String|Function]

The usage of injector.replace is exactly the same as String.prototype.replace

resource injection is based upon injector.replace

events

Built-in event listener, support async&sorted emitter, , which can call the listen function in turn, and can terminate the event delivery at any time.

- events.on(type, fn)

Usage

Param

  • type [String]: event name

  • fn(payload, ctrl): callback that has two params

    • payload [String]: event data that pass through emit

    • ctrl [Object]: control object, call ctrl.stop() to stop 'sorted emit'

If the callback returns a Promise (such as async function), it will be treated as an asynchronous watcher.

- events.emit(type, param, sorted)

Usage

Param

  • type [String]: event name

  • payload: event data

  • sorted [Boolean]: default is false, whether to pass events serially

Return

Promise

- events.off(name, handler)

Remove event watcher

Usage

builtin events

  • plugin: triggered after plugin building.

  • file:change: triggered when any file changes

  • ready: triggered when server starts, if you need to handle logic after server startup (such as getting the server port), you can register this event

config

Config service used to modify or query the options passed by user;

- config.get(path)

Get the config of this plugin.

Config is based on immutable data , you must always use config.get to ensure getting the latest config.

Usage

if you need to get global config,just add prefix $.

Param

  • field: field path,deep getter must be separated by ., such as user.name

Return

The value of the field

- config.set(field, value)

Modify the config

Usage

Param

  • field: field path,deep setter must be separated by ., such as user.name

  • value: field value

- config.watch(field, handler)

Listening for configuration changes, change digest will be triggered after the set, del, splice method calls

Param

  • field: field path,deep watcher must be separated by ., such as user.name

  • handler(evt): watch handler

    • evt.affect(field) [Function]: detect whether specific field has been changed

- config.del(field)

Remove some field

Usage

- config.splice(field, start[, delCount[, items...])

The Array.prototype.slice

Except for field,params are identical to Array.prototype.splice

Example

router

Extending Routing DSL

- router.route(register)

Register route,as same as Routing DSL

Usage

Param

  • register({...methods}):

- router.action(name, builder)

Register an action , like proxy or json

Usage

Param

  • name [String]: action name

  • builder(payload)

    • payload: payload that passed to action,like 'svrx' in above example

    • Return: builder must return a koa style middleware

- router.load(filename)

Load routing file manually ,Same as options --route

Also support hot reloading

Usage

Param

  • filename: absolute path for routing file

Return

Promise

logger

Logger module, who's level can be controlled by logger.level (default is warn)

Or cli way

Above example will output log that more than warn, such as notify, error

logger[level](msg)

Svrx provides multiple levels of logging: silent, notify, error, warn (default), info, debug

Usage

logger.log is an alias for logger.notify

io

io is used for the communication between server and client. Please check it out in client-side io

- io.on( type, handler )

Listening for client messages (send by client side io.emit)

Param

  • type: message type

  • handler(payload): handler for message

    • payload: message data

- io.emit(type, payload)

Send message to client

Usage

Server side

Client side

Param

  • type: message type

  • payload: message data

Message payload must be serializable because they are transmitted over the network

- io.off(type[, handler])

Remove the message watcher

Usage

- io.register(name, handler)

Register io service, which can be invoked by io.call in client and server.

Usage

Param

  • name [String]: service name used by io.call

  • handler: a Function return Promise, implement service logic

- io.call(name, payload)

Invoke registered service

Usage

Param

  • name [String]: service name

  • payload [Any]: service payload will passed to service handler

Return

Promise

Client API

The client APIs are uniformly exposed through global variable svrx

io

Responsible for communicating with the server

- io.on(type, handler)

Listening server-side message

Usage

Server side

Client side

Note that the io.emit() in server-side is a broadcast and all pages will receive a message for server.

Param

  • type: message type

  • handler(payload): message handler

    • payload: message payload passed by io.emit

- io.emit(type, payload)

Send client message to server side

Usage

Client side

Server side

Param

  • type: message type

  • payload: message data passed to message handler

payload must be serializable because it is transmitted over the network.

- io.off(type[, handler])

Remove io watcher

Usage

- io.call(name, payload)

io.call on the client side is exactly the same as the server, but make sure the payload is serializable** because it will be transmitted over the network

Usage

Param

  • name [String]: service name

  • payload [Any]: service payload

Return

Promise

events

This part is exactly the same as server events, no retelling

Usage

The difference between events and io is that io is used for the communication between the server and the client, but events is a single-ended communication.

config

config in client is almost identical to the server, the only difference is: the client version is asynchronous (because of network communication)

- config.get(field)

Usage

if you need to get global config,just add prefix $.

- config.setconfig.spliceconfig.del

The above three methods are the same as get, which is consistent with the server, but the return value becomes Promise.

Usage

config schema

Svrx config schema is based on JSON Schema

Usage

Field Details

  • type [String]: JSON-Schema field type ,can be an array,string,number,boolean,object or null

  • default [Any]: default value

  • required [Boolean]: whether it is required, default is false

  • properties [Object]: child fields

  • ui: svrx extension ,whether show the config in svrx-ui

  • anyOf: Choose one of the items, such as

    For further understanding, please refer to Official Documentation

How to test plugin?

It is not recommended that you publish the test plugin to npm. You can do local testing in the following ways.

After specifying the path parameter, svrx loads the local package instead of installing it from npm

More easier plugin development

Use Official svrx-create-plugin to help you create plugins more easily

Last updated

Was this helpful?