fix(modules): core offers a contribution, never a slot name
The inverted slot direction reached exactly one module. Core filled three
literal names - uo.guild.detail, uo.guild.forum, uo.guild.header - matched by
exact name in applyCoreFills, so a second game declaring a place under its own
id got an empty page and no error. "A fill for a slot nobody declared is not an
error" is the rule that made the miss invisible, and it is the right rule; what
was wrong was core knowing a slot's name at all.
It also put a module identifier inside core, in three string literals
scripts/checkModuleIdentifiers.js masks by construction and could never catch.
Found by the integration kit while writing the chapter that teaches this shape
to an audience outside this org - which is what that phase is for.
So the module says WHERE, in its own vocabulary, and WHICH of core's
contributions goes there:
declareModuleSlot(ID, 'uo.guild.detail', { core: 'team.activity' })
and core offers into the catalogue rather than into a name:
offerCoreFill('team.activity', TeamActivityFeed)
CORE_CONTRIBUTIONS is exported and fixed at build time, so asking for one core
does not offer THROWS at the declaration. That asymmetry with an unfilled slot
is deliberate: an unknown contribution is always a typo or a version skew - the
module's coreApi range has already been checked - and the failure it would
otherwise produce is a page that renders empty forever with nothing logged.
options.core is optional; a slot that asks for nothing stays empty, which is
what a module declaring a place it fills itself wants. More than one slot may
ask for the same contribution and each gets it: how many places a module wants
its feed in is a layout decision on a page core does not own.
Amends MODULE_API 1.6.0 in place rather than adding 1.7.0 - the same rule the
eighth and ninth members were given, and 1.6.0 has only ever been on edge.
Also: the UI kit is nine exports, not eight. Slot made it nine in phase 3 and
the comment beside it still said eighth.
288 client tests, 1162 server tests.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import { createRoot } from 'react-dom/client'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import App from './App.jsx'
|
||||
import { publishSharedDependencies } from './modules/shared.js'
|
||||
import { declareSlot, applyCoreFills, fillModuleSlot } from './modules/registry.js'
|
||||
import { declareSlot, applyCoreFills, offerCoreFill } from './modules/registry.js'
|
||||
import TeamActivityFeed from './modules/TeamActivityFeed.jsx'
|
||||
import TeamForumPanel from './modules/TeamForumPanel.jsx'
|
||||
import TeamNotifyToggle from './modules/TeamNotifyToggle.jsx'
|
||||
@@ -72,24 +72,33 @@ declareSlot('player.invite.accepted')
|
||||
// module chunk has evaluated, which is the only moment a module-declared slot
|
||||
// exists to be filled.
|
||||
//
|
||||
// Naming a slot no installed module declares is not an error. On a deployment
|
||||
// with no game module this fill simply never lands, which is the mirror of an
|
||||
// **Core offers a CONTRIBUTION and never names a slot.** The module that owns the
|
||||
// page says where each of these goes, in its own vocabulary, by asking for one on
|
||||
// `declareModuleSlot`. Naming the slots here instead — which is how this was first
|
||||
// written — meant core's Team content reached exactly one module: any other game
|
||||
// declaring a place under its own id got an empty page and no error, because a
|
||||
// fill nobody asked for is deliberately not an error. It also put a module id
|
||||
// inside core, in string literals `scripts/checkModuleIdentifiers.js` masks by
|
||||
// construction and so could never have caught.
|
||||
//
|
||||
// Offering something nothing asks for is still not an error: a deployment with no
|
||||
// game module installed asks for none of these, which is the mirror of an
|
||||
// unfilled slot rendering nothing.
|
||||
fillModuleSlot('uo.guild.detail', TeamActivityFeed)
|
||||
offerCoreFill('team.activity', TeamActivityFeed)
|
||||
|
||||
// The forum is core's for the same reason and goes in a SECOND place the module
|
||||
// declares, rather than joining the feed in the first: a slot takes one component
|
||||
// (first fill wins), and stacking two unrelated panels into one fill would make
|
||||
// the module unable to place them separately on its own page. It also keeps the
|
||||
// two independent — a deployment with the forum switched off renders the feed
|
||||
// exactly as before.
|
||||
fillModuleSlot('uo.guild.forum', TeamForumPanel)
|
||||
// The forum is core's for the same reason and goes wherever the module asked for
|
||||
// it — a SECOND place, in module-uo's case, rather than joining the feed in the
|
||||
// first: a slot takes one component (first fill wins), and stacking two unrelated
|
||||
// panels into one contribution would make the module unable to place them
|
||||
// separately on its own page. It also keeps the two independent — a deployment
|
||||
// with the forum switched off renders the feed exactly as before.
|
||||
offerCoreFill('team.forum', TeamForumPanel)
|
||||
|
||||
// And the notification control, in a third place the module declares ABOVE its
|
||||
// roster. A third slot rather than a corner of the feed for the same reason there
|
||||
// were two: this is an action on the page and the other two are content in it,
|
||||
// and only the module can say where each belongs on a page it owns.
|
||||
fillModuleSlot('uo.guild.header', TeamNotifyToggle)
|
||||
// And the notification control. A third contribution rather than a corner of the
|
||||
// feed for the same reason there were two: this is an action on the page and the
|
||||
// other two are content in it, and only the module can say where each belongs on
|
||||
// a page it owns.
|
||||
offerCoreFill('team.notify', TeamNotifyToggle)
|
||||
|
||||
// Render on DOMContentLoaded rather than immediately, and that is the one line
|
||||
// of core's boot the module system changes.
|
||||
|
||||
Reference in New Issue
Block a user