Write Your Own Plugin

As we mentioned in the previous section, plugin can access the Relno API and can be used to implement custom features. In this section, we will learn how to write a plugin.

Plugin Structure

A Relno plugin is a function that registers lifecycle hooks and custom sections. The function will be called by Relno when building the generator. Relno provides a function to provide typechecking for the plugin. In the following example, we create a plugin and export it as RelnoExamplePlugin.

TypeScript
import {  defineRelnoPlugin,} from "relno";export const RelnoExamplePlugin = defineRelnoPlugin(async (generator) => {    // register lifecycle hooks and custom sections here    ...});

Register Lifecycle Hooks

Relno provides a set of lifecycle hooks that can be used to customize the behavior of Relno. You can register a lifecycle hook by calling the addHook method of the generator. addHook method takes two parameters: the lifecycle hook and the callback function. The callback function will be called when the lifecycle hook is triggered. The callback function will be called with the generator and the context as parameters. The context contains the information that is relevant to the lifecycle hook. For this example, we register a BeforeGenerate hook. The BeforeGenerate hook is triggered before the generator starts to generate the release note. The context contains the commits that are included in the release note.

TypeScript
export const RelnoExamplePlugin = defineRelnoPlugin(async (generator) => {    generator.addHook(        Lifecycle.BeforeGenerate,        async (generator: Generator, context: BeforeGenerateContext) => {            const commits = context.commits;            // do something with the commits object        },    );});

Register Custom Sections

Relno provides custom section support. You can register a custom section by calling the addSection method of the generator. addSection method needs a section object as parameter. The section object controll the rendering logic of the section. For this example, we register a custom section named mySection and render it simply. The parse method of the section object will be called when the generator starts to parse mySection section. sectionNode parameter is the AST node parsed by parser. The parse method should return a SectionNode object. You should parse the children of the sectionNode and return the result. Relno provides a parseNode method to parse a node recursively. If the node is a section node, it will parse the node recursively. If the node is a text node, it will render the node and the expression syntax will be evaluated. The following example pass a variable named testVariable to the parseNode method.

TypeScript
import { Generator, Section, SectionNode, TemplateNodeType } from "relno";class MySection extends Section {    public constructor() {        super("mySection");    }    public async parse(        generator: Generator,        sectionNode: SectionNode,    ): Promise<SectionNode> {        const result: SectionNode = {            type: TemplateNodeType.Section,            name: sectionNode.name,            tags: sectionNode.tags,            children: [],        };        for (const child of sectionNode.children) {            result.children.push(                await generator.parseNode(generator, child, {                    // pass variables, the variables will be available in the section                    testVariable: "test",                }),            );        }        return result;    }}export const RelnoExamplePlugin = defineRelnoPlugin(async (generator) => {    generator.addSection(new MySection());});