You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
leaflet-components/docs/06-load-order.md

3.2 KiB

06 — Load order

The rule

Export order in src/index.ts is load-bearing. A component that listens for a bubbling announcement from another tag must be exported — and therefore customElements.define()d — before that other tag.

Why

customElements.define(name, Class) upgrades every matching element already in the document immediately and synchronously, running connectedCallback right then for elements that are already connected.

On a real static-HTML page the markup is fully parsed before the deferred module script runs, so at define() time the elements already exist. The first define() for a given tag wins the race:

  • If a child tag is defined before its listening parent tag, every instance of that child in the page upgrades and fires its one-shot connect-time announcement (leaflet-register, icon-changed, leaflet-line-sync, leaflet-crs-changed) into a parent element that has no listener attached yet — because the parent class isn't defined, so its connectedCallback hasn't run.
  • That announcement is gone for good: connectedCallback does not fire again for an element that stays connected.

So parents must be defined first.

The ordering in src/index.ts

Root to leaf (this is the comment at the top of the file, kept in sync):

LeafletMap                                    ← nothing can register before the root exists
  LeafletControlLayers, LeafletLayerGroup,
  LeafletFeatureGroup                          ← listen for leaflet-register from arbitrary layers
  every concrete layer type (Marker, Circle,
  Polygon, TileLayer, …)
    LeafletLine                               ← child of Polygon/Polyline
    LeafletPopup, LeafletTooltip              ← child of any layer
    LeafletIcon, LeafletDivIcon               ← child of Marker

Standalone controls (LeafletControlZoom, LeafletControlAttribution, LeafletControlScale) have no such relationship and can go anywhere.

The core re-exports (register.ts, props.ts, with-props.ts, shared-props.ts, event-types.ts) come first and carry no customElements.define, so their order is free.

The HTMLElementTagNameMap augmentation

At the bottom of src/index.ts. Because export { X } from '…' re-exports X without binding it locally, the tag map needs its own import type of every component class alongside the re-export. Those imports are type-only, erased at build, back no define() — their order doesn't matter.

The test that guards this

test/load-order.test.ts is structurally unlike every other test file on purpose: it never statically imports a component module, so nothing is customElements.defined until it dynamically import()s src/index.ts partway through — after building a DOM tree out of plain, undefined elements. That's the only test reproducing a real page's actual order (markup first, module script second).

Every other test file imports components up front, so components are already defined before any element is created — they structurally cannot catch an ordering regression. If you add a component with a "parent listens for a child's announcement" relationship, extend load-order.test.ts.