@moodia/override-resolver
v2.1.3
Published
A Webpack resolver which allows the option to override file import paths.
Readme
OverrideResolver
A powerful Webpack plugin that allows you to override module imports based on file paths, with built-in import guards and extensive debugging capabilities.
Features
- 🔄 File Override System: Override any module import from base source roots with custom implementations
- 🎯 Priority-Based Overrides: Multiple override roots with configurable priority order
- 🛡️ Import Guard: Enforce import restrictions to maintain architectural boundaries
- 🔍 Debug Mode: Detailed logging for specific files to troubleshoot override resolution
- 📊 Configuration Logging: Visual display of override configuration on startup
- 🎨 Filters: Apply overrides only to specific file patterns (e.g.,
.shell,.style)
Installation
npm install @moodia/override-resolverQuick Start
const OverrideResolver = require('@moodia/override-resolver');
const path = require('path');
module.exports = {
resolve: {
plugins: [
new OverrideResolver(
// Base source roots
[path.resolve(__dirname, 'core'), path.resolve(__dirname, 'modules')],
// Override source roots (with filters and extensions)
[
{
path: path.resolve(__dirname, 'electron/src/renderer'),
filters: ['.shell', '.style', '.abstract'],
extensions: ['.tsx', '.ts']
}
],
// Overridable directories
['components', 'modules'],
// Excluded directories
['node_modules'],
// Additional dependencies
{
'core': path.resolve(__dirname, 'core'),
'modules': path.resolve(__dirname, 'modules')
},
// Import guard rules
[],
// Enable configuration logging
true,
// Debug specific files
['MyComponent.shell', 'AnotherFile.tsx']
)
]
}
}How It Works
Override Resolution Process
When you import a file from a base source root, OverrideResolver follows this pipeline:
- Debug Setup: Checks if the file should be debugged
- Additional Dependencies: Resolves custom module path mappings
- Import Guard: Validates imports against security rules
- Relative Import Check: Only processes relative imports (starting with
.or/) - Applicability Check: Verifies the import is not from excluded directories
- Override Search: Looks for override files in configured override roots
- Resolution: Returns override if found, otherwise uses original file
Path Transformation
The plugin transforms paths by:
- Taking the relative path from the base source root
- Prepending the override source root
- Checking if a file exists at that location
Example:
Original: modules/Stripe/Component/File.shell.tsx
Override: electron/src/renderer/Stripe/Component/File.shell.tsx
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(relative path from base root)Priority Order
Override roots are checked in reverse order (last in array = highest priority):
overrideSourceRoots: [
{ path: '/project/override1' }, // Priority 2 (checked second)
{ path: '/project/override2' } // Priority 1 (checked first)
]Constructor Parameters
1. baseSourceRoots (required)
Type: string[]
Array of directory paths that contain your original source files.
[
path.resolve(__dirname, 'core'),
path.resolve(__dirname, 'modules')
]2. overrideSourceRoots (required)
Type: Array<string | OverrideRoot>
Array of override source roots. Can be strings or objects with configuration.
[
{
path: path.resolve(__dirname, 'electron/src/renderer'),
filters: ['.shell', '.style', '.abstract'], // Only override files with these in filename
extensions: ['.tsx', '.ts'] // Extensions to check
}
]OverrideRoot Object:
path: Directory path for overridesfilters(optional): Array of strings that must be in the filenameextensions(optional): File extensions to check (e.g.,['.tsx', '.ts'])
3. overridableDirectories (optional)
Type: string[]
Directory names that can be overridden. If empty, all directories are overridable.
['components', 'modules', 'routes']4. excludedRequestDirectories (optional)
Type: string[]
Directories where overrides should NOT be applied.
['node_modules', 'dist']5. additionalDependencies (optional)
Type: { [key: string]: string }
Maps module names to absolute paths for custom module resolution.
{
'core': path.resolve(__dirname, 'core'),
'modules': path.resolve(__dirname, 'modules')
}6. importGuard (optional)
Type: ImportGuardRule[]
Array of rules to enforce import restrictions.
[
{
ward: './src/restrictedFolder', // Directory to guard
whitelist: [ // Allowed imports
'INTERNAL', // Special keyword: allows internal imports
'./src/allowedFolder'
]
}
]ImportGuardRule:
ward: Directory path to protectwhitelist: Array of allowed import paths (or'INTERNAL'for internal imports)
7. logConfig (optional)
Type: boolean (default: false)
Enable startup configuration logging with visual examples.
true // Shows configuration on webpack startupOutput Example:
═══════════════════════════════════════════════════════
OverrideResolver Configuration
═══════════════════════════════════════════════════════
Base Source Roots:
1. /project/core
2. /project/modules
Override Source Roots (priority order, highest to lowest):
1. /project/electron/src/renderer [filters: .shell, .style]
Example:
Original: /project/modules/Module/Component/file.shell.tsx
Override: /project/electron/src/renderer/Module/Component/file.shell.tsx8. debugFiles (optional)
Type: string[] (default: [])
Array of filenames to debug. Shows detailed resolution process for matching files.
['StripePayment.shell', 'MyComponent.tsx']Output Example:
╔════════════════════════════════════════════════════════════════
║ DEBUG: StripePayment.shell
╠════════════════════════════════════════════════════════════════
Request Details:
request.request: ./StripePayment.shell
request.path: /project/modules/Stripe
resolved path: /project/modules/Stripe/StripePayment.shell
isApplicable: true
Checking Override Roots (reverse order):
[0] Override Root: /project/electron/src/renderer
Filters: .shell, .style
✓ Filter check passed
Base Source Root: /project/modules
✓ Path is below source root (relative: Stripe/StripePayment.shell)
Checking override path: /project/electron/src/renderer/Stripe/StripePayment.shell
Extensions to check: .tsx, .ts
✓ /project/electron/src/renderer/Stripe/StripePayment.shell.tsx
✗ /project/electron/src/renderer/Stripe/StripePayment.shell.ts
✓✓✓ OVERRIDE FOUND! Using: /project/electron/src/renderer/Stripe/StripePayment.shell.tsx
╚════════════════════════════════════════════════════════════════Usage Examples
Basic Override
Project Structure:
project/
├── modules/
│ └── MyModule/
│ └── Component.tsx # Original
├── electron/src/renderer/
│ └── MyModule/
│ └── Component.tsx # Override
└── webpack.config.jsConfiguration:
new OverrideResolver(
[path.resolve(__dirname, 'modules')],
[{
path: path.resolve(__dirname, 'electron/src/renderer'),
extensions: ['.tsx', '.ts']
}],
[],
['node_modules']
)Result:
import Component from 'modules/MyModule/Component';
// Resolves to: electron/src/renderer/MyModule/Component.tsxMultiple Override Roots with Priority
new OverrideResolver(
[path.resolve(__dirname, 'modules')],
[
{
path: path.resolve(__dirname, 'overrides/base'),
extensions: ['.tsx', '.ts']
},
{
path: path.resolve(__dirname, 'overrides/custom'), // Higher priority
extensions: ['.tsx', '.ts']
}
],
[],
['node_modules']
)If a file exists in both overrides/custom and overrides/base, the one in overrides/custom will be used.
Filtered Overrides
Only override specific file types:
new OverrideResolver(
[path.resolve(__dirname, 'modules')],
[{
path: path.resolve(__dirname, 'electron/src/renderer'),
filters: ['.shell', '.style'], // Only override .shell.tsx and .style.ts files
extensions: ['.tsx', '.ts']
}],
[],
['node_modules']
)Files that will be overridden:
Component.shell.tsx✓Component.style.ts✓Component.tsx✗ (no.shellor.stylein filename)
Import Guard
Restrict what modules can be imported from specific directories:
new OverrideResolver(
[path.resolve(__dirname, 'modules')],
[{ path: path.resolve(__dirname, 'overrides') }],
[],
['node_modules'],
{},
[
{
ward: path.resolve(__dirname, 'modules/CoreModule'),
whitelist: [
'INTERNAL', // Allow imports within CoreModule
path.resolve(__dirname, 'modules/SharedUtils') // Allow imports from SharedUtils
]
}
]
)Allowed:
// In modules/CoreModule/ComponentA.tsx
import ComponentB from './ComponentB'; // ✓ INTERNAL
import { helper } from 'modules/SharedUtils/helper'; // ✓ WhitelistedBlocked:
// In modules/CoreModule/ComponentA.tsx
import Something from 'modules/OtherModule/Something'; // ✗ Not whitelisted
// Error: A module in a guarded directory attempted an import that could not be matched with the whitelistDebug Specific Files
Troubleshoot override resolution for specific files:
new OverrideResolver(
[path.resolve(__dirname, 'modules')],
[{
path: path.resolve(__dirname, 'electron/src/renderer'),
extensions: ['.tsx', '.ts']
}],
[],
['node_modules'],
{},
[],
false, // Don't log configuration
['MyComponent.shell', 'AnotherComponent.tsx'] // Debug these files
)This will output detailed resolution information only for files matching MyComponent.shell or AnotherComponent.tsx.
Troubleshooting
Override Not Working?
Enable debug logging for the specific file:
debugFiles: ['YourFile.shell']Check the debug output to see:
- Is the file passing the filter check?
- Is the path below the source root?
- Does the override file exist at the expected location?
- What extensions are being checked?
Verify path structure:
- Override path = Override Root + Relative Path from Base Root
- Example: If original is
modules/A/B/File.tsx, override should beoverrideRoot/A/B/File.tsx(NOToverrideRoot/modules/A/B/File.tsx)
Enable configuration logging to verify your setup:
logConfig: true
Common Issues
Issue: Override file exists but isn't being used
- Check: Does the filename match the filter requirements?
- Check: Is the file extension in the
extensionsarray? - Check: Is webpack caching old results? Try deleting the cache directory.
Issue: Import guard errors
- Check: Are you importing from outside the whitelist?
- Check: Use
'INTERNAL'in whitelist to allow imports within the same directory
Issue: Relative imports not working
- Check: Only relative imports (starting with
.or/) are processed - Check: Library imports (e.g.,
import React from 'react') are not overridden
Best Practices
- Use filters to limit which files can be overridden (improves performance)
- Enable configuration logging during development to verify your setup
- Use debug files when troubleshooting specific override issues
- Organize overrides to mirror the structure of base source roots
- Document your override strategy in your project README
- Use import guards to enforce architectural boundaries
License
MIT
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
