Using esbuild to Build AMD Modules in a Moodle Plugin
How to replace Grunt with esbuild for compiling AMD modules in a Moodle plugin, covering installation, configuration, and the full build pipeline.
Moodle's standard way to build AMD JavaScript is its grunt toolchain, which pulls in Node, Babel and a fair amount of configuration. If you just want to compile a plugin's amd/src/ files into the amd/build/*.min.js that Moodle serves, esbuild is a much lighter alternative. The key to doing it cleanly is to author named AMD modules so the output loads exactly like a grunt-built module. This guide covers that setup, configuration, and the build workflow.
Using esbuild to Build AMD Modules in a Moodle Plugin
Purpose
To compile JavaScript source files in amd/src/ into the AMD .min.js files in amd/build/ that Moodle loads, using esbuild instead of grunt.
Directory Structure
Your Moodle plugin should contain:
mod/yourplugin/
amd/
src/
editor.js
runtime.js
build/ (created automatically by esbuild)
Each source file must begin with a named AMD define() block. The module name has to be plugintype_pluginname/filename, matching the plugin's component name and the file's path under amd/src/. This is the crucial detail: baking the name in yourself is what lets Moodle load the module directly, without relying on any server-side fix-up (see the note at the end).
// amd/src/editor.js (plugin mod_yourplugin)
define('mod_yourplugin/editor', ['jquery'], function($) {
return {
init: function(cfg) {
console.log('Hello from editor.js', cfg);
}
};
});
Step 1: Initialize NPM (once)
From your plugin root:
npm init -y
This creates a package.json file.
Step 2: Install esbuild
npm install --save-dev esbuild
This installs esbuild locally into your plugin.
Step 3: Create build.js file
Create a file named build.js in the plugin root:
touch build.js
Paste in:
// build.js
const esbuild = require('esbuild');
esbuild.build({
entryPoints: ['amd/src/editor.js', 'amd/src/runtime.js'],
outdir: 'amd/build',
outExtension: { '.js': '.min.js' },
bundle: false, // keep each module separate; do not bundle dependencies
minify: true,
sourcemap: true, // emit .min.js.map, matching what the grunt build ships
target: ['es2015'],
}).then) => {
console.log('JS build complete.');
}).catcherr) => {
console.error('Build failed:', err);
process.exit(1);
});
Step 4: Run the build
From your plugin root:
node build.js
You should see:
JS build complete.
Then verify:
ls amd/build/
You should see:
editor.min.js
editor.min.js.map
runtime.min.js
runtime.min.js.map
Optional: Add NPM build script
Edit package.json to add:
"scripts": {
"build": "node build.js"
}
Now you can build with:
npm run build
TL;DR Summary
npm init -ynpm install --save-dev esbuild- Author each source file as a named module:
define('plugintype_name/file', [...], ...) - Create
build.js(minify + sourcemap, noformatoverride) - Run
node build.jsornpm run build - Moodle loads your AMD modules from
amd/build/*.min.js
Why the module name matters
Moodle's AMD loader expects every module to declare its own name (define('plugintype_name/file', ...)). The grunt toolchain injects that name automatically during its Babel step. esbuild does not, so you add it yourself in the source. If you omit the name and emit an anonymous define([...]), Moodle will still load it, but only because lib/requirejs.php runs a fallback (requirejs_fix_define()) that patches the name in at serve time. That fallback is explicitly marked in core as deprecated and slated for removal, so do not rely on it: name your modules and the output loads on its own merits.
A caveat on the plugins directory
If you submit a plugin to the moodle.org Plugins directory, its automated checks include a grunt build-state step that rebuilds amd/src/ and compares against the committed amd/build/. esbuild output will not byte-match grunt's, so a submitted plugin built this way may be flagged for mismatched build files. For in-house or client plugins this is a non-issue; for directory submissions, run the standard grunt build for the final committed artifacts.
Solin specializes in Moodle plugin development and JavaScript tooling.
Contact us