A Vite plugin that optimizes imports through barrel files (index.ts files) by resolving them to direct imports from specific modules instead of going through re-exports.
- π Problem It Solves
- π¦ Installation
- βοΈ Usage
- βοΈ Configuration Options
β οΈ Important Recommendations- π‘ Usage Examples
- π Optimization Results
- π Logging and Debugging
- π§ Technical Details
- π Development and Testing
Barrel files (index.ts files) are a popular pattern for creating convenient APIs for module exports:
// src/widgets/index.ts (barrel file)
export { CreateShopLayout } from './authentication/createShopLayout';
export { ProductList } from './products/productList';
export { ShoppingCart } from './cart/shoppingCart';
// ... dozens of other exportsHowever, using barrel files creates serious performance problems:
π³ Tree-shaking doesn't work effectively - bundlers can't determine which modules are actually used
π¦ Excessive code in bundle - entire barrel file is imported even for a single component
π₯ HMR breaks or works poorly - changing one file forces reloading the entire barrel
π Increased bundle size - especially critical for production builds
β Instead of this (inefficient):
import { CreateShopLayout, ProductList } from 'widgets';
// β οΈ Imports entire barrel file with all dependenciesβ Plugin automatically transforms to (optimized):
import { CreateShopLayout } from './widgets/authentication/createShopLayout';
import { ProductList } from './widgets/products/productList';
// π― Direct imports of only needed modulesnpm install -D vite-plugin-resolve-barrels// vite.config.ts
import { defineConfig } from 'vite';
import { resolveBarrelsPlugin } from 'vite-plugin-resolve-barrels';
export default defineConfig({
plugins: [
resolveBarrelsPlugin({
directories: ['widgets', 'features', 'entities', 'shared'],
}),
],
});// vite.config.ts
import { defineConfig } from 'vite';
import { resolveBarrelsPlugin } from 'vite-plugin-resolve-barrels';
export default defineConfig({
plugins: [
resolveBarrelsPlugin({
directories: ['widgets', 'features', 'entities', 'shared'],
enable: process.env.NODE_ENV === 'production', // Only for production
logReplacements: true, // Console logging
logToFile: true, // Save logs to file
logFilePath: 'barrel-resolutions.log',
alias: { '@': 'src' }, // Path aliases support
barrelFiles: ['index.ts', 'api.ts', 'models.ts'], // Custom barrel file names
rootDir: 'src', // Root directory to scan (default: 'src')
preserveExtensions: false, // Keep file extensions in imports (default: false)
}),
],
});| Option | Type | Default | Description |
|---|---|---|---|
π directories |
string[] |
required | List of directories to process barrel files |
π§ enable |
boolean |
true |
Enable/disable the plugin |
π₯οΈ logReplacements |
boolean |
false |
Output replacement logs to console |
π logToFile |
boolean |
false |
Save logs to file |
π logFilePath |
string |
'' |
Path to log file |
π alias |
Record<string, string> |
{} |
Path aliases configuration (e.g., { '@': 'src' }) |
π¦ barrelFiles |
string[] |
['index.ts', 'index.tsx', 'index.js', 'index.jsx'] |
Barrel file names to recognize |
π rootDir |
string | string[] |
'src' |
Root directory(ies) to scan for barrel files |
π€ preserveExtensions |
boolean |
false |
Keep file extensions in resolved imports |
NOT recommended for development mode due to:
- Additional computations on every code change
- Debugging complexity
- HMR may not work properly or at all
// Recommended configuration
resolveBarrelsPlugin({
directories: ['widgets', 'features'],
enable: process.env.NODE_ENV === 'production', // π Only for production
});π Small projects - if your bundle is small, optimization may not provide significant benefits
π§ Development server - may slow down module reloading
π Projects without barrel files - plugin won't provide any benefits
// vite.config.ts
resolveBarrelsPlugin({
directories: ['app', 'pages', 'widgets', 'features', 'entities', 'shared'],
enable: process.env.NODE_ENV === 'production',
});The plugin supports path aliases, allowing you to use imports like @/widgets instead of widgets:
// vite.config.ts
import { defineConfig } from 'vite';
import { resolveBarrelsPlugin } from 'vite-plugin-resolve-barrels';
export default defineConfig({
plugins: [
resolveBarrelsPlugin({
directories: ['widgets', 'features'],
alias: {
'@': 'src', // @/widgets -> src/widgets
'~': 'src', // ~/features -> src/features
'@widgets': 'src/widgets', // @widgets -> src/widgets
'@app': 'src/app', // @app -> src/app
},
}),
],
});Example usage with aliases:
// Works with aliases β
import { Component } from '@/widgets';
import { Feature } from '~/features';
import { Widget } from '@widgets';
// Also works without aliases β
import { Component } from 'widgets';Notes:
- Aliases are matched by longest prefix first (e.g.,
@appmatches before@) - The plugin automatically strips the configured
rootDirprefix from alias paths - Both aliased and non-aliased imports work simultaneously
By default, the plugin recognizes only index.ts|tsx|js|jsx as barrel files. If your project uses other barrel file names (like api.ts, models.ts from openapi-generator), configure the barrelFiles option:
// vite.config.ts
resolveBarrelsPlugin({
directories: ['api'],
barrelFiles: ['index.ts', 'api.ts', 'models.ts'], // Recognize multiple barrel patterns
});Example with openapi-generator:
// Your barrel files:
// src/api/index.ts - main exports
// src/api/api.ts - API client exports
// src/api/models.ts - TypeScript interfaces/models
// Both work now β
import { UserApi, ProductApi } from 'api';
import { User, Product } from 'api';The plugin tries barrel files in order until it finds a match.
By default, the plugin scans the src/ directory. You can customize this with the rootDir option:
// Single custom root
resolveBarrelsPlugin({
directories: ['components', 'utils'],
rootDir: 'lib', // Scan lib/ instead of src/
});
// Multiple roots (monorepo/complex projects)
resolveBarrelsPlugin({
directories: ['widgets', 'features', 'utils'],
rootDir: ['src', 'lib', 'packages'], // Try each root in order
});Use cases:
- Libraries with
lib/directory - Monorepos with multiple package roots
- Projects with
components/,utils/, etc. at root level
By default, the plugin strips .ts|tsx|js|jsx extensions from imports. If you need to keep them (e.g., for ESM compatibility), use preserveExtensions:
resolveBarrelsPlugin({
directories: ['widgets'],
preserveExtensions: true,
});Result:
// With preserveExtensions: false (default)
import { Component } from './widgets/component';
// With preserveExtensions: true
import { Component } from './widgets/component.ts';Common scenario with openapi-generator:
// vite.config.ts
export default defineConfig({
plugins: [
resolveBarrelsPlugin({
directories: ['api'],
barrelFiles: ['index.ts', 'api.ts', 'models.ts'], // All openapi-generator barrels
alias: { '@api': 'src/api' },
enable: process.env.NODE_ENV === 'production',
}),
],
});// Your imports work seamlessly β
import { DefaultApi, UsersApi } from '@api/api';
import { User, Product, Order } from '@api/models';// main.tsx
import { CreateShopLayout, ProductCard, useAuth } from 'widgets';π¦ Bundle Size: ~150kb
π¨ Issues: Entire widgets/index.ts + all barrel dependencies
// Automatically transforms to:
import { CreateShopLayout } from './widgets/authentication/createShopLayout';
import { ProductCard } from './widgets/products/productCard';
import { useAuth } from './widgets/auth/useAuth';π¦ Bundle Size: ~45kb (70% reduction)
π― Result: Only specific files + direct dependencies
resolveBarrelsPlugin({
directories: ['widgets'],
logReplacements: true, // Enable console logs
});
// Output:
// [resolve-barrels] src/pages/main.tsx:
// original: import { CreateShopLayout } from 'widgets';
// transform: import { CreateShopLayout } from './widgets/authentication/createShopLayout';resolveBarrelsPlugin({
directories: ['widgets'],
logToFile: true,
logFilePath: './build-logs/barrel-resolutions.log',
});1οΈβ£ Barrel files analysis - scans index.ts files in specified directories
2οΈβ£ Export maps building - creates a map of what is exported from where
3οΈβ£ AST transformation - analyzes imports through TypeScript AST
4οΈβ£ Import replacement - replaces barrel imports with direct imports
// β
Supported
import { Component } from 'widgets';
import { Component as MyComponent } from 'widgets/auth';
import { Component1, Component2 } from 'features/auth';
// β Not supported (yet)
import * as widgets from 'widgets';
import widgets from 'widgets';React Only - This plugin is specifically designed and tested for React projects
Supports:.tsβ’.tsxβ’.jsβ’.jsxfiles
# π¦ Install dependencies
npm install
# π§ͺ Run tests
npm run test
# π Tests with coverage
npm run test:coverage
# π¨ Build package
npm run build
# π Linting
npm run lint:jsMade with β€οΈ for optimizing Vite projects
π Faster builds β’ π¦ Smaller bundles β’ π― Better performance