Mappa

The high-performance import map generator for the modern web, available as a CLI tool and Go library.

mappa - import map generator

Mappa (מַפָּה) is Hebrew for “map”.

Modern web applications use ES modules with bare specifiers like import { html } from 'lit'. Browsers need import maps to resolve these specifiers to actual URLs.

Mappa generates import maps from your package.json dependencies, pointing to your local node_modules paths or to a path of your choosing (like /assets/packages/.... It’s designed to be fast, with parallel dependency resolution.

Features

Installation

From Source

go install bennypowers.dev/mappa@latest

Quick Start

Generate an import map for your project:

# Local paths (default)
mappa generate

# Output as HTML script tag
mappa generate --format html

# Custom asset path
mappa generate --template "/assets/packages/{package}/{path}"

CLI Reference

mappa generate

Generate an import map from package.json dependencies.

Flags:
  -f, --format string        Output format: json, html (default "json")
      --include-package      Additional packages to include (repeatable)
      --input-map string     Import map file to merge with generated output
      --template string      URL template (default: /node_modules/{package}/{path})
  -p, --package string       Package directory (default ".")
  -o, --output string        Output file (default: stdout)

Examples:

# Include devDependencies
mappa generate --include-package fuse.js --include-package vitest

# Merge with manual overrides
mappa generate --input-map manual-imports.json

# Custom asset path
mappa generate --template "/assets/packages/{package}/{path}"

mappa trace

Trace HTML files to discover ES module imports and generate minimal import maps containing only the specifiers actually used.

Flags:
  -f, --format string        Output format: json, html, specifiers (default "json")
      --template string      URL template (default: /node_modules/{package}/{path})
      --conditions string    Export condition priority (e.g., production,browser,import,default)
      --glob string          Glob pattern to match HTML files (e.g., "_site/**/*.html")
  -j, --jobs int             Number of parallel workers (default: number of CPUs)
  -p, --package string       Package directory (default ".")
  -o, --output string        Output file (default: stdout)

Examples:

# Trace a single HTML file
mappa trace index.html

# Trace with custom template
mappa trace index.html --template "/assets/packages/{package}/{path}"

# Batch mode with glob pattern (outputs NDJSON)
mappa trace --glob "_site/**/*.html" -j 8

# Output raw traced specifiers for debugging
mappa trace index.html --format specifiers

How it works:

  1. Parses HTML to find <script type="module"> tags
  2. Uses tree-sitter to extract all import statements from JS modules
  3. Follows transitive dependencies through local and node_modules files
  4. Generates an import map with only the bare specifiers actually imported

Static Analysis Limitations:

The trace command uses static analysis to find imports. This means:

URL Templates

Templates use {variable} syntax for dynamic URL generation:

Variable Description Example
{package} Full package name @scope/name or name
{name} Package name without scope name
{scope} Scope without @ prefix scope
{path} File path within package index.js

Examples:

# Default (node_modules)
--template "/node_modules/{package}/{path}"

# Custom assets directory
--template "/assets/packages/{package}/{path}"

# Scoped package handling
--template "/libs/{scope}/{name}/{path}"

Performance

Mappa is written in Go for speed. Benchmarked against @jspm/generator on a real-world project (Red Hat Design System):

Tool Time
mappa 3.2ms ± 0.2ms
@jspm/generator 230ms ± 6ms

mappa is ~72x faster for local import map generation.

Integration Examples

11ty / Eleventy

import { execSync } from 'node:child_process';

export default function(eleventyConfig) {
  const result = execSync('mappa generate --template "/assets/packages/{package}/{path}"', {
    encoding: 'utf-8',
  });

  const importMap = JSON.parse(result);

  // Set up passthrough copies for each package
  for (const [, path] of Object.entries(importMap.imports)) {
    const match = path.match(/^\/assets\/packages\/(@[^/]+\/[^/]+|[^/]+)/);
    if (match) {
      eleventyConfig.addPassthroughCopy({
        [`node_modules/${match[1]}`]: `/assets/packages/${match[1]}`,
      });
    }
  }

  // Inject import map into HTML
  eleventyConfig.addTransform('importmap', (content, outputPath) => {
    if (!outputPath?.endsWith('.html')) return content;

    const script = `<script type="importmap">\n${JSON.stringify(importMap, null, 2)}\n</script>`;
    return content.replace('</head>', `${script}\n</head>`);
  });
}

License

GPLv3