Introduction
This tutorial will teach you how to write a modern NodeBB plugin using TypeScript, ES Modules, and Rolldown.
Starting with NodeBB 4.12.0, you can use ES Modules to write plugins.
Rolldown is a Fast Rust bundler that you can use to compile NodeBB plugins written in TypeScript.
Installation
Create a new plugins folder, then run:
pnpm init
pnpm add -D rolldown typescript @types/node
# or use your favorite package manager
Configuration
Create a tsconfig.json file (assuming you're writing this in the lib folder):
{
"compilerOptions": {
"target": "ES2020",
"module": "node16",
"moduleResolution": "node16",
"types": ["node"],
"declaration": true,
"allowImportingTsExtensions": true,
"noEmit": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": false,
"incremental": true,
"composite": true,
"skipLibCheck": true
},
"include": ["lib", "rolldown.config.ts"],
"exclude": ["node_modules", "build"]
}
If you need to write client-side code (assuming you're writing it in the public folder), create a public/tsconfig.json file:
{
"compilerOptions": {
"target": "ES2020",
"module": "umd",
"moduleResolution": "node10",
"declaration": true,
"allowImportingTsExtensions": true,
"noEmit": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": false,
"incremental": true,
"composite": true,
"skipLibCheck": true
},
"include": ["lib"]
}
Create a plugin.json file:
{
"id": "...",
"name": "...",
"description": "...",
"url": "...",
"hooks": [
...
],
"scripts": ["build/public/client.js"] // if you need client-side scripts
}
Create a rolldown.config.ts file:
import { defineConfig } from "rolldown";
export default defineConfig([
{
input: ["lib/index.ts"],
platform: "node",
output: {
dir: "build",
format: "es",
cleanDir: true,
strict: true,
topLevelVar: true,
minify: {
compress: true,
mangle: false
},
sourcemap: true
}
},
// if you need client-side scripts
{
input: ["public/lib/client.ts"],
platform: "browser",
output: {
dir: "build/public",
format: "umd",
cleanDir: true,
strict: true,
entryFileNames: "lib/[name].js",
chunkFileNames: "lib/[name].js",
topLevelVar: true,
minify: {
compress: true,
mangle: false
},
sourcemap: true
}
}
]);
Add the following to package.json:
{
...
"type": "module",
"main": "./build/index.js",
"scripts": {
"dev": "rolldown -c ./rolldown.config.ts --watch",
"build": "rolldown -c ./rolldown.config.ts && tsc --noEmit",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@types/node": "^25.9.3",
"rolldown": "^1.1.1",
"typescript": "^6.0.3"
},
"nbbpm": {
"compatibility": "^4.12.0"
}
...
}
Development
You can now use pnpm build to build plugins and pnpm dev to develop plugins.
You can create a symbolic link from the plugin directory to /path/to/nodebb/node_modules to enable the plugin locally.
Bundle SCSS
Add the following to rolldown.config.ts:
import { defineConfig, RolldownPluginOption } from "rolldown";
import fs from "node:fs";
import path from "node:path";
// Add the SCSS plugin
const rolldownCopyScssPlugin: RolldownPluginOption = {
name: "rolldown-plugin-copy-scss",
buildStart() {
const srcDir = path.resolve("public/scss");
if (!fs.existsSync(srcDir)) return;
const files = fs.readdirSync(srcDir);
for (const file of files) {
if (file.endsWith(".scss")) {
const filePath = path.join(srcDir, file);
const source = fs.readFileSync(filePath, "utf-8");
this.emitFile({
type: "asset",
fileName: `css/${file}`,
source: source
});
}
}
}
};
export default defineConfig([
// In the client build options
{
input: ["public/client.ts"],
platform: "browser",
output: {
dir: "build/public",
format: "umd",
cleanDir: true,
strict: true,
entryFileNames: "lib/[name].js",
chunkFileNames: "lib/[name].js",
topLevelVar: true,
minify: {
compress: true,
mangle: false
},
sourcemap: true
},
plugins: [rolldownCopyScssPlugin] // Load the SCSS plugin
}
]);
Using jQuery
To use jQuery, first install its type definitions:
pnpm add -D @types/jquery
Add the following to public/tsconfig.json:
{
"compilerOptions": {
...
"types": ["jquery"],
...
},
...
}
If you still get an error, add /// <reference types="jquery"/> to the top of your TypeScript file.