Build System and CI/CD
Purpose and Scope
This document describes Leaflet's build system, continuous integration/continuous deployment (CI/CD) pipeline, testing infrastructure, and distribution mechanisms. It covers the Rollup-based bundler configuration, output formats, GitHub Actions workflows, quality assurance processes, and the release pipeline.
For information about contributing code and following development standards, see Contributing Guidelines. For documentation generation, see Documentation and Examples.
Build Pipeline Overview
Leaflet uses a Rollup-based build system that compiles ES modules from src/ The build process produces multiple output formats to support different deployment scenarios: modern ESM bundles for tree-shaking and legacy UMD bundles for global script inclusion.
Rollup Configuration
The build configuration is defined in build/rollup-config.js and produces four JavaScript bundles with different characteristics:
| Output File | Format | Minified | Sourcemap | Use Case |
|---|---|---|---|---|
leaflet-src.js | ESM | No | Yes | Development with module bundlers |
leaflet.js | ESM | Yes | Yes | Production with module bundlers |
leaflet-global-src.js | UMD | No | Yes | Development with script tags |
leaflet-global.js | UMD | Yes | Yes | Production with script tags |
Build Configuration Structure
The UMD bundles are configured with the global name L and include noConflict support for compatibility with other libraries that might use the same namespace. The AMD module ID is set to "leaflet" for RequireJS compatibility.
Static Asset Handling
The staticAssetsPlugin custom Rollup plugin copies static assets to the output directory during the build:
leaflet.css- Core stylesimages/logo.svg- Leaflet logoimages/layers.svg- Layer control iconimages/marker-icon.svg- Default marker iconimages/marker-shadow.svg- Marker shadow
Package Configuration
The package.json defines the library's entry points and module type:
{
"type": "module",
"exports": {
".": "./dist/leaflet-src.js",
"./styles.css": "./dist/leaflet.css"
}
}The "type": "module" field indicates this package uses ES modules. The exports field provides conditional exports where the main entry point resolves to the unminified ESM build with sourcemaps, optimized for debugging during development.
CI/CD Workflow
The GitHub Actions workflow in .github/workflows/main.yml orchestrates the entire CI/CD pipeline across multiple operating systems and browsers.
Job Details
Setup Job
Runs on three operating systems to ensure cross-platform compatibility:
- Checks out repository
- Sets up Node.js 24 with npm cache
- Runs
npm cifor clean dependency installation - Executes
npm run build - Caches the entire workspace for dependent jobs
Build Documentation Job
Validates that the Jekyll-based documentation builds without errors:
- Sets up Ruby with bundler cache
- Runs
bundle exec jekyll build --strict_front_matter - Fails on any front matter validation errors
Lint Job
Enforces code quality standards:
- Restores cached workspace from setup job
- Executes
npm run lint(ESLint) - Validates all JavaScript, CSS, and Markdown files
Bundlemon Job
Monitors bundle size to prevent performance regressions:
- Only runs for the
Leafletorganization repositories - Enforces 40kB gzip limit for
leaflet.js - Enforces 3.2kB gzip limit for
leaflet.css - Reports results to GitHub checks
Server-Side Rendering Tests
Validates that Leaflet can be imported in non-browser environments:
- Node.js: Executes
node ./spec/ssr/ssr_node.js - Deno: Executes
deno run ./spec/ssr/ssr_deno.js
Cross-Browser Testing
Tests run on multiple browsers and operating systems:
| Browser | OS | Retina Variant | Touch Tests |
|---|---|---|---|
| Chrome | Ubuntu | No | Yes |
| Chrome | Windows | No | Yes |
| Chrome | macOS | No | Yes |
| Firefox | Ubuntu | No | Yes |
| Firefox | Ubuntu | Yes (2x DPR) | Yes |
| Safari | macOS | N/A | Yes |
Each test suite runs twice:
- Standard mode:
npm test -- --browsers {Browser} --single-run - Touch mode:
npm test -- --browsers {Browser} --touch-browser --single-run
Publish Artifacts Job
Publishes development snapshots to GitHub Releases:
- Trigger: Push to
mainbranch only - Permissions: Requires
contents: write - Compresses
dist/folder with changelog and license - Creates/updates the "dev" release tag as prerelease
- Uploads
dist/leaflet.zip
Publish to NPM Job
Publishes tagged releases to the NPM registry:
- Trigger: Push to tags matching
refs/tags/v*pattern - Extracts tag suffix (e.g.,
alpha,beta) from version - Publishes with appropriate NPM tag (
latest,alpha, etc.) - Requires
NPM_TOKENsecret
Quality Assurance Tools
ESLint Configuration
The ESLint configuration enforces code style and best practices across the codebase:
Key Rules Enforced:
- Import statements must include
.jsextensions - Indentation using tabs with specific alignment rules
- Curly braces required for all control structures
- Unused variables disallowed (except caught errors)
- Browser compatibility validation against "widely available" APIs
Pre-Commit Hooks
Husky and lint-staged ensure code quality before commits:
- Husky: Manages git hooks
- lint-staged: Runs ESLint with auto-fix on staged files
- Pattern:
*.(js|mjs|md)files are linted and automatically fixed
Testing Infrastructure
Karma Test Runner
Tests are executed using Karma with the following configuration:
Test Execution Commands:
npm test- Run tests in Karma (watch mode)npm run coverage- Generate coverage report and exit
Bundle Size Monitoring
The .bundlemonrc.json configuration defines strict size limits to prevent performance regressions:
| File | Max Size (gzip) | Purpose |
|---|---|---|
leaflet.js | 40 kB | JavaScript bundle size limit |
leaflet.css | 3.2 kB | Stylesheet size limit |
Bundlemon integrates with GitHub to:
- Create check runs on pull requests
- Block merges if size limits are exceeded
- Track size changes over time
Subresource Integrity (SRI)
The build/integrity.js script generates SHA-256 integrity hashes for CDN distribution:
The integrity hashes are embedded in the documentation to enable secure CDN usage:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@2.0.0/dist/leaflet.css"
integrity="sha256-..." crossorigin="" />Distribution Channels
Leaflet is distributed through three primary channels:
1. NPM Registry
- Package name:
leaflet - Entry point:
dist/leaflet-src.js(ESM with sourcemap) - Installation:
npm install leaflet - Publish trigger: Git tags matching
v*pattern - Tag strategy: Extracts pre-release identifier (alpha, beta) from version
2. jsDelivr CDN
Files are automatically published to the jsDelivr CDN when a new NPM version is released:
- Base URL:
https://cdn.jsdelivr.net/npm/leaflet@{version}/dist/ - Includes SRI hashes for security
- Files available:
leaflet.js- Minified ESMleaflet-src.js- Unminified ESMleaflet-global.js- Minified UMDleaflet-global-src.js- Unminified UMDleaflet.css- Stylesimages/- Icon assets
3. GitHub Releases
Development snapshots are published automatically:
- Release tag:
dev - Marked as: Prerelease
- Trigger: Every push to
mainbranch - Artifact:
dist/leaflet.zipcontaining:- All dist files
- CHANGELOG.md
- LICENSE
- README.md
Development Workflow
NPM Scripts
| Script | Command | Purpose |
|---|---|---|
build | rollup --config build/rollup-config.js | Build all output formats |
watch | npm run build -- --watch | Rebuild on file changes |
lint | eslint . | Check code style |
lintfix | npm run lint -- --fix | Auto-fix linting issues |
test | karma start ./spec/karma.conf.cjs | Run tests in watch mode |
coverage | karma start ... --coverage --single-run | Generate coverage report |
docs | node ./build/docs.js && node ./build/integrity.js | Generate documentation |
debug | http-server -c-1 | Start local development server |
serve | cd docs && bundle exec jekyll serve | Serve documentation site |
bundlemon | bundlemon | Check bundle sizes |
prepare | husky | Install git hooks |
Development Dependencies
The project uses 34 development dependencies organized by function:
Build Tools:
rollup- Module bundler@rollup/plugin-json- Import package.json@rollup/plugin-terser- Minificationterser- JavaScript compressor
Linting:
eslint- Code qualityeslint-config-mourner- Base configurationeslint-plugin-import-x- Import validationeslint-plugin-baseline-js- Browser compatibility@eslint/css- CSS linting@mapbox/eslint-plugin-script-tags- Markdown code blocks
Testing:
karma- Test runnermocha- Test frameworkchai- Assertionssinon- Mocking/spyingkarma-chrome-launcher,karma-firefox-launcher,karma-safarinative-launcher- Browser launcherskarma-coverage- Code coverageprosthetic-hand- Touch event simulationui-event-simulator- UI event simulation
Documentation:
leafdoc- API documentation generatorhttp-server- Local development server
Quality Assurance:
husky- Git hookslint-staged- Pre-commit lintingbundlemon- Bundle size monitoringssri- Subresource integrity generation
Build Output Structure
The dist/ directory structure after a successful build:
dist/
├── leaflet.js # ESM minified
├── leaflet.js.map # ESM minified sourcemap
├── leaflet-src.js # ESM unminified
├── leaflet-src.js.map # ESM unminified sourcemap
├── leaflet-global.js # UMD minified
├── leaflet-global.js.map # UMD minified sourcemap
├── leaflet-global-src.js # UMD unminified
├── leaflet-global-src.js.map # UMD unminified sourcemap
├── leaflet.css # Styles
└── images/
├── logo.svg # Leaflet logo
├── layers.svg # Layer control icon
├── marker-icon.svg # Default marker
└── marker-shadow.svg # Marker shadowAll JavaScript files include sourcemaps for debugging. The banner comment is prepended to all output files with copyright and version information.