Short answer
The onwarn hook is a pure notification filter. It intercepts every warning Rollup emits during the build, and a warning is suppressed simply by not forwarding it to the provided warn() handler. It cannot change the bundle, resolve a cycle, or alter plugin behavior — it only decides whether a message reaches the console.
Filtering circular dependency warnings
The canonical pattern checks the warning's code property and forwards everything else:
// rollup.config.js
export default {
input: 'src/main.js',
onwarn(warning, warn) {
if (warning.code === 'CIRCULAR_DEPENDENCY') return; // suppressed
warn(warning); // everything else still surfaces
}
};
The warning object also carries the modules involved (historically via warning.ids/warning.id or the message text, depending on version), so you can scope suppression to known, audited cycles instead of blanket-ignoring the code:
const knownCycles = ['src/legacy/a.js'];
onwarn(warning, warn) {
if (
warning.code === 'CIRCULAR_DEPENDENCY' &&
warning.ids?.some(id => knownCycles.some(k => id.endsWith(k)))
) return;
warn(warning);
}
Property names on the warning object have shifted between Rollup 2.x and 3.x/4.x, so log the full object once (console.log(warning) inside the hook) to confirm the shape on your version before writing the matcher.
Answers to the unresolved questions
1. Which codes are safe to suppress?
No code is universally safe — but suppression risk scales with what the warning protects. CIRCULAR_DEPENDENCY is the most commonly suppressed because many cycles are benign (type-only or lazily-used imports), yet a real cycle can cause undefined bindings at module-evaluation time. Suppress it only per-module, after auditing. Codes like UNRESOLVED_IMPORT, MISSING_EXPORT, or EMPTY_BUNDLE should essentially never be suppressed; they indicate broken output. Treat THIS_IS_UNDEFINED and EVAL as case-by-case.
2. Interaction with --silent
This is the genuinely under-documented part and should be verified on your exact version. In established behavior, --silent suppresses warning output at the CLI level, but onwarn still runs — so your hook executes and your filtering logic (and any logging you do inside it) still applies. Do not rely on this without a quick check: run rollup -c --silent with a console.log inside onwarn to confirm the hook fires on your version.
3. What happens if you omit warn()?
The warning is silently dropped. It is not queued, not printed later, and does not fail the build. That is the entire suppression mechanism — which is why a missing warn(warning) fallback (e.g., an early return added for debugging) can mute every warning, not just the one you targeted.
Verification
- Create two modules that import each other; build without the hook and confirm the
CIRCULAR_DEPENDENCY warning appears. - Add the hook and confirm the warning disappears while a deliberately triggered second warning (e.g., an unresolved import) still prints.
- Diff the emitted bundle with and without the hook — output should be byte-identical, proving the hook only affects notifications.
Caveat
Filtering noise here does not fix the architecture. If a cycle is intentional, document it in the allow-list; if it is accidental, break it with a shared third module or deferred access — the warning will then disappear on its own.