Build tools
Moodle provides React TypeScript component builds using esbuild integrated with Grunt. This page focuses on the React build toolchain used in Moodle.
What is covered
React build is integrated into Grunt
React is part of the JavaScript build pipeline alongside AMD and YUI. All JS build
orchestration lives in .grunt/tasks/javascript.js.
New Grunt task: react, with three modes:
| Command | Description |
|---|---|
grunt react | Production build (minified, no sourcemaps) |
grunt react:dev | Development build (inline sourcemaps, no minification) |
grunt react:watch | esbuild native watch mode (see below) |
Build orchestration is handled by .esbuild/build.mjs, which:
- Generates TypeScript path aliases.
- Builds all plugin and core React component bundles.
Context-aware grunt from React source directories
Running grunt in js/esm/src now triggers grunt react.
cd public/mod/book/js/esm/src
grunt
Watch mode uses esbuild native context
grunt react:watch uses esbuild's own context.watch() to monitor React source
files. This is intentionally separate from grunt watch — esbuild maintains an
incremental build graph internally, so rebuilds reuse the previous parse result
rather than restarting from scratch on every change.
Before each rebuild, XO lints the sources in check-only mode (no --fix), so a
rewrite never re-triggers esbuild. If XO reports an error, the rebuild is skipped
and the existing bundles are left as they are.
grunt react:watch
grunt watch does not monitor React files.
React linting
React sources are linted with XO,
not ESLint. grunt react lints first and does not build if XO reports an error.
grunt xo— lint onlygrunt xo --fix— apply the fixes XO can make itself
Cross-component alias generation is automatic
Aliases like @moodle/lms/<component>/* are generated from Moodle component metadata.
- Generator:
.esbuild/generate-aliases.mjs - Output:
tsconfig.aliases.json - Source of truth:
.grunt/components.js
Only components with real TypeScript files under js/esm/src are included.
What tsconfig.aliases.json is for
tsconfig.aliases.json is generated during the React build and provides TypeScript
path mappings for the @moodle/lms/* aliases used in React source code. It is read
by tsconfig.json so that editors and type-checking can resolve cross-component
imports correctly.
Do not edit tsconfig.aliases.json manually. It is generated by grunt react.
External packages are excluded from bundles
The following imports are marked as external in the esbuild configuration and are not bundled into component output:
reactreact/*react-domreact-dom/*@moodle/lms@moodle/lms/*@moodlehq/design-system@moodlehq/design-system/*
This ensures shared runtime packages are not duplicated across bundles.
Directory conventions
React source and output follow this structure per component:
<component>/
└── js/
└── esm/
├── src/ # TypeScript/TSX source
└── build/ # Generated ES module output
Commands for developers
# Build all React components (production)
grunt react
# Build all React components (development)
grunt react:dev
# Watch React files with esbuild native watch
grunt react:watch
# Lint React sources with XO, applying the fixes it can make
grunt xo --fix
See also
- Grunt - How to install Grunt and Watchman
- TypeScript coding style - The XO rules and Moodle's overrides