Sourcemap Explorer
Guide

How to see every JavaScript library a website uses

Wappalyzer, BuiltWith and WhatRuns will identify the big frameworks: React, Vue, lodash, jQuery. They'll miss every internal utility and every niche npm package. The complete list only exists inside the sourcemap, where the bundler left a record of every `node_modules/<pkg>/` path it touched during the build.

By Mapree ·

5 min readChrome DevTools, jq, Sourcemap Explorer extension

Background

A typical modern React application bundles 80-200 npm packages. The detector-visible subset — the ones with explicit fingerprint rules in Wappalyzer's database or the equivalent — is usually the 15-30 most popular: React, Next.js, jQuery, lodash, Chart.js, the big analytics SDKs, the obvious payment libraries. That leaves 50-170+ packages per site invisible to categorical detectors. Some of them are transitive (a utility your framework depends on, hoisted into the bundle automatically), some are deliberate choices (a form library, a date picker, an animation engine, an analytics SDK that no fingerprint database has caught up with), some are niche and make the site interesting (an animation library no one else uses, a specific charting package, a proprietary npm package the team published themselves).

Reading the sourcemap gives you the full set because every bundled package leaves `node_modules/<pkg>/<file>.js` paths in the `sources[]` array of every chunk's `.map` file. Collapsing those entries to unique package names is straightforward — for unscoped packages it is the segment immediately after `node_modules/`; for scoped packages (`@scope/name`) it is the scope plus the first subfolder. Extracting exact versions from embedded `package.json` files is one JSON.parse away. Validating that each name corresponds to a real npm package (rather than a private monorepo internal) is a single anonymous HEAD request to `registry.npmjs.org/<package>`. Done together, those three operations produce a complete, version-annotated, npm-validated library inventory of any modern web app — something no fingerprint-based detector can produce because the underlying detection technique is fundamentally different.

The approach scales remarkably well. A page that loads twenty separate JavaScript chunks (a typical Next.js App Router app) gives you twenty maps to walk; the union of `node_modules/<pkg>/` paths across them is the page's library footprint. Sourcemap Explorer parallelises the walk across the offscreen worker pool and reports the result in the popup's Stack tab in a few seconds, even on large bundles. The same data also enables every adjacent question — 'is this package on a CVE-vulnerable version', 'has this team migrated to React 19', 'does this site use TanStack Query or SWR', 'is this an internal design system or a public component library' — because the version detail and the precise package surface are both available in one read.

Why this matters

The 'invisible libraries' layer is often where the interesting stack decisions live. The framework and popular libraries are plausibly just defaults; the specific form library, the specific animation package, the specific date-handling choice reveals taste and architecture. For hiring research it tells you which kind of developer the team's stack would feel familiar to. For competitive analysis it tells you which build-vs-buy decisions the company has made and which third-party services they have integrated. For security auditing it expands the threat surface from 'the framework's known CVEs' to 'every dependency in the bundle, with versions'. For learning it gives you a fully annotated example of how a real production application is composed.

For partnerships and integrations the full library list is the difference between a generic outreach and a relevant one. Knowing a prospect uses TanStack Query 5 specifically tells you which integration shape will land; knowing they pair it with Zod for runtime validation tells you what their API contract probably looks like; knowing they use shadcn/ui plus Radix Themes plus a custom design system tells you what their component aesthetic is. None of that is visible in the categorical 'they use React' answer that public detectors return.

For performance work the bundled library list is the entry point. The first question is always 'what is in the bundle that should not be', and you cannot answer it without seeing every package the build pulled in. Heavy date-handling libraries (moment, date-fns large bundle), animation libraries (Framer Motion, GSAP) and analytics SDKs (Segment, Mixpanel, Amplitude) are usually the easy wins, and they only become visible when you have the full inventory.

Prerequisites

  • Sourcemaps available on the target site. Most public Next.js, Remix, Nuxt and Astro sites ship them by default; many enterprise apps do too because their build pipeline never disabled the option.
  • Chrome DevTools or `curl` for fetching the `.js` and `.map` files. DevTools is faster for one-off inspection; curl + jq is faster when you want to script across many chunks.
  • `jq` (or your favourite JSON tool) for filtering `sources[]` and parsing `sourcesContent[]` entries.
  • Optional: Sourcemap Explorer installed, which collapses every step into a single popup click and runs the package scan across every bundle on the page in parallel.

Step-by-step

  1. 1

    Find a sourcemap

    Open DevTools, switch to the Network tab, filter by 'JS', and reload. Click any of the application's own script requests (skip third-party CDN URLs — those almost never expose maps). Look for `SourceMap:` or `X-SourceMap:` in the response headers; if either is present, the URL it points at is the sourcemap. If neither is present, fetch the last few kilobytes of the script and look for a `//# sourceMappingURL=` comment — many builds put the reference there instead of in a header. Repeat across the chunks the page loads; a typical app has 5-30 of them.

    curl -sI https://example.com/_next/static/chunks/main.js | grep -i sourcemap
    # or, looking for the comment at the end of the script
    curl -s https://example.com/_next/static/chunks/main.js | tail -c 4096 | grep -o '//# sourceMappingURL=.*'
  2. 2

    List node_modules paths

    Download each map, parse the JSON, filter `sources[]` for anything matching `node_modules/<pkg>/`. Collapse to unique package names — for unscoped packages take the segment after `node_modules/`; for scoped packages (`@scope/name`) the scope plus the first subfolder. The result is your raw package list for that chunk. Run the same query across every chunk and union the results; that is the page's library footprint.

    jq -r '.sources[] | capture("node_modules/(?<p>(@[^/]+/)?[^/]+)") | .p' bundle.js.map | sort -u

    Tip: If the same package shows up across many chunks, that is normal — a shared library like React or lodash typically appears in every chunk that imports it. The dedup happens when you union the results across chunks.

  3. 3

    Extract versions where possible

    For each unique package, check if `node_modules/<pkg>/package.json` is in `sources[]`. If yes, the matching `sourcesContent[]` entry has the exact version, dependency tree and engines requirement. Most packages get included this way (the bundler's resolver touches `package.json` during module resolution); the exceptions are usually internal monorepo packages or builds that explicitly stripped `package.json` for byte savings.

    jq -r '. as $m | $m.sources | to_entries[] | select(.value | endswith("/package.json") and contains("node_modules/")) | $m.sourcesContent[.key] | fromjson | "\(.name)@\(.version)"' main.js.map
  4. 4

    Validate package names against the npm registry

    Some entries in `sources[]` will be private internal packages (`@company/utils`, `@workspace/ui`) that look identical to public packages but do not exist on npm. To filter them out, run an anonymous HEAD request against `https://registry.npmjs.org/<package>` for each name; a 404 means the name is private. Cache the lookups (the registry rarely changes for a given name) to avoid repeating the work.

    for pkg in $(cat package-list.txt); do
      status=$(curl -s -o /dev/null -w '%{http_code}' "https://registry.npmjs.org/$pkg")
      echo "$status $pkg"
    done | grep -v '^200'  # everything that didn't return 200 is private
  5. 5

    Filter out the transitive noise

    A bundle includes everything that was imported, transitively. That means you will see packages like `goober` (bundled by `react-hot-toast`), `entities` (bundled by `htmlparser2`), `aria-hidden` (bundled by `@radix-ui/react-dialog`) — all real, all in the bundle, but none of them deliberate choices the developer made. For most analyses you want the top-level direct dependencies. A curated skip list of common transitive-only packages does the filtering reliably; Sourcemap Explorer ships with one that has been calibrated against thousands of production sites.

  6. 6

    Let Sourcemap Explorer do all of it

    The extension's Stack tab runs this entire pipeline across every bundle on the page, deduplicates packages across chunks, extracts versions from every embedded `package.json`, validates names against the npm registry, and reports both known libraries (matched to Wappalyzer-style fingerprint rules) and ad-hoc packages (everything else, surfaced as 'libraries detected from sourcemap'). Click a row and you can see the originating chunk plus the source path that produced the detection. The whole thing takes a few seconds on a page that would have taken twenty minutes by hand.

Real-world example

Alternative methods

Bundle analysis tools

`source-map-explorer`, `webpack-bundle-analyzer`, `esbuild --analyze`, `bundlephobia` — all give you package composition of a specific bundle. Useful when you have the `.map` on disk and want a visual treemap of bytes-per-package; slower than the extension for casual use because they require a per-bundle setup pass and do not natively span multiple chunks.

Statistical inference from runtime

For sites without sourcemaps you're guessing — and usually only catching the top-N libraries with runtime signatures (window globals, console messages, recognisable error strings). The categorical detectors (Wappalyzer, BuiltWith, WhatRuns) all work this way and converge on the same 15-30-package answer. No complete list is possible without the map.

Disassemble the production bundle

Tools like `webcrack` and esbuild's bundle-decompilation modes can reverse-engineer some structure out of a minified bundle even without sourcemaps. The output is messy and incomplete compared with what you get from a real sourcemap, but it can sometimes surface library names from internal markers and string constants the minifier did not strip.

Troubleshooting

`sources[]` has hundreds of entries that all look like the same package.

Monorepo workspace — one package's source is split across many sub-paths. Collapse by the package-name prefix (the segment after `node_modules/`), not the full path. Sourcemap Explorer does this automatically.

A package appears but has no version.

`package.json` wasn't included in the map (some bundlers strip it as an optimization). The package is still confirmed-present; just no version available from the sourcemap layer. Path-based detection is still authoritative for presence.

I see packages that look private but the team has not published them on npm.

Almost certainly internal monorepo packages. The `package.json` will have `"private": true` or an `@company/` scope. The npm-registry HEAD request returns 404. Filter them out unless you specifically want to surface internal dependencies as part of the audit.

Two chunks show different versions of the same package.

Real duplicate. The build pulled `react@18.2.0` somewhere and `react@18.3.1` somewhere else, probably because of a peer-dep mismatch in the dependency tree. This is worth flagging in any audit — duplicate React in the bundle is both a performance and a correctness problem.

The map file is enormous (tens of MB).

Production maps for large apps can hit hundreds of MB. `jq --stream` is dramatically faster than the standard `jq` for filtering a single field; alternatively, Sourcemap Explorer streams the file in chunks rather than slurping it whole, which is why it can handle gigabyte-sized site dumps without choking.

Caveats

What to do next

With the full library list, common follow-ups are exact-version extraction per library via [find-the-exact-version-of-an-npm-package-on-a-site](/how-to/find-the-exact-version-of-an-npm-package-on-a-site), tree reconstruction for reading the author's code via [reconstruct-source-code-from-a-sourcemap](/how-to/reconstruct-source-code-from-a-sourcemap), and the framework-specific deep dives via [check-if-a-site-is-built-with-nextjs](/how-to/check-if-a-site-is-built-with-nextjs) and [detect-tailwind-css-on-a-website](/how-to/detect-tailwind-css-on-a-website). For security work the version detail you collected feeds directly into vulnerability databases — query each `name@version` against the GitHub Advisory Database, the Snyk DB, or `npm audit` output and you have a per-site CVE exposure list in minutes. For competitive intelligence the library list pairs with the framework / CMS detection ([detect-framework-of-any-website](/how-to/detect-framework-of-any-website), [identify-the-cms-behind-any-site](/how-to/identify-the-cms-behind-any-site)) to produce a full stack write-up. For comparing tools on this dimension specifically, the [Wappalyzer alternatives](/alternatives/wappalyzer) and [BuiltWith alternatives](/alternatives/builtwith) pages contrast Sourcemap Explorer's ad-hoc package detection with the categorical-only output of the established detectors.

FAQ

How is this different from reading the DOM?

The DOM shows libraries that register themselves as globals. Sourcemaps show libraries that were bundled. Most modern apps bundle hundreds more than they register — you only see the full picture via sourcemaps. ESM-style imports do not pollute the global object the way old-school `<script>` tags did, which is why DOM-only detection misses most of the modern bundle.

Can I get this list without the sourcemap?

Not completely. You can catch the top ~30 via runtime signatures (window globals, recognisable error strings, distinctive function signatures), but the long tail of internal utilities, niche libraries and silent dependencies is invisible without the map. Tools like Wappalyzer and BuiltWith stop at this top-30 layer because they have no other source.

Does Sourcemap Explorer show transitive dependencies?

By default the Stack tab emphasizes top-level meaningful packages; transitive-only packages (like `goober` bundled by `react-hot-toast`) are filtered out via a curated skip list. The raw sourcemap package scan is available for deeper analysis when you want the full transitive tree, including the second-degree imports.

Are the version numbers reliable?

Yes, when they come from `node_modules/<pkg>/package.json` inside the map. The bundler reads the file from disk during the build and writes it verbatim into the map; the version field is exactly what the build used. The only failure modes are deliberate stripping (rare) and genuine bundler error (extremely rare).

What about CSS-only packages?

`node_modules/tailwindcss/package.json`, `node_modules/postcss/package.json` and similar show up in the CSS sourcemap on sites that ship CSS maps. Sourcemap Explorer reads both JS and CSS maps in the same pass, so the library inventory includes the CSS-side packages.

Can I see the bundle size each library contributes?

Sourcemap Explorer surfaces presence and version, not bytes-per-package. For the bytes view, tools like `source-map-explorer` and `webpack-bundle-analyzer` are the right shape — they consume the same `.map` files but produce a treemap visualisation focused on size rather than identity.

Why don't Wappalyzer and BuiltWith do this?

Because their detection model is fingerprint-based and category-aware, not source-aware. Adding sourcemap parsing to either product would be a large engineering investment that does not match the strategic positioning either company has chosen — Wappalyzer is monetising bulk technographic data, BuiltWith is monetising sales-intelligence lead lists, and per-page depth is a developer-tool problem that Sourcemap Explorer was built specifically to solve.

Related

Skip the manual steps.

Sourcemap Explorer automates every workflow in this guide — free, local, no sign-up.

Install free on Chrome