Barrel file removal tool for JavaScript & TypeScript projects (ESM-only).
Barrel files are index files that only contain re-exports from other modules:
export { formatDate } from "./date.ts";
export { formatCurrency } from "./currency.ts";
export { capitalize } from "./string.ts";Barrel files are convenient, but they often come with trade-offs including:
- Performance and memory: they artificially inflate the module graph and slow down startup times, HMR, and CI pipelines.
- Tree-shaking failures: they often confuse tree-shakers, risk entire libraries to be bundled when only a single function is needed.
- Circular dependencies: they frequently create "import cycles", crashing bundlers or causing confusing errors.
- Speeding up the JavaScript ecosystem - The barrel file debacle (Marvin Hagemeister, 2023-10-08)
- How we optimized package imports in Next.js (Shu Ding, 2023-10-13)
- A practical guide against barrel files for library authors (Pascal Schilp, 2024-06-01)
- Please Stop Using Barrel Files (Dominik Dorfmeister, 2024-07-26)
- Automated rewiring of consumers to import directly from source
- Preserves path aliases
- Skips barrel files that are entry points (
package.json#exportsetc.) - Auto-detects or enforces file extensions to match project style
- Optional
--organize-importsto dedupe and clean up after rewrites - Granular control with
--skip,--onlyor add--barrel-like files - Use
--checkfor CI to fail if barrel files are detected - Go all out with
--unsafe-namespaceto namespace imports (warning: naive) - Verified on non-trivial repositories to not break the build/tests
npx unbarrelify
npx unbarrelify --helpIt runs safe in dry-mode until you add --write
npm install -D unbarrelifyunbarrelify - Remove barrel files and rewire imports
Usage: unbarrelify [options]
Options:
-c, --cwd <dir> Working directory (default: ".")
-o, --only <file> Process only selected barrel file (can be repeated)
-s, --skip <pattern> Barrel files to skip (glob, can be repeated)
-b, --barrel <pattern> Extra files to treat as barrels (glob, can be repeated)
-f, --files <pattern> Set file coverage (glob, can be repeated, default: use tsconfig.json)
-e, --ext <ext> Extension for rewritten imports (default: auto-detect)
-w, --write Write changes to disk (default: false/dry-run)
--organize-imports Run TypeScript's "Organize Imports" after rewrites to dedupe imports
--check, --ci Check mode for CI; non-zero exit if there are changes
--unsafe-namespace Rewrite namespace imports; may include types (bad) and cause identifier collisions (also bad)
-h, --help Show this help message
Examples:
unbarrelify
unbarrelify --cwd ./src
unbarrelify --only ./src/utils/index.ts
unbarrelify --skip ./public-api.ts
unbarrelify --barrel looks/like/barrel.ts
unbarrelify --files "src/**/*.ts" --files "lib/**/*.ts"
unbarrelify --ext .js
unbarrelify --write
unbarrelify --check
unbarrelify --unsafe-namespace
import { unbarrelify } from "unbarrelify";
const result = await unbarrelify({
cwd: "./src",
files: ["**/*.ts"],
skip: [],
ext: ".ts",
write: false,
});
console.log(`Modified ${result.modified.length} files`);
console.log(`Deleted ${result.deleted.length} barrel files`);The main function that processes files and removes barrel files.
interface Options {
cwd: string;
only?: string[];
files?: string[];
skip?: string[];
barrel?: string[];
ext?: string;
write: boolean;
check?: boolean;
unsafeNamespace?: boolean;
organizeImports?: boolean;
progress?: (event: ProgressEvent) => void;
}interface Result {
modified: string[];
deleted: string[];
preserved: PreservedBarrel[];
}
interface PreservedBarrel {
path: string;
reason: "skip" | "namespace-import" | "non-ts-import" | "dynamic-import";
consumers: string[];
}- Identify barrel files (files containing only re-export statements)
- Build import/export maps to track dependencies
- Rewire imports in consuming files to point directly to exporting source files
- Remove barrel files
// utils/index.ts
export { formatDate } from "./date.ts";
export { capitalize } from "./string.ts";
// module.ts
import { formatDate, capitalize } from "./utils/index.ts";// utils/index.ts - DELETED
// module.ts
import { formatDate } from "./utils/date.ts";
import { capitalize } from "./utils/string.ts";ISC