MobDev.Plugin.RuntimeManifest (mob_dev v0.7.29)

Copy Markdown View Source

Builds the host's runtime plugin manifest — the device-readable record of the activated plugins' tier-3/4 contributions.

Tiers 3 and 4 are pure-Elixir and runtime-wired: the host needs to know, on device, which screens exist, what lifecycle/settings/notification declarations each plugin made — but MobDev.Plugin.activated/0 is compile-time only. So at build time this module gathers those sections (running any spec-v2 :screens_generator under the host-config audit) and emits a terms file (priv/generated/mob_plugins.exs) that the core Mob.Plugins module reads at boot. It mirrors the driver_tab / MobPluginBootstrap codegen pattern, but for serializable Elixir data instead of native symbols.

Only the behavioral data lives here. Migration files and font/image assets are physically copied into the host at build time (see native_build), not carried in this manifest.

Everything emitted must be serializable terms — atoms, tuples, maps, strings, integers. The manifest validator forbids closures in plugin sections so this always holds.

Summary

Functions

Builds the merged runtime manifest map from {plugin_dir, manifest} pairs.

Renders the manifest map to the contents of priv/generated/mob_plugins.exs — a self-describing .exs that evaluates back to the map.

Validates a :screens_generator's output, raising on malformed specs.

Writes the rendered manifest to <host_root>/priv/generated/mob_plugins.exs and returns the path.

Functions

build(plugins)

@spec build([{Path.t(), map() | nil}]) :: map()

Builds the merged runtime manifest map from {plugin_dir, manifest} pairs.

Static :screens and spec-v2 :screens_generator output are combined (each tagged with its plugin); generators run under MobDev.Plugin.with_host_config_audit/3 so an undeclared host-config read fails the build.

render(manifest)

@spec render(map()) :: String.t()

Renders the manifest map to the contents of priv/generated/mob_plugins.exs — a self-describing .exs that evaluates back to the map.

Every map, nested ones included, is printed with its keys sorted, so the same manifest renders to the same bytes in any VM. Without that, a map's printed key order follows atom-creation order (OTP 26+), which differs between runs, and mix mob.regen_plugin_manifest --check reported drift right after a regen (MOB-383).

validate_generated_screens(screens, plugin_name)

@spec validate_generated_screens(term(), atom() | nil) :: [map()]

Validates a :screens_generator's output, raising on malformed specs.

A generator must return a list of %{module: atom, default_route: binary} maps (a bare map is wrapped). This mirrors MobDev.Plugin.Manifest's static :screens validation so generator-produced screens are held to the same shape contract — otherwise a typo'd or wrong-typed spec would be written into mob_plugins.exs and silently dropped at boot (Mob.Plugins.register_screens/0 pattern-matches %{module:, default_route:} and skips non-matching maps), making the plugin's screen vanish with no build-time error.

Returns the validated screen list (without the :plugin tag, which the caller adds). Public for testing.

write(host_root, manifest)

@spec write(Path.t(), map()) :: Path.t()

Writes the rendered manifest to <host_root>/priv/generated/mob_plugins.exs and returns the path.