npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

esmesm

v0.0.4

Published

Additive browser import-map bootstrapper with pluggable CDN providers and shared dependency policy.

Readme

esmesm

Additive browser import-map bootstrapping for CDN-hosted ESM packages.

esmesm lets a plain HTML page declare package mappings without hand-writing CDN URLs. Existing import-map entries are always authoritative: esmesm only fills missing mappings.

Quick start

esm.sh is the default provider, so no provider option is required:

<script
  src="https://raw.esm.sh/[email protected]/bootstrap.js"
  map="
    [email protected] [shared];
    @solidjs/[email protected];
    [email protected]
  ">
</script>

<script type="module">
  import { createSolidRuntime } from "solid-tag-runtime/solid";
  import { registerHTML } from "solid-tag-runtime/html";

  const runtime = await createSolidRuntime();
  registerHTML(runtime);
</script>

The bootstrap file itself may be hosted by raw.esm.sh, jsDelivr, unpkg, or your own server. Its hosting location is separate from the provider used to generate package mappings.

Install

npm install esmesm

The package has no runtime dependencies.

What it does

esmesm has a deliberately small job:

  1. parse package specs such as [email protected],
  2. ask a provider to turn them into browser-loadable URLs,
  3. generate package-prefix mappings when the provider can safely support clean bare subpath imports,
  4. inspect import maps already present on the page,
  5. inject only missing mappings,
  6. optionally expose the same workflow through window.esmesm.

It is not a package manager, bundler, module evaluator, npm registry crawler, or wildcard bare-import resolver.

Declarative bootstrap

<script
  src="https://cdn.jsdelivr.net/npm/[email protected]/bootstrap.js"
  map="[email protected] [shared]; [email protected]">
</script>

provider defaults to esm.sh. All v0.0.3 data-* attributes remain supported.

In v0.0.4 you may use shorter HTML attributes. Each data-* form has priority over its shorthand if both are present (even when empty):

| Short form (new) | Backward-compatible form | | --- | --- | | map | data-map | | shared | data-shared | | provider | data-provider | | origin | data-origin | | subpaths | data-subpaths |

Attributes never merge across the two spellings. For example, data-map="" disables a simultaneous map="pkg@1" declaration. subpaths="false" disables prefix generation. These are esmesm configuration attributes, not browser-native features.

Place the bootstrap before normal module scripts that depend on the generated mappings:

<script
  src="https://raw.esm.sh/[email protected]/bootstrap.js"
  data-map="[email protected]">
</script>

<script type="module">
  import { nanoid } from "nanoid";
  console.log(nanoid());
</script>

map / data-map

Entries are separated by semicolons or newlines:

data-map="
  [email protected];
  [email protected];
  @scope/[email protected]
"

Versionless packages are accepted:

data-map="nanoid; lodash-es"

Pin versions for reproducible production deployments. A bare package (for example nanoid) asks the provider to resolve its default version, not a version selected or cached by esmesm.

Inline [shared] (v0.0.4)

Use [shared] after a mapping to declare the import specifier as a global shared dependency, with no separate attribute required:

<script
  src="https://raw.esm.sh/[email protected]/bootstrap.js"
  map="solid-js@2 [shared]; @solidjs/html@2; my-library@1">
</script>

This marks solid-js as shared when producing all URLs in the batch; with esm.sh, each package URL receives external=solid-js. It is not a directive to externalize the dependencies of solid-js alone. The modifier requires whitespace before [shared], is case-sensitive, and unrecognized modifiers such as [other] throw.

This works with aliases, scoped packages, explicit package subpaths, and URL aliases:

map="react=preact@10/compat [shared]; @scope/pkg@2 [shared]; local=/vendor/lib.js"

For aliases the left-hand specifier is shared (react, not preact). This syntax also works in JavaScript esmesm.add(), esmesm.set(), parseMapEntry(), createImportMap(), and ensureImportMap().

Aliases

Use specifier=target:

data-map="[email protected]/compat"

Direct URLs require an alias:

data-map="my-lib=https://example.com/my-lib.js"

Existing import maps always win

Given:

<script type="importmap">
{
  "imports": {
    "react": "/vendor/react.js"
  }
}
</script>

then:

<script
  src="https://raw.esm.sh/[email protected]/bootstrap.js"
  data-map="[email protected]; [email protected]">
</script>

keeps react -> /vendor/react.js unchanged. esmesm adds only the missing react-dom mapping. If the page already owns a package root, esmesm also avoids synthesizing a provider-specific prefix mapping for that package.

Providers

esm.sh — default

<script
  src="https://raw.esm.sh/[email protected]/bootstrap.js"
  data-map="[email protected]">
</script>

Equivalent explicit configuration:

data-provider="esm.sh"

esm.sh supports the full shared model used by esmesm.

jsDelivr

<script
  src="https://cdn.jsdelivr.net/npm/[email protected]/bootstrap.js"
  data-provider="jsdelivr"
  data-map="[email protected]">
</script>

This generates a +esm URL such as:

https://cdn.jsdelivr.net/npm/[email protected]/+esm

Provider support is intentionally capability-aware:

| Capability | esm.sh | jsDelivr +esm | | --- | --- | --- | | Package-root ESM mapping | yes | yes | | Explicit package subpath | yes | yes | | Automatic package-prefix mapping | yes | no | | shared dependency preservation | yes | no | | shared="*" | yes | no | | Custom compatible origin | yes | yes |

jsDelivr does not expose an equivalent of esm.sh's external control, so esmesm throws if shared is requested with the jsDelivr provider rather than silently weakening the requested semantics.

Because jsDelivr's +esm marker belongs after the final package/subpath, esmesm does not generate an automatic pkg/ prefix mapping for jsDelivr. Explicit subpath specs still work:

data-provider="jsdelivr"
data-map="[email protected]/sub/path"

Custom provider origin

<script
  src="https://raw.esm.sh/[email protected]/bootstrap.js"
  data-origin="https://esm.example.com"
  data-map="[email protected]">
</script>

data-origin customizes the active built-in provider's base URL.

Shared dependencies

shared (or the legacy data-shared) describes dependencies whose identity should remain browser/import-map resolved instead of being privately resolved inside another CDN-transformed package:

<script
  src="https://raw.esm.sh/[email protected]/bootstrap.js"
  data-map="[email protected]; [email protected]; [email protected]"
  data-shared="react; react-dom">
</script>

With esm.sh, this becomes ?external=react,react-dom on package URLs.

shared="*" uses esm.sh's all-dependencies-external form. shared="none" contributes no global names but does not cancel inline [shared] entries. Global and inline names combine as a deduplicated union; * takes priority in the esm.sh provider. If a shared dependency is absent from map, it is not automatically mapped in the browser.

After commit(), successfully committed inline shared declarations remain active for later commits. set(), remove(), and clear() of pending entries update the effective sharing policy; unshare("react") removes a committed inline shared name and explicit global name for subsequent commits. Pending entries still marked [shared] contribute until replaced or removed. configure({ shared }) replaces only explicitly configured global names, not committed/pending inline declarations. Already committed browser URLs cannot be rewritten.

For versioned package-prefix mappings, esm.sh supports a trailing-slash query form, which esmesm emits automatically:

exact:  https://esm.sh/[email protected]?external=react
prefix: https://esm.sh/[email protected]&external=react/

If selective sharing is requested for a versionless package, esmesm conservatively skips the automatic prefix mapping. Pin the package version or map the required subpath explicitly.

Package subpaths and prefix mappings

Package subpaths are first-class in esmesm.

With the default esm.sh provider, mapping a package root automatically emits both the exact package mapping and a trailing-slash package-prefix mapping:

data-map="[email protected]"

produces the equivalent of:

{
  "imports": {
    "solid-tag-runtime": "https://esm.sh/[email protected]",
    "solid-tag-runtime/": "https://esm.sh/[email protected]/"
  }
}

That keeps application imports clean while allowing package subpaths:

import { createRuntime } from "solid-tag-runtime";
import { registerHTML } from "solid-tag-runtime/html";
import { createSolidRuntime } from "solid-tag-runtime/solid";

You do not need to enumerate every package export in data-map when the active provider supports native package-prefix mappings.

The version is a separate concern from subpath support. Versionless specs also get a root + prefix mapping when the provider can safely generate one:

data-map="solid-tag-runtime"

Pin a version when you want reproducibility/version coherence. When pinned, esmesm keeps the exact and prefix mappings on the same package release.

Explicit package subpaths are also supported:

data-map="[email protected]/sub/path"

An explicit subpath produces that exact mapping; it does not imply a package-wide prefix mapping.

Package-root aliases can also receive a prefix mapping:

data-map="[email protected]"

which allows both runtime and runtime/... imports. Aliases whose target is already an explicit subpath remain exact-only because extending them would change their meaning.

Disable automatic package-prefix generation with:

data-subpaths="false"

or programmatically:

esmesm.configure({ subpaths: false });

If the page already owns the package root, esmesm does not synthesize a provider-owned prefix for that package. This avoids mixing providers or versions behind one package name.

Global browser API

Loading bootstrap.js always exposes:

window.esmesm

Even if no data-map is supplied.

This allows an application to assemble a mapping batch before normal module scripts run:

<script src="https://raw.esm.sh/[email protected]/bootstrap.js"></script>

<script>
  esmesm.add("[email protected]");
  esmesm.add("[email protected]");
  esmesm.share("solid-js");
  esmesm.commit();
</script>

<script type="module">
  import { createSolidRuntime } from "solid-tag-runtime/solid";
</script>

Pending mappings

Before commit(), pending entries may be edited or removed:

esmesm.add("[email protected]");
esmesm.set("[email protected]");

esmesm.add("[email protected]");
esmesm.remove("lodash-es");

esmesm.commit();

add() / map() use additive ensure semantics. If the page already owns a specifier, they leave it alone.

set() is for editing pending state and throws if the specifier is already owned by the page or committed.

remove() removes pending state only. It throws for an already committed/existing mapping because native import maps cannot be undone.

Multiple commits

Later commits may add new specifiers:

esmesm.add("[email protected]");
esmesm.commit();

esmesm.add("[email protected]");
esmesm.commit();

A committed mapping cannot be updated or removed. Also, the browser may reject/drop a mapping added after that specifier has already been resolved, so commit mappings before dependent modules execute.

Select a provider

esm.sh is the default:

esmesm.provider; // "esm.sh"

Switch future pending commits to jsDelivr:

esmesm.configure({
  provider: "jsdelivr"
});

Or configure a custom origin:

esmesm.configure({
  provider: "esm.sh",
  origin: "https://esm.example.com"
});

Global API summary

esmesm.add(spec)
esmesm.add(specifier, target)
esmesm.map(spec)
esmesm.map(specifier, target)

esmesm.set(spec)
esmesm.set(specifier, target)
esmesm.remove(specifier)
esmesm.clear()

esmesm.share(specifierOrList)
esmesm.unshare(specifierOrList)

esmesm.configure(options)
esmesm.registerProvider(name, provider)
esmesm.commit()

esmesm.has(specifier)
esmesm.get(specifier)
esmesm.pending()
esmesm.committed()
esmesm.providers()

Programmatic ESM API

import {
  collectImportMaps,
  createEsmesm,
  createImportMap,
  ensureImportMap,
  installImportMap,
  parseMapList,
  esmSh,
  jsDelivr
} from "esmesm";

High-level controller

createEsmesm() provides the same pending/commit model as the browser global and defaults to esm.sh:

import { createEsmesm } from "esmesm";

const imports = createEsmesm();

imports.add("[email protected]");
imports.add("[email protected]");
imports.share("react");

imports.commit();

Select jsDelivr explicitly:

const imports = createEsmesm({
  provider: "jsdelivr"
});

Low-level map generation

import { createImportMap, esmSh } from "esmesm";

const map = createImportMap({
  provider: esmSh(),
  map: "[email protected]; [email protected]",
  shared: ["react"],
  existing: {
    imports: {
      react: "/vendor/react.js"
    }
  }
});

Provider subpaths

Providers are also exported individually:

import { esmSh } from "esmesm/providers/esm-sh";
import { jsDelivr } from "esmesm/providers/jsdelivr";

Custom provider

const provider = {
  name: "my-cdn",
  capabilities: {
    shared: true,
    packageSubpaths: true,
    packagePrefixMappings: true
  },

  resolve(target, context) {
    if (target.kind === "url") return target.url;
    return `https://cdn.example.com/${target.name}@${target.version}`;
  },

  resolvePrefix(target) {
    if (target.kind !== "package" || target.subpath) return;
    return `https://cdn.example.com/${target.name}@${target.version}/`;
  }
};

prefixMappings remains accepted as a deprecated compatibility alias for older custom providers; new providers should use packagePrefixMappings.

Register it in a high-level controller:

const imports = createEsmesm();
imports.registerProvider("my-cdn", provider);
imports.configure({ provider: "my-cdn" });

Security and operational notes

  • CDN modules execute with the privileges of the page. esmesm is not a sandbox.
  • Pin package versions for production/reproducible deployments.
  • Content Security Policy must allow the bootstrap source and generated module URLs.
  • esmesm never queries the npm registry and does not validate that a requested package/version exists.
  • Existing import-map entries are never replaced.
  • Commit mappings before dependent module specifiers are resolved.
  • Invalid existing import maps are left untouched; the browser remains responsible for reporting them.

Development

npm test
npm run build
npm run check

npm run check builds the classic bootstrap, runs the tests, and performs npm pack --dry-run.

Publishing

Version 0.0.1 was the initial release. 0.0.3 formalized provider subpath capabilities. 0.0.4 adds HTML attribute aliases, inline [shared] declarations, and unified shared-policy lifecycle semantics. See docs/CHANGELOG.md.

npm login
npm publish

License

MIT