DEV Community

Atsushi
Atsushi

Posted on

Developing GROWI Plug-ins (Remark Plug-ins)

GROWI, an open-source wiki, has a plug-in feature. You can use it to display your own data or customize the display.

In this article, I will explain the procedure for developing a GROWI plugin. I have previously created a plugin that automatically displays YouTube URLs in an embedded format, and this time, I will create a plugin that performs similar operations as a Remark plugin.

image.png

About the code

The code is goofmint/growi-plugin-remark-youtube: GROWI plugin for embed YouTube by Remark. The two files you should see are.

What is Remark?

Remark is a Markdown processor, which GROWI also uses as an engine to convert Markdown to HTML.

remarkjs/remark: markdown processor powered by plugins part of the @unifiedjs collective

Remark allows you to add your own notation within Markdown via plugins. Using this mechanism, you can also add plugins in GROWI.

Adding a plugin

This time, I will create a plugin that runs on the client side (browser). In this case, you will write the plugin's entry point in client-entry.tsx.

You can add a Remark plugin by adding a function (see below) for remarkPlugins.

import { plugin } from './src/youtube';

const activate = (): void => {
  if (growiFacade == null || growiFacade.markdownRenderer == null) {
    return;
  }

  const { optionsGenerators } = growiFacade.markdownRenderer;

  optionsGenerators.customGenerateViewOptions = (...args) => {
    const options = optionsGenerators.generateViewOptions(...args);
    // Add plugin
    options.remarkPlugins.push(plugin as any);
    return options;
  };
};
Enter fullscreen mode Exit fullscreen mode

Contents of plugin

The plugin is written in src/youtube.ts. This file is written to work as a Remark plugin.

import type { Plugin } from 'unified';
import { visit } from 'unist-util-visit';

// Add more properties
interface GrowiNode extends Node {
  name: string;
  type: string;
  attributes: {[key: string]: string}
  children: GrowiNode[];
  value: string;
}

// Define plugin function
export const plugin: Plugin = function() {
  return (tree) => {
    visit(tree, (node) => {
      const n = node as unknown as GrowiNode;
      try {
        if (n.type === 'leafGrowiPluginDirective' && n.name === 'youtube') {
          // 1st arg is YouTube video ID
          const id = Object.keys(n.attributes)[0];
          // optional parameters 
 width and height
          const { width, height } = n.attributes;
          n.type = 'html';
          // Create embed HTML of YouTube
          n.value = `<div style="width: 100%; aspect-ratio: 16/9">
            <iframe
              width="${width || '560'}"
              height="${height || '315'}"
              src="https://www.youtube.com/embed/${id}"
              title="YouTube video player"
              frameborder="0"
              allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
              referrerpolicy="strict-origin-when-cross-origin" allowfullscreen>
            </iframe>
          </div>
          `;
        }
      }
      catch (e) {
        n.type = 'html';
        n.value = `<div style="color: red;">Error: ${(e as Error).message}</div>`;
      }
    });
  };
};
Enter fullscreen mode Exit fullscreen mode

Description

The basic form of the plugin is as follows.

export const plugin: Plugin = function() {
  return (tree) => {
    visit(tree, (node) => {
      // Write your code here
    });
  };
};

Enter fullscreen mode Exit fullscreen mode

Inside node is the AST (Abstract Syntax Tree) in Remark; the AST is the data structure resulting from parsing Markdown. All node information is sent to us, so I process only what I need.

You can then send the HTML you want to render for node.value and it will be displayed on GROWI.

Usage

Here is how to use the plugin. This will display YouTube videos embedded.

$youtube(fGWaKXWrhdY,width=100%,height=100%)
Enter fullscreen mode Exit fullscreen mode

image.png

The width and height are optional. If not specified, the default values are applied.

$youtube(fGWaKXWrhdY)
$youtube(fGWaKXWrhdY,width=100%)
$youtube(fGWaKXWrhdY,height=100%)
Enter fullscreen mode Exit fullscreen mode

image.png

If there are any errors, they will be displayed in red text.

Difference from component-based plugins

Previously, I created a plugin called https://youtube.com/watch?v=fGWaKXWrhdY that automatically embeds and displays URLs. This plugin works by simply pasting the URL into Markdown. In this case, it works by overriding React's a tag behavior.

In the component-based case, it can only be used for tags used in Markdown notation, such as h1-6 tags and code tags. In other words, it cannot be used with div tags, p tags, etc.

In order to be able to use it as a plugin even if it is written as a ground text in the editor, such as $youtube(fGWaKXWrhdY), you need to create a Remark plugin.

In addition, Remark plug-ins are convenient in that they can be passed options. By using options, the display can be more finely customized.

Summary

GROWI's plugin mechanism can be further extended (e.g., to run on the server side). Please develop plug-ins to customize GROWI to your liking.

GROWI, an OSS development wiki tool | comfortable information sharing for all

Top comments (0)