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

vite-plugin-barrel-subpath-imports

v0.1.1

Published

Vite plugin that rewrites barrel-file imports into per-subpath imports at transform time, avoiding the full-barrel inline-sourcemap cost that hangs DevTools on every dev-server page load.

Readme

vite-plugin-barrel-subpath-imports

Rewrites import { X, Y } from '<barrel>' (and the export { X } from '...' re-export form) into one import per subpath, at transform time — so you can keep writing normal barrel-style imports in your own code while Vite's dev server never has to serve the whole barrel file on every request.

Why

A barrel file (one module re-exporting everything a package or directory contains) is convenient, but under Vite's dev pipeline, any file that isn't pre-bundled — a symlinked workspace package excluded from optimizeDeps, or your own project source — gets served through Vite's live per-request transform. That transform unconditionally embeds a full-content inline sourcemap. For a barrel that re-exports dozens or hundreds of modules, the sourcemap scales with the whole barrel's content, not just the few exports any given file actually imports — which is what can hang the browser DevTools' own Sources/Network panels for several seconds on every page load, even though only a handful of icons or components were ever used.

This plugin rewrites the import statement instead, so each consumer only ever pulls in the specific subpath module it needs — normal barrel-style code stays fast, without anyone needing to remember a per-subpath import convention by hand.

Install

npm install --save-dev vite-plugin-barrel-subpath-imports

Usage

// vite.config.ts
import { barrelSubpathImports, buildBarrelIndexExportMap } from 'vite-plugin-barrel-subpath-imports';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    barrelSubpathImports('my-ui-subpath-imports', '@my-org/ui', () =>
      buildBarrelIndexExportMap('../packages/ui/src/index.ts'),
    ),
  ],
});

Any import { Button } from '@my-org/ui' in your source is rewritten (at transform time only — your source files are never touched) into import { Button } from '@my-org/ui/components/Button'; (or whatever subpath buildBarrelIndexExportMap resolved from the barrel's own export { Button } from './components/Button' line).

barrelSubpathImports(pluginName, moduleSpecifier, loadExportMap)

The plugin factory. moduleSpecifier is the barrel's import specifier as it appears in your source ('@my-org/ui', 'lucide-react', a relative alias like 'shared/components', …). loadExportMap is called once (lazily, on first matching file) and must return a Record<string, BarrelExportInfo> mapping each exported name to where it actually lives:

interface BarrelExportInfo {
  from: string; // subpath appended to moduleSpecifier, e.g. 'components/Button'
  isType?: boolean; // re-emit as `import type` / `export type`
  isDefault?: boolean; // the subpath module's export is `default`, not a named export matching the key
}

You're free to build this map however fits your barrel's shape — three ready-made builders cover the common cases:

  • buildBarrelIndexExportMap(barrelIndexFile) — parses a single-file barrel of export { X, Y } from './sub' / export type {...} from './sub' re-export statements (one level deep — each re-exported module is a leaf, not itself another barrel to recurse into).
  • buildDirectoryBarrelExportMap(rootDir) — for a directory of subfolders, each its own small barrel (index.ts/.tsx re-exporting that subfolder's components/types), referenced from one root index.ts via export * from './subfolder' lines. Recurses through export * from './relative' re-exports within each subfolder's own files too.
  • buildDefaultReexportBarrelExportMap(options) — for a barrel where each export is a default as X re-export from its own leaf module (an icon library is the classic case: one file per icon, each a default export). Takes barrelPath plus optional statementPattern/toSubpath overrides for a barrel whose generated syntax or on-disk layout doesn't match the permissive default. A ready-made lucide-react preset built on top of this is deliberately not shipped here — see the example below.
// A lucide-react preset, built from the generic pieces above — worth keeping in your own repo
// (once, shared across every vite.config.ts that needs it) rather than hand-rolling per project:
import { createRequire } from 'node:module';
import { buildDefaultReexportBarrelExportMap } from 'vite-plugin-barrel-subpath-imports';

export function buildLucideReactExportMap() {
  const require = createRequire(import.meta.url);
  return buildDefaultReexportBarrelExportMap({
    barrelPath: require.resolve('lucide-react/dist/esm/lucide-react.mjs'),
    // lucide-react's generated barrel also re-exports a few non-icon helpers from this same file —
    // restrict to just the icon re-exports.
    statementPattern: /export\s*\{([^}]+)\}\s*from\s*'(\.\/icons\/[^']+)';/g,
    // The barrel lives inside dist/esm/ itself, so the icon paths it re-exports (relative to
    // itself) need that prefix added back to be valid `lucide-react/...` subpaths; the `.mjs`
    // extension is stripped to match how this plugin writes its rewritten imports.
    toSubpath: (p) => `dist/esm/${p.replace('./', '').replace('.mjs', '')}`,
  });
}

If none of these fit, write your own loadExportMap function — it just needs to return the same Record<string, BarrelExportInfo> shape.

Caveats

  • Statement detection uses es-module-lexer (the same parser Vite uses internally), so it only ever rewrites confirmed import/export ... from statements — never touches an unrelated statement. Its one gap is TypeScript's export type { X } from '...' re-export syntax, which es-module-lexer doesn't recognize as either an import or an export; this plugin covers that one shape with a narrowly-anchored regex fallback.
  • Runs at Vite's default transform stage (no enforce: 'pre'), so JSX/TSX syntax has already been stripped by @vitejs/plugin-react's own pre transform by the time this plugin sees the code — es-module-lexer throws on raw JSX.
  • If any name in an import/export statement isn't present in the export map, that whole statement is left untouched rather than partially rewritten.

License

MIT