@emahuni/rolldown-wrapper
v2.4.1
Published
A package to hack into rolldown or rollup to enable injection of rollup or rolldown options where it is not possible.
Downloads
22
Maintainers
Readme
Rolldown Wrapper
A Node.js ESM loader wrapper for Rollup and Rolldown that allows automatic configuration merging, runtime option injection, and optional debug logging, without requiring changes to consumer code.
⚠️ Node.js v20.10.0+ required.
Why
Normally, Rollup (and Rolldown) expect you to manage configuration through a rollup.config.js file or by passing options directly to their APIs.
This works fine if you control the build process.
But in some cases, like with Directus or other frameworks, Rollup/Rolldown are used internally and you don’t get access to their config files.
That means you cannot:
- Inject custom plugins
- Override or add to default rollup options
- Enforce consistent build settings across projects
This wrapper solves that by:
- Intercepting
rolldown/rollupat import time using Node’s ESM loader hooks. - Wrapping the internal functions so your own config and runtime options are always merged in.
- Ensuring consumers can continue using
import { rollup } from 'rollup'orimport { rolldown } from 'rolldown'without any changes.
In short:
👉 It gives you centralised control over Rollup/Rolldown behaviour, even when you don’t control the consumer code.
Features
- Automatically wraps
rollupandrolldownexports. - Merges config file options with runtime options.
- Supports:
- Direct function exports.
- Imported bindings and re-exports.
- Plain function or variable definitions.
- Fully compatible with ESM imports (
import { rollup } from 'rollup'). - Optional debug logging for patched sources.
- Graceful fallback if a module cannot be patched.
- Deep merge of objects and concatenation of arrays in options.
Installation
pnpm add @emahuni/rolldown-wrapperHow it Works
+------------------+
| Node Import |
| rollup/rolldown |
+--------+---------+
|
v
+-------------------------+
| Rolldown Wrapper Loader |
| - Detects export type |
| - Renames original fn |
| (__original_rollup) |
| - Injects wrapper fn |
| (rollup / rolldown) |
| - Merges config + runtime
| - Adds debug logs if enabled
| - Resolves loader dynamically
+--------+----------------+
|
v
+------------------+
| Consumer Code |
| import { rollup }|
| or import {rolldown} |
| Works as usual |
+------------------+Usage
1. Programmatic Registration via Script directus-cli.js
This is an example of a programmatic registration script for Directus CLI, as this was the original use case.
import { register } from 'node:module';
import { pathToFileURL } from 'node:url';
import { createRequire } from 'node:module';
import { init } from '@emahuni/rolldown-wrapper';
const require = createRequire(import.meta.url);
// Dynamically resolve loader package
const loaderPackage = '@emahuni/rolldown-wrapper';
const loaderPath = pathToFileURL(new URL(loaderPackage, import.meta.url).pathname);
// Initialise runtime options (optional)
init({
options : { // optional, Rollup/Rolldown options to merge with config (the key can also be rollupOptions or rolldownOptions)
plugins: [],
build : { minify: true },
},
debug : true, // optional, enables runtime debug logs
finalizeOptions: (options) => ({ ...options, customFlag: true }), // optional hook for final transformation
});
// Register loader
register(loaderPackage, loaderPath);
// Dynamically resolve Directus CLI
(async () => {
try {
const cliPath = require.resolve('directus/package.json')
.replace('package.json', 'cli.js');
const cli = await import(pathToFileURL(cliPath).href);
console.debug('Starting Directus CLI...');
} catch (err) {
console.error('Failed to start Directus CLI:', err);
}
})();⚠️ Recommended: avoids Node’s experimental loader warning.
2. Inline Registration with Dynamic Package Resolution
node --import "data:text/javascript,
import { register } from 'node:module';
import { pathToFileURL } from 'node:url';
import path from 'path';
// Dynamically resolve loader package
const loaderPackage = '@emahuni/rolldown-wrapper';
const loaderPath = pathToFileURL(new URL(loaderPackage, import.meta.url).pathname);
register(loaderPackage, loaderPath);" \
./node_modules/directus/cli.js bootstrapUsage
Run:
node directus-cli.js bootstrap
node directus-cli.js start- Works with any Directus CLI command or directus extensions.
- No experimental `--loader` warnings.
- Runtime options from init() are merged the wrapper's consumer, and with your rolldown.config.js if used (see Configuration below).
3. Deprecated: Using `--loader`
node --loader '@emahuni/rolldown-wrapper' ./node_modules/directus/cli.js bootstrap
node --loader '@emahuni/rolldown-wrapper' ./node_modules/directus/cli.js start⚠️ Deprecated: Node emits an experimental warning:
ExperimentalWarning: \`--experimental-loader\` may be removed in the future; instead use register()
Configuration
You can create a `rolldown.config.js` or `rollup.config.js` in the project root:
export default {
plugins: [],
build : {
minify: false,
},
};Important:
- Only accepts Rollup/Rolldown-compatible options.
- Does not accept package-specific options like debug or finalizeOptions. Those must be passed via init().
- These options are merged with the options (rollupOptions) object from init().
Runtime Options (init())
import { init } from '@emahuni/rolldown-wrapper';
init({
options : { // (the key can also be rollupOptions or rolldownOptions)
plugins: [/* ... */ ],
build : { minify: true },
},
configFilePath : 'path/to/my.config.js', // optional, path to config.js file, defaults to ./rolldown.config.js or ./rollup.config.js
debug : true, // enable debug logs at runtime
finalizeOptions: (options) => ({ ...options, customFlag: true }), // optional final transformation
});- Can include both package-specific options and rollupOptions.
- options are merged with rolldown.config.js. (the options key can also be rollupOptions or rolldownOptions)
- debug enables logging of merged options.
- finalizeOptions allows final transformation of the merged options before invoking the original Rollup/Rolldown function.
Behaviour
- Detects rollup/rolldown exports in various forms:
- Direct function export
- Exported from imported bindings
- Plain function/variable definitions
- Renames original function to _original and wraps it with merged options.
- Logs merged options if debug is enabled.
- If patching fails, warns in console but does not break the import.
- Deep merges objects and concatenates arrays in options.
Changes
- v2.2.0
- now uses either rollup.config.js or rolldown.config.js file, whichever exists..
- init() can pass package-specific options and rollupOptions.
- Added debug option for runtime logging.
- Added finalizeOptions function.
- Improved fallback for various export types.
- Deep merge of objects and array concatenation.
- Graceful handling when patching fails.
Contribution
- Contributions are welcome!
- Please open issues or pull requests on GitHub.
- Ensure new code includes tests and documentation updates.
Author
Emmanuel Mahuni
GitHub: emahuni
License
MIT
