Skip to main content

Deactivation

The mirror image of activation, and the reason it is not just disable().

definePlugin({
name: '@app/document-mode',
activationEvents: [onView('document')],
deactivationEvents: [onView('notebook')], // stand down when we leave
});

await reactor.fire(onView('notebook'));
// → { deactivated: ['@app/document-mode'], activated: ['@app/notebook-mode'] }

Deactivation runs before activation within one fire, so a single event retires the old thing and brings up the new. The other order would leave both running for a beat — which a host reading a contribution point in between would see.

Three states, not two

This is the distinction the whole feature rests on:

who decidedcomes back on an event?keeps its module?
not activatednobody yet — its condition has not been metyesnot fetched yet
deactivatedthe platform: the reason for running has passedyesyes
disableda person, with a checkboxnoyes

Collapse deactivated into disabled and you get one of two bugs: an event silently overrides somebody's checkbox, or a plugin that stood down can never come back. So fire() never revives a disabled plugin — it records that the condition was met, and enable() is what runs the phases.

Dependants stand down first

reactor.deactivate(name) does it directly. Either way, dependants stand down first, transitively: a dependant left running against a deactivated dependency is holding contributions nobody maintains.

reactor.deactivate('@app/base'); // '@app/top', then '@app/middle', then '@app/base'

What survives: its place in the list, its manifest, its module, its enabled flag, and — if it declared preserveOutput — what it built. What goes: its contributions, and its register / afterRegistration disposers run.

One subtlety

A contribution point fires its activation event only once, so that a module which failed to load is not refetched on every render. Standing a plugin down lifts that guard for the points that plugin waits on, and no others — otherwise a plugin woken by a read could never be woken by a read again.

Opposite defaults, deliberately

activationEvents: [] // → at startup. A plugin with no opinion should run.
deactivationEvents: [] // → never. A plugin with no opinion should keep running.

Point them the same way and the first event anybody fires tears down every plugin that said nothing.