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.
After each rebuild, ESLint runs on the changed source files in check-only mode
(no --fix) to avoid re-triggering esbuild with auto-fixed rewrites.
grunt react:watch
grunt watch does not monitor React files.
React linting
grunt eslint:react— runs ESLint withfix: true(auto-fix)
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
# React lint with auto-fix
grunt eslint:react
See also
- Grunt - How to install Grunt and Watchman