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

@bobfrankston/importgen

v0.1.28

Published

Generate ES Module import maps from package.json dependencies for native browser module loading

Downloads

1,271

Readme

@bobfrankston/importgen

Use the same npm packages and import statements in the browser as in Node.js/Bun -- no bundler required.

Why

In Node.js you write:

import { formatDate } from "date-utils";

Node resolves date-utils by looking in node_modules. Browsers can't do that. Normally you'd need a bundler (webpack, rollup, etc.) to make bare module specifiers work.

Browsers now support import maps -- a <script type="importmap"> block that maps package names to file paths. importgen reads your package.json and generates this import map automatically, injecting it into your HTML file. Your code stays identical between Node and browser -- transparent, no changes needed.

Install

npm install -g @bobfrankston/importgen

Usage

Run from your project directory (containing package.json and your HTML file):

importgen [htmlfile] [--watch|-w] [--freeze] [--unfreeze] [--version|-v]
# Generate import map in index.html (default)
importgen

# Specify a different HTML file
importgen default.htm
importgen app.html

# Watch mode: regenerate whenever package.json changes
importgen --watch
importgen default.htm --watch

# Show version
importgen -v

# Freeze: replace symlinks/junctions with real copies for deployment
importgen --freeze

# Unfreeze: restore symlinks and run npm install
importgen --unfreeze

importgen reads dependencies from package.json in the current directory and injects the import map into the HTML file.

If no HTML file is specified, it searches for index.html, default.html, default.htm (in that order) and uses the first one found.

If the HTML already has a <script type="importmap"> block, it is replaced in place. Otherwise the block is inserted before </head>.

What It Generates

Given a package.json like:

{
  "dependencies": {
    "date-utils": "^2.0.0",
    "my-ui-lib": "file:../shared/my-ui-lib"
  }
}

importgen produces:

<!-- Generated by importgen 0.1.14 on Feb 9, 2026, 3:45:12 PM EST -->
<script type="importmap">
{
    "imports": {
        "date-utils": "./node_modules/date-utils/index.js",
        "my-ui-lib": "./node_modules/my-ui-lib/index.js",
        "color-convert": "./node_modules/color-convert/index.js"
    }
}
</script>

A timestamp comment is added above the import map showing the version and local date/time. It is updated on each regeneration.

Transitive dependencies are included automatically -- if my-ui-lib depends on color-convert, it appears in the map too. Circular dependencies are detected and handled.

Using Imports in the Browser

Once the import map is in your HTML, <script type="module"> code uses the same imports as Node:

// This works identically in Node AND the browser
import { formatDate } from "date-utils";
import { render } from "my-ui-lib";
import { initApp } from "./app.js";

The browser sees date-utils, looks it up in the import map, and loads ./node_modules/date-utils/index.js. Local relative imports (./app.js) work as always without the import map.

The key point: your TypeScript source files are the same whether they run in Node or the browser. importgen just provides the mapping the browser needs to find the packages.

How Entry Points Are Resolved

For each dependency, importgen reads that package's package.json and picks the entry point in this order:

  1. exports["."].import or exports["."].default (modern ESM)
  2. module field (ESM convention)
  3. main field
  4. ./index.js (fallback)

Dependency Types

  • npm versions (^1.2.3) -- resolved from node_modules/
  • file: paths (file:../path/to/package) -- resolved relative to package.json
  • workspace: references -- resolved from node_modules/

.dependencies Override

Add a .dependencies field to package.json to override resolution paths for specific packages without changing the real dependencies:

{
  "dependencies": {
    "my-utils": "^1.0.0"
  },
  ".dependencies": {
    "my-utils": "file:../local-my-utils"
  }
}

The import map will use the .dependencies path instead of the node_modules path for that package.

Freeze / Unfreeze

--freeze prepares a project for deployment by replacing all symlinks and junctions in node_modules with real directory copies. This is useful when file: or workspace: dependencies point to local packages via symlinks that won't exist on a deployment target.

Freeze also adds a preinstall guard script to package.json that prevents accidental npm install from overwriting the frozen copies. If a preinstall script already exists, it is saved as frozen-preinstall so it can be restored later.

--unfreeze reverses the process: it removes the preinstall guard (restoring any original preinstall script), then runs npm install to re-establish normal symlinked dependencies.

VS Code tasks.json Integration

Add importgen --watch as a background task that starts automatically when you open the project. Combined with tsc --watch, your import map stays current as you develop.

Example .vscode/tasks.json:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "tsc: watch",
            "type": "shell",
            "command": "tsc",
            "args": ["--watch"],
            "runOptions": {
                "runOn": "folderOpen"
            },
            "problemMatcher": "$tsc-watch",
            "isBackground": true,
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "label": "importgen: watch",
            "type": "shell",
            "command": "importgen",
            "args": ["--watch"],
            "runOptions": {
                "runOn": "folderOpen"
            },
            "isBackground": true,
            "problemMatcher": {
                "pattern": {
                    "regexp": "^$",
                    "file": 1,
                    "location": 2,
                    "message": 3
                },
                "background": {
                    "activeOnStart": true,
                    "beginsPattern": "^\\[generate-importmap\\].*watching.*$",
                    "endsPattern": "^\\[generate-importmap\\].*Updated.*$"
                }
            }
        }
    ]
}

Key points:

  • "runOn": "folderOpen" starts both tasks automatically when you open the workspace
  • "isBackground": true keeps them running as persistent watchers
  • The custom problemMatcher for importgen tells VS Code this is a long-running watcher, not a one-shot build
  • If your HTML file isn't index.html, add it to the args: "args": ["default.htm", "--watch"]

Workflow: edit package.json (add/remove a dependency) -> importgen regenerates the import map in the HTML -> tsc recompiles TypeScript -> browser picks up changes on reload.

PWA Compatible

Generated import maps are static and can be cached by service workers.

License

MIT