Site / Site and the Router
How it works
A node is a router plus annotations
Site.route(router, routeCase, options) keeps the router you pass and reads
the tag from the route case, so one route case is one node. Nothing about the
URL is reimplemented: a node's address is always node.router.build(params),
so a link built through the Site parses back to the route it was built from.
Nodes are frozen values, and mounting one in a tree does not change it, so the
exported node builds targets and hrefs wherever it is used.
The tree is indexed once
Site.make(...roots) freezes the topology and walks it once, at definition,
so a bad tree fails where it is declared. The walk keeps three things for that
tree, in a WeakMap:
- each node's parent;
- each tag's node, refusing two nodes with one tag or one node mounted twice, since either would make "which node is this route?" ambiguous;
- the parser:
parseUrlWithFallback(oneOf(...routers))over every node's router, in tree order (depth-first, parents first).
So Site.parse is Foldkit's parser with the list taken from the tree, and
Site.routeAt(site, url) is the same parse with None instead of a
fallback. Every other lookup (chainOf, titleOf, sectionOf, metaOf) is
a map read and a walk up the parents.
Site.routing is the two branches, written once
It is a Bundle wiring, placed with Bundle.assemble beside the pages. It
claims the click Message, so the application's own update never sees one,
and handles the URL-change Message before passing it on.
Routing.reduces(message) names both for the application's guard.
On a click:
UrlRequest.External -> load the page in full
UrlRequest.Internal, the tree holds it -> pushUrl or replaceUrl, per Site.historyOf
UrlRequest.Internal, no node takes it -> load the page in fullThe last line is the difference from Foldkit's own split. Foldkit calls a link
internal when it is on this origin. The Site calls it internal when this
application draws it. A link to /docs served by another app on the same
domain is a full load, not a Navigate Command that could only draw NotFound.
Site.historyOf(current, next) adds a Back step for a move to another node,
or from an address the tree does not hold, so Back can return there. A move
within one node replaces the current entry, unless the node's history rule
says otherwise. In the example, another person is a step and another search
of the people list is a replace.
On a URL change the wiring parses the address with your parse. If the
result is the route the Model already shows, it returns the same Model, so
nothing renders and nothing pending is cleared. Your own replaceUrl, or a
URL mirror's write, comes back as exactly such a change. Otherwise it runs,
in order:
- set the route field;
- each routed page (
Site.placement) whose node the route belongs to is told, through the page's own Message; arrived(model, route), if you gave one, for anything else a new route does, such as moving focus.
The navigation Commands it returns are ordinary Foldkit Commands
(pushUrl, replaceUrl, load) that answer with your completed
Message. The wiring shares the URL-change tag with URL mirrors, which read
their own slices of the address first.
The rest reads the tree and nothing else
Site.view composes functions: the deepest node's view, wrapped by each
ancestor's layout, root first. A layout receives its child as an argument;
there is no Outlet and no nested router. Site.sources turns each node's
surface into a Surface.when on the route field, keyed by tag, so the
reads a page declares start when its route is shown and stop when it is not.
Site.targets runs each node's Site.paths Effect to list every address,
for a static build and a sitemap.