1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
//! Producer SDK for landing-host microfrontends.
//!
//! A microfrontend producer ships a self-registering ESM bundle: it defines a
//! custom element (`customElements.define("mfe-…", …)`) whose `connectedCallback`
//! mounts a Dioxus app into the host node (light DOM). The landing host curates a
//! registry of `{name, tag, kind}` and decides where to render each `<tag>`.
//!
//! This module is the entire Rust side of that contract. A producer crate writes
//! ~5 lines via [`mfe!`] and gets: the custom-element registration, the origin
//! self-derivation (so the bundle works cross-origin with no baked env), the
//! server-fn base override, the `{name,tag,kind}` manifest, and the host's
//! locale resolved into a [`Translator`](crate::i18n::Translator) above its root
//! — none of which it can get wrong, because the macro owns the naming
//! convention, the JSON shape, and where a locale comes from.
//!
//! wasm-only (see the `cfg` in `lib.rs`): the one cross-origin bundle is always a
//! `wasm32-unknown-unknown` cdylib. manganis/`dx` are unusable here — their asset
//! URLs are root-relative to the *serving* origin, i.e. inherently same-origin —
//! so producers hand-roll `wasm-bindgen --target web` and this SDK stays
//! manganis-free, deriving every URL from the bundle's own origin instead.
use Element;
use *;
use crate;
// The only JavaScript in the whole stack — the two things Rust can't express:
// subclassing `HTMLElement` (custom elements must extend it), and reading
// `import.meta.url` (a module-syntax form, not a value any Rust binding sees).
//
// `connectedCallback` fires on every (re)attachment, so it is guarded to mount
// once per element instance: a host router that detaches and re-inserts the node
// would otherwise stack a second live app on the first one's DOM. There is no
// paired `disconnectedCallback` because dioxus-web 0.7 exposes no teardown handle
// (`run() -> !` inside a bare `spawn_local`); the app therefore stays alive across
// a detach and is reused — with its DOM and its delegated listeners intact — when
// the element is re-inserted.
extern "C"
/// The bundle's own origin, from `import.meta.url`. Drives every URL the bundle
/// emits (server-fn base, stylesheet, seed images) so nothing is baked at build
/// time and the same artifact works behind any host origin.
/// Define `tag` as a custom element that calls `mount(element)` on its first
/// connect. Idempotent twice over: a no-op if `tag` is already registered, and
/// `mount` runs at most once per element instance, so re-attaching an element
/// (as an SPA host router does on navigate-back) reuses the running app instead
/// of stacking a second one. The `mount` closure must outlive the page — the
/// caller `forget`s it.
/// Launch a Dioxus app rooted at `el` (the custom element instance). Light DOM —
/// the host's fonts/tokens/preflight cascade in, so the bundle must not re-ship them.
/// The language the host page is written in, read off the DOM `el` is mounted
/// into. The Rust mirror of `localeOfElement` in `@evinvest/i18n`.
///
/// Read from the DOM rather than taken as an attribute or a prop, because a host
/// mounts the element *before* it applies attributes — React's `RemoteElement`
/// appends the node in one effect and sets its attributes in a later one — so
/// anything pushed in reads as absent at `connectedCallback` time. `lang` is the
/// platform's own answer to "what language is this subtree", every host that
/// serves more than one already sets it, and it is readable the instant the node
/// is attached.
///
/// A regional tag resolves to its base language (`ru-RU` → `ru`); an absent or
/// unpublished `lang` reads as [`DEFAULT_LOCALE`].
/// Define a microfrontend producer bundle. Expands in the producer crate (so its
/// `dioxus` rsx, `wasm-bindgen`, and `web-sys` resolve there), generating the
/// custom-element registration, the `wasm-bindgen(start)` entrypoint, and the
/// `MFE_MANIFEST` the build emits as `mfe.json`.
///
/// ```ignore
/// ev_lib::mfe! {
/// service: "real-estate", name: "overview", kind: component,
/// root: real_estate_allocation::embed::Overview, stylesheet: "mfe.css",
/// messages: crate::catalogue
/// }
/// ```
///
/// Naming is enforced: the tag is `mfe-{service}-{name}` and the registry name is
/// `{service}.{name}` — the host can't drift from the producer's identity. One
/// invocation per crate (it owns the single `wasm-bindgen(start)`).
///
/// `messages` is a `fn(Locale) -> Messages` and is **required**, not optional. A
/// producer with no copy passes an empty catalogue as one deliberate, reviewable
/// line; a producer *with* copy cannot forget to resolve its locale, which is
/// the bug the requirement exists to prevent. The macro resolves
/// [`host_locale`](crate::mfe::host_locale) at mount and provides a
/// [`Translator`](crate::i18n::Translator) as Dioxus context above the root, so
/// every component below reaches it with `use_context`.