Skip to content

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.

SVG
100%

Rollup Configuration

The build configuration is defined in build/rollup-config.js and produces four JavaScript bundles with different characteristics:

Output FileFormatMinifiedSourcemapUse Case
leaflet-src.jsESMNoYesDevelopment with module bundlers
leaflet.jsESMYesYesProduction with module bundlers
leaflet-global-src.jsUMDNoYesDevelopment with script tags
leaflet-global.jsUMDYesYesProduction with script tags

Build Configuration Structure

SVG
100%

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 styles
  • images/logo.svg - Leaflet logo
  • images/layers.svg - Layer control icon
  • images/marker-icon.svg - Default marker icon
  • images/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.

SVG
100%

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 ci for 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 Leaflet organization 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:

BrowserOSRetina VariantTouch Tests
ChromeUbuntuNoYes
ChromeWindowsNoYes
ChromemacOSNoYes
FirefoxUbuntuNoYes
FirefoxUbuntuYes (2x DPR)Yes
SafarimacOSN/AYes

Each test suite runs twice:

  1. Standard mode: npm test -- --browsers {Browser} --single-run
  2. Touch mode: npm test -- --browsers {Browser} --touch-browser --single-run

Publish Artifacts Job

Publishes development snapshots to GitHub Releases:

  • Trigger: Push to main branch 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_TOKEN secret

Quality Assurance Tools

ESLint Configuration

The ESLint configuration enforces code style and best practices across the codebase:

SVG
100%

Key Rules Enforced:

  • Import statements must include .js extensions
  • 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:

SVG
100%

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:

FileMax Size (gzip)Purpose
leaflet.js40 kBJavaScript bundle size limit
leaflet.css3.2 kBStylesheet 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:

SVG
100%

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 ESM
    • leaflet-src.js - Unminified ESM
    • leaflet-global.js - Minified UMD
    • leaflet-global-src.js - Unminified UMD
    • leaflet.css - Styles
    • images/ - Icon assets

3. GitHub Releases

Development snapshots are published automatically:

  • Release tag: dev
  • Marked as: Prerelease
  • Trigger: Every push to main branch
  • Artifact: dist/leaflet.zip containing:
    • All dist files
    • CHANGELOG.md
    • LICENSE
    • README.md

Development Workflow

NPM Scripts

ScriptCommandPurpose
buildrollup --config build/rollup-config.jsBuild all output formats
watchnpm run build -- --watchRebuild on file changes
linteslint .Check code style
lintfixnpm run lint -- --fixAuto-fix linting issues
testkarma start ./spec/karma.conf.cjsRun tests in watch mode
coveragekarma start ... --coverage --single-runGenerate coverage report
docsnode ./build/docs.js && node ./build/integrity.jsGenerate documentation
debughttp-server -c-1Start local development server
servecd docs && bundle exec jekyll serveServe documentation site
bundlemonbundlemonCheck bundle sizes
preparehuskyInstall 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 - Minification
  • terser - JavaScript compressor

Linting:

  • eslint - Code quality
  • eslint-config-mourner - Base configuration
  • eslint-plugin-import-x - Import validation
  • eslint-plugin-baseline-js - Browser compatibility
  • @eslint/css - CSS linting
  • @mapbox/eslint-plugin-script-tags - Markdown code blocks

Testing:

  • karma - Test runner
  • mocha - Test framework
  • chai - Assertions
  • sinon - Mocking/spying
  • karma-chrome-launcher, karma-firefox-launcher, karma-safarinative-launcher - Browser launchers
  • karma-coverage - Code coverage
  • prosthetic-hand - Touch event simulation
  • ui-event-simulator - UI event simulation

Documentation:

  • leafdoc - API documentation generator
  • http-server - Local development server

Quality Assurance:

  • husky - Git hooks
  • lint-staged - Pre-commit linting
  • bundlemon - Bundle size monitoring
  • ssri - 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 shadow

All JavaScript files include sourcemaps for debugging. The banner comment is prepended to all output files with copyright and version information.